From 609d47dea177c8bc7c60593600548f0eacc4477f Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 1 Jun 2025 11:36:14 -0500 Subject: [PATCH 001/251] refactor: addInstance instead of series etc --- internal/gkr/gkrinfo/info.go | 14 +-- std/gkrapi/compile.go | 228 +++++++++++++++++------------------ 2 files changed, 117 insertions(+), 125 deletions(-) diff --git a/internal/gkr/gkrinfo/info.go b/internal/gkr/gkrinfo/info.go index de9a845e8d..55fdb7e71d 100644 --- a/internal/gkr/gkrinfo/info.go +++ b/internal/gkr/gkrinfo/info.go @@ -30,13 +30,12 @@ type ( IsGkrVar []bool } StoringInfo struct { - Circuit Circuit - Dependencies [][]InputDependency // nil for input wires - NbInstances int - HashName string - SolveHintID solver.HintID - ProveHintID solver.HintID - Prints []PrintInfo + Circuit Circuit + NbInstances int + HashName string + SolveHintID solver.HintID + ProveHintID solver.HintID + Prints []PrintInfo } Permutations struct { @@ -58,7 +57,6 @@ func (w Wire) IsOutput() bool { func (d *StoringInfo) NewInputVariable() int { i := len(d.Circuit) d.Circuit = append(d.Circuit, Wire{}) - d.Dependencies = append(d.Dependencies, nil) return i } diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 6293e86add..d8d4d3e960 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -1,7 +1,6 @@ package gkrapi import ( - "errors" "fmt" "math/bits" @@ -23,18 +22,16 @@ type circuitDataForSnark struct { assignments gkrtypes.WireAssignment } -type Solution struct { - toStore gkrinfo.StoringInfo - assignments gkrtypes.WireAssignment - parentApi frontend.API - permutations gkrinfo.Permutations -} +type InitialChallengeGetter func() []frontend.Variable -func (api *API) nbInstances() int { - if len(api.assignments) == 0 { - return -1 - } - return api.assignments.NbInstances() +// Circuit represents a GKR circuit. +type Circuit struct { + toStore gkrinfo.StoringInfo + assignments gkrtypes.WireAssignment + getInitialChallenges InitialChallengeGetter // optional getter for the initial Fiat-Shamir challenge + ins []gkr.Variable + outs []gkr.Variable + api frontend.API // the parent API used for hints } // New creates a new GKR API @@ -50,162 +47,159 @@ func log2(x uint) int { return bits.TrailingZeros(x) } -// Series like in an electric circuit, binds an input of an instance to an output of another -func (api *API) Series(input, output gkr.Variable, inputInstance, outputInstance int) *API { - if api.assignments[input][inputInstance] != nil { - panic("dependency attempting to override explicit value assignment") - } - api.toStore.Dependencies[input] = - append(api.toStore.Dependencies[input], gkrinfo.InputDependency{ - OutputWire: int(output), - OutputInstance: outputInstance, - InputInstance: inputInstance, - }) - return api +// NewInput creates a new input variable. +func (api *API) NewInput() gkr.Variable { + return gkr.Variable(api.toStore.NewInputVariable()) } -// Import creates a new input variable, whose values across all instances are given by assignment. -// If the value in an instance depends on an output of another instance, leave the corresponding index in assignment nil and use Series to specify the dependency. -func (api *API) Import(assignment []frontend.Variable) (gkr.Variable, error) { - nbInstances := len(assignment) - logNbInstances := log2(uint(nbInstances)) - if logNbInstances == -1 { - return -1, errors.New("number of assignments must be a power of 2") - } +type compileOption func(*Circuit) - if currentNbInstances := api.nbInstances(); currentNbInstances != -1 && currentNbInstances != nbInstances { - return -1, errors.New("number of assignments must be consistent across all variables") +// WithInitialChallenge provides a getter for the initial Fiat-Shamir challenge. +// If not provided, the initial challenge will be a commitment to all the input and output values of the circuit. +func WithInitialChallenge(getInitialChallenge InitialChallengeGetter) compileOption { + return func(c *Circuit) { + c.getInitialChallenges = getInitialChallenge } - api.assignments = append(api.assignments, assignment) - return gkr.Variable(api.toStore.NewInputVariable()), nil } -// appendNonNil filters out nil values from src and appends the non-nil values to dst. -// i.e. dst = [0,1], src = [nil, 2, nil, 3] => dst = [0,1,2,3]. -func appendNonNil(dst *[]frontend.Variable, src []frontend.Variable) { - for i := range src { - if src[i] != nil { - *dst = append(*dst, src[i]) - } +// Compile finalizes the GKR circuit. +// From this point on, the circuit cannot be modified. +// But instances can be added to the circuit. +func (api *API) Compile(parentApi frontend.API, fiatshamirHashName string, options ...compileOption) *Circuit { + // TODO define levels here + res := Circuit{ + toStore: api.toStore, + assignments: make(gkrtypes.WireAssignment, len(api.toStore.Circuit)), + api: parentApi, } -} -// Solve finalizes the GKR circuit and returns the output variables in the order created -func (api *API) Solve(parentApi frontend.API) (Solution, error) { + api.toStore.HashName = fiatshamirHashName - var p gkrinfo.Permutations - var err error - if p, err = api.toStore.Compile(api.assignments.NbInstances()); err != nil { - return Solution{}, err + for _, opt := range options { + opt(&res) } - api.assignments.Permute(p) - nbInstances := api.toStore.NbInstances - circuit := api.toStore.Circuit + for i := range res.toStore.Circuit { + if res.toStore.Circuit[i].IsOutput() { + res.outs = append(res.ins, gkr.Variable(i)) + } + if res.toStore.Circuit[i].IsInput() { + res.ins = append(res.ins, gkr.Variable(i)) + } + } + res.toStore.SolveHintID = solver.GetHintID(SolveHintPlaceholder(res.toStore)) + res.toStore.ProveHintID = solver.GetHintID(ProveHintPlaceholder(fiatshamirHashName)) - solveHintNIn := 0 - solveHintNOut := 0 + parentApi.Compiler().Defer(res.verify) - for i := range circuit { - v := &circuit[i] - in, out := v.IsInput(), v.IsOutput() - if in && out { - return Solution{}, fmt.Errorf("unused input (variable #%d)", i) - } + return &res +} - if in { - solveHintNIn += nbInstances - len(api.toStore.Dependencies[i]) - } else if out { - solveHintNOut += nbInstances +// AddInstance adds a new instance to the GKR circuit, returning the values of output variables for the instance. +func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr.Variable]frontend.Variable, error) { + if len(input) != len(c.ins) { + for k := range input { + if k >= gkr.Variable(len(c.ins)) { + return nil, fmt.Errorf("variable %d is out of bounds (max %d)", k, len(c.ins)-1) + } + if !c.toStore.Circuit[k].IsInput() { + return nil, fmt.Errorf("value provided for non-input variable %d", k) + } } } - - // arrange inputs wire first, then in the order solved - ins := make([]frontend.Variable, 0, solveHintNIn) - for i := range circuit { - if circuit[i].IsInput() { - appendNonNil(&ins, api.assignments[i]) + hintIn := make([]frontend.Variable, 1+len(c.ins)) // first input denotes the instance number + hintIn[0] = c.toStore.NbInstances + for hintInI, in := range c.ins { + if inV, ok := input[in]; !ok { + return nil, fmt.Errorf("missing entry for input variable %d", in) + } else { + hintIn[hintInI+1] = inV } } - solveHintPlaceholder := SolveHintPlaceholder(api.toStore) - outsSerialized, err := parentApi.Compiler().NewHint(solveHintPlaceholder, solveHintNOut, ins...) - api.toStore.SolveHintID = solver.GetHintID(solveHintPlaceholder) + c.toStore.NbInstances++ + solveHintPlaceholder := SolveHintPlaceholder(c.toStore) + outsSerialized, err := c.api.Compiler().NewHint(solveHintPlaceholder, len(c.outs), hintIn...) if err != nil { - return Solution{}, err - } - - for i := range circuit { - if circuit[i].IsOutput() { - api.assignments[i] = outsSerialized[:nbInstances] - outsSerialized = outsSerialized[nbInstances:] - } + return nil, fmt.Errorf("failed to create solve hint: %w", err) } - - for i := range circuit { - for _, dep := range api.toStore.Dependencies[i] { - api.assignments[i][dep.InputInstance] = api.assignments[dep.OutputWire][dep.OutputInstance] - } + res := make(map[gkr.Variable]frontend.Variable, len(c.outs)) + for i, v := range c.outs { + res[v] = outsSerialized[i] + c.assignments[v] = append(c.assignments[v], outsSerialized[i]) } - return Solution{ - toStore: api.toStore, - assignments: api.assignments, - parentApi: parentApi, - permutations: p, - }, nil + return res, nil } -// Export returns the values of an output variable across all instances -func (s Solution) Export(v gkr.Variable) []frontend.Variable { - return utils.Map(s.permutations.SortedInstances, utils.SliceAt(s.assignments[v])) -} +// verify encodes the verification circuitry for the GKR circuit +func (c *Circuit) verify(api frontend.API) error { + if api != c.api { + panic("api mismatch") + } + + if len(c.outs) == 0 || len(c.assignments[0]) == 0 { + return nil + } -// Verify encodes the verification circuitry for the GKR circuit -func (s Solution) Verify(hashName string, initialChallenges ...frontend.Variable) error { var ( - err error - proofSerialized []frontend.Variable - proof gadget.Proof + err error + proofSerialized []frontend.Variable + proof gadget.Proof + initialChallenges []frontend.Variable ) - forSnark := newCircuitDataForSnark(s.toStore, s.assignments) - logNbInstances := log2(uint(s.assignments.NbInstances())) + if c.getInitialChallenges != nil { + initialChallenges = c.getInitialChallenges() + } else { + // default initial challenge is a commitment to all input and output values + initialChallenges = make([]frontend.Variable, 0, (len(c.ins)+len(c.outs))*len(c.assignments[c.ins[0]])) + for _, in := range c.ins { + initialChallenges = append(initialChallenges, c.assignments[in]...) + } + for _, out := range c.outs { + initialChallenges = append(initialChallenges, c.assignments[out]...) + } - hintIns := make([]frontend.Variable, len(initialChallenges)+1) // hack: adding one of the outputs of the solve hint to ensure "prove" is called after "solve" - for i, w := range s.toStore.Circuit { - if w.IsOutput() { - hintIns[0] = s.assignments[i][len(s.assignments[i])-1] - break + if initialChallenges[0], err = api.(frontend.Committer).Commit(initialChallenges...); err != nil { + return fmt.Errorf("failed to commit to in/out values: %w", err) } + initialChallenges = initialChallenges[:1] // use the commitment as the only initial challenge } + + forSnark := newCircuitDataForSnark(c.toStore, c.assignments) + logNbInstances := log2(uint(c.assignments.NbInstances())) + + hintIns := make([]frontend.Variable, len(initialChallenges)+1) // hack: adding one of the outputs of the solve hint to ensure "prove" is called after "solve" + firstOutputAssignment := c.assignments[c.outs[0]] + hintIns[0] = firstOutputAssignment[len(firstOutputAssignment)-1] // take the last output of the first output wire + copy(hintIns[1:], initialChallenges) - proveHintPlaceholder := ProveHintPlaceholder(hashName) - if proofSerialized, err = s.parentApi.Compiler().NewHint( + proveHintPlaceholder := ProveHintPlaceholder(c.toStore.HashName) + if proofSerialized, err = api.Compiler().NewHint( proveHintPlaceholder, gadget.ProofSize(forSnark.circuit, logNbInstances), hintIns...); err != nil { return err } - s.toStore.ProveHintID = solver.GetHintID(proveHintPlaceholder) + c.toStore.ProveHintID = solver.GetHintID(proveHintPlaceholder) - forSnarkSorted := utils.MapRange(0, len(s.toStore.Circuit), slicePtrAt(forSnark.circuit)) + forSnarkSorted := utils.MapRange(0, len(c.toStore.Circuit), slicePtrAt(forSnark.circuit)) if proof, err = gadget.DeserializeProof(forSnarkSorted, proofSerialized); err != nil { return err } var hsh hash.FieldHasher - if hsh, err = hash.GetFieldHasher(hashName, s.parentApi); err != nil { + if hsh, err = hash.GetFieldHasher(c.toStore.HashName, api); err != nil { return err } - s.toStore.HashName = hashName - err = gadget.Verify(s.parentApi, forSnark.circuit, forSnark.assignments, proof, fiatshamir.WithHash(hsh, initialChallenges...), gadget.WithSortedCircuit(forSnarkSorted)) + err = gadget.Verify(api, forSnark.circuit, forSnark.assignments, proof, fiatshamir.WithHash(hsh, initialChallenges...), gadget.WithSortedCircuit(forSnarkSorted)) if err != nil { return err } - return s.parentApi.(gkrinfo.ConstraintSystem).SetGkrInfo(s.toStore) + return api.(gkrinfo.ConstraintSystem).SetGkrInfo(c.toStore) } func slicePtrAt[T any](slice []T) func(int) *T { From 79d4cbfef3705f1ac25b7e349dd2e7739ac24cf4 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 1 Jun 2025 16:03:13 -0500 Subject: [PATCH 002/251] feat: check for duplicate gates, allow limiting curves for gate --- constraint/solver/gkrgates/registry.go | 52 ++++++++++++++++--- .../backend/template/gkr/gate_testing.go.tmpl | 11 ++++ internal/gkr/bls12-377/gate_testing.go | 11 ++++ internal/gkr/bls12-381/gate_testing.go | 11 ++++ internal/gkr/bls24-315/gate_testing.go | 11 ++++ internal/gkr/bls24-317/gate_testing.go | 11 ++++ internal/gkr/bn254/gate_testing.go | 11 ++++ internal/gkr/bw6-633/gate_testing.go | 11 ++++ internal/gkr/bw6-761/gate_testing.go | 11 ++++ internal/gkr/gkrtypes/types.go | 26 +++++++--- internal/gkr/small_rational/gate_testing.go | 11 ++++ std/gkrapi/compile.go | 32 +++++------- 12 files changed, 179 insertions(+), 30 deletions(-) diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index 49610a1789..88d0d3daa8 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -7,6 +7,7 @@ import ( "runtime" "sync" + "github.com/consensys/gnark" "github.com/consensys/gnark-crypto/ecc" bls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" @@ -100,12 +101,43 @@ func WithCurves(curves ...ecc.ID) registerOption { // - f is the polynomial function defining the gate. // - nbIn is the number of inputs to the gate. func Register(f gkr.GateFunction, nbIn int, options ...registerOption) error { - s := registerSettings{degree: -1, solvableVar: -1, name: GetDefaultGateName(f), curves: []ecc.ID{ecc.BN254}} + s := registerSettings{degree: -1, solvableVar: -1, name: GetDefaultGateName(f)} for _, option := range options { option(&s) } - for _, curve := range s.curves { + curvesForTesting := s.curves + allowedCurves := s.curves + if len(curvesForTesting) == 0 { + // no restriction on curves, but only test on BN254 + curvesForTesting = []ecc.ID{ecc.BN254} + allowedCurves = gnark.Curves() + } + + if g, ok := gates[s.name]; ok { + // gate already registered + if reflect.ValueOf(f).Pointer() != reflect.ValueOf(gates[s.name].Evaluate).Pointer() { + return fmt.Errorf("gate \"%s\" already registered with a different function", s.name) + } + // it still might be an anonymous function with different parameters. + // need to test further + if g.NbIn() != nbIn { + return fmt.Errorf("gate \"%s\" already registered with a different number of inputs (%d != %d)", s.name, g.NbIn(), nbIn) + } + + for _, curve := range curvesForTesting { + gateVer, err := NewGateVerifier(curve) + if err != nil { + return err + } + if !gateVer.equal(f, g.Evaluate, nbIn) { + return fmt.Errorf("mismatch with already registered gate \"%s\" (degree %d) over curve %s", s.name, s.degree, curve) + } + } + + } + + for _, curve := range curvesForTesting { gateVer, err := NewGateVerifier(curve) if err != nil { return err @@ -118,12 +150,12 @@ func Register(f gkr.GateFunction, nbIn int, options ...registerOption) error { const maxAutoDegreeBound = 32 var err error if s.degree, err = gateVer.findDegree(f, maxAutoDegreeBound, nbIn); err != nil { - return fmt.Errorf("for gate %s: %v", s.name, err) + return fmt.Errorf("for gate \"%s\": %v", s.name, err) } } else { if !s.noDegreeVerification { // check that the given degree is correct if err = gateVer.verifyDegree(f, s.degree, nbIn); err != nil { - return fmt.Errorf("for gate %s: %v", s.name, err) + return fmt.Errorf("for gate \"%s\": %v", s.name, err) } } } @@ -135,7 +167,7 @@ func Register(f gkr.GateFunction, nbIn int, options ...registerOption) error { } else { // solvable variable given if !s.noSolvableVarVerification && !gateVer.isVarSolvable(f, s.solvableVar, nbIn) { - return fmt.Errorf("cannot verify the solvability of variable %d in gate %s", s.solvableVar, s.name) + return fmt.Errorf("cannot verify the solvability of variable %d in gate \"%s\"", s.solvableVar, s.name) } } @@ -143,7 +175,7 @@ func Register(f gkr.GateFunction, nbIn int, options ...registerOption) error { gatesLock.Lock() defer gatesLock.Unlock() - gates[s.name] = gkrtypes.NewGate(f, nbIn, s.degree, s.solvableVar) + gates[s.name] = gkrtypes.NewGate(f, nbIn, s.degree, s.solvableVar, gkrtypes.WithCurves(allowedCurves...)) return nil } @@ -160,6 +192,7 @@ type gateVerifier struct { isAdditive func(f gkr.GateFunction, i int, nbIn int) bool findDegree func(f gkr.GateFunction, max, nbIn int) (int, error) verifyDegree func(f gkr.GateFunction, claimedDegree, nbIn int) error + equal func(f1, f2 gkr.GateFunction, nbIn int) bool } func NewGateVerifier(curve ecc.ID) (*gateVerifier, error) { @@ -172,30 +205,37 @@ func NewGateVerifier(curve ecc.ID) (*gateVerifier, error) { o.isAdditive = bls12377.IsGateFunctionAdditive o.findDegree = bls12377.FindGateFunctionDegree o.verifyDegree = bls12377.VerifyGateFunctionDegree + o.equal = bls12377.EqualGateFunction case ecc.BLS12_381: o.isAdditive = bls12381.IsGateFunctionAdditive o.findDegree = bls12381.FindGateFunctionDegree o.verifyDegree = bls12381.VerifyGateFunctionDegree + o.equal = bls12381.EqualGateFunction case ecc.BLS24_315: o.isAdditive = bls24315.IsGateFunctionAdditive o.findDegree = bls24315.FindGateFunctionDegree o.verifyDegree = bls24315.VerifyGateFunctionDegree + o.equal = bls24315.EqualGateFunction case ecc.BLS24_317: o.isAdditive = bls24317.IsGateFunctionAdditive o.findDegree = bls24317.FindGateFunctionDegree o.verifyDegree = bls24317.VerifyGateFunctionDegree + o.equal = bls24317.EqualGateFunction case ecc.BN254: o.isAdditive = bn254.IsGateFunctionAdditive o.findDegree = bn254.FindGateFunctionDegree o.verifyDegree = bn254.VerifyGateFunctionDegree + o.equal = bn254.EqualGateFunction case ecc.BW6_633: o.isAdditive = bw6633.IsGateFunctionAdditive o.findDegree = bw6633.FindGateFunctionDegree o.verifyDegree = bw6633.VerifyGateFunctionDegree + o.equal = bw6633.EqualGateFunction case ecc.BW6_761: o.isAdditive = bw6761.IsGateFunctionAdditive o.findDegree = bw6761.FindGateFunctionDegree o.verifyDegree = bw6761.VerifyGateFunctionDegree + o.equal = bw6761.EqualGateFunction default: err = fmt.Errorf("unsupported curve %s", curve) } diff --git a/internal/generator/backend/template/gkr/gate_testing.go.tmpl b/internal/generator/backend/template/gkr/gate_testing.go.tmpl index 534b4b01c8..89d1343be6 100644 --- a/internal/generator/backend/template/gkr/gate_testing.go.tmpl +++ b/internal/generator/backend/template/gkr/gate_testing.go.tmpl @@ -155,6 +155,17 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error return nil } +// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. +func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + x := make({{.FieldPackageName}}.Vector, nbIn) + x.MustSetRandom() + fFr := api.convertFunc(f) + gFr := api.convertFunc(g) + fAt := fFr(x...) + gAt := gFr(x...) + return fAt.Equal(gAt) +} + {{- if not .CanUseFFT }} // interpolate fits a polynomial of degree len(X) - 1 = len(Y) - 1 to the points (X[i], Y[i]) // Note that the runtime is O(len(X)³) diff --git a/internal/gkr/bls12-377/gate_testing.go b/internal/gkr/bls12-377/gate_testing.go index 415a5ff5b3..9e5a3868f3 100644 --- a/internal/gkr/bls12-377/gate_testing.go +++ b/internal/gkr/bls12-377/gate_testing.go @@ -142,3 +142,14 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error } return nil } + +// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. +func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + x := make(fr.Vector, nbIn) + x.MustSetRandom() + fFr := api.convertFunc(f) + gFr := api.convertFunc(g) + fAt := fFr(x...) + gAt := gFr(x...) + return fAt.Equal(gAt) +} diff --git a/internal/gkr/bls12-381/gate_testing.go b/internal/gkr/bls12-381/gate_testing.go index ef7694dc18..5b281fd634 100644 --- a/internal/gkr/bls12-381/gate_testing.go +++ b/internal/gkr/bls12-381/gate_testing.go @@ -142,3 +142,14 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error } return nil } + +// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. +func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + x := make(fr.Vector, nbIn) + x.MustSetRandom() + fFr := api.convertFunc(f) + gFr := api.convertFunc(g) + fAt := fFr(x...) + gAt := gFr(x...) + return fAt.Equal(gAt) +} diff --git a/internal/gkr/bls24-315/gate_testing.go b/internal/gkr/bls24-315/gate_testing.go index 1682d24771..058b53cc06 100644 --- a/internal/gkr/bls24-315/gate_testing.go +++ b/internal/gkr/bls24-315/gate_testing.go @@ -142,3 +142,14 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error } return nil } + +// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. +func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + x := make(fr.Vector, nbIn) + x.MustSetRandom() + fFr := api.convertFunc(f) + gFr := api.convertFunc(g) + fAt := fFr(x...) + gAt := gFr(x...) + return fAt.Equal(gAt) +} diff --git a/internal/gkr/bls24-317/gate_testing.go b/internal/gkr/bls24-317/gate_testing.go index 1bffab29e3..ed418ff1b0 100644 --- a/internal/gkr/bls24-317/gate_testing.go +++ b/internal/gkr/bls24-317/gate_testing.go @@ -142,3 +142,14 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error } return nil } + +// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. +func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + x := make(fr.Vector, nbIn) + x.MustSetRandom() + fFr := api.convertFunc(f) + gFr := api.convertFunc(g) + fAt := fFr(x...) + gAt := gFr(x...) + return fAt.Equal(gAt) +} diff --git a/internal/gkr/bn254/gate_testing.go b/internal/gkr/bn254/gate_testing.go index 716ba3891b..e9311a3ea5 100644 --- a/internal/gkr/bn254/gate_testing.go +++ b/internal/gkr/bn254/gate_testing.go @@ -142,3 +142,14 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error } return nil } + +// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. +func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + x := make(fr.Vector, nbIn) + x.MustSetRandom() + fFr := api.convertFunc(f) + gFr := api.convertFunc(g) + fAt := fFr(x...) + gAt := gFr(x...) + return fAt.Equal(gAt) +} diff --git a/internal/gkr/bw6-633/gate_testing.go b/internal/gkr/bw6-633/gate_testing.go index 0fafa45a0d..8074b9621c 100644 --- a/internal/gkr/bw6-633/gate_testing.go +++ b/internal/gkr/bw6-633/gate_testing.go @@ -142,3 +142,14 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error } return nil } + +// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. +func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + x := make(fr.Vector, nbIn) + x.MustSetRandom() + fFr := api.convertFunc(f) + gFr := api.convertFunc(g) + fAt := fFr(x...) + gAt := gFr(x...) + return fAt.Equal(gAt) +} diff --git a/internal/gkr/bw6-761/gate_testing.go b/internal/gkr/bw6-761/gate_testing.go index 6eda2ebe73..0bae6258dc 100644 --- a/internal/gkr/bw6-761/gate_testing.go +++ b/internal/gkr/bw6-761/gate_testing.go @@ -142,3 +142,14 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error } return nil } + +// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. +func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + x := make(fr.Vector, nbIn) + x.MustSetRandom() + fFr := api.convertFunc(f) + gFr := api.convertFunc(g) + fAt := fFr(x...) + gAt := gFr(x...) + return fAt.Equal(gAt) +} diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 7aed5ccd27..201f063952 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -4,6 +4,8 @@ import ( "errors" "fmt" + "github.com/consensys/gnark" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/utils" @@ -17,15 +19,27 @@ type Gate struct { nbIn int // number of inputs degree int // total degree of the polynomial solvableVar int // if there is a variable whose value can be uniquely determined from the value of the gate and the other inputs, its index, -1 otherwise + curves []ecc.ID // curves that the gate is allowed to be used over } -func NewGate(f gkr.GateFunction, nbIn int, degree int, solvableVar int) *Gate { +func NewGate(f gkr.GateFunction, nbIn int, degree int, solvableVar int, curves []ecc.ID) *Gate { + return &Gate{ evaluate: f, nbIn: nbIn, degree: degree, solvableVar: solvableVar, + curves: curves, + } +} + +func (g *Gate) SupportsCurve(curve ecc.ID) bool { + for _, c := range g.curves { + if c == curve { + return true + } } + return false } func (g *Gate) Evaluate(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { @@ -388,33 +402,33 @@ var ErrZeroFunction = errors.New("detected a zero function") func Identity() *Gate { return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return in[0] - }, 1, 1, 0) + }, 1, 1, 0, gnark.Curves()) } // Add2 gate: (x, y) -> x + y func Add2() *Gate { return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return api.Add(in[0], in[1]) - }, 2, 1, 0) + }, 2, 1, 0, gnark.Curves()) } // Sub2 gate: (x, y) -> x - y func Sub2() *Gate { return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return api.Sub(in[0], in[1]) - }, 2, 1, 0) + }, 2, 1, 0, gnark.Curves()) } // Neg gate: x -> -x func Neg() *Gate { return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return api.Neg(in[0]) - }, 1, 1, 0) + }, 1, 1, 0, gnark.Curves()) } // Mul2 gate: (x, y) -> x * y func Mul2() *Gate { return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return api.Mul(in[0], in[1]) - }, 2, 2, -1) + }, 2, 2, -1, gnark.Curves()) } diff --git a/internal/gkr/small_rational/gate_testing.go b/internal/gkr/small_rational/gate_testing.go index dc29624d7b..6e3dea5781 100644 --- a/internal/gkr/small_rational/gate_testing.go +++ b/internal/gkr/small_rational/gate_testing.go @@ -142,6 +142,17 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error return nil } +// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. +func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + x := make(small_rational.Vector, nbIn) + x.MustSetRandom() + fFr := api.convertFunc(f) + gFr := api.convertFunc(g) + fAt := fFr(x...) + gAt := gFr(x...) + return fAt.Equal(gAt) +} + // interpolate fits a polynomial of degree len(X) - 1 = len(Y) - 1 to the points (X[i], Y[i]) // Note that the runtime is O(len(X)³) func interpolate(X, Y []small_rational.SmallRational) (polynomial.Polynomial, error) { diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index d8d4d3e960..0ba6213286 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -4,6 +4,7 @@ import ( "fmt" "math/bits" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" "github.com/consensys/gnark/frontend" @@ -167,7 +168,10 @@ func (c *Circuit) verify(api frontend.API) error { initialChallenges = initialChallenges[:1] // use the commitment as the only initial challenge } - forSnark := newCircuitDataForSnark(c.toStore, c.assignments) + forSnark, err := newCircuitDataForSnark(utils.FieldToCurve(api.Compiler().Field()), c.toStore, c.assignments) + if err != nil { + return fmt.Errorf("failed to create circuit data for snark: %w", err) + } logNbInstances := log2(uint(c.assignments.NbInstances())) hintIns := make([]frontend.Variable, len(initialChallenges)+1) // hack: adding one of the outputs of the solve hint to ensure "prove" is called after "solve" @@ -208,30 +212,22 @@ func slicePtrAt[T any](slice []T) func(int) *T { } } -func ite[T any](condition bool, ifNot, IfSo T) T { - if condition { - return IfSo +func newCircuitDataForSnark(curve ecc.ID, info gkrinfo.StoringInfo, assignment gkrtypes.WireAssignment) (circuitDataForSnark, error) { + circuit, err := gkrtypes.CircuitInfoToCircuit(info.Circuit, gkrgates.Get) + if err != nil { + return circuitDataForSnark{}, fmt.Errorf("failed to convert GKR info to circuit: %w", err) } - return ifNot -} - -func newCircuitDataForSnark(info gkrinfo.StoringInfo, assignment gkrtypes.WireAssignment) circuitDataForSnark { - circuit := make(gkrtypes.Circuit, len(info.Circuit)) - snarkAssignment := make(gkrtypes.WireAssignment, len(info.Circuit)) for i := range circuit { - w := info.Circuit[i] - circuit[i] = gkrtypes.Wire{ - Gate: gkrgates.Get(ite(w.IsInput(), gkr.GateName(w.Gate), gkr.Identity)), - Inputs: w.Inputs, - NbUniqueOutputs: w.NbUniqueOutputs, + if !circuit[i].Gate.SupportsCurve(curve) { + return circuitDataForSnark{}, fmt.Errorf("gate \"%s\" not usable over curve \"%s\"", info.Circuit[i].Gate, curve) } - snarkAssignment[i] = assignment[i] } + return circuitDataForSnark{ circuit: circuit, - assignments: snarkAssignment, - } + assignments: assignment, + }, nil } func init() { From 82e33e54a94a7e4bb3bfd1aa6f3a9702be09a145 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 1 Jun 2025 17:03:06 -0500 Subject: [PATCH 003/251] refactor: gkr api tests --- constraint/solver/gkrgates/registry.go | 31 ++- std/gkrapi/api.go | 3 +- std/gkrapi/api_test.go | 366 +++++++++---------------- std/gkrapi/testing.go | 120 -------- 4 files changed, 153 insertions(+), 367 deletions(-) delete mode 100644 std/gkrapi/testing.go diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index 88d0d3daa8..2e1d8642ef 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -100,7 +100,9 @@ func WithCurves(curves ...ecc.ID) registerOption { // - name is a human-readable name for the gate. // - f is the polynomial function defining the gate. // - nbIn is the number of inputs to the gate. -func Register(f gkr.GateFunction, nbIn int, options ...registerOption) error { +// +// If the gate is already registered, it will return false and no error. +func Register(f gkr.GateFunction, nbIn int, options ...registerOption) (registered bool, err error) { s := registerSettings{degree: -1, solvableVar: -1, name: GetDefaultGateName(f)} for _, option := range options { option(&s) @@ -114,33 +116,37 @@ func Register(f gkr.GateFunction, nbIn int, options ...registerOption) error { allowedCurves = gnark.Curves() } + gatesLock.Lock() + defer gatesLock.Unlock() + if g, ok := gates[s.name]; ok { // gate already registered if reflect.ValueOf(f).Pointer() != reflect.ValueOf(gates[s.name].Evaluate).Pointer() { - return fmt.Errorf("gate \"%s\" already registered with a different function", s.name) + return false, fmt.Errorf("gate \"%s\" already registered with a different function", s.name) } // it still might be an anonymous function with different parameters. // need to test further if g.NbIn() != nbIn { - return fmt.Errorf("gate \"%s\" already registered with a different number of inputs (%d != %d)", s.name, g.NbIn(), nbIn) + return false, fmt.Errorf("gate \"%s\" already registered with a different number of inputs (%d != %d)", s.name, g.NbIn(), nbIn) } for _, curve := range curvesForTesting { gateVer, err := NewGateVerifier(curve) if err != nil { - return err + return false, err } if !gateVer.equal(f, g.Evaluate, nbIn) { - return fmt.Errorf("mismatch with already registered gate \"%s\" (degree %d) over curve %s", s.name, s.degree, curve) + return false, fmt.Errorf("mismatch with already registered gate \"%s\" (degree %d) over curve %s", s.name, s.degree, curve) } } + return false, nil // gate already registered } for _, curve := range curvesForTesting { gateVer, err := NewGateVerifier(curve) if err != nil { - return err + return false, err } if s.degree == -1 { // find a degree @@ -148,14 +154,13 @@ func Register(f gkr.GateFunction, nbIn int, options ...registerOption) error { panic("invalid settings") } const maxAutoDegreeBound = 32 - var err error if s.degree, err = gateVer.findDegree(f, maxAutoDegreeBound, nbIn); err != nil { - return fmt.Errorf("for gate \"%s\": %v", s.name, err) + return false, fmt.Errorf("for gate \"%s\": %v", s.name, err) } } else { if !s.noDegreeVerification { // check that the given degree is correct if err = gateVer.verifyDegree(f, s.degree, nbIn); err != nil { - return fmt.Errorf("for gate \"%s\": %v", s.name, err) + return false, fmt.Errorf("for gate \"%s\": %v", s.name, err) } } } @@ -167,16 +172,14 @@ func Register(f gkr.GateFunction, nbIn int, options ...registerOption) error { } else { // solvable variable given if !s.noSolvableVarVerification && !gateVer.isVarSolvable(f, s.solvableVar, nbIn) { - return fmt.Errorf("cannot verify the solvability of variable %d in gate \"%s\"", s.solvableVar, s.name) + return false, fmt.Errorf("cannot verify the solvability of variable %d in gate \"%s\"", s.solvableVar, s.name) } } } - gatesLock.Lock() - defer gatesLock.Unlock() - gates[s.name] = gkrtypes.NewGate(f, nbIn, s.degree, s.solvableVar, gkrtypes.WithCurves(allowedCurves...)) - return nil + gates[s.name] = gkrtypes.NewGate(f, nbIn, s.degree, s.solvableVar, allowedCurves) + return true, nil } func Get(name gkr.GateName) *gkrtypes.Gate { diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index 771613ce0d..18a9b23279 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -23,12 +23,11 @@ func (api *API) NamedGate(gate gkr.GateName, in ...gkr.Variable) gkr.Variable { Inputs: utils.Map(in, frontendVarToInt), }) api.assignments = append(api.assignments, nil) - api.toStore.Dependencies = append(api.toStore.Dependencies, nil) // formality. Dependencies are only defined for input vars. return gkr.Variable(len(api.toStore.Circuit) - 1) } func (api *API) Gate(gate gkr.GateFunction, in ...gkr.Variable) gkr.Variable { - if err := gkrgates.Register(gate, len(in)); err != nil { + if _, err := gkrgates.Register(gate, len(in)); err != nil { panic(err) } return api.NamedGate(gkrgates.GetDefaultGateName(gate), in...) diff --git a/std/gkrapi/api_test.go b/std/gkrapi/api_test.go index 7bb255d70c..5cd9163ed8 100644 --- a/std/gkrapi/api_test.go +++ b/std/gkrapi/api_test.go @@ -16,10 +16,8 @@ import ( "github.com/consensys/gnark-crypto/ecc" gcHash "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/backend/groth16" - "github.com/consensys/gnark/constraint/solver/gkrgates" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/std/gkrapi/gkr" stdHash "github.com/consensys/gnark/std/hash" "github.com/consensys/gnark/std/hash/mimc" @@ -40,23 +38,21 @@ type doubleNoDependencyCircuit struct { func (c *doubleNoDependencyCircuit) Define(api frontend.API) error { gkrApi := New() - var x gkr.Variable - var err error - if x, err = gkrApi.Import(c.X); err != nil { - return err - } + x := gkrApi.NewInput() z := gkrApi.Add(x, x) - var solution Solution - if solution, err = gkrApi.Solve(api); err != nil { - return err - } - Z := solution.Export(z) - for i := range Z { - api.AssertIsEqual(Z[i], api.Mul(2, c.X[i])) - } + gkrCircuit := gkrApi.Compile(api, c.hashName) - return solution.Verify(c.hashName) + instanceIn := make(map[gkr.Variable]frontend.Variable) + for i := range c.X { + instanceIn[x] = c.X[i] + instanceOut, err := gkrCircuit.AddInstance(instanceIn) + if err != nil { + return fmt.Errorf("failed to add instance: %w", err) + } + api.AssertIsEqual(instanceOut[z], api.Mul(2, c.X[i])) + } + return nil } func TestDoubleNoDependencyCircuit(t *testing.T) { @@ -88,23 +84,21 @@ type sqNoDependencyCircuit struct { func (c *sqNoDependencyCircuit) Define(api frontend.API) error { gkrApi := New() - var x gkr.Variable - var err error - if x, err = gkrApi.Import(c.X); err != nil { - return err - } + x := gkrApi.NewInput() z := gkrApi.Mul(x, x) - var solution Solution - if solution, err = gkrApi.Solve(api); err != nil { - return err - } - Z := solution.Export(z) - for i := range Z { - api.AssertIsEqual(Z[i], api.Mul(c.X[i], c.X[i])) - } + gkrCircuit := gkrApi.Compile(api, c.hashName) - return solution.Verify(c.hashName) + instanceIn := make(map[gkr.Variable]frontend.Variable) + for i := range c.X { + instanceIn[x] = c.X[i] + instanceOut, err := gkrCircuit.AddInstance(instanceIn) + if err != nil { + return fmt.Errorf("failed to add instance: %w", err) + } + api.AssertIsEqual(instanceOut[z], api.Mul(c.X[i], c.X[i])) + } + return nil } func TestSqNoDependencyCircuit(t *testing.T) { @@ -135,29 +129,23 @@ type mulNoDependencyCircuit struct { func (c *mulNoDependencyCircuit) Define(api frontend.API) error { gkrApi := New() - var x, y gkr.Variable - var err error - if x, err = gkrApi.Import(c.X); err != nil { - return err - } - if y, err = gkrApi.Import(c.Y); err != nil { - return err - } - gkrApi.Println(0, "values of x and y in instance number", 0, x, y) + x := gkrApi.NewInput() + y := gkrApi.NewInput() + z := gkrApi.Add(x, y) - z := gkrApi.Mul(x, y) - gkrApi.Println(1, "value of z in instance number", 1, z) - var solution Solution - if solution, err = gkrApi.Solve(api); err != nil { - return err - } - Z := solution.Export(z) + gkrCircuit := gkrApi.Compile(api, c.hashName) + instanceIn := make(map[gkr.Variable]frontend.Variable) for i := range c.X { - api.AssertIsEqual(Z[i], api.Mul(c.X[i], c.Y[i])) + instanceIn[x] = c.X[i] + instanceIn[y] = c.Y[i] + instanceOut, err := gkrCircuit.AddInstance(instanceIn) + if err != nil { + return fmt.Errorf("failed to add instance: %w", err) + } + api.AssertIsEqual(instanceOut[z], api.Mul(c.Y[i], c.X[i])) } - - return solution.Verify(c.hashName) + return nil } func TestMulNoDependency(t *testing.T) { @@ -191,91 +179,68 @@ func TestMulNoDependency(t *testing.T) { } type mulWithDependencyCircuit struct { - XLast frontend.Variable + XFirst frontend.Variable Y []frontend.Variable hashName string } func (c *mulWithDependencyCircuit) Define(api frontend.API) error { gkrApi := New() - var x, y gkr.Variable - var err error - - X := make([]frontend.Variable, len(c.Y)) - X[len(c.Y)-1] = c.XLast - if x, err = gkrApi.Import(X); err != nil { - return err - } - if y, err = gkrApi.Import(c.Y); err != nil { - return err - } + x := gkrApi.NewInput() // x is the state variable + y := gkrApi.NewInput() z := gkrApi.Mul(x, y) - for i := len(X) - 1; i > 0; i-- { - gkrApi.Series(x, z, i-1, i) - } + gkrCircuit := gkrApi.Compile(api, c.hashName) - var solution Solution - if solution, err = gkrApi.Solve(api); err != nil { - return err - } - X = solution.Export(x) - Y := solution.Export(y) - Z := solution.Export(z) + state := c.XFirst + instanceIn := make(map[gkr.Variable]frontend.Variable) + + for i := range c.Y { + instanceIn[x] = state + instanceIn[y] = c.Y[i] + + instanceOut, err := gkrCircuit.AddInstance(instanceIn) + if err != nil { + return fmt.Errorf("failed to add instance: %w", err) + } - lastI := len(X) - 1 - api.AssertIsEqual(Z[lastI], api.Mul(c.XLast, Y[lastI])) - for i := 0; i < lastI; i++ { - api.AssertIsEqual(Z[i], api.Mul(Z[i+1], Y[i])) + state = instanceOut[z] // update state for the next iteration + api.AssertIsEqual(state, api.Mul(state, c.Y[i])) } - return solution.Verify(c.hashName) + return nil } func TestSolveMulWithDependency(t *testing.T) { assert := test.NewAssert(t) assignment := mulWithDependencyCircuit{ - XLast: 1, - Y: []frontend.Variable{3, 2}, + XFirst: 1, + Y: []frontend.Variable{3, 2}, } circuit := mulWithDependencyCircuit{Y: make([]frontend.Variable, len(assignment.Y)), hashName: "-20"} assert.CheckCircuit(&circuit, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BN254)) } func TestApiMul(t *testing.T) { - var ( - x gkr.Variable - y gkr.Variable - z gkr.Variable - err error - ) api := New() - x, err = api.Import([]frontend.Variable{nil, nil}) - require.NoError(t, err) - y, err = api.Import([]frontend.Variable{nil, nil}) - require.NoError(t, err) - z = api.Mul(x, y) + x := api.NewInput() + y := api.NewInput() + z := api.Mul(x, y) assertSliceEqual(t, api.toStore.Circuit[z].Inputs, []int{int(x), int(y)}) // TODO: Find out why assert.Equal gives false positives ( []*Wire{x,x} as second argument passes when it shouldn't ) } func BenchmarkMiMCMerkleTree(b *testing.B) { - depth := 14 - bottom := make([]frontend.Variable, 1<= 0; d-- { - for i := 0; i < 1< 1 { + nextLayer := curLayer[:len(curLayer)/2] - challenge, err := api.(frontend.Committer).Commit(Z...) - if err != nil { - return err - } + for i := range nextLayer { + instanceIn[x] = curLayer[2*i] + instanceIn[y] = curLayer[2*i+1] - return solution.Verify("-20", challenge) + instanceOut, err := gkrCircuit.AddInstance(instanceIn) + if err != nil { + return fmt.Errorf("failed to add instance: %w", err) + } + nextLayer[i] = instanceOut[z] // store the result of the hash + } + + curLayer = nextLayer + } + return nil } -func registerMiMC() { +func init() { stdHash.Register("MIMC", func(api frontend.API) (stdHash.FieldHasher, error) { m, err := mimc.NewMiMC(api) return &m, err }) } -func init() { - registerMiMC() - registerMiMCGate() -} - -func registerMiMCGate() { - // register mimc gate - panicIfError(gkrgates.Register(func(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { - mimcSnarkTotalCalls++ +func mimcGate(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { + mimcSnarkTotalCalls++ - if len(input) != 2 { - panic("mimc has fan-in 2") - } - sum := api.Add(input[0], input[1] /*, m.Ark*/) + 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) - }, 2, gkrgates.WithDegree(7), gkrgates.WithName("MIMC"))) + sumCubed := api.Mul(sum, sum, sum) // sum^3 + return api.Mul(sumCubed, sumCubed, sum) } type constPseudoHash int @@ -465,26 +422,25 @@ type mimcNoDepCircuit struct { } func (c *mimcNoDepCircuit) Define(api frontend.API) error { - _gkr := New() - x, err := _gkr.Import(c.X) - if err != nil { - return err - } - var ( - y gkr.Variable - solution Solution - ) - if y, err = _gkr.Import(c.Y); err != nil { - return err - } + // define the circuit + gkrApi := New() + x := gkrApi.NewInput() + y := gkrApi.NewInput() + gkrApi.Gate(mimcGate, x, y) - z := _gkr.NamedGate("MIMC", x, y) + gkrCircuit := gkrApi.Compile(api, c.hashName) - if solution, err = _gkr.Solve(api); err != nil { - return err + instanceIn := make(map[gkr.Variable]frontend.Variable) + for i := range c.X { + instanceIn[x] = c.X[i] + instanceIn[y] = c.Y[i] + + _, err := gkrCircuit.AddInstance(instanceIn) + if err != nil { + return fmt.Errorf("failed to add instance: %w", err) + } } - Z := solution.Export(z) - return solution.Verify(c.hashName, Z...) + return nil } func mimcNoDepCircuits(mimcDepth, nbInstances int, hashName string) (circuit, assignment frontend.Circuit) { @@ -566,58 +522,6 @@ func mimcNoGkrCircuits(mimcDepth, nbInstances int) (circuit, assignment frontend return } -func TestSolveInTestEngine(t *testing.T) { - assignment := testSolveInTestEngineCircuit{ - X: []frontend.Variable{2, 3, 4, 5, 6, 7, 8, 9}, - } - circuit := testSolveInTestEngineCircuit{ - X: make([]frontend.Variable, len(assignment.X)), - } - - require.NoError(t, test.IsSolved(&circuit, &assignment, ecc.BN254.ScalarField())) - require.NoError(t, test.IsSolved(&circuit, &assignment, ecc.BLS24_315.ScalarField())) - require.NoError(t, test.IsSolved(&circuit, &assignment, ecc.BLS12_381.ScalarField())) - require.NoError(t, test.IsSolved(&circuit, &assignment, ecc.BLS24_317.ScalarField())) - require.NoError(t, test.IsSolved(&circuit, &assignment, ecc.BW6_633.ScalarField())) - require.NoError(t, test.IsSolved(&circuit, &assignment, ecc.BW6_761.ScalarField())) - require.NoError(t, test.IsSolved(&circuit, &assignment, ecc.BLS12_377.ScalarField())) -} - -type testSolveInTestEngineCircuit struct { - X []frontend.Variable -} - -func (c *testSolveInTestEngineCircuit) Define(api frontend.API) error { - gkrBn254 := New() - x, err := gkrBn254.Import(c.X) - if err != nil { - return err - } - Y := make([]frontend.Variable, len(c.X)) - Y[0] = 1 - y, err := gkrBn254.Import(Y) - if err != nil { - return err - } - - z := gkrBn254.Mul(x, y) - - for i := range len(c.X) - 1 { - gkrBn254.Series(y, z, i+1, i) - } - - assignments := gkrBn254.SolveInTestEngine(api) - - product := frontend.Variable(1) - for i := range c.X { - api.AssertIsEqual(assignments[y][i], product) - product = api.Mul(product, c.X[i]) - api.AssertIsEqual(assignments[z][i], product) - } - - return nil -} - func panicIfError(err error) { if err != nil { panic(err) diff --git a/std/gkrapi/testing.go b/std/gkrapi/testing.go deleted file mode 100644 index 17163c0b5a..0000000000 --- a/std/gkrapi/testing.go +++ /dev/null @@ -1,120 +0,0 @@ -package gkrapi - -import ( - "errors" - "fmt" - "sync" - - "github.com/consensys/gnark/constraint/solver/gkrgates" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" - stdHash "github.com/consensys/gnark/std/hash" -) - -type solveInTestEngineSettings struct { - hashName string -} - -type SolveInTestEngineOption func(*solveInTestEngineSettings) - -func WithHashName(name string) SolveInTestEngineOption { - return func(s *solveInTestEngineSettings) { - s.hashName = name - } -} - -// SolveInTestEngine solves the defined circuit directly inside the SNARK circuit. This means that the method does not compute the GKR proof of the circuit and does not embed the GKR proof verifier inside a SNARK. -// The output is the values of all variables, across all instances; i.e. indexed variable-first, instance-second. -// This method only works under the test engine and should only be called to debug a GKR circuit, as the GKR prover's errors can be obscure. -func (api *API) SolveInTestEngine(parentApi frontend.API, options ...SolveInTestEngineOption) [][]frontend.Variable { - gateVer, err := gkrgates.NewGateVerifier(utils.FieldToCurve(parentApi.Compiler().Field())) - if err != nil { - panic(err) - } - - var s solveInTestEngineSettings - for _, o := range options { - o(&s) - } - if s.hashName != "" { - // hash something and make sure it gives the same answer both on prover and verifier sides - // TODO @Tabaie If indeed cheap, move this feature to Verify so that it is always run - h, err := stdHash.GetFieldHasher(s.hashName, parentApi) - if err != nil { - panic(err) - } - nbBytes := (parentApi.Compiler().FieldBitLen() + 7) / 8 - toHash := frontend.Variable(0) - for i := range nbBytes { - toHash = parentApi.Add(parentApi.Mul(toHash, 256), i%256) - } - h.Reset() - h.Write(toHash) - hashed := h.Sum() - - hintOut, err := parentApi.Compiler().NewHint(CheckHashHint(s.hashName), 1, toHash, hashed) - if err != nil { - panic(err) - } - parentApi.AssertIsEqual(hintOut[0], hashed) // the hint already checks this - } - - res := make([][]frontend.Variable, len(api.toStore.Circuit)) - var verifiedGates sync.Map - for i, w := range api.toStore.Circuit { - res[i] = make([]frontend.Variable, api.nbInstances()) - copy(res[i], api.assignments[i]) - if len(w.Inputs) == 0 { - continue - } - } - for instanceI := range api.nbInstances() { - for wireI, w := range api.toStore.Circuit { - deps := api.toStore.Dependencies[wireI] - if len(deps) != 0 && len(w.Inputs) != 0 { - panic(fmt.Errorf("non-input wire %d should not have dependencies", wireI)) - } - for _, dep := range deps { - if dep.InputInstance == instanceI { - if dep.OutputInstance >= instanceI { - panic(fmt.Errorf("out of order dependency not yet supported in SolveInTestEngine; (wire %d, instance %d) depends on (wire %d, instance %d)", wireI, instanceI, dep.OutputWire, dep.OutputInstance)) - } - if res[wireI][instanceI] != nil { - panic(fmt.Errorf("dependency (wire %d, instance %d) <- (wire %d, instance %d) attempting to override existing value assignment", wireI, instanceI, dep.OutputWire, dep.OutputInstance)) - } - res[wireI][instanceI] = res[dep.OutputWire][dep.OutputInstance] - } - } - - if res[wireI][instanceI] == nil { // no assignment or dependency - if len(w.Inputs) == 0 { - panic(fmt.Errorf("input wire %d, instance %d has no dependency or explicit assignment", wireI, instanceI)) - } - ins := make([]frontend.Variable, len(w.Inputs)) - for i, in := range w.Inputs { - ins[i] = res[in][instanceI] - } - gate := gkrgates.Get(gkr.GateName(w.Gate)) - if gate == nil && !w.IsInput() { - panic(fmt.Errorf("gate %s not found", w.Gate)) - } - if _, ok := verifiedGates.Load(w.Gate); !ok { - verifiedGates.Store(w.Gate, struct{}{}) - - err = errors.Join( - gateVer.VerifyDegree(gate), - gateVer.VerifySolvability(gate), - ) - if err != nil { - panic(fmt.Errorf("gate %s: %w", w.Gate, err)) - } - } - if gate != nil { - res[wireI][instanceI] = gate.Evaluate(parentApi, ins...) - } - } - } - } - return res -} From ba06e5d433765e8fd417ab2bc024a91e95388599 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 1 Jun 2025 17:17:13 -0500 Subject: [PATCH 004/251] refactor gkr example --- std/gkrapi/compile_test.go | 139 ------------------------------------- std/gkrapi/example_test.go | 119 ++++++++++++------------------- 2 files changed, 44 insertions(+), 214 deletions(-) delete mode 100644 std/gkrapi/compile_test.go diff --git a/std/gkrapi/compile_test.go b/std/gkrapi/compile_test.go deleted file mode 100644 index a0ca992ed4..0000000000 --- a/std/gkrapi/compile_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package gkrapi - -import ( - "testing" - - "github.com/consensys/gnark/internal/gkr/gkrinfo" - "github.com/stretchr/testify/assert" -) - -func TestCompile2Cycles(t *testing.T) { - var d = gkrinfo.StoringInfo{ - Dependencies: [][]gkrinfo.InputDependency{ - nil, - { - { - OutputWire: 0, - OutputInstance: 1, - InputInstance: 0, - }, - }, - }, - Circuit: gkrinfo.Circuit{ - { - Inputs: []int{1}, - }, - { - Inputs: []int{}, - }, - }, - } - - expectedCompiled := gkrinfo.StoringInfo{ - Dependencies: [][]gkrinfo.InputDependency{ - {{ - OutputWire: 1, - OutputInstance: 0, - InputInstance: 1, - }}, - nil, - }, - Circuit: gkrinfo.Circuit{ - { - Inputs: []int{}, - NbUniqueOutputs: 1, - }, - { - Inputs: []int{0}, - }}, - NbInstances: 2, - } - - expectedPermutations := gkrinfo.Permutations{ - SortedInstances: []int{1, 0}, - SortedWires: []int{1, 0}, - InstancesPermutation: []int{1, 0}, - WiresPermutation: []int{1, 0}, - } - - p, err := d.Compile(2) - assert.NoError(t, err) - assert.Equal(t, expectedPermutations, p) - assert.Equal(t, expectedCompiled, d) -} - -func TestCompile3Cycles(t *testing.T) { - var d = gkrinfo.StoringInfo{ - Dependencies: [][]gkrinfo.InputDependency{ - nil, - { - { - OutputWire: 0, - OutputInstance: 2, - InputInstance: 0, - }, - { - OutputWire: 0, - OutputInstance: 1, - InputInstance: 2, - }, - }, - nil, - }, - Circuit: gkrinfo.Circuit{ - { - Inputs: []int{2}, - }, - { - Inputs: []int{}, - }, - { - Inputs: []int{1}, - }, - }, - } - - expectedCompiled := gkrinfo.StoringInfo{ - Dependencies: [][]gkrinfo.InputDependency{ - {{ - OutputWire: 2, - OutputInstance: 0, - InputInstance: 1, - }, { - OutputWire: 2, - OutputInstance: 1, - InputInstance: 2, - }}, - - nil, - nil, - }, - Circuit: gkrinfo.Circuit{ - { - Inputs: []int{}, - NbUniqueOutputs: 1, - }, - { - Inputs: []int{0}, - NbUniqueOutputs: 1, - }, - { - Inputs: []int{1}, - NbUniqueOutputs: 0, - }, - }, - NbInstances: 3, // not allowed if we were actually performing gkr - } - - expectedPermutations := gkrinfo.Permutations{ - SortedInstances: []int{1, 2, 0}, - SortedWires: []int{1, 2, 0}, - InstancesPermutation: []int{2, 0, 1}, - WiresPermutation: []int{2, 0, 1}, - } - - p, err := d.Compile(3) - assert.NoError(t, err) - assert.Equal(t, expectedPermutations, p) - assert.Equal(t, expectedCompiled, d) -} diff --git a/std/gkrapi/example_test.go b/std/gkrapi/example_test.go index 4078bb0b9e..49d0209192 100644 --- a/std/gkrapi/example_test.go +++ b/std/gkrapi/example_test.go @@ -10,8 +10,6 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/gkrapi" "github.com/consensys/gnark/std/gkrapi/gkr" - stdHash "github.com/consensys/gnark/std/hash" - "github.com/consensys/gnark/std/hash/mimc" "github.com/consensys/gnark/test" ) @@ -20,18 +18,22 @@ func Example() { // This means that the imported fr and fp packages are the same, being from BW6-761 and BLS12-377 respectively. TODO @Tabaie delete if no longer have fp imported // It is based on the function DoubleAssign() of type G1Jac in gnark-crypto v0.17.0. // github.com/consensys/gnark-crypto/ecc/bls12-377 - const fsHashName = "MIMC" // register the gates: Doing so is not needed here because // the proof is being computed in the same session as the // SNARK circuit being compiled. // But in production applications it would be necessary. - assertNoError(gkrgates.Register(squareGate, 1)) - assertNoError(gkrgates.Register(sGate, 4)) - assertNoError(gkrgates.Register(zGate, 4)) - assertNoError(gkrgates.Register(xGate, 2)) - assertNoError(gkrgates.Register(yGate, 4)) + _, err := gkrgates.Register(squareGate, 1) + assertNoError(err) + _, err = gkrgates.Register(sGate, 4) + assertNoError(err) + _, err = gkrgates.Register(zGate, 4) + assertNoError(err) + _, err = gkrgates.Register(xGate, 2) + assertNoError(err) + _, err = gkrgates.Register(yGate, 4) + assertNoError(err) const nbInstances = 2 // create instances @@ -64,13 +66,12 @@ func Example() { } circuit := exampleCircuit{ - X: make([]frontend.Variable, nbInstances), - Y: make([]frontend.Variable, nbInstances), - Z: make([]frontend.Variable, nbInstances), - XOut: make([]frontend.Variable, nbInstances), - YOut: make([]frontend.Variable, nbInstances), - ZOut: make([]frontend.Variable, nbInstances), - fsHashName: fsHashName, + X: make([]frontend.Variable, nbInstances), + Y: make([]frontend.Variable, nbInstances), + Z: make([]frontend.Variable, nbInstances), + XOut: make([]frontend.Variable, nbInstances), + YOut: make([]frontend.Variable, nbInstances), + ZOut: make([]frontend.Variable, nbInstances), } assertNoError(test.IsSolved(&circuit, &assignment, ecc.BW6_761.ScalarField())) @@ -81,7 +82,6 @@ func Example() { type exampleCircuit struct { X, Y, Z []frontend.Variable // Jacobian coordinates for each point (input) XOut, YOut, ZOut []frontend.Variable // Jacobian coordinates for the double of each point (expected output) - fsHashName string // name of the hash function used for Fiat-Shamir in the GKR verifier } func (c *exampleCircuit) Define(api frontend.API) error { @@ -91,21 +91,10 @@ func (c *exampleCircuit) Define(api frontend.API) error { gkrApi := gkrapi.New() - // create GKR circuit variables based on the given assignments - X, err := gkrApi.Import(c.X) - if err != nil { - return err - } - - Y, err := gkrApi.Import(c.Y) - if err != nil { - return err - } - - Z, err := gkrApi.Import(c.Z) - if err != nil { - return err - } + // create the GKR circuit + X := gkrApi.NewInput() + Y := gkrApi.NewInput() + Z := gkrApi.NewInput() XX := gkrApi.Gate(squareGate, X) // 405: XX.Square(&p.X) YY := gkrApi.Gate(squareGate, Y) // 406: YY.Square(&p.Y) @@ -117,51 +106,31 @@ func (c *exampleCircuit) Define(api frontend.API) error { // 414: M.Double(&XX).Add(&M, &XX) // Note (but don't explicitly compute) that M = 3XX - Z = gkrApi.Gate(zGate, Z, Y, YY, ZZ) // 415 - 418 - X = gkrApi.Gate(xGate, XX, S) // 419-422 - Y = gkrApi.Gate(yGate, S, X, XX, YYYY) // 423 - 426 - - // have to duplicate X for it to be considered an output variable - X = gkrApi.NamedGate(gkr.Identity, X) - - // register the hash function used for verification (fiat shamir) - stdHash.Register(c.fsHashName, func(api frontend.API) (stdHash.FieldHasher, error) { - m, err := mimc.NewMiMC(api) - return &m, err - }) - - // solve and prove the circuit - solution, err := gkrApi.Solve(api) - if err != nil { - return err + ZOut := gkrApi.Gate(zGate, Z, Y, YY, ZZ) // 415 - 418 + XOut := gkrApi.Gate(xGate, XX, S) // 419-422 + YOut := gkrApi.Gate(yGate, S, XOut, XX, YYYY) // 423 - 426 + + // have to duplicate X for it to be considered an output variable; this is an implementation detail and will be fixed in the future [https://github.com/Consensys/gnark/issues/1452] + XOut = gkrApi.NamedGate(gkr.Identity, XOut) + + gkrCircuit := gkrApi.Compile(api, "MIMC") + + // add input and check output for correctness + instanceIn := make(map[gkr.Variable]frontend.Variable) + for i := range c.X { + instanceIn[X] = c.X[i] + instanceIn[Y] = c.Y[i] + instanceIn[Z] = c.Z[i] + + instanceOut, err := gkrCircuit.AddInstance(instanceIn) + if err != nil { + return err + } + api.AssertIsEqual(instanceOut[XOut], c.XOut[i]) + api.AssertIsEqual(instanceOut[YOut], c.YOut[i]) + api.AssertIsEqual(instanceOut[ZOut], c.ZOut[i]) } - - // check the output - - XOut := solution.Export(X) - YOut := solution.Export(Y) - ZOut := solution.Export(Z) - for i := range XOut { - api.AssertIsEqual(XOut[i], c.XOut[i]) - api.AssertIsEqual(YOut[i], c.YOut[i]) - api.AssertIsEqual(ZOut[i], c.ZOut[i]) - } - - challenges := make([]frontend.Variable, 0, len(c.X)*6) - challenges = append(challenges, XOut...) - challenges = append(challenges, YOut...) - challenges = append(challenges, ZOut...) - challenges = append(challenges, c.X...) - challenges = append(challenges, c.Y...) - challenges = append(challenges, c.Z...) - - challenge, err := api.(frontend.Committer).Commit(challenges...) - if err != nil { - return err - } - - // verify the proof - return solution.Verify(c.fsHashName, challenge) + return nil } // custom gates From 700c152b6e8d670dbef5f2221cd1a81329d831da Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 2 Jun 2025 12:12:03 -0500 Subject: [PATCH 005/251] refactor: solve hint called per instance --- constraint/bls12-377/solver.go | 6 +- internal/gkr/bls12-377/solver_hints.go | 115 +++++++++---------------- internal/gkr/gkrinfo/info.go | 30 +++++++ internal/gkr/gkrtypes/types.go | 56 ++---------- {std/gkrapi => internal/gkr}/hints.go | 101 ++++++++-------------- std/gkrapi/api.go | 18 +--- std/gkrapi/compile.go | 12 ++- 7 files changed, 130 insertions(+), 208 deletions(-) rename {std/gkrapi => internal/gkr}/hints.go (53%) diff --git a/constraint/bls12-377/solver.go b/constraint/bls12-377/solver.go index f79940e3be..206fea5702 100644 --- a/constraint/bls12-377/solver.go +++ b/constraint/bls12-377/solver.go @@ -51,14 +51,14 @@ type solver struct { func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, error) { // add GKR options to overwrite the placeholder if cs.GkrInfo.Is() { - var gkrData gkr.SolvingData solvingInfo, err := gkrtypes.StoringToSolvingInfo(cs.GkrInfo, gkrgates.Get) if err != nil { return nil, err } + gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, - csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(solvingInfo, &gkrData)), - csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, &gkrData))) + csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), + csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } // parse options opt, err := csolver.NewConfig(opts...) diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 39547cff29..6353370a15 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -13,6 +13,7 @@ import ( "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -21,92 +22,65 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - workers *utils.WorkerPool + assignment WireAssignment + circuit gkrtypes.Circuit + workers *utils.WorkerPool + maxNbIn int // maximum number of inputs for a gate in the circuit + printsByInstance map[uint32][]gkrinfo.PrintInfo } -func (d *SolvingData) init(info gkrtypes.SolvingInfo) { - d.workers = utils.NewWorkerPool() - d.circuit = info.Circuit - d.circuit.SetNbUniqueOutputs() +func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { + d := SolvingData{ + workers: utils.NewWorkerPool(), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), + } - d.assignment = make(WireAssignment, len(d.circuit)) + d.circuit.SetNbUniqueOutputs() + d.maxNbIn = d.circuit.MaxGateNbIn() for i := range d.assignment { d.assignment[i] = make([]fr.Element, info.NbInstances) } + + return &d } // this module assumes that wire and instance indexes respect dependencies -func setOuts(a WireAssignment, circuit gkrtypes.Circuit, outs []*big.Int) { - outsI := 0 - for i := range circuit { - if circuit[i].IsOutput() { - for j := range a[i] { - a[i][j].BigInt(outs[outsI]) - outsI++ - } +func SolveHint(data *SolvingData) hint.Hint { + return func(_ *big.Int, ins, outs []*big.Int) error { + instanceI := ins[0].Uint64() + if !ins[0].IsUint64() { // TODO use idiomatic printf tag + return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) } - } - // Check if outsI == len(outs)? -} -func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - // assumes assignmentVector is arranged wire first, instance second in order of solution - offsets := info.AssignmentOffsets() - data.init(info) - maxNIn := data.circuit.MaxGateNbIn() - - chunks := info.Chunks() - - solveTask := func(chunkOffset int) utils.Task { - return func(startInChunk, endInChunk int) { - start := startInChunk + chunkOffset - end := endInChunk + chunkOffset - inputs := make([]frontend.Variable, maxNIn) - dependencyHeads := make([]int, len(data.circuit)) // for each wire, which of its dependencies we would look at next - for wI := range data.circuit { // skip instances that are not relevant (related to instances before the current task) - deps := info.Dependencies[wI] - dependencyHeads[wI] = algo_utils.BinarySearchFunc(func(i int) int { - return deps[i].InputInstance - }, len(deps), start) + gateIns := make([]frontend.Variable, data.maxNbIn) + outsI := 0 + insI := 1 // skip the first input, which is the instance index + for wI := range data.circuit { + w := &data.circuit[wI] + if w.IsInput() { // read from provided input + data.assignment[wI][instanceI].SetBigInt(ins[insI]) + insI++ + } else { + + // assemble input for gate + for i, inWI := range w.Inputs { + gateIns[i] = &data.assignment[inWI][instanceI] } - for instanceI := start; instanceI < end; instanceI++ { - for wireI := range data.circuit { - wire := &data.circuit[wireI] - deps := info.Dependencies[wireI] - if wire.IsInput() { - if dependencyHeads[wireI] < len(deps) && instanceI == deps[dependencyHeads[wireI]].InputInstance { - dep := deps[dependencyHeads[wireI]] - data.assignment[wireI][instanceI].Set(&data.assignment[dep.OutputWire][dep.OutputInstance]) - dependencyHeads[wireI]++ - } else { - data.assignment[wireI][instanceI].SetBigInt(ins[offsets[wireI]+instanceI-dependencyHeads[wireI]]) - } - } else { - // assemble the inputs - inputIndexes := info.Circuit[wireI].Inputs - for i, inputI := range inputIndexes { - inputs[i] = &data.assignment[inputI][instanceI] - } - gate := data.circuit[wireI].Gate - data.assignment[wireI][instanceI].Set(gate.Evaluate(api, inputs[:len(inputIndexes)]...).(*fr.Element)) - } - } - } + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + } + if w.IsOutput() { + data.assignment[wI][instanceI].BigInt(outs[outsI]) + outsI++ } } - start := 0 - for _, end := range chunks { - data.workers.Submit(end-start, solveTask(start), 1024).Wait() - start = end - } - - for _, p := range info.Prints { + prints := data.printsByInstance[uint32(instanceI)] + delete(data.printsByInstance, uint32(instanceI)) + for _, p := range prints { serializable := make([]any, len(p.Values)) for i, v := range p.Values { if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 @@ -117,9 +91,6 @@ func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { } fmt.Println(serializable...) } - - setOuts(data.assignment, info.Circuit, outs) - return nil } } diff --git a/internal/gkr/gkrinfo/info.go b/internal/gkr/gkrinfo/info.go index 55fdb7e71d..81902df8c6 100644 --- a/internal/gkr/gkrinfo/info.go +++ b/internal/gkr/gkrinfo/info.go @@ -7,6 +7,7 @@ import ( "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/internal/utils" + "github.com/consensys/gnark/std/gkrapi/gkr" ) type ( @@ -139,3 +140,32 @@ func (d *StoringInfo) Is() bool { type ConstraintSystem interface { SetGkrInfo(info StoringInfo) error } + +func NewPrint(instance int, a ...any) PrintInfo { + isVar := make([]bool, len(a)) + vals := make([]any, len(a)) + for i := range a { + v, ok := a[i].(gkr.Variable) + isVar[i] = ok + if ok { + vals[i] = uint32(v) + } else { + vals[i] = a[i] + } + } + + return PrintInfo{ + Values: vals, + Instance: uint32(instance), + IsGkrVar: isVar, + } +} + +// NewPrintInfoMap partitions printInfo into map elements, indexed by instance +func NewPrintInfoMap(printInfo []PrintInfo) map[uint32][]PrintInfo { + res := make(map[uint32][]PrintInfo) + for i := range printInfo { + res[printInfo[i].Instance] = append(res[printInfo[i].Instance], printInfo[i]) + } + return res +} diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 201f063952..12cdabf3d9 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -147,49 +147,10 @@ func (c Circuit) MemoryRequirements(nbInstances int) []int { } type SolvingInfo struct { - Circuit Circuit - Dependencies [][]gkrinfo.InputDependency - NbInstances int - HashName string - Prints []gkrinfo.PrintInfo -} - -// Chunks returns intervals of instances that are independent of each other and can be solved in parallel -func (info *SolvingInfo) Chunks() []int { - res := make([]int, 0, 1) - lastSeenDependencyI := make([]int, len(info.Circuit)) - - for start, end := 0, 0; start != info.NbInstances; start = end { - end = info.NbInstances - endWireI := -1 - for wI := range info.Circuit { - deps := info.Dependencies[wI] - if wDepI := lastSeenDependencyI[wI]; wDepI < len(deps) && deps[wDepI].InputInstance < end { - end = deps[wDepI].InputInstance - endWireI = wI - } - } - if endWireI != -1 { - lastSeenDependencyI[endWireI]++ - } - res = append(res, end) - } - return res -} - -// AssignmentOffsets describes the input layout of the Solve hint, by returning -// for each wire, the index of the first hint input element corresponding to it. -func (info *SolvingInfo) AssignmentOffsets() []int { - c := info.Circuit - res := make([]int, len(c)+1) - for i := range c { - nbExplicitAssignments := 0 - if c[i].IsInput() { - nbExplicitAssignments = info.NbInstances - len(info.Dependencies[i]) - } - res[i+1] = res[i] + nbExplicitAssignments - } - return res + Circuit Circuit + NbInstances int + HashName string + Prints []gkrinfo.PrintInfo } // OutputsList for each wire, returns the set of indexes of wires it is input to. @@ -282,11 +243,10 @@ func CircuitInfoToCircuit(info gkrinfo.Circuit, gateGetter func(name gkr.GateNam func StoringToSolvingInfo(info gkrinfo.StoringInfo, gateGetter func(name gkr.GateName) *Gate) (SolvingInfo, error) { circuit, err := CircuitInfoToCircuit(info.Circuit, gateGetter) return SolvingInfo{ - Circuit: circuit, - NbInstances: info.NbInstances, - HashName: info.HashName, - Dependencies: info.Dependencies, - Prints: info.Prints, + Circuit: circuit, + NbInstances: info.NbInstances, + HashName: info.HashName, + Prints: info.Prints, }, err } diff --git a/std/gkrapi/hints.go b/internal/gkr/hints.go similarity index 53% rename from std/gkrapi/hints.go rename to internal/gkr/hints.go index 577a4d6ed8..2c2621911e 100644 --- a/std/gkrapi/hints.go +++ b/internal/gkr/hints.go @@ -1,13 +1,10 @@ -package gkrapi +package gkr import ( "errors" - "fmt" "math/big" - "strings" "github.com/consensys/gnark-crypto/ecc" - gcHash "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" bls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" @@ -19,7 +16,6 @@ import ( bw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/internal/utils" ) var testEngineGkrSolvingData = make(map[string]any) @@ -28,6 +24,8 @@ func modKey(mod *big.Int) string { return mod.Text(32) } +// SolveHintPlaceholder solves one instance of a GKR circuit. +// The first input is the index of the instance. The rest are the inputs of the circuit, in their nominal order. func SolveHintPlaceholder(gkrInfo gkrinfo.StoringInfo) solver.Hint { return func(mod *big.Int, ins []*big.Int, outs []*big.Int) error { @@ -36,44 +34,42 @@ func SolveHintPlaceholder(gkrInfo gkrinfo.StoringInfo) solver.Hint { return err } + var hint solver.Hint + // TODO @Tabaie autogenerate this or decide not to if mod.Cmp(ecc.BLS12_377.ScalarField()) == 0 { - var data bls12377.SolvingData - testEngineGkrSolvingData[modKey(mod)] = &data - return bls12377.SolveHint(solvingInfo, &data)(mod, ins, outs) - } - if mod.Cmp(ecc.BLS12_381.ScalarField()) == 0 { - var data bls12381.SolvingData - testEngineGkrSolvingData[modKey(mod)] = &data - return bls12381.SolveHint(solvingInfo, &data)(mod, ins, outs) - } - if mod.Cmp(ecc.BLS24_315.ScalarField()) == 0 { - var data bls24315.SolvingData - testEngineGkrSolvingData[modKey(mod)] = &data - return bls24315.SolveHint(solvingInfo, &data)(mod, ins, outs) - } - if mod.Cmp(ecc.BLS24_317.ScalarField()) == 0 { - var data bls24317.SolvingData - testEngineGkrSolvingData[modKey(mod)] = &data - return bls24317.SolveHint(solvingInfo, &data)(mod, ins, outs) - } - if mod.Cmp(ecc.BN254.ScalarField()) == 0 { - var data bn254.SolvingData - testEngineGkrSolvingData[modKey(mod)] = &data - return bn254.SolveHint(solvingInfo, &data)(mod, ins, outs) - } - if mod.Cmp(ecc.BW6_633.ScalarField()) == 0 { - var data bw6633.SolvingData - testEngineGkrSolvingData[modKey(mod)] = &data - return bw6633.SolveHint(solvingInfo, &data)(mod, ins, outs) - } - if mod.Cmp(ecc.BW6_761.ScalarField()) == 0 { - var data bw6761.SolvingData - testEngineGkrSolvingData[modKey(mod)] = &data - return bw6761.SolveHint(solvingInfo, &data)(mod, ins, outs) - } - - return errors.New("unsupported modulus") + data := bls12377.NewSolvingData(solvingInfo) + hint = bls12377.SolveHint(solvingInfo, data) + testEngineGkrSolvingData[modKey(mod)] = data + } else if mod.Cmp(ecc.BLS12_381.ScalarField()) == 0 { + data := bls12381.NewSolvingData(solvingInfo) + hint = bls12381.SolveHint(solvingInfo, data) + testEngineGkrSolvingData[modKey(mod)] = data + } else if mod.Cmp(ecc.BLS24_315.ScalarField()) == 0 { + data := bls24315.NewSolvingData(solvingInfo) + hint = bls24315.SolveHint(solvingInfo, data) + testEngineGkrSolvingData[modKey(mod)] = data + } else if mod.Cmp(ecc.BLS24_317.ScalarField()) == 0 { + data := bls24317.NewSolvingData(solvingInfo) + hint = bls24317.SolveHint(solvingInfo, data) + testEngineGkrSolvingData[modKey(mod)] = data + } else if mod.Cmp(ecc.BN254.ScalarField()) == 0 { + data := bn254.NewSolvingData(solvingInfo) + hint = bn254.SolveHint(solvingInfo, data) + testEngineGkrSolvingData[modKey(mod)] = data + } else if mod.Cmp(ecc.BW6_633.ScalarField()) == 0 { + data := bw6633.NewSolvingData(solvingInfo) + hint = bw6633.SolveHint(solvingInfo, data) + testEngineGkrSolvingData[modKey(mod)] = data + } else if mod.Cmp(ecc.BW6_761.ScalarField()) == 0 { + data := bw6761.NewSolvingData(solvingInfo) + hint = bw6761.SolveHint(solvingInfo, data) + testEngineGkrSolvingData[modKey(mod)] = data + } else { + return errors.New("unsupported modulus") + } + + return hint(mod, ins, outs) } } @@ -112,26 +108,3 @@ func ProveHintPlaceholder(hashName string) solver.Hint { return errors.New("unsupported modulus") } } - -func CheckHashHint(hashName string) solver.Hint { - return func(mod *big.Int, ins, outs []*big.Int) error { - if len(ins) != 2 || len(outs) != 1 { - return errors.New("invalid number of inputs/outputs") - } - - toHash := ins[0].Bytes() - expectedHash := ins[1] - - hsh := gcHash.NewHash(fmt.Sprintf("%s_%s", hashName, strings.ToUpper(utils.FieldToCurve(mod).String()))) - hsh.Write(toHash) - hashed := hsh.Sum(nil) - - if hashed := new(big.Int).SetBytes(hashed); hashed.Cmp(expectedHash) != 0 { - return fmt.Errorf("hash mismatch: expected %s, got %s", expectedHash.String(), hashed.String()) - } - - outs[0].SetBytes(hashed) - - return nil - } -} diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index 18a9b23279..d4f27f5c9f 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -62,21 +62,5 @@ func (api *API) Mul(i1, i2 gkr.Variable) gkr.Variable { // Println writes to the standard output. // instance determines which values are chosen for gkr.Variable input. func (api *API) Println(instance int, a ...any) { - isVar := make([]bool, len(a)) - vals := make([]any, len(a)) - for i := range a { - v, ok := a[i].(gkr.Variable) - isVar[i] = ok - if ok { - vals[i] = uint32(v) - } else { - vals[i] = a[i] - } - } - - api.toStore.Prints = append(api.toStore.Prints, gkrinfo.PrintInfo{ - Values: vals, - Instance: uint32(instance), - IsGkrVar: isVar, - }) + api.toStore.Prints = append(api.toStore.Prints, gkrinfo.NewPrint(instance, a...)) } diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 0ba6213286..b51c9c8b6c 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -3,6 +3,7 @@ package gkrapi import ( "fmt" "math/bits" + "slices" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint/solver" @@ -88,8 +89,11 @@ func (api *API) Compile(parentApi frontend.API, fiatshamirHashName string, optio res.ins = append(res.ins, gkr.Variable(i)) } } - res.toStore.SolveHintID = solver.GetHintID(SolveHintPlaceholder(res.toStore)) - res.toStore.ProveHintID = solver.GetHintID(ProveHintPlaceholder(fiatshamirHashName)) + res.toStore.SolveHintID = solver.GetHintID(gadget.SolveHintPlaceholder(res.toStore)) + res.toStore.ProveHintID = solver.GetHintID(gadget.ProveHintPlaceholder(fiatshamirHashName)) + + // sort the prints before solving begins + slices.SortFunc(res.toStore.Prints, gkrinfo.PrintInfo.Cmp) parentApi.Compiler().Defer(res.verify) @@ -119,7 +123,7 @@ func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr } c.toStore.NbInstances++ - solveHintPlaceholder := SolveHintPlaceholder(c.toStore) + solveHintPlaceholder := gadget.SolveHintPlaceholder(c.toStore) outsSerialized, err := c.api.Compiler().NewHint(solveHintPlaceholder, len(c.outs), hintIn...) if err != nil { return nil, fmt.Errorf("failed to create solve hint: %w", err) @@ -180,7 +184,7 @@ func (c *Circuit) verify(api frontend.API) error { copy(hintIns[1:], initialChallenges) - proveHintPlaceholder := ProveHintPlaceholder(c.toStore.HashName) + proveHintPlaceholder := gadget.ProveHintPlaceholder(c.toStore.HashName) if proofSerialized, err = api.Compiler().NewHint( proveHintPlaceholder, gadget.ProofSize(forSnark.circuit, logNbInstances), hintIns...); err != nil { return err From 30f5633b54e7766d4819e5146cc24d1876aee930 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 2 Jun 2025 12:15:03 -0500 Subject: [PATCH 006/251] revert: newPrint back in std/gkr to avoid import cycle --- internal/gkr/gkrinfo/info.go | 21 --------------------- std/gkrapi/api.go | 22 +++++++++++++++++++++- std/gkrapi/compile.go | 4 ---- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/internal/gkr/gkrinfo/info.go b/internal/gkr/gkrinfo/info.go index 81902df8c6..5581221ab5 100644 --- a/internal/gkr/gkrinfo/info.go +++ b/internal/gkr/gkrinfo/info.go @@ -7,7 +7,6 @@ import ( "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" ) type ( @@ -141,26 +140,6 @@ type ConstraintSystem interface { SetGkrInfo(info StoringInfo) error } -func NewPrint(instance int, a ...any) PrintInfo { - isVar := make([]bool, len(a)) - vals := make([]any, len(a)) - for i := range a { - v, ok := a[i].(gkr.Variable) - isVar[i] = ok - if ok { - vals[i] = uint32(v) - } else { - vals[i] = a[i] - } - } - - return PrintInfo{ - Values: vals, - Instance: uint32(instance), - IsGkrVar: isVar, - } -} - // NewPrintInfoMap partitions printInfo into map elements, indexed by instance func NewPrintInfoMap(printInfo []PrintInfo) map[uint32][]PrintInfo { res := make(map[uint32][]PrintInfo) diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index d4f27f5c9f..5765fb98a1 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -62,5 +62,25 @@ func (api *API) Mul(i1, i2 gkr.Variable) gkr.Variable { // Println writes to the standard output. // instance determines which values are chosen for gkr.Variable input. func (api *API) Println(instance int, a ...any) { - api.toStore.Prints = append(api.toStore.Prints, gkrinfo.NewPrint(instance, a...)) + api.toStore.Prints = append(api.toStore.Prints, newPrint(instance, a...)) +} + +func newPrint(instance int, a ...any) gkrinfo.PrintInfo { + isVar := make([]bool, len(a)) + vals := make([]any, len(a)) + for i := range a { + v, ok := a[i].(gkr.Variable) + isVar[i] = ok + if ok { + vals[i] = uint32(v) + } else { + vals[i] = a[i] + } + } + + return gkrinfo.PrintInfo{ + Values: vals, + Instance: uint32(instance), + IsGkrVar: isVar, + } } diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index b51c9c8b6c..606596fee3 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -3,7 +3,6 @@ package gkrapi import ( "fmt" "math/bits" - "slices" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint/solver" @@ -92,9 +91,6 @@ func (api *API) Compile(parentApi frontend.API, fiatshamirHashName string, optio res.toStore.SolveHintID = solver.GetHintID(gadget.SolveHintPlaceholder(res.toStore)) res.toStore.ProveHintID = solver.GetHintID(gadget.ProveHintPlaceholder(fiatshamirHashName)) - // sort the prints before solving begins - slices.SortFunc(res.toStore.Prints, gkrinfo.PrintInfo.Cmp) - parentApi.Compiler().Defer(res.verify) return &res From 44d6b4cc76b068b0765b5d492598ee2953ac74cb Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 2 Jun 2025 12:19:18 -0500 Subject: [PATCH 007/251] refactor: remove circuit/instance rearranging --- internal/gkr/gkrinfo/info.go | 75 ------------------------------------ 1 file changed, 75 deletions(-) diff --git a/internal/gkr/gkrinfo/info.go b/internal/gkr/gkrinfo/info.go index 5581221ab5..f7cb5e589d 100644 --- a/internal/gkr/gkrinfo/info.go +++ b/internal/gkr/gkrinfo/info.go @@ -2,11 +2,7 @@ package gkrinfo import ( - "fmt" - "sort" - "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/utils" ) type ( @@ -60,77 +56,6 @@ func (d *StoringInfo) NewInputVariable() int { return i } -// Compile sorts the Circuit wires, their dependencies and the instances -func (d *StoringInfo) Compile(nbInstances int) (Permutations, error) { - - var p Permutations - d.NbInstances = nbInstances - // sort the instances to decide the order in which they are to be solved - instanceDeps := make([][]int, nbInstances) - for i := range d.Circuit { - for _, dep := range d.Dependencies[i] { - instanceDeps[dep.InputInstance] = append(instanceDeps[dep.InputInstance], dep.OutputInstance) - } - } - - p.SortedInstances, _ = utils.TopologicalSort(instanceDeps) - p.InstancesPermutation = utils.InvertPermutation(p.SortedInstances) - - // this whole circuit sorting is a bit of a charade. if things are built using an api, there's no way it could NOT already be topologically sorted - // worth keeping for future-proofing? - - inputs := utils.Map(d.Circuit, func(w Wire) []int { - return w.Inputs - }) - - var uniqueOuts [][]int - p.SortedWires, uniqueOuts = utils.TopologicalSort(inputs) - p.WiresPermutation = utils.InvertPermutation(p.SortedWires) - wirePermutationAt := utils.SliceAt(p.WiresPermutation) - sorted := make([]Wire, len(d.Circuit)) // TODO: Directly manipulate d.circuit instead - sortedDeps := make([][]InputDependency, len(d.Circuit)) - - // go through the wires in the sorted order and fix the input and dependency indices according to the permutations - for newI, oldI := range p.SortedWires { - oldW := d.Circuit[oldI] - - for depI := range d.Dependencies[oldI] { - dep := &d.Dependencies[oldI][depI] - dep.OutputWire = p.WiresPermutation[dep.OutputWire] - dep.InputInstance = p.InstancesPermutation[dep.InputInstance] - dep.OutputInstance = p.InstancesPermutation[dep.OutputInstance] - } - sort.Slice(d.Dependencies[oldI], func(i, j int) bool { - return d.Dependencies[oldI][i].InputInstance < d.Dependencies[oldI][j].InputInstance - }) - for i := 1; i < len(d.Dependencies[oldI]); i++ { - if d.Dependencies[oldI][i].InputInstance == d.Dependencies[oldI][i-1].InputInstance { - return p, fmt.Errorf("an input wire can only have one dependency per instance") - } - } // TODO: Check that dependencies and explicit assignments cover all instances - - sortedDeps[newI] = d.Dependencies[oldI] - sorted[newI] = Wire{ - Gate: oldW.Gate, - Inputs: utils.Map(oldW.Inputs, wirePermutationAt), - NbUniqueOutputs: len(uniqueOuts[oldI]), - } - } - - // re-arrange the prints - for i := range d.Prints { - for j, isVar := range d.Prints[i].IsGkrVar { - if isVar { - d.Prints[i].Values[j] = uint32(p.WiresPermutation[d.Prints[i].Values[j].(uint32)]) - } - } - } - - d.Circuit, d.Dependencies = sorted, sortedDeps - - return p, nil -} - func (d *StoringInfo) Is() bool { return d.Circuit != nil } From f5f726ce3206d70d55254fe670299c96426ed7d3 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 2 Jun 2025 12:32:02 -0500 Subject: [PATCH 008/251] chore: generify --- constraint/bls12-381/solver.go | 6 +- constraint/bls24-315/solver.go | 6 +- constraint/bls24-317/solver.go | 6 +- constraint/bn254/solver.go | 6 +- constraint/bw6-633/solver.go | 6 +- constraint/bw6-761/solver.go | 6 +- .../backend/template/gkr/solver_hints.go.tmpl | 114 +++++++----------- .../template/representations/solver.go.tmpl | 6 +- internal/gkr/bls12-377/solver_hints.go | 2 + internal/gkr/bls12-381/solver_hints.go | 113 +++++++---------- internal/gkr/bls24-315/solver_hints.go | 113 +++++++---------- internal/gkr/bls24-317/solver_hints.go | 113 +++++++---------- internal/gkr/bn254/solver_hints.go | 113 +++++++---------- internal/gkr/bw6-633/solver_hints.go | 113 +++++++---------- internal/gkr/bw6-761/solver_hints.go | 113 +++++++---------- internal/gkr/gkrtesting/gkrtesting.go | 5 +- 16 files changed, 328 insertions(+), 513 deletions(-) diff --git a/constraint/bls12-381/solver.go b/constraint/bls12-381/solver.go index 1bfa4c5884..4e7835fef1 100644 --- a/constraint/bls12-381/solver.go +++ b/constraint/bls12-381/solver.go @@ -51,14 +51,14 @@ type solver struct { func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, error) { // add GKR options to overwrite the placeholder if cs.GkrInfo.Is() { - var gkrData gkr.SolvingData solvingInfo, err := gkrtypes.StoringToSolvingInfo(cs.GkrInfo, gkrgates.Get) if err != nil { return nil, err } + gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, - csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(solvingInfo, &gkrData)), - csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, &gkrData))) + csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), + csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } // parse options opt, err := csolver.NewConfig(opts...) diff --git a/constraint/bls24-315/solver.go b/constraint/bls24-315/solver.go index 4f5b72c776..5dc3fc2ef7 100644 --- a/constraint/bls24-315/solver.go +++ b/constraint/bls24-315/solver.go @@ -51,14 +51,14 @@ type solver struct { func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, error) { // add GKR options to overwrite the placeholder if cs.GkrInfo.Is() { - var gkrData gkr.SolvingData solvingInfo, err := gkrtypes.StoringToSolvingInfo(cs.GkrInfo, gkrgates.Get) if err != nil { return nil, err } + gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, - csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(solvingInfo, &gkrData)), - csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, &gkrData))) + csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), + csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } // parse options opt, err := csolver.NewConfig(opts...) diff --git a/constraint/bls24-317/solver.go b/constraint/bls24-317/solver.go index 9462b5d3e4..f007d16494 100644 --- a/constraint/bls24-317/solver.go +++ b/constraint/bls24-317/solver.go @@ -51,14 +51,14 @@ type solver struct { func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, error) { // add GKR options to overwrite the placeholder if cs.GkrInfo.Is() { - var gkrData gkr.SolvingData solvingInfo, err := gkrtypes.StoringToSolvingInfo(cs.GkrInfo, gkrgates.Get) if err != nil { return nil, err } + gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, - csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(solvingInfo, &gkrData)), - csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, &gkrData))) + csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), + csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } // parse options opt, err := csolver.NewConfig(opts...) diff --git a/constraint/bn254/solver.go b/constraint/bn254/solver.go index 4ccc03e7e0..a7674e9542 100644 --- a/constraint/bn254/solver.go +++ b/constraint/bn254/solver.go @@ -51,14 +51,14 @@ type solver struct { func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, error) { // add GKR options to overwrite the placeholder if cs.GkrInfo.Is() { - var gkrData gkr.SolvingData solvingInfo, err := gkrtypes.StoringToSolvingInfo(cs.GkrInfo, gkrgates.Get) if err != nil { return nil, err } + gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, - csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(solvingInfo, &gkrData)), - csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, &gkrData))) + csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), + csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } // parse options opt, err := csolver.NewConfig(opts...) diff --git a/constraint/bw6-633/solver.go b/constraint/bw6-633/solver.go index 642369791f..f294f7d826 100644 --- a/constraint/bw6-633/solver.go +++ b/constraint/bw6-633/solver.go @@ -51,14 +51,14 @@ type solver struct { func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, error) { // add GKR options to overwrite the placeholder if cs.GkrInfo.Is() { - var gkrData gkr.SolvingData solvingInfo, err := gkrtypes.StoringToSolvingInfo(cs.GkrInfo, gkrgates.Get) if err != nil { return nil, err } + gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, - csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(solvingInfo, &gkrData)), - csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, &gkrData))) + csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), + csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } // parse options opt, err := csolver.NewConfig(opts...) diff --git a/constraint/bw6-761/solver.go b/constraint/bw6-761/solver.go index a65445eb4c..e10f2f21d7 100644 --- a/constraint/bw6-761/solver.go +++ b/constraint/bw6-761/solver.go @@ -51,14 +51,14 @@ type solver struct { func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, error) { // add GKR options to overwrite the placeholder if cs.GkrInfo.Is() { - var gkrData gkr.SolvingData solvingInfo, err := gkrtypes.StoringToSolvingInfo(cs.GkrInfo, gkrgates.Get) if err != nil { return nil, err } + gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, - csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(solvingInfo, &gkrData)), - csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, &gkrData))) + csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), + csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } // parse options opt, err := csolver.NewConfig(opts...) diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index e1d41e8cb8..472121423c 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -6,6 +6,7 @@ import ( "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -14,92 +15,68 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - workers *utils.WorkerPool + assignment WireAssignment + circuit gkrtypes.Circuit + workers *utils.WorkerPool + maxNbIn int // maximum number of inputs for a gate in the circuit + printsByInstance map[uint32][]gkrinfo.PrintInfo } -func (d *SolvingData) init(info gkrtypes.SolvingInfo) { - d.workers = utils.NewWorkerPool() - d.circuit = info.Circuit +func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { + d := SolvingData{ + workers: utils.NewWorkerPool(), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), + } + d.circuit.SetNbUniqueOutputs() + d.maxNbIn = d.circuit.MaxGateNbIn() d.assignment = make(WireAssignment, len(d.circuit)) for i := range d.assignment { d.assignment[i] = make([]fr.Element, info.NbInstances) } + + return &d } // this module assumes that wire and instance indexes respect dependencies -func setOuts(a WireAssignment, circuit gkrtypes.Circuit, outs []*big.Int) { - outsI := 0 - for i := range circuit { - if circuit[i].IsOutput() { - for j := range a[i] { - a[i][j].BigInt(outs[outsI]) - outsI++ - } - } - } - // Check if outsI == len(outs)? -} -func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { +func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - // assumes assignmentVector is arranged wire first, instance second in order of solution - offsets := info.AssignmentOffsets() - data.init(info) - maxNIn := data.circuit.MaxGateNbIn() - - chunks := info.Chunks() - - solveTask := func(chunkOffset int) utils.Task { - return func(startInChunk, endInChunk int) { - start := startInChunk + chunkOffset - end := endInChunk + chunkOffset - inputs := make([]frontend.Variable, maxNIn) - dependencyHeads := make([]int, len(data.circuit)) // for each wire, which of its dependencies we would look at next - for wI := range data.circuit { // skip instances that are not relevant (related to instances before the current task) - deps := info.Dependencies[wI] - dependencyHeads[wI] = algo_utils.BinarySearchFunc(func(i int) int { - return deps[i].InputInstance - }, len(deps), start) - } + instanceI := ins[0].Uint64() + if !ins[0].IsUint64() { // TODO use idiomatic printf tag + return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) + } - for instanceI := start; instanceI < end; instanceI++ { - for wireI := range data.circuit { - wire := &data.circuit[wireI] - deps := info.Dependencies[wireI] - if wire.IsInput() { - if dependencyHeads[wireI] < len(deps) && instanceI == deps[dependencyHeads[wireI]].InputInstance { - dep := deps[dependencyHeads[wireI]] - data.assignment[wireI][instanceI].Set(&data.assignment[dep.OutputWire][dep.OutputInstance]) - dependencyHeads[wireI]++ - } else { - data.assignment[wireI][instanceI].SetBigInt(ins[offsets[wireI]+instanceI-dependencyHeads[wireI]]) - } - } else { - // assemble the inputs - inputIndexes := info.Circuit[wireI].Inputs - for i, inputI := range inputIndexes { - inputs[i] = &data.assignment[inputI][instanceI] - } - gate := data.circuit[wireI].Gate - data.assignment[wireI][instanceI].Set(gate.Evaluate(api, inputs[:len(inputIndexes)]...).(*fr.Element)) - } - } + gateIns := make([]frontend.Variable, data.maxNbIn) + outsI := 0 + insI := 1 // skip the first input, which is the instance index + for wI := range data.circuit { + w := &data.circuit[wI] + if w.IsInput() { // read from provided input + data.assignment[wI][instanceI].SetBigInt(ins[insI]) + insI++ + } else { + + // assemble input for gate + for i, inWI := range w.Inputs { + gateIns[i] = &data.assignment[inWI][instanceI] } - } - } - start := 0 - for _, end := range chunks { - data.workers.Submit(end-start, solveTask(start), 1024).Wait() - start = end + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + } + if w.IsOutput() { + data.assignment[wI][instanceI].BigInt(outs[outsI]) + outsI++ + } } - for _, p := range info.Prints { + prints := data.printsByInstance[uint32(instanceI)] + delete(data.printsByInstance, uint32(instanceI)) + for _, p := range prints { serializable := make([]any, len(p.Values)) for i, v := range p.Values { if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 @@ -110,9 +87,6 @@ func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { } fmt.Println(serializable...) } - - setOuts(data.assignment, info.Circuit, outs) - return nil } } diff --git a/internal/generator/backend/template/representations/solver.go.tmpl b/internal/generator/backend/template/representations/solver.go.tmpl index fd685e6e21..202642b87a 100644 --- a/internal/generator/backend/template/representations/solver.go.tmpl +++ b/internal/generator/backend/template/representations/solver.go.tmpl @@ -43,14 +43,14 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, {{ if not .NoGKR -}} // add GKR options to overwrite the placeholder if cs.GkrInfo.Is() { - var gkrData gkr.SolvingData solvingInfo, err := gkrtypes.StoringToSolvingInfo(cs.GkrInfo, gkrgates.Get) if err != nil { return nil, err } + gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, - csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(solvingInfo, &gkrData)), - csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, &gkrData))) + csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), + csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } {{ end -}} diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 6353370a15..18ad9c91e9 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -39,6 +39,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { d.circuit.SetNbUniqueOutputs() d.maxNbIn = d.circuit.MaxGateNbIn() + + d.assignment = make(WireAssignment, len(d.circuit)) for i := range d.assignment { d.assignment[i] = make([]fr.Element, info.NbInstances) } diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index cb498c78b7..5c73505739 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -13,6 +13,7 @@ import ( "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -21,92 +22,67 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - workers *utils.WorkerPool + assignment WireAssignment + circuit gkrtypes.Circuit + workers *utils.WorkerPool + maxNbIn int // maximum number of inputs for a gate in the circuit + printsByInstance map[uint32][]gkrinfo.PrintInfo } -func (d *SolvingData) init(info gkrtypes.SolvingInfo) { - d.workers = utils.NewWorkerPool() - d.circuit = info.Circuit +func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { + d := SolvingData{ + workers: utils.NewWorkerPool(), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), + } + d.circuit.SetNbUniqueOutputs() + d.maxNbIn = d.circuit.MaxGateNbIn() d.assignment = make(WireAssignment, len(d.circuit)) for i := range d.assignment { d.assignment[i] = make([]fr.Element, info.NbInstances) } + + return &d } // this module assumes that wire and instance indexes respect dependencies -func setOuts(a WireAssignment, circuit gkrtypes.Circuit, outs []*big.Int) { - outsI := 0 - for i := range circuit { - if circuit[i].IsOutput() { - for j := range a[i] { - a[i][j].BigInt(outs[outsI]) - outsI++ - } +func SolveHint(data *SolvingData) hint.Hint { + return func(_ *big.Int, ins, outs []*big.Int) error { + instanceI := ins[0].Uint64() + if !ins[0].IsUint64() { // TODO use idiomatic printf tag + return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) } - } - // Check if outsI == len(outs)? -} -func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - // assumes assignmentVector is arranged wire first, instance second in order of solution - offsets := info.AssignmentOffsets() - data.init(info) - maxNIn := data.circuit.MaxGateNbIn() - - chunks := info.Chunks() - - solveTask := func(chunkOffset int) utils.Task { - return func(startInChunk, endInChunk int) { - start := startInChunk + chunkOffset - end := endInChunk + chunkOffset - inputs := make([]frontend.Variable, maxNIn) - dependencyHeads := make([]int, len(data.circuit)) // for each wire, which of its dependencies we would look at next - for wI := range data.circuit { // skip instances that are not relevant (related to instances before the current task) - deps := info.Dependencies[wI] - dependencyHeads[wI] = algo_utils.BinarySearchFunc(func(i int) int { - return deps[i].InputInstance - }, len(deps), start) + gateIns := make([]frontend.Variable, data.maxNbIn) + outsI := 0 + insI := 1 // skip the first input, which is the instance index + for wI := range data.circuit { + w := &data.circuit[wI] + if w.IsInput() { // read from provided input + data.assignment[wI][instanceI].SetBigInt(ins[insI]) + insI++ + } else { + + // assemble input for gate + for i, inWI := range w.Inputs { + gateIns[i] = &data.assignment[inWI][instanceI] } - for instanceI := start; instanceI < end; instanceI++ { - for wireI := range data.circuit { - wire := &data.circuit[wireI] - deps := info.Dependencies[wireI] - if wire.IsInput() { - if dependencyHeads[wireI] < len(deps) && instanceI == deps[dependencyHeads[wireI]].InputInstance { - dep := deps[dependencyHeads[wireI]] - data.assignment[wireI][instanceI].Set(&data.assignment[dep.OutputWire][dep.OutputInstance]) - dependencyHeads[wireI]++ - } else { - data.assignment[wireI][instanceI].SetBigInt(ins[offsets[wireI]+instanceI-dependencyHeads[wireI]]) - } - } else { - // assemble the inputs - inputIndexes := info.Circuit[wireI].Inputs - for i, inputI := range inputIndexes { - inputs[i] = &data.assignment[inputI][instanceI] - } - gate := data.circuit[wireI].Gate - data.assignment[wireI][instanceI].Set(gate.Evaluate(api, inputs[:len(inputIndexes)]...).(*fr.Element)) - } - } - } + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + } + if w.IsOutput() { + data.assignment[wI][instanceI].BigInt(outs[outsI]) + outsI++ } } - start := 0 - for _, end := range chunks { - data.workers.Submit(end-start, solveTask(start), 1024).Wait() - start = end - } - - for _, p := range info.Prints { + prints := data.printsByInstance[uint32(instanceI)] + delete(data.printsByInstance, uint32(instanceI)) + for _, p := range prints { serializable := make([]any, len(p.Values)) for i, v := range p.Values { if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 @@ -117,9 +93,6 @@ func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { } fmt.Println(serializable...) } - - setOuts(data.assignment, info.Circuit, outs) - return nil } } diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index 914c8a9d61..b44cea8feb 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -13,6 +13,7 @@ import ( "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -21,92 +22,67 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - workers *utils.WorkerPool + assignment WireAssignment + circuit gkrtypes.Circuit + workers *utils.WorkerPool + maxNbIn int // maximum number of inputs for a gate in the circuit + printsByInstance map[uint32][]gkrinfo.PrintInfo } -func (d *SolvingData) init(info gkrtypes.SolvingInfo) { - d.workers = utils.NewWorkerPool() - d.circuit = info.Circuit +func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { + d := SolvingData{ + workers: utils.NewWorkerPool(), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), + } + d.circuit.SetNbUniqueOutputs() + d.maxNbIn = d.circuit.MaxGateNbIn() d.assignment = make(WireAssignment, len(d.circuit)) for i := range d.assignment { d.assignment[i] = make([]fr.Element, info.NbInstances) } + + return &d } // this module assumes that wire and instance indexes respect dependencies -func setOuts(a WireAssignment, circuit gkrtypes.Circuit, outs []*big.Int) { - outsI := 0 - for i := range circuit { - if circuit[i].IsOutput() { - for j := range a[i] { - a[i][j].BigInt(outs[outsI]) - outsI++ - } +func SolveHint(data *SolvingData) hint.Hint { + return func(_ *big.Int, ins, outs []*big.Int) error { + instanceI := ins[0].Uint64() + if !ins[0].IsUint64() { // TODO use idiomatic printf tag + return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) } - } - // Check if outsI == len(outs)? -} -func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - // assumes assignmentVector is arranged wire first, instance second in order of solution - offsets := info.AssignmentOffsets() - data.init(info) - maxNIn := data.circuit.MaxGateNbIn() - - chunks := info.Chunks() - - solveTask := func(chunkOffset int) utils.Task { - return func(startInChunk, endInChunk int) { - start := startInChunk + chunkOffset - end := endInChunk + chunkOffset - inputs := make([]frontend.Variable, maxNIn) - dependencyHeads := make([]int, len(data.circuit)) // for each wire, which of its dependencies we would look at next - for wI := range data.circuit { // skip instances that are not relevant (related to instances before the current task) - deps := info.Dependencies[wI] - dependencyHeads[wI] = algo_utils.BinarySearchFunc(func(i int) int { - return deps[i].InputInstance - }, len(deps), start) + gateIns := make([]frontend.Variable, data.maxNbIn) + outsI := 0 + insI := 1 // skip the first input, which is the instance index + for wI := range data.circuit { + w := &data.circuit[wI] + if w.IsInput() { // read from provided input + data.assignment[wI][instanceI].SetBigInt(ins[insI]) + insI++ + } else { + + // assemble input for gate + for i, inWI := range w.Inputs { + gateIns[i] = &data.assignment[inWI][instanceI] } - for instanceI := start; instanceI < end; instanceI++ { - for wireI := range data.circuit { - wire := &data.circuit[wireI] - deps := info.Dependencies[wireI] - if wire.IsInput() { - if dependencyHeads[wireI] < len(deps) && instanceI == deps[dependencyHeads[wireI]].InputInstance { - dep := deps[dependencyHeads[wireI]] - data.assignment[wireI][instanceI].Set(&data.assignment[dep.OutputWire][dep.OutputInstance]) - dependencyHeads[wireI]++ - } else { - data.assignment[wireI][instanceI].SetBigInt(ins[offsets[wireI]+instanceI-dependencyHeads[wireI]]) - } - } else { - // assemble the inputs - inputIndexes := info.Circuit[wireI].Inputs - for i, inputI := range inputIndexes { - inputs[i] = &data.assignment[inputI][instanceI] - } - gate := data.circuit[wireI].Gate - data.assignment[wireI][instanceI].Set(gate.Evaluate(api, inputs[:len(inputIndexes)]...).(*fr.Element)) - } - } - } + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + } + if w.IsOutput() { + data.assignment[wI][instanceI].BigInt(outs[outsI]) + outsI++ } } - start := 0 - for _, end := range chunks { - data.workers.Submit(end-start, solveTask(start), 1024).Wait() - start = end - } - - for _, p := range info.Prints { + prints := data.printsByInstance[uint32(instanceI)] + delete(data.printsByInstance, uint32(instanceI)) + for _, p := range prints { serializable := make([]any, len(p.Values)) for i, v := range p.Values { if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 @@ -117,9 +93,6 @@ func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { } fmt.Println(serializable...) } - - setOuts(data.assignment, info.Circuit, outs) - return nil } } diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index f6e1ad993d..b6c22f4eb6 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -13,6 +13,7 @@ import ( "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -21,92 +22,67 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - workers *utils.WorkerPool + assignment WireAssignment + circuit gkrtypes.Circuit + workers *utils.WorkerPool + maxNbIn int // maximum number of inputs for a gate in the circuit + printsByInstance map[uint32][]gkrinfo.PrintInfo } -func (d *SolvingData) init(info gkrtypes.SolvingInfo) { - d.workers = utils.NewWorkerPool() - d.circuit = info.Circuit +func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { + d := SolvingData{ + workers: utils.NewWorkerPool(), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), + } + d.circuit.SetNbUniqueOutputs() + d.maxNbIn = d.circuit.MaxGateNbIn() d.assignment = make(WireAssignment, len(d.circuit)) for i := range d.assignment { d.assignment[i] = make([]fr.Element, info.NbInstances) } + + return &d } // this module assumes that wire and instance indexes respect dependencies -func setOuts(a WireAssignment, circuit gkrtypes.Circuit, outs []*big.Int) { - outsI := 0 - for i := range circuit { - if circuit[i].IsOutput() { - for j := range a[i] { - a[i][j].BigInt(outs[outsI]) - outsI++ - } +func SolveHint(data *SolvingData) hint.Hint { + return func(_ *big.Int, ins, outs []*big.Int) error { + instanceI := ins[0].Uint64() + if !ins[0].IsUint64() { // TODO use idiomatic printf tag + return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) } - } - // Check if outsI == len(outs)? -} -func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - // assumes assignmentVector is arranged wire first, instance second in order of solution - offsets := info.AssignmentOffsets() - data.init(info) - maxNIn := data.circuit.MaxGateNbIn() - - chunks := info.Chunks() - - solveTask := func(chunkOffset int) utils.Task { - return func(startInChunk, endInChunk int) { - start := startInChunk + chunkOffset - end := endInChunk + chunkOffset - inputs := make([]frontend.Variable, maxNIn) - dependencyHeads := make([]int, len(data.circuit)) // for each wire, which of its dependencies we would look at next - for wI := range data.circuit { // skip instances that are not relevant (related to instances before the current task) - deps := info.Dependencies[wI] - dependencyHeads[wI] = algo_utils.BinarySearchFunc(func(i int) int { - return deps[i].InputInstance - }, len(deps), start) + gateIns := make([]frontend.Variable, data.maxNbIn) + outsI := 0 + insI := 1 // skip the first input, which is the instance index + for wI := range data.circuit { + w := &data.circuit[wI] + if w.IsInput() { // read from provided input + data.assignment[wI][instanceI].SetBigInt(ins[insI]) + insI++ + } else { + + // assemble input for gate + for i, inWI := range w.Inputs { + gateIns[i] = &data.assignment[inWI][instanceI] } - for instanceI := start; instanceI < end; instanceI++ { - for wireI := range data.circuit { - wire := &data.circuit[wireI] - deps := info.Dependencies[wireI] - if wire.IsInput() { - if dependencyHeads[wireI] < len(deps) && instanceI == deps[dependencyHeads[wireI]].InputInstance { - dep := deps[dependencyHeads[wireI]] - data.assignment[wireI][instanceI].Set(&data.assignment[dep.OutputWire][dep.OutputInstance]) - dependencyHeads[wireI]++ - } else { - data.assignment[wireI][instanceI].SetBigInt(ins[offsets[wireI]+instanceI-dependencyHeads[wireI]]) - } - } else { - // assemble the inputs - inputIndexes := info.Circuit[wireI].Inputs - for i, inputI := range inputIndexes { - inputs[i] = &data.assignment[inputI][instanceI] - } - gate := data.circuit[wireI].Gate - data.assignment[wireI][instanceI].Set(gate.Evaluate(api, inputs[:len(inputIndexes)]...).(*fr.Element)) - } - } - } + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + } + if w.IsOutput() { + data.assignment[wI][instanceI].BigInt(outs[outsI]) + outsI++ } } - start := 0 - for _, end := range chunks { - data.workers.Submit(end-start, solveTask(start), 1024).Wait() - start = end - } - - for _, p := range info.Prints { + prints := data.printsByInstance[uint32(instanceI)] + delete(data.printsByInstance, uint32(instanceI)) + for _, p := range prints { serializable := make([]any, len(p.Values)) for i, v := range p.Values { if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 @@ -117,9 +93,6 @@ func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { } fmt.Println(serializable...) } - - setOuts(data.assignment, info.Circuit, outs) - return nil } } diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index 7bc3782932..8a8fa12a75 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -13,6 +13,7 @@ import ( "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -21,92 +22,67 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - workers *utils.WorkerPool + assignment WireAssignment + circuit gkrtypes.Circuit + workers *utils.WorkerPool + maxNbIn int // maximum number of inputs for a gate in the circuit + printsByInstance map[uint32][]gkrinfo.PrintInfo } -func (d *SolvingData) init(info gkrtypes.SolvingInfo) { - d.workers = utils.NewWorkerPool() - d.circuit = info.Circuit +func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { + d := SolvingData{ + workers: utils.NewWorkerPool(), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), + } + d.circuit.SetNbUniqueOutputs() + d.maxNbIn = d.circuit.MaxGateNbIn() d.assignment = make(WireAssignment, len(d.circuit)) for i := range d.assignment { d.assignment[i] = make([]fr.Element, info.NbInstances) } + + return &d } // this module assumes that wire and instance indexes respect dependencies -func setOuts(a WireAssignment, circuit gkrtypes.Circuit, outs []*big.Int) { - outsI := 0 - for i := range circuit { - if circuit[i].IsOutput() { - for j := range a[i] { - a[i][j].BigInt(outs[outsI]) - outsI++ - } +func SolveHint(data *SolvingData) hint.Hint { + return func(_ *big.Int, ins, outs []*big.Int) error { + instanceI := ins[0].Uint64() + if !ins[0].IsUint64() { // TODO use idiomatic printf tag + return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) } - } - // Check if outsI == len(outs)? -} -func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - // assumes assignmentVector is arranged wire first, instance second in order of solution - offsets := info.AssignmentOffsets() - data.init(info) - maxNIn := data.circuit.MaxGateNbIn() - - chunks := info.Chunks() - - solveTask := func(chunkOffset int) utils.Task { - return func(startInChunk, endInChunk int) { - start := startInChunk + chunkOffset - end := endInChunk + chunkOffset - inputs := make([]frontend.Variable, maxNIn) - dependencyHeads := make([]int, len(data.circuit)) // for each wire, which of its dependencies we would look at next - for wI := range data.circuit { // skip instances that are not relevant (related to instances before the current task) - deps := info.Dependencies[wI] - dependencyHeads[wI] = algo_utils.BinarySearchFunc(func(i int) int { - return deps[i].InputInstance - }, len(deps), start) + gateIns := make([]frontend.Variable, data.maxNbIn) + outsI := 0 + insI := 1 // skip the first input, which is the instance index + for wI := range data.circuit { + w := &data.circuit[wI] + if w.IsInput() { // read from provided input + data.assignment[wI][instanceI].SetBigInt(ins[insI]) + insI++ + } else { + + // assemble input for gate + for i, inWI := range w.Inputs { + gateIns[i] = &data.assignment[inWI][instanceI] } - for instanceI := start; instanceI < end; instanceI++ { - for wireI := range data.circuit { - wire := &data.circuit[wireI] - deps := info.Dependencies[wireI] - if wire.IsInput() { - if dependencyHeads[wireI] < len(deps) && instanceI == deps[dependencyHeads[wireI]].InputInstance { - dep := deps[dependencyHeads[wireI]] - data.assignment[wireI][instanceI].Set(&data.assignment[dep.OutputWire][dep.OutputInstance]) - dependencyHeads[wireI]++ - } else { - data.assignment[wireI][instanceI].SetBigInt(ins[offsets[wireI]+instanceI-dependencyHeads[wireI]]) - } - } else { - // assemble the inputs - inputIndexes := info.Circuit[wireI].Inputs - for i, inputI := range inputIndexes { - inputs[i] = &data.assignment[inputI][instanceI] - } - gate := data.circuit[wireI].Gate - data.assignment[wireI][instanceI].Set(gate.Evaluate(api, inputs[:len(inputIndexes)]...).(*fr.Element)) - } - } - } + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + } + if w.IsOutput() { + data.assignment[wI][instanceI].BigInt(outs[outsI]) + outsI++ } } - start := 0 - for _, end := range chunks { - data.workers.Submit(end-start, solveTask(start), 1024).Wait() - start = end - } - - for _, p := range info.Prints { + prints := data.printsByInstance[uint32(instanceI)] + delete(data.printsByInstance, uint32(instanceI)) + for _, p := range prints { serializable := make([]any, len(p.Values)) for i, v := range p.Values { if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 @@ -117,9 +93,6 @@ func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { } fmt.Println(serializable...) } - - setOuts(data.assignment, info.Circuit, outs) - return nil } } diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index 57343d291f..e1df46828f 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -13,6 +13,7 @@ import ( "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -21,92 +22,67 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - workers *utils.WorkerPool + assignment WireAssignment + circuit gkrtypes.Circuit + workers *utils.WorkerPool + maxNbIn int // maximum number of inputs for a gate in the circuit + printsByInstance map[uint32][]gkrinfo.PrintInfo } -func (d *SolvingData) init(info gkrtypes.SolvingInfo) { - d.workers = utils.NewWorkerPool() - d.circuit = info.Circuit +func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { + d := SolvingData{ + workers: utils.NewWorkerPool(), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), + } + d.circuit.SetNbUniqueOutputs() + d.maxNbIn = d.circuit.MaxGateNbIn() d.assignment = make(WireAssignment, len(d.circuit)) for i := range d.assignment { d.assignment[i] = make([]fr.Element, info.NbInstances) } + + return &d } // this module assumes that wire and instance indexes respect dependencies -func setOuts(a WireAssignment, circuit gkrtypes.Circuit, outs []*big.Int) { - outsI := 0 - for i := range circuit { - if circuit[i].IsOutput() { - for j := range a[i] { - a[i][j].BigInt(outs[outsI]) - outsI++ - } +func SolveHint(data *SolvingData) hint.Hint { + return func(_ *big.Int, ins, outs []*big.Int) error { + instanceI := ins[0].Uint64() + if !ins[0].IsUint64() { // TODO use idiomatic printf tag + return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) } - } - // Check if outsI == len(outs)? -} -func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - // assumes assignmentVector is arranged wire first, instance second in order of solution - offsets := info.AssignmentOffsets() - data.init(info) - maxNIn := data.circuit.MaxGateNbIn() - - chunks := info.Chunks() - - solveTask := func(chunkOffset int) utils.Task { - return func(startInChunk, endInChunk int) { - start := startInChunk + chunkOffset - end := endInChunk + chunkOffset - inputs := make([]frontend.Variable, maxNIn) - dependencyHeads := make([]int, len(data.circuit)) // for each wire, which of its dependencies we would look at next - for wI := range data.circuit { // skip instances that are not relevant (related to instances before the current task) - deps := info.Dependencies[wI] - dependencyHeads[wI] = algo_utils.BinarySearchFunc(func(i int) int { - return deps[i].InputInstance - }, len(deps), start) + gateIns := make([]frontend.Variable, data.maxNbIn) + outsI := 0 + insI := 1 // skip the first input, which is the instance index + for wI := range data.circuit { + w := &data.circuit[wI] + if w.IsInput() { // read from provided input + data.assignment[wI][instanceI].SetBigInt(ins[insI]) + insI++ + } else { + + // assemble input for gate + for i, inWI := range w.Inputs { + gateIns[i] = &data.assignment[inWI][instanceI] } - for instanceI := start; instanceI < end; instanceI++ { - for wireI := range data.circuit { - wire := &data.circuit[wireI] - deps := info.Dependencies[wireI] - if wire.IsInput() { - if dependencyHeads[wireI] < len(deps) && instanceI == deps[dependencyHeads[wireI]].InputInstance { - dep := deps[dependencyHeads[wireI]] - data.assignment[wireI][instanceI].Set(&data.assignment[dep.OutputWire][dep.OutputInstance]) - dependencyHeads[wireI]++ - } else { - data.assignment[wireI][instanceI].SetBigInt(ins[offsets[wireI]+instanceI-dependencyHeads[wireI]]) - } - } else { - // assemble the inputs - inputIndexes := info.Circuit[wireI].Inputs - for i, inputI := range inputIndexes { - inputs[i] = &data.assignment[inputI][instanceI] - } - gate := data.circuit[wireI].Gate - data.assignment[wireI][instanceI].Set(gate.Evaluate(api, inputs[:len(inputIndexes)]...).(*fr.Element)) - } - } - } + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + } + if w.IsOutput() { + data.assignment[wI][instanceI].BigInt(outs[outsI]) + outsI++ } } - start := 0 - for _, end := range chunks { - data.workers.Submit(end-start, solveTask(start), 1024).Wait() - start = end - } - - for _, p := range info.Prints { + prints := data.printsByInstance[uint32(instanceI)] + delete(data.printsByInstance, uint32(instanceI)) + for _, p := range prints { serializable := make([]any, len(p.Values)) for i, v := range p.Values { if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 @@ -117,9 +93,6 @@ func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { } fmt.Println(serializable...) } - - setOuts(data.assignment, info.Circuit, outs) - return nil } } diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index 606f13ec23..2104520d6b 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -13,6 +13,7 @@ import ( "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -21,92 +22,67 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - workers *utils.WorkerPool + assignment WireAssignment + circuit gkrtypes.Circuit + workers *utils.WorkerPool + maxNbIn int // maximum number of inputs for a gate in the circuit + printsByInstance map[uint32][]gkrinfo.PrintInfo } -func (d *SolvingData) init(info gkrtypes.SolvingInfo) { - d.workers = utils.NewWorkerPool() - d.circuit = info.Circuit +func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { + d := SolvingData{ + workers: utils.NewWorkerPool(), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), + } + d.circuit.SetNbUniqueOutputs() + d.maxNbIn = d.circuit.MaxGateNbIn() d.assignment = make(WireAssignment, len(d.circuit)) for i := range d.assignment { d.assignment[i] = make([]fr.Element, info.NbInstances) } + + return &d } // this module assumes that wire and instance indexes respect dependencies -func setOuts(a WireAssignment, circuit gkrtypes.Circuit, outs []*big.Int) { - outsI := 0 - for i := range circuit { - if circuit[i].IsOutput() { - for j := range a[i] { - a[i][j].BigInt(outs[outsI]) - outsI++ - } +func SolveHint(data *SolvingData) hint.Hint { + return func(_ *big.Int, ins, outs []*big.Int) error { + instanceI := ins[0].Uint64() + if !ins[0].IsUint64() { // TODO use idiomatic printf tag + return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) } - } - // Check if outsI == len(outs)? -} -func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - // assumes assignmentVector is arranged wire first, instance second in order of solution - offsets := info.AssignmentOffsets() - data.init(info) - maxNIn := data.circuit.MaxGateNbIn() - - chunks := info.Chunks() - - solveTask := func(chunkOffset int) utils.Task { - return func(startInChunk, endInChunk int) { - start := startInChunk + chunkOffset - end := endInChunk + chunkOffset - inputs := make([]frontend.Variable, maxNIn) - dependencyHeads := make([]int, len(data.circuit)) // for each wire, which of its dependencies we would look at next - for wI := range data.circuit { // skip instances that are not relevant (related to instances before the current task) - deps := info.Dependencies[wI] - dependencyHeads[wI] = algo_utils.BinarySearchFunc(func(i int) int { - return deps[i].InputInstance - }, len(deps), start) + gateIns := make([]frontend.Variable, data.maxNbIn) + outsI := 0 + insI := 1 // skip the first input, which is the instance index + for wI := range data.circuit { + w := &data.circuit[wI] + if w.IsInput() { // read from provided input + data.assignment[wI][instanceI].SetBigInt(ins[insI]) + insI++ + } else { + + // assemble input for gate + for i, inWI := range w.Inputs { + gateIns[i] = &data.assignment[inWI][instanceI] } - for instanceI := start; instanceI < end; instanceI++ { - for wireI := range data.circuit { - wire := &data.circuit[wireI] - deps := info.Dependencies[wireI] - if wire.IsInput() { - if dependencyHeads[wireI] < len(deps) && instanceI == deps[dependencyHeads[wireI]].InputInstance { - dep := deps[dependencyHeads[wireI]] - data.assignment[wireI][instanceI].Set(&data.assignment[dep.OutputWire][dep.OutputInstance]) - dependencyHeads[wireI]++ - } else { - data.assignment[wireI][instanceI].SetBigInt(ins[offsets[wireI]+instanceI-dependencyHeads[wireI]]) - } - } else { - // assemble the inputs - inputIndexes := info.Circuit[wireI].Inputs - for i, inputI := range inputIndexes { - inputs[i] = &data.assignment[inputI][instanceI] - } - gate := data.circuit[wireI].Gate - data.assignment[wireI][instanceI].Set(gate.Evaluate(api, inputs[:len(inputIndexes)]...).(*fr.Element)) - } - } - } + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + } + if w.IsOutput() { + data.assignment[wI][instanceI].BigInt(outs[outsI]) + outsI++ } } - start := 0 - for _, end := range chunks { - data.workers.Submit(end-start, solveTask(start), 1024).Wait() - start = end - } - - for _, p := range info.Prints { + prints := data.printsByInstance[uint32(instanceI)] + delete(data.printsByInstance, uint32(instanceI)) + for _, p := range prints { serializable := make([]any, len(p.Values)) for i, v := range p.Values { if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 @@ -117,9 +93,6 @@ func SolveHint(info gkrtypes.SolvingInfo, data *SolvingData) hint.Hint { } fmt.Println(serializable...) } - - setOuts(data.assignment, info.Circuit, outs) - return nil } } diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index ce9ba88942..4c901f04a2 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" + "github.com/consensys/gnark" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" @@ -35,10 +36,10 @@ func NewCache() *Cache { res = api.Mul(res, sum) // sum^7 return res - }, 2, 7, -1) + }, 2, 7, -1, gnark.Curves()) gates["select-input-3"] = gkrtypes.NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return in[2] - }, 3, 1, 0) + }, 3, 1, 0, gnark.Curves()) return &Cache{ circuits: make(map[string]gkrtypes.Circuit), From 644fd6a6ee688da606b37505bc715273d91349f2 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 2 Jun 2025 12:58:44 -0500 Subject: [PATCH 009/251] fix: registry duplicate detection --- constraint/solver/gkrgates/registry.go | 5 --- constraint/solver/gkrgates/registry_test.go | 46 ++++++++++++++++----- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index 2e1d8642ef..71b4969883 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -121,11 +121,6 @@ func Register(f gkr.GateFunction, nbIn int, options ...registerOption) (register if g, ok := gates[s.name]; ok { // gate already registered - if reflect.ValueOf(f).Pointer() != reflect.ValueOf(gates[s.name].Evaluate).Pointer() { - return false, fmt.Errorf("gate \"%s\" already registered with a different function", s.name) - } - // it still might be an anonymous function with different parameters. - // need to test further if g.NbIn() != nbIn { return false, fmt.Errorf("gate \"%s\" already registered with a different number of inputs (%d != %d)", s.name, g.NbIn(), nbIn) } diff --git a/constraint/solver/gkrgates/registry_test.go b/constraint/solver/gkrgates/registry_test.go index ec41888ef3..5a9e6e871d 100644 --- a/constraint/solver/gkrgates/registry_test.go +++ b/constraint/solver/gkrgates/registry_test.go @@ -11,20 +11,38 @@ import ( "github.com/stretchr/testify/assert" ) -func TestRegisterDegreeDetection(t *testing.T) { +func TestRegister(t *testing.T) { testGate := func(name gkr.GateName, f gkr.GateFunction, nbIn, degree int) { t.Run(string(name), func(t *testing.T) { name = name + "-register-gate-test" - assert.NoError(t, Register(f, nbIn, WithDegree(degree), WithName(name)), "given degree must be accepted") + added, err := Register(f, nbIn, WithDegree(degree), WithName(name+"_given")) + assert.NoError(t, err, "given degree must be accepted") + assert.True(t, added, "registration must succeed for given degree") - assert.Error(t, Register(f, nbIn, WithDegree(degree-1), WithName(name)), "lower degree must be rejected") + registered, err := Register(f, nbIn, WithDegree(degree-1), WithName(name+"_lower")) + assert.Error(t, err, "error must be returned for lower degree") + assert.False(t, registered, "registration must fail for lower degree") - assert.Error(t, Register(f, nbIn, WithDegree(degree+1), WithName(name)), "higher degree must be rejected") + registered, err = Register(f, nbIn, WithDegree(degree+1), WithName(name+"_higher")) + assert.Error(t, err, "error must be returned for higher degree") + assert.False(t, registered, "registration must fail for higher degree") - assert.NoError(t, Register(f, nbIn), "no degree must be accepted") + registered, err = Register(f, nbIn, WithName(name+"_no_degree")) + assert.NoError(t, err, "no error must be returned when no degree is specified") + assert.True(t, registered, "registration must succeed when no degree is specified") - assert.Equal(t, degree, Get(name).Degree(), "degree must be detected correctly") + assert.Equal(t, degree, Get(name+"_no_degree").Degree(), "degree must be detected correctly") + + added, err = Register(f, nbIn, WithDegree(degree), WithName(name+"_given")) + assert.NoError(t, err, "given degree must be accepted") + assert.False(t, added, "gate must not be re-registered") + + added, err = Register(func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { + return api.Add(f(api, x...), 1) + }, nbIn, WithDegree(degree), WithName(name+"_given")) + assert.Error(t, err, "registering another function under the same name must fail") + assert.False(t, added, "gate must not be re-registered") }) } @@ -47,15 +65,23 @@ func TestRegisterDegreeDetection(t *testing.T) { ) }, 2, 1) - // zero polynomial must not be accepted t.Run("zero", func(t *testing.T) { const gateName gkr.GateName = "zero-register-gate-test" - expectedError := fmt.Errorf("for gate %s: %v", gateName, gkrtypes.ErrZeroFunction) + expectedError := fmt.Errorf("for gate \"%s\": %v", gateName, gkrtypes.ErrZeroFunction) zeroGate := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Sub(x[0], x[0]) } - assert.Equal(t, expectedError, Register(zeroGate, 1, WithName(gateName))) - assert.Equal(t, expectedError, Register(zeroGate, 1, WithName(gateName), WithDegree(2))) + // Attempt to register the zero gate without specifying a degree + registered, err := Register(zeroGate, 1, WithName(gateName)) + assert.Error(t, err, "error must be returned for zero polynomial") + assert.Equal(t, expectedError, err, "error message must match expected error") + assert.False(t, registered, "registration must fail for zero polynomial") + + // Attempt to register the zero gate with a specified degree + registered, err = Register(zeroGate, 1, WithName(gateName), WithDegree(2)) + assert.Error(t, err, "error must be returned for zero polynomial with degree") + assert.Equal(t, expectedError, err, "error message must match expected error") + assert.False(t, registered, "registration must fail for zero polynomial with degree") }) } From 2001e8320931972a1ea2620fbdcdf52ea4c20caf Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 2 Jun 2025 15:43:31 -0500 Subject: [PATCH 010/251] fix: solver hint id mismatch --- .../backend/template/gkr/solver_hints.go.tmpl | 2 +- internal/gkr/bls12-377/solver_hints.go | 2 +- internal/gkr/bls12-381/solver_hints.go | 2 +- internal/gkr/bls24-315/solver_hints.go | 2 +- internal/gkr/bls24-317/solver_hints.go | 2 +- internal/gkr/bn254/solver_hints.go | 2 +- internal/gkr/bw6-633/solver_hints.go | 2 +- internal/gkr/bw6-761/solver_hints.go | 2 +- internal/gkr/hints.go | 19 +++++++------- std/gkrapi/compile.go | 26 ++++++++++++++----- 10 files changed, 37 insertions(+), 24 deletions(-) diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index 472121423c..170c1807da 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -66,7 +66,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 18ad9c91e9..b63ceebb83 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -72,7 +72,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index 5c73505739..93b65be2bf 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -72,7 +72,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index b44cea8feb..49317d446c 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -72,7 +72,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index b6c22f4eb6..c02c8ab135 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -72,7 +72,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index 8a8fa12a75..5c0a2e3ef3 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -72,7 +72,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index e1df46828f..21bc7961a5 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -72,7 +72,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index 2104520d6b..dacd7eb35f 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -72,7 +72,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/hints.go b/internal/gkr/hints.go index 2c2621911e..a6854e36df 100644 --- a/internal/gkr/hints.go +++ b/internal/gkr/hints.go @@ -26,8 +26,8 @@ func modKey(mod *big.Int) string { // SolveHintPlaceholder solves one instance of a GKR circuit. // The first input is the index of the instance. The rest are the inputs of the circuit, in their nominal order. -func SolveHintPlaceholder(gkrInfo gkrinfo.StoringInfo) solver.Hint { - return func(mod *big.Int, ins []*big.Int, outs []*big.Int) error { +func SolveHintPlaceholder(gkrInfo gkrinfo.StoringInfo) (solver.Hint, solver.HintID) { + hint := func(mod *big.Int, ins []*big.Int, outs []*big.Int) error { solvingInfo, err := gkrtypes.StoringToSolvingInfo(gkrInfo, gkrgates.Get) if err != nil { @@ -39,31 +39,31 @@ func SolveHintPlaceholder(gkrInfo gkrinfo.StoringInfo) solver.Hint { // TODO @Tabaie autogenerate this or decide not to if mod.Cmp(ecc.BLS12_377.ScalarField()) == 0 { data := bls12377.NewSolvingData(solvingInfo) - hint = bls12377.SolveHint(solvingInfo, data) + hint = bls12377.SolveHint(data) testEngineGkrSolvingData[modKey(mod)] = data } else if mod.Cmp(ecc.BLS12_381.ScalarField()) == 0 { data := bls12381.NewSolvingData(solvingInfo) - hint = bls12381.SolveHint(solvingInfo, data) + hint = bls12381.SolveHint(data) testEngineGkrSolvingData[modKey(mod)] = data } else if mod.Cmp(ecc.BLS24_315.ScalarField()) == 0 { data := bls24315.NewSolvingData(solvingInfo) - hint = bls24315.SolveHint(solvingInfo, data) + hint = bls24315.SolveHint(data) testEngineGkrSolvingData[modKey(mod)] = data } else if mod.Cmp(ecc.BLS24_317.ScalarField()) == 0 { data := bls24317.NewSolvingData(solvingInfo) - hint = bls24317.SolveHint(solvingInfo, data) + hint = bls24317.SolveHint(data) testEngineGkrSolvingData[modKey(mod)] = data } else if mod.Cmp(ecc.BN254.ScalarField()) == 0 { data := bn254.NewSolvingData(solvingInfo) - hint = bn254.SolveHint(solvingInfo, data) + hint = bn254.SolveHint(data) testEngineGkrSolvingData[modKey(mod)] = data } else if mod.Cmp(ecc.BW6_633.ScalarField()) == 0 { data := bw6633.NewSolvingData(solvingInfo) - hint = bw6633.SolveHint(solvingInfo, data) + hint = bw6633.SolveHint(data) testEngineGkrSolvingData[modKey(mod)] = data } else if mod.Cmp(ecc.BW6_761.ScalarField()) == 0 { data := bw6761.NewSolvingData(solvingInfo) - hint = bw6761.SolveHint(solvingInfo, data) + hint = bw6761.SolveHint(data) testEngineGkrSolvingData[modKey(mod)] = data } else { return errors.New("unsupported modulus") @@ -71,6 +71,7 @@ func SolveHintPlaceholder(gkrInfo gkrinfo.StoringInfo) solver.Hint { return hint(mod, ins, outs) } + return hint, solver.GetHintID(hint) } func ProveHintPlaceholder(hashName string) solver.Hint { diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 606596fee3..63ebe7c314 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -74,23 +74,35 @@ func (api *API) Compile(parentApi frontend.API, fiatshamirHashName string, optio api: parentApi, } - api.toStore.HashName = fiatshamirHashName + res.toStore.HashName = fiatshamirHashName for _, opt := range options { opt(&res) } + notOut := make([]bool, len(res.toStore.Circuit)) for i := range res.toStore.Circuit { - if res.toStore.Circuit[i].IsOutput() { - res.outs = append(res.ins, gkr.Variable(i)) - } if res.toStore.Circuit[i].IsInput() { res.ins = append(res.ins, gkr.Variable(i)) } + for _, inWI := range res.toStore.Circuit[i].Inputs { + notOut[inWI] = true + } } - res.toStore.SolveHintID = solver.GetHintID(gadget.SolveHintPlaceholder(res.toStore)) + + for i := range res.toStore.Circuit { + if !notOut[i] { + res.outs = append(res.outs, gkr.Variable(i)) + } + } + + _, res.toStore.SolveHintID = gadget.SolveHintPlaceholder(res.toStore) res.toStore.ProveHintID = solver.GetHintID(gadget.ProveHintPlaceholder(fiatshamirHashName)) + if err := parentApi.(gkrinfo.ConstraintSystem).SetGkrInfo(res.toStore); err != nil { + panic(err) + } + parentApi.Compiler().Defer(res.verify) return &res @@ -119,7 +131,7 @@ func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr } c.toStore.NbInstances++ - solveHintPlaceholder := gadget.SolveHintPlaceholder(c.toStore) + solveHintPlaceholder, _ := gadget.SolveHintPlaceholder(c.toStore) outsSerialized, err := c.api.Compiler().NewHint(solveHintPlaceholder, len(c.outs), hintIn...) if err != nil { return nil, fmt.Errorf("failed to create solve hint: %w", err) @@ -203,7 +215,7 @@ func (c *Circuit) verify(api frontend.API) error { return err } - return api.(gkrinfo.ConstraintSystem).SetGkrInfo(c.toStore) + return nil } func slicePtrAt[T any](slice []T) func(int) *T { From d3ca3c12d9d498081b1dbe3f0daaf752b2c1183e Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 2 Jun 2025 15:49:04 -0500 Subject: [PATCH 011/251] remove redundant make --- internal/gkr/bn254/solver_hints.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index 5c0a2e3ef3..e6a971268d 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -40,7 +40,6 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { d.circuit.SetNbUniqueOutputs() d.maxNbIn = d.circuit.MaxGateNbIn() - d.assignment = make(WireAssignment, len(d.circuit)) for i := range d.assignment { d.assignment[i] = make([]fr.Element, info.NbInstances) } From 647448206a4c4a39b88bc4d23a9a4be72904db34 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 2 Jun 2025 16:18:39 -0500 Subject: [PATCH 012/251] fix: works on plonk --- std/gkrapi/api.go | 6 ++++++ std/gkrapi/compile.go | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index 5765fb98a1..6c6d4b0976 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -65,6 +65,12 @@ func (api *API) Println(instance int, a ...any) { api.toStore.Prints = append(api.toStore.Prints, newPrint(instance, a...)) } +// Println writes to the standard output. +// instance determines which values are chosen for gkr.Variable input. +func (c *Circuit) Println(instance int, a ...any) { + c.toStore.Prints = append(c.toStore.Prints, newPrint(instance, a...)) +} + func newPrint(instance int, a ...any) gkrinfo.PrintInfo { isVar := make([]bool, len(a)) vals := make([]any, len(a)) diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 63ebe7c314..05a12bb8a9 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -99,10 +99,6 @@ func (api *API) Compile(parentApi frontend.API, fiatshamirHashName string, optio _, res.toStore.SolveHintID = gadget.SolveHintPlaceholder(res.toStore) res.toStore.ProveHintID = solver.GetHintID(gadget.ProveHintPlaceholder(fiatshamirHashName)) - if err := parentApi.(gkrinfo.ConstraintSystem).SetGkrInfo(res.toStore); err != nil { - panic(err) - } - parentApi.Compiler().Defer(res.verify) return &res @@ -151,6 +147,10 @@ func (c *Circuit) verify(api frontend.API) error { panic("api mismatch") } + if err := api.(gkrinfo.ConstraintSystem).SetGkrInfo(c.toStore); err != nil { + return err + } + if len(c.outs) == 0 || len(c.assignments[0]) == 0 { return nil } From 0c0b6b0b3a032f69436d2634a9a6edb2bb1c4ff1 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 2 Jun 2025 19:49:00 -0500 Subject: [PATCH 013/251] refactor: solve hint for test engine --- .../backend/template/gkr/gkr.go.tmpl | 2 +- .../backend/template/gkr/solver_hints.go.tmpl | 2 +- internal/gkr/engine_hints.go | 181 ++++++++++++++++++ internal/gkr/hints.go | 111 ----------- std/gkrapi/compile.go | 19 +- 5 files changed, 194 insertions(+), 121 deletions(-) create mode 100644 internal/gkr/engine_hints.go delete mode 100644 internal/gkr/hints.go diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 5105b0a33d..3e3881d15f 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -735,7 +735,7 @@ func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.V func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { var prod {{ .ElementType }} - prod.Add(cast(b), cast(c)) + prod.Mul(cast(b), cast(c)) res := cast(a) res.Add(res, &prod) return &res diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index 170c1807da..b9f570e26e 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -47,7 +47,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { // TODO use idiomatic printf tag + if !ins[0].IsUint64() { return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) } diff --git a/internal/gkr/engine_hints.go b/internal/gkr/engine_hints.go new file mode 100644 index 0000000000..ec7e10e00a --- /dev/null +++ b/internal/gkr/engine_hints.go @@ -0,0 +1,181 @@ +package gkr + +import ( + "errors" + "fmt" + "math/big" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/constraint/solver/gkrgates" + "github.com/consensys/gnark/frontend" + bls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" + bls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" + bls24315 "github.com/consensys/gnark/internal/gkr/bls24-315" + bls24317 "github.com/consensys/gnark/internal/gkr/bls24-317" + bn254 "github.com/consensys/gnark/internal/gkr/bn254" + bw6633 "github.com/consensys/gnark/internal/gkr/bw6-633" + bw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" + "github.com/consensys/gnark/internal/gkr/gkrinfo" + "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/utils" +) + +func modKey(mod *big.Int) string { + return mod.Text(32) +} + +type TestEngineHints struct { + assignment gkrtypes.WireAssignment + info *gkrinfo.StoringInfo + circuit gkrtypes.Circuit + gateIns []frontend.Variable +} + +func NewTestEngineHints(info *gkrinfo.StoringInfo) (*TestEngineHints, error) { + circuit, err := gkrtypes.CircuitInfoToCircuit(info.Circuit, gkrgates.Get) + if err != nil { + return nil, err + } + + return &TestEngineHints{ + info: info, + circuit: circuit, + gateIns: make([]frontend.Variable, circuit.MaxGateNbIn()), + }, + err +} + +// Solve solves one instance of a GKR circuit. +// The first input is the index of the instance. The rest are the inputs of the circuit, in their nominal order. +func (h *TestEngineHints) Solve(mod *big.Int, ins []*big.Int, outs []*big.Int) error { + + // TODO handle prints + + if in0 := ins[0].Uint64(); !ins[0].IsUint64() || in0 >= uint64(len(h.info.Circuit)) || in0 > 0xffffffff { + return errors.New("first input must be a uint32 instance index") + } else if in0 != uint64(h.info.NbInstances) || h.info.NbInstances != len(h.assignment[0]) { + return errors.New("first input must equal the number of instances, and calls to Solve must be done in order of instance index") + } + + api := gateAPI{mod} + + inI := 1 + outI := 0 + for wI := range h.circuit { + w := &h.circuit[wI] + var val frontend.Variable + if w.IsInput() { + val = utils.FromInterface(ins[inI]) + inI++ + } else { + for gateInI, inWI := range w.Inputs { + h.gateIns[gateInI] = h.assignment[inWI][gateInI] + } + val = w.Gate.Evaluate(api, h.gateIns[:len(w.Inputs)]...) + } + if w.IsOutput() { + *outs[outI] = utils.FromInterface(val) + } + h.assignment[wI] = append(h.assignment[wI], val) + } + return nil +} + +func (h *TestEngineHints) Prove(mod *big.Int, ins, outs []*big.Int) error { + + // todo handle prints + k := modKey(mod) + data, ok := testEngineGkrSolvingData[k] + if !ok { + return errors.New("solving data not found") + } + delete(testEngineGkrSolvingData, k) + + // TODO @Tabaie autogenerate this or decide not to + if mod.Cmp(ecc.BLS12_377.ScalarField()) == 0 { + return bls12377.ProveHint(hashName, data.(*bls12377.SolvingData))(mod, ins, outs) + } + if mod.Cmp(ecc.BLS12_381.ScalarField()) == 0 { + return bls12381.ProveHint(hashName, data.(*bls12381.SolvingData))(mod, ins, outs) + } + if mod.Cmp(ecc.BLS24_315.ScalarField()) == 0 { + return bls24315.ProveHint(hashName, data.(*bls24315.SolvingData))(mod, ins, outs) + } + if mod.Cmp(ecc.BLS24_317.ScalarField()) == 0 { + return bls24317.ProveHint(hashName, data.(*bls24317.SolvingData))(mod, ins, outs) + } + if mod.Cmp(ecc.BN254.ScalarField()) == 0 { + return bn254.ProveHint(hashName, data.(*bn254.SolvingData))(mod, ins, outs) + } + if mod.Cmp(ecc.BW6_633.ScalarField()) == 0 { + return bw6633.ProveHint(hashName, data.(*bw6633.SolvingData))(mod, ins, outs) + } + if mod.Cmp(ecc.BW6_761.ScalarField()) == 0 { + return bw6761.ProveHint(hashName, data.(*bw6761.SolvingData))(mod, ins, outs) + } + + return errors.New("unsupported modulus") + +} + +type gateAPI struct{ *big.Int } + +func (g gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + in1 := utils.FromInterface(i1) + in2 := utils.FromInterface(i2) + + in1.Add(&in1, &in2) + for _, v := range in { + inV := utils.FromInterface(v) + in1.Add(&in1, &inV) + } + return &in1 +} + +func (g gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { + x, y := utils.FromInterface(b), utils.FromInterface(c) + x.Mul(&x, &y) + y = utils.FromInterface(a) + x.Add(&x, &y) + return &x +} + +func (g gateAPI) Neg(i1 frontend.Variable) frontend.Variable { + x := utils.FromInterface(i1) + x.Neg(&x) + return &x +} + +func (g gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + x := utils.FromInterface(i1) + y := utils.FromInterface(i2) + x.Sub(&x, &y) + for _, v := range in { + y = utils.FromInterface(v) + x.Sub(&x, &y) + } + return &x +} + +func (g gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + x := utils.FromInterface(i1) + y := utils.FromInterface(i2) + x.Mul(&x, &y) + for _, v := range in { + y = utils.FromInterface(v) + x.Mul(&x, &y) + } + return &x +} + +func (g gateAPI) Println(a ...frontend.Variable) { + strings := make([]string, len(a)) + for i := range a { + if s, ok := a[i].(fmt.Stringer); ok { + strings[i] = s.String() + } else { + bigInt := utils.FromInterface(a[i]) + strings[i] = bigInt.String() + } + } +} diff --git a/internal/gkr/hints.go b/internal/gkr/hints.go deleted file mode 100644 index a6854e36df..0000000000 --- a/internal/gkr/hints.go +++ /dev/null @@ -1,111 +0,0 @@ -package gkr - -import ( - "errors" - "math/big" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/constraint/solver/gkrgates" - bls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" - bls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" - bls24315 "github.com/consensys/gnark/internal/gkr/bls24-315" - bls24317 "github.com/consensys/gnark/internal/gkr/bls24-317" - bn254 "github.com/consensys/gnark/internal/gkr/bn254" - bw6633 "github.com/consensys/gnark/internal/gkr/bw6-633" - bw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" - "github.com/consensys/gnark/internal/gkr/gkrinfo" - "github.com/consensys/gnark/internal/gkr/gkrtypes" -) - -var testEngineGkrSolvingData = make(map[string]any) - -func modKey(mod *big.Int) string { - return mod.Text(32) -} - -// SolveHintPlaceholder solves one instance of a GKR circuit. -// The first input is the index of the instance. The rest are the inputs of the circuit, in their nominal order. -func SolveHintPlaceholder(gkrInfo gkrinfo.StoringInfo) (solver.Hint, solver.HintID) { - hint := func(mod *big.Int, ins []*big.Int, outs []*big.Int) error { - - solvingInfo, err := gkrtypes.StoringToSolvingInfo(gkrInfo, gkrgates.Get) - if err != nil { - return err - } - - var hint solver.Hint - - // TODO @Tabaie autogenerate this or decide not to - if mod.Cmp(ecc.BLS12_377.ScalarField()) == 0 { - data := bls12377.NewSolvingData(solvingInfo) - hint = bls12377.SolveHint(data) - testEngineGkrSolvingData[modKey(mod)] = data - } else if mod.Cmp(ecc.BLS12_381.ScalarField()) == 0 { - data := bls12381.NewSolvingData(solvingInfo) - hint = bls12381.SolveHint(data) - testEngineGkrSolvingData[modKey(mod)] = data - } else if mod.Cmp(ecc.BLS24_315.ScalarField()) == 0 { - data := bls24315.NewSolvingData(solvingInfo) - hint = bls24315.SolveHint(data) - testEngineGkrSolvingData[modKey(mod)] = data - } else if mod.Cmp(ecc.BLS24_317.ScalarField()) == 0 { - data := bls24317.NewSolvingData(solvingInfo) - hint = bls24317.SolveHint(data) - testEngineGkrSolvingData[modKey(mod)] = data - } else if mod.Cmp(ecc.BN254.ScalarField()) == 0 { - data := bn254.NewSolvingData(solvingInfo) - hint = bn254.SolveHint(data) - testEngineGkrSolvingData[modKey(mod)] = data - } else if mod.Cmp(ecc.BW6_633.ScalarField()) == 0 { - data := bw6633.NewSolvingData(solvingInfo) - hint = bw6633.SolveHint(data) - testEngineGkrSolvingData[modKey(mod)] = data - } else if mod.Cmp(ecc.BW6_761.ScalarField()) == 0 { - data := bw6761.NewSolvingData(solvingInfo) - hint = bw6761.SolveHint(data) - testEngineGkrSolvingData[modKey(mod)] = data - } else { - return errors.New("unsupported modulus") - } - - return hint(mod, ins, outs) - } - return hint, solver.GetHintID(hint) -} - -func ProveHintPlaceholder(hashName string) solver.Hint { - return func(mod *big.Int, ins, outs []*big.Int) error { - k := modKey(mod) - data, ok := testEngineGkrSolvingData[k] - if !ok { - return errors.New("solving data not found") - } - delete(testEngineGkrSolvingData, k) - - // TODO @Tabaie autogenerate this or decide not to - if mod.Cmp(ecc.BLS12_377.ScalarField()) == 0 { - return bls12377.ProveHint(hashName, data.(*bls12377.SolvingData))(mod, ins, outs) - } - if mod.Cmp(ecc.BLS12_381.ScalarField()) == 0 { - return bls12381.ProveHint(hashName, data.(*bls12381.SolvingData))(mod, ins, outs) - } - if mod.Cmp(ecc.BLS24_315.ScalarField()) == 0 { - return bls24315.ProveHint(hashName, data.(*bls24315.SolvingData))(mod, ins, outs) - } - if mod.Cmp(ecc.BLS24_317.ScalarField()) == 0 { - return bls24317.ProveHint(hashName, data.(*bls24317.SolvingData))(mod, ins, outs) - } - if mod.Cmp(ecc.BN254.ScalarField()) == 0 { - return bn254.ProveHint(hashName, data.(*bn254.SolvingData))(mod, ins, outs) - } - if mod.Cmp(ecc.BW6_633.ScalarField()) == 0 { - return bw6633.ProveHint(hashName, data.(*bw6633.SolvingData))(mod, ins, outs) - } - if mod.Cmp(ecc.BW6_761.ScalarField()) == 0 { - return bw6761.ProveHint(hashName, data.(*bw6761.SolvingData))(mod, ins, outs) - } - - return errors.New("unsupported modulus") - } -} diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 05a12bb8a9..32979de0fe 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -32,7 +32,8 @@ type Circuit struct { getInitialChallenges InitialChallengeGetter // optional getter for the initial Fiat-Shamir challenge ins []gkr.Variable outs []gkr.Variable - api frontend.API // the parent API used for hints + api frontend.API // the parent API used for hints + hints *gadget.TestEngineHints // hints for the GKR circuit, used for testing purposes } // New creates a new GKR API @@ -75,6 +76,7 @@ func (api *API) Compile(parentApi frontend.API, fiatshamirHashName string, optio } res.toStore.HashName = fiatshamirHashName + res.hints = gadget.NewTestEngineHints(&res.toStore) for _, opt := range options { opt(&res) @@ -96,8 +98,7 @@ func (api *API) Compile(parentApi frontend.API, fiatshamirHashName string, optio } } - _, res.toStore.SolveHintID = gadget.SolveHintPlaceholder(res.toStore) - res.toStore.ProveHintID = solver.GetHintID(gadget.ProveHintPlaceholder(fiatshamirHashName)) + res.toStore.ProveHintID = solver.GetHintID(res.hints.Prove) parentApi.Compiler().Defer(res.verify) @@ -126,9 +127,12 @@ func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr } } + if c.toStore.NbInstances == 0 { + c.toStore.SolveHintID = solver.GetHintID(c.hints.Solve) + } + c.toStore.NbInstances++ - solveHintPlaceholder, _ := gadget.SolveHintPlaceholder(c.toStore) - outsSerialized, err := c.api.Compiler().NewHint(solveHintPlaceholder, len(c.outs), hintIn...) + outsSerialized, err := c.api.Compiler().NewHint(c.hints.Solve, len(c.outs), hintIn...) if err != nil { return nil, fmt.Errorf("failed to create solve hint: %w", err) } @@ -192,12 +196,11 @@ func (c *Circuit) verify(api frontend.API) error { copy(hintIns[1:], initialChallenges) - proveHintPlaceholder := gadget.ProveHintPlaceholder(c.toStore.HashName) if proofSerialized, err = api.Compiler().NewHint( - proveHintPlaceholder, gadget.ProofSize(forSnark.circuit, logNbInstances), hintIns...); err != nil { + c.hints.Prove, gadget.ProofSize(forSnark.circuit, logNbInstances), hintIns...); err != nil { return err } - c.toStore.ProveHintID = solver.GetHintID(proveHintPlaceholder) + c.toStore.ProveHintID = solver.GetHintID(c.hints.Prove) forSnarkSorted := utils.MapRange(0, len(c.toStore.Circuit), slicePtrAt(forSnark.circuit)) From 478d53d1e6385c498fa323cfc261e242114f2eb4 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 3 Jun 2025 13:22:56 -0500 Subject: [PATCH 014/251] fix prove hint --- internal/gkr/bn254/solver_hints.go | 40 ++++++++++++++++++++++++++---- internal/gkr/engine_hints.go | 37 +++++++++++++-------------- std/gkrapi/compile.go | 7 +++++- 3 files changed, 60 insertions(+), 24 deletions(-) diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index e6a971268d..88d521f531 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -10,7 +10,6 @@ import ( "math/big" "github.com/consensys/gnark-crypto/hash" - "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrinfo" @@ -24,14 +23,29 @@ import ( type SolvingData struct { assignment WireAssignment circuit gkrtypes.Circuit - workers *utils.WorkerPool maxNbIn int // maximum number of inputs for a gate in the circuit printsByInstance map[uint32][]gkrinfo.PrintInfo } -func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { +type newSolvingDataSettings struct { + assignment gkrtypes.WireAssignment +} + +type newSolvingDataOption func(*newSolvingDataSettings) + +func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { + return func(s *newSolvingDataSettings) { + s.assignment = assignment + } +} + +func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { + var s newSolvingDataSettings + for _, opt := range options { + opt(&s) + } + d := SolvingData{ - workers: utils.NewWorkerPool(), circuit: info.Circuit, assignment: make(WireAssignment, len(info.Circuit)), printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), @@ -44,6 +58,22 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { d.assignment[i] = make([]fr.Element, info.NbInstances) } + if s.assignment != nil { + if len(s.assignment) != len(d.assignment) { + panic(fmt.Errorf("provided assignment has %d wires, expected %d", len(s.assignment), len(d.assignment))) + } + for i := range d.assignment { + if len(s.assignment[i]) != info.NbInstances { + panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) + } + for j := range d.assignment[i] { + if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { + panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) + } + } + } + } + return &d } @@ -107,7 +137,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { hsh := hash.NewHash(hashName + "_BN254") - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...), WithWorkers(data.workers)) + proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) if err != nil { return err } diff --git a/internal/gkr/engine_hints.go b/internal/gkr/engine_hints.go index ec7e10e00a..dd6408c718 100644 --- a/internal/gkr/engine_hints.go +++ b/internal/gkr/engine_hints.go @@ -20,13 +20,9 @@ import ( "github.com/consensys/gnark/internal/utils" ) -func modKey(mod *big.Int) string { - return mod.Text(32) -} - type TestEngineHints struct { assignment gkrtypes.WireAssignment - info *gkrinfo.StoringInfo + info *gkrinfo.StoringInfo // we retain a reference to the solving info to allow the caller to modify it between calls to Solve and Prove circuit gkrtypes.Circuit gateIns []frontend.Variable } @@ -82,36 +78,41 @@ func (h *TestEngineHints) Solve(mod *big.Int, ins []*big.Int, outs []*big.Int) e } func (h *TestEngineHints) Prove(mod *big.Int, ins, outs []*big.Int) error { - // todo handle prints - k := modKey(mod) - data, ok := testEngineGkrSolvingData[k] - if !ok { - return errors.New("solving data not found") + + info, err := gkrtypes.StoringToSolvingInfo(*h.info, gkrgates.Get) + if err != nil { + return fmt.Errorf("failed to convert storing info to solving info: %w", err) } - delete(testEngineGkrSolvingData, k) // TODO @Tabaie autogenerate this or decide not to if mod.Cmp(ecc.BLS12_377.ScalarField()) == 0 { - return bls12377.ProveHint(hashName, data.(*bls12377.SolvingData))(mod, ins, outs) + data := bls12377.NewSolvingData(info, bls12377.WithAssignment(h.assignment)) + return bls12377.ProveHint(info.HashName, data)(mod, ins, outs) } if mod.Cmp(ecc.BLS12_381.ScalarField()) == 0 { - return bls12381.ProveHint(hashName, data.(*bls12381.SolvingData))(mod, ins, outs) + data := bls12381.NewSolvingData(info, bls12381.WithAssignment(h.assignment)) + return bls12381.ProveHint(info.HashName, data)(mod, ins, outs) } if mod.Cmp(ecc.BLS24_315.ScalarField()) == 0 { - return bls24315.ProveHint(hashName, data.(*bls24315.SolvingData))(mod, ins, outs) + data := bls24315.NewSolvingData(info, bls24315.WithAssignment(h.assignment)) + return bls24315.ProveHint(info.HashName, data)(mod, ins, outs) } if mod.Cmp(ecc.BLS24_317.ScalarField()) == 0 { - return bls24317.ProveHint(hashName, data.(*bls24317.SolvingData))(mod, ins, outs) + data := bls24317.NewSolvingData(info, bls24317.WithAssignment(h.assignment)) + return bls24317.ProveHint(info.HashName, data)(mod, ins, outs) } if mod.Cmp(ecc.BN254.ScalarField()) == 0 { - return bn254.ProveHint(hashName, data.(*bn254.SolvingData))(mod, ins, outs) + data := bn254.NewSolvingData(info, bn254.WithAssignment(h.assignment)) + return bn254.ProveHint(info.HashName, data)(mod, ins, outs) } if mod.Cmp(ecc.BW6_633.ScalarField()) == 0 { - return bw6633.ProveHint(hashName, data.(*bw6633.SolvingData))(mod, ins, outs) + data := bw6633.NewSolvingData(info, bw6633.WithAssignment(h.assignment)) + return bw6633.ProveHint(info.HashName, data)(mod, ins, outs) } if mod.Cmp(ecc.BW6_761.ScalarField()) == 0 { - return bw6761.ProveHint(hashName, data.(*bw6761.SolvingData))(mod, ins, outs) + data := bw6761.NewSolvingData(info, bw6761.WithAssignment(h.assignment)) + return bw6761.ProveHint(info.HashName, data)(mod, ins, outs) } return errors.New("unsupported modulus") diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 32979de0fe..87c863e7ee 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -76,7 +76,12 @@ func (api *API) Compile(parentApi frontend.API, fiatshamirHashName string, optio } res.toStore.HashName = fiatshamirHashName - res.hints = gadget.NewTestEngineHints(&res.toStore) + + var err error + res.hints, err = gadget.NewTestEngineHints(&res.toStore) + if err != nil { + panic(fmt.Errorf("failed to create GKR hints: %w", err)) + } for _, opt := range options { opt(&res) From 6c83740a95086e9921c5621b44553dade189f0f6 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 3 Jun 2025 14:13:21 -0500 Subject: [PATCH 015/251] fix package tests --- .../backend/template/gkr/solver_hints.go.tmpl | 41 +++++++++++++++--- internal/gkr/bls12-377/gkr.go | 2 +- internal/gkr/bls12-377/solver_hints.go | 43 ++++++++++++++++--- internal/gkr/bls12-381/gkr.go | 2 +- internal/gkr/bls12-381/solver_hints.go | 43 ++++++++++++++++--- internal/gkr/bls24-315/gkr.go | 2 +- internal/gkr/bls24-315/solver_hints.go | 43 ++++++++++++++++--- internal/gkr/bls24-317/gkr.go | 2 +- internal/gkr/bls24-317/solver_hints.go | 43 ++++++++++++++++--- internal/gkr/bn254/gkr.go | 2 +- internal/gkr/bn254/solver_hints.go | 4 +- internal/gkr/bw6-633/gkr.go | 2 +- internal/gkr/bw6-633/solver_hints.go | 43 ++++++++++++++++--- internal/gkr/bw6-761/gkr.go | 2 +- internal/gkr/bw6-761/solver_hints.go | 43 ++++++++++++++++--- internal/gkr/engine_hints.go | 14 +++--- internal/gkr/gkrinfo/info.go | 9 +--- internal/gkr/gkrtypes/types.go | 3 +- internal/gkr/small_rational/gkr.go | 2 +- std/gkrapi/api_test.go | 4 +- 20 files changed, 275 insertions(+), 74 deletions(-) diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index b9f570e26e..d8ff837ced 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -3,7 +3,6 @@ import ( "math/big" "github.com/consensys/gnark-crypto/hash" - "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrinfo" @@ -17,20 +16,34 @@ import ( type SolvingData struct { assignment WireAssignment circuit gkrtypes.Circuit - workers *utils.WorkerPool maxNbIn int // maximum number of inputs for a gate in the circuit printsByInstance map[uint32][]gkrinfo.PrintInfo } -func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { +type newSolvingDataSettings struct { + assignment gkrtypes.WireAssignment +} + +type newSolvingDataOption func(*newSolvingDataSettings) + +func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { + return func(s *newSolvingDataSettings) { + s.assignment = assignment + } +} + +func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { + var s newSolvingDataSettings + for _, opt := range options { + opt(&s) + } + d := SolvingData{ - workers: utils.NewWorkerPool(), circuit: info.Circuit, assignment: make(WireAssignment, len(info.Circuit)), printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), } - d.circuit.SetNbUniqueOutputs() d.maxNbIn = d.circuit.MaxGateNbIn() d.assignment = make(WireAssignment, len(d.circuit)) @@ -38,6 +51,22 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { d.assignment[i] = make([]fr.Element, info.NbInstances) } + if s.assignment != nil { + if len(s.assignment) != len(d.assignment) { + panic(fmt.Errorf("provided assignment has %d wires, expected %d", len(s.assignment), len(d.assignment))) + } + for i := range d.assignment { + if len(s.assignment[i]) != info.NbInstances { + panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) + } + for j := range d.assignment[i] { + if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { + panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) + } + } + } + } + return &d } @@ -102,7 +131,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { hsh := hash.NewHash(hashName + "_{{.FieldID}}") - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...), WithWorkers(data.workers)) + proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) if err != nil { return err } diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index f5dfad020e..b92ac1249d 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -739,7 +739,7 @@ func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.V func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { var prod fr.Element - prod.Add(cast(b), cast(c)) + prod.Mul(cast(b), cast(c)) res := cast(a) res.Add(res, &prod) return &res diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index b63ceebb83..d03272551d 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -10,7 +10,6 @@ import ( "math/big" "github.com/consensys/gnark-crypto/hash" - "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrinfo" @@ -24,20 +23,34 @@ import ( type SolvingData struct { assignment WireAssignment circuit gkrtypes.Circuit - workers *utils.WorkerPool maxNbIn int // maximum number of inputs for a gate in the circuit printsByInstance map[uint32][]gkrinfo.PrintInfo } -func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { +type newSolvingDataSettings struct { + assignment gkrtypes.WireAssignment +} + +type newSolvingDataOption func(*newSolvingDataSettings) + +func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { + return func(s *newSolvingDataSettings) { + s.assignment = assignment + } +} + +func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { + var s newSolvingDataSettings + for _, opt := range options { + opt(&s) + } + d := SolvingData{ - workers: utils.NewWorkerPool(), circuit: info.Circuit, assignment: make(WireAssignment, len(info.Circuit)), printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), } - d.circuit.SetNbUniqueOutputs() d.maxNbIn = d.circuit.MaxGateNbIn() d.assignment = make(WireAssignment, len(d.circuit)) @@ -45,6 +58,22 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { d.assignment[i] = make([]fr.Element, info.NbInstances) } + if s.assignment != nil { + if len(s.assignment) != len(d.assignment) { + panic(fmt.Errorf("provided assignment has %d wires, expected %d", len(s.assignment), len(d.assignment))) + } + for i := range d.assignment { + if len(s.assignment[i]) != info.NbInstances { + panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) + } + for j := range d.assignment[i] { + if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { + panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) + } + } + } + } + return &d } @@ -53,7 +82,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { // TODO use idiomatic printf tag + if !ins[0].IsUint64() { return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) } @@ -108,7 +137,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { hsh := hash.NewHash(hashName + "_BLS12_377") - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...), WithWorkers(data.workers)) + proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) if err != nil { return err } diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index f5617a59d4..82084049d9 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -739,7 +739,7 @@ func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.V func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { var prod fr.Element - prod.Add(cast(b), cast(c)) + prod.Mul(cast(b), cast(c)) res := cast(a) res.Add(res, &prod) return &res diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index 93b65be2bf..2c6db19a72 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -10,7 +10,6 @@ import ( "math/big" "github.com/consensys/gnark-crypto/hash" - "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrinfo" @@ -24,20 +23,34 @@ import ( type SolvingData struct { assignment WireAssignment circuit gkrtypes.Circuit - workers *utils.WorkerPool maxNbIn int // maximum number of inputs for a gate in the circuit printsByInstance map[uint32][]gkrinfo.PrintInfo } -func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { +type newSolvingDataSettings struct { + assignment gkrtypes.WireAssignment +} + +type newSolvingDataOption func(*newSolvingDataSettings) + +func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { + return func(s *newSolvingDataSettings) { + s.assignment = assignment + } +} + +func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { + var s newSolvingDataSettings + for _, opt := range options { + opt(&s) + } + d := SolvingData{ - workers: utils.NewWorkerPool(), circuit: info.Circuit, assignment: make(WireAssignment, len(info.Circuit)), printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), } - d.circuit.SetNbUniqueOutputs() d.maxNbIn = d.circuit.MaxGateNbIn() d.assignment = make(WireAssignment, len(d.circuit)) @@ -45,6 +58,22 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { d.assignment[i] = make([]fr.Element, info.NbInstances) } + if s.assignment != nil { + if len(s.assignment) != len(d.assignment) { + panic(fmt.Errorf("provided assignment has %d wires, expected %d", len(s.assignment), len(d.assignment))) + } + for i := range d.assignment { + if len(s.assignment[i]) != info.NbInstances { + panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) + } + for j := range d.assignment[i] { + if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { + panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) + } + } + } + } + return &d } @@ -53,7 +82,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { // TODO use idiomatic printf tag + if !ins[0].IsUint64() { return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) } @@ -108,7 +137,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { hsh := hash.NewHash(hashName + "_BLS12_381") - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...), WithWorkers(data.workers)) + proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) if err != nil { return err } diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 7d89baf7ef..f182c9176b 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -739,7 +739,7 @@ func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.V func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { var prod fr.Element - prod.Add(cast(b), cast(c)) + prod.Mul(cast(b), cast(c)) res := cast(a) res.Add(res, &prod) return &res diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index 49317d446c..c67b58605b 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -10,7 +10,6 @@ import ( "math/big" "github.com/consensys/gnark-crypto/hash" - "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrinfo" @@ -24,20 +23,34 @@ import ( type SolvingData struct { assignment WireAssignment circuit gkrtypes.Circuit - workers *utils.WorkerPool maxNbIn int // maximum number of inputs for a gate in the circuit printsByInstance map[uint32][]gkrinfo.PrintInfo } -func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { +type newSolvingDataSettings struct { + assignment gkrtypes.WireAssignment +} + +type newSolvingDataOption func(*newSolvingDataSettings) + +func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { + return func(s *newSolvingDataSettings) { + s.assignment = assignment + } +} + +func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { + var s newSolvingDataSettings + for _, opt := range options { + opt(&s) + } + d := SolvingData{ - workers: utils.NewWorkerPool(), circuit: info.Circuit, assignment: make(WireAssignment, len(info.Circuit)), printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), } - d.circuit.SetNbUniqueOutputs() d.maxNbIn = d.circuit.MaxGateNbIn() d.assignment = make(WireAssignment, len(d.circuit)) @@ -45,6 +58,22 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { d.assignment[i] = make([]fr.Element, info.NbInstances) } + if s.assignment != nil { + if len(s.assignment) != len(d.assignment) { + panic(fmt.Errorf("provided assignment has %d wires, expected %d", len(s.assignment), len(d.assignment))) + } + for i := range d.assignment { + if len(s.assignment[i]) != info.NbInstances { + panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) + } + for j := range d.assignment[i] { + if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { + panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) + } + } + } + } + return &d } @@ -53,7 +82,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { // TODO use idiomatic printf tag + if !ins[0].IsUint64() { return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) } @@ -108,7 +137,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { hsh := hash.NewHash(hashName + "_BLS24_315") - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...), WithWorkers(data.workers)) + proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) if err != nil { return err } diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index fc9908b918..a284f14ae9 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -739,7 +739,7 @@ func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.V func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { var prod fr.Element - prod.Add(cast(b), cast(c)) + prod.Mul(cast(b), cast(c)) res := cast(a) res.Add(res, &prod) return &res diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index c02c8ab135..9482066d24 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -10,7 +10,6 @@ import ( "math/big" "github.com/consensys/gnark-crypto/hash" - "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrinfo" @@ -24,20 +23,34 @@ import ( type SolvingData struct { assignment WireAssignment circuit gkrtypes.Circuit - workers *utils.WorkerPool maxNbIn int // maximum number of inputs for a gate in the circuit printsByInstance map[uint32][]gkrinfo.PrintInfo } -func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { +type newSolvingDataSettings struct { + assignment gkrtypes.WireAssignment +} + +type newSolvingDataOption func(*newSolvingDataSettings) + +func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { + return func(s *newSolvingDataSettings) { + s.assignment = assignment + } +} + +func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { + var s newSolvingDataSettings + for _, opt := range options { + opt(&s) + } + d := SolvingData{ - workers: utils.NewWorkerPool(), circuit: info.Circuit, assignment: make(WireAssignment, len(info.Circuit)), printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), } - d.circuit.SetNbUniqueOutputs() d.maxNbIn = d.circuit.MaxGateNbIn() d.assignment = make(WireAssignment, len(d.circuit)) @@ -45,6 +58,22 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { d.assignment[i] = make([]fr.Element, info.NbInstances) } + if s.assignment != nil { + if len(s.assignment) != len(d.assignment) { + panic(fmt.Errorf("provided assignment has %d wires, expected %d", len(s.assignment), len(d.assignment))) + } + for i := range d.assignment { + if len(s.assignment[i]) != info.NbInstances { + panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) + } + for j := range d.assignment[i] { + if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { + panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) + } + } + } + } + return &d } @@ -53,7 +82,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { // TODO use idiomatic printf tag + if !ins[0].IsUint64() { return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) } @@ -108,7 +137,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { hsh := hash.NewHash(hashName + "_BLS24_317") - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...), WithWorkers(data.workers)) + proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) if err != nil { return err } diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 04cf3512af..14269151b3 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -739,7 +739,7 @@ func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.V func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { var prod fr.Element - prod.Add(cast(b), cast(c)) + prod.Mul(cast(b), cast(c)) res := cast(a) res.Add(res, &prod) return &res diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index 88d521f531..53079dfd57 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -51,9 +51,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), } - d.circuit.SetNbUniqueOutputs() d.maxNbIn = d.circuit.MaxGateNbIn() + d.assignment = make(WireAssignment, len(d.circuit)) for i := range d.assignment { d.assignment[i] = make([]fr.Element, info.NbInstances) } @@ -82,7 +82,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { // TODO use idiomatic printf tag + if !ins[0].IsUint64() { return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) } diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index cc1245e726..ec1067f736 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -739,7 +739,7 @@ func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.V func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { var prod fr.Element - prod.Add(cast(b), cast(c)) + prod.Mul(cast(b), cast(c)) res := cast(a) res.Add(res, &prod) return &res diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index 21bc7961a5..0351a8cbe3 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -10,7 +10,6 @@ import ( "math/big" "github.com/consensys/gnark-crypto/hash" - "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrinfo" @@ -24,20 +23,34 @@ import ( type SolvingData struct { assignment WireAssignment circuit gkrtypes.Circuit - workers *utils.WorkerPool maxNbIn int // maximum number of inputs for a gate in the circuit printsByInstance map[uint32][]gkrinfo.PrintInfo } -func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { +type newSolvingDataSettings struct { + assignment gkrtypes.WireAssignment +} + +type newSolvingDataOption func(*newSolvingDataSettings) + +func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { + return func(s *newSolvingDataSettings) { + s.assignment = assignment + } +} + +func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { + var s newSolvingDataSettings + for _, opt := range options { + opt(&s) + } + d := SolvingData{ - workers: utils.NewWorkerPool(), circuit: info.Circuit, assignment: make(WireAssignment, len(info.Circuit)), printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), } - d.circuit.SetNbUniqueOutputs() d.maxNbIn = d.circuit.MaxGateNbIn() d.assignment = make(WireAssignment, len(d.circuit)) @@ -45,6 +58,22 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { d.assignment[i] = make([]fr.Element, info.NbInstances) } + if s.assignment != nil { + if len(s.assignment) != len(d.assignment) { + panic(fmt.Errorf("provided assignment has %d wires, expected %d", len(s.assignment), len(d.assignment))) + } + for i := range d.assignment { + if len(s.assignment[i]) != info.NbInstances { + panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) + } + for j := range d.assignment[i] { + if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { + panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) + } + } + } + } + return &d } @@ -53,7 +82,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { // TODO use idiomatic printf tag + if !ins[0].IsUint64() { return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) } @@ -108,7 +137,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { hsh := hash.NewHash(hashName + "_BW6_633") - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...), WithWorkers(data.workers)) + proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) if err != nil { return err } diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index f90f28114b..ad5197feef 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -739,7 +739,7 @@ func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.V func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { var prod fr.Element - prod.Add(cast(b), cast(c)) + prod.Mul(cast(b), cast(c)) res := cast(a) res.Add(res, &prod) return &res diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index dacd7eb35f..190d43caf2 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -10,7 +10,6 @@ import ( "math/big" "github.com/consensys/gnark-crypto/hash" - "github.com/consensys/gnark-crypto/utils" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrinfo" @@ -24,20 +23,34 @@ import ( type SolvingData struct { assignment WireAssignment circuit gkrtypes.Circuit - workers *utils.WorkerPool maxNbIn int // maximum number of inputs for a gate in the circuit printsByInstance map[uint32][]gkrinfo.PrintInfo } -func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { +type newSolvingDataSettings struct { + assignment gkrtypes.WireAssignment +} + +type newSolvingDataOption func(*newSolvingDataSettings) + +func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { + return func(s *newSolvingDataSettings) { + s.assignment = assignment + } +} + +func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { + var s newSolvingDataSettings + for _, opt := range options { + opt(&s) + } + d := SolvingData{ - workers: utils.NewWorkerPool(), circuit: info.Circuit, assignment: make(WireAssignment, len(info.Circuit)), printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), } - d.circuit.SetNbUniqueOutputs() d.maxNbIn = d.circuit.MaxGateNbIn() d.assignment = make(WireAssignment, len(d.circuit)) @@ -45,6 +58,22 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { d.assignment[i] = make([]fr.Element, info.NbInstances) } + if s.assignment != nil { + if len(s.assignment) != len(d.assignment) { + panic(fmt.Errorf("provided assignment has %d wires, expected %d", len(s.assignment), len(d.assignment))) + } + for i := range d.assignment { + if len(s.assignment[i]) != info.NbInstances { + panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) + } + for j := range d.assignment[i] { + if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { + panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) + } + } + } + } + return &d } @@ -53,7 +82,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo) *SolvingData { func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { // TODO use idiomatic printf tag + if !ins[0].IsUint64() { return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) } @@ -108,7 +137,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { hsh := hash.NewHash(hashName + "_BW6_761") - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...), WithWorkers(data.workers)) + proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) if err != nil { return err } diff --git a/internal/gkr/engine_hints.go b/internal/gkr/engine_hints.go index dd6408c718..9ee1635a45 100644 --- a/internal/gkr/engine_hints.go +++ b/internal/gkr/engine_hints.go @@ -34,9 +34,10 @@ func NewTestEngineHints(info *gkrinfo.StoringInfo) (*TestEngineHints, error) { } return &TestEngineHints{ - info: info, - circuit: circuit, - gateIns: make([]frontend.Variable, circuit.MaxGateNbIn()), + info: info, + circuit: circuit, + gateIns: make([]frontend.Variable, circuit.MaxGateNbIn()), + assignment: make(gkrtypes.WireAssignment, len(circuit)), }, err } @@ -47,9 +48,10 @@ func (h *TestEngineHints) Solve(mod *big.Int, ins []*big.Int, outs []*big.Int) e // TODO handle prints - if in0 := ins[0].Uint64(); !ins[0].IsUint64() || in0 >= uint64(len(h.info.Circuit)) || in0 > 0xffffffff { + instanceI := len(h.assignment[0]) + if in0 := ins[0].Uint64(); !ins[0].IsUint64() || in0 > 0xffffffff { return errors.New("first input must be a uint32 instance index") - } else if in0 != uint64(h.info.NbInstances) || h.info.NbInstances != len(h.assignment[0]) { + } else if in0 != uint64(instanceI) || h.info.NbInstances-1 != instanceI { return errors.New("first input must equal the number of instances, and calls to Solve must be done in order of instance index") } @@ -65,7 +67,7 @@ func (h *TestEngineHints) Solve(mod *big.Int, ins []*big.Int, outs []*big.Int) e inI++ } else { for gateInI, inWI := range w.Inputs { - h.gateIns[gateInI] = h.assignment[inWI][gateInI] + h.gateIns[gateInI] = h.assignment[inWI][instanceI] } val = w.Gate.Evaluate(api, h.gateIns[:len(w.Inputs)]...) } diff --git a/internal/gkr/gkrinfo/info.go b/internal/gkr/gkrinfo/info.go index f7cb5e589d..c8629f1db1 100644 --- a/internal/gkr/gkrinfo/info.go +++ b/internal/gkr/gkrinfo/info.go @@ -13,9 +13,8 @@ type ( } Wire struct { - Gate string - Inputs []int - NbUniqueOutputs int + Gate string + Inputs []int } Circuit []Wire @@ -46,10 +45,6 @@ func (w Wire) IsInput() bool { return len(w.Inputs) == 0 } -func (w Wire) IsOutput() bool { - return w.NbUniqueOutputs == 0 -} - func (d *StoringInfo) NewInputVariable() int { i := len(d.Circuit) d.Circuit = append(d.Circuit, Wire{}) diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 12cdabf3d9..1a07cba46c 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -181,7 +181,7 @@ func (c Circuit) OutputsList() [][]int { return res } -func (c Circuit) SetNbUniqueOutputs() { +func (c Circuit) setNbUniqueOutputs() { for i := range c { c[i].NbUniqueOutputs = 0 @@ -237,6 +237,7 @@ func CircuitInfoToCircuit(info gkrinfo.Circuit, gateGetter func(name gkr.GateNam return nil, fmt.Errorf("gate \"%s\" not found", info[i].Gate) } } + resCircuit.setNbUniqueOutputs() return resCircuit, nil } diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index e8e78f4b96..cdf62359f2 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -739,7 +739,7 @@ func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.V func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { var prod small_rational.SmallRational - prod.Add(cast(b), cast(c)) + prod.Mul(cast(b), cast(c)) res := cast(a) res.Add(res, &prod) return &res diff --git a/std/gkrapi/api_test.go b/std/gkrapi/api_test.go index 5cd9163ed8..ba8e595527 100644 --- a/std/gkrapi/api_test.go +++ b/std/gkrapi/api_test.go @@ -131,7 +131,7 @@ func (c *mulNoDependencyCircuit) Define(api frontend.API) error { gkrApi := New() x := gkrApi.NewInput() y := gkrApi.NewInput() - z := gkrApi.Add(x, y) + z := gkrApi.Mul(x, y) gkrCircuit := gkrApi.Compile(api, c.hashName) @@ -205,8 +205,8 @@ func (c *mulWithDependencyCircuit) Define(api frontend.API) error { return fmt.Errorf("failed to add instance: %w", err) } + api.AssertIsEqual(instanceOut[z], api.Mul(state, c.Y[i])) state = instanceOut[z] // update state for the next iteration - api.AssertIsEqual(state, api.Mul(state, c.Y[i])) } return nil } From d6e382f500c19582bd858d59fe07451fa4af371e Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 3 Jun 2025 14:16:54 -0500 Subject: [PATCH 016/251] refactor: remove println --- internal/gkr/bls12-381/solver_hints.go | 26 ++++----------------- internal/gkr/gkrinfo/info.go | 9 -------- std/gkrapi/api.go | 32 -------------------------- 3 files changed, 5 insertions(+), 62 deletions(-) diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index 2c6db19a72..50d1c2e6c3 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -21,10 +20,9 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - printsByInstance map[uint32][]gkrinfo.PrintInfo + assignment WireAssignment + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit } type newSolvingDataSettings struct { @@ -46,9 +44,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) } d := SolvingData{ - circuit: info.Circuit, - assignment: make(WireAssignment, len(info.Circuit)), - printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -109,19 +106,6 @@ func SolveHint(data *SolvingData) hint.Hint { } } - prints := data.printsByInstance[uint32(instanceI)] - delete(data.printsByInstance, uint32(instanceI)) - for _, p := range prints { - serializable := make([]any, len(p.Values)) - for i, v := range p.Values { - if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 - serializable[i] = data.assignment[algo_utils.ForceUint32(v)][p.Instance].String() - } else { - serializable[i] = v - } - } - fmt.Println(serializable...) - } return nil } } diff --git a/internal/gkr/gkrinfo/info.go b/internal/gkr/gkrinfo/info.go index c8629f1db1..690908ea35 100644 --- a/internal/gkr/gkrinfo/info.go +++ b/internal/gkr/gkrinfo/info.go @@ -59,12 +59,3 @@ func (d *StoringInfo) Is() bool { type ConstraintSystem interface { SetGkrInfo(info StoringInfo) error } - -// NewPrintInfoMap partitions printInfo into map elements, indexed by instance -func NewPrintInfoMap(printInfo []PrintInfo) map[uint32][]PrintInfo { - res := make(map[uint32][]PrintInfo) - for i := range printInfo { - res[printInfo[i].Instance] = append(res[printInfo[i].Instance], printInfo[i]) - } - return res -} diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index 6c6d4b0976..ae3c2b7954 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -58,35 +58,3 @@ func (api *API) Sub(i1, i2 gkr.Variable) gkr.Variable { func (api *API) Mul(i1, i2 gkr.Variable) gkr.Variable { return api.namedGate2PlusIn(gkr.Mul2, i1, i2) } - -// Println writes to the standard output. -// instance determines which values are chosen for gkr.Variable input. -func (api *API) Println(instance int, a ...any) { - api.toStore.Prints = append(api.toStore.Prints, newPrint(instance, a...)) -} - -// Println writes to the standard output. -// instance determines which values are chosen for gkr.Variable input. -func (c *Circuit) Println(instance int, a ...any) { - c.toStore.Prints = append(c.toStore.Prints, newPrint(instance, a...)) -} - -func newPrint(instance int, a ...any) gkrinfo.PrintInfo { - isVar := make([]bool, len(a)) - vals := make([]any, len(a)) - for i := range a { - v, ok := a[i].(gkr.Variable) - isVar[i] = ok - if ok { - vals[i] = uint32(v) - } else { - vals[i] = a[i] - } - } - - return gkrinfo.PrintInfo{ - Values: vals, - Instance: uint32(instance), - IsGkrVar: isVar, - } -} From a8f30a43d42a90831aa3d03bdee4ad3f2a87777b Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 3 Jun 2025 14:18:11 -0500 Subject: [PATCH 017/251] chore: generify print removal --- .../backend/template/gkr/solver_hints.go.tmpl | 16 ------------ internal/gkr/bls12-377/solver_hints.go | 26 ++++--------------- internal/gkr/bls24-315/solver_hints.go | 26 ++++--------------- internal/gkr/bls24-317/solver_hints.go | 26 ++++--------------- internal/gkr/bn254/solver_hints.go | 26 ++++--------------- internal/gkr/bw6-633/solver_hints.go | 26 ++++--------------- internal/gkr/bw6-761/solver_hints.go | 26 ++++--------------- 7 files changed, 30 insertions(+), 142 deletions(-) diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index d8ff837ced..7892021532 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -5,7 +5,6 @@ import ( "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -17,7 +16,6 @@ type SolvingData struct { assignment WireAssignment circuit gkrtypes.Circuit maxNbIn int // maximum number of inputs for a gate in the circuit - printsByInstance map[uint32][]gkrinfo.PrintInfo } type newSolvingDataSettings struct { @@ -41,7 +39,6 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) d := SolvingData{ circuit: info.Circuit, assignment: make(WireAssignment, len(info.Circuit)), - printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -103,19 +100,6 @@ func SolveHint(data *SolvingData) hint.Hint { } } - prints := data.printsByInstance[uint32(instanceI)] - delete(data.printsByInstance, uint32(instanceI)) - for _, p := range prints { - serializable := make([]any, len(p.Values)) - for i, v := range p.Values { - if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 - serializable[i] = data.assignment[algo_utils.ForceUint32(v)][p.Instance].String() - } else { - serializable[i] = v - } - } - fmt.Println(serializable...) - } return nil } } diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index d03272551d..e018ce2726 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -21,10 +20,9 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - printsByInstance map[uint32][]gkrinfo.PrintInfo + assignment WireAssignment + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit } type newSolvingDataSettings struct { @@ -46,9 +44,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) } d := SolvingData{ - circuit: info.Circuit, - assignment: make(WireAssignment, len(info.Circuit)), - printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -109,19 +106,6 @@ func SolveHint(data *SolvingData) hint.Hint { } } - prints := data.printsByInstance[uint32(instanceI)] - delete(data.printsByInstance, uint32(instanceI)) - for _, p := range prints { - serializable := make([]any, len(p.Values)) - for i, v := range p.Values { - if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 - serializable[i] = data.assignment[algo_utils.ForceUint32(v)][p.Instance].String() - } else { - serializable[i] = v - } - } - fmt.Println(serializable...) - } return nil } } diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index c67b58605b..285ca7b9f9 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -21,10 +20,9 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - printsByInstance map[uint32][]gkrinfo.PrintInfo + assignment WireAssignment + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit } type newSolvingDataSettings struct { @@ -46,9 +44,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) } d := SolvingData{ - circuit: info.Circuit, - assignment: make(WireAssignment, len(info.Circuit)), - printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -109,19 +106,6 @@ func SolveHint(data *SolvingData) hint.Hint { } } - prints := data.printsByInstance[uint32(instanceI)] - delete(data.printsByInstance, uint32(instanceI)) - for _, p := range prints { - serializable := make([]any, len(p.Values)) - for i, v := range p.Values { - if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 - serializable[i] = data.assignment[algo_utils.ForceUint32(v)][p.Instance].String() - } else { - serializable[i] = v - } - } - fmt.Println(serializable...) - } return nil } } diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index 9482066d24..b6e9047533 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -21,10 +20,9 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - printsByInstance map[uint32][]gkrinfo.PrintInfo + assignment WireAssignment + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit } type newSolvingDataSettings struct { @@ -46,9 +44,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) } d := SolvingData{ - circuit: info.Circuit, - assignment: make(WireAssignment, len(info.Circuit)), - printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -109,19 +106,6 @@ func SolveHint(data *SolvingData) hint.Hint { } } - prints := data.printsByInstance[uint32(instanceI)] - delete(data.printsByInstance, uint32(instanceI)) - for _, p := range prints { - serializable := make([]any, len(p.Values)) - for i, v := range p.Values { - if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 - serializable[i] = data.assignment[algo_utils.ForceUint32(v)][p.Instance].String() - } else { - serializable[i] = v - } - } - fmt.Println(serializable...) - } return nil } } diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index 53079dfd57..50d7c11364 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -21,10 +20,9 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - printsByInstance map[uint32][]gkrinfo.PrintInfo + assignment WireAssignment + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit } type newSolvingDataSettings struct { @@ -46,9 +44,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) } d := SolvingData{ - circuit: info.Circuit, - assignment: make(WireAssignment, len(info.Circuit)), - printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -109,19 +106,6 @@ func SolveHint(data *SolvingData) hint.Hint { } } - prints := data.printsByInstance[uint32(instanceI)] - delete(data.printsByInstance, uint32(instanceI)) - for _, p := range prints { - serializable := make([]any, len(p.Values)) - for i, v := range p.Values { - if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 - serializable[i] = data.assignment[algo_utils.ForceUint32(v)][p.Instance].String() - } else { - serializable[i] = v - } - } - fmt.Println(serializable...) - } return nil } } diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index 0351a8cbe3..65813d9ac0 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -21,10 +20,9 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - printsByInstance map[uint32][]gkrinfo.PrintInfo + assignment WireAssignment + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit } type newSolvingDataSettings struct { @@ -46,9 +44,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) } d := SolvingData{ - circuit: info.Circuit, - assignment: make(WireAssignment, len(info.Circuit)), - printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -109,19 +106,6 @@ func SolveHint(data *SolvingData) hint.Hint { } } - prints := data.printsByInstance[uint32(instanceI)] - delete(data.printsByInstance, uint32(instanceI)) - for _, p := range prints { - serializable := make([]any, len(p.Values)) - for i, v := range p.Values { - if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 - serializable[i] = data.assignment[algo_utils.ForceUint32(v)][p.Instance].String() - } else { - serializable[i] = v - } - } - fmt.Println(serializable...) - } return nil } } diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index 190d43caf2..7971b5540a 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" algo_utils "github.com/consensys/gnark/internal/utils" @@ -21,10 +20,9 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - printsByInstance map[uint32][]gkrinfo.PrintInfo + assignment WireAssignment + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit } type newSolvingDataSettings struct { @@ -46,9 +44,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) } d := SolvingData{ - circuit: info.Circuit, - assignment: make(WireAssignment, len(info.Circuit)), - printsByInstance: gkrinfo.NewPrintInfoMap(info.Prints), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -109,19 +106,6 @@ func SolveHint(data *SolvingData) hint.Hint { } } - prints := data.printsByInstance[uint32(instanceI)] - delete(data.printsByInstance, uint32(instanceI)) - for _, p := range prints { - serializable := make([]any, len(p.Values)) - for i, v := range p.Values { - if p.IsGkrVar[i] { // serializer stores uint32 in slices as uint64 - serializable[i] = data.assignment[algo_utils.ForceUint32(v)][p.Instance].String() - } else { - serializable[i] = v - } - } - fmt.Println(serializable...) - } return nil } } From 8ac5f9f2b9ef5b81530377995fdf70c8e9bdde15 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 3 Jun 2025 14:49:22 -0500 Subject: [PATCH 018/251] feat: GetValue --- constraint/bls12-377/solver.go | 1 + constraint/bls12-381/solver.go | 1 + constraint/bls24-315/solver.go | 1 + constraint/bls24-317/solver.go | 1 + constraint/bn254/solver.go | 1 + constraint/bw6-633/solver.go | 1 + constraint/bw6-761/solver.go | 1 + .../backend/template/gkr/solver_hints.go.tmpl | 17 ++++ .../template/representations/solver.go.tmpl | 1 + internal/gkr/bls12-377/solver_hints.go | 17 ++++ internal/gkr/bls12-381/solver_hints.go | 17 ++++ internal/gkr/bls24-315/solver_hints.go | 17 ++++ internal/gkr/bls24-317/solver_hints.go | 17 ++++ internal/gkr/bn254/solver_hints.go | 17 ++++ internal/gkr/bw6-633/solver_hints.go | 17 ++++ internal/gkr/bw6-761/solver_hints.go | 17 ++++ internal/gkr/engine_hints.go | 11 +++ internal/gkr/gkrinfo/info.go | 17 ++-- internal/gkr/gkrtypes/types.go | 2 - std/gkrapi/api_test.go | 88 +++++++++---------- std/gkrapi/compile.go | 12 +++ 21 files changed, 216 insertions(+), 58 deletions(-) diff --git a/constraint/bls12-377/solver.go b/constraint/bls12-377/solver.go index 206fea5702..f57abfae52 100644 --- a/constraint/bls12-377/solver.go +++ b/constraint/bls12-377/solver.go @@ -57,6 +57,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, } gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, + csolver.OverrideHint(cs.GkrInfo.GetAssignmentHintID, gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } diff --git a/constraint/bls12-381/solver.go b/constraint/bls12-381/solver.go index 4e7835fef1..67f12ef2aa 100644 --- a/constraint/bls12-381/solver.go +++ b/constraint/bls12-381/solver.go @@ -57,6 +57,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, } gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, + csolver.OverrideHint(cs.GkrInfo.GetAssignmentHintID, gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } diff --git a/constraint/bls24-315/solver.go b/constraint/bls24-315/solver.go index 5dc3fc2ef7..05d4c6f11c 100644 --- a/constraint/bls24-315/solver.go +++ b/constraint/bls24-315/solver.go @@ -57,6 +57,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, } gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, + csolver.OverrideHint(cs.GkrInfo.GetAssignmentHintID, gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } diff --git a/constraint/bls24-317/solver.go b/constraint/bls24-317/solver.go index f007d16494..29af5c28b2 100644 --- a/constraint/bls24-317/solver.go +++ b/constraint/bls24-317/solver.go @@ -57,6 +57,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, } gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, + csolver.OverrideHint(cs.GkrInfo.GetAssignmentHintID, gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } diff --git a/constraint/bn254/solver.go b/constraint/bn254/solver.go index a7674e9542..5e9b70c548 100644 --- a/constraint/bn254/solver.go +++ b/constraint/bn254/solver.go @@ -57,6 +57,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, } gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, + csolver.OverrideHint(cs.GkrInfo.GetAssignmentHintID, gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } diff --git a/constraint/bw6-633/solver.go b/constraint/bw6-633/solver.go index f294f7d826..7fc43652f6 100644 --- a/constraint/bw6-633/solver.go +++ b/constraint/bw6-633/solver.go @@ -57,6 +57,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, } gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, + csolver.OverrideHint(cs.GkrInfo.GetAssignmentHintID, gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } diff --git a/constraint/bw6-761/solver.go b/constraint/bw6-761/solver.go index e10f2f21d7..d226b03a53 100644 --- a/constraint/bw6-761/solver.go +++ b/constraint/bw6-761/solver.go @@ -57,6 +57,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, } gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, + csolver.OverrideHint(cs.GkrInfo.GetAssignmentHintID, gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index 7892021532..cd89a9ffff 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -70,6 +70,23 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies +func GetAssignmentHint(data *SolvingData) hint.Hint { + return func(_ *big.Int, ins, outs []*big.Int) error { + if len(ins) != 3 { + return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + } + wireI := ins[0].Uint64() + instanceI := ins[1].Uint64() + if !ins[0].IsUint64() || !ins[1].IsUint64() { + return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) + } + + data.assignment[wireI][instanceI].BigInt(outs[0]) + + return nil + } +} + func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() diff --git a/internal/generator/backend/template/representations/solver.go.tmpl b/internal/generator/backend/template/representations/solver.go.tmpl index 202642b87a..ddb0b7428c 100644 --- a/internal/generator/backend/template/representations/solver.go.tmpl +++ b/internal/generator/backend/template/representations/solver.go.tmpl @@ -49,6 +49,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, } gkrData := gkr.NewSolvingData(solvingInfo) opts = append(opts, + csolver.OverrideHint(cs.GkrInfo.GetAssignmentHintID, gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.SolveHintID, gkr.SolveHint(gkrData)), csolver.OverrideHint(cs.GkrInfo.ProveHintID, gkr.ProveHint(cs.GkrInfo.HashName, gkrData))) } diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index e018ce2726..43384769ac 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -76,6 +76,23 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies +func GetAssignmentHint(data *SolvingData) hint.Hint { + return func(_ *big.Int, ins, outs []*big.Int) error { + if len(ins) != 3 { + return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + } + wireI := ins[0].Uint64() + instanceI := ins[1].Uint64() + if !ins[0].IsUint64() || !ins[1].IsUint64() { + return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) + } + + data.assignment[wireI][instanceI].BigInt(outs[0]) + + return nil + } +} + func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index 50d1c2e6c3..bcf449e99c 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -76,6 +76,23 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies +func GetAssignmentHint(data *SolvingData) hint.Hint { + return func(_ *big.Int, ins, outs []*big.Int) error { + if len(ins) != 3 { + return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + } + wireI := ins[0].Uint64() + instanceI := ins[1].Uint64() + if !ins[0].IsUint64() || !ins[1].IsUint64() { + return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) + } + + data.assignment[wireI][instanceI].BigInt(outs[0]) + + return nil + } +} + func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index 285ca7b9f9..6f254299a0 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -76,6 +76,23 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies +func GetAssignmentHint(data *SolvingData) hint.Hint { + return func(_ *big.Int, ins, outs []*big.Int) error { + if len(ins) != 3 { + return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + } + wireI := ins[0].Uint64() + instanceI := ins[1].Uint64() + if !ins[0].IsUint64() || !ins[1].IsUint64() { + return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) + } + + data.assignment[wireI][instanceI].BigInt(outs[0]) + + return nil + } +} + func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index b6e9047533..6121504a68 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -76,6 +76,23 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies +func GetAssignmentHint(data *SolvingData) hint.Hint { + return func(_ *big.Int, ins, outs []*big.Int) error { + if len(ins) != 3 { + return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + } + wireI := ins[0].Uint64() + instanceI := ins[1].Uint64() + if !ins[0].IsUint64() || !ins[1].IsUint64() { + return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) + } + + data.assignment[wireI][instanceI].BigInt(outs[0]) + + return nil + } +} + func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index 50d7c11364..6bd76425a1 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -76,6 +76,23 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies +func GetAssignmentHint(data *SolvingData) hint.Hint { + return func(_ *big.Int, ins, outs []*big.Int) error { + if len(ins) != 3 { + return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + } + wireI := ins[0].Uint64() + instanceI := ins[1].Uint64() + if !ins[0].IsUint64() || !ins[1].IsUint64() { + return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) + } + + data.assignment[wireI][instanceI].BigInt(outs[0]) + + return nil + } +} + func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index 65813d9ac0..3678eab8bf 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -76,6 +76,23 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies +func GetAssignmentHint(data *SolvingData) hint.Hint { + return func(_ *big.Int, ins, outs []*big.Int) error { + if len(ins) != 3 { + return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + } + wireI := ins[0].Uint64() + instanceI := ins[1].Uint64() + if !ins[0].IsUint64() || !ins[1].IsUint64() { + return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) + } + + data.assignment[wireI][instanceI].BigInt(outs[0]) + + return nil + } +} + func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index 7971b5540a..baa1303132 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -76,6 +76,23 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies +func GetAssignmentHint(data *SolvingData) hint.Hint { + return func(_ *big.Int, ins, outs []*big.Int) error { + if len(ins) != 3 { + return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + } + wireI := ins[0].Uint64() + instanceI := ins[1].Uint64() + if !ins[0].IsUint64() || !ins[1].IsUint64() { + return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) + } + + data.assignment[wireI][instanceI].BigInt(outs[0]) + + return nil + } +} + func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() diff --git a/internal/gkr/engine_hints.go b/internal/gkr/engine_hints.go index 9ee1635a45..3f5884bc13 100644 --- a/internal/gkr/engine_hints.go +++ b/internal/gkr/engine_hints.go @@ -118,7 +118,18 @@ func (h *TestEngineHints) Prove(mod *big.Int, ins, outs []*big.Int) error { } return errors.New("unsupported modulus") +} +// GetAssignment returns the assignment for a particular wire and instance. +func (h *TestEngineHints) GetAssignment(_ *big.Int, ins []*big.Int, outs []*big.Int) error { + if len(ins) != 3 || !ins[0].IsUint64() || !ins[1].IsUint64() { + return errors.New("expected 3 inputs: wire index, instance index, and dummy output from the same instance") + } + if len(outs) != 1 { + return errors.New("expected 1 output: the value of the wire at the given instance") + } + *outs[0] = utils.FromInterface(h.assignment[ins[0].Uint64()][ins[1].Uint64()]) + return nil } type gateAPI struct{ *big.Int } diff --git a/internal/gkr/gkrinfo/info.go b/internal/gkr/gkrinfo/info.go index 690908ea35..6d14e37dfb 100644 --- a/internal/gkr/gkrinfo/info.go +++ b/internal/gkr/gkrinfo/info.go @@ -19,18 +19,13 @@ type ( Circuit []Wire - PrintInfo struct { - Values []any - Instance uint32 - IsGkrVar []bool - } StoringInfo struct { - Circuit Circuit - NbInstances int - HashName string - SolveHintID solver.HintID - ProveHintID solver.HintID - Prints []PrintInfo + Circuit Circuit + NbInstances int + HashName string + GetAssignmentHintID solver.HintID + SolveHintID solver.HintID + ProveHintID solver.HintID } Permutations struct { diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 1a07cba46c..3226f1aa2f 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -150,7 +150,6 @@ type SolvingInfo struct { Circuit Circuit NbInstances int HashName string - Prints []gkrinfo.PrintInfo } // OutputsList for each wire, returns the set of indexes of wires it is input to. @@ -247,7 +246,6 @@ func StoringToSolvingInfo(info gkrinfo.StoringInfo, gateGetter func(name gkr.Gat Circuit: circuit, NbInstances: info.NbInstances, HashName: info.HashName, - Prints: info.Prints, }, err } diff --git a/std/gkrapi/api_test.go b/std/gkrapi/api_test.go index ba8e595527..3b2be818bd 100644 --- a/std/gkrapi/api_test.go +++ b/std/gkrapi/api_test.go @@ -1,7 +1,6 @@ package gkrapi import ( - "bytes" "fmt" "hash" "math/big" @@ -26,7 +25,7 @@ import ( "github.com/stretchr/testify/require" ) -// compressThreshold --> if linear expressions are larger than this, the frontend will introduce +// compressThreshold → if linear expressions are larger than this, the frontend will introduce // intermediate constraints. The lower this number is, the faster compile time should be (to a point) // but resulting circuit will have more constraints (slower proving time). const compressThreshold = 1000 @@ -358,7 +357,7 @@ func mimcGate(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { } sum := api.Add(input[0], input[1] /*, m.Ark*/) - sumCubed := api.Mul(sum, sum, sum) // sum^3 + sumCubed := api.Mul(sum, sum, sum) // sum³ return api.Mul(sumCubed, sumCubed, sum) } @@ -522,12 +521,6 @@ func mimcNoGkrCircuits(mimcDepth, nbInstances int) (circuit, assignment frontend return } -func panicIfError(err error) { - if err != nil { - panic(err) - } -} - func assertSliceEqual[T comparable](t *testing.T, expected, seen []T) { assert.Equal(t, len(expected), len(seen)) for i := range seen { @@ -549,7 +542,7 @@ func (m MiMCCipherGate) Evaluate(api frontend.API, input ...frontend.Variable) f } sum := api.Add(input[0], input[1], m.Ark) - sumCubed := api.Mul(sum, sum, sum) // sum^3 + sumCubed := api.Mul(sum, sum, sum) // sum³ return api.Mul(sumCubed, sumCubed, sum) } @@ -602,46 +595,51 @@ func init() { } } -func ExamplePrintln() { +// pow3Circuit computes x⁴ and also checks the correctness of intermediate value x². +// This is to demonstrate the use of [Circuit.GetValue] and should not be done +// in production code, as it negates the performance benefits of using GKR in the first place. +type pow4Circuit struct { + X []frontend.Variable +} - circuit := &mulNoDependencyCircuit{ - X: make([]frontend.Variable, 2), - Y: make([]frontend.Variable, 2), - hashName: "MIMC", - } +func (c *pow4Circuit) Define(api frontend.API) error { + gkrApi := New() + x := gkrApi.NewInput() + x2 := gkrApi.Mul(x, x) // x² + x4 := gkrApi.Mul(x2, x2) // x⁴ - assignment := &mulNoDependencyCircuit{ - X: []frontend.Variable{10, 11}, - Y: []frontend.Variable{12, 13}, - } + gkrCircuit := gkrApi.Compile(api, "MIMC") + + for i := range c.X { + instanceIn := make(map[gkr.Variable]frontend.Variable) + instanceIn[x] = c.X[i] + + instanceOut, err := gkrCircuit.AddInstance(instanceIn) + if err != nil { + return fmt.Errorf("failed to add instance: %w", err) + } + + api.AssertIsEqual(gkrCircuit.GetValue(x, i), c.X[i]) // x + + v := api.Mul(c.X[i], c.X[i]) // x² + api.AssertIsEqual(gkrCircuit.GetValue(x2, i), v) // x² - field := ecc.BN254.ScalarField() + v = api.Mul(v, v) // x⁴ + api.AssertIsEqual(gkrCircuit.GetValue(x4, i), v) // x⁴ + api.AssertIsEqual(instanceOut[x4], v) // x⁴ + } - // with test engine - err := test.IsSolved(circuit, assignment, field) - panicIfError(err) + return nil +} - // with groth16 / serialized CS - firstCs, err := frontend.Compile(field, r1cs.NewBuilder, circuit) - panicIfError(err) +func TestPow4Circuit_GetValue(t *testing.T) { + assignment := pow4Circuit{ + X: []frontend.Variable{1, 2, 3, 4, 5}, + } - var bb bytes.Buffer - _, err = firstCs.WriteTo(&bb) - panicIfError(err) - cs := groth16.NewCS(ecc.BN254) - _, err = cs.ReadFrom(&bb) - panicIfError(err) + circuit := pow4Circuit{ + X: make([]frontend.Variable, len(assignment.X)), + } - pk, _, err := groth16.Setup(cs) - panicIfError(err) - w, err := frontend.NewWitness(assignment, field) - panicIfError(err) - _, err = groth16.Prove(cs, pk, w) - panicIfError(err) - - // Output: - // values of x and y in instance number 0 10 12 - // value of z in instance number 1 143 - // values of x and y in instance number 0 10 12 - // value of z in instance number 1 143 + test.NewAssert(t).CheckCircuit(&circuit, test.WithValidAssignment(&assignment)) } diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 87c863e7ee..4cd6c804e5 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -104,6 +104,7 @@ func (api *API) Compile(parentApi frontend.API, fiatshamirHashName string, optio } res.toStore.ProveHintID = solver.GetHintID(res.hints.Prove) + res.toStore.GetAssignmentHintID = solver.GetHintID(res.hints.GetAssignment) parentApi.Compiler().Defer(res.verify) @@ -257,3 +258,14 @@ func init() { return &h, err }) } + +// GetValue is a debugging utility returning the value of variable v at instance i. +// While v can be an input or output variable, GetValue is most useful for querying intermediate values in the circuit. +func (c *Circuit) GetValue(v gkr.Variable, i int) frontend.Variable { + // last input to ensure the solver's work is done before GetAssignment is called + res, err := c.api.Compiler().NewHint(c.hints.GetAssignment, 1, int(v), i, c.assignments[c.outs[0]][i]) + if err != nil { + panic(err) + } + return res[0] +} From 5bb879dfbce7477b790ad9b6d4fe8b355c8d6641 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 3 Jun 2025 14:51:59 -0500 Subject: [PATCH 019/251] fix all gkrapi tests pass --- internal/gkr/engine_hints.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/gkr/engine_hints.go b/internal/gkr/engine_hints.go index 3f5884bc13..7e60a21547 100644 --- a/internal/gkr/engine_hints.go +++ b/internal/gkr/engine_hints.go @@ -46,8 +46,6 @@ func NewTestEngineHints(info *gkrinfo.StoringInfo) (*TestEngineHints, error) { // The first input is the index of the instance. The rest are the inputs of the circuit, in their nominal order. func (h *TestEngineHints) Solve(mod *big.Int, ins []*big.Int, outs []*big.Int) error { - // TODO handle prints - instanceI := len(h.assignment[0]) if in0 := ins[0].Uint64(); !ins[0].IsUint64() || in0 > 0xffffffff { return errors.New("first input must be a uint32 instance index") @@ -73,6 +71,7 @@ func (h *TestEngineHints) Solve(mod *big.Int, ins []*big.Int, outs []*big.Int) e } if w.IsOutput() { *outs[outI] = utils.FromInterface(val) + outI++ } h.assignment[wI] = append(h.assignment[wI], val) } @@ -80,7 +79,6 @@ func (h *TestEngineHints) Solve(mod *big.Int, ins []*big.Int, outs []*big.Int) e } func (h *TestEngineHints) Prove(mod *big.Int, ins, outs []*big.Int) error { - // todo handle prints info, err := gkrtypes.StoringToSolvingInfo(*h.info, gkrgates.Get) if err != nil { From 7f9a0f1aaec5beb09a6d776afc972acc5d10c088 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 3 Jun 2025 15:24:14 -0500 Subject: [PATCH 020/251] fix gkr-poseidon2 --- .../poseidon2/gkr-poseidon2/gkr.go | 178 +++++------------- .../poseidon2/gkr-poseidon2/gkr_test.go | 4 +- 2 files changed, 52 insertions(+), 130 deletions(-) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr.go b/std/permutation/poseidon2/gkr-poseidon2/gkr.go index 330efdd589..f05836a798 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr.go @@ -1,24 +1,16 @@ package gkr_poseidon2 import ( - "errors" "fmt" - "math/big" "sync" "github.com/consensys/gnark/constraint/solver/gkrgates" - "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi" "github.com/consensys/gnark/std/gkrapi/gkr" - "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark-crypto/ecc" - frBls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" poseidon2Bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/poseidon2" "github.com/consensys/gnark/frontend" - stdHash "github.com/consensys/gnark/std/hash" - "github.com/consensys/gnark/std/hash/mimc" ) // extKeyGate applies the external matrix mul, then adds the round key @@ -117,62 +109,67 @@ func extAddGate(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { } type GkrCompressions struct { - api frontend.API - ins1 []frontend.Variable - ins2 []frontend.Variable - outs []frontend.Variable + api frontend.API + gkrCircuit *gkrapi.Circuit + in1, in2, out gkr.Variable } -// NewGkrCompressions returns an object that can compute the Poseidon2 compression function (currently only for BLS12-377) +// NewGkrCompressor returns an object that can compute the Poseidon2 compression function (currently only for BLS12-377) // which consists of a permutation along with the input fed forward. // The correctness of the compression functions is proven using GKR. -// Note that the solver will need the function RegisterGkrSolverOptions to be called with the desired curves -func NewGkrCompressions(api frontend.API) *GkrCompressions { - res := GkrCompressions{ - api: api, +// Note that the solver will need the function RegisterGkrGates to be called with the desired curves +func NewGkrCompressor(api frontend.API) *GkrCompressions { + if api.Compiler().Field().Cmp(ecc.BLS12_377.ScalarField()) != 0 { + panic("currently only BL12-377 is supported") + } + gkrApi, in1, in2, out, err := defineCircuitBls12377() + if err != nil { + panic(fmt.Errorf("failed to define GKR circuit: %v", err)) + } + return &GkrCompressions{ + api: api, + gkrCircuit: gkrApi.Compile(api, "MIMC"), + in1: in1, + in2: in2, + out: out, } - api.Compiler().Defer(res.finalize) - return &res } func (p *GkrCompressions) Compress(a, b frontend.Variable) frontend.Variable { - s, err := p.api.Compiler().NewHint(permuteHint, 1, a, b) + outs, err := p.gkrCircuit.AddInstance(map[gkr.Variable]frontend.Variable{p.in1: a, p.in2: b}) if err != nil { panic(err) } - p.ins1 = append(p.ins1, a) - p.ins2 = append(p.ins2, b) - p.outs = append(p.outs, s[0]) - return s[0] + + return outs[p.out] } -// defineCircuit defines the GKR circuit for the Poseidon2 permutation over BLS12-377 +// defineCircuitBls12377 defines the GKR circuit for the Poseidon2 permutation over BLS12-377 // insLeft and insRight are the inputs to the permutation // they must be padded to a power of 2 -func defineCircuit(insLeft, insRight []frontend.Variable) (*gkrapi.API, gkr.Variable, error) { +func defineCircuitBls12377() (gkrApi *gkrapi.API, in1, in2, out gkr.Variable, err error) { // variable indexes const ( xI = iota yI ) + if err = registerGatesBls12377(); err != nil { + return + } + // poseidon2 parameters gateNamer := newRoundGateNamer(poseidon2Bls12377.GetDefaultParameters()) rF := poseidon2Bls12377.GetDefaultParameters().NbFullRounds rP := poseidon2Bls12377.GetDefaultParameters().NbPartialRounds halfRf := rF / 2 - gkrApi := gkrapi.New() + gkrApi = gkrapi.New() - x, err := gkrApi.Import(insLeft) - if err != nil { - return nil, -1, err - } - y, err := gkrApi.Import(insRight) - y0 := y // save to feed forward at the end - if err != nil { - return nil, -1, err - } + x := gkrApi.NewInput() + y := gkrApi.NewInput() + + in1, in2 = x, y // save to feed forward at the end // *** helper functions to register and apply gates *** @@ -241,86 +238,9 @@ func defineCircuit(insLeft, insRight []frontend.Variable) (*gkrapi.API, gkr.Vari } // apply the external matrix one last time to obtain the final value of y - y = gkrApi.NamedGate(gateNamer.linear(yI, rP+rF), y, x, y0) + out = gkrApi.NamedGate(gateNamer.linear(yI, rP+rF), y, x, in2) - return gkrApi, y, nil -} - -func (p *GkrCompressions) finalize(api frontend.API) error { - if p.api != api { - panic("unexpected API") - } - - // register MiMC to be used as a random oracle in the GKR proof - stdHash.Register("MIMC", func(api frontend.API) (stdHash.FieldHasher, error) { - m, err := mimc.NewMiMC(api) - return &m, err - }) - - // register gates - registerGkrSolverOptions(api) - - // pad instances into a power of 2 - // TODO @Tabaie the GKR API to do this automatically? - ins1Padded := make([]frontend.Variable, ecc.NextPowerOfTwo(uint64(len(p.ins1)))) - ins2Padded := make([]frontend.Variable, len(ins1Padded)) - copy(ins1Padded, p.ins1) - copy(ins2Padded, p.ins2) - for i := len(p.ins1); i < len(ins1Padded); i++ { - ins1Padded[i] = 0 - ins2Padded[i] = 0 - } - - gkrApi, y, err := defineCircuit(ins1Padded, ins2Padded) - if err != nil { - return err - } - - // connect to output - // TODO can we save 1 constraint per instance by giving the desired outputs to the gkr api? - solution, err := gkrApi.Solve(api) - if err != nil { - return err - } - yVals := solution.Export(y) - for i := range p.outs { - api.AssertIsEqual(yVals[i], p.outs[i]) - } - - // verify GKR proof - allVals := make([]frontend.Variable, 0, 3*len(p.ins1)) - allVals = append(allVals, p.ins1...) - allVals = append(allVals, p.ins2...) - allVals = append(allVals, p.outs...) - challenge, err := p.api.(frontend.Committer).Commit(allVals...) - if err != nil { - return err - } - return solution.Verify("MIMC", challenge) -} - -// registerGkrSolverOptions is a wrapper for RegisterGkrSolverOptions -// that performs the registration for the curve associated with api. -func registerGkrSolverOptions(api frontend.API) { - RegisterGkrSolverOptions(utils.FieldToCurve(api.Compiler().Field())) -} - -func permuteHint(m *big.Int, ins, outs []*big.Int) error { - if m.Cmp(ecc.BLS12_377.ScalarField()) != 0 { - return errors.New("only bls12-377 supported") - } - if len(ins) != 2 || len(outs) != 1 { - return errors.New("expected 2 inputs and 1 output") - } - var x [2]frBls12377.Element - x[0].SetBigInt(ins[0]) - x[1].SetBigInt(ins[1]) - y0 := x[1] - - err := bls12377Permutation().Permutation(x[:]) - x[1].Add(&x[1], &y0) // feed forward - x[1].BigInt(outs[0]) - return err + return } var bls12377Permutation = sync.OnceValue(func() *poseidon2Bls12377.Permutation { @@ -328,16 +248,15 @@ var bls12377Permutation = sync.OnceValue(func() *poseidon2Bls12377.Permutation { return poseidon2Bls12377.NewPermutation(2, params.NbFullRounds, params.NbPartialRounds) // TODO @Tabaie add NewDefaultPermutation to gnark-crypto }) -// RegisterGkrSolverOptions registers the GKR gates corresponding to the given curves for the solver -func RegisterGkrSolverOptions(curves ...ecc.ID) { +// RegisterGkrGates registers the GKR gates corresponding to the given curves for the solver +func RegisterGkrGates(curves ...ecc.ID) { if len(curves) == 0 { panic("expected at least one curve") } - solver.RegisterHint(permuteHint) for _, curve := range curves { switch curve { case ecc.BLS12_377: - if err := registerGkrGatesBls12377(); err != nil { + if err := registerGatesBls12377(); err != nil { panic(err) } default: @@ -346,7 +265,7 @@ func RegisterGkrSolverOptions(curves ...ecc.ID) { } } -func registerGkrGatesBls12377() error { +func registerGatesBls12377() error { const ( x = iota y @@ -356,29 +275,31 @@ func registerGkrGatesBls12377() error { halfRf := p.NbFullRounds / 2 gateNames := newRoundGateNamer(p) - if err := gkrgates.Register(pow2Gate, 1, gkrgates.WithUnverifiedDegree(2), gkrgates.WithNoSolvableVar()); err != nil { + if _, err := gkrgates.Register(pow2Gate, 1, gkrgates.WithUnverifiedDegree(2), gkrgates.WithNoSolvableVar(), gkrgates.WithCurves(ecc.BLS12_377)); err != nil { return err } - if err := gkrgates.Register(pow4Gate, 1, gkrgates.WithUnverifiedDegree(4), gkrgates.WithNoSolvableVar()); err != nil { + if _, err := gkrgates.Register(pow4Gate, 1, gkrgates.WithUnverifiedDegree(4), gkrgates.WithNoSolvableVar(), gkrgates.WithCurves(ecc.BLS12_377)); err != nil { return err } - if err := gkrgates.Register(pow2TimesGate, 2, gkrgates.WithUnverifiedDegree(3), gkrgates.WithNoSolvableVar()); err != nil { + if _, err := gkrgates.Register(pow2TimesGate, 2, gkrgates.WithUnverifiedDegree(3), gkrgates.WithNoSolvableVar(), gkrgates.WithCurves(ecc.BLS12_377)); err != nil { return err } - if err := gkrgates.Register(pow4TimesGate, 2, gkrgates.WithUnverifiedDegree(5), gkrgates.WithNoSolvableVar()); err != nil { + if _, err := gkrgates.Register(pow4TimesGate, 2, gkrgates.WithUnverifiedDegree(5), gkrgates.WithNoSolvableVar(), gkrgates.WithCurves(ecc.BLS12_377)); err != nil { return err } - if err := gkrgates.Register(intGate2, 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0)); err != nil { + if _, err := gkrgates.Register(intGate2, 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithCurves(ecc.BLS12_377)); err != nil { return err } extKeySBox := func(round int, varIndex int) error { - return gkrgates.Register(extKeyGate(&p.RoundKeys[round][varIndex]), 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(varIndex, round))) + _, err := gkrgates.Register(extKeyGate(&p.RoundKeys[round][varIndex]), 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(varIndex, round)), gkrgates.WithCurves(ecc.BLS12_377)) + return err } intKeySBox2 := func(round int) error { - return gkrgates.Register(intKeyGate2(&p.RoundKeys[round][1]), 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(y, round))) + _, err := gkrgates.Register(intKeyGate2(&p.RoundKeys[round][1]), 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(y, round)), gkrgates.WithCurves(ecc.BLS12_377)) + return err } fullRound := func(i int) error { @@ -422,7 +343,8 @@ func registerGkrGatesBls12377() error { } } - return gkrgates.Register(extAddGate, 3, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(y, p.NbPartialRounds+p.NbFullRounds))) + _, err := gkrgates.Register(extAddGate, 3, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(y, p.NbPartialRounds+p.NbFullRounds)), gkrgates.WithCurves(ecc.BLS12_377)) + return err } type roundGateNamer string diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go index 1503054a59..22e9def87f 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go @@ -37,7 +37,7 @@ func TestGkrCompression(t *testing.T) { Outs: outs, } - RegisterGkrSolverOptions(ecc.BLS12_377) + RegisterGkrGates(ecc.BLS12_377) test.NewAssert(t).CheckCircuit(&testGkrPermutationCircuit{Ins: make([][2]frontend.Variable, len(ins)), Outs: make([]frontend.Variable, len(outs))}, test.WithValidAssignment(&circuit), test.WithCurves(ecc.BLS12_377)) } @@ -49,7 +49,7 @@ type testGkrPermutationCircuit struct { func (c *testGkrPermutationCircuit) Define(api frontend.API) error { - pos2 := NewGkrCompressions(api) + pos2 := NewGkrCompressor(api) api.AssertIsEqual(len(c.Ins), len(c.Outs)) for i := range c.Ins { api.AssertIsEqual(c.Outs[i], pos2.Compress(c.Ins[i][0], c.Ins[i][1])) From cfdb9d3d46730d1774fcd7dea93f0b78da1a023d Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 3 Jun 2025 15:48:15 -0500 Subject: [PATCH 021/251] fix: reduce in test engine --- internal/gkr/engine_hints.go | 2 ++ std/permutation/poseidon2/gkr-poseidon2/gkr.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/gkr/engine_hints.go b/internal/gkr/engine_hints.go index 7e60a21547..7968c12793 100644 --- a/internal/gkr/engine_hints.go +++ b/internal/gkr/engine_hints.go @@ -147,6 +147,7 @@ func (g gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend func (g gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { x, y := utils.FromInterface(b), utils.FromInterface(c) x.Mul(&x, &y) + x.Mod(&x, g.Int) // reduce y = utils.FromInterface(a) x.Add(&x, &y) return &x @@ -177,6 +178,7 @@ func (g gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend y = utils.FromInterface(v) x.Mul(&x, &y) } + x.Mod(&x, g.Int) // reduce return &x } diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr.go b/std/permutation/poseidon2/gkr-poseidon2/gkr.go index f05836a798..04ca60b49a 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr.go @@ -38,7 +38,7 @@ func pow4Gate(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { // pow4TimesGate computes a, b -> a⁴ * b func pow4TimesGate(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { if len(x) != 2 { - panic("expected 1 input") + panic("expected 2 input") } y := api.Mul(x[0], x[0]) y = api.Mul(y, y) From 12788f34d416474fb4229ba7dc481830d3a41067 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 3 Jun 2025 16:01:00 -0500 Subject: [PATCH 022/251] fix: rename GkrCompressions -> GkrPermutations --- std/permutation/poseidon2/gkr-poseidon2/gkr.go | 10 +++++----- std/permutation/poseidon2/gkr-poseidon2/gkr_test.go | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr.go b/std/permutation/poseidon2/gkr-poseidon2/gkr.go index 04ca60b49a..d9a7dcfbbb 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr.go @@ -108,17 +108,17 @@ func extAddGate(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Add(api.Mul(x[0], 2), x[1], x[2]) } -type GkrCompressions struct { +type GkrPermutations struct { api frontend.API gkrCircuit *gkrapi.Circuit in1, in2, out gkr.Variable } -// NewGkrCompressor returns an object that can compute the Poseidon2 compression function (currently only for BLS12-377) +// NewGkrPermutations returns an object that can compute the Poseidon2 compression function (currently only for BLS12-377) // which consists of a permutation along with the input fed forward. // The correctness of the compression functions is proven using GKR. // Note that the solver will need the function RegisterGkrGates to be called with the desired curves -func NewGkrCompressor(api frontend.API) *GkrCompressions { +func NewGkrPermutations(api frontend.API) *GkrPermutations { if api.Compiler().Field().Cmp(ecc.BLS12_377.ScalarField()) != 0 { panic("currently only BL12-377 is supported") } @@ -126,7 +126,7 @@ func NewGkrCompressor(api frontend.API) *GkrCompressions { if err != nil { panic(fmt.Errorf("failed to define GKR circuit: %v", err)) } - return &GkrCompressions{ + return &GkrPermutations{ api: api, gkrCircuit: gkrApi.Compile(api, "MIMC"), in1: in1, @@ -135,7 +135,7 @@ func NewGkrCompressor(api frontend.API) *GkrCompressions { } } -func (p *GkrCompressions) Compress(a, b frontend.Variable) frontend.Variable { +func (p *GkrPermutations) Compress(a, b frontend.Variable) frontend.Variable { outs, err := p.gkrCircuit.AddInstance(map[gkr.Variable]frontend.Variable{p.in1: a, p.in2: b}) if err != nil { panic(err) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go index 22e9def87f..1562aac070 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go @@ -49,7 +49,7 @@ type testGkrPermutationCircuit struct { func (c *testGkrPermutationCircuit) Define(api frontend.API) error { - pos2 := NewGkrCompressor(api) + pos2 := NewGkrPermutations(api) api.AssertIsEqual(len(c.Ins), len(c.Outs)) for i := range c.Ins { api.AssertIsEqual(c.Outs[i], pos2.Compress(c.Ins[i][0], c.Ins[i][1])) From 9a0bf0e7d171fbfcca15a6975c847b01701ae824 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 3 Jun 2025 16:15:01 -0500 Subject: [PATCH 023/251] bench: gkrposeidon2 --- .../poseidon2/gkr-poseidon2/gkr_test.go | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go index 1562aac070..7d64aedb92 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go @@ -2,6 +2,8 @@ package gkr_poseidon2 import ( "fmt" + "os" + "runtime/pprof" "testing" "github.com/consensys/gnark-crypto/ecc" @@ -12,8 +14,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestGkrCompression(t *testing.T) { - const n = 2 +func gkrPermutationsCircuits(t require.TestingT, n int) (circuit, assignment testGkrPermutationCircuit) { var k int64 ins := make([][2]frontend.Variable, n) outs := make([]frontend.Variable, n) @@ -32,14 +33,19 @@ func TestGkrCompression(t *testing.T) { k += 2 } - circuit := testGkrPermutationCircuit{ - Ins: ins, - Outs: outs, - } + return testGkrPermutationCircuit{ + Ins: make([][2]frontend.Variable, len(ins)), + Outs: make([]frontend.Variable, len(outs)), + }, testGkrPermutationCircuit{ + Ins: ins, + Outs: outs, + } +} - RegisterGkrGates(ecc.BLS12_377) +func TestGkrCompression(t *testing.T) { + circuit, assignment := gkrPermutationsCircuits(t, 2) - test.NewAssert(t).CheckCircuit(&testGkrPermutationCircuit{Ins: make([][2]frontend.Variable, len(ins)), Outs: make([]frontend.Variable, len(outs))}, test.WithValidAssignment(&circuit), test.WithCurves(ecc.BLS12_377)) + test.NewAssert(t).CheckCircuit(&circuit, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BLS12_377)) } type testGkrPermutationCircuit struct { @@ -67,3 +73,27 @@ func TestGkrPermutationCompiles(t *testing.T) { require.NoError(t, err) fmt.Println(cs.GetNbConstraints(), "constraints") } + +func BenchmarkGkrPermutations(b *testing.B) { + circuit, assignmment := gkrPermutationsCircuits(b, 50000) + + cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &circuit) + require.NoError(b, err) + + witness, err := frontend.NewWitness(&assignmment, ecc.BLS12_377.ScalarField()) + require.NoError(b, err) + + // cpu profile + f, err := os.Create("cpu.pprof") + require.NoError(b, err) + defer func() { + require.NoError(b, f.Close()) + }() + + err = pprof.StartCPUProfile(f) + require.NoError(b, err) + defer pprof.StopCPUProfile() + + _, err = cs.Solve(witness) + require.NoError(b, err) +} From 618beffd04333607dd74f631d881cbb20707a435 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 3 Jun 2025 18:54:39 -0500 Subject: [PATCH 024/251] fix pad for bls12377 --- internal/gkr/bls12-377/solver_hints.go | 9 +++-- internal/gkr/engine_hints.go | 2 +- internal/gkr/gkrtypes/types.go | 1 + internal/utils/slices.go | 12 +++++++ internal/utils/slices_test.go | 25 ++++++++++++++ std/gkrapi/compile.go | 46 +++++++++++++------------- 6 files changed, 69 insertions(+), 26 deletions(-) create mode 100644 internal/utils/slices_test.go diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 43384769ac..5a7c995b29 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -9,6 +9,7 @@ import ( "fmt" "math/big" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" @@ -43,6 +44,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) opt(&s) } + nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info.NbInstances))) + d := SolvingData{ circuit: info.Circuit, assignment: make(WireAssignment, len(info.Circuit)), @@ -50,9 +53,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) d.maxNbIn = d.circuit.MaxGateNbIn() - d.assignment = make(WireAssignment, len(d.circuit)) for i := range d.assignment { - d.assignment[i] = make([]fr.Element, info.NbInstances) + d.assignment[i] = make([]fr.Element, nbPaddedInstances) } if s.assignment != nil { @@ -68,6 +70,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { + d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value + } } } diff --git a/internal/gkr/engine_hints.go b/internal/gkr/engine_hints.go index 7968c12793..74b15c77ba 100644 --- a/internal/gkr/engine_hints.go +++ b/internal/gkr/engine_hints.go @@ -49,7 +49,7 @@ func (h *TestEngineHints) Solve(mod *big.Int, ins []*big.Int, outs []*big.Int) e instanceI := len(h.assignment[0]) if in0 := ins[0].Uint64(); !ins[0].IsUint64() || in0 > 0xffffffff { return errors.New("first input must be a uint32 instance index") - } else if in0 != uint64(instanceI) || h.info.NbInstances-1 != instanceI { + } else if in0 != uint64(instanceI) || h.info.NbInstances != instanceI { return errors.New("first input must equal the number of instances, and calls to Solve must be done in order of instance index") } diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 3226f1aa2f..d313a7bc59 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -228,6 +228,7 @@ func CircuitInfoToCircuit(info gkrinfo.Circuit, gateGetter func(name gkr.GateNam resCircuit := make(Circuit, len(info)) for i := range info { if info[i].Gate == "" && len(info[i].Inputs) == 0 { + resCircuit[i].Gate = Identity() // input wire continue } resCircuit[i].Inputs = info[i].Inputs diff --git a/internal/utils/slices.go b/internal/utils/slices.go index dd2e2db31f..f493bf4bca 100644 --- a/internal/utils/slices.go +++ b/internal/utils/slices.go @@ -16,3 +16,15 @@ func References[T any](v []T) []*T { } return res } + +// ExtendRepeatLast extends the slice s by repeating the last element until it reaches the length n. +func ExtendRepeatLast[T any](s []T, n int) []T { + if n <= len(s) { + return s[:n] + } + s = s[:len(s):len(s)] // ensure s is a slice with a capacity equal to its length + for len(s) < n { + s = append(s, s[len(s)-1]) // append the last element until the length is n + } + return s +} diff --git a/internal/utils/slices_test.go b/internal/utils/slices_test.go new file mode 100644 index 0000000000..f61ec18fed --- /dev/null +++ b/internal/utils/slices_test.go @@ -0,0 +1,25 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestExtendRepeatLast(t *testing.T) { + // normal case + s := []int{1, 2, 3} + u := ExtendRepeatLast(s, 5) + assert.Equal(t, []int{1, 2, 3, 3, 3}, u) + + // don't overwrite super-slice + s = []int{1, 2, 3} + u = ExtendRepeatLast(s[:1], 2) + assert.Equal(t, []int{1, 1}, u) + assert.Equal(t, []int{1, 2, 3}, s) + + // trim if n < len(s) + s = []int{1, 2, 3} + u = ExtendRepeatLast(s, 2) + assert.Equal(t, []int{1, 2}, u) +} diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 4cd6c804e5..898c65a41f 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -41,14 +41,6 @@ func New() *API { return &API{} } -// log2 returns -1 if x is not a power of 2 -func log2(x uint) int { - if bits.OnesCount(x) != 1 { - return -1 - } - return bits.TrailingZeros(x) -} - // NewInput creates a new input variable. func (api *API) NewInput() gkr.Variable { return gkr.Variable(api.toStore.NewInputVariable()) @@ -80,7 +72,7 @@ func (api *API) Compile(parentApi frontend.API, fiatshamirHashName string, optio var err error res.hints, err = gadget.NewTestEngineHints(&res.toStore) if err != nil { - panic(fmt.Errorf("failed to create GKR hints: %w", err)) + panic(fmt.Errorf("failed to call GKR hints: %w", err)) } for _, opt := range options { @@ -103,8 +95,9 @@ func (api *API) Compile(parentApi frontend.API, fiatshamirHashName string, optio } } - res.toStore.ProveHintID = solver.GetHintID(res.hints.Prove) res.toStore.GetAssignmentHintID = solver.GetHintID(res.hints.GetAssignment) + res.toStore.ProveHintID = solver.GetHintID(res.hints.Prove) + res.toStore.SolveHintID = solver.GetHintID(res.hints.Solve) parentApi.Compiler().Defer(res.verify) @@ -125,23 +118,20 @@ func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr } hintIn := make([]frontend.Variable, 1+len(c.ins)) // first input denotes the instance number hintIn[0] = c.toStore.NbInstances - for hintInI, in := range c.ins { - if inV, ok := input[in]; !ok { - return nil, fmt.Errorf("missing entry for input variable %d", in) + for hintInI, wI := range c.ins { + if inV, ok := input[wI]; !ok { + return nil, fmt.Errorf("missing entry for input variable %d", wI) } else { hintIn[hintInI+1] = inV + c.assignments[wI] = append(c.assignments[wI], inV) } } - if c.toStore.NbInstances == 0 { - c.toStore.SolveHintID = solver.GetHintID(c.hints.Solve) - } - - c.toStore.NbInstances++ outsSerialized, err := c.api.Compiler().NewHint(c.hints.Solve, len(c.outs), hintIn...) if err != nil { - return nil, fmt.Errorf("failed to create solve hint: %w", err) + return nil, fmt.Errorf("failed to call solve hint: %w", err) } + c.toStore.NbInstances++ res := make(map[gkr.Variable]frontend.Variable, len(c.outs)) for i, v := range c.outs { res[v] = outsSerialized[i] @@ -157,11 +147,22 @@ func (c *Circuit) verify(api frontend.API) error { panic("api mismatch") } + nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(c.toStore.NbInstances))) + // pad instances to the next power of 2 by repeating the last instance + if c.toStore.NbInstances < nbPaddedInstances && c.toStore.NbInstances > 0 { + for _, wI := range c.ins { + c.assignments[wI] = utils.ExtendRepeatLast(c.assignments[wI], nbPaddedInstances) + } + for _, wI := range c.outs { + c.assignments[wI] = utils.ExtendRepeatLast(c.assignments[wI], nbPaddedInstances) + } + } + if err := api.(gkrinfo.ConstraintSystem).SetGkrInfo(c.toStore); err != nil { return err } - if len(c.outs) == 0 || len(c.assignments[0]) == 0 { + if len(c.outs) == 0 || len(c.assignments[0]) == 0 { // wire 0 is always an input wire return nil } @@ -194,7 +195,6 @@ func (c *Circuit) verify(api frontend.API) error { if err != nil { return fmt.Errorf("failed to create circuit data for snark: %w", err) } - logNbInstances := log2(uint(c.assignments.NbInstances())) hintIns := make([]frontend.Variable, len(initialChallenges)+1) // hack: adding one of the outputs of the solve hint to ensure "prove" is called after "solve" firstOutputAssignment := c.assignments[c.outs[0]] @@ -203,7 +203,7 @@ func (c *Circuit) verify(api frontend.API) error { copy(hintIns[1:], initialChallenges) if proofSerialized, err = api.Compiler().NewHint( - c.hints.Prove, gadget.ProofSize(forSnark.circuit, logNbInstances), hintIns...); err != nil { + c.hints.Prove, gadget.ProofSize(forSnark.circuit, bits.TrailingZeros(uint(nbPaddedInstances))), hintIns...); err != nil { return err } c.toStore.ProveHintID = solver.GetHintID(c.hints.Prove) @@ -253,7 +253,7 @@ func newCircuitDataForSnark(curve ecc.ID, info gkrinfo.StoringInfo, assignment g func init() { // TODO Move this to the hash package if the import cycle issue is fixed. - hash.Register("mimc", func(api frontend.API) (hash.FieldHasher, error) { + hash.Register("MIMC", func(api frontend.API) (hash.FieldHasher, error) { h, err := mimc.NewMiMC(api) return &h, err }) From 22b77f2d604842e012ac1c6e7beb80469ab0fee8 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 3 Jun 2025 18:58:23 -0500 Subject: [PATCH 025/251] some more padding fixes --- .../backend/template/gkr/solver_hints.go.tmpl | 10 +++++++--- internal/gkr/bls12-377/solver_hints.go | 5 ++--- internal/gkr/bls12-381/solver_hints.go | 10 +++++++--- internal/gkr/bls24-315/solver_hints.go | 10 +++++++--- internal/gkr/bls24-317/solver_hints.go | 10 +++++++--- internal/gkr/bn254/solver_hints.go | 10 +++++++--- internal/gkr/bw6-633/solver_hints.go | 10 +++++++--- internal/gkr/bw6-761/solver_hints.go | 10 +++++++--- 8 files changed, 51 insertions(+), 24 deletions(-) diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index cd89a9ffff..df8e758be1 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -2,6 +2,7 @@ import ( "fmt" "math/big" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" @@ -43,9 +44,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) d.maxNbIn = d.circuit.MaxGateNbIn() - d.assignment = make(WireAssignment, len(d.circuit)) + nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info.NbInstances))) for i := range d.assignment { - d.assignment[i] = make([]fr.Element, info.NbInstances) + d.assignment[i] = make([]fr.Element, nbPaddedInstances) } if s.assignment != nil { @@ -56,11 +57,14 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) if len(s.assignment[i]) != info.NbInstances { panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } - for j := range d.assignment[i] { + for j := range s.assignment[i] { if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { + d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value + } } } diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 5a7c995b29..1e7c4e1f31 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -44,8 +44,6 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) opt(&s) } - nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info.NbInstances))) - d := SolvingData{ circuit: info.Circuit, assignment: make(WireAssignment, len(info.Circuit)), @@ -53,6 +51,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) d.maxNbIn = d.circuit.MaxGateNbIn() + nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info.NbInstances))) for i := range d.assignment { d.assignment[i] = make([]fr.Element, nbPaddedInstances) } @@ -65,7 +64,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) if len(s.assignment[i]) != info.NbInstances { panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } - for j := range d.assignment[i] { + for j := range s.assignment[i] { if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index bcf449e99c..fa88d12c42 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -9,6 +9,7 @@ import ( "fmt" "math/big" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" @@ -50,9 +51,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) d.maxNbIn = d.circuit.MaxGateNbIn() - d.assignment = make(WireAssignment, len(d.circuit)) + nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info.NbInstances))) for i := range d.assignment { - d.assignment[i] = make([]fr.Element, info.NbInstances) + d.assignment[i] = make([]fr.Element, nbPaddedInstances) } if s.assignment != nil { @@ -63,11 +64,14 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) if len(s.assignment[i]) != info.NbInstances { panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } - for j := range d.assignment[i] { + for j := range s.assignment[i] { if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { + d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value + } } } diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index 6f254299a0..012be51dee 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -9,6 +9,7 @@ import ( "fmt" "math/big" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" @@ -50,9 +51,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) d.maxNbIn = d.circuit.MaxGateNbIn() - d.assignment = make(WireAssignment, len(d.circuit)) + nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info.NbInstances))) for i := range d.assignment { - d.assignment[i] = make([]fr.Element, info.NbInstances) + d.assignment[i] = make([]fr.Element, nbPaddedInstances) } if s.assignment != nil { @@ -63,11 +64,14 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) if len(s.assignment[i]) != info.NbInstances { panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } - for j := range d.assignment[i] { + for j := range s.assignment[i] { if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { + d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value + } } } diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index 6121504a68..3fda62407b 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -9,6 +9,7 @@ import ( "fmt" "math/big" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" @@ -50,9 +51,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) d.maxNbIn = d.circuit.MaxGateNbIn() - d.assignment = make(WireAssignment, len(d.circuit)) + nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info.NbInstances))) for i := range d.assignment { - d.assignment[i] = make([]fr.Element, info.NbInstances) + d.assignment[i] = make([]fr.Element, nbPaddedInstances) } if s.assignment != nil { @@ -63,11 +64,14 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) if len(s.assignment[i]) != info.NbInstances { panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } - for j := range d.assignment[i] { + for j := range s.assignment[i] { if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { + d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value + } } } diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index 6bd76425a1..5c8d3e7b1d 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -9,6 +9,7 @@ import ( "fmt" "math/big" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" @@ -50,9 +51,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) d.maxNbIn = d.circuit.MaxGateNbIn() - d.assignment = make(WireAssignment, len(d.circuit)) + nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info.NbInstances))) for i := range d.assignment { - d.assignment[i] = make([]fr.Element, info.NbInstances) + d.assignment[i] = make([]fr.Element, nbPaddedInstances) } if s.assignment != nil { @@ -63,11 +64,14 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) if len(s.assignment[i]) != info.NbInstances { panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } - for j := range d.assignment[i] { + for j := range s.assignment[i] { if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { + d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value + } } } diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index 3678eab8bf..43e90fdfde 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -9,6 +9,7 @@ import ( "fmt" "math/big" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" @@ -50,9 +51,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) d.maxNbIn = d.circuit.MaxGateNbIn() - d.assignment = make(WireAssignment, len(d.circuit)) + nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info.NbInstances))) for i := range d.assignment { - d.assignment[i] = make([]fr.Element, info.NbInstances) + d.assignment[i] = make([]fr.Element, nbPaddedInstances) } if s.assignment != nil { @@ -63,11 +64,14 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) if len(s.assignment[i]) != info.NbInstances { panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } - for j := range d.assignment[i] { + for j := range s.assignment[i] { if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { + d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value + } } } diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index baa1303132..82c9dd0eb8 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -9,6 +9,7 @@ import ( "fmt" "math/big" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" @@ -50,9 +51,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) d.maxNbIn = d.circuit.MaxGateNbIn() - d.assignment = make(WireAssignment, len(d.circuit)) + nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info.NbInstances))) for i := range d.assignment { - d.assignment[i] = make([]fr.Element, info.NbInstances) + d.assignment[i] = make([]fr.Element, nbPaddedInstances) } if s.assignment != nil { @@ -63,11 +64,14 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) if len(s.assignment[i]) != info.NbInstances { panic(fmt.Errorf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } - for j := range d.assignment[i] { + for j := range s.assignment[i] { if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { + d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value + } } } From 250a35bc3cbbfe3fc7720d7d8f34ed7e4950c577 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 3 Jun 2025 20:49:28 -0500 Subject: [PATCH 026/251] fix: padding issue in bn254 --- internal/gkr/bn254/solver_hints.go | 31 ++++++++++++++++++++++++------ std/gkrapi/api_test.go | 17 ++++++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index 5c8d3e7b1d..484d2d7122 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -21,9 +21,10 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int } type newSolvingDataSettings struct { @@ -45,8 +46,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) } d := SolvingData{ - circuit: info.Circuit, - assignment: make(WireAssignment, len(info.Circuit)), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + nbInstances: info.NbInstances, } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -69,6 +71,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + // inline equivalent of RepeatUntilEnd for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value } @@ -134,7 +137,13 @@ func SolveHint(data *SolvingData) hint.Hint { func ProveHint(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { // the first input is dummy, just to ensure the solver's work is done before the prover is called + + data.assignment.RepeatUntilEnd(data.nbInstances) + + // The first input is dummy, just to ensure the solver's work is done before the prover is called. + // The rest constitute the initial fiat shamir challenge + insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { + b := make([]byte, fr.Bytes) i.FillBytes(b) return b[:] @@ -151,3 +160,13 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } + +// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +// e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} +func (a WireAssignment) RepeatUntilEnd(n int) { + for i := range a { + for j := n; j < len(a[i]); j++ { + a[i][j] = a[i][j-1] + } + } +} diff --git a/std/gkrapi/api_test.go b/std/gkrapi/api_test.go index 3b2be818bd..5128f337b6 100644 --- a/std/gkrapi/api_test.go +++ b/std/gkrapi/api_test.go @@ -17,6 +17,7 @@ import ( "github.com/consensys/gnark/backend/groth16" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/std/gkrapi/gkr" stdHash "github.com/consensys/gnark/std/hash" "github.com/consensys/gnark/std/hash/mimc" @@ -643,3 +644,19 @@ func TestPow4Circuit_GetValue(t *testing.T) { test.NewAssert(t).CheckCircuit(&circuit, test.WithValidAssignment(&assignment)) } + +func TestWitnessExtend(t *testing.T) { + circuit := doubleNoDependencyCircuit{X: make([]frontend.Variable, 3), hashName: "-1"} + assignment := doubleNoDependencyCircuit{X: []frontend.Variable{0, 0, 1}} + + cs, err := frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, &circuit) + require.NoError(t, err) + + witness, err := frontend.NewWitness(&assignment, ecc.BN254.ScalarField()) + require.NoError(t, err) + + _, err = cs.Solve(witness) + require.NoError(t, err) + + //test.NewAssert(t).CheckCircuit(&circuit, test.WithValidAssignment(&assignment)) +} From 15867602fb4ddbd9e15777ddcec4974fbcaf08e9 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 3 Jun 2025 20:53:57 -0500 Subject: [PATCH 027/251] chore generify fix --- .../backend/template/gkr/solver_hints.go.tmpl | 31 +++++++++++++++---- internal/gkr/bls12-377/solver_hints.go | 31 +++++++++++++++---- internal/gkr/bls12-381/solver_hints.go | 31 +++++++++++++++---- internal/gkr/bls24-315/solver_hints.go | 31 +++++++++++++++---- internal/gkr/bls24-317/solver_hints.go | 31 +++++++++++++++---- internal/gkr/bw6-633/solver_hints.go | 31 +++++++++++++++---- internal/gkr/bw6-761/solver_hints.go | 31 +++++++++++++++---- 7 files changed, 175 insertions(+), 42 deletions(-) diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index df8e758be1..917fef8f7f 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -14,9 +14,10 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int } type newSolvingDataSettings struct { @@ -38,8 +39,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) } d := SolvingData{ - circuit: info.Circuit, - assignment: make(WireAssignment, len(info.Circuit)), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + nbInstances: info.NbInstances, } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -62,6 +64,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + // inline equivalent of RepeatUntilEnd for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value } @@ -128,7 +131,13 @@ func SolveHint(data *SolvingData) hint.Hint { func ProveHint(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { // the first input is dummy, just to ensure the solver's work is done before the prover is called + + data.assignment.RepeatUntilEnd(data.nbInstances) + + // The first input is dummy, just to ensure the solver's work is done before the prover is called. + // The rest constitute the initial fiat shamir challenge + insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { + b := make([]byte, fr.Bytes) i.FillBytes(b) return b[:] @@ -144,4 +153,14 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { return proof.SerializeToBigInts(outs) } +} + +// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +{{ print "// e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}}"}} +func (a WireAssignment) RepeatUntilEnd(n int) { + for i := range a { + for j := n; j < len(a[i]); j++ { + a[i][j] = a[i][j-1] + } + } } \ No newline at end of file diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 1e7c4e1f31..258c4990e6 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -21,9 +21,10 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int } type newSolvingDataSettings struct { @@ -45,8 +46,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) } d := SolvingData{ - circuit: info.Circuit, - assignment: make(WireAssignment, len(info.Circuit)), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + nbInstances: info.NbInstances, } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -69,6 +71,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + // inline equivalent of RepeatUntilEnd for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value } @@ -134,7 +137,13 @@ func SolveHint(data *SolvingData) hint.Hint { func ProveHint(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { // the first input is dummy, just to ensure the solver's work is done before the prover is called + + data.assignment.RepeatUntilEnd(data.nbInstances) + + // The first input is dummy, just to ensure the solver's work is done before the prover is called. + // The rest constitute the initial fiat shamir challenge + insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { + b := make([]byte, fr.Bytes) i.FillBytes(b) return b[:] @@ -151,3 +160,13 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } + +// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +// e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} +func (a WireAssignment) RepeatUntilEnd(n int) { + for i := range a { + for j := n; j < len(a[i]); j++ { + a[i][j] = a[i][j-1] + } + } +} diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index fa88d12c42..81dadc3be5 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -21,9 +21,10 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int } type newSolvingDataSettings struct { @@ -45,8 +46,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) } d := SolvingData{ - circuit: info.Circuit, - assignment: make(WireAssignment, len(info.Circuit)), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + nbInstances: info.NbInstances, } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -69,6 +71,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + // inline equivalent of RepeatUntilEnd for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value } @@ -134,7 +137,13 @@ func SolveHint(data *SolvingData) hint.Hint { func ProveHint(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { // the first input is dummy, just to ensure the solver's work is done before the prover is called + + data.assignment.RepeatUntilEnd(data.nbInstances) + + // The first input is dummy, just to ensure the solver's work is done before the prover is called. + // The rest constitute the initial fiat shamir challenge + insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { + b := make([]byte, fr.Bytes) i.FillBytes(b) return b[:] @@ -151,3 +160,13 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } + +// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +// e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} +func (a WireAssignment) RepeatUntilEnd(n int) { + for i := range a { + for j := n; j < len(a[i]); j++ { + a[i][j] = a[i][j-1] + } + } +} diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index 012be51dee..fe313bd479 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -21,9 +21,10 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int } type newSolvingDataSettings struct { @@ -45,8 +46,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) } d := SolvingData{ - circuit: info.Circuit, - assignment: make(WireAssignment, len(info.Circuit)), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + nbInstances: info.NbInstances, } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -69,6 +71,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + // inline equivalent of RepeatUntilEnd for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value } @@ -134,7 +137,13 @@ func SolveHint(data *SolvingData) hint.Hint { func ProveHint(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { // the first input is dummy, just to ensure the solver's work is done before the prover is called + + data.assignment.RepeatUntilEnd(data.nbInstances) + + // The first input is dummy, just to ensure the solver's work is done before the prover is called. + // The rest constitute the initial fiat shamir challenge + insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { + b := make([]byte, fr.Bytes) i.FillBytes(b) return b[:] @@ -151,3 +160,13 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } + +// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +// e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} +func (a WireAssignment) RepeatUntilEnd(n int) { + for i := range a { + for j := n; j < len(a[i]); j++ { + a[i][j] = a[i][j-1] + } + } +} diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index 3fda62407b..1734b80b6b 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -21,9 +21,10 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int } type newSolvingDataSettings struct { @@ -45,8 +46,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) } d := SolvingData{ - circuit: info.Circuit, - assignment: make(WireAssignment, len(info.Circuit)), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + nbInstances: info.NbInstances, } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -69,6 +71,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + // inline equivalent of RepeatUntilEnd for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value } @@ -134,7 +137,13 @@ func SolveHint(data *SolvingData) hint.Hint { func ProveHint(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { // the first input is dummy, just to ensure the solver's work is done before the prover is called + + data.assignment.RepeatUntilEnd(data.nbInstances) + + // The first input is dummy, just to ensure the solver's work is done before the prover is called. + // The rest constitute the initial fiat shamir challenge + insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { + b := make([]byte, fr.Bytes) i.FillBytes(b) return b[:] @@ -151,3 +160,13 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } + +// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +// e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} +func (a WireAssignment) RepeatUntilEnd(n int) { + for i := range a { + for j := n; j < len(a[i]); j++ { + a[i][j] = a[i][j-1] + } + } +} diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index 43e90fdfde..3d4cee67f4 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -21,9 +21,10 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int } type newSolvingDataSettings struct { @@ -45,8 +46,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) } d := SolvingData{ - circuit: info.Circuit, - assignment: make(WireAssignment, len(info.Circuit)), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + nbInstances: info.NbInstances, } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -69,6 +71,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + // inline equivalent of RepeatUntilEnd for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value } @@ -134,7 +137,13 @@ func SolveHint(data *SolvingData) hint.Hint { func ProveHint(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { // the first input is dummy, just to ensure the solver's work is done before the prover is called + + data.assignment.RepeatUntilEnd(data.nbInstances) + + // The first input is dummy, just to ensure the solver's work is done before the prover is called. + // The rest constitute the initial fiat shamir challenge + insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { + b := make([]byte, fr.Bytes) i.FillBytes(b) return b[:] @@ -151,3 +160,13 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } + +// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +// e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} +func (a WireAssignment) RepeatUntilEnd(n int) { + for i := range a { + for j := n; j < len(a[i]); j++ { + a[i][j] = a[i][j-1] + } + } +} diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index 82c9dd0eb8..c75a88eb4f 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -21,9 +21,10 @@ import ( ) type SolvingData struct { - assignment WireAssignment - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int } type newSolvingDataSettings struct { @@ -45,8 +46,9 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) } d := SolvingData{ - circuit: info.Circuit, - assignment: make(WireAssignment, len(info.Circuit)), + circuit: info.Circuit, + assignment: make(WireAssignment, len(info.Circuit)), + nbInstances: info.NbInstances, } d.maxNbIn = d.circuit.MaxGateNbIn() @@ -69,6 +71,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) panic(fmt.Errorf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } + // inline equivalent of RepeatUntilEnd for j := len(s.assignment[i]); j < nbPaddedInstances; j++ { d.assignment[i][j] = d.assignment[i][j-1] // pad with the last value } @@ -134,7 +137,13 @@ func SolveHint(data *SolvingData) hint.Hint { func ProveHint(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { // the first input is dummy, just to ensure the solver's work is done before the prover is called + + data.assignment.RepeatUntilEnd(data.nbInstances) + + // The first input is dummy, just to ensure the solver's work is done before the prover is called. + // The rest constitute the initial fiat shamir challenge + insBytes := algo_utils.Map(ins[1:], func(i *big.Int) []byte { + b := make([]byte, fr.Bytes) i.FillBytes(b) return b[:] @@ -151,3 +160,13 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } + +// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +// e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} +func (a WireAssignment) RepeatUntilEnd(n int) { + for i := range a { + for j := n; j < len(a[i]); j++ { + a[i][j] = a[i][j-1] + } + } +} From e593d9087a9e09bfadcb8c2176a8e9d18e83fab7 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 3 Jun 2025 23:55:49 -0500 Subject: [PATCH 028/251] Let Uint64 panic --- .../generator/backend/template/gkr/solver_hints.go.tmpl | 6 ------ internal/gkr/bls12-377/solver_hints.go | 6 ------ internal/gkr/bls12-381/solver_hints.go | 6 ------ internal/gkr/bls24-315/solver_hints.go | 6 ------ internal/gkr/bls24-317/solver_hints.go | 6 ------ internal/gkr/bn254/solver_hints.go | 6 ------ internal/gkr/bw6-633/solver_hints.go | 6 ------ internal/gkr/bw6-761/solver_hints.go | 6 ------ 8 files changed, 48 deletions(-) diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index 917fef8f7f..29698e0e3b 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -84,9 +84,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() - if !ins[0].IsUint64() || !ins[1].IsUint64() { - return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) - } data.assignment[wireI][instanceI].BigInt(outs[0]) @@ -97,9 +94,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { - return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) - } gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 258c4990e6..04c5f52586 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -90,9 +90,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() - if !ins[0].IsUint64() || !ins[1].IsUint64() { - return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) - } data.assignment[wireI][instanceI].BigInt(outs[0]) @@ -103,9 +100,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { - return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) - } gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index 81dadc3be5..e92e543398 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -90,9 +90,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() - if !ins[0].IsUint64() || !ins[1].IsUint64() { - return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) - } data.assignment[wireI][instanceI].BigInt(outs[0]) @@ -103,9 +100,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { - return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) - } gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index fe313bd479..f57537b985 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -90,9 +90,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() - if !ins[0].IsUint64() || !ins[1].IsUint64() { - return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) - } data.assignment[wireI][instanceI].BigInt(outs[0]) @@ -103,9 +100,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { - return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) - } gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index 1734b80b6b..d2cc4d32b1 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -90,9 +90,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() - if !ins[0].IsUint64() || !ins[1].IsUint64() { - return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) - } data.assignment[wireI][instanceI].BigInt(outs[0]) @@ -103,9 +100,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { - return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) - } gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index 484d2d7122..5813b89661 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -90,9 +90,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() - if !ins[0].IsUint64() || !ins[1].IsUint64() { - return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) - } data.assignment[wireI][instanceI].BigInt(outs[0]) @@ -103,9 +100,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { - return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) - } gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index 3d4cee67f4..ef945e25f7 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -90,9 +90,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() - if !ins[0].IsUint64() || !ins[1].IsUint64() { - return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) - } data.assignment[wireI][instanceI].BigInt(outs[0]) @@ -103,9 +100,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { - return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) - } gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index c75a88eb4f..1a91928171 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -90,9 +90,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() - if !ins[0].IsUint64() || !ins[1].IsUint64() { - return fmt.Errorf("inputs to GetAssignmentHint must be the wire index and instance index; provided values %s and %s don't fit in 64 bits", ins[0], ins[1]) - } data.assignment[wireI][instanceI].BigInt(outs[0]) @@ -103,9 +100,6 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { func SolveHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { instanceI := ins[0].Uint64() - if !ins[0].IsUint64() { - return fmt.Errorf("first input to solving hint must be the instance index; provided value %s doesn't fit in 64 bits", ins[0]) - } gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 From 9df619ab1dfa758975193dda6ced8ae8a4177790 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 4 Jun 2025 07:59:54 -0500 Subject: [PATCH 029/251] Update constraint/solver/gkrgates/registry.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- constraint/solver/gkrgates/registry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index 71b4969883..23b821cf63 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -131,7 +131,7 @@ func Register(f gkr.GateFunction, nbIn int, options ...registerOption) (register return false, err } if !gateVer.equal(f, g.Evaluate, nbIn) { - return false, fmt.Errorf("mismatch with already registered gate \"%s\" (degree %d) over curve %s", s.name, s.degree, curve) + return false, fmt.Errorf("mismatch with already registered gate \"%s\" (degree %d) over curve %s", s.name, g.Degree(), curve) } } From 8c88c52a604a29682981d052a3943f01c82b56b4 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 4 Jun 2025 08:03:35 -0500 Subject: [PATCH 030/251] refactor: use assert.EqualError --- constraint/solver/gkrgates/registry_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/constraint/solver/gkrgates/registry_test.go b/constraint/solver/gkrgates/registry_test.go index 5a9e6e871d..7fe739b152 100644 --- a/constraint/solver/gkrgates/registry_test.go +++ b/constraint/solver/gkrgates/registry_test.go @@ -67,7 +67,7 @@ func TestRegister(t *testing.T) { t.Run("zero", func(t *testing.T) { const gateName gkr.GateName = "zero-register-gate-test" - expectedError := fmt.Errorf("for gate \"%s\": %v", gateName, gkrtypes.ErrZeroFunction) + expectedError := fmt.Errorf("for gate \"%s\": %v", gateName, gkrtypes.ErrZeroFunction).Error() zeroGate := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Sub(x[0], x[0]) } @@ -75,13 +75,13 @@ func TestRegister(t *testing.T) { // Attempt to register the zero gate without specifying a degree registered, err := Register(zeroGate, 1, WithName(gateName)) assert.Error(t, err, "error must be returned for zero polynomial") - assert.Equal(t, expectedError, err, "error message must match expected error") + assert.EqualError(t, err, expectedError, "error message must match expected error") assert.False(t, registered, "registration must fail for zero polynomial") // Attempt to register the zero gate with a specified degree registered, err = Register(zeroGate, 1, WithName(gateName), WithDegree(2)) assert.Error(t, err, "error must be returned for zero polynomial with degree") - assert.Equal(t, expectedError, err, "error message must match expected error") + assert.EqualError(t, err, expectedError, "error message must match expected error") assert.False(t, registered, "registration must fail for zero polynomial with degree") }) } From 499ff522c0e2555e985e0c6ec6ccb31ff7f0b5d8 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Thu, 5 Jun 2025 17:58:58 -0500 Subject: [PATCH 031/251] refactor: import hash/all in test --- std/permutation/poseidon2/gkr-poseidon2/gkr_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go index 7d64aedb92..601d80cb70 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go @@ -10,6 +10,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/scs" + _ "github.com/consensys/gnark/std/hash/all" "github.com/consensys/gnark/test" "github.com/stretchr/testify/require" ) From be747e027e35f5ceccaffe6ea835e75c1ba59f8c Mon Sep 17 00:00:00 2001 From: Tabaie Date: Thu, 5 Jun 2025 18:26:51 -0500 Subject: [PATCH 032/251] refactor: shorten import name --- std/permutation/poseidon2/gkr-poseidon2/gkr.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr.go b/std/permutation/poseidon2/gkr-poseidon2/gkr.go index d9a7dcfbbb..471c1f3887 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr.go @@ -9,7 +9,7 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/consensys/gnark-crypto/ecc" - poseidon2Bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/poseidon2" + bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/poseidon2" "github.com/consensys/gnark/frontend" ) @@ -159,9 +159,9 @@ func defineCircuitBls12377() (gkrApi *gkrapi.API, in1, in2, out gkr.Variable, er } // poseidon2 parameters - gateNamer := newRoundGateNamer(poseidon2Bls12377.GetDefaultParameters()) - rF := poseidon2Bls12377.GetDefaultParameters().NbFullRounds - rP := poseidon2Bls12377.GetDefaultParameters().NbPartialRounds + gateNamer := newRoundGateNamer(bls12377.GetDefaultParameters()) + rF := bls12377.GetDefaultParameters().NbFullRounds + rP := bls12377.GetDefaultParameters().NbPartialRounds halfRf := rF / 2 gkrApi = gkrapi.New() @@ -243,9 +243,9 @@ func defineCircuitBls12377() (gkrApi *gkrapi.API, in1, in2, out gkr.Variable, er return } -var bls12377Permutation = sync.OnceValue(func() *poseidon2Bls12377.Permutation { - params := poseidon2Bls12377.GetDefaultParameters() - return poseidon2Bls12377.NewPermutation(2, params.NbFullRounds, params.NbPartialRounds) // TODO @Tabaie add NewDefaultPermutation to gnark-crypto +var bls12377Permutation = sync.OnceValue(func() *bls12377.Permutation { + params := bls12377.GetDefaultParameters() + return bls12377.NewPermutation(2, params.NbFullRounds, params.NbPartialRounds) // TODO @Tabaie add NewDefaultPermutation to gnark-crypto }) // RegisterGkrGates registers the GKR gates corresponding to the given curves for the solver @@ -271,7 +271,7 @@ func registerGatesBls12377() error { y ) - p := poseidon2Bls12377.GetDefaultParameters() + p := bls12377.GetDefaultParameters() halfRf := p.NbFullRounds / 2 gateNames := newRoundGateNamer(p) From 4b43ce33f59ef939396d9f5253f5f7e989c7e06b Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 8 Jun 2025 11:12:18 -0500 Subject: [PATCH 033/251] perf: addition rather than multiplication in gates --- std/permutation/poseidon2/gkr-poseidon2/gkr.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr.go b/std/permutation/poseidon2/gkr-poseidon2/gkr.go index 471c1f3887..a9af651bda 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr.go @@ -20,7 +20,7 @@ func extKeyGate(roundKey frontend.Variable) gkr.GateFunction { if len(x) != 2 { panic("expected 2 inputs") } - return api.Add(api.Mul(x[0], 2), x[1], roundKey) + return api.Add(x[0], x[0], x[1], roundKey) } } @@ -71,7 +71,7 @@ func extGate2(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { if len(x) != 2 { panic("expected 2 inputs") } - return api.Add(api.Mul(x[1], 2), x[0]) + return api.Add(x[1], x[1], x[0]) } // intKeyGate2 applies the internal matrix mul, then adds the round key @@ -80,7 +80,7 @@ func intKeyGate2(roundKey frontend.Variable) gkr.GateFunction { if len(x) != 2 { panic("expected 2 inputs") } - return api.Add(api.Mul(x[1], 3), x[0], roundKey) + return api.Add(x[1], x[1], x[1], x[0], roundKey) } } @@ -89,7 +89,7 @@ func intGate2(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { if len(x) != 2 { panic("expected 2 inputs") } - return api.Add(api.Mul(x[1], 3), x[0]) + return api.Add(x[1], x[1], x[1], x[0]) } // extGate applies the first row of the external matrix @@ -97,7 +97,7 @@ func extGate(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { if len(x) != 2 { panic("expected 2 inputs") } - return api.Add(api.Mul(x[0], 2), x[1]) + return api.Add(x[0], x[0], x[1]) } // extAddGate applies the first row of the external matrix to the first two elements and adds the third @@ -105,7 +105,7 @@ func extAddGate(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { if len(x) != 3 { panic("expected 3 inputs") } - return api.Add(api.Mul(x[0], 2), x[1], x[2]) + return api.Add(x[0], x[0], x[1], x[2]) } type GkrPermutations struct { From 6b86526d708e66cc5e3846452f3259a9a41b724f Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 8 Jun 2025 12:57:43 -0500 Subject: [PATCH 034/251] Revert "perf: addition rather than multiplication in gates" This reverts commit 4b43ce33f59ef939396d9f5253f5f7e989c7e06b. --- std/permutation/poseidon2/gkr-poseidon2/gkr.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr.go b/std/permutation/poseidon2/gkr-poseidon2/gkr.go index a9af651bda..471c1f3887 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr.go @@ -20,7 +20,7 @@ func extKeyGate(roundKey frontend.Variable) gkr.GateFunction { if len(x) != 2 { panic("expected 2 inputs") } - return api.Add(x[0], x[0], x[1], roundKey) + return api.Add(api.Mul(x[0], 2), x[1], roundKey) } } @@ -71,7 +71,7 @@ func extGate2(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { if len(x) != 2 { panic("expected 2 inputs") } - return api.Add(x[1], x[1], x[0]) + return api.Add(api.Mul(x[1], 2), x[0]) } // intKeyGate2 applies the internal matrix mul, then adds the round key @@ -80,7 +80,7 @@ func intKeyGate2(roundKey frontend.Variable) gkr.GateFunction { if len(x) != 2 { panic("expected 2 inputs") } - return api.Add(x[1], x[1], x[1], x[0], roundKey) + return api.Add(api.Mul(x[1], 3), x[0], roundKey) } } @@ -89,7 +89,7 @@ func intGate2(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { if len(x) != 2 { panic("expected 2 inputs") } - return api.Add(x[1], x[1], x[1], x[0]) + return api.Add(api.Mul(x[1], 3), x[0]) } // extGate applies the first row of the external matrix @@ -97,7 +97,7 @@ func extGate(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { if len(x) != 2 { panic("expected 2 inputs") } - return api.Add(x[0], x[0], x[1]) + return api.Add(api.Mul(x[0], 2), x[1]) } // extAddGate applies the first row of the external matrix to the first two elements and adds the third @@ -105,7 +105,7 @@ func extAddGate(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { if len(x) != 3 { panic("expected 3 inputs") } - return api.Add(x[0], x[0], x[1], x[2]) + return api.Add(api.Mul(x[0], 2), x[1], x[2]) } type GkrPermutations struct { From c4b01b579e908db2f1f22de7a66fc3309fab2b16 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 8 Jun 2025 14:14:28 -0500 Subject: [PATCH 035/251] feat: generify poseidon2-gkr (not all s-Boxes available yet) --- .../poseidon2/gkr-poseidon2/gkr.go | 114 ++++--- .../poseidon2/gkr-poseidon2/gkr_test.go | 4 +- std/permutation/poseidon2/poseidon2.go | 281 +++++++++++++----- 3 files changed, 253 insertions(+), 146 deletions(-) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr.go b/std/permutation/poseidon2/gkr-poseidon2/gkr.go index 471c1f3887..dcc51a4031 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr.go @@ -1,15 +1,16 @@ package gkr_poseidon2 import ( + "errors" "fmt" - "sync" "github.com/consensys/gnark/constraint/solver/gkrgates" + "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi" "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/permutation/poseidon2" "github.com/consensys/gnark-crypto/ecc" - bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/poseidon2" "github.com/consensys/gnark/frontend" ) @@ -117,18 +118,18 @@ type GkrPermutations struct { // NewGkrPermutations returns an object that can compute the Poseidon2 compression function (currently only for BLS12-377) // which consists of a permutation along with the input fed forward. // The correctness of the compression functions is proven using GKR. -// Note that the solver will need the function RegisterGkrGates to be called with the desired curves +// Note that the solver will need the function RegisterGates to be called with the desired curves func NewGkrPermutations(api frontend.API) *GkrPermutations { if api.Compiler().Field().Cmp(ecc.BLS12_377.ScalarField()) != 0 { panic("currently only BL12-377 is supported") } - gkrApi, in1, in2, out, err := defineCircuitBls12377() + gkrCircuit, in1, in2, out, err := defineCircuit(api) if err != nil { - panic(fmt.Errorf("failed to define GKR circuit: %v", err)) + panic(fmt.Errorf("failed to define GKR circuit: %w", err)) } return &GkrPermutations{ api: api, - gkrCircuit: gkrApi.Compile(api, "MIMC"), + gkrCircuit: gkrCircuit, in1: in1, in2: in2, out: out, @@ -144,27 +145,28 @@ func (p *GkrPermutations) Compress(a, b frontend.Variable) frontend.Variable { return outs[p.out] } -// defineCircuitBls12377 defines the GKR circuit for the Poseidon2 permutation over BLS12-377 +// defineCircuit defines the GKR circuit for the Poseidon2 permutation over BLS12-377 // insLeft and insRight are the inputs to the permutation // they must be padded to a power of 2 -func defineCircuitBls12377() (gkrApi *gkrapi.API, in1, in2, out gkr.Variable, err error) { +func defineCircuit(api frontend.API) (gkrCircuit *gkrapi.Circuit, in1, in2, out gkr.Variable, err error) { // variable indexes const ( xI = iota yI ) - if err = registerGatesBls12377(); err != nil { + curve := utils.FieldToCurve(api.Compiler().Field()) + p, err := poseidon2.GetDefaultParameters(curve) + if err != nil { return } + gateNamer := newRoundGateNamer(&p, curve) - // poseidon2 parameters - gateNamer := newRoundGateNamer(bls12377.GetDefaultParameters()) - rF := bls12377.GetDefaultParameters().NbFullRounds - rP := bls12377.GetDefaultParameters().NbPartialRounds - halfRf := rF / 2 + if err = registerGates(&p, curve); err != nil { + return + } - gkrApi = gkrapi.New() + gkrApi := gkrapi.New() x := gkrApi.NewInput() y := gkrApi.NewInput() @@ -181,9 +183,17 @@ func defineCircuitBls12377() (gkrApi *gkrapi.API, in1, in2, out gkr.Variable, er // apply the s-Box to u // the s-Box gates: u¹⁷ = (u⁴)⁴ * u - sBox := func(u gkr.Variable) gkr.Variable { - v := gkrApi.Gate(pow4Gate, u) // u⁴ - return gkrApi.Gate(pow4TimesGate, v, u) // u¹⁷ + + var sBox func(gkr.Variable) gkr.Variable + switch p.DegreeSBox { + case 17: + sBox = func(u gkr.Variable) gkr.Variable { + v := gkrApi.Gate(pow4Gate, u) // u⁴ + return gkrApi.Gate(pow4TimesGate, v, u) // u¹⁷ + } + default: + err = fmt.Errorf("unsupported s-Box degree %d", p.DegreeSBox) + return } // apply external matrix multiplication and round key addition @@ -208,89 +218,68 @@ func defineCircuitBls12377() (gkrApi *gkrapi.API, in1, in2, out gkr.Variable, er // *** construct the circuit *** - for i := range halfRf { + for i := range p.NbFullRounds / 2 { fullRound(i) } { // i = halfRf: first partial round // still using the external matrix, since the linear operation still belongs to a full (canonical) round - x1 := extKeySBox(halfRf, xI, x, y) + x1 := extKeySBox(p.NbFullRounds/2, xI, x, y) x, y = x1, gkrApi.Gate(extGate2, x, y) } - for i := halfRf + 1; i < halfRf+rP; i++ { + for i := p.NbFullRounds/2 + 1; i < p.NbFullRounds/2+p.NbPartialRounds; i++ { x1 := extKeySBox(i, xI, x, y) // the first row of the internal matrix is the same as that of the external matrix x, y = x1, gkrApi.Gate(intGate2, x, y) } { - i := halfRf + rP + i := p.NbFullRounds/2 + p.NbPartialRounds // first iteration of the final batch of full rounds // still using the internal matrix, since the linear operation still belongs to a partial (canonical) round x1 := extKeySBox(i, xI, x, y) x, y = x1, intKeySBox2(i, x, y) } - for i := halfRf + rP + 1; i < rP+rF; i++ { + for i := p.NbFullRounds/2 + p.NbPartialRounds + 1; i < p.NbPartialRounds+p.NbFullRounds; i++ { fullRound(i) } // apply the external matrix one last time to obtain the final value of y - out = gkrApi.NamedGate(gateNamer.linear(yI, rP+rF), y, x, in2) + out = gkrApi.Gate(extAddGate, y, x, in2) + + gkrCircuit = gkrApi.Compile(api, "MIMC") return } -var bls12377Permutation = sync.OnceValue(func() *bls12377.Permutation { - params := bls12377.GetDefaultParameters() - return bls12377.NewPermutation(2, params.NbFullRounds, params.NbPartialRounds) // TODO @Tabaie add NewDefaultPermutation to gnark-crypto -}) - -// RegisterGkrGates registers the GKR gates corresponding to the given curves for the solver -func RegisterGkrGates(curves ...ecc.ID) { +// RegisterGates registers the GKR gates corresponding to the given curves for the solver. +func RegisterGates(curves ...ecc.ID) error { if len(curves) == 0 { - panic("expected at least one curve") + return errors.New("expected at least one curve") } for _, curve := range curves { - switch curve { - case ecc.BLS12_377: - if err := registerGatesBls12377(); err != nil { - panic(err) - } - default: - panic(fmt.Sprintf("curve %s not currently supported", curve)) + p, err := poseidon2.GetDefaultParameters(curve) + if err != nil { + return fmt.Errorf("failed to get default parameters for curve %s: %w", curve, err) + } + if err = registerGates(&p, curve); err != nil { + return fmt.Errorf("failed to register gates for curve %s: %w", curve, err) } } + return nil } -func registerGatesBls12377() error { +func registerGates(p *poseidon2.Parameters, curve ecc.ID) error { const ( x = iota y ) - p := bls12377.GetDefaultParameters() + gateNames := newRoundGateNamer(p, curve) halfRf := p.NbFullRounds / 2 - gateNames := newRoundGateNamer(p) - - if _, err := gkrgates.Register(pow2Gate, 1, gkrgates.WithUnverifiedDegree(2), gkrgates.WithNoSolvableVar(), gkrgates.WithCurves(ecc.BLS12_377)); err != nil { - return err - } - if _, err := gkrgates.Register(pow4Gate, 1, gkrgates.WithUnverifiedDegree(4), gkrgates.WithNoSolvableVar(), gkrgates.WithCurves(ecc.BLS12_377)); err != nil { - return err - } - if _, err := gkrgates.Register(pow2TimesGate, 2, gkrgates.WithUnverifiedDegree(3), gkrgates.WithNoSolvableVar(), gkrgates.WithCurves(ecc.BLS12_377)); err != nil { - return err - } - if _, err := gkrgates.Register(pow4TimesGate, 2, gkrgates.WithUnverifiedDegree(5), gkrgates.WithNoSolvableVar(), gkrgates.WithCurves(ecc.BLS12_377)); err != nil { - return err - } - - if _, err := gkrgates.Register(intGate2, 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithCurves(ecc.BLS12_377)); err != nil { - return err - } extKeySBox := func(round int, varIndex int) error { _, err := gkrgates.Register(extKeyGate(&p.RoundKeys[round][varIndex]), 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(varIndex, round)), gkrgates.WithCurves(ecc.BLS12_377)) @@ -343,15 +332,14 @@ func registerGatesBls12377() error { } } - _, err := gkrgates.Register(extAddGate, 3, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(y, p.NbPartialRounds+p.NbFullRounds)), gkrgates.WithCurves(ecc.BLS12_377)) - return err + return nil } type roundGateNamer string // newRoundGateNamer returns an object that returns standardized names for gates in the GKR circuit -func newRoundGateNamer(p fmt.Stringer) roundGateNamer { - return roundGateNamer(p.String()) +func newRoundGateNamer(p *poseidon2.Parameters, curve ecc.ID) roundGateNamer { + return roundGateNamer(fmt.Sprintf("Poseidon2-%s[t=%d,rF=%d,rP=%d,d=%d]", curve.String(), p.Width, p.NbFullRounds, p.NbPartialRounds, p.DegreeSBox)) } // linear is the name of a gate where a polynomial of total degree 1 is applied to the input diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go index 601d80cb70..2d9ad7ccb5 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go @@ -76,12 +76,12 @@ func TestGkrPermutationCompiles(t *testing.T) { } func BenchmarkGkrPermutations(b *testing.B) { - circuit, assignmment := gkrPermutationsCircuits(b, 50000) + circuit, assignment := gkrPermutationsCircuits(b, 50000) cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &circuit) require.NoError(b, err) - witness, err := frontend.NewWitness(&assignmment, ecc.BLS12_377.ScalarField()) + witness, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField()) require.NoError(b, err) // cpu profile diff --git a/std/permutation/poseidon2/poseidon2.go b/std/permutation/poseidon2/poseidon2.go index 55afe73be5..317cc977dd 100644 --- a/std/permutation/poseidon2/poseidon2.go +++ b/std/permutation/poseidon2/poseidon2.go @@ -23,38 +23,157 @@ var ( type Permutation struct { api frontend.API - params parameters + params Parameters } -// parameters describing the poseidon2 implementation -type parameters struct { +// Parameters describing the poseidon2 implementation +type Parameters struct { // len(preimage)+len(digest)=len(preimage)+ceil(log(2*/r)) - width int + Width int // sbox degree - degreeSBox int + DegreeSBox int // number of full rounds (even number) - nbFullRounds int + NbFullRounds int // number of partial rounds - nbPartialRounds int + NbPartialRounds int // round keys: ordered by round then variable - roundKeys [][]big.Int + RoundKeys [][]big.Int +} + +func GetDefaultParameters(curve ecc.ID) (Parameters, error) { + switch curve { // TODO: assumes pairing based builder, reconsider when supporting other backends + case ecc.BN254: + p := poseidonbn254.GetDefaultParameters() + res := Parameters{ + Width: p.Width, + DegreeSBox: poseidonbn254.DegreeSBox(), + NbFullRounds: p.NbFullRounds, + NbPartialRounds: p.NbPartialRounds, + RoundKeys: make([][]big.Int, len(p.RoundKeys)), + } + for i := range res.RoundKeys { + res.RoundKeys[i] = make([]big.Int, len(p.RoundKeys[i])) + for j := range res.RoundKeys[i] { + p.RoundKeys[i][j].BigInt(&res.RoundKeys[i][j]) + } + } + return res, nil + case ecc.BLS12_381: + p := poseidonbls12381.GetDefaultParameters() + res := Parameters{ + Width: p.Width, + DegreeSBox: poseidonbls12381.DegreeSBox(), + NbFullRounds: p.NbFullRounds, + NbPartialRounds: p.NbPartialRounds, + RoundKeys: make([][]big.Int, len(p.RoundKeys)), + } + for i := range res.RoundKeys { + res.RoundKeys[i] = make([]big.Int, len(p.RoundKeys[i])) + for j := range res.RoundKeys[i] { + p.RoundKeys[i][j].BigInt(&res.RoundKeys[i][j]) + } + } + return res, nil + case ecc.BLS12_377: + p := poseidonbls12377.GetDefaultParameters() + res := Parameters{ + Width: p.Width, + DegreeSBox: poseidonbls12377.DegreeSBox(), + NbFullRounds: p.NbFullRounds, + NbPartialRounds: p.NbPartialRounds, + RoundKeys: make([][]big.Int, len(p.RoundKeys)), + } + for i := range res.RoundKeys { + res.RoundKeys[i] = make([]big.Int, len(p.RoundKeys[i])) + for j := range res.RoundKeys[i] { + p.RoundKeys[i][j].BigInt(&res.RoundKeys[i][j]) + } + } + return res, nil + case ecc.BW6_761: + p := poseidonbw6761.GetDefaultParameters() + res := Parameters{ + Width: p.Width, + DegreeSBox: poseidonbw6761.DegreeSBox(), + NbFullRounds: p.NbFullRounds, + NbPartialRounds: p.NbPartialRounds, + RoundKeys: make([][]big.Int, len(p.RoundKeys)), + } + for i := range res.RoundKeys { + res.RoundKeys[i] = make([]big.Int, len(p.RoundKeys[i])) + for j := range res.RoundKeys[i] { + p.RoundKeys[i][j].BigInt(&res.RoundKeys[i][j]) + } + } + return res, nil + case ecc.BW6_633: + p := poseidonbw6633.GetDefaultParameters() + res := Parameters{ + Width: p.Width, + DegreeSBox: poseidonbw6633.DegreeSBox(), + NbFullRounds: p.NbFullRounds, + NbPartialRounds: p.NbPartialRounds, + RoundKeys: make([][]big.Int, len(p.RoundKeys)), + } + for i := range res.RoundKeys { + res.RoundKeys[i] = make([]big.Int, len(p.RoundKeys[i])) + for j := range res.RoundKeys[i] { + p.RoundKeys[i][j].BigInt(&res.RoundKeys[i][j]) + } + } + return res, nil + case ecc.BLS24_315: + p := poseidonbls24315.GetDefaultParameters() + res := Parameters{ + Width: p.Width, + DegreeSBox: poseidonbls24315.DegreeSBox(), + NbFullRounds: p.NbFullRounds, + NbPartialRounds: p.NbPartialRounds, + RoundKeys: make([][]big.Int, len(p.RoundKeys)), + } + for i := range res.RoundKeys { + res.RoundKeys[i] = make([]big.Int, len(p.RoundKeys[i])) + for j := range res.RoundKeys[i] { + p.RoundKeys[i][j].BigInt(&res.RoundKeys[i][j]) + } + } + return res, nil + case ecc.BLS24_317: + p := poseidonbls24317.GetDefaultParameters() + res := Parameters{ + Width: p.Width, + DegreeSBox: poseidonbls24317.DegreeSBox(), + NbFullRounds: p.NbFullRounds, + NbPartialRounds: p.NbPartialRounds, + RoundKeys: make([][]big.Int, len(p.RoundKeys)), + } + for i := range res.RoundKeys { + res.RoundKeys[i] = make([]big.Int, len(p.RoundKeys[i])) + for j := range res.RoundKeys[i] { + p.RoundKeys[i][j].BigInt(&res.RoundKeys[i][j]) + } + } + return res, nil + default: + return Parameters{}, fmt.Errorf("curve %s not supported", curve) + } } // NewPoseidon2 returns a new Poseidon2 hasher with default parameters as // defined in the gnark-crypto library. func NewPoseidon2(api frontend.API) (*Permutation, error) { - switch utils.FieldToCurve(api.Compiler().Field()) { // TODO: assumes pairing based builder, reconsider when supporting other backends - case ecc.BLS12_377: - params := poseidonbls12377.GetDefaultParameters() - return NewPoseidon2FromParameters(api, 2, params.NbFullRounds, params.NbPartialRounds) - // TODO: we don't have default parameters for other curves yet. Update this when we do. - default: - return nil, fmt.Errorf("field %s not supported", api.Compiler().Field().String()) + params, err := GetDefaultParameters(utils.FieldToCurve(api.Compiler().Field())) + if err != nil { + return nil, err } + return &Permutation{ + api: api, + params: params, + }, nil } // NewPoseidon2FromParameters returns a new Poseidon2 hasher with the given parameters. @@ -62,76 +181,76 @@ func NewPoseidon2(api frontend.API) (*Permutation, error) { // is deterministic and depends on the curve ID. See the corresponding NewParameters // function in the gnark-crypto library poseidon2 packages for more details. func NewPoseidon2FromParameters(api frontend.API, width, nbFullRounds, nbPartialRounds int) (*Permutation, error) { - params := parameters{width: width, nbFullRounds: nbFullRounds, nbPartialRounds: nbPartialRounds} + params := Parameters{Width: width, NbFullRounds: nbFullRounds, NbPartialRounds: nbPartialRounds} switch utils.FieldToCurve(api.Compiler().Field()) { // TODO: assumes pairing based builder, reconsider when supporting other backends case ecc.BN254: - params.degreeSBox = poseidonbn254.DegreeSBox() + params.DegreeSBox = poseidonbn254.DegreeSBox() concreteParams := poseidonbn254.NewParameters(width, nbFullRounds, nbPartialRounds) - params.roundKeys = make([][]big.Int, len(concreteParams.RoundKeys)) - for i := range params.roundKeys { - params.roundKeys[i] = make([]big.Int, len(concreteParams.RoundKeys[i])) - for j := range params.roundKeys[i] { - concreteParams.RoundKeys[i][j].BigInt(¶ms.roundKeys[i][j]) + params.RoundKeys = make([][]big.Int, len(concreteParams.RoundKeys)) + for i := range params.RoundKeys { + params.RoundKeys[i] = make([]big.Int, len(concreteParams.RoundKeys[i])) + for j := range params.RoundKeys[i] { + concreteParams.RoundKeys[i][j].BigInt(¶ms.RoundKeys[i][j]) } } case ecc.BLS12_381: - params.degreeSBox = poseidonbls12381.DegreeSBox() + params.DegreeSBox = poseidonbls12381.DegreeSBox() concreteParams := poseidonbls12381.NewParameters(width, nbFullRounds, nbPartialRounds) - params.roundKeys = make([][]big.Int, len(concreteParams.RoundKeys)) - for i := range params.roundKeys { - params.roundKeys[i] = make([]big.Int, len(concreteParams.RoundKeys[i])) - for j := range params.roundKeys[i] { - concreteParams.RoundKeys[i][j].BigInt(¶ms.roundKeys[i][j]) + params.RoundKeys = make([][]big.Int, len(concreteParams.RoundKeys)) + for i := range params.RoundKeys { + params.RoundKeys[i] = make([]big.Int, len(concreteParams.RoundKeys[i])) + for j := range params.RoundKeys[i] { + concreteParams.RoundKeys[i][j].BigInt(¶ms.RoundKeys[i][j]) } } case ecc.BLS12_377: - params.degreeSBox = poseidonbls12377.DegreeSBox() + params.DegreeSBox = poseidonbls12377.DegreeSBox() concreteParams := poseidonbls12377.NewParameters(width, nbFullRounds, nbPartialRounds) - params.roundKeys = make([][]big.Int, len(concreteParams.RoundKeys)) - for i := range params.roundKeys { - params.roundKeys[i] = make([]big.Int, len(concreteParams.RoundKeys[i])) - for j := range params.roundKeys[i] { - concreteParams.RoundKeys[i][j].BigInt(¶ms.roundKeys[i][j]) + params.RoundKeys = make([][]big.Int, len(concreteParams.RoundKeys)) + for i := range params.RoundKeys { + params.RoundKeys[i] = make([]big.Int, len(concreteParams.RoundKeys[i])) + for j := range params.RoundKeys[i] { + concreteParams.RoundKeys[i][j].BigInt(¶ms.RoundKeys[i][j]) } } case ecc.BW6_761: - params.degreeSBox = poseidonbw6761.DegreeSBox() + params.DegreeSBox = poseidonbw6761.DegreeSBox() concreteParams := poseidonbw6761.NewParameters(width, nbFullRounds, nbPartialRounds) - params.roundKeys = make([][]big.Int, len(concreteParams.RoundKeys)) - for i := range params.roundKeys { - params.roundKeys[i] = make([]big.Int, len(concreteParams.RoundKeys[i])) - for j := range params.roundKeys[i] { - concreteParams.RoundKeys[i][j].BigInt(¶ms.roundKeys[i][j]) + params.RoundKeys = make([][]big.Int, len(concreteParams.RoundKeys)) + for i := range params.RoundKeys { + params.RoundKeys[i] = make([]big.Int, len(concreteParams.RoundKeys[i])) + for j := range params.RoundKeys[i] { + concreteParams.RoundKeys[i][j].BigInt(¶ms.RoundKeys[i][j]) } } case ecc.BW6_633: - params.degreeSBox = poseidonbw6633.DegreeSBox() + params.DegreeSBox = poseidonbw6633.DegreeSBox() concreteParams := poseidonbw6633.NewParameters(width, nbFullRounds, nbPartialRounds) - params.roundKeys = make([][]big.Int, len(concreteParams.RoundKeys)) - for i := range params.roundKeys { - params.roundKeys[i] = make([]big.Int, len(concreteParams.RoundKeys[i])) - for j := range params.roundKeys[i] { - concreteParams.RoundKeys[i][j].BigInt(¶ms.roundKeys[i][j]) + params.RoundKeys = make([][]big.Int, len(concreteParams.RoundKeys)) + for i := range params.RoundKeys { + params.RoundKeys[i] = make([]big.Int, len(concreteParams.RoundKeys[i])) + for j := range params.RoundKeys[i] { + concreteParams.RoundKeys[i][j].BigInt(¶ms.RoundKeys[i][j]) } } case ecc.BLS24_315: - params.degreeSBox = poseidonbls24315.DegreeSBox() + params.DegreeSBox = poseidonbls24315.DegreeSBox() concreteParams := poseidonbls24315.NewParameters(width, nbFullRounds, nbPartialRounds) - params.roundKeys = make([][]big.Int, len(concreteParams.RoundKeys)) - for i := range params.roundKeys { - params.roundKeys[i] = make([]big.Int, len(concreteParams.RoundKeys[i])) - for j := range params.roundKeys[i] { - concreteParams.RoundKeys[i][j].BigInt(¶ms.roundKeys[i][j]) + params.RoundKeys = make([][]big.Int, len(concreteParams.RoundKeys)) + for i := range params.RoundKeys { + params.RoundKeys[i] = make([]big.Int, len(concreteParams.RoundKeys[i])) + for j := range params.RoundKeys[i] { + concreteParams.RoundKeys[i][j].BigInt(¶ms.RoundKeys[i][j]) } } case ecc.BLS24_317: - params.degreeSBox = poseidonbls24317.DegreeSBox() + params.DegreeSBox = poseidonbls24317.DegreeSBox() concreteParams := poseidonbls24317.NewParameters(width, nbFullRounds, nbPartialRounds) - params.roundKeys = make([][]big.Int, len(concreteParams.RoundKeys)) - for i := range params.roundKeys { - params.roundKeys[i] = make([]big.Int, len(concreteParams.RoundKeys[i])) - for j := range params.roundKeys[i] { - concreteParams.RoundKeys[i][j].BigInt(¶ms.roundKeys[i][j]) + params.RoundKeys = make([][]big.Int, len(concreteParams.RoundKeys)) + for i := range params.RoundKeys { + params.RoundKeys[i] = make([]big.Int, len(concreteParams.RoundKeys[i])) + for j := range params.RoundKeys[i] { + concreteParams.RoundKeys[i][j].BigInt(¶ms.RoundKeys[i][j]) } } default: @@ -143,25 +262,25 @@ func NewPoseidon2FromParameters(api frontend.API, width, nbFullRounds, nbPartial // sBox applies the sBox on buffer[index] func (h *Permutation) sBox(index int, input []frontend.Variable) { tmp := input[index] - if h.params.degreeSBox == 3 { + if h.params.DegreeSBox == 3 { input[index] = h.api.Mul(input[index], input[index]) input[index] = h.api.Mul(tmp, input[index]) - } else if h.params.degreeSBox == 5 { + } else if h.params.DegreeSBox == 5 { input[index] = h.api.Mul(input[index], input[index]) input[index] = h.api.Mul(input[index], input[index]) input[index] = h.api.Mul(input[index], tmp) - } else if h.params.degreeSBox == 7 { + } else if h.params.DegreeSBox == 7 { input[index] = h.api.Mul(input[index], input[index]) input[index] = h.api.Mul(input[index], tmp) input[index] = h.api.Mul(input[index], input[index]) input[index] = h.api.Mul(input[index], tmp) - } else if h.params.degreeSBox == 17 { + } else if h.params.DegreeSBox == 17 { input[index] = h.api.Mul(input[index], input[index]) input[index] = h.api.Mul(input[index], input[index]) input[index] = h.api.Mul(input[index], input[index]) input[index] = h.api.Mul(input[index], input[index]) input[index] = h.api.Mul(input[index], tmp) - } else if h.params.degreeSBox == -1 { + } else if h.params.DegreeSBox == -1 { input[index] = h.api.Inverse(input[index]) } } @@ -204,30 +323,30 @@ func (h *Permutation) matMulM4InPlace(s []frontend.Variable) { // see https://eprint.iacr.org/2023/323.pdf func (h *Permutation) matMulExternalInPlace(input []frontend.Variable) { - if h.params.width == 2 { + if h.params.Width == 2 { tmp := h.api.Add(input[0], input[1]) input[0] = h.api.Add(tmp, input[0]) input[1] = h.api.Add(tmp, input[1]) - } else if h.params.width == 3 { + } else if h.params.Width == 3 { tmp := h.api.Add(input[0], input[1]) tmp = h.api.Add(tmp, input[2]) input[0] = h.api.Add(input[0], tmp) input[1] = h.api.Add(input[1], tmp) input[2] = h.api.Add(input[2], tmp) - } else if h.params.width == 4 { + } else if h.params.Width == 4 { h.matMulM4InPlace(input) } else { // at this stage t is supposed to be a multiple of 4 // the MDS matrix is circ(2M4,M4,..,M4) h.matMulM4InPlace(input) tmp := make([]frontend.Variable, 4) - for i := 0; i < h.params.width/4; i++ { + for i := 0; i < h.params.Width/4; i++ { tmp[0] = h.api.Add(tmp[0], input[4*i]) tmp[1] = h.api.Add(tmp[1], input[4*i+1]) tmp[2] = h.api.Add(tmp[2], input[4*i+2]) tmp[3] = h.api.Add(tmp[3], input[4*i+3]) } - for i := 0; i < h.params.width/4; i++ { + for i := 0; i < h.params.Width/4; i++ { input[4*i] = h.api.Add(input[4*i], tmp[0]) input[4*i+1] = h.api.Add(input[4*i], tmp[1]) input[4*i+2] = h.api.Add(input[4*i], tmp[2]) @@ -239,12 +358,12 @@ func (h *Permutation) matMulExternalInPlace(input []frontend.Variable) { // when t=2,3 the matrix are respectively [[2,1][1,3]] and [[2,1,1][1,2,1][1,1,3]] // otherwise the matrix is filled with ones except on the diagonal, func (h *Permutation) matMulInternalInPlace(input []frontend.Variable) { - if h.params.width == 2 { + if h.params.Width == 2 { sum := h.api.Add(input[0], input[1]) input[0] = h.api.Add(input[0], sum) input[1] = h.api.Mul(2, input[1]) input[1] = h.api.Add(input[1], sum) - } else if h.params.width == 3 { + } else if h.params.Width == 3 { sum := h.api.Add(input[0], input[1]) sum = h.api.Add(sum, input[2]) input[0] = h.api.Add(input[0], sum) @@ -259,10 +378,10 @@ func (h *Permutation) matMulInternalInPlace(input []frontend.Variable) { // var sum frontend.Variable // sum = input[0] - // for i := 1; i < h.params.width; i++ { + // for i := 1; i < h.params.Width; i++ { // sum = api.Add(sum, input[i]) // } - // for i := 0; i < h.params.width; i++ { + // for i := 0; i < h.params.Width; i++ { // input[i] = api.Mul(input[i], h.params.diagInternalMatrices[i]) // input[i] = api.Add(input[i], sum) // } @@ -272,40 +391,40 @@ func (h *Permutation) matMulInternalInPlace(input []frontend.Variable) { // addRoundKeyInPlace adds the round-th key to the buffer func (h *Permutation) addRoundKeyInPlace(round int, input []frontend.Variable) { - for i := 0; i < len(h.params.roundKeys[round]); i++ { - input[i] = h.api.Add(input[i], h.params.roundKeys[round][i]) + for i := 0; i < len(h.params.RoundKeys[round]); i++ { + input[i] = h.api.Add(input[i], h.params.RoundKeys[round][i]) } } // Permutation applies the permutation on input, and stores the result in input. func (h *Permutation) Permutation(input []frontend.Variable) error { - if len(input) != h.params.width { + if len(input) != h.params.Width { return ErrInvalidSizebuffer } // external matrix multiplication, cf https://eprint.iacr.org/2023/323.pdf page 14 (part 6) h.matMulExternalInPlace(input) - rf := h.params.nbFullRounds / 2 + rf := h.params.NbFullRounds / 2 for i := 0; i < rf; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.width; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) } - for i := rf; i < rf+h.params.nbPartialRounds; i++ { + for i := rf; i < rf+h.params.NbPartialRounds; i++ { // one round = matMulInternal(sBox_sparse(addRoundKey)) h.addRoundKeyInPlace(i, input) h.sBox(0, input) h.matMulInternalInPlace(input) } - for i := rf + h.params.nbPartialRounds; i < h.params.nbFullRounds+h.params.nbPartialRounds; i++ { + for i := rf + h.params.NbPartialRounds; i < h.params.NbFullRounds+h.params.NbPartialRounds; i++ { // one round = matMulExternal(sBox_Full(addRoundKey)) h.addRoundKeyInPlace(i, input) - for j := 0; j < h.params.width; j++ { + for j := 0; j < h.params.Width; j++ { h.sBox(j, input) } h.matMulExternalInPlace(input) @@ -321,7 +440,7 @@ func (h *Permutation) Permutation(input []frontend.Variable) error { // Implements the [hash.Compressor] interface for building a Merkle-Damgard // hash construction. func (h *Permutation) Compress(left, right frontend.Variable) frontend.Variable { - if h.params.width != 2 { + if h.params.Width != 2 { panic("poseidon2: Compress can only be used when t=2") } vars := [2]frontend.Variable{left, right} From 4765d61a173989114a1ca98daa9d19d18c0f7798 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 8 Jun 2025 14:19:38 -0500 Subject: [PATCH 036/251] revert incorrect renaming --- std/permutation/poseidon2/gkr-poseidon2/gkr.go | 10 +++++----- std/permutation/poseidon2/gkr-poseidon2/gkr_test.go | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr.go b/std/permutation/poseidon2/gkr-poseidon2/gkr.go index d9a7dcfbbb..800e79ed05 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr.go @@ -108,17 +108,17 @@ func extAddGate(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Add(api.Mul(x[0], 2), x[1], x[2]) } -type GkrPermutations struct { +type GkrCompressor struct { api frontend.API gkrCircuit *gkrapi.Circuit in1, in2, out gkr.Variable } -// NewGkrPermutations returns an object that can compute the Poseidon2 compression function (currently only for BLS12-377) +// NewGkrCompressor returns an object that can compute the Poseidon2 compression function (currently only for BLS12-377) // which consists of a permutation along with the input fed forward. // The correctness of the compression functions is proven using GKR. // Note that the solver will need the function RegisterGkrGates to be called with the desired curves -func NewGkrPermutations(api frontend.API) *GkrPermutations { +func NewGkrCompressor(api frontend.API) *GkrCompressor { if api.Compiler().Field().Cmp(ecc.BLS12_377.ScalarField()) != 0 { panic("currently only BL12-377 is supported") } @@ -126,7 +126,7 @@ func NewGkrPermutations(api frontend.API) *GkrPermutations { if err != nil { panic(fmt.Errorf("failed to define GKR circuit: %v", err)) } - return &GkrPermutations{ + return &GkrCompressor{ api: api, gkrCircuit: gkrApi.Compile(api, "MIMC"), in1: in1, @@ -135,7 +135,7 @@ func NewGkrPermutations(api frontend.API) *GkrPermutations { } } -func (p *GkrPermutations) Compress(a, b frontend.Variable) frontend.Variable { +func (p *GkrCompressor) Compress(a, b frontend.Variable) frontend.Variable { outs, err := p.gkrCircuit.AddInstance(map[gkr.Variable]frontend.Variable{p.in1: a, p.in2: b}) if err != nil { panic(err) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go index 601d80cb70..ffa60d8ccb 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go @@ -56,7 +56,7 @@ type testGkrPermutationCircuit struct { func (c *testGkrPermutationCircuit) Define(api frontend.API) error { - pos2 := NewGkrPermutations(api) + pos2 := NewGkrCompressor(api) api.AssertIsEqual(len(c.Ins), len(c.Outs)) for i := range c.Ins { api.AssertIsEqual(c.Outs[i], pos2.Compress(c.Ins[i][0], c.Ins[i][1])) From 5342952398b3c4aa7e45ea3edcc37b3eab85293d Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 8 Jun 2025 14:20:46 -0500 Subject: [PATCH 037/251] fix test --- std/permutation/poseidon2/gkr-poseidon2/gkr_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go index 2d9ad7ccb5..9808d5f8dc 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go @@ -8,6 +8,7 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" + poseidonbls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/poseidon2" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/scs" _ "github.com/consensys/gnark/std/hash/all" @@ -19,6 +20,8 @@ func gkrPermutationsCircuits(t require.TestingT, n int) (circuit, assignment tes var k int64 ins := make([][2]frontend.Variable, n) outs := make([]frontend.Variable, n) + params := poseidonbls12377.GetDefaultParameters() + permutation := poseidonbls12377.NewPermutation(params.Width, params.NbFullRounds, params.NbPartialRounds) for i := range n { var x [2]fr.Element ins[i] = [2]frontend.Variable{k, k + 1} @@ -27,7 +30,7 @@ func gkrPermutationsCircuits(t require.TestingT, n int) (circuit, assignment tes x[1].SetInt64(k + 1) y0 := x[1] - require.NoError(t, bls12377Permutation().Permutation(x[:])) + require.NoError(t, permutation.Permutation(x[:])) x[1].Add(&x[1], &y0) outs[i] = x[1] From 9d9b13b0cc833e503fc9abb693e60959f410cd1e Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 8 Jun 2025 14:26:10 -0500 Subject: [PATCH 038/251] refactor generify tests --- .../poseidon2/gkr-poseidon2/gkr.go | 8 +-- .../poseidon2/gkr-poseidon2/gkr_test.go | 55 ++++++++----------- 2 files changed, 27 insertions(+), 36 deletions(-) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr.go b/std/permutation/poseidon2/gkr-poseidon2/gkr.go index dcc51a4031..c87cd450e9 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr.go @@ -119,13 +119,13 @@ type GkrPermutations struct { // which consists of a permutation along with the input fed forward. // The correctness of the compression functions is proven using GKR. // Note that the solver will need the function RegisterGates to be called with the desired curves -func NewGkrPermutations(api frontend.API) *GkrPermutations { +func NewGkrPermutations(api frontend.API) (*GkrPermutations, error) { if api.Compiler().Field().Cmp(ecc.BLS12_377.ScalarField()) != 0 { - panic("currently only BL12-377 is supported") + return nil, errors.New("currently only BL12-377 is supported") } gkrCircuit, in1, in2, out, err := defineCircuit(api) if err != nil { - panic(fmt.Errorf("failed to define GKR circuit: %w", err)) + return nil, fmt.Errorf("failed to define GKR circuit: %w", err) } return &GkrPermutations{ api: api, @@ -133,7 +133,7 @@ func NewGkrPermutations(api frontend.API) *GkrPermutations { in1: in1, in2: in2, out: out, - } + }, nil } func (p *GkrPermutations) Compress(a, b frontend.Variable) frontend.Variable { diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go index 9808d5f8dc..b510663959 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go @@ -7,62 +7,53 @@ import ( "testing" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" - poseidonbls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/poseidon2" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/scs" _ "github.com/consensys/gnark/std/hash/all" + "github.com/consensys/gnark/std/permutation/poseidon2" "github.com/consensys/gnark/test" "github.com/stretchr/testify/require" ) -func gkrPermutationsCircuits(t require.TestingT, n int) (circuit, assignment testGkrPermutationCircuit) { - var k int64 +func gkrPermutationsCircuits(n int) (circuit, assignment testGkrPermutationCircuit) { ins := make([][2]frontend.Variable, n) - outs := make([]frontend.Variable, n) - params := poseidonbls12377.GetDefaultParameters() - permutation := poseidonbls12377.NewPermutation(params.Width, params.NbFullRounds, params.NbPartialRounds) for i := range n { - var x [2]fr.Element - ins[i] = [2]frontend.Variable{k, k + 1} - - x[0].SetInt64(k) - x[1].SetInt64(k + 1) - y0 := x[1] - - require.NoError(t, permutation.Permutation(x[:])) - x[1].Add(&x[1], &y0) - outs[i] = x[1] - - k += 2 + ins[i] = [2]frontend.Variable{i * 2, i*2 + 1} } return testGkrPermutationCircuit{ - Ins: make([][2]frontend.Variable, len(ins)), - Outs: make([]frontend.Variable, len(outs)), + Ins: make([][2]frontend.Variable, len(ins)), }, testGkrPermutationCircuit{ - Ins: ins, - Outs: outs, + Ins: ins, } } func TestGkrCompression(t *testing.T) { - circuit, assignment := gkrPermutationsCircuits(t, 2) + circuit, assignment := gkrPermutationsCircuits(2) test.NewAssert(t).CheckCircuit(&circuit, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BLS12_377)) } type testGkrPermutationCircuit struct { - Ins [][2]frontend.Variable - Outs []frontend.Variable + Ins [][2]frontend.Variable + skipCheck bool } func (c *testGkrPermutationCircuit) Define(api frontend.API) error { - pos2 := NewGkrPermutations(api) - api.AssertIsEqual(len(c.Ins), len(c.Outs)) + gkr, err := NewGkrPermutations(api) + if err != nil { + return err + } + pos2, err := poseidon2.NewPoseidon2(api) + if err != nil { + return err + } for i := range c.Ins { - api.AssertIsEqual(c.Outs[i], pos2.Compress(c.Ins[i][0], c.Ins[i][1])) + fromGkr := gkr.Compress(c.Ins[i][0], c.Ins[i][1]) + if !c.skipCheck { + api.AssertIsEqual(pos2.Compress(c.Ins[i][0], c.Ins[i][1]), fromGkr) + } } return nil @@ -71,15 +62,15 @@ func (c *testGkrPermutationCircuit) Define(api frontend.API) error { func TestGkrPermutationCompiles(t *testing.T) { // just measure the number of constraints cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &testGkrPermutationCircuit{ - Ins: make([][2]frontend.Variable, 52000), - Outs: make([]frontend.Variable, 52000), + Ins: make([][2]frontend.Variable, 52000), + skipCheck: true, }) require.NoError(t, err) fmt.Println(cs.GetNbConstraints(), "constraints") } func BenchmarkGkrPermutations(b *testing.B) { - circuit, assignment := gkrPermutationsCircuits(b, 50000) + circuit, assignment := gkrPermutationsCircuits(50000) cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &circuit) require.NoError(b, err) From 430661accfe2fc202f86c249d0de1f08d32b74df Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 8 Jun 2025 14:36:56 -0500 Subject: [PATCH 039/251] feat: gkrposeidon2 compression for all curves --- .../poseidon2/gkr-poseidon2/gkr.go | 26 ++++++++++++++----- .../poseidon2/gkr-poseidon2/gkr_test.go | 2 +- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr.go b/std/permutation/poseidon2/gkr-poseidon2/gkr.go index c87cd450e9..fea7608ac6 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr.go @@ -47,6 +47,14 @@ func pow4TimesGate(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Mul(y, x[1]) } +// pow3Gate computes a -> a³ +func pow3Gate(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { + if len(x) != 1 { + panic("expected 1 input") + } + return api.Mul(x[0], x[0], x[0]) +} + // pow2Gate computes a -> a² func pow2Gate(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { if len(x) != 1 { @@ -120,9 +128,6 @@ type GkrPermutations struct { // The correctness of the compression functions is proven using GKR. // Note that the solver will need the function RegisterGates to be called with the desired curves func NewGkrPermutations(api frontend.API) (*GkrPermutations, error) { - if api.Compiler().Field().Cmp(ecc.BLS12_377.ScalarField()) != 0 { - return nil, errors.New("currently only BL12-377 is supported") - } gkrCircuit, in1, in2, out, err := defineCircuit(api) if err != nil { return nil, fmt.Errorf("failed to define GKR circuit: %w", err) @@ -182,10 +187,19 @@ func defineCircuit(api frontend.API) (gkrCircuit *gkrapi.Circuit, in1, in2, out // in every round comes from the previous (canonical) round. // apply the s-Box to u - // the s-Box gates: u¹⁷ = (u⁴)⁴ * u var sBox func(gkr.Variable) gkr.Variable switch p.DegreeSBox { + case 5: + sBox = func(u gkr.Variable) gkr.Variable { + v := gkrApi.Gate(pow2Gate, u) // u² + return gkrApi.Gate(pow2TimesGate, v, u) // u⁵ + } + case 7: + sBox = func(u gkr.Variable) gkr.Variable { + v := gkrApi.Gate(pow3Gate, u) // u³ + return gkrApi.Gate(pow2TimesGate, v, u) // u⁷ + } case 17: sBox = func(u gkr.Variable) gkr.Variable { v := gkrApi.Gate(pow4Gate, u) // u⁴ @@ -282,12 +296,12 @@ func registerGates(p *poseidon2.Parameters, curve ecc.ID) error { halfRf := p.NbFullRounds / 2 extKeySBox := func(round int, varIndex int) error { - _, err := gkrgates.Register(extKeyGate(&p.RoundKeys[round][varIndex]), 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(varIndex, round)), gkrgates.WithCurves(ecc.BLS12_377)) + _, err := gkrgates.Register(extKeyGate(&p.RoundKeys[round][varIndex]), 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(varIndex, round)), gkrgates.WithCurves(curve)) return err } intKeySBox2 := func(round int) error { - _, err := gkrgates.Register(intKeyGate2(&p.RoundKeys[round][1]), 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(y, round)), gkrgates.WithCurves(ecc.BLS12_377)) + _, err := gkrgates.Register(intKeyGate2(&p.RoundKeys[round][1]), 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(y, round)), gkrgates.WithCurves(curve)) return err } diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go index b510663959..592b9abf7c 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go @@ -31,7 +31,7 @@ func gkrPermutationsCircuits(n int) (circuit, assignment testGkrPermutationCircu func TestGkrCompression(t *testing.T) { circuit, assignment := gkrPermutationsCircuits(2) - test.NewAssert(t).CheckCircuit(&circuit, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BLS12_377)) + test.NewAssert(t).CheckCircuit(&circuit, test.WithValidAssignment(&assignment)) } type testGkrPermutationCircuit struct { From b75843dec8c8546984630e4d8372aad8fb6f94e7 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 8 Jun 2025 14:44:18 -0500 Subject: [PATCH 040/251] feat gkr-poseidon2 hasher --- .../poseidon2/gkr-poseidon2/gkr-poseidon2.go | 18 ++++++++++++++++++ std/hash/poseidon2/poseidon2.go | 6 +++--- std/hash/poseidon2/poseidon2_test.go | 16 ++++++++++++---- 3 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go diff --git a/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go b/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go new file mode 100644 index 0000000000..db1566bbc5 --- /dev/null +++ b/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go @@ -0,0 +1,18 @@ +package gkr_poseidon2 + +import ( + "fmt" + + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/hash" + _ "github.com/consensys/gnark/std/hash/all" + gkr_poseidon2 "github.com/consensys/gnark/std/permutation/poseidon2/gkr-poseidon2" +) + +func NewGkrPoseidon2(api frontend.API) (hash.FieldHasher, error) { + f, err := gkr_poseidon2.NewGkrPermutations(api) + if err != nil { + return nil, fmt.Errorf("could not create poseidon2 hasher: %w", err) + } + return hash.NewMerkleDamgardHasher(api, f, 0), nil +} diff --git a/std/hash/poseidon2/poseidon2.go b/std/hash/poseidon2/poseidon2.go index 804740ff7c..a5b562fcb2 100644 --- a/std/hash/poseidon2/poseidon2.go +++ b/std/hash/poseidon2/poseidon2.go @@ -8,9 +8,9 @@ import ( "github.com/consensys/gnark/std/permutation/poseidon2" ) -// NewMerkleDamgardHasher returns a Poseidon2 hasher using the Merkle-Damgard +// NewPoseidon2 returns a Poseidon2 hasher using the Merkle-Damgard // construction with the default parameters. -func NewMerkleDamgardHasher(api frontend.API) (hash.FieldHasher, error) { +func NewPoseidon2(api frontend.API) (hash.FieldHasher, error) { f, err := poseidon2.NewPoseidon2(api) if err != nil { return nil, fmt.Errorf("could not create poseidon2 hasher: %w", err) @@ -19,5 +19,5 @@ func NewMerkleDamgardHasher(api frontend.API) (hash.FieldHasher, error) { } func init() { - hash.Register(hash.POSEIDON2, NewMerkleDamgardHasher) + hash.Register(hash.POSEIDON2, NewPoseidon2) } diff --git a/std/hash/poseidon2/poseidon2_test.go b/std/hash/poseidon2/poseidon2_test.go index 1ce1d46fef..4a5374258c 100644 --- a/std/hash/poseidon2/poseidon2_test.go +++ b/std/hash/poseidon2/poseidon2_test.go @@ -1,11 +1,13 @@ -package poseidon2 +package poseidon2_test import ( "testing" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/poseidon2" + poseidonbls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/poseidon2" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/hash/poseidon2" + gkr_poseidon2 "github.com/consensys/gnark/std/hash/poseidon2/gkr-poseidon2" "github.com/consensys/gnark/test" ) @@ -15,12 +17,18 @@ type Poseidon2Circuit struct { } func (c *Poseidon2Circuit) Define(api frontend.API) error { - hsh, err := NewMerkleDamgardHasher(api) + hsh, err := poseidon2.NewPoseidon2(api) + if err != nil { + return err + } + gkr, err := gkr_poseidon2.NewGkrPoseidon2(api) if err != nil { return err } hsh.Write(c.Input...) api.AssertIsEqual(hsh.Sum(), c.Expected) + gkr.Write(c.Input...) + api.AssertIsEqual(gkr.Sum(), c.Expected) return nil } @@ -29,7 +37,7 @@ func TestPoseidon2Hash(t *testing.T) { const nbInputs = 5 // prepare expected output - h := poseidon2.NewMerkleDamgardHasher() + h := poseidonbls12377.NewMerkleDamgardHasher() circInput := make([]frontend.Variable, nbInputs) for i := range nbInputs { _, err := h.Write([]byte{byte(i)}) From be8a07e29dc7019aca7370a9a480d7ec3d6c2ac8 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 8 Jun 2025 14:49:38 -0500 Subject: [PATCH 041/251] fix more renaming --- std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go | 4 ++-- std/hash/poseidon2/poseidon2.go | 6 +++--- std/hash/poseidon2/poseidon2_test.go | 4 ++-- std/internal/mimc/encrypt.go | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go b/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go index db1566bbc5..ffc59dee74 100644 --- a/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go +++ b/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go @@ -9,8 +9,8 @@ import ( gkr_poseidon2 "github.com/consensys/gnark/std/permutation/poseidon2/gkr-poseidon2" ) -func NewGkrPoseidon2(api frontend.API) (hash.FieldHasher, error) { - f, err := gkr_poseidon2.NewGkrPermutations(api) +func New(api frontend.API) (hash.FieldHasher, error) { + f, err := gkr_poseidon2.NewGkrCompressor(api) if err != nil { return nil, fmt.Errorf("could not create poseidon2 hasher: %w", err) } diff --git a/std/hash/poseidon2/poseidon2.go b/std/hash/poseidon2/poseidon2.go index a5b562fcb2..f53b8716f3 100644 --- a/std/hash/poseidon2/poseidon2.go +++ b/std/hash/poseidon2/poseidon2.go @@ -8,9 +8,9 @@ import ( "github.com/consensys/gnark/std/permutation/poseidon2" ) -// NewPoseidon2 returns a Poseidon2 hasher using the Merkle-Damgard +// New returns a Poseidon2 hasher using the Merkle-Damgard // construction with the default parameters. -func NewPoseidon2(api frontend.API) (hash.FieldHasher, error) { +func New(api frontend.API) (hash.FieldHasher, error) { f, err := poseidon2.NewPoseidon2(api) if err != nil { return nil, fmt.Errorf("could not create poseidon2 hasher: %w", err) @@ -19,5 +19,5 @@ func NewPoseidon2(api frontend.API) (hash.FieldHasher, error) { } func init() { - hash.Register(hash.POSEIDON2, NewPoseidon2) + hash.Register(hash.POSEIDON2, New) } diff --git a/std/hash/poseidon2/poseidon2_test.go b/std/hash/poseidon2/poseidon2_test.go index 4a5374258c..c3998ccc5b 100644 --- a/std/hash/poseidon2/poseidon2_test.go +++ b/std/hash/poseidon2/poseidon2_test.go @@ -17,11 +17,11 @@ type Poseidon2Circuit struct { } func (c *Poseidon2Circuit) Define(api frontend.API) error { - hsh, err := poseidon2.NewPoseidon2(api) + hsh, err := poseidon2.New(api) if err != nil { return err } - gkr, err := gkr_poseidon2.NewGkrPoseidon2(api) + gkr, err := gkr_poseidon2.New(api) if err != nil { return err } diff --git a/std/internal/mimc/encrypt.go b/std/internal/mimc/encrypt.go index 0d45a81506..9c499be976 100644 --- a/std/internal/mimc/encrypt.go +++ b/std/internal/mimc/encrypt.go @@ -106,7 +106,7 @@ func newMimcBW633(api frontend.API) MiMC { } // ------------------------------------------------------------------------------------------------- -// encryptions functions +// encryption functions func pow5(api frontend.API, x frontend.Variable) frontend.Variable { r := api.Mul(x, x) From 1894cfafae5c97778c8eef7eb89637f0a8221049 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 9 Jun 2025 17:49:28 -0500 Subject: [PATCH 042/251] feat: gkrmimc for sbox degree 5 --- std/permutation/gkr-mimc/gkr-mimc.go | 143 ++++++++++++++++++ .../{gkr.go => gkr-poseidon2.go} | 0 .../{gkr_test.go => gkr-poseidon2_test.go} | 0 3 files changed, 143 insertions(+) create mode 100644 std/permutation/gkr-mimc/gkr-mimc.go rename std/permutation/poseidon2/gkr-poseidon2/{gkr.go => gkr-poseidon2.go} (100%) rename std/permutation/poseidon2/gkr-poseidon2/{gkr_test.go => gkr-poseidon2_test.go} (100%) diff --git a/std/permutation/gkr-mimc/gkr-mimc.go b/std/permutation/gkr-mimc/gkr-mimc.go new file mode 100644 index 0000000000..f495f2b981 --- /dev/null +++ b/std/permutation/gkr-mimc/gkr-mimc.go @@ -0,0 +1,143 @@ +package gkr_mimc + +import ( + "fmt" + "math/big" + + "github.com/consensys/gnark-crypto/ecc" + bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/mimc" + bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/mimc" + bls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/mimc" + bls24317 "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/mimc" + bn254 "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" + bw6633 "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/mimc" + bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/mimc" + "github.com/consensys/gnark/constraint/solver/gkrgates" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/utils" + "github.com/consensys/gnark/std/gkrapi" + "github.com/consensys/gnark/std/gkrapi/gkr" +) + +// mimcCompressor implements a compression function by applying +// the Miyaguchi–Preneel transformation to the MiMC encryption function. +type mimcCompressor struct { + gkrCircuit *gkrapi.Circuit + in0, in1, out gkr.Variable +} + +func newGkrCompressor(api frontend.API) (*mimcCompressor, error) { + gkrApi := gkrapi.New() + + in0 := gkrApi.NewInput() + in1 := gkrApi.NewInput() + + y := in1 + + curve := utils.FieldToCurve(api.Compiler().Field()) + params, _, err := getParams(curve) // params is only used for its length + if err != nil { + return nil, err + } + if err = RegisterGates(curve); err != nil { + return nil, err + } + gateNamer := newGateNamer(curve) + + for i := range len(params) - 1 { + y = gkrApi.NamedGate(gateNamer.round(i), in0, y) + } + + y = gkrApi.NamedGate(gateNamer.round(len(params)-1), in0, y, in1) + + return &mimcCompressor{ + gkrCircuit: gkrApi.Compile(api, "poseidon2"), + in0: in0, + in1: in1, + out: y, + }, nil +} + +func RegisterGates(curves ...ecc.ID) error { + for _, curve := range curves { + constants, deg, err := getParams(curve) + if err != nil { + return err + } + gateNamer := newGateNamer(curve) + var lastLayerSBox, nonLastLayerSBox func(*big.Int) gkr.GateFunction + switch deg { + case 5: + lastLayerSBox = addPow5Add + nonLastLayerSBox = addPow5 + default: + return fmt.Errorf("s-Box of degree %d not supported", deg) + } + + for i := range len(constants) - 1 { + if _, err = gkrgates.Register(nonLastLayerSBox(&constants[i]), 2, gkrgates.WithName(gateNamer.round(i)), gkrgates.WithUnverifiedDegree(deg), gkrgates.WithCurves(curve)); err != nil { + return fmt.Errorf("failed to register keyed GKR gate for round %d of MiMC on curve %s: %w", i, curve, err) + } + } + + if _, err = gkrgates.Register(lastLayerSBox(&constants[len(constants)-1]), 3, gkrgates.WithName(gateNamer.round(len(constants)-1)), gkrgates.WithUnverifiedDegree(deg), gkrgates.WithCurves(curve)); err != nil { + return fmt.Errorf("failed to register keyed GKR gate for round %d of MiMC on curve %s: %w", len(constants)-1, curve, err) + } + } + return nil +} + +// getParams returns the parameters for the MiMC encryption function for the given curve. +// It also returns the degree of the s-Box +func getParams(curve ecc.ID) ([]big.Int, int, error) { + switch curve { + case ecc.BN254: + return bn254.GetConstants(), 5, nil + case ecc.BLS12_381: + return bls12381.GetConstants(), 5, nil + case ecc.BLS12_377: + return bls12377.GetConstants(), 17, nil + case ecc.BLS24_315: + return bls24315.GetConstants(), 5, nil + case ecc.BLS24_317: + return bls24317.GetConstants(), 7, nil + case ecc.BW6_633: + return bw6633.GetConstants(), 5, nil + case ecc.BW6_761: + return bw6761.GetConstants(), 5, nil + default: + return nil, -1, fmt.Errorf("unsupported curve ID: %s", curve) + } +} + +type gateNamer string + +func newGateNamer(o fmt.Stringer) gateNamer { + return gateNamer("MiMC-" + o.String() + "-round-") +} +func (n gateNamer) round(i int) gkr.GateName { + return gkr.GateName(fmt.Sprintf("%s%d", string(n), i)) +} + +func addPow5(key *big.Int) gkr.GateFunction { + return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + if len(in) != 2 { + panic("expected two input") + } + s := api.Add(in[0], in[1], key) + t := api.Mul(s, s) + return api.Mul(t, t, s) + } +} + +// addPow5Add: (in[0]+in[1]+key)⁵ + in[0] + in[2] +func addPow5Add(key *big.Int) gkr.GateFunction { + return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + if len(in) != 3 { + panic("expected three input") + } + s := api.Add(in[0], in[1], key) + t := api.Mul(s, s) + return api.Add(api.Mul(t, t, s), in[0], in[2]) + } +} diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr.go b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go similarity index 100% rename from std/permutation/poseidon2/gkr-poseidon2/gkr.go rename to std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go similarity index 100% rename from std/permutation/poseidon2/gkr-poseidon2/gkr_test.go rename to std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go From 241d64533f491642261b5945029d4ac128ccf7da Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 9 Jun 2025 22:19:50 -0500 Subject: [PATCH 043/251] mimc length 1 works --- std/hash/mimc/gkr-mimc/gkr-mimc.go | 17 ++++++ std/hash/mimc/gkr-mimc/gkr-mimc_test.go | 66 ++++++++++++++++++++++ std/permutation/gkr-mimc/gkr-mimc.go | 73 ++++++++++++++++++++++++- 3 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 std/hash/mimc/gkr-mimc/gkr-mimc.go create mode 100644 std/hash/mimc/gkr-mimc/gkr-mimc_test.go diff --git a/std/hash/mimc/gkr-mimc/gkr-mimc.go b/std/hash/mimc/gkr-mimc/gkr-mimc.go new file mode 100644 index 0000000000..26be877f41 --- /dev/null +++ b/std/hash/mimc/gkr-mimc/gkr-mimc.go @@ -0,0 +1,17 @@ +package gkr_mimc + +import ( + "fmt" + + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/hash" + gkr_mimc "github.com/consensys/gnark/std/permutation/gkr-mimc" +) + +func New(api frontend.API) (hash.FieldHasher, error) { + f, err := gkr_mimc.NewCompressor(api) + if err != nil { + return nil, fmt.Errorf("could not create mimc hasher: %w", err) + } + return hash.NewMerkleDamgardHasher(api, f, 0), nil +} diff --git a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go new file mode 100644 index 0000000000..aa558dfd96 --- /dev/null +++ b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go @@ -0,0 +1,66 @@ +package gkr_mimc + +import ( + "slices" + "testing" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/hash/mimc" + "github.com/consensys/gnark/test" +) + +func TestGkrMiMC(t *testing.T) { + lengths := []int{1, 2, 3} + vals := make([]frontend.Variable, len(lengths)*2) + for i := range vals { + vals[i] = i + 1 + } + + for _, length := range lengths[1:2] { + circuit := &testGkrMiMCCircuit{ + In: make([]frontend.Variable, length*2), + } + assignment := &testGkrMiMCCircuit{ + In: slices.Clone(vals[:length*2]), + } + + test.NewAssert(t).CheckCircuit(circuit, test.WithValidAssignment(assignment), test.WithCurves(ecc.BN254)) + } +} + +type testGkrMiMCCircuit struct { + In []frontend.Variable + skipCheck bool +} + +func (c *testGkrMiMCCircuit) Define(api frontend.API) error { + gkrmimc, err := New(api) + if err != nil { + return err + } + + plainMiMC, err := mimc.New(api) + if err != nil { + return err + } + + // first check that empty input is handled correctly + api.AssertIsEqual(gkrmimc.Sum(), plainMiMC.Sum()) + + ins := [][]frontend.Variable{c.In[:len(c.In)/2], c.In[len(c.In)/2:]} + for _, in := range ins { + gkrmimc.Reset() + gkrmimc.Write(in...) + res := gkrmimc.Sum() + + if !c.skipCheck { + plainMiMC.Reset() + plainMiMC.Write(in...) + expected := plainMiMC.Sum() + api.AssertIsEqual(res, expected) + } + } + + return nil +} diff --git a/std/permutation/gkr-mimc/gkr-mimc.go b/std/permutation/gkr-mimc/gkr-mimc.go index f495f2b981..c1d98d67f1 100644 --- a/std/permutation/gkr-mimc/gkr-mimc.go +++ b/std/permutation/gkr-mimc/gkr-mimc.go @@ -17,6 +17,8 @@ import ( "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi" "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/hash" + _ "github.com/consensys/gnark/std/hash/all" ) // mimcCompressor implements a compression function by applying @@ -26,7 +28,15 @@ type mimcCompressor struct { in0, in1, out gkr.Variable } -func newGkrCompressor(api frontend.API) (*mimcCompressor, error) { +func (c *mimcCompressor) Compress(x frontend.Variable, y frontend.Variable) frontend.Variable { + res, err := c.gkrCircuit.AddInstance(map[gkr.Variable]frontend.Variable{c.in0: x, c.in1: y}) + if err != nil { + panic(err) + } + return res[c.out] +} + +func NewCompressor(api frontend.API) (hash.Compressor, error) { gkrApi := gkrapi.New() in0 := gkrApi.NewInput() @@ -51,7 +61,7 @@ func newGkrCompressor(api frontend.API) (*mimcCompressor, error) { y = gkrApi.NamedGate(gateNamer.round(len(params)-1), in0, y, in1) return &mimcCompressor{ - gkrCircuit: gkrApi.Compile(api, "poseidon2"), + gkrCircuit: gkrApi.Compile(api, "POSEIDON2"), in0: in0, in1: in1, out: y, @@ -70,6 +80,12 @@ func RegisterGates(curves ...ecc.ID) error { case 5: lastLayerSBox = addPow5Add nonLastLayerSBox = addPow5 + case 7: + lastLayerSBox = addPow7Add + nonLastLayerSBox = addPow7 + case 17: + lastLayerSBox = addPow17Add + nonLastLayerSBox = addPow17 default: return fmt.Errorf("s-Box of degree %d not supported", deg) } @@ -141,3 +157,56 @@ func addPow5Add(key *big.Int) gkr.GateFunction { return api.Add(api.Mul(t, t, s), in[0], in[2]) } } + +func addPow7(key *big.Int) gkr.GateFunction { + return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + if len(in) != 2 { + panic("expected two input") + } + s := api.Add(in[0], in[1], key) + t := api.Mul(s, s) + return api.Mul(t, t, t, s) // s⁶ × s + } +} + +// addPow7Add: (in[0]+in[1]+key)⁷ + in[0] + in[2] +func addPow7Add(key *big.Int) gkr.GateFunction { + return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + if len(in) != 3 { + panic("expected three input") + } + s := api.Add(in[0], in[1], key) + t := api.Mul(s, s) + return api.Add(api.Mul(t, t, t, s), in[0], in[2]) // s⁶ × s + in[0] + in[2] + } +} + +// addPow17: (in[0]+in[1]+key)¹⁷ +func addPow17(key *big.Int) gkr.GateFunction { + return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + if len(in) != 2 { + panic("expected two input") + } + s := api.Add(in[0], in[1], key) + t := api.Mul(s, s) // s² + t = api.Mul(t, t) // s⁴ + t = api.Mul(t, t) // s⁸ + t = api.Mul(t, t) // s¹⁶ + return api.Mul(t, s) // s¹⁶ × s + } +} + +// addPow17Add: (in[0]+in[1]+key)¹⁷ + in[0] + in[2] +func addPow17Add(key *big.Int) gkr.GateFunction { + return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + if len(in) != 3 { + panic("expected three input") + } + s := api.Add(in[0], in[1], key) + t := api.Mul(s, s) // s² + t = api.Mul(t, t) // s⁴ + t = api.Mul(t, t) // s⁸ + t = api.Mul(t, t) // s¹⁶ + return api.Add(api.Mul(t, s), in[0], in[2]) // s¹⁶ × s + in[0] + in[2] + } +} From bb3645966028a73f97e9f8a716697b8ffd47b1b6 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 10 Jun 2025 16:20:36 -0500 Subject: [PATCH 044/251] fix final layer --- internal/gkr/bn254/gkr.go | 4 ++-- internal/gkr/engine_hints.go | 2 +- std/hash/mimc/gkr-mimc/gkr-mimc_test.go | 17 +++++++++++++++-- std/permutation/gkr-mimc/gkr-mimc.go | 20 +++++++++++--------- test/engine.go | 2 +- 5 files changed, 30 insertions(+), 15 deletions(-) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 14269151b3..0174caa564 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -775,13 +775,13 @@ func (gateAPI) Println(a ...frontend.Variable) { for i, v := range a { if _, err := x.SetInterface(v); err != nil { - toPrint[i] = x.String() - } else { if s, ok := v.(string); ok { toPrint[i] = s continue } panic(fmt.Errorf("not numeric or string: %w", err)) + } else { + toPrint[i] = x.String() } } fmt.Println(toPrint...) diff --git a/internal/gkr/engine_hints.go b/internal/gkr/engine_hints.go index 74b15c77ba..8c8bc1b797 100644 --- a/internal/gkr/engine_hints.go +++ b/internal/gkr/engine_hints.go @@ -187,7 +187,7 @@ func (g gateAPI) Println(a ...frontend.Variable) { for i := range a { if s, ok := a[i].(fmt.Stringer); ok { strings[i] = s.String() - } else { + } else if strings[i], ok = a[i].(string); !ok { bigInt := utils.FromInterface(a[i]) strings[i] = bigInt.String() } diff --git a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go index aa558dfd96..559861af2d 100644 --- a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go +++ b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go @@ -1,13 +1,16 @@ package gkr_mimc import ( + "fmt" "slices" "testing" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/std/hash/mimc" "github.com/consensys/gnark/test" + "github.com/stretchr/testify/require" ) func TestGkrMiMC(t *testing.T) { @@ -25,7 +28,7 @@ func TestGkrMiMC(t *testing.T) { In: slices.Clone(vals[:length*2]), } - test.NewAssert(t).CheckCircuit(circuit, test.WithValidAssignment(assignment), test.WithCurves(ecc.BN254)) + test.NewAssert(t).CheckCircuit(circuit, test.WithValidAssignment(assignment)) } } @@ -58,9 +61,19 @@ func (c *testGkrMiMCCircuit) Define(api frontend.API) error { plainMiMC.Reset() plainMiMC.Write(in...) expected := plainMiMC.Sum() - api.AssertIsEqual(res, expected) + api.AssertIsEqual(expected, res) } } return nil } + +func TestGkrMiMCCompiles(t *testing.T) { + const n = 52000 + circuit := testGkrMiMCCircuit{ + In: make([]frontend.Variable, n), + } + cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &circuit, frontend.WithCapacity(27_000_000)) + require.NoError(t, err) + fmt.Println(cs.GetNbConstraints(), "constraints") +} diff --git a/std/permutation/gkr-mimc/gkr-mimc.go b/std/permutation/gkr-mimc/gkr-mimc.go index c1d98d67f1..df6517f7cf 100644 --- a/std/permutation/gkr-mimc/gkr-mimc.go +++ b/std/permutation/gkr-mimc/gkr-mimc.go @@ -146,7 +146,7 @@ func addPow5(key *big.Int) gkr.GateFunction { } } -// addPow5Add: (in[0]+in[1]+key)⁵ + in[0] + in[2] +// addPow5Add: (in[0]+in[1]+key)⁵ + 2*in[0] + in[2] func addPow5Add(key *big.Int) gkr.GateFunction { return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { if len(in) != 3 { @@ -154,7 +154,9 @@ func addPow5Add(key *big.Int) gkr.GateFunction { } s := api.Add(in[0], in[1], key) t := api.Mul(s, s) - return api.Add(api.Mul(t, t, s), in[0], in[2]) + t = api.Mul(t, t, s) + + return api.Add(t, in[0], in[0], in[2]) } } @@ -169,7 +171,7 @@ func addPow7(key *big.Int) gkr.GateFunction { } } -// addPow7Add: (in[0]+in[1]+key)⁷ + in[0] + in[2] +// addPow7Add: (in[0]+in[1]+key)⁷ + 2*in[0] + in[2] func addPow7Add(key *big.Int) gkr.GateFunction { return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { if len(in) != 3 { @@ -177,7 +179,7 @@ func addPow7Add(key *big.Int) gkr.GateFunction { } s := api.Add(in[0], in[1], key) t := api.Mul(s, s) - return api.Add(api.Mul(t, t, t, s), in[0], in[2]) // s⁶ × s + in[0] + in[2] + return api.Add(api.Mul(t, t, t, s), in[0], in[0], in[2]) // s⁶ × s + 2*in[0] + in[2] } } @@ -203,10 +205,10 @@ func addPow17Add(key *big.Int) gkr.GateFunction { panic("expected three input") } s := api.Add(in[0], in[1], key) - t := api.Mul(s, s) // s² - t = api.Mul(t, t) // s⁴ - t = api.Mul(t, t) // s⁸ - t = api.Mul(t, t) // s¹⁶ - return api.Add(api.Mul(t, s), in[0], in[2]) // s¹⁶ × s + in[0] + in[2] + t := api.Mul(s, s) // s² + t = api.Mul(t, t) // s⁴ + t = api.Mul(t, t) // s⁸ + t = api.Mul(t, t) // s¹⁶ + return api.Add(api.Mul(t, s), in[0], in[0], in[2]) // s¹⁶ × s + 2*in[0] + in[2] } } diff --git a/test/engine.go b/test/engine.go index 79322af440..aaa63ac7ce 100644 --- a/test/engine.go +++ b/test/engine.go @@ -110,7 +110,7 @@ func IsSolved(circuit, witness frontend.Circuit, field *big.Int, opts ...TestEng defer func() { if r := recover(); r != nil { - err = fmt.Errorf("%v\n%s", r, string(debug.Stack())) + err = fmt.Errorf("%v\n%s", r, debug.Stack()) } }() From 311d9d9368d1fede362e4693cc87b59bebfa521c Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 10 Jun 2025 16:22:20 -0500 Subject: [PATCH 045/251] chore generify Println changes --- internal/generator/backend/template/gkr/gkr.go.tmpl | 4 ++-- internal/gkr/bls12-377/gkr.go | 4 ++-- internal/gkr/bls12-381/gkr.go | 4 ++-- internal/gkr/bls24-315/gkr.go | 4 ++-- internal/gkr/bls24-317/gkr.go | 4 ++-- internal/gkr/bw6-633/gkr.go | 4 ++-- internal/gkr/bw6-761/gkr.go | 4 ++-- internal/gkr/small_rational/gkr.go | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 3e3881d15f..16d5eb970b 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -771,13 +771,13 @@ func (gateAPI) Println(a ...frontend.Variable) { for i, v := range a { if _, err := x.SetInterface(v); err != nil { - toPrint[i] = x.String() - } else { if s, ok := v.(string); ok { toPrint[i] = s continue } panic(fmt.Errorf("not numeric or string: %w", err)) + } else { + toPrint[i] = x.String() } } fmt.Println(toPrint...) diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index b92ac1249d..b8ef9ea973 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -775,13 +775,13 @@ func (gateAPI) Println(a ...frontend.Variable) { for i, v := range a { if _, err := x.SetInterface(v); err != nil { - toPrint[i] = x.String() - } else { if s, ok := v.(string); ok { toPrint[i] = s continue } panic(fmt.Errorf("not numeric or string: %w", err)) + } else { + toPrint[i] = x.String() } } fmt.Println(toPrint...) diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 82084049d9..8f72898737 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -775,13 +775,13 @@ func (gateAPI) Println(a ...frontend.Variable) { for i, v := range a { if _, err := x.SetInterface(v); err != nil { - toPrint[i] = x.String() - } else { if s, ok := v.(string); ok { toPrint[i] = s continue } panic(fmt.Errorf("not numeric or string: %w", err)) + } else { + toPrint[i] = x.String() } } fmt.Println(toPrint...) diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index f182c9176b..7aee277ba4 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -775,13 +775,13 @@ func (gateAPI) Println(a ...frontend.Variable) { for i, v := range a { if _, err := x.SetInterface(v); err != nil { - toPrint[i] = x.String() - } else { if s, ok := v.(string); ok { toPrint[i] = s continue } panic(fmt.Errorf("not numeric or string: %w", err)) + } else { + toPrint[i] = x.String() } } fmt.Println(toPrint...) diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index a284f14ae9..7c679216fc 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -775,13 +775,13 @@ func (gateAPI) Println(a ...frontend.Variable) { for i, v := range a { if _, err := x.SetInterface(v); err != nil { - toPrint[i] = x.String() - } else { if s, ok := v.(string); ok { toPrint[i] = s continue } panic(fmt.Errorf("not numeric or string: %w", err)) + } else { + toPrint[i] = x.String() } } fmt.Println(toPrint...) diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index ec1067f736..2c2bda2037 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -775,13 +775,13 @@ func (gateAPI) Println(a ...frontend.Variable) { for i, v := range a { if _, err := x.SetInterface(v); err != nil { - toPrint[i] = x.String() - } else { if s, ok := v.(string); ok { toPrint[i] = s continue } panic(fmt.Errorf("not numeric or string: %w", err)) + } else { + toPrint[i] = x.String() } } fmt.Println(toPrint...) diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index ad5197feef..099b015b02 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -775,13 +775,13 @@ func (gateAPI) Println(a ...frontend.Variable) { for i, v := range a { if _, err := x.SetInterface(v); err != nil { - toPrint[i] = x.String() - } else { if s, ok := v.(string); ok { toPrint[i] = s continue } panic(fmt.Errorf("not numeric or string: %w", err)) + } else { + toPrint[i] = x.String() } } fmt.Println(toPrint...) diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index cdf62359f2..d085c6305f 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -775,13 +775,13 @@ func (gateAPI) Println(a ...frontend.Variable) { for i, v := range a { if _, err := x.SetInterface(v); err != nil { - toPrint[i] = x.String() - } else { if s, ok := v.(string); ok { toPrint[i] = s continue } panic(fmt.Errorf("not numeric or string: %w", err)) + } else { + toPrint[i] = x.String() } } fmt.Println(toPrint...) From b3d1af83bba4f91aa2f9ac024ed7e76fde65c55d Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 10 Jun 2025 17:38:55 -0500 Subject: [PATCH 046/251] fix: use multicommitter --- std/gkrapi/compile.go | 60 ++++++++++----------- std/lookup/logderivlookup/logderivlookup.go | 2 +- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index a5f81a283e..61d3b4a736 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -15,6 +15,7 @@ import ( fiatshamir "github.com/consensys/gnark/std/fiat-shamir" "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/consensys/gnark/std/hash" + "github.com/consensys/gnark/std/multicommit" ) type circuitDataForSnark struct { @@ -98,7 +99,7 @@ func (api *API) Compile(parentApi frontend.API, fiatshamirHashName string, optio res.toStore.ProveHintID = solver.GetHintID(res.hints.Prove) res.toStore.SolveHintID = solver.GetHintID(res.hints.Solve) - parentApi.Compiler().Defer(res.verify) + parentApi.Compiler().Defer(res.finalize) return &res } @@ -140,12 +141,13 @@ func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr return res, nil } -// verify encodes the verification circuitry for the GKR circuit -func (c *Circuit) verify(api frontend.API) error { +// finalize encodes the verification circuitry for the GKR circuit +func (c *Circuit) finalize(api frontend.API) error { if api != c.api { panic("api mismatch") } + // pad instances to the next power of 2 nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(c.toStore.NbInstances))) // pad instances to the next power of 2 by repeating the last instance if c.toStore.NbInstances < nbPaddedInstances && c.toStore.NbInstances > 0 { @@ -165,31 +167,27 @@ func (c *Circuit) verify(api frontend.API) error { return nil } - var ( - err error - proofSerialized []frontend.Variable - proof gadget.Proof - initialChallenges []frontend.Variable - ) - if c.getInitialChallenges != nil { - initialChallenges = c.getInitialChallenges() - } else { - // default initial challenge is a commitment to all input and output values - initialChallenges = make([]frontend.Variable, 0, (len(c.ins)+len(c.outs))*len(c.assignments[c.ins[0]])) - for _, in := range c.ins { - initialChallenges = append(initialChallenges, c.assignments[in]...) - } - for _, out := range c.outs { - initialChallenges = append(initialChallenges, c.assignments[out]...) - } + return c.verify(api, c.getInitialChallenges()) + } - if initialChallenges[0], err = api.(frontend.Committer).Commit(initialChallenges...); err != nil { - return fmt.Errorf("failed to commit to in/out values: %w", err) - } - initialChallenges = initialChallenges[:1] // use the commitment as the only initial challenge + // default initial challenge is a commitment to all input and output values + insOuts := make([]frontend.Variable, 0, (len(c.ins)+len(c.outs))*len(c.assignments[c.ins[0]])) + for _, in := range c.ins { + insOuts = append(insOuts, c.assignments[in]...) + } + for _, out := range c.outs { + insOuts = append(insOuts, c.assignments[out]...) } + multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { + return c.verify(api, []frontend.Variable{commitment}) + }, insOuts...) + + return nil +} + +func (c *Circuit) verify(api frontend.API, initialChallenges []frontend.Variable) error { forSnark, err := newCircuitDataForSnark(utils.FieldToCurve(api.Compiler().Field()), c.toStore, c.assignments) if err != nil { return fmt.Errorf("failed to create circuit data for snark: %w", err) @@ -201,8 +199,13 @@ func (c *Circuit) verify(api frontend.API) error { copy(hintIns[1:], initialChallenges) + var ( + proofSerialized []frontend.Variable + proof gadget.Proof + ) + if proofSerialized, err = api.Compiler().NewHint( - c.hints.Prove, gadget.ProofSize(forSnark.circuit, bits.TrailingZeros(uint(nbPaddedInstances))), hintIns...); err != nil { + c.hints.Prove, gadget.ProofSize(forSnark.circuit, bits.TrailingZeros(uint(len(c.assignments[0])))), hintIns...); err != nil { return err } c.toStore.ProveHintID = solver.GetHintID(c.hints.Prove) @@ -218,12 +221,7 @@ func (c *Circuit) verify(api frontend.API) error { return err } - err = gadget.Verify(api, forSnark.circuit, forSnark.assignments, proof, fiatshamir.WithHash(hsh, initialChallenges...), gadget.WithSortedCircuit(forSnarkSorted)) - if err != nil { - return err - } - - return nil + return gadget.Verify(api, forSnark.circuit, forSnark.assignments, proof, fiatshamir.WithHash(hsh, initialChallenges...), gadget.WithSortedCircuit(forSnarkSorted)) } func slicePtrAt[T any](slice []T) func(int) *T { diff --git a/std/lookup/logderivlookup/logderivlookup.go b/std/lookup/logderivlookup/logderivlookup.go index 63f2bc694d..dbeb042762 100644 --- a/std/lookup/logderivlookup/logderivlookup.go +++ b/std/lookup/logderivlookup/logderivlookup.go @@ -1,4 +1,4 @@ -// Package logderiv implements append-only lookups using log-derivative +// Package logderivlookup implements append-only lookups using log-derivative // argument. // // The lookup is based on log-derivative argument as described in [logderivarg]. From 815adc76b383667b65c74c6f9c59b2eaa94db076 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 10 Jun 2025 17:54:31 -0500 Subject: [PATCH 047/251] feat: use kvstore for caching instances --- .../poseidon2/gkr-poseidon2/gkr-poseidon2.go | 2 +- std/permutation/gkr-mimc/gkr-mimc.go | 39 ++++++++++++++----- .../poseidon2/gkr-poseidon2/gkr-poseidon2.go | 31 ++++++++++++--- .../gkr-poseidon2/gkr-poseidon2_test.go | 2 +- 4 files changed, 57 insertions(+), 17 deletions(-) diff --git a/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go b/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go index ffc59dee74..88c8baf260 100644 --- a/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go +++ b/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go @@ -10,7 +10,7 @@ import ( ) func New(api frontend.API) (hash.FieldHasher, error) { - f, err := gkr_poseidon2.NewGkrCompressor(api) + f, err := gkr_poseidon2.NewCompressor(api) if err != nil { return nil, fmt.Errorf("could not create poseidon2 hasher: %w", err) } diff --git a/std/permutation/gkr-mimc/gkr-mimc.go b/std/permutation/gkr-mimc/gkr-mimc.go index df6517f7cf..266ee00e67 100644 --- a/std/permutation/gkr-mimc/gkr-mimc.go +++ b/std/permutation/gkr-mimc/gkr-mimc.go @@ -14,6 +14,7 @@ import ( bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/mimc" "github.com/consensys/gnark/constraint/solver/gkrgates" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/kvstore" "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi" "github.com/consensys/gnark/std/gkrapi/gkr" @@ -21,14 +22,14 @@ import ( _ "github.com/consensys/gnark/std/hash/all" ) -// mimcCompressor implements a compression function by applying +// compressor implements a compression function by applying // the Miyaguchi–Preneel transformation to the MiMC encryption function. -type mimcCompressor struct { +type compressor struct { gkrCircuit *gkrapi.Circuit in0, in1, out gkr.Variable } -func (c *mimcCompressor) Compress(x frontend.Variable, y frontend.Variable) frontend.Variable { +func (c *compressor) Compress(x frontend.Variable, y frontend.Variable) frontend.Variable { res, err := c.gkrCircuit.AddInstance(map[gkr.Variable]frontend.Variable{c.in0: x, c.in1: y}) if err != nil { panic(err) @@ -37,6 +38,20 @@ func (c *mimcCompressor) Compress(x frontend.Variable, y frontend.Variable) fron } func NewCompressor(api frontend.API) (hash.Compressor, error) { + + store, ok := api.(kvstore.Store) + if !ok { + return nil, fmt.Errorf("api of type %T does not implement kvstore.Store", api) + } + + cached := store.GetKeyValue(gkrMiMCKey{}) + if cached != nil { + if compressor, ok := cached.(*compressor); ok { + return compressor, nil + } + return nil, fmt.Errorf("cached value is of type %T, not a compressor", cached) + } + gkrApi := gkrapi.New() in0 := gkrApi.NewInput() @@ -60,12 +75,16 @@ func NewCompressor(api frontend.API) (hash.Compressor, error) { y = gkrApi.NamedGate(gateNamer.round(len(params)-1), in0, y, in1) - return &mimcCompressor{ - gkrCircuit: gkrApi.Compile(api, "POSEIDON2"), - in0: in0, - in1: in1, - out: y, - }, nil + res := + &compressor{ + gkrCircuit: gkrApi.Compile(api, "POSEIDON2"), + in0: in0, + in1: in1, + out: y, + } + + store.SetKeyValue(gkrMiMCKey{}, res) + return res, nil } func RegisterGates(curves ...ecc.ID) error { @@ -212,3 +231,5 @@ func addPow17Add(key *big.Int) gkr.GateFunction { return api.Add(api.Mul(t, s), in[0], in[0], in[2]) // s¹⁶ × s + 2*in[0] + in[2] } } + +type gkrMiMCKey struct{} diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go index 9fbd53246c..4bb0b7a6a7 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go @@ -5,9 +5,11 @@ import ( "fmt" "github.com/consensys/gnark/constraint/solver/gkrgates" + "github.com/consensys/gnark/internal/kvstore" "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi" "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/hash" "github.com/consensys/gnark/std/permutation/poseidon2" "github.com/consensys/gnark-crypto/ecc" @@ -117,31 +119,46 @@ func extAddGate(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Add(api.Mul(x[0], 2), x[1], x[2]) } -type GkrCompressor struct { +type compressor struct { api frontend.API gkrCircuit *gkrapi.Circuit in1, in2, out gkr.Variable } -// NewGkrCompressor returns an object that can compute the Poseidon2 compression function (currently only for BLS12-377) +// NewCompressor returns an object that can compute the Poseidon2 compression function (currently only for BLS12-377) // which consists of a permutation along with the input fed forward. // The correctness of the compression functions is proven using GKR. // Note that the solver will need the function RegisterGates to be called with the desired curves -func NewGkrCompressor(api frontend.API) (*GkrCompressor, error) { +func NewCompressor(api frontend.API) (hash.Compressor, error) { + store, ok := api.(kvstore.Store) + if !ok { + return nil, fmt.Errorf("api of type %T does not implement kvstore.Store", api) + } + + cached := store.GetKeyValue(gkrPoseidon2Key{}) + if cached != nil { + if compressor, ok := cached.(*compressor); ok { + return compressor, nil + } + return nil, fmt.Errorf("cached value is of type %T, not a mimcCompressor", cached) + } + gkrCircuit, in1, in2, out, err := defineCircuit(api) if err != nil { return nil, fmt.Errorf("failed to define GKR circuit: %w", err) } - return &GkrCompressor{ + res := &compressor{ api: api, gkrCircuit: gkrCircuit, in1: in1, in2: in2, out: out, - }, nil + } + store.SetKeyValue(gkrPoseidon2Key{}, res) + return res, nil } -func (p *GkrCompressor) Compress(a, b frontend.Variable) frontend.Variable { +func (p *compressor) Compress(a, b frontend.Variable) frontend.Variable { outs, err := p.gkrCircuit.AddInstance(map[gkr.Variable]frontend.Variable{p.in1: a, p.in2: b}) if err != nil { panic(err) @@ -365,3 +382,5 @@ func (n roundGateNamer) linear(varIndex, round int) gkr.GateName { func (n roundGateNamer) integrated(varIndex, round int) gkr.GateName { return gkr.GateName(fmt.Sprintf("x%d-i-op-round=%d;%s", varIndex, round, n)) } + +type gkrPoseidon2Key struct{} diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go index c5214364b8..7c21281d66 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go @@ -41,7 +41,7 @@ type testGkrPermutationCircuit struct { func (c *testGkrPermutationCircuit) Define(api frontend.API) error { - gkr, err := NewGkrCompressor(api) + gkr, err := NewCompressor(api) if err != nil { return err } From 7ab8702d5c8ae79c579f6bca65b45fc93ba890be Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 11 Jun 2025 10:34:50 -0500 Subject: [PATCH 048/251] feat: merkledamgard hasher as statestorer --- std/hash/hash.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/std/hash/hash.go b/std/hash/hash.go index c077fd0d37..564f122a48 100644 --- a/std/hash/hash.go +++ b/std/hash/hash.go @@ -5,6 +5,8 @@ package hash import ( + "fmt" + "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/math/uints" ) @@ -110,7 +112,7 @@ type merkleDamgardHasher struct { // NewMerkleDamgardHasher transforms a 2-1 one-way function into a hash // initialState is a value whose preimage is not known -func NewMerkleDamgardHasher(api frontend.API, f Compressor, initialState frontend.Variable) FieldHasher { +func NewMerkleDamgardHasher(api frontend.API, f Compressor, initialState frontend.Variable) StateStorer { return &merkleDamgardHasher{ state: initialState, iv: initialState, @@ -132,3 +134,18 @@ func (h *merkleDamgardHasher) Write(data ...frontend.Variable) { func (h *merkleDamgardHasher) Sum() frontend.Variable { return h.state } + +func (h *merkleDamgardHasher) State() []frontend.Variable { + return []frontend.Variable{h.state} +} + +func (h *merkleDamgardHasher) SetState(state []frontend.Variable) error { + if h.state != h.iv { + return fmt.Errorf("the hasher is not in an initial state; reset before attempting to set the state") + } + if len(state) != 1 { + return fmt.Errorf("expected one state variable, got %d", len(state)) + } + h.state = state[0] + return nil +} From 2fb7daa16a5dcff8806bc4a569621d734bccf321 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 11 Jun 2025 10:51:26 -0500 Subject: [PATCH 049/251] test: SetState --- std/hash/mimc/gkr-mimc/gkr-mimc.go | 2 +- .../poseidon2/gkr-poseidon2/gkr-poseidon2.go | 2 +- std/hash/poseidon2/poseidon2.go | 6 +- std/hash/poseidon2/poseidon2_test.go | 60 ++++++++++++++++++- 4 files changed, 63 insertions(+), 7 deletions(-) diff --git a/std/hash/mimc/gkr-mimc/gkr-mimc.go b/std/hash/mimc/gkr-mimc/gkr-mimc.go index 26be877f41..8e6a8766d8 100644 --- a/std/hash/mimc/gkr-mimc/gkr-mimc.go +++ b/std/hash/mimc/gkr-mimc/gkr-mimc.go @@ -8,7 +8,7 @@ import ( gkr_mimc "github.com/consensys/gnark/std/permutation/gkr-mimc" ) -func New(api frontend.API) (hash.FieldHasher, error) { +func New(api frontend.API) (hash.StateStorer, error) { f, err := gkr_mimc.NewCompressor(api) if err != nil { return nil, fmt.Errorf("could not create mimc hasher: %w", err) diff --git a/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go b/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go index 88c8baf260..bbbef1f87c 100644 --- a/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go +++ b/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go @@ -9,7 +9,7 @@ import ( gkr_poseidon2 "github.com/consensys/gnark/std/permutation/poseidon2/gkr-poseidon2" ) -func New(api frontend.API) (hash.FieldHasher, error) { +func New(api frontend.API) (hash.StateStorer, error) { f, err := gkr_poseidon2.NewCompressor(api) if err != nil { return nil, fmt.Errorf("could not create poseidon2 hasher: %w", err) diff --git a/std/hash/poseidon2/poseidon2.go b/std/hash/poseidon2/poseidon2.go index f53b8716f3..e15c4ca587 100644 --- a/std/hash/poseidon2/poseidon2.go +++ b/std/hash/poseidon2/poseidon2.go @@ -10,7 +10,7 @@ import ( // New returns a Poseidon2 hasher using the Merkle-Damgard // construction with the default parameters. -func New(api frontend.API) (hash.FieldHasher, error) { +func New(api frontend.API) (hash.StateStorer, error) { f, err := poseidon2.NewPoseidon2(api) if err != nil { return nil, fmt.Errorf("could not create poseidon2 hasher: %w", err) @@ -19,5 +19,7 @@ func New(api frontend.API) (hash.FieldHasher, error) { } func init() { - hash.Register(hash.POSEIDON2, New) + hash.Register(hash.POSEIDON2, func(api frontend.API) (hash.FieldHasher, error) { + return New(api) + }) } diff --git a/std/hash/poseidon2/poseidon2_test.go b/std/hash/poseidon2/poseidon2_test.go index c3998ccc5b..f6c57736df 100644 --- a/std/hash/poseidon2/poseidon2_test.go +++ b/std/hash/poseidon2/poseidon2_test.go @@ -11,12 +11,12 @@ import ( "github.com/consensys/gnark/test" ) -type Poseidon2Circuit struct { +type poseidon2Circuit struct { Input []frontend.Variable Expected frontend.Variable `gnark:",public"` } -func (c *Poseidon2Circuit) Define(api frontend.API) error { +func (c *poseidon2Circuit) Define(api frontend.API) error { hsh, err := poseidon2.New(api) if err != nil { return err @@ -45,5 +45,59 @@ func TestPoseidon2Hash(t *testing.T) { circInput[i] = i } res := h.Sum(nil) - assert.CheckCircuit(&Poseidon2Circuit{Input: make([]frontend.Variable, nbInputs)}, test.WithValidAssignment(&Poseidon2Circuit{Input: circInput, Expected: res}), test.WithCurves(ecc.BLS12_377)) // we have parametrized currently only for BLS12-377 + assert.CheckCircuit(&poseidon2Circuit{Input: make([]frontend.Variable, nbInputs)}, test.WithValidAssignment(&poseidon2Circuit{Input: circInput, Expected: res}), test.WithCurves(ecc.BLS12_377)) // we have parametrized currently only for BLS12-377 +} + +func TestStateStorer(t *testing.T) { + assignment := testStateStorerCircuit{ + Input: [][]frontend.Variable{ + {0, 1, 2, 3, 4}, + }, + } + + circuit := testStateStorerCircuit{ + Input: make([][]frontend.Variable, len(assignment.Input)), + } + for i := range assignment.Input { + circuit.Input[i] = make([]frontend.Variable, len(assignment.Input[i])) + } + + test.NewAssert(t).CheckCircuit(&circuit, test.WithValidAssignment(&assignment)) +} + +type testStateStorerCircuit struct { + Input [][]frontend.Variable +} + +func (c *testStateStorerCircuit) Define(api frontend.API) error { + // hashes the whole input in one go + hshFull, err := poseidon2.New(api) + if err != nil { + return err + } + + // hashes the input in two parts + hshPartial, err := poseidon2.New(api) + if err != nil { + return err + } + + for _, input := range c.Input { + // compute desired output + hshFull.Reset() + hshFull.Write(input...) + digest := hshFull.Sum() + + hshPartial.Reset() + hshPartial.Write(input[:len(input)/2]...) + state := hshPartial.State() + hshPartial.Reset() + api.AssertIsEqual(hshPartial.State()[0], 0) + if err = hshPartial.SetState(state); err != nil { + return err + } + hshPartial.Write(input[len(input)/2:]...) + api.AssertIsEqual(hshPartial.Sum(), digest) + } + return nil } From de55a270eb9c81ca37cfefddb0ca52eca2bbe4ce Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 11 Jun 2025 11:01:41 -0500 Subject: [PATCH 050/251] feat: mimc.New to return StateStorer --- std/hash/mimc/mimc.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/std/hash/mimc/mimc.go b/std/hash/mimc/mimc.go index 9d8a98e306..db72f54429 100644 --- a/std/hash/mimc/mimc.go +++ b/std/hash/mimc/mimc.go @@ -34,7 +34,7 @@ func NewMiMC(api frontend.API) (MiMC, error) { // NB! See the package documentation for length extension attack consideration. // // [gnark-crypto]: https://pkg.go.dev/github.com/consensys/gnark-crypto/hash -func New(api frontend.API) (hash.FieldHasher, error) { +func New(api frontend.API) (hash.StateStorer, error) { h, err := NewMiMC(api) if err != nil { return nil, err @@ -43,5 +43,7 @@ func New(api frontend.API) (hash.FieldHasher, error) { } func init() { - hash.Register(hash.MIMC, New) + hash.Register(hash.MIMC, func(api frontend.API) (hash.FieldHasher, error) { + return New(api) + }) } From 31a0a598cfe7b562d3bd789c793f10b6b7cdb5dd Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 11 Jun 2025 13:41:31 -0500 Subject: [PATCH 051/251] fix: single instance and no instance edge cases --- std/gkrapi/api_test.go | 34 +++++++++++++++++++++++++++++++++- std/gkrapi/compile.go | 25 ++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/std/gkrapi/api_test.go b/std/gkrapi/api_test.go index 53642bc6a7..0d22ec80a3 100644 --- a/std/gkrapi/api_test.go +++ b/std/gkrapi/api_test.go @@ -649,6 +649,38 @@ func TestWitnessExtend(t *testing.T) { _, err = cs.Solve(witness) require.NoError(t, err) +} + +func TestSingleInstance(t *testing.T) { + circuit := doubleNoDependencyCircuit{ + X: make([]frontend.Variable, 1), + hashName: "MIMC", + } + assignment := doubleNoDependencyCircuit{ + X: []frontend.Variable{10}, + } + + test.NewAssert(t).CheckCircuit(&circuit, test.WithValidAssignment(&assignment)) +} + +func TestNoInstance(t *testing.T) { + var circuit testNoInstanceCircuit + assignment := testNoInstanceCircuit{0} - //test.NewAssert(t).CheckCircuit(&circuit, test.WithValidAssignment(&assignment)) + test.NewAssert(t).CheckCircuit(&circuit, test.WithValidAssignment(&assignment)) +} + +type testNoInstanceCircuit struct { + Dummy frontend.Variable // Plonk prover would fail on an empty witness +} + +func (c *testNoInstanceCircuit) Define(api frontend.API) error { + gkrApi := New() + x := gkrApi.NewInput() + y := gkrApi.NewInput() + gkrApi.Mul(x, y) + + gkrApi.Compile(api, "MIMC") + + return nil } diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 61d3b4a736..99f8ab9dcc 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -147,6 +147,11 @@ func (c *Circuit) finalize(api frontend.API) error { panic("api mismatch") } + // if the circuit is empty or with no instances, there is nothing to do. + if len(c.outs) == 0 || len(c.assignments[0]) == 0 { // wire 0 is always an input wire + return nil + } + // pad instances to the next power of 2 nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(c.toStore.NbInstances))) // pad instances to the next power of 2 by repeating the last instance @@ -163,7 +168,25 @@ func (c *Circuit) finalize(api frontend.API) error { return err } - if len(c.outs) == 0 || len(c.assignments[0]) == 0 { // wire 0 is always an input wire + // if the circuit consists of only one instance, directly solve the circuit + if len(c.assignments[c.ins[0]]) == 1 { + circuit, err := gkrtypes.CircuitInfoToCircuit(c.toStore.Circuit, gkrgates.Get) + if err != nil { + return fmt.Errorf("failed to convert GKR info to circuit: %w", err) + } + gateIn := make([]frontend.Variable, circuit.MaxGateNbIn()) + for wI, w := range circuit { + if w.IsInput() { + continue + } + for inI, inWI := range w.Inputs { + gateIn[inI] = c.assignments[inWI][0] // take the first (only) instance + } + res := w.Gate.Evaluate(api, gateIn[:len(w.Inputs)]...) + if w.IsOutput() { + api.AssertIsEqual(res, c.assignments[gkr.Variable(wI)][0]) + } + } return nil } From db56157c9334e7715a2a9bd0c32d7e6bb444bfa5 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 11 Jun 2025 15:17:55 -0500 Subject: [PATCH 052/251] fix: single-instance, circuit with depth --- std/gkrapi/api_test.go | 27 +++++++++++++++++---------- std/gkrapi/compile.go | 4 +++- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/std/gkrapi/api_test.go b/std/gkrapi/api_test.go index 0d22ec80a3..1f3d187dae 100644 --- a/std/gkrapi/api_test.go +++ b/std/gkrapi/api_test.go @@ -275,8 +275,6 @@ func benchProof(b *testing.B, circuit, assignment frontend.Circuit) { _, err = groth16.Prove(cs, pk, fullWitness) require.NoError(b, err) fmt.Println("groth16 proved", id, "in", time.Now().UnixMicro()-start, "μs") - - fmt.Println("mimc total calls: fr=", mimcFrTotalCalls, ", snark=", mimcSnarkTotalCalls) } } @@ -364,8 +362,6 @@ func (c constPseudoHash) Write(...frontend.Variable) {} func (c constPseudoHash) Reset() {} -var mimcFrTotalCalls = 0 - type mimcNoGkrCircuit struct { X []frontend.Variable Y []frontend.Variable @@ -418,7 +414,15 @@ func (c *mimcNoDepCircuit) Define(api frontend.API) error { gkrApi := New() x := gkrApi.NewInput() y := gkrApi.NewInput() - gkrApi.Gate(mimcGate, x, y) + + if c.mimcDepth < 1 { + return fmt.Errorf("mimcDepth must be at least 1, got %d", c.mimcDepth) + } + + z := y + for range c.mimcDepth { + z = gkrApi.Gate(mimcGate, x, z) + } gkrCircuit := gkrApi.Compile(api, c.hashName) @@ -652,12 +656,15 @@ func TestWitnessExtend(t *testing.T) { } func TestSingleInstance(t *testing.T) { - circuit := doubleNoDependencyCircuit{ - X: make([]frontend.Variable, 1), - hashName: "MIMC", + circuit := mimcNoDepCircuit{ + X: make([]frontend.Variable, 1), + Y: make([]frontend.Variable, 1), + mimcDepth: 2, + hashName: "MIMC", } - assignment := doubleNoDependencyCircuit{ + assignment := mimcNoDepCircuit{ X: []frontend.Variable{10}, + Y: []frontend.Variable{2}, } test.NewAssert(t).CheckCircuit(&circuit, test.WithValidAssignment(&assignment)) @@ -677,7 +684,7 @@ type testNoInstanceCircuit struct { func (c *testNoInstanceCircuit) Define(api frontend.API) error { gkrApi := New() x := gkrApi.NewInput() - y := gkrApi.NewInput() + y := gkrApi.Mul(x, x) gkrApi.Mul(x, y) gkrApi.Compile(api, "MIMC") diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 99f8ab9dcc..390647b89d 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -184,7 +184,9 @@ func (c *Circuit) finalize(api frontend.API) error { } res := w.Gate.Evaluate(api, gateIn[:len(w.Inputs)]...) if w.IsOutput() { - api.AssertIsEqual(res, c.assignments[gkr.Variable(wI)][0]) + api.AssertIsEqual(res, c.assignments[wI][0]) + } else { + c.assignments[wI] = append(c.assignments[wI], res) } } return nil From 4ca453c5467bc681708288a88153d0a5b7505d05 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 8 Jul 2025 17:18:34 -0500 Subject: [PATCH 053/251] docs: address cursor comments --- .../backend/template/gkr/solver_hints.go.tmpl | 4 ++-- internal/gkr/bls12-377/solver_hints.go | 4 ++-- internal/gkr/bls12-381/solver_hints.go | 4 ++-- internal/gkr/bls24-315/solver_hints.go | 4 ++-- internal/gkr/bls24-317/solver_hints.go | 4 ++-- internal/gkr/bn254/solver_hints.go | 4 ++-- internal/gkr/bw6-633/solver_hints.go | 4 ++-- internal/gkr/bw6-761/solver_hints.go | 4 ++-- internal/utils/slices.go | 11 ++++++----- 9 files changed, 22 insertions(+), 21 deletions(-) diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index 29698e0e3b..04873fdcb8 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -80,7 +80,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) func GetAssignmentHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { if len(ins) != 3 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + return fmt.Errorf("GetAssignmentHint expects 3 inputs: wire index, instance index, and dummy dependency enforcer") } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() @@ -149,7 +149,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +// RepeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. {{ print "// e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}}"}} func (a WireAssignment) RepeatUntilEnd(n int) { for i := range a { diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 04c5f52586..c977d4997a 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -86,7 +86,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) func GetAssignmentHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { if len(ins) != 3 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + return fmt.Errorf("GetAssignmentHint expects 3 inputs: wire index, instance index, and dummy dependency enforcer") } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() @@ -155,7 +155,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +// RepeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. // e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} func (a WireAssignment) RepeatUntilEnd(n int) { for i := range a { diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index e92e543398..81572a4ac4 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -86,7 +86,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) func GetAssignmentHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { if len(ins) != 3 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + return fmt.Errorf("GetAssignmentHint expects 3 inputs: wire index, instance index, and dummy dependency enforcer") } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() @@ -155,7 +155,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +// RepeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. // e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} func (a WireAssignment) RepeatUntilEnd(n int) { for i := range a { diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index f57537b985..783cc964c8 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -86,7 +86,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) func GetAssignmentHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { if len(ins) != 3 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + return fmt.Errorf("GetAssignmentHint expects 3 inputs: wire index, instance index, and dummy dependency enforcer") } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() @@ -155,7 +155,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +// RepeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. // e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} func (a WireAssignment) RepeatUntilEnd(n int) { for i := range a { diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index d2cc4d32b1..234a327324 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -86,7 +86,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) func GetAssignmentHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { if len(ins) != 3 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + return fmt.Errorf("GetAssignmentHint expects 3 inputs: wire index, instance index, and dummy dependency enforcer") } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() @@ -155,7 +155,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +// RepeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. // e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} func (a WireAssignment) RepeatUntilEnd(n int) { for i := range a { diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index 5813b89661..f855222636 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -86,7 +86,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) func GetAssignmentHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { if len(ins) != 3 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + return fmt.Errorf("GetAssignmentHint expects 3 inputs: wire index, instance index, and dummy dependency enforcer") } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() @@ -155,7 +155,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +// RepeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. // e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} func (a WireAssignment) RepeatUntilEnd(n int) { for i := range a { diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index ef945e25f7..19d347d099 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -86,7 +86,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) func GetAssignmentHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { if len(ins) != 3 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + return fmt.Errorf("GetAssignmentHint expects 3 inputs: wire index, instance index, and dummy dependency enforcer") } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() @@ -155,7 +155,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +// RepeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. // e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} func (a WireAssignment) RepeatUntilEnd(n int) { for i := range a { diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index 1a91928171..09e9c13f0f 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -86,7 +86,7 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) func GetAssignmentHint(data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { if len(ins) != 3 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: instance index, wire index, and dummy dependency enforcer") + return fmt.Errorf("GetAssignmentHint expects 3 inputs: wire index, instance index, and dummy dependency enforcer") } wireI := ins[0].Uint64() instanceI := ins[1].Uint64() @@ -155,7 +155,7 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// RepeatUntilEnd for each wire, sets all the values starting from n to its predecessor. +// RepeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. // e.g. {{1, 2, 3}, {4, 5, 6}}.RepeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} func (a WireAssignment) RepeatUntilEnd(n int) { for i := range a { diff --git a/internal/utils/slices.go b/internal/utils/slices.go index f493bf4bca..bdd86119fa 100644 --- a/internal/utils/slices.go +++ b/internal/utils/slices.go @@ -17,14 +17,15 @@ func References[T any](v []T) []*T { return res } -// ExtendRepeatLast extends the slice s by repeating the last element until it reaches the length n. +// ExtendRepeatLast extends a non-empty slice s by repeating the last element until it reaches the length n. func ExtendRepeatLast[T any](s []T, n int) []T { if n <= len(s) { return s[:n] } - s = s[:len(s):len(s)] // ensure s is a slice with a capacity equal to its length - for len(s) < n { - s = append(s, s[len(s)-1]) // append the last element until the length is n + res := make([]T, n) + copy(res, s) + for i := len(s); i < n; i++ { + res[i] = res[i-1] } - return s + return res } From 8947d9f74925b69b38fc6d7ed543df8556d1d0dc Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 19 Aug 2025 17:01:20 -0500 Subject: [PATCH 054/251] refactor: remove MapRange --- internal/utils/algo_utils.go | 70 ++----------------- std/gkrapi/compile.go | 8 +-- .../poseidon2/gkr-poseidon2/gkr_test.go | 42 +++-------- 3 files changed, 16 insertions(+), 104 deletions(-) diff --git a/internal/utils/algo_utils.go b/internal/utils/algo_utils.go index f836625370..4bee19443e 100644 --- a/internal/utils/algo_utils.go +++ b/internal/utils/algo_utils.go @@ -24,6 +24,7 @@ func Permute[T any](slice []T, permutation []int) { } } +// Map returns [f(in[0]), f(in[1]), ..., f(in[len(in)-1])] func Map[T, S any](in []T, f func(T) S) []S { out := make([]S, len(in)) for i, t := range in { @@ -32,41 +33,6 @@ func Map[T, S any](in []T, f func(T) S) []S { return out } -func MapRange[S any](begin, end int, f func(int) S) []S { - out := make([]S, end-begin) - for i := begin; i < end; i++ { - out[i] = f(i) - } - return out -} - -func SliceAt[T any](slice []T) func(int) T { - return func(i int) T { - return slice[i] - } -} - -func SlicePtrAt[T any](slice []T) func(int) *T { - return func(i int) *T { - return &slice[i] - } -} - -func MapAt[K comparable, V any](mp map[K]V) func(K) V { - return func(k K) V { - return mp[k] - } -} - -// InvertPermutation input permutation must contain exactly 0, ..., len(permutation)-1 -func InvertPermutation(permutation []int) []int { - res := make([]int, len(permutation)) - for i := range permutation { - res[permutation[i]] = i - } - return res -} - // TODO: Move this to gnark-crypto and use it for gkr there as well // TopologicalSort takes a list of lists of dependencies and proposes a sorting of the lists in order of dependence. Such that for any wire, any one it depends on @@ -143,33 +109,11 @@ func (d *topSortData) markDone(i int) { } } -// BinarySearch looks for toFind in a sorted slice, and returns the index at which it either is or would be were it to be inserted. -func BinarySearch(slice []int, toFind int) int { - var start int - for end := len(slice); start != end; { - mid := (start + end) / 2 - if toFind >= slice[mid] { - start = mid - } - if toFind <= slice[mid] { - end = mid - } +// SliceOfRefs returns [&slice[0], &slice[1], ..., &slice[len(slice)-1]] +func SliceOfRefs[T any](slice []T) []*T { + res := make([]*T, len(slice)) + for i := range slice { + res[i] = &slice[i] } - return start -} - -// BinarySearchFunc looks for toFind in an increasing function of domain 0 ... (end-1), and returns the index at which it either is or would be were it to be inserted. -func BinarySearchFunc(eval func(int) int, end int, toFind int) int { - var start int - for start != end { - mid := (start + end) / 2 - val := eval(mid) - if toFind >= val { - start = mid - } - if toFind <= val { - end = mid - } - } - return start + return res } diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 390647b89d..64205b80b8 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -235,7 +235,7 @@ func (c *Circuit) verify(api frontend.API, initialChallenges []frontend.Variable } c.toStore.ProveHintID = solver.GetHintID(c.hints.Prove) - forSnarkSorted := utils.MapRange(0, len(c.toStore.Circuit), slicePtrAt(forSnark.circuit)) + forSnarkSorted := utils.SliceOfRefs(forSnark.circuit) if proof, err = gadget.DeserializeProof(forSnarkSorted, proofSerialized); err != nil { return err @@ -249,12 +249,6 @@ func (c *Circuit) verify(api frontend.API, initialChallenges []frontend.Variable return gadget.Verify(api, forSnark.circuit, forSnark.assignments, proof, fiatshamir.WithHash(hsh, initialChallenges...), gadget.WithSortedCircuit(forSnarkSorted)) } -func slicePtrAt[T any](slice []T) func(int) *T { - return func(i int) *T { - return &slice[i] - } -} - func newCircuitDataForSnark(curve ecc.ID, info gkrinfo.StoringInfo, assignment gkrtypes.WireAssignment) (circuitDataForSnark, error) { circuit, err := gkrtypes.CircuitInfoToCircuit(info.Circuit, gkrgates.Get) if err != nil { diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go index ffa60d8ccb..0a230c4381 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go @@ -2,8 +2,6 @@ package gkr_poseidon2 import ( "fmt" - "os" - "runtime/pprof" "testing" "github.com/consensys/gnark-crypto/ecc" @@ -15,7 +13,7 @@ import ( "github.com/stretchr/testify/require" ) -func gkrPermutationsCircuits(t require.TestingT, n int) (circuit, assignment testGkrPermutationCircuit) { +func gkrCompressionCircuits(t require.TestingT, n int) (circuit, assignment testGkrCompressionCircuit) { var k int64 ins := make([][2]frontend.Variable, n) outs := make([]frontend.Variable, n) @@ -34,27 +32,27 @@ func gkrPermutationsCircuits(t require.TestingT, n int) (circuit, assignment tes k += 2 } - return testGkrPermutationCircuit{ + return testGkrCompressionCircuit{ Ins: make([][2]frontend.Variable, len(ins)), Outs: make([]frontend.Variable, len(outs)), - }, testGkrPermutationCircuit{ + }, testGkrCompressionCircuit{ Ins: ins, Outs: outs, } } func TestGkrCompression(t *testing.T) { - circuit, assignment := gkrPermutationsCircuits(t, 2) + circuit, assignment := gkrCompressionCircuits(t, 2) test.NewAssert(t).CheckCircuit(&circuit, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BLS12_377)) } -type testGkrPermutationCircuit struct { +type testGkrCompressionCircuit struct { Ins [][2]frontend.Variable Outs []frontend.Variable } -func (c *testGkrPermutationCircuit) Define(api frontend.API) error { +func (c *testGkrCompressionCircuit) Define(api frontend.API) error { pos2 := NewGkrCompressor(api) api.AssertIsEqual(len(c.Ins), len(c.Outs)) @@ -65,36 +63,12 @@ func (c *testGkrPermutationCircuit) Define(api frontend.API) error { return nil } -func TestGkrPermutationCompiles(t *testing.T) { +func TestGkrCompressionCompiles(t *testing.T) { // just measure the number of constraints - cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &testGkrPermutationCircuit{ + cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &testGkrCompressionCircuit{ Ins: make([][2]frontend.Variable, 52000), Outs: make([]frontend.Variable, 52000), }) require.NoError(t, err) fmt.Println(cs.GetNbConstraints(), "constraints") } - -func BenchmarkGkrPermutations(b *testing.B) { - circuit, assignmment := gkrPermutationsCircuits(b, 50000) - - cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &circuit) - require.NoError(b, err) - - witness, err := frontend.NewWitness(&assignmment, ecc.BLS12_377.ScalarField()) - require.NoError(b, err) - - // cpu profile - f, err := os.Create("cpu.pprof") - require.NoError(b, err) - defer func() { - require.NoError(b, f.Close()) - }() - - err = pprof.StartCPUProfile(f) - require.NoError(b, err) - defer pprof.StopCPUProfile() - - _, err = cs.Solve(witness) - require.NoError(b, err) -} From 8c37435765a54be6acdb4048a78f0b1c388955ba Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 17 Sep 2025 13:27:37 -0500 Subject: [PATCH 055/251] fix: bad merge --- .../poseidon2/gkr-poseidon2/gkr-poseidon2_test.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go index f3af71983f..b224bf1414 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go @@ -76,11 +76,6 @@ func BenchmarkGkrCompressions(b *testing.B) { witness, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField()) require.NoError(b, err) - // cpu profile - defer func() { - require.NoError(b, f.Close()) - }() - _, err = cs.Solve(witness) require.NoError(b, err) } From 1e208500ab2139899e9c42090eba912e54b73358 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 17 Sep 2025 13:31:18 -0500 Subject: [PATCH 056/251] perf: single-elem pool for bls12-377 --- internal/gkr/bls12-377/gate_testing.go | 5 ++ internal/gkr/bls12-377/gkr.go | 98 ++++++++++++++++---------- internal/gkr/bls12-377/solver_hints.go | 5 +- 3 files changed, 68 insertions(+), 40 deletions(-) diff --git a/internal/gkr/bls12-377/gate_testing.go b/internal/gkr/bls12-377/gate_testing.go index 9e5a3868f3..6088644ea0 100644 --- a/internal/gkr/bls12-377/gate_testing.go +++ b/internal/gkr/bls12-377/gate_testing.go @@ -21,6 +21,7 @@ import ( // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { + var api gateAPI fWrapped := api.convertFunc(f) // fix all variables except the i-th one at random points @@ -118,6 +119,7 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { + var api gateAPI fFr := api.convertFunc(f) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { @@ -127,11 +129,13 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { } return len(p) - 1, nil } + api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { + var api gateAPI fFr := api.convertFunc(f) if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) @@ -145,6 +149,7 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + var api gateAPI x := make(fr.Vector, nbIn) x.MustSetRandom() fFr := api.convertFunc(f) diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index b8ef9ea973..16e7af5c12 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -104,8 +104,9 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - - gateEvaluation.Set(wire.Gate.Evaluate(api, inputEvaluations...).(*fr.Element)) + var api gateAPI + gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*fr.Element)) + api.freeElements() } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,7 +237,10 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var step fr.Element + var ( + step fr.Element + api gateAPI + ) res := make([]fr.Element, degGJ) @@ -266,11 +270,13 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { for i := range gateInput { gateInput[i] = &mlEvals[eIndex+1+i] } - summand := wire.Gate.Evaluate(api, gateInput...).(*fr.Element) + summand := wire.Gate.Evaluate(&api, gateInput...).(*fr.Element) summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } + + api.freeElements() } mu.Lock() for i := range gJ { @@ -668,6 +674,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } + var api gateAPI ins := make([]fr.Element, maxNbIns) for i := range nbInstances { for wI, w := range wires { @@ -724,52 +731,54 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } // gateAPI implements gkr.GateAPI. -type gateAPI struct{} - -var api gateAPI +// It uses a synchronous memory pool underneath to minimize heap allocations. +type gateAPI struct { + allocated []*fr.Element + nbUsed int +} -func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element // TODO Heap allocated. Keep an eye on perf - res.Add(cast(i1), cast(i2)) +func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Add(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Add(&res, cast(v)) + res.Add(res, api.cast(v)) } - return &res + return res } -func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - var prod fr.Element - prod.Mul(cast(b), cast(c)) - res := cast(a) - res.Add(res, &prod) - return &res +func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { + prod := api.newElement() + prod.Mul(api.cast(b), api.cast(c)) + res := api.cast(a) + res.Add(res, prod) + return res } -func (gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - var res fr.Element - res.Neg(cast(i1)) - return &res +func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { + res := api.newElement() + res.Neg(api.cast(i1)) + return res } -func (gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element - res.Sub(cast(i1), cast(i2)) +func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Sub(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Sub(&res, cast(v)) + res.Sub(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element - res.Mul(cast(i1), cast(i2)) +func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Mul(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Mul(&res, cast(v)) + res.Mul(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Println(a ...frontend.Variable) { +func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -787,7 +796,7 @@ func (gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { +func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { inVar[i] = &in[i] @@ -795,22 +804,35 @@ func (api gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { return f(api, inVar...).(*fr.Element) } +// Done with the current task. Put back all the allocated slices. +func (api *gateAPI) freeElements() { + api.nbUsed = 0 +} + +func (api *gateAPI) newElement() *fr.Element { + api.nbUsed++ + if api.nbUsed >= len(api.allocated) { + api.allocated = append(api.allocated, new(fr.Element)) + } + return api.allocated[api.nbUsed-1] +} + type gateFunctionFr func(...fr.Element) *fr.Element // convertFunc turns f into a function that accepts and returns fr.Element. -func (api gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { +func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { return func(in ...fr.Element) *fr.Element { return api.evaluate(f, in...) } } -func cast(v frontend.Variable) *fr.Element { +func (api *gateAPI) cast(v frontend.Variable) *fr.Element { if x, ok := v.(*fr.Element); ok { // fast path, no extra heap allocation return x } - var x fr.Element + x := api.newElement() if _, err := x.SetInterface(v); err != nil { panic(err) } - return &x + return x } diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index c977d4997a..96b6636151 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -103,7 +103,8 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 - insI := 1 // skip the first input, which is the instance index + insI := 1 // skip the first input, which is the instance index + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. for wI := range data.circuit { w := &data.circuit[wI] if w.IsInput() { // read from provided input @@ -116,7 +117,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) From 4c69b6518416ed6e5b1af41c89831d7ce9a1f9ab Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 17 Sep 2025 13:47:13 -0500 Subject: [PATCH 057/251] build: generify changes --- .../backend/template/gkr/gate_testing.go.tmpl | 5 + .../backend/template/gkr/gkr.go.tmpl | 96 +++++++++++-------- .../backend/template/gkr/solver_hints.go.tmpl | 3 +- internal/gkr/bls12-377/gkr.go | 2 - internal/gkr/bls12-377/solver_hints.go | 2 +- internal/gkr/bls12-381/gate_testing.go | 5 + internal/gkr/bls12-381/gkr.go | 96 +++++++++++-------- internal/gkr/bls12-381/solver_hints.go | 3 +- internal/gkr/bls24-315/gate_testing.go | 5 + internal/gkr/bls24-315/gkr.go | 96 +++++++++++-------- internal/gkr/bls24-315/solver_hints.go | 3 +- internal/gkr/bls24-317/gate_testing.go | 5 + internal/gkr/bls24-317/gkr.go | 96 +++++++++++-------- internal/gkr/bls24-317/solver_hints.go | 3 +- internal/gkr/bn254/gate_testing.go | 5 + internal/gkr/bn254/gkr.go | 96 +++++++++++-------- internal/gkr/bn254/solver_hints.go | 3 +- internal/gkr/bw6-633/gate_testing.go | 5 + internal/gkr/bw6-633/gkr.go | 96 +++++++++++-------- internal/gkr/bw6-633/solver_hints.go | 3 +- internal/gkr/bw6-761/gate_testing.go | 5 + internal/gkr/bw6-761/gkr.go | 96 +++++++++++-------- internal/gkr/bw6-761/solver_hints.go | 3 +- internal/gkr/small_rational/gate_testing.go | 5 + internal/gkr/small_rational/gkr.go | 96 +++++++++++-------- 25 files changed, 519 insertions(+), 314 deletions(-) diff --git a/internal/generator/backend/template/gkr/gate_testing.go.tmpl b/internal/generator/backend/template/gkr/gate_testing.go.tmpl index a782015cfa..7c24b0d27a 100644 --- a/internal/generator/backend/template/gkr/gate_testing.go.tmpl +++ b/internal/generator/backend/template/gkr/gate_testing.go.tmpl @@ -15,6 +15,7 @@ import ( // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { + var api gateAPI fWrapped := api.convertFunc(f) // fix all variables except the i-th one at random points @@ -130,6 +131,7 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { + var api gateAPI fFr := api.convertFunc(f) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { @@ -139,11 +141,13 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { } return len(p) - 1, nil } + api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { + var api gateAPI fFr := api.convertFunc(f) if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) @@ -157,6 +161,7 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + var api gateAPI x := make({{.FieldPackageName}}.Vector, nbIn) x.MustSetRandom() fFr := api.convertFunc(f) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 16d5eb970b..c5c5b21dc2 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -97,8 +97,8 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []{{ .ElementType for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - - gateEvaluation.Set(wire.Gate.Evaluate(api, inputEvaluations...).(*{{ .ElementType }})) + var api gateAPI + gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*{{ .ElementType }})) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -230,7 +230,10 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]{{ .ElementType }}, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var step {{ .ElementType }} + var ( + step {{ .ElementType }} + api gateAPI + ) res := make([]{{ .ElementType }}, degGJ) @@ -260,11 +263,12 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { for i := range gateInput { gateInput[i] = &mlEvals[eIndex+1+i] } - summand := wire.Gate.Evaluate(api, gateInput...).(*{{ .ElementType }}) + summand := wire.Gate.Evaluate(&api, gateInput...).(*{{ .ElementType }}) summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } + api.freeElements() } mu.Lock() for i := range gJ { @@ -663,6 +667,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } + var api gateAPI ins := make([]{{ .ElementType }}, maxNbIns) for i := range nbInstances { for wI, w := range wires { @@ -720,52 +725,54 @@ func frToBigInts(dst []*big.Int, src []{{ .ElementType }}) { // gateAPI implements gkr.GateAPI. -type gateAPI struct{} - -var api gateAPI +// It uses a synchronous memory pool underneath to minimize heap allocations. +type gateAPI struct { + allocated []*{{ .ElementType }} + nbUsed int +} -func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res {{ .ElementType }} // TODO Heap allocated. Keep an eye on perf - res.Add(cast(i1), cast(i2)) +func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Add(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Add(&res, cast(v)) + res.Add(res, api.cast(v)) } - return &res + return res } -func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - var prod {{ .ElementType }} - prod.Mul(cast(b), cast(c)) - res := cast(a) - res.Add(res, &prod) - return &res +func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { + prod := api.newElement() + prod.Mul(api.cast(b), api.cast(c)) + res := api.cast(a) + res.Add(res, prod) + return res } -func (gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - var res {{ .ElementType }} - res.Neg(cast(i1)) - return &res +func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { + res := api.newElement() + res.Neg(api.cast(i1)) + return res } -func (gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res {{ .ElementType }} - res.Sub(cast(i1), cast(i2)) +func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Sub(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Sub(&res, cast(v)) + res.Sub(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res {{ .ElementType }} - res.Mul(cast(i1), cast(i2)) +func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Mul(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Mul(&res, cast(v)) + res.Mul(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Println(a ...frontend.Variable) { +func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x {{ .ElementType }} @@ -783,7 +790,7 @@ func (gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api gateAPI) evaluate(f gkr.GateFunction, in ...{{ .ElementType }}) *{{ .ElementType }} { +func (api *gateAPI) evaluate(f gkr.GateFunction, in ...{{ .ElementType }}) *{{ .ElementType }} { inVar := make([]frontend.Variable, len(in)) for i := range in { inVar[i] = &in[i] @@ -791,22 +798,35 @@ func (api gateAPI) evaluate(f gkr.GateFunction, in ...{{ .ElementType }}) *{{ .E return f(api, inVar...).(*{{ .ElementType }}) } +// Done with the current task. Put back all the allocated slices. +func (api *gateAPI) freeElements() { + api.nbUsed = 0 +} + +func (api *gateAPI) newElement() *{{ .ElementType }} { + api.nbUsed++ + if api.nbUsed >= len(api.allocated) { + api.allocated = append(api.allocated, new({{ .ElementType }})) + } + return api.allocated[api.nbUsed-1] +} + type gateFunctionFr func(...{{ .ElementType }}) *{{ .ElementType }} // convertFunc turns f into a function that accepts and returns {{ .ElementType }}. -func (api gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { +func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { return func(in ...{{ .ElementType }}) *{{ .ElementType }} { return api.evaluate(f, in...) } } -func cast(v frontend.Variable) *{{ .ElementType }} { +func (api *gateAPI) cast(v frontend.Variable) *{{ .ElementType }} { if x, ok := v.(*{{ .ElementType }}); ok { // fast path, no extra heap allocation return x } - var x {{ .ElementType }} + x := api.newElement() if _, err := x.SetInterface(v); err != nil { panic(err) } - return &x + return x } \ No newline at end of file diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index 04873fdcb8..eebc96850f 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -97,7 +97,8 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 - insI := 1 // skip the first input, which is the instance index + insI := 1 // skip the first input, which is the instance index + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. for wI := range data.circuit { w := &data.circuit[wI] if w.IsInput() { // read from provided input diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 16e7af5c12..c31d447691 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -106,7 +106,6 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb } var api gateAPI gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*fr.Element)) - api.freeElements() } evaluation.Mul(&evaluation, &gateEvaluation) @@ -275,7 +274,6 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } - api.freeElements() } mu.Lock() diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 96b6636151..6c504f7692 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -117,7 +117,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bls12-381/gate_testing.go b/internal/gkr/bls12-381/gate_testing.go index 5b281fd634..275ce2efb0 100644 --- a/internal/gkr/bls12-381/gate_testing.go +++ b/internal/gkr/bls12-381/gate_testing.go @@ -21,6 +21,7 @@ import ( // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { + var api gateAPI fWrapped := api.convertFunc(f) // fix all variables except the i-th one at random points @@ -118,6 +119,7 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { + var api gateAPI fFr := api.convertFunc(f) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { @@ -127,11 +129,13 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { } return len(p) - 1, nil } + api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { + var api gateAPI fFr := api.convertFunc(f) if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) @@ -145,6 +149,7 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + var api gateAPI x := make(fr.Vector, nbIn) x.MustSetRandom() fFr := api.convertFunc(f) diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 8f72898737..12b5aff144 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -104,8 +104,8 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - - gateEvaluation.Set(wire.Gate.Evaluate(api, inputEvaluations...).(*fr.Element)) + var api gateAPI + gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*fr.Element)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,7 +236,10 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var step fr.Element + var ( + step fr.Element + api gateAPI + ) res := make([]fr.Element, degGJ) @@ -266,11 +269,12 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { for i := range gateInput { gateInput[i] = &mlEvals[eIndex+1+i] } - summand := wire.Gate.Evaluate(api, gateInput...).(*fr.Element) + summand := wire.Gate.Evaluate(&api, gateInput...).(*fr.Element) summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } + api.freeElements() } mu.Lock() for i := range gJ { @@ -668,6 +672,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } + var api gateAPI ins := make([]fr.Element, maxNbIns) for i := range nbInstances { for wI, w := range wires { @@ -724,52 +729,54 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } // gateAPI implements gkr.GateAPI. -type gateAPI struct{} - -var api gateAPI +// It uses a synchronous memory pool underneath to minimize heap allocations. +type gateAPI struct { + allocated []*fr.Element + nbUsed int +} -func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element // TODO Heap allocated. Keep an eye on perf - res.Add(cast(i1), cast(i2)) +func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Add(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Add(&res, cast(v)) + res.Add(res, api.cast(v)) } - return &res + return res } -func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - var prod fr.Element - prod.Mul(cast(b), cast(c)) - res := cast(a) - res.Add(res, &prod) - return &res +func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { + prod := api.newElement() + prod.Mul(api.cast(b), api.cast(c)) + res := api.cast(a) + res.Add(res, prod) + return res } -func (gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - var res fr.Element - res.Neg(cast(i1)) - return &res +func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { + res := api.newElement() + res.Neg(api.cast(i1)) + return res } -func (gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element - res.Sub(cast(i1), cast(i2)) +func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Sub(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Sub(&res, cast(v)) + res.Sub(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element - res.Mul(cast(i1), cast(i2)) +func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Mul(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Mul(&res, cast(v)) + res.Mul(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Println(a ...frontend.Variable) { +func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -787,7 +794,7 @@ func (gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { +func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { inVar[i] = &in[i] @@ -795,22 +802,35 @@ func (api gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { return f(api, inVar...).(*fr.Element) } +// Done with the current task. Put back all the allocated slices. +func (api *gateAPI) freeElements() { + api.nbUsed = 0 +} + +func (api *gateAPI) newElement() *fr.Element { + api.nbUsed++ + if api.nbUsed >= len(api.allocated) { + api.allocated = append(api.allocated, new(fr.Element)) + } + return api.allocated[api.nbUsed-1] +} + type gateFunctionFr func(...fr.Element) *fr.Element // convertFunc turns f into a function that accepts and returns fr.Element. -func (api gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { +func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { return func(in ...fr.Element) *fr.Element { return api.evaluate(f, in...) } } -func cast(v frontend.Variable) *fr.Element { +func (api *gateAPI) cast(v frontend.Variable) *fr.Element { if x, ok := v.(*fr.Element); ok { // fast path, no extra heap allocation return x } - var x fr.Element + x := api.newElement() if _, err := x.SetInterface(v); err != nil { panic(err) } - return &x + return x } diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index 81572a4ac4..372d3f2811 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -103,7 +103,8 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 - insI := 1 // skip the first input, which is the instance index + insI := 1 // skip the first input, which is the instance index + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. for wI := range data.circuit { w := &data.circuit[wI] if w.IsInput() { // read from provided input diff --git a/internal/gkr/bls24-315/gate_testing.go b/internal/gkr/bls24-315/gate_testing.go index 058b53cc06..c25c46bb4d 100644 --- a/internal/gkr/bls24-315/gate_testing.go +++ b/internal/gkr/bls24-315/gate_testing.go @@ -21,6 +21,7 @@ import ( // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { + var api gateAPI fWrapped := api.convertFunc(f) // fix all variables except the i-th one at random points @@ -118,6 +119,7 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { + var api gateAPI fFr := api.convertFunc(f) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { @@ -127,11 +129,13 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { } return len(p) - 1, nil } + api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { + var api gateAPI fFr := api.convertFunc(f) if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) @@ -145,6 +149,7 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + var api gateAPI x := make(fr.Vector, nbIn) x.MustSetRandom() fFr := api.convertFunc(f) diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 7aee277ba4..c93b8a3c95 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -104,8 +104,8 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - - gateEvaluation.Set(wire.Gate.Evaluate(api, inputEvaluations...).(*fr.Element)) + var api gateAPI + gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*fr.Element)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,7 +236,10 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var step fr.Element + var ( + step fr.Element + api gateAPI + ) res := make([]fr.Element, degGJ) @@ -266,11 +269,12 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { for i := range gateInput { gateInput[i] = &mlEvals[eIndex+1+i] } - summand := wire.Gate.Evaluate(api, gateInput...).(*fr.Element) + summand := wire.Gate.Evaluate(&api, gateInput...).(*fr.Element) summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } + api.freeElements() } mu.Lock() for i := range gJ { @@ -668,6 +672,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } + var api gateAPI ins := make([]fr.Element, maxNbIns) for i := range nbInstances { for wI, w := range wires { @@ -724,52 +729,54 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } // gateAPI implements gkr.GateAPI. -type gateAPI struct{} - -var api gateAPI +// It uses a synchronous memory pool underneath to minimize heap allocations. +type gateAPI struct { + allocated []*fr.Element + nbUsed int +} -func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element // TODO Heap allocated. Keep an eye on perf - res.Add(cast(i1), cast(i2)) +func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Add(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Add(&res, cast(v)) + res.Add(res, api.cast(v)) } - return &res + return res } -func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - var prod fr.Element - prod.Mul(cast(b), cast(c)) - res := cast(a) - res.Add(res, &prod) - return &res +func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { + prod := api.newElement() + prod.Mul(api.cast(b), api.cast(c)) + res := api.cast(a) + res.Add(res, prod) + return res } -func (gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - var res fr.Element - res.Neg(cast(i1)) - return &res +func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { + res := api.newElement() + res.Neg(api.cast(i1)) + return res } -func (gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element - res.Sub(cast(i1), cast(i2)) +func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Sub(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Sub(&res, cast(v)) + res.Sub(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element - res.Mul(cast(i1), cast(i2)) +func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Mul(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Mul(&res, cast(v)) + res.Mul(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Println(a ...frontend.Variable) { +func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -787,7 +794,7 @@ func (gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { +func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { inVar[i] = &in[i] @@ -795,22 +802,35 @@ func (api gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { return f(api, inVar...).(*fr.Element) } +// Done with the current task. Put back all the allocated slices. +func (api *gateAPI) freeElements() { + api.nbUsed = 0 +} + +func (api *gateAPI) newElement() *fr.Element { + api.nbUsed++ + if api.nbUsed >= len(api.allocated) { + api.allocated = append(api.allocated, new(fr.Element)) + } + return api.allocated[api.nbUsed-1] +} + type gateFunctionFr func(...fr.Element) *fr.Element // convertFunc turns f into a function that accepts and returns fr.Element. -func (api gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { +func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { return func(in ...fr.Element) *fr.Element { return api.evaluate(f, in...) } } -func cast(v frontend.Variable) *fr.Element { +func (api *gateAPI) cast(v frontend.Variable) *fr.Element { if x, ok := v.(*fr.Element); ok { // fast path, no extra heap allocation return x } - var x fr.Element + x := api.newElement() if _, err := x.SetInterface(v); err != nil { panic(err) } - return &x + return x } diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index 783cc964c8..aa7d9cd19d 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -103,7 +103,8 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 - insI := 1 // skip the first input, which is the instance index + insI := 1 // skip the first input, which is the instance index + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. for wI := range data.circuit { w := &data.circuit[wI] if w.IsInput() { // read from provided input diff --git a/internal/gkr/bls24-317/gate_testing.go b/internal/gkr/bls24-317/gate_testing.go index ed418ff1b0..7aac990c1b 100644 --- a/internal/gkr/bls24-317/gate_testing.go +++ b/internal/gkr/bls24-317/gate_testing.go @@ -21,6 +21,7 @@ import ( // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { + var api gateAPI fWrapped := api.convertFunc(f) // fix all variables except the i-th one at random points @@ -118,6 +119,7 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { + var api gateAPI fFr := api.convertFunc(f) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { @@ -127,11 +129,13 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { } return len(p) - 1, nil } + api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { + var api gateAPI fFr := api.convertFunc(f) if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) @@ -145,6 +149,7 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + var api gateAPI x := make(fr.Vector, nbIn) x.MustSetRandom() fFr := api.convertFunc(f) diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 7c679216fc..c697f94a7e 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -104,8 +104,8 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - - gateEvaluation.Set(wire.Gate.Evaluate(api, inputEvaluations...).(*fr.Element)) + var api gateAPI + gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*fr.Element)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,7 +236,10 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var step fr.Element + var ( + step fr.Element + api gateAPI + ) res := make([]fr.Element, degGJ) @@ -266,11 +269,12 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { for i := range gateInput { gateInput[i] = &mlEvals[eIndex+1+i] } - summand := wire.Gate.Evaluate(api, gateInput...).(*fr.Element) + summand := wire.Gate.Evaluate(&api, gateInput...).(*fr.Element) summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } + api.freeElements() } mu.Lock() for i := range gJ { @@ -668,6 +672,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } + var api gateAPI ins := make([]fr.Element, maxNbIns) for i := range nbInstances { for wI, w := range wires { @@ -724,52 +729,54 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } // gateAPI implements gkr.GateAPI. -type gateAPI struct{} - -var api gateAPI +// It uses a synchronous memory pool underneath to minimize heap allocations. +type gateAPI struct { + allocated []*fr.Element + nbUsed int +} -func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element // TODO Heap allocated. Keep an eye on perf - res.Add(cast(i1), cast(i2)) +func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Add(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Add(&res, cast(v)) + res.Add(res, api.cast(v)) } - return &res + return res } -func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - var prod fr.Element - prod.Mul(cast(b), cast(c)) - res := cast(a) - res.Add(res, &prod) - return &res +func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { + prod := api.newElement() + prod.Mul(api.cast(b), api.cast(c)) + res := api.cast(a) + res.Add(res, prod) + return res } -func (gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - var res fr.Element - res.Neg(cast(i1)) - return &res +func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { + res := api.newElement() + res.Neg(api.cast(i1)) + return res } -func (gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element - res.Sub(cast(i1), cast(i2)) +func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Sub(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Sub(&res, cast(v)) + res.Sub(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element - res.Mul(cast(i1), cast(i2)) +func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Mul(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Mul(&res, cast(v)) + res.Mul(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Println(a ...frontend.Variable) { +func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -787,7 +794,7 @@ func (gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { +func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { inVar[i] = &in[i] @@ -795,22 +802,35 @@ func (api gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { return f(api, inVar...).(*fr.Element) } +// Done with the current task. Put back all the allocated slices. +func (api *gateAPI) freeElements() { + api.nbUsed = 0 +} + +func (api *gateAPI) newElement() *fr.Element { + api.nbUsed++ + if api.nbUsed >= len(api.allocated) { + api.allocated = append(api.allocated, new(fr.Element)) + } + return api.allocated[api.nbUsed-1] +} + type gateFunctionFr func(...fr.Element) *fr.Element // convertFunc turns f into a function that accepts and returns fr.Element. -func (api gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { +func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { return func(in ...fr.Element) *fr.Element { return api.evaluate(f, in...) } } -func cast(v frontend.Variable) *fr.Element { +func (api *gateAPI) cast(v frontend.Variable) *fr.Element { if x, ok := v.(*fr.Element); ok { // fast path, no extra heap allocation return x } - var x fr.Element + x := api.newElement() if _, err := x.SetInterface(v); err != nil { panic(err) } - return &x + return x } diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index 234a327324..dc4fe325e4 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -103,7 +103,8 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 - insI := 1 // skip the first input, which is the instance index + insI := 1 // skip the first input, which is the instance index + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. for wI := range data.circuit { w := &data.circuit[wI] if w.IsInput() { // read from provided input diff --git a/internal/gkr/bn254/gate_testing.go b/internal/gkr/bn254/gate_testing.go index e9311a3ea5..5d5260ee19 100644 --- a/internal/gkr/bn254/gate_testing.go +++ b/internal/gkr/bn254/gate_testing.go @@ -21,6 +21,7 @@ import ( // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { + var api gateAPI fWrapped := api.convertFunc(f) // fix all variables except the i-th one at random points @@ -118,6 +119,7 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { + var api gateAPI fFr := api.convertFunc(f) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { @@ -127,11 +129,13 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { } return len(p) - 1, nil } + api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { + var api gateAPI fFr := api.convertFunc(f) if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) @@ -145,6 +149,7 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + var api gateAPI x := make(fr.Vector, nbIn) x.MustSetRandom() fFr := api.convertFunc(f) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 0174caa564..f5291406cb 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -104,8 +104,8 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - - gateEvaluation.Set(wire.Gate.Evaluate(api, inputEvaluations...).(*fr.Element)) + var api gateAPI + gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*fr.Element)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,7 +236,10 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var step fr.Element + var ( + step fr.Element + api gateAPI + ) res := make([]fr.Element, degGJ) @@ -266,11 +269,12 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { for i := range gateInput { gateInput[i] = &mlEvals[eIndex+1+i] } - summand := wire.Gate.Evaluate(api, gateInput...).(*fr.Element) + summand := wire.Gate.Evaluate(&api, gateInput...).(*fr.Element) summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } + api.freeElements() } mu.Lock() for i := range gJ { @@ -668,6 +672,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } + var api gateAPI ins := make([]fr.Element, maxNbIns) for i := range nbInstances { for wI, w := range wires { @@ -724,52 +729,54 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } // gateAPI implements gkr.GateAPI. -type gateAPI struct{} - -var api gateAPI +// It uses a synchronous memory pool underneath to minimize heap allocations. +type gateAPI struct { + allocated []*fr.Element + nbUsed int +} -func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element // TODO Heap allocated. Keep an eye on perf - res.Add(cast(i1), cast(i2)) +func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Add(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Add(&res, cast(v)) + res.Add(res, api.cast(v)) } - return &res + return res } -func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - var prod fr.Element - prod.Mul(cast(b), cast(c)) - res := cast(a) - res.Add(res, &prod) - return &res +func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { + prod := api.newElement() + prod.Mul(api.cast(b), api.cast(c)) + res := api.cast(a) + res.Add(res, prod) + return res } -func (gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - var res fr.Element - res.Neg(cast(i1)) - return &res +func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { + res := api.newElement() + res.Neg(api.cast(i1)) + return res } -func (gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element - res.Sub(cast(i1), cast(i2)) +func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Sub(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Sub(&res, cast(v)) + res.Sub(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element - res.Mul(cast(i1), cast(i2)) +func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Mul(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Mul(&res, cast(v)) + res.Mul(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Println(a ...frontend.Variable) { +func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -787,7 +794,7 @@ func (gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { +func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { inVar[i] = &in[i] @@ -795,22 +802,35 @@ func (api gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { return f(api, inVar...).(*fr.Element) } +// Done with the current task. Put back all the allocated slices. +func (api *gateAPI) freeElements() { + api.nbUsed = 0 +} + +func (api *gateAPI) newElement() *fr.Element { + api.nbUsed++ + if api.nbUsed >= len(api.allocated) { + api.allocated = append(api.allocated, new(fr.Element)) + } + return api.allocated[api.nbUsed-1] +} + type gateFunctionFr func(...fr.Element) *fr.Element // convertFunc turns f into a function that accepts and returns fr.Element. -func (api gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { +func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { return func(in ...fr.Element) *fr.Element { return api.evaluate(f, in...) } } -func cast(v frontend.Variable) *fr.Element { +func (api *gateAPI) cast(v frontend.Variable) *fr.Element { if x, ok := v.(*fr.Element); ok { // fast path, no extra heap allocation return x } - var x fr.Element + x := api.newElement() if _, err := x.SetInterface(v); err != nil { panic(err) } - return &x + return x } diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index f855222636..ab61f1ef43 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -103,7 +103,8 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 - insI := 1 // skip the first input, which is the instance index + insI := 1 // skip the first input, which is the instance index + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. for wI := range data.circuit { w := &data.circuit[wI] if w.IsInput() { // read from provided input diff --git a/internal/gkr/bw6-633/gate_testing.go b/internal/gkr/bw6-633/gate_testing.go index 8074b9621c..70d8aa7d4b 100644 --- a/internal/gkr/bw6-633/gate_testing.go +++ b/internal/gkr/bw6-633/gate_testing.go @@ -21,6 +21,7 @@ import ( // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { + var api gateAPI fWrapped := api.convertFunc(f) // fix all variables except the i-th one at random points @@ -118,6 +119,7 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { + var api gateAPI fFr := api.convertFunc(f) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { @@ -127,11 +129,13 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { } return len(p) - 1, nil } + api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { + var api gateAPI fFr := api.convertFunc(f) if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) @@ -145,6 +149,7 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + var api gateAPI x := make(fr.Vector, nbIn) x.MustSetRandom() fFr := api.convertFunc(f) diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 2c2bda2037..5f3acb6842 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -104,8 +104,8 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - - gateEvaluation.Set(wire.Gate.Evaluate(api, inputEvaluations...).(*fr.Element)) + var api gateAPI + gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*fr.Element)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,7 +236,10 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var step fr.Element + var ( + step fr.Element + api gateAPI + ) res := make([]fr.Element, degGJ) @@ -266,11 +269,12 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { for i := range gateInput { gateInput[i] = &mlEvals[eIndex+1+i] } - summand := wire.Gate.Evaluate(api, gateInput...).(*fr.Element) + summand := wire.Gate.Evaluate(&api, gateInput...).(*fr.Element) summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } + api.freeElements() } mu.Lock() for i := range gJ { @@ -668,6 +672,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } + var api gateAPI ins := make([]fr.Element, maxNbIns) for i := range nbInstances { for wI, w := range wires { @@ -724,52 +729,54 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } // gateAPI implements gkr.GateAPI. -type gateAPI struct{} - -var api gateAPI +// It uses a synchronous memory pool underneath to minimize heap allocations. +type gateAPI struct { + allocated []*fr.Element + nbUsed int +} -func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element // TODO Heap allocated. Keep an eye on perf - res.Add(cast(i1), cast(i2)) +func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Add(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Add(&res, cast(v)) + res.Add(res, api.cast(v)) } - return &res + return res } -func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - var prod fr.Element - prod.Mul(cast(b), cast(c)) - res := cast(a) - res.Add(res, &prod) - return &res +func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { + prod := api.newElement() + prod.Mul(api.cast(b), api.cast(c)) + res := api.cast(a) + res.Add(res, prod) + return res } -func (gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - var res fr.Element - res.Neg(cast(i1)) - return &res +func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { + res := api.newElement() + res.Neg(api.cast(i1)) + return res } -func (gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element - res.Sub(cast(i1), cast(i2)) +func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Sub(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Sub(&res, cast(v)) + res.Sub(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element - res.Mul(cast(i1), cast(i2)) +func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Mul(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Mul(&res, cast(v)) + res.Mul(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Println(a ...frontend.Variable) { +func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -787,7 +794,7 @@ func (gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { +func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { inVar[i] = &in[i] @@ -795,22 +802,35 @@ func (api gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { return f(api, inVar...).(*fr.Element) } +// Done with the current task. Put back all the allocated slices. +func (api *gateAPI) freeElements() { + api.nbUsed = 0 +} + +func (api *gateAPI) newElement() *fr.Element { + api.nbUsed++ + if api.nbUsed >= len(api.allocated) { + api.allocated = append(api.allocated, new(fr.Element)) + } + return api.allocated[api.nbUsed-1] +} + type gateFunctionFr func(...fr.Element) *fr.Element // convertFunc turns f into a function that accepts and returns fr.Element. -func (api gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { +func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { return func(in ...fr.Element) *fr.Element { return api.evaluate(f, in...) } } -func cast(v frontend.Variable) *fr.Element { +func (api *gateAPI) cast(v frontend.Variable) *fr.Element { if x, ok := v.(*fr.Element); ok { // fast path, no extra heap allocation return x } - var x fr.Element + x := api.newElement() if _, err := x.SetInterface(v); err != nil { panic(err) } - return &x + return x } diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index 19d347d099..de4a5c49c2 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -103,7 +103,8 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 - insI := 1 // skip the first input, which is the instance index + insI := 1 // skip the first input, which is the instance index + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. for wI := range data.circuit { w := &data.circuit[wI] if w.IsInput() { // read from provided input diff --git a/internal/gkr/bw6-761/gate_testing.go b/internal/gkr/bw6-761/gate_testing.go index 0bae6258dc..f534002d83 100644 --- a/internal/gkr/bw6-761/gate_testing.go +++ b/internal/gkr/bw6-761/gate_testing.go @@ -21,6 +21,7 @@ import ( // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { + var api gateAPI fWrapped := api.convertFunc(f) // fix all variables except the i-th one at random points @@ -118,6 +119,7 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { + var api gateAPI fFr := api.convertFunc(f) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { @@ -127,11 +129,13 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { } return len(p) - 1, nil } + api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { + var api gateAPI fFr := api.convertFunc(f) if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) @@ -145,6 +149,7 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + var api gateAPI x := make(fr.Vector, nbIn) x.MustSetRandom() fFr := api.convertFunc(f) diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 099b015b02..f063ca4fa0 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -104,8 +104,8 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - - gateEvaluation.Set(wire.Gate.Evaluate(api, inputEvaluations...).(*fr.Element)) + var api gateAPI + gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*fr.Element)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,7 +236,10 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var step fr.Element + var ( + step fr.Element + api gateAPI + ) res := make([]fr.Element, degGJ) @@ -266,11 +269,12 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { for i := range gateInput { gateInput[i] = &mlEvals[eIndex+1+i] } - summand := wire.Gate.Evaluate(api, gateInput...).(*fr.Element) + summand := wire.Gate.Evaluate(&api, gateInput...).(*fr.Element) summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } + api.freeElements() } mu.Lock() for i := range gJ { @@ -668,6 +672,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } + var api gateAPI ins := make([]fr.Element, maxNbIns) for i := range nbInstances { for wI, w := range wires { @@ -724,52 +729,54 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } // gateAPI implements gkr.GateAPI. -type gateAPI struct{} - -var api gateAPI +// It uses a synchronous memory pool underneath to minimize heap allocations. +type gateAPI struct { + allocated []*fr.Element + nbUsed int +} -func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element // TODO Heap allocated. Keep an eye on perf - res.Add(cast(i1), cast(i2)) +func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Add(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Add(&res, cast(v)) + res.Add(res, api.cast(v)) } - return &res + return res } -func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - var prod fr.Element - prod.Mul(cast(b), cast(c)) - res := cast(a) - res.Add(res, &prod) - return &res +func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { + prod := api.newElement() + prod.Mul(api.cast(b), api.cast(c)) + res := api.cast(a) + res.Add(res, prod) + return res } -func (gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - var res fr.Element - res.Neg(cast(i1)) - return &res +func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { + res := api.newElement() + res.Neg(api.cast(i1)) + return res } -func (gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element - res.Sub(cast(i1), cast(i2)) +func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Sub(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Sub(&res, cast(v)) + res.Sub(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res fr.Element - res.Mul(cast(i1), cast(i2)) +func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Mul(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Mul(&res, cast(v)) + res.Mul(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Println(a ...frontend.Variable) { +func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -787,7 +794,7 @@ func (gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { +func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { inVar[i] = &in[i] @@ -795,22 +802,35 @@ func (api gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { return f(api, inVar...).(*fr.Element) } +// Done with the current task. Put back all the allocated slices. +func (api *gateAPI) freeElements() { + api.nbUsed = 0 +} + +func (api *gateAPI) newElement() *fr.Element { + api.nbUsed++ + if api.nbUsed >= len(api.allocated) { + api.allocated = append(api.allocated, new(fr.Element)) + } + return api.allocated[api.nbUsed-1] +} + type gateFunctionFr func(...fr.Element) *fr.Element // convertFunc turns f into a function that accepts and returns fr.Element. -func (api gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { +func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { return func(in ...fr.Element) *fr.Element { return api.evaluate(f, in...) } } -func cast(v frontend.Variable) *fr.Element { +func (api *gateAPI) cast(v frontend.Variable) *fr.Element { if x, ok := v.(*fr.Element); ok { // fast path, no extra heap allocation return x } - var x fr.Element + x := api.newElement() if _, err := x.SetInterface(v); err != nil { panic(err) } - return &x + return x } diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index 09e9c13f0f..41ebcaf4c1 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -103,7 +103,8 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns := make([]frontend.Variable, data.maxNbIn) outsI := 0 - insI := 1 // skip the first input, which is the instance index + insI := 1 // skip the first input, which is the instance index + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. for wI := range data.circuit { w := &data.circuit[wI] if w.IsInput() { // read from provided input diff --git a/internal/gkr/small_rational/gate_testing.go b/internal/gkr/small_rational/gate_testing.go index 93c4ca4191..11c60d8e9c 100644 --- a/internal/gkr/small_rational/gate_testing.go +++ b/internal/gkr/small_rational/gate_testing.go @@ -21,6 +21,7 @@ import ( // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { + var api gateAPI fWrapped := api.convertFunc(f) // fix all variables except the i-th one at random points @@ -117,6 +118,7 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { + var api gateAPI fFr := api.convertFunc(f) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { @@ -126,11 +128,13 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { } return len(p) - 1, nil } + api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { + var api gateAPI fFr := api.convertFunc(f) if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) @@ -144,6 +148,7 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { + var api gateAPI x := make(small_rational.Vector, nbIn) x.MustSetRandom() fFr := api.convertFunc(f) diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index d085c6305f..3be9191db4 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -104,8 +104,8 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []small_rational.S for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - - gateEvaluation.Set(wire.Gate.Evaluate(api, inputEvaluations...).(*small_rational.SmallRational)) + var api gateAPI + gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*small_rational.SmallRational)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,7 +236,10 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]small_rational.SmallRational, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var step small_rational.SmallRational + var ( + step small_rational.SmallRational + api gateAPI + ) res := make([]small_rational.SmallRational, degGJ) @@ -266,11 +269,12 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { for i := range gateInput { gateInput[i] = &mlEvals[eIndex+1+i] } - summand := wire.Gate.Evaluate(api, gateInput...).(*small_rational.SmallRational) + summand := wire.Gate.Evaluate(&api, gateInput...).(*small_rational.SmallRational) summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } + api.freeElements() } mu.Lock() for i := range gJ { @@ -668,6 +672,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } + var api gateAPI ins := make([]small_rational.SmallRational, maxNbIns) for i := range nbInstances { for wI, w := range wires { @@ -724,52 +729,54 @@ func frToBigInts(dst []*big.Int, src []small_rational.SmallRational) { } // gateAPI implements gkr.GateAPI. -type gateAPI struct{} - -var api gateAPI +// It uses a synchronous memory pool underneath to minimize heap allocations. +type gateAPI struct { + allocated []*small_rational.SmallRational + nbUsed int +} -func (gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res small_rational.SmallRational // TODO Heap allocated. Keep an eye on perf - res.Add(cast(i1), cast(i2)) +func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Add(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Add(&res, cast(v)) + res.Add(res, api.cast(v)) } - return &res + return res } -func (gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - var prod small_rational.SmallRational - prod.Mul(cast(b), cast(c)) - res := cast(a) - res.Add(res, &prod) - return &res +func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { + prod := api.newElement() + prod.Mul(api.cast(b), api.cast(c)) + res := api.cast(a) + res.Add(res, prod) + return res } -func (gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - var res small_rational.SmallRational - res.Neg(cast(i1)) - return &res +func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { + res := api.newElement() + res.Neg(api.cast(i1)) + return res } -func (gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res small_rational.SmallRational - res.Sub(cast(i1), cast(i2)) +func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Sub(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Sub(&res, cast(v)) + res.Sub(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - var res small_rational.SmallRational - res.Mul(cast(i1), cast(i2)) +func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := api.newElement() + res.Mul(api.cast(i1), api.cast(i2)) for _, v := range in { - res.Mul(&res, cast(v)) + res.Mul(res, api.cast(v)) } - return &res + return res } -func (gateAPI) Println(a ...frontend.Variable) { +func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x small_rational.SmallRational @@ -787,7 +794,7 @@ func (gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api gateAPI) evaluate(f gkr.GateFunction, in ...small_rational.SmallRational) *small_rational.SmallRational { +func (api *gateAPI) evaluate(f gkr.GateFunction, in ...small_rational.SmallRational) *small_rational.SmallRational { inVar := make([]frontend.Variable, len(in)) for i := range in { inVar[i] = &in[i] @@ -795,22 +802,35 @@ func (api gateAPI) evaluate(f gkr.GateFunction, in ...small_rational.SmallRation return f(api, inVar...).(*small_rational.SmallRational) } +// Done with the current task. Put back all the allocated slices. +func (api *gateAPI) freeElements() { + api.nbUsed = 0 +} + +func (api *gateAPI) newElement() *small_rational.SmallRational { + api.nbUsed++ + if api.nbUsed >= len(api.allocated) { + api.allocated = append(api.allocated, new(small_rational.SmallRational)) + } + return api.allocated[api.nbUsed-1] +} + type gateFunctionFr func(...small_rational.SmallRational) *small_rational.SmallRational // convertFunc turns f into a function that accepts and returns small_rational.SmallRational. -func (api gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { +func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { return func(in ...small_rational.SmallRational) *small_rational.SmallRational { return api.evaluate(f, in...) } } -func cast(v frontend.Variable) *small_rational.SmallRational { +func (api *gateAPI) cast(v frontend.Variable) *small_rational.SmallRational { if x, ok := v.(*small_rational.SmallRational); ok { // fast path, no extra heap allocation return x } - var x small_rational.SmallRational + x := api.newElement() if _, err := x.SetInterface(v); err != nil { panic(err) } - return &x + return x } From b12c0ee95a5536865a0e9f99a657af0bba58e2d0 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 17 Sep 2025 13:56:40 -0500 Subject: [PATCH 058/251] fix: api pointer receiver --- internal/generator/backend/template/gkr/solver_hints.go.tmpl | 2 +- internal/gkr/bls12-377/solver_hints.go | 2 +- internal/gkr/bls12-381/solver_hints.go | 2 +- internal/gkr/bls24-315/solver_hints.go | 2 +- internal/gkr/bls24-317/solver_hints.go | 2 +- internal/gkr/bn254/solver_hints.go | 2 +- internal/gkr/bw6-633/solver_hints.go | 2 +- internal/gkr/bw6-761/solver_hints.go | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index eebc96850f..82b5c8927d 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -111,7 +111,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 6c504f7692..96b6636151 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -117,7 +117,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index 372d3f2811..416eb334e8 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -117,7 +117,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index aa7d9cd19d..ff0267ad5f 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -117,7 +117,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index dc4fe325e4..f2ebc7a410 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -117,7 +117,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index ab61f1ef43..048895e003 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -117,7 +117,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index de4a5c49c2..1e2b9aae00 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -117,7 +117,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index 41ebcaf4c1..a64e8ad154 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -117,7 +117,7 @@ func SolveHint(data *SolvingData) hint.Hint { gateIns[i] = &data.assignment[inWI][instanceI] } - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) From e6a64bc46455e0925013574a3d1109fdfa66377a Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 17 Sep 2025 19:15:13 +0000 Subject: [PATCH 059/251] bench: gkr mimc permutations --- std/hash/mimc/gkr-mimc/gkr-mimc_test.go | 86 +++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go index 559861af2d..8a9381bfd7 100644 --- a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go +++ b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go @@ -1,11 +1,15 @@ package gkr_mimc import ( + "errors" "fmt" + "os" "slices" "testing" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/backend/plonk" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/std/hash/mimc" @@ -77,3 +81,85 @@ func TestGkrMiMCCompiles(t *testing.T) { require.NoError(t, err) fmt.Println(cs.GetNbConstraints(), "constraints") } + +type hashTreeCircuit struct { + Leaves []frontend.Variable +} + +func (c hashTreeCircuit) Define(api frontend.API) error { + if len(c.Leaves) == 0 { + return errors.New("no hashing to do") + } + + hsh, err := New(api) + if err != nil { + return err + } + + layer := slices.Clone(c.Leaves) + + for len(layer) > 1 { + if len(layer)%2 == 1 { + layer = append(layer, 0) // pad with zero + } + + for i := range len(layer) / 2 { + hsh.Reset() + hsh.Write(layer[2*i], layer[2*i+1]) + layer[i] = hsh.Sum() + } + + layer = layer[:len(layer)/2] + } + + api.AssertIsDifferent(layer[0], 0) + return nil +} + +func loadCs(t require.TestingT, filename string, circuit frontend.Circuit) constraint.ConstraintSystem { + f, err := os.Open(filename) + + if os.IsNotExist(err) { + // actually compile + cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, circuit) + require.NoError(t, err) + f, err = os.Create(filename) + require.NoError(t, err) + defer f.Close() + _, err = cs.WriteTo(f) + require.NoError(t, err) + return cs + } + + defer f.Close() + require.NoError(t, err) + + cs := plonk.NewCS(ecc.BLS12_377) + + _, err = cs.ReadFrom(f) + require.NoError(t, err) + + return cs +} + +func BenchmarkHashTree(b *testing.B) { + const size = 1 << 15 // about 2 ^ 16 total hashes + + circuit := hashTreeCircuit{ + Leaves: make([]frontend.Variable, size), + } + assignment := hashTreeCircuit{ + Leaves: make([]frontend.Variable, size), + } + + for i := range assignment.Leaves { + assignment.Leaves[i] = i + } + + cs := loadCs(b, "gkrmimc_hashtree.cs", &circuit) + + w, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField()) + require.NoError(b, err) + + require.NoError(b, cs.IsSolved(w)) +} From d55dbf4238c216b39f3ca19a38c96cce684c6c2f Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 17 Sep 2025 19:18:43 +0000 Subject: [PATCH 060/251] bench: gkr-mimc permutations --- std/permutation/gkr-mimc/gkr-mimc_test.go | 70 +++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 std/permutation/gkr-mimc/gkr-mimc_test.go diff --git a/std/permutation/gkr-mimc/gkr-mimc_test.go b/std/permutation/gkr-mimc/gkr-mimc_test.go new file mode 100644 index 0000000000..93143b1279 --- /dev/null +++ b/std/permutation/gkr-mimc/gkr-mimc_test.go @@ -0,0 +1,70 @@ +package gkr_mimc + +import ( + "errors" + "slices" + "testing" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/scs" + "github.com/stretchr/testify/require" +) + +type hashTreeCircuit struct { + Leaves []frontend.Variable +} + +func (c hashTreeCircuit) Define(api frontend.API) error { + if len(c.Leaves) == 0 { + return errors.New("no hashing to do") + } + + hsh, err := NewCompressor(api) + if err != nil { + return err + } + + layer := slices.Clone(c.Leaves) + + for len(layer) > 1 { + if len(layer)%2 == 1 { + layer = append(layer, 0) // pad with zero + } + + for i := range len(layer) / 2 { + layer[i] = hsh.Compress(layer[2*i], layer[2*i+1]) + } + + layer = layer[:len(layer)/2] + } + + api.AssertIsDifferent(layer[0], 0) + return nil +} + +func BenchmarkGkrPermutations(b *testing.B) { + circuit, assignment := hashTreeCircuits(50000) + + cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &circuit) + require.NoError(b, err) + + witness, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField()) + require.NoError(b, err) + + _, err = cs.Solve(witness) + require.NoError(b, err) +} + +func hashTreeCircuits(n int) (circuit, assignment hashTreeCircuit) { + leaves := make([]frontend.Variable, n) + for i := range n { + leaves[i] = i + } + + return hashTreeCircuit{ + Leaves: make([]frontend.Variable, len(leaves)), + }, hashTreeCircuit{ + Leaves: leaves, + } +} From 466f16bbab16ca8754220073959dad62d86b9fcd Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 17 Sep 2025 19:43:26 +0000 Subject: [PATCH 061/251] refactor: remove loadCs --- std/hash/mimc/gkr-mimc/gkr-mimc_test.go | 32 ++----------------------- 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go index 8a9381bfd7..751ecaa473 100644 --- a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go +++ b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go @@ -3,13 +3,10 @@ package gkr_mimc import ( "errors" "fmt" - "os" "slices" "testing" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend/plonk" - "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/std/hash/mimc" @@ -116,32 +113,6 @@ func (c hashTreeCircuit) Define(api frontend.API) error { return nil } -func loadCs(t require.TestingT, filename string, circuit frontend.Circuit) constraint.ConstraintSystem { - f, err := os.Open(filename) - - if os.IsNotExist(err) { - // actually compile - cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, circuit) - require.NoError(t, err) - f, err = os.Create(filename) - require.NoError(t, err) - defer f.Close() - _, err = cs.WriteTo(f) - require.NoError(t, err) - return cs - } - - defer f.Close() - require.NoError(t, err) - - cs := plonk.NewCS(ecc.BLS12_377) - - _, err = cs.ReadFrom(f) - require.NoError(t, err) - - return cs -} - func BenchmarkHashTree(b *testing.B) { const size = 1 << 15 // about 2 ^ 16 total hashes @@ -156,7 +127,8 @@ func BenchmarkHashTree(b *testing.B) { assignment.Leaves[i] = i } - cs := loadCs(b, "gkrmimc_hashtree.cs", &circuit) + cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &circuit) + require.NoError(b, err) w, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField()) require.NoError(b, err) From 57e95bcf7a621505abe3503a0a5ac1b087cec159 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 17 Sep 2025 20:01:43 +0000 Subject: [PATCH 062/251] perf: reset api in gkr solver --- internal/gkr/bls12-377/solver_hints.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 96b6636151..1b28a97da7 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -118,6 +118,7 @@ func SolveHint(data *SolvingData) hint.Hint { } data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) From 6755e03ad4ef5c888dab63ad7ad405b1b5039c26 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 17 Sep 2025 15:36:55 -0500 Subject: [PATCH 063/251] build: generify api reset --- internal/generator/backend/template/gkr/solver_hints.go.tmpl | 1 + internal/gkr/bls12-381/solver_hints.go | 1 + internal/gkr/bls24-315/solver_hints.go | 1 + internal/gkr/bls24-317/solver_hints.go | 1 + internal/gkr/bn254/solver_hints.go | 1 + internal/gkr/bw6-633/solver_hints.go | 1 + internal/gkr/bw6-761/solver_hints.go | 1 + 7 files changed, 7 insertions(+) diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index 82b5c8927d..bfa2d3114a 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -112,6 +112,7 @@ func SolveHint(data *SolvingData) hint.Hint { } data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index 416eb334e8..e576d3994d 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -118,6 +118,7 @@ func SolveHint(data *SolvingData) hint.Hint { } data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index ff0267ad5f..c122606692 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -118,6 +118,7 @@ func SolveHint(data *SolvingData) hint.Hint { } data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index f2ebc7a410..256d6cf9dc 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -118,6 +118,7 @@ func SolveHint(data *SolvingData) hint.Hint { } data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index 048895e003..164d353e9e 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -118,6 +118,7 @@ func SolveHint(data *SolvingData) hint.Hint { } data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index 1e2b9aae00..4c57f6e651 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -118,6 +118,7 @@ func SolveHint(data *SolvingData) hint.Hint { } data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index a64e8ad154..679fc6270f 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -118,6 +118,7 @@ func SolveHint(data *SolvingData) hint.Hint { } data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { data.assignment[wI][instanceI].BigInt(outs[outsI]) From b1de7358250e74baf1fad62c5aca5db3b6d4fac4 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 17 Sep 2025 15:42:15 -0500 Subject: [PATCH 064/251] `hashTree` -> `merkleTree` --- std/hash/mimc/gkr-mimc/gkr-mimc_test.go | 10 +++++----- std/permutation/gkr-mimc/gkr-mimc_test.go | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go index 751ecaa473..95956ff6c4 100644 --- a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go +++ b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go @@ -79,11 +79,11 @@ func TestGkrMiMCCompiles(t *testing.T) { fmt.Println(cs.GetNbConstraints(), "constraints") } -type hashTreeCircuit struct { +type merkleTreeCircuit struct { Leaves []frontend.Variable } -func (c hashTreeCircuit) Define(api frontend.API) error { +func (c merkleTreeCircuit) Define(api frontend.API) error { if len(c.Leaves) == 0 { return errors.New("no hashing to do") } @@ -113,13 +113,13 @@ func (c hashTreeCircuit) Define(api frontend.API) error { return nil } -func BenchmarkHashTree(b *testing.B) { +func BenchmarkMerkleTree(b *testing.B) { const size = 1 << 15 // about 2 ^ 16 total hashes - circuit := hashTreeCircuit{ + circuit := merkleTreeCircuit{ Leaves: make([]frontend.Variable, size), } - assignment := hashTreeCircuit{ + assignment := merkleTreeCircuit{ Leaves: make([]frontend.Variable, size), } diff --git a/std/permutation/gkr-mimc/gkr-mimc_test.go b/std/permutation/gkr-mimc/gkr-mimc_test.go index 93143b1279..6cc1bde714 100644 --- a/std/permutation/gkr-mimc/gkr-mimc_test.go +++ b/std/permutation/gkr-mimc/gkr-mimc_test.go @@ -11,11 +11,11 @@ import ( "github.com/stretchr/testify/require" ) -type hashTreeCircuit struct { +type merkleTreeCircuit struct { Leaves []frontend.Variable } -func (c hashTreeCircuit) Define(api frontend.API) error { +func (c merkleTreeCircuit) Define(api frontend.API) error { if len(c.Leaves) == 0 { return errors.New("no hashing to do") } @@ -43,7 +43,7 @@ func (c hashTreeCircuit) Define(api frontend.API) error { return nil } -func BenchmarkGkrPermutations(b *testing.B) { +func BenchmarkMerkleTree(b *testing.B) { circuit, assignment := hashTreeCircuits(50000) cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &circuit) @@ -56,15 +56,15 @@ func BenchmarkGkrPermutations(b *testing.B) { require.NoError(b, err) } -func hashTreeCircuits(n int) (circuit, assignment hashTreeCircuit) { +func hashTreeCircuits(n int) (circuit, assignment merkleTreeCircuit) { leaves := make([]frontend.Variable, n) for i := range n { leaves[i] = i } - return hashTreeCircuit{ + return merkleTreeCircuit{ Leaves: make([]frontend.Variable, len(leaves)), - }, hashTreeCircuit{ + }, merkleTreeCircuit{ Leaves: leaves, } } From 6c4c5c665974c8140219a8f65012e7ca2093a559 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 17 Sep 2025 15:55:28 -0500 Subject: [PATCH 065/251] docs: copilot-inspired explanation for `freeElements` --- internal/generator/backend/template/gkr/gkr.go.tmpl | 2 +- internal/gkr/bls12-377/gkr.go | 2 +- internal/gkr/bls12-381/gkr.go | 2 +- internal/gkr/bls24-315/gkr.go | 2 +- internal/gkr/bls24-317/gkr.go | 2 +- internal/gkr/bn254/gkr.go | 2 +- internal/gkr/bw6-633/gkr.go | 2 +- internal/gkr/bw6-761/gkr.go | 2 +- internal/gkr/small_rational/gkr.go | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index c5c5b21dc2..c2d2901aab 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -798,7 +798,7 @@ func (api *gateAPI) evaluate(f gkr.GateFunction, in ...{{ .ElementType }}) *{{ . return f(api, inVar...).(*{{ .ElementType }}) } -// Done with the current task. Put back all the allocated slices. +// Put all elements back in the pool. func (api *gateAPI) freeElements() { api.nbUsed = 0 } diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index c31d447691..af149027cb 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -802,7 +802,7 @@ func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { return f(api, inVar...).(*fr.Element) } -// Done with the current task. Put back all the allocated slices. +// Put all elements back in the pool. func (api *gateAPI) freeElements() { api.nbUsed = 0 } diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 12b5aff144..9f2b371057 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -802,7 +802,7 @@ func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { return f(api, inVar...).(*fr.Element) } -// Done with the current task. Put back all the allocated slices. +// Put all elements back in the pool. func (api *gateAPI) freeElements() { api.nbUsed = 0 } diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index c93b8a3c95..b9dab1c6fe 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -802,7 +802,7 @@ func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { return f(api, inVar...).(*fr.Element) } -// Done with the current task. Put back all the allocated slices. +// Put all elements back in the pool. func (api *gateAPI) freeElements() { api.nbUsed = 0 } diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index c697f94a7e..ecb1a6bcb8 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -802,7 +802,7 @@ func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { return f(api, inVar...).(*fr.Element) } -// Done with the current task. Put back all the allocated slices. +// Put all elements back in the pool. func (api *gateAPI) freeElements() { api.nbUsed = 0 } diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index f5291406cb..77cb8d085b 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -802,7 +802,7 @@ func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { return f(api, inVar...).(*fr.Element) } -// Done with the current task. Put back all the allocated slices. +// Put all elements back in the pool. func (api *gateAPI) freeElements() { api.nbUsed = 0 } diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 5f3acb6842..a443549110 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -802,7 +802,7 @@ func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { return f(api, inVar...).(*fr.Element) } -// Done with the current task. Put back all the allocated slices. +// Put all elements back in the pool. func (api *gateAPI) freeElements() { api.nbUsed = 0 } diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index f063ca4fa0..0ae90d3c41 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -802,7 +802,7 @@ func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { return f(api, inVar...).(*fr.Element) } -// Done with the current task. Put back all the allocated slices. +// Put all elements back in the pool. func (api *gateAPI) freeElements() { api.nbUsed = 0 } diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 3be9191db4..0557f938df 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -802,7 +802,7 @@ func (api *gateAPI) evaluate(f gkr.GateFunction, in ...small_rational.SmallRatio return f(api, inVar...).(*small_rational.SmallRational) } -// Done with the current task. Put back all the allocated slices. +// Put all elements back in the pool. func (api *gateAPI) freeElements() { api.nbUsed = 0 } From 8d0b353e41734e9c6f740e1f93cba831850c97fd Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 17 Sep 2025 22:55:44 +0000 Subject: [PATCH 066/251] perf: more pool freeing --- internal/generator/backend/template/gkr/gkr.go.tmpl | 2 +- internal/gkr/bls12-377/gkr.go | 2 +- internal/gkr/bls12-381/gkr.go | 2 +- internal/gkr/bls24-315/gkr.go | 2 +- internal/gkr/bls24-317/gkr.go | 2 +- internal/gkr/bn254/gkr.go | 2 +- internal/gkr/bw6-633/gkr.go | 2 +- internal/gkr/bw6-761/gkr.go | 2 +- internal/gkr/small_rational/gkr.go | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index c2d2901aab..2131aea723 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -267,8 +267,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) + api.freeElements() } - api.freeElements() } mu.Lock() for i := range gJ { diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index af149027cb..c28e3df8e3 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -273,8 +273,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) + api.freeElements() } - api.freeElements() } mu.Lock() for i := range gJ { diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 9f2b371057..5c87cdd85d 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -273,8 +273,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) + api.freeElements() } - api.freeElements() } mu.Lock() for i := range gJ { diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index b9dab1c6fe..acbebf56cf 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -273,8 +273,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) + api.freeElements() } - api.freeElements() } mu.Lock() for i := range gJ { diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index ecb1a6bcb8..3d4a80d557 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -273,8 +273,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) + api.freeElements() } - api.freeElements() } mu.Lock() for i := range gJ { diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 77cb8d085b..bed1d89329 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -273,8 +273,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) + api.freeElements() } - api.freeElements() } mu.Lock() for i := range gJ { diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index a443549110..4962d1a4f9 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -273,8 +273,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) + api.freeElements() } - api.freeElements() } mu.Lock() for i := range gJ { diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 0ae90d3c41..6ea38abf29 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -273,8 +273,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) + api.freeElements() } - api.freeElements() } mu.Lock() for i := range gJ { diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 0557f938df..e65c94752e 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -273,8 +273,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) + api.freeElements() } - api.freeElements() } mu.Lock() for i := range gJ { From 5a048b6b4fefe37ba271210086b3b33ccd47ad7b Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 22 Sep 2025 14:10:47 -0500 Subject: [PATCH 067/251] perf: dedicated exp function --- .../backend/template/gkr/gkr.go.tmpl | 26 ++++++++++++++++++ internal/gkr/bls12-377/gkr.go | 27 +++++++++++++++++++ internal/gkr/bls12-381/gkr.go | 26 ++++++++++++++++++ internal/gkr/bls24-315/gkr.go | 26 ++++++++++++++++++ internal/gkr/bls24-317/gkr.go | 26 ++++++++++++++++++ internal/gkr/bn254/gkr.go | 26 ++++++++++++++++++ internal/gkr/bw6-633/gkr.go | 26 ++++++++++++++++++ internal/gkr/bw6-761/gkr.go | 26 ++++++++++++++++++ internal/gkr/engine_hints.go | 12 ++++++--- internal/gkr/gkr.go | 23 +++++++++++++++- internal/gkr/small_rational/gkr.go | 26 ++++++++++++++++++ std/gkrapi/compile.go | 2 +- std/gkrapi/gkr/types.go | 3 +++ std/hash/mimc/gkr-mimc/gkr-mimc_test.go | 5 +++- std/permutation/gkr-mimc/gkr-mimc.go | 7 +---- .../gkr-poseidon2/gkr-poseidon2_test.go | 16 +++++++++++ 16 files changed, 291 insertions(+), 12 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 2131aea723..32accd6dca 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -790,6 +790,32 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } +func (api *gateAPI) Exp(i frontend.Variable, n uint) frontend.Variable { + var res *{{ .ElementType }} + x := api.cast(i) + + if n % 2 == 0 { + res = api.cast(1) + } else { + *res = *x + } + + n /= 2 + + // square and multiply + for n != 0 { + res.Mul(res, res) + + if n % 2 != 0 { + res.Mul(res, x) + } + + n /= 2 + } + + return res +} + func (api *gateAPI) evaluate(f gkr.GateFunction, in ...{{ .ElementType }}) *{{ .ElementType }} { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index c28e3df8e3..d96f91d7d1 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "math/big" + "math/bits" "strconv" "sync" @@ -794,6 +795,32 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } +func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { + if e == 0 { + return 1 + } + n := bits.Len8(e) - 1 + e = bits.Reverse8(e) >> (8 - n) + + res := api.newElement() + x := api.cast(i) + *res = *x + + // square and multiply + for n != 0 { + res.Mul(res, res) + + if e%2 != 0 { + res.Mul(res, x) + } + + e /= 2 + n-- + } + + return res +} + func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 5c87cdd85d..0941a05deb 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -794,6 +794,32 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } +func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { + var res *fr.Element + x := api.cast(i) + + if e%2 == 0 { + res = api.cast(1) + } else { + *res = *x + } + + e /= 2 + + // square and multiply + for e != 0 { + res.Mul(res, res) + + if e%2 != 0 { + res.Mul(res, x) + } + + e /= 2 + } + + return res +} + func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index acbebf56cf..7f70f6a12c 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -794,6 +794,32 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } +func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { + var res *fr.Element + x := api.cast(i) + + if e%2 == 0 { + res = api.cast(1) + } else { + *res = *x + } + + e /= 2 + + // square and multiply + for e != 0 { + res.Mul(res, res) + + if e%2 != 0 { + res.Mul(res, x) + } + + e /= 2 + } + + return res +} + func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 3d4a80d557..0320deb5de 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -794,6 +794,32 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } +func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { + var res *fr.Element + x := api.cast(i) + + if e%2 == 0 { + res = api.cast(1) + } else { + *res = *x + } + + e /= 2 + + // square and multiply + for e != 0 { + res.Mul(res, res) + + if e%2 != 0 { + res.Mul(res, x) + } + + e /= 2 + } + + return res +} + func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index bed1d89329..a380373951 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -794,6 +794,32 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } +func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { + var res *fr.Element + x := api.cast(i) + + if e%2 == 0 { + res = api.cast(1) + } else { + *res = *x + } + + e /= 2 + + // square and multiply + for e != 0 { + res.Mul(res, res) + + if e%2 != 0 { + res.Mul(res, x) + } + + e /= 2 + } + + return res +} + func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 4962d1a4f9..66d957f8f4 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -794,6 +794,32 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } +func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { + var res *fr.Element + x := api.cast(i) + + if e%2 == 0 { + res = api.cast(1) + } else { + *res = *x + } + + e /= 2 + + // square and multiply + for e != 0 { + res.Mul(res, res) + + if e%2 != 0 { + res.Mul(res, x) + } + + e /= 2 + } + + return res +} + func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 6ea38abf29..35d6f86bad 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -794,6 +794,32 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } +func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { + var res *fr.Element + x := api.cast(i) + + if e%2 == 0 { + res = api.cast(1) + } else { + *res = *x + } + + e /= 2 + + // square and multiply + for e != 0 { + res.Mul(res, res) + + if e%2 != 0 { + res.Mul(res, x) + } + + e /= 2 + } + + return res +} + func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/engine_hints.go b/internal/gkr/engine_hints.go index 8c8bc1b797..0e272f12a4 100644 --- a/internal/gkr/engine_hints.go +++ b/internal/gkr/engine_hints.go @@ -130,7 +130,7 @@ func (h *TestEngineHints) GetAssignment(_ *big.Int, ins []*big.Int, outs []*big. return nil } -type gateAPI struct{ *big.Int } +type gateAPI struct{ mod *big.Int } func (g gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { in1 := utils.FromInterface(i1) @@ -147,7 +147,7 @@ func (g gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend func (g gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { x, y := utils.FromInterface(b), utils.FromInterface(c) x.Mul(&x, &y) - x.Mod(&x, g.Int) // reduce + x.Mod(&x, g.mod) // reduce y = utils.FromInterface(a) x.Add(&x, &y) return &x @@ -178,7 +178,13 @@ func (g gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend y = utils.FromInterface(v) x.Mul(&x, &y) } - x.Mod(&x, g.Int) // reduce + x.Mod(&x, g.mod) // reduce + return &x +} + +func (g gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { + x := utils.FromInterface(i) + x.Exp(&x, big.NewInt(int64(e)), g.mod) return &x } diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index 955ad8a354..6b8b5ebd4f 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -78,7 +78,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(api frontend.API, r inputEvaluations[i] = uniqueInputEvaluations[uniqueI] } - gateEvaluation = wire.Gate.Evaluate(api, inputEvaluations...) + gateEvaluation = wire.Gate.Evaluate(FrontendApiWrapper{api}, inputEvaluations...) } evaluation = api.Mul(evaluation, gateEvaluation) @@ -383,3 +383,24 @@ func DeserializeProof(sorted []*gkrtypes.Wire, serializedProof []frontend.Variab } return proof, nil } + +// FrontendApiWrapper implements additional functions to satisfy the gkr.GateAPI interface. +type FrontendApiWrapper struct { + frontend.API +} + +func (api FrontendApiWrapper) Exp(i frontend.Variable, e uint8) frontend.Variable { + res := frontend.Variable(1) + if e%2 != 0 { + res = i + } + e /= 2 + for e != 0 { + res = api.Mul(res, res) + if e%2 != 0 { + res = api.Mul(res, i) + } + e /= 2 + } + return res +} diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index e65c94752e..73cf93a146 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -794,6 +794,32 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } +func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { + var res *small_rational.SmallRational + x := api.cast(i) + + if e%2 == 0 { + res = api.cast(1) + } else { + *res = *x + } + + e /= 2 + + // square and multiply + for e != 0 { + res.Mul(res, res) + + if e%2 != 0 { + res.Mul(res, x) + } + + e /= 2 + } + + return res +} + func (api *gateAPI) evaluate(f gkr.GateFunction, in ...small_rational.SmallRational) *small_rational.SmallRational { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 64205b80b8..5aca90cad2 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -182,7 +182,7 @@ func (c *Circuit) finalize(api frontend.API) error { for inI, inWI := range w.Inputs { gateIn[inI] = c.assignments[inWI][0] // take the first (only) instance } - res := w.Gate.Evaluate(api, gateIn[:len(w.Inputs)]...) + res := w.Gate.Evaluate(gadget.FrontendApiWrapper{API: api}, gateIn[:len(w.Inputs)]...) if w.IsOutput() { api.AssertIsEqual(res, c.assignments[wI][0]) } else { diff --git a/std/gkrapi/gkr/types.go b/std/gkrapi/gkr/types.go index af8a40fcd6..c9b8678b7b 100644 --- a/std/gkrapi/gkr/types.go +++ b/std/gkrapi/gkr/types.go @@ -35,6 +35,9 @@ type GateAPI interface { // Mul returns res = i1 * i2 * ... in Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable + // Exp returns res = iᵉ + Exp(i frontend.Variable, e uint8) frontend.Variable + // Println behaves like fmt.Println but accepts frontend.Variable as parameter // whose value will be resolved at runtime when computed by the solver Println(a ...frontend.Variable) diff --git a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go index 95956ff6c4..3ad685e791 100644 --- a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go +++ b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go @@ -6,6 +6,7 @@ import ( "slices" "testing" + "github.com/consensys/gnark" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/scs" @@ -29,7 +30,9 @@ func TestGkrMiMC(t *testing.T) { In: slices.Clone(vals[:length*2]), } - test.NewAssert(t).CheckCircuit(circuit, test.WithValidAssignment(assignment)) + allCurves := gnark.Curves() + allCurves = []ecc.ID{ecc.BLS12_377} // TODO REMOVE + test.NewAssert(t).CheckCircuit(circuit, test.WithValidAssignment(assignment), test.WithCurves(allCurves[0], allCurves[1:]...)) } } diff --git a/std/permutation/gkr-mimc/gkr-mimc.go b/std/permutation/gkr-mimc/gkr-mimc.go index 266ee00e67..c98d100303 100644 --- a/std/permutation/gkr-mimc/gkr-mimc.go +++ b/std/permutation/gkr-mimc/gkr-mimc.go @@ -208,12 +208,7 @@ func addPow17(key *big.Int) gkr.GateFunction { if len(in) != 2 { panic("expected two input") } - s := api.Add(in[0], in[1], key) - t := api.Mul(s, s) // s² - t = api.Mul(t, t) // s⁴ - t = api.Mul(t, t) // s⁸ - t = api.Mul(t, t) // s¹⁶ - return api.Mul(t, s) // s¹⁶ × s + return api.Exp(api.Add(in[0], in[1], key), 17) } } diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go index b224bf1414..b76024c888 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go @@ -2,6 +2,8 @@ package gkr_poseidon2 import ( "fmt" + "math/bits" + "strings" "testing" "github.com/consensys/gnark-crypto/ecc" @@ -79,3 +81,17 @@ func BenchmarkGkrCompressions(b *testing.B) { _, err = cs.Solve(witness) require.NoError(b, err) } + +func TestGenerateTable(t *testing.T) { + var sb strings.Builder + for n := range 256 { + if n%16 == 0 { + sb.WriteString("\"+\n\"") + } + b := uint8(n) + b <<= bits.LeadingZeros8(b) + b = bits.Reverse8(b) + sb.WriteString(fmt.Sprintf("\\x%x", b)) + } + fmt.Println(sb.String()) +} From c6b830c3fdf9137884b4015b4bdff72e3658884a Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 22 Sep 2025 14:14:37 -0500 Subject: [PATCH 068/251] build: generify exp changes --- .../backend/template/gkr/gkr.go.tmpl | 23 ++++++++++--------- internal/gkr/bls12-381/gkr.go | 19 +++++++-------- internal/gkr/bls24-315/gkr.go | 19 +++++++-------- internal/gkr/bls24-317/gkr.go | 19 +++++++-------- internal/gkr/bn254/gkr.go | 19 +++++++-------- internal/gkr/bw6-633/gkr.go | 19 +++++++-------- internal/gkr/bw6-761/gkr.go | 19 +++++++-------- internal/gkr/small_rational/gkr.go | 19 +++++++-------- 8 files changed, 82 insertions(+), 74 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 32accd6dca..0d97343a33 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -6,6 +6,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" "math/big" + "math/bits" "strconv" "sync" "github.com/consensys/gnark/frontend" @@ -790,27 +791,27 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) Exp(i frontend.Variable, n uint) frontend.Variable { - var res *{{ .ElementType }} - x := api.cast(i) - - if n % 2 == 0 { - res = api.cast(1) - } else { - *res = *x +func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { + if e == 0 { + return 1 } + n := bits.Len8(e) - 1 + e = bits.Reverse8(e) >> (8 - n) - n /= 2 + res := api.newElement() + x := api.cast(i) + *res = *x // square and multiply for n != 0 { res.Mul(res, res) - if n % 2 != 0 { + if e % 2 != 0 { res.Mul(res, x) } - n /= 2 + e /= 2 + n-- } return res diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 0941a05deb..05560b9014 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "math/big" + "math/bits" "strconv" "sync" @@ -795,19 +796,18 @@ func (api *gateAPI) Println(a ...frontend.Variable) { } func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - var res *fr.Element - x := api.cast(i) - - if e%2 == 0 { - res = api.cast(1) - } else { - *res = *x + if e == 0 { + return 1 } + n := bits.Len8(e) - 1 + e = bits.Reverse8(e) >> (8 - n) - e /= 2 + res := api.newElement() + x := api.cast(i) + *res = *x // square and multiply - for e != 0 { + for n != 0 { res.Mul(res, res) if e%2 != 0 { @@ -815,6 +815,7 @@ func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { } e /= 2 + n-- } return res diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 7f70f6a12c..8f6feb48b6 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "math/big" + "math/bits" "strconv" "sync" @@ -795,19 +796,18 @@ func (api *gateAPI) Println(a ...frontend.Variable) { } func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - var res *fr.Element - x := api.cast(i) - - if e%2 == 0 { - res = api.cast(1) - } else { - *res = *x + if e == 0 { + return 1 } + n := bits.Len8(e) - 1 + e = bits.Reverse8(e) >> (8 - n) - e /= 2 + res := api.newElement() + x := api.cast(i) + *res = *x // square and multiply - for e != 0 { + for n != 0 { res.Mul(res, res) if e%2 != 0 { @@ -815,6 +815,7 @@ func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { } e /= 2 + n-- } return res diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 0320deb5de..495658b89d 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "math/big" + "math/bits" "strconv" "sync" @@ -795,19 +796,18 @@ func (api *gateAPI) Println(a ...frontend.Variable) { } func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - var res *fr.Element - x := api.cast(i) - - if e%2 == 0 { - res = api.cast(1) - } else { - *res = *x + if e == 0 { + return 1 } + n := bits.Len8(e) - 1 + e = bits.Reverse8(e) >> (8 - n) - e /= 2 + res := api.newElement() + x := api.cast(i) + *res = *x // square and multiply - for e != 0 { + for n != 0 { res.Mul(res, res) if e%2 != 0 { @@ -815,6 +815,7 @@ func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { } e /= 2 + n-- } return res diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index a380373951..01d2a5e9de 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "math/big" + "math/bits" "strconv" "sync" @@ -795,19 +796,18 @@ func (api *gateAPI) Println(a ...frontend.Variable) { } func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - var res *fr.Element - x := api.cast(i) - - if e%2 == 0 { - res = api.cast(1) - } else { - *res = *x + if e == 0 { + return 1 } + n := bits.Len8(e) - 1 + e = bits.Reverse8(e) >> (8 - n) - e /= 2 + res := api.newElement() + x := api.cast(i) + *res = *x // square and multiply - for e != 0 { + for n != 0 { res.Mul(res, res) if e%2 != 0 { @@ -815,6 +815,7 @@ func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { } e /= 2 + n-- } return res diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 66d957f8f4..af578e59d7 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "math/big" + "math/bits" "strconv" "sync" @@ -795,19 +796,18 @@ func (api *gateAPI) Println(a ...frontend.Variable) { } func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - var res *fr.Element - x := api.cast(i) - - if e%2 == 0 { - res = api.cast(1) - } else { - *res = *x + if e == 0 { + return 1 } + n := bits.Len8(e) - 1 + e = bits.Reverse8(e) >> (8 - n) - e /= 2 + res := api.newElement() + x := api.cast(i) + *res = *x // square and multiply - for e != 0 { + for n != 0 { res.Mul(res, res) if e%2 != 0 { @@ -815,6 +815,7 @@ func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { } e /= 2 + n-- } return res diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 35d6f86bad..63dabac899 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "math/big" + "math/bits" "strconv" "sync" @@ -795,19 +796,18 @@ func (api *gateAPI) Println(a ...frontend.Variable) { } func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - var res *fr.Element - x := api.cast(i) - - if e%2 == 0 { - res = api.cast(1) - } else { - *res = *x + if e == 0 { + return 1 } + n := bits.Len8(e) - 1 + e = bits.Reverse8(e) >> (8 - n) - e /= 2 + res := api.newElement() + x := api.cast(i) + *res = *x // square and multiply - for e != 0 { + for n != 0 { res.Mul(res, res) if e%2 != 0 { @@ -815,6 +815,7 @@ func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { } e /= 2 + n-- } return res diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 73cf93a146..ffd730e69e 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "math/big" + "math/bits" "strconv" "sync" @@ -795,19 +796,18 @@ func (api *gateAPI) Println(a ...frontend.Variable) { } func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - var res *small_rational.SmallRational - x := api.cast(i) - - if e%2 == 0 { - res = api.cast(1) - } else { - *res = *x + if e == 0 { + return 1 } + n := bits.Len8(e) - 1 + e = bits.Reverse8(e) >> (8 - n) - e /= 2 + res := api.newElement() + x := api.cast(i) + *res = *x // square and multiply - for e != 0 { + for n != 0 { res.Mul(res, res) if e%2 != 0 { @@ -815,6 +815,7 @@ func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { } e /= 2 + n-- } return res From 7bb78e7e8e9dcf8653021b8be566bad1d7e7cd82 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 22 Sep 2025 14:18:48 -0500 Subject: [PATCH 069/251] fix: test for all curves --- std/hash/mimc/gkr-mimc/gkr-mimc_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go index 3ad685e791..1cd6579e0c 100644 --- a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go +++ b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go @@ -8,6 +8,7 @@ import ( "github.com/consensys/gnark" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/backend" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/std/hash/mimc" @@ -31,8 +32,12 @@ func TestGkrMiMC(t *testing.T) { } allCurves := gnark.Curves() - allCurves = []ecc.ID{ecc.BLS12_377} // TODO REMOVE - test.NewAssert(t).CheckCircuit(circuit, test.WithValidAssignment(assignment), test.WithCurves(allCurves[0], allCurves[1:]...)) + test.NewAssert(t).CheckCircuit( + circuit, + test.WithValidAssignment(assignment), + test.WithCurves(allCurves[0], allCurves[1:]...), + test.WithBackends(backend.PLONK), + ) } } From 58a0fdb0083252917943b1e4f432b14ceee834dc Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 22 Sep 2025 21:33:41 +0000 Subject: [PATCH 070/251] perf: fastpath for ^17 --- internal/gkr/bls12-377/gkr.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index d96f91d7d1..efaa8f9a6f 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -795,15 +795,29 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } +var seventeen = big.NewInt(17) + func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { + + res := api.newElement() + x := api.cast(i) + + if e == 17 { + res.Mul(x, x) // x² + res.Mul(res, res) // x⁴ + res.Mul(res, res) // x⁸ + res.Mul(res, res) // x¹⁶ + res.Mul(res, x) // x¹⁷ + + return res + } + if e == 0 { return 1 } n := bits.Len8(e) - 1 e = bits.Reverse8(e) >> (8 - n) - res := api.newElement() - x := api.cast(i) *res = *x // square and multiply From 4b2c10207523d766c4ea3a45711f6e745a78b687 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 22 Sep 2025 16:36:56 -0500 Subject: [PATCH 071/251] fix: exp --- internal/gkr/gkr.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index 6b8b5ebd4f..b115c56aa6 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -391,16 +391,13 @@ type FrontendApiWrapper struct { func (api FrontendApiWrapper) Exp(i frontend.Variable, e uint8) frontend.Variable { res := frontend.Variable(1) - if e%2 != 0 { - res = i - } - e /= 2 - for e != 0 { + + for range 8 { res = api.Mul(res, res) - if e%2 != 0 { + if e%128 != 0 { res = api.Mul(res, i) } - e /= 2 + e <<= 1 } return res } From 30c39b4fee11c6fa13ba67474451ea0d056ebd8d Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 22 Sep 2025 16:38:31 -0500 Subject: [PATCH 072/251] fix: exp. really --- internal/gkr/gkr.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index b115c56aa6..ee43864d7a 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -394,7 +394,7 @@ func (api FrontendApiWrapper) Exp(i frontend.Variable, e uint8) frontend.Variabl for range 8 { res = api.Mul(res, res) - if e%128 != 0 { + if e&128 != 0 { res = api.Mul(res, i) } e <<= 1 From 6c7a7fccb4365761f517044555c32d755b29f40b Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 22 Sep 2025 16:43:43 -0500 Subject: [PATCH 073/251] test: modernize benchmark --- std/hash/mimc/gkr-mimc/gkr-mimc_test.go | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go index 1cd6579e0c..569201ffa1 100644 --- a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go +++ b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go @@ -2,7 +2,6 @@ package gkr_mimc import ( "errors" - "fmt" "slices" "testing" @@ -77,16 +76,6 @@ func (c *testGkrMiMCCircuit) Define(api frontend.API) error { return nil } -func TestGkrMiMCCompiles(t *testing.T) { - const n = 52000 - circuit := testGkrMiMCCircuit{ - In: make([]frontend.Variable, n), - } - cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &circuit, frontend.WithCapacity(27_000_000)) - require.NoError(t, err) - fmt.Println(cs.GetNbConstraints(), "constraints") -} - type merkleTreeCircuit struct { Leaves []frontend.Variable } @@ -141,5 +130,9 @@ func BenchmarkMerkleTree(b *testing.B) { w, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField()) require.NoError(b, err) - require.NoError(b, cs.IsSolved(w)) + for b.Loop() { + s, err := cs.Solve(w) + require.NoError(b, err) + _ = s + } } From 266c5f6d057498a714b715fda19bd8b22bae968b Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 22 Sep 2025 16:44:53 -0500 Subject: [PATCH 074/251] Revert "build: generify exp changes" This reverts commit c6b830c3fdf9137884b4015b4bdff72e3658884a. --- .../backend/template/gkr/gkr.go.tmpl | 23 +++++++++---------- internal/gkr/bls12-381/gkr.go | 19 ++++++++------- internal/gkr/bls24-315/gkr.go | 19 ++++++++------- internal/gkr/bls24-317/gkr.go | 19 ++++++++------- internal/gkr/bn254/gkr.go | 19 ++++++++------- internal/gkr/bw6-633/gkr.go | 19 ++++++++------- internal/gkr/bw6-761/gkr.go | 19 ++++++++------- internal/gkr/small_rational/gkr.go | 19 ++++++++------- 8 files changed, 74 insertions(+), 82 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 0d97343a33..32accd6dca 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -6,7 +6,6 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" "math/big" - "math/bits" "strconv" "sync" "github.com/consensys/gnark/frontend" @@ -791,27 +790,27 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - if e == 0 { - return 1 +func (api *gateAPI) Exp(i frontend.Variable, n uint) frontend.Variable { + var res *{{ .ElementType }} + x := api.cast(i) + + if n % 2 == 0 { + res = api.cast(1) + } else { + *res = *x } - n := bits.Len8(e) - 1 - e = bits.Reverse8(e) >> (8 - n) - res := api.newElement() - x := api.cast(i) - *res = *x + n /= 2 // square and multiply for n != 0 { res.Mul(res, res) - if e % 2 != 0 { + if n % 2 != 0 { res.Mul(res, x) } - e /= 2 - n-- + n /= 2 } return res diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 05560b9014..0941a05deb 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "math/big" - "math/bits" "strconv" "sync" @@ -796,18 +795,19 @@ func (api *gateAPI) Println(a ...frontend.Variable) { } func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - if e == 0 { - return 1 + var res *fr.Element + x := api.cast(i) + + if e%2 == 0 { + res = api.cast(1) + } else { + *res = *x } - n := bits.Len8(e) - 1 - e = bits.Reverse8(e) >> (8 - n) - res := api.newElement() - x := api.cast(i) - *res = *x + e /= 2 // square and multiply - for n != 0 { + for e != 0 { res.Mul(res, res) if e%2 != 0 { @@ -815,7 +815,6 @@ func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { } e /= 2 - n-- } return res diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 8f6feb48b6..7f70f6a12c 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "math/big" - "math/bits" "strconv" "sync" @@ -796,18 +795,19 @@ func (api *gateAPI) Println(a ...frontend.Variable) { } func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - if e == 0 { - return 1 + var res *fr.Element + x := api.cast(i) + + if e%2 == 0 { + res = api.cast(1) + } else { + *res = *x } - n := bits.Len8(e) - 1 - e = bits.Reverse8(e) >> (8 - n) - res := api.newElement() - x := api.cast(i) - *res = *x + e /= 2 // square and multiply - for n != 0 { + for e != 0 { res.Mul(res, res) if e%2 != 0 { @@ -815,7 +815,6 @@ func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { } e /= 2 - n-- } return res diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 495658b89d..0320deb5de 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "math/big" - "math/bits" "strconv" "sync" @@ -796,18 +795,19 @@ func (api *gateAPI) Println(a ...frontend.Variable) { } func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - if e == 0 { - return 1 + var res *fr.Element + x := api.cast(i) + + if e%2 == 0 { + res = api.cast(1) + } else { + *res = *x } - n := bits.Len8(e) - 1 - e = bits.Reverse8(e) >> (8 - n) - res := api.newElement() - x := api.cast(i) - *res = *x + e /= 2 // square and multiply - for n != 0 { + for e != 0 { res.Mul(res, res) if e%2 != 0 { @@ -815,7 +815,6 @@ func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { } e /= 2 - n-- } return res diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 01d2a5e9de..a380373951 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "math/big" - "math/bits" "strconv" "sync" @@ -796,18 +795,19 @@ func (api *gateAPI) Println(a ...frontend.Variable) { } func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - if e == 0 { - return 1 + var res *fr.Element + x := api.cast(i) + + if e%2 == 0 { + res = api.cast(1) + } else { + *res = *x } - n := bits.Len8(e) - 1 - e = bits.Reverse8(e) >> (8 - n) - res := api.newElement() - x := api.cast(i) - *res = *x + e /= 2 // square and multiply - for n != 0 { + for e != 0 { res.Mul(res, res) if e%2 != 0 { @@ -815,7 +815,6 @@ func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { } e /= 2 - n-- } return res diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index af578e59d7..66d957f8f4 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "math/big" - "math/bits" "strconv" "sync" @@ -796,18 +795,19 @@ func (api *gateAPI) Println(a ...frontend.Variable) { } func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - if e == 0 { - return 1 + var res *fr.Element + x := api.cast(i) + + if e%2 == 0 { + res = api.cast(1) + } else { + *res = *x } - n := bits.Len8(e) - 1 - e = bits.Reverse8(e) >> (8 - n) - res := api.newElement() - x := api.cast(i) - *res = *x + e /= 2 // square and multiply - for n != 0 { + for e != 0 { res.Mul(res, res) if e%2 != 0 { @@ -815,7 +815,6 @@ func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { } e /= 2 - n-- } return res diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 63dabac899..35d6f86bad 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "math/big" - "math/bits" "strconv" "sync" @@ -796,18 +795,19 @@ func (api *gateAPI) Println(a ...frontend.Variable) { } func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - if e == 0 { - return 1 + var res *fr.Element + x := api.cast(i) + + if e%2 == 0 { + res = api.cast(1) + } else { + *res = *x } - n := bits.Len8(e) - 1 - e = bits.Reverse8(e) >> (8 - n) - res := api.newElement() - x := api.cast(i) - *res = *x + e /= 2 // square and multiply - for n != 0 { + for e != 0 { res.Mul(res, res) if e%2 != 0 { @@ -815,7 +815,6 @@ func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { } e /= 2 - n-- } return res diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index ffd730e69e..73cf93a146 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "math/big" - "math/bits" "strconv" "sync" @@ -796,18 +795,19 @@ func (api *gateAPI) Println(a ...frontend.Variable) { } func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - if e == 0 { - return 1 + var res *small_rational.SmallRational + x := api.cast(i) + + if e%2 == 0 { + res = api.cast(1) + } else { + *res = *x } - n := bits.Len8(e) - 1 - e = bits.Reverse8(e) >> (8 - n) - res := api.newElement() - x := api.cast(i) - *res = *x + e /= 2 // square and multiply - for n != 0 { + for e != 0 { res.Mul(res, res) if e%2 != 0 { @@ -815,7 +815,6 @@ func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { } e /= 2 - n-- } return res From 5dd1d61d207a854c477b53a886d99f379c28b956 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 22 Sep 2025 16:47:02 -0500 Subject: [PATCH 075/251] revert: exp func --- .../backend/template/gkr/gkr.go.tmpl | 26 ------------ internal/gkr/bls12-377/gkr.go | 41 ------------------- internal/gkr/bls12-381/gkr.go | 26 ------------ internal/gkr/bls24-315/gkr.go | 26 ------------ internal/gkr/bls24-317/gkr.go | 26 ------------ internal/gkr/bn254/gkr.go | 26 ------------ internal/gkr/bw6-633/gkr.go | 26 ------------ internal/gkr/bw6-761/gkr.go | 26 ------------ internal/gkr/engine_hints.go | 12 ++---- internal/gkr/small_rational/gkr.go | 26 ------------ std/gkrapi/compile.go | 2 +- std/gkrapi/gkr/types.go | 3 -- std/permutation/gkr-mimc/gkr-mimc.go | 7 +++- .../gkr-poseidon2/gkr-poseidon2_test.go | 16 -------- 14 files changed, 10 insertions(+), 279 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 32accd6dca..2131aea723 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -790,32 +790,6 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) Exp(i frontend.Variable, n uint) frontend.Variable { - var res *{{ .ElementType }} - x := api.cast(i) - - if n % 2 == 0 { - res = api.cast(1) - } else { - *res = *x - } - - n /= 2 - - // square and multiply - for n != 0 { - res.Mul(res, res) - - if n % 2 != 0 { - res.Mul(res, x) - } - - n /= 2 - } - - return res -} - func (api *gateAPI) evaluate(f gkr.GateFunction, in ...{{ .ElementType }}) *{{ .ElementType }} { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index efaa8f9a6f..c28e3df8e3 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "math/big" - "math/bits" "strconv" "sync" @@ -795,46 +794,6 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -var seventeen = big.NewInt(17) - -func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - - res := api.newElement() - x := api.cast(i) - - if e == 17 { - res.Mul(x, x) // x² - res.Mul(res, res) // x⁴ - res.Mul(res, res) // x⁸ - res.Mul(res, res) // x¹⁶ - res.Mul(res, x) // x¹⁷ - - return res - } - - if e == 0 { - return 1 - } - n := bits.Len8(e) - 1 - e = bits.Reverse8(e) >> (8 - n) - - *res = *x - - // square and multiply - for n != 0 { - res.Mul(res, res) - - if e%2 != 0 { - res.Mul(res, x) - } - - e /= 2 - n-- - } - - return res -} - func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 0941a05deb..5c87cdd85d 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -794,32 +794,6 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - var res *fr.Element - x := api.cast(i) - - if e%2 == 0 { - res = api.cast(1) - } else { - *res = *x - } - - e /= 2 - - // square and multiply - for e != 0 { - res.Mul(res, res) - - if e%2 != 0 { - res.Mul(res, x) - } - - e /= 2 - } - - return res -} - func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 7f70f6a12c..acbebf56cf 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -794,32 +794,6 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - var res *fr.Element - x := api.cast(i) - - if e%2 == 0 { - res = api.cast(1) - } else { - *res = *x - } - - e /= 2 - - // square and multiply - for e != 0 { - res.Mul(res, res) - - if e%2 != 0 { - res.Mul(res, x) - } - - e /= 2 - } - - return res -} - func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 0320deb5de..3d4a80d557 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -794,32 +794,6 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - var res *fr.Element - x := api.cast(i) - - if e%2 == 0 { - res = api.cast(1) - } else { - *res = *x - } - - e /= 2 - - // square and multiply - for e != 0 { - res.Mul(res, res) - - if e%2 != 0 { - res.Mul(res, x) - } - - e /= 2 - } - - return res -} - func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index a380373951..bed1d89329 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -794,32 +794,6 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - var res *fr.Element - x := api.cast(i) - - if e%2 == 0 { - res = api.cast(1) - } else { - *res = *x - } - - e /= 2 - - // square and multiply - for e != 0 { - res.Mul(res, res) - - if e%2 != 0 { - res.Mul(res, x) - } - - e /= 2 - } - - return res -} - func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 66d957f8f4..4962d1a4f9 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -794,32 +794,6 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - var res *fr.Element - x := api.cast(i) - - if e%2 == 0 { - res = api.cast(1) - } else { - *res = *x - } - - e /= 2 - - // square and multiply - for e != 0 { - res.Mul(res, res) - - if e%2 != 0 { - res.Mul(res, x) - } - - e /= 2 - } - - return res -} - func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 35d6f86bad..6ea38abf29 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -794,32 +794,6 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - var res *fr.Element - x := api.cast(i) - - if e%2 == 0 { - res = api.cast(1) - } else { - *res = *x - } - - e /= 2 - - // square and multiply - for e != 0 { - res.Mul(res, res) - - if e%2 != 0 { - res.Mul(res, x) - } - - e /= 2 - } - - return res -} - func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/internal/gkr/engine_hints.go b/internal/gkr/engine_hints.go index 0e272f12a4..8c8bc1b797 100644 --- a/internal/gkr/engine_hints.go +++ b/internal/gkr/engine_hints.go @@ -130,7 +130,7 @@ func (h *TestEngineHints) GetAssignment(_ *big.Int, ins []*big.Int, outs []*big. return nil } -type gateAPI struct{ mod *big.Int } +type gateAPI struct{ *big.Int } func (g gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { in1 := utils.FromInterface(i1) @@ -147,7 +147,7 @@ func (g gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend func (g gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { x, y := utils.FromInterface(b), utils.FromInterface(c) x.Mul(&x, &y) - x.Mod(&x, g.mod) // reduce + x.Mod(&x, g.Int) // reduce y = utils.FromInterface(a) x.Add(&x, &y) return &x @@ -178,13 +178,7 @@ func (g gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend y = utils.FromInterface(v) x.Mul(&x, &y) } - x.Mod(&x, g.mod) // reduce - return &x -} - -func (g gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - x := utils.FromInterface(i) - x.Exp(&x, big.NewInt(int64(e)), g.mod) + x.Mod(&x, g.Int) // reduce return &x } diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 73cf93a146..e65c94752e 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -794,32 +794,6 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) Exp(i frontend.Variable, e uint8) frontend.Variable { - var res *small_rational.SmallRational - x := api.cast(i) - - if e%2 == 0 { - res = api.cast(1) - } else { - *res = *x - } - - e /= 2 - - // square and multiply - for e != 0 { - res.Mul(res, res) - - if e%2 != 0 { - res.Mul(res, x) - } - - e /= 2 - } - - return res -} - func (api *gateAPI) evaluate(f gkr.GateFunction, in ...small_rational.SmallRational) *small_rational.SmallRational { inVar := make([]frontend.Variable, len(in)) for i := range in { diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 5aca90cad2..64205b80b8 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -182,7 +182,7 @@ func (c *Circuit) finalize(api frontend.API) error { for inI, inWI := range w.Inputs { gateIn[inI] = c.assignments[inWI][0] // take the first (only) instance } - res := w.Gate.Evaluate(gadget.FrontendApiWrapper{API: api}, gateIn[:len(w.Inputs)]...) + res := w.Gate.Evaluate(api, gateIn[:len(w.Inputs)]...) if w.IsOutput() { api.AssertIsEqual(res, c.assignments[wI][0]) } else { diff --git a/std/gkrapi/gkr/types.go b/std/gkrapi/gkr/types.go index c9b8678b7b..af8a40fcd6 100644 --- a/std/gkrapi/gkr/types.go +++ b/std/gkrapi/gkr/types.go @@ -35,9 +35,6 @@ type GateAPI interface { // Mul returns res = i1 * i2 * ... in Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable - // Exp returns res = iᵉ - Exp(i frontend.Variable, e uint8) frontend.Variable - // Println behaves like fmt.Println but accepts frontend.Variable as parameter // whose value will be resolved at runtime when computed by the solver Println(a ...frontend.Variable) diff --git a/std/permutation/gkr-mimc/gkr-mimc.go b/std/permutation/gkr-mimc/gkr-mimc.go index c98d100303..266ee00e67 100644 --- a/std/permutation/gkr-mimc/gkr-mimc.go +++ b/std/permutation/gkr-mimc/gkr-mimc.go @@ -208,7 +208,12 @@ func addPow17(key *big.Int) gkr.GateFunction { if len(in) != 2 { panic("expected two input") } - return api.Exp(api.Add(in[0], in[1], key), 17) + s := api.Add(in[0], in[1], key) + t := api.Mul(s, s) // s² + t = api.Mul(t, t) // s⁴ + t = api.Mul(t, t) // s⁸ + t = api.Mul(t, t) // s¹⁶ + return api.Mul(t, s) // s¹⁶ × s } } diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go index b76024c888..b224bf1414 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go @@ -2,8 +2,6 @@ package gkr_poseidon2 import ( "fmt" - "math/bits" - "strings" "testing" "github.com/consensys/gnark-crypto/ecc" @@ -81,17 +79,3 @@ func BenchmarkGkrCompressions(b *testing.B) { _, err = cs.Solve(witness) require.NoError(b, err) } - -func TestGenerateTable(t *testing.T) { - var sb strings.Builder - for n := range 256 { - if n%16 == 0 { - sb.WriteString("\"+\n\"") - } - b := uint8(n) - b <<= bits.LeadingZeros8(b) - b = bits.Reverse8(b) - sb.WriteString(fmt.Sprintf("\\x%x", b)) - } - fmt.Println(sb.String()) -} From 1374bab9805cdb95bc199db8af8f98d188d59131 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 22 Sep 2025 16:49:36 -0500 Subject: [PATCH 076/251] refactor: modulus name --- internal/gkr/engine_hints.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/gkr/engine_hints.go b/internal/gkr/engine_hints.go index 8c8bc1b797..9e44dc7a3e 100644 --- a/internal/gkr/engine_hints.go +++ b/internal/gkr/engine_hints.go @@ -130,7 +130,7 @@ func (h *TestEngineHints) GetAssignment(_ *big.Int, ins []*big.Int, outs []*big. return nil } -type gateAPI struct{ *big.Int } +type gateAPI struct{ mod *big.Int } func (g gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { in1 := utils.FromInterface(i1) @@ -147,7 +147,7 @@ func (g gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend func (g gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { x, y := utils.FromInterface(b), utils.FromInterface(c) x.Mul(&x, &y) - x.Mod(&x, g.Int) // reduce + x.Mod(&x, g.mod) // reduce y = utils.FromInterface(a) x.Add(&x, &y) return &x @@ -178,7 +178,7 @@ func (g gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend y = utils.FromInterface(v) x.Mul(&x, &y) } - x.Mod(&x, g.Int) // reduce + x.Mod(&x, g.mod) // reduce return &x } From 9959f8a45cbc26be5dfb1d94a1c48880fe58d5be Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 22 Sep 2025 16:52:05 -0500 Subject: [PATCH 077/251] revert: remove FrontendApiWrapper --- internal/gkr/gkr.go | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index ee43864d7a..955ad8a354 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -78,7 +78,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(api frontend.API, r inputEvaluations[i] = uniqueInputEvaluations[uniqueI] } - gateEvaluation = wire.Gate.Evaluate(FrontendApiWrapper{api}, inputEvaluations...) + gateEvaluation = wire.Gate.Evaluate(api, inputEvaluations...) } evaluation = api.Mul(evaluation, gateEvaluation) @@ -383,21 +383,3 @@ func DeserializeProof(sorted []*gkrtypes.Wire, serializedProof []frontend.Variab } return proof, nil } - -// FrontendApiWrapper implements additional functions to satisfy the gkr.GateAPI interface. -type FrontendApiWrapper struct { - frontend.API -} - -func (api FrontendApiWrapper) Exp(i frontend.Variable, e uint8) frontend.Variable { - res := frontend.Variable(1) - - for range 8 { - res = api.Mul(res, res) - if e&128 != 0 { - res = api.Mul(res, i) - } - e <<= 1 - } - return res -} From 271cff9225786d4e1f27046a090fee99cc8f3e68 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 23 Sep 2025 18:12:53 -0500 Subject: [PATCH 078/251] perf: dedicated exp17 func --- .../generator/backend/template/gkr/gkr.go.tmpl | 10 ++++++++++ internal/gkr/bls12-377/gkr.go | 10 ++++++++++ internal/gkr/bls12-381/gkr.go | 10 ++++++++++ internal/gkr/bls24-315/gkr.go | 10 ++++++++++ internal/gkr/bls24-317/gkr.go | 10 ++++++++++ internal/gkr/bn254/gkr.go | 10 ++++++++++ internal/gkr/bw6-633/gkr.go | 10 ++++++++++ internal/gkr/bw6-761/gkr.go | 10 ++++++++++ internal/gkr/engine_hints.go | 7 +++++++ internal/gkr/gkr.go | 14 +++++++++++++- internal/gkr/small_rational/gkr.go | 10 ++++++++++ std/gkrapi/compile.go | 2 +- std/gkrapi/gkr/types.go | 2 ++ std/permutation/gkr-mimc/gkr-mimc.go | 7 +------ 14 files changed, 114 insertions(+), 8 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 2131aea723..31819961e0 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -772,6 +772,16 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } +func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { + res := api.newElement() + x := api.cast(i) + res.Mul(x, x) // x^2 + res.Mul(res, res) // x^4 + res.Mul(res, res) // x^8 + res.Mul(res, res) // x^16 + return res.Mul(res, x) // x^17 +} + func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x {{ .ElementType }} diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index c28e3df8e3..996f4a6b4b 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -776,6 +776,16 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } +func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { + res := api.newElement() + x := api.cast(i) + res.Mul(x, x) // x^2 + res.Mul(res, res) // x^4 + res.Mul(res, res) // x^8 + res.Mul(res, res) // x^16 + return res.Mul(res, x) // x^17 +} + func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 5c87cdd85d..863a2f71c8 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -776,6 +776,16 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } +func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { + res := api.newElement() + x := api.cast(i) + res.Mul(x, x) // x^2 + res.Mul(res, res) // x^4 + res.Mul(res, res) // x^8 + res.Mul(res, res) // x^16 + return res.Mul(res, x) // x^17 +} + func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index acbebf56cf..dc6eb854ad 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -776,6 +776,16 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } +func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { + res := api.newElement() + x := api.cast(i) + res.Mul(x, x) // x^2 + res.Mul(res, res) // x^4 + res.Mul(res, res) // x^8 + res.Mul(res, res) // x^16 + return res.Mul(res, x) // x^17 +} + func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 3d4a80d557..6333662f83 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -776,6 +776,16 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } +func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { + res := api.newElement() + x := api.cast(i) + res.Mul(x, x) // x^2 + res.Mul(res, res) // x^4 + res.Mul(res, res) // x^8 + res.Mul(res, res) // x^16 + return res.Mul(res, x) // x^17 +} + func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index bed1d89329..f2361557c6 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -776,6 +776,16 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } +func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { + res := api.newElement() + x := api.cast(i) + res.Mul(x, x) // x^2 + res.Mul(res, res) // x^4 + res.Mul(res, res) // x^8 + res.Mul(res, res) // x^16 + return res.Mul(res, x) // x^17 +} + func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 4962d1a4f9..e8b1f9d27d 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -776,6 +776,16 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } +func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { + res := api.newElement() + x := api.cast(i) + res.Mul(x, x) // x^2 + res.Mul(res, res) // x^4 + res.Mul(res, res) // x^8 + res.Mul(res, res) // x^16 + return res.Mul(res, x) // x^17 +} + func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 6ea38abf29..27315ad58d 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -776,6 +776,16 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } +func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { + res := api.newElement() + x := api.cast(i) + res.Mul(x, x) // x^2 + res.Mul(res, res) // x^4 + res.Mul(res, res) // x^8 + res.Mul(res, res) // x^16 + return res.Mul(res, x) // x^17 +} + func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element diff --git a/internal/gkr/engine_hints.go b/internal/gkr/engine_hints.go index 9e44dc7a3e..35f8cd5c7c 100644 --- a/internal/gkr/engine_hints.go +++ b/internal/gkr/engine_hints.go @@ -182,6 +182,13 @@ func (g gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend return &x } +func (g gateAPI) Exp17(i frontend.Variable) frontend.Variable { + x := utils.FromInterface(i) + var res big.Int + res.Exp(&x, big.NewInt(17), g.mod) + return &res +} + func (g gateAPI) Println(a ...frontend.Variable) { strings := make([]string, len(a)) for i := range a { diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index 955ad8a354..dc2e0c9fd3 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -78,7 +78,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(api frontend.API, r inputEvaluations[i] = uniqueInputEvaluations[uniqueI] } - gateEvaluation = wire.Gate.Evaluate(api, inputEvaluations...) + gateEvaluation = wire.Gate.Evaluate(FrontendAPIWrapper{api}, inputEvaluations...) } evaluation = api.Mul(evaluation, gateEvaluation) @@ -383,3 +383,15 @@ func DeserializeProof(sorted []*gkrtypes.Wire, serializedProof []frontend.Variab } return proof, nil } + +type FrontendAPIWrapper struct { + frontend.API +} + +func (api FrontendAPIWrapper) Exp17(i frontend.Variable) frontend.Variable { + res := api.Mul(i, i) // i^2 + res = api.Mul(res, res) // i^4 + res = api.Mul(res, res) // i^8 + res = api.Mul(res, res) // i^16 + return api.Mul(res, i) // i^17 +} diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index e65c94752e..a884902d4c 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -776,6 +776,16 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } +func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { + res := api.newElement() + x := api.cast(i) + res.Mul(x, x) // x^2 + res.Mul(res, res) // x^4 + res.Mul(res, res) // x^8 + res.Mul(res, res) // x^16 + return res.Mul(res, x) // x^17 +} + func (api *gateAPI) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x small_rational.SmallRational diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 64205b80b8..d8c0985ae5 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -182,7 +182,7 @@ func (c *Circuit) finalize(api frontend.API) error { for inI, inWI := range w.Inputs { gateIn[inI] = c.assignments[inWI][0] // take the first (only) instance } - res := w.Gate.Evaluate(api, gateIn[:len(w.Inputs)]...) + res := w.Gate.Evaluate(gadget.FrontendAPIWrapper{API: api}, gateIn[:len(w.Inputs)]...) if w.IsOutput() { api.AssertIsEqual(res, c.assignments[wI][0]) } else { diff --git a/std/gkrapi/gkr/types.go b/std/gkrapi/gkr/types.go index af8a40fcd6..5acc2cda9f 100644 --- a/std/gkrapi/gkr/types.go +++ b/std/gkrapi/gkr/types.go @@ -38,6 +38,8 @@ type GateAPI interface { // Println behaves like fmt.Println but accepts frontend.Variable as parameter // whose value will be resolved at runtime when computed by the solver Println(a ...frontend.Variable) + + Exp17(a frontend.Variable) frontend.Variable } // GateFunction is a function that evaluates a polynomial over its inputs diff --git a/std/permutation/gkr-mimc/gkr-mimc.go b/std/permutation/gkr-mimc/gkr-mimc.go index 266ee00e67..2d0e4ada69 100644 --- a/std/permutation/gkr-mimc/gkr-mimc.go +++ b/std/permutation/gkr-mimc/gkr-mimc.go @@ -208,12 +208,7 @@ func addPow17(key *big.Int) gkr.GateFunction { if len(in) != 2 { panic("expected two input") } - s := api.Add(in[0], in[1], key) - t := api.Mul(s, s) // s² - t = api.Mul(t, t) // s⁴ - t = api.Mul(t, t) // s⁸ - t = api.Mul(t, t) // s¹⁶ - return api.Mul(t, s) // s¹⁶ × s + return api.Exp17(api.Add(in[0], in[1], key)) } } From 1519d5276db1919b78a6f4627880e9dd439168ab Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 24 Sep 2025 10:02:13 -0500 Subject: [PATCH 079/251] perf: SumExp17 --- .../backend/template/gkr/gkr.go.tmpl | 12 +++++++--- internal/gkr/bls12-377/gkr.go | 23 +++++++++++++------ internal/gkr/bls12-381/gkr.go | 12 +++++++--- internal/gkr/bls24-315/gkr.go | 12 +++++++--- internal/gkr/bls24-317/gkr.go | 12 +++++++--- internal/gkr/bn254/gkr.go | 12 +++++++--- internal/gkr/bw6-633/gkr.go | 12 +++++++--- internal/gkr/bw6-761/gkr.go | 12 +++++++--- internal/gkr/engine_hints.go | 10 +++++--- internal/gkr/gkr.go | 3 ++- internal/gkr/small_rational/gkr.go | 12 +++++++--- std/gkrapi/gkr/types.go | 2 +- std/permutation/gkr-mimc/gkr-mimc.go | 2 +- 13 files changed, 99 insertions(+), 37 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 31819961e0..bf0fb4ecd9 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -772,9 +772,15 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } -func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { - res := api.newElement() - x := api.cast(i) +func (api *gateAPI) SumExp17(a,b,c frontend.Variable) frontend.Variable { + x := api.cast(a) + res := api.cast(b) + x.Add(res, x) + if _, err := res.SetInterface(c); err != nil { + panic(err) + } + x.Add(res, x) + res.Mul(x, x) // x^2 res.Mul(res, res) // x^4 res.Mul(res, res) // x^8 diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 996f4a6b4b..7925291eeb 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -776,14 +776,23 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } -func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { +func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { + var x fr.Element + + if _, err := x.SetInterface(c); err != nil { // a, b are expected to be *fr.Element but not c + panic(err) + } + + x.Add(&x, api.cast(a)) + x.Add(&x, api.cast(b)) + res := api.newElement() - x := api.cast(i) - res.Mul(x, x) // x^2 - res.Mul(res, res) // x^4 - res.Mul(res, res) // x^8 - res.Mul(res, res) // x^16 - return res.Mul(res, x) // x^17 + + res.Mul(&x, &x) // x^2 + res.Mul(res, res) // x^4 + res.Mul(res, res) // x^8 + res.Mul(res, res) // x^16 + return res.Mul(res, &x) // x^17 } func (api *gateAPI) Println(a ...frontend.Variable) { diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 863a2f71c8..0b34a40e23 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -776,9 +776,15 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } -func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { - res := api.newElement() - x := api.cast(i) +func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { + x := api.cast(a) + res := api.cast(b) + x.Add(res, x) + if _, err := res.SetInterface(c); err != nil { + panic(err) + } + x.Add(res, x) + res.Mul(x, x) // x^2 res.Mul(res, res) // x^4 res.Mul(res, res) // x^8 diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index dc6eb854ad..a0a059e6df 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -776,9 +776,15 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } -func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { - res := api.newElement() - x := api.cast(i) +func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { + x := api.cast(a) + res := api.cast(b) + x.Add(res, x) + if _, err := res.SetInterface(c); err != nil { + panic(err) + } + x.Add(res, x) + res.Mul(x, x) // x^2 res.Mul(res, res) // x^4 res.Mul(res, res) // x^8 diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 6333662f83..42f1e5e56c 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -776,9 +776,15 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } -func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { - res := api.newElement() - x := api.cast(i) +func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { + x := api.cast(a) + res := api.cast(b) + x.Add(res, x) + if _, err := res.SetInterface(c); err != nil { + panic(err) + } + x.Add(res, x) + res.Mul(x, x) // x^2 res.Mul(res, res) // x^4 res.Mul(res, res) // x^8 diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index f2361557c6..a84276a35a 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -776,9 +776,15 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } -func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { - res := api.newElement() - x := api.cast(i) +func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { + x := api.cast(a) + res := api.cast(b) + x.Add(res, x) + if _, err := res.SetInterface(c); err != nil { + panic(err) + } + x.Add(res, x) + res.Mul(x, x) // x^2 res.Mul(res, res) // x^4 res.Mul(res, res) // x^8 diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index e8b1f9d27d..4daeb83900 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -776,9 +776,15 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } -func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { - res := api.newElement() - x := api.cast(i) +func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { + x := api.cast(a) + res := api.cast(b) + x.Add(res, x) + if _, err := res.SetInterface(c); err != nil { + panic(err) + } + x.Add(res, x) + res.Mul(x, x) // x^2 res.Mul(res, res) // x^4 res.Mul(res, res) // x^8 diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 27315ad58d..9821871edd 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -776,9 +776,15 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } -func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { - res := api.newElement() - x := api.cast(i) +func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { + x := api.cast(a) + res := api.cast(b) + x.Add(res, x) + if _, err := res.SetInterface(c); err != nil { + panic(err) + } + x.Add(res, x) + res.Mul(x, x) // x^2 res.Mul(res, res) // x^4 res.Mul(res, res) // x^8 diff --git a/internal/gkr/engine_hints.go b/internal/gkr/engine_hints.go index 35f8cd5c7c..9d53255f4e 100644 --- a/internal/gkr/engine_hints.go +++ b/internal/gkr/engine_hints.go @@ -182,9 +182,13 @@ func (g gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend return &x } -func (g gateAPI) Exp17(i frontend.Variable) frontend.Variable { - x := utils.FromInterface(i) - var res big.Int +func (g gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { + x := utils.FromInterface(a) + res := utils.FromInterface(b) + + x.Add(&x, &res) + res = utils.FromInterface(c) + x.Add(&x, &res) res.Exp(&x, big.NewInt(17), g.mod) return &res } diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index dc2e0c9fd3..b2ea42e8ba 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -388,7 +388,8 @@ type FrontendAPIWrapper struct { frontend.API } -func (api FrontendAPIWrapper) Exp17(i frontend.Variable) frontend.Variable { +func (api FrontendAPIWrapper) SumExp17(a, b, c frontend.Variable) frontend.Variable { + i := api.Add(a, b, c) res := api.Mul(i, i) // i^2 res = api.Mul(res, res) // i^4 res = api.Mul(res, res) // i^8 diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index a884902d4c..37f9490950 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -776,9 +776,15 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front return res } -func (api *gateAPI) Exp17(i frontend.Variable) frontend.Variable { - res := api.newElement() - x := api.cast(i) +func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { + x := api.cast(a) + res := api.cast(b) + x.Add(res, x) + if _, err := res.SetInterface(c); err != nil { + panic(err) + } + x.Add(res, x) + res.Mul(x, x) // x^2 res.Mul(res, res) // x^4 res.Mul(res, res) // x^8 diff --git a/std/gkrapi/gkr/types.go b/std/gkrapi/gkr/types.go index 5acc2cda9f..3c03f75df1 100644 --- a/std/gkrapi/gkr/types.go +++ b/std/gkrapi/gkr/types.go @@ -39,7 +39,7 @@ type GateAPI interface { // whose value will be resolved at runtime when computed by the solver Println(a ...frontend.Variable) - Exp17(a frontend.Variable) frontend.Variable + SumExp17(a, b, c frontend.Variable) frontend.Variable } // GateFunction is a function that evaluates a polynomial over its inputs diff --git a/std/permutation/gkr-mimc/gkr-mimc.go b/std/permutation/gkr-mimc/gkr-mimc.go index 2d0e4ada69..1fa4cbce93 100644 --- a/std/permutation/gkr-mimc/gkr-mimc.go +++ b/std/permutation/gkr-mimc/gkr-mimc.go @@ -208,7 +208,7 @@ func addPow17(key *big.Int) gkr.GateFunction { if len(in) != 2 { panic("expected two input") } - return api.Exp17(api.Add(in[0], in[1], key)) + return api.SumExp17(in[0], in[1], key) } } From 2e3be9f26ca8318d07f019d99a4615f67c81549c Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 24 Sep 2025 10:38:14 -0500 Subject: [PATCH 080/251] perf: cache key as fr.Element --- internal/gkr/bls12-377/gkr.go | 8 ++------ std/permutation/gkr-mimc/gkr-mimc.go | 14 +++++++++++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 7925291eeb..13923e0983 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -779,12 +779,8 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { var x fr.Element - if _, err := x.SetInterface(c); err != nil { // a, b are expected to be *fr.Element but not c - panic(err) - } - - x.Add(&x, api.cast(a)) - x.Add(&x, api.cast(b)) + x.Add(api.cast(a), api.cast(b)) + x.Add(&x, api.cast(c)) res := api.newElement() diff --git a/std/permutation/gkr-mimc/gkr-mimc.go b/std/permutation/gkr-mimc/gkr-mimc.go index 1fa4cbce93..b2c2b7527e 100644 --- a/std/permutation/gkr-mimc/gkr-mimc.go +++ b/std/permutation/gkr-mimc/gkr-mimc.go @@ -5,6 +5,7 @@ import ( "math/big" "github.com/consensys/gnark-crypto/ecc" + frBls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/mimc" bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/mimc" bls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/mimc" @@ -204,11 +205,22 @@ func addPow7Add(key *big.Int) gkr.GateFunction { // addPow17: (in[0]+in[1]+key)¹⁷ func addPow17(key *big.Int) gkr.GateFunction { + var cachedKey frontend.Variable return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { if len(in) != 2 { panic("expected two input") } - return api.SumExp17(in[0], in[1], key) + if cachedKey == nil { + if _, ok := in[0].(*frBls12377.Element); ok { + var ck frBls12377.Element + ck.SetBigInt(key) + cachedKey = &ck + } else { + return api.SumExp17(in[0], in[1], key) + } + } + + return api.SumExp17(in[0], in[1], cachedKey) } } From a05f8d63e757d58cc41cbec6d4d7db5237e9f015 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 24 Sep 2025 10:59:08 -0500 Subject: [PATCH 081/251] build: generify --- .../backend/template/gkr/gkr.go.tmpl | 23 +++++++++---------- internal/gkr/bls12-377/gkr.go | 10 ++++---- internal/gkr/bls12-381/gkr.go | 23 +++++++++---------- internal/gkr/bls24-315/gkr.go | 23 +++++++++---------- internal/gkr/bls24-317/gkr.go | 23 +++++++++---------- internal/gkr/bn254/gkr.go | 23 +++++++++---------- internal/gkr/bw6-633/gkr.go | 23 +++++++++---------- internal/gkr/bw6-761/gkr.go | 23 +++++++++---------- internal/gkr/small_rational/gkr.go | 23 +++++++++---------- 9 files changed, 93 insertions(+), 101 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index bf0fb4ecd9..12b55c8d30 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -773,19 +773,18 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front } func (api *gateAPI) SumExp17(a,b,c frontend.Variable) frontend.Variable { - x := api.cast(a) - res := api.cast(b) - x.Add(res, x) - if _, err := res.SetInterface(c); err != nil { - panic(err) - } - x.Add(res, x) + var x {{ .ElementType }} + + x.Add(api.cast(a), api.cast(b)) + x.Add(&x, api.cast(c)) + + res := api.newElement() - res.Mul(x, x) // x^2 - res.Mul(res, res) // x^4 - res.Mul(res, res) // x^8 - res.Mul(res, res) // x^16 - return res.Mul(res, x) // x^17 + res.Mul(&x, &x) // x² + res.Mul(res, res) // x⁴ + res.Mul(res, res) // x⁸ + res.Mul(res, res) // x¹⁶ + return res.Mul(res, &x) // x¹⁷ } func (api *gateAPI) Println(a ...frontend.Variable) { diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 13923e0983..8f7532228e 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -784,11 +784,11 @@ func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { res := api.newElement() - res.Mul(&x, &x) // x^2 - res.Mul(res, res) // x^4 - res.Mul(res, res) // x^8 - res.Mul(res, res) // x^16 - return res.Mul(res, &x) // x^17 + res.Mul(&x, &x) // x² + res.Mul(res, res) // x⁴ + res.Mul(res, res) // x⁸ + res.Mul(res, res) // x¹⁶ + return res.Mul(res, &x) // x¹⁷ } func (api *gateAPI) Println(a ...frontend.Variable) { diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 0b34a40e23..b4bdff0594 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -777,19 +777,18 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front } func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - x := api.cast(a) - res := api.cast(b) - x.Add(res, x) - if _, err := res.SetInterface(c); err != nil { - panic(err) - } - x.Add(res, x) + var x fr.Element + + x.Add(api.cast(a), api.cast(b)) + x.Add(&x, api.cast(c)) + + res := api.newElement() - res.Mul(x, x) // x^2 - res.Mul(res, res) // x^4 - res.Mul(res, res) // x^8 - res.Mul(res, res) // x^16 - return res.Mul(res, x) // x^17 + res.Mul(&x, &x) // x² + res.Mul(res, res) // x⁴ + res.Mul(res, res) // x⁸ + res.Mul(res, res) // x¹⁶ + return res.Mul(res, &x) // x¹⁷ } func (api *gateAPI) Println(a ...frontend.Variable) { diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index a0a059e6df..c095671e76 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -777,19 +777,18 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front } func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - x := api.cast(a) - res := api.cast(b) - x.Add(res, x) - if _, err := res.SetInterface(c); err != nil { - panic(err) - } - x.Add(res, x) + var x fr.Element + + x.Add(api.cast(a), api.cast(b)) + x.Add(&x, api.cast(c)) + + res := api.newElement() - res.Mul(x, x) // x^2 - res.Mul(res, res) // x^4 - res.Mul(res, res) // x^8 - res.Mul(res, res) // x^16 - return res.Mul(res, x) // x^17 + res.Mul(&x, &x) // x² + res.Mul(res, res) // x⁴ + res.Mul(res, res) // x⁸ + res.Mul(res, res) // x¹⁶ + return res.Mul(res, &x) // x¹⁷ } func (api *gateAPI) Println(a ...frontend.Variable) { diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 42f1e5e56c..bfa2fb778d 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -777,19 +777,18 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front } func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - x := api.cast(a) - res := api.cast(b) - x.Add(res, x) - if _, err := res.SetInterface(c); err != nil { - panic(err) - } - x.Add(res, x) + var x fr.Element + + x.Add(api.cast(a), api.cast(b)) + x.Add(&x, api.cast(c)) + + res := api.newElement() - res.Mul(x, x) // x^2 - res.Mul(res, res) // x^4 - res.Mul(res, res) // x^8 - res.Mul(res, res) // x^16 - return res.Mul(res, x) // x^17 + res.Mul(&x, &x) // x² + res.Mul(res, res) // x⁴ + res.Mul(res, res) // x⁸ + res.Mul(res, res) // x¹⁶ + return res.Mul(res, &x) // x¹⁷ } func (api *gateAPI) Println(a ...frontend.Variable) { diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index a84276a35a..d075ced6cd 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -777,19 +777,18 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front } func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - x := api.cast(a) - res := api.cast(b) - x.Add(res, x) - if _, err := res.SetInterface(c); err != nil { - panic(err) - } - x.Add(res, x) + var x fr.Element + + x.Add(api.cast(a), api.cast(b)) + x.Add(&x, api.cast(c)) + + res := api.newElement() - res.Mul(x, x) // x^2 - res.Mul(res, res) // x^4 - res.Mul(res, res) // x^8 - res.Mul(res, res) // x^16 - return res.Mul(res, x) // x^17 + res.Mul(&x, &x) // x² + res.Mul(res, res) // x⁴ + res.Mul(res, res) // x⁸ + res.Mul(res, res) // x¹⁶ + return res.Mul(res, &x) // x¹⁷ } func (api *gateAPI) Println(a ...frontend.Variable) { diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 4daeb83900..e6932181a6 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -777,19 +777,18 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front } func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - x := api.cast(a) - res := api.cast(b) - x.Add(res, x) - if _, err := res.SetInterface(c); err != nil { - panic(err) - } - x.Add(res, x) + var x fr.Element + + x.Add(api.cast(a), api.cast(b)) + x.Add(&x, api.cast(c)) + + res := api.newElement() - res.Mul(x, x) // x^2 - res.Mul(res, res) // x^4 - res.Mul(res, res) // x^8 - res.Mul(res, res) // x^16 - return res.Mul(res, x) // x^17 + res.Mul(&x, &x) // x² + res.Mul(res, res) // x⁴ + res.Mul(res, res) // x⁸ + res.Mul(res, res) // x¹⁶ + return res.Mul(res, &x) // x¹⁷ } func (api *gateAPI) Println(a ...frontend.Variable) { diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 9821871edd..acb157dd5f 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -777,19 +777,18 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front } func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - x := api.cast(a) - res := api.cast(b) - x.Add(res, x) - if _, err := res.SetInterface(c); err != nil { - panic(err) - } - x.Add(res, x) + var x fr.Element + + x.Add(api.cast(a), api.cast(b)) + x.Add(&x, api.cast(c)) + + res := api.newElement() - res.Mul(x, x) // x^2 - res.Mul(res, res) // x^4 - res.Mul(res, res) // x^8 - res.Mul(res, res) // x^16 - return res.Mul(res, x) // x^17 + res.Mul(&x, &x) // x² + res.Mul(res, res) // x⁴ + res.Mul(res, res) // x⁸ + res.Mul(res, res) // x¹⁶ + return res.Mul(res, &x) // x¹⁷ } func (api *gateAPI) Println(a ...frontend.Variable) { diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 37f9490950..427bac1877 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -777,19 +777,18 @@ func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) front } func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - x := api.cast(a) - res := api.cast(b) - x.Add(res, x) - if _, err := res.SetInterface(c); err != nil { - panic(err) - } - x.Add(res, x) + var x small_rational.SmallRational + + x.Add(api.cast(a), api.cast(b)) + x.Add(&x, api.cast(c)) + + res := api.newElement() - res.Mul(x, x) // x^2 - res.Mul(res, res) // x^4 - res.Mul(res, res) // x^8 - res.Mul(res, res) // x^16 - return res.Mul(res, x) // x^17 + res.Mul(&x, &x) // x² + res.Mul(res, res) // x⁴ + res.Mul(res, res) // x⁸ + res.Mul(res, res) // x¹⁶ + return res.Mul(res, &x) // x¹⁷ } func (api *gateAPI) Println(a ...frontend.Variable) { From f6613ce75532aaac67454077c5bd30a4591c1481 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 30 Sep 2025 16:56:24 -0500 Subject: [PATCH 082/251] perf: store keys as fr.Elements instead of big.Int --- std/permutation/gkr-mimc/gkr-mimc.go | 100 ++++++++++++++++++--------- 1 file changed, 68 insertions(+), 32 deletions(-) diff --git a/std/permutation/gkr-mimc/gkr-mimc.go b/std/permutation/gkr-mimc/gkr-mimc.go index b2c2b7527e..1645552881 100644 --- a/std/permutation/gkr-mimc/gkr-mimc.go +++ b/std/permutation/gkr-mimc/gkr-mimc.go @@ -2,16 +2,21 @@ package gkr_mimc import ( "fmt" - "math/big" "github.com/consensys/gnark-crypto/ecc" - frBls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" + frbls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/mimc" + frbls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/mimc" + frbls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" bls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/mimc" + frbls24317 "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" bls24317 "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/mimc" + frbn254 "github.com/consensys/gnark-crypto/ecc/bn254/fr" bn254 "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" + frbw6633 "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" bw6633 "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/mimc" + frbw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/mimc" "github.com/consensys/gnark/constraint/solver/gkrgates" "github.com/consensys/gnark/frontend" @@ -95,7 +100,7 @@ func RegisterGates(curves ...ecc.ID) error { return err } gateNamer := newGateNamer(curve) - var lastLayerSBox, nonLastLayerSBox func(*big.Int) gkr.GateFunction + var lastLayerSBox, nonLastLayerSBox func(frontend.Variable) gkr.GateFunction switch deg { case 5: lastLayerSBox = addPow5Add @@ -111,12 +116,12 @@ func RegisterGates(curves ...ecc.ID) error { } for i := range len(constants) - 1 { - if _, err = gkrgates.Register(nonLastLayerSBox(&constants[i]), 2, gkrgates.WithName(gateNamer.round(i)), gkrgates.WithUnverifiedDegree(deg), gkrgates.WithCurves(curve)); err != nil { + if _, err = gkrgates.Register(nonLastLayerSBox(constants[i]), 2, gkrgates.WithName(gateNamer.round(i)), gkrgates.WithUnverifiedDegree(deg), gkrgates.WithCurves(curve)); err != nil { return fmt.Errorf("failed to register keyed GKR gate for round %d of MiMC on curve %s: %w", i, curve, err) } } - if _, err = gkrgates.Register(lastLayerSBox(&constants[len(constants)-1]), 3, gkrgates.WithName(gateNamer.round(len(constants)-1)), gkrgates.WithUnverifiedDegree(deg), gkrgates.WithCurves(curve)); err != nil { + if _, err = gkrgates.Register(lastLayerSBox(constants[len(constants)-1]), 3, gkrgates.WithName(gateNamer.round(len(constants)-1)), gkrgates.WithUnverifiedDegree(deg), gkrgates.WithCurves(curve)); err != nil { return fmt.Errorf("failed to register keyed GKR gate for round %d of MiMC on curve %s: %w", len(constants)-1, curve, err) } } @@ -124,23 +129,65 @@ func RegisterGates(curves ...ecc.ID) error { } // getParams returns the parameters for the MiMC encryption function for the given curve. -// It also returns the degree of the s-Box -func getParams(curve ecc.ID) ([]big.Int, int, error) { +// It also returns the degree of the s-Box. +func getParams(curve ecc.ID) ([]frontend.Variable, int, error) { switch curve { case ecc.BN254: - return bn254.GetConstants(), 5, nil + c := bn254.GetConstants() + res := make([]frontend.Variable, len(c)) + for i := range res { + var v frbn254.Element + res[i] = v.SetBigInt(&c[i]) + } + return res, 5, nil case ecc.BLS12_381: - return bls12381.GetConstants(), 5, nil + c := bls12381.GetConstants() + res := make([]frontend.Variable, len(c)) + for i := range res { + var v frbls12381.Element + res[i] = v.SetBigInt(&c[i]) + } + return res, 5, nil case ecc.BLS12_377: - return bls12377.GetConstants(), 17, nil + c := bls12377.GetConstants() + res := make([]frontend.Variable, len(c)) + for i := range res { + var v frbls12377.Element + res[i] = v.SetBigInt(&c[i]) + } + return res, 17, nil case ecc.BLS24_315: - return bls24315.GetConstants(), 5, nil + c := bls24315.GetConstants() + res := make([]frontend.Variable, len(c)) + for i := range res { + var v frbls24315.Element + res[i] = v.SetBigInt(&c[i]) + } + return res, 5, nil case ecc.BLS24_317: - return bls24317.GetConstants(), 7, nil + c := bls24317.GetConstants() + res := make([]frontend.Variable, len(c)) + for i := range res { + var v frbls24317.Element + res[i] = v.SetBigInt(&c[i]) + } + return res, 7, nil case ecc.BW6_633: - return bw6633.GetConstants(), 5, nil + c := bw6633.GetConstants() + res := make([]frontend.Variable, len(c)) + for i := range res { + var v frbw6633.Element + res[i] = v.SetBigInt(&c[i]) + } + return res, 5, nil case ecc.BW6_761: - return bw6761.GetConstants(), 5, nil + c := bw6761.GetConstants() + res := make([]frontend.Variable, len(c)) + for i := range res { + var v frbw6761.Element + res[i] = v.SetBigInt(&c[i]) + } + return res, 5, nil default: return nil, -1, fmt.Errorf("unsupported curve ID: %s", curve) } @@ -155,7 +202,7 @@ func (n gateNamer) round(i int) gkr.GateName { return gkr.GateName(fmt.Sprintf("%s%d", string(n), i)) } -func addPow5(key *big.Int) gkr.GateFunction { +func addPow5(key frontend.Variable) gkr.GateFunction { return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { if len(in) != 2 { panic("expected two input") @@ -167,7 +214,7 @@ func addPow5(key *big.Int) gkr.GateFunction { } // addPow5Add: (in[0]+in[1]+key)⁵ + 2*in[0] + in[2] -func addPow5Add(key *big.Int) gkr.GateFunction { +func addPow5Add(key frontend.Variable) gkr.GateFunction { return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { if len(in) != 3 { panic("expected three input") @@ -180,7 +227,7 @@ func addPow5Add(key *big.Int) gkr.GateFunction { } } -func addPow7(key *big.Int) gkr.GateFunction { +func addPow7(key frontend.Variable) gkr.GateFunction { return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { if len(in) != 2 { panic("expected two input") @@ -192,7 +239,7 @@ func addPow7(key *big.Int) gkr.GateFunction { } // addPow7Add: (in[0]+in[1]+key)⁷ + 2*in[0] + in[2] -func addPow7Add(key *big.Int) gkr.GateFunction { +func addPow7Add(key frontend.Variable) gkr.GateFunction { return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { if len(in) != 3 { panic("expected three input") @@ -204,28 +251,17 @@ func addPow7Add(key *big.Int) gkr.GateFunction { } // addPow17: (in[0]+in[1]+key)¹⁷ -func addPow17(key *big.Int) gkr.GateFunction { - var cachedKey frontend.Variable +func addPow17(key frontend.Variable) gkr.GateFunction { return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { if len(in) != 2 { panic("expected two input") } - if cachedKey == nil { - if _, ok := in[0].(*frBls12377.Element); ok { - var ck frBls12377.Element - ck.SetBigInt(key) - cachedKey = &ck - } else { - return api.SumExp17(in[0], in[1], key) - } - } - - return api.SumExp17(in[0], in[1], cachedKey) + return api.SumExp17(in[0], in[1], key) } } // addPow17Add: (in[0]+in[1]+key)¹⁷ + in[0] + in[2] -func addPow17Add(key *big.Int) gkr.GateFunction { +func addPow17Add(key frontend.Variable) gkr.GateFunction { return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { if len(in) != 3 { panic("expected three input") From 610d49ca3db9dfb9a31497d746d3d54605e90721 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 7 Jan 2026 10:56:35 -0600 Subject: [PATCH 083/251] fix: match api changes --- std/permutation/gkr-mimc/gkr-mimc.go | 16 +++- .../poseidon2/gkr-poseidon2/gkr-poseidon2.go | 13 ++-- .../poseidon2/gkr-poseidon2/gkr_test.go | 74 ------------------- 3 files changed, 19 insertions(+), 84 deletions(-) delete mode 100644 std/permutation/poseidon2/gkr-poseidon2/gkr_test.go diff --git a/std/permutation/gkr-mimc/gkr-mimc.go b/std/permutation/gkr-mimc/gkr-mimc.go index 266ee00e67..75736b22ef 100644 --- a/std/permutation/gkr-mimc/gkr-mimc.go +++ b/std/permutation/gkr-mimc/gkr-mimc.go @@ -52,7 +52,10 @@ func NewCompressor(api frontend.API) (hash.Compressor, error) { return nil, fmt.Errorf("cached value is of type %T, not a compressor", cached) } - gkrApi := gkrapi.New() + gkrApi, err := gkrapi.New(api) + if err != nil { + return nil, err + } in0 := gkrApi.NewInput() in1 := gkrApi.NewInput() @@ -75,9 +78,14 @@ func NewCompressor(api frontend.API) (hash.Compressor, error) { y = gkrApi.NamedGate(gateNamer.round(len(params)-1), in0, y, in1) + gkrCircuit, err := gkrApi.Compile("POSEIDON2") + if err != nil { + return nil, err + } + res := &compressor{ - gkrCircuit: gkrApi.Compile(api, "POSEIDON2"), + gkrCircuit: gkrCircuit, in0: in0, in1: in1, out: y, @@ -110,12 +118,12 @@ func RegisterGates(curves ...ecc.ID) error { } for i := range len(constants) - 1 { - if _, err = gkrgates.Register(nonLastLayerSBox(&constants[i]), 2, gkrgates.WithName(gateNamer.round(i)), gkrgates.WithUnverifiedDegree(deg), gkrgates.WithCurves(curve)); err != nil { + if err = gkrgates.Register(nonLastLayerSBox(&constants[i]), 2, gkrgates.WithName(gateNamer.round(i)), gkrgates.WithUnverifiedDegree(deg), gkrgates.WithCurves(curve)); err != nil { return fmt.Errorf("failed to register keyed GKR gate for round %d of MiMC on curve %s: %w", i, curve, err) } } - if _, err = gkrgates.Register(lastLayerSBox(&constants[len(constants)-1]), 3, gkrgates.WithName(gateNamer.round(len(constants)-1)), gkrgates.WithUnverifiedDegree(deg), gkrgates.WithCurves(curve)); err != nil { + if err = gkrgates.Register(lastLayerSBox(&constants[len(constants)-1]), 3, gkrgates.WithName(gateNamer.round(len(constants)-1)), gkrgates.WithUnverifiedDegree(deg), gkrgates.WithCurves(curve)); err != nil { return fmt.Errorf("failed to register keyed GKR gate for round %d of MiMC on curve %s: %w", len(constants)-1, curve, err) } } diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go index 4bb0b7a6a7..5b3d34b2ee 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go @@ -188,7 +188,10 @@ func defineCircuit(api frontend.API) (gkrCircuit *gkrapi.Circuit, in1, in2, out return } - gkrApi := gkrapi.New() + gkrApi, err := gkrapi.New(api) + if err != nil { + return + } x := gkrApi.NewInput() y := gkrApi.NewInput() @@ -281,7 +284,7 @@ func defineCircuit(api frontend.API) (gkrCircuit *gkrapi.Circuit, in1, in2, out // apply the external matrix one last time to obtain the final value of y out = gkrApi.Gate(extAddGate, y, x, in2) - gkrCircuit = gkrApi.Compile(api, "MIMC") + gkrCircuit, err = gkrApi.Compile("MIMC") return } @@ -313,13 +316,11 @@ func registerGates(p *poseidon2.Parameters, curve ecc.ID) error { halfRf := p.NbFullRounds / 2 extKeySBox := func(round int, varIndex int) error { - _, err := gkrgates.Register(extKeyGate(&p.RoundKeys[round][varIndex]), 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(varIndex, round)), gkrgates.WithCurves(curve)) - return err + return gkrgates.Register(extKeyGate(&p.RoundKeys[round][varIndex]), 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(varIndex, round)), gkrgates.WithCurves(curve)) } intKeySBox2 := func(round int) error { - _, err := gkrgates.Register(intKeyGate2(&p.RoundKeys[round][1]), 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(y, round)), gkrgates.WithCurves(curve)) - return err + return gkrgates.Register(intKeyGate2(&p.RoundKeys[round][1]), 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(y, round)), gkrgates.WithCurves(curve)) } fullRound := func(i int) error { diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go deleted file mode 100644 index 0a230c4381..0000000000 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package gkr_poseidon2 - -import ( - "fmt" - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/frontend/cs/scs" - _ "github.com/consensys/gnark/std/hash/all" - "github.com/consensys/gnark/test" - "github.com/stretchr/testify/require" -) - -func gkrCompressionCircuits(t require.TestingT, n int) (circuit, assignment testGkrCompressionCircuit) { - var k int64 - ins := make([][2]frontend.Variable, n) - outs := make([]frontend.Variable, n) - for i := range n { - var x [2]fr.Element - ins[i] = [2]frontend.Variable{k, k + 1} - - x[0].SetInt64(k) - x[1].SetInt64(k + 1) - y0 := x[1] - - require.NoError(t, bls12377Permutation().Permutation(x[:])) - x[1].Add(&x[1], &y0) - outs[i] = x[1] - - k += 2 - } - - return testGkrCompressionCircuit{ - Ins: make([][2]frontend.Variable, len(ins)), - Outs: make([]frontend.Variable, len(outs)), - }, testGkrCompressionCircuit{ - Ins: ins, - Outs: outs, - } -} - -func TestGkrCompression(t *testing.T) { - circuit, assignment := gkrCompressionCircuits(t, 2) - - test.NewAssert(t).CheckCircuit(&circuit, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BLS12_377)) -} - -type testGkrCompressionCircuit struct { - Ins [][2]frontend.Variable - Outs []frontend.Variable -} - -func (c *testGkrCompressionCircuit) Define(api frontend.API) error { - - pos2 := NewGkrCompressor(api) - api.AssertIsEqual(len(c.Ins), len(c.Outs)) - for i := range c.Ins { - api.AssertIsEqual(c.Outs[i], pos2.Compress(c.Ins[i][0], c.Ins[i][1])) - } - - return nil -} - -func TestGkrCompressionCompiles(t *testing.T) { - // just measure the number of constraints - cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &testGkrCompressionCircuit{ - Ins: make([][2]frontend.Variable, 52000), - Outs: make([]frontend.Variable, 52000), - }) - require.NoError(t, err) - fmt.Println(cs.GetNbConstraints(), "constraints") -} From 8549a0bcba14f11d683510f98e900a1ff3b58274 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 7 Jan 2026 10:58:44 -0600 Subject: [PATCH 084/251] fix: api use --- std/gkrapi/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index 019b954cd8..54a1e11aba 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -30,7 +30,7 @@ func (api *API) NamedGate(gate gkr.GateName, in ...gkr.Variable) gkr.Variable { } func (api *API) Gate(gate gkr.GateFunction, in ...gkr.Variable) gkr.Variable { - if _, err := gkrgates.Register(gate, len(in)); err != nil { + if err := gkrgates.Register(gate, len(in)); err != nil { panic(err) } return api.NamedGate(gkrgates.GetDefaultGateName(gate), in...) From 8bcce8f4f64cfcb894a40462bd9a99ed671ef437 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 7 Jan 2026 11:01:19 -0600 Subject: [PATCH 085/251] remove engine_hints --- internal/gkr/engine_hints.go | 195 ----------------------------------- 1 file changed, 195 deletions(-) delete mode 100644 internal/gkr/engine_hints.go diff --git a/internal/gkr/engine_hints.go b/internal/gkr/engine_hints.go deleted file mode 100644 index 8c8bc1b797..0000000000 --- a/internal/gkr/engine_hints.go +++ /dev/null @@ -1,195 +0,0 @@ -package gkr - -import ( - "errors" - "fmt" - "math/big" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/constraint/solver/gkrgates" - "github.com/consensys/gnark/frontend" - bls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" - bls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" - bls24315 "github.com/consensys/gnark/internal/gkr/bls24-315" - bls24317 "github.com/consensys/gnark/internal/gkr/bls24-317" - bn254 "github.com/consensys/gnark/internal/gkr/bn254" - bw6633 "github.com/consensys/gnark/internal/gkr/bw6-633" - bw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" - "github.com/consensys/gnark/internal/gkr/gkrinfo" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/internal/utils" -) - -type TestEngineHints struct { - assignment gkrtypes.WireAssignment - info *gkrinfo.StoringInfo // we retain a reference to the solving info to allow the caller to modify it between calls to Solve and Prove - circuit gkrtypes.Circuit - gateIns []frontend.Variable -} - -func NewTestEngineHints(info *gkrinfo.StoringInfo) (*TestEngineHints, error) { - circuit, err := gkrtypes.CircuitInfoToCircuit(info.Circuit, gkrgates.Get) - if err != nil { - return nil, err - } - - return &TestEngineHints{ - info: info, - circuit: circuit, - gateIns: make([]frontend.Variable, circuit.MaxGateNbIn()), - assignment: make(gkrtypes.WireAssignment, len(circuit)), - }, - err -} - -// Solve solves one instance of a GKR circuit. -// The first input is the index of the instance. The rest are the inputs of the circuit, in their nominal order. -func (h *TestEngineHints) Solve(mod *big.Int, ins []*big.Int, outs []*big.Int) error { - - instanceI := len(h.assignment[0]) - if in0 := ins[0].Uint64(); !ins[0].IsUint64() || in0 > 0xffffffff { - return errors.New("first input must be a uint32 instance index") - } else if in0 != uint64(instanceI) || h.info.NbInstances != instanceI { - return errors.New("first input must equal the number of instances, and calls to Solve must be done in order of instance index") - } - - api := gateAPI{mod} - - inI := 1 - outI := 0 - for wI := range h.circuit { - w := &h.circuit[wI] - var val frontend.Variable - if w.IsInput() { - val = utils.FromInterface(ins[inI]) - inI++ - } else { - for gateInI, inWI := range w.Inputs { - h.gateIns[gateInI] = h.assignment[inWI][instanceI] - } - val = w.Gate.Evaluate(api, h.gateIns[:len(w.Inputs)]...) - } - if w.IsOutput() { - *outs[outI] = utils.FromInterface(val) - outI++ - } - h.assignment[wI] = append(h.assignment[wI], val) - } - return nil -} - -func (h *TestEngineHints) Prove(mod *big.Int, ins, outs []*big.Int) error { - - info, err := gkrtypes.StoringToSolvingInfo(*h.info, gkrgates.Get) - if err != nil { - return fmt.Errorf("failed to convert storing info to solving info: %w", err) - } - - // TODO @Tabaie autogenerate this or decide not to - if mod.Cmp(ecc.BLS12_377.ScalarField()) == 0 { - data := bls12377.NewSolvingData(info, bls12377.WithAssignment(h.assignment)) - return bls12377.ProveHint(info.HashName, data)(mod, ins, outs) - } - if mod.Cmp(ecc.BLS12_381.ScalarField()) == 0 { - data := bls12381.NewSolvingData(info, bls12381.WithAssignment(h.assignment)) - return bls12381.ProveHint(info.HashName, data)(mod, ins, outs) - } - if mod.Cmp(ecc.BLS24_315.ScalarField()) == 0 { - data := bls24315.NewSolvingData(info, bls24315.WithAssignment(h.assignment)) - return bls24315.ProveHint(info.HashName, data)(mod, ins, outs) - } - if mod.Cmp(ecc.BLS24_317.ScalarField()) == 0 { - data := bls24317.NewSolvingData(info, bls24317.WithAssignment(h.assignment)) - return bls24317.ProveHint(info.HashName, data)(mod, ins, outs) - } - if mod.Cmp(ecc.BN254.ScalarField()) == 0 { - data := bn254.NewSolvingData(info, bn254.WithAssignment(h.assignment)) - return bn254.ProveHint(info.HashName, data)(mod, ins, outs) - } - if mod.Cmp(ecc.BW6_633.ScalarField()) == 0 { - data := bw6633.NewSolvingData(info, bw6633.WithAssignment(h.assignment)) - return bw6633.ProveHint(info.HashName, data)(mod, ins, outs) - } - if mod.Cmp(ecc.BW6_761.ScalarField()) == 0 { - data := bw6761.NewSolvingData(info, bw6761.WithAssignment(h.assignment)) - return bw6761.ProveHint(info.HashName, data)(mod, ins, outs) - } - - return errors.New("unsupported modulus") -} - -// GetAssignment returns the assignment for a particular wire and instance. -func (h *TestEngineHints) GetAssignment(_ *big.Int, ins []*big.Int, outs []*big.Int) error { - if len(ins) != 3 || !ins[0].IsUint64() || !ins[1].IsUint64() { - return errors.New("expected 3 inputs: wire index, instance index, and dummy output from the same instance") - } - if len(outs) != 1 { - return errors.New("expected 1 output: the value of the wire at the given instance") - } - *outs[0] = utils.FromInterface(h.assignment[ins[0].Uint64()][ins[1].Uint64()]) - return nil -} - -type gateAPI struct{ *big.Int } - -func (g gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - in1 := utils.FromInterface(i1) - in2 := utils.FromInterface(i2) - - in1.Add(&in1, &in2) - for _, v := range in { - inV := utils.FromInterface(v) - in1.Add(&in1, &inV) - } - return &in1 -} - -func (g gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - x, y := utils.FromInterface(b), utils.FromInterface(c) - x.Mul(&x, &y) - x.Mod(&x, g.Int) // reduce - y = utils.FromInterface(a) - x.Add(&x, &y) - return &x -} - -func (g gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - x := utils.FromInterface(i1) - x.Neg(&x) - return &x -} - -func (g gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - x := utils.FromInterface(i1) - y := utils.FromInterface(i2) - x.Sub(&x, &y) - for _, v := range in { - y = utils.FromInterface(v) - x.Sub(&x, &y) - } - return &x -} - -func (g gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - x := utils.FromInterface(i1) - y := utils.FromInterface(i2) - x.Mul(&x, &y) - for _, v := range in { - y = utils.FromInterface(v) - x.Mul(&x, &y) - } - x.Mod(&x, g.Int) // reduce - return &x -} - -func (g gateAPI) Println(a ...frontend.Variable) { - strings := make([]string, len(a)) - for i := range a { - if s, ok := a[i].(fmt.Stringer); ok { - strings[i] = s.String() - } else if strings[i], ok = a[i].(string); !ok { - bigInt := utils.FromInterface(a[i]) - strings[i] = bigInt.String() - } - } -} From a045f1171e63744dd1503756ab7b942dfe0f456d Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 7 Jan 2026 11:06:31 -0600 Subject: [PATCH 086/251] revert change to engine.go --- test/engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/engine.go b/test/engine.go index b37bc68acc..ff7f9e2c7a 100644 --- a/test/engine.go +++ b/test/engine.go @@ -110,7 +110,7 @@ func IsSolved(circuit, witness frontend.Circuit, field *big.Int, opts ...TestEng defer func() { if r := recover(); r != nil { - err = fmt.Errorf("%v\n%s", r, debug.Stack()) + err = fmt.Errorf("%v\n%s", r, string(debug.Stack())) } }() From 9c35fd7220595baee2f52c67618fe6e9a74cb841 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 7 Jan 2026 14:21:28 -0600 Subject: [PATCH 087/251] fix: mimc tests --- std/hash/mimc/gkr-mimc/gkr-mimc_test.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go index 8a9381bfd7..a6c2085833 100644 --- a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go +++ b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "os" + "path/filepath" "slices" "testing" @@ -24,7 +25,7 @@ func TestGkrMiMC(t *testing.T) { vals[i] = i + 1 } - for _, length := range lengths[1:2] { + for _, length := range lengths { circuit := &testGkrMiMCCircuit{ In: make([]frontend.Variable, length*2), } @@ -116,23 +117,29 @@ func (c hashTreeCircuit) Define(api frontend.API) error { return nil } -func loadCs(t require.TestingT, filename string, circuit frontend.Circuit) constraint.ConstraintSystem { - f, err := os.Open(filename) +func loadCs(t require.TestingT, fileTitle string, circuit frontend.Circuit) constraint.ConstraintSystem { + filename := filepath.Join(os.TempDir(), fileTitle) + _, err := os.Stat(filename) if os.IsNotExist(err) { // actually compile cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, circuit) require.NoError(t, err) - f, err = os.Create(filename) + f, err := os.Create(filename) require.NoError(t, err) - defer f.Close() + defer func() { + require.NoError(t, f.Close()) + }() _, err = cs.WriteTo(f) require.NoError(t, err) return cs } - defer f.Close() + f, err := os.Open(filename) require.NoError(t, err) + defer func() { + require.NoError(t, f.Close()) + }() cs := plonk.NewCS(ecc.BLS12_377) From dd4e36a8482aebd7a486fe0a4b70aedc1810820a Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 7 Jan 2026 14:38:14 -0600 Subject: [PATCH 088/251] fix: error message --- std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go index 5b3d34b2ee..a6e8c7bb1d 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go @@ -140,7 +140,7 @@ func NewCompressor(api frontend.API) (hash.Compressor, error) { if compressor, ok := cached.(*compressor); ok { return compressor, nil } - return nil, fmt.Errorf("cached value is of type %T, not a mimcCompressor", cached) + return nil, fmt.Errorf("cached value is of type %T, not a gkr-poseidon2.Compressor", cached) } gkrCircuit, in1, in2, out, err := defineCircuit(api) From 560e80efa336bf0c9fec75cfff6588cbebee2f2a Mon Sep 17 00:00:00 2001 From: Tabaie Date: Thu, 8 Jan 2026 13:02:32 -0600 Subject: [PATCH 089/251] fix: error on empty list --- std/permutation/gkr-mimc/gkr-mimc.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/std/permutation/gkr-mimc/gkr-mimc.go b/std/permutation/gkr-mimc/gkr-mimc.go index 75736b22ef..4a0bc0355e 100644 --- a/std/permutation/gkr-mimc/gkr-mimc.go +++ b/std/permutation/gkr-mimc/gkr-mimc.go @@ -1,6 +1,7 @@ package gkr_mimc import ( + "errors" "fmt" "math/big" @@ -96,6 +97,9 @@ func NewCompressor(api frontend.API) (hash.Compressor, error) { } func RegisterGates(curves ...ecc.ID) error { + if len(curves) == 0 { + return errors.New("expected at least one curve") + } for _, curve := range curves { constants, deg, err := getParams(curve) if err != nil { From c80eff5cb961464ca8ed651d30b069042450c192 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 11 Jan 2026 17:21:08 -0600 Subject: [PATCH 090/251] revert: api in the solve hint --- .../backend/template/gkr/solver_hints.go.tmpl | 9 +++++---- internal/gkr/bls12-377/solver_hints.go | 11 ++++++----- internal/gkr/bls12-381/solver_hints.go | 11 ++++++----- internal/gkr/bls24-315/solver_hints.go | 11 ++++++----- internal/gkr/bls24-317/solver_hints.go | 11 ++++++----- internal/gkr/bn254/solver_hints.go | 11 ++++++----- internal/gkr/bw6-633/solver_hints.go | 11 ++++++----- internal/gkr/bw6-761/solver_hints.go | 11 ++++++----- 8 files changed, 47 insertions(+), 39 deletions(-) diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index d7e5c7f7a5..278ca9fdce 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -138,6 +138,7 @@ func SolveHint(data []SolvingData) hint.Hint { // indices for reading inputs and outputs outsI := 0 insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -153,10 +154,10 @@ func SolveHint(data []SolvingData) hint.Hint { for i, inWI := range w.Inputs { gateIns[i] = &data.assignment[inWI][instanceI] } - // evaluate the gate on the inputs - eval := w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*{{ .ElementType }}) - // store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(eval) + + // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 0231c46dfa..7c37f29b2f 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -146,7 +146,8 @@ func SolveHint(data []SolvingData) hint.Hint { // indices for reading inputs and outputs outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -162,10 +163,10 @@ func SolveHint(data []SolvingData) hint.Hint { for i, inWI := range w.Inputs { gateIns[i] = &data.assignment[inWI][instanceI] } - // evaluate the gate on the inputs - eval := w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element) - // store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(eval) + + // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index b2924f152d..b270dfe45d 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -146,7 +146,8 @@ func SolveHint(data []SolvingData) hint.Hint { // indices for reading inputs and outputs outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -162,10 +163,10 @@ func SolveHint(data []SolvingData) hint.Hint { for i, inWI := range w.Inputs { gateIns[i] = &data.assignment[inWI][instanceI] } - // evaluate the gate on the inputs - eval := w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element) - // store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(eval) + + // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index 21d00c720d..3a9b7eda63 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -146,7 +146,8 @@ func SolveHint(data []SolvingData) hint.Hint { // indices for reading inputs and outputs outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -162,10 +163,10 @@ func SolveHint(data []SolvingData) hint.Hint { for i, inWI := range w.Inputs { gateIns[i] = &data.assignment[inWI][instanceI] } - // evaluate the gate on the inputs - eval := w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element) - // store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(eval) + + // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index 04826fdacf..fc18d24659 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -146,7 +146,8 @@ func SolveHint(data []SolvingData) hint.Hint { // indices for reading inputs and outputs outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -162,10 +163,10 @@ func SolveHint(data []SolvingData) hint.Hint { for i, inWI := range w.Inputs { gateIns[i] = &data.assignment[inWI][instanceI] } - // evaluate the gate on the inputs - eval := w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element) - // store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(eval) + + // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index f28cdf8eae..01b14882e7 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -146,7 +146,8 @@ func SolveHint(data []SolvingData) hint.Hint { // indices for reading inputs and outputs outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -162,10 +163,10 @@ func SolveHint(data []SolvingData) hint.Hint { for i, inWI := range w.Inputs { gateIns[i] = &data.assignment[inWI][instanceI] } - // evaluate the gate on the inputs - eval := w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element) - // store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(eval) + + // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index 9ee13e82dc..42a801f1fc 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -146,7 +146,8 @@ func SolveHint(data []SolvingData) hint.Hint { // indices for reading inputs and outputs outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -162,10 +163,10 @@ func SolveHint(data []SolvingData) hint.Hint { for i, inWI := range w.Inputs { gateIns[i] = &data.assignment[inWI][instanceI] } - // evaluate the gate on the inputs - eval := w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element) - // store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(eval) + + // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index 4cd5f99a99..dc5456fdb5 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -146,7 +146,8 @@ func SolveHint(data []SolvingData) hint.Hint { // indices for reading inputs and outputs outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -162,10 +163,10 @@ func SolveHint(data []SolvingData) hint.Hint { for i, inWI := range w.Inputs { gateIns[i] = &data.assignment[inWI][instanceI] } - // evaluate the gate on the inputs - eval := w.Gate.Evaluate(api, gateIns[:len(w.Inputs)]...).(*fr.Element) - // store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(eval) + + // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) + data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) + api.freeElements() } if w.IsOutput() { // write to provided output. From 17f7285ecbb393a87400a711078751e79e0a0d97 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 12 Jan 2026 14:21:46 -0600 Subject: [PATCH 091/251] feat: first pass at compiled gates --- .../backend/template/gkr/gate_testing.go.tmpl | 107 ++++-- .../backend/template/gkr/gkr.go.tmpl | 272 +++++++++++----- .../backend/template/gkr/solver_hints.go.tmpl | 28 +- internal/gkr/bls12-377/gate_testing.go | 104 ++++-- internal/gkr/bls12-377/gkr.go | 271 +++++++++++----- internal/gkr/bls12-377/solver_hints.go | 31 +- internal/gkr/bls12-381/gate_testing.go | 104 ++++-- internal/gkr/bls12-381/gkr.go | 271 +++++++++++----- internal/gkr/bls12-381/solver_hints.go | 31 +- internal/gkr/bls24-315/gate_testing.go | 104 ++++-- internal/gkr/bls24-315/gkr.go | 271 +++++++++++----- internal/gkr/bls24-315/solver_hints.go | 31 +- internal/gkr/bls24-317/gate_testing.go | 104 ++++-- internal/gkr/bls24-317/gkr.go | 271 +++++++++++----- internal/gkr/bls24-317/solver_hints.go | 31 +- internal/gkr/bn254/gate_testing.go | 104 ++++-- internal/gkr/bn254/gkr.go | 271 +++++++++++----- internal/gkr/bn254/solver_hints.go | 31 +- internal/gkr/bw6-633/gate_testing.go | 104 ++++-- internal/gkr/bw6-633/gkr.go | 271 +++++++++++----- internal/gkr/bw6-633/solver_hints.go | 31 +- internal/gkr/bw6-761/gate_testing.go | 104 ++++-- internal/gkr/bw6-761/gkr.go | 271 +++++++++++----- internal/gkr/bw6-761/solver_hints.go | 31 +- internal/gkr/gkrtypes/compiledgate.go | 307 ++++++++++++++++++ internal/gkr/gkrtypes/compiledgate_test.go | 132 ++++++++ internal/gkr/gkrtypes/types.go | 92 +++++- internal/gkr/small_rational/gkr.go | 271 +++++++++++----- ...uts_select-input-3_gate_two_instances.json | 4 +- internal/small_rational/small-rational.go | 8 + 30 files changed, 2979 insertions(+), 1084 deletions(-) create mode 100644 internal/gkr/gkrtypes/compiledgate.go create mode 100644 internal/gkr/gkrtypes/compiledgate_test.go diff --git a/internal/generator/backend/template/gkr/gate_testing.go.tmpl b/internal/generator/backend/template/gkr/gate_testing.go.tmpl index 7fa4cee0d2..187abca35e 100644 --- a/internal/generator/backend/template/gkr/gate_testing.go.tmpl +++ b/internal/generator/backend/template/gkr/gate_testing.go.tmpl @@ -13,10 +13,55 @@ import ( "slices" ) +// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. +// The arena is pre-loaded with constants and reused across all evaluations. +type compiledGateTester struct { + compiled *gkrtypes.CompiledGate + arena elementArena + nbConsts int + inputBuf []*{{ .ElementType }} // reusable buffer for input pointers +} + +// newCompiledGateTester compiles a gate function and prepares it for testing. +func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { + compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) + + tester := &compiledGateTester{ + compiled: compiled, + nbConsts: compiled.NbConstants(), + inputBuf: make([]*{{ .ElementType }}, nbIn), + } + + // Preload constants into arena once + tester.arena.preloadConstants(compiled) + + return tester +} + +// eval evaluates the gate on the given inputs. +// The arena is automatically reset after each call. +// Returns a copy of the result to avoid arena aliasing issues. +func (t *compiledGateTester) eval(in ...{{ .ElementType }}) {{ .ElementType }} { + // Use input buffer to avoid allocation + for i := range in { + t.inputBuf[i] = &in[i] + } + + resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) + + // Make a copy before resetting the arena + var result {{ .ElementType }} + result.Set(resultPtr) + + // Reset arena to just constants for next invocation + t.arena.shrinkToConstants(t.nbConsts) + + return result +} + // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - var api gateAPI - fWrapped := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable @@ -26,20 +71,20 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := fWrapped(in...) + y0 := tester.eval(in...) x[i] = x0 copy(in, x) - y1 := fWrapped(in...) + y1 := tester.eval(in...) x[i].Double(&x[i]) copy(in, x) - y2 := fWrapped(in...) + y2 := tester.eval(in...) - y2.Sub(y2, y1) - y1.Sub(y1, y0) + y2.Sub(&y2, &y1) + y1.Sub(&y1, &y0) - if !y2.Equal(y1) { + if !y2.Equal(&y1) { return false // not linear } @@ -52,21 +97,23 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = fWrapped(in...) + y0 = tester.eval(in...) x[i] = x0 copy(in, x) - y1 = fWrapped(in...) + y1 = tester.eval(in...) - y1.Sub(y1, y0) + y1.Sub(&y1, &y0) - return y1.Equal(y2) + return y1.Equal(&y2) } -// fitPoly tries to fit a polynomial of degree less than degreeBound to f. +// fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynomial { +func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { + nbIn := t.compiled.NbInputs + // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants fIn := make([]{{ .ElementType }}, nbIn) @@ -83,7 +130,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - p[i].Set(f(fIn...)) + result := t.eval(fIn...) + p[i].Set(&result) x.Mul(&x, &domain.Generator) } @@ -99,7 +147,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom for j := range consts { fIn[j+1].Mul(&x[i], &consts[j]) } - p[i].Set(f(fIn...)) + result := t.eval(fIn...) + p[i].Set(&result) } // obtain p's coefficients @@ -115,8 +164,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := f(fIn...) - if !pAt.Equal(fAt) { + fAt := t.eval(fIn...) + if !pAt.Equal(&fAt) { return nil } @@ -131,25 +180,22 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - var api gateAPI - fFr := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := fFr.fitPoly(nbIn, degreeBound); p != nil { + if p := tester.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } return len(p) - 1, nil } - api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - var api gateAPI - fFr := api.convertFunc(f) - if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { + tester := newCompiledGateTester(f, nbIn) + if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -161,14 +207,13 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - var api gateAPI x := make({{.FieldPackageName}}.Vector, nbIn) x.MustSetRandom() - fFr := api.convertFunc(f) - gFr := api.convertFunc(g) - fAt := fFr(x...) - gAt := gFr(x...) - return fAt.Equal(gAt) + fTester := newCompiledGateTester(f, nbIn) + gTester := newCompiledGateTester(g, nbIn) + fAt := fTester.eval(x...) + gAt := gTester.eval(x...) + return fAt.Equal(&gAt) } {{- if not .CanUseFFT }} diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 6149fdc8c0..0a3a87d048 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -10,7 +10,6 @@ import ( "sync" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit @@ -93,12 +92,14 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []{{ .ElementType e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]frontend.Variable, len(wire.Inputs)) + inputEvaluations := make([]*{{ .ElementType }}, len(wire.Inputs)) for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - var api gateAPI - gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*{{ .ElementType }})) + var stack elementStack + compiled := wire.Gate.Compiled() + stack.preloadConstants(compiled) + gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -230,10 +231,11 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]{{ .ElementType }}, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var ( - step {{ .ElementType }} - api gateAPI - ) + var step {{ .ElementType }} + + // Create gate evaluator once per thread + compiled := wire.Gate.Compiled() + evaluator := newGateEvaluator(compiled) res := make([]{{ .ElementType }}, degGJ) @@ -243,7 +245,6 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ... // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]{{ .ElementType }}, degGJ*len(ml)) - gateInput := make([]frontend.Variable, nbGateIn) for h := start; h < end; h++ { // h counts across instances @@ -260,14 +261,14 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { - for i := range gateInput { - gateInput[i] = &mlEvals[eIndex+1+i] + // Push gate inputs + for i := 0; i < nbGateIn; i++ { + evaluator.pushInput(&mlEvals[eIndex+1+i]) } - summand := wire.Gate.Evaluate(&api, gateInput...).(*{{ .ElementType }}) + summand := evaluator.evaluate() summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) - api.freeElements() } } mu.Lock() @@ -667,15 +668,35 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } - var api gateAPI + var stack elementStack ins := make([]{{ .ElementType }}, maxNbIns) + insPtr := make([]*{{ .ElementType }}, maxNbIns) + + // Track current gate to know when to reload constants + var currentCompiled *gkrtypes.CompiledGate + var nbConsts int + for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { + compiled := w.Gate.Compiled() + + // Preload constants if this is a new gate + if compiled != currentCompiled { + stack.freeElements() + stack.preloadConstants(compiled) + currentCompiled = compiled + nbConsts = compiled.NbConstants() + } + for inI, in := range w.Inputs { ins[inI] = a[in][i] + insPtr[inI] = &ins[inI] } - a[wI][i].Set(api.evaluate(w.Gate.Evaluate, ins[:len(w.Inputs)]...)) + a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) + + // Shrink stack back to just constants for reuse + stack.shrinkToConstants(nbConsts) } } } @@ -724,70 +745,96 @@ func frToBigInts(dst []*big.Int, src []{{ .ElementType }}) { } -// gateAPI implements gkr.GateAPI. -// It uses a synchronous memory pool underneath to minimize heap allocations. -type gateAPI struct { +// elementStack implements a synchronous memory pool underneath to minimize heap allocations. +type elementStack struct { allocated []*{{ .ElementType }} nbUsed int } -func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Add(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Add(res, api.cast(v)) +// preloadConstants loads gate constants into the stack at indices [0, nbConsts). +// This should be called once per gate to prepare the stack for repeated use. +func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { + for _, constVal := range gate.Constants { + elem := stack.newElement() + elem.SetBigInt(constVal) + } +} + +// shrinkToConstants resets the stack to contain only constants, ready for next invocation. +func (stack *elementStack) truncate(n int) { + stack.nbUsed = n +} + +func (stack *elementStack) newElement() *{{ .ElementType }} { + stack.nbUsed++ + if stack.nbUsed >= len(stack.allocated) { + stack.allocated = append(stack.allocated, new({{ .ElementType }})) + } + return stack.allocated[api.nbUsed-1] +} + +func (stack *elementStack) push(x *{{ .ElementType }}) { + stack.newElement().Set(x) +} + +func opAdd(stack *elementStack, x []*{{ .ElementType }}) *{{ .ElementType }} { + res := stack.newElement() + res.Add(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Add(res, x[i]) } return res } -func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - prod := api.newElement() - prod.Mul(api.cast(b), api.cast(c)) - res := api.cast(a) - res.Add(res, prod) +func opMulAcc(stack *elementStack, x []*{{ .ElementType }}) *{{ .ElementType }} { + // result = x[0] + (x[1] * x[2]) + prod := stack.newElement() + prod.Mul(x[1], x[2]) + res := stack.newElement() + res.Add(x[0], prod) return res } -func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - res := api.newElement() - res.Neg(api.cast(i1)) +func opNeg(stack *elementStack, x []*{{ .ElementType }}) *{{ .ElementType }} { + res := stack.newElement() + res.Neg(x[0]) return res } -func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Sub(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Sub(res, api.cast(v)) +func opSub(stack *elementStack, x []*{{ .ElementType }}) *{{ .ElementType }} { + res := stack.newElement() + res.Sub(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Sub(res, x[i]) } return res } -func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Mul(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Mul(res, api.cast(v)) +func opMul(stack *elementStack, x []*{{ .ElementType }}) *{{ .ElementType }} { + res := stack.newElement() + res.Mul(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Mul(res, x[i]) } return res } -func (api *gateAPI) SumExp17(a,b,c frontend.Variable) frontend.Variable { - var x {{ .ElementType }} - - x.Add(api.cast(a), api.cast(b)) - x.Add(&x, api.cast(c)) - - res := api.newElement() +func opSumExp17(stack *elementStack, x []*{{ .ElementType }}) *{{ .ElementType }} { + // result = (x[0] + x[1] + x[2])^17 + sum := stack.newElement() + sum.Add(x[0], x[1]) + sum.Add(sum, x[2]) - res.Mul(&x, &x) // x² - res.Mul(res, res) // x⁴ - res.Mul(res, res) // x⁸ - res.Mul(res, res) // x¹⁶ - return res.Mul(res, &x) // x¹⁷ + res := stack.newElement() + res.Mul(sum, sum) // sum² + res.Mul(res, res) // sum⁴ + res.Mul(res, res) // sum⁸ + res.Mul(res, res) // sum¹⁶ + res.Mul(res, sum) // sum¹⁷ + return res } -func (api *gateAPI) Println(a ...frontend.Variable) { +func (api *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x {{ .ElementType }} @@ -805,43 +852,104 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) evaluate(f gkr.GateFunction, in ...{{ .ElementType }}) *{{ .ElementType }} { - inVar := make([]frontend.Variable, len(in)) - for i := range in { - inVar[i] = &in[i] + +// executeCompiledGate executes a compiled gate using curve-specific field operations. +// The stack is expected to already contain constants at indices [0, nbConsts). +// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. +// After execution, the stack can be shrunk back to nbConsts for reuse. +func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*{{.ElementType}}) *{{ .ElementType }} { + + // Instruction executor functions + executors := [...]func(*elementStack, []*{{ .ElementType }}) *{{ .ElementType }}{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, } - return f(api, inVar...).(*{{ .ElementType }}) -} -// Put all elements back in the pool. -func (api *gateAPI) freeElements() { - api.nbUsed = 0 -} + nbConsts := gate.NbConstants() -func (api *gateAPI) newElement() *{{ .ElementType }} { - api.nbUsed++ - if api.nbUsed >= len(api.allocated) { - api.allocated = append(api.allocated, new({{ .ElementType }})) + // If no instructions, this is an identity-like gate; return first input + if len(gate.Instructions) == 0 { + if gate.NbInputs == 0 { + panic("gate has no inputs and no instructions") + } + return inputs[0] } - return api.allocated[api.nbUsed-1] -} -type gateFunctionFr func(...{{ .ElementType }}) *{{ .ElementType }} + // Append inputs to stack after constants + // The stack now contains: [constants | inputs] + for _, input := range inputs { + ptr := stack.newElement() + ptr.Set(input) + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range gate.Instructions { + inst := &gate.Instructions[i] + executor := executors[inst.Op] + if executor == nil { + panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) + } + + // Resolve operands from stack using instruction indices + operands := make([]*{{ .ElementType }}, len(inst.Inputs)) + for j, idx := range inst.Inputs { + operands[j] = stack.allocated[idx] + } -// convertFunc turns f into a function that accepts and returns {{ .ElementType }}. -func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { - return func(in ...{{ .ElementType }}) *{{ .ElementType }} { - return api.evaluate(f, in...) + _ = executor(stack, operands) + // Result is automatically appended to stack by executor } + + // Last value in stack is the result + resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 + return stack.allocated[resultIdx] +} + +// gateEvaluator provides a high-level API for evaluating compiled gates efficiently. +// It manages the stack internally and handles input buffering, making it easy to +// evaluate the same gate multiple times with different inputs. +type gateEvaluator struct { + gate *gkrtypes.CompiledGate + stack elementStack + nbConsts int } -func (api *gateAPI) cast(v frontend.Variable) *{{ .ElementType }} { - if x, ok := v.(*{{ .ElementType }}); ok { // fast path, no extra heap allocation - return x +// newGateEvaluator creates an evaluator for the given compiled gate. +// The stack is preloaded with constants and ready for evaluation. +func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { + eval := &gateEvaluator{ + gate: gate, + nbConsts: gate.NbConstants(), + inputs: make([]*{{ .ElementType }}, 0, gate.NbInputs), } - x := api.newElement() - if _, err := x.SetInterface(v); err != nil { - panic(err) + eval.stack.preloadConstants(gate) + return eval +} + +// pushInput adds an input to the evaluator's input buffer. +// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +func (e *gateEvaluator) pushInput(input *{{ .ElementType }}) { + e.inputs = append(e.inputs, input) +} + +// evaluate executes the gate with the current inputs and returns the result. +// The stack and input buffer are automatically reset after evaluation, +// making the evaluator ready for the next evaluation. +func (e *gateEvaluator) evaluate() *{{ .ElementType }} { + if len(e.inputs) != e.gate.NbInputs { + panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) } - return x + + result := executeCompiledGate(e.gate, &e.stack, e.inputs) + + // Reset for next evaluation + e.stack.shrinkToConstants(e.nbConsts) + e.inputs = e.inputs[:0] // Clear input buffer + + return result } \ No newline at end of file diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index 278ca9fdce..6d30c0bef6 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -132,13 +132,13 @@ func SolveHint(data []SolvingData) hint.Hint { data := data[ins[0].Uint64()] instanceI := ins[1].Uint64() - // create buffer for every gate input. It will be reused for every evaluation. - gateIns := make([]frontend.Variable, data.maxNbIn) - // indices for reading inputs and outputs outsI := 0 insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. + + // Track current gate evaluator to reuse across wires with the same gate + var currentEvaluator *gateEvaluator + var currentCompiled *gkrtypes.CompiledGate // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -150,14 +150,22 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - // assemble input for gate - for i, inWI := range w.Inputs { - gateIns[i] = &data.assignment[inWI][instanceI] + compiled := w.Gate.Compiled() + + // Create new evaluator if this is a different gate + if compiled != currentCompiled { + currentEvaluator = newGateEvaluator(compiled) + currentCompiled = compiled + } + + // Push gate inputs + for _, inWI := range w.Inputs { + currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) } - // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) - api.freeElements() + // Evaluate the gate + result := currentEvaluator.evaluate() + data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bls12-377/gate_testing.go b/internal/gkr/bls12-377/gate_testing.go index 49916a4248..34d96c6b5d 100644 --- a/internal/gkr/bls12-377/gate_testing.go +++ b/internal/gkr/bls12-377/gate_testing.go @@ -19,10 +19,55 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) +// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. +// The arena is pre-loaded with constants and reused across all evaluations. +type compiledGateTester struct { + compiled *gkrtypes.CompiledGate + arena elementArena + nbConsts int + inputBuf []*fr.Element // reusable buffer for input pointers +} + +// newCompiledGateTester compiles a gate function and prepares it for testing. +func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { + compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) + + tester := &compiledGateTester{ + compiled: compiled, + nbConsts: compiled.NbConstants(), + inputBuf: make([]*fr.Element, nbIn), + } + + // Preload constants into arena once + tester.arena.preloadConstants(compiled) + + return tester +} + +// eval evaluates the gate on the given inputs. +// The arena is automatically reset after each call. +// Returns a copy of the result to avoid arena aliasing issues. +func (t *compiledGateTester) eval(in ...fr.Element) fr.Element { + // Use input buffer to avoid allocation + for i := range in { + t.inputBuf[i] = &in[i] + } + + resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) + + // Make a copy before resetting the arena + var result fr.Element + result.Set(resultPtr) + + // Reset arena to just constants for next invocation + t.arena.shrinkToConstants(t.nbConsts) + + return result +} + // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - var api gateAPI - fWrapped := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable @@ -32,20 +77,20 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := fWrapped(in...) + y0 := tester.eval(in...) x[i] = x0 copy(in, x) - y1 := fWrapped(in...) + y1 := tester.eval(in...) x[i].Double(&x[i]) copy(in, x) - y2 := fWrapped(in...) + y2 := tester.eval(in...) - y2.Sub(y2, y1) - y1.Sub(y1, y0) + y2.Sub(&y2, &y1) + y1.Sub(&y1, &y0) - if !y2.Equal(y1) { + if !y2.Equal(&y1) { return false // not linear } @@ -58,21 +103,23 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = fWrapped(in...) + y0 = tester.eval(in...) x[i] = x0 copy(in, x) - y1 = fWrapped(in...) + y1 = tester.eval(in...) - y1.Sub(y1, y0) + y1.Sub(&y1, &y0) - return y1.Equal(y2) + return y1.Equal(&y2) } -// fitPoly tries to fit a polynomial of degree less than degreeBound to f. +// fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynomial { +func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { + nbIn := t.compiled.NbInputs + // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants fIn := make([]fr.Element, nbIn) @@ -88,7 +135,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - p[i].Set(f(fIn...)) + result := t.eval(fIn...) + p[i].Set(&result) x.Mul(&x, &domain.Generator) } @@ -103,8 +151,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := f(fIn...) - if !pAt.Equal(fAt) { + fAt := t.eval(fIn...) + if !pAt.Equal(&fAt) { return nil } @@ -119,25 +167,22 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - var api gateAPI - fFr := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := fFr.fitPoly(nbIn, degreeBound); p != nil { + if p := tester.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } return len(p) - 1, nil } - api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - var api gateAPI - fFr := api.convertFunc(f) - if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { + tester := newCompiledGateTester(f, nbIn) + if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -149,12 +194,11 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - var api gateAPI x := make(fr.Vector, nbIn) x.MustSetRandom() - fFr := api.convertFunc(f) - gFr := api.convertFunc(g) - fAt := fFr(x...) - gAt := gFr(x...) - return fAt.Equal(gAt) + fTester := newCompiledGateTester(f, nbIn) + gTester := newCompiledGateTester(g, nbIn) + fAt := fTester.eval(x...) + gAt := gTester.eval(x...) + return fAt.Equal(&gAt) } diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 176fdd66fb..acf72ac48e 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -18,7 +18,6 @@ import ( "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit @@ -100,12 +99,14 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]frontend.Variable, len(wire.Inputs)) + inputEvaluations := make([]*fr.Element, len(wire.Inputs)) for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - var api gateAPI - gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*fr.Element)) + var stack elementStack + compiled := wire.Gate.Compiled() + stack.preloadConstants(compiled) + gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,10 +237,11 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var ( - step fr.Element - api gateAPI - ) + var step fr.Element + + // Create gate evaluator once per thread + compiled := wire.Gate.Compiled() + evaluator := newGateEvaluator(compiled) res := make([]fr.Element, degGJ) @@ -249,7 +251,6 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ... // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]fr.Element, degGJ*len(ml)) - gateInput := make([]frontend.Variable, nbGateIn) for h := start; h < end; h++ { // h counts across instances @@ -266,14 +267,14 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { - for i := range gateInput { - gateInput[i] = &mlEvals[eIndex+1+i] + // Push gate inputs + for i := 0; i < nbGateIn; i++ { + evaluator.pushInput(&mlEvals[eIndex+1+i]) } - summand := wire.Gate.Evaluate(&api, gateInput...).(*fr.Element) + summand := evaluator.evaluate() summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) - api.freeElements() } } mu.Lock() @@ -672,15 +673,35 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } - var api gateAPI + var stack elementStack ins := make([]fr.Element, maxNbIns) + insPtr := make([]*fr.Element, maxNbIns) + + // Track current gate to know when to reload constants + var currentCompiled *gkrtypes.CompiledGate + var nbConsts int + for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { + compiled := w.Gate.Compiled() + + // Preload constants if this is a new gate + if compiled != currentCompiled { + stack.freeElements() + stack.preloadConstants(compiled) + currentCompiled = compiled + nbConsts = compiled.NbConstants() + } + for inI, in := range w.Inputs { ins[inI] = a[in][i] + insPtr[inI] = &ins[inI] } - a[wI][i].Set(api.evaluate(w.Gate.Evaluate, ins[:len(w.Inputs)]...)) + a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) + + // Shrink stack back to just constants for reuse + stack.shrinkToConstants(nbConsts) } } } @@ -728,70 +749,96 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// gateAPI implements gkr.GateAPI. -// It uses a synchronous memory pool underneath to minimize heap allocations. -type gateAPI struct { +// elementStack implements a synchronous memory pool underneath to minimize heap allocations. +type elementStack struct { allocated []*fr.Element nbUsed int } -func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Add(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Add(res, api.cast(v)) +// preloadConstants loads gate constants into the stack at indices [0, nbConsts). +// This should be called once per gate to prepare the stack for repeated use. +func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { + for _, constVal := range gate.Constants { + elem := stack.newElement() + elem.SetBigInt(constVal) + } +} + +// shrinkToConstants resets the stack to contain only constants, ready for next invocation. +func (stack *elementStack) truncate(n int) { + stack.nbUsed = n +} + +func (stack *elementStack) newElement() *fr.Element { + stack.nbUsed++ + if stack.nbUsed >= len(stack.allocated) { + stack.allocated = append(stack.allocated, new(fr.Element)) + } + return stack.allocated[api.nbUsed-1] +} + +func (stack *elementStack) push(x *fr.Element) { + stack.newElement().Set(x) +} + +func opAdd(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Add(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Add(res, x[i]) } return res } -func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - prod := api.newElement() - prod.Mul(api.cast(b), api.cast(c)) - res := api.cast(a) - res.Add(res, prod) +func opMulAcc(stack *elementStack, x []*fr.Element) *fr.Element { + // result = x[0] + (x[1] * x[2]) + prod := stack.newElement() + prod.Mul(x[1], x[2]) + res := stack.newElement() + res.Add(x[0], prod) return res } -func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - res := api.newElement() - res.Neg(api.cast(i1)) +func opNeg(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Neg(x[0]) return res } -func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Sub(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Sub(res, api.cast(v)) +func opSub(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Sub(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Sub(res, x[i]) } return res } -func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Mul(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Mul(res, api.cast(v)) +func opMul(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Mul(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Mul(res, x[i]) } return res } -func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - var x fr.Element - - x.Add(api.cast(a), api.cast(b)) - x.Add(&x, api.cast(c)) +func opSumExp17(stack *elementStack, x []*fr.Element) *fr.Element { + // result = (x[0] + x[1] + x[2])^17 + sum := stack.newElement() + sum.Add(x[0], x[1]) + sum.Add(sum, x[2]) - res := api.newElement() - - res.Mul(&x, &x) // x² - res.Mul(res, res) // x⁴ - res.Mul(res, res) // x⁸ - res.Mul(res, res) // x¹⁶ - return res.Mul(res, &x) // x¹⁷ + res := stack.newElement() + res.Mul(sum, sum) // sum² + res.Mul(res, res) // sum⁴ + res.Mul(res, res) // sum⁸ + res.Mul(res, res) // sum¹⁶ + res.Mul(res, sum) // sum¹⁷ + return res } -func (api *gateAPI) Println(a ...frontend.Variable) { +func (api *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -809,43 +856,103 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { - inVar := make([]frontend.Variable, len(in)) - for i := range in { - inVar[i] = &in[i] +// executeCompiledGate executes a compiled gate using curve-specific field operations. +// The stack is expected to already contain constants at indices [0, nbConsts). +// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. +// After execution, the stack can be shrunk back to nbConsts for reuse. +func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*fr.Element) *fr.Element { + + // Instruction executor functions + executors := [...]func(*elementStack, []*fr.Element) *fr.Element{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, } - return f(api, inVar...).(*fr.Element) -} -// Put all elements back in the pool. -func (api *gateAPI) freeElements() { - api.nbUsed = 0 -} + nbConsts := gate.NbConstants() -func (api *gateAPI) newElement() *fr.Element { - api.nbUsed++ - if api.nbUsed >= len(api.allocated) { - api.allocated = append(api.allocated, new(fr.Element)) + // If no instructions, this is an identity-like gate; return first input + if len(gate.Instructions) == 0 { + if gate.NbInputs == 0 { + panic("gate has no inputs and no instructions") + } + return inputs[0] } - return api.allocated[api.nbUsed-1] -} -type gateFunctionFr func(...fr.Element) *fr.Element + // Append inputs to stack after constants + // The stack now contains: [constants | inputs] + for _, input := range inputs { + ptr := stack.newElement() + ptr.Set(input) + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range gate.Instructions { + inst := &gate.Instructions[i] + executor := executors[inst.Op] + if executor == nil { + panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) + } + + // Resolve operands from stack using instruction indices + operands := make([]*fr.Element, len(inst.Inputs)) + for j, idx := range inst.Inputs { + operands[j] = stack.allocated[idx] + } -// convertFunc turns f into a function that accepts and returns fr.Element. -func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { - return func(in ...fr.Element) *fr.Element { - return api.evaluate(f, in...) + _ = executor(stack, operands) + // Result is automatically appended to stack by executor } + + // Last value in stack is the result + resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 + return stack.allocated[resultIdx] +} + +// gateEvaluator provides a high-level API for evaluating compiled gates efficiently. +// It manages the stack internally and handles input buffering, making it easy to +// evaluate the same gate multiple times with different inputs. +type gateEvaluator struct { + gate *gkrtypes.CompiledGate + stack elementStack + nbConsts int } -func (api *gateAPI) cast(v frontend.Variable) *fr.Element { - if x, ok := v.(*fr.Element); ok { // fast path, no extra heap allocation - return x +// newGateEvaluator creates an evaluator for the given compiled gate. +// The stack is preloaded with constants and ready for evaluation. +func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { + eval := &gateEvaluator{ + gate: gate, + nbConsts: gate.NbConstants(), + inputs: make([]*fr.Element, 0, gate.NbInputs), } - x := api.newElement() - if _, err := x.SetInterface(v); err != nil { - panic(err) + eval.stack.preloadConstants(gate) + return eval +} + +// pushInput adds an input to the evaluator's input buffer. +// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +func (e *gateEvaluator) pushInput(input *fr.Element) { + e.inputs = append(e.inputs, input) +} + +// evaluate executes the gate with the current inputs and returns the result. +// The stack and input buffer are automatically reset after evaluation, +// making the evaluator ready for the next evaluation. +func (e *gateEvaluator) evaluate() *fr.Element { + if len(e.inputs) != e.gate.NbInputs { + panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) } - return x + + result := executeCompiledGate(e.gate, &e.stack, e.inputs) + + // Reset for next evaluation + e.stack.shrinkToConstants(e.nbConsts) + e.inputs = e.inputs[:0] // Clear input buffer + + return result } diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 7c37f29b2f..4e3bd1e618 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" @@ -141,13 +140,13 @@ func SolveHint(data []SolvingData) hint.Hint { data := data[ins[0].Uint64()] instanceI := ins[1].Uint64() - // create buffer for every gate input. It will be reused for every evaluation. - gateIns := make([]frontend.Variable, data.maxNbIn) - // indices for reading inputs and outputs outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. + insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + + // Track current gate evaluator to reuse across wires with the same gate + var currentEvaluator *gateEvaluator + var currentCompiled *gkrtypes.CompiledGate // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -159,14 +158,22 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - // assemble input for gate - for i, inWI := range w.Inputs { - gateIns[i] = &data.assignment[inWI][instanceI] + compiled := w.Gate.Compiled() + + // Create new evaluator if this is a different gate + if compiled != currentCompiled { + currentEvaluator = newGateEvaluator(compiled) + currentCompiled = compiled + } + + // Push gate inputs + for _, inWI := range w.Inputs { + currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) } - // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) - api.freeElements() + // Evaluate the gate + result := currentEvaluator.evaluate() + data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bls12-381/gate_testing.go b/internal/gkr/bls12-381/gate_testing.go index 66104ee7fe..2d3fd4e6be 100644 --- a/internal/gkr/bls12-381/gate_testing.go +++ b/internal/gkr/bls12-381/gate_testing.go @@ -19,10 +19,55 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) +// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. +// The arena is pre-loaded with constants and reused across all evaluations. +type compiledGateTester struct { + compiled *gkrtypes.CompiledGate + arena elementArena + nbConsts int + inputBuf []*fr.Element // reusable buffer for input pointers +} + +// newCompiledGateTester compiles a gate function and prepares it for testing. +func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { + compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) + + tester := &compiledGateTester{ + compiled: compiled, + nbConsts: compiled.NbConstants(), + inputBuf: make([]*fr.Element, nbIn), + } + + // Preload constants into arena once + tester.arena.preloadConstants(compiled) + + return tester +} + +// eval evaluates the gate on the given inputs. +// The arena is automatically reset after each call. +// Returns a copy of the result to avoid arena aliasing issues. +func (t *compiledGateTester) eval(in ...fr.Element) fr.Element { + // Use input buffer to avoid allocation + for i := range in { + t.inputBuf[i] = &in[i] + } + + resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) + + // Make a copy before resetting the arena + var result fr.Element + result.Set(resultPtr) + + // Reset arena to just constants for next invocation + t.arena.shrinkToConstants(t.nbConsts) + + return result +} + // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - var api gateAPI - fWrapped := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable @@ -32,20 +77,20 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := fWrapped(in...) + y0 := tester.eval(in...) x[i] = x0 copy(in, x) - y1 := fWrapped(in...) + y1 := tester.eval(in...) x[i].Double(&x[i]) copy(in, x) - y2 := fWrapped(in...) + y2 := tester.eval(in...) - y2.Sub(y2, y1) - y1.Sub(y1, y0) + y2.Sub(&y2, &y1) + y1.Sub(&y1, &y0) - if !y2.Equal(y1) { + if !y2.Equal(&y1) { return false // not linear } @@ -58,21 +103,23 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = fWrapped(in...) + y0 = tester.eval(in...) x[i] = x0 copy(in, x) - y1 = fWrapped(in...) + y1 = tester.eval(in...) - y1.Sub(y1, y0) + y1.Sub(&y1, &y0) - return y1.Equal(y2) + return y1.Equal(&y2) } -// fitPoly tries to fit a polynomial of degree less than degreeBound to f. +// fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynomial { +func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { + nbIn := t.compiled.NbInputs + // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants fIn := make([]fr.Element, nbIn) @@ -88,7 +135,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - p[i].Set(f(fIn...)) + result := t.eval(fIn...) + p[i].Set(&result) x.Mul(&x, &domain.Generator) } @@ -103,8 +151,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := f(fIn...) - if !pAt.Equal(fAt) { + fAt := t.eval(fIn...) + if !pAt.Equal(&fAt) { return nil } @@ -119,25 +167,22 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - var api gateAPI - fFr := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := fFr.fitPoly(nbIn, degreeBound); p != nil { + if p := tester.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } return len(p) - 1, nil } - api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - var api gateAPI - fFr := api.convertFunc(f) - if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { + tester := newCompiledGateTester(f, nbIn) + if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -149,12 +194,11 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - var api gateAPI x := make(fr.Vector, nbIn) x.MustSetRandom() - fFr := api.convertFunc(f) - gFr := api.convertFunc(g) - fAt := fFr(x...) - gAt := gFr(x...) - return fAt.Equal(gAt) + fTester := newCompiledGateTester(f, nbIn) + gTester := newCompiledGateTester(g, nbIn) + fAt := fTester.eval(x...) + gAt := gTester.eval(x...) + return fAt.Equal(&gAt) } diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 1f267d1c7b..e4372f1dc7 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -18,7 +18,6 @@ import ( "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit @@ -100,12 +99,14 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]frontend.Variable, len(wire.Inputs)) + inputEvaluations := make([]*fr.Element, len(wire.Inputs)) for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - var api gateAPI - gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*fr.Element)) + var stack elementStack + compiled := wire.Gate.Compiled() + stack.preloadConstants(compiled) + gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,10 +237,11 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var ( - step fr.Element - api gateAPI - ) + var step fr.Element + + // Create gate evaluator once per thread + compiled := wire.Gate.Compiled() + evaluator := newGateEvaluator(compiled) res := make([]fr.Element, degGJ) @@ -249,7 +251,6 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ... // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]fr.Element, degGJ*len(ml)) - gateInput := make([]frontend.Variable, nbGateIn) for h := start; h < end; h++ { // h counts across instances @@ -266,14 +267,14 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { - for i := range gateInput { - gateInput[i] = &mlEvals[eIndex+1+i] + // Push gate inputs + for i := 0; i < nbGateIn; i++ { + evaluator.pushInput(&mlEvals[eIndex+1+i]) } - summand := wire.Gate.Evaluate(&api, gateInput...).(*fr.Element) + summand := evaluator.evaluate() summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) - api.freeElements() } } mu.Lock() @@ -672,15 +673,35 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } - var api gateAPI + var stack elementStack ins := make([]fr.Element, maxNbIns) + insPtr := make([]*fr.Element, maxNbIns) + + // Track current gate to know when to reload constants + var currentCompiled *gkrtypes.CompiledGate + var nbConsts int + for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { + compiled := w.Gate.Compiled() + + // Preload constants if this is a new gate + if compiled != currentCompiled { + stack.freeElements() + stack.preloadConstants(compiled) + currentCompiled = compiled + nbConsts = compiled.NbConstants() + } + for inI, in := range w.Inputs { ins[inI] = a[in][i] + insPtr[inI] = &ins[inI] } - a[wI][i].Set(api.evaluate(w.Gate.Evaluate, ins[:len(w.Inputs)]...)) + a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) + + // Shrink stack back to just constants for reuse + stack.shrinkToConstants(nbConsts) } } } @@ -728,70 +749,96 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// gateAPI implements gkr.GateAPI. -// It uses a synchronous memory pool underneath to minimize heap allocations. -type gateAPI struct { +// elementStack implements a synchronous memory pool underneath to minimize heap allocations. +type elementStack struct { allocated []*fr.Element nbUsed int } -func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Add(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Add(res, api.cast(v)) +// preloadConstants loads gate constants into the stack at indices [0, nbConsts). +// This should be called once per gate to prepare the stack for repeated use. +func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { + for _, constVal := range gate.Constants { + elem := stack.newElement() + elem.SetBigInt(constVal) + } +} + +// shrinkToConstants resets the stack to contain only constants, ready for next invocation. +func (stack *elementStack) truncate(n int) { + stack.nbUsed = n +} + +func (stack *elementStack) newElement() *fr.Element { + stack.nbUsed++ + if stack.nbUsed >= len(stack.allocated) { + stack.allocated = append(stack.allocated, new(fr.Element)) + } + return stack.allocated[api.nbUsed-1] +} + +func (stack *elementStack) push(x *fr.Element) { + stack.newElement().Set(x) +} + +func opAdd(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Add(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Add(res, x[i]) } return res } -func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - prod := api.newElement() - prod.Mul(api.cast(b), api.cast(c)) - res := api.cast(a) - res.Add(res, prod) +func opMulAcc(stack *elementStack, x []*fr.Element) *fr.Element { + // result = x[0] + (x[1] * x[2]) + prod := stack.newElement() + prod.Mul(x[1], x[2]) + res := stack.newElement() + res.Add(x[0], prod) return res } -func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - res := api.newElement() - res.Neg(api.cast(i1)) +func opNeg(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Neg(x[0]) return res } -func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Sub(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Sub(res, api.cast(v)) +func opSub(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Sub(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Sub(res, x[i]) } return res } -func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Mul(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Mul(res, api.cast(v)) +func opMul(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Mul(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Mul(res, x[i]) } return res } -func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - var x fr.Element - - x.Add(api.cast(a), api.cast(b)) - x.Add(&x, api.cast(c)) +func opSumExp17(stack *elementStack, x []*fr.Element) *fr.Element { + // result = (x[0] + x[1] + x[2])^17 + sum := stack.newElement() + sum.Add(x[0], x[1]) + sum.Add(sum, x[2]) - res := api.newElement() - - res.Mul(&x, &x) // x² - res.Mul(res, res) // x⁴ - res.Mul(res, res) // x⁸ - res.Mul(res, res) // x¹⁶ - return res.Mul(res, &x) // x¹⁷ + res := stack.newElement() + res.Mul(sum, sum) // sum² + res.Mul(res, res) // sum⁴ + res.Mul(res, res) // sum⁸ + res.Mul(res, res) // sum¹⁶ + res.Mul(res, sum) // sum¹⁷ + return res } -func (api *gateAPI) Println(a ...frontend.Variable) { +func (api *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -809,43 +856,103 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { - inVar := make([]frontend.Variable, len(in)) - for i := range in { - inVar[i] = &in[i] +// executeCompiledGate executes a compiled gate using curve-specific field operations. +// The stack is expected to already contain constants at indices [0, nbConsts). +// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. +// After execution, the stack can be shrunk back to nbConsts for reuse. +func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*fr.Element) *fr.Element { + + // Instruction executor functions + executors := [...]func(*elementStack, []*fr.Element) *fr.Element{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, } - return f(api, inVar...).(*fr.Element) -} -// Put all elements back in the pool. -func (api *gateAPI) freeElements() { - api.nbUsed = 0 -} + nbConsts := gate.NbConstants() -func (api *gateAPI) newElement() *fr.Element { - api.nbUsed++ - if api.nbUsed >= len(api.allocated) { - api.allocated = append(api.allocated, new(fr.Element)) + // If no instructions, this is an identity-like gate; return first input + if len(gate.Instructions) == 0 { + if gate.NbInputs == 0 { + panic("gate has no inputs and no instructions") + } + return inputs[0] } - return api.allocated[api.nbUsed-1] -} -type gateFunctionFr func(...fr.Element) *fr.Element + // Append inputs to stack after constants + // The stack now contains: [constants | inputs] + for _, input := range inputs { + ptr := stack.newElement() + ptr.Set(input) + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range gate.Instructions { + inst := &gate.Instructions[i] + executor := executors[inst.Op] + if executor == nil { + panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) + } + + // Resolve operands from stack using instruction indices + operands := make([]*fr.Element, len(inst.Inputs)) + for j, idx := range inst.Inputs { + operands[j] = stack.allocated[idx] + } -// convertFunc turns f into a function that accepts and returns fr.Element. -func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { - return func(in ...fr.Element) *fr.Element { - return api.evaluate(f, in...) + _ = executor(stack, operands) + // Result is automatically appended to stack by executor } + + // Last value in stack is the result + resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 + return stack.allocated[resultIdx] +} + +// gateEvaluator provides a high-level API for evaluating compiled gates efficiently. +// It manages the stack internally and handles input buffering, making it easy to +// evaluate the same gate multiple times with different inputs. +type gateEvaluator struct { + gate *gkrtypes.CompiledGate + stack elementStack + nbConsts int } -func (api *gateAPI) cast(v frontend.Variable) *fr.Element { - if x, ok := v.(*fr.Element); ok { // fast path, no extra heap allocation - return x +// newGateEvaluator creates an evaluator for the given compiled gate. +// The stack is preloaded with constants and ready for evaluation. +func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { + eval := &gateEvaluator{ + gate: gate, + nbConsts: gate.NbConstants(), + inputs: make([]*fr.Element, 0, gate.NbInputs), } - x := api.newElement() - if _, err := x.SetInterface(v); err != nil { - panic(err) + eval.stack.preloadConstants(gate) + return eval +} + +// pushInput adds an input to the evaluator's input buffer. +// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +func (e *gateEvaluator) pushInput(input *fr.Element) { + e.inputs = append(e.inputs, input) +} + +// evaluate executes the gate with the current inputs and returns the result. +// The stack and input buffer are automatically reset after evaluation, +// making the evaluator ready for the next evaluation. +func (e *gateEvaluator) evaluate() *fr.Element { + if len(e.inputs) != e.gate.NbInputs { + panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) } - return x + + result := executeCompiledGate(e.gate, &e.stack, e.inputs) + + // Reset for next evaluation + e.stack.shrinkToConstants(e.nbConsts) + e.inputs = e.inputs[:0] // Clear input buffer + + return result } diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index b270dfe45d..95bf686e8e 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" @@ -141,13 +140,13 @@ func SolveHint(data []SolvingData) hint.Hint { data := data[ins[0].Uint64()] instanceI := ins[1].Uint64() - // create buffer for every gate input. It will be reused for every evaluation. - gateIns := make([]frontend.Variable, data.maxNbIn) - // indices for reading inputs and outputs outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. + insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + + // Track current gate evaluator to reuse across wires with the same gate + var currentEvaluator *gateEvaluator + var currentCompiled *gkrtypes.CompiledGate // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -159,14 +158,22 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - // assemble input for gate - for i, inWI := range w.Inputs { - gateIns[i] = &data.assignment[inWI][instanceI] + compiled := w.Gate.Compiled() + + // Create new evaluator if this is a different gate + if compiled != currentCompiled { + currentEvaluator = newGateEvaluator(compiled) + currentCompiled = compiled + } + + // Push gate inputs + for _, inWI := range w.Inputs { + currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) } - // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) - api.freeElements() + // Evaluate the gate + result := currentEvaluator.evaluate() + data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bls24-315/gate_testing.go b/internal/gkr/bls24-315/gate_testing.go index 689e9ea757..730d6e4d2c 100644 --- a/internal/gkr/bls24-315/gate_testing.go +++ b/internal/gkr/bls24-315/gate_testing.go @@ -19,10 +19,55 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) +// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. +// The arena is pre-loaded with constants and reused across all evaluations. +type compiledGateTester struct { + compiled *gkrtypes.CompiledGate + arena elementArena + nbConsts int + inputBuf []*fr.Element // reusable buffer for input pointers +} + +// newCompiledGateTester compiles a gate function and prepares it for testing. +func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { + compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) + + tester := &compiledGateTester{ + compiled: compiled, + nbConsts: compiled.NbConstants(), + inputBuf: make([]*fr.Element, nbIn), + } + + // Preload constants into arena once + tester.arena.preloadConstants(compiled) + + return tester +} + +// eval evaluates the gate on the given inputs. +// The arena is automatically reset after each call. +// Returns a copy of the result to avoid arena aliasing issues. +func (t *compiledGateTester) eval(in ...fr.Element) fr.Element { + // Use input buffer to avoid allocation + for i := range in { + t.inputBuf[i] = &in[i] + } + + resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) + + // Make a copy before resetting the arena + var result fr.Element + result.Set(resultPtr) + + // Reset arena to just constants for next invocation + t.arena.shrinkToConstants(t.nbConsts) + + return result +} + // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - var api gateAPI - fWrapped := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable @@ -32,20 +77,20 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := fWrapped(in...) + y0 := tester.eval(in...) x[i] = x0 copy(in, x) - y1 := fWrapped(in...) + y1 := tester.eval(in...) x[i].Double(&x[i]) copy(in, x) - y2 := fWrapped(in...) + y2 := tester.eval(in...) - y2.Sub(y2, y1) - y1.Sub(y1, y0) + y2.Sub(&y2, &y1) + y1.Sub(&y1, &y0) - if !y2.Equal(y1) { + if !y2.Equal(&y1) { return false // not linear } @@ -58,21 +103,23 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = fWrapped(in...) + y0 = tester.eval(in...) x[i] = x0 copy(in, x) - y1 = fWrapped(in...) + y1 = tester.eval(in...) - y1.Sub(y1, y0) + y1.Sub(&y1, &y0) - return y1.Equal(y2) + return y1.Equal(&y2) } -// fitPoly tries to fit a polynomial of degree less than degreeBound to f. +// fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynomial { +func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { + nbIn := t.compiled.NbInputs + // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants fIn := make([]fr.Element, nbIn) @@ -88,7 +135,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - p[i].Set(f(fIn...)) + result := t.eval(fIn...) + p[i].Set(&result) x.Mul(&x, &domain.Generator) } @@ -103,8 +151,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := f(fIn...) - if !pAt.Equal(fAt) { + fAt := t.eval(fIn...) + if !pAt.Equal(&fAt) { return nil } @@ -119,25 +167,22 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - var api gateAPI - fFr := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := fFr.fitPoly(nbIn, degreeBound); p != nil { + if p := tester.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } return len(p) - 1, nil } - api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - var api gateAPI - fFr := api.convertFunc(f) - if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { + tester := newCompiledGateTester(f, nbIn) + if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -149,12 +194,11 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - var api gateAPI x := make(fr.Vector, nbIn) x.MustSetRandom() - fFr := api.convertFunc(f) - gFr := api.convertFunc(g) - fAt := fFr(x...) - gAt := gFr(x...) - return fAt.Equal(gAt) + fTester := newCompiledGateTester(f, nbIn) + gTester := newCompiledGateTester(g, nbIn) + fAt := fTester.eval(x...) + gAt := gTester.eval(x...) + return fAt.Equal(&gAt) } diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index c265b200e2..520e8acfec 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -18,7 +18,6 @@ import ( "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit @@ -100,12 +99,14 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]frontend.Variable, len(wire.Inputs)) + inputEvaluations := make([]*fr.Element, len(wire.Inputs)) for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - var api gateAPI - gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*fr.Element)) + var stack elementStack + compiled := wire.Gate.Compiled() + stack.preloadConstants(compiled) + gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,10 +237,11 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var ( - step fr.Element - api gateAPI - ) + var step fr.Element + + // Create gate evaluator once per thread + compiled := wire.Gate.Compiled() + evaluator := newGateEvaluator(compiled) res := make([]fr.Element, degGJ) @@ -249,7 +251,6 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ... // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]fr.Element, degGJ*len(ml)) - gateInput := make([]frontend.Variable, nbGateIn) for h := start; h < end; h++ { // h counts across instances @@ -266,14 +267,14 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { - for i := range gateInput { - gateInput[i] = &mlEvals[eIndex+1+i] + // Push gate inputs + for i := 0; i < nbGateIn; i++ { + evaluator.pushInput(&mlEvals[eIndex+1+i]) } - summand := wire.Gate.Evaluate(&api, gateInput...).(*fr.Element) + summand := evaluator.evaluate() summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) - api.freeElements() } } mu.Lock() @@ -672,15 +673,35 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } - var api gateAPI + var stack elementStack ins := make([]fr.Element, maxNbIns) + insPtr := make([]*fr.Element, maxNbIns) + + // Track current gate to know when to reload constants + var currentCompiled *gkrtypes.CompiledGate + var nbConsts int + for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { + compiled := w.Gate.Compiled() + + // Preload constants if this is a new gate + if compiled != currentCompiled { + stack.freeElements() + stack.preloadConstants(compiled) + currentCompiled = compiled + nbConsts = compiled.NbConstants() + } + for inI, in := range w.Inputs { ins[inI] = a[in][i] + insPtr[inI] = &ins[inI] } - a[wI][i].Set(api.evaluate(w.Gate.Evaluate, ins[:len(w.Inputs)]...)) + a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) + + // Shrink stack back to just constants for reuse + stack.shrinkToConstants(nbConsts) } } } @@ -728,70 +749,96 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// gateAPI implements gkr.GateAPI. -// It uses a synchronous memory pool underneath to minimize heap allocations. -type gateAPI struct { +// elementStack implements a synchronous memory pool underneath to minimize heap allocations. +type elementStack struct { allocated []*fr.Element nbUsed int } -func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Add(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Add(res, api.cast(v)) +// preloadConstants loads gate constants into the stack at indices [0, nbConsts). +// This should be called once per gate to prepare the stack for repeated use. +func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { + for _, constVal := range gate.Constants { + elem := stack.newElement() + elem.SetBigInt(constVal) + } +} + +// shrinkToConstants resets the stack to contain only constants, ready for next invocation. +func (stack *elementStack) truncate(n int) { + stack.nbUsed = n +} + +func (stack *elementStack) newElement() *fr.Element { + stack.nbUsed++ + if stack.nbUsed >= len(stack.allocated) { + stack.allocated = append(stack.allocated, new(fr.Element)) + } + return stack.allocated[api.nbUsed-1] +} + +func (stack *elementStack) push(x *fr.Element) { + stack.newElement().Set(x) +} + +func opAdd(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Add(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Add(res, x[i]) } return res } -func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - prod := api.newElement() - prod.Mul(api.cast(b), api.cast(c)) - res := api.cast(a) - res.Add(res, prod) +func opMulAcc(stack *elementStack, x []*fr.Element) *fr.Element { + // result = x[0] + (x[1] * x[2]) + prod := stack.newElement() + prod.Mul(x[1], x[2]) + res := stack.newElement() + res.Add(x[0], prod) return res } -func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - res := api.newElement() - res.Neg(api.cast(i1)) +func opNeg(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Neg(x[0]) return res } -func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Sub(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Sub(res, api.cast(v)) +func opSub(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Sub(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Sub(res, x[i]) } return res } -func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Mul(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Mul(res, api.cast(v)) +func opMul(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Mul(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Mul(res, x[i]) } return res } -func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - var x fr.Element - - x.Add(api.cast(a), api.cast(b)) - x.Add(&x, api.cast(c)) +func opSumExp17(stack *elementStack, x []*fr.Element) *fr.Element { + // result = (x[0] + x[1] + x[2])^17 + sum := stack.newElement() + sum.Add(x[0], x[1]) + sum.Add(sum, x[2]) - res := api.newElement() - - res.Mul(&x, &x) // x² - res.Mul(res, res) // x⁴ - res.Mul(res, res) // x⁸ - res.Mul(res, res) // x¹⁶ - return res.Mul(res, &x) // x¹⁷ + res := stack.newElement() + res.Mul(sum, sum) // sum² + res.Mul(res, res) // sum⁴ + res.Mul(res, res) // sum⁸ + res.Mul(res, res) // sum¹⁶ + res.Mul(res, sum) // sum¹⁷ + return res } -func (api *gateAPI) Println(a ...frontend.Variable) { +func (api *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -809,43 +856,103 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { - inVar := make([]frontend.Variable, len(in)) - for i := range in { - inVar[i] = &in[i] +// executeCompiledGate executes a compiled gate using curve-specific field operations. +// The stack is expected to already contain constants at indices [0, nbConsts). +// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. +// After execution, the stack can be shrunk back to nbConsts for reuse. +func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*fr.Element) *fr.Element { + + // Instruction executor functions + executors := [...]func(*elementStack, []*fr.Element) *fr.Element{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, } - return f(api, inVar...).(*fr.Element) -} -// Put all elements back in the pool. -func (api *gateAPI) freeElements() { - api.nbUsed = 0 -} + nbConsts := gate.NbConstants() -func (api *gateAPI) newElement() *fr.Element { - api.nbUsed++ - if api.nbUsed >= len(api.allocated) { - api.allocated = append(api.allocated, new(fr.Element)) + // If no instructions, this is an identity-like gate; return first input + if len(gate.Instructions) == 0 { + if gate.NbInputs == 0 { + panic("gate has no inputs and no instructions") + } + return inputs[0] } - return api.allocated[api.nbUsed-1] -} -type gateFunctionFr func(...fr.Element) *fr.Element + // Append inputs to stack after constants + // The stack now contains: [constants | inputs] + for _, input := range inputs { + ptr := stack.newElement() + ptr.Set(input) + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range gate.Instructions { + inst := &gate.Instructions[i] + executor := executors[inst.Op] + if executor == nil { + panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) + } + + // Resolve operands from stack using instruction indices + operands := make([]*fr.Element, len(inst.Inputs)) + for j, idx := range inst.Inputs { + operands[j] = stack.allocated[idx] + } -// convertFunc turns f into a function that accepts and returns fr.Element. -func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { - return func(in ...fr.Element) *fr.Element { - return api.evaluate(f, in...) + _ = executor(stack, operands) + // Result is automatically appended to stack by executor } + + // Last value in stack is the result + resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 + return stack.allocated[resultIdx] +} + +// gateEvaluator provides a high-level API for evaluating compiled gates efficiently. +// It manages the stack internally and handles input buffering, making it easy to +// evaluate the same gate multiple times with different inputs. +type gateEvaluator struct { + gate *gkrtypes.CompiledGate + stack elementStack + nbConsts int } -func (api *gateAPI) cast(v frontend.Variable) *fr.Element { - if x, ok := v.(*fr.Element); ok { // fast path, no extra heap allocation - return x +// newGateEvaluator creates an evaluator for the given compiled gate. +// The stack is preloaded with constants and ready for evaluation. +func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { + eval := &gateEvaluator{ + gate: gate, + nbConsts: gate.NbConstants(), + inputs: make([]*fr.Element, 0, gate.NbInputs), } - x := api.newElement() - if _, err := x.SetInterface(v); err != nil { - panic(err) + eval.stack.preloadConstants(gate) + return eval +} + +// pushInput adds an input to the evaluator's input buffer. +// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +func (e *gateEvaluator) pushInput(input *fr.Element) { + e.inputs = append(e.inputs, input) +} + +// evaluate executes the gate with the current inputs and returns the result. +// The stack and input buffer are automatically reset after evaluation, +// making the evaluator ready for the next evaluation. +func (e *gateEvaluator) evaluate() *fr.Element { + if len(e.inputs) != e.gate.NbInputs { + panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) } - return x + + result := executeCompiledGate(e.gate, &e.stack, e.inputs) + + // Reset for next evaluation + e.stack.shrinkToConstants(e.nbConsts) + e.inputs = e.inputs[:0] // Clear input buffer + + return result } diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index 3a9b7eda63..c03ed0030f 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" @@ -141,13 +140,13 @@ func SolveHint(data []SolvingData) hint.Hint { data := data[ins[0].Uint64()] instanceI := ins[1].Uint64() - // create buffer for every gate input. It will be reused for every evaluation. - gateIns := make([]frontend.Variable, data.maxNbIn) - // indices for reading inputs and outputs outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. + insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + + // Track current gate evaluator to reuse across wires with the same gate + var currentEvaluator *gateEvaluator + var currentCompiled *gkrtypes.CompiledGate // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -159,14 +158,22 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - // assemble input for gate - for i, inWI := range w.Inputs { - gateIns[i] = &data.assignment[inWI][instanceI] + compiled := w.Gate.Compiled() + + // Create new evaluator if this is a different gate + if compiled != currentCompiled { + currentEvaluator = newGateEvaluator(compiled) + currentCompiled = compiled + } + + // Push gate inputs + for _, inWI := range w.Inputs { + currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) } - // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) - api.freeElements() + // Evaluate the gate + result := currentEvaluator.evaluate() + data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bls24-317/gate_testing.go b/internal/gkr/bls24-317/gate_testing.go index 3ada9b16bd..37e332bd44 100644 --- a/internal/gkr/bls24-317/gate_testing.go +++ b/internal/gkr/bls24-317/gate_testing.go @@ -19,10 +19,55 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) +// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. +// The arena is pre-loaded with constants and reused across all evaluations. +type compiledGateTester struct { + compiled *gkrtypes.CompiledGate + arena elementArena + nbConsts int + inputBuf []*fr.Element // reusable buffer for input pointers +} + +// newCompiledGateTester compiles a gate function and prepares it for testing. +func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { + compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) + + tester := &compiledGateTester{ + compiled: compiled, + nbConsts: compiled.NbConstants(), + inputBuf: make([]*fr.Element, nbIn), + } + + // Preload constants into arena once + tester.arena.preloadConstants(compiled) + + return tester +} + +// eval evaluates the gate on the given inputs. +// The arena is automatically reset after each call. +// Returns a copy of the result to avoid arena aliasing issues. +func (t *compiledGateTester) eval(in ...fr.Element) fr.Element { + // Use input buffer to avoid allocation + for i := range in { + t.inputBuf[i] = &in[i] + } + + resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) + + // Make a copy before resetting the arena + var result fr.Element + result.Set(resultPtr) + + // Reset arena to just constants for next invocation + t.arena.shrinkToConstants(t.nbConsts) + + return result +} + // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - var api gateAPI - fWrapped := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable @@ -32,20 +77,20 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := fWrapped(in...) + y0 := tester.eval(in...) x[i] = x0 copy(in, x) - y1 := fWrapped(in...) + y1 := tester.eval(in...) x[i].Double(&x[i]) copy(in, x) - y2 := fWrapped(in...) + y2 := tester.eval(in...) - y2.Sub(y2, y1) - y1.Sub(y1, y0) + y2.Sub(&y2, &y1) + y1.Sub(&y1, &y0) - if !y2.Equal(y1) { + if !y2.Equal(&y1) { return false // not linear } @@ -58,21 +103,23 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = fWrapped(in...) + y0 = tester.eval(in...) x[i] = x0 copy(in, x) - y1 = fWrapped(in...) + y1 = tester.eval(in...) - y1.Sub(y1, y0) + y1.Sub(&y1, &y0) - return y1.Equal(y2) + return y1.Equal(&y2) } -// fitPoly tries to fit a polynomial of degree less than degreeBound to f. +// fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynomial { +func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { + nbIn := t.compiled.NbInputs + // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants fIn := make([]fr.Element, nbIn) @@ -88,7 +135,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - p[i].Set(f(fIn...)) + result := t.eval(fIn...) + p[i].Set(&result) x.Mul(&x, &domain.Generator) } @@ -103,8 +151,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := f(fIn...) - if !pAt.Equal(fAt) { + fAt := t.eval(fIn...) + if !pAt.Equal(&fAt) { return nil } @@ -119,25 +167,22 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - var api gateAPI - fFr := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := fFr.fitPoly(nbIn, degreeBound); p != nil { + if p := tester.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } return len(p) - 1, nil } - api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - var api gateAPI - fFr := api.convertFunc(f) - if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { + tester := newCompiledGateTester(f, nbIn) + if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -149,12 +194,11 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - var api gateAPI x := make(fr.Vector, nbIn) x.MustSetRandom() - fFr := api.convertFunc(f) - gFr := api.convertFunc(g) - fAt := fFr(x...) - gAt := gFr(x...) - return fAt.Equal(gAt) + fTester := newCompiledGateTester(f, nbIn) + gTester := newCompiledGateTester(g, nbIn) + fAt := fTester.eval(x...) + gAt := gTester.eval(x...) + return fAt.Equal(&gAt) } diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 334f8df7f0..b3952244bb 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -18,7 +18,6 @@ import ( "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit @@ -100,12 +99,14 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]frontend.Variable, len(wire.Inputs)) + inputEvaluations := make([]*fr.Element, len(wire.Inputs)) for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - var api gateAPI - gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*fr.Element)) + var stack elementStack + compiled := wire.Gate.Compiled() + stack.preloadConstants(compiled) + gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,10 +237,11 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var ( - step fr.Element - api gateAPI - ) + var step fr.Element + + // Create gate evaluator once per thread + compiled := wire.Gate.Compiled() + evaluator := newGateEvaluator(compiled) res := make([]fr.Element, degGJ) @@ -249,7 +251,6 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ... // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]fr.Element, degGJ*len(ml)) - gateInput := make([]frontend.Variable, nbGateIn) for h := start; h < end; h++ { // h counts across instances @@ -266,14 +267,14 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { - for i := range gateInput { - gateInput[i] = &mlEvals[eIndex+1+i] + // Push gate inputs + for i := 0; i < nbGateIn; i++ { + evaluator.pushInput(&mlEvals[eIndex+1+i]) } - summand := wire.Gate.Evaluate(&api, gateInput...).(*fr.Element) + summand := evaluator.evaluate() summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) - api.freeElements() } } mu.Lock() @@ -672,15 +673,35 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } - var api gateAPI + var stack elementStack ins := make([]fr.Element, maxNbIns) + insPtr := make([]*fr.Element, maxNbIns) + + // Track current gate to know when to reload constants + var currentCompiled *gkrtypes.CompiledGate + var nbConsts int + for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { + compiled := w.Gate.Compiled() + + // Preload constants if this is a new gate + if compiled != currentCompiled { + stack.freeElements() + stack.preloadConstants(compiled) + currentCompiled = compiled + nbConsts = compiled.NbConstants() + } + for inI, in := range w.Inputs { ins[inI] = a[in][i] + insPtr[inI] = &ins[inI] } - a[wI][i].Set(api.evaluate(w.Gate.Evaluate, ins[:len(w.Inputs)]...)) + a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) + + // Shrink stack back to just constants for reuse + stack.shrinkToConstants(nbConsts) } } } @@ -728,70 +749,96 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// gateAPI implements gkr.GateAPI. -// It uses a synchronous memory pool underneath to minimize heap allocations. -type gateAPI struct { +// elementStack implements a synchronous memory pool underneath to minimize heap allocations. +type elementStack struct { allocated []*fr.Element nbUsed int } -func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Add(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Add(res, api.cast(v)) +// preloadConstants loads gate constants into the stack at indices [0, nbConsts). +// This should be called once per gate to prepare the stack for repeated use. +func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { + for _, constVal := range gate.Constants { + elem := stack.newElement() + elem.SetBigInt(constVal) + } +} + +// shrinkToConstants resets the stack to contain only constants, ready for next invocation. +func (stack *elementStack) truncate(n int) { + stack.nbUsed = n +} + +func (stack *elementStack) newElement() *fr.Element { + stack.nbUsed++ + if stack.nbUsed >= len(stack.allocated) { + stack.allocated = append(stack.allocated, new(fr.Element)) + } + return stack.allocated[api.nbUsed-1] +} + +func (stack *elementStack) push(x *fr.Element) { + stack.newElement().Set(x) +} + +func opAdd(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Add(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Add(res, x[i]) } return res } -func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - prod := api.newElement() - prod.Mul(api.cast(b), api.cast(c)) - res := api.cast(a) - res.Add(res, prod) +func opMulAcc(stack *elementStack, x []*fr.Element) *fr.Element { + // result = x[0] + (x[1] * x[2]) + prod := stack.newElement() + prod.Mul(x[1], x[2]) + res := stack.newElement() + res.Add(x[0], prod) return res } -func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - res := api.newElement() - res.Neg(api.cast(i1)) +func opNeg(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Neg(x[0]) return res } -func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Sub(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Sub(res, api.cast(v)) +func opSub(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Sub(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Sub(res, x[i]) } return res } -func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Mul(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Mul(res, api.cast(v)) +func opMul(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Mul(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Mul(res, x[i]) } return res } -func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - var x fr.Element - - x.Add(api.cast(a), api.cast(b)) - x.Add(&x, api.cast(c)) +func opSumExp17(stack *elementStack, x []*fr.Element) *fr.Element { + // result = (x[0] + x[1] + x[2])^17 + sum := stack.newElement() + sum.Add(x[0], x[1]) + sum.Add(sum, x[2]) - res := api.newElement() - - res.Mul(&x, &x) // x² - res.Mul(res, res) // x⁴ - res.Mul(res, res) // x⁸ - res.Mul(res, res) // x¹⁶ - return res.Mul(res, &x) // x¹⁷ + res := stack.newElement() + res.Mul(sum, sum) // sum² + res.Mul(res, res) // sum⁴ + res.Mul(res, res) // sum⁸ + res.Mul(res, res) // sum¹⁶ + res.Mul(res, sum) // sum¹⁷ + return res } -func (api *gateAPI) Println(a ...frontend.Variable) { +func (api *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -809,43 +856,103 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { - inVar := make([]frontend.Variable, len(in)) - for i := range in { - inVar[i] = &in[i] +// executeCompiledGate executes a compiled gate using curve-specific field operations. +// The stack is expected to already contain constants at indices [0, nbConsts). +// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. +// After execution, the stack can be shrunk back to nbConsts for reuse. +func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*fr.Element) *fr.Element { + + // Instruction executor functions + executors := [...]func(*elementStack, []*fr.Element) *fr.Element{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, } - return f(api, inVar...).(*fr.Element) -} -// Put all elements back in the pool. -func (api *gateAPI) freeElements() { - api.nbUsed = 0 -} + nbConsts := gate.NbConstants() -func (api *gateAPI) newElement() *fr.Element { - api.nbUsed++ - if api.nbUsed >= len(api.allocated) { - api.allocated = append(api.allocated, new(fr.Element)) + // If no instructions, this is an identity-like gate; return first input + if len(gate.Instructions) == 0 { + if gate.NbInputs == 0 { + panic("gate has no inputs and no instructions") + } + return inputs[0] } - return api.allocated[api.nbUsed-1] -} -type gateFunctionFr func(...fr.Element) *fr.Element + // Append inputs to stack after constants + // The stack now contains: [constants | inputs] + for _, input := range inputs { + ptr := stack.newElement() + ptr.Set(input) + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range gate.Instructions { + inst := &gate.Instructions[i] + executor := executors[inst.Op] + if executor == nil { + panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) + } + + // Resolve operands from stack using instruction indices + operands := make([]*fr.Element, len(inst.Inputs)) + for j, idx := range inst.Inputs { + operands[j] = stack.allocated[idx] + } -// convertFunc turns f into a function that accepts and returns fr.Element. -func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { - return func(in ...fr.Element) *fr.Element { - return api.evaluate(f, in...) + _ = executor(stack, operands) + // Result is automatically appended to stack by executor } + + // Last value in stack is the result + resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 + return stack.allocated[resultIdx] +} + +// gateEvaluator provides a high-level API for evaluating compiled gates efficiently. +// It manages the stack internally and handles input buffering, making it easy to +// evaluate the same gate multiple times with different inputs. +type gateEvaluator struct { + gate *gkrtypes.CompiledGate + stack elementStack + nbConsts int } -func (api *gateAPI) cast(v frontend.Variable) *fr.Element { - if x, ok := v.(*fr.Element); ok { // fast path, no extra heap allocation - return x +// newGateEvaluator creates an evaluator for the given compiled gate. +// The stack is preloaded with constants and ready for evaluation. +func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { + eval := &gateEvaluator{ + gate: gate, + nbConsts: gate.NbConstants(), + inputs: make([]*fr.Element, 0, gate.NbInputs), } - x := api.newElement() - if _, err := x.SetInterface(v); err != nil { - panic(err) + eval.stack.preloadConstants(gate) + return eval +} + +// pushInput adds an input to the evaluator's input buffer. +// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +func (e *gateEvaluator) pushInput(input *fr.Element) { + e.inputs = append(e.inputs, input) +} + +// evaluate executes the gate with the current inputs and returns the result. +// The stack and input buffer are automatically reset after evaluation, +// making the evaluator ready for the next evaluation. +func (e *gateEvaluator) evaluate() *fr.Element { + if len(e.inputs) != e.gate.NbInputs { + panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) } - return x + + result := executeCompiledGate(e.gate, &e.stack, e.inputs) + + // Reset for next evaluation + e.stack.shrinkToConstants(e.nbConsts) + e.inputs = e.inputs[:0] // Clear input buffer + + return result } diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index fc18d24659..e0f839d482 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" @@ -141,13 +140,13 @@ func SolveHint(data []SolvingData) hint.Hint { data := data[ins[0].Uint64()] instanceI := ins[1].Uint64() - // create buffer for every gate input. It will be reused for every evaluation. - gateIns := make([]frontend.Variable, data.maxNbIn) - // indices for reading inputs and outputs outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. + insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + + // Track current gate evaluator to reuse across wires with the same gate + var currentEvaluator *gateEvaluator + var currentCompiled *gkrtypes.CompiledGate // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -159,14 +158,22 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - // assemble input for gate - for i, inWI := range w.Inputs { - gateIns[i] = &data.assignment[inWI][instanceI] + compiled := w.Gate.Compiled() + + // Create new evaluator if this is a different gate + if compiled != currentCompiled { + currentEvaluator = newGateEvaluator(compiled) + currentCompiled = compiled + } + + // Push gate inputs + for _, inWI := range w.Inputs { + currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) } - // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) - api.freeElements() + // Evaluate the gate + result := currentEvaluator.evaluate() + data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bn254/gate_testing.go b/internal/gkr/bn254/gate_testing.go index 09b0ac96f4..490ef7cfc2 100644 --- a/internal/gkr/bn254/gate_testing.go +++ b/internal/gkr/bn254/gate_testing.go @@ -19,10 +19,55 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) +// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. +// The arena is pre-loaded with constants and reused across all evaluations. +type compiledGateTester struct { + compiled *gkrtypes.CompiledGate + arena elementArena + nbConsts int + inputBuf []*fr.Element // reusable buffer for input pointers +} + +// newCompiledGateTester compiles a gate function and prepares it for testing. +func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { + compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) + + tester := &compiledGateTester{ + compiled: compiled, + nbConsts: compiled.NbConstants(), + inputBuf: make([]*fr.Element, nbIn), + } + + // Preload constants into arena once + tester.arena.preloadConstants(compiled) + + return tester +} + +// eval evaluates the gate on the given inputs. +// The arena is automatically reset after each call. +// Returns a copy of the result to avoid arena aliasing issues. +func (t *compiledGateTester) eval(in ...fr.Element) fr.Element { + // Use input buffer to avoid allocation + for i := range in { + t.inputBuf[i] = &in[i] + } + + resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) + + // Make a copy before resetting the arena + var result fr.Element + result.Set(resultPtr) + + // Reset arena to just constants for next invocation + t.arena.shrinkToConstants(t.nbConsts) + + return result +} + // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - var api gateAPI - fWrapped := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable @@ -32,20 +77,20 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := fWrapped(in...) + y0 := tester.eval(in...) x[i] = x0 copy(in, x) - y1 := fWrapped(in...) + y1 := tester.eval(in...) x[i].Double(&x[i]) copy(in, x) - y2 := fWrapped(in...) + y2 := tester.eval(in...) - y2.Sub(y2, y1) - y1.Sub(y1, y0) + y2.Sub(&y2, &y1) + y1.Sub(&y1, &y0) - if !y2.Equal(y1) { + if !y2.Equal(&y1) { return false // not linear } @@ -58,21 +103,23 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = fWrapped(in...) + y0 = tester.eval(in...) x[i] = x0 copy(in, x) - y1 = fWrapped(in...) + y1 = tester.eval(in...) - y1.Sub(y1, y0) + y1.Sub(&y1, &y0) - return y1.Equal(y2) + return y1.Equal(&y2) } -// fitPoly tries to fit a polynomial of degree less than degreeBound to f. +// fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynomial { +func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { + nbIn := t.compiled.NbInputs + // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants fIn := make([]fr.Element, nbIn) @@ -88,7 +135,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - p[i].Set(f(fIn...)) + result := t.eval(fIn...) + p[i].Set(&result) x.Mul(&x, &domain.Generator) } @@ -103,8 +151,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := f(fIn...) - if !pAt.Equal(fAt) { + fAt := t.eval(fIn...) + if !pAt.Equal(&fAt) { return nil } @@ -119,25 +167,22 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - var api gateAPI - fFr := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := fFr.fitPoly(nbIn, degreeBound); p != nil { + if p := tester.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } return len(p) - 1, nil } - api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - var api gateAPI - fFr := api.convertFunc(f) - if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { + tester := newCompiledGateTester(f, nbIn) + if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -149,12 +194,11 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - var api gateAPI x := make(fr.Vector, nbIn) x.MustSetRandom() - fFr := api.convertFunc(f) - gFr := api.convertFunc(g) - fAt := fFr(x...) - gAt := gFr(x...) - return fAt.Equal(gAt) + fTester := newCompiledGateTester(f, nbIn) + gTester := newCompiledGateTester(g, nbIn) + fAt := fTester.eval(x...) + gAt := gTester.eval(x...) + return fAt.Equal(&gAt) } diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index d257762ecd..009af57b48 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -18,7 +18,6 @@ import ( "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit @@ -100,12 +99,14 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]frontend.Variable, len(wire.Inputs)) + inputEvaluations := make([]*fr.Element, len(wire.Inputs)) for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - var api gateAPI - gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*fr.Element)) + var stack elementStack + compiled := wire.Gate.Compiled() + stack.preloadConstants(compiled) + gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,10 +237,11 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var ( - step fr.Element - api gateAPI - ) + var step fr.Element + + // Create gate evaluator once per thread + compiled := wire.Gate.Compiled() + evaluator := newGateEvaluator(compiled) res := make([]fr.Element, degGJ) @@ -249,7 +251,6 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ... // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]fr.Element, degGJ*len(ml)) - gateInput := make([]frontend.Variable, nbGateIn) for h := start; h < end; h++ { // h counts across instances @@ -266,14 +267,14 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { - for i := range gateInput { - gateInput[i] = &mlEvals[eIndex+1+i] + // Push gate inputs + for i := 0; i < nbGateIn; i++ { + evaluator.pushInput(&mlEvals[eIndex+1+i]) } - summand := wire.Gate.Evaluate(&api, gateInput...).(*fr.Element) + summand := evaluator.evaluate() summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) - api.freeElements() } } mu.Lock() @@ -672,15 +673,35 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } - var api gateAPI + var stack elementStack ins := make([]fr.Element, maxNbIns) + insPtr := make([]*fr.Element, maxNbIns) + + // Track current gate to know when to reload constants + var currentCompiled *gkrtypes.CompiledGate + var nbConsts int + for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { + compiled := w.Gate.Compiled() + + // Preload constants if this is a new gate + if compiled != currentCompiled { + stack.freeElements() + stack.preloadConstants(compiled) + currentCompiled = compiled + nbConsts = compiled.NbConstants() + } + for inI, in := range w.Inputs { ins[inI] = a[in][i] + insPtr[inI] = &ins[inI] } - a[wI][i].Set(api.evaluate(w.Gate.Evaluate, ins[:len(w.Inputs)]...)) + a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) + + // Shrink stack back to just constants for reuse + stack.shrinkToConstants(nbConsts) } } } @@ -728,70 +749,96 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// gateAPI implements gkr.GateAPI. -// It uses a synchronous memory pool underneath to minimize heap allocations. -type gateAPI struct { +// elementStack implements a synchronous memory pool underneath to minimize heap allocations. +type elementStack struct { allocated []*fr.Element nbUsed int } -func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Add(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Add(res, api.cast(v)) +// preloadConstants loads gate constants into the stack at indices [0, nbConsts). +// This should be called once per gate to prepare the stack for repeated use. +func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { + for _, constVal := range gate.Constants { + elem := stack.newElement() + elem.SetBigInt(constVal) + } +} + +// shrinkToConstants resets the stack to contain only constants, ready for next invocation. +func (stack *elementStack) truncate(n int) { + stack.nbUsed = n +} + +func (stack *elementStack) newElement() *fr.Element { + stack.nbUsed++ + if stack.nbUsed >= len(stack.allocated) { + stack.allocated = append(stack.allocated, new(fr.Element)) + } + return stack.allocated[api.nbUsed-1] +} + +func (stack *elementStack) push(x *fr.Element) { + stack.newElement().Set(x) +} + +func opAdd(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Add(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Add(res, x[i]) } return res } -func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - prod := api.newElement() - prod.Mul(api.cast(b), api.cast(c)) - res := api.cast(a) - res.Add(res, prod) +func opMulAcc(stack *elementStack, x []*fr.Element) *fr.Element { + // result = x[0] + (x[1] * x[2]) + prod := stack.newElement() + prod.Mul(x[1], x[2]) + res := stack.newElement() + res.Add(x[0], prod) return res } -func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - res := api.newElement() - res.Neg(api.cast(i1)) +func opNeg(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Neg(x[0]) return res } -func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Sub(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Sub(res, api.cast(v)) +func opSub(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Sub(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Sub(res, x[i]) } return res } -func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Mul(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Mul(res, api.cast(v)) +func opMul(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Mul(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Mul(res, x[i]) } return res } -func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - var x fr.Element - - x.Add(api.cast(a), api.cast(b)) - x.Add(&x, api.cast(c)) +func opSumExp17(stack *elementStack, x []*fr.Element) *fr.Element { + // result = (x[0] + x[1] + x[2])^17 + sum := stack.newElement() + sum.Add(x[0], x[1]) + sum.Add(sum, x[2]) - res := api.newElement() - - res.Mul(&x, &x) // x² - res.Mul(res, res) // x⁴ - res.Mul(res, res) // x⁸ - res.Mul(res, res) // x¹⁶ - return res.Mul(res, &x) // x¹⁷ + res := stack.newElement() + res.Mul(sum, sum) // sum² + res.Mul(res, res) // sum⁴ + res.Mul(res, res) // sum⁸ + res.Mul(res, res) // sum¹⁶ + res.Mul(res, sum) // sum¹⁷ + return res } -func (api *gateAPI) Println(a ...frontend.Variable) { +func (api *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -809,43 +856,103 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { - inVar := make([]frontend.Variable, len(in)) - for i := range in { - inVar[i] = &in[i] +// executeCompiledGate executes a compiled gate using curve-specific field operations. +// The stack is expected to already contain constants at indices [0, nbConsts). +// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. +// After execution, the stack can be shrunk back to nbConsts for reuse. +func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*fr.Element) *fr.Element { + + // Instruction executor functions + executors := [...]func(*elementStack, []*fr.Element) *fr.Element{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, } - return f(api, inVar...).(*fr.Element) -} -// Put all elements back in the pool. -func (api *gateAPI) freeElements() { - api.nbUsed = 0 -} + nbConsts := gate.NbConstants() -func (api *gateAPI) newElement() *fr.Element { - api.nbUsed++ - if api.nbUsed >= len(api.allocated) { - api.allocated = append(api.allocated, new(fr.Element)) + // If no instructions, this is an identity-like gate; return first input + if len(gate.Instructions) == 0 { + if gate.NbInputs == 0 { + panic("gate has no inputs and no instructions") + } + return inputs[0] } - return api.allocated[api.nbUsed-1] -} -type gateFunctionFr func(...fr.Element) *fr.Element + // Append inputs to stack after constants + // The stack now contains: [constants | inputs] + for _, input := range inputs { + ptr := stack.newElement() + ptr.Set(input) + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range gate.Instructions { + inst := &gate.Instructions[i] + executor := executors[inst.Op] + if executor == nil { + panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) + } + + // Resolve operands from stack using instruction indices + operands := make([]*fr.Element, len(inst.Inputs)) + for j, idx := range inst.Inputs { + operands[j] = stack.allocated[idx] + } -// convertFunc turns f into a function that accepts and returns fr.Element. -func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { - return func(in ...fr.Element) *fr.Element { - return api.evaluate(f, in...) + _ = executor(stack, operands) + // Result is automatically appended to stack by executor } + + // Last value in stack is the result + resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 + return stack.allocated[resultIdx] +} + +// gateEvaluator provides a high-level API for evaluating compiled gates efficiently. +// It manages the stack internally and handles input buffering, making it easy to +// evaluate the same gate multiple times with different inputs. +type gateEvaluator struct { + gate *gkrtypes.CompiledGate + stack elementStack + nbConsts int } -func (api *gateAPI) cast(v frontend.Variable) *fr.Element { - if x, ok := v.(*fr.Element); ok { // fast path, no extra heap allocation - return x +// newGateEvaluator creates an evaluator for the given compiled gate. +// The stack is preloaded with constants and ready for evaluation. +func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { + eval := &gateEvaluator{ + gate: gate, + nbConsts: gate.NbConstants(), + inputs: make([]*fr.Element, 0, gate.NbInputs), } - x := api.newElement() - if _, err := x.SetInterface(v); err != nil { - panic(err) + eval.stack.preloadConstants(gate) + return eval +} + +// pushInput adds an input to the evaluator's input buffer. +// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +func (e *gateEvaluator) pushInput(input *fr.Element) { + e.inputs = append(e.inputs, input) +} + +// evaluate executes the gate with the current inputs and returns the result. +// The stack and input buffer are automatically reset after evaluation, +// making the evaluator ready for the next evaluation. +func (e *gateEvaluator) evaluate() *fr.Element { + if len(e.inputs) != e.gate.NbInputs { + panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) } - return x + + result := executeCompiledGate(e.gate, &e.stack, e.inputs) + + // Reset for next evaluation + e.stack.shrinkToConstants(e.nbConsts) + e.inputs = e.inputs[:0] // Clear input buffer + + return result } diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index 01b14882e7..161b41852f 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bn254/fr" @@ -141,13 +140,13 @@ func SolveHint(data []SolvingData) hint.Hint { data := data[ins[0].Uint64()] instanceI := ins[1].Uint64() - // create buffer for every gate input. It will be reused for every evaluation. - gateIns := make([]frontend.Variable, data.maxNbIn) - // indices for reading inputs and outputs outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. + insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + + // Track current gate evaluator to reuse across wires with the same gate + var currentEvaluator *gateEvaluator + var currentCompiled *gkrtypes.CompiledGate // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -159,14 +158,22 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - // assemble input for gate - for i, inWI := range w.Inputs { - gateIns[i] = &data.assignment[inWI][instanceI] + compiled := w.Gate.Compiled() + + // Create new evaluator if this is a different gate + if compiled != currentCompiled { + currentEvaluator = newGateEvaluator(compiled) + currentCompiled = compiled + } + + // Push gate inputs + for _, inWI := range w.Inputs { + currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) } - // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) - api.freeElements() + // Evaluate the gate + result := currentEvaluator.evaluate() + data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bw6-633/gate_testing.go b/internal/gkr/bw6-633/gate_testing.go index 5c7e7eca61..ea83391f9d 100644 --- a/internal/gkr/bw6-633/gate_testing.go +++ b/internal/gkr/bw6-633/gate_testing.go @@ -19,10 +19,55 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) +// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. +// The arena is pre-loaded with constants and reused across all evaluations. +type compiledGateTester struct { + compiled *gkrtypes.CompiledGate + arena elementArena + nbConsts int + inputBuf []*fr.Element // reusable buffer for input pointers +} + +// newCompiledGateTester compiles a gate function and prepares it for testing. +func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { + compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) + + tester := &compiledGateTester{ + compiled: compiled, + nbConsts: compiled.NbConstants(), + inputBuf: make([]*fr.Element, nbIn), + } + + // Preload constants into arena once + tester.arena.preloadConstants(compiled) + + return tester +} + +// eval evaluates the gate on the given inputs. +// The arena is automatically reset after each call. +// Returns a copy of the result to avoid arena aliasing issues. +func (t *compiledGateTester) eval(in ...fr.Element) fr.Element { + // Use input buffer to avoid allocation + for i := range in { + t.inputBuf[i] = &in[i] + } + + resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) + + // Make a copy before resetting the arena + var result fr.Element + result.Set(resultPtr) + + // Reset arena to just constants for next invocation + t.arena.shrinkToConstants(t.nbConsts) + + return result +} + // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - var api gateAPI - fWrapped := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable @@ -32,20 +77,20 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := fWrapped(in...) + y0 := tester.eval(in...) x[i] = x0 copy(in, x) - y1 := fWrapped(in...) + y1 := tester.eval(in...) x[i].Double(&x[i]) copy(in, x) - y2 := fWrapped(in...) + y2 := tester.eval(in...) - y2.Sub(y2, y1) - y1.Sub(y1, y0) + y2.Sub(&y2, &y1) + y1.Sub(&y1, &y0) - if !y2.Equal(y1) { + if !y2.Equal(&y1) { return false // not linear } @@ -58,21 +103,23 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = fWrapped(in...) + y0 = tester.eval(in...) x[i] = x0 copy(in, x) - y1 = fWrapped(in...) + y1 = tester.eval(in...) - y1.Sub(y1, y0) + y1.Sub(&y1, &y0) - return y1.Equal(y2) + return y1.Equal(&y2) } -// fitPoly tries to fit a polynomial of degree less than degreeBound to f. +// fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynomial { +func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { + nbIn := t.compiled.NbInputs + // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants fIn := make([]fr.Element, nbIn) @@ -88,7 +135,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - p[i].Set(f(fIn...)) + result := t.eval(fIn...) + p[i].Set(&result) x.Mul(&x, &domain.Generator) } @@ -103,8 +151,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := f(fIn...) - if !pAt.Equal(fAt) { + fAt := t.eval(fIn...) + if !pAt.Equal(&fAt) { return nil } @@ -119,25 +167,22 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - var api gateAPI - fFr := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := fFr.fitPoly(nbIn, degreeBound); p != nil { + if p := tester.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } return len(p) - 1, nil } - api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - var api gateAPI - fFr := api.convertFunc(f) - if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { + tester := newCompiledGateTester(f, nbIn) + if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -149,12 +194,11 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - var api gateAPI x := make(fr.Vector, nbIn) x.MustSetRandom() - fFr := api.convertFunc(f) - gFr := api.convertFunc(g) - fAt := fFr(x...) - gAt := gFr(x...) - return fAt.Equal(gAt) + fTester := newCompiledGateTester(f, nbIn) + gTester := newCompiledGateTester(g, nbIn) + fAt := fTester.eval(x...) + gAt := gTester.eval(x...) + return fAt.Equal(&gAt) } diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 075e936024..d033f09e6a 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -18,7 +18,6 @@ import ( "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit @@ -100,12 +99,14 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]frontend.Variable, len(wire.Inputs)) + inputEvaluations := make([]*fr.Element, len(wire.Inputs)) for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - var api gateAPI - gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*fr.Element)) + var stack elementStack + compiled := wire.Gate.Compiled() + stack.preloadConstants(compiled) + gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,10 +237,11 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var ( - step fr.Element - api gateAPI - ) + var step fr.Element + + // Create gate evaluator once per thread + compiled := wire.Gate.Compiled() + evaluator := newGateEvaluator(compiled) res := make([]fr.Element, degGJ) @@ -249,7 +251,6 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ... // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]fr.Element, degGJ*len(ml)) - gateInput := make([]frontend.Variable, nbGateIn) for h := start; h < end; h++ { // h counts across instances @@ -266,14 +267,14 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { - for i := range gateInput { - gateInput[i] = &mlEvals[eIndex+1+i] + // Push gate inputs + for i := 0; i < nbGateIn; i++ { + evaluator.pushInput(&mlEvals[eIndex+1+i]) } - summand := wire.Gate.Evaluate(&api, gateInput...).(*fr.Element) + summand := evaluator.evaluate() summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) - api.freeElements() } } mu.Lock() @@ -672,15 +673,35 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } - var api gateAPI + var stack elementStack ins := make([]fr.Element, maxNbIns) + insPtr := make([]*fr.Element, maxNbIns) + + // Track current gate to know when to reload constants + var currentCompiled *gkrtypes.CompiledGate + var nbConsts int + for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { + compiled := w.Gate.Compiled() + + // Preload constants if this is a new gate + if compiled != currentCompiled { + stack.freeElements() + stack.preloadConstants(compiled) + currentCompiled = compiled + nbConsts = compiled.NbConstants() + } + for inI, in := range w.Inputs { ins[inI] = a[in][i] + insPtr[inI] = &ins[inI] } - a[wI][i].Set(api.evaluate(w.Gate.Evaluate, ins[:len(w.Inputs)]...)) + a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) + + // Shrink stack back to just constants for reuse + stack.shrinkToConstants(nbConsts) } } } @@ -728,70 +749,96 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// gateAPI implements gkr.GateAPI. -// It uses a synchronous memory pool underneath to minimize heap allocations. -type gateAPI struct { +// elementStack implements a synchronous memory pool underneath to minimize heap allocations. +type elementStack struct { allocated []*fr.Element nbUsed int } -func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Add(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Add(res, api.cast(v)) +// preloadConstants loads gate constants into the stack at indices [0, nbConsts). +// This should be called once per gate to prepare the stack for repeated use. +func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { + for _, constVal := range gate.Constants { + elem := stack.newElement() + elem.SetBigInt(constVal) + } +} + +// shrinkToConstants resets the stack to contain only constants, ready for next invocation. +func (stack *elementStack) truncate(n int) { + stack.nbUsed = n +} + +func (stack *elementStack) newElement() *fr.Element { + stack.nbUsed++ + if stack.nbUsed >= len(stack.allocated) { + stack.allocated = append(stack.allocated, new(fr.Element)) + } + return stack.allocated[api.nbUsed-1] +} + +func (stack *elementStack) push(x *fr.Element) { + stack.newElement().Set(x) +} + +func opAdd(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Add(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Add(res, x[i]) } return res } -func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - prod := api.newElement() - prod.Mul(api.cast(b), api.cast(c)) - res := api.cast(a) - res.Add(res, prod) +func opMulAcc(stack *elementStack, x []*fr.Element) *fr.Element { + // result = x[0] + (x[1] * x[2]) + prod := stack.newElement() + prod.Mul(x[1], x[2]) + res := stack.newElement() + res.Add(x[0], prod) return res } -func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - res := api.newElement() - res.Neg(api.cast(i1)) +func opNeg(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Neg(x[0]) return res } -func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Sub(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Sub(res, api.cast(v)) +func opSub(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Sub(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Sub(res, x[i]) } return res } -func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Mul(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Mul(res, api.cast(v)) +func opMul(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Mul(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Mul(res, x[i]) } return res } -func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - var x fr.Element - - x.Add(api.cast(a), api.cast(b)) - x.Add(&x, api.cast(c)) +func opSumExp17(stack *elementStack, x []*fr.Element) *fr.Element { + // result = (x[0] + x[1] + x[2])^17 + sum := stack.newElement() + sum.Add(x[0], x[1]) + sum.Add(sum, x[2]) - res := api.newElement() - - res.Mul(&x, &x) // x² - res.Mul(res, res) // x⁴ - res.Mul(res, res) // x⁸ - res.Mul(res, res) // x¹⁶ - return res.Mul(res, &x) // x¹⁷ + res := stack.newElement() + res.Mul(sum, sum) // sum² + res.Mul(res, res) // sum⁴ + res.Mul(res, res) // sum⁸ + res.Mul(res, res) // sum¹⁶ + res.Mul(res, sum) // sum¹⁷ + return res } -func (api *gateAPI) Println(a ...frontend.Variable) { +func (api *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -809,43 +856,103 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { - inVar := make([]frontend.Variable, len(in)) - for i := range in { - inVar[i] = &in[i] +// executeCompiledGate executes a compiled gate using curve-specific field operations. +// The stack is expected to already contain constants at indices [0, nbConsts). +// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. +// After execution, the stack can be shrunk back to nbConsts for reuse. +func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*fr.Element) *fr.Element { + + // Instruction executor functions + executors := [...]func(*elementStack, []*fr.Element) *fr.Element{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, } - return f(api, inVar...).(*fr.Element) -} -// Put all elements back in the pool. -func (api *gateAPI) freeElements() { - api.nbUsed = 0 -} + nbConsts := gate.NbConstants() -func (api *gateAPI) newElement() *fr.Element { - api.nbUsed++ - if api.nbUsed >= len(api.allocated) { - api.allocated = append(api.allocated, new(fr.Element)) + // If no instructions, this is an identity-like gate; return first input + if len(gate.Instructions) == 0 { + if gate.NbInputs == 0 { + panic("gate has no inputs and no instructions") + } + return inputs[0] } - return api.allocated[api.nbUsed-1] -} -type gateFunctionFr func(...fr.Element) *fr.Element + // Append inputs to stack after constants + // The stack now contains: [constants | inputs] + for _, input := range inputs { + ptr := stack.newElement() + ptr.Set(input) + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range gate.Instructions { + inst := &gate.Instructions[i] + executor := executors[inst.Op] + if executor == nil { + panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) + } + + // Resolve operands from stack using instruction indices + operands := make([]*fr.Element, len(inst.Inputs)) + for j, idx := range inst.Inputs { + operands[j] = stack.allocated[idx] + } -// convertFunc turns f into a function that accepts and returns fr.Element. -func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { - return func(in ...fr.Element) *fr.Element { - return api.evaluate(f, in...) + _ = executor(stack, operands) + // Result is automatically appended to stack by executor } + + // Last value in stack is the result + resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 + return stack.allocated[resultIdx] +} + +// gateEvaluator provides a high-level API for evaluating compiled gates efficiently. +// It manages the stack internally and handles input buffering, making it easy to +// evaluate the same gate multiple times with different inputs. +type gateEvaluator struct { + gate *gkrtypes.CompiledGate + stack elementStack + nbConsts int } -func (api *gateAPI) cast(v frontend.Variable) *fr.Element { - if x, ok := v.(*fr.Element); ok { // fast path, no extra heap allocation - return x +// newGateEvaluator creates an evaluator for the given compiled gate. +// The stack is preloaded with constants and ready for evaluation. +func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { + eval := &gateEvaluator{ + gate: gate, + nbConsts: gate.NbConstants(), + inputs: make([]*fr.Element, 0, gate.NbInputs), } - x := api.newElement() - if _, err := x.SetInterface(v); err != nil { - panic(err) + eval.stack.preloadConstants(gate) + return eval +} + +// pushInput adds an input to the evaluator's input buffer. +// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +func (e *gateEvaluator) pushInput(input *fr.Element) { + e.inputs = append(e.inputs, input) +} + +// evaluate executes the gate with the current inputs and returns the result. +// The stack and input buffer are automatically reset after evaluation, +// making the evaluator ready for the next evaluation. +func (e *gateEvaluator) evaluate() *fr.Element { + if len(e.inputs) != e.gate.NbInputs { + panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) } - return x + + result := executeCompiledGate(e.gate, &e.stack, e.inputs) + + // Reset for next evaluation + e.stack.shrinkToConstants(e.nbConsts) + e.inputs = e.inputs[:0] // Clear input buffer + + return result } diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index 42a801f1fc..f2d7c41bcf 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" @@ -141,13 +140,13 @@ func SolveHint(data []SolvingData) hint.Hint { data := data[ins[0].Uint64()] instanceI := ins[1].Uint64() - // create buffer for every gate input. It will be reused for every evaluation. - gateIns := make([]frontend.Variable, data.maxNbIn) - // indices for reading inputs and outputs outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. + insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + + // Track current gate evaluator to reuse across wires with the same gate + var currentEvaluator *gateEvaluator + var currentCompiled *gkrtypes.CompiledGate // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -159,14 +158,22 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - // assemble input for gate - for i, inWI := range w.Inputs { - gateIns[i] = &data.assignment[inWI][instanceI] + compiled := w.Gate.Compiled() + + // Create new evaluator if this is a different gate + if compiled != currentCompiled { + currentEvaluator = newGateEvaluator(compiled) + currentCompiled = compiled + } + + // Push gate inputs + for _, inWI := range w.Inputs { + currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) } - // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) - api.freeElements() + // Evaluate the gate + result := currentEvaluator.evaluate() + data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bw6-761/gate_testing.go b/internal/gkr/bw6-761/gate_testing.go index 28c822c812..465c0c2893 100644 --- a/internal/gkr/bw6-761/gate_testing.go +++ b/internal/gkr/bw6-761/gate_testing.go @@ -19,10 +19,55 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) +// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. +// The arena is pre-loaded with constants and reused across all evaluations. +type compiledGateTester struct { + compiled *gkrtypes.CompiledGate + arena elementArena + nbConsts int + inputBuf []*fr.Element // reusable buffer for input pointers +} + +// newCompiledGateTester compiles a gate function and prepares it for testing. +func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { + compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) + + tester := &compiledGateTester{ + compiled: compiled, + nbConsts: compiled.NbConstants(), + inputBuf: make([]*fr.Element, nbIn), + } + + // Preload constants into arena once + tester.arena.preloadConstants(compiled) + + return tester +} + +// eval evaluates the gate on the given inputs. +// The arena is automatically reset after each call. +// Returns a copy of the result to avoid arena aliasing issues. +func (t *compiledGateTester) eval(in ...fr.Element) fr.Element { + // Use input buffer to avoid allocation + for i := range in { + t.inputBuf[i] = &in[i] + } + + resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) + + // Make a copy before resetting the arena + var result fr.Element + result.Set(resultPtr) + + // Reset arena to just constants for next invocation + t.arena.shrinkToConstants(t.nbConsts) + + return result +} + // IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - var api gateAPI - fWrapped := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable @@ -32,20 +77,20 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := fWrapped(in...) + y0 := tester.eval(in...) x[i] = x0 copy(in, x) - y1 := fWrapped(in...) + y1 := tester.eval(in...) x[i].Double(&x[i]) copy(in, x) - y2 := fWrapped(in...) + y2 := tester.eval(in...) - y2.Sub(y2, y1) - y1.Sub(y1, y0) + y2.Sub(&y2, &y1) + y1.Sub(&y1, &y0) - if !y2.Equal(y1) { + if !y2.Equal(&y1) { return false // not linear } @@ -58,21 +103,23 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = fWrapped(in...) + y0 = tester.eval(in...) x[i] = x0 copy(in, x) - y1 = fWrapped(in...) + y1 = tester.eval(in...) - y1.Sub(y1, y0) + y1.Sub(&y1, &y0) - return y1.Equal(y2) + return y1.Equal(&y2) } -// fitPoly tries to fit a polynomial of degree less than degreeBound to f. +// fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynomial { +func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { + nbIn := t.compiled.NbInputs + // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants fIn := make([]fr.Element, nbIn) @@ -88,7 +135,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - p[i].Set(f(fIn...)) + result := t.eval(fIn...) + p[i].Set(&result) x.Mul(&x, &domain.Generator) } @@ -103,8 +151,8 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := f(fIn...) - if !pAt.Equal(fAt) { + fAt := t.eval(fIn...) + if !pAt.Equal(&fAt) { return nil } @@ -119,25 +167,22 @@ func (f gateFunctionFr) fitPoly(nbIn int, degreeBound uint64) polynomial.Polynom // FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - var api gateAPI - fFr := api.convertFunc(f) + tester := newCompiledGateTester(f, nbIn) bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := fFr.fitPoly(nbIn, degreeBound); p != nil { + if p := tester.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } return len(p) - 1, nil } - api.freeElements() // not strictly necessary as few iterations are expected. } return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - var api gateAPI - fFr := api.convertFunc(f) - if p := fFr.fitPoly(nbIn, ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { + tester := newCompiledGateTester(f, nbIn) + if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -149,12 +194,11 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error // EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - var api gateAPI x := make(fr.Vector, nbIn) x.MustSetRandom() - fFr := api.convertFunc(f) - gFr := api.convertFunc(g) - fAt := fFr(x...) - gAt := gFr(x...) - return fAt.Equal(gAt) + fTester := newCompiledGateTester(f, nbIn) + gTester := newCompiledGateTester(g, nbIn) + fAt := fTester.eval(x...) + gAt := gTester.eval(x...) + return fAt.Equal(&gAt) } diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index cac9576191..482ae9fc26 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -18,7 +18,6 @@ import ( "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit @@ -100,12 +99,14 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]frontend.Variable, len(wire.Inputs)) + inputEvaluations := make([]*fr.Element, len(wire.Inputs)) for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - var api gateAPI - gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*fr.Element)) + var stack elementStack + compiled := wire.Gate.Compiled() + stack.preloadConstants(compiled) + gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,10 +237,11 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var ( - step fr.Element - api gateAPI - ) + var step fr.Element + + // Create gate evaluator once per thread + compiled := wire.Gate.Compiled() + evaluator := newGateEvaluator(compiled) res := make([]fr.Element, degGJ) @@ -249,7 +251,6 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ... // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]fr.Element, degGJ*len(ml)) - gateInput := make([]frontend.Variable, nbGateIn) for h := start; h < end; h++ { // h counts across instances @@ -266,14 +267,14 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { - for i := range gateInput { - gateInput[i] = &mlEvals[eIndex+1+i] + // Push gate inputs + for i := 0; i < nbGateIn; i++ { + evaluator.pushInput(&mlEvals[eIndex+1+i]) } - summand := wire.Gate.Evaluate(&api, gateInput...).(*fr.Element) + summand := evaluator.evaluate() summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) - api.freeElements() } } mu.Lock() @@ -672,15 +673,35 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } - var api gateAPI + var stack elementStack ins := make([]fr.Element, maxNbIns) + insPtr := make([]*fr.Element, maxNbIns) + + // Track current gate to know when to reload constants + var currentCompiled *gkrtypes.CompiledGate + var nbConsts int + for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { + compiled := w.Gate.Compiled() + + // Preload constants if this is a new gate + if compiled != currentCompiled { + stack.freeElements() + stack.preloadConstants(compiled) + currentCompiled = compiled + nbConsts = compiled.NbConstants() + } + for inI, in := range w.Inputs { ins[inI] = a[in][i] + insPtr[inI] = &ins[inI] } - a[wI][i].Set(api.evaluate(w.Gate.Evaluate, ins[:len(w.Inputs)]...)) + a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) + + // Shrink stack back to just constants for reuse + stack.shrinkToConstants(nbConsts) } } } @@ -728,70 +749,96 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// gateAPI implements gkr.GateAPI. -// It uses a synchronous memory pool underneath to minimize heap allocations. -type gateAPI struct { +// elementStack implements a synchronous memory pool underneath to minimize heap allocations. +type elementStack struct { allocated []*fr.Element nbUsed int } -func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Add(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Add(res, api.cast(v)) +// preloadConstants loads gate constants into the stack at indices [0, nbConsts). +// This should be called once per gate to prepare the stack for repeated use. +func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { + for _, constVal := range gate.Constants { + elem := stack.newElement() + elem.SetBigInt(constVal) + } +} + +// shrinkToConstants resets the stack to contain only constants, ready for next invocation. +func (stack *elementStack) truncate(n int) { + stack.nbUsed = n +} + +func (stack *elementStack) newElement() *fr.Element { + stack.nbUsed++ + if stack.nbUsed >= len(stack.allocated) { + stack.allocated = append(stack.allocated, new(fr.Element)) + } + return stack.allocated[api.nbUsed-1] +} + +func (stack *elementStack) push(x *fr.Element) { + stack.newElement().Set(x) +} + +func opAdd(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Add(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Add(res, x[i]) } return res } -func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - prod := api.newElement() - prod.Mul(api.cast(b), api.cast(c)) - res := api.cast(a) - res.Add(res, prod) +func opMulAcc(stack *elementStack, x []*fr.Element) *fr.Element { + // result = x[0] + (x[1] * x[2]) + prod := stack.newElement() + prod.Mul(x[1], x[2]) + res := stack.newElement() + res.Add(x[0], prod) return res } -func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - res := api.newElement() - res.Neg(api.cast(i1)) +func opNeg(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Neg(x[0]) return res } -func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Sub(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Sub(res, api.cast(v)) +func opSub(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Sub(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Sub(res, x[i]) } return res } -func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Mul(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Mul(res, api.cast(v)) +func opMul(stack *elementStack, x []*fr.Element) *fr.Element { + res := stack.newElement() + res.Mul(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Mul(res, x[i]) } return res } -func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - var x fr.Element - - x.Add(api.cast(a), api.cast(b)) - x.Add(&x, api.cast(c)) +func opSumExp17(stack *elementStack, x []*fr.Element) *fr.Element { + // result = (x[0] + x[1] + x[2])^17 + sum := stack.newElement() + sum.Add(x[0], x[1]) + sum.Add(sum, x[2]) - res := api.newElement() - - res.Mul(&x, &x) // x² - res.Mul(res, res) // x⁴ - res.Mul(res, res) // x⁸ - res.Mul(res, res) // x¹⁶ - return res.Mul(res, &x) // x¹⁷ + res := stack.newElement() + res.Mul(sum, sum) // sum² + res.Mul(res, res) // sum⁴ + res.Mul(res, res) // sum⁸ + res.Mul(res, res) // sum¹⁶ + res.Mul(res, sum) // sum¹⁷ + return res } -func (api *gateAPI) Println(a ...frontend.Variable) { +func (api *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -809,43 +856,103 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) evaluate(f gkr.GateFunction, in ...fr.Element) *fr.Element { - inVar := make([]frontend.Variable, len(in)) - for i := range in { - inVar[i] = &in[i] +// executeCompiledGate executes a compiled gate using curve-specific field operations. +// The stack is expected to already contain constants at indices [0, nbConsts). +// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. +// After execution, the stack can be shrunk back to nbConsts for reuse. +func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*fr.Element) *fr.Element { + + // Instruction executor functions + executors := [...]func(*elementStack, []*fr.Element) *fr.Element{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, } - return f(api, inVar...).(*fr.Element) -} -// Put all elements back in the pool. -func (api *gateAPI) freeElements() { - api.nbUsed = 0 -} + nbConsts := gate.NbConstants() -func (api *gateAPI) newElement() *fr.Element { - api.nbUsed++ - if api.nbUsed >= len(api.allocated) { - api.allocated = append(api.allocated, new(fr.Element)) + // If no instructions, this is an identity-like gate; return first input + if len(gate.Instructions) == 0 { + if gate.NbInputs == 0 { + panic("gate has no inputs and no instructions") + } + return inputs[0] } - return api.allocated[api.nbUsed-1] -} -type gateFunctionFr func(...fr.Element) *fr.Element + // Append inputs to stack after constants + // The stack now contains: [constants | inputs] + for _, input := range inputs { + ptr := stack.newElement() + ptr.Set(input) + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range gate.Instructions { + inst := &gate.Instructions[i] + executor := executors[inst.Op] + if executor == nil { + panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) + } + + // Resolve operands from stack using instruction indices + operands := make([]*fr.Element, len(inst.Inputs)) + for j, idx := range inst.Inputs { + operands[j] = stack.allocated[idx] + } -// convertFunc turns f into a function that accepts and returns fr.Element. -func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { - return func(in ...fr.Element) *fr.Element { - return api.evaluate(f, in...) + _ = executor(stack, operands) + // Result is automatically appended to stack by executor } + + // Last value in stack is the result + resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 + return stack.allocated[resultIdx] +} + +// gateEvaluator provides a high-level API for evaluating compiled gates efficiently. +// It manages the stack internally and handles input buffering, making it easy to +// evaluate the same gate multiple times with different inputs. +type gateEvaluator struct { + gate *gkrtypes.CompiledGate + stack elementStack + nbConsts int } -func (api *gateAPI) cast(v frontend.Variable) *fr.Element { - if x, ok := v.(*fr.Element); ok { // fast path, no extra heap allocation - return x +// newGateEvaluator creates an evaluator for the given compiled gate. +// The stack is preloaded with constants and ready for evaluation. +func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { + eval := &gateEvaluator{ + gate: gate, + nbConsts: gate.NbConstants(), + inputs: make([]*fr.Element, 0, gate.NbInputs), } - x := api.newElement() - if _, err := x.SetInterface(v); err != nil { - panic(err) + eval.stack.preloadConstants(gate) + return eval +} + +// pushInput adds an input to the evaluator's input buffer. +// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +func (e *gateEvaluator) pushInput(input *fr.Element) { + e.inputs = append(e.inputs, input) +} + +// evaluate executes the gate with the current inputs and returns the result. +// The stack and input buffer are automatically reset after evaluation, +// making the evaluator ready for the next evaluation. +func (e *gateEvaluator) evaluate() *fr.Element { + if len(e.inputs) != e.gate.NbInputs { + panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) } - return x + + result := executeCompiledGate(e.gate, &e.stack, e.inputs) + + // Reset for next evaluation + e.stack.shrinkToConstants(e.nbConsts) + e.inputs = e.inputs[:0] // Clear input buffer + + return result } diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index dc5456fdb5..0327b0eae0 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" hint "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" @@ -141,13 +140,13 @@ func SolveHint(data []SolvingData) hint.Hint { data := data[ins[0].Uint64()] instanceI := ins[1].Uint64() - // create buffer for every gate input. It will be reused for every evaluation. - gateIns := make([]frontend.Variable, data.maxNbIn) - // indices for reading inputs and outputs outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - var api gateAPI // since the api is synchronous, we can't share it across Solve Hint invocations. + insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. + + // Track current gate evaluator to reuse across wires with the same gate + var currentEvaluator *gateEvaluator + var currentCompiled *gkrtypes.CompiledGate // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -159,14 +158,22 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - // assemble input for gate - for i, inWI := range w.Inputs { - gateIns[i] = &data.assignment[inWI][instanceI] + compiled := w.Gate.Compiled() + + // Create new evaluator if this is a different gate + if compiled != currentCompiled { + currentEvaluator = newGateEvaluator(compiled) + currentCompiled = compiled + } + + // Push gate inputs + for _, inWI := range w.Inputs { + currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) } - // evaluate the gate on the inputs and store the result in the assignment (for the following gates to use) - data.assignment[wI][instanceI].Set(w.Gate.Evaluate(&api, gateIns[:len(w.Inputs)]...).(*fr.Element)) - api.freeElements() + // Evaluate the gate + result := currentEvaluator.evaluate() + data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/gkrtypes/compiledgate.go b/internal/gkr/gkrtypes/compiledgate.go new file mode 100644 index 0000000000..e04be809bd --- /dev/null +++ b/internal/gkr/gkrtypes/compiledgate.go @@ -0,0 +1,307 @@ +package gkrtypes + +import ( + "math/big" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/utils" + "github.com/consensys/gnark/std/gkrapi/gkr" +) + +// GateOp represents an arithmetic operation in a compiled gate. +type GateOp uint8 + +const ( + OpAdd GateOp = iota // result = src1 + src2 + ... (variadic) + OpSub // result = src1 - src2 - ... + OpMul // result = src1 * src2 * ... + OpNeg // result = -src1 + OpMulAcc // result = src1 + (src2 * src3) + OpSumExp17 // result = (src1 + src2 + src3)^17 +) + +// GateInstruction represents a single operation in a compiled gate. +// Each instruction produces a new variable (no explicit dst field). +// Index space layout: +// - [0, nbConsts): constant values (from CompiledGate.Constants) +// - [nbConsts, nbConsts+nbInputs): gate inputs +// - [nbConsts+nbInputs, ...): instruction results +type GateInstruction struct { + Op GateOp + Inputs []uint16 // indices into the unified value space +} + +// CompiledGate represents a gate function compiled into a sequence of instructions. +// The compiled form is independent of curve-specific types and can be serialized. +// The index space is unified: constants (0..nbConsts-1), inputs (nbConsts..nbConsts+nbInputs-1), +// then instruction results. +type CompiledGate struct { + Instructions []GateInstruction // sequence of operations + Constants []*big.Int // constant values at indices [0, nbConsts) + NbInputs int // number of gate inputs + Degree int // total degree of the polynomial + SolvableVar int // if there is a solvable variable, its index, -1 otherwise + Curves []ecc.ID // curves that the gate is allowed to be used over +} + +// NbConstants returns the number of constants in the gate +func (g *CompiledGate) NbConstants() int { + return len(g.Constants) +} + +// String returns a human-readable representation of the operation +func (op GateOp) String() string { + switch op { + case OpAdd: + return "add" + case OpSub: + return "sub" + case OpMul: + return "mul" + case OpNeg: + return "neg" + case OpMulAcc: + return "mulacc" + case OpSumExp17: + return "sumexp17" + default: + return "unknown" + } +} + +// gateCompiler is an implementation of gkr.GateAPI that records operations +// instead of executing them. This is used to compile gate functions into +// instruction sequences. During compilation, temporary indices are used: +// - Constants: high indices (starting at 0x8000) +// - Inputs: 0..nbInputs-1 +// - Results: nbInputs onwards +// +// After compilation, indices are remapped to: constants, inputs, results. +type gateCompiler struct { + instructions []GateInstruction + constants []*big.Int // constant values pool + constantIndex map[string]uint16 // map from constant value to its temp index (0x8000+) + nbInputs int + nextVarID uint16 // next variable ID to assign (for inputs and results) + nextConstID uint16 // next constant ID to assign (starts at 0x8000) +} + +// newGateCompiler creates a new recording API with the given number of inputs. +// Input variables are assigned temporary IDs 0 to nbInputs-1. +// Constants are assigned temporary IDs starting at 0x8000. +func newGateCompiler(nbInputs int) *gateCompiler { + return &gateCompiler{ + instructions: make([]GateInstruction, 0), + constants: make([]*big.Int, 0), + constantIndex: make(map[string]uint16), + nbInputs: nbInputs, + nextVarID: uint16(nbInputs), // start after input variables + nextConstID: 0x8000, // constants use high temp indices + } +} + +// compilationVar represents a variable during gate compilation. +type compilationVar struct { + id uint16 +} + +// Add records an addition operation. +func (gc *gateCompiler) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + inputs := make([]uint16, 0, 2+len(in)) + inputs = append(inputs, gc.getVarID(i1)) + inputs = append(inputs, gc.getVarID(i2)) + for _, v := range in { + inputs = append(inputs, gc.getVarID(v)) + } + + gc.instructions = append(gc.instructions, GateInstruction{ + Op: OpAdd, + Inputs: inputs, + }) + + result := &compilationVar{id: gc.nextVarID} + gc.nextVarID++ + return result +} + +// MulAcc records a multiply-accumulate operation: a + (b * c) +func (gc *gateCompiler) MulAcc(a, b, c frontend.Variable) frontend.Variable { + inputs := []uint16{ + gc.getVarID(a), + gc.getVarID(b), + gc.getVarID(c), + } + + gc.instructions = append(gc.instructions, GateInstruction{ + Op: OpMulAcc, + Inputs: inputs, + }) + + result := &compilationVar{id: gc.nextVarID} + gc.nextVarID++ + return result +} + +// Neg records a negation operation +func (gc *gateCompiler) Neg(i1 frontend.Variable) frontend.Variable { + inputs := []uint16{gc.getVarID(i1)} + + gc.instructions = append(gc.instructions, GateInstruction{ + Op: OpNeg, + Inputs: inputs, + }) + + result := &compilationVar{id: gc.nextVarID} + gc.nextVarID++ + return result +} + +// Sub records a subtraction operation +func (gc *gateCompiler) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + inputs := make([]uint16, 0, 2+len(in)) + inputs = append(inputs, gc.getVarID(i1)) + inputs = append(inputs, gc.getVarID(i2)) + for _, v := range in { + inputs = append(inputs, gc.getVarID(v)) + } + + gc.instructions = append(gc.instructions, GateInstruction{ + Op: OpSub, + Inputs: inputs, + }) + + result := &compilationVar{id: gc.nextVarID} + gc.nextVarID++ + return result +} + +// Mul records a multiplication operation +func (gc *gateCompiler) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + inputs := make([]uint16, 0, 2+len(in)) + inputs = append(inputs, gc.getVarID(i1)) + inputs = append(inputs, gc.getVarID(i2)) + for _, v := range in { + inputs = append(inputs, gc.getVarID(v)) + } + + gc.instructions = append(gc.instructions, GateInstruction{ + Op: OpMul, + Inputs: inputs, + }) + + result := &compilationVar{id: gc.nextVarID} + gc.nextVarID++ + return result +} + +// Println is a no-op during recording +func (gc *gateCompiler) Println(a ...frontend.Variable) { + // No-op: we don't need to record print statements +} + +// SumExp17 records (a + b + c)^17 as a single instruction +func (gc *gateCompiler) SumExp17(a, b, c frontend.Variable) frontend.Variable { + inputs := []uint16{ + gc.getVarID(a), + gc.getVarID(b), + gc.getVarID(c), + } + + gc.instructions = append(gc.instructions, GateInstruction{ + Op: OpSumExp17, + Inputs: inputs, + }) + + result := &compilationVar{id: gc.nextVarID} + gc.nextVarID++ + return result +} + +// getVarID extracts or creates a temporary index from a value. +// Returns a temporary index: inputs at 0..nbInputs-1, constants at 0x8000+, results at nbInputs+. +func (gc *gateCompiler) getVarID(v frontend.Variable) uint16 { + if rv, ok := v.(*compilationVar); ok { + return rv.id + } + // If it's an integer in the valid input range, treat it as an input variable index + if idx, ok := v.(int); ok { + if idx >= 0 && idx < gc.nbInputs { + return uint16(idx) + } + // Integer outside input range - treat as constant + } + + // Otherwise, it must be a constant value + // Convert to big.Int for curve-agnostic storage + val := utils.FromInterface(v) + + // Check if we've seen this constant before + key := val.String() + if idx, exists := gc.constantIndex[key]; exists { + return idx + } + + // Add new constant to the pool with temp index 0x8000+ + tempIdx := gc.nextConstID + gc.nextConstID++ + gc.constants = append(gc.constants, new(big.Int).Set(&val)) + gc.constantIndex[key] = tempIdx + return tempIdx +} + +// GetInstructions returns the recorded instructions +func (gc *gateCompiler) GetInstructions() []GateInstruction { + return gc.instructions +} + +// GetNbInputs returns the number of inputs +func (gc *gateCompiler) GetNbInputs() int { + return gc.nbInputs +} + +// remapIndices transforms temporary indices to final layout: constants, inputs, results. +func (gc *gateCompiler) remapIndices() { + nbConsts := uint16(len(gc.constants)) + + // Remap all instruction inputs + for i := range gc.instructions { + for j := range gc.instructions[i].Inputs { + if gc.instructions[i].Inputs[j]&0x8000 != 0 { + // constant + gc.instructions[i].Inputs[j] &= 0x7fff + } else { + // variable + gc.instructions[i].Inputs[j] += nbConsts + } + } + } +} + +// CompileGateFunction compiles a gate function into a CompiledGate. +// The gate function should be of type gkr.GateFunction. +func CompileGateFunction(f gkr.GateFunction, nbInputs int, degree int, solvableVar int, curves []ecc.ID) *CompiledGate { + // Create recording API + compiler := newGateCompiler(nbInputs) + + // Create input variables as integers 0, 1, 2, ... + inputs := make([]frontend.Variable, nbInputs) + for i := range nbInputs { + inputs[i] = i + } + + // Execute the gate function to record operations + f(compiler, inputs...) + + // Remap indices from temporary layout to final layout + compiler.remapIndices() + + return &CompiledGate{ + Instructions: compiler.GetInstructions(), + Constants: compiler.constants, + NbInputs: nbInputs, + Degree: degree, + SolvableVar: solvableVar, + Curves: curves, + } +} diff --git a/internal/gkr/gkrtypes/compiledgate_test.go b/internal/gkr/gkrtypes/compiledgate_test.go new file mode 100644 index 0000000000..86cc719bec --- /dev/null +++ b/internal/gkr/gkrtypes/compiledgate_test.go @@ -0,0 +1,132 @@ +package gkrtypes + +import ( + "math/big" + "testing" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/gkrapi/gkr" +) + +// TestCompiledGateWithConstants tests that gates can use constant values +func TestCompiledGateWithConstants(t *testing.T) { + // Create a gate that adds a constant: f(x) = x + 5 + addConstantGate := func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + five := big.NewInt(5) + return api.Add(in[0], five) + } + + // Compile the gate + compiled := CompileGateFunction(addConstantGate, 1, 1, -1, []ecc.ID{ecc.BN254}) + + // Verify the gate has constants + if len(compiled.Constants) == 0 { + t.Fatal("Expected compiled gate to have constants") + } + + // Verify the constant value + if compiled.Constants[0].Cmp(big.NewInt(5)) != 0 { + t.Errorf("Expected constant to be 5, got %s", compiled.Constants[0].String()) + } + + // Verify instructions reference the constant + if len(compiled.Instructions) == 0 { + t.Fatal("Expected compiled gate to have instructions") + } + + inst := compiled.Instructions[0] + if inst.Op != OpAdd { + t.Errorf("Expected OpAdd, got %v", inst.Op) + } + + // Verify index layout: constants at [0, nbConsts), inputs at [nbConsts, nbConsts+nbInputs) + nbConsts := len(compiled.Constants) + hasConstant := false + hasInput := false + for _, idx := range inst.Inputs { + if idx < uint16(nbConsts) { + hasConstant = true + if idx != 0 { + t.Errorf("Expected constant index 0, got %d", idx) + } + } else if idx >= uint16(nbConsts) && idx < uint16(nbConsts+compiled.NbInputs) { + hasInput = true + inputIdx := idx - uint16(nbConsts) + if inputIdx != 0 { + t.Errorf("Expected input index 0 (remapped to %d), got %d", nbConsts, idx) + } + } + } + + if !hasConstant { + t.Error("Expected instruction to reference a constant") + } + if !hasInput { + t.Error("Expected instruction to reference an input") + } + + t.Logf("Successfully compiled gate with %d constants and %d instructions", + len(compiled.Constants), len(compiled.Instructions)) + t.Logf("Index layout: constants [0,%d), inputs [%d,%d)", + nbConsts, nbConsts, nbConsts+compiled.NbInputs) +} + +// TestCompiledGateWithMultipleConstants tests gates with multiple different constants +func TestCompiledGateWithMultipleConstants(t *testing.T) { + // Create a gate: f(x) = (x + 3) * 7 + complexGate := func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + three := big.NewInt(3) + seven := big.NewInt(7) + sum := api.Add(in[0], three) + return api.Mul(sum, seven) + } + + // Compile the gate + compiled := CompileGateFunction(complexGate, 1, 2, -1, []ecc.ID{ecc.BN254}) + + // Verify we have two constants + if len(compiled.Constants) != 2 { + t.Errorf("Expected 2 constants, got %d", len(compiled.Constants)) + } + + // Verify the constants are 3 and 7 + constantValues := make(map[int64]bool) + for _, c := range compiled.Constants { + constantValues[c.Int64()] = true + } + + if !constantValues[3] || !constantValues[7] { + t.Error("Expected constants to be 3 and 7") + } + + t.Logf("Successfully compiled gate with %d constants: %v", + len(compiled.Constants), compiled.Constants) +} + +// TestConstantDeduplication tests that identical constants are deduplicated +func TestConstantDeduplication(t *testing.T) { + // Create a gate that uses the same constant multiple times: f(x, y) = (x + 5) + (y + 5) + gateWithDuplicates := func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + five1 := big.NewInt(5) + five2 := big.NewInt(5) // Same value, should be deduplicated + sum1 := api.Add(in[0], five1) + sum2 := api.Add(in[1], five2) + return api.Add(sum1, sum2) + } + + // Compile the gate + compiled := CompileGateFunction(gateWithDuplicates, 2, 1, -1, []ecc.ID{ecc.BN254}) + + // Verify only one constant is stored (deduplication) + if len(compiled.Constants) != 1 { + t.Errorf("Expected 1 deduplicated constant, got %d", len(compiled.Constants)) + } + + if compiled.Constants[0].Cmp(big.NewInt(5)) != 0 { + t.Errorf("Expected constant to be 5, got %s", compiled.Constants[0].String()) + } + + t.Logf("Successfully deduplicated constants: %d unique constant(s)", + len(compiled.Constants)) +} diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 4cb55a00a9..a560e61d2c 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -16,11 +16,11 @@ import ( // A Gate is a low-degree multivariate polynomial type Gate struct { - evaluate gkr.GateFunction // Evaluate the polynomial function defining the gate - nbIn int // number of inputs - degree int // total degree of the polynomial - solvableVar int // if there is a variable whose value can be uniquely determined from the value of the gate and the other inputs, its index, -1 otherwise - curves []ecc.ID // curves that the gate is allowed to be used over + compiled *CompiledGate // Compiled form of the gate + nbIn int // number of inputs + degree int // total degree of the polynomial + solvableVar int // if there is a variable whose value can be uniquely determined from the value of the gate and the other inputs, its index, -1 otherwise + curves []ecc.ID // curves that the gate is allowed to be used over } // NewGate creates a new gate function the given parameters: @@ -30,9 +30,11 @@ type Gate struct { // - solvableVar: if there is a variable whose value can be uniquely determined from the value of the gate and the other inputs, its index, -1 otherwise // - curves: curves that the gate is allowed to be used over func NewGate(f gkr.GateFunction, nbIn int, degree int, solvableVar int, curves []ecc.ID) *Gate { + // Compile the gate function immediately + compiled := CompileGateFunction(f, nbIn, degree, solvableVar, curves) return &Gate{ - evaluate: f, + compiled: compiled, nbIn: nbIn, degree: degree, solvableVar: solvableVar, @@ -45,10 +47,71 @@ func (g *Gate) SupportsCurve(curve ecc.ID) bool { return slices.Contains(g.curves, curve) } -// Evaluate evaluates the gate on the given inputs. The number of inputs must -// match the gate's fan-in. If not, then it panics. +// Evaluate evaluates the gate on the given inputs by interpreting the compiled +// instruction sequence using the provided API. The number of inputs must match +// the gate's fan-in. If not, then it panics. func (g *Gate) Evaluate(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - return g.evaluate(api, in...) + if len(in) != g.compiled.NbInputs { + panic("number of inputs does not match gate's NbInputs") + } + + nbConsts := g.compiled.NbConstants() + // Variables array: constants [0..nbConsts), inputs [nbConsts..nbConsts+nbInputs), results after + vars := make([]frontend.Variable, nbConsts+g.compiled.NbInputs+len(g.compiled.Instructions)) + + // Populate constants + for i, c := range g.compiled.Constants { + vars[i] = c + } + + // Populate inputs starting at nbConsts + copy(vars[nbConsts:], in) + + // Execute instructions sequentially + for i, inst := range g.compiled.Instructions { + resultIdx := nbConsts + g.compiled.NbInputs + i + + // Get input variables/constants for this instruction + inputs := make([]frontend.Variable, len(inst.Inputs)) + for j, idx := range inst.Inputs { + inputs[j] = vars[idx] + } + + // Execute the operation + switch inst.Op { + case OpAdd: + result := inputs[0] + for j := 1; j < len(inputs); j++ { + result = api.Add(result, inputs[j]) + } + vars[resultIdx] = result + case OpSub: + result := inputs[0] + for j := 1; j < len(inputs); j++ { + result = api.Sub(result, inputs[j]) + } + vars[resultIdx] = result + case OpMul: + result := inputs[0] + for j := 1; j < len(inputs); j++ { + result = api.Mul(result, inputs[j]) + } + vars[resultIdx] = result + case OpNeg: + vars[resultIdx] = api.Neg(inputs[0]) + case OpMulAcc: + // result = inputs[0] + (inputs[1] * inputs[2]) + vars[resultIdx] = api.MulAcc(inputs[0], inputs[1], inputs[2]) + case OpSumExp17: + // result = (inputs[0] + inputs[1] + inputs[2])^17 + vars[resultIdx] = api.SumExp17(inputs[0], inputs[1], inputs[2]) + default: + panic("unknown operation") + } + } + + // The last computed value is the result + return vars[len(vars)-1] } // Degree returns the total degree of the gate's polynomial e.g. Degree(xy²) = 3 @@ -67,6 +130,17 @@ func (g *Gate) NbIn() int { return g.nbIn } +// Compiled returns the compiled form of the gate. +func (g *Gate) Compiled() *CompiledGate { + return g.compiled +} + +// SetCompiled sets the compiled form of the gate. This is used when loading +// a pre-compiled gate. +func (g *Gate) SetCompiled(compiled *CompiledGate) { + g.compiled = compiled +} + // Wire represents a wire in the GKR circuit. A wire is defined through its gate // and its inputs. type Wire struct { diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 80774db6f7..56ce964e17 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -18,7 +18,6 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) // The goal is to prove/verify evaluations of many instances of the same circuit @@ -100,12 +99,14 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []small_rational.S e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]frontend.Variable, len(wire.Inputs)) + inputEvaluations := make([]*small_rational.SmallRational, len(wire.Inputs)) for i, uniqueI := range injectionLeftInv { // map from all to unique inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] } - var api gateAPI - gateEvaluation.Set(wire.Gate.Evaluate(&api, inputEvaluations...).(*small_rational.SmallRational)) + var stack elementStack + compiled := wire.Gate.Compiled() + stack.preloadConstants(compiled) + gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -236,10 +237,11 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]small_rational.SmallRational, degGJ) var mu sync.Mutex computeAll := func(start, end int) { // compute method to allow parallelization across instances - var ( - step small_rational.SmallRational - api gateAPI - ) + var step small_rational.SmallRational + + // Create gate evaluator once per thread + compiled := wire.Gate.Compiled() + evaluator := newGateEvaluator(compiled) res := make([]small_rational.SmallRational, degGJ) @@ -249,7 +251,6 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ... // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]small_rational.SmallRational, degGJ*len(ml)) - gateInput := make([]frontend.Variable, nbGateIn) for h := start; h < end; h++ { // h counts across instances @@ -266,14 +267,14 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { - for i := range gateInput { - gateInput[i] = &mlEvals[eIndex+1+i] + // Push gate inputs + for i := 0; i < nbGateIn; i++ { + evaluator.pushInput(&mlEvals[eIndex+1+i]) } - summand := wire.Gate.Evaluate(&api, gateInput...).(*small_rational.SmallRational) + summand := evaluator.evaluate() summand.Mul(summand, &mlEvals[eIndex]) res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) - api.freeElements() } } mu.Lock() @@ -672,15 +673,35 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { } } - var api gateAPI + var stack elementStack ins := make([]small_rational.SmallRational, maxNbIns) + insPtr := make([]*small_rational.SmallRational, maxNbIns) + + // Track current gate to know when to reload constants + var currentCompiled *gkrtypes.CompiledGate + var nbConsts int + for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { + compiled := w.Gate.Compiled() + + // Preload constants if this is a new gate + if compiled != currentCompiled { + stack.freeElements() + stack.preloadConstants(compiled) + currentCompiled = compiled + nbConsts = compiled.NbConstants() + } + for inI, in := range w.Inputs { ins[inI] = a[in][i] + insPtr[inI] = &ins[inI] } - a[wI][i].Set(api.evaluate(w.Gate.Evaluate, ins[:len(w.Inputs)]...)) + a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) + + // Shrink stack back to just constants for reuse + stack.shrinkToConstants(nbConsts) } } } @@ -728,70 +749,96 @@ func frToBigInts(dst []*big.Int, src []small_rational.SmallRational) { } } -// gateAPI implements gkr.GateAPI. -// It uses a synchronous memory pool underneath to minimize heap allocations. -type gateAPI struct { +// elementStack implements a synchronous memory pool underneath to minimize heap allocations. +type elementStack struct { allocated []*small_rational.SmallRational nbUsed int } -func (api *gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Add(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Add(res, api.cast(v)) +// preloadConstants loads gate constants into the stack at indices [0, nbConsts). +// This should be called once per gate to prepare the stack for repeated use. +func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { + for _, constVal := range gate.Constants { + elem := stack.newElement() + elem.SetBigInt(constVal) + } +} + +// shrinkToConstants resets the stack to contain only constants, ready for next invocation. +func (stack *elementStack) truncate(n int) { + stack.nbUsed = n +} + +func (stack *elementStack) newElement() *small_rational.SmallRational { + stack.nbUsed++ + if stack.nbUsed >= len(stack.allocated) { + stack.allocated = append(stack.allocated, new(small_rational.SmallRational)) + } + return stack.allocated[api.nbUsed-1] +} + +func (stack *elementStack) push(x *small_rational.SmallRational) { + stack.newElement().Set(x) +} + +func opAdd(stack *elementStack, x []*small_rational.SmallRational) *small_rational.SmallRational { + res := stack.newElement() + res.Add(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Add(res, x[i]) } return res } -func (api *gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - prod := api.newElement() - prod.Mul(api.cast(b), api.cast(c)) - res := api.cast(a) - res.Add(res, prod) +func opMulAcc(stack *elementStack, x []*small_rational.SmallRational) *small_rational.SmallRational { + // result = x[0] + (x[1] * x[2]) + prod := stack.newElement() + prod.Mul(x[1], x[2]) + res := stack.newElement() + res.Add(x[0], prod) return res } -func (api *gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - res := api.newElement() - res.Neg(api.cast(i1)) +func opNeg(stack *elementStack, x []*small_rational.SmallRational) *small_rational.SmallRational { + res := stack.newElement() + res.Neg(x[0]) return res } -func (api *gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Sub(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Sub(res, api.cast(v)) +func opSub(stack *elementStack, x []*small_rational.SmallRational) *small_rational.SmallRational { + res := stack.newElement() + res.Sub(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Sub(res, x[i]) } return res } -func (api *gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := api.newElement() - res.Mul(api.cast(i1), api.cast(i2)) - for _, v := range in { - res.Mul(res, api.cast(v)) +func opMul(stack *elementStack, x []*small_rational.SmallRational) *small_rational.SmallRational { + res := stack.newElement() + res.Mul(x[0], x[1]) + for i := 2; i < len(x); i++ { + res.Mul(res, x[i]) } return res } -func (api *gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - var x small_rational.SmallRational - - x.Add(api.cast(a), api.cast(b)) - x.Add(&x, api.cast(c)) +func opSumExp17(stack *elementStack, x []*small_rational.SmallRational) *small_rational.SmallRational { + // result = (x[0] + x[1] + x[2])^17 + sum := stack.newElement() + sum.Add(x[0], x[1]) + sum.Add(sum, x[2]) - res := api.newElement() - - res.Mul(&x, &x) // x² - res.Mul(res, res) // x⁴ - res.Mul(res, res) // x⁸ - res.Mul(res, res) // x¹⁶ - return res.Mul(res, &x) // x¹⁷ + res := stack.newElement() + res.Mul(sum, sum) // sum² + res.Mul(res, res) // sum⁴ + res.Mul(res, res) // sum⁸ + res.Mul(res, res) // sum¹⁶ + res.Mul(res, sum) // sum¹⁷ + return res } -func (api *gateAPI) Println(a ...frontend.Variable) { +func (api *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x small_rational.SmallRational @@ -809,43 +856,103 @@ func (api *gateAPI) Println(a ...frontend.Variable) { fmt.Println(toPrint...) } -func (api *gateAPI) evaluate(f gkr.GateFunction, in ...small_rational.SmallRational) *small_rational.SmallRational { - inVar := make([]frontend.Variable, len(in)) - for i := range in { - inVar[i] = &in[i] +// executeCompiledGate executes a compiled gate using curve-specific field operations. +// The stack is expected to already contain constants at indices [0, nbConsts). +// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. +// After execution, the stack can be shrunk back to nbConsts for reuse. +func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*small_rational.SmallRational) *small_rational.SmallRational { + + // Instruction executor functions + executors := [...]func(*elementStack, []*small_rational.SmallRational) *small_rational.SmallRational{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, } - return f(api, inVar...).(*small_rational.SmallRational) -} -// Put all elements back in the pool. -func (api *gateAPI) freeElements() { - api.nbUsed = 0 -} + nbConsts := gate.NbConstants() -func (api *gateAPI) newElement() *small_rational.SmallRational { - api.nbUsed++ - if api.nbUsed >= len(api.allocated) { - api.allocated = append(api.allocated, new(small_rational.SmallRational)) + // If no instructions, this is an identity-like gate; return first input + if len(gate.Instructions) == 0 { + if gate.NbInputs == 0 { + panic("gate has no inputs and no instructions") + } + return inputs[0] } - return api.allocated[api.nbUsed-1] -} -type gateFunctionFr func(...small_rational.SmallRational) *small_rational.SmallRational + // Append inputs to stack after constants + // The stack now contains: [constants | inputs] + for _, input := range inputs { + ptr := stack.newElement() + ptr.Set(input) + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range gate.Instructions { + inst := &gate.Instructions[i] + executor := executors[inst.Op] + if executor == nil { + panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) + } + + // Resolve operands from stack using instruction indices + operands := make([]*small_rational.SmallRational, len(inst.Inputs)) + for j, idx := range inst.Inputs { + operands[j] = stack.allocated[idx] + } -// convertFunc turns f into a function that accepts and returns small_rational.SmallRational. -func (api *gateAPI) convertFunc(f gkr.GateFunction) gateFunctionFr { - return func(in ...small_rational.SmallRational) *small_rational.SmallRational { - return api.evaluate(f, in...) + _ = executor(stack, operands) + // Result is automatically appended to stack by executor } + + // Last value in stack is the result + resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 + return stack.allocated[resultIdx] +} + +// gateEvaluator provides a high-level API for evaluating compiled gates efficiently. +// It manages the stack internally and handles input buffering, making it easy to +// evaluate the same gate multiple times with different inputs. +type gateEvaluator struct { + gate *gkrtypes.CompiledGate + stack elementStack + nbConsts int } -func (api *gateAPI) cast(v frontend.Variable) *small_rational.SmallRational { - if x, ok := v.(*small_rational.SmallRational); ok { // fast path, no extra heap allocation - return x +// newGateEvaluator creates an evaluator for the given compiled gate. +// The stack is preloaded with constants and ready for evaluation. +func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { + eval := &gateEvaluator{ + gate: gate, + nbConsts: gate.NbConstants(), + inputs: make([]*small_rational.SmallRational, 0, gate.NbInputs), } - x := api.newElement() - if _, err := x.SetInterface(v); err != nil { - panic(err) + eval.stack.preloadConstants(gate) + return eval +} + +// pushInput adds an input to the evaluator's input buffer. +// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +func (e *gateEvaluator) pushInput(input *small_rational.SmallRational) { + e.inputs = append(e.inputs, input) +} + +// evaluate executes the gate with the current inputs and returns the result. +// The stack and input buffer are automatically reset after evaluation, +// making the evaluator ready for the next evaluation. +func (e *gateEvaluator) evaluate() *small_rational.SmallRational { + if len(e.inputs) != e.gate.NbInputs { + panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) } - return x + + result := executeCompiledGate(e.gate, &e.stack, e.inputs) + + // Reset for next evaluation + e.stack.shrinkToConstants(e.nbConsts) + e.inputs = e.inputs[:0] // Clear input buffer + + return result } diff --git a/internal/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json b/internal/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json index 2dca0746a2..34814b5e10 100644 --- a/internal/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json +++ b/internal/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json @@ -36,8 +36,8 @@ ], "partialSumPolys": [ [ - -3, - -16 + -1, + -8 ] ] } diff --git a/internal/small_rational/small-rational.go b/internal/small_rational/small-rational.go index 6cac8cea24..c561a84415 100644 --- a/internal/small_rational/small-rational.go +++ b/internal/small_rational/small-rational.go @@ -263,6 +263,14 @@ func (z *SmallRational) SetInt64(i int64) *SmallRational { return z } +// SetBigInt sets z to the integer value i (denominator = 1). +func (z *SmallRational) SetBigInt(i *big.Int) *SmallRational { + z.numerator.Set(i) + z.denominator.SetInt64(1) + z.text = i.String() + return z +} + func (z *SmallRational) SetRandom() (*SmallRational, error) { bytes := make([]byte, 1) From c4b73d06d4279534bd80df53f98c458a19149162 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 12 Jan 2026 15:06:46 -0600 Subject: [PATCH 092/251] refactor: clean up gate compilation --- internal/gkr/gkrtypes/compiledgate.go | 127 ++++++++------------------ 1 file changed, 38 insertions(+), 89 deletions(-) diff --git a/internal/gkr/gkrtypes/compiledgate.go b/internal/gkr/gkrtypes/compiledgate.go index e04be809bd..fbe37a3dfc 100644 --- a/internal/gkr/gkrtypes/compiledgate.go +++ b/internal/gkr/gkrtypes/compiledgate.go @@ -79,14 +79,14 @@ func (op GateOp) String() string { // // After compilation, indices are remapped to: constants, inputs, results. type gateCompiler struct { - instructions []GateInstruction + instructions []GateInstruction // each instruction defines exactly one output variable constants []*big.Int // constant values pool constantIndex map[string]uint16 // map from constant value to its temp index (0x8000+) nbInputs int - nextVarID uint16 // next variable ID to assign (for inputs and results) - nextConstID uint16 // next constant ID to assign (starts at 0x8000) } +const constMarker = 0x8000 + // newGateCompiler creates a new recording API with the given number of inputs. // Input variables are assigned temporary IDs 0 to nbInputs-1. // Constants are assigned temporary IDs starting at 0x8000. @@ -96,8 +96,6 @@ func newGateCompiler(nbInputs int) *gateCompiler { constants: make([]*big.Int, 0), constantIndex: make(map[string]uint16), nbInputs: nbInputs, - nextVarID: uint16(nbInputs), // start after input variables - nextConstID: 0x8000, // constants use high temp indices } } @@ -106,93 +104,53 @@ type compilationVar struct { id uint16 } -// Add records an addition operation. -func (gc *gateCompiler) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - inputs := make([]uint16, 0, 2+len(in)) - inputs = append(inputs, gc.getVarID(i1)) - inputs = append(inputs, gc.getVarID(i2)) - for _, v := range in { - inputs = append(inputs, gc.getVarID(v)) +func (gc *gateCompiler) addInstruction(op GateOp, inputs ...frontend.Variable) compilationVar { + ins := make([]uint16, len(inputs)) + for i := range ins { + ins[i] = gc.getVarID(inputs[i]) } + result := compilationVar{id: uint16(len(gc.instructions) + gc.nbInputs)} + gc.instructions = append(gc.instructions, GateInstruction{ - Op: OpAdd, - Inputs: inputs, + Op: op, + Inputs: ins, }) - result := &compilationVar{id: gc.nextVarID} - gc.nextVarID++ return result } -// MulAcc records a multiply-accumulate operation: a + (b * c) -func (gc *gateCompiler) MulAcc(a, b, c frontend.Variable) frontend.Variable { - inputs := []uint16{ - gc.getVarID(a), - gc.getVarID(b), - gc.getVarID(c), - } +func (gc *gateCompiler) addInstruction2Plus(op GateOp, i1, i2 frontend.Variable, in ...frontend.Variable) compilationVar { + ins := make([]frontend.Variable, len(in)+2) + ins[0] = i1 + ins[1] = i2 + copy(ins[2:], in) + return gc.addInstruction(op, ins...) +} - gc.instructions = append(gc.instructions, GateInstruction{ - Op: OpMulAcc, - Inputs: inputs, - }) +// Add records an addition operation. +func (gc *gateCompiler) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + return gc.addInstruction2Plus(OpAdd, i1, i2, in...) +} - result := &compilationVar{id: gc.nextVarID} - gc.nextVarID++ - return result +// MulAcc records a multiply-accumulate operation: a + (b * c) +func (gc *gateCompiler) MulAcc(a, b, c frontend.Variable) frontend.Variable { + return gc.addInstruction(OpMulAcc, a, b, c) } // Neg records a negation operation func (gc *gateCompiler) Neg(i1 frontend.Variable) frontend.Variable { - inputs := []uint16{gc.getVarID(i1)} - - gc.instructions = append(gc.instructions, GateInstruction{ - Op: OpNeg, - Inputs: inputs, - }) - - result := &compilationVar{id: gc.nextVarID} - gc.nextVarID++ - return result + return gc.addInstruction(OpNeg, i1) } // Sub records a subtraction operation func (gc *gateCompiler) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - inputs := make([]uint16, 0, 2+len(in)) - inputs = append(inputs, gc.getVarID(i1)) - inputs = append(inputs, gc.getVarID(i2)) - for _, v := range in { - inputs = append(inputs, gc.getVarID(v)) - } - - gc.instructions = append(gc.instructions, GateInstruction{ - Op: OpSub, - Inputs: inputs, - }) - - result := &compilationVar{id: gc.nextVarID} - gc.nextVarID++ - return result + return gc.addInstruction2Plus(OpSub, i1, i2, in...) } // Mul records a multiplication operation func (gc *gateCompiler) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - inputs := make([]uint16, 0, 2+len(in)) - inputs = append(inputs, gc.getVarID(i1)) - inputs = append(inputs, gc.getVarID(i2)) - for _, v := range in { - inputs = append(inputs, gc.getVarID(v)) - } - - gc.instructions = append(gc.instructions, GateInstruction{ - Op: OpMul, - Inputs: inputs, - }) - - result := &compilationVar{id: gc.nextVarID} - gc.nextVarID++ - return result + return gc.addInstruction2Plus(OpMul, i1, i2, in...) } // Println is a no-op during recording @@ -202,20 +160,7 @@ func (gc *gateCompiler) Println(a ...frontend.Variable) { // SumExp17 records (a + b + c)^17 as a single instruction func (gc *gateCompiler) SumExp17(a, b, c frontend.Variable) frontend.Variable { - inputs := []uint16{ - gc.getVarID(a), - gc.getVarID(b), - gc.getVarID(c), - } - - gc.instructions = append(gc.instructions, GateInstruction{ - Op: OpSumExp17, - Inputs: inputs, - }) - - result := &compilationVar{id: gc.nextVarID} - gc.nextVarID++ - return result + return gc.addInstruction(OpSumExp17, a, b, c) } // getVarID extracts or creates a temporary index from a value. @@ -243,8 +188,7 @@ func (gc *gateCompiler) getVarID(v frontend.Variable) uint16 { } // Add new constant to the pool with temp index 0x8000+ - tempIdx := gc.nextConstID - gc.nextConstID++ + tempIdx := uint16(len(gc.constants)) | constMarker gc.constants = append(gc.constants, new(big.Int).Set(&val)) gc.constantIndex[key] = tempIdx return tempIdx @@ -267,9 +211,9 @@ func (gc *gateCompiler) remapIndices() { // Remap all instruction inputs for i := range gc.instructions { for j := range gc.instructions[i].Inputs { - if gc.instructions[i].Inputs[j]&0x8000 != 0 { + if gc.instructions[i].Inputs[j]&constMarker != 0 { // constant - gc.instructions[i].Inputs[j] &= 0x7fff + gc.instructions[i].Inputs[j] &= ^uint16(constMarker) } else { // variable gc.instructions[i].Inputs[j] += nbConsts @@ -291,7 +235,12 @@ func CompileGateFunction(f gkr.GateFunction, nbInputs int, degree int, solvableV } // Execute the gate function to record operations - f(compiler, inputs...) + out := f(compiler, inputs...) + + // All instructions after the output are no-ops. Prune them and the corresponding variables. + // Henceforth we guarantee that the variable with the highest index is the gate output. + lastEffectiveInstructionIndex := int(out.(compilationVar).id) - compiler.nbInputs + compiler.instructions = compiler.instructions[:lastEffectiveInstructionIndex+1] // Remap indices from temporary layout to final layout compiler.remapIndices() From 5e9239e6b4a2896b7d3e0434216bdb06c233b142 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 12 Jan 2026 16:43:54 -0600 Subject: [PATCH 093/251] fix: gkr.go and gkr_testing.go for bn254 --- constraint/solver/gkrgates/registry.go | 20 +- internal/gkr/bn254/gate_testing.go | 107 ++++------ internal/gkr/bn254/gkr.go | 272 ++++++++----------------- internal/gkr/gkrtypes/types.go | 75 +------ 4 files changed, 133 insertions(+), 341 deletions(-) diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index 9c85a3d591..888864c78b 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -235,20 +235,15 @@ func Get(name gkr.GateName) *gkrtypes.Gate { panic(fmt.Sprintf("gate \"%s\" not found", name)) } -// gateVerifier handles finding/verifying of gate degrees . -// Some of the work is done on a per-curve basis. -type gateVerifier struct { - isAdditive func(f gkr.GateFunction, i int, nbIn int) bool - findDegree func(f gkr.GateFunction, max, nbIn int) (int, error) - verifyGateFunctionDegree func(f gkr.GateFunction, claimedDegree, nbIn int) error - equal func(f1, f2 gkr.GateFunction, nbIn int) bool +type gateTester interface { + IsAdditive(varIndex int) bool + FindDegree(max int) int + VerifyDegree(claimedDegree int) + Equal(other gkr.GateFunction) bool } -func newGateVerifier(curve ecc.ID) (*gateVerifier, error) { - var ( - o gateVerifier - err error - ) +func newGateTester(curve ecc.ID) (gateTester, error) { + switch curve { case ecc.BLS12_377: o.isAdditive = bls12377.IsGateFunctionAdditive @@ -271,6 +266,7 @@ func newGateVerifier(curve ecc.ID) (*gateVerifier, error) { o.verifyGateFunctionDegree = bls24317.VerifyGateFunctionDegree o.equal = bls24317.EqualGateFunction case ecc.BN254: + return bn254.NewGateTester() o.isAdditive = bn254.IsGateFunctionAdditive o.findDegree = bn254.FindGateFunctionDegree o.verifyGateFunctionDegree = bn254.VerifyGateFunctionDegree diff --git a/internal/gkr/bn254/gate_testing.go b/internal/gkr/bn254/gate_testing.go index 490ef7cfc2..e2941fd60d 100644 --- a/internal/gkr/bn254/gate_testing.go +++ b/internal/gkr/bn254/gate_testing.go @@ -19,73 +19,45 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) -// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. -// The arena is pre-loaded with constants and reused across all evaluations. -type compiledGateTester struct { - compiled *gkrtypes.CompiledGate - arena elementArena - nbConsts int - inputBuf []*fr.Element // reusable buffer for input pointers +type gengateTester interface { + IsAdditive() bool + FindDegree() int + VerifyDegree(degree int) + Equal(other gkr.GateFunction) bool } -// newCompiledGateTester compiles a gate function and prepares it for testing. -func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { - compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) - - tester := &compiledGateTester{ - compiled: compiled, - nbConsts: compiled.NbConstants(), - inputBuf: make([]*fr.Element, nbIn), - } - - // Preload constants into arena once - tester.arena.preloadConstants(compiled) - - return tester +type GateTester struct { + evaluator gateEvaluator + nbIn int } -// eval evaluates the gate on the given inputs. -// The arena is automatically reset after each call. -// Returns a copy of the result to avoid arena aliasing issues. -func (t *compiledGateTester) eval(in ...fr.Element) fr.Element { - // Use input buffer to avoid allocation - for i := range in { - t.inputBuf[i] = &in[i] +func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { + return &GateTester{ + evaluator: newGateEvaluator(g), + nbIn: nbIn, } - - resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) - - // Make a copy before resetting the arena - var result fr.Element - result.Set(resultPtr) - - // Reset arena to just constants for next invocation - t.arena.shrinkToConstants(t.nbConsts) - - return result } -// IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f -func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - tester := newCompiledGateTester(f, nbIn) +// IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in e +func (t *GateTester) IsGateFunctionAdditive(e gateEvaluator, i int) bool { // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable // check if f(-, 0, -) + f(-, 2*x1, -) = 2*f(-, x1, -) - x := make(fr.Vector, nbIn) + x := make(fr.Vector, t.nbIn) x.MustSetRandom() x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := tester.eval(in...) + y0 := *e.evaluate(in...) x[i] = x0 copy(in, x) - y1 := tester.eval(in...) + y1 := *e.evaluate(in...) x[i].Double(&x[i]) copy(in, x) - y2 := tester.eval(in...) + y2 := *e.evaluate(in...) y2.Sub(&y2, &y1) y1.Sub(&y1, &y0) @@ -103,11 +75,11 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = tester.eval(in...) + y0 = *e.evaluate(in...) x[i] = x0 copy(in, x) - y1 = tester.eval(in...) + y1 = *e.evaluate(in...) y1.Sub(&y1, &y0) @@ -117,13 +89,12 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { // fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { - nbIn := t.compiled.NbInputs +func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants - fIn := make([]fr.Element, nbIn) - consts := make(fr.Vector, nbIn-1) + fIn := make([]fr.Element, t.nbIn) + consts := make(fr.Vector, t.nbIn-1) consts.MustSetRandom() p := make(polynomial.Polynomial, degreeBound) @@ -135,8 +106,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - result := t.eval(fIn...) - p[i].Set(&result) + p[i].Set(t.evaluator.evaluate(fIn...)) x.Mul(&x, &domain.Generator) } @@ -151,7 +121,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := t.eval(fIn...) + fAt := *t.evaluator.evaluate(fIn...) if !pAt.Equal(&fAt) { return nil } @@ -164,13 +134,12 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { return p[:lastNonZero+1] } -// FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. +// FindDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. -func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - tester := newCompiledGateTester(f, nbIn) +func (t *GateTester) FindDegree(max int) (int, error) { bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := tester.fitPoly(degreeBound); p != nil { + if p := t.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } @@ -180,9 +149,8 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } -func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - tester := newCompiledGateTester(f, nbIn) - if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { +func (t *GateTester) VerifyDegree(claimedDegree int) error { + if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -192,13 +160,12 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error return nil } -// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. -func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - x := make(fr.Vector, nbIn) +// Equal checks if two gate functions are equal, by testing the same at a random point. +func (t *GateTester) Equal(g gkr.GateFunction) bool { + x := make(fr.Vector, t.nbIn) x.MustSetRandom() - fTester := newCompiledGateTester(f, nbIn) - gTester := newCompiledGateTester(g, nbIn) - fAt := fTester.eval(x...) - gAt := gTester.eval(x...) - return fAt.Equal(&gAt) + fAt := t.evaluator.evaluate(x...) + gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gAt := gEval.evaluate(x...) + return fAt.Equal(gAt) } diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 009af57b48..312b724257 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -16,7 +16,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -99,14 +98,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]*fr.Element, len(wire.Inputs)) - for i, uniqueI := range injectionLeftInv { // map from all to unique - inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] + evaluator := newGateEvaluator(wire.Gate.Compiled()) + for _, uniqueI := range injectionLeftInv { // map from all to unique + evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } - var stack elementStack - compiled := wire.Gate.Compiled() - stack.preloadConstants(compiled) - gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) + + gateEvaluation.Set(evaluator.evaluate()) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -665,43 +662,23 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() maxNbIns := 0 + evaluators := make([]gateEvaluator, len(wires)) for i, w := range wires { maxNbIns = max(maxNbIns, len(w.Inputs)) if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) } - var stack elementStack - ins := make([]fr.Element, maxNbIns) - insPtr := make([]*fr.Element, maxNbIns) - - // Track current gate to know when to reload constants - var currentCompiled *gkrtypes.CompiledGate - var nbConsts int - for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { - compiled := w.Gate.Compiled() - - // Preload constants if this is a new gate - if compiled != currentCompiled { - stack.freeElements() - stack.preloadConstants(compiled) - currentCompiled = compiled - nbConsts = compiled.NbConstants() + for _, in := range w.Inputs { + evaluators[wI].pushInput(&a[in][i]) } - - for inI, in := range w.Inputs { - ins[inI] = a[in][i] - insPtr[inI] = &ins[inI] - } - a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) - - // Shrink stack back to just constants for reuse - stack.shrinkToConstants(nbConsts) + a[wI][i].Set(evaluators[wI].evaluate()) } } } @@ -749,96 +726,56 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// elementStack implements a synchronous memory pool underneath to minimize heap allocations. -type elementStack struct { - allocated []*fr.Element - nbUsed int -} +// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the +// result in dst. In practice, dst will be one of the vars but this is not required. +type gateOp func(dst *fr.Element, vars []fr.Element, in []uint16) -// preloadConstants loads gate constants into the stack at indices [0, nbConsts). -// This should be called once per gate to prepare the stack for repeated use. -func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { - for _, constVal := range gate.Constants { - elem := stack.newElement() - elem.SetBigInt(constVal) +func opAdd(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Add(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Add(dst, &vars[in[i]]) } } -// shrinkToConstants resets the stack to contain only constants, ready for next invocation. -func (stack *elementStack) truncate(n int) { - stack.nbUsed = n -} - -func (stack *elementStack) newElement() *fr.Element { - stack.nbUsed++ - if stack.nbUsed >= len(stack.allocated) { - stack.allocated = append(stack.allocated, new(fr.Element)) - } - return stack.allocated[api.nbUsed-1] +func opMulAcc(dst *fr.Element, vars []fr.Element, in []uint16) { + var prod fr.Element + prod.Mul(&vars[in[1]], &vars[in[2]]) + dst.Add(&vars[in[0]], &prod) } -func (stack *elementStack) push(x *fr.Element) { - stack.newElement().Set(x) +func opNeg(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Neg(&vars[in[0]]) } -func opAdd(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Add(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Add(res, x[i]) +func opSub(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Sub(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Sub(dst, &vars[in[i]]) } - return res -} - -func opMulAcc(stack *elementStack, x []*fr.Element) *fr.Element { - // result = x[0] + (x[1] * x[2]) - prod := stack.newElement() - prod.Mul(x[1], x[2]) - res := stack.newElement() - res.Add(x[0], prod) - return res -} - -func opNeg(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Neg(x[0]) - return res } -func opSub(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Sub(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Sub(res, x[i]) +func opMul(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Mul(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Mul(dst, &vars[in[i]]) } - return res -} - -func opMul(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Mul(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Mul(res, x[i]) - } - return res } -func opSumExp17(stack *elementStack, x []*fr.Element) *fr.Element { +func opSumExp17(dst *fr.Element, vars []fr.Element, in []uint16) { // result = (x[0] + x[1] + x[2])^17 - sum := stack.newElement() - sum.Add(x[0], x[1]) - sum.Add(sum, x[2]) - - res := stack.newElement() - res.Mul(sum, sum) // sum² - res.Mul(res, res) // sum⁴ - res.Mul(res, res) // sum⁸ - res.Mul(res, res) // sum¹⁶ - res.Mul(res, sum) // sum¹⁷ - return res + var sum fr.Element + sum.Add(&vars[in[0]], &vars[in[1]]) + sum.Add(&sum, &vars[in[2]]) + + dst.Mul(&sum, &sum) // sum² + dst.Mul(dst, dst) // sum⁴ + dst.Mul(dst, dst) // sum⁸ + dst.Mul(dst, dst) // sum¹⁶ + dst.Mul(dst, &sum) // sum¹⁷ } -func (api *elementStack) Println(a ...frontend.Variable) { +/* +func (stack *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -854,105 +791,70 @@ func (api *elementStack) Println(a ...frontend.Variable) { } } fmt.Println(toPrint...) -} - -// executeCompiledGate executes a compiled gate using curve-specific field operations. -// The stack is expected to already contain constants at indices [0, nbConsts). -// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. -// After execution, the stack can be shrunk back to nbConsts for reuse. -func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*fr.Element) *fr.Element { - - // Instruction executor functions - executors := [...]func(*elementStack, []*fr.Element) *fr.Element{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - - nbConsts := gate.NbConstants() - - // If no instructions, this is an identity-like gate; return first input - if len(gate.Instructions) == 0 { - if gate.NbInputs == 0 { - panic("gate has no inputs and no instructions") - } - return inputs[0] - } - - // Append inputs to stack after constants - // The stack now contains: [constants | inputs] - for _, input := range inputs { - ptr := stack.newElement() - ptr.Set(input) - } - - // Execute instructions, appending results to stack - // The stack grows to: [constants | inputs | results] - for i := range gate.Instructions { - inst := &gate.Instructions[i] - executor := executors[inst.Op] - if executor == nil { - panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) - } - - // Resolve operands from stack using instruction indices - operands := make([]*fr.Element, len(inst.Inputs)) - for j, idx := range inst.Inputs { - operands[j] = stack.allocated[idx] - } - - _ = executor(stack, operands) - // Result is automatically appended to stack by executor - } - - // Last value in stack is the result - resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 - return stack.allocated[resultIdx] -} +}*/ // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate - stack elementStack - nbConsts int + gate *gkrtypes.CompiledGate + vars []fr.Element + frameSize int // number of constants plus currently pushed inputs } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { - eval := &gateEvaluator{ - gate: gate, - nbConsts: gate.NbConstants(), - inputs: make([]*fr.Element, 0, gate.NbInputs), +func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { + e := gateEvaluator{ + gate: gate, + vars: make([]fr.Element, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), } - eval.stack.preloadConstants(gate) - return eval + for i, constVal := range gate.Constants { + e.vars[i].SetBigInt(constVal) + } + return e } // pushInput adds an input to the evaluator's input buffer. -// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +// Inputs must be added in order, and the number of inputs must match the gate's NbInputs. func (e *gateEvaluator) pushInput(input *fr.Element) { - e.inputs = append(e.inputs, input) + e.vars[e.frameSize].Set(input) + e.frameSize++ } -// evaluate executes the gate with the current inputs and returns the result. -// The stack and input buffer are automatically reset after evaluation, +// evaluate adds top to the top of the stack, executes the gate on it and returns the result. +// The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. -func (e *gateEvaluator) evaluate() *fr.Element { - if len(e.inputs) != e.gate.NbInputs { - panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) +// NB! The result is short-lived. It will be overwritten the next time evaluate is called. +func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { + for i := range top { + e.pushInput(&top[i]) } - result := executeCompiledGate(e.gate, &e.stack, e.inputs) + if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + } + + // Instruction executor functions + executors := [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range e.gate.Instructions { + executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + } + + res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] // Reset for next evaluation - e.stack.shrinkToConstants(e.nbConsts) - e.inputs = e.inputs[:0] // Clear input buffer + e.frameSize = e.gate.NbConstants() - return result + return res } diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index a560e61d2c..669f0f1e9a 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -23,7 +23,7 @@ type Gate struct { curves []ecc.ID // curves that the gate is allowed to be used over } -// NewGate creates a new gate function the given parameters: +// NewGate creates a new gate function with the given parameters: // - f: the polynomial function defining the gate // - nbIn: number of inputs to the gate // - degree: total degree of the polynomial. In case of multivariate polynomials, it is the maximum degree over all terms. @@ -47,73 +47,6 @@ func (g *Gate) SupportsCurve(curve ecc.ID) bool { return slices.Contains(g.curves, curve) } -// Evaluate evaluates the gate on the given inputs by interpreting the compiled -// instruction sequence using the provided API. The number of inputs must match -// the gate's fan-in. If not, then it panics. -func (g *Gate) Evaluate(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - if len(in) != g.compiled.NbInputs { - panic("number of inputs does not match gate's NbInputs") - } - - nbConsts := g.compiled.NbConstants() - // Variables array: constants [0..nbConsts), inputs [nbConsts..nbConsts+nbInputs), results after - vars := make([]frontend.Variable, nbConsts+g.compiled.NbInputs+len(g.compiled.Instructions)) - - // Populate constants - for i, c := range g.compiled.Constants { - vars[i] = c - } - - // Populate inputs starting at nbConsts - copy(vars[nbConsts:], in) - - // Execute instructions sequentially - for i, inst := range g.compiled.Instructions { - resultIdx := nbConsts + g.compiled.NbInputs + i - - // Get input variables/constants for this instruction - inputs := make([]frontend.Variable, len(inst.Inputs)) - for j, idx := range inst.Inputs { - inputs[j] = vars[idx] - } - - // Execute the operation - switch inst.Op { - case OpAdd: - result := inputs[0] - for j := 1; j < len(inputs); j++ { - result = api.Add(result, inputs[j]) - } - vars[resultIdx] = result - case OpSub: - result := inputs[0] - for j := 1; j < len(inputs); j++ { - result = api.Sub(result, inputs[j]) - } - vars[resultIdx] = result - case OpMul: - result := inputs[0] - for j := 1; j < len(inputs); j++ { - result = api.Mul(result, inputs[j]) - } - vars[resultIdx] = result - case OpNeg: - vars[resultIdx] = api.Neg(inputs[0]) - case OpMulAcc: - // result = inputs[0] + (inputs[1] * inputs[2]) - vars[resultIdx] = api.MulAcc(inputs[0], inputs[1], inputs[2]) - case OpSumExp17: - // result = (inputs[0] + inputs[1] + inputs[2])^17 - vars[resultIdx] = api.SumExp17(inputs[0], inputs[1], inputs[2]) - default: - panic("unknown operation") - } - } - - // The last computed value is the result - return vars[len(vars)-1] -} - // Degree returns the total degree of the gate's polynomial e.g. Degree(xy²) = 3 func (g *Gate) Degree() int { return g.degree @@ -135,12 +68,6 @@ func (g *Gate) Compiled() *CompiledGate { return g.compiled } -// SetCompiled sets the compiled form of the gate. This is used when loading -// a pre-compiled gate. -func (g *Gate) SetCompiled(compiled *CompiledGate) { - g.compiled = compiled -} - // Wire represents a wire in the GKR circuit. A wire is defined through its gate // and its inputs. type Wire struct { From 82aad28e4a6fbae9ee3fb9e182a391ca2317d7d7 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 12 Jan 2026 17:08:36 -0600 Subject: [PATCH 094/251] build: generify gate_testing --- .../backend/template/gkr/gate_testing.go.tmpl | 108 ++++++------------ internal/gkr/bls12-377/gate_testing.go | 105 ++++++----------- internal/gkr/bls12-381/gate_testing.go | 105 ++++++----------- internal/gkr/bls24-315/gate_testing.go | 105 ++++++----------- internal/gkr/bls24-317/gate_testing.go | 105 ++++++----------- internal/gkr/bn254/gate_testing.go | 22 ++-- internal/gkr/bw6-633/gate_testing.go | 105 ++++++----------- internal/gkr/bw6-761/gate_testing.go | 105 ++++++----------- 8 files changed, 232 insertions(+), 528 deletions(-) diff --git a/internal/generator/backend/template/gkr/gate_testing.go.tmpl b/internal/generator/backend/template/gkr/gate_testing.go.tmpl index 187abca35e..310afb216d 100644 --- a/internal/generator/backend/template/gkr/gate_testing.go.tmpl +++ b/internal/generator/backend/template/gkr/gate_testing.go.tmpl @@ -13,73 +13,38 @@ import ( "slices" ) -// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. -// The arena is pre-loaded with constants and reused across all evaluations. -type compiledGateTester struct { - compiled *gkrtypes.CompiledGate - arena elementArena - nbConsts int - inputBuf []*{{ .ElementType }} // reusable buffer for input pointers +type GateTester struct { + evaluator gateEvaluator + nbIn int } -// newCompiledGateTester compiles a gate function and prepares it for testing. -func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { - compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) - - tester := &compiledGateTester{ - compiled: compiled, - nbConsts: compiled.NbConstants(), - inputBuf: make([]*{{ .ElementType }}, nbIn), +func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { + return &GateTester{ + evaluator: newGateEvaluator(g), + nbIn: nbIn, } - - // Preload constants into arena once - tester.arena.preloadConstants(compiled) - - return tester } -// eval evaluates the gate on the given inputs. -// The arena is automatically reset after each call. -// Returns a copy of the result to avoid arena aliasing issues. -func (t *compiledGateTester) eval(in ...{{ .ElementType }}) {{ .ElementType }} { - // Use input buffer to avoid allocation - for i := range in { - t.inputBuf[i] = &in[i] - } - - resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) - - // Make a copy before resetting the arena - var result {{ .ElementType }} - result.Set(resultPtr) - - // Reset arena to just constants for next invocation - t.arena.shrinkToConstants(t.nbConsts) - - return result -} - -// IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f -func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - tester := newCompiledGateTester(f, nbIn) +// IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e +func (t *GateTester) IsAdditive(i int) bool { // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable // check if f(-, 0, -) + f(-, 2*x1, -) = 2*f(-, x1, -) - x := make({{.FieldPackageName}}.Vector, nbIn) + x := make({{.FieldPackageName}}.Vector, t.nbIn) x.MustSetRandom() x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := tester.eval(in...) + y0 := *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 := tester.eval(in...) + y1 := *t.evaluator.evaluate(in...) x[i].Double(&x[i]) copy(in, x) - y2 := tester.eval(in...) + y2 := *t.evaluator.evaluate(in...) y2.Sub(&y2, &y1) y1.Sub(&y1, &y0) @@ -97,12 +62,11 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = tester.eval(in...) + y0 = *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 = tester.eval(in...) - + y1 = *t.evaluator.evaluate(in...) y1.Sub(&y1, &y0) return y1.Equal(&y2) @@ -111,13 +75,12 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { // fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { - nbIn := t.compiled.NbInputs +func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants - fIn := make([]{{ .ElementType }}, nbIn) - consts := make({{.FieldPackageName}}.Vector, nbIn-1) + fIn := make([]{{ .ElementType }}, t.nbIn) + consts := make({{.FieldPackageName}}.Vector, t.nbIn-1) consts.MustSetRandom() p := make(polynomial.Polynomial, degreeBound) @@ -130,8 +93,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - result := t.eval(fIn...) - p[i].Set(&result) + p[i].Set(t.evaluator.evaluate(fIn...)) x.Mul(&x, &domain.Generator) } @@ -147,8 +109,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { for j := range consts { fIn[j+1].Mul(&x[i], &consts[j]) } - result := t.eval(fIn...) - p[i].Set(&result) + p[i].Set(t.evaluator.evaluate(fIn...)) } // obtain p's coefficients @@ -164,7 +125,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := t.eval(fIn...) + fAt := *t.evaluator.evaluate(fIn...) if !pAt.Equal(&fAt) { return nil } @@ -177,13 +138,12 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { return p[:lastNonZero+1] } -// FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. +// FindDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. -func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - tester := newCompiledGateTester(f, nbIn) +func (t *GateTester) FindDegree(max int) (int, error) { bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := tester.fitPoly(degreeBound); p != nil { + if p := t.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } @@ -193,9 +153,8 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } -func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - tester := newCompiledGateTester(f, nbIn) - if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { +func (t *GateTester) VerifyDegree(claimedDegree int) error { + if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -205,15 +164,14 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error return nil } -// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. -func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - x := make({{.FieldPackageName}}.Vector, nbIn) +// Equal checks if two gate functions are equal, by testing the same at a random point. +func (t *GateTester) Equal(g gkr.GateFunction) bool { + x := make({{.FieldPackageName}}.Vector, t.nbIn) x.MustSetRandom() - fTester := newCompiledGateTester(f, nbIn) - gTester := newCompiledGateTester(g, nbIn) - fAt := fTester.eval(x...) - gAt := gTester.eval(x...) - return fAt.Equal(&gAt) + fAt := t.evaluator.evaluate(x...) + gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gAt := gEval.evaluate(x...) + return fAt.Equal(gAt) } {{- if not .CanUseFFT }} diff --git a/internal/gkr/bls12-377/gate_testing.go b/internal/gkr/bls12-377/gate_testing.go index 34d96c6b5d..8d7ff44a85 100644 --- a/internal/gkr/bls12-377/gate_testing.go +++ b/internal/gkr/bls12-377/gate_testing.go @@ -19,73 +19,38 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) -// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. -// The arena is pre-loaded with constants and reused across all evaluations. -type compiledGateTester struct { - compiled *gkrtypes.CompiledGate - arena elementArena - nbConsts int - inputBuf []*fr.Element // reusable buffer for input pointers +type GateTester struct { + evaluator gateEvaluator + nbIn int } -// newCompiledGateTester compiles a gate function and prepares it for testing. -func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { - compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) - - tester := &compiledGateTester{ - compiled: compiled, - nbConsts: compiled.NbConstants(), - inputBuf: make([]*fr.Element, nbIn), +func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { + return &GateTester{ + evaluator: newGateEvaluator(g), + nbIn: nbIn, } - - // Preload constants into arena once - tester.arena.preloadConstants(compiled) - - return tester } -// eval evaluates the gate on the given inputs. -// The arena is automatically reset after each call. -// Returns a copy of the result to avoid arena aliasing issues. -func (t *compiledGateTester) eval(in ...fr.Element) fr.Element { - // Use input buffer to avoid allocation - for i := range in { - t.inputBuf[i] = &in[i] - } - - resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) - - // Make a copy before resetting the arena - var result fr.Element - result.Set(resultPtr) - - // Reset arena to just constants for next invocation - t.arena.shrinkToConstants(t.nbConsts) - - return result -} - -// IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f -func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - tester := newCompiledGateTester(f, nbIn) +// IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e +func (t *GateTester) IsAdditive(i int) bool { // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable // check if f(-, 0, -) + f(-, 2*x1, -) = 2*f(-, x1, -) - x := make(fr.Vector, nbIn) + x := make(fr.Vector, t.nbIn) x.MustSetRandom() x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := tester.eval(in...) + y0 := *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 := tester.eval(in...) + y1 := *t.evaluator.evaluate(in...) x[i].Double(&x[i]) copy(in, x) - y2 := tester.eval(in...) + y2 := *t.evaluator.evaluate(in...) y2.Sub(&y2, &y1) y1.Sub(&y1, &y0) @@ -103,12 +68,11 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = tester.eval(in...) + y0 = *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 = tester.eval(in...) - + y1 = *t.evaluator.evaluate(in...) y1.Sub(&y1, &y0) return y1.Equal(&y2) @@ -117,13 +81,12 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { // fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { - nbIn := t.compiled.NbInputs +func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants - fIn := make([]fr.Element, nbIn) - consts := make(fr.Vector, nbIn-1) + fIn := make([]fr.Element, t.nbIn) + consts := make(fr.Vector, t.nbIn-1) consts.MustSetRandom() p := make(polynomial.Polynomial, degreeBound) @@ -135,8 +98,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - result := t.eval(fIn...) - p[i].Set(&result) + p[i].Set(t.evaluator.evaluate(fIn...)) x.Mul(&x, &domain.Generator) } @@ -151,7 +113,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := t.eval(fIn...) + fAt := *t.evaluator.evaluate(fIn...) if !pAt.Equal(&fAt) { return nil } @@ -164,13 +126,12 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { return p[:lastNonZero+1] } -// FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. +// FindDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. -func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - tester := newCompiledGateTester(f, nbIn) +func (t *GateTester) FindDegree(max int) (int, error) { bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := tester.fitPoly(degreeBound); p != nil { + if p := t.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } @@ -180,9 +141,8 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } -func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - tester := newCompiledGateTester(f, nbIn) - if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { +func (t *GateTester) VerifyDegree(claimedDegree int) error { + if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -192,13 +152,12 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error return nil } -// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. -func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - x := make(fr.Vector, nbIn) +// Equal checks if two gate functions are equal, by testing the same at a random point. +func (t *GateTester) Equal(g gkr.GateFunction) bool { + x := make(fr.Vector, t.nbIn) x.MustSetRandom() - fTester := newCompiledGateTester(f, nbIn) - gTester := newCompiledGateTester(g, nbIn) - fAt := fTester.eval(x...) - gAt := gTester.eval(x...) - return fAt.Equal(&gAt) + fAt := t.evaluator.evaluate(x...) + gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gAt := gEval.evaluate(x...) + return fAt.Equal(gAt) } diff --git a/internal/gkr/bls12-381/gate_testing.go b/internal/gkr/bls12-381/gate_testing.go index 2d3fd4e6be..f1ca5e423f 100644 --- a/internal/gkr/bls12-381/gate_testing.go +++ b/internal/gkr/bls12-381/gate_testing.go @@ -19,73 +19,38 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) -// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. -// The arena is pre-loaded with constants and reused across all evaluations. -type compiledGateTester struct { - compiled *gkrtypes.CompiledGate - arena elementArena - nbConsts int - inputBuf []*fr.Element // reusable buffer for input pointers +type GateTester struct { + evaluator gateEvaluator + nbIn int } -// newCompiledGateTester compiles a gate function and prepares it for testing. -func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { - compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) - - tester := &compiledGateTester{ - compiled: compiled, - nbConsts: compiled.NbConstants(), - inputBuf: make([]*fr.Element, nbIn), +func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { + return &GateTester{ + evaluator: newGateEvaluator(g), + nbIn: nbIn, } - - // Preload constants into arena once - tester.arena.preloadConstants(compiled) - - return tester } -// eval evaluates the gate on the given inputs. -// The arena is automatically reset after each call. -// Returns a copy of the result to avoid arena aliasing issues. -func (t *compiledGateTester) eval(in ...fr.Element) fr.Element { - // Use input buffer to avoid allocation - for i := range in { - t.inputBuf[i] = &in[i] - } - - resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) - - // Make a copy before resetting the arena - var result fr.Element - result.Set(resultPtr) - - // Reset arena to just constants for next invocation - t.arena.shrinkToConstants(t.nbConsts) - - return result -} - -// IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f -func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - tester := newCompiledGateTester(f, nbIn) +// IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e +func (t *GateTester) IsAdditive(i int) bool { // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable // check if f(-, 0, -) + f(-, 2*x1, -) = 2*f(-, x1, -) - x := make(fr.Vector, nbIn) + x := make(fr.Vector, t.nbIn) x.MustSetRandom() x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := tester.eval(in...) + y0 := *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 := tester.eval(in...) + y1 := *t.evaluator.evaluate(in...) x[i].Double(&x[i]) copy(in, x) - y2 := tester.eval(in...) + y2 := *t.evaluator.evaluate(in...) y2.Sub(&y2, &y1) y1.Sub(&y1, &y0) @@ -103,12 +68,11 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = tester.eval(in...) + y0 = *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 = tester.eval(in...) - + y1 = *t.evaluator.evaluate(in...) y1.Sub(&y1, &y0) return y1.Equal(&y2) @@ -117,13 +81,12 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { // fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { - nbIn := t.compiled.NbInputs +func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants - fIn := make([]fr.Element, nbIn) - consts := make(fr.Vector, nbIn-1) + fIn := make([]fr.Element, t.nbIn) + consts := make(fr.Vector, t.nbIn-1) consts.MustSetRandom() p := make(polynomial.Polynomial, degreeBound) @@ -135,8 +98,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - result := t.eval(fIn...) - p[i].Set(&result) + p[i].Set(t.evaluator.evaluate(fIn...)) x.Mul(&x, &domain.Generator) } @@ -151,7 +113,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := t.eval(fIn...) + fAt := *t.evaluator.evaluate(fIn...) if !pAt.Equal(&fAt) { return nil } @@ -164,13 +126,12 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { return p[:lastNonZero+1] } -// FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. +// FindDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. -func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - tester := newCompiledGateTester(f, nbIn) +func (t *GateTester) FindDegree(max int) (int, error) { bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := tester.fitPoly(degreeBound); p != nil { + if p := t.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } @@ -180,9 +141,8 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } -func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - tester := newCompiledGateTester(f, nbIn) - if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { +func (t *GateTester) VerifyDegree(claimedDegree int) error { + if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -192,13 +152,12 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error return nil } -// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. -func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - x := make(fr.Vector, nbIn) +// Equal checks if two gate functions are equal, by testing the same at a random point. +func (t *GateTester) Equal(g gkr.GateFunction) bool { + x := make(fr.Vector, t.nbIn) x.MustSetRandom() - fTester := newCompiledGateTester(f, nbIn) - gTester := newCompiledGateTester(g, nbIn) - fAt := fTester.eval(x...) - gAt := gTester.eval(x...) - return fAt.Equal(&gAt) + fAt := t.evaluator.evaluate(x...) + gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gAt := gEval.evaluate(x...) + return fAt.Equal(gAt) } diff --git a/internal/gkr/bls24-315/gate_testing.go b/internal/gkr/bls24-315/gate_testing.go index 730d6e4d2c..4273a01438 100644 --- a/internal/gkr/bls24-315/gate_testing.go +++ b/internal/gkr/bls24-315/gate_testing.go @@ -19,73 +19,38 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) -// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. -// The arena is pre-loaded with constants and reused across all evaluations. -type compiledGateTester struct { - compiled *gkrtypes.CompiledGate - arena elementArena - nbConsts int - inputBuf []*fr.Element // reusable buffer for input pointers +type GateTester struct { + evaluator gateEvaluator + nbIn int } -// newCompiledGateTester compiles a gate function and prepares it for testing. -func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { - compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) - - tester := &compiledGateTester{ - compiled: compiled, - nbConsts: compiled.NbConstants(), - inputBuf: make([]*fr.Element, nbIn), +func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { + return &GateTester{ + evaluator: newGateEvaluator(g), + nbIn: nbIn, } - - // Preload constants into arena once - tester.arena.preloadConstants(compiled) - - return tester } -// eval evaluates the gate on the given inputs. -// The arena is automatically reset after each call. -// Returns a copy of the result to avoid arena aliasing issues. -func (t *compiledGateTester) eval(in ...fr.Element) fr.Element { - // Use input buffer to avoid allocation - for i := range in { - t.inputBuf[i] = &in[i] - } - - resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) - - // Make a copy before resetting the arena - var result fr.Element - result.Set(resultPtr) - - // Reset arena to just constants for next invocation - t.arena.shrinkToConstants(t.nbConsts) - - return result -} - -// IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f -func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - tester := newCompiledGateTester(f, nbIn) +// IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e +func (t *GateTester) IsAdditive(i int) bool { // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable // check if f(-, 0, -) + f(-, 2*x1, -) = 2*f(-, x1, -) - x := make(fr.Vector, nbIn) + x := make(fr.Vector, t.nbIn) x.MustSetRandom() x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := tester.eval(in...) + y0 := *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 := tester.eval(in...) + y1 := *t.evaluator.evaluate(in...) x[i].Double(&x[i]) copy(in, x) - y2 := tester.eval(in...) + y2 := *t.evaluator.evaluate(in...) y2.Sub(&y2, &y1) y1.Sub(&y1, &y0) @@ -103,12 +68,11 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = tester.eval(in...) + y0 = *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 = tester.eval(in...) - + y1 = *t.evaluator.evaluate(in...) y1.Sub(&y1, &y0) return y1.Equal(&y2) @@ -117,13 +81,12 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { // fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { - nbIn := t.compiled.NbInputs +func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants - fIn := make([]fr.Element, nbIn) - consts := make(fr.Vector, nbIn-1) + fIn := make([]fr.Element, t.nbIn) + consts := make(fr.Vector, t.nbIn-1) consts.MustSetRandom() p := make(polynomial.Polynomial, degreeBound) @@ -135,8 +98,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - result := t.eval(fIn...) - p[i].Set(&result) + p[i].Set(t.evaluator.evaluate(fIn...)) x.Mul(&x, &domain.Generator) } @@ -151,7 +113,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := t.eval(fIn...) + fAt := *t.evaluator.evaluate(fIn...) if !pAt.Equal(&fAt) { return nil } @@ -164,13 +126,12 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { return p[:lastNonZero+1] } -// FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. +// FindDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. -func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - tester := newCompiledGateTester(f, nbIn) +func (t *GateTester) FindDegree(max int) (int, error) { bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := tester.fitPoly(degreeBound); p != nil { + if p := t.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } @@ -180,9 +141,8 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } -func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - tester := newCompiledGateTester(f, nbIn) - if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { +func (t *GateTester) VerifyDegree(claimedDegree int) error { + if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -192,13 +152,12 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error return nil } -// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. -func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - x := make(fr.Vector, nbIn) +// Equal checks if two gate functions are equal, by testing the same at a random point. +func (t *GateTester) Equal(g gkr.GateFunction) bool { + x := make(fr.Vector, t.nbIn) x.MustSetRandom() - fTester := newCompiledGateTester(f, nbIn) - gTester := newCompiledGateTester(g, nbIn) - fAt := fTester.eval(x...) - gAt := gTester.eval(x...) - return fAt.Equal(&gAt) + fAt := t.evaluator.evaluate(x...) + gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gAt := gEval.evaluate(x...) + return fAt.Equal(gAt) } diff --git a/internal/gkr/bls24-317/gate_testing.go b/internal/gkr/bls24-317/gate_testing.go index 37e332bd44..af076a174b 100644 --- a/internal/gkr/bls24-317/gate_testing.go +++ b/internal/gkr/bls24-317/gate_testing.go @@ -19,73 +19,38 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) -// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. -// The arena is pre-loaded with constants and reused across all evaluations. -type compiledGateTester struct { - compiled *gkrtypes.CompiledGate - arena elementArena - nbConsts int - inputBuf []*fr.Element // reusable buffer for input pointers +type GateTester struct { + evaluator gateEvaluator + nbIn int } -// newCompiledGateTester compiles a gate function and prepares it for testing. -func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { - compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) - - tester := &compiledGateTester{ - compiled: compiled, - nbConsts: compiled.NbConstants(), - inputBuf: make([]*fr.Element, nbIn), +func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { + return &GateTester{ + evaluator: newGateEvaluator(g), + nbIn: nbIn, } - - // Preload constants into arena once - tester.arena.preloadConstants(compiled) - - return tester } -// eval evaluates the gate on the given inputs. -// The arena is automatically reset after each call. -// Returns a copy of the result to avoid arena aliasing issues. -func (t *compiledGateTester) eval(in ...fr.Element) fr.Element { - // Use input buffer to avoid allocation - for i := range in { - t.inputBuf[i] = &in[i] - } - - resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) - - // Make a copy before resetting the arena - var result fr.Element - result.Set(resultPtr) - - // Reset arena to just constants for next invocation - t.arena.shrinkToConstants(t.nbConsts) - - return result -} - -// IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f -func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - tester := newCompiledGateTester(f, nbIn) +// IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e +func (t *GateTester) IsAdditive(i int) bool { // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable // check if f(-, 0, -) + f(-, 2*x1, -) = 2*f(-, x1, -) - x := make(fr.Vector, nbIn) + x := make(fr.Vector, t.nbIn) x.MustSetRandom() x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := tester.eval(in...) + y0 := *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 := tester.eval(in...) + y1 := *t.evaluator.evaluate(in...) x[i].Double(&x[i]) copy(in, x) - y2 := tester.eval(in...) + y2 := *t.evaluator.evaluate(in...) y2.Sub(&y2, &y1) y1.Sub(&y1, &y0) @@ -103,12 +68,11 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = tester.eval(in...) + y0 = *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 = tester.eval(in...) - + y1 = *t.evaluator.evaluate(in...) y1.Sub(&y1, &y0) return y1.Equal(&y2) @@ -117,13 +81,12 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { // fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { - nbIn := t.compiled.NbInputs +func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants - fIn := make([]fr.Element, nbIn) - consts := make(fr.Vector, nbIn-1) + fIn := make([]fr.Element, t.nbIn) + consts := make(fr.Vector, t.nbIn-1) consts.MustSetRandom() p := make(polynomial.Polynomial, degreeBound) @@ -135,8 +98,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - result := t.eval(fIn...) - p[i].Set(&result) + p[i].Set(t.evaluator.evaluate(fIn...)) x.Mul(&x, &domain.Generator) } @@ -151,7 +113,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := t.eval(fIn...) + fAt := *t.evaluator.evaluate(fIn...) if !pAt.Equal(&fAt) { return nil } @@ -164,13 +126,12 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { return p[:lastNonZero+1] } -// FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. +// FindDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. -func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - tester := newCompiledGateTester(f, nbIn) +func (t *GateTester) FindDegree(max int) (int, error) { bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := tester.fitPoly(degreeBound); p != nil { + if p := t.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } @@ -180,9 +141,8 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } -func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - tester := newCompiledGateTester(f, nbIn) - if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { +func (t *GateTester) VerifyDegree(claimedDegree int) error { + if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -192,13 +152,12 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error return nil } -// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. -func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - x := make(fr.Vector, nbIn) +// Equal checks if two gate functions are equal, by testing the same at a random point. +func (t *GateTester) Equal(g gkr.GateFunction) bool { + x := make(fr.Vector, t.nbIn) x.MustSetRandom() - fTester := newCompiledGateTester(f, nbIn) - gTester := newCompiledGateTester(g, nbIn) - fAt := fTester.eval(x...) - gAt := gTester.eval(x...) - return fAt.Equal(&gAt) + fAt := t.evaluator.evaluate(x...) + gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gAt := gEval.evaluate(x...) + return fAt.Equal(gAt) } diff --git a/internal/gkr/bn254/gate_testing.go b/internal/gkr/bn254/gate_testing.go index e2941fd60d..4f40d280a9 100644 --- a/internal/gkr/bn254/gate_testing.go +++ b/internal/gkr/bn254/gate_testing.go @@ -19,13 +19,6 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) -type gengateTester interface { - IsAdditive() bool - FindDegree() int - VerifyDegree(degree int) - Equal(other gkr.GateFunction) bool -} - type GateTester struct { evaluator gateEvaluator nbIn int @@ -38,8 +31,8 @@ func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { } } -// IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in e -func (t *GateTester) IsGateFunctionAdditive(e gateEvaluator, i int) bool { +// IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e +func (t *GateTester) IsAdditive(i int) bool { // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable @@ -49,15 +42,15 @@ func (t *GateTester) IsGateFunctionAdditive(e gateEvaluator, i int) bool { x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := *e.evaluate(in...) + y0 := *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 := *e.evaluate(in...) + y1 := *t.evaluator.evaluate(in...) x[i].Double(&x[i]) copy(in, x) - y2 := *e.evaluate(in...) + y2 := *t.evaluator.evaluate(in...) y2.Sub(&y2, &y1) y1.Sub(&y1, &y0) @@ -75,12 +68,11 @@ func (t *GateTester) IsGateFunctionAdditive(e gateEvaluator, i int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = *e.evaluate(in...) + y0 = *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 = *e.evaluate(in...) - + y1 = *t.evaluator.evaluate(in...) y1.Sub(&y1, &y0) return y1.Equal(&y2) diff --git a/internal/gkr/bw6-633/gate_testing.go b/internal/gkr/bw6-633/gate_testing.go index ea83391f9d..05415ac00e 100644 --- a/internal/gkr/bw6-633/gate_testing.go +++ b/internal/gkr/bw6-633/gate_testing.go @@ -19,73 +19,38 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) -// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. -// The arena is pre-loaded with constants and reused across all evaluations. -type compiledGateTester struct { - compiled *gkrtypes.CompiledGate - arena elementArena - nbConsts int - inputBuf []*fr.Element // reusable buffer for input pointers +type GateTester struct { + evaluator gateEvaluator + nbIn int } -// newCompiledGateTester compiles a gate function and prepares it for testing. -func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { - compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) - - tester := &compiledGateTester{ - compiled: compiled, - nbConsts: compiled.NbConstants(), - inputBuf: make([]*fr.Element, nbIn), +func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { + return &GateTester{ + evaluator: newGateEvaluator(g), + nbIn: nbIn, } - - // Preload constants into arena once - tester.arena.preloadConstants(compiled) - - return tester } -// eval evaluates the gate on the given inputs. -// The arena is automatically reset after each call. -// Returns a copy of the result to avoid arena aliasing issues. -func (t *compiledGateTester) eval(in ...fr.Element) fr.Element { - // Use input buffer to avoid allocation - for i := range in { - t.inputBuf[i] = &in[i] - } - - resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) - - // Make a copy before resetting the arena - var result fr.Element - result.Set(resultPtr) - - // Reset arena to just constants for next invocation - t.arena.shrinkToConstants(t.nbConsts) - - return result -} - -// IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f -func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - tester := newCompiledGateTester(f, nbIn) +// IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e +func (t *GateTester) IsAdditive(i int) bool { // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable // check if f(-, 0, -) + f(-, 2*x1, -) = 2*f(-, x1, -) - x := make(fr.Vector, nbIn) + x := make(fr.Vector, t.nbIn) x.MustSetRandom() x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := tester.eval(in...) + y0 := *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 := tester.eval(in...) + y1 := *t.evaluator.evaluate(in...) x[i].Double(&x[i]) copy(in, x) - y2 := tester.eval(in...) + y2 := *t.evaluator.evaluate(in...) y2.Sub(&y2, &y1) y1.Sub(&y1, &y0) @@ -103,12 +68,11 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = tester.eval(in...) + y0 = *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 = tester.eval(in...) - + y1 = *t.evaluator.evaluate(in...) y1.Sub(&y1, &y0) return y1.Equal(&y2) @@ -117,13 +81,12 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { // fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { - nbIn := t.compiled.NbInputs +func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants - fIn := make([]fr.Element, nbIn) - consts := make(fr.Vector, nbIn-1) + fIn := make([]fr.Element, t.nbIn) + consts := make(fr.Vector, t.nbIn-1) consts.MustSetRandom() p := make(polynomial.Polynomial, degreeBound) @@ -135,8 +98,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - result := t.eval(fIn...) - p[i].Set(&result) + p[i].Set(t.evaluator.evaluate(fIn...)) x.Mul(&x, &domain.Generator) } @@ -151,7 +113,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := t.eval(fIn...) + fAt := *t.evaluator.evaluate(fIn...) if !pAt.Equal(&fAt) { return nil } @@ -164,13 +126,12 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { return p[:lastNonZero+1] } -// FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. +// FindDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. -func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - tester := newCompiledGateTester(f, nbIn) +func (t *GateTester) FindDegree(max int) (int, error) { bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := tester.fitPoly(degreeBound); p != nil { + if p := t.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } @@ -180,9 +141,8 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } -func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - tester := newCompiledGateTester(f, nbIn) - if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { +func (t *GateTester) VerifyDegree(claimedDegree int) error { + if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -192,13 +152,12 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error return nil } -// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. -func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - x := make(fr.Vector, nbIn) +// Equal checks if two gate functions are equal, by testing the same at a random point. +func (t *GateTester) Equal(g gkr.GateFunction) bool { + x := make(fr.Vector, t.nbIn) x.MustSetRandom() - fTester := newCompiledGateTester(f, nbIn) - gTester := newCompiledGateTester(g, nbIn) - fAt := fTester.eval(x...) - gAt := gTester.eval(x...) - return fAt.Equal(&gAt) + fAt := t.evaluator.evaluate(x...) + gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gAt := gEval.evaluate(x...) + return fAt.Equal(gAt) } diff --git a/internal/gkr/bw6-761/gate_testing.go b/internal/gkr/bw6-761/gate_testing.go index 465c0c2893..8cc8ed50cc 100644 --- a/internal/gkr/bw6-761/gate_testing.go +++ b/internal/gkr/bw6-761/gate_testing.go @@ -19,73 +19,38 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) -// compiledGateTester wraps a compiled gate with an arena for efficient repeated evaluation. -// The arena is pre-loaded with constants and reused across all evaluations. -type compiledGateTester struct { - compiled *gkrtypes.CompiledGate - arena elementArena - nbConsts int - inputBuf []*fr.Element // reusable buffer for input pointers +type GateTester struct { + evaluator gateEvaluator + nbIn int } -// newCompiledGateTester compiles a gate function and prepares it for testing. -func newCompiledGateTester(f gkr.GateFunction, nbIn int) *compiledGateTester { - compiled := gkrtypes.CompileGateFunction(f, nbIn, -1, -1, []ecc.ID{}) - - tester := &compiledGateTester{ - compiled: compiled, - nbConsts: compiled.NbConstants(), - inputBuf: make([]*fr.Element, nbIn), +func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { + return &GateTester{ + evaluator: newGateEvaluator(g), + nbIn: nbIn, } - - // Preload constants into arena once - tester.arena.preloadConstants(compiled) - - return tester } -// eval evaluates the gate on the given inputs. -// The arena is automatically reset after each call. -// Returns a copy of the result to avoid arena aliasing issues. -func (t *compiledGateTester) eval(in ...fr.Element) fr.Element { - // Use input buffer to avoid allocation - for i := range in { - t.inputBuf[i] = &in[i] - } - - resultPtr := executeCompiledGate(t.compiled, &t.arena, t.inputBuf) - - // Make a copy before resetting the arena - var result fr.Element - result.Set(resultPtr) - - // Reset arena to just constants for next invocation - t.arena.shrinkToConstants(t.nbConsts) - - return result -} - -// IsGateFunctionAdditive returns whether x_i occurs only in a monomial of total degree 1 in f -func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { - tester := newCompiledGateTester(f, nbIn) +// IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e +func (t *GateTester) IsAdditive(i int) bool { // fix all variables except the i-th one at random points // pick random value x1 for the i-th variable // check if f(-, 0, -) + f(-, 2*x1, -) = 2*f(-, x1, -) - x := make(fr.Vector, nbIn) + x := make(fr.Vector, t.nbIn) x.MustSetRandom() x0 := x[i] x[i].SetZero() in := slices.Clone(x) - y0 := tester.eval(in...) + y0 := *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 := tester.eval(in...) + y1 := *t.evaluator.evaluate(in...) x[i].Double(&x[i]) copy(in, x) - y2 := tester.eval(in...) + y2 := *t.evaluator.evaluate(in...) y2.Sub(&y2, &y1) y1.Sub(&y1, &y0) @@ -103,12 +68,11 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { x.MustSetRandom() x[i].SetZero() copy(in, x) - y0 = tester.eval(in...) + y0 = *t.evaluator.evaluate(in...) x[i] = x0 copy(in, x) - y1 = tester.eval(in...) - + y1 = *t.evaluator.evaluate(in...) y1.Sub(&y1, &y0) return y1.Equal(&y2) @@ -117,13 +81,12 @@ func IsGateFunctionAdditive(f gkr.GateFunction, i, nbIn int) bool { // fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. // degreeBound must be a power of 2. // It returns the polynomial if successful, nil otherwise -func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { - nbIn := t.compiled.NbInputs +func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants - fIn := make([]fr.Element, nbIn) - consts := make(fr.Vector, nbIn-1) + fIn := make([]fr.Element, t.nbIn) + consts := make(fr.Vector, t.nbIn-1) consts.MustSetRandom() p := make(polynomial.Polynomial, degreeBound) @@ -135,8 +98,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { for j := range consts { fIn[j+1].Mul(&x, &consts[j]) } - result := t.eval(fIn...) - p[i].Set(&result) + p[i].Set(t.evaluator.evaluate(fIn...)) x.Mul(&x, &domain.Generator) } @@ -151,7 +113,7 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { fIn[i+1].Mul(&fIn[0], &consts[i]) } pAt := p.Eval(&fIn[0]) - fAt := t.eval(fIn...) + fAt := *t.evaluator.evaluate(fIn...) if !pAt.Equal(&fAt) { return nil } @@ -164,13 +126,12 @@ func (t *compiledGateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { return p[:lastNonZero+1] } -// FindGateFunctionDegree returns the degree of the gate function, or -1 if it fails. +// FindDegree returns the degree of the gate function, or -1 if it fails. // Failure could be due to the degree being higher than max or the function not being a polynomial at all. -func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { - tester := newCompiledGateTester(f, nbIn) +func (t *GateTester) FindDegree(max int) (int, error) { bound := uint64(max) + 1 for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := tester.fitPoly(degreeBound); p != nil { + if p := t.fitPoly(degreeBound); p != nil { if len(p) == 0 { return -1, gkrtypes.ErrZeroFunction } @@ -180,9 +141,8 @@ func FindGateFunctionDegree(f gkr.GateFunction, max, nbIn int) (int, error) { return -1, fmt.Errorf("could not find a degree: tried up to %d", max) } -func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - tester := newCompiledGateTester(f, nbIn) - if p := tester.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { +func (t *GateTester) VerifyDegree(claimedDegree int) error { + if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -192,13 +152,12 @@ func VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error return nil } -// EqualGateFunction checks if two gate functions are equal, by testing the same at a random point. -func EqualGateFunction(f gkr.GateFunction, g gkr.GateFunction, nbIn int) bool { - x := make(fr.Vector, nbIn) +// Equal checks if two gate functions are equal, by testing the same at a random point. +func (t *GateTester) Equal(g gkr.GateFunction) bool { + x := make(fr.Vector, t.nbIn) x.MustSetRandom() - fTester := newCompiledGateTester(f, nbIn) - gTester := newCompiledGateTester(g, nbIn) - fAt := fTester.eval(x...) - gAt := gTester.eval(x...) - return fAt.Equal(&gAt) + fAt := t.evaluator.evaluate(x...) + gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gAt := gEval.evaluate(x...) + return fAt.Equal(gAt) } From 2a854dcc396ddfbbdd11c89c71f4b9d24ac91a87 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 12 Jan 2026 17:16:54 -0600 Subject: [PATCH 095/251] build: generify changes to gkr.go --- .../backend/template/gkr/gkr.go.tmpl | 276 ++++++------------ internal/gkr/bls12-377/gkr.go | 272 ++++++----------- internal/gkr/bls12-381/gkr.go | 272 ++++++----------- internal/gkr/bls24-315/gkr.go | 272 ++++++----------- internal/gkr/bls24-317/gkr.go | 272 ++++++----------- internal/gkr/bw6-633/gkr.go | 272 ++++++----------- internal/gkr/bw6-761/gkr.go | 272 ++++++----------- internal/gkr/small_rational/gkr.go | 272 ++++++----------- 8 files changed, 697 insertions(+), 1483 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 0a3a87d048..05a27ae9d2 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -8,7 +8,6 @@ import ( "math/big" "strconv" "sync" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -92,14 +91,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []{{ .ElementType e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]*{{ .ElementType }}, len(wire.Inputs)) - for i, uniqueI := range injectionLeftInv { // map from all to unique - inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] + evaluator := newGateEvaluator(wire.Gate.Compiled()) + for _, uniqueI := range injectionLeftInv { // map from all to unique + evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } - var stack elementStack - compiled := wire.Gate.Compiled() - stack.preloadConstants(compiled) - gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) + + gateEvaluation.Set(evaluator.evaluate()) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -660,43 +657,23 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() maxNbIns := 0 + evaluators := make([]gateEvaluator, len(wires)) for i, w := range wires { maxNbIns = max(maxNbIns, len(w.Inputs)) if len(a[i]) != nbInstances { a[i] = make([]{{ .ElementType }}, nbInstances) } + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) } - var stack elementStack - ins := make([]{{ .ElementType }}, maxNbIns) - insPtr := make([]*{{ .ElementType }}, maxNbIns) - - // Track current gate to know when to reload constants - var currentCompiled *gkrtypes.CompiledGate - var nbConsts int - for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { - compiled := w.Gate.Compiled() - - // Preload constants if this is a new gate - if compiled != currentCompiled { - stack.freeElements() - stack.preloadConstants(compiled) - currentCompiled = compiled - nbConsts = compiled.NbConstants() + for _, in := range w.Inputs { + evaluators[wI].pushInput(&a[in][i]) } - - for inI, in := range w.Inputs { - ins[inI] = a[in][i] - insPtr[inI] = &ins[inI] - } - a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) - - // Shrink stack back to just constants for reuse - stack.shrinkToConstants(nbConsts) + a[wI][i].Set(evaluators[wI].evaluate()) } } } @@ -744,97 +721,56 @@ func frToBigInts(dst []*big.Int, src []{{ .ElementType }}) { } } +// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the +// result in dst. In practice, dst will be one of the vars but this is not required. +type gateOp func(dst *{{ .ElementType }}, vars []{{ .ElementType }}, in []uint16) -// elementStack implements a synchronous memory pool underneath to minimize heap allocations. -type elementStack struct { - allocated []*{{ .ElementType }} - nbUsed int -} - -// preloadConstants loads gate constants into the stack at indices [0, nbConsts). -// This should be called once per gate to prepare the stack for repeated use. -func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { - for _, constVal := range gate.Constants { - elem := stack.newElement() - elem.SetBigInt(constVal) +func opAdd(dst *{{ .ElementType }}, vars []{{ .ElementType }}, in []uint16) { + dst.Add(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Add(dst, &vars[in[i]]) } } -// shrinkToConstants resets the stack to contain only constants, ready for next invocation. -func (stack *elementStack) truncate(n int) { - stack.nbUsed = n -} - -func (stack *elementStack) newElement() *{{ .ElementType }} { - stack.nbUsed++ - if stack.nbUsed >= len(stack.allocated) { - stack.allocated = append(stack.allocated, new({{ .ElementType }})) - } - return stack.allocated[api.nbUsed-1] +func opMulAcc(dst *{{ .ElementType }}, vars []{{ .ElementType }}, in []uint16) { + var prod {{ .ElementType }} + prod.Mul(&vars[in[1]], &vars[in[2]]) + dst.Add(&vars[in[0]], &prod) } -func (stack *elementStack) push(x *{{ .ElementType }}) { - stack.newElement().Set(x) +func opNeg(dst *{{ .ElementType }}, vars []{{ .ElementType }}, in []uint16) { + dst.Neg(&vars[in[0]]) } -func opAdd(stack *elementStack, x []*{{ .ElementType }}) *{{ .ElementType }} { - res := stack.newElement() - res.Add(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Add(res, x[i]) +func opSub(dst *{{ .ElementType }}, vars []{{ .ElementType }}, in []uint16) { + dst.Sub(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Sub(dst, &vars[in[i]]) } - return res -} - -func opMulAcc(stack *elementStack, x []*{{ .ElementType }}) *{{ .ElementType }} { - // result = x[0] + (x[1] * x[2]) - prod := stack.newElement() - prod.Mul(x[1], x[2]) - res := stack.newElement() - res.Add(x[0], prod) - return res } -func opNeg(stack *elementStack, x []*{{ .ElementType }}) *{{ .ElementType }} { - res := stack.newElement() - res.Neg(x[0]) - return res -} - -func opSub(stack *elementStack, x []*{{ .ElementType }}) *{{ .ElementType }} { - res := stack.newElement() - res.Sub(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Sub(res, x[i]) - } - return res -} - -func opMul(stack *elementStack, x []*{{ .ElementType }}) *{{ .ElementType }} { - res := stack.newElement() - res.Mul(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Mul(res, x[i]) +func opMul(dst *{{ .ElementType }}, vars []{{ .ElementType }}, in []uint16) { + dst.Mul(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Mul(dst, &vars[in[i]]) } - return res } -func opSumExp17(stack *elementStack, x []*{{ .ElementType }}) *{{ .ElementType }} { +func opSumExp17(dst *{{ .ElementType }}, vars []{{ .ElementType }}, in []uint16) { // result = (x[0] + x[1] + x[2])^17 - sum := stack.newElement() - sum.Add(x[0], x[1]) - sum.Add(sum, x[2]) - - res := stack.newElement() - res.Mul(sum, sum) // sum² - res.Mul(res, res) // sum⁴ - res.Mul(res, res) // sum⁸ - res.Mul(res, res) // sum¹⁶ - res.Mul(res, sum) // sum¹⁷ - return res + var sum {{ .ElementType }} + sum.Add(&vars[in[0]], &vars[in[1]]) + sum.Add(&sum, &vars[in[2]]) + + dst.Mul(&sum, &sum) // sum² + dst.Mul(dst, dst) // sum⁴ + dst.Mul(dst, dst) // sum⁸ + dst.Mul(dst, dst) // sum¹⁶ + dst.Mul(dst, &sum) // sum¹⁷ } -func (api *elementStack) Println(a ...frontend.Variable) { +/* +func (stack *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x {{ .ElementType }} @@ -850,106 +786,70 @@ func (api *elementStack) Println(a ...frontend.Variable) { } } fmt.Println(toPrint...) -} - - -// executeCompiledGate executes a compiled gate using curve-specific field operations. -// The stack is expected to already contain constants at indices [0, nbConsts). -// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. -// After execution, the stack can be shrunk back to nbConsts for reuse. -func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*{{.ElementType}}) *{{ .ElementType }} { - - // Instruction executor functions - executors := [...]func(*elementStack, []*{{ .ElementType }}) *{{ .ElementType }}{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - - nbConsts := gate.NbConstants() - - // If no instructions, this is an identity-like gate; return first input - if len(gate.Instructions) == 0 { - if gate.NbInputs == 0 { - panic("gate has no inputs and no instructions") - } - return inputs[0] - } - - // Append inputs to stack after constants - // The stack now contains: [constants | inputs] - for _, input := range inputs { - ptr := stack.newElement() - ptr.Set(input) - } - - // Execute instructions, appending results to stack - // The stack grows to: [constants | inputs | results] - for i := range gate.Instructions { - inst := &gate.Instructions[i] - executor := executors[inst.Op] - if executor == nil { - panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) - } - - // Resolve operands from stack using instruction indices - operands := make([]*{{ .ElementType }}, len(inst.Inputs)) - for j, idx := range inst.Inputs { - operands[j] = stack.allocated[idx] - } - - _ = executor(stack, operands) - // Result is automatically appended to stack by executor - } - - // Last value in stack is the result - resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 - return stack.allocated[resultIdx] -} +}*/ // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate - stack elementStack - nbConsts int + gate *gkrtypes.CompiledGate + vars []{{ .ElementType }} + frameSize int // number of constants plus currently pushed inputs } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { - eval := &gateEvaluator{ - gate: gate, - nbConsts: gate.NbConstants(), - inputs: make([]*{{ .ElementType }}, 0, gate.NbInputs), +func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { + e := gateEvaluator{ + gate: gate, + vars: make([]{{ .ElementType }}, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), } - eval.stack.preloadConstants(gate) - return eval + for i, constVal := range gate.Constants { + e.vars[i].SetBigInt(constVal) + } + return e } // pushInput adds an input to the evaluator's input buffer. -// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +// Inputs must be added in order, and the number of inputs must match the gate's NbInputs. func (e *gateEvaluator) pushInput(input *{{ .ElementType }}) { - e.inputs = append(e.inputs, input) + e.vars[e.frameSize].Set(input) + e.frameSize++ } -// evaluate executes the gate with the current inputs and returns the result. -// The stack and input buffer are automatically reset after evaluation, +// evaluate adds top to the top of the stack, executes the gate on it and returns the result. +// The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. -func (e *gateEvaluator) evaluate() *{{ .ElementType }} { - if len(e.inputs) != e.gate.NbInputs { - panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) +// NB! The result is short-lived. It will be overwritten the next time evaluate is called. +func (e *gateEvaluator) evaluate(top ...{{ .ElementType }}) *{{ .ElementType }} { + for i := range top { + e.pushInput(&top[i]) + } + + if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) } - result := executeCompiledGate(e.gate, &e.stack, e.inputs) + // Instruction executor functions + executors := [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range e.gate.Instructions { + executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + } + + res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] // Reset for next evaluation - e.stack.shrinkToConstants(e.nbConsts) - e.inputs = e.inputs[:0] // Clear input buffer + e.frameSize = e.gate.NbConstants() - return result -} \ No newline at end of file + return res +} diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index acf72ac48e..2469259ea1 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -16,7 +16,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -99,14 +98,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]*fr.Element, len(wire.Inputs)) - for i, uniqueI := range injectionLeftInv { // map from all to unique - inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] + evaluator := newGateEvaluator(wire.Gate.Compiled()) + for _, uniqueI := range injectionLeftInv { // map from all to unique + evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } - var stack elementStack - compiled := wire.Gate.Compiled() - stack.preloadConstants(compiled) - gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) + + gateEvaluation.Set(evaluator.evaluate()) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -665,43 +662,23 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() maxNbIns := 0 + evaluators := make([]gateEvaluator, len(wires)) for i, w := range wires { maxNbIns = max(maxNbIns, len(w.Inputs)) if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) } - var stack elementStack - ins := make([]fr.Element, maxNbIns) - insPtr := make([]*fr.Element, maxNbIns) - - // Track current gate to know when to reload constants - var currentCompiled *gkrtypes.CompiledGate - var nbConsts int - for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { - compiled := w.Gate.Compiled() - - // Preload constants if this is a new gate - if compiled != currentCompiled { - stack.freeElements() - stack.preloadConstants(compiled) - currentCompiled = compiled - nbConsts = compiled.NbConstants() + for _, in := range w.Inputs { + evaluators[wI].pushInput(&a[in][i]) } - - for inI, in := range w.Inputs { - ins[inI] = a[in][i] - insPtr[inI] = &ins[inI] - } - a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) - - // Shrink stack back to just constants for reuse - stack.shrinkToConstants(nbConsts) + a[wI][i].Set(evaluators[wI].evaluate()) } } } @@ -749,96 +726,56 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// elementStack implements a synchronous memory pool underneath to minimize heap allocations. -type elementStack struct { - allocated []*fr.Element - nbUsed int -} +// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the +// result in dst. In practice, dst will be one of the vars but this is not required. +type gateOp func(dst *fr.Element, vars []fr.Element, in []uint16) -// preloadConstants loads gate constants into the stack at indices [0, nbConsts). -// This should be called once per gate to prepare the stack for repeated use. -func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { - for _, constVal := range gate.Constants { - elem := stack.newElement() - elem.SetBigInt(constVal) +func opAdd(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Add(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Add(dst, &vars[in[i]]) } } -// shrinkToConstants resets the stack to contain only constants, ready for next invocation. -func (stack *elementStack) truncate(n int) { - stack.nbUsed = n -} - -func (stack *elementStack) newElement() *fr.Element { - stack.nbUsed++ - if stack.nbUsed >= len(stack.allocated) { - stack.allocated = append(stack.allocated, new(fr.Element)) - } - return stack.allocated[api.nbUsed-1] +func opMulAcc(dst *fr.Element, vars []fr.Element, in []uint16) { + var prod fr.Element + prod.Mul(&vars[in[1]], &vars[in[2]]) + dst.Add(&vars[in[0]], &prod) } -func (stack *elementStack) push(x *fr.Element) { - stack.newElement().Set(x) +func opNeg(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Neg(&vars[in[0]]) } -func opAdd(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Add(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Add(res, x[i]) +func opSub(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Sub(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Sub(dst, &vars[in[i]]) } - return res -} - -func opMulAcc(stack *elementStack, x []*fr.Element) *fr.Element { - // result = x[0] + (x[1] * x[2]) - prod := stack.newElement() - prod.Mul(x[1], x[2]) - res := stack.newElement() - res.Add(x[0], prod) - return res -} - -func opNeg(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Neg(x[0]) - return res } -func opSub(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Sub(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Sub(res, x[i]) +func opMul(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Mul(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Mul(dst, &vars[in[i]]) } - return res -} - -func opMul(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Mul(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Mul(res, x[i]) - } - return res } -func opSumExp17(stack *elementStack, x []*fr.Element) *fr.Element { +func opSumExp17(dst *fr.Element, vars []fr.Element, in []uint16) { // result = (x[0] + x[1] + x[2])^17 - sum := stack.newElement() - sum.Add(x[0], x[1]) - sum.Add(sum, x[2]) - - res := stack.newElement() - res.Mul(sum, sum) // sum² - res.Mul(res, res) // sum⁴ - res.Mul(res, res) // sum⁸ - res.Mul(res, res) // sum¹⁶ - res.Mul(res, sum) // sum¹⁷ - return res + var sum fr.Element + sum.Add(&vars[in[0]], &vars[in[1]]) + sum.Add(&sum, &vars[in[2]]) + + dst.Mul(&sum, &sum) // sum² + dst.Mul(dst, dst) // sum⁴ + dst.Mul(dst, dst) // sum⁸ + dst.Mul(dst, dst) // sum¹⁶ + dst.Mul(dst, &sum) // sum¹⁷ } -func (api *elementStack) Println(a ...frontend.Variable) { +/* +func (stack *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -854,105 +791,70 @@ func (api *elementStack) Println(a ...frontend.Variable) { } } fmt.Println(toPrint...) -} - -// executeCompiledGate executes a compiled gate using curve-specific field operations. -// The stack is expected to already contain constants at indices [0, nbConsts). -// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. -// After execution, the stack can be shrunk back to nbConsts for reuse. -func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*fr.Element) *fr.Element { - - // Instruction executor functions - executors := [...]func(*elementStack, []*fr.Element) *fr.Element{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - - nbConsts := gate.NbConstants() - - // If no instructions, this is an identity-like gate; return first input - if len(gate.Instructions) == 0 { - if gate.NbInputs == 0 { - panic("gate has no inputs and no instructions") - } - return inputs[0] - } - - // Append inputs to stack after constants - // The stack now contains: [constants | inputs] - for _, input := range inputs { - ptr := stack.newElement() - ptr.Set(input) - } - - // Execute instructions, appending results to stack - // The stack grows to: [constants | inputs | results] - for i := range gate.Instructions { - inst := &gate.Instructions[i] - executor := executors[inst.Op] - if executor == nil { - panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) - } - - // Resolve operands from stack using instruction indices - operands := make([]*fr.Element, len(inst.Inputs)) - for j, idx := range inst.Inputs { - operands[j] = stack.allocated[idx] - } - - _ = executor(stack, operands) - // Result is automatically appended to stack by executor - } - - // Last value in stack is the result - resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 - return stack.allocated[resultIdx] -} +}*/ // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate - stack elementStack - nbConsts int + gate *gkrtypes.CompiledGate + vars []fr.Element + frameSize int // number of constants plus currently pushed inputs } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { - eval := &gateEvaluator{ - gate: gate, - nbConsts: gate.NbConstants(), - inputs: make([]*fr.Element, 0, gate.NbInputs), +func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { + e := gateEvaluator{ + gate: gate, + vars: make([]fr.Element, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), } - eval.stack.preloadConstants(gate) - return eval + for i, constVal := range gate.Constants { + e.vars[i].SetBigInt(constVal) + } + return e } // pushInput adds an input to the evaluator's input buffer. -// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +// Inputs must be added in order, and the number of inputs must match the gate's NbInputs. func (e *gateEvaluator) pushInput(input *fr.Element) { - e.inputs = append(e.inputs, input) + e.vars[e.frameSize].Set(input) + e.frameSize++ } -// evaluate executes the gate with the current inputs and returns the result. -// The stack and input buffer are automatically reset after evaluation, +// evaluate adds top to the top of the stack, executes the gate on it and returns the result. +// The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. -func (e *gateEvaluator) evaluate() *fr.Element { - if len(e.inputs) != e.gate.NbInputs { - panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) +// NB! The result is short-lived. It will be overwritten the next time evaluate is called. +func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { + for i := range top { + e.pushInput(&top[i]) } - result := executeCompiledGate(e.gate, &e.stack, e.inputs) + if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + } + + // Instruction executor functions + executors := [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range e.gate.Instructions { + executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + } + + res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] // Reset for next evaluation - e.stack.shrinkToConstants(e.nbConsts) - e.inputs = e.inputs[:0] // Clear input buffer + e.frameSize = e.gate.NbConstants() - return result + return res } diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index e4372f1dc7..a77652a1b4 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -16,7 +16,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -99,14 +98,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]*fr.Element, len(wire.Inputs)) - for i, uniqueI := range injectionLeftInv { // map from all to unique - inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] + evaluator := newGateEvaluator(wire.Gate.Compiled()) + for _, uniqueI := range injectionLeftInv { // map from all to unique + evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } - var stack elementStack - compiled := wire.Gate.Compiled() - stack.preloadConstants(compiled) - gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) + + gateEvaluation.Set(evaluator.evaluate()) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -665,43 +662,23 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() maxNbIns := 0 + evaluators := make([]gateEvaluator, len(wires)) for i, w := range wires { maxNbIns = max(maxNbIns, len(w.Inputs)) if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) } - var stack elementStack - ins := make([]fr.Element, maxNbIns) - insPtr := make([]*fr.Element, maxNbIns) - - // Track current gate to know when to reload constants - var currentCompiled *gkrtypes.CompiledGate - var nbConsts int - for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { - compiled := w.Gate.Compiled() - - // Preload constants if this is a new gate - if compiled != currentCompiled { - stack.freeElements() - stack.preloadConstants(compiled) - currentCompiled = compiled - nbConsts = compiled.NbConstants() + for _, in := range w.Inputs { + evaluators[wI].pushInput(&a[in][i]) } - - for inI, in := range w.Inputs { - ins[inI] = a[in][i] - insPtr[inI] = &ins[inI] - } - a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) - - // Shrink stack back to just constants for reuse - stack.shrinkToConstants(nbConsts) + a[wI][i].Set(evaluators[wI].evaluate()) } } } @@ -749,96 +726,56 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// elementStack implements a synchronous memory pool underneath to minimize heap allocations. -type elementStack struct { - allocated []*fr.Element - nbUsed int -} +// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the +// result in dst. In practice, dst will be one of the vars but this is not required. +type gateOp func(dst *fr.Element, vars []fr.Element, in []uint16) -// preloadConstants loads gate constants into the stack at indices [0, nbConsts). -// This should be called once per gate to prepare the stack for repeated use. -func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { - for _, constVal := range gate.Constants { - elem := stack.newElement() - elem.SetBigInt(constVal) +func opAdd(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Add(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Add(dst, &vars[in[i]]) } } -// shrinkToConstants resets the stack to contain only constants, ready for next invocation. -func (stack *elementStack) truncate(n int) { - stack.nbUsed = n -} - -func (stack *elementStack) newElement() *fr.Element { - stack.nbUsed++ - if stack.nbUsed >= len(stack.allocated) { - stack.allocated = append(stack.allocated, new(fr.Element)) - } - return stack.allocated[api.nbUsed-1] +func opMulAcc(dst *fr.Element, vars []fr.Element, in []uint16) { + var prod fr.Element + prod.Mul(&vars[in[1]], &vars[in[2]]) + dst.Add(&vars[in[0]], &prod) } -func (stack *elementStack) push(x *fr.Element) { - stack.newElement().Set(x) +func opNeg(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Neg(&vars[in[0]]) } -func opAdd(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Add(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Add(res, x[i]) +func opSub(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Sub(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Sub(dst, &vars[in[i]]) } - return res -} - -func opMulAcc(stack *elementStack, x []*fr.Element) *fr.Element { - // result = x[0] + (x[1] * x[2]) - prod := stack.newElement() - prod.Mul(x[1], x[2]) - res := stack.newElement() - res.Add(x[0], prod) - return res -} - -func opNeg(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Neg(x[0]) - return res } -func opSub(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Sub(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Sub(res, x[i]) +func opMul(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Mul(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Mul(dst, &vars[in[i]]) } - return res -} - -func opMul(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Mul(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Mul(res, x[i]) - } - return res } -func opSumExp17(stack *elementStack, x []*fr.Element) *fr.Element { +func opSumExp17(dst *fr.Element, vars []fr.Element, in []uint16) { // result = (x[0] + x[1] + x[2])^17 - sum := stack.newElement() - sum.Add(x[0], x[1]) - sum.Add(sum, x[2]) - - res := stack.newElement() - res.Mul(sum, sum) // sum² - res.Mul(res, res) // sum⁴ - res.Mul(res, res) // sum⁸ - res.Mul(res, res) // sum¹⁶ - res.Mul(res, sum) // sum¹⁷ - return res + var sum fr.Element + sum.Add(&vars[in[0]], &vars[in[1]]) + sum.Add(&sum, &vars[in[2]]) + + dst.Mul(&sum, &sum) // sum² + dst.Mul(dst, dst) // sum⁴ + dst.Mul(dst, dst) // sum⁸ + dst.Mul(dst, dst) // sum¹⁶ + dst.Mul(dst, &sum) // sum¹⁷ } -func (api *elementStack) Println(a ...frontend.Variable) { +/* +func (stack *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -854,105 +791,70 @@ func (api *elementStack) Println(a ...frontend.Variable) { } } fmt.Println(toPrint...) -} - -// executeCompiledGate executes a compiled gate using curve-specific field operations. -// The stack is expected to already contain constants at indices [0, nbConsts). -// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. -// After execution, the stack can be shrunk back to nbConsts for reuse. -func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*fr.Element) *fr.Element { - - // Instruction executor functions - executors := [...]func(*elementStack, []*fr.Element) *fr.Element{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - - nbConsts := gate.NbConstants() - - // If no instructions, this is an identity-like gate; return first input - if len(gate.Instructions) == 0 { - if gate.NbInputs == 0 { - panic("gate has no inputs and no instructions") - } - return inputs[0] - } - - // Append inputs to stack after constants - // The stack now contains: [constants | inputs] - for _, input := range inputs { - ptr := stack.newElement() - ptr.Set(input) - } - - // Execute instructions, appending results to stack - // The stack grows to: [constants | inputs | results] - for i := range gate.Instructions { - inst := &gate.Instructions[i] - executor := executors[inst.Op] - if executor == nil { - panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) - } - - // Resolve operands from stack using instruction indices - operands := make([]*fr.Element, len(inst.Inputs)) - for j, idx := range inst.Inputs { - operands[j] = stack.allocated[idx] - } - - _ = executor(stack, operands) - // Result is automatically appended to stack by executor - } - - // Last value in stack is the result - resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 - return stack.allocated[resultIdx] -} +}*/ // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate - stack elementStack - nbConsts int + gate *gkrtypes.CompiledGate + vars []fr.Element + frameSize int // number of constants plus currently pushed inputs } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { - eval := &gateEvaluator{ - gate: gate, - nbConsts: gate.NbConstants(), - inputs: make([]*fr.Element, 0, gate.NbInputs), +func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { + e := gateEvaluator{ + gate: gate, + vars: make([]fr.Element, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), } - eval.stack.preloadConstants(gate) - return eval + for i, constVal := range gate.Constants { + e.vars[i].SetBigInt(constVal) + } + return e } // pushInput adds an input to the evaluator's input buffer. -// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +// Inputs must be added in order, and the number of inputs must match the gate's NbInputs. func (e *gateEvaluator) pushInput(input *fr.Element) { - e.inputs = append(e.inputs, input) + e.vars[e.frameSize].Set(input) + e.frameSize++ } -// evaluate executes the gate with the current inputs and returns the result. -// The stack and input buffer are automatically reset after evaluation, +// evaluate adds top to the top of the stack, executes the gate on it and returns the result. +// The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. -func (e *gateEvaluator) evaluate() *fr.Element { - if len(e.inputs) != e.gate.NbInputs { - panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) +// NB! The result is short-lived. It will be overwritten the next time evaluate is called. +func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { + for i := range top { + e.pushInput(&top[i]) } - result := executeCompiledGate(e.gate, &e.stack, e.inputs) + if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + } + + // Instruction executor functions + executors := [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range e.gate.Instructions { + executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + } + + res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] // Reset for next evaluation - e.stack.shrinkToConstants(e.nbConsts) - e.inputs = e.inputs[:0] // Clear input buffer + e.frameSize = e.gate.NbConstants() - return result + return res } diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 520e8acfec..a8344f36f3 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -16,7 +16,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -99,14 +98,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]*fr.Element, len(wire.Inputs)) - for i, uniqueI := range injectionLeftInv { // map from all to unique - inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] + evaluator := newGateEvaluator(wire.Gate.Compiled()) + for _, uniqueI := range injectionLeftInv { // map from all to unique + evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } - var stack elementStack - compiled := wire.Gate.Compiled() - stack.preloadConstants(compiled) - gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) + + gateEvaluation.Set(evaluator.evaluate()) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -665,43 +662,23 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() maxNbIns := 0 + evaluators := make([]gateEvaluator, len(wires)) for i, w := range wires { maxNbIns = max(maxNbIns, len(w.Inputs)) if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) } - var stack elementStack - ins := make([]fr.Element, maxNbIns) - insPtr := make([]*fr.Element, maxNbIns) - - // Track current gate to know when to reload constants - var currentCompiled *gkrtypes.CompiledGate - var nbConsts int - for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { - compiled := w.Gate.Compiled() - - // Preload constants if this is a new gate - if compiled != currentCompiled { - stack.freeElements() - stack.preloadConstants(compiled) - currentCompiled = compiled - nbConsts = compiled.NbConstants() + for _, in := range w.Inputs { + evaluators[wI].pushInput(&a[in][i]) } - - for inI, in := range w.Inputs { - ins[inI] = a[in][i] - insPtr[inI] = &ins[inI] - } - a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) - - // Shrink stack back to just constants for reuse - stack.shrinkToConstants(nbConsts) + a[wI][i].Set(evaluators[wI].evaluate()) } } } @@ -749,96 +726,56 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// elementStack implements a synchronous memory pool underneath to minimize heap allocations. -type elementStack struct { - allocated []*fr.Element - nbUsed int -} +// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the +// result in dst. In practice, dst will be one of the vars but this is not required. +type gateOp func(dst *fr.Element, vars []fr.Element, in []uint16) -// preloadConstants loads gate constants into the stack at indices [0, nbConsts). -// This should be called once per gate to prepare the stack for repeated use. -func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { - for _, constVal := range gate.Constants { - elem := stack.newElement() - elem.SetBigInt(constVal) +func opAdd(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Add(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Add(dst, &vars[in[i]]) } } -// shrinkToConstants resets the stack to contain only constants, ready for next invocation. -func (stack *elementStack) truncate(n int) { - stack.nbUsed = n -} - -func (stack *elementStack) newElement() *fr.Element { - stack.nbUsed++ - if stack.nbUsed >= len(stack.allocated) { - stack.allocated = append(stack.allocated, new(fr.Element)) - } - return stack.allocated[api.nbUsed-1] +func opMulAcc(dst *fr.Element, vars []fr.Element, in []uint16) { + var prod fr.Element + prod.Mul(&vars[in[1]], &vars[in[2]]) + dst.Add(&vars[in[0]], &prod) } -func (stack *elementStack) push(x *fr.Element) { - stack.newElement().Set(x) +func opNeg(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Neg(&vars[in[0]]) } -func opAdd(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Add(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Add(res, x[i]) +func opSub(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Sub(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Sub(dst, &vars[in[i]]) } - return res -} - -func opMulAcc(stack *elementStack, x []*fr.Element) *fr.Element { - // result = x[0] + (x[1] * x[2]) - prod := stack.newElement() - prod.Mul(x[1], x[2]) - res := stack.newElement() - res.Add(x[0], prod) - return res -} - -func opNeg(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Neg(x[0]) - return res } -func opSub(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Sub(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Sub(res, x[i]) +func opMul(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Mul(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Mul(dst, &vars[in[i]]) } - return res -} - -func opMul(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Mul(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Mul(res, x[i]) - } - return res } -func opSumExp17(stack *elementStack, x []*fr.Element) *fr.Element { +func opSumExp17(dst *fr.Element, vars []fr.Element, in []uint16) { // result = (x[0] + x[1] + x[2])^17 - sum := stack.newElement() - sum.Add(x[0], x[1]) - sum.Add(sum, x[2]) - - res := stack.newElement() - res.Mul(sum, sum) // sum² - res.Mul(res, res) // sum⁴ - res.Mul(res, res) // sum⁸ - res.Mul(res, res) // sum¹⁶ - res.Mul(res, sum) // sum¹⁷ - return res + var sum fr.Element + sum.Add(&vars[in[0]], &vars[in[1]]) + sum.Add(&sum, &vars[in[2]]) + + dst.Mul(&sum, &sum) // sum² + dst.Mul(dst, dst) // sum⁴ + dst.Mul(dst, dst) // sum⁸ + dst.Mul(dst, dst) // sum¹⁶ + dst.Mul(dst, &sum) // sum¹⁷ } -func (api *elementStack) Println(a ...frontend.Variable) { +/* +func (stack *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -854,105 +791,70 @@ func (api *elementStack) Println(a ...frontend.Variable) { } } fmt.Println(toPrint...) -} - -// executeCompiledGate executes a compiled gate using curve-specific field operations. -// The stack is expected to already contain constants at indices [0, nbConsts). -// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. -// After execution, the stack can be shrunk back to nbConsts for reuse. -func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*fr.Element) *fr.Element { - - // Instruction executor functions - executors := [...]func(*elementStack, []*fr.Element) *fr.Element{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - - nbConsts := gate.NbConstants() - - // If no instructions, this is an identity-like gate; return first input - if len(gate.Instructions) == 0 { - if gate.NbInputs == 0 { - panic("gate has no inputs and no instructions") - } - return inputs[0] - } - - // Append inputs to stack after constants - // The stack now contains: [constants | inputs] - for _, input := range inputs { - ptr := stack.newElement() - ptr.Set(input) - } - - // Execute instructions, appending results to stack - // The stack grows to: [constants | inputs | results] - for i := range gate.Instructions { - inst := &gate.Instructions[i] - executor := executors[inst.Op] - if executor == nil { - panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) - } - - // Resolve operands from stack using instruction indices - operands := make([]*fr.Element, len(inst.Inputs)) - for j, idx := range inst.Inputs { - operands[j] = stack.allocated[idx] - } - - _ = executor(stack, operands) - // Result is automatically appended to stack by executor - } - - // Last value in stack is the result - resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 - return stack.allocated[resultIdx] -} +}*/ // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate - stack elementStack - nbConsts int + gate *gkrtypes.CompiledGate + vars []fr.Element + frameSize int // number of constants plus currently pushed inputs } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { - eval := &gateEvaluator{ - gate: gate, - nbConsts: gate.NbConstants(), - inputs: make([]*fr.Element, 0, gate.NbInputs), +func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { + e := gateEvaluator{ + gate: gate, + vars: make([]fr.Element, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), } - eval.stack.preloadConstants(gate) - return eval + for i, constVal := range gate.Constants { + e.vars[i].SetBigInt(constVal) + } + return e } // pushInput adds an input to the evaluator's input buffer. -// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +// Inputs must be added in order, and the number of inputs must match the gate's NbInputs. func (e *gateEvaluator) pushInput(input *fr.Element) { - e.inputs = append(e.inputs, input) + e.vars[e.frameSize].Set(input) + e.frameSize++ } -// evaluate executes the gate with the current inputs and returns the result. -// The stack and input buffer are automatically reset after evaluation, +// evaluate adds top to the top of the stack, executes the gate on it and returns the result. +// The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. -func (e *gateEvaluator) evaluate() *fr.Element { - if len(e.inputs) != e.gate.NbInputs { - panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) +// NB! The result is short-lived. It will be overwritten the next time evaluate is called. +func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { + for i := range top { + e.pushInput(&top[i]) } - result := executeCompiledGate(e.gate, &e.stack, e.inputs) + if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + } + + // Instruction executor functions + executors := [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range e.gate.Instructions { + executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + } + + res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] // Reset for next evaluation - e.stack.shrinkToConstants(e.nbConsts) - e.inputs = e.inputs[:0] // Clear input buffer + e.frameSize = e.gate.NbConstants() - return result + return res } diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index b3952244bb..d6d8287c45 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -16,7 +16,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -99,14 +98,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]*fr.Element, len(wire.Inputs)) - for i, uniqueI := range injectionLeftInv { // map from all to unique - inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] + evaluator := newGateEvaluator(wire.Gate.Compiled()) + for _, uniqueI := range injectionLeftInv { // map from all to unique + evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } - var stack elementStack - compiled := wire.Gate.Compiled() - stack.preloadConstants(compiled) - gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) + + gateEvaluation.Set(evaluator.evaluate()) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -665,43 +662,23 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() maxNbIns := 0 + evaluators := make([]gateEvaluator, len(wires)) for i, w := range wires { maxNbIns = max(maxNbIns, len(w.Inputs)) if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) } - var stack elementStack - ins := make([]fr.Element, maxNbIns) - insPtr := make([]*fr.Element, maxNbIns) - - // Track current gate to know when to reload constants - var currentCompiled *gkrtypes.CompiledGate - var nbConsts int - for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { - compiled := w.Gate.Compiled() - - // Preload constants if this is a new gate - if compiled != currentCompiled { - stack.freeElements() - stack.preloadConstants(compiled) - currentCompiled = compiled - nbConsts = compiled.NbConstants() + for _, in := range w.Inputs { + evaluators[wI].pushInput(&a[in][i]) } - - for inI, in := range w.Inputs { - ins[inI] = a[in][i] - insPtr[inI] = &ins[inI] - } - a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) - - // Shrink stack back to just constants for reuse - stack.shrinkToConstants(nbConsts) + a[wI][i].Set(evaluators[wI].evaluate()) } } } @@ -749,96 +726,56 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// elementStack implements a synchronous memory pool underneath to minimize heap allocations. -type elementStack struct { - allocated []*fr.Element - nbUsed int -} +// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the +// result in dst. In practice, dst will be one of the vars but this is not required. +type gateOp func(dst *fr.Element, vars []fr.Element, in []uint16) -// preloadConstants loads gate constants into the stack at indices [0, nbConsts). -// This should be called once per gate to prepare the stack for repeated use. -func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { - for _, constVal := range gate.Constants { - elem := stack.newElement() - elem.SetBigInt(constVal) +func opAdd(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Add(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Add(dst, &vars[in[i]]) } } -// shrinkToConstants resets the stack to contain only constants, ready for next invocation. -func (stack *elementStack) truncate(n int) { - stack.nbUsed = n -} - -func (stack *elementStack) newElement() *fr.Element { - stack.nbUsed++ - if stack.nbUsed >= len(stack.allocated) { - stack.allocated = append(stack.allocated, new(fr.Element)) - } - return stack.allocated[api.nbUsed-1] +func opMulAcc(dst *fr.Element, vars []fr.Element, in []uint16) { + var prod fr.Element + prod.Mul(&vars[in[1]], &vars[in[2]]) + dst.Add(&vars[in[0]], &prod) } -func (stack *elementStack) push(x *fr.Element) { - stack.newElement().Set(x) +func opNeg(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Neg(&vars[in[0]]) } -func opAdd(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Add(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Add(res, x[i]) +func opSub(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Sub(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Sub(dst, &vars[in[i]]) } - return res -} - -func opMulAcc(stack *elementStack, x []*fr.Element) *fr.Element { - // result = x[0] + (x[1] * x[2]) - prod := stack.newElement() - prod.Mul(x[1], x[2]) - res := stack.newElement() - res.Add(x[0], prod) - return res -} - -func opNeg(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Neg(x[0]) - return res } -func opSub(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Sub(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Sub(res, x[i]) +func opMul(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Mul(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Mul(dst, &vars[in[i]]) } - return res -} - -func opMul(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Mul(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Mul(res, x[i]) - } - return res } -func opSumExp17(stack *elementStack, x []*fr.Element) *fr.Element { +func opSumExp17(dst *fr.Element, vars []fr.Element, in []uint16) { // result = (x[0] + x[1] + x[2])^17 - sum := stack.newElement() - sum.Add(x[0], x[1]) - sum.Add(sum, x[2]) - - res := stack.newElement() - res.Mul(sum, sum) // sum² - res.Mul(res, res) // sum⁴ - res.Mul(res, res) // sum⁸ - res.Mul(res, res) // sum¹⁶ - res.Mul(res, sum) // sum¹⁷ - return res + var sum fr.Element + sum.Add(&vars[in[0]], &vars[in[1]]) + sum.Add(&sum, &vars[in[2]]) + + dst.Mul(&sum, &sum) // sum² + dst.Mul(dst, dst) // sum⁴ + dst.Mul(dst, dst) // sum⁸ + dst.Mul(dst, dst) // sum¹⁶ + dst.Mul(dst, &sum) // sum¹⁷ } -func (api *elementStack) Println(a ...frontend.Variable) { +/* +func (stack *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -854,105 +791,70 @@ func (api *elementStack) Println(a ...frontend.Variable) { } } fmt.Println(toPrint...) -} - -// executeCompiledGate executes a compiled gate using curve-specific field operations. -// The stack is expected to already contain constants at indices [0, nbConsts). -// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. -// After execution, the stack can be shrunk back to nbConsts for reuse. -func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*fr.Element) *fr.Element { - - // Instruction executor functions - executors := [...]func(*elementStack, []*fr.Element) *fr.Element{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - - nbConsts := gate.NbConstants() - - // If no instructions, this is an identity-like gate; return first input - if len(gate.Instructions) == 0 { - if gate.NbInputs == 0 { - panic("gate has no inputs and no instructions") - } - return inputs[0] - } - - // Append inputs to stack after constants - // The stack now contains: [constants | inputs] - for _, input := range inputs { - ptr := stack.newElement() - ptr.Set(input) - } - - // Execute instructions, appending results to stack - // The stack grows to: [constants | inputs | results] - for i := range gate.Instructions { - inst := &gate.Instructions[i] - executor := executors[inst.Op] - if executor == nil { - panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) - } - - // Resolve operands from stack using instruction indices - operands := make([]*fr.Element, len(inst.Inputs)) - for j, idx := range inst.Inputs { - operands[j] = stack.allocated[idx] - } - - _ = executor(stack, operands) - // Result is automatically appended to stack by executor - } - - // Last value in stack is the result - resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 - return stack.allocated[resultIdx] -} +}*/ // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate - stack elementStack - nbConsts int + gate *gkrtypes.CompiledGate + vars []fr.Element + frameSize int // number of constants plus currently pushed inputs } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { - eval := &gateEvaluator{ - gate: gate, - nbConsts: gate.NbConstants(), - inputs: make([]*fr.Element, 0, gate.NbInputs), +func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { + e := gateEvaluator{ + gate: gate, + vars: make([]fr.Element, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), } - eval.stack.preloadConstants(gate) - return eval + for i, constVal := range gate.Constants { + e.vars[i].SetBigInt(constVal) + } + return e } // pushInput adds an input to the evaluator's input buffer. -// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +// Inputs must be added in order, and the number of inputs must match the gate's NbInputs. func (e *gateEvaluator) pushInput(input *fr.Element) { - e.inputs = append(e.inputs, input) + e.vars[e.frameSize].Set(input) + e.frameSize++ } -// evaluate executes the gate with the current inputs and returns the result. -// The stack and input buffer are automatically reset after evaluation, +// evaluate adds top to the top of the stack, executes the gate on it and returns the result. +// The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. -func (e *gateEvaluator) evaluate() *fr.Element { - if len(e.inputs) != e.gate.NbInputs { - panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) +// NB! The result is short-lived. It will be overwritten the next time evaluate is called. +func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { + for i := range top { + e.pushInput(&top[i]) } - result := executeCompiledGate(e.gate, &e.stack, e.inputs) + if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + } + + // Instruction executor functions + executors := [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range e.gate.Instructions { + executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + } + + res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] // Reset for next evaluation - e.stack.shrinkToConstants(e.nbConsts) - e.inputs = e.inputs[:0] // Clear input buffer + e.frameSize = e.gate.NbConstants() - return result + return res } diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index d033f09e6a..92f3c4ca45 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -16,7 +16,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -99,14 +98,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]*fr.Element, len(wire.Inputs)) - for i, uniqueI := range injectionLeftInv { // map from all to unique - inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] + evaluator := newGateEvaluator(wire.Gate.Compiled()) + for _, uniqueI := range injectionLeftInv { // map from all to unique + evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } - var stack elementStack - compiled := wire.Gate.Compiled() - stack.preloadConstants(compiled) - gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) + + gateEvaluation.Set(evaluator.evaluate()) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -665,43 +662,23 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() maxNbIns := 0 + evaluators := make([]gateEvaluator, len(wires)) for i, w := range wires { maxNbIns = max(maxNbIns, len(w.Inputs)) if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) } - var stack elementStack - ins := make([]fr.Element, maxNbIns) - insPtr := make([]*fr.Element, maxNbIns) - - // Track current gate to know when to reload constants - var currentCompiled *gkrtypes.CompiledGate - var nbConsts int - for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { - compiled := w.Gate.Compiled() - - // Preload constants if this is a new gate - if compiled != currentCompiled { - stack.freeElements() - stack.preloadConstants(compiled) - currentCompiled = compiled - nbConsts = compiled.NbConstants() + for _, in := range w.Inputs { + evaluators[wI].pushInput(&a[in][i]) } - - for inI, in := range w.Inputs { - ins[inI] = a[in][i] - insPtr[inI] = &ins[inI] - } - a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) - - // Shrink stack back to just constants for reuse - stack.shrinkToConstants(nbConsts) + a[wI][i].Set(evaluators[wI].evaluate()) } } } @@ -749,96 +726,56 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// elementStack implements a synchronous memory pool underneath to minimize heap allocations. -type elementStack struct { - allocated []*fr.Element - nbUsed int -} +// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the +// result in dst. In practice, dst will be one of the vars but this is not required. +type gateOp func(dst *fr.Element, vars []fr.Element, in []uint16) -// preloadConstants loads gate constants into the stack at indices [0, nbConsts). -// This should be called once per gate to prepare the stack for repeated use. -func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { - for _, constVal := range gate.Constants { - elem := stack.newElement() - elem.SetBigInt(constVal) +func opAdd(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Add(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Add(dst, &vars[in[i]]) } } -// shrinkToConstants resets the stack to contain only constants, ready for next invocation. -func (stack *elementStack) truncate(n int) { - stack.nbUsed = n -} - -func (stack *elementStack) newElement() *fr.Element { - stack.nbUsed++ - if stack.nbUsed >= len(stack.allocated) { - stack.allocated = append(stack.allocated, new(fr.Element)) - } - return stack.allocated[api.nbUsed-1] +func opMulAcc(dst *fr.Element, vars []fr.Element, in []uint16) { + var prod fr.Element + prod.Mul(&vars[in[1]], &vars[in[2]]) + dst.Add(&vars[in[0]], &prod) } -func (stack *elementStack) push(x *fr.Element) { - stack.newElement().Set(x) +func opNeg(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Neg(&vars[in[0]]) } -func opAdd(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Add(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Add(res, x[i]) +func opSub(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Sub(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Sub(dst, &vars[in[i]]) } - return res -} - -func opMulAcc(stack *elementStack, x []*fr.Element) *fr.Element { - // result = x[0] + (x[1] * x[2]) - prod := stack.newElement() - prod.Mul(x[1], x[2]) - res := stack.newElement() - res.Add(x[0], prod) - return res -} - -func opNeg(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Neg(x[0]) - return res } -func opSub(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Sub(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Sub(res, x[i]) +func opMul(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Mul(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Mul(dst, &vars[in[i]]) } - return res -} - -func opMul(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Mul(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Mul(res, x[i]) - } - return res } -func opSumExp17(stack *elementStack, x []*fr.Element) *fr.Element { +func opSumExp17(dst *fr.Element, vars []fr.Element, in []uint16) { // result = (x[0] + x[1] + x[2])^17 - sum := stack.newElement() - sum.Add(x[0], x[1]) - sum.Add(sum, x[2]) - - res := stack.newElement() - res.Mul(sum, sum) // sum² - res.Mul(res, res) // sum⁴ - res.Mul(res, res) // sum⁸ - res.Mul(res, res) // sum¹⁶ - res.Mul(res, sum) // sum¹⁷ - return res + var sum fr.Element + sum.Add(&vars[in[0]], &vars[in[1]]) + sum.Add(&sum, &vars[in[2]]) + + dst.Mul(&sum, &sum) // sum² + dst.Mul(dst, dst) // sum⁴ + dst.Mul(dst, dst) // sum⁸ + dst.Mul(dst, dst) // sum¹⁶ + dst.Mul(dst, &sum) // sum¹⁷ } -func (api *elementStack) Println(a ...frontend.Variable) { +/* +func (stack *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -854,105 +791,70 @@ func (api *elementStack) Println(a ...frontend.Variable) { } } fmt.Println(toPrint...) -} - -// executeCompiledGate executes a compiled gate using curve-specific field operations. -// The stack is expected to already contain constants at indices [0, nbConsts). -// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. -// After execution, the stack can be shrunk back to nbConsts for reuse. -func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*fr.Element) *fr.Element { - - // Instruction executor functions - executors := [...]func(*elementStack, []*fr.Element) *fr.Element{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - - nbConsts := gate.NbConstants() - - // If no instructions, this is an identity-like gate; return first input - if len(gate.Instructions) == 0 { - if gate.NbInputs == 0 { - panic("gate has no inputs and no instructions") - } - return inputs[0] - } - - // Append inputs to stack after constants - // The stack now contains: [constants | inputs] - for _, input := range inputs { - ptr := stack.newElement() - ptr.Set(input) - } - - // Execute instructions, appending results to stack - // The stack grows to: [constants | inputs | results] - for i := range gate.Instructions { - inst := &gate.Instructions[i] - executor := executors[inst.Op] - if executor == nil { - panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) - } - - // Resolve operands from stack using instruction indices - operands := make([]*fr.Element, len(inst.Inputs)) - for j, idx := range inst.Inputs { - operands[j] = stack.allocated[idx] - } - - _ = executor(stack, operands) - // Result is automatically appended to stack by executor - } - - // Last value in stack is the result - resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 - return stack.allocated[resultIdx] -} +}*/ // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate - stack elementStack - nbConsts int + gate *gkrtypes.CompiledGate + vars []fr.Element + frameSize int // number of constants plus currently pushed inputs } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { - eval := &gateEvaluator{ - gate: gate, - nbConsts: gate.NbConstants(), - inputs: make([]*fr.Element, 0, gate.NbInputs), +func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { + e := gateEvaluator{ + gate: gate, + vars: make([]fr.Element, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), } - eval.stack.preloadConstants(gate) - return eval + for i, constVal := range gate.Constants { + e.vars[i].SetBigInt(constVal) + } + return e } // pushInput adds an input to the evaluator's input buffer. -// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +// Inputs must be added in order, and the number of inputs must match the gate's NbInputs. func (e *gateEvaluator) pushInput(input *fr.Element) { - e.inputs = append(e.inputs, input) + e.vars[e.frameSize].Set(input) + e.frameSize++ } -// evaluate executes the gate with the current inputs and returns the result. -// The stack and input buffer are automatically reset after evaluation, +// evaluate adds top to the top of the stack, executes the gate on it and returns the result. +// The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. -func (e *gateEvaluator) evaluate() *fr.Element { - if len(e.inputs) != e.gate.NbInputs { - panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) +// NB! The result is short-lived. It will be overwritten the next time evaluate is called. +func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { + for i := range top { + e.pushInput(&top[i]) } - result := executeCompiledGate(e.gate, &e.stack, e.inputs) + if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + } + + // Instruction executor functions + executors := [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range e.gate.Instructions { + executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + } + + res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] // Reset for next evaluation - e.stack.shrinkToConstants(e.nbConsts) - e.inputs = e.inputs[:0] // Clear input buffer + e.frameSize = e.gate.NbConstants() - return result + return res } diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 482ae9fc26..6e196b365a 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -16,7 +16,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -99,14 +98,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]*fr.Element, len(wire.Inputs)) - for i, uniqueI := range injectionLeftInv { // map from all to unique - inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] + evaluator := newGateEvaluator(wire.Gate.Compiled()) + for _, uniqueI := range injectionLeftInv { // map from all to unique + evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } - var stack elementStack - compiled := wire.Gate.Compiled() - stack.preloadConstants(compiled) - gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) + + gateEvaluation.Set(evaluator.evaluate()) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -665,43 +662,23 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() maxNbIns := 0 + evaluators := make([]gateEvaluator, len(wires)) for i, w := range wires { maxNbIns = max(maxNbIns, len(w.Inputs)) if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) } - var stack elementStack - ins := make([]fr.Element, maxNbIns) - insPtr := make([]*fr.Element, maxNbIns) - - // Track current gate to know when to reload constants - var currentCompiled *gkrtypes.CompiledGate - var nbConsts int - for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { - compiled := w.Gate.Compiled() - - // Preload constants if this is a new gate - if compiled != currentCompiled { - stack.freeElements() - stack.preloadConstants(compiled) - currentCompiled = compiled - nbConsts = compiled.NbConstants() + for _, in := range w.Inputs { + evaluators[wI].pushInput(&a[in][i]) } - - for inI, in := range w.Inputs { - ins[inI] = a[in][i] - insPtr[inI] = &ins[inI] - } - a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) - - // Shrink stack back to just constants for reuse - stack.shrinkToConstants(nbConsts) + a[wI][i].Set(evaluators[wI].evaluate()) } } } @@ -749,96 +726,56 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// elementStack implements a synchronous memory pool underneath to minimize heap allocations. -type elementStack struct { - allocated []*fr.Element - nbUsed int -} +// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the +// result in dst. In practice, dst will be one of the vars but this is not required. +type gateOp func(dst *fr.Element, vars []fr.Element, in []uint16) -// preloadConstants loads gate constants into the stack at indices [0, nbConsts). -// This should be called once per gate to prepare the stack for repeated use. -func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { - for _, constVal := range gate.Constants { - elem := stack.newElement() - elem.SetBigInt(constVal) +func opAdd(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Add(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Add(dst, &vars[in[i]]) } } -// shrinkToConstants resets the stack to contain only constants, ready for next invocation. -func (stack *elementStack) truncate(n int) { - stack.nbUsed = n -} - -func (stack *elementStack) newElement() *fr.Element { - stack.nbUsed++ - if stack.nbUsed >= len(stack.allocated) { - stack.allocated = append(stack.allocated, new(fr.Element)) - } - return stack.allocated[api.nbUsed-1] +func opMulAcc(dst *fr.Element, vars []fr.Element, in []uint16) { + var prod fr.Element + prod.Mul(&vars[in[1]], &vars[in[2]]) + dst.Add(&vars[in[0]], &prod) } -func (stack *elementStack) push(x *fr.Element) { - stack.newElement().Set(x) +func opNeg(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Neg(&vars[in[0]]) } -func opAdd(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Add(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Add(res, x[i]) +func opSub(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Sub(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Sub(dst, &vars[in[i]]) } - return res -} - -func opMulAcc(stack *elementStack, x []*fr.Element) *fr.Element { - // result = x[0] + (x[1] * x[2]) - prod := stack.newElement() - prod.Mul(x[1], x[2]) - res := stack.newElement() - res.Add(x[0], prod) - return res -} - -func opNeg(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Neg(x[0]) - return res } -func opSub(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Sub(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Sub(res, x[i]) +func opMul(dst *fr.Element, vars []fr.Element, in []uint16) { + dst.Mul(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Mul(dst, &vars[in[i]]) } - return res -} - -func opMul(stack *elementStack, x []*fr.Element) *fr.Element { - res := stack.newElement() - res.Mul(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Mul(res, x[i]) - } - return res } -func opSumExp17(stack *elementStack, x []*fr.Element) *fr.Element { +func opSumExp17(dst *fr.Element, vars []fr.Element, in []uint16) { // result = (x[0] + x[1] + x[2])^17 - sum := stack.newElement() - sum.Add(x[0], x[1]) - sum.Add(sum, x[2]) - - res := stack.newElement() - res.Mul(sum, sum) // sum² - res.Mul(res, res) // sum⁴ - res.Mul(res, res) // sum⁸ - res.Mul(res, res) // sum¹⁶ - res.Mul(res, sum) // sum¹⁷ - return res + var sum fr.Element + sum.Add(&vars[in[0]], &vars[in[1]]) + sum.Add(&sum, &vars[in[2]]) + + dst.Mul(&sum, &sum) // sum² + dst.Mul(dst, dst) // sum⁴ + dst.Mul(dst, dst) // sum⁸ + dst.Mul(dst, dst) // sum¹⁶ + dst.Mul(dst, &sum) // sum¹⁷ } -func (api *elementStack) Println(a ...frontend.Variable) { +/* +func (stack *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x fr.Element @@ -854,105 +791,70 @@ func (api *elementStack) Println(a ...frontend.Variable) { } } fmt.Println(toPrint...) -} - -// executeCompiledGate executes a compiled gate using curve-specific field operations. -// The stack is expected to already contain constants at indices [0, nbConsts). -// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. -// After execution, the stack can be shrunk back to nbConsts for reuse. -func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*fr.Element) *fr.Element { - - // Instruction executor functions - executors := [...]func(*elementStack, []*fr.Element) *fr.Element{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - - nbConsts := gate.NbConstants() - - // If no instructions, this is an identity-like gate; return first input - if len(gate.Instructions) == 0 { - if gate.NbInputs == 0 { - panic("gate has no inputs and no instructions") - } - return inputs[0] - } - - // Append inputs to stack after constants - // The stack now contains: [constants | inputs] - for _, input := range inputs { - ptr := stack.newElement() - ptr.Set(input) - } - - // Execute instructions, appending results to stack - // The stack grows to: [constants | inputs | results] - for i := range gate.Instructions { - inst := &gate.Instructions[i] - executor := executors[inst.Op] - if executor == nil { - panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) - } - - // Resolve operands from stack using instruction indices - operands := make([]*fr.Element, len(inst.Inputs)) - for j, idx := range inst.Inputs { - operands[j] = stack.allocated[idx] - } - - _ = executor(stack, operands) - // Result is automatically appended to stack by executor - } - - // Last value in stack is the result - resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 - return stack.allocated[resultIdx] -} +}*/ // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate - stack elementStack - nbConsts int + gate *gkrtypes.CompiledGate + vars []fr.Element + frameSize int // number of constants plus currently pushed inputs } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { - eval := &gateEvaluator{ - gate: gate, - nbConsts: gate.NbConstants(), - inputs: make([]*fr.Element, 0, gate.NbInputs), +func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { + e := gateEvaluator{ + gate: gate, + vars: make([]fr.Element, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), } - eval.stack.preloadConstants(gate) - return eval + for i, constVal := range gate.Constants { + e.vars[i].SetBigInt(constVal) + } + return e } // pushInput adds an input to the evaluator's input buffer. -// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +// Inputs must be added in order, and the number of inputs must match the gate's NbInputs. func (e *gateEvaluator) pushInput(input *fr.Element) { - e.inputs = append(e.inputs, input) + e.vars[e.frameSize].Set(input) + e.frameSize++ } -// evaluate executes the gate with the current inputs and returns the result. -// The stack and input buffer are automatically reset after evaluation, +// evaluate adds top to the top of the stack, executes the gate on it and returns the result. +// The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. -func (e *gateEvaluator) evaluate() *fr.Element { - if len(e.inputs) != e.gate.NbInputs { - panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) +// NB! The result is short-lived. It will be overwritten the next time evaluate is called. +func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { + for i := range top { + e.pushInput(&top[i]) } - result := executeCompiledGate(e.gate, &e.stack, e.inputs) + if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + } + + // Instruction executor functions + executors := [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range e.gate.Instructions { + executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + } + + res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] // Reset for next evaluation - e.stack.shrinkToConstants(e.nbConsts) - e.inputs = e.inputs[:0] // Clear input buffer + e.frameSize = e.gate.NbConstants() - return result + return res } diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 56ce964e17..3aa8bef1d3 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -14,7 +14,6 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" @@ -99,14 +98,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []small_rational.S e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - inputEvaluations := make([]*small_rational.SmallRational, len(wire.Inputs)) - for i, uniqueI := range injectionLeftInv { // map from all to unique - inputEvaluations[i] = &uniqueInputEvaluations[uniqueI] + evaluator := newGateEvaluator(wire.Gate.Compiled()) + for _, uniqueI := range injectionLeftInv { // map from all to unique + evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } - var stack elementStack - compiled := wire.Gate.Compiled() - stack.preloadConstants(compiled) - gateEvaluation.Set(executeCompiledGate(compiled, &stack, inputEvaluations)) + + gateEvaluation.Set(evaluator.evaluate()) } evaluation.Mul(&evaluation, &gateEvaluation) @@ -665,43 +662,23 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() maxNbIns := 0 + evaluators := make([]gateEvaluator, len(wires)) for i, w := range wires { maxNbIns = max(maxNbIns, len(w.Inputs)) if len(a[i]) != nbInstances { a[i] = make([]small_rational.SmallRational, nbInstances) } + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) } - var stack elementStack - ins := make([]small_rational.SmallRational, maxNbIns) - insPtr := make([]*small_rational.SmallRational, maxNbIns) - - // Track current gate to know when to reload constants - var currentCompiled *gkrtypes.CompiledGate - var nbConsts int - for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { - compiled := w.Gate.Compiled() - - // Preload constants if this is a new gate - if compiled != currentCompiled { - stack.freeElements() - stack.preloadConstants(compiled) - currentCompiled = compiled - nbConsts = compiled.NbConstants() + for _, in := range w.Inputs { + evaluators[wI].pushInput(&a[in][i]) } - - for inI, in := range w.Inputs { - ins[inI] = a[in][i] - insPtr[inI] = &ins[inI] - } - a[wI][i].Set(executeCompiledGate(compiled, &stack, insPtr[:len(w.Inputs)])) - - // Shrink stack back to just constants for reuse - stack.shrinkToConstants(nbConsts) + a[wI][i].Set(evaluators[wI].evaluate()) } } } @@ -749,96 +726,56 @@ func frToBigInts(dst []*big.Int, src []small_rational.SmallRational) { } } -// elementStack implements a synchronous memory pool underneath to minimize heap allocations. -type elementStack struct { - allocated []*small_rational.SmallRational - nbUsed int -} +// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the +// result in dst. In practice, dst will be one of the vars but this is not required. +type gateOp func(dst *small_rational.SmallRational, vars []small_rational.SmallRational, in []uint16) -// preloadConstants loads gate constants into the stack at indices [0, nbConsts). -// This should be called once per gate to prepare the stack for repeated use. -func (stack *elementStack) preloadConstants(gate *gkrtypes.CompiledGate) { - for _, constVal := range gate.Constants { - elem := stack.newElement() - elem.SetBigInt(constVal) +func opAdd(dst *small_rational.SmallRational, vars []small_rational.SmallRational, in []uint16) { + dst.Add(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Add(dst, &vars[in[i]]) } } -// shrinkToConstants resets the stack to contain only constants, ready for next invocation. -func (stack *elementStack) truncate(n int) { - stack.nbUsed = n -} - -func (stack *elementStack) newElement() *small_rational.SmallRational { - stack.nbUsed++ - if stack.nbUsed >= len(stack.allocated) { - stack.allocated = append(stack.allocated, new(small_rational.SmallRational)) - } - return stack.allocated[api.nbUsed-1] +func opMulAcc(dst *small_rational.SmallRational, vars []small_rational.SmallRational, in []uint16) { + var prod small_rational.SmallRational + prod.Mul(&vars[in[1]], &vars[in[2]]) + dst.Add(&vars[in[0]], &prod) } -func (stack *elementStack) push(x *small_rational.SmallRational) { - stack.newElement().Set(x) +func opNeg(dst *small_rational.SmallRational, vars []small_rational.SmallRational, in []uint16) { + dst.Neg(&vars[in[0]]) } -func opAdd(stack *elementStack, x []*small_rational.SmallRational) *small_rational.SmallRational { - res := stack.newElement() - res.Add(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Add(res, x[i]) +func opSub(dst *small_rational.SmallRational, vars []small_rational.SmallRational, in []uint16) { + dst.Sub(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Sub(dst, &vars[in[i]]) } - return res -} - -func opMulAcc(stack *elementStack, x []*small_rational.SmallRational) *small_rational.SmallRational { - // result = x[0] + (x[1] * x[2]) - prod := stack.newElement() - prod.Mul(x[1], x[2]) - res := stack.newElement() - res.Add(x[0], prod) - return res -} - -func opNeg(stack *elementStack, x []*small_rational.SmallRational) *small_rational.SmallRational { - res := stack.newElement() - res.Neg(x[0]) - return res } -func opSub(stack *elementStack, x []*small_rational.SmallRational) *small_rational.SmallRational { - res := stack.newElement() - res.Sub(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Sub(res, x[i]) +func opMul(dst *small_rational.SmallRational, vars []small_rational.SmallRational, in []uint16) { + dst.Mul(&vars[in[0]], &vars[in[1]]) + for i := 2; i < len(in); i++ { + dst.Mul(dst, &vars[in[i]]) } - return res -} - -func opMul(stack *elementStack, x []*small_rational.SmallRational) *small_rational.SmallRational { - res := stack.newElement() - res.Mul(x[0], x[1]) - for i := 2; i < len(x); i++ { - res.Mul(res, x[i]) - } - return res } -func opSumExp17(stack *elementStack, x []*small_rational.SmallRational) *small_rational.SmallRational { +func opSumExp17(dst *small_rational.SmallRational, vars []small_rational.SmallRational, in []uint16) { // result = (x[0] + x[1] + x[2])^17 - sum := stack.newElement() - sum.Add(x[0], x[1]) - sum.Add(sum, x[2]) - - res := stack.newElement() - res.Mul(sum, sum) // sum² - res.Mul(res, res) // sum⁴ - res.Mul(res, res) // sum⁸ - res.Mul(res, res) // sum¹⁶ - res.Mul(res, sum) // sum¹⁷ - return res + var sum small_rational.SmallRational + sum.Add(&vars[in[0]], &vars[in[1]]) + sum.Add(&sum, &vars[in[2]]) + + dst.Mul(&sum, &sum) // sum² + dst.Mul(dst, dst) // sum⁴ + dst.Mul(dst, dst) // sum⁸ + dst.Mul(dst, dst) // sum¹⁶ + dst.Mul(dst, &sum) // sum¹⁷ } -func (api *elementStack) Println(a ...frontend.Variable) { +/* +func (stack *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) var x small_rational.SmallRational @@ -854,105 +791,70 @@ func (api *elementStack) Println(a ...frontend.Variable) { } } fmt.Println(toPrint...) -} - -// executeCompiledGate executes a compiled gate using curve-specific field operations. -// The stack is expected to already contain constants at indices [0, nbConsts). -// Inputs are appended at [nbConsts, nbConsts+nbInputs), and results follow. -// After execution, the stack can be shrunk back to nbConsts for reuse. -func executeCompiledGate(gate *gkrtypes.CompiledGate, stack *elementStack, inputs []*small_rational.SmallRational) *small_rational.SmallRational { - - // Instruction executor functions - executors := [...]func(*elementStack, []*small_rational.SmallRational) *small_rational.SmallRational{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - - nbConsts := gate.NbConstants() - - // If no instructions, this is an identity-like gate; return first input - if len(gate.Instructions) == 0 { - if gate.NbInputs == 0 { - panic("gate has no inputs and no instructions") - } - return inputs[0] - } - - // Append inputs to stack after constants - // The stack now contains: [constants | inputs] - for _, input := range inputs { - ptr := stack.newElement() - ptr.Set(input) - } - - // Execute instructions, appending results to stack - // The stack grows to: [constants | inputs | results] - for i := range gate.Instructions { - inst := &gate.Instructions[i] - executor := executors[inst.Op] - if executor == nil { - panic(fmt.Sprintf("unknown gate operation: %d", inst.Op)) - } - - // Resolve operands from stack using instruction indices - operands := make([]*small_rational.SmallRational, len(inst.Inputs)) - for j, idx := range inst.Inputs { - operands[j] = stack.allocated[idx] - } - - _ = executor(stack, operands) - // Result is automatically appended to stack by executor - } - - // Last value in stack is the result - resultIdx := nbConsts + gate.NbInputs + len(gate.Instructions) - 1 - return stack.allocated[resultIdx] -} +}*/ // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate - stack elementStack - nbConsts int + gate *gkrtypes.CompiledGate + vars []small_rational.SmallRational + frameSize int // number of constants plus currently pushed inputs } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) *gateEvaluator { - eval := &gateEvaluator{ - gate: gate, - nbConsts: gate.NbConstants(), - inputs: make([]*small_rational.SmallRational, 0, gate.NbInputs), +func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { + e := gateEvaluator{ + gate: gate, + vars: make([]small_rational.SmallRational, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), } - eval.stack.preloadConstants(gate) - return eval + for i, constVal := range gate.Constants { + e.vars[i].SetBigInt(constVal) + } + return e } // pushInput adds an input to the evaluator's input buffer. -// Inputs must be added in order and the number of inputs must match the gate's NbInputs. +// Inputs must be added in order, and the number of inputs must match the gate's NbInputs. func (e *gateEvaluator) pushInput(input *small_rational.SmallRational) { - e.inputs = append(e.inputs, input) + e.vars[e.frameSize].Set(input) + e.frameSize++ } -// evaluate executes the gate with the current inputs and returns the result. -// The stack and input buffer are automatically reset after evaluation, +// evaluate adds top to the top of the stack, executes the gate on it and returns the result. +// The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. -func (e *gateEvaluator) evaluate() *small_rational.SmallRational { - if len(e.inputs) != e.gate.NbInputs { - panic(fmt.Sprintf("expected %d inputs, got %d", e.gate.NbInputs, len(e.inputs))) +// NB! The result is short-lived. It will be overwritten the next time evaluate is called. +func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rational.SmallRational { + for i := range top { + e.pushInput(&top[i]) } - result := executeCompiledGate(e.gate, &e.stack, e.inputs) + if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + } + + // Instruction executor functions + executors := [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, + } + + // Execute instructions, appending results to stack + // The stack grows to: [constants | inputs | results] + for i := range e.gate.Instructions { + executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + } + + res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] // Reset for next evaluation - e.stack.shrinkToConstants(e.nbConsts) - e.inputs = e.inputs[:0] // Clear input buffer + e.frameSize = e.gate.NbConstants() - return result + return res } From 9830c6bb413e4086c7f2bcb9b596e9aff56fe402 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 12 Jan 2026 18:09:29 -0600 Subject: [PATCH 096/251] fix: gate registry --- constraint/solver/gkrgates/registry.go | 74 +++++++------------ .../backend/template/gkr/gate_testing.go.tmpl | 4 +- internal/gkr/bls12-377/gate_testing.go | 5 +- internal/gkr/bls12-381/gate_testing.go | 5 +- internal/gkr/bls24-315/gate_testing.go | 5 +- internal/gkr/bls24-317/gate_testing.go | 5 +- internal/gkr/bn254/gate_testing.go | 5 +- internal/gkr/bw6-633/gate_testing.go | 5 +- internal/gkr/bw6-761/gate_testing.go | 5 +- internal/gkr/gkrtypes/compiledgate.go | 13 +--- internal/gkr/gkrtypes/types.go | 27 ++++++- 11 files changed, 67 insertions(+), 86 deletions(-) diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index 888864c78b..816c16b6ba 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -162,6 +162,8 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { allowedCurves = gnark.Curves() } + compiled := gkrtypes.CompileGateFunction(f, nbIn) + gatesLock.Lock() defer gatesLock.Unlock() @@ -172,11 +174,11 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { } for _, curve := range curvesForTesting { - gateVer, err := newGateVerifier(curve) + gateVer, err := newGateTester(g.Compiled(), g.NbIn(), curve) if err != nil { return err } - if !gateVer.equal(f, g.Evaluate, nbIn) { + if !gateVer.Equal(compiled) { return fmt.Errorf("mismatch with already registered gate \"%s\" (degree %d) over curve %s", s.name, g.Degree(), curve) } } @@ -185,7 +187,7 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { } for _, curve := range curvesForTesting { - gateVer, err := newGateVerifier(curve) + t, err := newGateTester(compiled, nbIn, curve) if err != nil { return err } @@ -195,12 +197,12 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { panic("invalid settings") } const maxAutoDegreeBound = 32 - if s.degree, err = gateVer.findDegree(f, maxAutoDegreeBound, nbIn); err != nil { + if s.degree, err = t.FindDegree(maxAutoDegreeBound); err != nil { return fmt.Errorf("for gate \"%s\": %v", s.name, err) } } else { if !s.noDegreeVerification { // check that the given degree is correct - if err = gateVer.verifyGateFunctionDegree(f, s.degree, nbIn); err != nil { + if err = t.VerifyDegree(s.degree); err != nil { return fmt.Errorf("for gate \"%s\": %v", s.name, err) } } @@ -208,18 +210,18 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { if s.solvableVar == -1 { if !s.noSolvableVarVerification { // find a solvable variable - s.solvableVar = gateVer.findSolvableVar(f, nbIn) + s.solvableVar = findSolvableVar(t, nbIn) } } else { // solvable variable given - if !s.noSolvableVarVerification && !gateVer.isVarSolvable(f, s.solvableVar, nbIn) { + if !s.noSolvableVarVerification && !isVarSolvable(t, s.solvableVar, nbIn) { return fmt.Errorf("cannot verify the solvability of variable %d in gate \"%s\"", s.solvableVar, s.name) } } } - gates[s.name] = gkrtypes.NewGate(f, nbIn, s.degree, s.solvableVar, allowedCurves) + gates[s.name] = gkrtypes.NewGate(f, compiled, nbIn, s.degree, s.solvableVar, allowedCurves) return nil } @@ -237,54 +239,30 @@ func Get(name gkr.GateName) *gkrtypes.Gate { type gateTester interface { IsAdditive(varIndex int) bool - FindDegree(max int) int - VerifyDegree(claimedDegree int) - Equal(other gkr.GateFunction) bool + FindDegree(max int) (int, error) + VerifyDegree(claimedDegree int) error + Equal(other *gkrtypes.CompiledGate) bool } -func newGateTester(curve ecc.ID) (gateTester, error) { +func newGateTester(g *gkrtypes.CompiledGate, nbIn int, curve ecc.ID) (gateTester, error) { switch curve { case ecc.BLS12_377: - o.isAdditive = bls12377.IsGateFunctionAdditive - o.findDegree = bls12377.FindGateFunctionDegree - o.verifyGateFunctionDegree = bls12377.VerifyGateFunctionDegree - o.equal = bls12377.EqualGateFunction + return bls12377.NewGateTester(g, nbIn), nil case ecc.BLS12_381: - o.isAdditive = bls12381.IsGateFunctionAdditive - o.findDegree = bls12381.FindGateFunctionDegree - o.verifyGateFunctionDegree = bls12381.VerifyGateFunctionDegree - o.equal = bls12381.EqualGateFunction + return bls12381.NewGateTester(g, nbIn), nil case ecc.BLS24_315: - o.isAdditive = bls24315.IsGateFunctionAdditive - o.findDegree = bls24315.FindGateFunctionDegree - o.verifyGateFunctionDegree = bls24315.VerifyGateFunctionDegree - o.equal = bls24315.EqualGateFunction + return bls24315.NewGateTester(g, nbIn), nil case ecc.BLS24_317: - o.isAdditive = bls24317.IsGateFunctionAdditive - o.findDegree = bls24317.FindGateFunctionDegree - o.verifyGateFunctionDegree = bls24317.VerifyGateFunctionDegree - o.equal = bls24317.EqualGateFunction + return bls24317.NewGateTester(g, nbIn), nil case ecc.BN254: - return bn254.NewGateTester() - o.isAdditive = bn254.IsGateFunctionAdditive - o.findDegree = bn254.FindGateFunctionDegree - o.verifyGateFunctionDegree = bn254.VerifyGateFunctionDegree - o.equal = bn254.EqualGateFunction + return bn254.NewGateTester(g, nbIn), nil case ecc.BW6_633: - o.isAdditive = bw6633.IsGateFunctionAdditive - o.findDegree = bw6633.FindGateFunctionDegree - o.verifyGateFunctionDegree = bw6633.VerifyGateFunctionDegree - o.equal = bw6633.EqualGateFunction + return bw6633.NewGateTester(g, nbIn), nil case ecc.BW6_761: - o.isAdditive = bw6761.IsGateFunctionAdditive - o.findDegree = bw6761.FindGateFunctionDegree - o.verifyGateFunctionDegree = bw6761.VerifyGateFunctionDegree - o.equal = bw6761.EqualGateFunction - default: - err = fmt.Errorf("unsupported curve %s", curve) + return bw6761.NewGateTester(g, nbIn), nil } - return &o, err + return nil, fmt.Errorf("unsupported curve %s", curve) } // GetDefaultGateName provides a standardized name for a gate function, depending on its package and name. @@ -297,9 +275,9 @@ func GetDefaultGateName(fn gkr.GateFunction) gkr.GateName { // findSolvableVar returns the index of a variable whose value can be uniquely determined from that of the other variables along with the gate's output. // It returns -1 if it fails to find one. // nbIn is the number of inputs to the gate -func (v *gateVerifier) findSolvableVar(f gkr.GateFunction, nbIn int) int { +func findSolvableVar(t gateTester, nbIn int) int { for i := range nbIn { - if v.isAdditive(f, i, nbIn) { + if t.IsAdditive(i) { return i } } @@ -309,8 +287,8 @@ func (v *gateVerifier) findSolvableVar(f gkr.GateFunction, nbIn int) int { // isVarSolvable returns whether claimedSolvableVar is a variable whose value can be uniquely determined from that of the other variables along with the gate's output. // It returns false if it fails to verify this claim. // nbIn is the number of inputs to the gate. -func (v *gateVerifier) isVarSolvable(f gkr.GateFunction, claimedSolvableVar, nbIn int) bool { - return v.isAdditive(f, claimedSolvableVar, nbIn) +func isVarSolvable(t gateTester, claimedSolvableVar, nbIn int) bool { + return t.IsAdditive(claimedSolvableVar) } func init() { diff --git a/internal/generator/backend/template/gkr/gate_testing.go.tmpl b/internal/generator/backend/template/gkr/gate_testing.go.tmpl index 310afb216d..53d8c0ba7d 100644 --- a/internal/generator/backend/template/gkr/gate_testing.go.tmpl +++ b/internal/generator/backend/template/gkr/gate_testing.go.tmpl @@ -165,11 +165,11 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g gkr.GateFunction) bool { +func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make({{.FieldPackageName}}.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gEval := newGateEvaluator(g) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bls12-377/gate_testing.go b/internal/gkr/bls12-377/gate_testing.go index 8d7ff44a85..5b1fe90ed4 100644 --- a/internal/gkr/bls12-377/gate_testing.go +++ b/internal/gkr/bls12-377/gate_testing.go @@ -16,7 +16,6 @@ import ( "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/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) type GateTester struct { @@ -153,11 +152,11 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g gkr.GateFunction) bool { +func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gEval := newGateEvaluator(g) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bls12-381/gate_testing.go b/internal/gkr/bls12-381/gate_testing.go index f1ca5e423f..f1462c5353 100644 --- a/internal/gkr/bls12-381/gate_testing.go +++ b/internal/gkr/bls12-381/gate_testing.go @@ -16,7 +16,6 @@ import ( "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/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) type GateTester struct { @@ -153,11 +152,11 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g gkr.GateFunction) bool { +func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gEval := newGateEvaluator(g) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bls24-315/gate_testing.go b/internal/gkr/bls24-315/gate_testing.go index 4273a01438..206139595d 100644 --- a/internal/gkr/bls24-315/gate_testing.go +++ b/internal/gkr/bls24-315/gate_testing.go @@ -16,7 +16,6 @@ import ( "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/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) type GateTester struct { @@ -153,11 +152,11 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g gkr.GateFunction) bool { +func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gEval := newGateEvaluator(g) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bls24-317/gate_testing.go b/internal/gkr/bls24-317/gate_testing.go index af076a174b..761f5c1cad 100644 --- a/internal/gkr/bls24-317/gate_testing.go +++ b/internal/gkr/bls24-317/gate_testing.go @@ -16,7 +16,6 @@ import ( "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/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) type GateTester struct { @@ -153,11 +152,11 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g gkr.GateFunction) bool { +func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gEval := newGateEvaluator(g) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bn254/gate_testing.go b/internal/gkr/bn254/gate_testing.go index 4f40d280a9..ecf52f4660 100644 --- a/internal/gkr/bn254/gate_testing.go +++ b/internal/gkr/bn254/gate_testing.go @@ -16,7 +16,6 @@ import ( "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/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) type GateTester struct { @@ -153,11 +152,11 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g gkr.GateFunction) bool { +func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gEval := newGateEvaluator(g) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bw6-633/gate_testing.go b/internal/gkr/bw6-633/gate_testing.go index 05415ac00e..8e03bcb582 100644 --- a/internal/gkr/bw6-633/gate_testing.go +++ b/internal/gkr/bw6-633/gate_testing.go @@ -16,7 +16,6 @@ import ( "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/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) type GateTester struct { @@ -153,11 +152,11 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g gkr.GateFunction) bool { +func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gEval := newGateEvaluator(g) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bw6-761/gate_testing.go b/internal/gkr/bw6-761/gate_testing.go index 8cc8ed50cc..0274a09790 100644 --- a/internal/gkr/bw6-761/gate_testing.go +++ b/internal/gkr/bw6-761/gate_testing.go @@ -16,7 +16,6 @@ import ( "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/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" ) type GateTester struct { @@ -153,11 +152,11 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g gkr.GateFunction) bool { +func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(gkrtypes.CompileGateFunction(g, t.nbIn, -1, -1, []ecc.ID{})) // TODO empty params concerning + gEval := newGateEvaluator(g) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/gkrtypes/compiledgate.go b/internal/gkr/gkrtypes/compiledgate.go index fbe37a3dfc..76c22dd7c3 100644 --- a/internal/gkr/gkrtypes/compiledgate.go +++ b/internal/gkr/gkrtypes/compiledgate.go @@ -3,7 +3,6 @@ package gkrtypes import ( "math/big" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi/gkr" @@ -39,10 +38,6 @@ type GateInstruction struct { type CompiledGate struct { Instructions []GateInstruction // sequence of operations Constants []*big.Int // constant values at indices [0, nbConsts) - NbInputs int // number of gate inputs - Degree int // total degree of the polynomial - SolvableVar int // if there is a solvable variable, its index, -1 otherwise - Curves []ecc.ID // curves that the gate is allowed to be used over } // NbConstants returns the number of constants in the gate @@ -224,13 +219,13 @@ func (gc *gateCompiler) remapIndices() { // CompileGateFunction compiles a gate function into a CompiledGate. // The gate function should be of type gkr.GateFunction. -func CompileGateFunction(f gkr.GateFunction, nbInputs int, degree int, solvableVar int, curves []ecc.ID) *CompiledGate { +func CompileGateFunction(f gkr.GateFunction, nbInputs int) *CompiledGate { // Create recording API compiler := newGateCompiler(nbInputs) // Create input variables as integers 0, 1, 2, ... inputs := make([]frontend.Variable, nbInputs) - for i := range nbInputs { + for i := range inputs { inputs[i] = i } @@ -248,9 +243,5 @@ func CompileGateFunction(f gkr.GateFunction, nbInputs int, degree int, solvableV return &CompiledGate{ Instructions: compiler.GetInstructions(), Constants: compiler.constants, - NbInputs: nbInputs, - Degree: degree, - SolvableVar: solvableVar, - Curves: curves, } } diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 669f0f1e9a..b77a368740 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -25,13 +25,12 @@ type Gate struct { // NewGate creates a new gate function with the given parameters: // - f: the polynomial function defining the gate +// - compiled: the compiled form of the gate function // - nbIn: number of inputs to the gate // - degree: total degree of the polynomial. In case of multivariate polynomials, it is the maximum degree over all terms. // - solvableVar: if there is a variable whose value can be uniquely determined from the value of the gate and the other inputs, its index, -1 otherwise // - curves: curves that the gate is allowed to be used over -func NewGate(f gkr.GateFunction, nbIn int, degree int, solvableVar int, curves []ecc.ID) *Gate { - // Compile the gate function immediately - compiled := CompileGateFunction(f, nbIn, degree, solvableVar, curves) +func NewGate(f gkr.GateFunction, compiled *CompiledGate, nbIn int, degree int, solvableVar int, curves []ecc.ID) *Gate { return &Gate{ compiled: compiled, @@ -411,13 +410,18 @@ var ErrZeroFunction = errors.New("detected a zero function") func Identity() *Gate { return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return in[0] - }, 1, 1, 0, gnark.Curves()) + }, &CompiledGate{}, 1, 1, 0, gnark.Curves()) } // Add2 gate: (x, y) -> x + y func Add2() *Gate { return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return api.Add(in[0], in[1]) + }, &CompiledGate{ + Instructions: []GateInstruction{{ + Op: OpAdd, + Inputs: []uint16{0, 1}, + }}, }, 2, 1, 0, gnark.Curves()) } @@ -425,6 +429,11 @@ func Add2() *Gate { func Sub2() *Gate { return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return api.Sub(in[0], in[1]) + }, &CompiledGate{ + Instructions: []GateInstruction{{ + Op: OpSub, + Inputs: []uint16{0, 1}, + }}, }, 2, 1, 0, gnark.Curves()) } @@ -432,6 +441,11 @@ func Sub2() *Gate { func Neg() *Gate { return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return api.Neg(in[0]) + }, &CompiledGate{ + Instructions: []GateInstruction{{ + Op: OpNeg, + Inputs: []uint16{0}, + }}, }, 1, 1, 0, gnark.Curves()) } @@ -439,5 +453,10 @@ func Neg() *Gate { func Mul2() *Gate { return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return api.Mul(in[0], in[1]) + }, &CompiledGate{ + Instructions: []GateInstruction{{ + Op: OpMul, + Inputs: []uint16{0, 1}, + }}, }, 2, 2, -1, gnark.Curves()) } From c1b406242fe214c18fbcfeeac91ab1243b2f1b62 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 12 Jan 2026 18:50:19 -0600 Subject: [PATCH 097/251] revert: bring back Evaluate --- .../backend/template/gkr/gate_testing.go.tmpl | 4 +-- .../backend/template/gkr/gkr.go.tmpl | 27 +++++++++---------- internal/gkr/bls12-377/gate_testing.go | 4 +-- internal/gkr/bls12-377/gkr.go | 27 +++++++++---------- internal/gkr/bls12-381/gate_testing.go | 4 +-- internal/gkr/bls12-381/gkr.go | 27 +++++++++---------- internal/gkr/bls24-315/gate_testing.go | 4 +-- internal/gkr/bls24-315/gkr.go | 27 +++++++++---------- internal/gkr/bls24-317/gate_testing.go | 4 +-- internal/gkr/bls24-317/gkr.go | 27 +++++++++---------- internal/gkr/bn254/gate_testing.go | 4 +-- internal/gkr/bn254/gkr.go | 27 +++++++++---------- internal/gkr/bw6-633/gate_testing.go | 4 +-- internal/gkr/bw6-633/gkr.go | 27 +++++++++---------- internal/gkr/bw6-761/gate_testing.go | 4 +-- internal/gkr/bw6-761/gkr.go | 27 +++++++++---------- internal/gkr/gkrtesting/gkrtesting.go | 8 +++--- internal/gkr/gkrtypes/compiledgate_test.go | 17 +++++------- internal/gkr/gkrtypes/types.go | 12 +++++---- internal/gkr/small_rational/gkr.go | 27 +++++++++---------- 20 files changed, 152 insertions(+), 160 deletions(-) diff --git a/internal/generator/backend/template/gkr/gate_testing.go.tmpl b/internal/generator/backend/template/gkr/gate_testing.go.tmpl index 53d8c0ba7d..445ee6eb25 100644 --- a/internal/generator/backend/template/gkr/gate_testing.go.tmpl +++ b/internal/generator/backend/template/gkr/gate_testing.go.tmpl @@ -20,7 +20,7 @@ type GateTester struct { func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { return &GateTester{ - evaluator: newGateEvaluator(g), + evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, } } @@ -169,7 +169,7 @@ func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make({{.FieldPackageName}}.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(g) + gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 05a27ae9d2..9a2138dec2 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -91,7 +91,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []{{ .ElementType e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled()) + evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -231,8 +231,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { var step {{ .ElementType }} // Create gate evaluator once per thread - compiled := wire.Gate.Compiled() - evaluator := newGateEvaluator(compiled) + evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) res := make([]{{ .ElementType }}, degGJ) @@ -259,7 +258,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { nextEIndex := len(ml) for d := range degGJ { // Push gate inputs - for i := 0; i < nbGateIn; i++ { + for i := range nbGateIn { evaluator.pushInput(&mlEvals[eIndex+1+i]) } summand := evaluator.evaluate() @@ -294,13 +293,13 @@ func (c *eqTimesGateEvalSumcheckClaims) next(challenge {{ .ElementType }}) polyn n := len(c.eq) / 2 if n < minBlockSize { // no parallelization - for i := 0; i < len(c.input); i++ { + for i := range c.input { c.input[i].Fold(challenge) } c.eq.Fold(challenge) } else { wgs := make([]*sync.WaitGroup, len(c.input)) - for i := 0; i < len(c.input); i++ { + for i := range c.input { wgs[i] = c.manager.workers.Submit(n, c.input[i].FoldParallel(challenge), minBlockSize) } c.manager.workers.Submit(n, c.eq.FoldParallel(challenge), minBlockSize).Wait() @@ -656,15 +655,13 @@ func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcri func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() - maxNbIns := 0 evaluators := make([]gateEvaluator, len(wires)) - for i, w := range wires { - maxNbIns = max(maxNbIns, len(w.Inputs)) + for i := range wires { if len(a[i]) != nbInstances { a[i] = make([]{{ .ElementType }}, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) } for i := range nbInstances { @@ -795,14 +792,16 @@ type gateEvaluator struct { gate *gkrtypes.CompiledGate vars []{{ .ElementType }} frameSize int // number of constants plus currently pushed inputs + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ gate: gate, - vars: make([]{{ .ElementType }}, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), + nbIn: nbIn, + vars: make([]{{ .ElementType }}, gate.NbConstants()+nbIn+len(gate.Instructions)), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) @@ -826,8 +825,8 @@ func (e *gateEvaluator) evaluate(top ...{{ .ElementType }}) *{{ .ElementType }} e.pushInput(&top[i]) } - if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + if e.frameSize != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } // Instruction executor functions diff --git a/internal/gkr/bls12-377/gate_testing.go b/internal/gkr/bls12-377/gate_testing.go index 5b1fe90ed4..09ee174e09 100644 --- a/internal/gkr/bls12-377/gate_testing.go +++ b/internal/gkr/bls12-377/gate_testing.go @@ -25,7 +25,7 @@ type GateTester struct { func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { return &GateTester{ - evaluator: newGateEvaluator(g), + evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, } } @@ -156,7 +156,7 @@ func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(g) + gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 2469259ea1..3f0a31972f 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -98,7 +98,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled()) + evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -237,8 +237,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { var step fr.Element // Create gate evaluator once per thread - compiled := wire.Gate.Compiled() - evaluator := newGateEvaluator(compiled) + evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) res := make([]fr.Element, degGJ) @@ -265,7 +264,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { nextEIndex := len(ml) for d := range degGJ { // Push gate inputs - for i := 0; i < nbGateIn; i++ { + for i := range nbGateIn { evaluator.pushInput(&mlEvals[eIndex+1+i]) } summand := evaluator.evaluate() @@ -300,13 +299,13 @@ func (c *eqTimesGateEvalSumcheckClaims) next(challenge fr.Element) polynomial.Po n := len(c.eq) / 2 if n < minBlockSize { // no parallelization - for i := 0; i < len(c.input); i++ { + for i := range c.input { c.input[i].Fold(challenge) } c.eq.Fold(challenge) } else { wgs := make([]*sync.WaitGroup, len(c.input)) - for i := 0; i < len(c.input); i++ { + for i := range c.input { wgs[i] = c.manager.workers.Submit(n, c.input[i].FoldParallel(challenge), minBlockSize) } c.manager.workers.Submit(n, c.eq.FoldParallel(challenge), minBlockSize).Wait() @@ -661,15 +660,13 @@ func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcri func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() - maxNbIns := 0 evaluators := make([]gateEvaluator, len(wires)) - for i, w := range wires { - maxNbIns = max(maxNbIns, len(w.Inputs)) + for i := range wires { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) } for i := range nbInstances { @@ -800,14 +797,16 @@ type gateEvaluator struct { gate *gkrtypes.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ gate: gate, - vars: make([]fr.Element, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), + nbIn: nbIn, + vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) @@ -831,8 +830,8 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { e.pushInput(&top[i]) } - if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + if e.frameSize != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } // Instruction executor functions diff --git a/internal/gkr/bls12-381/gate_testing.go b/internal/gkr/bls12-381/gate_testing.go index f1462c5353..169821fd27 100644 --- a/internal/gkr/bls12-381/gate_testing.go +++ b/internal/gkr/bls12-381/gate_testing.go @@ -25,7 +25,7 @@ type GateTester struct { func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { return &GateTester{ - evaluator: newGateEvaluator(g), + evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, } } @@ -156,7 +156,7 @@ func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(g) + gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index a77652a1b4..0089a09454 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -98,7 +98,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled()) + evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -237,8 +237,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { var step fr.Element // Create gate evaluator once per thread - compiled := wire.Gate.Compiled() - evaluator := newGateEvaluator(compiled) + evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) res := make([]fr.Element, degGJ) @@ -265,7 +264,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { nextEIndex := len(ml) for d := range degGJ { // Push gate inputs - for i := 0; i < nbGateIn; i++ { + for i := range nbGateIn { evaluator.pushInput(&mlEvals[eIndex+1+i]) } summand := evaluator.evaluate() @@ -300,13 +299,13 @@ func (c *eqTimesGateEvalSumcheckClaims) next(challenge fr.Element) polynomial.Po n := len(c.eq) / 2 if n < minBlockSize { // no parallelization - for i := 0; i < len(c.input); i++ { + for i := range c.input { c.input[i].Fold(challenge) } c.eq.Fold(challenge) } else { wgs := make([]*sync.WaitGroup, len(c.input)) - for i := 0; i < len(c.input); i++ { + for i := range c.input { wgs[i] = c.manager.workers.Submit(n, c.input[i].FoldParallel(challenge), minBlockSize) } c.manager.workers.Submit(n, c.eq.FoldParallel(challenge), minBlockSize).Wait() @@ -661,15 +660,13 @@ func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcri func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() - maxNbIns := 0 evaluators := make([]gateEvaluator, len(wires)) - for i, w := range wires { - maxNbIns = max(maxNbIns, len(w.Inputs)) + for i := range wires { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) } for i := range nbInstances { @@ -800,14 +797,16 @@ type gateEvaluator struct { gate *gkrtypes.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ gate: gate, - vars: make([]fr.Element, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), + nbIn: nbIn, + vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) @@ -831,8 +830,8 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { e.pushInput(&top[i]) } - if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + if e.frameSize != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } // Instruction executor functions diff --git a/internal/gkr/bls24-315/gate_testing.go b/internal/gkr/bls24-315/gate_testing.go index 206139595d..3b0d415e84 100644 --- a/internal/gkr/bls24-315/gate_testing.go +++ b/internal/gkr/bls24-315/gate_testing.go @@ -25,7 +25,7 @@ type GateTester struct { func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { return &GateTester{ - evaluator: newGateEvaluator(g), + evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, } } @@ -156,7 +156,7 @@ func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(g) + gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index a8344f36f3..0b13654029 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -98,7 +98,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled()) + evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -237,8 +237,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { var step fr.Element // Create gate evaluator once per thread - compiled := wire.Gate.Compiled() - evaluator := newGateEvaluator(compiled) + evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) res := make([]fr.Element, degGJ) @@ -265,7 +264,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { nextEIndex := len(ml) for d := range degGJ { // Push gate inputs - for i := 0; i < nbGateIn; i++ { + for i := range nbGateIn { evaluator.pushInput(&mlEvals[eIndex+1+i]) } summand := evaluator.evaluate() @@ -300,13 +299,13 @@ func (c *eqTimesGateEvalSumcheckClaims) next(challenge fr.Element) polynomial.Po n := len(c.eq) / 2 if n < minBlockSize { // no parallelization - for i := 0; i < len(c.input); i++ { + for i := range c.input { c.input[i].Fold(challenge) } c.eq.Fold(challenge) } else { wgs := make([]*sync.WaitGroup, len(c.input)) - for i := 0; i < len(c.input); i++ { + for i := range c.input { wgs[i] = c.manager.workers.Submit(n, c.input[i].FoldParallel(challenge), minBlockSize) } c.manager.workers.Submit(n, c.eq.FoldParallel(challenge), minBlockSize).Wait() @@ -661,15 +660,13 @@ func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcri func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() - maxNbIns := 0 evaluators := make([]gateEvaluator, len(wires)) - for i, w := range wires { - maxNbIns = max(maxNbIns, len(w.Inputs)) + for i := range wires { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) } for i := range nbInstances { @@ -800,14 +797,16 @@ type gateEvaluator struct { gate *gkrtypes.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ gate: gate, - vars: make([]fr.Element, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), + nbIn: nbIn, + vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) @@ -831,8 +830,8 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { e.pushInput(&top[i]) } - if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + if e.frameSize != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } // Instruction executor functions diff --git a/internal/gkr/bls24-317/gate_testing.go b/internal/gkr/bls24-317/gate_testing.go index 761f5c1cad..be906b0a55 100644 --- a/internal/gkr/bls24-317/gate_testing.go +++ b/internal/gkr/bls24-317/gate_testing.go @@ -25,7 +25,7 @@ type GateTester struct { func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { return &GateTester{ - evaluator: newGateEvaluator(g), + evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, } } @@ -156,7 +156,7 @@ func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(g) + gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index d6d8287c45..4322e2e3d9 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -98,7 +98,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled()) + evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -237,8 +237,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { var step fr.Element // Create gate evaluator once per thread - compiled := wire.Gate.Compiled() - evaluator := newGateEvaluator(compiled) + evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) res := make([]fr.Element, degGJ) @@ -265,7 +264,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { nextEIndex := len(ml) for d := range degGJ { // Push gate inputs - for i := 0; i < nbGateIn; i++ { + for i := range nbGateIn { evaluator.pushInput(&mlEvals[eIndex+1+i]) } summand := evaluator.evaluate() @@ -300,13 +299,13 @@ func (c *eqTimesGateEvalSumcheckClaims) next(challenge fr.Element) polynomial.Po n := len(c.eq) / 2 if n < minBlockSize { // no parallelization - for i := 0; i < len(c.input); i++ { + for i := range c.input { c.input[i].Fold(challenge) } c.eq.Fold(challenge) } else { wgs := make([]*sync.WaitGroup, len(c.input)) - for i := 0; i < len(c.input); i++ { + for i := range c.input { wgs[i] = c.manager.workers.Submit(n, c.input[i].FoldParallel(challenge), minBlockSize) } c.manager.workers.Submit(n, c.eq.FoldParallel(challenge), minBlockSize).Wait() @@ -661,15 +660,13 @@ func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcri func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() - maxNbIns := 0 evaluators := make([]gateEvaluator, len(wires)) - for i, w := range wires { - maxNbIns = max(maxNbIns, len(w.Inputs)) + for i := range wires { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) } for i := range nbInstances { @@ -800,14 +797,16 @@ type gateEvaluator struct { gate *gkrtypes.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ gate: gate, - vars: make([]fr.Element, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), + nbIn: nbIn, + vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) @@ -831,8 +830,8 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { e.pushInput(&top[i]) } - if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + if e.frameSize != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } // Instruction executor functions diff --git a/internal/gkr/bn254/gate_testing.go b/internal/gkr/bn254/gate_testing.go index ecf52f4660..1ea0cd8a82 100644 --- a/internal/gkr/bn254/gate_testing.go +++ b/internal/gkr/bn254/gate_testing.go @@ -25,7 +25,7 @@ type GateTester struct { func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { return &GateTester{ - evaluator: newGateEvaluator(g), + evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, } } @@ -156,7 +156,7 @@ func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(g) + gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 312b724257..2780f53da5 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -98,7 +98,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled()) + evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -237,8 +237,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { var step fr.Element // Create gate evaluator once per thread - compiled := wire.Gate.Compiled() - evaluator := newGateEvaluator(compiled) + evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) res := make([]fr.Element, degGJ) @@ -265,7 +264,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { nextEIndex := len(ml) for d := range degGJ { // Push gate inputs - for i := 0; i < nbGateIn; i++ { + for i := range nbGateIn { evaluator.pushInput(&mlEvals[eIndex+1+i]) } summand := evaluator.evaluate() @@ -300,13 +299,13 @@ func (c *eqTimesGateEvalSumcheckClaims) next(challenge fr.Element) polynomial.Po n := len(c.eq) / 2 if n < minBlockSize { // no parallelization - for i := 0; i < len(c.input); i++ { + for i := range c.input { c.input[i].Fold(challenge) } c.eq.Fold(challenge) } else { wgs := make([]*sync.WaitGroup, len(c.input)) - for i := 0; i < len(c.input); i++ { + for i := range c.input { wgs[i] = c.manager.workers.Submit(n, c.input[i].FoldParallel(challenge), minBlockSize) } c.manager.workers.Submit(n, c.eq.FoldParallel(challenge), minBlockSize).Wait() @@ -661,15 +660,13 @@ func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcri func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() - maxNbIns := 0 evaluators := make([]gateEvaluator, len(wires)) - for i, w := range wires { - maxNbIns = max(maxNbIns, len(w.Inputs)) + for i := range wires { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) } for i := range nbInstances { @@ -800,14 +797,16 @@ type gateEvaluator struct { gate *gkrtypes.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ gate: gate, - vars: make([]fr.Element, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), + nbIn: nbIn, + vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) @@ -831,8 +830,8 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { e.pushInput(&top[i]) } - if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + if e.frameSize != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } // Instruction executor functions diff --git a/internal/gkr/bw6-633/gate_testing.go b/internal/gkr/bw6-633/gate_testing.go index 8e03bcb582..cb085e336a 100644 --- a/internal/gkr/bw6-633/gate_testing.go +++ b/internal/gkr/bw6-633/gate_testing.go @@ -25,7 +25,7 @@ type GateTester struct { func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { return &GateTester{ - evaluator: newGateEvaluator(g), + evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, } } @@ -156,7 +156,7 @@ func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(g) + gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 92f3c4ca45..8eb8d22eb1 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -98,7 +98,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled()) + evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -237,8 +237,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { var step fr.Element // Create gate evaluator once per thread - compiled := wire.Gate.Compiled() - evaluator := newGateEvaluator(compiled) + evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) res := make([]fr.Element, degGJ) @@ -265,7 +264,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { nextEIndex := len(ml) for d := range degGJ { // Push gate inputs - for i := 0; i < nbGateIn; i++ { + for i := range nbGateIn { evaluator.pushInput(&mlEvals[eIndex+1+i]) } summand := evaluator.evaluate() @@ -300,13 +299,13 @@ func (c *eqTimesGateEvalSumcheckClaims) next(challenge fr.Element) polynomial.Po n := len(c.eq) / 2 if n < minBlockSize { // no parallelization - for i := 0; i < len(c.input); i++ { + for i := range c.input { c.input[i].Fold(challenge) } c.eq.Fold(challenge) } else { wgs := make([]*sync.WaitGroup, len(c.input)) - for i := 0; i < len(c.input); i++ { + for i := range c.input { wgs[i] = c.manager.workers.Submit(n, c.input[i].FoldParallel(challenge), minBlockSize) } c.manager.workers.Submit(n, c.eq.FoldParallel(challenge), minBlockSize).Wait() @@ -661,15 +660,13 @@ func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcri func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() - maxNbIns := 0 evaluators := make([]gateEvaluator, len(wires)) - for i, w := range wires { - maxNbIns = max(maxNbIns, len(w.Inputs)) + for i := range wires { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) } for i := range nbInstances { @@ -800,14 +797,16 @@ type gateEvaluator struct { gate *gkrtypes.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ gate: gate, - vars: make([]fr.Element, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), + nbIn: nbIn, + vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) @@ -831,8 +830,8 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { e.pushInput(&top[i]) } - if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + if e.frameSize != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } // Instruction executor functions diff --git a/internal/gkr/bw6-761/gate_testing.go b/internal/gkr/bw6-761/gate_testing.go index 0274a09790..345421e617 100644 --- a/internal/gkr/bw6-761/gate_testing.go +++ b/internal/gkr/bw6-761/gate_testing.go @@ -25,7 +25,7 @@ type GateTester struct { func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { return &GateTester{ - evaluator: newGateEvaluator(g), + evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, } } @@ -156,7 +156,7 @@ func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(g) + gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 6e196b365a..06d7883f37 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -98,7 +98,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled()) + evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -237,8 +237,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { var step fr.Element // Create gate evaluator once per thread - compiled := wire.Gate.Compiled() - evaluator := newGateEvaluator(compiled) + evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) res := make([]fr.Element, degGJ) @@ -265,7 +264,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { nextEIndex := len(ml) for d := range degGJ { // Push gate inputs - for i := 0; i < nbGateIn; i++ { + for i := range nbGateIn { evaluator.pushInput(&mlEvals[eIndex+1+i]) } summand := evaluator.evaluate() @@ -300,13 +299,13 @@ func (c *eqTimesGateEvalSumcheckClaims) next(challenge fr.Element) polynomial.Po n := len(c.eq) / 2 if n < minBlockSize { // no parallelization - for i := 0; i < len(c.input); i++ { + for i := range c.input { c.input[i].Fold(challenge) } c.eq.Fold(challenge) } else { wgs := make([]*sync.WaitGroup, len(c.input)) - for i := 0; i < len(c.input); i++ { + for i := range c.input { wgs[i] = c.manager.workers.Submit(n, c.input[i].FoldParallel(challenge), minBlockSize) } c.manager.workers.Submit(n, c.eq.FoldParallel(challenge), minBlockSize).Wait() @@ -661,15 +660,13 @@ func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcri func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() - maxNbIns := 0 evaluators := make([]gateEvaluator, len(wires)) - for i, w := range wires { - maxNbIns = max(maxNbIns, len(w.Inputs)) + for i := range wires { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) } for i := range nbInstances { @@ -800,14 +797,16 @@ type gateEvaluator struct { gate *gkrtypes.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ gate: gate, - vars: make([]fr.Element, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), + nbIn: nbIn, + vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) @@ -831,8 +830,8 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { e.pushInput(&top[i]) } - if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + if e.frameSize != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } // Instruction executor functions diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index d4810b31b2..7e37aedeb5 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -28,7 +28,7 @@ func NewCache() *Cache { gates[gkr.Sub2] = gkrtypes.Sub2() gates[gkr.Neg] = gkrtypes.Neg() gates[gkr.Mul2] = gkrtypes.Mul2() - gates["mimc"] = gkrtypes.NewGate(func(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { + mimcF := func(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { sum := api.Add(input[0], input[1]) //.Add(&sum, &m.ark) TODO: add ark res := api.Mul(sum, sum) // sum^2 res = api.Mul(res, sum) // sum^3 @@ -36,10 +36,12 @@ func NewCache() *Cache { res = api.Mul(res, sum) // sum^7 return res - }, 2, 7, -1, gnark.Curves()) + } + mimcCompiled := gkrtypes.CompileGateFunction(mimcF, 2) + gates["mimc"] = gkrtypes.NewGate(mimcF, mimcCompiled, 2, 7, -1, gnark.Curves()) gates["select-input-3"] = gkrtypes.NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return in[2] - }, 3, 1, 0, gnark.Curves()) + }, &gkrtypes.CompiledGate{}, 3, 1, 0, gnark.Curves()) return &Cache{ circuits: make(map[string]gkrtypes.Circuit), diff --git a/internal/gkr/gkrtypes/compiledgate_test.go b/internal/gkr/gkrtypes/compiledgate_test.go index 86cc719bec..b733396bc8 100644 --- a/internal/gkr/gkrtypes/compiledgate_test.go +++ b/internal/gkr/gkrtypes/compiledgate_test.go @@ -4,7 +4,6 @@ import ( "math/big" "testing" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/gkrapi/gkr" ) @@ -17,8 +16,9 @@ func TestCompiledGateWithConstants(t *testing.T) { return api.Add(in[0], five) } + const nbIn = 1 // Compile the gate - compiled := CompileGateFunction(addConstantGate, 1, 1, -1, []ecc.ID{ecc.BN254}) + compiled := CompileGateFunction(addConstantGate, nbIn) // Verify the gate has constants if len(compiled.Constants) == 0 { @@ -50,7 +50,7 @@ func TestCompiledGateWithConstants(t *testing.T) { if idx != 0 { t.Errorf("Expected constant index 0, got %d", idx) } - } else if idx >= uint16(nbConsts) && idx < uint16(nbConsts+compiled.NbInputs) { + } else if idx >= uint16(nbConsts) && idx < uint16(nbConsts+nbIn) { hasInput = true inputIdx := idx - uint16(nbConsts) if inputIdx != 0 { @@ -65,11 +65,6 @@ func TestCompiledGateWithConstants(t *testing.T) { if !hasInput { t.Error("Expected instruction to reference an input") } - - t.Logf("Successfully compiled gate with %d constants and %d instructions", - len(compiled.Constants), len(compiled.Instructions)) - t.Logf("Index layout: constants [0,%d), inputs [%d,%d)", - nbConsts, nbConsts, nbConsts+compiled.NbInputs) } // TestCompiledGateWithMultipleConstants tests gates with multiple different constants @@ -83,7 +78,8 @@ func TestCompiledGateWithMultipleConstants(t *testing.T) { } // Compile the gate - compiled := CompileGateFunction(complexGate, 1, 2, -1, []ecc.ID{ecc.BN254}) + const nbIn = 1 + compiled := CompileGateFunction(complexGate, nbIn) // Verify we have two constants if len(compiled.Constants) != 2 { @@ -115,8 +111,9 @@ func TestConstantDeduplication(t *testing.T) { return api.Add(sum1, sum2) } + const nbIn = 2 // Compile the gate - compiled := CompileGateFunction(gateWithDuplicates, 2, 1, -1, []ecc.ID{ecc.BN254}) + compiled := CompileGateFunction(gateWithDuplicates, nbIn) // Verify only one constant is stored (deduplication) if len(compiled.Constants) != 1 { diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index b77a368740..951d9a5654 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -16,11 +16,12 @@ import ( // A Gate is a low-degree multivariate polynomial type Gate struct { - compiled *CompiledGate // Compiled form of the gate - nbIn int // number of inputs - degree int // total degree of the polynomial - solvableVar int // if there is a variable whose value can be uniquely determined from the value of the gate and the other inputs, its index, -1 otherwise - curves []ecc.ID // curves that the gate is allowed to be used over + Evaluate gkr.GateFunction // Evaluate the polynomial function defining the gate + compiled *CompiledGate // Compiled form of the gate function (used in native prover) + nbIn int // number of inputs + degree int // total degree of the polynomial + solvableVar int // if there is a variable whose value can be uniquely determined from the value of the gate and the other inputs, its index, -1 otherwise + curves []ecc.ID // curves that the gate is allowed to be used over } // NewGate creates a new gate function with the given parameters: @@ -33,6 +34,7 @@ type Gate struct { func NewGate(f gkr.GateFunction, compiled *CompiledGate, nbIn int, degree int, solvableVar int, curves []ecc.ID) *Gate { return &Gate{ + Evaluate: f, compiled: compiled, nbIn: nbIn, degree: degree, diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 3aa8bef1d3..51c1c1ae9d 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -98,7 +98,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []small_rational.S e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled()) + evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -237,8 +237,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { var step small_rational.SmallRational // Create gate evaluator once per thread - compiled := wire.Gate.Compiled() - evaluator := newGateEvaluator(compiled) + evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) res := make([]small_rational.SmallRational, degGJ) @@ -265,7 +264,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { nextEIndex := len(ml) for d := range degGJ { // Push gate inputs - for i := 0; i < nbGateIn; i++ { + for i := range nbGateIn { evaluator.pushInput(&mlEvals[eIndex+1+i]) } summand := evaluator.evaluate() @@ -300,13 +299,13 @@ func (c *eqTimesGateEvalSumcheckClaims) next(challenge small_rational.SmallRatio n := len(c.eq) / 2 if n < minBlockSize { // no parallelization - for i := 0; i < len(c.input); i++ { + for i := range c.input { c.input[i].Fold(challenge) } c.eq.Fold(challenge) } else { wgs := make([]*sync.WaitGroup, len(c.input)) - for i := 0; i < len(c.input); i++ { + for i := range c.input { wgs[i] = c.manager.workers.Submit(n, c.input[i].FoldParallel(challenge), minBlockSize) } c.manager.workers.Submit(n, c.eq.FoldParallel(challenge), minBlockSize).Wait() @@ -661,15 +660,13 @@ func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcri func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() - maxNbIns := 0 evaluators := make([]gateEvaluator, len(wires)) - for i, w := range wires { - maxNbIns = max(maxNbIns, len(w.Inputs)) + for i := range wires { if len(a[i]) != nbInstances { a[i] = make([]small_rational.SmallRational, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled()) + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) } for i := range nbInstances { @@ -800,14 +797,16 @@ type gateEvaluator struct { gate *gkrtypes.CompiledGate vars []small_rational.SmallRational frameSize int // number of constants plus currently pushed inputs + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ gate: gate, - vars: make([]small_rational.SmallRational, gate.NbConstants()+gate.NbInputs+len(gate.Instructions)), + nbIn: nbIn, + vars: make([]small_rational.SmallRational, gate.NbConstants()+nbIn+len(gate.Instructions)), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) @@ -831,8 +830,8 @@ func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rat e.pushInput(&top[i]) } - if e.frameSize != e.gate.NbInputs+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.gate.NbInputs+e.gate.NbConstants(), e.gate.NbConstants(), e.gate.NbInputs, e.frameSize)) + if e.frameSize != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } // Instruction executor functions From c70ae4de53d33472c56be07ae21f61f2974150d1 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 12 Jan 2026 18:54:42 -0600 Subject: [PATCH 098/251] fix: gkr_test --- .../generator/backend/template/gkr/gkr.test.go.tmpl | 13 ++++++++----- internal/gkr/bls12-377/gkr_test.go | 13 ++++++++----- internal/gkr/bls12-381/gkr_test.go | 13 ++++++++----- internal/gkr/bls24-315/gkr_test.go | 13 ++++++++----- internal/gkr/bls24-317/gkr_test.go | 13 ++++++++----- internal/gkr/bn254/gkr_test.go | 13 ++++++++----- internal/gkr/bw6-633/gkr_test.go | 13 ++++++++----- internal/gkr/bw6-761/gkr_test.go | 13 ++++++++----- 8 files changed, 64 insertions(+), 40 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index afeaabf986..11248e3ffa 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -268,13 +268,16 @@ func TestIsAdditive(t *testing.T) { return api.Add(x[0], x[0]) } - assert.False(t, IsGateFunctionAdditive(f, 1, 2)) - assert.False(t, IsGateFunctionAdditive(f, 0, 2)) + tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + assert.False(t, tester.IsAdditive(1)) + assert.False(t, tester.IsAdditive(0)) - assert.False(t, IsGateFunctionAdditive(g, 0, 2)) - assert.True(t, IsGateFunctionAdditive(g, 1, 2)) + tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + assert.False(t, tester.IsAdditive(0)) + assert.True(t, tester.IsAdditive(1)) - assert.True(t, IsGateFunctionAdditive(h, 0, 1)) + tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + assert.True(t, tester.IsAdditive(0)) } func generateTestProver(path string) func(t *testing.T) { diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index 8dcf2096ec..61b43d22fe 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -273,13 +273,16 @@ func TestIsAdditive(t *testing.T) { return api.Add(x[0], x[0]) } - assert.False(t, IsGateFunctionAdditive(f, 1, 2)) - assert.False(t, IsGateFunctionAdditive(f, 0, 2)) + tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + assert.False(t, tester.IsAdditive(1)) + assert.False(t, tester.IsAdditive(0)) - assert.False(t, IsGateFunctionAdditive(g, 0, 2)) - assert.True(t, IsGateFunctionAdditive(g, 1, 2)) + tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + assert.False(t, tester.IsAdditive(0)) + assert.True(t, tester.IsAdditive(1)) - assert.True(t, IsGateFunctionAdditive(h, 0, 1)) + tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + assert.True(t, tester.IsAdditive(0)) } func generateTestProver(path string) func(t *testing.T) { diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index b1d3845780..46ff25a7ff 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -273,13 +273,16 @@ func TestIsAdditive(t *testing.T) { return api.Add(x[0], x[0]) } - assert.False(t, IsGateFunctionAdditive(f, 1, 2)) - assert.False(t, IsGateFunctionAdditive(f, 0, 2)) + tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + assert.False(t, tester.IsAdditive(1)) + assert.False(t, tester.IsAdditive(0)) - assert.False(t, IsGateFunctionAdditive(g, 0, 2)) - assert.True(t, IsGateFunctionAdditive(g, 1, 2)) + tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + assert.False(t, tester.IsAdditive(0)) + assert.True(t, tester.IsAdditive(1)) - assert.True(t, IsGateFunctionAdditive(h, 0, 1)) + tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + assert.True(t, tester.IsAdditive(0)) } func generateTestProver(path string) func(t *testing.T) { diff --git a/internal/gkr/bls24-315/gkr_test.go b/internal/gkr/bls24-315/gkr_test.go index e68f350ae7..fc22a95c8d 100644 --- a/internal/gkr/bls24-315/gkr_test.go +++ b/internal/gkr/bls24-315/gkr_test.go @@ -273,13 +273,16 @@ func TestIsAdditive(t *testing.T) { return api.Add(x[0], x[0]) } - assert.False(t, IsGateFunctionAdditive(f, 1, 2)) - assert.False(t, IsGateFunctionAdditive(f, 0, 2)) + tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + assert.False(t, tester.IsAdditive(1)) + assert.False(t, tester.IsAdditive(0)) - assert.False(t, IsGateFunctionAdditive(g, 0, 2)) - assert.True(t, IsGateFunctionAdditive(g, 1, 2)) + tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + assert.False(t, tester.IsAdditive(0)) + assert.True(t, tester.IsAdditive(1)) - assert.True(t, IsGateFunctionAdditive(h, 0, 1)) + tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + assert.True(t, tester.IsAdditive(0)) } func generateTestProver(path string) func(t *testing.T) { diff --git a/internal/gkr/bls24-317/gkr_test.go b/internal/gkr/bls24-317/gkr_test.go index eb08aad81d..d485d84af5 100644 --- a/internal/gkr/bls24-317/gkr_test.go +++ b/internal/gkr/bls24-317/gkr_test.go @@ -273,13 +273,16 @@ func TestIsAdditive(t *testing.T) { return api.Add(x[0], x[0]) } - assert.False(t, IsGateFunctionAdditive(f, 1, 2)) - assert.False(t, IsGateFunctionAdditive(f, 0, 2)) + tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + assert.False(t, tester.IsAdditive(1)) + assert.False(t, tester.IsAdditive(0)) - assert.False(t, IsGateFunctionAdditive(g, 0, 2)) - assert.True(t, IsGateFunctionAdditive(g, 1, 2)) + tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + assert.False(t, tester.IsAdditive(0)) + assert.True(t, tester.IsAdditive(1)) - assert.True(t, IsGateFunctionAdditive(h, 0, 1)) + tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + assert.True(t, tester.IsAdditive(0)) } func generateTestProver(path string) func(t *testing.T) { diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index ecbf5ec7f7..07ddf10a4d 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -273,13 +273,16 @@ func TestIsAdditive(t *testing.T) { return api.Add(x[0], x[0]) } - assert.False(t, IsGateFunctionAdditive(f, 1, 2)) - assert.False(t, IsGateFunctionAdditive(f, 0, 2)) + tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + assert.False(t, tester.IsAdditive(1)) + assert.False(t, tester.IsAdditive(0)) - assert.False(t, IsGateFunctionAdditive(g, 0, 2)) - assert.True(t, IsGateFunctionAdditive(g, 1, 2)) + tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + assert.False(t, tester.IsAdditive(0)) + assert.True(t, tester.IsAdditive(1)) - assert.True(t, IsGateFunctionAdditive(h, 0, 1)) + tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + assert.True(t, tester.IsAdditive(0)) } func generateTestProver(path string) func(t *testing.T) { diff --git a/internal/gkr/bw6-633/gkr_test.go b/internal/gkr/bw6-633/gkr_test.go index 6bf891cd06..3c7089bfa8 100644 --- a/internal/gkr/bw6-633/gkr_test.go +++ b/internal/gkr/bw6-633/gkr_test.go @@ -273,13 +273,16 @@ func TestIsAdditive(t *testing.T) { return api.Add(x[0], x[0]) } - assert.False(t, IsGateFunctionAdditive(f, 1, 2)) - assert.False(t, IsGateFunctionAdditive(f, 0, 2)) + tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + assert.False(t, tester.IsAdditive(1)) + assert.False(t, tester.IsAdditive(0)) - assert.False(t, IsGateFunctionAdditive(g, 0, 2)) - assert.True(t, IsGateFunctionAdditive(g, 1, 2)) + tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + assert.False(t, tester.IsAdditive(0)) + assert.True(t, tester.IsAdditive(1)) - assert.True(t, IsGateFunctionAdditive(h, 0, 1)) + tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + assert.True(t, tester.IsAdditive(0)) } func generateTestProver(path string) func(t *testing.T) { diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index 308b51e170..e0a9286a08 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -273,13 +273,16 @@ func TestIsAdditive(t *testing.T) { return api.Add(x[0], x[0]) } - assert.False(t, IsGateFunctionAdditive(f, 1, 2)) - assert.False(t, IsGateFunctionAdditive(f, 0, 2)) + tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + assert.False(t, tester.IsAdditive(1)) + assert.False(t, tester.IsAdditive(0)) - assert.False(t, IsGateFunctionAdditive(g, 0, 2)) - assert.True(t, IsGateFunctionAdditive(g, 1, 2)) + tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + assert.False(t, tester.IsAdditive(0)) + assert.True(t, tester.IsAdditive(1)) - assert.True(t, IsGateFunctionAdditive(h, 0, 1)) + tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + assert.True(t, tester.IsAdditive(0)) } func generateTestProver(path string) func(t *testing.T) { From d7f365c1f76daff49593cb93bf6753367a04967c Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 12 Jan 2026 19:39:40 -0600 Subject: [PATCH 099/251] feat: circuit evaluator pool in Solve Hint --- .../backend/template/gkr/solver_hints.go.tmpl | 52 +++++++++++++------ internal/gkr/bls12-377/solver_hints.go | 52 +++++++++++++------ internal/gkr/bls12-381/solver_hints.go | 52 +++++++++++++------ internal/gkr/bls24-315/solver_hints.go | 52 +++++++++++++------ internal/gkr/bls24-317/solver_hints.go | 52 +++++++++++++------ internal/gkr/bn254/solver_hints.go | 52 +++++++++++++------ internal/gkr/bw6-633/solver_hints.go | 52 +++++++++++++------ internal/gkr/bw6-761/solver_hints.go | 52 +++++++++++++------ 8 files changed, 280 insertions(+), 136 deletions(-) diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index 6d30c0bef6..f2ee61721f 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -1,6 +1,7 @@ import ( "fmt" "math/big" + "sync" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" @@ -12,12 +13,17 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int + hashName string + evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse } type newSolvingDataSettings struct { @@ -91,6 +97,23 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } } + + // Initialize circuit evaluator pool + circuit := d[k].circuit // capture for closure + d[k].evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { // input wires don't need evaluators + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce + }, + } } return d @@ -136,9 +159,9 @@ func SolveHint(data []SolvingData) hint.Hint { outsI := 0 insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - // Track current gate evaluator to reuse across wires with the same gate - var currentEvaluator *gateEvaluator - var currentCompiled *gkrtypes.CompiledGate + // Get a circuit evaluator from the pool + ce := data.evaluatorPool.Get().(*circuitEvaluator) + defer data.evaluatorPool.Put(ce) // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -150,21 +173,16 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - compiled := w.Gate.Compiled() - - // Create new evaluator if this is a different gate - if compiled != currentCompiled { - currentEvaluator = newGateEvaluator(compiled) - currentCompiled = compiled - } + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) + evaluator.pushInput(&data.assignment[inWI][instanceI]) } // Evaluate the gate - result := currentEvaluator.evaluate() + result := evaluator.evaluate() data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 4e3bd1e618..c13140f5f7 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/big" + "sync" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" @@ -18,12 +19,17 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int + hashName string + evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse } type newSolvingDataSettings struct { @@ -99,6 +105,23 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } } + + // Initialize circuit evaluator pool + circuit := d[k].circuit // capture for closure + d[k].evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { // input wires don't need evaluators + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce + }, + } } return d @@ -144,9 +167,9 @@ func SolveHint(data []SolvingData) hint.Hint { outsI := 0 insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - // Track current gate evaluator to reuse across wires with the same gate - var currentEvaluator *gateEvaluator - var currentCompiled *gkrtypes.CompiledGate + // Get a circuit evaluator from the pool + ce := data.evaluatorPool.Get().(*circuitEvaluator) + defer data.evaluatorPool.Put(ce) // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -158,21 +181,16 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - compiled := w.Gate.Compiled() - - // Create new evaluator if this is a different gate - if compiled != currentCompiled { - currentEvaluator = newGateEvaluator(compiled) - currentCompiled = compiled - } + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) + evaluator.pushInput(&data.assignment[inWI][instanceI]) } // Evaluate the gate - result := currentEvaluator.evaluate() + result := evaluator.evaluate() data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index 95bf686e8e..57fb835f65 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/big" + "sync" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" @@ -18,12 +19,17 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int + hashName string + evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse } type newSolvingDataSettings struct { @@ -99,6 +105,23 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } } + + // Initialize circuit evaluator pool + circuit := d[k].circuit // capture for closure + d[k].evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { // input wires don't need evaluators + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce + }, + } } return d @@ -144,9 +167,9 @@ func SolveHint(data []SolvingData) hint.Hint { outsI := 0 insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - // Track current gate evaluator to reuse across wires with the same gate - var currentEvaluator *gateEvaluator - var currentCompiled *gkrtypes.CompiledGate + // Get a circuit evaluator from the pool + ce := data.evaluatorPool.Get().(*circuitEvaluator) + defer data.evaluatorPool.Put(ce) // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -158,21 +181,16 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - compiled := w.Gate.Compiled() - - // Create new evaluator if this is a different gate - if compiled != currentCompiled { - currentEvaluator = newGateEvaluator(compiled) - currentCompiled = compiled - } + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) + evaluator.pushInput(&data.assignment[inWI][instanceI]) } // Evaluate the gate - result := currentEvaluator.evaluate() + result := evaluator.evaluate() data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index c03ed0030f..fdb2a15ed1 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/big" + "sync" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" @@ -18,12 +19,17 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int + hashName string + evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse } type newSolvingDataSettings struct { @@ -99,6 +105,23 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } } + + // Initialize circuit evaluator pool + circuit := d[k].circuit // capture for closure + d[k].evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { // input wires don't need evaluators + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce + }, + } } return d @@ -144,9 +167,9 @@ func SolveHint(data []SolvingData) hint.Hint { outsI := 0 insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - // Track current gate evaluator to reuse across wires with the same gate - var currentEvaluator *gateEvaluator - var currentCompiled *gkrtypes.CompiledGate + // Get a circuit evaluator from the pool + ce := data.evaluatorPool.Get().(*circuitEvaluator) + defer data.evaluatorPool.Put(ce) // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -158,21 +181,16 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - compiled := w.Gate.Compiled() - - // Create new evaluator if this is a different gate - if compiled != currentCompiled { - currentEvaluator = newGateEvaluator(compiled) - currentCompiled = compiled - } + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) + evaluator.pushInput(&data.assignment[inWI][instanceI]) } // Evaluate the gate - result := currentEvaluator.evaluate() + result := evaluator.evaluate() data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index e0f839d482..4323f5d880 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/big" + "sync" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" @@ -18,12 +19,17 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int + hashName string + evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse } type newSolvingDataSettings struct { @@ -99,6 +105,23 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } } + + // Initialize circuit evaluator pool + circuit := d[k].circuit // capture for closure + d[k].evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { // input wires don't need evaluators + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce + }, + } } return d @@ -144,9 +167,9 @@ func SolveHint(data []SolvingData) hint.Hint { outsI := 0 insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - // Track current gate evaluator to reuse across wires with the same gate - var currentEvaluator *gateEvaluator - var currentCompiled *gkrtypes.CompiledGate + // Get a circuit evaluator from the pool + ce := data.evaluatorPool.Get().(*circuitEvaluator) + defer data.evaluatorPool.Put(ce) // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -158,21 +181,16 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - compiled := w.Gate.Compiled() - - // Create new evaluator if this is a different gate - if compiled != currentCompiled { - currentEvaluator = newGateEvaluator(compiled) - currentCompiled = compiled - } + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) + evaluator.pushInput(&data.assignment[inWI][instanceI]) } // Evaluate the gate - result := currentEvaluator.evaluate() + result := evaluator.evaluate() data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index 161b41852f..8ff11d2b47 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/big" + "sync" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" @@ -18,12 +19,17 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int + hashName string + evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse } type newSolvingDataSettings struct { @@ -99,6 +105,23 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } } + + // Initialize circuit evaluator pool + circuit := d[k].circuit // capture for closure + d[k].evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { // input wires don't need evaluators + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce + }, + } } return d @@ -144,9 +167,9 @@ func SolveHint(data []SolvingData) hint.Hint { outsI := 0 insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - // Track current gate evaluator to reuse across wires with the same gate - var currentEvaluator *gateEvaluator - var currentCompiled *gkrtypes.CompiledGate + // Get a circuit evaluator from the pool + ce := data.evaluatorPool.Get().(*circuitEvaluator) + defer data.evaluatorPool.Put(ce) // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -158,21 +181,16 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - compiled := w.Gate.Compiled() - - // Create new evaluator if this is a different gate - if compiled != currentCompiled { - currentEvaluator = newGateEvaluator(compiled) - currentCompiled = compiled - } + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) + evaluator.pushInput(&data.assignment[inWI][instanceI]) } // Evaluate the gate - result := currentEvaluator.evaluate() + result := evaluator.evaluate() data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index f2d7c41bcf..e52f724cd3 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/big" + "sync" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" @@ -18,12 +19,17 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int + hashName string + evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse } type newSolvingDataSettings struct { @@ -99,6 +105,23 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } } + + // Initialize circuit evaluator pool + circuit := d[k].circuit // capture for closure + d[k].evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { // input wires don't need evaluators + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce + }, + } } return d @@ -144,9 +167,9 @@ func SolveHint(data []SolvingData) hint.Hint { outsI := 0 insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - // Track current gate evaluator to reuse across wires with the same gate - var currentEvaluator *gateEvaluator - var currentCompiled *gkrtypes.CompiledGate + // Get a circuit evaluator from the pool + ce := data.evaluatorPool.Get().(*circuitEvaluator) + defer data.evaluatorPool.Put(ce) // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -158,21 +181,16 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - compiled := w.Gate.Compiled() - - // Create new evaluator if this is a different gate - if compiled != currentCompiled { - currentEvaluator = newGateEvaluator(compiled) - currentCompiled = compiled - } + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) + evaluator.pushInput(&data.assignment[inWI][instanceI]) } // Evaluate the gate - result := currentEvaluator.evaluate() + result := evaluator.evaluate() data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index 0327b0eae0..308178cfea 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/big" + "sync" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" @@ -18,12 +19,17 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string + assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. + circuit gkrtypes.Circuit + maxNbIn int // maximum number of inputs for a gate in the circuit + nbInstances int + hashName string + evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse } type newSolvingDataSettings struct { @@ -99,6 +105,23 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } } + + // Initialize circuit evaluator pool + circuit := d[k].circuit // capture for closure + d[k].evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { // input wires don't need evaluators + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce + }, + } } return d @@ -144,9 +167,9 @@ func SolveHint(data []SolvingData) hint.Hint { outsI := 0 insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - // Track current gate evaluator to reuse across wires with the same gate - var currentEvaluator *gateEvaluator - var currentCompiled *gkrtypes.CompiledGate + // Get a circuit evaluator from the pool + ce := data.evaluatorPool.Get().(*circuitEvaluator) + defer data.evaluatorPool.Put(ce) // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -158,21 +181,16 @@ func SolveHint(data []SolvingData) hint.Hint { data.assignment[wI][instanceI].SetBigInt(ins[insI]) insI++ } else { - compiled := w.Gate.Compiled() - - // Create new evaluator if this is a different gate - if compiled != currentCompiled { - currentEvaluator = newGateEvaluator(compiled) - currentCompiled = compiled - } + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - currentEvaluator.pushInput(&data.assignment[inWI][instanceI]) + evaluator.pushInput(&data.assignment[inWI][instanceI]) } // Evaluate the gate - result := currentEvaluator.evaluate() + result := evaluator.evaluate() data.assignment[wI][instanceI].Set(result) } if w.IsOutput() { From f0668e51fd66e5885b23a5d05dba95fcde2656d1 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 12 Jan 2026 19:49:35 -0600 Subject: [PATCH 100/251] fix: not reading variables bug --- internal/gkr/gkrtypes/compiledgate.go | 36 ++++++++------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/internal/gkr/gkrtypes/compiledgate.go b/internal/gkr/gkrtypes/compiledgate.go index 76c22dd7c3..4fba0391c9 100644 --- a/internal/gkr/gkrtypes/compiledgate.go +++ b/internal/gkr/gkrtypes/compiledgate.go @@ -82,18 +82,6 @@ type gateCompiler struct { const constMarker = 0x8000 -// newGateCompiler creates a new recording API with the given number of inputs. -// Input variables are assigned temporary IDs 0 to nbInputs-1. -// Constants are assigned temporary IDs starting at 0x8000. -func newGateCompiler(nbInputs int) *gateCompiler { - return &gateCompiler{ - instructions: make([]GateInstruction, 0), - constants: make([]*big.Int, 0), - constantIndex: make(map[string]uint16), - nbInputs: nbInputs, - } -} - // compilationVar represents a variable during gate compilation. type compilationVar struct { id uint16 @@ -161,16 +149,9 @@ func (gc *gateCompiler) SumExp17(a, b, c frontend.Variable) frontend.Variable { // getVarID extracts or creates a temporary index from a value. // Returns a temporary index: inputs at 0..nbInputs-1, constants at 0x8000+, results at nbInputs+. func (gc *gateCompiler) getVarID(v frontend.Variable) uint16 { - if rv, ok := v.(*compilationVar); ok { + if rv, ok := v.(compilationVar); ok { return rv.id } - // If it's an integer in the valid input range, treat it as an input variable index - if idx, ok := v.(int); ok { - if idx >= 0 && idx < gc.nbInputs { - return uint16(idx) - } - // Integer outside input range - treat as constant - } // Otherwise, it must be a constant value // Convert to big.Int for curve-agnostic storage @@ -220,17 +201,20 @@ func (gc *gateCompiler) remapIndices() { // CompileGateFunction compiles a gate function into a CompiledGate. // The gate function should be of type gkr.GateFunction. func CompileGateFunction(f gkr.GateFunction, nbInputs int) *CompiledGate { - // Create recording API - compiler := newGateCompiler(nbInputs) + // Create compiling API + compiler := gateCompiler{ + constantIndex: make(map[string]uint16), + nbInputs: nbInputs, + } - // Create input variables as integers 0, 1, 2, ... + // Create input variables inputs := make([]frontend.Variable, nbInputs) - for i := range inputs { - inputs[i] = i + for i := range uint16(nbInputs) { + inputs[i] = compilationVar{i} } // Execute the gate function to record operations - out := f(compiler, inputs...) + out := f(&compiler, inputs...) // All instructions after the output are no-ops. Prune them and the corresponding variables. // Henceforth we guarantee that the variable with the highest index is the gate output. From fd88aed150a257175e16c94c168f4d45966a8724 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 12 Jan 2026 19:55:37 -0600 Subject: [PATCH 101/251] fix: Complete() bug --- internal/generator/backend/template/gkr/gkr.go.tmpl | 4 +++- internal/gkr/bls12-377/gkr.go | 4 +++- internal/gkr/bls12-381/gkr.go | 4 +++- internal/gkr/bls24-315/gkr.go | 4 +++- internal/gkr/bls24-317/gkr.go | 4 +++- internal/gkr/bn254/gkr.go | 4 +++- internal/gkr/bw6-633/gkr.go | 4 +++- internal/gkr/bw6-761/gkr.go | 4 +++- internal/gkr/small_rational/gkr.go | 4 +++- .../two_inputs_select-input-3_gate_two_instances.json | 4 ++-- 10 files changed, 29 insertions(+), 11 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 9a2138dec2..9a04bbcde8 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -661,7 +661,9 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { if len(a[i]) != nbInstances { a[i] = make([]{{ .ElementType }}, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + if !wires[i].IsInput() { + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + } } for i := range nbInstances { diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 3f0a31972f..57ea594903 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -666,7 +666,9 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + if !wires[i].IsInput() { + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + } } for i := range nbInstances { diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 0089a09454..e7997246aa 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -666,7 +666,9 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + if !wires[i].IsInput() { + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + } } for i := range nbInstances { diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 0b13654029..07fe674165 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -666,7 +666,9 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + if !wires[i].IsInput() { + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + } } for i := range nbInstances { diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 4322e2e3d9..059d021be8 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -666,7 +666,9 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + if !wires[i].IsInput() { + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + } } for i := range nbInstances { diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 2780f53da5..01c535bf55 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -666,7 +666,9 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + if !wires[i].IsInput() { + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + } } for i := range nbInstances { diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 8eb8d22eb1..c27a88f304 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -666,7 +666,9 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + if !wires[i].IsInput() { + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + } } for i := range nbInstances { diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 06d7883f37..7003a2f7d6 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -666,7 +666,9 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + if !wires[i].IsInput() { + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + } } for i := range nbInstances { diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 51c1c1ae9d..c4bf8e1666 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -666,7 +666,9 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { if len(a[i]) != nbInstances { a[i] = make([]small_rational.SmallRational, nbInstances) } - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + if !wires[i].IsInput() { + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + } } for i := range nbInstances { diff --git a/internal/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json b/internal/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json index 34814b5e10..2dca0746a2 100644 --- a/internal/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json +++ b/internal/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json @@ -36,8 +36,8 @@ ], "partialSumPolys": [ [ - -1, - -8 + -3, + -16 ] ] } From 8548102752a44deb9a425236af13871c6bb5c732 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 12 Jan 2026 20:00:12 -0600 Subject: [PATCH 102/251] fix: startup frame size --- internal/generator/backend/template/gkr/gkr.go.tmpl | 1 + internal/gkr/bls12-377/gkr.go | 7 ++++--- internal/gkr/bls12-381/gkr.go | 7 ++++--- internal/gkr/bls24-315/gkr.go | 7 ++++--- internal/gkr/bls24-317/gkr.go | 7 ++++--- internal/gkr/bn254/gkr.go | 7 ++++--- internal/gkr/bw6-633/gkr.go | 7 ++++--- internal/gkr/bw6-761/gkr.go | 7 ++++--- internal/gkr/small_rational/gkr.go | 7 ++++--- 9 files changed, 33 insertions(+), 24 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 9a04bbcde8..c89c1d8a93 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -804,6 +804,7 @@ func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { gate: gate, nbIn: nbIn, vars: make([]{{ .ElementType }}, gate.NbConstants()+nbIn+len(gate.Instructions)), + frameSize: gate.NbConstants(), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 57ea594903..b3644e878a 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -806,9 +806,10 @@ type gateEvaluator struct { // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, - vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), + gate: gate, + nbIn: nbIn, + vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), + frameSize: gate.NbConstants(), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index e7997246aa..38096e9655 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -806,9 +806,10 @@ type gateEvaluator struct { // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, - vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), + gate: gate, + nbIn: nbIn, + vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), + frameSize: gate.NbConstants(), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 07fe674165..24d92940bb 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -806,9 +806,10 @@ type gateEvaluator struct { // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, - vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), + gate: gate, + nbIn: nbIn, + vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), + frameSize: gate.NbConstants(), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 059d021be8..4b9fade494 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -806,9 +806,10 @@ type gateEvaluator struct { // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, - vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), + gate: gate, + nbIn: nbIn, + vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), + frameSize: gate.NbConstants(), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 01c535bf55..39afde0159 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -806,9 +806,10 @@ type gateEvaluator struct { // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, - vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), + gate: gate, + nbIn: nbIn, + vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), + frameSize: gate.NbConstants(), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index c27a88f304..4a0f5dde54 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -806,9 +806,10 @@ type gateEvaluator struct { // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, - vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), + gate: gate, + nbIn: nbIn, + vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), + frameSize: gate.NbConstants(), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 7003a2f7d6..1b44cac642 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -806,9 +806,10 @@ type gateEvaluator struct { // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, - vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), + gate: gate, + nbIn: nbIn, + vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), + frameSize: gate.NbConstants(), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index c4bf8e1666..750b8e5be1 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -806,9 +806,10 @@ type gateEvaluator struct { // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, - vars: make([]small_rational.SmallRational, gate.NbConstants()+nbIn+len(gate.Instructions)), + gate: gate, + nbIn: nbIn, + vars: make([]small_rational.SmallRational, gate.NbConstants()+nbIn+len(gate.Instructions)), + frameSize: gate.NbConstants(), } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) From 53e6e8e1f9677475296bee841301a2ed1028347d Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 12 Jan 2026 20:15:52 -0600 Subject: [PATCH 103/251] fix: error on empty gates --- constraint/solver/gkrgates/registry.go | 5 ++++- constraint/solver/gkrgates/registry_test.go | 4 ---- .../generator/backend/template/gkr/gkr.test.go.tmpl | 13 +++++++++---- internal/gkr/bls12-377/gkr_test.go | 13 +++++++++---- internal/gkr/bls12-381/gkr_test.go | 13 +++++++++---- internal/gkr/bls24-315/gkr_test.go | 13 +++++++++---- internal/gkr/bls24-317/gkr_test.go | 13 +++++++++---- internal/gkr/bn254/gkr_test.go | 13 +++++++++---- internal/gkr/bw6-633/gkr_test.go | 13 +++++++++---- internal/gkr/bw6-761/gkr_test.go | 13 +++++++++---- internal/gkr/gkrtesting/gkrtesting.go | 5 ++++- internal/gkr/gkrtypes/compiledgate.go | 8 ++++++-- internal/gkr/gkrtypes/compiledgate_test.go | 10 +++++++--- 13 files changed, 93 insertions(+), 43 deletions(-) diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index 816c16b6ba..e3dd49b797 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -162,7 +162,10 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { allowedCurves = gnark.Curves() } - compiled := gkrtypes.CompileGateFunction(f, nbIn) + compiled, err := gkrtypes.CompileGateFunction(f, nbIn) + if err != nil { + return err + } gatesLock.Lock() defer gatesLock.Unlock() diff --git a/constraint/solver/gkrgates/registry_test.go b/constraint/solver/gkrgates/registry_test.go index fbca20ffba..350383b79e 100644 --- a/constraint/solver/gkrgates/registry_test.go +++ b/constraint/solver/gkrgates/registry_test.go @@ -44,10 +44,6 @@ func TestRegister(t *testing.T) { }) } - testGate("select", func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - return x[0] - }, 3, 1) - testGate("add3", func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Add(x[0], x[1], x[2]) }, 3, 1) diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index 11248e3ffa..a244b97694 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -263,20 +263,25 @@ func TestIsAdditive(t *testing.T) { } // h: x -> 2x - // but it edits it input h := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Add(x[0], x[0]) } - tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + newTester := func(f gkr.GateFunction, nbIn int) *GateTester { + cg, err := gkrtypes.CompileGateFunction(f, nbIn) + assert.NoError(t, err) + return NewGateTester(cg, nbIn) + } + + tester := newTester(f, 2) assert.False(t, tester.IsAdditive(1)) assert.False(t, tester.IsAdditive(0)) - tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + tester = newTester(g, 2) assert.False(t, tester.IsAdditive(0)) assert.True(t, tester.IsAdditive(1)) - tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + tester = newTester(h, 1) assert.True(t, tester.IsAdditive(0)) } diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index 61b43d22fe..ab49d127b7 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -268,20 +268,25 @@ func TestIsAdditive(t *testing.T) { } // h: x -> 2x - // but it edits it input h := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Add(x[0], x[0]) } - tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + newTester := func(f gkr.GateFunction, nbIn int) *GateTester { + cg, err := gkrtypes.CompileGateFunction(f, nbIn) + assert.NoError(t, err) + return NewGateTester(cg, nbIn) + } + + tester := newTester(f, 2) assert.False(t, tester.IsAdditive(1)) assert.False(t, tester.IsAdditive(0)) - tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + tester = newTester(g, 2) assert.False(t, tester.IsAdditive(0)) assert.True(t, tester.IsAdditive(1)) - tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + tester = newTester(h, 1) assert.True(t, tester.IsAdditive(0)) } diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 46ff25a7ff..85992a05d9 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -268,20 +268,25 @@ func TestIsAdditive(t *testing.T) { } // h: x -> 2x - // but it edits it input h := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Add(x[0], x[0]) } - tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + newTester := func(f gkr.GateFunction, nbIn int) *GateTester { + cg, err := gkrtypes.CompileGateFunction(f, nbIn) + assert.NoError(t, err) + return NewGateTester(cg, nbIn) + } + + tester := newTester(f, 2) assert.False(t, tester.IsAdditive(1)) assert.False(t, tester.IsAdditive(0)) - tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + tester = newTester(g, 2) assert.False(t, tester.IsAdditive(0)) assert.True(t, tester.IsAdditive(1)) - tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + tester = newTester(h, 1) assert.True(t, tester.IsAdditive(0)) } diff --git a/internal/gkr/bls24-315/gkr_test.go b/internal/gkr/bls24-315/gkr_test.go index fc22a95c8d..df50bf8166 100644 --- a/internal/gkr/bls24-315/gkr_test.go +++ b/internal/gkr/bls24-315/gkr_test.go @@ -268,20 +268,25 @@ func TestIsAdditive(t *testing.T) { } // h: x -> 2x - // but it edits it input h := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Add(x[0], x[0]) } - tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + newTester := func(f gkr.GateFunction, nbIn int) *GateTester { + cg, err := gkrtypes.CompileGateFunction(f, nbIn) + assert.NoError(t, err) + return NewGateTester(cg, nbIn) + } + + tester := newTester(f, 2) assert.False(t, tester.IsAdditive(1)) assert.False(t, tester.IsAdditive(0)) - tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + tester = newTester(g, 2) assert.False(t, tester.IsAdditive(0)) assert.True(t, tester.IsAdditive(1)) - tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + tester = newTester(h, 1) assert.True(t, tester.IsAdditive(0)) } diff --git a/internal/gkr/bls24-317/gkr_test.go b/internal/gkr/bls24-317/gkr_test.go index d485d84af5..0c3470e6d5 100644 --- a/internal/gkr/bls24-317/gkr_test.go +++ b/internal/gkr/bls24-317/gkr_test.go @@ -268,20 +268,25 @@ func TestIsAdditive(t *testing.T) { } // h: x -> 2x - // but it edits it input h := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Add(x[0], x[0]) } - tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + newTester := func(f gkr.GateFunction, nbIn int) *GateTester { + cg, err := gkrtypes.CompileGateFunction(f, nbIn) + assert.NoError(t, err) + return NewGateTester(cg, nbIn) + } + + tester := newTester(f, 2) assert.False(t, tester.IsAdditive(1)) assert.False(t, tester.IsAdditive(0)) - tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + tester = newTester(g, 2) assert.False(t, tester.IsAdditive(0)) assert.True(t, tester.IsAdditive(1)) - tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + tester = newTester(h, 1) assert.True(t, tester.IsAdditive(0)) } diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 07ddf10a4d..275803af51 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -268,20 +268,25 @@ func TestIsAdditive(t *testing.T) { } // h: x -> 2x - // but it edits it input h := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Add(x[0], x[0]) } - tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + newTester := func(f gkr.GateFunction, nbIn int) *GateTester { + cg, err := gkrtypes.CompileGateFunction(f, nbIn) + assert.NoError(t, err) + return NewGateTester(cg, nbIn) + } + + tester := newTester(f, 2) assert.False(t, tester.IsAdditive(1)) assert.False(t, tester.IsAdditive(0)) - tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + tester = newTester(g, 2) assert.False(t, tester.IsAdditive(0)) assert.True(t, tester.IsAdditive(1)) - tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + tester = newTester(h, 1) assert.True(t, tester.IsAdditive(0)) } diff --git a/internal/gkr/bw6-633/gkr_test.go b/internal/gkr/bw6-633/gkr_test.go index 3c7089bfa8..508879c2f4 100644 --- a/internal/gkr/bw6-633/gkr_test.go +++ b/internal/gkr/bw6-633/gkr_test.go @@ -268,20 +268,25 @@ func TestIsAdditive(t *testing.T) { } // h: x -> 2x - // but it edits it input h := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Add(x[0], x[0]) } - tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + newTester := func(f gkr.GateFunction, nbIn int) *GateTester { + cg, err := gkrtypes.CompileGateFunction(f, nbIn) + assert.NoError(t, err) + return NewGateTester(cg, nbIn) + } + + tester := newTester(f, 2) assert.False(t, tester.IsAdditive(1)) assert.False(t, tester.IsAdditive(0)) - tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + tester = newTester(g, 2) assert.False(t, tester.IsAdditive(0)) assert.True(t, tester.IsAdditive(1)) - tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + tester = newTester(h, 1) assert.True(t, tester.IsAdditive(0)) } diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index e0a9286a08..46ba23b0a9 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -268,20 +268,25 @@ func TestIsAdditive(t *testing.T) { } // h: x -> 2x - // but it edits it input h := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Add(x[0], x[0]) } - tester := NewGateTester(gkrtypes.CompileGateFunction(f, 2), 2) + newTester := func(f gkr.GateFunction, nbIn int) *GateTester { + cg, err := gkrtypes.CompileGateFunction(f, nbIn) + assert.NoError(t, err) + return NewGateTester(cg, nbIn) + } + + tester := newTester(f, 2) assert.False(t, tester.IsAdditive(1)) assert.False(t, tester.IsAdditive(0)) - tester = NewGateTester(gkrtypes.CompileGateFunction(g, 2), 2) + tester = newTester(g, 2) assert.False(t, tester.IsAdditive(0)) assert.True(t, tester.IsAdditive(1)) - tester = NewGateTester(gkrtypes.CompileGateFunction(h, 1), 1) + tester = newTester(h, 1) assert.True(t, tester.IsAdditive(0)) } diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 7e37aedeb5..23100b289d 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -37,7 +37,10 @@ func NewCache() *Cache { return res } - mimcCompiled := gkrtypes.CompileGateFunction(mimcF, 2) + mimcCompiled, err := gkrtypes.CompileGateFunction(mimcF, 2) + if err != nil { + panic(err) + } gates["mimc"] = gkrtypes.NewGate(mimcF, mimcCompiled, 2, 7, -1, gnark.Curves()) gates["select-input-3"] = gkrtypes.NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return in[2] diff --git a/internal/gkr/gkrtypes/compiledgate.go b/internal/gkr/gkrtypes/compiledgate.go index 4fba0391c9..f8969f4b5d 100644 --- a/internal/gkr/gkrtypes/compiledgate.go +++ b/internal/gkr/gkrtypes/compiledgate.go @@ -1,6 +1,7 @@ package gkrtypes import ( + "errors" "math/big" "github.com/consensys/gnark/frontend" @@ -200,7 +201,7 @@ func (gc *gateCompiler) remapIndices() { // CompileGateFunction compiles a gate function into a CompiledGate. // The gate function should be of type gkr.GateFunction. -func CompileGateFunction(f gkr.GateFunction, nbInputs int) *CompiledGate { +func CompileGateFunction(f gkr.GateFunction, nbInputs int) (*CompiledGate, error) { // Create compiling API compiler := gateCompiler{ constantIndex: make(map[string]uint16), @@ -215,6 +216,9 @@ func CompileGateFunction(f gkr.GateFunction, nbInputs int) *CompiledGate { // Execute the gate function to record operations out := f(&compiler, inputs...) + if len(compiler.instructions) == 0 { + return nil, errors.New("every gate must perform a non-trivial operation") + } // All instructions after the output are no-ops. Prune them and the corresponding variables. // Henceforth we guarantee that the variable with the highest index is the gate output. @@ -227,5 +231,5 @@ func CompileGateFunction(f gkr.GateFunction, nbInputs int) *CompiledGate { return &CompiledGate{ Instructions: compiler.GetInstructions(), Constants: compiler.constants, - } + }, nil } diff --git a/internal/gkr/gkrtypes/compiledgate_test.go b/internal/gkr/gkrtypes/compiledgate_test.go index b733396bc8..c604a02e96 100644 --- a/internal/gkr/gkrtypes/compiledgate_test.go +++ b/internal/gkr/gkrtypes/compiledgate_test.go @@ -6,6 +6,7 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/stretchr/testify/require" ) // TestCompiledGateWithConstants tests that gates can use constant values @@ -18,7 +19,8 @@ func TestCompiledGateWithConstants(t *testing.T) { const nbIn = 1 // Compile the gate - compiled := CompileGateFunction(addConstantGate, nbIn) + compiled, err := CompileGateFunction(addConstantGate, nbIn) + require.NoError(t, err) // Verify the gate has constants if len(compiled.Constants) == 0 { @@ -79,7 +81,8 @@ func TestCompiledGateWithMultipleConstants(t *testing.T) { // Compile the gate const nbIn = 1 - compiled := CompileGateFunction(complexGate, nbIn) + compiled, err := CompileGateFunction(complexGate, nbIn) + require.NoError(t, err) // Verify we have two constants if len(compiled.Constants) != 2 { @@ -113,7 +116,8 @@ func TestConstantDeduplication(t *testing.T) { const nbIn = 2 // Compile the gate - compiled := CompileGateFunction(gateWithDuplicates, nbIn) + compiled, err := CompileGateFunction(gateWithDuplicates, nbIn) + require.NoError(t, err) // Verify only one constant is stored (deduplication) if len(compiled.Constants) != 1 { From 1b961f56d9268b1f51edbf6aa0650b68835dcf9d Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 12 Jan 2026 20:55:17 -0600 Subject: [PATCH 104/251] perf: take executors outside --- .../backend/template/gkr/gkr.go.tmpl | 21 +++++++++---------- internal/gkr/bls12-377/gkr.go | 21 +++++++++---------- internal/gkr/bls12-381/gkr.go | 21 +++++++++---------- internal/gkr/bls24-315/gkr.go | 21 +++++++++---------- internal/gkr/bls24-317/gkr.go | 21 +++++++++---------- internal/gkr/bn254/gkr.go | 21 +++++++++---------- internal/gkr/bw6-633/gkr.go | 21 +++++++++---------- internal/gkr/bw6-761/gkr.go | 21 +++++++++---------- internal/gkr/small_rational/gkr.go | 21 +++++++++---------- 9 files changed, 90 insertions(+), 99 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index c89c1d8a93..360df6ba92 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -819,6 +819,15 @@ func (e *gateEvaluator) pushInput(input *{{ .ElementType }}) { e.frameSize++ } +var gateExecutors = [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, +} + // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -832,20 +841,10 @@ func (e *gateEvaluator) evaluate(top ...{{ .ElementType }}) *{{ .ElementType }} panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } - // Instruction executor functions - executors := [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index b3644e878a..4e1f36922e 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -824,6 +824,15 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } +var gateExecutors = [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, +} + // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -837,20 +846,10 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } - // Instruction executor functions - executors := [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 38096e9655..35fc351c54 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -824,6 +824,15 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } +var gateExecutors = [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, +} + // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -837,20 +846,10 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } - // Instruction executor functions - executors := [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 24d92940bb..a2cdeefc23 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -824,6 +824,15 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } +var gateExecutors = [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, +} + // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -837,20 +846,10 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } - // Instruction executor functions - executors := [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 4b9fade494..dc0163f62d 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -824,6 +824,15 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } +var gateExecutors = [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, +} + // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -837,20 +846,10 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } - // Instruction executor functions - executors := [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 39afde0159..ca34bbaa4c 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -824,6 +824,15 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } +var gateExecutors = [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, +} + // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -837,20 +846,10 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } - // Instruction executor functions - executors := [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 4a0f5dde54..fdf544316d 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -824,6 +824,15 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } +var gateExecutors = [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, +} + // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -837,20 +846,10 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } - // Instruction executor functions - executors := [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 1b44cac642..516ba72b2c 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -824,6 +824,15 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } +var gateExecutors = [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, +} + // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -837,20 +846,10 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } - // Instruction executor functions - executors := [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 750b8e5be1..c42dcfaa85 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -824,6 +824,15 @@ func (e *gateEvaluator) pushInput(input *small_rational.SmallRational) { e.frameSize++ } +var gateExecutors = [...]gateOp{ + gkrtypes.OpAdd: opAdd, + gkrtypes.OpSub: opSub, + gkrtypes.OpMul: opMul, + gkrtypes.OpNeg: opNeg, + gkrtypes.OpMulAcc: opMulAcc, + gkrtypes.OpSumExp17: opSumExp17, +} + // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -837,20 +846,10 @@ func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rat panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) } - // Instruction executor functions - executors := [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, - } - // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - executors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] From a38d67b381a7c1b6bbe219160153ad0e5c3c4ea8 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 13 Jan 2026 15:45:38 -0600 Subject: [PATCH 105/251] fix: don't copy async things --- .../generator/backend/template/gkr/solver_hints.go.tmpl | 6 +++--- internal/gkr/bls12-377/solver_hints.go | 6 +++--- internal/gkr/bls12-381/solver_hints.go | 6 +++--- internal/gkr/bls24-315/solver_hints.go | 6 +++--- internal/gkr/bls24-317/solver_hints.go | 6 +++--- internal/gkr/bn254/solver_hints.go | 6 +++--- internal/gkr/bw6-633/solver_hints.go | 6 +++--- internal/gkr/bw6-761/solver_hints.go | 6 +++--- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index f2ee61721f..928a37195e 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -131,7 +131,7 @@ func GetAssignmentHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] wireI := ins[1].Uint64() instanceI := ins[2].Uint64() @@ -152,7 +152,7 @@ func SolveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] instanceI := ins[1].Uint64() // indices for reading inputs and outputs @@ -211,7 +211,7 @@ func ProveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] hashName := data.hashName // drop the first input which indicates the current circuit index ins = ins[1:] diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index c13140f5f7..05df4f8944 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -139,7 +139,7 @@ func GetAssignmentHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] wireI := ins[1].Uint64() instanceI := ins[2].Uint64() @@ -160,7 +160,7 @@ func SolveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] instanceI := ins[1].Uint64() // indices for reading inputs and outputs @@ -219,7 +219,7 @@ func ProveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] hashName := data.hashName // drop the first input which indicates the current circuit index ins = ins[1:] diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index 57fb835f65..dfa99ad954 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -139,7 +139,7 @@ func GetAssignmentHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] wireI := ins[1].Uint64() instanceI := ins[2].Uint64() @@ -160,7 +160,7 @@ func SolveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] instanceI := ins[1].Uint64() // indices for reading inputs and outputs @@ -219,7 +219,7 @@ func ProveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] hashName := data.hashName // drop the first input which indicates the current circuit index ins = ins[1:] diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index fdb2a15ed1..833b5b8645 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -139,7 +139,7 @@ func GetAssignmentHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] wireI := ins[1].Uint64() instanceI := ins[2].Uint64() @@ -160,7 +160,7 @@ func SolveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] instanceI := ins[1].Uint64() // indices for reading inputs and outputs @@ -219,7 +219,7 @@ func ProveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] hashName := data.hashName // drop the first input which indicates the current circuit index ins = ins[1:] diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index 4323f5d880..9f0a6beb00 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -139,7 +139,7 @@ func GetAssignmentHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] wireI := ins[1].Uint64() instanceI := ins[2].Uint64() @@ -160,7 +160,7 @@ func SolveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] instanceI := ins[1].Uint64() // indices for reading inputs and outputs @@ -219,7 +219,7 @@ func ProveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] hashName := data.hashName // drop the first input which indicates the current circuit index ins = ins[1:] diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index 8ff11d2b47..bd238d4d40 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -139,7 +139,7 @@ func GetAssignmentHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] wireI := ins[1].Uint64() instanceI := ins[2].Uint64() @@ -160,7 +160,7 @@ func SolveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] instanceI := ins[1].Uint64() // indices for reading inputs and outputs @@ -219,7 +219,7 @@ func ProveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] hashName := data.hashName // drop the first input which indicates the current circuit index ins = ins[1:] diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index e52f724cd3..fe54e1e1fc 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -139,7 +139,7 @@ func GetAssignmentHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] wireI := ins[1].Uint64() instanceI := ins[2].Uint64() @@ -160,7 +160,7 @@ func SolveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] instanceI := ins[1].Uint64() // indices for reading inputs and outputs @@ -219,7 +219,7 @@ func ProveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] hashName := data.hashName // drop the first input which indicates the current circuit index ins = ins[1:] diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index 308178cfea..4514b9610c 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -139,7 +139,7 @@ func GetAssignmentHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] wireI := ins[1].Uint64() instanceI := ins[2].Uint64() @@ -160,7 +160,7 @@ func SolveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] instanceI := ins[1].Uint64() // indices for reading inputs and outputs @@ -219,7 +219,7 @@ func ProveHint(data []SolvingData) hint.Hint { if !ins[0].IsUint64() { return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") } - data := data[ins[0].Uint64()] + data := &data[ins[0].Uint64()] hashName := data.hashName // drop the first input which indicates the current circuit index ins = ins[1:] From 2aa37208b18b77f483dc098b039bf6224571e42e Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 14 Jan 2026 01:18:52 +0000 Subject: [PATCH 106/251] perf: gate evaluator pools --- internal/gkr/bls12-377/gkr.go | 74 ++++++++++++++++++++++++-- internal/gkr/bls12-377/solver_hints.go | 22 ++++++-- 2 files changed, 89 insertions(+), 7 deletions(-) diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 4e1f36922e..da3cabb16a 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -126,6 +126,8 @@ type eqTimesGateEvalSumcheckClaims struct { input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) + + gateEvaluatorPool *gateEvaluatorPool } func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { @@ -236,8 +238,9 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - // Create gate evaluator once per thread - evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) + // Get gate evaluator from pool (one per thread) + evaluator := c.gateEvaluatorPool.Get() + defer c.gateEvaluatorPool.Put(evaluator) res := make([]fr.Element, degGJ) @@ -340,6 +343,9 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem c.manager.memPool.Dump(c.claimedEvaluations, c.eq) + // Retire the gate evaluator pool for this layer + c.gateEvaluatorPool.DumpAll() + return evaluations } @@ -400,6 +406,9 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { res.input[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied } } + + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + return res } @@ -804,13 +813,17 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, - vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), frameSize: gate.NbConstants(), } + if len(elementPool) > 0 { + e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) + } else { + e.vars = make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)) + } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } @@ -859,3 +872,56 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { return res } + +// gateEvaluatorPool manages a pool of gate evaluators for a specific gate type +// All evaluators share the same underlying polynomial.Pool for fr.Element slices +type gateEvaluatorPool struct { + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + frPool *polynomial.Pool // shared fr.Element pool +} + +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { + gep := &gateEvaluatorPool{ + gate: gate, + nbIn: nbIn, + frPool: frPool, + available: make(map[*gateEvaluator]struct{}), + } + return gep +} + +func (gep *gateEvaluatorPool) Get() *gateEvaluator { + gep.lock.Lock() + defer gep.lock.Unlock() + + for e := range gep.available { + delete(gep.available, e) + return e + } + + // No available evaluator, create a new one + e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + + return &e +} + +func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { + gep.lock.Lock() + defer gep.lock.Unlock() + + // Reset evaluator state (but keep the vars slice for reuse) + e.frameSize = gep.gate.NbConstants() + + // Return evaluator to pool (it keeps its vars slice from polynomial pool) + gep.available[e] = struct{}{} +} + +// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool +func (gep *gateEvaluatorPool) DumpAll() { + for e := range gep.available { + gep.frPool.Dump(e.vars) + } +} diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 05df4f8944..b33fad3edb 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -16,6 +16,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) @@ -106,8 +107,21 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } + // Initialize polynomial pool for fr.Elements + // Size the pool for the worst case: max gate size across all wires + maxGateStackSize := 0 + for _, w := range d[k].circuit { + if !w.IsInput() { + stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) + if stackSize > maxGateStackSize { + maxGateStackSize = stackSize + } + } + } + // Initialize circuit evaluator pool - circuit := d[k].circuit // capture for closure + circuit := d[k].circuit // capture for closure + elementPool := polynomial.NewPool(maxGateStackSize) // capture pool reference d[k].evaluatorPool = sync.Pool{ New: func() interface{} { ce := &circuitEvaluator{ @@ -116,7 +130,7 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption for wI := range circuit { w := &circuit[wI] if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) } } return ce @@ -169,7 +183,9 @@ func SolveHint(data []SolvingData) hint.Hint { // Get a circuit evaluator from the pool ce := data.evaluatorPool.Get().(*circuitEvaluator) - defer data.evaluatorPool.Put(ce) + defer func() { + data.evaluatorPool.Put(ce) + }() // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. From 380120040b07babc421fa203098c213f4f9db2dc Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 13 Jan 2026 19:34:41 -0600 Subject: [PATCH 107/251] build: generify --- .../backend/template/gkr/gkr.go.tmpl | 71 +++++++++++++++++-- .../backend/template/gkr/solver_hints.go.tmpl | 18 ++++- internal/gkr/bls12-377/gkr.go | 7 +- internal/gkr/bls12-377/solver_hints.go | 10 ++- internal/gkr/bls12-381/gkr.go | 71 +++++++++++++++++-- internal/gkr/bls12-381/solver_hints.go | 18 ++++- internal/gkr/bls24-315/gkr.go | 71 +++++++++++++++++-- internal/gkr/bls24-315/solver_hints.go | 18 ++++- internal/gkr/bls24-317/gkr.go | 71 +++++++++++++++++-- internal/gkr/bls24-317/solver_hints.go | 18 ++++- internal/gkr/bn254/gkr.go | 71 +++++++++++++++++-- internal/gkr/bn254/solver_hints.go | 18 ++++- internal/gkr/bw6-633/gkr.go | 71 +++++++++++++++++-- internal/gkr/bw6-633/solver_hints.go | 18 ++++- internal/gkr/bw6-761/gkr.go | 71 +++++++++++++++++-- internal/gkr/bw6-761/solver_hints.go | 18 ++++- internal/gkr/small_rational/gkr.go | 71 +++++++++++++++++-- 17 files changed, 654 insertions(+), 57 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 360df6ba92..b31c8ebc7f 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -119,6 +119,8 @@ type eqTimesGateEvalSumcheckClaims struct { input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) + + gateEvaluatorPool *gateEvaluatorPool } func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { @@ -230,8 +232,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step {{ .ElementType }} - // Create gate evaluator once per thread - evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) + evaluator := c.gateEvaluatorPool.Get() + defer c.gateEvaluatorPool.Put(evaluator) res := make([]{{ .ElementType }}, degGJ) @@ -333,6 +335,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []{{ .ElementType }}) [ } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) + c.gateEvaluatorPool.DumpAll() return evaluations } @@ -394,6 +397,9 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { res.input[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied } } + + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + return res } @@ -799,13 +805,17 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, - vars: make([]{{ .ElementType }}, gate.NbConstants()+nbIn+len(gate.Instructions)), frameSize: gate.NbConstants(), } + if len(elementPool) > 0 { + e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) + } else { + e.vars = make([]{{.ElementType}}, gate.NbConstants()+nbIn+len(gate.Instructions)) + } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } @@ -854,3 +864,56 @@ func (e *gateEvaluator) evaluate(top ...{{ .ElementType }}) *{{ .ElementType }} return res } + +// gateEvaluatorPool manages a pool of gate evaluators for a specific gate type +// All evaluators share the same underlying polynomial.Pool for element slices +type gateEvaluatorPool struct { + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + frPool *polynomial.Pool // shared element pool +} + +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { + gep := &gateEvaluatorPool{ + gate: gate, + nbIn: nbIn, + frPool: frPool, + available: make(map[*gateEvaluator]struct{}), + } + return gep +} + +func (gep *gateEvaluatorPool) Get() *gateEvaluator { + gep.lock.Lock() + defer gep.lock.Unlock() + + for e := range gep.available { + delete(gep.available, e) + return e + } + + // No available evaluator, create a new one + e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + + return &e +} + +func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { + gep.lock.Lock() + defer gep.lock.Unlock() + + // Reset evaluator state (but keep the vars slice for reuse) + e.frameSize = gep.gate.NbConstants() + + // Return evaluator to pool (it keeps its vars slice from polynomial pool) + gep.available[e] = struct{}{} +} + +// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool +func (gep *gateEvaluatorPool) DumpAll() { + for e := range gep.available { + gep.frPool.Dump(e.vars) + } +} \ No newline at end of file diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index 928a37195e..a02c88f0e2 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -10,6 +10,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtypes" "{{ .FieldPackagePath }}" + "{{ .FieldPackagePath }}/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) @@ -98,8 +99,21 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } + // Initialize polynomial pool for elements + // Size the pool for the worst case: max gate size across all wires + maxGateStackSize := 0 + for _, w := range d[k].circuit { + if !w.IsInput() { + stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) + if stackSize > maxGateStackSize { + maxGateStackSize = stackSize + } + } + } + // Initialize circuit evaluator pool - circuit := d[k].circuit // capture for closure + circuit := d[k].circuit + elementPool := polynomial.NewPool(maxGateStackSize) d[k].evaluatorPool = sync.Pool{ New: func() interface{} { ce := &circuitEvaluator{ @@ -108,7 +122,7 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption for wI := range circuit { w := &circuit[wI] if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) } } return ce diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index da3cabb16a..b9a1f9903d 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -238,7 +238,6 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - // Get gate evaluator from pool (one per thread) evaluator := c.gateEvaluatorPool.Get() defer c.gateEvaluatorPool.Put(evaluator) @@ -342,8 +341,6 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - - // Retire the gate evaluator pool for this layer c.gateEvaluatorPool.DumpAll() return evaluations @@ -874,13 +871,13 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { } // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type -// All evaluators share the same underlying polynomial.Pool for fr.Element slices +// All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { gate *gkrtypes.CompiledGate nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} - frPool *polynomial.Pool // shared fr.Element pool + frPool *polynomial.Pool // shared element pool } func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index b33fad3edb..a455b09111 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -107,7 +107,7 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } - // Initialize polynomial pool for fr.Elements + // Initialize polynomial pool for elements // Size the pool for the worst case: max gate size across all wires maxGateStackSize := 0 for _, w := range d[k].circuit { @@ -120,8 +120,8 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } // Initialize circuit evaluator pool - circuit := d[k].circuit // capture for closure - elementPool := polynomial.NewPool(maxGateStackSize) // capture pool reference + circuit := d[k].circuit + elementPool := polynomial.NewPool(maxGateStackSize) d[k].evaluatorPool = sync.Pool{ New: func() interface{} { ce := &circuitEvaluator{ @@ -183,9 +183,7 @@ func SolveHint(data []SolvingData) hint.Hint { // Get a circuit evaluator from the pool ce := data.evaluatorPool.Get().(*circuitEvaluator) - defer func() { - data.evaluatorPool.Put(ce) - }() + defer data.evaluatorPool.Put(ce) // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 35fc351c54..88f90a8674 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -126,6 +126,8 @@ type eqTimesGateEvalSumcheckClaims struct { input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) + + gateEvaluatorPool *gateEvaluatorPool } func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { @@ -236,8 +238,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - // Create gate evaluator once per thread - evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) + evaluator := c.gateEvaluatorPool.Get() + defer c.gateEvaluatorPool.Put(evaluator) res := make([]fr.Element, degGJ) @@ -339,6 +341,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) + c.gateEvaluatorPool.DumpAll() return evaluations } @@ -400,6 +403,9 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { res.input[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied } } + + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + return res } @@ -804,13 +810,17 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, - vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), frameSize: gate.NbConstants(), } + if len(elementPool) > 0 { + e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) + } else { + e.vars = make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)) + } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } @@ -859,3 +869,56 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { return res } + +// gateEvaluatorPool manages a pool of gate evaluators for a specific gate type +// All evaluators share the same underlying polynomial.Pool for element slices +type gateEvaluatorPool struct { + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + frPool *polynomial.Pool // shared element pool +} + +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { + gep := &gateEvaluatorPool{ + gate: gate, + nbIn: nbIn, + frPool: frPool, + available: make(map[*gateEvaluator]struct{}), + } + return gep +} + +func (gep *gateEvaluatorPool) Get() *gateEvaluator { + gep.lock.Lock() + defer gep.lock.Unlock() + + for e := range gep.available { + delete(gep.available, e) + return e + } + + // No available evaluator, create a new one + e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + + return &e +} + +func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { + gep.lock.Lock() + defer gep.lock.Unlock() + + // Reset evaluator state (but keep the vars slice for reuse) + e.frameSize = gep.gate.NbConstants() + + // Return evaluator to pool (it keeps its vars slice from polynomial pool) + gep.available[e] = struct{}{} +} + +// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool +func (gep *gateEvaluatorPool) DumpAll() { + for e := range gep.available { + gep.frPool.Dump(e.vars) + } +} diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index dfa99ad954..dba5b5e71b 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -16,6 +16,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) @@ -106,8 +107,21 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } + // Initialize polynomial pool for elements + // Size the pool for the worst case: max gate size across all wires + maxGateStackSize := 0 + for _, w := range d[k].circuit { + if !w.IsInput() { + stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) + if stackSize > maxGateStackSize { + maxGateStackSize = stackSize + } + } + } + // Initialize circuit evaluator pool - circuit := d[k].circuit // capture for closure + circuit := d[k].circuit + elementPool := polynomial.NewPool(maxGateStackSize) d[k].evaluatorPool = sync.Pool{ New: func() interface{} { ce := &circuitEvaluator{ @@ -116,7 +130,7 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption for wI := range circuit { w := &circuit[wI] if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) } } return ce diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index a2cdeefc23..1618de561e 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -126,6 +126,8 @@ type eqTimesGateEvalSumcheckClaims struct { input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) + + gateEvaluatorPool *gateEvaluatorPool } func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { @@ -236,8 +238,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - // Create gate evaluator once per thread - evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) + evaluator := c.gateEvaluatorPool.Get() + defer c.gateEvaluatorPool.Put(evaluator) res := make([]fr.Element, degGJ) @@ -339,6 +341,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) + c.gateEvaluatorPool.DumpAll() return evaluations } @@ -400,6 +403,9 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { res.input[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied } } + + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + return res } @@ -804,13 +810,17 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, - vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), frameSize: gate.NbConstants(), } + if len(elementPool) > 0 { + e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) + } else { + e.vars = make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)) + } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } @@ -859,3 +869,56 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { return res } + +// gateEvaluatorPool manages a pool of gate evaluators for a specific gate type +// All evaluators share the same underlying polynomial.Pool for element slices +type gateEvaluatorPool struct { + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + frPool *polynomial.Pool // shared element pool +} + +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { + gep := &gateEvaluatorPool{ + gate: gate, + nbIn: nbIn, + frPool: frPool, + available: make(map[*gateEvaluator]struct{}), + } + return gep +} + +func (gep *gateEvaluatorPool) Get() *gateEvaluator { + gep.lock.Lock() + defer gep.lock.Unlock() + + for e := range gep.available { + delete(gep.available, e) + return e + } + + // No available evaluator, create a new one + e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + + return &e +} + +func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { + gep.lock.Lock() + defer gep.lock.Unlock() + + // Reset evaluator state (but keep the vars slice for reuse) + e.frameSize = gep.gate.NbConstants() + + // Return evaluator to pool (it keeps its vars slice from polynomial pool) + gep.available[e] = struct{}{} +} + +// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool +func (gep *gateEvaluatorPool) DumpAll() { + for e := range gep.available { + gep.frPool.Dump(e.vars) + } +} diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index 833b5b8645..efab765920 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -16,6 +16,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) @@ -106,8 +107,21 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } + // Initialize polynomial pool for elements + // Size the pool for the worst case: max gate size across all wires + maxGateStackSize := 0 + for _, w := range d[k].circuit { + if !w.IsInput() { + stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) + if stackSize > maxGateStackSize { + maxGateStackSize = stackSize + } + } + } + // Initialize circuit evaluator pool - circuit := d[k].circuit // capture for closure + circuit := d[k].circuit + elementPool := polynomial.NewPool(maxGateStackSize) d[k].evaluatorPool = sync.Pool{ New: func() interface{} { ce := &circuitEvaluator{ @@ -116,7 +130,7 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption for wI := range circuit { w := &circuit[wI] if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) } } return ce diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index dc0163f62d..0120e3a41b 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -126,6 +126,8 @@ type eqTimesGateEvalSumcheckClaims struct { input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) + + gateEvaluatorPool *gateEvaluatorPool } func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { @@ -236,8 +238,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - // Create gate evaluator once per thread - evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) + evaluator := c.gateEvaluatorPool.Get() + defer c.gateEvaluatorPool.Put(evaluator) res := make([]fr.Element, degGJ) @@ -339,6 +341,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) + c.gateEvaluatorPool.DumpAll() return evaluations } @@ -400,6 +403,9 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { res.input[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied } } + + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + return res } @@ -804,13 +810,17 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, - vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), frameSize: gate.NbConstants(), } + if len(elementPool) > 0 { + e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) + } else { + e.vars = make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)) + } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } @@ -859,3 +869,56 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { return res } + +// gateEvaluatorPool manages a pool of gate evaluators for a specific gate type +// All evaluators share the same underlying polynomial.Pool for element slices +type gateEvaluatorPool struct { + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + frPool *polynomial.Pool // shared element pool +} + +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { + gep := &gateEvaluatorPool{ + gate: gate, + nbIn: nbIn, + frPool: frPool, + available: make(map[*gateEvaluator]struct{}), + } + return gep +} + +func (gep *gateEvaluatorPool) Get() *gateEvaluator { + gep.lock.Lock() + defer gep.lock.Unlock() + + for e := range gep.available { + delete(gep.available, e) + return e + } + + // No available evaluator, create a new one + e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + + return &e +} + +func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { + gep.lock.Lock() + defer gep.lock.Unlock() + + // Reset evaluator state (but keep the vars slice for reuse) + e.frameSize = gep.gate.NbConstants() + + // Return evaluator to pool (it keeps its vars slice from polynomial pool) + gep.available[e] = struct{}{} +} + +// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool +func (gep *gateEvaluatorPool) DumpAll() { + for e := range gep.available { + gep.frPool.Dump(e.vars) + } +} diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index 9f0a6beb00..bcd1741665 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -16,6 +16,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" + "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) @@ -106,8 +107,21 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } + // Initialize polynomial pool for elements + // Size the pool for the worst case: max gate size across all wires + maxGateStackSize := 0 + for _, w := range d[k].circuit { + if !w.IsInput() { + stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) + if stackSize > maxGateStackSize { + maxGateStackSize = stackSize + } + } + } + // Initialize circuit evaluator pool - circuit := d[k].circuit // capture for closure + circuit := d[k].circuit + elementPool := polynomial.NewPool(maxGateStackSize) d[k].evaluatorPool = sync.Pool{ New: func() interface{} { ce := &circuitEvaluator{ @@ -116,7 +130,7 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption for wI := range circuit { w := &circuit[wI] if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) } } return ce diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index ca34bbaa4c..c8b14ac207 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -126,6 +126,8 @@ type eqTimesGateEvalSumcheckClaims struct { input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) + + gateEvaluatorPool *gateEvaluatorPool } func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { @@ -236,8 +238,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - // Create gate evaluator once per thread - evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) + evaluator := c.gateEvaluatorPool.Get() + defer c.gateEvaluatorPool.Put(evaluator) res := make([]fr.Element, degGJ) @@ -339,6 +341,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) + c.gateEvaluatorPool.DumpAll() return evaluations } @@ -400,6 +403,9 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { res.input[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied } } + + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + return res } @@ -804,13 +810,17 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, - vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), frameSize: gate.NbConstants(), } + if len(elementPool) > 0 { + e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) + } else { + e.vars = make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)) + } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } @@ -859,3 +869,56 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { return res } + +// gateEvaluatorPool manages a pool of gate evaluators for a specific gate type +// All evaluators share the same underlying polynomial.Pool for element slices +type gateEvaluatorPool struct { + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + frPool *polynomial.Pool // shared element pool +} + +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { + gep := &gateEvaluatorPool{ + gate: gate, + nbIn: nbIn, + frPool: frPool, + available: make(map[*gateEvaluator]struct{}), + } + return gep +} + +func (gep *gateEvaluatorPool) Get() *gateEvaluator { + gep.lock.Lock() + defer gep.lock.Unlock() + + for e := range gep.available { + delete(gep.available, e) + return e + } + + // No available evaluator, create a new one + e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + + return &e +} + +func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { + gep.lock.Lock() + defer gep.lock.Unlock() + + // Reset evaluator state (but keep the vars slice for reuse) + e.frameSize = gep.gate.NbConstants() + + // Return evaluator to pool (it keeps its vars slice from polynomial pool) + gep.available[e] = struct{}{} +} + +// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool +func (gep *gateEvaluatorPool) DumpAll() { + for e := range gep.available { + gep.frPool.Dump(e.vars) + } +} diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index bd238d4d40..c0befba7fe 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -16,6 +16,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bn254/fr" + "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) @@ -106,8 +107,21 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } + // Initialize polynomial pool for elements + // Size the pool for the worst case: max gate size across all wires + maxGateStackSize := 0 + for _, w := range d[k].circuit { + if !w.IsInput() { + stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) + if stackSize > maxGateStackSize { + maxGateStackSize = stackSize + } + } + } + // Initialize circuit evaluator pool - circuit := d[k].circuit // capture for closure + circuit := d[k].circuit + elementPool := polynomial.NewPool(maxGateStackSize) d[k].evaluatorPool = sync.Pool{ New: func() interface{} { ce := &circuitEvaluator{ @@ -116,7 +130,7 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption for wI := range circuit { w := &circuit[wI] if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) } } return ce diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index fdf544316d..dd707d1ca8 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -126,6 +126,8 @@ type eqTimesGateEvalSumcheckClaims struct { input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) + + gateEvaluatorPool *gateEvaluatorPool } func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { @@ -236,8 +238,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - // Create gate evaluator once per thread - evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) + evaluator := c.gateEvaluatorPool.Get() + defer c.gateEvaluatorPool.Put(evaluator) res := make([]fr.Element, degGJ) @@ -339,6 +341,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) + c.gateEvaluatorPool.DumpAll() return evaluations } @@ -400,6 +403,9 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { res.input[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied } } + + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + return res } @@ -804,13 +810,17 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, - vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), frameSize: gate.NbConstants(), } + if len(elementPool) > 0 { + e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) + } else { + e.vars = make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)) + } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } @@ -859,3 +869,56 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { return res } + +// gateEvaluatorPool manages a pool of gate evaluators for a specific gate type +// All evaluators share the same underlying polynomial.Pool for element slices +type gateEvaluatorPool struct { + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + frPool *polynomial.Pool // shared element pool +} + +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { + gep := &gateEvaluatorPool{ + gate: gate, + nbIn: nbIn, + frPool: frPool, + available: make(map[*gateEvaluator]struct{}), + } + return gep +} + +func (gep *gateEvaluatorPool) Get() *gateEvaluator { + gep.lock.Lock() + defer gep.lock.Unlock() + + for e := range gep.available { + delete(gep.available, e) + return e + } + + // No available evaluator, create a new one + e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + + return &e +} + +func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { + gep.lock.Lock() + defer gep.lock.Unlock() + + // Reset evaluator state (but keep the vars slice for reuse) + e.frameSize = gep.gate.NbConstants() + + // Return evaluator to pool (it keeps its vars slice from polynomial pool) + gep.available[e] = struct{}{} +} + +// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool +func (gep *gateEvaluatorPool) DumpAll() { + for e := range gep.available { + gep.frPool.Dump(e.vars) + } +} diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index fe54e1e1fc..04c247ce2b 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -16,6 +16,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" + "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) @@ -106,8 +107,21 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } + // Initialize polynomial pool for elements + // Size the pool for the worst case: max gate size across all wires + maxGateStackSize := 0 + for _, w := range d[k].circuit { + if !w.IsInput() { + stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) + if stackSize > maxGateStackSize { + maxGateStackSize = stackSize + } + } + } + // Initialize circuit evaluator pool - circuit := d[k].circuit // capture for closure + circuit := d[k].circuit + elementPool := polynomial.NewPool(maxGateStackSize) d[k].evaluatorPool = sync.Pool{ New: func() interface{} { ce := &circuitEvaluator{ @@ -116,7 +130,7 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption for wI := range circuit { w := &circuit[wI] if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) } } return ce diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 516ba72b2c..9a95d03122 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -126,6 +126,8 @@ type eqTimesGateEvalSumcheckClaims struct { input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) + + gateEvaluatorPool *gateEvaluatorPool } func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { @@ -236,8 +238,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - // Create gate evaluator once per thread - evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) + evaluator := c.gateEvaluatorPool.Get() + defer c.gateEvaluatorPool.Put(evaluator) res := make([]fr.Element, degGJ) @@ -339,6 +341,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) + c.gateEvaluatorPool.DumpAll() return evaluations } @@ -400,6 +403,9 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { res.input[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied } } + + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + return res } @@ -804,13 +810,17 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, - vars: make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)), frameSize: gate.NbConstants(), } + if len(elementPool) > 0 { + e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) + } else { + e.vars = make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)) + } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } @@ -859,3 +869,56 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { return res } + +// gateEvaluatorPool manages a pool of gate evaluators for a specific gate type +// All evaluators share the same underlying polynomial.Pool for element slices +type gateEvaluatorPool struct { + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + frPool *polynomial.Pool // shared element pool +} + +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { + gep := &gateEvaluatorPool{ + gate: gate, + nbIn: nbIn, + frPool: frPool, + available: make(map[*gateEvaluator]struct{}), + } + return gep +} + +func (gep *gateEvaluatorPool) Get() *gateEvaluator { + gep.lock.Lock() + defer gep.lock.Unlock() + + for e := range gep.available { + delete(gep.available, e) + return e + } + + // No available evaluator, create a new one + e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + + return &e +} + +func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { + gep.lock.Lock() + defer gep.lock.Unlock() + + // Reset evaluator state (but keep the vars slice for reuse) + e.frameSize = gep.gate.NbConstants() + + // Return evaluator to pool (it keeps its vars slice from polynomial pool) + gep.available[e] = struct{}{} +} + +// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool +func (gep *gateEvaluatorPool) DumpAll() { + for e := range gep.available { + gep.frPool.Dump(e.vars) + } +} diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index 4514b9610c..5d99a2033c 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -16,6 +16,7 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" + "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) @@ -106,8 +107,21 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } } + // Initialize polynomial pool for elements + // Size the pool for the worst case: max gate size across all wires + maxGateStackSize := 0 + for _, w := range d[k].circuit { + if !w.IsInput() { + stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) + if stackSize > maxGateStackSize { + maxGateStackSize = stackSize + } + } + } + // Initialize circuit evaluator pool - circuit := d[k].circuit // capture for closure + circuit := d[k].circuit + elementPool := polynomial.NewPool(maxGateStackSize) d[k].evaluatorPool = sync.Pool{ New: func() interface{} { ce := &circuitEvaluator{ @@ -116,7 +130,7 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption for wI := range circuit { w := &circuit[wI] if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) } } return ce diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index c42dcfaa85..68a0302b3e 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -126,6 +126,8 @@ type eqTimesGateEvalSumcheckClaims struct { input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) + + gateEvaluatorPool *gateEvaluatorPool } func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { @@ -236,8 +238,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step small_rational.SmallRational - // Create gate evaluator once per thread - evaluator := newGateEvaluator(wire.Gate.Compiled(), nbGateIn) + evaluator := c.gateEvaluatorPool.Get() + defer c.gateEvaluatorPool.Put(evaluator) res := make([]small_rational.SmallRational, degGJ) @@ -339,6 +341,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []small_rational.SmallR } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) + c.gateEvaluatorPool.DumpAll() return evaluations } @@ -400,6 +403,9 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { res.input[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied } } + + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + return res } @@ -804,13 +810,17 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, - vars: make([]small_rational.SmallRational, gate.NbConstants()+nbIn+len(gate.Instructions)), frameSize: gate.NbConstants(), } + if len(elementPool) > 0 { + e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) + } else { + e.vars = make([]small_rational.SmallRational, gate.NbConstants()+nbIn+len(gate.Instructions)) + } for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } @@ -859,3 +869,56 @@ func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rat return res } + +// gateEvaluatorPool manages a pool of gate evaluators for a specific gate type +// All evaluators share the same underlying polynomial.Pool for element slices +type gateEvaluatorPool struct { + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + frPool *polynomial.Pool // shared element pool +} + +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { + gep := &gateEvaluatorPool{ + gate: gate, + nbIn: nbIn, + frPool: frPool, + available: make(map[*gateEvaluator]struct{}), + } + return gep +} + +func (gep *gateEvaluatorPool) Get() *gateEvaluator { + gep.lock.Lock() + defer gep.lock.Unlock() + + for e := range gep.available { + delete(gep.available, e) + return e + } + + // No available evaluator, create a new one + e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + + return &e +} + +func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { + gep.lock.Lock() + defer gep.lock.Unlock() + + // Reset evaluator state (but keep the vars slice for reuse) + e.frameSize = gep.gate.NbConstants() + + // Return evaluator to pool (it keeps its vars slice from polynomial pool) + gep.available[e] = struct{}{} +} + +// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool +func (gep *gateEvaluatorPool) DumpAll() { + for e := range gep.available { + gep.frPool.Dump(e.vars) + } +} From 40d43c51d1a94548047dfc39061ed4dbe5a0f1d5 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 14 Jan 2026 02:14:19 +0000 Subject: [PATCH 108/251] perf: use switch instead of func ptr --- internal/gkr/bls12-377/gkr.go | 97 ++++++++++++++--------------------- 1 file changed, 39 insertions(+), 58 deletions(-) diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index b9a1f9903d..50b0376239 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -731,54 +731,6 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the -// result in dst. In practice, dst will be one of the vars but this is not required. -type gateOp func(dst *fr.Element, vars []fr.Element, in []uint16) - -func opAdd(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Add(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Add(dst, &vars[in[i]]) - } -} - -func opMulAcc(dst *fr.Element, vars []fr.Element, in []uint16) { - var prod fr.Element - prod.Mul(&vars[in[1]], &vars[in[2]]) - dst.Add(&vars[in[0]], &prod) -} - -func opNeg(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Neg(&vars[in[0]]) -} - -func opSub(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Sub(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Sub(dst, &vars[in[i]]) - } -} - -func opMul(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Mul(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Mul(dst, &vars[in[i]]) - } -} - -func opSumExp17(dst *fr.Element, vars []fr.Element, in []uint16) { - // result = (x[0] + x[1] + x[2])^17 - var sum fr.Element - sum.Add(&vars[in[0]], &vars[in[1]]) - sum.Add(&sum, &vars[in[2]]) - - dst.Mul(&sum, &sum) // sum² - dst.Mul(dst, dst) // sum⁴ - dst.Mul(dst, dst) // sum⁸ - dst.Mul(dst, dst) // sum¹⁶ - dst.Mul(dst, &sum) // sum¹⁷ -} - /* func (stack *elementStack) Println(a ...frontend.Variable) { toPrint := make([]any, len(a)) @@ -834,15 +786,6 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } -var gateExecutors = [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, -} - // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -859,7 +802,45 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + inst := &e.gate.Instructions[i] + dst := &e.vars[i+e.frameSize] + + // Use switch instead of function pointer for better inlining + switch inst.Op { + case gkrtypes.OpAdd: + dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Add(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpMul: + dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Mul(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpSub: + dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Sub(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpNeg: + dst.Neg(&e.vars[inst.Inputs[0]]) + case gkrtypes.OpMulAcc: + var prod fr.Element + prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) + dst.Add(&e.vars[inst.Inputs[0]], &prod) + case gkrtypes.OpSumExp17: + // result = (x[0] + x[1] + x[2])^17 + var sum fr.Element + sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + sum.Add(&sum, &e.vars[inst.Inputs[2]]) + dst.Mul(&sum, &sum) // x² + dst.Mul(dst, dst) // x⁴ + dst.Mul(dst, dst) // x⁸ + dst.Mul(dst, dst) // x¹⁶ + dst.Mul(dst, &sum) // x¹⁷ + default: + panic(fmt.Sprintf("unknown operation: %d", inst.Op)) + } } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] From bbc0b59539e7780f0b17371596b76396bfe49263 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 13 Jan 2026 20:30:18 -0600 Subject: [PATCH 109/251] build: generify and get rid of Println --- .../backend/template/gkr/gkr.go.tmpl | 116 ++++++------------ internal/gkr/bls12-377/gkr.go | 19 --- internal/gkr/bls12-381/gkr.go | 116 ++++++------------ internal/gkr/bls24-315/gkr.go | 116 ++++++------------ internal/gkr/bls24-317/gkr.go | 116 ++++++------------ internal/gkr/bn254/gkr.go | 116 ++++++------------ internal/gkr/bw6-633/gkr.go | 116 ++++++------------ internal/gkr/bw6-761/gkr.go | 116 ++++++------------ internal/gkr/gkrhints/engine_hints.go | 12 -- internal/gkr/gkrtypes/compiledgate.go | 5 - internal/gkr/small_rational/gkr.go | 116 ++++++------------ std/gkrapi/gkr/types.go | 4 - 12 files changed, 312 insertions(+), 656 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index b31c8ebc7f..c64980442a 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -726,73 +726,6 @@ func frToBigInts(dst []*big.Int, src []{{ .ElementType }}) { } } -// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the -// result in dst. In practice, dst will be one of the vars but this is not required. -type gateOp func(dst *{{ .ElementType }}, vars []{{ .ElementType }}, in []uint16) - -func opAdd(dst *{{ .ElementType }}, vars []{{ .ElementType }}, in []uint16) { - dst.Add(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Add(dst, &vars[in[i]]) - } -} - -func opMulAcc(dst *{{ .ElementType }}, vars []{{ .ElementType }}, in []uint16) { - var prod {{ .ElementType }} - prod.Mul(&vars[in[1]], &vars[in[2]]) - dst.Add(&vars[in[0]], &prod) -} - -func opNeg(dst *{{ .ElementType }}, vars []{{ .ElementType }}, in []uint16) { - dst.Neg(&vars[in[0]]) -} - -func opSub(dst *{{ .ElementType }}, vars []{{ .ElementType }}, in []uint16) { - dst.Sub(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Sub(dst, &vars[in[i]]) - } -} - -func opMul(dst *{{ .ElementType }}, vars []{{ .ElementType }}, in []uint16) { - dst.Mul(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Mul(dst, &vars[in[i]]) - } -} - -func opSumExp17(dst *{{ .ElementType }}, vars []{{ .ElementType }}, in []uint16) { - // result = (x[0] + x[1] + x[2])^17 - var sum {{ .ElementType }} - sum.Add(&vars[in[0]], &vars[in[1]]) - sum.Add(&sum, &vars[in[2]]) - - dst.Mul(&sum, &sum) // sum² - dst.Mul(dst, dst) // sum⁴ - dst.Mul(dst, dst) // sum⁸ - dst.Mul(dst, dst) // sum¹⁶ - dst.Mul(dst, &sum) // sum¹⁷ -} - -/* -func (stack *elementStack) Println(a ...frontend.Variable) { - toPrint := make([]any, len(a)) - var x {{ .ElementType }} - - for i, v := range a { - if _, err := x.SetInterface(v); err != nil { - if s, ok := v.(string); ok { - toPrint[i] = s - continue - } - panic(fmt.Errorf("not numeric or string: %w", err)) - } else { - toPrint[i] = x.String() - } - } - fmt.Println(toPrint...) -}*/ - // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. @@ -829,15 +762,6 @@ func (e *gateEvaluator) pushInput(input *{{ .ElementType }}) { e.frameSize++ } -var gateExecutors = [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, -} - // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -854,7 +778,45 @@ func (e *gateEvaluator) evaluate(top ...{{ .ElementType }}) *{{ .ElementType }} // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + inst := &e.gate.Instructions[i] + dst := &e.vars[i+e.frameSize] + + // Use switch instead of function pointer for better inlining + switch inst.Op { + case gkrtypes.OpAdd: + dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Add(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpMul: + dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Mul(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpSub: + dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Sub(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpNeg: + dst.Neg(&e.vars[inst.Inputs[0]]) + case gkrtypes.OpMulAcc: + var prod {{ .ElementType }} + prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) + dst.Add(&e.vars[inst.Inputs[0]], &prod) + case gkrtypes.OpSumExp17: + // result = (x[0] + x[1] + x[2])^17 + var sum {{ .ElementType }} + sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + sum.Add(&sum, &e.vars[inst.Inputs[2]]) + dst.Mul(&sum, &sum) // x² + dst.Mul(dst, dst) // x⁴ + dst.Mul(dst, dst) // x⁸ + dst.Mul(dst, dst) // x¹⁶ + dst.Mul(dst, &sum) // x¹⁷ + default: + panic(fmt.Sprintf("unknown operation: %d", inst.Op)) + } } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 50b0376239..65588f643a 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -731,25 +731,6 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -/* -func (stack *elementStack) Println(a ...frontend.Variable) { - toPrint := make([]any, len(a)) - var x fr.Element - - for i, v := range a { - if _, err := x.SetInterface(v); err != nil { - if s, ok := v.(string); ok { - toPrint[i] = s - continue - } - panic(fmt.Errorf("not numeric or string: %w", err)) - } else { - toPrint[i] = x.String() - } - } - fmt.Println(toPrint...) -}*/ - // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 88f90a8674..15ebe5ccea 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -731,73 +731,6 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the -// result in dst. In practice, dst will be one of the vars but this is not required. -type gateOp func(dst *fr.Element, vars []fr.Element, in []uint16) - -func opAdd(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Add(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Add(dst, &vars[in[i]]) - } -} - -func opMulAcc(dst *fr.Element, vars []fr.Element, in []uint16) { - var prod fr.Element - prod.Mul(&vars[in[1]], &vars[in[2]]) - dst.Add(&vars[in[0]], &prod) -} - -func opNeg(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Neg(&vars[in[0]]) -} - -func opSub(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Sub(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Sub(dst, &vars[in[i]]) - } -} - -func opMul(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Mul(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Mul(dst, &vars[in[i]]) - } -} - -func opSumExp17(dst *fr.Element, vars []fr.Element, in []uint16) { - // result = (x[0] + x[1] + x[2])^17 - var sum fr.Element - sum.Add(&vars[in[0]], &vars[in[1]]) - sum.Add(&sum, &vars[in[2]]) - - dst.Mul(&sum, &sum) // sum² - dst.Mul(dst, dst) // sum⁴ - dst.Mul(dst, dst) // sum⁸ - dst.Mul(dst, dst) // sum¹⁶ - dst.Mul(dst, &sum) // sum¹⁷ -} - -/* -func (stack *elementStack) Println(a ...frontend.Variable) { - toPrint := make([]any, len(a)) - var x fr.Element - - for i, v := range a { - if _, err := x.SetInterface(v); err != nil { - if s, ok := v.(string); ok { - toPrint[i] = s - continue - } - panic(fmt.Errorf("not numeric or string: %w", err)) - } else { - toPrint[i] = x.String() - } - } - fmt.Println(toPrint...) -}*/ - // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. @@ -834,15 +767,6 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } -var gateExecutors = [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, -} - // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -859,7 +783,45 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + inst := &e.gate.Instructions[i] + dst := &e.vars[i+e.frameSize] + + // Use switch instead of function pointer for better inlining + switch inst.Op { + case gkrtypes.OpAdd: + dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Add(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpMul: + dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Mul(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpSub: + dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Sub(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpNeg: + dst.Neg(&e.vars[inst.Inputs[0]]) + case gkrtypes.OpMulAcc: + var prod fr.Element + prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) + dst.Add(&e.vars[inst.Inputs[0]], &prod) + case gkrtypes.OpSumExp17: + // result = (x[0] + x[1] + x[2])^17 + var sum fr.Element + sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + sum.Add(&sum, &e.vars[inst.Inputs[2]]) + dst.Mul(&sum, &sum) // x² + dst.Mul(dst, dst) // x⁴ + dst.Mul(dst, dst) // x⁸ + dst.Mul(dst, dst) // x¹⁶ + dst.Mul(dst, &sum) // x¹⁷ + default: + panic(fmt.Sprintf("unknown operation: %d", inst.Op)) + } } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 1618de561e..1156d683e4 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -731,73 +731,6 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the -// result in dst. In practice, dst will be one of the vars but this is not required. -type gateOp func(dst *fr.Element, vars []fr.Element, in []uint16) - -func opAdd(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Add(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Add(dst, &vars[in[i]]) - } -} - -func opMulAcc(dst *fr.Element, vars []fr.Element, in []uint16) { - var prod fr.Element - prod.Mul(&vars[in[1]], &vars[in[2]]) - dst.Add(&vars[in[0]], &prod) -} - -func opNeg(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Neg(&vars[in[0]]) -} - -func opSub(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Sub(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Sub(dst, &vars[in[i]]) - } -} - -func opMul(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Mul(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Mul(dst, &vars[in[i]]) - } -} - -func opSumExp17(dst *fr.Element, vars []fr.Element, in []uint16) { - // result = (x[0] + x[1] + x[2])^17 - var sum fr.Element - sum.Add(&vars[in[0]], &vars[in[1]]) - sum.Add(&sum, &vars[in[2]]) - - dst.Mul(&sum, &sum) // sum² - dst.Mul(dst, dst) // sum⁴ - dst.Mul(dst, dst) // sum⁸ - dst.Mul(dst, dst) // sum¹⁶ - dst.Mul(dst, &sum) // sum¹⁷ -} - -/* -func (stack *elementStack) Println(a ...frontend.Variable) { - toPrint := make([]any, len(a)) - var x fr.Element - - for i, v := range a { - if _, err := x.SetInterface(v); err != nil { - if s, ok := v.(string); ok { - toPrint[i] = s - continue - } - panic(fmt.Errorf("not numeric or string: %w", err)) - } else { - toPrint[i] = x.String() - } - } - fmt.Println(toPrint...) -}*/ - // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. @@ -834,15 +767,6 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } -var gateExecutors = [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, -} - // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -859,7 +783,45 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + inst := &e.gate.Instructions[i] + dst := &e.vars[i+e.frameSize] + + // Use switch instead of function pointer for better inlining + switch inst.Op { + case gkrtypes.OpAdd: + dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Add(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpMul: + dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Mul(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpSub: + dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Sub(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpNeg: + dst.Neg(&e.vars[inst.Inputs[0]]) + case gkrtypes.OpMulAcc: + var prod fr.Element + prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) + dst.Add(&e.vars[inst.Inputs[0]], &prod) + case gkrtypes.OpSumExp17: + // result = (x[0] + x[1] + x[2])^17 + var sum fr.Element + sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + sum.Add(&sum, &e.vars[inst.Inputs[2]]) + dst.Mul(&sum, &sum) // x² + dst.Mul(dst, dst) // x⁴ + dst.Mul(dst, dst) // x⁸ + dst.Mul(dst, dst) // x¹⁶ + dst.Mul(dst, &sum) // x¹⁷ + default: + panic(fmt.Sprintf("unknown operation: %d", inst.Op)) + } } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 0120e3a41b..6473c22f6b 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -731,73 +731,6 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the -// result in dst. In practice, dst will be one of the vars but this is not required. -type gateOp func(dst *fr.Element, vars []fr.Element, in []uint16) - -func opAdd(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Add(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Add(dst, &vars[in[i]]) - } -} - -func opMulAcc(dst *fr.Element, vars []fr.Element, in []uint16) { - var prod fr.Element - prod.Mul(&vars[in[1]], &vars[in[2]]) - dst.Add(&vars[in[0]], &prod) -} - -func opNeg(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Neg(&vars[in[0]]) -} - -func opSub(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Sub(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Sub(dst, &vars[in[i]]) - } -} - -func opMul(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Mul(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Mul(dst, &vars[in[i]]) - } -} - -func opSumExp17(dst *fr.Element, vars []fr.Element, in []uint16) { - // result = (x[0] + x[1] + x[2])^17 - var sum fr.Element - sum.Add(&vars[in[0]], &vars[in[1]]) - sum.Add(&sum, &vars[in[2]]) - - dst.Mul(&sum, &sum) // sum² - dst.Mul(dst, dst) // sum⁴ - dst.Mul(dst, dst) // sum⁸ - dst.Mul(dst, dst) // sum¹⁶ - dst.Mul(dst, &sum) // sum¹⁷ -} - -/* -func (stack *elementStack) Println(a ...frontend.Variable) { - toPrint := make([]any, len(a)) - var x fr.Element - - for i, v := range a { - if _, err := x.SetInterface(v); err != nil { - if s, ok := v.(string); ok { - toPrint[i] = s - continue - } - panic(fmt.Errorf("not numeric or string: %w", err)) - } else { - toPrint[i] = x.String() - } - } - fmt.Println(toPrint...) -}*/ - // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. @@ -834,15 +767,6 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } -var gateExecutors = [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, -} - // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -859,7 +783,45 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + inst := &e.gate.Instructions[i] + dst := &e.vars[i+e.frameSize] + + // Use switch instead of function pointer for better inlining + switch inst.Op { + case gkrtypes.OpAdd: + dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Add(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpMul: + dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Mul(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpSub: + dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Sub(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpNeg: + dst.Neg(&e.vars[inst.Inputs[0]]) + case gkrtypes.OpMulAcc: + var prod fr.Element + prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) + dst.Add(&e.vars[inst.Inputs[0]], &prod) + case gkrtypes.OpSumExp17: + // result = (x[0] + x[1] + x[2])^17 + var sum fr.Element + sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + sum.Add(&sum, &e.vars[inst.Inputs[2]]) + dst.Mul(&sum, &sum) // x² + dst.Mul(dst, dst) // x⁴ + dst.Mul(dst, dst) // x⁸ + dst.Mul(dst, dst) // x¹⁶ + dst.Mul(dst, &sum) // x¹⁷ + default: + panic(fmt.Sprintf("unknown operation: %d", inst.Op)) + } } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index c8b14ac207..ce405edc11 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -731,73 +731,6 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the -// result in dst. In practice, dst will be one of the vars but this is not required. -type gateOp func(dst *fr.Element, vars []fr.Element, in []uint16) - -func opAdd(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Add(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Add(dst, &vars[in[i]]) - } -} - -func opMulAcc(dst *fr.Element, vars []fr.Element, in []uint16) { - var prod fr.Element - prod.Mul(&vars[in[1]], &vars[in[2]]) - dst.Add(&vars[in[0]], &prod) -} - -func opNeg(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Neg(&vars[in[0]]) -} - -func opSub(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Sub(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Sub(dst, &vars[in[i]]) - } -} - -func opMul(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Mul(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Mul(dst, &vars[in[i]]) - } -} - -func opSumExp17(dst *fr.Element, vars []fr.Element, in []uint16) { - // result = (x[0] + x[1] + x[2])^17 - var sum fr.Element - sum.Add(&vars[in[0]], &vars[in[1]]) - sum.Add(&sum, &vars[in[2]]) - - dst.Mul(&sum, &sum) // sum² - dst.Mul(dst, dst) // sum⁴ - dst.Mul(dst, dst) // sum⁸ - dst.Mul(dst, dst) // sum¹⁶ - dst.Mul(dst, &sum) // sum¹⁷ -} - -/* -func (stack *elementStack) Println(a ...frontend.Variable) { - toPrint := make([]any, len(a)) - var x fr.Element - - for i, v := range a { - if _, err := x.SetInterface(v); err != nil { - if s, ok := v.(string); ok { - toPrint[i] = s - continue - } - panic(fmt.Errorf("not numeric or string: %w", err)) - } else { - toPrint[i] = x.String() - } - } - fmt.Println(toPrint...) -}*/ - // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. @@ -834,15 +767,6 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } -var gateExecutors = [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, -} - // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -859,7 +783,45 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + inst := &e.gate.Instructions[i] + dst := &e.vars[i+e.frameSize] + + // Use switch instead of function pointer for better inlining + switch inst.Op { + case gkrtypes.OpAdd: + dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Add(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpMul: + dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Mul(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpSub: + dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Sub(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpNeg: + dst.Neg(&e.vars[inst.Inputs[0]]) + case gkrtypes.OpMulAcc: + var prod fr.Element + prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) + dst.Add(&e.vars[inst.Inputs[0]], &prod) + case gkrtypes.OpSumExp17: + // result = (x[0] + x[1] + x[2])^17 + var sum fr.Element + sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + sum.Add(&sum, &e.vars[inst.Inputs[2]]) + dst.Mul(&sum, &sum) // x² + dst.Mul(dst, dst) // x⁴ + dst.Mul(dst, dst) // x⁸ + dst.Mul(dst, dst) // x¹⁶ + dst.Mul(dst, &sum) // x¹⁷ + default: + panic(fmt.Sprintf("unknown operation: %d", inst.Op)) + } } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index dd707d1ca8..68a6d8c643 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -731,73 +731,6 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the -// result in dst. In practice, dst will be one of the vars but this is not required. -type gateOp func(dst *fr.Element, vars []fr.Element, in []uint16) - -func opAdd(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Add(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Add(dst, &vars[in[i]]) - } -} - -func opMulAcc(dst *fr.Element, vars []fr.Element, in []uint16) { - var prod fr.Element - prod.Mul(&vars[in[1]], &vars[in[2]]) - dst.Add(&vars[in[0]], &prod) -} - -func opNeg(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Neg(&vars[in[0]]) -} - -func opSub(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Sub(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Sub(dst, &vars[in[i]]) - } -} - -func opMul(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Mul(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Mul(dst, &vars[in[i]]) - } -} - -func opSumExp17(dst *fr.Element, vars []fr.Element, in []uint16) { - // result = (x[0] + x[1] + x[2])^17 - var sum fr.Element - sum.Add(&vars[in[0]], &vars[in[1]]) - sum.Add(&sum, &vars[in[2]]) - - dst.Mul(&sum, &sum) // sum² - dst.Mul(dst, dst) // sum⁴ - dst.Mul(dst, dst) // sum⁸ - dst.Mul(dst, dst) // sum¹⁶ - dst.Mul(dst, &sum) // sum¹⁷ -} - -/* -func (stack *elementStack) Println(a ...frontend.Variable) { - toPrint := make([]any, len(a)) - var x fr.Element - - for i, v := range a { - if _, err := x.SetInterface(v); err != nil { - if s, ok := v.(string); ok { - toPrint[i] = s - continue - } - panic(fmt.Errorf("not numeric or string: %w", err)) - } else { - toPrint[i] = x.String() - } - } - fmt.Println(toPrint...) -}*/ - // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. @@ -834,15 +767,6 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } -var gateExecutors = [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, -} - // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -859,7 +783,45 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + inst := &e.gate.Instructions[i] + dst := &e.vars[i+e.frameSize] + + // Use switch instead of function pointer for better inlining + switch inst.Op { + case gkrtypes.OpAdd: + dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Add(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpMul: + dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Mul(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpSub: + dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Sub(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpNeg: + dst.Neg(&e.vars[inst.Inputs[0]]) + case gkrtypes.OpMulAcc: + var prod fr.Element + prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) + dst.Add(&e.vars[inst.Inputs[0]], &prod) + case gkrtypes.OpSumExp17: + // result = (x[0] + x[1] + x[2])^17 + var sum fr.Element + sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + sum.Add(&sum, &e.vars[inst.Inputs[2]]) + dst.Mul(&sum, &sum) // x² + dst.Mul(dst, dst) // x⁴ + dst.Mul(dst, dst) // x⁸ + dst.Mul(dst, dst) // x¹⁶ + dst.Mul(dst, &sum) // x¹⁷ + default: + panic(fmt.Sprintf("unknown operation: %d", inst.Op)) + } } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 9a95d03122..0f5281d0b4 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -731,73 +731,6 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } } -// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the -// result in dst. In practice, dst will be one of the vars but this is not required. -type gateOp func(dst *fr.Element, vars []fr.Element, in []uint16) - -func opAdd(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Add(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Add(dst, &vars[in[i]]) - } -} - -func opMulAcc(dst *fr.Element, vars []fr.Element, in []uint16) { - var prod fr.Element - prod.Mul(&vars[in[1]], &vars[in[2]]) - dst.Add(&vars[in[0]], &prod) -} - -func opNeg(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Neg(&vars[in[0]]) -} - -func opSub(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Sub(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Sub(dst, &vars[in[i]]) - } -} - -func opMul(dst *fr.Element, vars []fr.Element, in []uint16) { - dst.Mul(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Mul(dst, &vars[in[i]]) - } -} - -func opSumExp17(dst *fr.Element, vars []fr.Element, in []uint16) { - // result = (x[0] + x[1] + x[2])^17 - var sum fr.Element - sum.Add(&vars[in[0]], &vars[in[1]]) - sum.Add(&sum, &vars[in[2]]) - - dst.Mul(&sum, &sum) // sum² - dst.Mul(dst, dst) // sum⁴ - dst.Mul(dst, dst) // sum⁸ - dst.Mul(dst, dst) // sum¹⁶ - dst.Mul(dst, &sum) // sum¹⁷ -} - -/* -func (stack *elementStack) Println(a ...frontend.Variable) { - toPrint := make([]any, len(a)) - var x fr.Element - - for i, v := range a { - if _, err := x.SetInterface(v); err != nil { - if s, ok := v.(string); ok { - toPrint[i] = s - continue - } - panic(fmt.Errorf("not numeric or string: %w", err)) - } else { - toPrint[i] = x.String() - } - } - fmt.Println(toPrint...) -}*/ - // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. @@ -834,15 +767,6 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } -var gateExecutors = [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, -} - // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -859,7 +783,45 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + inst := &e.gate.Instructions[i] + dst := &e.vars[i+e.frameSize] + + // Use switch instead of function pointer for better inlining + switch inst.Op { + case gkrtypes.OpAdd: + dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Add(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpMul: + dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Mul(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpSub: + dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Sub(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpNeg: + dst.Neg(&e.vars[inst.Inputs[0]]) + case gkrtypes.OpMulAcc: + var prod fr.Element + prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) + dst.Add(&e.vars[inst.Inputs[0]], &prod) + case gkrtypes.OpSumExp17: + // result = (x[0] + x[1] + x[2])^17 + var sum fr.Element + sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + sum.Add(&sum, &e.vars[inst.Inputs[2]]) + dst.Mul(&sum, &sum) // x² + dst.Mul(dst, dst) // x⁴ + dst.Mul(dst, dst) // x⁸ + dst.Mul(dst, dst) // x¹⁶ + dst.Mul(dst, &sum) // x¹⁷ + default: + panic(fmt.Sprintf("unknown operation: %d", inst.Op)) + } } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/internal/gkr/gkrhints/engine_hints.go b/internal/gkr/gkrhints/engine_hints.go index 443ae4025b..a6fa5c2403 100644 --- a/internal/gkr/gkrhints/engine_hints.go +++ b/internal/gkr/gkrhints/engine_hints.go @@ -193,15 +193,3 @@ func (g gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { res.Exp(&x, big.NewInt(17), g.mod) return &res } - -func (g gateAPI) Println(a ...frontend.Variable) { - strings := make([]string, len(a)) - for i := range a { - if s, ok := a[i].(fmt.Stringer); ok { - strings[i] = s.String() - } else { - bigInt := utils.FromInterface(a[i]) - strings[i] = bigInt.String() - } - } -} diff --git a/internal/gkr/gkrtypes/compiledgate.go b/internal/gkr/gkrtypes/compiledgate.go index f8969f4b5d..bc634d7821 100644 --- a/internal/gkr/gkrtypes/compiledgate.go +++ b/internal/gkr/gkrtypes/compiledgate.go @@ -137,11 +137,6 @@ func (gc *gateCompiler) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) f return gc.addInstruction2Plus(OpMul, i1, i2, in...) } -// Println is a no-op during recording -func (gc *gateCompiler) Println(a ...frontend.Variable) { - // No-op: we don't need to record print statements -} - // SumExp17 records (a + b + c)^17 as a single instruction func (gc *gateCompiler) SumExp17(a, b, c frontend.Variable) frontend.Variable { return gc.addInstruction(OpSumExp17, a, b, c) diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 68a0302b3e..6f5de87318 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -731,73 +731,6 @@ func frToBigInts(dst []*big.Int, src []small_rational.SmallRational) { } } -// A gateOp performs an operation on (vars[in[0]], vars[in[1]], ...) and stores the -// result in dst. In practice, dst will be one of the vars but this is not required. -type gateOp func(dst *small_rational.SmallRational, vars []small_rational.SmallRational, in []uint16) - -func opAdd(dst *small_rational.SmallRational, vars []small_rational.SmallRational, in []uint16) { - dst.Add(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Add(dst, &vars[in[i]]) - } -} - -func opMulAcc(dst *small_rational.SmallRational, vars []small_rational.SmallRational, in []uint16) { - var prod small_rational.SmallRational - prod.Mul(&vars[in[1]], &vars[in[2]]) - dst.Add(&vars[in[0]], &prod) -} - -func opNeg(dst *small_rational.SmallRational, vars []small_rational.SmallRational, in []uint16) { - dst.Neg(&vars[in[0]]) -} - -func opSub(dst *small_rational.SmallRational, vars []small_rational.SmallRational, in []uint16) { - dst.Sub(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Sub(dst, &vars[in[i]]) - } -} - -func opMul(dst *small_rational.SmallRational, vars []small_rational.SmallRational, in []uint16) { - dst.Mul(&vars[in[0]], &vars[in[1]]) - for i := 2; i < len(in); i++ { - dst.Mul(dst, &vars[in[i]]) - } -} - -func opSumExp17(dst *small_rational.SmallRational, vars []small_rational.SmallRational, in []uint16) { - // result = (x[0] + x[1] + x[2])^17 - var sum small_rational.SmallRational - sum.Add(&vars[in[0]], &vars[in[1]]) - sum.Add(&sum, &vars[in[2]]) - - dst.Mul(&sum, &sum) // sum² - dst.Mul(dst, dst) // sum⁴ - dst.Mul(dst, dst) // sum⁸ - dst.Mul(dst, dst) // sum¹⁶ - dst.Mul(dst, &sum) // sum¹⁷ -} - -/* -func (stack *elementStack) Println(a ...frontend.Variable) { - toPrint := make([]any, len(a)) - var x small_rational.SmallRational - - for i, v := range a { - if _, err := x.SetInterface(v); err != nil { - if s, ok := v.(string); ok { - toPrint[i] = s - continue - } - panic(fmt.Errorf("not numeric or string: %w", err)) - } else { - toPrint[i] = x.String() - } - } - fmt.Println(toPrint...) -}*/ - // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. @@ -834,15 +767,6 @@ func (e *gateEvaluator) pushInput(input *small_rational.SmallRational) { e.frameSize++ } -var gateExecutors = [...]gateOp{ - gkrtypes.OpAdd: opAdd, - gkrtypes.OpSub: opSub, - gkrtypes.OpMul: opMul, - gkrtypes.OpNeg: opNeg, - gkrtypes.OpMulAcc: opMulAcc, - gkrtypes.OpSumExp17: opSumExp17, -} - // evaluate adds top to the top of the stack, executes the gate on it and returns the result. // The stack is automatically reset after evaluation, // making the evaluator ready for the next evaluation. @@ -859,7 +783,45 @@ func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rat // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { - gateExecutors[e.gate.Instructions[i].Op](&e.vars[i+e.frameSize], e.vars, e.gate.Instructions[i].Inputs) + inst := &e.gate.Instructions[i] + dst := &e.vars[i+e.frameSize] + + // Use switch instead of function pointer for better inlining + switch inst.Op { + case gkrtypes.OpAdd: + dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Add(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpMul: + dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Mul(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpSub: + dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + for j := 2; j < len(inst.Inputs); j++ { + dst.Sub(dst, &e.vars[inst.Inputs[j]]) + } + case gkrtypes.OpNeg: + dst.Neg(&e.vars[inst.Inputs[0]]) + case gkrtypes.OpMulAcc: + var prod small_rational.SmallRational + prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) + dst.Add(&e.vars[inst.Inputs[0]], &prod) + case gkrtypes.OpSumExp17: + // result = (x[0] + x[1] + x[2])^17 + var sum small_rational.SmallRational + sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) + sum.Add(&sum, &e.vars[inst.Inputs[2]]) + dst.Mul(&sum, &sum) // x² + dst.Mul(dst, dst) // x⁴ + dst.Mul(dst, dst) // x⁸ + dst.Mul(dst, dst) // x¹⁶ + dst.Mul(dst, &sum) // x¹⁷ + default: + panic(fmt.Sprintf("unknown operation: %d", inst.Op)) + } } res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] diff --git a/std/gkrapi/gkr/types.go b/std/gkrapi/gkr/types.go index 3c03f75df1..f5235055ba 100644 --- a/std/gkrapi/gkr/types.go +++ b/std/gkrapi/gkr/types.go @@ -35,10 +35,6 @@ type GateAPI interface { // Mul returns res = i1 * i2 * ... in Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable - // Println behaves like fmt.Println but accepts frontend.Variable as parameter - // whose value will be resolved at runtime when computed by the solver - Println(a ...frontend.Variable) - SumExp17(a, b, c frontend.Variable) frontend.Variable } From c8b3da3ee69936d9fae3752c1fc76b9c2c2abc0b Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 13 Jan 2026 20:46:59 -0600 Subject: [PATCH 110/251] fix: AI feedback --- .../generator/backend/template/gkr/gkr.go.tmpl | 15 ++++++++------- internal/gkr/bls12-377/gkr.go | 15 ++++++++------- internal/gkr/bls12-381/gkr.go | 15 ++++++++------- internal/gkr/bls24-315/gkr.go | 15 ++++++++------- internal/gkr/bls24-317/gkr.go | 15 ++++++++------- internal/gkr/bn254/gkr.go | 15 ++++++++------- internal/gkr/bw6-633/gkr.go | 15 ++++++++------- internal/gkr/bw6-761/gkr.go | 15 ++++++++------- internal/gkr/small_rational/gkr.go | 15 ++++++++------- 9 files changed, 72 insertions(+), 63 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index c64980442a..c6ad2f13df 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -232,8 +232,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step {{ .ElementType }} - evaluator := c.gateEvaluatorPool.Get() - defer c.gateEvaluatorPool.Put(evaluator) + evaluator := c.gateEvaluatorPool.get() + defer c.gateEvaluatorPool.put(evaluator) res := make([]{{ .ElementType }}, degGJ) @@ -335,7 +335,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []{{ .ElementType }}) [ } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.DumpAll() + c.gateEvaluatorPool.dumpAll() return evaluations } @@ -847,7 +847,7 @@ func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynom return gep } -func (gep *gateEvaluatorPool) Get() *gateEvaluator { +func (gep *gateEvaluatorPool) get() *gateEvaluator { gep.lock.Lock() defer gep.lock.Unlock() @@ -862,7 +862,7 @@ func (gep *gateEvaluatorPool) Get() *gateEvaluator { return &e } -func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { +func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() @@ -873,8 +873,9 @@ func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool -func (gep *gateEvaluatorPool) DumpAll() { +// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. +func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.frPool.Dump(e.vars) } diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 65588f643a..7e6910c1ab 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -238,8 +238,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - evaluator := c.gateEvaluatorPool.Get() - defer c.gateEvaluatorPool.Put(evaluator) + evaluator := c.gateEvaluatorPool.get() + defer c.gateEvaluatorPool.put(evaluator) res := make([]fr.Element, degGJ) @@ -341,7 +341,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.DumpAll() + c.gateEvaluatorPool.dumpAll() return evaluations } @@ -852,7 +852,7 @@ func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynom return gep } -func (gep *gateEvaluatorPool) Get() *gateEvaluator { +func (gep *gateEvaluatorPool) get() *gateEvaluator { gep.lock.Lock() defer gep.lock.Unlock() @@ -867,7 +867,7 @@ func (gep *gateEvaluatorPool) Get() *gateEvaluator { return &e } -func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { +func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() @@ -878,8 +878,9 @@ func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool -func (gep *gateEvaluatorPool) DumpAll() { +// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. +func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.frPool.Dump(e.vars) } diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 15ebe5ccea..5eca9ad96f 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -238,8 +238,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - evaluator := c.gateEvaluatorPool.Get() - defer c.gateEvaluatorPool.Put(evaluator) + evaluator := c.gateEvaluatorPool.get() + defer c.gateEvaluatorPool.put(evaluator) res := make([]fr.Element, degGJ) @@ -341,7 +341,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.DumpAll() + c.gateEvaluatorPool.dumpAll() return evaluations } @@ -852,7 +852,7 @@ func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynom return gep } -func (gep *gateEvaluatorPool) Get() *gateEvaluator { +func (gep *gateEvaluatorPool) get() *gateEvaluator { gep.lock.Lock() defer gep.lock.Unlock() @@ -867,7 +867,7 @@ func (gep *gateEvaluatorPool) Get() *gateEvaluator { return &e } -func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { +func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() @@ -878,8 +878,9 @@ func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool -func (gep *gateEvaluatorPool) DumpAll() { +// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. +func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.frPool.Dump(e.vars) } diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 1156d683e4..be23c51631 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -238,8 +238,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - evaluator := c.gateEvaluatorPool.Get() - defer c.gateEvaluatorPool.Put(evaluator) + evaluator := c.gateEvaluatorPool.get() + defer c.gateEvaluatorPool.put(evaluator) res := make([]fr.Element, degGJ) @@ -341,7 +341,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.DumpAll() + c.gateEvaluatorPool.dumpAll() return evaluations } @@ -852,7 +852,7 @@ func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynom return gep } -func (gep *gateEvaluatorPool) Get() *gateEvaluator { +func (gep *gateEvaluatorPool) get() *gateEvaluator { gep.lock.Lock() defer gep.lock.Unlock() @@ -867,7 +867,7 @@ func (gep *gateEvaluatorPool) Get() *gateEvaluator { return &e } -func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { +func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() @@ -878,8 +878,9 @@ func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool -func (gep *gateEvaluatorPool) DumpAll() { +// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. +func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.frPool.Dump(e.vars) } diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 6473c22f6b..cfaf75b853 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -238,8 +238,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - evaluator := c.gateEvaluatorPool.Get() - defer c.gateEvaluatorPool.Put(evaluator) + evaluator := c.gateEvaluatorPool.get() + defer c.gateEvaluatorPool.put(evaluator) res := make([]fr.Element, degGJ) @@ -341,7 +341,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.DumpAll() + c.gateEvaluatorPool.dumpAll() return evaluations } @@ -852,7 +852,7 @@ func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynom return gep } -func (gep *gateEvaluatorPool) Get() *gateEvaluator { +func (gep *gateEvaluatorPool) get() *gateEvaluator { gep.lock.Lock() defer gep.lock.Unlock() @@ -867,7 +867,7 @@ func (gep *gateEvaluatorPool) Get() *gateEvaluator { return &e } -func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { +func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() @@ -878,8 +878,9 @@ func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool -func (gep *gateEvaluatorPool) DumpAll() { +// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. +func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.frPool.Dump(e.vars) } diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index ce405edc11..24f63f9b7e 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -238,8 +238,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - evaluator := c.gateEvaluatorPool.Get() - defer c.gateEvaluatorPool.Put(evaluator) + evaluator := c.gateEvaluatorPool.get() + defer c.gateEvaluatorPool.put(evaluator) res := make([]fr.Element, degGJ) @@ -341,7 +341,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.DumpAll() + c.gateEvaluatorPool.dumpAll() return evaluations } @@ -852,7 +852,7 @@ func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynom return gep } -func (gep *gateEvaluatorPool) Get() *gateEvaluator { +func (gep *gateEvaluatorPool) get() *gateEvaluator { gep.lock.Lock() defer gep.lock.Unlock() @@ -867,7 +867,7 @@ func (gep *gateEvaluatorPool) Get() *gateEvaluator { return &e } -func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { +func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() @@ -878,8 +878,9 @@ func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool -func (gep *gateEvaluatorPool) DumpAll() { +// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. +func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.frPool.Dump(e.vars) } diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 68a6d8c643..e8feb25771 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -238,8 +238,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - evaluator := c.gateEvaluatorPool.Get() - defer c.gateEvaluatorPool.Put(evaluator) + evaluator := c.gateEvaluatorPool.get() + defer c.gateEvaluatorPool.put(evaluator) res := make([]fr.Element, degGJ) @@ -341,7 +341,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.DumpAll() + c.gateEvaluatorPool.dumpAll() return evaluations } @@ -852,7 +852,7 @@ func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynom return gep } -func (gep *gateEvaluatorPool) Get() *gateEvaluator { +func (gep *gateEvaluatorPool) get() *gateEvaluator { gep.lock.Lock() defer gep.lock.Unlock() @@ -867,7 +867,7 @@ func (gep *gateEvaluatorPool) Get() *gateEvaluator { return &e } -func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { +func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() @@ -878,8 +878,9 @@ func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool -func (gep *gateEvaluatorPool) DumpAll() { +// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. +func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.frPool.Dump(e.vars) } diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 0f5281d0b4..7f0fd304ef 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -238,8 +238,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - evaluator := c.gateEvaluatorPool.Get() - defer c.gateEvaluatorPool.Put(evaluator) + evaluator := c.gateEvaluatorPool.get() + defer c.gateEvaluatorPool.put(evaluator) res := make([]fr.Element, degGJ) @@ -341,7 +341,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.DumpAll() + c.gateEvaluatorPool.dumpAll() return evaluations } @@ -852,7 +852,7 @@ func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynom return gep } -func (gep *gateEvaluatorPool) Get() *gateEvaluator { +func (gep *gateEvaluatorPool) get() *gateEvaluator { gep.lock.Lock() defer gep.lock.Unlock() @@ -867,7 +867,7 @@ func (gep *gateEvaluatorPool) Get() *gateEvaluator { return &e } -func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { +func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() @@ -878,8 +878,9 @@ func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool -func (gep *gateEvaluatorPool) DumpAll() { +// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. +func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.frPool.Dump(e.vars) } diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 6f5de87318..9c1ab24f73 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -238,8 +238,8 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { computeAll := func(start, end int) { // compute method to allow parallelization across instances var step small_rational.SmallRational - evaluator := c.gateEvaluatorPool.Get() - defer c.gateEvaluatorPool.Put(evaluator) + evaluator := c.gateEvaluatorPool.get() + defer c.gateEvaluatorPool.put(evaluator) res := make([]small_rational.SmallRational, degGJ) @@ -341,7 +341,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []small_rational.SmallR } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.DumpAll() + c.gateEvaluatorPool.dumpAll() return evaluations } @@ -852,7 +852,7 @@ func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynom return gep } -func (gep *gateEvaluatorPool) Get() *gateEvaluator { +func (gep *gateEvaluatorPool) get() *gateEvaluator { gep.lock.Lock() defer gep.lock.Unlock() @@ -867,7 +867,7 @@ func (gep *gateEvaluatorPool) Get() *gateEvaluator { return &e } -func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { +func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() @@ -878,8 +878,9 @@ func (gep *gateEvaluatorPool) Put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// DumpAll dumps all evaluator vars slices back to the polynomial pool and clears the pool -func (gep *gateEvaluatorPool) DumpAll() { +// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. +func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.frPool.Dump(e.vars) } From 1651b4f4ff8cf15798197ec519605f87cb229612 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 13 Jan 2026 21:00:06 -0600 Subject: [PATCH 111/251] minor cleanups --- .../backend/template/gkr/gkr.go.tmpl | 11 ++--- .../backend/template/gkr/solver_hints.go.tmpl | 3 +- internal/gkr/bls12-377/gkr.go | 25 +++++----- internal/gkr/bls12-377/solver_hints.go | 3 +- internal/gkr/bls12-381/gkr.go | 25 +++++----- internal/gkr/bls12-381/solver_hints.go | 3 +- internal/gkr/bls24-315/gkr.go | 25 +++++----- internal/gkr/bls24-315/solver_hints.go | 3 +- internal/gkr/bls24-317/gkr.go | 25 +++++----- internal/gkr/bls24-317/solver_hints.go | 3 +- internal/gkr/bn254/gkr.go | 25 +++++----- internal/gkr/bn254/solver_hints.go | 3 +- internal/gkr/bw6-633/gkr.go | 25 +++++----- internal/gkr/bw6-633/solver_hints.go | 3 +- internal/gkr/bw6-761/gkr.go | 25 +++++----- internal/gkr/bw6-761/solver_hints.go | 3 +- internal/gkr/gkrtypes/compiledgate_test.go | 49 +++++-------------- internal/gkr/small_rational/gkr.go | 25 +++++----- 18 files changed, 122 insertions(+), 162 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index c6ad2f13df..ea56241ea1 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -834,14 +834,14 @@ type gateEvaluatorPool struct { nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} - frPool *polynomial.Pool // shared element pool + elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, - frPool: frPool, + elementPool: elementPool, available: make(map[*gateEvaluator]struct{}), } return gep @@ -856,8 +856,7 @@ func (gep *gateEvaluatorPool) get() *gateEvaluator { return e } - // No available evaluator, create a new one - e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + e := newGateEvaluator(gep.gate, gep.nbIn, gep.elementPool) return &e } @@ -877,6 +876,6 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { - gep.frPool.Dump(e.vars) + gep.elementPool.Dump(e.vars) } } \ No newline at end of file diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index a02c88f0e2..6c174cc316 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -196,8 +196,7 @@ func SolveHint(data []SolvingData) hint.Hint { } // Evaluate the gate - result := evaluator.evaluate() - data.assignment[wI][instanceI].Set(result) + data.assignment[wI][instanceI].Set(evaluator.evaluate()) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 7e6910c1ab..502d939897 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -835,19 +835,19 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - frPool *polynomial.Pool // shared element pool + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - frPool: frPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -861,8 +861,7 @@ func (gep *gateEvaluatorPool) get() *gateEvaluator { return e } - // No available evaluator, create a new one - e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + e := newGateEvaluator(gep.gate, gep.nbIn, gep.elementPool) return &e } @@ -882,6 +881,6 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { - gep.frPool.Dump(e.vars) + gep.elementPool.Dump(e.vars) } } diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index a455b09111..5a491679f3 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -204,8 +204,7 @@ func SolveHint(data []SolvingData) hint.Hint { } // Evaluate the gate - result := evaluator.evaluate() - data.assignment[wI][instanceI].Set(result) + data.assignment[wI][instanceI].Set(evaluator.evaluate()) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 5eca9ad96f..1ed9304836 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -835,19 +835,19 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - frPool *polynomial.Pool // shared element pool + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - frPool: frPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -861,8 +861,7 @@ func (gep *gateEvaluatorPool) get() *gateEvaluator { return e } - // No available evaluator, create a new one - e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + e := newGateEvaluator(gep.gate, gep.nbIn, gep.elementPool) return &e } @@ -882,6 +881,6 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { - gep.frPool.Dump(e.vars) + gep.elementPool.Dump(e.vars) } } diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index dba5b5e71b..c0b263f110 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -204,8 +204,7 @@ func SolveHint(data []SolvingData) hint.Hint { } // Evaluate the gate - result := evaluator.evaluate() - data.assignment[wI][instanceI].Set(result) + data.assignment[wI][instanceI].Set(evaluator.evaluate()) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index be23c51631..09d57e0b9e 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -835,19 +835,19 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - frPool *polynomial.Pool // shared element pool + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - frPool: frPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -861,8 +861,7 @@ func (gep *gateEvaluatorPool) get() *gateEvaluator { return e } - // No available evaluator, create a new one - e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + e := newGateEvaluator(gep.gate, gep.nbIn, gep.elementPool) return &e } @@ -882,6 +881,6 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { - gep.frPool.Dump(e.vars) + gep.elementPool.Dump(e.vars) } } diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index efab765920..ca2490a77d 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -204,8 +204,7 @@ func SolveHint(data []SolvingData) hint.Hint { } // Evaluate the gate - result := evaluator.evaluate() - data.assignment[wI][instanceI].Set(result) + data.assignment[wI][instanceI].Set(evaluator.evaluate()) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index cfaf75b853..83dbae428b 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -835,19 +835,19 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - frPool *polynomial.Pool // shared element pool + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - frPool: frPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -861,8 +861,7 @@ func (gep *gateEvaluatorPool) get() *gateEvaluator { return e } - // No available evaluator, create a new one - e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + e := newGateEvaluator(gep.gate, gep.nbIn, gep.elementPool) return &e } @@ -882,6 +881,6 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { - gep.frPool.Dump(e.vars) + gep.elementPool.Dump(e.vars) } } diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index bcd1741665..8763d0456b 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -204,8 +204,7 @@ func SolveHint(data []SolvingData) hint.Hint { } // Evaluate the gate - result := evaluator.evaluate() - data.assignment[wI][instanceI].Set(result) + data.assignment[wI][instanceI].Set(evaluator.evaluate()) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 24f63f9b7e..1cd5868b16 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -835,19 +835,19 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - frPool *polynomial.Pool // shared element pool + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - frPool: frPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -861,8 +861,7 @@ func (gep *gateEvaluatorPool) get() *gateEvaluator { return e } - // No available evaluator, create a new one - e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + e := newGateEvaluator(gep.gate, gep.nbIn, gep.elementPool) return &e } @@ -882,6 +881,6 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { - gep.frPool.Dump(e.vars) + gep.elementPool.Dump(e.vars) } } diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index c0befba7fe..a18a06a829 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -204,8 +204,7 @@ func SolveHint(data []SolvingData) hint.Hint { } // Evaluate the gate - result := evaluator.evaluate() - data.assignment[wI][instanceI].Set(result) + data.assignment[wI][instanceI].Set(evaluator.evaluate()) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index e8feb25771..7b8f785dbe 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -835,19 +835,19 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - frPool *polynomial.Pool // shared element pool + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - frPool: frPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -861,8 +861,7 @@ func (gep *gateEvaluatorPool) get() *gateEvaluator { return e } - // No available evaluator, create a new one - e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + e := newGateEvaluator(gep.gate, gep.nbIn, gep.elementPool) return &e } @@ -882,6 +881,6 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { - gep.frPool.Dump(e.vars) + gep.elementPool.Dump(e.vars) } } diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index 04c247ce2b..81c2c8bd0d 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -204,8 +204,7 @@ func SolveHint(data []SolvingData) hint.Hint { } // Evaluate the gate - result := evaluator.evaluate() - data.assignment[wI][instanceI].Set(result) + data.assignment[wI][instanceI].Set(evaluator.evaluate()) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 7f0fd304ef..0653e21ec5 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -835,19 +835,19 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - frPool *polynomial.Pool // shared element pool + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - frPool: frPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -861,8 +861,7 @@ func (gep *gateEvaluatorPool) get() *gateEvaluator { return e } - // No available evaluator, create a new one - e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + e := newGateEvaluator(gep.gate, gep.nbIn, gep.elementPool) return &e } @@ -882,6 +881,6 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { - gep.frPool.Dump(e.vars) + gep.elementPool.Dump(e.vars) } } diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index 5d99a2033c..a41ed9cbfc 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -204,8 +204,7 @@ func SolveHint(data []SolvingData) hint.Hint { } // Evaluate the gate - result := evaluator.evaluate() - data.assignment[wI][instanceI].Set(result) + data.assignment[wI][instanceI].Set(evaluator.evaluate()) } if w.IsOutput() { // write to provided output. diff --git a/internal/gkr/gkrtypes/compiledgate_test.go b/internal/gkr/gkrtypes/compiledgate_test.go index c604a02e96..01a32a3ff2 100644 --- a/internal/gkr/gkrtypes/compiledgate_test.go +++ b/internal/gkr/gkrtypes/compiledgate_test.go @@ -6,6 +6,7 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -23,24 +24,16 @@ func TestCompiledGateWithConstants(t *testing.T) { require.NoError(t, err) // Verify the gate has constants - if len(compiled.Constants) == 0 { - t.Fatal("Expected compiled gate to have constants") - } + assert.NotEmpty(t, compiled.Constants, "Expected compiled gate to have constants") // Verify the constant value - if compiled.Constants[0].Cmp(big.NewInt(5)) != 0 { - t.Errorf("Expected constant to be 5, got %s", compiled.Constants[0].String()) - } + assert.Equal(t, 0, compiled.Constants[0].Cmp(big.NewInt(5)), "Expected constant to be 5, got %s", compiled.Constants[0].String()) // Verify instructions reference the constant - if len(compiled.Instructions) == 0 { - t.Fatal("Expected compiled gate to have instructions") - } + assert.NotEmpty(t, compiled.Instructions, "Expected compiled gate to have instructions") inst := compiled.Instructions[0] - if inst.Op != OpAdd { - t.Errorf("Expected OpAdd, got %v", inst.Op) - } + assert.Equal(t, OpAdd, inst.Op, "Expected OpAdd, got %v", inst.Op) // Verify index layout: constants at [0, nbConsts), inputs at [nbConsts, nbConsts+nbInputs) nbConsts := len(compiled.Constants) @@ -49,24 +42,16 @@ func TestCompiledGateWithConstants(t *testing.T) { for _, idx := range inst.Inputs { if idx < uint16(nbConsts) { hasConstant = true - if idx != 0 { - t.Errorf("Expected constant index 0, got %d", idx) - } + assert.Equal(t, uint16(0), idx, "Expected constant index 0, got %d", idx) } else if idx >= uint16(nbConsts) && idx < uint16(nbConsts+nbIn) { hasInput = true inputIdx := idx - uint16(nbConsts) - if inputIdx != 0 { - t.Errorf("Expected input index 0 (remapped to %d), got %d", nbConsts, idx) - } + assert.Equal(t, uint16(0), inputIdx, "Expected input index 0 (remapped to %d), got %d", nbConsts, idx) } } - if !hasConstant { - t.Error("Expected instruction to reference a constant") - } - if !hasInput { - t.Error("Expected instruction to reference an input") - } + assert.True(t, hasConstant, "Expected instruction to reference a constant") + assert.True(t, hasInput, "Expected instruction to reference an input") } // TestCompiledGateWithMultipleConstants tests gates with multiple different constants @@ -85,9 +70,7 @@ func TestCompiledGateWithMultipleConstants(t *testing.T) { require.NoError(t, err) // Verify we have two constants - if len(compiled.Constants) != 2 { - t.Errorf("Expected 2 constants, got %d", len(compiled.Constants)) - } + assert.Equal(t, 2, len(compiled.Constants), "Expected 2 constants, got %d", len(compiled.Constants)) // Verify the constants are 3 and 7 constantValues := make(map[int64]bool) @@ -95,9 +78,7 @@ func TestCompiledGateWithMultipleConstants(t *testing.T) { constantValues[c.Int64()] = true } - if !constantValues[3] || !constantValues[7] { - t.Error("Expected constants to be 3 and 7") - } + assert.True(t, constantValues[3] && constantValues[7], "Expected constants to be 3 and 7") t.Logf("Successfully compiled gate with %d constants: %v", len(compiled.Constants), compiled.Constants) @@ -120,13 +101,9 @@ func TestConstantDeduplication(t *testing.T) { require.NoError(t, err) // Verify only one constant is stored (deduplication) - if len(compiled.Constants) != 1 { - t.Errorf("Expected 1 deduplicated constant, got %d", len(compiled.Constants)) - } + assert.Equal(t, 1, len(compiled.Constants), "Expected 1 deduplicated constant, got %d", len(compiled.Constants)) - if compiled.Constants[0].Cmp(big.NewInt(5)) != 0 { - t.Errorf("Expected constant to be 5, got %s", compiled.Constants[0].String()) - } + assert.Equal(t, 0, compiled.Constants[0].Cmp(big.NewInt(5)), "Expected constant to be 5, got %s", compiled.Constants[0].String()) t.Logf("Successfully deduplicated constants: %d unique constant(s)", len(compiled.Constants)) diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 9c1ab24f73..fa2616a1fa 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -835,19 +835,19 @@ func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rat // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - frPool *polynomial.Pool // shared element pool + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, frPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - frPool: frPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -861,8 +861,7 @@ func (gep *gateEvaluatorPool) get() *gateEvaluator { return e } - // No available evaluator, create a new one - e := newGateEvaluator(gep.gate, gep.nbIn, gep.frPool) + e := newGateEvaluator(gep.gate, gep.nbIn, gep.elementPool) return &e } @@ -882,6 +881,6 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { - gep.frPool.Dump(e.vars) + gep.elementPool.Dump(e.vars) } } From a2fa8b3945858741bc8e93b77946bbb29465d329 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 13 Jan 2026 21:10:37 -0600 Subject: [PATCH 112/251] fix: AI suggestions --- internal/generator/backend/template/gkr/gkr.go.tmpl | 4 +++- internal/gkr/bls12-377/gkr.go | 4 +++- internal/gkr/bls12-381/gkr.go | 4 +++- internal/gkr/bls24-315/gkr.go | 4 +++- internal/gkr/bls24-317/gkr.go | 4 +++- internal/gkr/bn254/gkr.go | 4 +++- internal/gkr/bw6-633/gkr.go | 4 +++- internal/gkr/bw6-761/gkr.go | 4 +++- internal/gkr/small_rational/gkr.go | 4 +++- 9 files changed, 27 insertions(+), 9 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index ea56241ea1..753bac6492 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -872,9 +872,11 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// dumpAll dumps all available evaluator vars slices back to the polynomial pool. It is not to be used after that. // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { + gep.lock.Lock() + defer gep.lock.Unlock() for e := range gep.available { gep.elementPool.Dump(e.vars) } diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 502d939897..5e6532d3cb 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -877,9 +877,11 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// dumpAll dumps all available evaluator vars slices back to the polynomial pool. It is not to be used after that. // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { + gep.lock.Lock() + defer gep.lock.Unlock() for e := range gep.available { gep.elementPool.Dump(e.vars) } diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 1ed9304836..d6134b9b95 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -877,9 +877,11 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// dumpAll dumps all available evaluator vars slices back to the polynomial pool. It is not to be used after that. // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { + gep.lock.Lock() + defer gep.lock.Unlock() for e := range gep.available { gep.elementPool.Dump(e.vars) } diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 09d57e0b9e..2e5dbcd28b 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -877,9 +877,11 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// dumpAll dumps all available evaluator vars slices back to the polynomial pool. It is not to be used after that. // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { + gep.lock.Lock() + defer gep.lock.Unlock() for e := range gep.available { gep.elementPool.Dump(e.vars) } diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 83dbae428b..6baa34c17e 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -877,9 +877,11 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// dumpAll dumps all available evaluator vars slices back to the polynomial pool. It is not to be used after that. // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { + gep.lock.Lock() + defer gep.lock.Unlock() for e := range gep.available { gep.elementPool.Dump(e.vars) } diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 1cd5868b16..4efba767a9 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -877,9 +877,11 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// dumpAll dumps all available evaluator vars slices back to the polynomial pool. It is not to be used after that. // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { + gep.lock.Lock() + defer gep.lock.Unlock() for e := range gep.available { gep.elementPool.Dump(e.vars) } diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 7b8f785dbe..0f1fd6a5b4 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -877,9 +877,11 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// dumpAll dumps all available evaluator vars slices back to the polynomial pool. It is not to be used after that. // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { + gep.lock.Lock() + defer gep.lock.Unlock() for e := range gep.available { gep.elementPool.Dump(e.vars) } diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 0653e21ec5..9a58ccff40 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -877,9 +877,11 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// dumpAll dumps all available evaluator vars slices back to the polynomial pool. It is not to be used after that. // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { + gep.lock.Lock() + defer gep.lock.Unlock() for e := range gep.available { gep.elementPool.Dump(e.vars) } diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index fa2616a1fa..71a4b38b01 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -877,9 +877,11 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.available[e] = struct{}{} } -// dumpAll dumps all available evaluator vars slices back to the polynomial pool and clears the pool. +// dumpAll dumps all available evaluator vars slices back to the polynomial pool. It is not to be used after that. // NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. func (gep *gateEvaluatorPool) dumpAll() { + gep.lock.Lock() + defer gep.lock.Unlock() for e := range gep.available { gep.elementPool.Dump(e.vars) } From 68570668a650eb0fa47a0c6111c00dae9de4b8c8 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Fri, 16 Jan 2026 16:06:07 -0600 Subject: [PATCH 113/251] bench: merkle tree --- std/permutation/gkr-mimc/gkr-mimc_test.go | 24 +++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/std/permutation/gkr-mimc/gkr-mimc_test.go b/std/permutation/gkr-mimc/gkr-mimc_test.go index 93143b1279..e236540996 100644 --- a/std/permutation/gkr-mimc/gkr-mimc_test.go +++ b/std/permutation/gkr-mimc/gkr-mimc_test.go @@ -11,11 +11,11 @@ import ( "github.com/stretchr/testify/require" ) -type hashTreeCircuit struct { +type merkleTreeCircuit struct { Leaves []frontend.Variable } -func (c hashTreeCircuit) Define(api frontend.API) error { +func (c merkleTreeCircuit) Define(api frontend.API) error { if len(c.Leaves) == 0 { return errors.New("no hashing to do") } @@ -44,27 +44,31 @@ func (c hashTreeCircuit) Define(api frontend.API) error { } func BenchmarkGkrPermutations(b *testing.B) { - circuit, assignment := hashTreeCircuits(50000) + circuit, assignment := merkleTreeCircuits(50000) cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &circuit) require.NoError(b, err) - witness, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField()) - require.NoError(b, err) + b.ResetTimer() - _, err = cs.Solve(witness) - require.NoError(b, err) + for b.Loop() { + witness, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField()) + require.NoError(b, err) + + _, err = cs.Solve(witness) + require.NoError(b, err) + } } -func hashTreeCircuits(n int) (circuit, assignment hashTreeCircuit) { +func merkleTreeCircuits(n int) (circuit, assignment merkleTreeCircuit) { leaves := make([]frontend.Variable, n) for i := range n { leaves[i] = i } - return hashTreeCircuit{ + return merkleTreeCircuit{ Leaves: make([]frontend.Variable, len(leaves)), - }, hashTreeCircuit{ + }, merkleTreeCircuit{ Leaves: leaves, } } From c6de241b975adbb4e6eac1b4ae7c7c17f467c8eb Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 20 Jan 2026 18:19:28 -0600 Subject: [PATCH 114/251] perf: universal gate evaluators --- internal/gkr/bls12-377/gate_testing.go | 10 +- internal/gkr/bls12-377/gkr.go | 151 +++++++++++++------------ internal/gkr/bls12-377/gkr_test.go | 29 ----- internal/gkr/bls12-377/solver_hints.go | 33 +----- internal/gkr/gkrtypes/types.go | 15 +++ 5 files changed, 105 insertions(+), 133 deletions(-) diff --git a/internal/gkr/bls12-377/gate_testing.go b/internal/gkr/bls12-377/gate_testing.go index 09ee174e09..962e97a1b5 100644 --- a/internal/gkr/bls12-377/gate_testing.go +++ b/internal/gkr/bls12-377/gate_testing.go @@ -24,8 +24,12 @@ type GateTester struct { } func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { + // Compute stack size for this gate + stackSize := g.NbConstants() + nbIn + len(g.Instructions) + evaluator := newGateEvaluator(stackSize) + evaluator.setGate(g) return &GateTester{ - evaluator: newGateEvaluator(g, nbIn), + evaluator: evaluator, nbIn: nbIn, } } @@ -156,7 +160,9 @@ func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(g, t.nbIn) + stackSize := g.NbConstants() + t.nbIn + len(g.Instructions) + gEval := newGateEvaluator(stackSize) + gEval.setGate(g) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 5e6532d3cb..a81fefcb65 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -98,7 +98,8 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) + evaluator := e.manager.gateEvaluatorPool.get() + evaluator.setGate(wire.Gate.Compiled()) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -126,8 +127,6 @@ type eqTimesGateEvalSumcheckClaims struct { input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) - - gateEvaluatorPool *gateEvaluatorPool } func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { @@ -235,11 +234,13 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex + gate := wire.Gate.Compiled() computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - evaluator := c.gateEvaluatorPool.get() - defer c.gateEvaluatorPool.put(evaluator) + evaluator := c.manager.gateEvaluatorPool.get() + defer c.manager.gateEvaluatorPool.put(evaluator) + evaluator.setGate(gate) res := make([]fr.Element, degGJ) @@ -341,25 +342,26 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.dumpAll() return evaluations } type claimsManager struct { - claims []*eqTimesGateEvalSumcheckLazyClaims - assignment WireAssignment - memPool *polynomial.Pool - workers *utils.WorkerPool - wires gkrtypes.Wires + claims []*eqTimesGateEvalSumcheckLazyClaims + assignment WireAssignment + memPool *polynomial.Pool + workers *utils.WorkerPool + wires gkrtypes.Wires + gateEvaluatorPool *gateEvaluatorPool } -func newClaimsManager(wires []*gkrtypes.Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(wires gkrtypes.Wires, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) manager.memPool = o.pool manager.workers = o.workers manager.wires = wires + manager.gateEvaluatorPool = o.gateEvaluatorPool for i, wire := range wires { @@ -404,8 +406,6 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) - return res } @@ -415,12 +415,13 @@ func (m *claimsManager) deleteClaim(wire int) { } type settings struct { - pool *polynomial.Pool - sorted []*gkrtypes.Wire - transcript *fiatshamir.Transcript - transcriptPrefix string - nbVars int - workers *utils.WorkerPool + pool *polynomial.Pool + sorted []*gkrtypes.Wire + transcript *fiatshamir.Transcript + transcriptPrefix string + nbVars int + workers *utils.WorkerPool + gateEvaluatorPool *gateEvaluatorPool } type Option func(*settings) @@ -431,6 +432,12 @@ func WithPool(pool *polynomial.Pool) Option { } } +func WithGateEvaluatorPool(pool *gateEvaluatorPool) Option { + return func(options *settings) { + options.gateEvaluatorPool = pool + } +} + func WithSortedCircuit(sorted []*gkrtypes.Wire) Option { return func(options *settings) { options.sorted = sorted @@ -461,6 +468,10 @@ func setup(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fia o.pool = &pool } + if o.gateEvaluatorPool == nil { + o.gateEvaluatorPool = newGateEvaluatorPool(c.MaxStackSize()) + } + if o.workers == nil { o.workers = utils.NewWorkerPool() } @@ -666,24 +677,34 @@ func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcri func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() - evaluators := make([]gateEvaluator, len(wires)) + + // Compute maximum stack size needed for any gate evaluator + maxStackSize := 0 + for _, wire := range wires { + if !wire.IsInput() { + gate := wire.Gate.Compiled() + nbIn := len(wire.Inputs) + stackSize := gate.NbConstants() + nbIn + len(gate.Instructions) + maxStackSize = max(maxStackSize, stackSize) + } + } + + evaluator := newGateEvaluator(maxStackSize) for i := range wires { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) - } } for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { + evaluator.setGate(w.Gate.Compiled()) for _, in := range w.Inputs { - evaluators[wI].pushInput(&a[in][i]) + evaluator.pushInput(&a[in][i]) } - a[wI][i].Set(evaluators[wI].evaluate()) + a[wI][i].Set(evaluator.evaluate()) } } } @@ -732,32 +753,31 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. -// It manages the stack internally and handles input buffering, making it easy to -// evaluate the same gate multiple times with different inputs. +// It is a universal evaluator initialized with the maximum stack size needed across all gates +// in the circuit, allowing it to evaluate any gate without reallocation. type gateEvaluator struct { - gate *gkrtypes.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs - nbIn int // number of inputs expected + gate *gkrtypes.CompiledGate } -// newGateEvaluator creates an evaluator for the given compiled gate. -// The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { - e := gateEvaluator{ - gate: gate, - nbIn: nbIn, - frameSize: gate.NbConstants(), - } - if len(elementPool) > 0 { - e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) - } else { - e.vars = make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)) +// newGateEvaluator creates a universal gate evaluator with the given maximum stack size. +// The evaluator can handle any gate whose stack size is at most maxStackSize. +func newGateEvaluator(maxStackSize int) gateEvaluator { + return gateEvaluator{ + frameSize: 0, + vars: make([]fr.Element, maxStackSize), } +} + +// setGate prepares the evaluator for evaluating the given gate by loading its constants. +// This must be called before evaluating a new gate type. +func (e *gateEvaluator) setGate(gate *gkrtypes.CompiledGate) { for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } - return e + e.frameSize = gate.NbConstants() + e.gate = gate } // pushInput adds an input to the evaluator's input buffer. @@ -767,19 +787,15 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } -// evaluate adds top to the top of the stack, executes the gate on it and returns the result. -// The stack is automatically reset after evaluation, -// making the evaluator ready for the next evaluation. +// evaluate executes the gate on the current inputs and returns the result. +// The gate parameter specifies which gate to evaluate, and nbIn is the expected number of inputs. +// The stack is automatically reset after evaluation, making the evaluator ready for the next evaluation. // NB! The result is short-lived. It will be overwritten the next time evaluate is called. func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { for i := range top { e.pushInput(&top[i]) } - if e.frameSize != e.nbIn+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) - } - // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { @@ -832,22 +848,19 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { return res } -// gateEvaluatorPool manages a pool of gate evaluators for a specific gate type -// All evaluators share the same underlying polynomial.Pool for element slices +// gateEvaluatorPool manages a pool of universal gate evaluators. +// All evaluators are initialized with the same maximum stack size and can handle any gate. +// All evaluators share the same underlying polynomial.Pool for element slices. type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - elementPool *polynomial.Pool + maxStackSize int + lock sync.Mutex + available map[*gateEvaluator]struct{} } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(maxStackSize int) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - elementPool: elementPool, - available: make(map[*gateEvaluator]struct{}), + maxStackSize: maxStackSize, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -861,7 +874,7 @@ func (gep *gateEvaluatorPool) get() *gateEvaluator { return e } - e := newGateEvaluator(gep.gate, gep.nbIn, gep.elementPool) + e := newGateEvaluator(gep.maxStackSize) return &e } @@ -871,18 +884,8 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { defer gep.lock.Unlock() // Reset evaluator state (but keep the vars slice for reuse) - e.frameSize = gep.gate.NbConstants() + e.frameSize = 0 // Return evaluator to pool (it keeps its vars slice from polynomial pool) gep.available[e] = struct{}{} } - -// dumpAll dumps all available evaluator vars slices back to the polynomial pool. It is not to be used after that. -// NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. -func (gep *gateEvaluatorPool) dumpAll() { - gep.lock.Lock() - defer gep.lock.Unlock() - for e := range gep.available { - gep.elementPool.Dump(e.vars) - } -} diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index ab49d127b7..c70feb32d1 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -19,7 +19,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/mimc" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - gcUtils "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" @@ -109,34 +108,6 @@ func TestMimc(t *testing.T) { test(t, mimcCircuit(93)) } -func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := gkrtypes.Circuit{gkrtypes.Wire{ - Gate: gkrtypes.Identity(), - NbUniqueOutputs: 2, - }} - - assignment := WireAssignment{[]fr.Element{two, three}} - var o settings - pool := polynomial.NewPool(256, 1<<11) - workers := gcUtils.NewWorkerPool() - o.pool = &pool - o.workers = workers - - claimsManagerGen := func() *claimsManager { - manager := newClaimsManager(utils.References(circuit), assignment, o) - manager.add(0, []fr.Element{three}, five) - manager.add(0, []fr.Element{four}, six) - return &manager - } - - transcriptGen := newMessageCounterGenerator(4, 1) - - proof, err := sumcheckProve(claimsManagerGen().getClaim(0), fiatshamir.WithHash(transcriptGen(), nil)) - assert.NoError(t, err) - err = sumcheckVerify(claimsManagerGen().getLazyClaim(0), proof, fiatshamir.WithHash(transcriptGen(), nil)) - assert.NoError(t, err) -} - var one, two, three, four, five, six fr.Element func init() { diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 5a491679f3..cc5a7bf602 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -8,7 +8,6 @@ package gkr import ( "fmt" "math/big" - "sync" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" @@ -16,21 +15,16 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) -type circuitEvaluator struct { - evaluators []gateEvaluator // one evaluator per wire -} - type SolvingData struct { assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. circuit gkrtypes.Circuit maxNbIn int // maximum number of inputs for a gate in the circuit nbInstances int hashName string - evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse + evaluatorPool *gateEvaluatorPool // pool of circuit evaluators for concurrent reuse } type newSolvingDataSettings struct { @@ -120,22 +114,7 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } // Initialize circuit evaluator pool - circuit := d[k].circuit - elementPool := polynomial.NewPool(maxGateStackSize) - d[k].evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(circuit)), - } - for wI := range circuit { - w := &circuit[wI] - if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) - } - } - return ce - }, - } + d[k].evaluatorPool = newGateEvaluatorPool(info[k].Circuit.MaxStackSize()) } return d @@ -181,9 +160,7 @@ func SolveHint(data []SolvingData) hint.Hint { outsI := 0 insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - // Get a circuit evaluator from the pool - ce := data.evaluatorPool.Get().(*circuitEvaluator) - defer data.evaluatorPool.Put(ce) + evaluator := data.evaluatorPool.get() // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -196,7 +173,7 @@ func SolveHint(data []SolvingData) hint.Hint { insI++ } else { // Get evaluator for this wire from the circuit evaluator - evaluator := &ce.evaluators[wI] + evaluator.setGate(w.Gate.Compiled()) // Push gate inputs for _, inWI := range w.Inputs { @@ -252,7 +229,7 @@ func ProveHint(data []SolvingData) hint.Hint { hsh := hash.NewHash(hashName + "_BLS12_377") - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) + proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...), WithGateEvaluatorPool(data.evaluatorPool)) if err != nil { return err } diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 951d9a5654..26f3b126a2 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -179,6 +179,21 @@ func (c Circuit) maxGateDegree() int { return res } +// MaxStackSize returns the maximum stack size needed by any gate evaluator in the circuit. +// This is used to initialize universal gate evaluators that can handle any gate in the circuit. +func (c Circuit) MaxStackSize() int { + maxSize := 0 + for i := range c { + if !c[i].IsInput() { + gate := c[i].Gate.Compiled() + nbIn := len(c[i].Inputs) + stackSize := gate.NbConstants() + nbIn + len(gate.Instructions) + maxSize = max(maxSize, stackSize) + } + } + return maxSize +} + // MemoryRequirements returns an increasing vector of memory allocation sizes required for proving a GKR statement func (c Circuit) MemoryRequirements(nbInstances int) []int { res := []int{256, nbInstances, nbInstances * (c.maxGateDegree() + 1)} From b2e17d09ca217ba3474bb0ce4335eab91e361c33 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 21 Jan 2026 00:31:20 +0000 Subject: [PATCH 115/251] bench: modernize the gkr mimc benchmark --- std/hash/mimc/gkr-mimc/gkr-mimc_test.go | 57 ++++++------------------- 1 file changed, 12 insertions(+), 45 deletions(-) diff --git a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go index a6c2085833..d9ff537690 100644 --- a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go +++ b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go @@ -3,14 +3,10 @@ package gkr_mimc import ( "errors" "fmt" - "os" - "path/filepath" "slices" "testing" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend/plonk" - "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/std/hash/mimc" @@ -83,11 +79,11 @@ func TestGkrMiMCCompiles(t *testing.T) { fmt.Println(cs.GetNbConstraints(), "constraints") } -type hashTreeCircuit struct { +type merkleTreeCircuit struct { Leaves []frontend.Variable } -func (c hashTreeCircuit) Define(api frontend.API) error { +func (c merkleTreeCircuit) Define(api frontend.API) error { if len(c.Leaves) == 0 { return errors.New("no hashing to do") } @@ -117,45 +113,13 @@ func (c hashTreeCircuit) Define(api frontend.API) error { return nil } -func loadCs(t require.TestingT, fileTitle string, circuit frontend.Circuit) constraint.ConstraintSystem { - filename := filepath.Join(os.TempDir(), fileTitle) - _, err := os.Stat(filename) - - if os.IsNotExist(err) { - // actually compile - cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, circuit) - require.NoError(t, err) - f, err := os.Create(filename) - require.NoError(t, err) - defer func() { - require.NoError(t, f.Close()) - }() - _, err = cs.WriteTo(f) - require.NoError(t, err) - return cs - } - - f, err := os.Open(filename) - require.NoError(t, err) - defer func() { - require.NoError(t, f.Close()) - }() - - cs := plonk.NewCS(ecc.BLS12_377) - - _, err = cs.ReadFrom(f) - require.NoError(t, err) - - return cs -} - -func BenchmarkHashTree(b *testing.B) { +func BenchmarkGkrMiMC(b *testing.B) { const size = 1 << 15 // about 2 ^ 16 total hashes - circuit := hashTreeCircuit{ + circuit := merkleTreeCircuit{ Leaves: make([]frontend.Variable, size), } - assignment := hashTreeCircuit{ + assignment := merkleTreeCircuit{ Leaves: make([]frontend.Variable, size), } @@ -163,10 +127,13 @@ func BenchmarkHashTree(b *testing.B) { assignment.Leaves[i] = i } - cs := loadCs(b, "gkrmimc_hashtree.cs", &circuit) - - w, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField()) + cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, circuit) require.NoError(b, err) - require.NoError(b, cs.IsSolved(w)) + for b.Loop() { + w, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField()) + require.NoError(b, err) + + require.NoError(b, cs.IsSolved(w)) + } } From 7bd8382427259827693e21e3950bee873249bc58 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 20 Jan 2026 19:16:26 -0600 Subject: [PATCH 116/251] revert: universal gates --- internal/gkr/bls12-377/gate_testing.go | 10 +- internal/gkr/bls12-377/gkr.go | 151 ++++++++++++------------ internal/gkr/bls12-377/gkr_test.go | 29 +++++ internal/gkr/bls12-377/solver_hints.go | 33 +++++- std/hash/mimc/gkr-mimc/gkr-mimc_test.go | 2 + 5 files changed, 135 insertions(+), 90 deletions(-) diff --git a/internal/gkr/bls12-377/gate_testing.go b/internal/gkr/bls12-377/gate_testing.go index 962e97a1b5..09ee174e09 100644 --- a/internal/gkr/bls12-377/gate_testing.go +++ b/internal/gkr/bls12-377/gate_testing.go @@ -24,12 +24,8 @@ type GateTester struct { } func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { - // Compute stack size for this gate - stackSize := g.NbConstants() + nbIn + len(g.Instructions) - evaluator := newGateEvaluator(stackSize) - evaluator.setGate(g) return &GateTester{ - evaluator: evaluator, + evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, } } @@ -160,9 +156,7 @@ func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) - stackSize := g.NbConstants() + t.nbIn + len(g.Instructions) - gEval := newGateEvaluator(stackSize) - gEval.setGate(g) + gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) } diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index a81fefcb65..5e6532d3cb 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -98,8 +98,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := e.manager.gateEvaluatorPool.get() - evaluator.setGate(wire.Gate.Compiled()) + evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -127,6 +126,8 @@ type eqTimesGateEvalSumcheckClaims struct { input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) + + gateEvaluatorPool *gateEvaluatorPool } func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { @@ -234,13 +235,11 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { gJ := make([]fr.Element, degGJ) var mu sync.Mutex - gate := wire.Gate.Compiled() computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element - evaluator := c.manager.gateEvaluatorPool.get() - defer c.manager.gateEvaluatorPool.put(evaluator) - evaluator.setGate(gate) + evaluator := c.gateEvaluatorPool.get() + defer c.gateEvaluatorPool.put(evaluator) res := make([]fr.Element, degGJ) @@ -342,26 +341,25 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } c.manager.memPool.Dump(c.claimedEvaluations, c.eq) + c.gateEvaluatorPool.dumpAll() return evaluations } type claimsManager struct { - claims []*eqTimesGateEvalSumcheckLazyClaims - assignment WireAssignment - memPool *polynomial.Pool - workers *utils.WorkerPool - wires gkrtypes.Wires - gateEvaluatorPool *gateEvaluatorPool + claims []*eqTimesGateEvalSumcheckLazyClaims + assignment WireAssignment + memPool *polynomial.Pool + workers *utils.WorkerPool + wires gkrtypes.Wires } -func newClaimsManager(wires gkrtypes.Wires, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(wires []*gkrtypes.Wire, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) manager.memPool = o.pool manager.workers = o.workers manager.wires = wires - manager.gateEvaluatorPool = o.gateEvaluatorPool for i, wire := range wires { @@ -406,6 +404,8 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + return res } @@ -415,13 +415,12 @@ func (m *claimsManager) deleteClaim(wire int) { } type settings struct { - pool *polynomial.Pool - sorted []*gkrtypes.Wire - transcript *fiatshamir.Transcript - transcriptPrefix string - nbVars int - workers *utils.WorkerPool - gateEvaluatorPool *gateEvaluatorPool + pool *polynomial.Pool + sorted []*gkrtypes.Wire + transcript *fiatshamir.Transcript + transcriptPrefix string + nbVars int + workers *utils.WorkerPool } type Option func(*settings) @@ -432,12 +431,6 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithGateEvaluatorPool(pool *gateEvaluatorPool) Option { - return func(options *settings) { - options.gateEvaluatorPool = pool - } -} - func WithSortedCircuit(sorted []*gkrtypes.Wire) Option { return func(options *settings) { options.sorted = sorted @@ -468,10 +461,6 @@ func setup(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fia o.pool = &pool } - if o.gateEvaluatorPool == nil { - o.gateEvaluatorPool = newGateEvaluatorPool(c.MaxStackSize()) - } - if o.workers == nil { o.workers = utils.NewWorkerPool() } @@ -677,34 +666,24 @@ func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcri func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { nbInstances := a.NumInstances() - - // Compute maximum stack size needed for any gate evaluator - maxStackSize := 0 - for _, wire := range wires { - if !wire.IsInput() { - gate := wire.Gate.Compiled() - nbIn := len(wire.Inputs) - stackSize := gate.NbConstants() + nbIn + len(gate.Instructions) - maxStackSize = max(maxStackSize, stackSize) - } - } - - evaluator := newGateEvaluator(maxStackSize) + evaluators := make([]gateEvaluator, len(wires)) for i := range wires { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } + if !wires[i].IsInput() { + evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + } } for i := range nbInstances { for wI, w := range wires { if !w.IsInput() { - evaluator.setGate(w.Gate.Compiled()) for _, in := range w.Inputs { - evaluator.pushInput(&a[in][i]) + evaluators[wI].pushInput(&a[in][i]) } - a[wI][i].Set(evaluator.evaluate()) + a[wI][i].Set(evaluators[wI].evaluate()) } } } @@ -753,31 +732,32 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. -// It is a universal evaluator initialized with the maximum stack size needed across all gates -// in the circuit, allowing it to evaluate any gate without reallocation. +// It manages the stack internally and handles input buffering, making it easy to +// evaluate the same gate multiple times with different inputs. type gateEvaluator struct { + gate *gkrtypes.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs - gate *gkrtypes.CompiledGate + nbIn int // number of inputs expected } -// newGateEvaluator creates a universal gate evaluator with the given maximum stack size. -// The evaluator can handle any gate whose stack size is at most maxStackSize. -func newGateEvaluator(maxStackSize int) gateEvaluator { - return gateEvaluator{ - frameSize: 0, - vars: make([]fr.Element, maxStackSize), +// newGateEvaluator creates an evaluator for the given compiled gate. +// The stack is preloaded with constants and ready for evaluation. +func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { + e := gateEvaluator{ + gate: gate, + nbIn: nbIn, + frameSize: gate.NbConstants(), + } + if len(elementPool) > 0 { + e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) + } else { + e.vars = make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)) } -} - -// setGate prepares the evaluator for evaluating the given gate by loading its constants. -// This must be called before evaluating a new gate type. -func (e *gateEvaluator) setGate(gate *gkrtypes.CompiledGate) { for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } - e.frameSize = gate.NbConstants() - e.gate = gate + return e } // pushInput adds an input to the evaluator's input buffer. @@ -787,15 +767,19 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { e.frameSize++ } -// evaluate executes the gate on the current inputs and returns the result. -// The gate parameter specifies which gate to evaluate, and nbIn is the expected number of inputs. -// The stack is automatically reset after evaluation, making the evaluator ready for the next evaluation. +// evaluate adds top to the top of the stack, executes the gate on it and returns the result. +// The stack is automatically reset after evaluation, +// making the evaluator ready for the next evaluation. // NB! The result is short-lived. It will be overwritten the next time evaluate is called. func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { for i := range top { e.pushInput(&top[i]) } + if e.frameSize != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) + } + // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { @@ -848,19 +832,22 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { return res } -// gateEvaluatorPool manages a pool of universal gate evaluators. -// All evaluators are initialized with the same maximum stack size and can handle any gate. -// All evaluators share the same underlying polynomial.Pool for element slices. +// gateEvaluatorPool manages a pool of gate evaluators for a specific gate type +// All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - maxStackSize int - lock sync.Mutex - available map[*gateEvaluator]struct{} + gate *gkrtypes.CompiledGate + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } -func newGateEvaluatorPool(maxStackSize int) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - maxStackSize: maxStackSize, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -874,7 +861,7 @@ func (gep *gateEvaluatorPool) get() *gateEvaluator { return e } - e := newGateEvaluator(gep.maxStackSize) + e := newGateEvaluator(gep.gate, gep.nbIn, gep.elementPool) return &e } @@ -884,8 +871,18 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { defer gep.lock.Unlock() // Reset evaluator state (but keep the vars slice for reuse) - e.frameSize = 0 + e.frameSize = gep.gate.NbConstants() // Return evaluator to pool (it keeps its vars slice from polynomial pool) gep.available[e] = struct{}{} } + +// dumpAll dumps all available evaluator vars slices back to the polynomial pool. It is not to be used after that. +// NB! User must ensure all evaluators have been put back in the pool to prevent memory leaks. +func (gep *gateEvaluatorPool) dumpAll() { + gep.lock.Lock() + defer gep.lock.Unlock() + for e := range gep.available { + gep.elementPool.Dump(e.vars) + } +} diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index c70feb32d1..ab49d127b7 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -19,6 +19,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/mimc" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + gcUtils "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" @@ -108,6 +109,34 @@ func TestMimc(t *testing.T) { test(t, mimcCircuit(93)) } +func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { + circuit := gkrtypes.Circuit{gkrtypes.Wire{ + Gate: gkrtypes.Identity(), + NbUniqueOutputs: 2, + }} + + assignment := WireAssignment{[]fr.Element{two, three}} + var o settings + pool := polynomial.NewPool(256, 1<<11) + workers := gcUtils.NewWorkerPool() + o.pool = &pool + o.workers = workers + + claimsManagerGen := func() *claimsManager { + manager := newClaimsManager(utils.References(circuit), assignment, o) + manager.add(0, []fr.Element{three}, five) + manager.add(0, []fr.Element{four}, six) + return &manager + } + + transcriptGen := newMessageCounterGenerator(4, 1) + + proof, err := sumcheckProve(claimsManagerGen().getClaim(0), fiatshamir.WithHash(transcriptGen(), nil)) + assert.NoError(t, err) + err = sumcheckVerify(claimsManagerGen().getLazyClaim(0), proof, fiatshamir.WithHash(transcriptGen(), nil)) + assert.NoError(t, err) +} + var one, two, three, four, five, six fr.Element func init() { diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index cc5a7bf602..5a491679f3 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/big" + "sync" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" @@ -15,16 +16,21 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + type SolvingData struct { assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. circuit gkrtypes.Circuit maxNbIn int // maximum number of inputs for a gate in the circuit nbInstances int hashName string - evaluatorPool *gateEvaluatorPool // pool of circuit evaluators for concurrent reuse + evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse } type newSolvingDataSettings struct { @@ -114,7 +120,22 @@ func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption } // Initialize circuit evaluator pool - d[k].evaluatorPool = newGateEvaluatorPool(info[k].Circuit.MaxStackSize()) + circuit := d[k].circuit + elementPool := polynomial.NewPool(maxGateStackSize) + d[k].evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { // input wires don't need evaluators + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) + } + } + return ce + }, + } } return d @@ -160,7 +181,9 @@ func SolveHint(data []SolvingData) hint.Hint { outsI := 0 insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - evaluator := data.evaluatorPool.get() + // Get a circuit evaluator from the pool + ce := data.evaluatorPool.Get().(*circuitEvaluator) + defer data.evaluatorPool.Put(ce) // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. @@ -173,7 +196,7 @@ func SolveHint(data []SolvingData) hint.Hint { insI++ } else { // Get evaluator for this wire from the circuit evaluator - evaluator.setGate(w.Gate.Compiled()) + evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { @@ -229,7 +252,7 @@ func ProveHint(data []SolvingData) hint.Hint { hsh := hash.NewHash(hashName + "_BLS12_377") - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...), WithGateEvaluatorPool(data.evaluatorPool)) + proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) if err != nil { return err } diff --git a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go index d9ff537690..e5b8ed4f83 100644 --- a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go +++ b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go @@ -130,6 +130,8 @@ func BenchmarkGkrMiMC(b *testing.B) { cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, circuit) require.NoError(b, err) + b.ResetTimer() + for b.Loop() { w, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField()) require.NoError(b, err) From ae8f5e17f50addd726e70d23c3208487b552def6 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 21 Jan 2026 18:17:44 -0600 Subject: [PATCH 117/251] feat: blueprint for bn254 --- internal/gkr/bn254/blueprint.go | 353 ++++++++++++++++++++++++++++++++ 1 file changed, 353 insertions(+) create mode 100644 internal/gkr/bn254/blueprint.go diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go new file mode 100644 index 0000000000..ebb54b3eec --- /dev/null +++ b/internal/gkr/bn254/blueprint.go @@ -0,0 +1,353 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +package gkr + +import ( + "fmt" + "math/big" + "sync" + + "github.com/consensys/gnark-crypto/ecc/bn254/fr" + "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" + fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + "github.com/consensys/gnark-crypto/hash" + "github.com/consensys/gnark/constraint" + "github.com/consensys/gnark/internal/gkr/gkrtypes" +) + +// circuitEvaluator evaluates all gates in a circuit for one instance +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + +// BlueprintGKRSolve is a BN254-specific blueprint for solving GKR circuit instances. +type BlueprintGKRSolve struct { + // Circuit structure + Circuit gkrtypes.Circuit + NbInputs int + NbOutputVars int + InputWires []int + OutputWires []int + MaxNbIn int // maximum number of inputs for any gate + + // Stateful data - stored as native fr.Element + nbInstances int + assignment WireAssignment // []polynomial.MultiLin + evaluatorPool sync.Pool // pool of circuitEvaluator for reuse + + lock sync.Mutex +} + +// InitializeEvaluatorPool initializes the evaluator pool for this blueprint +func (b *BlueprintGKRSolve) InitializeEvaluatorPool(circuit gkrtypes.Circuit, maxGateStackSize int) { + elementPool := polynomial.NewPool(maxGateStackSize) + b.evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) + } + } + return ce + }, + } +} + +// Ensures BlueprintGKRSolve implements BlueprintStateful +var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintGKRSolve)(nil) + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintGKRSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + instanceIdx := b.nbInstances + + // Initialize assignment array if this is the first instance + if b.assignment == nil { + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + } + } + + // Grow assignment slices to accommodate this instance + for i := range b.assignment { + for len(b.assignment[i]) <= instanceIdx { + var zero fr.Element + b.assignment[i] = append(b.assignment[i], zero) + } + } + + // Read input values from instruction calldata + nbInputsInst := int(inst.Calldata[1]) + if nbInputsInst != b.NbInputs { + return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) + } + + offset := 2 + + // Get a circuit evaluator from the pool + ce := b.evaluatorPool.Get().(*circuitEvaluator) + defer b.evaluatorPool.Put(ce) + + // Process all wires in topological order (circuit is already sorted) + inputIdx := 0 + for wI := range b.Circuit { + w := &b.Circuit[wI] + + if w.IsInput() { + // Read input value from calldata and convert to fr.Element + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to fr.Element + bigInt := s.ToBigInt(val) + b.assignment[wI][instanceIdx].SetBigInt(bigInt) + inputIdx++ + } else { + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] + + // Push gate inputs + for _, inWI := range w.Inputs { + evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + } + + // Evaluate the gate + b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + } + } + + // Set output wires for the instruction (convert fr.Element to U64) + outputIdx := 0 + for _, outWI := range b.OutputWires { + var bigInt big.Int + b.assignment[outWI][instanceIdx].BigInt(&bigInt) + s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) + outputIdx++ + } + + b.nbInstances++ + return nil +} + +// Reset implements BlueprintStateful +func (b *BlueprintGKRSolve) Reset() { + b.nbInstances = 0 + b.assignment = nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintGKRSolve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintGKRSolve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintGKRSolve) NbOutputs(inst constraint.Instruction) int { + return b.NbOutputVars +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintGKRSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + nbInputsInst := int(inst.Calldata[1]) + offset := 2 + + for range nbInputsInst { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + for i := range b.NbOutputVars { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} + +// GetAssignment returns the assignment for a specific wire and instance (for debugging) +func (b *BlueprintGKRSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { + b.lock.Lock() + defer b.lock.Unlock() + + var zero constraint.U64 + if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + } + + // Convert fr.Element to U64 + var bigInt big.Int + b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) + return s.FromInterface(&bigInt), nil +} + +// GetAssignments returns all assignments for proving +func (b *BlueprintGKRSolve) GetAssignments() WireAssignment { + b.lock.Lock() + defer b.lock.Unlock() + return b.assignment +} + +// GetNbInstances returns the number of instances solved +func (b *BlueprintGKRSolve) GetNbInstances() int { + b.lock.Lock() + defer b.lock.Unlock() + return b.nbInstances +} + +// BlueprintGKRProve is a BN254-specific blueprint for generating GKR proofs. +type BlueprintGKRProve struct { + SolveBlueprint *BlueprintGKRSolve + HashName string + + lock sync.Mutex +} + +// Ensures BlueprintGKRProve implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGKRProve)(nil) + +// Solve implements the BlueprintSolvable interface for proving. +func (b *BlueprintGKRProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Get assignments from solve blueprint (already in fr.Element form) + assignments := b.SolveBlueprint.GetAssignments() + if len(assignments) == 0 { + return fmt.Errorf("no assignments available for proving") + } + + // Read initial challenges from instruction calldata + nbChallenges := int(inst.Calldata[2]) + challenges := make([]fr.Element, nbChallenges) + offset := 3 + for i := range nbChallenges { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to fr.Element + bigInt := s.ToBigInt(val) + challenges[i].SetBigInt(bigInt) + } + + // Convert challenges to [][]byte for Fiat-Shamir + insBytes := make([][]byte, len(challenges)) + for i := range challenges { + insBytes[i] = make([]byte, fr.Bytes) + challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + } + + // Create Fiat-Shamir settings + hsh := hash.NewHash(b.HashName + "_BN254") + fsSettings := fiatshamir.WithHash(hsh, insBytes...) + + // Call the BN254-specific Prove function (assignments already WireAssignment type) + proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + if err != nil { + return fmt.Errorf("bn254 prove failed: %w", err) + } + + // Serialize proof and convert to U64 + proofSize := 0 + for i := range proof { + for _, poly := range proof[i].partialSumPolys { + proofSize += len(poly) + } + if proof[i].finalEvalProof != nil { + proofSize += len(proof[i].finalEvalProof) + } + } + + outsBig := make([]*big.Int, proofSize) + for i := range outsBig { + outsBig[i] = new(big.Int) + } + if err := proof.SerializeToBigInts(outsBig); err != nil { + return fmt.Errorf("failed to serialize proof: %w", err) + } + + // Set output wires (convert big.Int to U64) + for i, bigVal := range outsBig { + s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + } + + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintGKRProve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintGKRProve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintGKRProve) NbOutputs(inst constraint.Instruction) int { + if len(inst.Calldata) > 1 { + return int(inst.Calldata[1]) + } + return 0 +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintGKRProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + if len(inst.Calldata) < 3 { + return maxLevel + 1 + } + + nbChallenges := int(inst.Calldata[2]) + offset := 3 + + for range nbChallenges { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + proofSize := int(inst.Calldata[1]) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} From 7d86c470152bd0a31bc889bec296a929832802a1 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Thu, 22 Jan 2026 18:27:00 -0600 Subject: [PATCH 118/251] build: generify --- constraint/babybear/system.go | 5 - constraint/bls12-377/solver.go | 25 -- constraint/bls12-377/system.go | 5 - constraint/bls12-381/solver.go | 25 -- constraint/bls12-381/system.go | 5 - constraint/bls24-315/solver.go | 25 -- constraint/bls24-315/system.go | 5 - constraint/bls24-317/solver.go | 25 -- constraint/bls24-317/system.go | 5 - constraint/bn254/solver.go | 25 -- constraint/bn254/system.go | 5 - constraint/bw6-633/solver.go | 25 -- constraint/bw6-633/system.go | 5 - constraint/bw6-761/solver.go | 25 -- constraint/bw6-761/system.go | 5 - constraint/core.go | 12 - constraint/koalabear/system.go | 5 - constraint/system.go | 7 - constraint/tinyfield/system.go | 5 - frontend/cs/r1cs/api.go | 5 - frontend/cs/scs/api.go | 5 - internal/generator/backend/main.go | 2 +- .../backend/template/gkr/blueprint.go.tmpl | 348 +++++++++++++++++ .../backend/template/gkr/solver_hints.go.tmpl | 265 ------------- .../template/representations/solver.go.tmpl | 30 -- .../template/representations/system.go.tmpl | 5 - internal/gkr/bls12-377/blueprint.go | 355 ++++++++++++++++++ internal/gkr/bls12-377/solver_hints.go | 273 -------------- internal/gkr/bls12-381/blueprint.go | 355 ++++++++++++++++++ internal/gkr/bls12-381/solver_hints.go | 273 -------------- internal/gkr/bls24-315/blueprint.go | 355 ++++++++++++++++++ internal/gkr/bls24-315/solver_hints.go | 273 -------------- internal/gkr/bls24-317/blueprint.go | 355 ++++++++++++++++++ internal/gkr/bls24-317/solver_hints.go | 273 -------------- internal/gkr/bn254/blueprint.go | 50 +-- internal/gkr/bn254/solver_hints.go | 273 -------------- internal/gkr/bw6-633/blueprint.go | 355 ++++++++++++++++++ internal/gkr/bw6-633/solver_hints.go | 273 -------------- internal/gkr/bw6-761/blueprint.go | 355 ++++++++++++++++++ internal/gkr/bw6-761/solver_hints.go | 273 -------------- internal/gkr/gkrhints/engine_hints.go | 195 ---------- internal/gkr/gkrinfo/info.go | 23 -- internal/gkr/gkrtypes/types.go | 22 -- std/gkrapi/api.go | 7 +- std/gkrapi/compile.go | 250 ++++++++---- test/engine.go | 6 - 46 files changed, 2686 insertions(+), 2812 deletions(-) create mode 100644 internal/generator/backend/template/gkr/blueprint.go.tmpl delete mode 100644 internal/generator/backend/template/gkr/solver_hints.go.tmpl create mode 100644 internal/gkr/bls12-377/blueprint.go delete mode 100644 internal/gkr/bls12-377/solver_hints.go create mode 100644 internal/gkr/bls12-381/blueprint.go delete mode 100644 internal/gkr/bls12-381/solver_hints.go create mode 100644 internal/gkr/bls24-315/blueprint.go delete mode 100644 internal/gkr/bls24-315/solver_hints.go create mode 100644 internal/gkr/bls24-317/blueprint.go delete mode 100644 internal/gkr/bls24-317/solver_hints.go delete mode 100644 internal/gkr/bn254/solver_hints.go create mode 100644 internal/gkr/bw6-633/blueprint.go delete mode 100644 internal/gkr/bw6-633/solver_hints.go create mode 100644 internal/gkr/bw6-761/blueprint.go delete mode 100644 internal/gkr/bw6-761/solver_hints.go delete mode 100644 internal/gkr/gkrhints/engine_hints.go diff --git a/constraint/babybear/system.go b/constraint/babybear/system.go index 7b0313014d..706d7524da 100644 --- a/constraint/babybear/system.go +++ b/constraint/babybear/system.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/logger" "github.com/consensys/gnark-crypto/ecc" @@ -288,7 +287,3 @@ func (t *SparseR1CSSolution) ReadFrom(r io.Reader) (int64, error) { n += a return n, err } - -func (cs *system) NewGkr() (*gkrinfo.StoringInfo, int) { - return cs.System.NewGkr() -} diff --git a/constraint/bls12-377/solver.go b/constraint/bls12-377/solver.go index f91c74bcae..1af47d2ec4 100644 --- a/constraint/bls12-377/solver.go +++ b/constraint/bls12-377/solver.go @@ -19,10 +19,6 @@ import ( "github.com/consensys/gnark-crypto/field/pool" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/constraint/solver/gkrgates" - gkr "github.com/consensys/gnark/internal/gkr/bls12-377" - "github.com/consensys/gnark/internal/gkr/gkrhints" - "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/rs/zerolog" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" @@ -50,27 +46,6 @@ type solver struct { } func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, error) { - // add GKR options to overwrite placeholder hints - if len(cs.GkrInfo) != 0 { - solvingInfo, err := gkrtypes.NewSolvingInfo(cs.GkrInfo, gkrgates.Get) - if err != nil { - return nil, err - } - - gkrData := gkr.NewSolvingData(solvingInfo) - // we need to get the current hint ID for each of the GKR hints. - // Currently the hints are defined on gkrhints.TestEngineHints, so - // we create a temporary instance for hint retrieval. In case this is - // not replaced, then we still keep using the big-int based hints on the - // GKR test engine. - var gkrHints *gkrhints.TestEngineHints - // Replace the hint calls with actual references to GKR assignment - // getter, solver, and prover based on the defined circuits. - opts = append(opts, - csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Prove), gkr.ProveHint(gkrData))) - } // parse options opt, err := csolver.NewConfig(opts...) if err != nil { diff --git a/constraint/bls12-377/system.go b/constraint/bls12-377/system.go index 7a2c11ba8c..1605186575 100644 --- a/constraint/bls12-377/system.go +++ b/constraint/bls12-377/system.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/logger" "github.com/consensys/gnark-crypto/ecc" @@ -288,7 +287,3 @@ func (t *SparseR1CSSolution) ReadFrom(r io.Reader) (int64, error) { n += a return n, err } - -func (cs *system) NewGkr() (*gkrinfo.StoringInfo, int) { - return cs.System.NewGkr() -} diff --git a/constraint/bls12-381/solver.go b/constraint/bls12-381/solver.go index d4f2dfe5b1..65c63d5546 100644 --- a/constraint/bls12-381/solver.go +++ b/constraint/bls12-381/solver.go @@ -19,10 +19,6 @@ import ( "github.com/consensys/gnark-crypto/field/pool" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/constraint/solver/gkrgates" - gkr "github.com/consensys/gnark/internal/gkr/bls12-381" - "github.com/consensys/gnark/internal/gkr/gkrhints" - "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/rs/zerolog" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" @@ -50,27 +46,6 @@ type solver struct { } func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, error) { - // add GKR options to overwrite placeholder hints - if len(cs.GkrInfo) != 0 { - solvingInfo, err := gkrtypes.NewSolvingInfo(cs.GkrInfo, gkrgates.Get) - if err != nil { - return nil, err - } - - gkrData := gkr.NewSolvingData(solvingInfo) - // we need to get the current hint ID for each of the GKR hints. - // Currently the hints are defined on gkrhints.TestEngineHints, so - // we create a temporary instance for hint retrieval. In case this is - // not replaced, then we still keep using the big-int based hints on the - // GKR test engine. - var gkrHints *gkrhints.TestEngineHints - // Replace the hint calls with actual references to GKR assignment - // getter, solver, and prover based on the defined circuits. - opts = append(opts, - csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Prove), gkr.ProveHint(gkrData))) - } // parse options opt, err := csolver.NewConfig(opts...) if err != nil { diff --git a/constraint/bls12-381/system.go b/constraint/bls12-381/system.go index 55381c8775..c1c5a48742 100644 --- a/constraint/bls12-381/system.go +++ b/constraint/bls12-381/system.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/logger" "github.com/consensys/gnark-crypto/ecc" @@ -288,7 +287,3 @@ func (t *SparseR1CSSolution) ReadFrom(r io.Reader) (int64, error) { n += a return n, err } - -func (cs *system) NewGkr() (*gkrinfo.StoringInfo, int) { - return cs.System.NewGkr() -} diff --git a/constraint/bls24-315/solver.go b/constraint/bls24-315/solver.go index 4e3aa01b9a..527b194760 100644 --- a/constraint/bls24-315/solver.go +++ b/constraint/bls24-315/solver.go @@ -19,10 +19,6 @@ import ( "github.com/consensys/gnark-crypto/field/pool" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/constraint/solver/gkrgates" - gkr "github.com/consensys/gnark/internal/gkr/bls24-315" - "github.com/consensys/gnark/internal/gkr/gkrhints" - "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/rs/zerolog" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" @@ -50,27 +46,6 @@ type solver struct { } func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, error) { - // add GKR options to overwrite placeholder hints - if len(cs.GkrInfo) != 0 { - solvingInfo, err := gkrtypes.NewSolvingInfo(cs.GkrInfo, gkrgates.Get) - if err != nil { - return nil, err - } - - gkrData := gkr.NewSolvingData(solvingInfo) - // we need to get the current hint ID for each of the GKR hints. - // Currently the hints are defined on gkrhints.TestEngineHints, so - // we create a temporary instance for hint retrieval. In case this is - // not replaced, then we still keep using the big-int based hints on the - // GKR test engine. - var gkrHints *gkrhints.TestEngineHints - // Replace the hint calls with actual references to GKR assignment - // getter, solver, and prover based on the defined circuits. - opts = append(opts, - csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Prove), gkr.ProveHint(gkrData))) - } // parse options opt, err := csolver.NewConfig(opts...) if err != nil { diff --git a/constraint/bls24-315/system.go b/constraint/bls24-315/system.go index fbab0c5f19..9b42c874ad 100644 --- a/constraint/bls24-315/system.go +++ b/constraint/bls24-315/system.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/logger" "github.com/consensys/gnark-crypto/ecc" @@ -288,7 +287,3 @@ func (t *SparseR1CSSolution) ReadFrom(r io.Reader) (int64, error) { n += a return n, err } - -func (cs *system) NewGkr() (*gkrinfo.StoringInfo, int) { - return cs.System.NewGkr() -} diff --git a/constraint/bls24-317/solver.go b/constraint/bls24-317/solver.go index 7065f77c60..d1a702d1dd 100644 --- a/constraint/bls24-317/solver.go +++ b/constraint/bls24-317/solver.go @@ -19,10 +19,6 @@ import ( "github.com/consensys/gnark-crypto/field/pool" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/constraint/solver/gkrgates" - gkr "github.com/consensys/gnark/internal/gkr/bls24-317" - "github.com/consensys/gnark/internal/gkr/gkrhints" - "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/rs/zerolog" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" @@ -50,27 +46,6 @@ type solver struct { } func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, error) { - // add GKR options to overwrite placeholder hints - if len(cs.GkrInfo) != 0 { - solvingInfo, err := gkrtypes.NewSolvingInfo(cs.GkrInfo, gkrgates.Get) - if err != nil { - return nil, err - } - - gkrData := gkr.NewSolvingData(solvingInfo) - // we need to get the current hint ID for each of the GKR hints. - // Currently the hints are defined on gkrhints.TestEngineHints, so - // we create a temporary instance for hint retrieval. In case this is - // not replaced, then we still keep using the big-int based hints on the - // GKR test engine. - var gkrHints *gkrhints.TestEngineHints - // Replace the hint calls with actual references to GKR assignment - // getter, solver, and prover based on the defined circuits. - opts = append(opts, - csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Prove), gkr.ProveHint(gkrData))) - } // parse options opt, err := csolver.NewConfig(opts...) if err != nil { diff --git a/constraint/bls24-317/system.go b/constraint/bls24-317/system.go index caa0ee8620..e9c0fcbb44 100644 --- a/constraint/bls24-317/system.go +++ b/constraint/bls24-317/system.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/logger" "github.com/consensys/gnark-crypto/ecc" @@ -288,7 +287,3 @@ func (t *SparseR1CSSolution) ReadFrom(r io.Reader) (int64, error) { n += a return n, err } - -func (cs *system) NewGkr() (*gkrinfo.StoringInfo, int) { - return cs.System.NewGkr() -} diff --git a/constraint/bn254/solver.go b/constraint/bn254/solver.go index 0f750e30c7..dd1f0d9956 100644 --- a/constraint/bn254/solver.go +++ b/constraint/bn254/solver.go @@ -19,10 +19,6 @@ import ( "github.com/consensys/gnark-crypto/field/pool" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/constraint/solver/gkrgates" - gkr "github.com/consensys/gnark/internal/gkr/bn254" - "github.com/consensys/gnark/internal/gkr/gkrhints" - "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/rs/zerolog" "github.com/consensys/gnark-crypto/ecc/bn254/fr" @@ -50,27 +46,6 @@ type solver struct { } func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, error) { - // add GKR options to overwrite placeholder hints - if len(cs.GkrInfo) != 0 { - solvingInfo, err := gkrtypes.NewSolvingInfo(cs.GkrInfo, gkrgates.Get) - if err != nil { - return nil, err - } - - gkrData := gkr.NewSolvingData(solvingInfo) - // we need to get the current hint ID for each of the GKR hints. - // Currently the hints are defined on gkrhints.TestEngineHints, so - // we create a temporary instance for hint retrieval. In case this is - // not replaced, then we still keep using the big-int based hints on the - // GKR test engine. - var gkrHints *gkrhints.TestEngineHints - // Replace the hint calls with actual references to GKR assignment - // getter, solver, and prover based on the defined circuits. - opts = append(opts, - csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Prove), gkr.ProveHint(gkrData))) - } // parse options opt, err := csolver.NewConfig(opts...) if err != nil { diff --git a/constraint/bn254/system.go b/constraint/bn254/system.go index 031327ff8b..70e63dc6d3 100644 --- a/constraint/bn254/system.go +++ b/constraint/bn254/system.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/logger" "github.com/consensys/gnark-crypto/ecc" @@ -288,7 +287,3 @@ func (t *SparseR1CSSolution) ReadFrom(r io.Reader) (int64, error) { n += a return n, err } - -func (cs *system) NewGkr() (*gkrinfo.StoringInfo, int) { - return cs.System.NewGkr() -} diff --git a/constraint/bw6-633/solver.go b/constraint/bw6-633/solver.go index 4fba3fafec..4b7e00cbb1 100644 --- a/constraint/bw6-633/solver.go +++ b/constraint/bw6-633/solver.go @@ -19,10 +19,6 @@ import ( "github.com/consensys/gnark-crypto/field/pool" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/constraint/solver/gkrgates" - gkr "github.com/consensys/gnark/internal/gkr/bw6-633" - "github.com/consensys/gnark/internal/gkr/gkrhints" - "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/rs/zerolog" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" @@ -50,27 +46,6 @@ type solver struct { } func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, error) { - // add GKR options to overwrite placeholder hints - if len(cs.GkrInfo) != 0 { - solvingInfo, err := gkrtypes.NewSolvingInfo(cs.GkrInfo, gkrgates.Get) - if err != nil { - return nil, err - } - - gkrData := gkr.NewSolvingData(solvingInfo) - // we need to get the current hint ID for each of the GKR hints. - // Currently the hints are defined on gkrhints.TestEngineHints, so - // we create a temporary instance for hint retrieval. In case this is - // not replaced, then we still keep using the big-int based hints on the - // GKR test engine. - var gkrHints *gkrhints.TestEngineHints - // Replace the hint calls with actual references to GKR assignment - // getter, solver, and prover based on the defined circuits. - opts = append(opts, - csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Prove), gkr.ProveHint(gkrData))) - } // parse options opt, err := csolver.NewConfig(opts...) if err != nil { diff --git a/constraint/bw6-633/system.go b/constraint/bw6-633/system.go index ff0ddf956f..f860494ce8 100644 --- a/constraint/bw6-633/system.go +++ b/constraint/bw6-633/system.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/logger" "github.com/consensys/gnark-crypto/ecc" @@ -288,7 +287,3 @@ func (t *SparseR1CSSolution) ReadFrom(r io.Reader) (int64, error) { n += a return n, err } - -func (cs *system) NewGkr() (*gkrinfo.StoringInfo, int) { - return cs.System.NewGkr() -} diff --git a/constraint/bw6-761/solver.go b/constraint/bw6-761/solver.go index 158463ef9b..3530188bcf 100644 --- a/constraint/bw6-761/solver.go +++ b/constraint/bw6-761/solver.go @@ -19,10 +19,6 @@ import ( "github.com/consensys/gnark-crypto/field/pool" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/constraint/solver/gkrgates" - gkr "github.com/consensys/gnark/internal/gkr/bw6-761" - "github.com/consensys/gnark/internal/gkr/gkrhints" - "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/rs/zerolog" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" @@ -50,27 +46,6 @@ type solver struct { } func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, error) { - // add GKR options to overwrite placeholder hints - if len(cs.GkrInfo) != 0 { - solvingInfo, err := gkrtypes.NewSolvingInfo(cs.GkrInfo, gkrgates.Get) - if err != nil { - return nil, err - } - - gkrData := gkr.NewSolvingData(solvingInfo) - // we need to get the current hint ID for each of the GKR hints. - // Currently the hints are defined on gkrhints.TestEngineHints, so - // we create a temporary instance for hint retrieval. In case this is - // not replaced, then we still keep using the big-int based hints on the - // GKR test engine. - var gkrHints *gkrhints.TestEngineHints - // Replace the hint calls with actual references to GKR assignment - // getter, solver, and prover based on the defined circuits. - opts = append(opts, - csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Prove), gkr.ProveHint(gkrData))) - } // parse options opt, err := csolver.NewConfig(opts...) if err != nil { diff --git a/constraint/bw6-761/system.go b/constraint/bw6-761/system.go index 03a4166d58..ca6aeb4060 100644 --- a/constraint/bw6-761/system.go +++ b/constraint/bw6-761/system.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/logger" "github.com/consensys/gnark-crypto/ecc" @@ -288,7 +287,3 @@ func (t *SparseR1CSSolution) ReadFrom(r io.Reader) (int64, error) { n += a return n, err } - -func (cs *system) NewGkr() (*gkrinfo.StoringInfo, int) { - return cs.System.NewGkr() -} diff --git a/constraint/core.go b/constraint/core.go index c64e124d0b..0709a286c6 100644 --- a/constraint/core.go +++ b/constraint/core.go @@ -11,7 +11,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/debug" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/smallfields" "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/logger" @@ -125,7 +124,6 @@ type System struct { lbWireLevel []Level `cbor:"-"` // at which level we solve a wire. init at -1. CommitmentInfo Commitments - GkrInfo []*gkrinfo.StoringInfo genericHint BlueprintID } @@ -470,13 +468,3 @@ func putBuffer(buf *[]uint32) { } bufPool.Put(buf) } - -// NewGkr registers a GKR sub-circuit, returning a reference to an object where -// serializable data about the sub-circuit is to be stored, and an index as a -// reference to the GKR sub-circuit. The index is the first argument to all -// GKR-related hints. -func (system *System) NewGkr() (*gkrinfo.StoringInfo, int) { - i := len(system.GkrInfo) - system.GkrInfo = append(system.GkrInfo, new(gkrinfo.StoringInfo)) - return system.GkrInfo[i], i -} diff --git a/constraint/koalabear/system.go b/constraint/koalabear/system.go index cc4b6b8b88..ac2284b844 100644 --- a/constraint/koalabear/system.go +++ b/constraint/koalabear/system.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/logger" "github.com/consensys/gnark-crypto/ecc" @@ -288,7 +287,3 @@ func (t *SparseR1CSSolution) ReadFrom(r io.Reader) (int64, error) { n += a return n, err } - -func (cs *system) NewGkr() (*gkrinfo.StoringInfo, int) { - return cs.System.NewGkr() -} diff --git a/constraint/system.go b/constraint/system.go index 0e1082f666..a913ffcb63 100644 --- a/constraint/system.go +++ b/constraint/system.go @@ -6,7 +6,6 @@ import ( "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrinfo" ) // ConstraintSystem is an interfaces that all constraint systems implement. This @@ -67,12 +66,6 @@ type ConstraintSystemGeneric[E Element] interface { AddCommitment(c Commitment) error GetCommitments() Commitments - // NewGkr registers a GKR sub-circuit, returning a reference to an object - // where serializable data about the sub-circuit is to be stored, and an - // index as a reference to the GKR sub-circuit. The index is the first - // argument to all GKR-related hints. - NewGkr() (*gkrinfo.StoringInfo, int) - AddLog(l LogEntry) // MakeTerm returns a new Term. The constraint system may store coefficients in a map, so diff --git a/constraint/tinyfield/system.go b/constraint/tinyfield/system.go index 7480176bd2..476b629dfe 100644 --- a/constraint/tinyfield/system.go +++ b/constraint/tinyfield/system.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/logger" "github.com/consensys/gnark-crypto/ecc" @@ -288,7 +287,3 @@ func (t *SparseR1CSSolution) ReadFrom(r io.Reader) (int64, error) { n += a return n, err } - -func (cs *system) NewGkr() (*gkrinfo.StoringInfo, int) { - return cs.System.NewGkr() -} diff --git a/frontend/cs/r1cs/api.go b/frontend/cs/r1cs/api.go index 7087f2de74..713021adff 100644 --- a/frontend/cs/r1cs/api.go +++ b/frontend/cs/r1cs/api.go @@ -11,7 +11,6 @@ import ( "runtime" "strings" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/hints" "github.com/consensys/gnark/internal/smallfields" @@ -867,7 +866,3 @@ func (builder *builder[E]) wireIDsToVars(wireIDs ...[]int) []frontend.Variable { } return res } - -func (builder *builder[E]) NewGkr() (*gkrinfo.StoringInfo, int) { - return builder.cs.NewGkr() -} diff --git a/frontend/cs/scs/api.go b/frontend/cs/scs/api.go index 792920a631..cd7e54c027 100644 --- a/frontend/cs/scs/api.go +++ b/frontend/cs/scs/api.go @@ -19,7 +19,6 @@ import ( "github.com/consensys/gnark/frontend/internal/expr" "github.com/consensys/gnark/frontend/schema" "github.com/consensys/gnark/internal/frontendtype" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/smallfields" "github.com/consensys/gnark/std/math/bits" ) @@ -748,7 +747,3 @@ func (builder *builder[E]) AddPlonkConstraint(a, b, o frontend.Variable, qL, qR, func (*builder[E]) FrontendType() frontendtype.Type { return frontendtype.SCS } - -func (builder *builder[E]) NewGkr() (*gkrinfo.StoringInfo, int) { - return builder.cs.NewGkr() -} diff --git a/internal/generator/backend/main.go b/internal/generator/backend/main.go index 7f0e5e3777..8a51e37263 100644 --- a/internal/generator/backend/main.go +++ b/internal/generator/backend/main.go @@ -319,7 +319,7 @@ func generateGkrBackend(cfg gkrConfig) error { }...) } else { entries = append(entries, []bavard.Entry{ - {File: filepath.Join(packageDir, "solver_hints.go"), Templates: []string{"solver_hints.go.tmpl"}}, + {File: filepath.Join(packageDir, "blueprint.go"), Templates: []string{"blueprint.go.tmpl"}}, {File: filepath.Join(packageDir, "gate_testing.go"), Templates: []string{"gate_testing.go.tmpl"}}, }...) } diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl new file mode 100644 index 0000000000..e6cf63b409 --- /dev/null +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -0,0 +1,348 @@ +import ( + "fmt" + "math/big" + "sync" + + "{{ .FieldPackagePath }}" + "{{ .FieldPackagePath }}/polynomial" + fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + "github.com/consensys/gnark-crypto/hash" + "github.com/consensys/gnark/constraint" + "github.com/consensys/gnark/internal/gkr/gkrtypes" +) + +// circuitEvaluator evaluates all gates in a circuit for one instance +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + +// BlueprintSolve is a {{.FieldID}}-specific blueprint for solving GKR circuit instances. +type BlueprintSolve struct { + // Circuit structure + Circuit gkrtypes.Circuit + NbInputs int + NbOutputVars int + InputWires []int + OutputWires []int + MaxNbIn int // maximum number of inputs for any gate + + // Stateful data - stored as native {{ .ElementType }} + nbInstances int + assignment WireAssignment // []polynomial.MultiLin + evaluatorPool sync.Pool // pool of circuitEvaluator for reuse + + lock sync.Mutex +} + +// InitializeEvaluatorPool initializes the evaluator pool for this blueprint +func (b *BlueprintSolve) InitializeEvaluatorPool(circuit gkrtypes.Circuit, maxGateStackSize int) { + elementPool := polynomial.NewPool(maxGateStackSize) + b.evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) + } + } + return ce + }, + } +} + +// Ensures BlueprintSolve implements BlueprintStateful +var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + instanceIdx := b.nbInstances + + // Initialize assignment array if this is the first instance + if b.assignment == nil { + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + } + } + + // Grow assignment slices to accommodate this instance + for i := range b.assignment { + for len(b.assignment[i]) <= instanceIdx { + var zero {{ .ElementType }} + b.assignment[i] = append(b.assignment[i], zero) + } + } + + // Read input values from instruction calldata + nbInputsInst := int(inst.Calldata[1]) + if nbInputsInst != b.NbInputs { + return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) + } + + offset := 2 + + // Get a circuit evaluator from the pool + ce := b.evaluatorPool.Get().(*circuitEvaluator) + defer b.evaluatorPool.Put(ce) + + // Process all wires in topological order (circuit is already sorted) + inputIdx := 0 + for wI := range b.Circuit { + w := &b.Circuit[wI] + + if w.IsInput() { + // Read input value from calldata and convert to {{ .ElementType }} + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to {{ .ElementType }} + bigInt := s.ToBigInt(val) + b.assignment[wI][instanceIdx].SetBigInt(bigInt) + inputIdx++ + } else { + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] + + // Push gate inputs + for _, inWI := range w.Inputs { + evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + } + + // Evaluate the gate + b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + } + } + + // Set output wires for the instruction (convert {{ .ElementType }} to U64) + outputIdx := 0 + for _, outWI := range b.OutputWires { + var bigInt big.Int + b.assignment[outWI][instanceIdx].BigInt(&bigInt) + s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) + outputIdx++ + } + + b.nbInstances++ + return nil +} + +// Reset implements BlueprintStateful +func (b *BlueprintSolve) Reset() { + b.nbInstances = 0 + b.assignment = nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintSolve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintSolve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { + return b.NbOutputVars +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + nbInputsInst := int(inst.Calldata[1]) + offset := 2 + + for range nbInputsInst { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + for i := range b.NbOutputVars { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} + +// GetAssignment returns the assignment for a specific wire and instance (for debugging) +func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { + b.lock.Lock() + defer b.lock.Unlock() + + var zero constraint.U64 + if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + } + + // Convert {{ .ElementType }} to U64 + var bigInt big.Int + b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) + return s.FromInterface(&bigInt), nil +} + +// GetAssignments returns all assignments for proving +func (b *BlueprintSolve) GetAssignments() WireAssignment { + b.lock.Lock() + defer b.lock.Unlock() + return b.assignment +} + +// GetNbInstances returns the number of instances solved +func (b *BlueprintSolve) GetNbInstances() int { + b.lock.Lock() + defer b.lock.Unlock() + return b.nbInstances +} + +// BlueprintProve is a {{.FieldID}}-specific blueprint for generating GKR proofs. +type BlueprintProve struct { + SolveBlueprint *BlueprintSolve + HashName string + + lock sync.Mutex +} + +// Ensures BlueprintProve implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) + +// Solve implements the BlueprintSolvable interface for proving. +func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Get assignments from solve blueprint (already in {{ .ElementType }} form) + assignments := b.SolveBlueprint.GetAssignments() + if len(assignments) == 0 { + return fmt.Errorf("no assignments available for proving") + } + + // Read initial challenges from instruction calldata + nbChallenges := int(inst.Calldata[2]) + challenges := make([]{{ .ElementType }}, nbChallenges) + offset := 3 + for i := range nbChallenges { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to {{ .ElementType }} + bigInt := s.ToBigInt(val) + challenges[i].SetBigInt(bigInt) + } + + // Convert challenges to [][]byte for Fiat-Shamir + insBytes := make([][]byte, len(challenges)) + for i := range challenges { + insBytes[i] = make([]byte, fr.Bytes) + challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + } + + // Create Fiat-Shamir settings + hsh := hash.NewHash(b.HashName + "_{{.FieldID}}") + fsSettings := fiatshamir.WithHash(hsh, insBytes...) + + // Call the {{.FieldID}}-specific Prove function (assignments already WireAssignment type) + proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + if err != nil { + return fmt.Errorf("{{toLower .FieldID}} prove failed: %w", err) + } + + // Serialize proof and convert to U64 + proofSize := 0 + for i := range proof { + for _, poly := range proof[i].partialSumPolys { + proofSize += len(poly) + } + if proof[i].finalEvalProof != nil { + proofSize += len(proof[i].finalEvalProof) + } + } + + outsBig := make([]*big.Int, proofSize) + for i := range outsBig { + outsBig[i] = new(big.Int) + } + if err := proof.SerializeToBigInts(outsBig); err != nil { + return fmt.Errorf("failed to serialize proof: %w", err) + } + + // Set output wires (convert big.Int to U64) + for i, bigVal := range outsBig { + s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + } + + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintProve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintProve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { + if len(inst.Calldata) > 1 { + return int(inst.Calldata[1]) + } + return 0 +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + if len(inst.Calldata) < 3 { + return maxLevel + 1 + } + + nbChallenges := int(inst.Calldata[2]) + offset := 3 + + for range nbChallenges { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + proofSize := int(inst.Calldata[1]) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl deleted file mode 100644 index 6c174cc316..0000000000 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ /dev/null @@ -1,265 +0,0 @@ -import ( - "fmt" - "math/big" - "sync" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/hash" - hint "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - - "{{ .FieldPackagePath }}" - "{{ .FieldPackagePath }}/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -type circuitEvaluator struct { - evaluators []gateEvaluator // one evaluator per wire -} - -type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string - evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse -} - -type newSolvingDataSettings struct { - assignments []gkrtypes.WireAssignment -} - -type NewSolvingDataOption func(*newSolvingDataSettings) - -// WithAssignments re-use already computed wire assignments. -// If set, the number of assignments must be equal to the -// number of SolvingInfo objects fed to NewSolvingData. -// However, the caller can still choose to provide values for certain -// circuits only. For example: -// WithAssignments(assignment1, nil, assignment3) -// is a valid option for three circuits. In this case, the assignments for -// the second circuit will be computed from scratch. -func WithAssignments(assignments ...gkrtypes.WireAssignment) NewSolvingDataOption { - return func(s *newSolvingDataSettings) { - s.assignments = assignments - } -} - -// NewSolvingData converts []gkrtypes.SolvingInfo into concrete SolvingData objects: -// - The gates are loaded in accordance with their names. -// - The instances/assignments are padded into a power of 2, suitable for the multilinear extensions used -// in the GKR prover. -// Optionally, the caller can provide pre-computed assignments for each circuit using WithAssignments. -func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption) []SolvingData { - var s newSolvingDataSettings - for _, opt := range options { - opt(&s) - } - if len(s.assignments) == 0 { - s.assignments = make([]gkrtypes.WireAssignment, len(info)) - } else if len(s.assignments) != len(info) { - panic(fmt.Sprintf("expected %d assignments (one for each circuit), got %d", len(info), len(s.assignments))) - } - - d := make([]SolvingData, len(info)) - - for k := range info { - - d[k].circuit = info[k].Circuit - d[k].assignment = make(WireAssignment, len(info[k].Circuit)) - d[k].nbInstances = info[k].NbInstances - d[k].hashName = info[k].HashName - - d[k].maxNbIn = d[k].circuit.MaxGateNbIn() - - nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info[k].NbInstances))) - for i := range d[k].assignment { - d[k].assignment[i] = make([]{{ .ElementType }}, nbPaddedInstances) - } - - if s.assignments[k] != nil { - if len(s.assignments[k]) != len(d[k].assignment) { - panic(fmt.Sprintf("provided assignment has %d wires, expected %d", len(s.assignments[k]), len(d[k].assignment))) - } - for i := range d[k].assignment { - if len(s.assignments[k][i]) != info[k].NbInstances { - panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignments[k][i]), info[k].NbInstances)) - } - for j := range s.assignments[k][i] { - if _, err := d[k].assignment[i][j].SetInterface(s.assignments[k][i][j]); err != nil { - panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) - } - } - // inline equivalent of repeatUntilEnd - for j := len(s.assignments[k][i]); j < nbPaddedInstances; j++ { - d[k].assignment[i][j] = d[k].assignment[i][j-1] // pad with the last value - } - } - } - - // Initialize polynomial pool for elements - // Size the pool for the worst case: max gate size across all wires - maxGateStackSize := 0 - for _, w := range d[k].circuit { - if !w.IsInput() { - stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) - if stackSize > maxGateStackSize { - maxGateStackSize = stackSize - } - } - } - - // Initialize circuit evaluator pool - circuit := d[k].circuit - elementPool := polynomial.NewPool(maxGateStackSize) - d[k].evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(circuit)), - } - for wI := range circuit { - w := &circuit[wI] - if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) - } - } - return ce - }, - } - } - - return d -} - -// this module assumes that wire and instance indexes respect dependencies - -// GetAssignmentHint generates a hint that returns the value of a wire of a circuit at an instance. -// It is intended for use in the debugging function gkrapi.API.GetValue. -func GetAssignmentHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - if len(ins) != 4 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: GKR sub-circuit index, wire index, instance index, and dummy dependency enforcer") - } - if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { - return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") - } - data := &data[ins[0].Uint64()] - wireI := ins[1].Uint64() - instanceI := ins[2].Uint64() - - data.assignment[wireI][instanceI].BigInt(outs[0]) - - return nil - } -} - -// SolveHint generate a hint that computes the assignments for all wires in a circuit instance. -// It is intended for use in gkrapi.API.AddInstance. -func SolveHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: instance index of the circuit to solve - // - rest: values for input wires - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - instanceI := ins[1].Uint64() - - // indices for reading inputs and outputs - outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - - // Get a circuit evaluator from the pool - ce := data.evaluatorPool.Get().(*circuitEvaluator) - defer data.evaluatorPool.Put(ce) - - // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, - // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. - for wI := range data.circuit { - w := &data.circuit[wI] - if w.IsInput() { - // read from provided input - // there is no gate to compute for input wires. We only need to set them in the assignment. - data.assignment[wI][instanceI].SetBigInt(ins[insI]) - insI++ - } else { - // Get evaluator for this wire from the circuit evaluator - evaluator := &ce.evaluators[wI] - - // Push gate inputs - for _, inWI := range w.Inputs { - evaluator.pushInput(&data.assignment[inWI][instanceI]) - } - - // Evaluate the gate - data.assignment[wI][instanceI].Set(evaluator.evaluate()) - } - if w.IsOutput() { - // write to provided output. - // NB! even if it is output wire, then it was already computed - // above. So the current condition is not exclusive with the - // previous one. - data.assignment[wI][instanceI].BigInt(outs[outsI]) - outsI++ - } - } - - return nil - } -} - -// ProveHint generates a hint that produces the GKR proof using the computed assignments contained in data. -// It is meant for use in gkrapi.Circuit.finalize. -func ProveHint(data []SolvingData) hint.Hint { - - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: dummy, just to ensure the solver's work is done before the prover is called. - // - rest: initial Fiat-Shamir challenge (provided by the caller, typically a commitment to all inputs and outputs) - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - hashName := data.hashName - // drop the first input which indicates the current circuit index - ins = ins[1:] - - data.assignment.repeatUntilEnd(data.nbInstances) - - // The second input (the dummy) is an output from the last call to AddInstance. - // We need to ensure that the circuit has been completely solved for all instances of all wires. - // By feeding one of the output of the last call to AddInstance, we ensure that the gnark solver - // schedules proof generation after all solving is done. - // The rest constitute the initial fiat shamir challenge. - insBytes := make([][]byte, len(ins)-1) - for i := 1; i < len(ins); i++ { - insBytes[i-1] = make([]byte, fr.Bytes) - ins[i].FillBytes(insBytes[i-1]) - } - - hsh := hash.NewHash(hashName + "_{{.FieldID}}") - - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) - if err != nil { - return err - } - - return proof.SerializeToBigInts(outs) - - } -} - -// repeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. -{{ print "// e.g. {{1, 2, 3}, {4, 5, 6}}.repeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}}"}} -func (a WireAssignment) repeatUntilEnd(n int) { - for i := range a { - for j := n; j < len(a[i]); j++ { - a[i][j] = a[i][j-1] - } - } -} \ No newline at end of file diff --git a/internal/generator/backend/template/representations/solver.go.tmpl b/internal/generator/backend/template/representations/solver.go.tmpl index d1866761d6..356f537cb5 100644 --- a/internal/generator/backend/template/representations/solver.go.tmpl +++ b/internal/generator/backend/template/representations/solver.go.tmpl @@ -12,12 +12,6 @@ import ( "github.com/rs/zerolog" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/field/pool" - {{- if not .NoGKR }} - "github.com/consensys/gnark/constraint/solver/gkrgates" - gkr "github.com/consensys/gnark/internal/gkr/{{ toLower .Curve }}" - "github.com/consensys/gnark/internal/gkr/gkrhints" - {{- end }} - "github.com/consensys/gnark/internal/gkr/gkrtypes" {{ template "import_fr" . }} ) @@ -43,30 +37,6 @@ type solver struct { } func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, error) { - {{ if not .NoGKR -}} - // add GKR options to overwrite placeholder hints - if len(cs.GkrInfo) != 0 { - solvingInfo, err := gkrtypes.NewSolvingInfo(cs.GkrInfo, gkrgates.Get) - if err != nil { - return nil, err - } - - gkrData := gkr.NewSolvingData(solvingInfo) - // we need to get the current hint ID for each of the GKR hints. - // Currently the hints are defined on gkrhints.TestEngineHints, so - // we create a temporary instance for hint retrieval. In case this is - // not replaced, then we still keep using the big-int based hints on the - // GKR test engine. - var gkrHints *gkrhints.TestEngineHints - // Replace the hint calls with actual references to GKR assignment - // getter, solver, and prover based on the defined circuits. - opts = append(opts, - csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)), - csolver.OverrideHint(csolver.GetHintID(gkrHints.Prove), gkr.ProveHint(gkrData))) - } - {{ end -}} - // parse options opt, err := csolver.NewConfig(opts...) if err != nil { diff --git a/internal/generator/backend/template/representations/system.go.tmpl b/internal/generator/backend/template/representations/system.go.tmpl index 14dad60266..6b08498bdc 100644 --- a/internal/generator/backend/template/representations/system.go.tmpl +++ b/internal/generator/backend/template/representations/system.go.tmpl @@ -6,7 +6,6 @@ import ( "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/logger" "github.com/consensys/gnark/backend/witness" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark-crypto/ecc" @@ -292,8 +291,4 @@ func (t *SparseR1CSSolution) ReadFrom(r io.Reader) (int64, error) { a, err = t.O.ReadFrom(r) n += a return n, err -} - -func (cs *system) NewGkr() (*gkrinfo.StoringInfo, int) { - return cs.System.NewGkr() } \ No newline at end of file diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go new file mode 100644 index 0000000000..e300370e59 --- /dev/null +++ b/internal/gkr/bls12-377/blueprint.go @@ -0,0 +1,355 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// Code generated by gnark DO NOT EDIT + +package gkr + +import ( + "fmt" + "math/big" + "sync" + + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" + fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + "github.com/consensys/gnark-crypto/hash" + "github.com/consensys/gnark/constraint" + "github.com/consensys/gnark/internal/gkr/gkrtypes" +) + +// circuitEvaluator evaluates all gates in a circuit for one instance +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + +// BlueprintSolve is a BLS12_377-specific blueprint for solving GKR circuit instances. +type BlueprintSolve struct { + // Circuit structure + Circuit gkrtypes.Circuit + NbInputs int + NbOutputVars int + InputWires []int + OutputWires []int + MaxNbIn int // maximum number of inputs for any gate + + // Stateful data - stored as native fr.Element + nbInstances int + assignment WireAssignment // []polynomial.MultiLin + evaluatorPool sync.Pool // pool of circuitEvaluator for reuse + + lock sync.Mutex +} + +// InitializeEvaluatorPool initializes the evaluator pool for this blueprint +func (b *BlueprintSolve) InitializeEvaluatorPool(circuit gkrtypes.Circuit, maxGateStackSize int) { + elementPool := polynomial.NewPool(maxGateStackSize) + b.evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) + } + } + return ce + }, + } +} + +// Ensures BlueprintSolve implements BlueprintStateful +var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + instanceIdx := b.nbInstances + + // Initialize assignment array if this is the first instance + if b.assignment == nil { + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + } + } + + // Grow assignment slices to accommodate this instance + for i := range b.assignment { + for len(b.assignment[i]) <= instanceIdx { + var zero fr.Element + b.assignment[i] = append(b.assignment[i], zero) + } + } + + // Read input values from instruction calldata + nbInputsInst := int(inst.Calldata[1]) + if nbInputsInst != b.NbInputs { + return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) + } + + offset := 2 + + // Get a circuit evaluator from the pool + ce := b.evaluatorPool.Get().(*circuitEvaluator) + defer b.evaluatorPool.Put(ce) + + // Process all wires in topological order (circuit is already sorted) + inputIdx := 0 + for wI := range b.Circuit { + w := &b.Circuit[wI] + + if w.IsInput() { + // Read input value from calldata and convert to fr.Element + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to fr.Element + bigInt := s.ToBigInt(val) + b.assignment[wI][instanceIdx].SetBigInt(bigInt) + inputIdx++ + } else { + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] + + // Push gate inputs + for _, inWI := range w.Inputs { + evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + } + + // Evaluate the gate + b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + } + } + + // Set output wires for the instruction (convert fr.Element to U64) + outputIdx := 0 + for _, outWI := range b.OutputWires { + var bigInt big.Int + b.assignment[outWI][instanceIdx].BigInt(&bigInt) + s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) + outputIdx++ + } + + b.nbInstances++ + return nil +} + +// Reset implements BlueprintStateful +func (b *BlueprintSolve) Reset() { + b.nbInstances = 0 + b.assignment = nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintSolve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintSolve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { + return b.NbOutputVars +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + nbInputsInst := int(inst.Calldata[1]) + offset := 2 + + for range nbInputsInst { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + for i := range b.NbOutputVars { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} + +// GetAssignment returns the assignment for a specific wire and instance (for debugging) +func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { + b.lock.Lock() + defer b.lock.Unlock() + + var zero constraint.U64 + if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + } + + // Convert fr.Element to U64 + var bigInt big.Int + b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) + return s.FromInterface(&bigInt), nil +} + +// GetAssignments returns all assignments for proving +func (b *BlueprintSolve) GetAssignments() WireAssignment { + b.lock.Lock() + defer b.lock.Unlock() + return b.assignment +} + +// GetNbInstances returns the number of instances solved +func (b *BlueprintSolve) GetNbInstances() int { + b.lock.Lock() + defer b.lock.Unlock() + return b.nbInstances +} + +// BlueprintProve is a BLS12_377-specific blueprint for generating GKR proofs. +type BlueprintProve struct { + SolveBlueprint *BlueprintSolve + HashName string + + lock sync.Mutex +} + +// Ensures BlueprintProve implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) + +// Solve implements the BlueprintSolvable interface for proving. +func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Get assignments from solve blueprint (already in fr.Element form) + assignments := b.SolveBlueprint.GetAssignments() + if len(assignments) == 0 { + return fmt.Errorf("no assignments available for proving") + } + + // Read initial challenges from instruction calldata + nbChallenges := int(inst.Calldata[2]) + challenges := make([]fr.Element, nbChallenges) + offset := 3 + for i := range nbChallenges { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to fr.Element + bigInt := s.ToBigInt(val) + challenges[i].SetBigInt(bigInt) + } + + // Convert challenges to [][]byte for Fiat-Shamir + insBytes := make([][]byte, len(challenges)) + for i := range challenges { + insBytes[i] = make([]byte, fr.Bytes) + challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + } + + // Create Fiat-Shamir settings + hsh := hash.NewHash(b.HashName + "_BLS12_377") + fsSettings := fiatshamir.WithHash(hsh, insBytes...) + + // Call the BLS12_377-specific Prove function (assignments already WireAssignment type) + proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + if err != nil { + return fmt.Errorf("bls12_377 prove failed: %w", err) + } + + // Serialize proof and convert to U64 + proofSize := 0 + for i := range proof { + for _, poly := range proof[i].partialSumPolys { + proofSize += len(poly) + } + if proof[i].finalEvalProof != nil { + proofSize += len(proof[i].finalEvalProof) + } + } + + outsBig := make([]*big.Int, proofSize) + for i := range outsBig { + outsBig[i] = new(big.Int) + } + if err := proof.SerializeToBigInts(outsBig); err != nil { + return fmt.Errorf("failed to serialize proof: %w", err) + } + + // Set output wires (convert big.Int to U64) + for i, bigVal := range outsBig { + s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + } + + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintProve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintProve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { + if len(inst.Calldata) > 1 { + return int(inst.Calldata[1]) + } + return 0 +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + if len(inst.Calldata) < 3 { + return maxLevel + 1 + } + + nbChallenges := int(inst.Calldata[2]) + offset := 3 + + for range nbChallenges { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + proofSize := int(inst.Calldata[1]) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go deleted file mode 100644 index 5a491679f3..0000000000 --- a/internal/gkr/bls12-377/solver_hints.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2020-2026 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -// Code generated by gnark DO NOT EDIT - -package gkr - -import ( - "fmt" - "math/big" - "sync" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/hash" - hint "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - - "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -type circuitEvaluator struct { - evaluators []gateEvaluator // one evaluator per wire -} - -type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string - evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse -} - -type newSolvingDataSettings struct { - assignments []gkrtypes.WireAssignment -} - -type NewSolvingDataOption func(*newSolvingDataSettings) - -// WithAssignments re-use already computed wire assignments. -// If set, the number of assignments must be equal to the -// number of SolvingInfo objects fed to NewSolvingData. -// However, the caller can still choose to provide values for certain -// circuits only. For example: -// -// WithAssignments(assignment1, nil, assignment3) -// -// is a valid option for three circuits. In this case, the assignments for -// the second circuit will be computed from scratch. -func WithAssignments(assignments ...gkrtypes.WireAssignment) NewSolvingDataOption { - return func(s *newSolvingDataSettings) { - s.assignments = assignments - } -} - -// NewSolvingData converts []gkrtypes.SolvingInfo into concrete SolvingData objects: -// - The gates are loaded in accordance with their names. -// - The instances/assignments are padded into a power of 2, suitable for the multilinear extensions used -// in the GKR prover. -// Optionally, the caller can provide pre-computed assignments for each circuit using WithAssignments. -func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption) []SolvingData { - var s newSolvingDataSettings - for _, opt := range options { - opt(&s) - } - if len(s.assignments) == 0 { - s.assignments = make([]gkrtypes.WireAssignment, len(info)) - } else if len(s.assignments) != len(info) { - panic(fmt.Sprintf("expected %d assignments (one for each circuit), got %d", len(info), len(s.assignments))) - } - - d := make([]SolvingData, len(info)) - - for k := range info { - - d[k].circuit = info[k].Circuit - d[k].assignment = make(WireAssignment, len(info[k].Circuit)) - d[k].nbInstances = info[k].NbInstances - d[k].hashName = info[k].HashName - - d[k].maxNbIn = d[k].circuit.MaxGateNbIn() - - nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info[k].NbInstances))) - for i := range d[k].assignment { - d[k].assignment[i] = make([]fr.Element, nbPaddedInstances) - } - - if s.assignments[k] != nil { - if len(s.assignments[k]) != len(d[k].assignment) { - panic(fmt.Sprintf("provided assignment has %d wires, expected %d", len(s.assignments[k]), len(d[k].assignment))) - } - for i := range d[k].assignment { - if len(s.assignments[k][i]) != info[k].NbInstances { - panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignments[k][i]), info[k].NbInstances)) - } - for j := range s.assignments[k][i] { - if _, err := d[k].assignment[i][j].SetInterface(s.assignments[k][i][j]); err != nil { - panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) - } - } - // inline equivalent of repeatUntilEnd - for j := len(s.assignments[k][i]); j < nbPaddedInstances; j++ { - d[k].assignment[i][j] = d[k].assignment[i][j-1] // pad with the last value - } - } - } - - // Initialize polynomial pool for elements - // Size the pool for the worst case: max gate size across all wires - maxGateStackSize := 0 - for _, w := range d[k].circuit { - if !w.IsInput() { - stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) - if stackSize > maxGateStackSize { - maxGateStackSize = stackSize - } - } - } - - // Initialize circuit evaluator pool - circuit := d[k].circuit - elementPool := polynomial.NewPool(maxGateStackSize) - d[k].evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(circuit)), - } - for wI := range circuit { - w := &circuit[wI] - if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) - } - } - return ce - }, - } - } - - return d -} - -// this module assumes that wire and instance indexes respect dependencies - -// GetAssignmentHint generates a hint that returns the value of a wire of a circuit at an instance. -// It is intended for use in the debugging function gkrapi.API.GetValue. -func GetAssignmentHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - if len(ins) != 4 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: GKR sub-circuit index, wire index, instance index, and dummy dependency enforcer") - } - if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { - return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") - } - data := &data[ins[0].Uint64()] - wireI := ins[1].Uint64() - instanceI := ins[2].Uint64() - - data.assignment[wireI][instanceI].BigInt(outs[0]) - - return nil - } -} - -// SolveHint generate a hint that computes the assignments for all wires in a circuit instance. -// It is intended for use in gkrapi.API.AddInstance. -func SolveHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: instance index of the circuit to solve - // - rest: values for input wires - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - instanceI := ins[1].Uint64() - - // indices for reading inputs and outputs - outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - - // Get a circuit evaluator from the pool - ce := data.evaluatorPool.Get().(*circuitEvaluator) - defer data.evaluatorPool.Put(ce) - - // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, - // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. - for wI := range data.circuit { - w := &data.circuit[wI] - if w.IsInput() { - // read from provided input - // there is no gate to compute for input wires. We only need to set them in the assignment. - data.assignment[wI][instanceI].SetBigInt(ins[insI]) - insI++ - } else { - // Get evaluator for this wire from the circuit evaluator - evaluator := &ce.evaluators[wI] - - // Push gate inputs - for _, inWI := range w.Inputs { - evaluator.pushInput(&data.assignment[inWI][instanceI]) - } - - // Evaluate the gate - data.assignment[wI][instanceI].Set(evaluator.evaluate()) - } - if w.IsOutput() { - // write to provided output. - // NB! even if it is output wire, then it was already computed - // above. So the current condition is not exclusive with the - // previous one. - data.assignment[wI][instanceI].BigInt(outs[outsI]) - outsI++ - } - } - - return nil - } -} - -// ProveHint generates a hint that produces the GKR proof using the computed assignments contained in data. -// It is meant for use in gkrapi.Circuit.finalize. -func ProveHint(data []SolvingData) hint.Hint { - - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: dummy, just to ensure the solver's work is done before the prover is called. - // - rest: initial Fiat-Shamir challenge (provided by the caller, typically a commitment to all inputs and outputs) - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - hashName := data.hashName - // drop the first input which indicates the current circuit index - ins = ins[1:] - - data.assignment.repeatUntilEnd(data.nbInstances) - - // The second input (the dummy) is an output from the last call to AddInstance. - // We need to ensure that the circuit has been completely solved for all instances of all wires. - // By feeding one of the output of the last call to AddInstance, we ensure that the gnark solver - // schedules proof generation after all solving is done. - // The rest constitute the initial fiat shamir challenge. - insBytes := make([][]byte, len(ins)-1) - for i := 1; i < len(ins); i++ { - insBytes[i-1] = make([]byte, fr.Bytes) - ins[i].FillBytes(insBytes[i-1]) - } - - hsh := hash.NewHash(hashName + "_BLS12_377") - - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) - if err != nil { - return err - } - - return proof.SerializeToBigInts(outs) - - } -} - -// repeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. -// e.g. {{1, 2, 3}, {4, 5, 6}}.repeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} -func (a WireAssignment) repeatUntilEnd(n int) { - for i := range a { - for j := n; j < len(a[i]); j++ { - a[i][j] = a[i][j-1] - } - } -} diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go new file mode 100644 index 0000000000..6cb46344d2 --- /dev/null +++ b/internal/gkr/bls12-381/blueprint.go @@ -0,0 +1,355 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// Code generated by gnark DO NOT EDIT + +package gkr + +import ( + "fmt" + "math/big" + "sync" + + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" + fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + "github.com/consensys/gnark-crypto/hash" + "github.com/consensys/gnark/constraint" + "github.com/consensys/gnark/internal/gkr/gkrtypes" +) + +// circuitEvaluator evaluates all gates in a circuit for one instance +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + +// BlueprintSolve is a BLS12_381-specific blueprint for solving GKR circuit instances. +type BlueprintSolve struct { + // Circuit structure + Circuit gkrtypes.Circuit + NbInputs int + NbOutputVars int + InputWires []int + OutputWires []int + MaxNbIn int // maximum number of inputs for any gate + + // Stateful data - stored as native fr.Element + nbInstances int + assignment WireAssignment // []polynomial.MultiLin + evaluatorPool sync.Pool // pool of circuitEvaluator for reuse + + lock sync.Mutex +} + +// InitializeEvaluatorPool initializes the evaluator pool for this blueprint +func (b *BlueprintSolve) InitializeEvaluatorPool(circuit gkrtypes.Circuit, maxGateStackSize int) { + elementPool := polynomial.NewPool(maxGateStackSize) + b.evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) + } + } + return ce + }, + } +} + +// Ensures BlueprintSolve implements BlueprintStateful +var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + instanceIdx := b.nbInstances + + // Initialize assignment array if this is the first instance + if b.assignment == nil { + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + } + } + + // Grow assignment slices to accommodate this instance + for i := range b.assignment { + for len(b.assignment[i]) <= instanceIdx { + var zero fr.Element + b.assignment[i] = append(b.assignment[i], zero) + } + } + + // Read input values from instruction calldata + nbInputsInst := int(inst.Calldata[1]) + if nbInputsInst != b.NbInputs { + return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) + } + + offset := 2 + + // Get a circuit evaluator from the pool + ce := b.evaluatorPool.Get().(*circuitEvaluator) + defer b.evaluatorPool.Put(ce) + + // Process all wires in topological order (circuit is already sorted) + inputIdx := 0 + for wI := range b.Circuit { + w := &b.Circuit[wI] + + if w.IsInput() { + // Read input value from calldata and convert to fr.Element + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to fr.Element + bigInt := s.ToBigInt(val) + b.assignment[wI][instanceIdx].SetBigInt(bigInt) + inputIdx++ + } else { + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] + + // Push gate inputs + for _, inWI := range w.Inputs { + evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + } + + // Evaluate the gate + b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + } + } + + // Set output wires for the instruction (convert fr.Element to U64) + outputIdx := 0 + for _, outWI := range b.OutputWires { + var bigInt big.Int + b.assignment[outWI][instanceIdx].BigInt(&bigInt) + s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) + outputIdx++ + } + + b.nbInstances++ + return nil +} + +// Reset implements BlueprintStateful +func (b *BlueprintSolve) Reset() { + b.nbInstances = 0 + b.assignment = nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintSolve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintSolve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { + return b.NbOutputVars +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + nbInputsInst := int(inst.Calldata[1]) + offset := 2 + + for range nbInputsInst { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + for i := range b.NbOutputVars { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} + +// GetAssignment returns the assignment for a specific wire and instance (for debugging) +func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { + b.lock.Lock() + defer b.lock.Unlock() + + var zero constraint.U64 + if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + } + + // Convert fr.Element to U64 + var bigInt big.Int + b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) + return s.FromInterface(&bigInt), nil +} + +// GetAssignments returns all assignments for proving +func (b *BlueprintSolve) GetAssignments() WireAssignment { + b.lock.Lock() + defer b.lock.Unlock() + return b.assignment +} + +// GetNbInstances returns the number of instances solved +func (b *BlueprintSolve) GetNbInstances() int { + b.lock.Lock() + defer b.lock.Unlock() + return b.nbInstances +} + +// BlueprintProve is a BLS12_381-specific blueprint for generating GKR proofs. +type BlueprintProve struct { + SolveBlueprint *BlueprintSolve + HashName string + + lock sync.Mutex +} + +// Ensures BlueprintProve implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) + +// Solve implements the BlueprintSolvable interface for proving. +func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Get assignments from solve blueprint (already in fr.Element form) + assignments := b.SolveBlueprint.GetAssignments() + if len(assignments) == 0 { + return fmt.Errorf("no assignments available for proving") + } + + // Read initial challenges from instruction calldata + nbChallenges := int(inst.Calldata[2]) + challenges := make([]fr.Element, nbChallenges) + offset := 3 + for i := range nbChallenges { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to fr.Element + bigInt := s.ToBigInt(val) + challenges[i].SetBigInt(bigInt) + } + + // Convert challenges to [][]byte for Fiat-Shamir + insBytes := make([][]byte, len(challenges)) + for i := range challenges { + insBytes[i] = make([]byte, fr.Bytes) + challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + } + + // Create Fiat-Shamir settings + hsh := hash.NewHash(b.HashName + "_BLS12_381") + fsSettings := fiatshamir.WithHash(hsh, insBytes...) + + // Call the BLS12_381-specific Prove function (assignments already WireAssignment type) + proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + if err != nil { + return fmt.Errorf("bls12_381 prove failed: %w", err) + } + + // Serialize proof and convert to U64 + proofSize := 0 + for i := range proof { + for _, poly := range proof[i].partialSumPolys { + proofSize += len(poly) + } + if proof[i].finalEvalProof != nil { + proofSize += len(proof[i].finalEvalProof) + } + } + + outsBig := make([]*big.Int, proofSize) + for i := range outsBig { + outsBig[i] = new(big.Int) + } + if err := proof.SerializeToBigInts(outsBig); err != nil { + return fmt.Errorf("failed to serialize proof: %w", err) + } + + // Set output wires (convert big.Int to U64) + for i, bigVal := range outsBig { + s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + } + + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintProve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintProve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { + if len(inst.Calldata) > 1 { + return int(inst.Calldata[1]) + } + return 0 +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + if len(inst.Calldata) < 3 { + return maxLevel + 1 + } + + nbChallenges := int(inst.Calldata[2]) + offset := 3 + + for range nbChallenges { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + proofSize := int(inst.Calldata[1]) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go deleted file mode 100644 index c0b263f110..0000000000 --- a/internal/gkr/bls12-381/solver_hints.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2020-2026 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -// Code generated by gnark DO NOT EDIT - -package gkr - -import ( - "fmt" - "math/big" - "sync" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/hash" - hint "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - - "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -type circuitEvaluator struct { - evaluators []gateEvaluator // one evaluator per wire -} - -type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string - evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse -} - -type newSolvingDataSettings struct { - assignments []gkrtypes.WireAssignment -} - -type NewSolvingDataOption func(*newSolvingDataSettings) - -// WithAssignments re-use already computed wire assignments. -// If set, the number of assignments must be equal to the -// number of SolvingInfo objects fed to NewSolvingData. -// However, the caller can still choose to provide values for certain -// circuits only. For example: -// -// WithAssignments(assignment1, nil, assignment3) -// -// is a valid option for three circuits. In this case, the assignments for -// the second circuit will be computed from scratch. -func WithAssignments(assignments ...gkrtypes.WireAssignment) NewSolvingDataOption { - return func(s *newSolvingDataSettings) { - s.assignments = assignments - } -} - -// NewSolvingData converts []gkrtypes.SolvingInfo into concrete SolvingData objects: -// - The gates are loaded in accordance with their names. -// - The instances/assignments are padded into a power of 2, suitable for the multilinear extensions used -// in the GKR prover. -// Optionally, the caller can provide pre-computed assignments for each circuit using WithAssignments. -func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption) []SolvingData { - var s newSolvingDataSettings - for _, opt := range options { - opt(&s) - } - if len(s.assignments) == 0 { - s.assignments = make([]gkrtypes.WireAssignment, len(info)) - } else if len(s.assignments) != len(info) { - panic(fmt.Sprintf("expected %d assignments (one for each circuit), got %d", len(info), len(s.assignments))) - } - - d := make([]SolvingData, len(info)) - - for k := range info { - - d[k].circuit = info[k].Circuit - d[k].assignment = make(WireAssignment, len(info[k].Circuit)) - d[k].nbInstances = info[k].NbInstances - d[k].hashName = info[k].HashName - - d[k].maxNbIn = d[k].circuit.MaxGateNbIn() - - nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info[k].NbInstances))) - for i := range d[k].assignment { - d[k].assignment[i] = make([]fr.Element, nbPaddedInstances) - } - - if s.assignments[k] != nil { - if len(s.assignments[k]) != len(d[k].assignment) { - panic(fmt.Sprintf("provided assignment has %d wires, expected %d", len(s.assignments[k]), len(d[k].assignment))) - } - for i := range d[k].assignment { - if len(s.assignments[k][i]) != info[k].NbInstances { - panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignments[k][i]), info[k].NbInstances)) - } - for j := range s.assignments[k][i] { - if _, err := d[k].assignment[i][j].SetInterface(s.assignments[k][i][j]); err != nil { - panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) - } - } - // inline equivalent of repeatUntilEnd - for j := len(s.assignments[k][i]); j < nbPaddedInstances; j++ { - d[k].assignment[i][j] = d[k].assignment[i][j-1] // pad with the last value - } - } - } - - // Initialize polynomial pool for elements - // Size the pool for the worst case: max gate size across all wires - maxGateStackSize := 0 - for _, w := range d[k].circuit { - if !w.IsInput() { - stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) - if stackSize > maxGateStackSize { - maxGateStackSize = stackSize - } - } - } - - // Initialize circuit evaluator pool - circuit := d[k].circuit - elementPool := polynomial.NewPool(maxGateStackSize) - d[k].evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(circuit)), - } - for wI := range circuit { - w := &circuit[wI] - if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) - } - } - return ce - }, - } - } - - return d -} - -// this module assumes that wire and instance indexes respect dependencies - -// GetAssignmentHint generates a hint that returns the value of a wire of a circuit at an instance. -// It is intended for use in the debugging function gkrapi.API.GetValue. -func GetAssignmentHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - if len(ins) != 4 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: GKR sub-circuit index, wire index, instance index, and dummy dependency enforcer") - } - if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { - return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") - } - data := &data[ins[0].Uint64()] - wireI := ins[1].Uint64() - instanceI := ins[2].Uint64() - - data.assignment[wireI][instanceI].BigInt(outs[0]) - - return nil - } -} - -// SolveHint generate a hint that computes the assignments for all wires in a circuit instance. -// It is intended for use in gkrapi.API.AddInstance. -func SolveHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: instance index of the circuit to solve - // - rest: values for input wires - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - instanceI := ins[1].Uint64() - - // indices for reading inputs and outputs - outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - - // Get a circuit evaluator from the pool - ce := data.evaluatorPool.Get().(*circuitEvaluator) - defer data.evaluatorPool.Put(ce) - - // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, - // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. - for wI := range data.circuit { - w := &data.circuit[wI] - if w.IsInput() { - // read from provided input - // there is no gate to compute for input wires. We only need to set them in the assignment. - data.assignment[wI][instanceI].SetBigInt(ins[insI]) - insI++ - } else { - // Get evaluator for this wire from the circuit evaluator - evaluator := &ce.evaluators[wI] - - // Push gate inputs - for _, inWI := range w.Inputs { - evaluator.pushInput(&data.assignment[inWI][instanceI]) - } - - // Evaluate the gate - data.assignment[wI][instanceI].Set(evaluator.evaluate()) - } - if w.IsOutput() { - // write to provided output. - // NB! even if it is output wire, then it was already computed - // above. So the current condition is not exclusive with the - // previous one. - data.assignment[wI][instanceI].BigInt(outs[outsI]) - outsI++ - } - } - - return nil - } -} - -// ProveHint generates a hint that produces the GKR proof using the computed assignments contained in data. -// It is meant for use in gkrapi.Circuit.finalize. -func ProveHint(data []SolvingData) hint.Hint { - - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: dummy, just to ensure the solver's work is done before the prover is called. - // - rest: initial Fiat-Shamir challenge (provided by the caller, typically a commitment to all inputs and outputs) - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - hashName := data.hashName - // drop the first input which indicates the current circuit index - ins = ins[1:] - - data.assignment.repeatUntilEnd(data.nbInstances) - - // The second input (the dummy) is an output from the last call to AddInstance. - // We need to ensure that the circuit has been completely solved for all instances of all wires. - // By feeding one of the output of the last call to AddInstance, we ensure that the gnark solver - // schedules proof generation after all solving is done. - // The rest constitute the initial fiat shamir challenge. - insBytes := make([][]byte, len(ins)-1) - for i := 1; i < len(ins); i++ { - insBytes[i-1] = make([]byte, fr.Bytes) - ins[i].FillBytes(insBytes[i-1]) - } - - hsh := hash.NewHash(hashName + "_BLS12_381") - - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) - if err != nil { - return err - } - - return proof.SerializeToBigInts(outs) - - } -} - -// repeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. -// e.g. {{1, 2, 3}, {4, 5, 6}}.repeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} -func (a WireAssignment) repeatUntilEnd(n int) { - for i := range a { - for j := n; j < len(a[i]); j++ { - a[i][j] = a[i][j-1] - } - } -} diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go new file mode 100644 index 0000000000..cdf9daf70f --- /dev/null +++ b/internal/gkr/bls24-315/blueprint.go @@ -0,0 +1,355 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// Code generated by gnark DO NOT EDIT + +package gkr + +import ( + "fmt" + "math/big" + "sync" + + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/polynomial" + fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + "github.com/consensys/gnark-crypto/hash" + "github.com/consensys/gnark/constraint" + "github.com/consensys/gnark/internal/gkr/gkrtypes" +) + +// circuitEvaluator evaluates all gates in a circuit for one instance +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + +// BlueprintSolve is a BLS24_315-specific blueprint for solving GKR circuit instances. +type BlueprintSolve struct { + // Circuit structure + Circuit gkrtypes.Circuit + NbInputs int + NbOutputVars int + InputWires []int + OutputWires []int + MaxNbIn int // maximum number of inputs for any gate + + // Stateful data - stored as native fr.Element + nbInstances int + assignment WireAssignment // []polynomial.MultiLin + evaluatorPool sync.Pool // pool of circuitEvaluator for reuse + + lock sync.Mutex +} + +// InitializeEvaluatorPool initializes the evaluator pool for this blueprint +func (b *BlueprintSolve) InitializeEvaluatorPool(circuit gkrtypes.Circuit, maxGateStackSize int) { + elementPool := polynomial.NewPool(maxGateStackSize) + b.evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) + } + } + return ce + }, + } +} + +// Ensures BlueprintSolve implements BlueprintStateful +var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + instanceIdx := b.nbInstances + + // Initialize assignment array if this is the first instance + if b.assignment == nil { + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + } + } + + // Grow assignment slices to accommodate this instance + for i := range b.assignment { + for len(b.assignment[i]) <= instanceIdx { + var zero fr.Element + b.assignment[i] = append(b.assignment[i], zero) + } + } + + // Read input values from instruction calldata + nbInputsInst := int(inst.Calldata[1]) + if nbInputsInst != b.NbInputs { + return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) + } + + offset := 2 + + // Get a circuit evaluator from the pool + ce := b.evaluatorPool.Get().(*circuitEvaluator) + defer b.evaluatorPool.Put(ce) + + // Process all wires in topological order (circuit is already sorted) + inputIdx := 0 + for wI := range b.Circuit { + w := &b.Circuit[wI] + + if w.IsInput() { + // Read input value from calldata and convert to fr.Element + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to fr.Element + bigInt := s.ToBigInt(val) + b.assignment[wI][instanceIdx].SetBigInt(bigInt) + inputIdx++ + } else { + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] + + // Push gate inputs + for _, inWI := range w.Inputs { + evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + } + + // Evaluate the gate + b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + } + } + + // Set output wires for the instruction (convert fr.Element to U64) + outputIdx := 0 + for _, outWI := range b.OutputWires { + var bigInt big.Int + b.assignment[outWI][instanceIdx].BigInt(&bigInt) + s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) + outputIdx++ + } + + b.nbInstances++ + return nil +} + +// Reset implements BlueprintStateful +func (b *BlueprintSolve) Reset() { + b.nbInstances = 0 + b.assignment = nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintSolve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintSolve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { + return b.NbOutputVars +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + nbInputsInst := int(inst.Calldata[1]) + offset := 2 + + for range nbInputsInst { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + for i := range b.NbOutputVars { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} + +// GetAssignment returns the assignment for a specific wire and instance (for debugging) +func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { + b.lock.Lock() + defer b.lock.Unlock() + + var zero constraint.U64 + if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + } + + // Convert fr.Element to U64 + var bigInt big.Int + b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) + return s.FromInterface(&bigInt), nil +} + +// GetAssignments returns all assignments for proving +func (b *BlueprintSolve) GetAssignments() WireAssignment { + b.lock.Lock() + defer b.lock.Unlock() + return b.assignment +} + +// GetNbInstances returns the number of instances solved +func (b *BlueprintSolve) GetNbInstances() int { + b.lock.Lock() + defer b.lock.Unlock() + return b.nbInstances +} + +// BlueprintProve is a BLS24_315-specific blueprint for generating GKR proofs. +type BlueprintProve struct { + SolveBlueprint *BlueprintSolve + HashName string + + lock sync.Mutex +} + +// Ensures BlueprintProve implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) + +// Solve implements the BlueprintSolvable interface for proving. +func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Get assignments from solve blueprint (already in fr.Element form) + assignments := b.SolveBlueprint.GetAssignments() + if len(assignments) == 0 { + return fmt.Errorf("no assignments available for proving") + } + + // Read initial challenges from instruction calldata + nbChallenges := int(inst.Calldata[2]) + challenges := make([]fr.Element, nbChallenges) + offset := 3 + for i := range nbChallenges { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to fr.Element + bigInt := s.ToBigInt(val) + challenges[i].SetBigInt(bigInt) + } + + // Convert challenges to [][]byte for Fiat-Shamir + insBytes := make([][]byte, len(challenges)) + for i := range challenges { + insBytes[i] = make([]byte, fr.Bytes) + challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + } + + // Create Fiat-Shamir settings + hsh := hash.NewHash(b.HashName + "_BLS24_315") + fsSettings := fiatshamir.WithHash(hsh, insBytes...) + + // Call the BLS24_315-specific Prove function (assignments already WireAssignment type) + proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + if err != nil { + return fmt.Errorf("bls24_315 prove failed: %w", err) + } + + // Serialize proof and convert to U64 + proofSize := 0 + for i := range proof { + for _, poly := range proof[i].partialSumPolys { + proofSize += len(poly) + } + if proof[i].finalEvalProof != nil { + proofSize += len(proof[i].finalEvalProof) + } + } + + outsBig := make([]*big.Int, proofSize) + for i := range outsBig { + outsBig[i] = new(big.Int) + } + if err := proof.SerializeToBigInts(outsBig); err != nil { + return fmt.Errorf("failed to serialize proof: %w", err) + } + + // Set output wires (convert big.Int to U64) + for i, bigVal := range outsBig { + s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + } + + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintProve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintProve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { + if len(inst.Calldata) > 1 { + return int(inst.Calldata[1]) + } + return 0 +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + if len(inst.Calldata) < 3 { + return maxLevel + 1 + } + + nbChallenges := int(inst.Calldata[2]) + offset := 3 + + for range nbChallenges { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + proofSize := int(inst.Calldata[1]) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go deleted file mode 100644 index ca2490a77d..0000000000 --- a/internal/gkr/bls24-315/solver_hints.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2020-2026 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -// Code generated by gnark DO NOT EDIT - -package gkr - -import ( - "fmt" - "math/big" - "sync" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/hash" - hint "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - - "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" - "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -type circuitEvaluator struct { - evaluators []gateEvaluator // one evaluator per wire -} - -type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string - evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse -} - -type newSolvingDataSettings struct { - assignments []gkrtypes.WireAssignment -} - -type NewSolvingDataOption func(*newSolvingDataSettings) - -// WithAssignments re-use already computed wire assignments. -// If set, the number of assignments must be equal to the -// number of SolvingInfo objects fed to NewSolvingData. -// However, the caller can still choose to provide values for certain -// circuits only. For example: -// -// WithAssignments(assignment1, nil, assignment3) -// -// is a valid option for three circuits. In this case, the assignments for -// the second circuit will be computed from scratch. -func WithAssignments(assignments ...gkrtypes.WireAssignment) NewSolvingDataOption { - return func(s *newSolvingDataSettings) { - s.assignments = assignments - } -} - -// NewSolvingData converts []gkrtypes.SolvingInfo into concrete SolvingData objects: -// - The gates are loaded in accordance with their names. -// - The instances/assignments are padded into a power of 2, suitable for the multilinear extensions used -// in the GKR prover. -// Optionally, the caller can provide pre-computed assignments for each circuit using WithAssignments. -func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption) []SolvingData { - var s newSolvingDataSettings - for _, opt := range options { - opt(&s) - } - if len(s.assignments) == 0 { - s.assignments = make([]gkrtypes.WireAssignment, len(info)) - } else if len(s.assignments) != len(info) { - panic(fmt.Sprintf("expected %d assignments (one for each circuit), got %d", len(info), len(s.assignments))) - } - - d := make([]SolvingData, len(info)) - - for k := range info { - - d[k].circuit = info[k].Circuit - d[k].assignment = make(WireAssignment, len(info[k].Circuit)) - d[k].nbInstances = info[k].NbInstances - d[k].hashName = info[k].HashName - - d[k].maxNbIn = d[k].circuit.MaxGateNbIn() - - nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info[k].NbInstances))) - for i := range d[k].assignment { - d[k].assignment[i] = make([]fr.Element, nbPaddedInstances) - } - - if s.assignments[k] != nil { - if len(s.assignments[k]) != len(d[k].assignment) { - panic(fmt.Sprintf("provided assignment has %d wires, expected %d", len(s.assignments[k]), len(d[k].assignment))) - } - for i := range d[k].assignment { - if len(s.assignments[k][i]) != info[k].NbInstances { - panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignments[k][i]), info[k].NbInstances)) - } - for j := range s.assignments[k][i] { - if _, err := d[k].assignment[i][j].SetInterface(s.assignments[k][i][j]); err != nil { - panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) - } - } - // inline equivalent of repeatUntilEnd - for j := len(s.assignments[k][i]); j < nbPaddedInstances; j++ { - d[k].assignment[i][j] = d[k].assignment[i][j-1] // pad with the last value - } - } - } - - // Initialize polynomial pool for elements - // Size the pool for the worst case: max gate size across all wires - maxGateStackSize := 0 - for _, w := range d[k].circuit { - if !w.IsInput() { - stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) - if stackSize > maxGateStackSize { - maxGateStackSize = stackSize - } - } - } - - // Initialize circuit evaluator pool - circuit := d[k].circuit - elementPool := polynomial.NewPool(maxGateStackSize) - d[k].evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(circuit)), - } - for wI := range circuit { - w := &circuit[wI] - if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) - } - } - return ce - }, - } - } - - return d -} - -// this module assumes that wire and instance indexes respect dependencies - -// GetAssignmentHint generates a hint that returns the value of a wire of a circuit at an instance. -// It is intended for use in the debugging function gkrapi.API.GetValue. -func GetAssignmentHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - if len(ins) != 4 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: GKR sub-circuit index, wire index, instance index, and dummy dependency enforcer") - } - if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { - return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") - } - data := &data[ins[0].Uint64()] - wireI := ins[1].Uint64() - instanceI := ins[2].Uint64() - - data.assignment[wireI][instanceI].BigInt(outs[0]) - - return nil - } -} - -// SolveHint generate a hint that computes the assignments for all wires in a circuit instance. -// It is intended for use in gkrapi.API.AddInstance. -func SolveHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: instance index of the circuit to solve - // - rest: values for input wires - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - instanceI := ins[1].Uint64() - - // indices for reading inputs and outputs - outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - - // Get a circuit evaluator from the pool - ce := data.evaluatorPool.Get().(*circuitEvaluator) - defer data.evaluatorPool.Put(ce) - - // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, - // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. - for wI := range data.circuit { - w := &data.circuit[wI] - if w.IsInput() { - // read from provided input - // there is no gate to compute for input wires. We only need to set them in the assignment. - data.assignment[wI][instanceI].SetBigInt(ins[insI]) - insI++ - } else { - // Get evaluator for this wire from the circuit evaluator - evaluator := &ce.evaluators[wI] - - // Push gate inputs - for _, inWI := range w.Inputs { - evaluator.pushInput(&data.assignment[inWI][instanceI]) - } - - // Evaluate the gate - data.assignment[wI][instanceI].Set(evaluator.evaluate()) - } - if w.IsOutput() { - // write to provided output. - // NB! even if it is output wire, then it was already computed - // above. So the current condition is not exclusive with the - // previous one. - data.assignment[wI][instanceI].BigInt(outs[outsI]) - outsI++ - } - } - - return nil - } -} - -// ProveHint generates a hint that produces the GKR proof using the computed assignments contained in data. -// It is meant for use in gkrapi.Circuit.finalize. -func ProveHint(data []SolvingData) hint.Hint { - - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: dummy, just to ensure the solver's work is done before the prover is called. - // - rest: initial Fiat-Shamir challenge (provided by the caller, typically a commitment to all inputs and outputs) - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - hashName := data.hashName - // drop the first input which indicates the current circuit index - ins = ins[1:] - - data.assignment.repeatUntilEnd(data.nbInstances) - - // The second input (the dummy) is an output from the last call to AddInstance. - // We need to ensure that the circuit has been completely solved for all instances of all wires. - // By feeding one of the output of the last call to AddInstance, we ensure that the gnark solver - // schedules proof generation after all solving is done. - // The rest constitute the initial fiat shamir challenge. - insBytes := make([][]byte, len(ins)-1) - for i := 1; i < len(ins); i++ { - insBytes[i-1] = make([]byte, fr.Bytes) - ins[i].FillBytes(insBytes[i-1]) - } - - hsh := hash.NewHash(hashName + "_BLS24_315") - - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) - if err != nil { - return err - } - - return proof.SerializeToBigInts(outs) - - } -} - -// repeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. -// e.g. {{1, 2, 3}, {4, 5, 6}}.repeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} -func (a WireAssignment) repeatUntilEnd(n int) { - for i := range a { - for j := n; j < len(a[i]); j++ { - a[i][j] = a[i][j-1] - } - } -} diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go new file mode 100644 index 0000000000..cf83c045fb --- /dev/null +++ b/internal/gkr/bls24-317/blueprint.go @@ -0,0 +1,355 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// Code generated by gnark DO NOT EDIT + +package gkr + +import ( + "fmt" + "math/big" + "sync" + + "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" + "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/polynomial" + fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + "github.com/consensys/gnark-crypto/hash" + "github.com/consensys/gnark/constraint" + "github.com/consensys/gnark/internal/gkr/gkrtypes" +) + +// circuitEvaluator evaluates all gates in a circuit for one instance +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + +// BlueprintSolve is a BLS24_317-specific blueprint for solving GKR circuit instances. +type BlueprintSolve struct { + // Circuit structure + Circuit gkrtypes.Circuit + NbInputs int + NbOutputVars int + InputWires []int + OutputWires []int + MaxNbIn int // maximum number of inputs for any gate + + // Stateful data - stored as native fr.Element + nbInstances int + assignment WireAssignment // []polynomial.MultiLin + evaluatorPool sync.Pool // pool of circuitEvaluator for reuse + + lock sync.Mutex +} + +// InitializeEvaluatorPool initializes the evaluator pool for this blueprint +func (b *BlueprintSolve) InitializeEvaluatorPool(circuit gkrtypes.Circuit, maxGateStackSize int) { + elementPool := polynomial.NewPool(maxGateStackSize) + b.evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) + } + } + return ce + }, + } +} + +// Ensures BlueprintSolve implements BlueprintStateful +var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + instanceIdx := b.nbInstances + + // Initialize assignment array if this is the first instance + if b.assignment == nil { + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + } + } + + // Grow assignment slices to accommodate this instance + for i := range b.assignment { + for len(b.assignment[i]) <= instanceIdx { + var zero fr.Element + b.assignment[i] = append(b.assignment[i], zero) + } + } + + // Read input values from instruction calldata + nbInputsInst := int(inst.Calldata[1]) + if nbInputsInst != b.NbInputs { + return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) + } + + offset := 2 + + // Get a circuit evaluator from the pool + ce := b.evaluatorPool.Get().(*circuitEvaluator) + defer b.evaluatorPool.Put(ce) + + // Process all wires in topological order (circuit is already sorted) + inputIdx := 0 + for wI := range b.Circuit { + w := &b.Circuit[wI] + + if w.IsInput() { + // Read input value from calldata and convert to fr.Element + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to fr.Element + bigInt := s.ToBigInt(val) + b.assignment[wI][instanceIdx].SetBigInt(bigInt) + inputIdx++ + } else { + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] + + // Push gate inputs + for _, inWI := range w.Inputs { + evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + } + + // Evaluate the gate + b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + } + } + + // Set output wires for the instruction (convert fr.Element to U64) + outputIdx := 0 + for _, outWI := range b.OutputWires { + var bigInt big.Int + b.assignment[outWI][instanceIdx].BigInt(&bigInt) + s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) + outputIdx++ + } + + b.nbInstances++ + return nil +} + +// Reset implements BlueprintStateful +func (b *BlueprintSolve) Reset() { + b.nbInstances = 0 + b.assignment = nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintSolve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintSolve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { + return b.NbOutputVars +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + nbInputsInst := int(inst.Calldata[1]) + offset := 2 + + for range nbInputsInst { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + for i := range b.NbOutputVars { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} + +// GetAssignment returns the assignment for a specific wire and instance (for debugging) +func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { + b.lock.Lock() + defer b.lock.Unlock() + + var zero constraint.U64 + if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + } + + // Convert fr.Element to U64 + var bigInt big.Int + b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) + return s.FromInterface(&bigInt), nil +} + +// GetAssignments returns all assignments for proving +func (b *BlueprintSolve) GetAssignments() WireAssignment { + b.lock.Lock() + defer b.lock.Unlock() + return b.assignment +} + +// GetNbInstances returns the number of instances solved +func (b *BlueprintSolve) GetNbInstances() int { + b.lock.Lock() + defer b.lock.Unlock() + return b.nbInstances +} + +// BlueprintProve is a BLS24_317-specific blueprint for generating GKR proofs. +type BlueprintProve struct { + SolveBlueprint *BlueprintSolve + HashName string + + lock sync.Mutex +} + +// Ensures BlueprintProve implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) + +// Solve implements the BlueprintSolvable interface for proving. +func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Get assignments from solve blueprint (already in fr.Element form) + assignments := b.SolveBlueprint.GetAssignments() + if len(assignments) == 0 { + return fmt.Errorf("no assignments available for proving") + } + + // Read initial challenges from instruction calldata + nbChallenges := int(inst.Calldata[2]) + challenges := make([]fr.Element, nbChallenges) + offset := 3 + for i := range nbChallenges { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to fr.Element + bigInt := s.ToBigInt(val) + challenges[i].SetBigInt(bigInt) + } + + // Convert challenges to [][]byte for Fiat-Shamir + insBytes := make([][]byte, len(challenges)) + for i := range challenges { + insBytes[i] = make([]byte, fr.Bytes) + challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + } + + // Create Fiat-Shamir settings + hsh := hash.NewHash(b.HashName + "_BLS24_317") + fsSettings := fiatshamir.WithHash(hsh, insBytes...) + + // Call the BLS24_317-specific Prove function (assignments already WireAssignment type) + proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + if err != nil { + return fmt.Errorf("bls24_317 prove failed: %w", err) + } + + // Serialize proof and convert to U64 + proofSize := 0 + for i := range proof { + for _, poly := range proof[i].partialSumPolys { + proofSize += len(poly) + } + if proof[i].finalEvalProof != nil { + proofSize += len(proof[i].finalEvalProof) + } + } + + outsBig := make([]*big.Int, proofSize) + for i := range outsBig { + outsBig[i] = new(big.Int) + } + if err := proof.SerializeToBigInts(outsBig); err != nil { + return fmt.Errorf("failed to serialize proof: %w", err) + } + + // Set output wires (convert big.Int to U64) + for i, bigVal := range outsBig { + s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + } + + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintProve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintProve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { + if len(inst.Calldata) > 1 { + return int(inst.Calldata[1]) + } + return 0 +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + if len(inst.Calldata) < 3 { + return maxLevel + 1 + } + + nbChallenges := int(inst.Calldata[2]) + offset := 3 + + for range nbChallenges { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + proofSize := int(inst.Calldata[1]) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go deleted file mode 100644 index 8763d0456b..0000000000 --- a/internal/gkr/bls24-317/solver_hints.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2020-2026 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -// Code generated by gnark DO NOT EDIT - -package gkr - -import ( - "fmt" - "math/big" - "sync" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/hash" - hint "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - - "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" - "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -type circuitEvaluator struct { - evaluators []gateEvaluator // one evaluator per wire -} - -type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string - evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse -} - -type newSolvingDataSettings struct { - assignments []gkrtypes.WireAssignment -} - -type NewSolvingDataOption func(*newSolvingDataSettings) - -// WithAssignments re-use already computed wire assignments. -// If set, the number of assignments must be equal to the -// number of SolvingInfo objects fed to NewSolvingData. -// However, the caller can still choose to provide values for certain -// circuits only. For example: -// -// WithAssignments(assignment1, nil, assignment3) -// -// is a valid option for three circuits. In this case, the assignments for -// the second circuit will be computed from scratch. -func WithAssignments(assignments ...gkrtypes.WireAssignment) NewSolvingDataOption { - return func(s *newSolvingDataSettings) { - s.assignments = assignments - } -} - -// NewSolvingData converts []gkrtypes.SolvingInfo into concrete SolvingData objects: -// - The gates are loaded in accordance with their names. -// - The instances/assignments are padded into a power of 2, suitable for the multilinear extensions used -// in the GKR prover. -// Optionally, the caller can provide pre-computed assignments for each circuit using WithAssignments. -func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption) []SolvingData { - var s newSolvingDataSettings - for _, opt := range options { - opt(&s) - } - if len(s.assignments) == 0 { - s.assignments = make([]gkrtypes.WireAssignment, len(info)) - } else if len(s.assignments) != len(info) { - panic(fmt.Sprintf("expected %d assignments (one for each circuit), got %d", len(info), len(s.assignments))) - } - - d := make([]SolvingData, len(info)) - - for k := range info { - - d[k].circuit = info[k].Circuit - d[k].assignment = make(WireAssignment, len(info[k].Circuit)) - d[k].nbInstances = info[k].NbInstances - d[k].hashName = info[k].HashName - - d[k].maxNbIn = d[k].circuit.MaxGateNbIn() - - nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info[k].NbInstances))) - for i := range d[k].assignment { - d[k].assignment[i] = make([]fr.Element, nbPaddedInstances) - } - - if s.assignments[k] != nil { - if len(s.assignments[k]) != len(d[k].assignment) { - panic(fmt.Sprintf("provided assignment has %d wires, expected %d", len(s.assignments[k]), len(d[k].assignment))) - } - for i := range d[k].assignment { - if len(s.assignments[k][i]) != info[k].NbInstances { - panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignments[k][i]), info[k].NbInstances)) - } - for j := range s.assignments[k][i] { - if _, err := d[k].assignment[i][j].SetInterface(s.assignments[k][i][j]); err != nil { - panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) - } - } - // inline equivalent of repeatUntilEnd - for j := len(s.assignments[k][i]); j < nbPaddedInstances; j++ { - d[k].assignment[i][j] = d[k].assignment[i][j-1] // pad with the last value - } - } - } - - // Initialize polynomial pool for elements - // Size the pool for the worst case: max gate size across all wires - maxGateStackSize := 0 - for _, w := range d[k].circuit { - if !w.IsInput() { - stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) - if stackSize > maxGateStackSize { - maxGateStackSize = stackSize - } - } - } - - // Initialize circuit evaluator pool - circuit := d[k].circuit - elementPool := polynomial.NewPool(maxGateStackSize) - d[k].evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(circuit)), - } - for wI := range circuit { - w := &circuit[wI] - if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) - } - } - return ce - }, - } - } - - return d -} - -// this module assumes that wire and instance indexes respect dependencies - -// GetAssignmentHint generates a hint that returns the value of a wire of a circuit at an instance. -// It is intended for use in the debugging function gkrapi.API.GetValue. -func GetAssignmentHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - if len(ins) != 4 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: GKR sub-circuit index, wire index, instance index, and dummy dependency enforcer") - } - if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { - return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") - } - data := &data[ins[0].Uint64()] - wireI := ins[1].Uint64() - instanceI := ins[2].Uint64() - - data.assignment[wireI][instanceI].BigInt(outs[0]) - - return nil - } -} - -// SolveHint generate a hint that computes the assignments for all wires in a circuit instance. -// It is intended for use in gkrapi.API.AddInstance. -func SolveHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: instance index of the circuit to solve - // - rest: values for input wires - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - instanceI := ins[1].Uint64() - - // indices for reading inputs and outputs - outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - - // Get a circuit evaluator from the pool - ce := data.evaluatorPool.Get().(*circuitEvaluator) - defer data.evaluatorPool.Put(ce) - - // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, - // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. - for wI := range data.circuit { - w := &data.circuit[wI] - if w.IsInput() { - // read from provided input - // there is no gate to compute for input wires. We only need to set them in the assignment. - data.assignment[wI][instanceI].SetBigInt(ins[insI]) - insI++ - } else { - // Get evaluator for this wire from the circuit evaluator - evaluator := &ce.evaluators[wI] - - // Push gate inputs - for _, inWI := range w.Inputs { - evaluator.pushInput(&data.assignment[inWI][instanceI]) - } - - // Evaluate the gate - data.assignment[wI][instanceI].Set(evaluator.evaluate()) - } - if w.IsOutput() { - // write to provided output. - // NB! even if it is output wire, then it was already computed - // above. So the current condition is not exclusive with the - // previous one. - data.assignment[wI][instanceI].BigInt(outs[outsI]) - outsI++ - } - } - - return nil - } -} - -// ProveHint generates a hint that produces the GKR proof using the computed assignments contained in data. -// It is meant for use in gkrapi.Circuit.finalize. -func ProveHint(data []SolvingData) hint.Hint { - - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: dummy, just to ensure the solver's work is done before the prover is called. - // - rest: initial Fiat-Shamir challenge (provided by the caller, typically a commitment to all inputs and outputs) - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - hashName := data.hashName - // drop the first input which indicates the current circuit index - ins = ins[1:] - - data.assignment.repeatUntilEnd(data.nbInstances) - - // The second input (the dummy) is an output from the last call to AddInstance. - // We need to ensure that the circuit has been completely solved for all instances of all wires. - // By feeding one of the output of the last call to AddInstance, we ensure that the gnark solver - // schedules proof generation after all solving is done. - // The rest constitute the initial fiat shamir challenge. - insBytes := make([][]byte, len(ins)-1) - for i := 1; i < len(ins); i++ { - insBytes[i-1] = make([]byte, fr.Bytes) - ins[i].FillBytes(insBytes[i-1]) - } - - hsh := hash.NewHash(hashName + "_BLS24_317") - - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) - if err != nil { - return err - } - - return proof.SerializeToBigInts(outs) - - } -} - -// repeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. -// e.g. {{1, 2, 3}, {4, 5, 6}}.repeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} -func (a WireAssignment) repeatUntilEnd(n int) { - for i := range a { - for j := n; j < len(a[i]); j++ { - a[i][j] = a[i][j-1] - } - } -} diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index ebb54b3eec..c3b21de03b 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -1,6 +1,8 @@ // Copyright 2020-2026 Consensys Software Inc. // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. +// Code generated by gnark DO NOT EDIT + package gkr import ( @@ -21,8 +23,8 @@ type circuitEvaluator struct { evaluators []gateEvaluator // one evaluator per wire } -// BlueprintGKRSolve is a BN254-specific blueprint for solving GKR circuit instances. -type BlueprintGKRSolve struct { +// BlueprintSolve is a BN254-specific blueprint for solving GKR circuit instances. +type BlueprintSolve struct { // Circuit structure Circuit gkrtypes.Circuit NbInputs int @@ -40,7 +42,7 @@ type BlueprintGKRSolve struct { } // InitializeEvaluatorPool initializes the evaluator pool for this blueprint -func (b *BlueprintGKRSolve) InitializeEvaluatorPool(circuit gkrtypes.Circuit, maxGateStackSize int) { +func (b *BlueprintSolve) InitializeEvaluatorPool(circuit gkrtypes.Circuit, maxGateStackSize int) { elementPool := polynomial.NewPool(maxGateStackSize) b.evaluatorPool = sync.Pool{ New: func() interface{} { @@ -58,11 +60,11 @@ func (b *BlueprintGKRSolve) InitializeEvaluatorPool(circuit gkrtypes.Circuit, ma } } -// Ensures BlueprintGKRSolve implements BlueprintStateful -var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintGKRSolve)(nil) +// Ensures BlueprintSolve implements BlueprintStateful +var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) // Solve implements the BlueprintStateful interface. -func (b *BlueprintGKRSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { b.lock.Lock() defer b.lock.Unlock() @@ -138,28 +140,28 @@ func (b *BlueprintGKRSolve) Solve(s constraint.Solver[constraint.U64], inst cons } // Reset implements BlueprintStateful -func (b *BlueprintGKRSolve) Reset() { +func (b *BlueprintSolve) Reset() { b.nbInstances = 0 b.assignment = nil } // CalldataSize implements Blueprint -func (b *BlueprintGKRSolve) CalldataSize() int { +func (b *BlueprintSolve) CalldataSize() int { return -1 // variable size } // NbConstraints implements Blueprint -func (b *BlueprintGKRSolve) NbConstraints() int { +func (b *BlueprintSolve) NbConstraints() int { return 0 } // NbOutputs implements Blueprint -func (b *BlueprintGKRSolve) NbOutputs(inst constraint.Instruction) int { +func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { return b.NbOutputVars } // UpdateInstructionTree implements Blueprint -func (b *BlueprintGKRSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { +func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset nbInputsInst := int(inst.Calldata[1]) @@ -190,7 +192,7 @@ func (b *BlueprintGKRSolve) UpdateInstructionTree(inst constraint.Instruction, t } // GetAssignment returns the assignment for a specific wire and instance (for debugging) -func (b *BlueprintGKRSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { +func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { b.lock.Lock() defer b.lock.Unlock() @@ -206,32 +208,32 @@ func (b *BlueprintGKRSolve) GetAssignment(s constraint.Solver[constraint.U64], w } // GetAssignments returns all assignments for proving -func (b *BlueprintGKRSolve) GetAssignments() WireAssignment { +func (b *BlueprintSolve) GetAssignments() WireAssignment { b.lock.Lock() defer b.lock.Unlock() return b.assignment } // GetNbInstances returns the number of instances solved -func (b *BlueprintGKRSolve) GetNbInstances() int { +func (b *BlueprintSolve) GetNbInstances() int { b.lock.Lock() defer b.lock.Unlock() return b.nbInstances } -// BlueprintGKRProve is a BN254-specific blueprint for generating GKR proofs. -type BlueprintGKRProve struct { - SolveBlueprint *BlueprintGKRSolve +// BlueprintProve is a BN254-specific blueprint for generating GKR proofs. +type BlueprintProve struct { + SolveBlueprint *BlueprintSolve HashName string lock sync.Mutex } -// Ensures BlueprintGKRProve implements BlueprintSolvable -var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGKRProve)(nil) +// Ensures BlueprintProve implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) // Solve implements the BlueprintSolvable interface for proving. -func (b *BlueprintGKRProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { +func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { b.lock.Lock() defer b.lock.Unlock() @@ -299,17 +301,17 @@ func (b *BlueprintGKRProve) Solve(s constraint.Solver[constraint.U64], inst cons } // CalldataSize implements Blueprint -func (b *BlueprintGKRProve) CalldataSize() int { +func (b *BlueprintProve) CalldataSize() int { return -1 // variable size } // NbConstraints implements Blueprint -func (b *BlueprintGKRProve) NbConstraints() int { +func (b *BlueprintProve) NbConstraints() int { return 0 } // NbOutputs implements Blueprint -func (b *BlueprintGKRProve) NbOutputs(inst constraint.Instruction) int { +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { if len(inst.Calldata) > 1 { return int(inst.Calldata[1]) } @@ -317,7 +319,7 @@ func (b *BlueprintGKRProve) NbOutputs(inst constraint.Instruction) int { } // UpdateInstructionTree implements Blueprint -func (b *BlueprintGKRProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { +func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset if len(inst.Calldata) < 3 { diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go deleted file mode 100644 index a18a06a829..0000000000 --- a/internal/gkr/bn254/solver_hints.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2020-2026 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -// Code generated by gnark DO NOT EDIT - -package gkr - -import ( - "fmt" - "math/big" - "sync" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/hash" - hint "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - - "github.com/consensys/gnark-crypto/ecc/bn254/fr" - "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -type circuitEvaluator struct { - evaluators []gateEvaluator // one evaluator per wire -} - -type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string - evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse -} - -type newSolvingDataSettings struct { - assignments []gkrtypes.WireAssignment -} - -type NewSolvingDataOption func(*newSolvingDataSettings) - -// WithAssignments re-use already computed wire assignments. -// If set, the number of assignments must be equal to the -// number of SolvingInfo objects fed to NewSolvingData. -// However, the caller can still choose to provide values for certain -// circuits only. For example: -// -// WithAssignments(assignment1, nil, assignment3) -// -// is a valid option for three circuits. In this case, the assignments for -// the second circuit will be computed from scratch. -func WithAssignments(assignments ...gkrtypes.WireAssignment) NewSolvingDataOption { - return func(s *newSolvingDataSettings) { - s.assignments = assignments - } -} - -// NewSolvingData converts []gkrtypes.SolvingInfo into concrete SolvingData objects: -// - The gates are loaded in accordance with their names. -// - The instances/assignments are padded into a power of 2, suitable for the multilinear extensions used -// in the GKR prover. -// Optionally, the caller can provide pre-computed assignments for each circuit using WithAssignments. -func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption) []SolvingData { - var s newSolvingDataSettings - for _, opt := range options { - opt(&s) - } - if len(s.assignments) == 0 { - s.assignments = make([]gkrtypes.WireAssignment, len(info)) - } else if len(s.assignments) != len(info) { - panic(fmt.Sprintf("expected %d assignments (one for each circuit), got %d", len(info), len(s.assignments))) - } - - d := make([]SolvingData, len(info)) - - for k := range info { - - d[k].circuit = info[k].Circuit - d[k].assignment = make(WireAssignment, len(info[k].Circuit)) - d[k].nbInstances = info[k].NbInstances - d[k].hashName = info[k].HashName - - d[k].maxNbIn = d[k].circuit.MaxGateNbIn() - - nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info[k].NbInstances))) - for i := range d[k].assignment { - d[k].assignment[i] = make([]fr.Element, nbPaddedInstances) - } - - if s.assignments[k] != nil { - if len(s.assignments[k]) != len(d[k].assignment) { - panic(fmt.Sprintf("provided assignment has %d wires, expected %d", len(s.assignments[k]), len(d[k].assignment))) - } - for i := range d[k].assignment { - if len(s.assignments[k][i]) != info[k].NbInstances { - panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignments[k][i]), info[k].NbInstances)) - } - for j := range s.assignments[k][i] { - if _, err := d[k].assignment[i][j].SetInterface(s.assignments[k][i][j]); err != nil { - panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) - } - } - // inline equivalent of repeatUntilEnd - for j := len(s.assignments[k][i]); j < nbPaddedInstances; j++ { - d[k].assignment[i][j] = d[k].assignment[i][j-1] // pad with the last value - } - } - } - - // Initialize polynomial pool for elements - // Size the pool for the worst case: max gate size across all wires - maxGateStackSize := 0 - for _, w := range d[k].circuit { - if !w.IsInput() { - stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) - if stackSize > maxGateStackSize { - maxGateStackSize = stackSize - } - } - } - - // Initialize circuit evaluator pool - circuit := d[k].circuit - elementPool := polynomial.NewPool(maxGateStackSize) - d[k].evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(circuit)), - } - for wI := range circuit { - w := &circuit[wI] - if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) - } - } - return ce - }, - } - } - - return d -} - -// this module assumes that wire and instance indexes respect dependencies - -// GetAssignmentHint generates a hint that returns the value of a wire of a circuit at an instance. -// It is intended for use in the debugging function gkrapi.API.GetValue. -func GetAssignmentHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - if len(ins) != 4 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: GKR sub-circuit index, wire index, instance index, and dummy dependency enforcer") - } - if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { - return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") - } - data := &data[ins[0].Uint64()] - wireI := ins[1].Uint64() - instanceI := ins[2].Uint64() - - data.assignment[wireI][instanceI].BigInt(outs[0]) - - return nil - } -} - -// SolveHint generate a hint that computes the assignments for all wires in a circuit instance. -// It is intended for use in gkrapi.API.AddInstance. -func SolveHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: instance index of the circuit to solve - // - rest: values for input wires - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - instanceI := ins[1].Uint64() - - // indices for reading inputs and outputs - outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - - // Get a circuit evaluator from the pool - ce := data.evaluatorPool.Get().(*circuitEvaluator) - defer data.evaluatorPool.Put(ce) - - // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, - // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. - for wI := range data.circuit { - w := &data.circuit[wI] - if w.IsInput() { - // read from provided input - // there is no gate to compute for input wires. We only need to set them in the assignment. - data.assignment[wI][instanceI].SetBigInt(ins[insI]) - insI++ - } else { - // Get evaluator for this wire from the circuit evaluator - evaluator := &ce.evaluators[wI] - - // Push gate inputs - for _, inWI := range w.Inputs { - evaluator.pushInput(&data.assignment[inWI][instanceI]) - } - - // Evaluate the gate - data.assignment[wI][instanceI].Set(evaluator.evaluate()) - } - if w.IsOutput() { - // write to provided output. - // NB! even if it is output wire, then it was already computed - // above. So the current condition is not exclusive with the - // previous one. - data.assignment[wI][instanceI].BigInt(outs[outsI]) - outsI++ - } - } - - return nil - } -} - -// ProveHint generates a hint that produces the GKR proof using the computed assignments contained in data. -// It is meant for use in gkrapi.Circuit.finalize. -func ProveHint(data []SolvingData) hint.Hint { - - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: dummy, just to ensure the solver's work is done before the prover is called. - // - rest: initial Fiat-Shamir challenge (provided by the caller, typically a commitment to all inputs and outputs) - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - hashName := data.hashName - // drop the first input which indicates the current circuit index - ins = ins[1:] - - data.assignment.repeatUntilEnd(data.nbInstances) - - // The second input (the dummy) is an output from the last call to AddInstance. - // We need to ensure that the circuit has been completely solved for all instances of all wires. - // By feeding one of the output of the last call to AddInstance, we ensure that the gnark solver - // schedules proof generation after all solving is done. - // The rest constitute the initial fiat shamir challenge. - insBytes := make([][]byte, len(ins)-1) - for i := 1; i < len(ins); i++ { - insBytes[i-1] = make([]byte, fr.Bytes) - ins[i].FillBytes(insBytes[i-1]) - } - - hsh := hash.NewHash(hashName + "_BN254") - - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) - if err != nil { - return err - } - - return proof.SerializeToBigInts(outs) - - } -} - -// repeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. -// e.g. {{1, 2, 3}, {4, 5, 6}}.repeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} -func (a WireAssignment) repeatUntilEnd(n int) { - for i := range a { - for j := n; j < len(a[i]); j++ { - a[i][j] = a[i][j-1] - } - } -} diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go new file mode 100644 index 0000000000..81059eb7fb --- /dev/null +++ b/internal/gkr/bw6-633/blueprint.go @@ -0,0 +1,355 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// Code generated by gnark DO NOT EDIT + +package gkr + +import ( + "fmt" + "math/big" + "sync" + + "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" + "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/polynomial" + fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + "github.com/consensys/gnark-crypto/hash" + "github.com/consensys/gnark/constraint" + "github.com/consensys/gnark/internal/gkr/gkrtypes" +) + +// circuitEvaluator evaluates all gates in a circuit for one instance +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + +// BlueprintSolve is a BW6_633-specific blueprint for solving GKR circuit instances. +type BlueprintSolve struct { + // Circuit structure + Circuit gkrtypes.Circuit + NbInputs int + NbOutputVars int + InputWires []int + OutputWires []int + MaxNbIn int // maximum number of inputs for any gate + + // Stateful data - stored as native fr.Element + nbInstances int + assignment WireAssignment // []polynomial.MultiLin + evaluatorPool sync.Pool // pool of circuitEvaluator for reuse + + lock sync.Mutex +} + +// InitializeEvaluatorPool initializes the evaluator pool for this blueprint +func (b *BlueprintSolve) InitializeEvaluatorPool(circuit gkrtypes.Circuit, maxGateStackSize int) { + elementPool := polynomial.NewPool(maxGateStackSize) + b.evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) + } + } + return ce + }, + } +} + +// Ensures BlueprintSolve implements BlueprintStateful +var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + instanceIdx := b.nbInstances + + // Initialize assignment array if this is the first instance + if b.assignment == nil { + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + } + } + + // Grow assignment slices to accommodate this instance + for i := range b.assignment { + for len(b.assignment[i]) <= instanceIdx { + var zero fr.Element + b.assignment[i] = append(b.assignment[i], zero) + } + } + + // Read input values from instruction calldata + nbInputsInst := int(inst.Calldata[1]) + if nbInputsInst != b.NbInputs { + return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) + } + + offset := 2 + + // Get a circuit evaluator from the pool + ce := b.evaluatorPool.Get().(*circuitEvaluator) + defer b.evaluatorPool.Put(ce) + + // Process all wires in topological order (circuit is already sorted) + inputIdx := 0 + for wI := range b.Circuit { + w := &b.Circuit[wI] + + if w.IsInput() { + // Read input value from calldata and convert to fr.Element + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to fr.Element + bigInt := s.ToBigInt(val) + b.assignment[wI][instanceIdx].SetBigInt(bigInt) + inputIdx++ + } else { + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] + + // Push gate inputs + for _, inWI := range w.Inputs { + evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + } + + // Evaluate the gate + b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + } + } + + // Set output wires for the instruction (convert fr.Element to U64) + outputIdx := 0 + for _, outWI := range b.OutputWires { + var bigInt big.Int + b.assignment[outWI][instanceIdx].BigInt(&bigInt) + s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) + outputIdx++ + } + + b.nbInstances++ + return nil +} + +// Reset implements BlueprintStateful +func (b *BlueprintSolve) Reset() { + b.nbInstances = 0 + b.assignment = nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintSolve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintSolve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { + return b.NbOutputVars +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + nbInputsInst := int(inst.Calldata[1]) + offset := 2 + + for range nbInputsInst { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + for i := range b.NbOutputVars { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} + +// GetAssignment returns the assignment for a specific wire and instance (for debugging) +func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { + b.lock.Lock() + defer b.lock.Unlock() + + var zero constraint.U64 + if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + } + + // Convert fr.Element to U64 + var bigInt big.Int + b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) + return s.FromInterface(&bigInt), nil +} + +// GetAssignments returns all assignments for proving +func (b *BlueprintSolve) GetAssignments() WireAssignment { + b.lock.Lock() + defer b.lock.Unlock() + return b.assignment +} + +// GetNbInstances returns the number of instances solved +func (b *BlueprintSolve) GetNbInstances() int { + b.lock.Lock() + defer b.lock.Unlock() + return b.nbInstances +} + +// BlueprintProve is a BW6_633-specific blueprint for generating GKR proofs. +type BlueprintProve struct { + SolveBlueprint *BlueprintSolve + HashName string + + lock sync.Mutex +} + +// Ensures BlueprintProve implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) + +// Solve implements the BlueprintSolvable interface for proving. +func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Get assignments from solve blueprint (already in fr.Element form) + assignments := b.SolveBlueprint.GetAssignments() + if len(assignments) == 0 { + return fmt.Errorf("no assignments available for proving") + } + + // Read initial challenges from instruction calldata + nbChallenges := int(inst.Calldata[2]) + challenges := make([]fr.Element, nbChallenges) + offset := 3 + for i := range nbChallenges { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to fr.Element + bigInt := s.ToBigInt(val) + challenges[i].SetBigInt(bigInt) + } + + // Convert challenges to [][]byte for Fiat-Shamir + insBytes := make([][]byte, len(challenges)) + for i := range challenges { + insBytes[i] = make([]byte, fr.Bytes) + challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + } + + // Create Fiat-Shamir settings + hsh := hash.NewHash(b.HashName + "_BW6_633") + fsSettings := fiatshamir.WithHash(hsh, insBytes...) + + // Call the BW6_633-specific Prove function (assignments already WireAssignment type) + proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + if err != nil { + return fmt.Errorf("bw6_633 prove failed: %w", err) + } + + // Serialize proof and convert to U64 + proofSize := 0 + for i := range proof { + for _, poly := range proof[i].partialSumPolys { + proofSize += len(poly) + } + if proof[i].finalEvalProof != nil { + proofSize += len(proof[i].finalEvalProof) + } + } + + outsBig := make([]*big.Int, proofSize) + for i := range outsBig { + outsBig[i] = new(big.Int) + } + if err := proof.SerializeToBigInts(outsBig); err != nil { + return fmt.Errorf("failed to serialize proof: %w", err) + } + + // Set output wires (convert big.Int to U64) + for i, bigVal := range outsBig { + s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + } + + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintProve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintProve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { + if len(inst.Calldata) > 1 { + return int(inst.Calldata[1]) + } + return 0 +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + if len(inst.Calldata) < 3 { + return maxLevel + 1 + } + + nbChallenges := int(inst.Calldata[2]) + offset := 3 + + for range nbChallenges { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + proofSize := int(inst.Calldata[1]) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go deleted file mode 100644 index 81c2c8bd0d..0000000000 --- a/internal/gkr/bw6-633/solver_hints.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2020-2026 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -// Code generated by gnark DO NOT EDIT - -package gkr - -import ( - "fmt" - "math/big" - "sync" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/hash" - hint "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - - "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -type circuitEvaluator struct { - evaluators []gateEvaluator // one evaluator per wire -} - -type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string - evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse -} - -type newSolvingDataSettings struct { - assignments []gkrtypes.WireAssignment -} - -type NewSolvingDataOption func(*newSolvingDataSettings) - -// WithAssignments re-use already computed wire assignments. -// If set, the number of assignments must be equal to the -// number of SolvingInfo objects fed to NewSolvingData. -// However, the caller can still choose to provide values for certain -// circuits only. For example: -// -// WithAssignments(assignment1, nil, assignment3) -// -// is a valid option for three circuits. In this case, the assignments for -// the second circuit will be computed from scratch. -func WithAssignments(assignments ...gkrtypes.WireAssignment) NewSolvingDataOption { - return func(s *newSolvingDataSettings) { - s.assignments = assignments - } -} - -// NewSolvingData converts []gkrtypes.SolvingInfo into concrete SolvingData objects: -// - The gates are loaded in accordance with their names. -// - The instances/assignments are padded into a power of 2, suitable for the multilinear extensions used -// in the GKR prover. -// Optionally, the caller can provide pre-computed assignments for each circuit using WithAssignments. -func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption) []SolvingData { - var s newSolvingDataSettings - for _, opt := range options { - opt(&s) - } - if len(s.assignments) == 0 { - s.assignments = make([]gkrtypes.WireAssignment, len(info)) - } else if len(s.assignments) != len(info) { - panic(fmt.Sprintf("expected %d assignments (one for each circuit), got %d", len(info), len(s.assignments))) - } - - d := make([]SolvingData, len(info)) - - for k := range info { - - d[k].circuit = info[k].Circuit - d[k].assignment = make(WireAssignment, len(info[k].Circuit)) - d[k].nbInstances = info[k].NbInstances - d[k].hashName = info[k].HashName - - d[k].maxNbIn = d[k].circuit.MaxGateNbIn() - - nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info[k].NbInstances))) - for i := range d[k].assignment { - d[k].assignment[i] = make([]fr.Element, nbPaddedInstances) - } - - if s.assignments[k] != nil { - if len(s.assignments[k]) != len(d[k].assignment) { - panic(fmt.Sprintf("provided assignment has %d wires, expected %d", len(s.assignments[k]), len(d[k].assignment))) - } - for i := range d[k].assignment { - if len(s.assignments[k][i]) != info[k].NbInstances { - panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignments[k][i]), info[k].NbInstances)) - } - for j := range s.assignments[k][i] { - if _, err := d[k].assignment[i][j].SetInterface(s.assignments[k][i][j]); err != nil { - panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) - } - } - // inline equivalent of repeatUntilEnd - for j := len(s.assignments[k][i]); j < nbPaddedInstances; j++ { - d[k].assignment[i][j] = d[k].assignment[i][j-1] // pad with the last value - } - } - } - - // Initialize polynomial pool for elements - // Size the pool for the worst case: max gate size across all wires - maxGateStackSize := 0 - for _, w := range d[k].circuit { - if !w.IsInput() { - stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) - if stackSize > maxGateStackSize { - maxGateStackSize = stackSize - } - } - } - - // Initialize circuit evaluator pool - circuit := d[k].circuit - elementPool := polynomial.NewPool(maxGateStackSize) - d[k].evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(circuit)), - } - for wI := range circuit { - w := &circuit[wI] - if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) - } - } - return ce - }, - } - } - - return d -} - -// this module assumes that wire and instance indexes respect dependencies - -// GetAssignmentHint generates a hint that returns the value of a wire of a circuit at an instance. -// It is intended for use in the debugging function gkrapi.API.GetValue. -func GetAssignmentHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - if len(ins) != 4 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: GKR sub-circuit index, wire index, instance index, and dummy dependency enforcer") - } - if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { - return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") - } - data := &data[ins[0].Uint64()] - wireI := ins[1].Uint64() - instanceI := ins[2].Uint64() - - data.assignment[wireI][instanceI].BigInt(outs[0]) - - return nil - } -} - -// SolveHint generate a hint that computes the assignments for all wires in a circuit instance. -// It is intended for use in gkrapi.API.AddInstance. -func SolveHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: instance index of the circuit to solve - // - rest: values for input wires - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - instanceI := ins[1].Uint64() - - // indices for reading inputs and outputs - outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - - // Get a circuit evaluator from the pool - ce := data.evaluatorPool.Get().(*circuitEvaluator) - defer data.evaluatorPool.Put(ce) - - // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, - // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. - for wI := range data.circuit { - w := &data.circuit[wI] - if w.IsInput() { - // read from provided input - // there is no gate to compute for input wires. We only need to set them in the assignment. - data.assignment[wI][instanceI].SetBigInt(ins[insI]) - insI++ - } else { - // Get evaluator for this wire from the circuit evaluator - evaluator := &ce.evaluators[wI] - - // Push gate inputs - for _, inWI := range w.Inputs { - evaluator.pushInput(&data.assignment[inWI][instanceI]) - } - - // Evaluate the gate - data.assignment[wI][instanceI].Set(evaluator.evaluate()) - } - if w.IsOutput() { - // write to provided output. - // NB! even if it is output wire, then it was already computed - // above. So the current condition is not exclusive with the - // previous one. - data.assignment[wI][instanceI].BigInt(outs[outsI]) - outsI++ - } - } - - return nil - } -} - -// ProveHint generates a hint that produces the GKR proof using the computed assignments contained in data. -// It is meant for use in gkrapi.Circuit.finalize. -func ProveHint(data []SolvingData) hint.Hint { - - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: dummy, just to ensure the solver's work is done before the prover is called. - // - rest: initial Fiat-Shamir challenge (provided by the caller, typically a commitment to all inputs and outputs) - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - hashName := data.hashName - // drop the first input which indicates the current circuit index - ins = ins[1:] - - data.assignment.repeatUntilEnd(data.nbInstances) - - // The second input (the dummy) is an output from the last call to AddInstance. - // We need to ensure that the circuit has been completely solved for all instances of all wires. - // By feeding one of the output of the last call to AddInstance, we ensure that the gnark solver - // schedules proof generation after all solving is done. - // The rest constitute the initial fiat shamir challenge. - insBytes := make([][]byte, len(ins)-1) - for i := 1; i < len(ins); i++ { - insBytes[i-1] = make([]byte, fr.Bytes) - ins[i].FillBytes(insBytes[i-1]) - } - - hsh := hash.NewHash(hashName + "_BW6_633") - - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) - if err != nil { - return err - } - - return proof.SerializeToBigInts(outs) - - } -} - -// repeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. -// e.g. {{1, 2, 3}, {4, 5, 6}}.repeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} -func (a WireAssignment) repeatUntilEnd(n int) { - for i := range a { - for j := n; j < len(a[i]); j++ { - a[i][j] = a[i][j-1] - } - } -} diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go new file mode 100644 index 0000000000..9bc13fbc56 --- /dev/null +++ b/internal/gkr/bw6-761/blueprint.go @@ -0,0 +1,355 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// Code generated by gnark DO NOT EDIT + +package gkr + +import ( + "fmt" + "math/big" + "sync" + + "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" + "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" + fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + "github.com/consensys/gnark-crypto/hash" + "github.com/consensys/gnark/constraint" + "github.com/consensys/gnark/internal/gkr/gkrtypes" +) + +// circuitEvaluator evaluates all gates in a circuit for one instance +type circuitEvaluator struct { + evaluators []gateEvaluator // one evaluator per wire +} + +// BlueprintSolve is a BW6_761-specific blueprint for solving GKR circuit instances. +type BlueprintSolve struct { + // Circuit structure + Circuit gkrtypes.Circuit + NbInputs int + NbOutputVars int + InputWires []int + OutputWires []int + MaxNbIn int // maximum number of inputs for any gate + + // Stateful data - stored as native fr.Element + nbInstances int + assignment WireAssignment // []polynomial.MultiLin + evaluatorPool sync.Pool // pool of circuitEvaluator for reuse + + lock sync.Mutex +} + +// InitializeEvaluatorPool initializes the evaluator pool for this blueprint +func (b *BlueprintSolve) InitializeEvaluatorPool(circuit gkrtypes.Circuit, maxGateStackSize int) { + elementPool := polynomial.NewPool(maxGateStackSize) + b.evaluatorPool = sync.Pool{ + New: func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(circuit)), + } + for wI := range circuit { + w := &circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) + } + } + return ce + }, + } +} + +// Ensures BlueprintSolve implements BlueprintStateful +var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + instanceIdx := b.nbInstances + + // Initialize assignment array if this is the first instance + if b.assignment == nil { + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + } + } + + // Grow assignment slices to accommodate this instance + for i := range b.assignment { + for len(b.assignment[i]) <= instanceIdx { + var zero fr.Element + b.assignment[i] = append(b.assignment[i], zero) + } + } + + // Read input values from instruction calldata + nbInputsInst := int(inst.Calldata[1]) + if nbInputsInst != b.NbInputs { + return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) + } + + offset := 2 + + // Get a circuit evaluator from the pool + ce := b.evaluatorPool.Get().(*circuitEvaluator) + defer b.evaluatorPool.Put(ce) + + // Process all wires in topological order (circuit is already sorted) + inputIdx := 0 + for wI := range b.Circuit { + w := &b.Circuit[wI] + + if w.IsInput() { + // Read input value from calldata and convert to fr.Element + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to fr.Element + bigInt := s.ToBigInt(val) + b.assignment[wI][instanceIdx].SetBigInt(bigInt) + inputIdx++ + } else { + // Get evaluator for this wire from the circuit evaluator + evaluator := &ce.evaluators[wI] + + // Push gate inputs + for _, inWI := range w.Inputs { + evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + } + + // Evaluate the gate + b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + } + } + + // Set output wires for the instruction (convert fr.Element to U64) + outputIdx := 0 + for _, outWI := range b.OutputWires { + var bigInt big.Int + b.assignment[outWI][instanceIdx].BigInt(&bigInt) + s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) + outputIdx++ + } + + b.nbInstances++ + return nil +} + +// Reset implements BlueprintStateful +func (b *BlueprintSolve) Reset() { + b.nbInstances = 0 + b.assignment = nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintSolve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintSolve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { + return b.NbOutputVars +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + nbInputsInst := int(inst.Calldata[1]) + offset := 2 + + for range nbInputsInst { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + for i := range b.NbOutputVars { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} + +// GetAssignment returns the assignment for a specific wire and instance (for debugging) +func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { + b.lock.Lock() + defer b.lock.Unlock() + + var zero constraint.U64 + if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + } + + // Convert fr.Element to U64 + var bigInt big.Int + b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) + return s.FromInterface(&bigInt), nil +} + +// GetAssignments returns all assignments for proving +func (b *BlueprintSolve) GetAssignments() WireAssignment { + b.lock.Lock() + defer b.lock.Unlock() + return b.assignment +} + +// GetNbInstances returns the number of instances solved +func (b *BlueprintSolve) GetNbInstances() int { + b.lock.Lock() + defer b.lock.Unlock() + return b.nbInstances +} + +// BlueprintProve is a BW6_761-specific blueprint for generating GKR proofs. +type BlueprintProve struct { + SolveBlueprint *BlueprintSolve + HashName string + + lock sync.Mutex +} + +// Ensures BlueprintProve implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) + +// Solve implements the BlueprintSolvable interface for proving. +func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Get assignments from solve blueprint (already in fr.Element form) + assignments := b.SolveBlueprint.GetAssignments() + if len(assignments) == 0 { + return fmt.Errorf("no assignments available for proving") + } + + // Read initial challenges from instruction calldata + nbChallenges := int(inst.Calldata[2]) + challenges := make([]fr.Element, nbChallenges) + offset := 3 + for i := range nbChallenges { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + + // Convert U64 to fr.Element + bigInt := s.ToBigInt(val) + challenges[i].SetBigInt(bigInt) + } + + // Convert challenges to [][]byte for Fiat-Shamir + insBytes := make([][]byte, len(challenges)) + for i := range challenges { + insBytes[i] = make([]byte, fr.Bytes) + challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + } + + // Create Fiat-Shamir settings + hsh := hash.NewHash(b.HashName + "_BW6_761") + fsSettings := fiatshamir.WithHash(hsh, insBytes...) + + // Call the BW6_761-specific Prove function (assignments already WireAssignment type) + proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + if err != nil { + return fmt.Errorf("bw6_761 prove failed: %w", err) + } + + // Serialize proof and convert to U64 + proofSize := 0 + for i := range proof { + for _, poly := range proof[i].partialSumPolys { + proofSize += len(poly) + } + if proof[i].finalEvalProof != nil { + proofSize += len(proof[i].finalEvalProof) + } + } + + outsBig := make([]*big.Int, proofSize) + for i := range outsBig { + outsBig[i] = new(big.Int) + } + if err := proof.SerializeToBigInts(outsBig); err != nil { + return fmt.Errorf("failed to serialize proof: %w", err) + } + + // Set output wires (convert big.Int to U64) + for i, bigVal := range outsBig { + s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + } + + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintProve) CalldataSize() int { + return -1 // variable size +} + +// NbConstraints implements Blueprint +func (b *BlueprintProve) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { + if len(inst.Calldata) > 1 { + return int(inst.Calldata[1]) + } + return 0 +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + if len(inst.Calldata) < 3 { + return maxLevel + 1 + } + + nbChallenges := int(inst.Calldata[2]) + offset := 3 + + for range nbChallenges { + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + } + + outputLevel := maxLevel + 1 + proofSize := int(inst.Calldata[1]) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } + + return outputLevel +} diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go deleted file mode 100644 index a41ed9cbfc..0000000000 --- a/internal/gkr/bw6-761/solver_hints.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2020-2026 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -// Code generated by gnark DO NOT EDIT - -package gkr - -import ( - "fmt" - "math/big" - "sync" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/hash" - hint "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - - "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -type circuitEvaluator struct { - evaluators []gateEvaluator // one evaluator per wire -} - -type SolvingData struct { - assignment WireAssignment // assignment is indexed wire-first, instance-second. The number of instances is padded to a power of 2. - circuit gkrtypes.Circuit - maxNbIn int // maximum number of inputs for a gate in the circuit - nbInstances int - hashName string - evaluatorPool sync.Pool // pool of circuit evaluators for concurrent reuse -} - -type newSolvingDataSettings struct { - assignments []gkrtypes.WireAssignment -} - -type NewSolvingDataOption func(*newSolvingDataSettings) - -// WithAssignments re-use already computed wire assignments. -// If set, the number of assignments must be equal to the -// number of SolvingInfo objects fed to NewSolvingData. -// However, the caller can still choose to provide values for certain -// circuits only. For example: -// -// WithAssignments(assignment1, nil, assignment3) -// -// is a valid option for three circuits. In this case, the assignments for -// the second circuit will be computed from scratch. -func WithAssignments(assignments ...gkrtypes.WireAssignment) NewSolvingDataOption { - return func(s *newSolvingDataSettings) { - s.assignments = assignments - } -} - -// NewSolvingData converts []gkrtypes.SolvingInfo into concrete SolvingData objects: -// - The gates are loaded in accordance with their names. -// - The instances/assignments are padded into a power of 2, suitable for the multilinear extensions used -// in the GKR prover. -// Optionally, the caller can provide pre-computed assignments for each circuit using WithAssignments. -func NewSolvingData(info []gkrtypes.SolvingInfo, options ...NewSolvingDataOption) []SolvingData { - var s newSolvingDataSettings - for _, opt := range options { - opt(&s) - } - if len(s.assignments) == 0 { - s.assignments = make([]gkrtypes.WireAssignment, len(info)) - } else if len(s.assignments) != len(info) { - panic(fmt.Sprintf("expected %d assignments (one for each circuit), got %d", len(info), len(s.assignments))) - } - - d := make([]SolvingData, len(info)) - - for k := range info { - - d[k].circuit = info[k].Circuit - d[k].assignment = make(WireAssignment, len(info[k].Circuit)) - d[k].nbInstances = info[k].NbInstances - d[k].hashName = info[k].HashName - - d[k].maxNbIn = d[k].circuit.MaxGateNbIn() - - nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(info[k].NbInstances))) - for i := range d[k].assignment { - d[k].assignment[i] = make([]fr.Element, nbPaddedInstances) - } - - if s.assignments[k] != nil { - if len(s.assignments[k]) != len(d[k].assignment) { - panic(fmt.Sprintf("provided assignment has %d wires, expected %d", len(s.assignments[k]), len(d[k].assignment))) - } - for i := range d[k].assignment { - if len(s.assignments[k][i]) != info[k].NbInstances { - panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignments[k][i]), info[k].NbInstances)) - } - for j := range s.assignments[k][i] { - if _, err := d[k].assignment[i][j].SetInterface(s.assignments[k][i][j]); err != nil { - panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) - } - } - // inline equivalent of repeatUntilEnd - for j := len(s.assignments[k][i]); j < nbPaddedInstances; j++ { - d[k].assignment[i][j] = d[k].assignment[i][j-1] // pad with the last value - } - } - } - - // Initialize polynomial pool for elements - // Size the pool for the worst case: max gate size across all wires - maxGateStackSize := 0 - for _, w := range d[k].circuit { - if !w.IsInput() { - stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) - if stackSize > maxGateStackSize { - maxGateStackSize = stackSize - } - } - } - - // Initialize circuit evaluator pool - circuit := d[k].circuit - elementPool := polynomial.NewPool(maxGateStackSize) - d[k].evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(circuit)), - } - for wI := range circuit { - w := &circuit[wI] - if !w.IsInput() { // input wires don't need evaluators - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs), &elementPool) - } - } - return ce - }, - } - } - - return d -} - -// this module assumes that wire and instance indexes respect dependencies - -// GetAssignmentHint generates a hint that returns the value of a wire of a circuit at an instance. -// It is intended for use in the debugging function gkrapi.API.GetValue. -func GetAssignmentHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - if len(ins) != 4 { - return fmt.Errorf("GetAssignmentHint expects 3 inputs: GKR sub-circuit index, wire index, instance index, and dummy dependency enforcer") - } - if !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { - return fmt.Errorf("all 3 non-dummy input to GetAssignmentHint must fit in uint64") - } - data := &data[ins[0].Uint64()] - wireI := ins[1].Uint64() - instanceI := ins[2].Uint64() - - data.assignment[wireI][instanceI].BigInt(outs[0]) - - return nil - } -} - -// SolveHint generate a hint that computes the assignments for all wires in a circuit instance. -// It is intended for use in gkrapi.API.AddInstance. -func SolveHint(data []SolvingData) hint.Hint { - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: instance index of the circuit to solve - // - rest: values for input wires - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - instanceI := ins[1].Uint64() - - // indices for reading inputs and outputs - outsI := 0 - insI := 2 // skip the first two input, which are the circuit and instance indices, respectively. - - // Get a circuit evaluator from the pool - ce := data.evaluatorPool.Get().(*circuitEvaluator) - defer data.evaluatorPool.Put(ce) - - // we can now iterate over all the wires in the circuit. The wires are already topologically sorted, - // i.e. all inputs of a gate appear before the gate itself. So it is safe to iterate linearly. - for wI := range data.circuit { - w := &data.circuit[wI] - if w.IsInput() { - // read from provided input - // there is no gate to compute for input wires. We only need to set them in the assignment. - data.assignment[wI][instanceI].SetBigInt(ins[insI]) - insI++ - } else { - // Get evaluator for this wire from the circuit evaluator - evaluator := &ce.evaluators[wI] - - // Push gate inputs - for _, inWI := range w.Inputs { - evaluator.pushInput(&data.assignment[inWI][instanceI]) - } - - // Evaluate the gate - data.assignment[wI][instanceI].Set(evaluator.evaluate()) - } - if w.IsOutput() { - // write to provided output. - // NB! even if it is output wire, then it was already computed - // above. So the current condition is not exclusive with the - // previous one. - data.assignment[wI][instanceI].BigInt(outs[outsI]) - outsI++ - } - } - - return nil - } -} - -// ProveHint generates a hint that produces the GKR proof using the computed assignments contained in data. -// It is meant for use in gkrapi.Circuit.finalize. -func ProveHint(data []SolvingData) hint.Hint { - - return func(_ *big.Int, ins, outs []*big.Int) error { - // the input format is: - // - first input: circuit index - // - second input: dummy, just to ensure the solver's work is done before the prover is called. - // - rest: initial Fiat-Shamir challenge (provided by the caller, typically a commitment to all inputs and outputs) - if !ins[0].IsUint64() { - return fmt.Errorf("first input to GKR prove hint must be the sub-circuit index") - } - data := &data[ins[0].Uint64()] - hashName := data.hashName - // drop the first input which indicates the current circuit index - ins = ins[1:] - - data.assignment.repeatUntilEnd(data.nbInstances) - - // The second input (the dummy) is an output from the last call to AddInstance. - // We need to ensure that the circuit has been completely solved for all instances of all wires. - // By feeding one of the output of the last call to AddInstance, we ensure that the gnark solver - // schedules proof generation after all solving is done. - // The rest constitute the initial fiat shamir challenge. - insBytes := make([][]byte, len(ins)-1) - for i := 1; i < len(ins); i++ { - insBytes[i-1] = make([]byte, fr.Bytes) - ins[i].FillBytes(insBytes[i-1]) - } - - hsh := hash.NewHash(hashName + "_BW6_761") - - proof, err := Prove(data.circuit, data.assignment, fiatshamir.WithHash(hsh, insBytes...)) - if err != nil { - return err - } - - return proof.SerializeToBigInts(outs) - - } -} - -// repeatUntilEnd for each wire, sets all the values starting from n > 0 to its predecessor. -// e.g. {{1, 2, 3}, {4, 5, 6}}.repeatUntilEnd(2) -> {{1, 2, 2}, {4, 5, 5}} -func (a WireAssignment) repeatUntilEnd(n int) { - for i := range a { - for j := n; j < len(a[i]); j++ { - a[i][j] = a[i][j-1] - } - } -} diff --git a/internal/gkr/gkrhints/engine_hints.go b/internal/gkr/gkrhints/engine_hints.go deleted file mode 100644 index a6fa5c2403..0000000000 --- a/internal/gkr/gkrhints/engine_hints.go +++ /dev/null @@ -1,195 +0,0 @@ -package gkrhints - -import ( - "errors" - "fmt" - "math/big" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/constraint/solver/gkrgates" - "github.com/consensys/gnark/frontend" - bls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" - bls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" - bls24315 "github.com/consensys/gnark/internal/gkr/bls24-315" - bls24317 "github.com/consensys/gnark/internal/gkr/bls24-317" - bn254 "github.com/consensys/gnark/internal/gkr/bn254" - bw6633 "github.com/consensys/gnark/internal/gkr/bw6-633" - bw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" - "github.com/consensys/gnark/internal/gkr/gkrinfo" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/internal/utils" -) - -type TestEngineHints struct { - assignment gkrtypes.WireAssignment - info *gkrinfo.StoringInfo // we retain a reference to the solving info to allow the caller to modify it between calls to Solve and Prove - circuit gkrtypes.Circuit - gateIns []frontend.Variable -} - -func NewTestEngineHints(info *gkrinfo.StoringInfo) (*TestEngineHints, error) { - circuit, err := gkrtypes.NewCircuit(info.Circuit, gkrgates.Get) - if err != nil { - return nil, err - } - - return &TestEngineHints{ - info: info, - circuit: circuit, - gateIns: make([]frontend.Variable, circuit.MaxGateNbIn()), - assignment: make(gkrtypes.WireAssignment, len(circuit)), - }, - err -} - -// Solve solves one instance of a GKR circuit. -// The second input is the index of the instance. The rest are the inputs of the circuit, in their nominal order. -func (h *TestEngineHints) Solve(mod *big.Int, ins []*big.Int, outs []*big.Int) error { - - // ignore the first input. - instanceI := len(h.assignment[0]) - if in1 := ins[1].Uint64(); !ins[1].IsUint64() || in1 > 0xffffffff { - return errors.New("second input must be a uint32 instance index") - } else if in1 != uint64(instanceI) || h.info.NbInstances != instanceI { - return errors.New("second input must equal the number of instances, and calls to Solve must be done in order of instance index") - } - - api := gateAPI{mod} - - inI := 2 - outI := 0 - for wI := range h.circuit { - w := &h.circuit[wI] - var val frontend.Variable - if w.IsInput() { - val = utils.FromInterface(ins[inI]) - inI++ - } else { - for gateInI, inWI := range w.Inputs { - h.gateIns[gateInI] = h.assignment[inWI][instanceI] - } - val = w.Gate.Evaluate(api, h.gateIns[:len(w.Inputs)]...) - } - if w.IsOutput() { - *outs[outI] = utils.FromInterface(val) - outI++ - } - h.assignment[wI] = append(h.assignment[wI], val) - } - return nil -} - -func (h *TestEngineHints) Prove(mod *big.Int, ins, outs []*big.Int) error { - - infos, err := gkrtypes.NewSolvingInfo([]*gkrinfo.StoringInfo{h.info}, gkrgates.Get) - if err != nil { - return fmt.Errorf("failed to convert storing info to solving info: %w", err) - } - ins[0].SetUint64(0) - - if mod.Cmp(ecc.BLS12_377.ScalarField()) == 0 { - data := bls12377.NewSolvingData(infos, bls12377.WithAssignments(h.assignment)) - return bls12377.ProveHint(data)(mod, ins, outs) - } - if mod.Cmp(ecc.BLS12_381.ScalarField()) == 0 { - data := bls12381.NewSolvingData(infos, bls12381.WithAssignments(h.assignment)) - return bls12381.ProveHint(data)(mod, ins, outs) - } - if mod.Cmp(ecc.BLS24_315.ScalarField()) == 0 { - data := bls24315.NewSolvingData(infos, bls24315.WithAssignments(h.assignment)) - return bls24315.ProveHint(data)(mod, ins, outs) - } - if mod.Cmp(ecc.BLS24_317.ScalarField()) == 0 { - data := bls24317.NewSolvingData(infos, bls24317.WithAssignments(h.assignment)) - return bls24317.ProveHint(data)(mod, ins, outs) - } - if mod.Cmp(ecc.BN254.ScalarField()) == 0 { - data := bn254.NewSolvingData(infos, bn254.WithAssignments(h.assignment)) - return bn254.ProveHint(data)(mod, ins, outs) - } - if mod.Cmp(ecc.BW6_633.ScalarField()) == 0 { - data := bw6633.NewSolvingData(infos, bw6633.WithAssignments(h.assignment)) - return bw6633.ProveHint(data)(mod, ins, outs) - } - if mod.Cmp(ecc.BW6_761.ScalarField()) == 0 { - data := bw6761.NewSolvingData(infos, bw6761.WithAssignments(h.assignment)) - return bw6761.ProveHint(data)(mod, ins, outs) - } - - return errors.New("unsupported modulus") -} - -// GetAssignment returns the assignment for a particular wire and instance. -func (h *TestEngineHints) GetAssignment(_ *big.Int, ins []*big.Int, outs []*big.Int) error { - if len(ins) != 4 || !ins[0].IsUint64() || !ins[1].IsUint64() || !ins[2].IsUint64() { - return errors.New("expected 3 inputs: circuit index, wire index, instance index, and dummy output from the same instance") - } - if len(outs) != 1 { - return errors.New("expected 1 output: the value of the wire at the given instance") - } - *outs[0] = utils.FromInterface(h.assignment[ins[1].Uint64()][ins[2].Uint64()]) - return nil -} - -type gateAPI struct{ mod *big.Int } - -func (g gateAPI) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - in1 := utils.FromInterface(i1) - in2 := utils.FromInterface(i2) - - in1.Add(&in1, &in2) - for _, v := range in { - inV := utils.FromInterface(v) - in1.Add(&in1, &inV) - } - return &in1 -} - -func (g gateAPI) MulAcc(a, b, c frontend.Variable) frontend.Variable { - x, y := utils.FromInterface(b), utils.FromInterface(c) - x.Mul(&x, &y) - x.Mod(&x, g.mod) // reduce - y = utils.FromInterface(a) - x.Add(&x, &y) - return &x -} - -func (g gateAPI) Neg(i1 frontend.Variable) frontend.Variable { - x := utils.FromInterface(i1) - x.Neg(&x) - return &x -} - -func (g gateAPI) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - x := utils.FromInterface(i1) - y := utils.FromInterface(i2) - x.Sub(&x, &y) - for _, v := range in { - y = utils.FromInterface(v) - x.Sub(&x, &y) - } - return &x -} - -func (g gateAPI) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - x := utils.FromInterface(i1) - y := utils.FromInterface(i2) - x.Mul(&x, &y) - for _, v := range in { - y = utils.FromInterface(v) - x.Mul(&x, &y) - } - x.Mod(&x, g.mod) // reduce - return &x -} - -func (g gateAPI) SumExp17(a, b, c frontend.Variable) frontend.Variable { - x := utils.FromInterface(a) - res := utils.FromInterface(b) - - x.Add(&x, &res) - res = utils.FromInterface(c) - x.Add(&x, &res) - res.Exp(&x, big.NewInt(17), g.mod) - return &res -} diff --git a/internal/gkr/gkrinfo/info.go b/internal/gkr/gkrinfo/info.go index 81cb8efc09..560f8ee814 100644 --- a/internal/gkr/gkrinfo/info.go +++ b/internal/gkr/gkrinfo/info.go @@ -15,12 +15,6 @@ type ( Circuit []Wire - StoringInfo struct { - Circuit Circuit - NbInstances int - HashName string - } - Permutations struct { SortedInstances []int SortedWires []int @@ -32,20 +26,3 @@ type ( func (w Wire) IsInput() bool { return len(w.Inputs) == 0 } - -func (d *StoringInfo) NewInputVariable() int { - i := len(d.Circuit) - d.Circuit = append(d.Circuit, Wire{}) - return i -} - -// A ConstraintSystem that supports GKR. If a constraint system implements this -// interface, then it stores and proves GKR sub-circuits automatically at circuit -// solving time. -type ConstraintSystem interface { - // NewGkr registers a GKR sub-circuit, returning a reference to an object - // where serializable data about the sub-circuit is to be stored, and an - // index as a reference to the GKR sub-circuit. The index is the first - // argument to all GKR-related hints. - NewGkr() (*StoringInfo, int) -} diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 26f3b126a2..6f9c236a41 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -293,28 +293,6 @@ type SolvingInfo struct { HashName string } -// NewSolvingInfo converts []gkrinfo.StoringInfo into concrete SolvingInfo objects: -// - The gates are loaded in accordance with their names. -// - The instances/assignments are padded into a power of 2, as needed for creating multilinear extensions. -// -// The gateGetter function is used to retrieve gate definitions by name. It is provided for avoiding -// import cycles and should typically be gkrgates.Get. -func NewSolvingInfo(info []*gkrinfo.StoringInfo, gateGetter func(name gkr.GateName) *Gate) ([]SolvingInfo, error) { - res := make([]SolvingInfo, len(info)) - for i := range info { - circuit, err := NewCircuit(info[i].Circuit, gateGetter) - if err != nil { - return nil, err - } - res[i] = SolvingInfo{ - Circuit: circuit, - NbInstances: info[i].NbInstances, - HashName: info[i].HashName, - } - } - return res, nil -} - // WireAssignment is assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index 54a1e11aba..63ee8b5ff4 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -10,8 +10,7 @@ import ( ) type API struct { - toStore *gkrinfo.StoringInfo - index int + circuit gkrinfo.Circuit assignments gkrtypes.WireAssignment parentApi frontend.API } @@ -21,12 +20,12 @@ func frontendVarToInt(a gkr.Variable) int { } func (api *API) NamedGate(gate gkr.GateName, in ...gkr.Variable) gkr.Variable { - api.toStore.Circuit = append(api.toStore.Circuit, gkrinfo.Wire{ + api.circuit = append(api.circuit, gkrinfo.Wire{ Gate: string(gate), Inputs: utils.Map(in, frontendVarToInt), }) api.assignments = append(api.assignments, nil) - return gkr.Variable(len(api.toStore.Circuit) - 1) + return gkr.Variable(len(api.circuit) - 1) } func (api *API) Gate(gate gkr.GateFunction, in ...gkr.Variable) gkr.Variable { diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 7990ebac82..7f1753a217 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -6,10 +6,11 @@ import ( "math/bits" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/constraint/solver/gkrgates" "github.com/consensys/gnark/frontend" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrhints" + gkrbn254 "github.com/consensys/gnark/internal/gkr/bn254" "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" @@ -30,33 +31,36 @@ type InitialChallengeGetter func() []frontend.Variable // Circuit represents a GKR circuit. type Circuit struct { - toStore *gkrinfo.StoringInfo + circuit gkrinfo.Circuit // untyped circuit definition assignments gkrtypes.WireAssignment getInitialChallenges InitialChallengeGetter // optional getter for the initial Fiat-Shamir challenge ins []gkr.Variable outs []gkr.Variable - api frontend.API // the parent API used for hints - hints *gkrhints.TestEngineHints // hints for the GKR circuit, used for testing purposes - index int // index among all GKR circuits + api frontend.API // the parent API + + // Blueprint-based fields + solveBlueprintID constraint.BlueprintID + proveBlueprintID constraint.BlueprintID + blueprint interface{} // actual type is *gkrbn254.BlueprintSolve + + // Metadata + hashName string + nbInstances int } // New creates a new GKR API func New(api frontend.API) (*API, error) { - gkrer, ok := api.Compiler().(gkrinfo.ConstraintSystem) - if !ok { - return nil, errors.New("provided api does not support GKR") - } - toStore, index := gkrer.NewGkr() return &API{ - toStore: toStore, - index: index, + circuit: make(gkrinfo.Circuit, 0), parentApi: api, }, nil } // NewInput creates a new input variable. func (api *API) NewInput() gkr.Variable { - return gkr.Variable(api.toStore.NewInputVariable()) + i := len(api.circuit) + api.circuit = append(api.circuit, gkrinfo.Wire{}) + return gkr.Variable(i) } type CompileOption func(*Circuit) @@ -74,40 +78,36 @@ func WithInitialChallenge(getInitialChallenge InitialChallengeGetter) CompileOpt // but instances can be added to it. func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*Circuit, error) { res := Circuit{ - toStore: api.toStore, - index: api.index, - assignments: make(gkrtypes.WireAssignment, len(api.toStore.Circuit)), + circuit: api.circuit, + assignments: make(gkrtypes.WireAssignment, len(api.circuit)), api: api.parentApi, + hashName: fiatshamirHashName, } - res.toStore.HashName = fiatshamirHashName - res.toStore.Circuit = api.toStore.Circuit - - var err error - res.hints, err = gkrhints.NewTestEngineHints(res.toStore) - if err != nil { - return nil, fmt.Errorf("failed to call GKR hints: %w", err) + // Create and populate blueprint + if err := res.createBlueprint(); err != nil { + return nil, fmt.Errorf("failed to create GKR blueprint: %w", err) } for _, opt := range options { opt(&res) } - notOut := make([]bool, len(res.toStore.Circuit)) - for i := range res.toStore.Circuit { - if res.toStore.Circuit[i].IsInput() { + notOut := make([]bool, len(res.circuit)) + for i := range res.circuit { + if res.circuit[i].IsInput() { res.ins = append(res.ins, gkr.Variable(i)) } - for _, inWI := range res.toStore.Circuit[i].Inputs { + for _, inWI := range res.circuit[i].Inputs { notOut[inWI] = true } } - if len(res.ins) == len(res.toStore.Circuit) { + if len(res.ins) == len(res.circuit) { return nil, errors.New("circuit has no non-input wires") } - for i := range res.toStore.Circuit { + for i := range res.circuit { if !notOut[i] { res.outs = append(res.outs, gkr.Variable(i)) } @@ -118,39 +118,122 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C return &res, nil } +// createBlueprint creates and initializes the GKR blueprint for this circuit +func (c *Circuit) createBlueprint() error { + blueprint := &gkrbn254.BlueprintSolve{} + + // Convert circuit to typed circuit + circuit, err := gkrtypes.NewCircuit(c.circuit, gkrgates.Get) + if err != nil { + return fmt.Errorf("failed to convert circuit: %w", err) + } + + blueprint.Circuit = circuit + blueprint.MaxNbIn = circuit.MaxGateNbIn() + + // Identify input wires + for i := range circuit { + if circuit[i].IsInput() { + blueprint.NbInputs++ + blueprint.InputWires = append(blueprint.InputWires, i) + } + } + + // Identify output wires (not inputs to any other wire) + isOutput := make([]bool, len(circuit)) + for i := range circuit { + isOutput[i] = true + } + for _, wire := range c.circuit { + for _, inIdx := range wire.Inputs { + isOutput[inIdx] = false + } + } + for i := range circuit { + if isOutput[i] { + blueprint.NbOutputVars++ + blueprint.OutputWires = append(blueprint.OutputWires, i) + } + } + + // Initialize evaluator pool (same as in NewSolvingData) + maxGateStackSize := 0 + for _, w := range circuit { + if !w.IsInput() { + stackSize := w.Gate.Compiled().NbConstants() + len(w.Inputs) + len(w.Gate.Compiled().Instructions) + if stackSize > maxGateStackSize { + maxGateStackSize = stackSize + } + } + } + + // Initialize evaluator pool - this creates the sync.Pool with a factory function + // that creates circuitEvaluators. The actual initialization happens inside the blueprint + // when InitializeEvaluatorPool is called. + blueprint.InitializeEvaluatorPool(circuit, maxGateStackSize) + + // Register solve blueprint with compiler + c.solveBlueprintID = c.api.Compiler().AddBlueprint(blueprint) + c.blueprint = blueprint + + // Create and register prove blueprint with reference to solve blueprint + proveBlueprint := &gkrbn254.BlueprintProve{ + SolveBlueprint: blueprint, + HashName: c.hashName, + } + c.proveBlueprintID = c.api.Compiler().AddBlueprint(proveBlueprint) + + return nil +} + // AddInstance adds a new instance to the GKR circuit, returning the values of output variables for the instance. func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr.Variable]frontend.Variable, error) { if len(input) != len(c.ins) { for k := range input { - if k >= gkr.Variable(len(c.toStore.Circuit)) { - return nil, fmt.Errorf("variable %d is out of bounds (max %d)", k, len(c.toStore.Circuit)-1) + if k >= gkr.Variable(len(c.circuit)) { + return nil, fmt.Errorf("variable %d is out of bounds (max %d)", k, len(c.circuit)-1) } - if !c.toStore.Circuit[k].IsInput() { + if !c.circuit[k].IsInput() { return nil, fmt.Errorf("value provided for non-input variable %d", k) } } } - hintIn := make([]frontend.Variable, 2+len(c.ins)) // first and second input denote the circuit and instance, respectively. - hintIn[0] = c.index - hintIn[1] = c.toStore.NbInstances - for hintInI, wI := range c.ins { - if inV, ok := input[wI]; !ok { + + // Build instruction calldata for blueprint + // Format: [0]=length, [1]=nbInputs, [2...]=input values as linear expressions + compiler := c.api.Compiler() + calldata := make([]uint32, 2, 2+len(c.ins)*2+2) // pre-allocate roughly + calldata[1] = uint32(len(c.ins)) + + // Encode input variables + for _, wI := range c.ins { + inV, ok := input[wI] + if !ok { return nil, fmt.Errorf("missing entry for input variable %d", wI) - } else { - hintIn[hintInI+2] = inV - c.assignments[wI] = append(c.assignments[wI], inV) } - } + // Store in assignment for later use + c.assignments[wI] = append(c.assignments[wI], inV) - outsSerialized, err := c.api.Compiler().NewHint(c.hints.Solve, len(c.outs), hintIn...) - if err != nil { - return nil, fmt.Errorf("failed to call solve hint: %w", err) + // Encode as linear expression in calldata + v := compiler.ToCanonicalVariable(inV) + v.Compress(&calldata) } - c.toStore.NbInstances++ + + // Set total length + calldata[0] = uint32(len(calldata)) + + // Execute solve blueprint instruction + outputs := compiler.AddInstruction(c.solveBlueprintID, calldata) + + // Track instance count + c.nbInstances++ + + // Convert outputs to map res := make(map[gkr.Variable]frontend.Variable, len(c.outs)) for i, v := range c.outs { - res[v] = outsSerialized[i] - c.assignments[v] = append(c.assignments[v], outsSerialized[i]) + outVar := compiler.InternalVariable(outputs[i]) + res[v] = outVar + c.assignments[v] = append(c.assignments[v], outVar) } return res, nil @@ -165,9 +248,9 @@ func (c *Circuit) finalize(api frontend.API) error { } // pad instances to the next power of 2 - nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(c.toStore.NbInstances))) + nbPaddedInstances := int(ecc.NextPowerOfTwo(uint64(c.nbInstances))) // pad instances to the next power of 2 by repeating the last instance - if c.toStore.NbInstances < nbPaddedInstances && c.toStore.NbInstances > 0 { + if c.nbInstances < nbPaddedInstances && c.nbInstances > 0 { for _, wI := range c.ins { c.assignments[wI] = utils.ExtendRepeatLast(c.assignments[wI], nbPaddedInstances) } @@ -178,7 +261,7 @@ func (c *Circuit) finalize(api frontend.API) error { // if the circuit consists of only one instance, directly solve the circuit if len(c.assignments[c.ins[0]]) == 1 { - circuit, err := gkrtypes.NewCircuit(c.toStore.Circuit, gkrgates.Get) + circuit, err := gkrtypes.NewCircuit(c.circuit, gkrgates.Get) if err != nil { return fmt.Errorf("failed to convert GKR info to circuit: %w", err) } @@ -221,30 +304,41 @@ func (c *Circuit) finalize(api frontend.API) error { } func (c *Circuit) verify(api frontend.API, initialChallenges []frontend.Variable) error { - forSnark, err := newCircuitDataForSnark(utils.FieldToCurve(api.Compiler().Field()), c.toStore, c.assignments) + forSnark, err := newCircuitDataForSnark(utils.FieldToCurve(api.Compiler().Field()), c.circuit, c.assignments) if err != nil { return fmt.Errorf("failed to create circuit data for snark: %w", err) } - // first input is the circuit index. - // hack: adding one of the outputs of the solve hint to ensure "prove" is called after "solve". - hintIns := make([]frontend.Variable, len(initialChallenges)+2) - firstOutputAssignment := c.assignments[c.outs[0]] - hintIns[0] = c.index - hintIns[1] = firstOutputAssignment[len(firstOutputAssignment)-1] // take the last output of the first output wire + // Build prove instruction using blueprint + proofSize := gadget.ProofSize(forSnark.circuit, bits.TrailingZeros(uint(len(c.assignments[0])))) - copy(hintIns[2:], initialChallenges) + compiler := api.Compiler() - var ( - proofSerialized []frontend.Variable - proof gadget.Proof - ) + // Build calldata for prove instruction + // Format: [0]=length, [1]=proofSize, [2]=nbChallenges, [3...]=challenge linear expressions + proveCalldata := make([]uint32, 3, 3+len(initialChallenges)*2+2) + proveCalldata[1] = uint32(proofSize) + proveCalldata[2] = uint32(len(initialChallenges)) - if proofSerialized, err = api.Compiler().NewHint( - c.hints.Prove, gadget.ProofSize(forSnark.circuit, bits.TrailingZeros(uint(len(c.assignments[0])))), hintIns...); err != nil { - return err + // Encode initial challenges + for _, challenge := range initialChallenges { + v := compiler.ToCanonicalVariable(challenge) + v.Compress(&proveCalldata) } + proveCalldata[0] = uint32(len(proveCalldata)) + + // Execute prove blueprint instruction + proofOutputs := compiler.AddInstruction(c.proveBlueprintID, proveCalldata) + + // Convert outputs to proof + proofSerialized := make([]frontend.Variable, len(proofOutputs)) + for i, wireID := range proofOutputs { + proofSerialized[i] = compiler.InternalVariable(wireID) + } + + var proof gadget.Proof + forSnarkSorted := utils.SliceOfRefs(forSnark.circuit) if proof, err = gadget.DeserializeProof(forSnarkSorted, proofSerialized); err != nil { @@ -252,22 +346,22 @@ func (c *Circuit) verify(api frontend.API, initialChallenges []frontend.Variable } var hsh hash.FieldHasher - if hsh, err = hash.GetFieldHasher(c.toStore.HashName, api); err != nil { + if hsh, err = hash.GetFieldHasher(c.hashName, api); err != nil { return err } return gadget.Verify(api, forSnark.circuit, forSnark.assignments, proof, fiatshamir.WithHash(hsh, initialChallenges...), gadget.WithSortedCircuit(forSnarkSorted)) } -func newCircuitDataForSnark(curve ecc.ID, info *gkrinfo.StoringInfo, assignment gkrtypes.WireAssignment) (circuitDataForSnark, error) { - circuit, err := gkrtypes.NewCircuit(info.Circuit, gkrgates.Get) +func newCircuitDataForSnark(curve ecc.ID, untypedCircuit gkrinfo.Circuit, assignment gkrtypes.WireAssignment) (circuitDataForSnark, error) { + circuit, err := gkrtypes.NewCircuit(untypedCircuit, gkrgates.Get) if err != nil { return circuitDataForSnark{}, fmt.Errorf("failed to convert GKR info to circuit: %w", err) } for i := range circuit { if !circuit[i].Gate.SupportsCurve(curve) { - return circuitDataForSnark{}, fmt.Errorf("gate \"%s\" not usable over curve \"%s\"", info.Circuit[i].Gate, curve) + return circuitDataForSnark{}, fmt.Errorf("gate \"%s\" not usable over curve \"%s\"", untypedCircuit[i].Gate, curve) } } @@ -280,10 +374,22 @@ func newCircuitDataForSnark(curve ecc.ID, info *gkrinfo.StoringInfo, assignment // GetValue is a debugging utility returning the value of variable v at instance i. // While v can be an input or output variable, GetValue is most useful for querying intermediate values in the circuit. func (c *Circuit) GetValue(v gkr.Variable, i int) frontend.Variable { - // last input to ensure the solver's work is done before GetAssignment is called - res, err := c.api.Compiler().NewHint(c.hints.GetAssignment, 1, c.index, int(v), i, c.assignments[c.outs[0]][i]) + // Access blueprint directly to get assignment + // The blueprint stores all wire assignments after solving + if c.blueprint == nil { + panic("blueprint not initialized") + } + + // Get blueprint (GKR only works with U64/large fields) + bp := c.blueprint.(*gkrbn254.BlueprintSolve) + compiler := c.api.Compiler() + solver, ok := compiler.(constraint.Solver[constraint.U64]) + if !ok { + panic("compiler does not implement Solver[U64] interface") + } + val, err := bp.GetAssignment(solver, int(v), i) if err != nil { panic(err) } - return res[0] + return val } diff --git a/test/engine.go b/test/engine.go index cf45ee90ae..f258d51fa4 100644 --- a/test/engine.go +++ b/test/engine.go @@ -15,8 +15,6 @@ import ( "github.com/bits-and-blooms/bitset" "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/internal/gkr/gkrinfo" - "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/debug" "github.com/consensys/gnark/frontend/schema" @@ -791,10 +789,6 @@ func (e *engine) ToCanonicalVariable(v frontend.Variable) frontend.CanonicalVari return wrappedBigInt{Int: r, modulus: e.q} } -func (e *engine) NewGkr() (*gkrinfo.StoringInfo, int) { - return new(gkrinfo.StoringInfo), 0 // the index is not used in the solver -} - // MustBeLessOrEqCst implements method comparing value given by its bits aBits // to a bound. func (e *engine) MustBeLessOrEqCst(aBits []frontend.Variable, bound *big.Int, aForDebug frontend.Variable) { From f74a6cb787a9670482ea94416a4cd22015a5bd82 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Thu, 22 Jan 2026 18:54:20 -0600 Subject: [PATCH 119/251] refactor: remove unnecessary size parameters from instruction input --- .../backend/template/gkr/blueprint.go.tmpl | 83 ++++++++++--------- internal/gkr/bls12-377/blueprint.go | 83 ++++++++++--------- internal/gkr/bls12-381/blueprint.go | 83 ++++++++++--------- internal/gkr/bls24-315/blueprint.go | 83 ++++++++++--------- internal/gkr/bls24-317/blueprint.go | 83 ++++++++++--------- internal/gkr/bn254/blueprint.go | 83 ++++++++++--------- internal/gkr/bw6-633/blueprint.go | 83 ++++++++++--------- internal/gkr/bw6-761/blueprint.go | 83 ++++++++++--------- std/gkrapi/compile.go | 21 ++--- 9 files changed, 357 insertions(+), 328 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index e6cf63b409..ef98899ec3 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -1,6 +1,7 @@ import ( "fmt" "math/big" + "math/bits" "sync" "{{ .FieldPackagePath }}" @@ -8,6 +9,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" + gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -61,7 +63,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() - instanceIdx := b.nbInstances + // Read instance index from calldata + instanceIdx := int(inst.Calldata[0]) // Initialize assignment array if this is the first instance if b.assignment == nil { @@ -79,31 +82,30 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Read input values from instruction calldata - nbInputsInst := int(inst.Calldata[1]) - if nbInputsInst != b.NbInputs { - return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) - } - - offset := 2 + // Read input values + offset := 1 // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) + // Read exactly b.NbInputs values from calldata + inputValues := make([]{{ .ElementType }}, b.NbInputs) + for i := range b.NbInputs { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + bigInt := s.ToBigInt(val) + inputValues[i].SetBigInt(bigInt) + } + // Process all wires in topological order (circuit is already sorted) inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] if w.IsInput() { - // Read input value from calldata and convert to {{ .ElementType }} - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta - - // Convert U64 to {{ .ElementType }} - bigInt := s.ToBigInt(val) - b.assignment[wI][instanceIdx].SetBigInt(bigInt) + // Use pre-read input value + b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) inputIdx++ } else { // Get evaluator for this wire from the circuit evaluator @@ -157,10 +159,9 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - nbInputsInst := int(inst.Calldata[1]) - offset := 2 - - for range nbInputsInst { + // Parse exactly b.NbInputs linear expressions + offset := 1 // skip instance index + for range b.NbInputs { n := int(inst.Calldata[offset]) offset++ @@ -236,17 +237,18 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } - // Read initial challenges from instruction calldata - nbChallenges := int(inst.Calldata[2]) - challenges := make([]{{ .ElementType }}, nbChallenges) - offset := 3 - for i := range nbChallenges { + // Read initial challenges from instruction calldata (parse dynamically, no metadata) + challenges := make([]{{ .ElementType }}, 0, 4) // pre-allocate reasonable size + offset := 0 + for offset < len(inst.Calldata) { val, delta := s.Read(inst.Calldata[offset:]) offset += delta // Convert U64 to {{ .ElementType }} + var challenge {{ .ElementType }} bigInt := s.ToBigInt(val) - challenges[i].SetBigInt(bigInt) + challenge.SetBigInt(bigInt) + challenges = append(challenges, challenge) } // Convert challenges to [][]byte for Fiat-Shamir @@ -305,24 +307,22 @@ func (b *BlueprintProve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - if len(inst.Calldata) > 1 { - return int(inst.Calldata[1]) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances == 0 { + return 0 } - return 0 + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - if len(inst.Calldata) < 3 { - return maxLevel + 1 - } - - nbChallenges := int(inst.Calldata[2]) - offset := 3 - - for range nbChallenges { + // Parse all challenges dynamically (no metadata) + offset := 0 + for offset < len(inst.Calldata) { n := int(inst.Calldata[offset]) offset++ @@ -339,9 +339,14 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - proofSize := int(inst.Calldata[1]) - for i := range proofSize { - tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances > 0 { + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } } return outputLevel diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index e300370e59..3192b218fb 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/big" + "math/bits" "sync" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" @@ -15,6 +16,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" + gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -68,7 +70,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() - instanceIdx := b.nbInstances + // Read instance index from calldata + instanceIdx := int(inst.Calldata[0]) // Initialize assignment array if this is the first instance if b.assignment == nil { @@ -86,31 +89,30 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Read input values from instruction calldata - nbInputsInst := int(inst.Calldata[1]) - if nbInputsInst != b.NbInputs { - return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) - } - - offset := 2 + // Read input values from instruction calldata (no metadata, just inputs) + offset := 1 // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) + // Read exactly b.NbInputs values from calldata + inputValues := make([]fr.Element, b.NbInputs) + for i := range b.NbInputs { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + bigInt := s.ToBigInt(val) + inputValues[i].SetBigInt(bigInt) + } + // Process all wires in topological order (circuit is already sorted) inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] if w.IsInput() { - // Read input value from calldata and convert to fr.Element - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta - - // Convert U64 to fr.Element - bigInt := s.ToBigInt(val) - b.assignment[wI][instanceIdx].SetBigInt(bigInt) + // Use pre-read input value + b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) inputIdx++ } else { // Get evaluator for this wire from the circuit evaluator @@ -164,10 +166,9 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - nbInputsInst := int(inst.Calldata[1]) - offset := 2 - - for range nbInputsInst { + // Parse exactly b.NbInputs linear expressions + offset := 1 // skip instance index + for range b.NbInputs { n := int(inst.Calldata[offset]) offset++ @@ -243,17 +244,18 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } - // Read initial challenges from instruction calldata - nbChallenges := int(inst.Calldata[2]) - challenges := make([]fr.Element, nbChallenges) - offset := 3 - for i := range nbChallenges { + // Read initial challenges from instruction calldata (parse dynamically, no metadata) + challenges := make([]fr.Element, 0, 4) // pre-allocate reasonable size + offset := 0 + for offset < len(inst.Calldata) { val, delta := s.Read(inst.Calldata[offset:]) offset += delta // Convert U64 to fr.Element + var challenge fr.Element bigInt := s.ToBigInt(val) - challenges[i].SetBigInt(bigInt) + challenge.SetBigInt(bigInt) + challenges = append(challenges, challenge) } // Convert challenges to [][]byte for Fiat-Shamir @@ -312,24 +314,22 @@ func (b *BlueprintProve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - if len(inst.Calldata) > 1 { - return int(inst.Calldata[1]) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances == 0 { + return 0 } - return 0 + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - if len(inst.Calldata) < 3 { - return maxLevel + 1 - } - - nbChallenges := int(inst.Calldata[2]) - offset := 3 - - for range nbChallenges { + // Parse all challenges dynamically (no metadata) + offset := 0 + for offset < len(inst.Calldata) { n := int(inst.Calldata[offset]) offset++ @@ -346,9 +346,14 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - proofSize := int(inst.Calldata[1]) - for i := range proofSize { - tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances > 0 { + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } } return outputLevel diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 6cb46344d2..548cf3e942 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/big" + "math/bits" "sync" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" @@ -15,6 +16,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" + gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -68,7 +70,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() - instanceIdx := b.nbInstances + // Read instance index from calldata + instanceIdx := int(inst.Calldata[0]) // Initialize assignment array if this is the first instance if b.assignment == nil { @@ -86,31 +89,30 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Read input values from instruction calldata - nbInputsInst := int(inst.Calldata[1]) - if nbInputsInst != b.NbInputs { - return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) - } - - offset := 2 + // Read input values from instruction calldata (no metadata, just inputs) + offset := 1 // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) + // Read exactly b.NbInputs values from calldata + inputValues := make([]fr.Element, b.NbInputs) + for i := range b.NbInputs { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + bigInt := s.ToBigInt(val) + inputValues[i].SetBigInt(bigInt) + } + // Process all wires in topological order (circuit is already sorted) inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] if w.IsInput() { - // Read input value from calldata and convert to fr.Element - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta - - // Convert U64 to fr.Element - bigInt := s.ToBigInt(val) - b.assignment[wI][instanceIdx].SetBigInt(bigInt) + // Use pre-read input value + b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) inputIdx++ } else { // Get evaluator for this wire from the circuit evaluator @@ -164,10 +166,9 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - nbInputsInst := int(inst.Calldata[1]) - offset := 2 - - for range nbInputsInst { + // Parse exactly b.NbInputs linear expressions + offset := 1 // skip instance index + for range b.NbInputs { n := int(inst.Calldata[offset]) offset++ @@ -243,17 +244,18 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } - // Read initial challenges from instruction calldata - nbChallenges := int(inst.Calldata[2]) - challenges := make([]fr.Element, nbChallenges) - offset := 3 - for i := range nbChallenges { + // Read initial challenges from instruction calldata (parse dynamically, no metadata) + challenges := make([]fr.Element, 0, 4) // pre-allocate reasonable size + offset := 0 + for offset < len(inst.Calldata) { val, delta := s.Read(inst.Calldata[offset:]) offset += delta // Convert U64 to fr.Element + var challenge fr.Element bigInt := s.ToBigInt(val) - challenges[i].SetBigInt(bigInt) + challenge.SetBigInt(bigInt) + challenges = append(challenges, challenge) } // Convert challenges to [][]byte for Fiat-Shamir @@ -312,24 +314,22 @@ func (b *BlueprintProve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - if len(inst.Calldata) > 1 { - return int(inst.Calldata[1]) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances == 0 { + return 0 } - return 0 + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - if len(inst.Calldata) < 3 { - return maxLevel + 1 - } - - nbChallenges := int(inst.Calldata[2]) - offset := 3 - - for range nbChallenges { + // Parse all challenges dynamically (no metadata) + offset := 0 + for offset < len(inst.Calldata) { n := int(inst.Calldata[offset]) offset++ @@ -346,9 +346,14 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - proofSize := int(inst.Calldata[1]) - for i := range proofSize { - tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances > 0 { + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } } return outputLevel diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index cdf9daf70f..eb7cf12993 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/big" + "math/bits" "sync" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" @@ -15,6 +16,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" + gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -68,7 +70,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() - instanceIdx := b.nbInstances + // Read instance index from calldata + instanceIdx := int(inst.Calldata[0]) // Initialize assignment array if this is the first instance if b.assignment == nil { @@ -86,31 +89,30 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Read input values from instruction calldata - nbInputsInst := int(inst.Calldata[1]) - if nbInputsInst != b.NbInputs { - return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) - } - - offset := 2 + // Read input values from instruction calldata (no metadata, just inputs) + offset := 1 // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) + // Read exactly b.NbInputs values from calldata + inputValues := make([]fr.Element, b.NbInputs) + for i := range b.NbInputs { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + bigInt := s.ToBigInt(val) + inputValues[i].SetBigInt(bigInt) + } + // Process all wires in topological order (circuit is already sorted) inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] if w.IsInput() { - // Read input value from calldata and convert to fr.Element - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta - - // Convert U64 to fr.Element - bigInt := s.ToBigInt(val) - b.assignment[wI][instanceIdx].SetBigInt(bigInt) + // Use pre-read input value + b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) inputIdx++ } else { // Get evaluator for this wire from the circuit evaluator @@ -164,10 +166,9 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - nbInputsInst := int(inst.Calldata[1]) - offset := 2 - - for range nbInputsInst { + // Parse exactly b.NbInputs linear expressions + offset := 1 // skip instance index + for range b.NbInputs { n := int(inst.Calldata[offset]) offset++ @@ -243,17 +244,18 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } - // Read initial challenges from instruction calldata - nbChallenges := int(inst.Calldata[2]) - challenges := make([]fr.Element, nbChallenges) - offset := 3 - for i := range nbChallenges { + // Read initial challenges from instruction calldata (parse dynamically, no metadata) + challenges := make([]fr.Element, 0, 4) // pre-allocate reasonable size + offset := 0 + for offset < len(inst.Calldata) { val, delta := s.Read(inst.Calldata[offset:]) offset += delta // Convert U64 to fr.Element + var challenge fr.Element bigInt := s.ToBigInt(val) - challenges[i].SetBigInt(bigInt) + challenge.SetBigInt(bigInt) + challenges = append(challenges, challenge) } // Convert challenges to [][]byte for Fiat-Shamir @@ -312,24 +314,22 @@ func (b *BlueprintProve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - if len(inst.Calldata) > 1 { - return int(inst.Calldata[1]) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances == 0 { + return 0 } - return 0 + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - if len(inst.Calldata) < 3 { - return maxLevel + 1 - } - - nbChallenges := int(inst.Calldata[2]) - offset := 3 - - for range nbChallenges { + // Parse all challenges dynamically (no metadata) + offset := 0 + for offset < len(inst.Calldata) { n := int(inst.Calldata[offset]) offset++ @@ -346,9 +346,14 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - proofSize := int(inst.Calldata[1]) - for i := range proofSize { - tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances > 0 { + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } } return outputLevel diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index cf83c045fb..887e7fd792 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/big" + "math/bits" "sync" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" @@ -15,6 +16,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" + gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -68,7 +70,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() - instanceIdx := b.nbInstances + // Read instance index from calldata + instanceIdx := int(inst.Calldata[0]) // Initialize assignment array if this is the first instance if b.assignment == nil { @@ -86,31 +89,30 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Read input values from instruction calldata - nbInputsInst := int(inst.Calldata[1]) - if nbInputsInst != b.NbInputs { - return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) - } - - offset := 2 + // Read input values from instruction calldata (no metadata, just inputs) + offset := 1 // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) + // Read exactly b.NbInputs values from calldata + inputValues := make([]fr.Element, b.NbInputs) + for i := range b.NbInputs { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + bigInt := s.ToBigInt(val) + inputValues[i].SetBigInt(bigInt) + } + // Process all wires in topological order (circuit is already sorted) inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] if w.IsInput() { - // Read input value from calldata and convert to fr.Element - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta - - // Convert U64 to fr.Element - bigInt := s.ToBigInt(val) - b.assignment[wI][instanceIdx].SetBigInt(bigInt) + // Use pre-read input value + b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) inputIdx++ } else { // Get evaluator for this wire from the circuit evaluator @@ -164,10 +166,9 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - nbInputsInst := int(inst.Calldata[1]) - offset := 2 - - for range nbInputsInst { + // Parse exactly b.NbInputs linear expressions + offset := 1 // skip instance index + for range b.NbInputs { n := int(inst.Calldata[offset]) offset++ @@ -243,17 +244,18 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } - // Read initial challenges from instruction calldata - nbChallenges := int(inst.Calldata[2]) - challenges := make([]fr.Element, nbChallenges) - offset := 3 - for i := range nbChallenges { + // Read initial challenges from instruction calldata (parse dynamically, no metadata) + challenges := make([]fr.Element, 0, 4) // pre-allocate reasonable size + offset := 0 + for offset < len(inst.Calldata) { val, delta := s.Read(inst.Calldata[offset:]) offset += delta // Convert U64 to fr.Element + var challenge fr.Element bigInt := s.ToBigInt(val) - challenges[i].SetBigInt(bigInt) + challenge.SetBigInt(bigInt) + challenges = append(challenges, challenge) } // Convert challenges to [][]byte for Fiat-Shamir @@ -312,24 +314,22 @@ func (b *BlueprintProve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - if len(inst.Calldata) > 1 { - return int(inst.Calldata[1]) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances == 0 { + return 0 } - return 0 + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - if len(inst.Calldata) < 3 { - return maxLevel + 1 - } - - nbChallenges := int(inst.Calldata[2]) - offset := 3 - - for range nbChallenges { + // Parse all challenges dynamically (no metadata) + offset := 0 + for offset < len(inst.Calldata) { n := int(inst.Calldata[offset]) offset++ @@ -346,9 +346,14 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - proofSize := int(inst.Calldata[1]) - for i := range proofSize { - tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances > 0 { + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } } return outputLevel diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index c3b21de03b..4c011f2e3f 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/big" + "math/bits" "sync" "github.com/consensys/gnark-crypto/ecc/bn254/fr" @@ -15,6 +16,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" + gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -68,7 +70,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() - instanceIdx := b.nbInstances + // Read instance index from calldata + instanceIdx := int(inst.Calldata[0]) // Initialize assignment array if this is the first instance if b.assignment == nil { @@ -86,31 +89,30 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Read input values from instruction calldata - nbInputsInst := int(inst.Calldata[1]) - if nbInputsInst != b.NbInputs { - return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) - } - - offset := 2 + // Read input values from instruction calldata (no metadata, just inputs) + offset := 1 // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) + // Read exactly b.NbInputs values from calldata + inputValues := make([]fr.Element, b.NbInputs) + for i := range b.NbInputs { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + bigInt := s.ToBigInt(val) + inputValues[i].SetBigInt(bigInt) + } + // Process all wires in topological order (circuit is already sorted) inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] if w.IsInput() { - // Read input value from calldata and convert to fr.Element - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta - - // Convert U64 to fr.Element - bigInt := s.ToBigInt(val) - b.assignment[wI][instanceIdx].SetBigInt(bigInt) + // Use pre-read input value + b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) inputIdx++ } else { // Get evaluator for this wire from the circuit evaluator @@ -164,10 +166,9 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - nbInputsInst := int(inst.Calldata[1]) - offset := 2 - - for range nbInputsInst { + // Parse exactly b.NbInputs linear expressions + offset := 1 // skip instance index + for range b.NbInputs { n := int(inst.Calldata[offset]) offset++ @@ -243,17 +244,18 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } - // Read initial challenges from instruction calldata - nbChallenges := int(inst.Calldata[2]) - challenges := make([]fr.Element, nbChallenges) - offset := 3 - for i := range nbChallenges { + // Read initial challenges from instruction calldata (parse dynamically, no metadata) + challenges := make([]fr.Element, 0, 4) // pre-allocate reasonable size + offset := 0 + for offset < len(inst.Calldata) { val, delta := s.Read(inst.Calldata[offset:]) offset += delta // Convert U64 to fr.Element + var challenge fr.Element bigInt := s.ToBigInt(val) - challenges[i].SetBigInt(bigInt) + challenge.SetBigInt(bigInt) + challenges = append(challenges, challenge) } // Convert challenges to [][]byte for Fiat-Shamir @@ -312,24 +314,22 @@ func (b *BlueprintProve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - if len(inst.Calldata) > 1 { - return int(inst.Calldata[1]) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances == 0 { + return 0 } - return 0 + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - if len(inst.Calldata) < 3 { - return maxLevel + 1 - } - - nbChallenges := int(inst.Calldata[2]) - offset := 3 - - for range nbChallenges { + // Parse all challenges dynamically (no metadata) + offset := 0 + for offset < len(inst.Calldata) { n := int(inst.Calldata[offset]) offset++ @@ -346,9 +346,14 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - proofSize := int(inst.Calldata[1]) - for i := range proofSize { - tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances > 0 { + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } } return outputLevel diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 81059eb7fb..3ca6c84e7e 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/big" + "math/bits" "sync" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" @@ -15,6 +16,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" + gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -68,7 +70,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() - instanceIdx := b.nbInstances + // Read instance index from calldata + instanceIdx := int(inst.Calldata[0]) // Initialize assignment array if this is the first instance if b.assignment == nil { @@ -86,31 +89,30 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Read input values from instruction calldata - nbInputsInst := int(inst.Calldata[1]) - if nbInputsInst != b.NbInputs { - return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) - } - - offset := 2 + // Read input values from instruction calldata (no metadata, just inputs) + offset := 1 // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) + // Read exactly b.NbInputs values from calldata + inputValues := make([]fr.Element, b.NbInputs) + for i := range b.NbInputs { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + bigInt := s.ToBigInt(val) + inputValues[i].SetBigInt(bigInt) + } + // Process all wires in topological order (circuit is already sorted) inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] if w.IsInput() { - // Read input value from calldata and convert to fr.Element - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta - - // Convert U64 to fr.Element - bigInt := s.ToBigInt(val) - b.assignment[wI][instanceIdx].SetBigInt(bigInt) + // Use pre-read input value + b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) inputIdx++ } else { // Get evaluator for this wire from the circuit evaluator @@ -164,10 +166,9 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - nbInputsInst := int(inst.Calldata[1]) - offset := 2 - - for range nbInputsInst { + // Parse exactly b.NbInputs linear expressions + offset := 1 // skip instance index + for range b.NbInputs { n := int(inst.Calldata[offset]) offset++ @@ -243,17 +244,18 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } - // Read initial challenges from instruction calldata - nbChallenges := int(inst.Calldata[2]) - challenges := make([]fr.Element, nbChallenges) - offset := 3 - for i := range nbChallenges { + // Read initial challenges from instruction calldata (parse dynamically, no metadata) + challenges := make([]fr.Element, 0, 4) // pre-allocate reasonable size + offset := 0 + for offset < len(inst.Calldata) { val, delta := s.Read(inst.Calldata[offset:]) offset += delta // Convert U64 to fr.Element + var challenge fr.Element bigInt := s.ToBigInt(val) - challenges[i].SetBigInt(bigInt) + challenge.SetBigInt(bigInt) + challenges = append(challenges, challenge) } // Convert challenges to [][]byte for Fiat-Shamir @@ -312,24 +314,22 @@ func (b *BlueprintProve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - if len(inst.Calldata) > 1 { - return int(inst.Calldata[1]) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances == 0 { + return 0 } - return 0 + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - if len(inst.Calldata) < 3 { - return maxLevel + 1 - } - - nbChallenges := int(inst.Calldata[2]) - offset := 3 - - for range nbChallenges { + // Parse all challenges dynamically (no metadata) + offset := 0 + for offset < len(inst.Calldata) { n := int(inst.Calldata[offset]) offset++ @@ -346,9 +346,14 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - proofSize := int(inst.Calldata[1]) - for i := range proofSize { - tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances > 0 { + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } } return outputLevel diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 9bc13fbc56..fd7cc5f346 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/big" + "math/bits" "sync" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" @@ -15,6 +16,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" + gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -68,7 +70,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() - instanceIdx := b.nbInstances + // Read instance index from calldata + instanceIdx := int(inst.Calldata[0]) // Initialize assignment array if this is the first instance if b.assignment == nil { @@ -86,31 +89,30 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Read input values from instruction calldata - nbInputsInst := int(inst.Calldata[1]) - if nbInputsInst != b.NbInputs { - return fmt.Errorf("expected %d inputs, got %d", b.NbInputs, nbInputsInst) - } - - offset := 2 + // Read input values from instruction calldata (no metadata, just inputs) + offset := 1 // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) + // Read exactly b.NbInputs values from calldata + inputValues := make([]fr.Element, b.NbInputs) + for i := range b.NbInputs { + val, delta := s.Read(inst.Calldata[offset:]) + offset += delta + bigInt := s.ToBigInt(val) + inputValues[i].SetBigInt(bigInt) + } + // Process all wires in topological order (circuit is already sorted) inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] if w.IsInput() { - // Read input value from calldata and convert to fr.Element - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta - - // Convert U64 to fr.Element - bigInt := s.ToBigInt(val) - b.assignment[wI][instanceIdx].SetBigInt(bigInt) + // Use pre-read input value + b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) inputIdx++ } else { // Get evaluator for this wire from the circuit evaluator @@ -164,10 +166,9 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - nbInputsInst := int(inst.Calldata[1]) - offset := 2 - - for range nbInputsInst { + // Parse exactly b.NbInputs linear expressions + offset := 1 // skip instance index + for range b.NbInputs { n := int(inst.Calldata[offset]) offset++ @@ -243,17 +244,18 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } - // Read initial challenges from instruction calldata - nbChallenges := int(inst.Calldata[2]) - challenges := make([]fr.Element, nbChallenges) - offset := 3 - for i := range nbChallenges { + // Read initial challenges from instruction calldata (parse dynamically, no metadata) + challenges := make([]fr.Element, 0, 4) // pre-allocate reasonable size + offset := 0 + for offset < len(inst.Calldata) { val, delta := s.Read(inst.Calldata[offset:]) offset += delta // Convert U64 to fr.Element + var challenge fr.Element bigInt := s.ToBigInt(val) - challenges[i].SetBigInt(bigInt) + challenge.SetBigInt(bigInt) + challenges = append(challenges, challenge) } // Convert challenges to [][]byte for Fiat-Shamir @@ -312,24 +314,22 @@ func (b *BlueprintProve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - if len(inst.Calldata) > 1 { - return int(inst.Calldata[1]) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances == 0 { + return 0 } - return 0 + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - if len(inst.Calldata) < 3 { - return maxLevel + 1 - } - - nbChallenges := int(inst.Calldata[2]) - offset := 3 - - for range nbChallenges { + // Parse all challenges dynamically (no metadata) + offset := 0 + for offset < len(inst.Calldata) { n := int(inst.Calldata[offset]) offset++ @@ -346,9 +346,14 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - proofSize := int(inst.Calldata[1]) - for i := range proofSize { - tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + // Compute proof size from blueprint state + nbInstances := b.SolveBlueprint.GetNbInstances() + if nbInstances > 0 { + logNbInstances := bits.TrailingZeros(uint(nbInstances)) + proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + for i := range proofSize { + tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) + } } return outputLevel diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 7f1753a217..c45f65f557 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -3,7 +3,6 @@ package gkrapi import ( "errors" "fmt" - "math/bits" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" @@ -200,10 +199,10 @@ func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr } // Build instruction calldata for blueprint - // Format: [0]=length, [1]=nbInputs, [2...]=input values as linear expressions + // Format: [0]=instanceIndex, [1...]=input values as linear expressions compiler := c.api.Compiler() - calldata := make([]uint32, 2, 2+len(c.ins)*2+2) // pre-allocate roughly - calldata[1] = uint32(len(c.ins)) + calldata := make([]uint32, 1, 1+len(c.ins)*2+2) // pre-allocate roughly + calldata[0] = uint32(c.nbInstances) // instance index // Encode input variables for _, wI := range c.ins { @@ -219,9 +218,6 @@ func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr v.Compress(&calldata) } - // Set total length - calldata[0] = uint32(len(calldata)) - // Execute solve blueprint instruction outputs := compiler.AddInstruction(c.solveBlueprintID, calldata) @@ -309,16 +305,11 @@ func (c *Circuit) verify(api frontend.API, initialChallenges []frontend.Variable return fmt.Errorf("failed to create circuit data for snark: %w", err) } - // Build prove instruction using blueprint - proofSize := gadget.ProofSize(forSnark.circuit, bits.TrailingZeros(uint(len(c.assignments[0])))) - compiler := api.Compiler() // Build calldata for prove instruction - // Format: [0]=length, [1]=proofSize, [2]=nbChallenges, [3...]=challenge linear expressions - proveCalldata := make([]uint32, 3, 3+len(initialChallenges)*2+2) - proveCalldata[1] = uint32(proofSize) - proveCalldata[2] = uint32(len(initialChallenges)) + // Format: [0...]=challenge linear expressions (no metadata) + proveCalldata := make([]uint32, 0, len(initialChallenges)*2+2) // Encode initial challenges for _, challenge := range initialChallenges { @@ -326,8 +317,6 @@ func (c *Circuit) verify(api frontend.API, initialChallenges []frontend.Variable v.Compress(&proveCalldata) } - proveCalldata[0] = uint32(len(proveCalldata)) - // Execute prove blueprint instruction proofOutputs := compiler.AddInstruction(c.proveBlueprintID, proveCalldata) From 0bc2b79e2c1bf3a20a51ff27dae098482e3fed04 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Thu, 22 Jan 2026 19:05:13 -0600 Subject: [PATCH 120/251] fix: runtime pool initialization --- internal/generator/backend/backend | Bin 0 -> 9074130 bytes .../backend/template/gkr/blueprint.go.tmpl | 22 ++++++++++------ internal/gkr/bls12-377/blueprint.go | 24 +++++++++++------- internal/gkr/bls12-381/blueprint.go | 24 +++++++++++------- internal/gkr/bls24-315/blueprint.go | 24 +++++++++++------- internal/gkr/bls24-317/blueprint.go | 24 +++++++++++------- internal/gkr/bn254/blueprint.go | 24 +++++++++++------- internal/gkr/bw6-633/blueprint.go | 24 +++++++++++------- internal/gkr/bw6-761/blueprint.go | 24 +++++++++++------- std/gkrapi/compile.go | 16 +----------- 10 files changed, 120 insertions(+), 86 deletions(-) create mode 100755 internal/generator/backend/backend diff --git a/internal/generator/backend/backend b/internal/generator/backend/backend new file mode 100755 index 0000000000000000000000000000000000000000..92fb12efaade52d22132f3ebb016aa1c8c255b2e GIT binary patch literal 9074130 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 P{%>P?JnJMyOyJqTMm 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

lhU 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%0D{K3-k3l}3V8RNsLF(Q(#b+npnA|W0{LFIT_>m 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

WOUY( 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

UVGctEp~io9d#fo+1EM{eikj~ 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

sfyl$B5%qUQzuJqemSji?QBfyT*a!4`% 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

vEGU;cuYK$8J}(Z9t|G0U!E53#?Ri3f3^!d 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

IwRZ*C%P4qh}k9_AcO6A8s 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 z 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

hrnh1ZSeikzYFd!p@A^>k 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 >j?#s~A%k{hey* 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

3jRjb?-;6({r1Rrh7gwkE^UL;Hihu1` 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

e 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

7V&Vk(SqT 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

?js5ZPH8Ld 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

+!s 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¨9f?X^c0ysOVyW*TB9zf4 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

  • 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 h2q^$9i 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

    nPZl3Abvy|KGkWIIZCv>G3BUZeO@zubY;* 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

    3%(zD3+}^b`69mC)}`ASrKy0`}$)jhN0A*qb@g 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 xl;;r937{$u$y_bE6;=fD4p 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

    })&0W;b2R=5Z* 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

    7M>8jP5v8@64dhuS+vV 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�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

    l)MR_dI*5PbP$jMS1(O)y=s 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 z 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

    F_{Nj?Hp5qe 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

    _Pj!qd?IE{vqZLcitTN` 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

    l3}bDW24qe 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�rc*%BesY*QnK!!x+xsfx1HN*)Rr#XCz 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

    2ucZFBc*2;+Cl4rGgA@w3GnDdS7pRF1RC=y& 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�KU8t4L97 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 z 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 maxGateStackSize { - maxGateStackSize = stackSize - } - } - } - - // Initialize evaluator pool - this creates the sync.Pool with a factory function - // that creates circuitEvaluators. The actual initialization happens inside the blueprint - // when InitializeEvaluatorPool is called. - blueprint.InitializeEvaluatorPool(circuit, maxGateStackSize) + // Note: evaluatorPool is initialized lazily on first Solve() call // Register solve blueprint with compiler c.solveBlueprintID = c.api.Compiler().AddBlueprint(blueprint) From 487cf7d9b4ce3bd5d33b4f80f46ebe71ce67e034 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Thu, 22 Jan 2026 19:59:38 -0600 Subject: [PATCH 121/251] fix: montgomery conversion --- constraint/babybear/solver.go | 5 + constraint/bls12-377/solver.go | 5 + constraint/bls12-381/solver.go | 5 + constraint/bls24-315/solver.go | 5 + constraint/bls24-317/solver.go | 5 + constraint/blueprint.go | 4 + constraint/bn254/solver.go | 5 + constraint/bw6-633/solver.go | 5 + constraint/bw6-761/solver.go | 5 + constraint/koalabear/solver.go | 5 + constraint/tinyfield/solver.go | 5 + .../backend/template/gkr/blueprint.go.tmpl | 116 +++++++++--------- .../template/representations/solver.go.tmpl | 5 + internal/gkr/bls12-377/blueprint.go | 40 +++--- internal/gkr/bls12-381/blueprint.go | 40 +++--- internal/gkr/bls24-315/blueprint.go | 40 +++--- internal/gkr/bls24-317/blueprint.go | 40 +++--- internal/gkr/bn254/blueprint.go | 40 +++--- internal/gkr/bw6-633/blueprint.go | 40 +++--- internal/gkr/bw6-761/blueprint.go | 40 +++--- std/gkrapi/compile.go | 5 +- test/blueprint_solver.go | 5 + test/engine.go | 1 + 23 files changed, 252 insertions(+), 214 deletions(-) diff --git a/constraint/babybear/solver.go b/constraint/babybear/solver.go index e0ef0ffd74..b2d5159fd4 100644 --- a/constraint/babybear/solver.go +++ b/constraint/babybear/solver.go @@ -345,6 +345,11 @@ func (s *solver) IsSolved(vID uint32) bool { return s.solved[vID] } +// GetBlueprint returns the blueprint with the given ID. +func (s *solver) GetBlueprint(id constraint.BlueprintID) constraint.Blueprint { + return s.Blueprints[id] +} + // Read interprets input calldata as either a LinearExpression (if R1CS) or a Term (if Plonkish), // evaluates it and return the result and the number of uint32 word read. func (s *solver) Read(calldata []uint32) (constraint.U32, int) { diff --git a/constraint/bls12-377/solver.go b/constraint/bls12-377/solver.go index 1af47d2ec4..3a129b659d 100644 --- a/constraint/bls12-377/solver.go +++ b/constraint/bls12-377/solver.go @@ -345,6 +345,11 @@ func (s *solver) IsSolved(vID uint32) bool { return s.solved[vID] } +// GetBlueprint returns the blueprint with the given ID. +func (s *solver) GetBlueprint(id constraint.BlueprintID) constraint.Blueprint { + return s.Blueprints[id] +} + // Read interprets input calldata as either a LinearExpression (if R1CS) or a Term (if Plonkish), // evaluates it and return the result and the number of uint32 word read. func (s *solver) Read(calldata []uint32) (constraint.U64, int) { diff --git a/constraint/bls12-381/solver.go b/constraint/bls12-381/solver.go index 65c63d5546..132d7d88c8 100644 --- a/constraint/bls12-381/solver.go +++ b/constraint/bls12-381/solver.go @@ -345,6 +345,11 @@ func (s *solver) IsSolved(vID uint32) bool { return s.solved[vID] } +// GetBlueprint returns the blueprint with the given ID. +func (s *solver) GetBlueprint(id constraint.BlueprintID) constraint.Blueprint { + return s.Blueprints[id] +} + // Read interprets input calldata as either a LinearExpression (if R1CS) or a Term (if Plonkish), // evaluates it and return the result and the number of uint32 word read. func (s *solver) Read(calldata []uint32) (constraint.U64, int) { diff --git a/constraint/bls24-315/solver.go b/constraint/bls24-315/solver.go index 527b194760..c88c164904 100644 --- a/constraint/bls24-315/solver.go +++ b/constraint/bls24-315/solver.go @@ -345,6 +345,11 @@ func (s *solver) IsSolved(vID uint32) bool { return s.solved[vID] } +// GetBlueprint returns the blueprint with the given ID. +func (s *solver) GetBlueprint(id constraint.BlueprintID) constraint.Blueprint { + return s.Blueprints[id] +} + // Read interprets input calldata as either a LinearExpression (if R1CS) or a Term (if Plonkish), // evaluates it and return the result and the number of uint32 word read. func (s *solver) Read(calldata []uint32) (constraint.U64, int) { diff --git a/constraint/bls24-317/solver.go b/constraint/bls24-317/solver.go index d1a702d1dd..993273cb31 100644 --- a/constraint/bls24-317/solver.go +++ b/constraint/bls24-317/solver.go @@ -345,6 +345,11 @@ func (s *solver) IsSolved(vID uint32) bool { return s.solved[vID] } +// GetBlueprint returns the blueprint with the given ID. +func (s *solver) GetBlueprint(id constraint.BlueprintID) constraint.Blueprint { + return s.Blueprints[id] +} + // Read interprets input calldata as either a LinearExpression (if R1CS) or a Term (if Plonkish), // evaluates it and return the result and the number of uint32 word read. func (s *solver) Read(calldata []uint32) (constraint.U64, int) { diff --git a/constraint/blueprint.go b/constraint/blueprint.go index cfe2fa09f5..eea0265a7a 100644 --- a/constraint/blueprint.go +++ b/constraint/blueprint.go @@ -37,6 +37,10 @@ type Solver[E Element] interface { // Read interprets input calldata as a LinearExpression, // evaluates it and return the result and the number of uint32 word read. Read(calldata []uint32) (E, int) + + // GetBlueprint returns the blueprint with the given ID. + // This allows blueprints to query other blueprints by ID. + GetBlueprint(id BlueprintID) Blueprint } // BlueprintSolvable represents a blueprint that knows how to solve itself. diff --git a/constraint/bn254/solver.go b/constraint/bn254/solver.go index dd1f0d9956..8c658f0939 100644 --- a/constraint/bn254/solver.go +++ b/constraint/bn254/solver.go @@ -345,6 +345,11 @@ func (s *solver) IsSolved(vID uint32) bool { return s.solved[vID] } +// GetBlueprint returns the blueprint with the given ID. +func (s *solver) GetBlueprint(id constraint.BlueprintID) constraint.Blueprint { + return s.Blueprints[id] +} + // Read interprets input calldata as either a LinearExpression (if R1CS) or a Term (if Plonkish), // evaluates it and return the result and the number of uint32 word read. func (s *solver) Read(calldata []uint32) (constraint.U64, int) { diff --git a/constraint/bw6-633/solver.go b/constraint/bw6-633/solver.go index 4b7e00cbb1..9e82d560dd 100644 --- a/constraint/bw6-633/solver.go +++ b/constraint/bw6-633/solver.go @@ -345,6 +345,11 @@ func (s *solver) IsSolved(vID uint32) bool { return s.solved[vID] } +// GetBlueprint returns the blueprint with the given ID. +func (s *solver) GetBlueprint(id constraint.BlueprintID) constraint.Blueprint { + return s.Blueprints[id] +} + // Read interprets input calldata as either a LinearExpression (if R1CS) or a Term (if Plonkish), // evaluates it and return the result and the number of uint32 word read. func (s *solver) Read(calldata []uint32) (constraint.U64, int) { diff --git a/constraint/bw6-761/solver.go b/constraint/bw6-761/solver.go index 3530188bcf..5f6d007638 100644 --- a/constraint/bw6-761/solver.go +++ b/constraint/bw6-761/solver.go @@ -345,6 +345,11 @@ func (s *solver) IsSolved(vID uint32) bool { return s.solved[vID] } +// GetBlueprint returns the blueprint with the given ID. +func (s *solver) GetBlueprint(id constraint.BlueprintID) constraint.Blueprint { + return s.Blueprints[id] +} + // Read interprets input calldata as either a LinearExpression (if R1CS) or a Term (if Plonkish), // evaluates it and return the result and the number of uint32 word read. func (s *solver) Read(calldata []uint32) (constraint.U64, int) { diff --git a/constraint/koalabear/solver.go b/constraint/koalabear/solver.go index 35c224f51b..dc44c9f198 100644 --- a/constraint/koalabear/solver.go +++ b/constraint/koalabear/solver.go @@ -345,6 +345,11 @@ func (s *solver) IsSolved(vID uint32) bool { return s.solved[vID] } +// GetBlueprint returns the blueprint with the given ID. +func (s *solver) GetBlueprint(id constraint.BlueprintID) constraint.Blueprint { + return s.Blueprints[id] +} + // Read interprets input calldata as either a LinearExpression (if R1CS) or a Term (if Plonkish), // evaluates it and return the result and the number of uint32 word read. func (s *solver) Read(calldata []uint32) (constraint.U32, int) { diff --git a/constraint/tinyfield/solver.go b/constraint/tinyfield/solver.go index d4a39113e3..a8c3127b0d 100644 --- a/constraint/tinyfield/solver.go +++ b/constraint/tinyfield/solver.go @@ -345,6 +345,11 @@ func (s *solver) IsSolved(vID uint32) bool { return s.solved[vID] } +// GetBlueprint returns the blueprint with the given ID. +func (s *solver) GetBlueprint(id constraint.BlueprintID) constraint.Blueprint { + return s.Blueprints[id] +} + // Read interprets input calldata as either a LinearExpression (if R1CS) or a Term (if Plonkish), // evaluates it and return the result and the number of uint32 word read. func (s *solver) Read(calldata []uint32) (constraint.U32, int) { diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index ecb28b4ef9..5fc51ec2b5 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -22,62 +22,67 @@ type circuitEvaluator struct { type BlueprintSolve struct { // Circuit structure Circuit gkrtypes.Circuit - NbInputs int - NbOutputVars int - InputWires []int - OutputWires []int - MaxNbIn int // maximum number of inputs for any gate + + NbInstances int // Stateful data - stored as native {{ .ElementType }} // Not serialized - recreated lazily at solve time - nbInstances int assignment WireAssignment // []polynomial.MultiLin evaluatorPool sync.Pool // pool of circuitEvaluator, lazy-initialized + nbInputs int + nbOutputVars int + inputWires []int + outputWires []int + maxNbIn int // maximum number of inputs for any gate lock sync.Mutex } -// initializeEvaluatorPool lazily initializes the evaluator pool on first solve -func (b *BlueprintSolve) initializeEvaluatorPool() { - b.evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), - } - for wI := range b.Circuit { - w := &b.Circuit[wI] - if !w.IsInput() { - // Each gate evaluator allocates its own appropriately-sized stack - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) - } - } - return ce - }, - } -} - // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -// Solve implements the BlueprintStateful interface. -func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { +func (b *BlueprintSolve) initialize() { + + if b.assignment != nil { + return + } b.lock.Lock() defer b.lock.Unlock() - // Read instance index from calldata - instanceIdx := int(inst.Calldata[0]) + if b.assignment != nil { + return // the unlikely event that two or more instructions were competing to initialize the blueprint + } - // Initialize assignment array if this is the first instance - if b.assignment == nil { - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + b.evaluatorPool.New = func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(b.Circuit)), } + for wI := range b.Circuit { + w := &b.Circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce } + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, b.nbInstances) + } +} + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + + b.initialize() + + // Read instance index from calldata + instanceI := int(inst.Calldata[0]) + // Grow assignment slices to accommodate this instance for i := range b.assignment { - for len(b.assignment[i]) <= instanceIdx { + for len(b.assignment[i]) <= instanceI { var zero {{ .ElementType }} b.assignment[i] = append(b.assignment[i], zero) } @@ -85,7 +90,6 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Lazy initialize evaluator pool on first use if b.evaluatorPool.New == nil { - b.initializeEvaluatorPool() } // Read input values @@ -100,8 +104,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra for i := range b.NbInputs { val, delta := s.Read(inst.Calldata[offset:]) offset += delta - bigInt := s.ToBigInt(val) - inputValues[i].SetBigInt(bigInt) + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(inputValues[i][:], val[:]) } // Process all wires in topological order (circuit is already sorted) @@ -223,8 +227,9 @@ func (b *BlueprintSolve) GetNbInstances() int { // BlueprintProve is a {{.FieldID}}-specific blueprint for generating GKR proofs. type BlueprintProve struct { - SolveBlueprint *BlueprintSolve - HashName string + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + HashName string lock sync.Mutex } @@ -237,31 +242,26 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + // Get assignments from solve blueprint (already in {{ .ElementType }} form) - assignments := b.SolveBlueprint.GetAssignments() + assignments := solveBlueprint.GetAssignments() if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } // Read initial challenges from instruction calldata (parse dynamically, no metadata) - challenges := make([]{{ .ElementType }}, 0, 4) // pre-allocate reasonable size - offset := 0 - for offset < len(inst.Calldata) { - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta + insBytes := make([][]byte, 0) // first challenges + calldata := inst.Calldata + for len(calldata) != 0 { + val, delta := s.Read(inst.Calldata[delta:]) + calldata = calldata[delta:] - // Convert U64 to {{ .ElementType }} + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) var challenge {{ .ElementType }} - bigInt := s.ToBigInt(val) - challenge.SetBigInt(bigInt) - challenges = append(challenges, challenge) - } - - // Convert challenges to [][]byte for Fiat-Shamir - insBytes := make([][]byte, len(challenges)) - for i := range challenges { - insBytes[i] = make([]byte, fr.Bytes) - challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + copy(challenge[:], val[:]) + insBytes = append(insBytes, challenge.Marshal()) } // Create Fiat-Shamir settings @@ -269,7 +269,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the {{.FieldID}}-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("{{toLower .FieldID}} prove failed: %w", err) } diff --git a/internal/generator/backend/template/representations/solver.go.tmpl b/internal/generator/backend/template/representations/solver.go.tmpl index 356f537cb5..93b7fdf871 100644 --- a/internal/generator/backend/template/representations/solver.go.tmpl +++ b/internal/generator/backend/template/representations/solver.go.tmpl @@ -346,6 +346,11 @@ func (s *solver) IsSolved(vID uint32) bool { return s.solved[vID] } +// GetBlueprint returns the blueprint with the given ID. +func (s *solver) GetBlueprint(id constraint.BlueprintID) constraint.Blueprint { + return s.Blueprints[id] +} + // Read interprets input calldata as either a LinearExpression (if R1CS) or a Term (if Plonkish), // evaluates it and return the result and the number of uint32 word read. func (s *solver) Read(calldata []uint32) (constraint.{{ .ElementType }}, int) { diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 89b5e10d56..f177225c61 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -107,8 +107,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra for i := range b.NbInputs { val, delta := s.Read(inst.Calldata[offset:]) offset += delta - bigInt := s.ToBigInt(val) - inputValues[i].SetBigInt(bigInt) + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(inputValues[i][:], val[:]) } // Process all wires in topological order (circuit is already sorted) @@ -230,8 +230,9 @@ func (b *BlueprintSolve) GetNbInstances() int { // BlueprintProve is a BLS12_377-specific blueprint for generating GKR proofs. type BlueprintProve struct { - SolveBlueprint *BlueprintSolve - HashName string + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + HashName string lock sync.Mutex } @@ -244,31 +245,26 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + // Get assignments from solve blueprint (already in fr.Element form) - assignments := b.SolveBlueprint.GetAssignments() + assignments := solveBlueprint.GetAssignments() if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } // Read initial challenges from instruction calldata (parse dynamically, no metadata) - challenges := make([]fr.Element, 0, 4) // pre-allocate reasonable size - offset := 0 - for offset < len(inst.Calldata) { - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta + insBytes := make([][]byte, 0) // first challenges + calldata := inst.Calldata + for len(calldata) != 0 { + val, delta := s.Read(inst.Calldata[delta:]) + calldata = calldata[delta:] - // Convert U64 to fr.Element + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) var challenge fr.Element - bigInt := s.ToBigInt(val) - challenge.SetBigInt(bigInt) - challenges = append(challenges, challenge) - } - - // Convert challenges to [][]byte for Fiat-Shamir - insBytes := make([][]byte, len(challenges)) - for i := range challenges { - insBytes[i] = make([]byte, fr.Bytes) - challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + copy(challenge[:], val[:]) + insBytes = append(insBytes, challenge.Marshal()) } // Create Fiat-Shamir settings @@ -276,7 +272,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BLS12_377-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("bls12_377 prove failed: %w", err) } diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 04cb908171..b2f870147a 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -107,8 +107,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra for i := range b.NbInputs { val, delta := s.Read(inst.Calldata[offset:]) offset += delta - bigInt := s.ToBigInt(val) - inputValues[i].SetBigInt(bigInt) + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(inputValues[i][:], val[:]) } // Process all wires in topological order (circuit is already sorted) @@ -230,8 +230,9 @@ func (b *BlueprintSolve) GetNbInstances() int { // BlueprintProve is a BLS12_381-specific blueprint for generating GKR proofs. type BlueprintProve struct { - SolveBlueprint *BlueprintSolve - HashName string + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + HashName string lock sync.Mutex } @@ -244,31 +245,26 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + // Get assignments from solve blueprint (already in fr.Element form) - assignments := b.SolveBlueprint.GetAssignments() + assignments := solveBlueprint.GetAssignments() if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } // Read initial challenges from instruction calldata (parse dynamically, no metadata) - challenges := make([]fr.Element, 0, 4) // pre-allocate reasonable size - offset := 0 - for offset < len(inst.Calldata) { - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta + insBytes := make([][]byte, 0) // first challenges + calldata := inst.Calldata + for len(calldata) != 0 { + val, delta := s.Read(inst.Calldata[delta:]) + calldata = calldata[delta:] - // Convert U64 to fr.Element + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) var challenge fr.Element - bigInt := s.ToBigInt(val) - challenge.SetBigInt(bigInt) - challenges = append(challenges, challenge) - } - - // Convert challenges to [][]byte for Fiat-Shamir - insBytes := make([][]byte, len(challenges)) - for i := range challenges { - insBytes[i] = make([]byte, fr.Bytes) - challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + copy(challenge[:], val[:]) + insBytes = append(insBytes, challenge.Marshal()) } // Create Fiat-Shamir settings @@ -276,7 +272,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BLS12_381-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("bls12_381 prove failed: %w", err) } diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index 10eb0917fc..b083abbf5f 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -107,8 +107,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra for i := range b.NbInputs { val, delta := s.Read(inst.Calldata[offset:]) offset += delta - bigInt := s.ToBigInt(val) - inputValues[i].SetBigInt(bigInt) + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(inputValues[i][:], val[:]) } // Process all wires in topological order (circuit is already sorted) @@ -230,8 +230,9 @@ func (b *BlueprintSolve) GetNbInstances() int { // BlueprintProve is a BLS24_315-specific blueprint for generating GKR proofs. type BlueprintProve struct { - SolveBlueprint *BlueprintSolve - HashName string + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + HashName string lock sync.Mutex } @@ -244,31 +245,26 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + // Get assignments from solve blueprint (already in fr.Element form) - assignments := b.SolveBlueprint.GetAssignments() + assignments := solveBlueprint.GetAssignments() if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } // Read initial challenges from instruction calldata (parse dynamically, no metadata) - challenges := make([]fr.Element, 0, 4) // pre-allocate reasonable size - offset := 0 - for offset < len(inst.Calldata) { - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta + insBytes := make([][]byte, 0) // first challenges + calldata := inst.Calldata + for len(calldata) != 0 { + val, delta := s.Read(inst.Calldata[delta:]) + calldata = calldata[delta:] - // Convert U64 to fr.Element + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) var challenge fr.Element - bigInt := s.ToBigInt(val) - challenge.SetBigInt(bigInt) - challenges = append(challenges, challenge) - } - - // Convert challenges to [][]byte for Fiat-Shamir - insBytes := make([][]byte, len(challenges)) - for i := range challenges { - insBytes[i] = make([]byte, fr.Bytes) - challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + copy(challenge[:], val[:]) + insBytes = append(insBytes, challenge.Marshal()) } // Create Fiat-Shamir settings @@ -276,7 +272,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BLS24_315-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("bls24_315 prove failed: %w", err) } diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index ab1f5d3be6..5660720e89 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -107,8 +107,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra for i := range b.NbInputs { val, delta := s.Read(inst.Calldata[offset:]) offset += delta - bigInt := s.ToBigInt(val) - inputValues[i].SetBigInt(bigInt) + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(inputValues[i][:], val[:]) } // Process all wires in topological order (circuit is already sorted) @@ -230,8 +230,9 @@ func (b *BlueprintSolve) GetNbInstances() int { // BlueprintProve is a BLS24_317-specific blueprint for generating GKR proofs. type BlueprintProve struct { - SolveBlueprint *BlueprintSolve - HashName string + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + HashName string lock sync.Mutex } @@ -244,31 +245,26 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + // Get assignments from solve blueprint (already in fr.Element form) - assignments := b.SolveBlueprint.GetAssignments() + assignments := solveBlueprint.GetAssignments() if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } // Read initial challenges from instruction calldata (parse dynamically, no metadata) - challenges := make([]fr.Element, 0, 4) // pre-allocate reasonable size - offset := 0 - for offset < len(inst.Calldata) { - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta + insBytes := make([][]byte, 0) // first challenges + calldata := inst.Calldata + for len(calldata) != 0 { + val, delta := s.Read(inst.Calldata[delta:]) + calldata = calldata[delta:] - // Convert U64 to fr.Element + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) var challenge fr.Element - bigInt := s.ToBigInt(val) - challenge.SetBigInt(bigInt) - challenges = append(challenges, challenge) - } - - // Convert challenges to [][]byte for Fiat-Shamir - insBytes := make([][]byte, len(challenges)) - for i := range challenges { - insBytes[i] = make([]byte, fr.Bytes) - challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + copy(challenge[:], val[:]) + insBytes = append(insBytes, challenge.Marshal()) } // Create Fiat-Shamir settings @@ -276,7 +272,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BLS24_317-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("bls24_317 prove failed: %w", err) } diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 5bbc3b7918..29981fe744 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -107,8 +107,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra for i := range b.NbInputs { val, delta := s.Read(inst.Calldata[offset:]) offset += delta - bigInt := s.ToBigInt(val) - inputValues[i].SetBigInt(bigInt) + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(inputValues[i][:], val[:]) } // Process all wires in topological order (circuit is already sorted) @@ -230,8 +230,9 @@ func (b *BlueprintSolve) GetNbInstances() int { // BlueprintProve is a BN254-specific blueprint for generating GKR proofs. type BlueprintProve struct { - SolveBlueprint *BlueprintSolve - HashName string + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + HashName string lock sync.Mutex } @@ -244,31 +245,26 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + // Get assignments from solve blueprint (already in fr.Element form) - assignments := b.SolveBlueprint.GetAssignments() + assignments := solveBlueprint.GetAssignments() if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } // Read initial challenges from instruction calldata (parse dynamically, no metadata) - challenges := make([]fr.Element, 0, 4) // pre-allocate reasonable size - offset := 0 - for offset < len(inst.Calldata) { - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta + insBytes := make([][]byte, 0) // first challenges + calldata := inst.Calldata + for len(calldata) != 0 { + val, delta := s.Read(inst.Calldata[delta:]) + calldata = calldata[delta:] - // Convert U64 to fr.Element + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) var challenge fr.Element - bigInt := s.ToBigInt(val) - challenge.SetBigInt(bigInt) - challenges = append(challenges, challenge) - } - - // Convert challenges to [][]byte for Fiat-Shamir - insBytes := make([][]byte, len(challenges)) - for i := range challenges { - insBytes[i] = make([]byte, fr.Bytes) - challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + copy(challenge[:], val[:]) + insBytes = append(insBytes, challenge.Marshal()) } // Create Fiat-Shamir settings @@ -276,7 +272,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BN254-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("bn254 prove failed: %w", err) } diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 133322b4cc..061a4c6e57 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -107,8 +107,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra for i := range b.NbInputs { val, delta := s.Read(inst.Calldata[offset:]) offset += delta - bigInt := s.ToBigInt(val) - inputValues[i].SetBigInt(bigInt) + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(inputValues[i][:], val[:]) } // Process all wires in topological order (circuit is already sorted) @@ -230,8 +230,9 @@ func (b *BlueprintSolve) GetNbInstances() int { // BlueprintProve is a BW6_633-specific blueprint for generating GKR proofs. type BlueprintProve struct { - SolveBlueprint *BlueprintSolve - HashName string + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + HashName string lock sync.Mutex } @@ -244,31 +245,26 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + // Get assignments from solve blueprint (already in fr.Element form) - assignments := b.SolveBlueprint.GetAssignments() + assignments := solveBlueprint.GetAssignments() if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } // Read initial challenges from instruction calldata (parse dynamically, no metadata) - challenges := make([]fr.Element, 0, 4) // pre-allocate reasonable size - offset := 0 - for offset < len(inst.Calldata) { - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta + insBytes := make([][]byte, 0) // first challenges + calldata := inst.Calldata + for len(calldata) != 0 { + val, delta := s.Read(inst.Calldata[delta:]) + calldata = calldata[delta:] - // Convert U64 to fr.Element + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) var challenge fr.Element - bigInt := s.ToBigInt(val) - challenge.SetBigInt(bigInt) - challenges = append(challenges, challenge) - } - - // Convert challenges to [][]byte for Fiat-Shamir - insBytes := make([][]byte, len(challenges)) - for i := range challenges { - insBytes[i] = make([]byte, fr.Bytes) - challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + copy(challenge[:], val[:]) + insBytes = append(insBytes, challenge.Marshal()) } // Create Fiat-Shamir settings @@ -276,7 +272,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BW6_633-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("bw6_633 prove failed: %w", err) } diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 3bc1e5c65a..604f4fc000 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -107,8 +107,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra for i := range b.NbInputs { val, delta := s.Read(inst.Calldata[offset:]) offset += delta - bigInt := s.ToBigInt(val) - inputValues[i].SetBigInt(bigInt) + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(inputValues[i][:], val[:]) } // Process all wires in topological order (circuit is already sorted) @@ -230,8 +230,9 @@ func (b *BlueprintSolve) GetNbInstances() int { // BlueprintProve is a BW6_761-specific blueprint for generating GKR proofs. type BlueprintProve struct { - SolveBlueprint *BlueprintSolve - HashName string + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + HashName string lock sync.Mutex } @@ -244,31 +245,26 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra b.lock.Lock() defer b.lock.Unlock() + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + // Get assignments from solve blueprint (already in fr.Element form) - assignments := b.SolveBlueprint.GetAssignments() + assignments := solveBlueprint.GetAssignments() if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } // Read initial challenges from instruction calldata (parse dynamically, no metadata) - challenges := make([]fr.Element, 0, 4) // pre-allocate reasonable size - offset := 0 - for offset < len(inst.Calldata) { - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta + insBytes := make([][]byte, 0) // first challenges + calldata := inst.Calldata + for len(calldata) != 0 { + val, delta := s.Read(inst.Calldata[delta:]) + calldata = calldata[delta:] - // Convert U64 to fr.Element + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) var challenge fr.Element - bigInt := s.ToBigInt(val) - challenge.SetBigInt(bigInt) - challenges = append(challenges, challenge) - } - - // Convert challenges to [][]byte for Fiat-Shamir - insBytes := make([][]byte, len(challenges)) - for i := range challenges { - insBytes[i] = make([]byte, fr.Bytes) - challenges[i].BigInt((*big.Int)(nil)).FillBytes(insBytes[i]) + copy(challenge[:], val[:]) + insBytes = append(insBytes, challenge.Marshal()) } // Create Fiat-Shamir settings @@ -276,7 +272,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BW6_761-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(b.SolveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("bw6_761 prove failed: %w", err) } diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index a41476be7a..6cf98caf40 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -163,8 +163,9 @@ func (c *Circuit) createBlueprint() error { // Create and register prove blueprint with reference to solve blueprint proveBlueprint := &gkrbn254.BlueprintProve{ - SolveBlueprint: blueprint, - HashName: c.hashName, + SolveBlueprintID: c.solveBlueprintID, + SolveBlueprint: blueprint, + HashName: c.hashName, } c.proveBlueprintID = c.api.Compiler().AddBlueprint(proveBlueprint) diff --git a/test/blueprint_solver.go b/test/blueprint_solver.go index fe0b636cee..50fcf35cdf 100644 --- a/test/blueprint_solver.go +++ b/test/blueprint_solver.go @@ -13,6 +13,7 @@ import ( type blueprintSolver[E constraint.Element] struct { internalVariables []*big.Int q *big.Int + blueprints []constraint.Blueprint } // implements constraint.Solver @@ -36,6 +37,10 @@ func (s *blueprintSolver[E]) IsSolved(vID uint32) bool { panic("not implemented in test.Engine") } +func (s *blueprintSolver[E]) GetBlueprint(id constraint.BlueprintID) constraint.Blueprint { + return s.blueprints[id] +} + // implements constraint.Field func (s *blueprintSolver[E]) FromInterface(i interface{}) E { diff --git a/test/engine.go b/test/engine.go index f258d51fa4..edfa64d2da 100644 --- a/test/engine.go +++ b/test/engine.go @@ -736,6 +736,7 @@ func addInstructionGeneric[E constraint.Element](e *engine, bID constraint.Bluep s := blueprintSolver[E]{ internalVariables: e.internalVariables, q: e.q, + blueprints: e.blueprints, } if err := blueprint.Solve(&s, inst); err != nil { panic(err) From 66e3d1fe242148b05f7a3c57e6a2059ee6053fbc Mon Sep 17 00:00:00 2001 From: Tabaie Date: Thu, 22 Jan 2026 20:18:52 -0600 Subject: [PATCH 122/251] fix: make metadata private --- .../backend/template/gkr/blueprint.go.tmpl | 122 +++++++------- internal/gkr/bls12-377/blueprint.go | 154 ++++++++++-------- internal/gkr/bls12-381/blueprint.go | 154 ++++++++++-------- internal/gkr/bls24-315/blueprint.go | 154 ++++++++++-------- internal/gkr/bls24-317/blueprint.go | 154 ++++++++++-------- internal/gkr/bn254/blueprint.go | 154 ++++++++++-------- internal/gkr/bw6-633/blueprint.go | 154 ++++++++++-------- internal/gkr/bw6-761/blueprint.go | 154 ++++++++++-------- std/gkrapi/compile.go | 33 +--- 9 files changed, 678 insertions(+), 555 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 5fc51ec2b5..3c659a058c 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -20,22 +20,20 @@ type circuitEvaluator struct { // BlueprintSolve is a {{.FieldID}}-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { - // Circuit structure - Circuit gkrtypes.Circuit + // Circuit structure (serialized) + Circuit gkrtypes.Circuit + NbInstances int - NbInstances int - - // Stateful data - stored as native {{ .ElementType }} // Not serialized - recreated lazily at solve time - assignment WireAssignment // []polynomial.MultiLin - evaluatorPool sync.Pool // pool of circuitEvaluator, lazy-initialized - nbInputs int - nbOutputVars int - inputWires []int - outputWires []int - maxNbIn int // maximum number of inputs for any gate - - lock sync.Mutex + assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + nbOutputVars int `cbor:"-"` + inputWires []int `cbor:"-"` + outputWires []int `cbor:"-"` + maxNbIn int `cbor:"-"` // maximum number of inputs for any gate + + lock sync.Mutex `cbor:"-"` } // Ensures BlueprintSolve implements BlueprintStateful @@ -53,6 +51,32 @@ func (b *BlueprintSolve) initialize() { return // the unlikely event that two or more instructions were competing to initialize the blueprint } + // Compute metadata from Circuit + b.maxNbIn = b.Circuit.MaxGateNbIn() + for i := range b.Circuit { + if b.Circuit[i].IsInput() { + b.nbInputs++ + b.inputWires = append(b.inputWires, i) + } + } + + // Identify output wires (not inputs to any other wire) + isOutput := make([]bool, len(b.Circuit)) + for i := range b.Circuit { + isOutput[i] = true + } + for _, w := range b.Circuit { + for _, inIdx := range w.Inputs { + isOutput[inIdx] = false + } + } + for i := range b.Circuit { + if isOutput[i] { + b.nbOutputVars++ + b.outputWires = append(b.outputWires, i) + } + } + b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ evaluators: make([]gateEvaluator, len(b.Circuit)), @@ -68,7 +92,7 @@ func (b *BlueprintSolve) initialize() { b.assignment = make(WireAssignment, len(b.Circuit)) for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, b.nbInstances) + b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) } } @@ -77,76 +101,60 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.initialize() - // Read instance index from calldata - instanceI := int(inst.Calldata[0]) - - // Grow assignment slices to accommodate this instance - for i := range b.assignment { - for len(b.assignment[i]) <= instanceI { - var zero {{ .ElementType }} - b.assignment[i] = append(b.assignment[i], zero) - } - } - - // Lazy initialize evaluator pool on first use - if b.evaluatorPool.New == nil { - } - - // Read input values - offset := 1 // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.NbInputs values from calldata - inputValues := make([]{{ .ElementType }}, b.NbInputs) - for i := range b.NbInputs { - val, delta := s.Read(inst.Calldata[offset:]) + // Read exactly b.nbInputs values from calldata + inputValues := make([]{{ .ElementType }}, b.nbInputs) + for i := range b.nbInputs { + offset += delta // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) copy(inputValues[i][:], val[:]) } + instanceI := int(inst.Calldata[0]) + calldata := inst.Calldata[1:] + inputI := 0 // Process all wires in topological order (circuit is already sorted) - inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] - if w.IsInput() { - // Use pre-read input value - b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) - inputIdx++ + if w.IsInput(){ + val, delta := s.Read(calldata) + calldata = calldata[delta:] + copy(b.assignment[wI][instanceI][:], val) + inputI++ } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + evaluator.pushInput(&b.assignment[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + b.assignment[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires for the instruction (convert {{ .ElementType }} to U64) - outputIdx := 0 - for _, outWI := range b.OutputWires { + outputI := 0 + for _, outWI := range b.outputWires { var bigInt big.Int - b.assignment[outWI][instanceIdx].BigInt(&bigInt) - s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) + b.assignment[outWI][instanceI].BigInt(&bigInt) + s.SetValue(uint32(outputI+int(inst.WireOffset)), s.FromInterface(&bigInt)) outputIdx++ } - b.nbInstances++ return nil } // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.nbInstances = 0 b.assignment = nil } @@ -162,16 +170,16 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.NbOutputVars + return b.nbOutputVars } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse exactly b.NbInputs linear expressions + // Parse exactly b.nbInputs linear expressions offset := 1 // skip instance index - for range b.NbInputs { + for range b.nbInputs { n := int(inst.Calldata[offset]) offset++ @@ -188,7 +196,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.NbOutputVars { + for i := range b.nbOutputVars { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -218,11 +226,9 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances solved +// GetNbInstances returns the number of instances (compile-time) func (b *BlueprintSolve) GetNbInstances() int { - b.lock.Lock() - defer b.lock.Unlock() - return b.nbInstances + return b.NbInstances } // BlueprintProve is a {{.FieldID}}-specific blueprint for generating GKR proofs. @@ -255,7 +261,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra insBytes := make([][]byte, 0) // first challenges calldata := inst.Calldata for len(calldata) != 0 { - val, delta := s.Read(inst.Calldata[delta:]) + val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index f177225c61..23062cdffa 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -27,74 +27,98 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS12_377-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { - // Circuit structure - Circuit gkrtypes.Circuit - NbInputs int - NbOutputVars int - InputWires []int - OutputWires []int - MaxNbIn int // maximum number of inputs for any gate - - // Stateful data - stored as native fr.Element - // Not serialized - recreated lazily at solve time - nbInstances int - assignment WireAssignment // []polynomial.MultiLin - evaluatorPool sync.Pool // pool of circuitEvaluator, lazy-initialized + // Circuit structure (serialized) + Circuit gkrtypes.Circuit + NbInstances int - lock sync.Mutex -} - -// initializeEvaluatorPool lazily initializes the evaluator pool on first solve -func (b *BlueprintSolve) initializeEvaluatorPool() { - b.evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), - } - for wI := range b.Circuit { - w := &b.Circuit[wI] - if !w.IsInput() { - // Each gate evaluator allocates its own appropriately-sized stack - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) - } - } - return ce - }, - } + // Not serialized - recreated lazily at solve time + assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + nbOutputVars int `cbor:"-"` + inputWires []int `cbor:"-"` + outputWires []int `cbor:"-"` + maxNbIn int `cbor:"-"` // maximum number of inputs for any gate + + lock sync.Mutex `cbor:"-"` } // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -// Solve implements the BlueprintStateful interface. -func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { +func (b *BlueprintSolve) initialize() { + + if b.assignment != nil { + return + } b.lock.Lock() defer b.lock.Unlock() - // Read instance index from calldata - instanceIdx := int(inst.Calldata[0]) + if b.assignment != nil { + return // the unlikely event that two or more instructions were competing to initialize the blueprint + } + + // Compute metadata from Circuit + b.maxNbIn = b.Circuit.MaxGateNbIn() + for i := range b.Circuit { + if b.Circuit[i].IsInput() { + b.nbInputs++ + b.inputWires = append(b.inputWires, i) + } + } - // Initialize assignment array if this is the first instance - if b.assignment == nil { - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + // Identify output wires (not inputs to any other wire) + isOutput := make([]bool, len(b.Circuit)) + for i := range b.Circuit { + isOutput[i] = true + } + for _, w := range b.Circuit { + for _, inIdx := range w.Inputs { + isOutput[inIdx] = false + } + } + for i := range b.Circuit { + if isOutput[i] { + b.nbOutputVars++ + b.outputWires = append(b.outputWires, i) } } + b.evaluatorPool.New = func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(b.Circuit)), + } + for wI := range b.Circuit { + w := &b.Circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce + } + + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) + } +} + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + + b.initialize() + + // Read instance index from calldata + instanceI := int(inst.Calldata[0]) + // Grow assignment slices to accommodate this instance for i := range b.assignment { - for len(b.assignment[i]) <= instanceIdx { + for len(b.assignment[i]) <= instanceI { var zero fr.Element b.assignment[i] = append(b.assignment[i], zero) } } - // Lazy initialize evaluator pool on first use - if b.evaluatorPool.New == nil { - b.initializeEvaluatorPool() - } - // Read input values offset := 1 @@ -102,9 +126,9 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.NbInputs values from calldata - inputValues := make([]fr.Element, b.NbInputs) - for i := range b.NbInputs { + // Read exactly b.nbInputs values from calldata + inputValues := make([]fr.Element, b.nbInputs) + for i := range b.nbInputs { val, delta := s.Read(inst.Calldata[offset:]) offset += delta // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) @@ -118,7 +142,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra if w.IsInput() { // Use pre-read input value - b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) + b.assignment[wI][instanceI].Set(&inputValues[inputIdx]) inputIdx++ } else { // Get evaluator for this wire from the circuit evaluator @@ -126,30 +150,28 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + evaluator.pushInput(&b.assignment[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + b.assignment[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires for the instruction (convert fr.Element to U64) outputIdx := 0 - for _, outWI := range b.OutputWires { + for _, outWI := range b.outputWires { var bigInt big.Int - b.assignment[outWI][instanceIdx].BigInt(&bigInt) + b.assignment[outWI][instanceI].BigInt(&bigInt) s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) outputIdx++ } - b.nbInstances++ return nil } // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.nbInstances = 0 b.assignment = nil } @@ -165,16 +187,16 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.NbOutputVars + return b.nbOutputVars } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse exactly b.NbInputs linear expressions + // Parse exactly b.nbInputs linear expressions offset := 1 // skip instance index - for range b.NbInputs { + for range b.nbInputs { n := int(inst.Calldata[offset]) offset++ @@ -191,7 +213,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.NbOutputVars { + for i := range b.nbOutputVars { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -221,11 +243,9 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances solved +// GetNbInstances returns the number of instances (compile-time) func (b *BlueprintSolve) GetNbInstances() int { - b.lock.Lock() - defer b.lock.Unlock() - return b.nbInstances + return b.NbInstances } // BlueprintProve is a BLS12_377-specific blueprint for generating GKR proofs. @@ -258,7 +278,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra insBytes := make([][]byte, 0) // first challenges calldata := inst.Calldata for len(calldata) != 0 { - val, delta := s.Read(inst.Calldata[delta:]) + val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index b2f870147a..e79e9b11bb 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -27,74 +27,98 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS12_381-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { - // Circuit structure - Circuit gkrtypes.Circuit - NbInputs int - NbOutputVars int - InputWires []int - OutputWires []int - MaxNbIn int // maximum number of inputs for any gate - - // Stateful data - stored as native fr.Element - // Not serialized - recreated lazily at solve time - nbInstances int - assignment WireAssignment // []polynomial.MultiLin - evaluatorPool sync.Pool // pool of circuitEvaluator, lazy-initialized + // Circuit structure (serialized) + Circuit gkrtypes.Circuit + NbInstances int - lock sync.Mutex -} - -// initializeEvaluatorPool lazily initializes the evaluator pool on first solve -func (b *BlueprintSolve) initializeEvaluatorPool() { - b.evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), - } - for wI := range b.Circuit { - w := &b.Circuit[wI] - if !w.IsInput() { - // Each gate evaluator allocates its own appropriately-sized stack - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) - } - } - return ce - }, - } + // Not serialized - recreated lazily at solve time + assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + nbOutputVars int `cbor:"-"` + inputWires []int `cbor:"-"` + outputWires []int `cbor:"-"` + maxNbIn int `cbor:"-"` // maximum number of inputs for any gate + + lock sync.Mutex `cbor:"-"` } // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -// Solve implements the BlueprintStateful interface. -func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { +func (b *BlueprintSolve) initialize() { + + if b.assignment != nil { + return + } b.lock.Lock() defer b.lock.Unlock() - // Read instance index from calldata - instanceIdx := int(inst.Calldata[0]) + if b.assignment != nil { + return // the unlikely event that two or more instructions were competing to initialize the blueprint + } + + // Compute metadata from Circuit + b.maxNbIn = b.Circuit.MaxGateNbIn() + for i := range b.Circuit { + if b.Circuit[i].IsInput() { + b.nbInputs++ + b.inputWires = append(b.inputWires, i) + } + } - // Initialize assignment array if this is the first instance - if b.assignment == nil { - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + // Identify output wires (not inputs to any other wire) + isOutput := make([]bool, len(b.Circuit)) + for i := range b.Circuit { + isOutput[i] = true + } + for _, w := range b.Circuit { + for _, inIdx := range w.Inputs { + isOutput[inIdx] = false + } + } + for i := range b.Circuit { + if isOutput[i] { + b.nbOutputVars++ + b.outputWires = append(b.outputWires, i) } } + b.evaluatorPool.New = func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(b.Circuit)), + } + for wI := range b.Circuit { + w := &b.Circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce + } + + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) + } +} + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + + b.initialize() + + // Read instance index from calldata + instanceI := int(inst.Calldata[0]) + // Grow assignment slices to accommodate this instance for i := range b.assignment { - for len(b.assignment[i]) <= instanceIdx { + for len(b.assignment[i]) <= instanceI { var zero fr.Element b.assignment[i] = append(b.assignment[i], zero) } } - // Lazy initialize evaluator pool on first use - if b.evaluatorPool.New == nil { - b.initializeEvaluatorPool() - } - // Read input values offset := 1 @@ -102,9 +126,9 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.NbInputs values from calldata - inputValues := make([]fr.Element, b.NbInputs) - for i := range b.NbInputs { + // Read exactly b.nbInputs values from calldata + inputValues := make([]fr.Element, b.nbInputs) + for i := range b.nbInputs { val, delta := s.Read(inst.Calldata[offset:]) offset += delta // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) @@ -118,7 +142,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra if w.IsInput() { // Use pre-read input value - b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) + b.assignment[wI][instanceI].Set(&inputValues[inputIdx]) inputIdx++ } else { // Get evaluator for this wire from the circuit evaluator @@ -126,30 +150,28 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + evaluator.pushInput(&b.assignment[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + b.assignment[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires for the instruction (convert fr.Element to U64) outputIdx := 0 - for _, outWI := range b.OutputWires { + for _, outWI := range b.outputWires { var bigInt big.Int - b.assignment[outWI][instanceIdx].BigInt(&bigInt) + b.assignment[outWI][instanceI].BigInt(&bigInt) s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) outputIdx++ } - b.nbInstances++ return nil } // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.nbInstances = 0 b.assignment = nil } @@ -165,16 +187,16 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.NbOutputVars + return b.nbOutputVars } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse exactly b.NbInputs linear expressions + // Parse exactly b.nbInputs linear expressions offset := 1 // skip instance index - for range b.NbInputs { + for range b.nbInputs { n := int(inst.Calldata[offset]) offset++ @@ -191,7 +213,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.NbOutputVars { + for i := range b.nbOutputVars { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -221,11 +243,9 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances solved +// GetNbInstances returns the number of instances (compile-time) func (b *BlueprintSolve) GetNbInstances() int { - b.lock.Lock() - defer b.lock.Unlock() - return b.nbInstances + return b.NbInstances } // BlueprintProve is a BLS12_381-specific blueprint for generating GKR proofs. @@ -258,7 +278,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra insBytes := make([][]byte, 0) // first challenges calldata := inst.Calldata for len(calldata) != 0 { - val, delta := s.Read(inst.Calldata[delta:]) + val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index b083abbf5f..40dea4f242 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -27,74 +27,98 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS24_315-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { - // Circuit structure - Circuit gkrtypes.Circuit - NbInputs int - NbOutputVars int - InputWires []int - OutputWires []int - MaxNbIn int // maximum number of inputs for any gate - - // Stateful data - stored as native fr.Element - // Not serialized - recreated lazily at solve time - nbInstances int - assignment WireAssignment // []polynomial.MultiLin - evaluatorPool sync.Pool // pool of circuitEvaluator, lazy-initialized + // Circuit structure (serialized) + Circuit gkrtypes.Circuit + NbInstances int - lock sync.Mutex -} - -// initializeEvaluatorPool lazily initializes the evaluator pool on first solve -func (b *BlueprintSolve) initializeEvaluatorPool() { - b.evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), - } - for wI := range b.Circuit { - w := &b.Circuit[wI] - if !w.IsInput() { - // Each gate evaluator allocates its own appropriately-sized stack - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) - } - } - return ce - }, - } + // Not serialized - recreated lazily at solve time + assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + nbOutputVars int `cbor:"-"` + inputWires []int `cbor:"-"` + outputWires []int `cbor:"-"` + maxNbIn int `cbor:"-"` // maximum number of inputs for any gate + + lock sync.Mutex `cbor:"-"` } // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -// Solve implements the BlueprintStateful interface. -func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { +func (b *BlueprintSolve) initialize() { + + if b.assignment != nil { + return + } b.lock.Lock() defer b.lock.Unlock() - // Read instance index from calldata - instanceIdx := int(inst.Calldata[0]) + if b.assignment != nil { + return // the unlikely event that two or more instructions were competing to initialize the blueprint + } + + // Compute metadata from Circuit + b.maxNbIn = b.Circuit.MaxGateNbIn() + for i := range b.Circuit { + if b.Circuit[i].IsInput() { + b.nbInputs++ + b.inputWires = append(b.inputWires, i) + } + } - // Initialize assignment array if this is the first instance - if b.assignment == nil { - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + // Identify output wires (not inputs to any other wire) + isOutput := make([]bool, len(b.Circuit)) + for i := range b.Circuit { + isOutput[i] = true + } + for _, w := range b.Circuit { + for _, inIdx := range w.Inputs { + isOutput[inIdx] = false + } + } + for i := range b.Circuit { + if isOutput[i] { + b.nbOutputVars++ + b.outputWires = append(b.outputWires, i) } } + b.evaluatorPool.New = func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(b.Circuit)), + } + for wI := range b.Circuit { + w := &b.Circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce + } + + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) + } +} + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + + b.initialize() + + // Read instance index from calldata + instanceI := int(inst.Calldata[0]) + // Grow assignment slices to accommodate this instance for i := range b.assignment { - for len(b.assignment[i]) <= instanceIdx { + for len(b.assignment[i]) <= instanceI { var zero fr.Element b.assignment[i] = append(b.assignment[i], zero) } } - // Lazy initialize evaluator pool on first use - if b.evaluatorPool.New == nil { - b.initializeEvaluatorPool() - } - // Read input values offset := 1 @@ -102,9 +126,9 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.NbInputs values from calldata - inputValues := make([]fr.Element, b.NbInputs) - for i := range b.NbInputs { + // Read exactly b.nbInputs values from calldata + inputValues := make([]fr.Element, b.nbInputs) + for i := range b.nbInputs { val, delta := s.Read(inst.Calldata[offset:]) offset += delta // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) @@ -118,7 +142,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra if w.IsInput() { // Use pre-read input value - b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) + b.assignment[wI][instanceI].Set(&inputValues[inputIdx]) inputIdx++ } else { // Get evaluator for this wire from the circuit evaluator @@ -126,30 +150,28 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + evaluator.pushInput(&b.assignment[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + b.assignment[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires for the instruction (convert fr.Element to U64) outputIdx := 0 - for _, outWI := range b.OutputWires { + for _, outWI := range b.outputWires { var bigInt big.Int - b.assignment[outWI][instanceIdx].BigInt(&bigInt) + b.assignment[outWI][instanceI].BigInt(&bigInt) s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) outputIdx++ } - b.nbInstances++ return nil } // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.nbInstances = 0 b.assignment = nil } @@ -165,16 +187,16 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.NbOutputVars + return b.nbOutputVars } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse exactly b.NbInputs linear expressions + // Parse exactly b.nbInputs linear expressions offset := 1 // skip instance index - for range b.NbInputs { + for range b.nbInputs { n := int(inst.Calldata[offset]) offset++ @@ -191,7 +213,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.NbOutputVars { + for i := range b.nbOutputVars { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -221,11 +243,9 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances solved +// GetNbInstances returns the number of instances (compile-time) func (b *BlueprintSolve) GetNbInstances() int { - b.lock.Lock() - defer b.lock.Unlock() - return b.nbInstances + return b.NbInstances } // BlueprintProve is a BLS24_315-specific blueprint for generating GKR proofs. @@ -258,7 +278,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra insBytes := make([][]byte, 0) // first challenges calldata := inst.Calldata for len(calldata) != 0 { - val, delta := s.Read(inst.Calldata[delta:]) + val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 5660720e89..1f31cb10f2 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -27,74 +27,98 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS24_317-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { - // Circuit structure - Circuit gkrtypes.Circuit - NbInputs int - NbOutputVars int - InputWires []int - OutputWires []int - MaxNbIn int // maximum number of inputs for any gate - - // Stateful data - stored as native fr.Element - // Not serialized - recreated lazily at solve time - nbInstances int - assignment WireAssignment // []polynomial.MultiLin - evaluatorPool sync.Pool // pool of circuitEvaluator, lazy-initialized + // Circuit structure (serialized) + Circuit gkrtypes.Circuit + NbInstances int - lock sync.Mutex -} - -// initializeEvaluatorPool lazily initializes the evaluator pool on first solve -func (b *BlueprintSolve) initializeEvaluatorPool() { - b.evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), - } - for wI := range b.Circuit { - w := &b.Circuit[wI] - if !w.IsInput() { - // Each gate evaluator allocates its own appropriately-sized stack - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) - } - } - return ce - }, - } + // Not serialized - recreated lazily at solve time + assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + nbOutputVars int `cbor:"-"` + inputWires []int `cbor:"-"` + outputWires []int `cbor:"-"` + maxNbIn int `cbor:"-"` // maximum number of inputs for any gate + + lock sync.Mutex `cbor:"-"` } // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -// Solve implements the BlueprintStateful interface. -func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { +func (b *BlueprintSolve) initialize() { + + if b.assignment != nil { + return + } b.lock.Lock() defer b.lock.Unlock() - // Read instance index from calldata - instanceIdx := int(inst.Calldata[0]) + if b.assignment != nil { + return // the unlikely event that two or more instructions were competing to initialize the blueprint + } + + // Compute metadata from Circuit + b.maxNbIn = b.Circuit.MaxGateNbIn() + for i := range b.Circuit { + if b.Circuit[i].IsInput() { + b.nbInputs++ + b.inputWires = append(b.inputWires, i) + } + } - // Initialize assignment array if this is the first instance - if b.assignment == nil { - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + // Identify output wires (not inputs to any other wire) + isOutput := make([]bool, len(b.Circuit)) + for i := range b.Circuit { + isOutput[i] = true + } + for _, w := range b.Circuit { + for _, inIdx := range w.Inputs { + isOutput[inIdx] = false + } + } + for i := range b.Circuit { + if isOutput[i] { + b.nbOutputVars++ + b.outputWires = append(b.outputWires, i) } } + b.evaluatorPool.New = func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(b.Circuit)), + } + for wI := range b.Circuit { + w := &b.Circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce + } + + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) + } +} + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + + b.initialize() + + // Read instance index from calldata + instanceI := int(inst.Calldata[0]) + // Grow assignment slices to accommodate this instance for i := range b.assignment { - for len(b.assignment[i]) <= instanceIdx { + for len(b.assignment[i]) <= instanceI { var zero fr.Element b.assignment[i] = append(b.assignment[i], zero) } } - // Lazy initialize evaluator pool on first use - if b.evaluatorPool.New == nil { - b.initializeEvaluatorPool() - } - // Read input values offset := 1 @@ -102,9 +126,9 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.NbInputs values from calldata - inputValues := make([]fr.Element, b.NbInputs) - for i := range b.NbInputs { + // Read exactly b.nbInputs values from calldata + inputValues := make([]fr.Element, b.nbInputs) + for i := range b.nbInputs { val, delta := s.Read(inst.Calldata[offset:]) offset += delta // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) @@ -118,7 +142,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra if w.IsInput() { // Use pre-read input value - b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) + b.assignment[wI][instanceI].Set(&inputValues[inputIdx]) inputIdx++ } else { // Get evaluator for this wire from the circuit evaluator @@ -126,30 +150,28 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + evaluator.pushInput(&b.assignment[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + b.assignment[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires for the instruction (convert fr.Element to U64) outputIdx := 0 - for _, outWI := range b.OutputWires { + for _, outWI := range b.outputWires { var bigInt big.Int - b.assignment[outWI][instanceIdx].BigInt(&bigInt) + b.assignment[outWI][instanceI].BigInt(&bigInt) s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) outputIdx++ } - b.nbInstances++ return nil } // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.nbInstances = 0 b.assignment = nil } @@ -165,16 +187,16 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.NbOutputVars + return b.nbOutputVars } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse exactly b.NbInputs linear expressions + // Parse exactly b.nbInputs linear expressions offset := 1 // skip instance index - for range b.NbInputs { + for range b.nbInputs { n := int(inst.Calldata[offset]) offset++ @@ -191,7 +213,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.NbOutputVars { + for i := range b.nbOutputVars { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -221,11 +243,9 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances solved +// GetNbInstances returns the number of instances (compile-time) func (b *BlueprintSolve) GetNbInstances() int { - b.lock.Lock() - defer b.lock.Unlock() - return b.nbInstances + return b.NbInstances } // BlueprintProve is a BLS24_317-specific blueprint for generating GKR proofs. @@ -258,7 +278,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra insBytes := make([][]byte, 0) // first challenges calldata := inst.Calldata for len(calldata) != 0 { - val, delta := s.Read(inst.Calldata[delta:]) + val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 29981fe744..c7537fd046 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -27,74 +27,98 @@ type circuitEvaluator struct { // BlueprintSolve is a BN254-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { - // Circuit structure - Circuit gkrtypes.Circuit - NbInputs int - NbOutputVars int - InputWires []int - OutputWires []int - MaxNbIn int // maximum number of inputs for any gate - - // Stateful data - stored as native fr.Element - // Not serialized - recreated lazily at solve time - nbInstances int - assignment WireAssignment // []polynomial.MultiLin - evaluatorPool sync.Pool // pool of circuitEvaluator, lazy-initialized + // Circuit structure (serialized) + Circuit gkrtypes.Circuit + NbInstances int - lock sync.Mutex -} - -// initializeEvaluatorPool lazily initializes the evaluator pool on first solve -func (b *BlueprintSolve) initializeEvaluatorPool() { - b.evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), - } - for wI := range b.Circuit { - w := &b.Circuit[wI] - if !w.IsInput() { - // Each gate evaluator allocates its own appropriately-sized stack - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) - } - } - return ce - }, - } + // Not serialized - recreated lazily at solve time + assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + nbOutputVars int `cbor:"-"` + inputWires []int `cbor:"-"` + outputWires []int `cbor:"-"` + maxNbIn int `cbor:"-"` // maximum number of inputs for any gate + + lock sync.Mutex `cbor:"-"` } // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -// Solve implements the BlueprintStateful interface. -func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { +func (b *BlueprintSolve) initialize() { + + if b.assignment != nil { + return + } b.lock.Lock() defer b.lock.Unlock() - // Read instance index from calldata - instanceIdx := int(inst.Calldata[0]) + if b.assignment != nil { + return // the unlikely event that two or more instructions were competing to initialize the blueprint + } + + // Compute metadata from Circuit + b.maxNbIn = b.Circuit.MaxGateNbIn() + for i := range b.Circuit { + if b.Circuit[i].IsInput() { + b.nbInputs++ + b.inputWires = append(b.inputWires, i) + } + } - // Initialize assignment array if this is the first instance - if b.assignment == nil { - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + // Identify output wires (not inputs to any other wire) + isOutput := make([]bool, len(b.Circuit)) + for i := range b.Circuit { + isOutput[i] = true + } + for _, w := range b.Circuit { + for _, inIdx := range w.Inputs { + isOutput[inIdx] = false + } + } + for i := range b.Circuit { + if isOutput[i] { + b.nbOutputVars++ + b.outputWires = append(b.outputWires, i) } } + b.evaluatorPool.New = func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(b.Circuit)), + } + for wI := range b.Circuit { + w := &b.Circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce + } + + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) + } +} + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + + b.initialize() + + // Read instance index from calldata + instanceI := int(inst.Calldata[0]) + // Grow assignment slices to accommodate this instance for i := range b.assignment { - for len(b.assignment[i]) <= instanceIdx { + for len(b.assignment[i]) <= instanceI { var zero fr.Element b.assignment[i] = append(b.assignment[i], zero) } } - // Lazy initialize evaluator pool on first use - if b.evaluatorPool.New == nil { - b.initializeEvaluatorPool() - } - // Read input values offset := 1 @@ -102,9 +126,9 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.NbInputs values from calldata - inputValues := make([]fr.Element, b.NbInputs) - for i := range b.NbInputs { + // Read exactly b.nbInputs values from calldata + inputValues := make([]fr.Element, b.nbInputs) + for i := range b.nbInputs { val, delta := s.Read(inst.Calldata[offset:]) offset += delta // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) @@ -118,7 +142,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra if w.IsInput() { // Use pre-read input value - b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) + b.assignment[wI][instanceI].Set(&inputValues[inputIdx]) inputIdx++ } else { // Get evaluator for this wire from the circuit evaluator @@ -126,30 +150,28 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + evaluator.pushInput(&b.assignment[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + b.assignment[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires for the instruction (convert fr.Element to U64) outputIdx := 0 - for _, outWI := range b.OutputWires { + for _, outWI := range b.outputWires { var bigInt big.Int - b.assignment[outWI][instanceIdx].BigInt(&bigInt) + b.assignment[outWI][instanceI].BigInt(&bigInt) s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) outputIdx++ } - b.nbInstances++ return nil } // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.nbInstances = 0 b.assignment = nil } @@ -165,16 +187,16 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.NbOutputVars + return b.nbOutputVars } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse exactly b.NbInputs linear expressions + // Parse exactly b.nbInputs linear expressions offset := 1 // skip instance index - for range b.NbInputs { + for range b.nbInputs { n := int(inst.Calldata[offset]) offset++ @@ -191,7 +213,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.NbOutputVars { + for i := range b.nbOutputVars { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -221,11 +243,9 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances solved +// GetNbInstances returns the number of instances (compile-time) func (b *BlueprintSolve) GetNbInstances() int { - b.lock.Lock() - defer b.lock.Unlock() - return b.nbInstances + return b.NbInstances } // BlueprintProve is a BN254-specific blueprint for generating GKR proofs. @@ -258,7 +278,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra insBytes := make([][]byte, 0) // first challenges calldata := inst.Calldata for len(calldata) != 0 { - val, delta := s.Read(inst.Calldata[delta:]) + val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 061a4c6e57..5b95d1d369 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -27,74 +27,98 @@ type circuitEvaluator struct { // BlueprintSolve is a BW6_633-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { - // Circuit structure - Circuit gkrtypes.Circuit - NbInputs int - NbOutputVars int - InputWires []int - OutputWires []int - MaxNbIn int // maximum number of inputs for any gate - - // Stateful data - stored as native fr.Element - // Not serialized - recreated lazily at solve time - nbInstances int - assignment WireAssignment // []polynomial.MultiLin - evaluatorPool sync.Pool // pool of circuitEvaluator, lazy-initialized + // Circuit structure (serialized) + Circuit gkrtypes.Circuit + NbInstances int - lock sync.Mutex -} - -// initializeEvaluatorPool lazily initializes the evaluator pool on first solve -func (b *BlueprintSolve) initializeEvaluatorPool() { - b.evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), - } - for wI := range b.Circuit { - w := &b.Circuit[wI] - if !w.IsInput() { - // Each gate evaluator allocates its own appropriately-sized stack - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) - } - } - return ce - }, - } + // Not serialized - recreated lazily at solve time + assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + nbOutputVars int `cbor:"-"` + inputWires []int `cbor:"-"` + outputWires []int `cbor:"-"` + maxNbIn int `cbor:"-"` // maximum number of inputs for any gate + + lock sync.Mutex `cbor:"-"` } // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -// Solve implements the BlueprintStateful interface. -func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { +func (b *BlueprintSolve) initialize() { + + if b.assignment != nil { + return + } b.lock.Lock() defer b.lock.Unlock() - // Read instance index from calldata - instanceIdx := int(inst.Calldata[0]) + if b.assignment != nil { + return // the unlikely event that two or more instructions were competing to initialize the blueprint + } + + // Compute metadata from Circuit + b.maxNbIn = b.Circuit.MaxGateNbIn() + for i := range b.Circuit { + if b.Circuit[i].IsInput() { + b.nbInputs++ + b.inputWires = append(b.inputWires, i) + } + } - // Initialize assignment array if this is the first instance - if b.assignment == nil { - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + // Identify output wires (not inputs to any other wire) + isOutput := make([]bool, len(b.Circuit)) + for i := range b.Circuit { + isOutput[i] = true + } + for _, w := range b.Circuit { + for _, inIdx := range w.Inputs { + isOutput[inIdx] = false + } + } + for i := range b.Circuit { + if isOutput[i] { + b.nbOutputVars++ + b.outputWires = append(b.outputWires, i) } } + b.evaluatorPool.New = func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(b.Circuit)), + } + for wI := range b.Circuit { + w := &b.Circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce + } + + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) + } +} + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + + b.initialize() + + // Read instance index from calldata + instanceI := int(inst.Calldata[0]) + // Grow assignment slices to accommodate this instance for i := range b.assignment { - for len(b.assignment[i]) <= instanceIdx { + for len(b.assignment[i]) <= instanceI { var zero fr.Element b.assignment[i] = append(b.assignment[i], zero) } } - // Lazy initialize evaluator pool on first use - if b.evaluatorPool.New == nil { - b.initializeEvaluatorPool() - } - // Read input values offset := 1 @@ -102,9 +126,9 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.NbInputs values from calldata - inputValues := make([]fr.Element, b.NbInputs) - for i := range b.NbInputs { + // Read exactly b.nbInputs values from calldata + inputValues := make([]fr.Element, b.nbInputs) + for i := range b.nbInputs { val, delta := s.Read(inst.Calldata[offset:]) offset += delta // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) @@ -118,7 +142,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra if w.IsInput() { // Use pre-read input value - b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) + b.assignment[wI][instanceI].Set(&inputValues[inputIdx]) inputIdx++ } else { // Get evaluator for this wire from the circuit evaluator @@ -126,30 +150,28 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + evaluator.pushInput(&b.assignment[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + b.assignment[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires for the instruction (convert fr.Element to U64) outputIdx := 0 - for _, outWI := range b.OutputWires { + for _, outWI := range b.outputWires { var bigInt big.Int - b.assignment[outWI][instanceIdx].BigInt(&bigInt) + b.assignment[outWI][instanceI].BigInt(&bigInt) s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) outputIdx++ } - b.nbInstances++ return nil } // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.nbInstances = 0 b.assignment = nil } @@ -165,16 +187,16 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.NbOutputVars + return b.nbOutputVars } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse exactly b.NbInputs linear expressions + // Parse exactly b.nbInputs linear expressions offset := 1 // skip instance index - for range b.NbInputs { + for range b.nbInputs { n := int(inst.Calldata[offset]) offset++ @@ -191,7 +213,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.NbOutputVars { + for i := range b.nbOutputVars { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -221,11 +243,9 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances solved +// GetNbInstances returns the number of instances (compile-time) func (b *BlueprintSolve) GetNbInstances() int { - b.lock.Lock() - defer b.lock.Unlock() - return b.nbInstances + return b.NbInstances } // BlueprintProve is a BW6_633-specific blueprint for generating GKR proofs. @@ -258,7 +278,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra insBytes := make([][]byte, 0) // first challenges calldata := inst.Calldata for len(calldata) != 0 { - val, delta := s.Read(inst.Calldata[delta:]) + val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 604f4fc000..c995e6b8ae 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -27,74 +27,98 @@ type circuitEvaluator struct { // BlueprintSolve is a BW6_761-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { - // Circuit structure - Circuit gkrtypes.Circuit - NbInputs int - NbOutputVars int - InputWires []int - OutputWires []int - MaxNbIn int // maximum number of inputs for any gate - - // Stateful data - stored as native fr.Element - // Not serialized - recreated lazily at solve time - nbInstances int - assignment WireAssignment // []polynomial.MultiLin - evaluatorPool sync.Pool // pool of circuitEvaluator, lazy-initialized + // Circuit structure (serialized) + Circuit gkrtypes.Circuit + NbInstances int - lock sync.Mutex -} - -// initializeEvaluatorPool lazily initializes the evaluator pool on first solve -func (b *BlueprintSolve) initializeEvaluatorPool() { - b.evaluatorPool = sync.Pool{ - New: func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), - } - for wI := range b.Circuit { - w := &b.Circuit[wI] - if !w.IsInput() { - // Each gate evaluator allocates its own appropriately-sized stack - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) - } - } - return ce - }, - } + // Not serialized - recreated lazily at solve time + assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + nbOutputVars int `cbor:"-"` + inputWires []int `cbor:"-"` + outputWires []int `cbor:"-"` + maxNbIn int `cbor:"-"` // maximum number of inputs for any gate + + lock sync.Mutex `cbor:"-"` } // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -// Solve implements the BlueprintStateful interface. -func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { +func (b *BlueprintSolve) initialize() { + + if b.assignment != nil { + return + } b.lock.Lock() defer b.lock.Unlock() - // Read instance index from calldata - instanceIdx := int(inst.Calldata[0]) + if b.assignment != nil { + return // the unlikely event that two or more instructions were competing to initialize the blueprint + } + + // Compute metadata from Circuit + b.maxNbIn = b.Circuit.MaxGateNbIn() + for i := range b.Circuit { + if b.Circuit[i].IsInput() { + b.nbInputs++ + b.inputWires = append(b.inputWires, i) + } + } - // Initialize assignment array if this is the first instance - if b.assignment == nil { - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, 0, 16) // pre-allocate + // Identify output wires (not inputs to any other wire) + isOutput := make([]bool, len(b.Circuit)) + for i := range b.Circuit { + isOutput[i] = true + } + for _, w := range b.Circuit { + for _, inIdx := range w.Inputs { + isOutput[inIdx] = false + } + } + for i := range b.Circuit { + if isOutput[i] { + b.nbOutputVars++ + b.outputWires = append(b.outputWires, i) } } + b.evaluatorPool.New = func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(b.Circuit)), + } + for wI := range b.Circuit { + w := &b.Circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + } + } + return ce + } + + b.assignment = make(WireAssignment, len(b.Circuit)) + for i := range b.assignment { + b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) + } +} + +// Solve implements the BlueprintStateful interface. +func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + + b.initialize() + + // Read instance index from calldata + instanceI := int(inst.Calldata[0]) + // Grow assignment slices to accommodate this instance for i := range b.assignment { - for len(b.assignment[i]) <= instanceIdx { + for len(b.assignment[i]) <= instanceI { var zero fr.Element b.assignment[i] = append(b.assignment[i], zero) } } - // Lazy initialize evaluator pool on first use - if b.evaluatorPool.New == nil { - b.initializeEvaluatorPool() - } - // Read input values offset := 1 @@ -102,9 +126,9 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.NbInputs values from calldata - inputValues := make([]fr.Element, b.NbInputs) - for i := range b.NbInputs { + // Read exactly b.nbInputs values from calldata + inputValues := make([]fr.Element, b.nbInputs) + for i := range b.nbInputs { val, delta := s.Read(inst.Calldata[offset:]) offset += delta // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) @@ -118,7 +142,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra if w.IsInput() { // Use pre-read input value - b.assignment[wI][instanceIdx].Set(&inputValues[inputIdx]) + b.assignment[wI][instanceI].Set(&inputValues[inputIdx]) inputIdx++ } else { // Get evaluator for this wire from the circuit evaluator @@ -126,30 +150,28 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceIdx]) + evaluator.pushInput(&b.assignment[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceIdx].Set(evaluator.evaluate()) + b.assignment[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires for the instruction (convert fr.Element to U64) outputIdx := 0 - for _, outWI := range b.OutputWires { + for _, outWI := range b.outputWires { var bigInt big.Int - b.assignment[outWI][instanceIdx].BigInt(&bigInt) + b.assignment[outWI][instanceI].BigInt(&bigInt) s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) outputIdx++ } - b.nbInstances++ return nil } // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.nbInstances = 0 b.assignment = nil } @@ -165,16 +187,16 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.NbOutputVars + return b.nbOutputVars } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse exactly b.NbInputs linear expressions + // Parse exactly b.nbInputs linear expressions offset := 1 // skip instance index - for range b.NbInputs { + for range b.nbInputs { n := int(inst.Calldata[offset]) offset++ @@ -191,7 +213,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.NbOutputVars { + for i := range b.nbOutputVars { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -221,11 +243,9 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances solved +// GetNbInstances returns the number of instances (compile-time) func (b *BlueprintSolve) GetNbInstances() int { - b.lock.Lock() - defer b.lock.Unlock() - return b.nbInstances + return b.NbInstances } // BlueprintProve is a BW6_761-specific blueprint for generating GKR proofs. @@ -258,7 +278,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra insBytes := make([][]byte, 0) // first challenges calldata := inst.Calldata for len(calldata) != 0 { - val, delta := s.Read(inst.Calldata[delta:]) + val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 6cf98caf40..36010e4a60 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -128,34 +128,7 @@ func (c *Circuit) createBlueprint() error { } blueprint.Circuit = circuit - blueprint.MaxNbIn = circuit.MaxGateNbIn() - - // Identify input wires - for i := range circuit { - if circuit[i].IsInput() { - blueprint.NbInputs++ - blueprint.InputWires = append(blueprint.InputWires, i) - } - } - - // Identify output wires (not inputs to any other wire) - isOutput := make([]bool, len(circuit)) - for i := range circuit { - isOutput[i] = true - } - for _, wire := range c.circuit { - for _, inIdx := range wire.Inputs { - isOutput[inIdx] = false - } - } - for i := range circuit { - if isOutput[i] { - blueprint.NbOutputVars++ - blueprint.OutputWires = append(blueprint.OutputWires, i) - } - } - - // Note: evaluatorPool is initialized lazily on first Solve() call + // Note: metadata and evaluatorPool are initialized lazily on first Solve() call // Register solve blueprint with compiler c.solveBlueprintID = c.api.Compiler().AddBlueprint(blueprint) @@ -242,6 +215,10 @@ func (c *Circuit) finalize(api frontend.API) error { } } + // Set NbInstances in the solve blueprint (used for pre-allocation and proof size computation) + solveBlueprint := c.blueprint.(*gkrbn254.BlueprintSolve) + solveBlueprint.NbInstances = nbPaddedInstances + // if the circuit consists of only one instance, directly solve the circuit if len(c.assignments[c.ins[0]]) == 1 { circuit, err := gkrtypes.NewCircuit(c.circuit, gkrgates.Get) From a2dae18579aef25778cdfe54c7726bec762ab437 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Thu, 22 Jan 2026 20:30:18 -0600 Subject: [PATCH 123/251] fix: solve blueprint --- .../backend/template/gkr/blueprint.go.tmpl | 30 ++++--------- internal/gkr/bls12-377/blueprint.go | 44 +++++-------------- internal/gkr/bls12-381/blueprint.go | 44 +++++-------------- internal/gkr/bls24-315/blueprint.go | 44 +++++-------------- internal/gkr/bls24-317/blueprint.go | 44 +++++-------------- internal/gkr/bn254/blueprint.go | 44 +++++-------------- internal/gkr/bw6-633/blueprint.go | 44 +++++-------------- internal/gkr/bw6-761/blueprint.go | 44 +++++-------------- 8 files changed, 93 insertions(+), 245 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 3c659a058c..30936c7c34 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -101,32 +101,22 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.initialize() - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.nbInputs values from calldata - inputValues := make([]{{ .ElementType }}, b.nbInputs) - for i := range b.nbInputs { - - offset += delta - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(inputValues[i][:], val[:]) - } - instanceI := int(inst.Calldata[0]) calldata := inst.Calldata[1:] - inputI := 0 + // Process all wires in topological order (circuit is already sorted) for wI := range b.Circuit { w := &b.Circuit[wI] - if w.IsInput(){ + if w.IsInput() { val, delta := s.Read(calldata) calldata = calldata[delta:] - copy(b.assignment[wI][instanceI][:], val) - inputI++ + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(b.assignment[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] @@ -141,13 +131,11 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Set output wires for the instruction (convert {{ .ElementType }} to U64) - outputI := 0 - for _, outWI := range b.outputWires { - var bigInt big.Int - b.assignment[outWI][instanceI].BigInt(&bigInt) - s.SetValue(uint32(outputI+int(inst.WireOffset)), s.FromInterface(&bigInt)) - outputIdx++ + // Set output wires (copy fr.Element to U64 in Montgomery form) + for outI, outWI := range b.outputWires { + var val constraint.U64 + copy(val[:], b.assignment[outWI][instanceI][:]) + s.SetValue(uint32(outI+int(inst.WireOffset)), val) } return nil diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 23062cdffa..4ef3942f29 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -108,42 +108,22 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.initialize() - // Read instance index from calldata - instanceI := int(inst.Calldata[0]) - - // Grow assignment slices to accommodate this instance - for i := range b.assignment { - for len(b.assignment[i]) <= instanceI { - var zero fr.Element - b.assignment[i] = append(b.assignment[i], zero) - } - } - - // Read input values - offset := 1 - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.nbInputs values from calldata - inputValues := make([]fr.Element, b.nbInputs) - for i := range b.nbInputs { - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(inputValues[i][:], val[:]) - } + instanceI := int(inst.Calldata[0]) + calldata := inst.Calldata[1:] // Process all wires in topological order (circuit is already sorted) - inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] if w.IsInput() { - // Use pre-read input value - b.assignment[wI][instanceI].Set(&inputValues[inputIdx]) - inputIdx++ + val, delta := s.Read(calldata) + calldata = calldata[delta:] + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(b.assignment[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] @@ -158,13 +138,13 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Set output wires for the instruction (convert fr.Element to U64) - outputIdx := 0 + // Set output wires (copy fr.Element to U64 in Montgomery form) + outputI := 0 for _, outWI := range b.outputWires { - var bigInt big.Int - b.assignment[outWI][instanceI].BigInt(&bigInt) - s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) - outputIdx++ + var val constraint.U64 + copy(val[:], b.assignment[outWI][instanceI][:]) + s.SetValue(uint32(outputI+int(inst.WireOffset)), val) + outputI++ } return nil diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index e79e9b11bb..4dd297a3a5 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -108,42 +108,22 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.initialize() - // Read instance index from calldata - instanceI := int(inst.Calldata[0]) - - // Grow assignment slices to accommodate this instance - for i := range b.assignment { - for len(b.assignment[i]) <= instanceI { - var zero fr.Element - b.assignment[i] = append(b.assignment[i], zero) - } - } - - // Read input values - offset := 1 - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.nbInputs values from calldata - inputValues := make([]fr.Element, b.nbInputs) - for i := range b.nbInputs { - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(inputValues[i][:], val[:]) - } + instanceI := int(inst.Calldata[0]) + calldata := inst.Calldata[1:] // Process all wires in topological order (circuit is already sorted) - inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] if w.IsInput() { - // Use pre-read input value - b.assignment[wI][instanceI].Set(&inputValues[inputIdx]) - inputIdx++ + val, delta := s.Read(calldata) + calldata = calldata[delta:] + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(b.assignment[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] @@ -158,13 +138,13 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Set output wires for the instruction (convert fr.Element to U64) - outputIdx := 0 + // Set output wires (copy fr.Element to U64 in Montgomery form) + outputI := 0 for _, outWI := range b.outputWires { - var bigInt big.Int - b.assignment[outWI][instanceI].BigInt(&bigInt) - s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) - outputIdx++ + var val constraint.U64 + copy(val[:], b.assignment[outWI][instanceI][:]) + s.SetValue(uint32(outputI+int(inst.WireOffset)), val) + outputI++ } return nil diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index 40dea4f242..502347739b 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -108,42 +108,22 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.initialize() - // Read instance index from calldata - instanceI := int(inst.Calldata[0]) - - // Grow assignment slices to accommodate this instance - for i := range b.assignment { - for len(b.assignment[i]) <= instanceI { - var zero fr.Element - b.assignment[i] = append(b.assignment[i], zero) - } - } - - // Read input values - offset := 1 - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.nbInputs values from calldata - inputValues := make([]fr.Element, b.nbInputs) - for i := range b.nbInputs { - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(inputValues[i][:], val[:]) - } + instanceI := int(inst.Calldata[0]) + calldata := inst.Calldata[1:] // Process all wires in topological order (circuit is already sorted) - inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] if w.IsInput() { - // Use pre-read input value - b.assignment[wI][instanceI].Set(&inputValues[inputIdx]) - inputIdx++ + val, delta := s.Read(calldata) + calldata = calldata[delta:] + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(b.assignment[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] @@ -158,13 +138,13 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Set output wires for the instruction (convert fr.Element to U64) - outputIdx := 0 + // Set output wires (copy fr.Element to U64 in Montgomery form) + outputI := 0 for _, outWI := range b.outputWires { - var bigInt big.Int - b.assignment[outWI][instanceI].BigInt(&bigInt) - s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) - outputIdx++ + var val constraint.U64 + copy(val[:], b.assignment[outWI][instanceI][:]) + s.SetValue(uint32(outputI+int(inst.WireOffset)), val) + outputI++ } return nil diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 1f31cb10f2..ceea119eed 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -108,42 +108,22 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.initialize() - // Read instance index from calldata - instanceI := int(inst.Calldata[0]) - - // Grow assignment slices to accommodate this instance - for i := range b.assignment { - for len(b.assignment[i]) <= instanceI { - var zero fr.Element - b.assignment[i] = append(b.assignment[i], zero) - } - } - - // Read input values - offset := 1 - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.nbInputs values from calldata - inputValues := make([]fr.Element, b.nbInputs) - for i := range b.nbInputs { - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(inputValues[i][:], val[:]) - } + instanceI := int(inst.Calldata[0]) + calldata := inst.Calldata[1:] // Process all wires in topological order (circuit is already sorted) - inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] if w.IsInput() { - // Use pre-read input value - b.assignment[wI][instanceI].Set(&inputValues[inputIdx]) - inputIdx++ + val, delta := s.Read(calldata) + calldata = calldata[delta:] + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(b.assignment[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] @@ -158,13 +138,13 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Set output wires for the instruction (convert fr.Element to U64) - outputIdx := 0 + // Set output wires (copy fr.Element to U64 in Montgomery form) + outputI := 0 for _, outWI := range b.outputWires { - var bigInt big.Int - b.assignment[outWI][instanceI].BigInt(&bigInt) - s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) - outputIdx++ + var val constraint.U64 + copy(val[:], b.assignment[outWI][instanceI][:]) + s.SetValue(uint32(outputI+int(inst.WireOffset)), val) + outputI++ } return nil diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index c7537fd046..fd00e92713 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -108,42 +108,22 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.initialize() - // Read instance index from calldata - instanceI := int(inst.Calldata[0]) - - // Grow assignment slices to accommodate this instance - for i := range b.assignment { - for len(b.assignment[i]) <= instanceI { - var zero fr.Element - b.assignment[i] = append(b.assignment[i], zero) - } - } - - // Read input values - offset := 1 - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.nbInputs values from calldata - inputValues := make([]fr.Element, b.nbInputs) - for i := range b.nbInputs { - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(inputValues[i][:], val[:]) - } + instanceI := int(inst.Calldata[0]) + calldata := inst.Calldata[1:] // Process all wires in topological order (circuit is already sorted) - inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] if w.IsInput() { - // Use pre-read input value - b.assignment[wI][instanceI].Set(&inputValues[inputIdx]) - inputIdx++ + val, delta := s.Read(calldata) + calldata = calldata[delta:] + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(b.assignment[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] @@ -158,13 +138,13 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Set output wires for the instruction (convert fr.Element to U64) - outputIdx := 0 + // Set output wires (copy fr.Element to U64 in Montgomery form) + outputI := 0 for _, outWI := range b.outputWires { - var bigInt big.Int - b.assignment[outWI][instanceI].BigInt(&bigInt) - s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) - outputIdx++ + var val constraint.U64 + copy(val[:], b.assignment[outWI][instanceI][:]) + s.SetValue(uint32(outputI+int(inst.WireOffset)), val) + outputI++ } return nil diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 5b95d1d369..736ae86df1 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -108,42 +108,22 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.initialize() - // Read instance index from calldata - instanceI := int(inst.Calldata[0]) - - // Grow assignment slices to accommodate this instance - for i := range b.assignment { - for len(b.assignment[i]) <= instanceI { - var zero fr.Element - b.assignment[i] = append(b.assignment[i], zero) - } - } - - // Read input values - offset := 1 - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.nbInputs values from calldata - inputValues := make([]fr.Element, b.nbInputs) - for i := range b.nbInputs { - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(inputValues[i][:], val[:]) - } + instanceI := int(inst.Calldata[0]) + calldata := inst.Calldata[1:] // Process all wires in topological order (circuit is already sorted) - inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] if w.IsInput() { - // Use pre-read input value - b.assignment[wI][instanceI].Set(&inputValues[inputIdx]) - inputIdx++ + val, delta := s.Read(calldata) + calldata = calldata[delta:] + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(b.assignment[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] @@ -158,13 +138,13 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Set output wires for the instruction (convert fr.Element to U64) - outputIdx := 0 + // Set output wires (copy fr.Element to U64 in Montgomery form) + outputI := 0 for _, outWI := range b.outputWires { - var bigInt big.Int - b.assignment[outWI][instanceI].BigInt(&bigInt) - s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) - outputIdx++ + var val constraint.U64 + copy(val[:], b.assignment[outWI][instanceI][:]) + s.SetValue(uint32(outputI+int(inst.WireOffset)), val) + outputI++ } return nil diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index c995e6b8ae..deee8e2d60 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -108,42 +108,22 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra b.initialize() - // Read instance index from calldata - instanceI := int(inst.Calldata[0]) - - // Grow assignment slices to accommodate this instance - for i := range b.assignment { - for len(b.assignment[i]) <= instanceI { - var zero fr.Element - b.assignment[i] = append(b.assignment[i], zero) - } - } - - // Read input values - offset := 1 - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - // Read exactly b.nbInputs values from calldata - inputValues := make([]fr.Element, b.nbInputs) - for i := range b.nbInputs { - val, delta := s.Read(inst.Calldata[offset:]) - offset += delta - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(inputValues[i][:], val[:]) - } + instanceI := int(inst.Calldata[0]) + calldata := inst.Calldata[1:] // Process all wires in topological order (circuit is already sorted) - inputIdx := 0 for wI := range b.Circuit { w := &b.Circuit[wI] if w.IsInput() { - // Use pre-read input value - b.assignment[wI][instanceI].Set(&inputValues[inputIdx]) - inputIdx++ + val, delta := s.Read(calldata) + calldata = calldata[delta:] + // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + copy(b.assignment[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] @@ -158,13 +138,13 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Set output wires for the instruction (convert fr.Element to U64) - outputIdx := 0 + // Set output wires (copy fr.Element to U64 in Montgomery form) + outputI := 0 for _, outWI := range b.outputWires { - var bigInt big.Int - b.assignment[outWI][instanceI].BigInt(&bigInt) - s.SetValue(uint32(outputIdx+int(inst.WireOffset)), s.FromInterface(&bigInt)) - outputIdx++ + var val constraint.U64 + copy(val[:], b.assignment[outWI][instanceI][:]) + s.SetValue(uint32(outputI+int(inst.WireOffset)), val) + outputI++ } return nil From b449e1ca95837708eb38f9527101bd9724f84da1 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Thu, 22 Jan 2026 21:00:42 -0600 Subject: [PATCH 124/251] feat: get value blueprint --- internal/generator/backend/backend | Bin 9074130 -> 9074130 bytes .../backend/template/gkr/blueprint.go.tmpl | 92 ++++++++++++++-- internal/gkr/bls12-377/blueprint.go | 99 +++++++++++++++--- internal/gkr/bls12-381/blueprint.go | 99 +++++++++++++++--- internal/gkr/bls24-315/blueprint.go | 99 +++++++++++++++--- internal/gkr/bls24-317/blueprint.go | 99 +++++++++++++++--- internal/gkr/blueprint.go | 8 ++ internal/gkr/bn254/blueprint.go | 99 +++++++++++++++--- internal/gkr/bw6-633/blueprint.go | 99 +++++++++++++++--- internal/gkr/bw6-761/blueprint.go | 99 +++++++++++++++--- std/gkrapi/compile.go | 67 ++++++------ 11 files changed, 738 insertions(+), 122 deletions(-) create mode 100644 internal/gkr/blueprint.go diff --git a/internal/generator/backend/backend b/internal/generator/backend/backend index 92fb12efaade52d22132f3ebb016aa1c8c255b2e..44775b94146c488a9c3f6cd7a13bfa83643fa1f4 100755 GIT binary patch delta 1027 zcmb`?%~Om40LO9Z1tnG?FHuxPZS6k0_3Y-QZF|}6y=~dH7ixQUw?(o=C9g}U%ycnh zq9G<1;ZPZ~4j2b-!=M_CnP^PLI4~2MaWOQ$-TVO#pUZFNcleHc?eiN!zbsH@8~@#@ zw`)f~NyEFRA3v+V9U1uD-)zr!Ib1^HA$NgVu4t+&w7T`BiPc8YkY=%2a)nxxxiZ;W zze}5)sFsvXrFOe@Uy;QuWm${08drK^nWWU33^@+9qPftSpO>Mwn2oKHtWA>nU^aZ= z2Y&=$4(4JW0uhAy2*v^|#LRyYLJ*3@Sc0Vp!!j(#3amspR$(%4$Odl|reJ+XTDKq0}UaTARcB`iMsKjs16W z3Q3}_jE1U_&(|*x`T^HE>}Uc4H4}u@`mN2RG``fc-dtMl_)r2hoBwLzL0zyK?+w>z@IGLB6y1{@~h%scyk5NF7(*I{D|pp^NVqd@Q;fIQCq2 zBjDzgepY+yw5 xyy>ViM!HWdzU23e-XR_hG+s?*+Vl9vSy#dqn-?Vb4SFx$v{P(!ZyqmeBwJ delta 1027 zcmb`?%~Om40LSqm^rEs#M5Pze3)$Vrv-><7YW1e=P1{m!V%wgTQhIOOLM_s^Mw)SH z9F!T2=)_=lhJ&}5>ELw?2WD_IUNR0EGX`Hbe}Kd1@|*b`zRy2iUho`OR6(k_U(&tg ztA?N7qV~9KUy5R`#RYv`XerIrnqA#l9bK-h&U(AvV%0ml)un~bg5oqyg`=@oD74j^ zs*1HeYEjWTtqwy3wB_ut)tS0%F2!P%Dw~cgDvzR?hxzcs z0xZNL_#*&;2*P3nBLtyXg1`Tz2tzoQAp*;>0+CpWRfs|~Vz3&qh(kOQum*`(i*-ms zGE$I=_1J(kY=jybXdz$|MChQ0;~VH!?NU;{O_cf zG@>l*vl~QFtGA1aLl87ton2S!kcH8WZaLatldcof1&zn-mSI3THe(C6A_JK)VjHrM zjU41658IIs6ADm>A{3(pJ5UNU%3#4x>_RyzPzfulu-j*LS63MSbL1$I(&*SzA;jaJ zuErkhg$*^ZqZSGr*oQjoM?D&F0F7wEK{TTUt!P6#I?#zOIMEFkdeDnQIE*9cLqFU& zieosA0i3`fJU;jIQ2Us%tz>+>yR$#(QbfszS8E>Dyo=1dJ7pT3X)HG+P5S~S=O$Mr z_I;Kg-kj}dNqnDeuUmZMd+NE@gKruVc2%e3Pxu7Id%o3pt-mHR;$3^*{rvcuw-Xm0 z$DV3C`*P9v_u=2E%Vw{p`aQUGsNXU?*ZL%MaOtzEDM691-|~(<8X1Tm8E8J8anTp{ v!dsIcH+?#=JuqnS_I9g(Oyh{!JM`mNblr?KeD=;K_v&D$$#*9Dq|y2ZSH_W% diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 30936c7c34..d7b3813183 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -191,20 +191,20 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (for debugging) -func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { +// GetAssignment returns the assignment for a specific wire and instance (internal use) +func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { b.lock.Lock() defer b.lock.Unlock() var zero constraint.U64 - if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) } - // Convert {{ .ElementType }} to U64 - var bigInt big.Int - b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) - return s.FromInterface(&bigInt), nil + // Copy fr.Element to U64 directly (both in Montgomery form) + var val constraint.U64 + copy(val[:], b.assignment[wireI][instanceI][:]) + return val, nil } // GetAssignments returns all assignments for proving @@ -351,3 +351,79 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } +// BlueprintGetAssignment is a {{.FieldID}}-specific blueprint for retrieving wire assignments. +type BlueprintGetAssignment struct { + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + + lock sync.Mutex +} + +// Ensures BlueprintGetAssignment implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) + +// Solve implements the BlueprintSolvable interface for getting assignments. +func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Read wireI and instanceI from calldata + wireI := int(inst.Calldata[0]) + instanceI := int(inst.Calldata[1]) + + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + + // Get assignment + val, err := solveBlueprint.GetAssignment(wireI, instanceI) + if err != nil { + return err + } + + // Set output wire + s.SetValue(inst.WireOffset, val) + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintGetAssignment) CalldataSize() int { + return -1 // variable size: [wireI, instanceI, dependency_linear_expression] +} + +// NbConstraints implements Blueprint +func (b *BlueprintGetAssignment) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { + return 1 // returns one assignment value +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + // Parse wireI and instanceI (skip them) + offset := 2 + + // Parse dependency linear expression + // This ensures we run after the solve instruction for this instance + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + + outputLevel := maxLevel + 1 + tree.InsertWire(inst.WireOffset, outputLevel) + return outputLevel +} diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 4ef3942f29..9ae84c8770 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -139,12 +139,10 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Set output wires (copy fr.Element to U64 in Montgomery form) - outputI := 0 - for _, outWI := range b.outputWires { + for outI, outWI := range b.outputWires { var val constraint.U64 copy(val[:], b.assignment[outWI][instanceI][:]) - s.SetValue(uint32(outputI+int(inst.WireOffset)), val) - outputI++ + s.SetValue(uint32(outI+int(inst.WireOffset)), val) } return nil @@ -200,20 +198,20 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (for debugging) -func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { +// GetAssignment returns the assignment for a specific wire and instance (internal use) +func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { b.lock.Lock() defer b.lock.Unlock() var zero constraint.U64 - if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) } - // Convert fr.Element to U64 - var bigInt big.Int - b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) - return s.FromInterface(&bigInt), nil + // Copy fr.Element to U64 directly (both in Montgomery form) + var val constraint.U64 + copy(val[:], b.assignment[wireI][instanceI][:]) + return val, nil } // GetAssignments returns all assignments for proving @@ -360,3 +358,80 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } + +// BlueprintGetAssignment is a BLS12_377-specific blueprint for retrieving wire assignments. +type BlueprintGetAssignment struct { + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + + lock sync.Mutex +} + +// Ensures BlueprintGetAssignment implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) + +// Solve implements the BlueprintSolvable interface for getting assignments. +func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Read wireI and instanceI from calldata + wireI := int(inst.Calldata[0]) + instanceI := int(inst.Calldata[1]) + + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + + // Get assignment + val, err := solveBlueprint.GetAssignment(wireI, instanceI) + if err != nil { + return err + } + + // Set output wire + s.SetValue(inst.WireOffset, val) + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintGetAssignment) CalldataSize() int { + return -1 // variable size: [wireI, instanceI, dependency_linear_expression] +} + +// NbConstraints implements Blueprint +func (b *BlueprintGetAssignment) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { + return 1 // returns one assignment value +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + // Parse wireI and instanceI (skip them) + offset := 2 + + // Parse dependency linear expression + // This ensures we run after the solve instruction for this instance + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + + outputLevel := maxLevel + 1 + tree.InsertWire(inst.WireOffset, outputLevel) + return outputLevel +} diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 4dd297a3a5..4f0c059aa3 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -139,12 +139,10 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Set output wires (copy fr.Element to U64 in Montgomery form) - outputI := 0 - for _, outWI := range b.outputWires { + for outI, outWI := range b.outputWires { var val constraint.U64 copy(val[:], b.assignment[outWI][instanceI][:]) - s.SetValue(uint32(outputI+int(inst.WireOffset)), val) - outputI++ + s.SetValue(uint32(outI+int(inst.WireOffset)), val) } return nil @@ -200,20 +198,20 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (for debugging) -func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { +// GetAssignment returns the assignment for a specific wire and instance (internal use) +func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { b.lock.Lock() defer b.lock.Unlock() var zero constraint.U64 - if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) } - // Convert fr.Element to U64 - var bigInt big.Int - b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) - return s.FromInterface(&bigInt), nil + // Copy fr.Element to U64 directly (both in Montgomery form) + var val constraint.U64 + copy(val[:], b.assignment[wireI][instanceI][:]) + return val, nil } // GetAssignments returns all assignments for proving @@ -360,3 +358,80 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } + +// BlueprintGetAssignment is a BLS12_381-specific blueprint for retrieving wire assignments. +type BlueprintGetAssignment struct { + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + + lock sync.Mutex +} + +// Ensures BlueprintGetAssignment implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) + +// Solve implements the BlueprintSolvable interface for getting assignments. +func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Read wireI and instanceI from calldata + wireI := int(inst.Calldata[0]) + instanceI := int(inst.Calldata[1]) + + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + + // Get assignment + val, err := solveBlueprint.GetAssignment(wireI, instanceI) + if err != nil { + return err + } + + // Set output wire + s.SetValue(inst.WireOffset, val) + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintGetAssignment) CalldataSize() int { + return -1 // variable size: [wireI, instanceI, dependency_linear_expression] +} + +// NbConstraints implements Blueprint +func (b *BlueprintGetAssignment) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { + return 1 // returns one assignment value +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + // Parse wireI and instanceI (skip them) + offset := 2 + + // Parse dependency linear expression + // This ensures we run after the solve instruction for this instance + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + + outputLevel := maxLevel + 1 + tree.InsertWire(inst.WireOffset, outputLevel) + return outputLevel +} diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index 502347739b..4484279fe6 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -139,12 +139,10 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Set output wires (copy fr.Element to U64 in Montgomery form) - outputI := 0 - for _, outWI := range b.outputWires { + for outI, outWI := range b.outputWires { var val constraint.U64 copy(val[:], b.assignment[outWI][instanceI][:]) - s.SetValue(uint32(outputI+int(inst.WireOffset)), val) - outputI++ + s.SetValue(uint32(outI+int(inst.WireOffset)), val) } return nil @@ -200,20 +198,20 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (for debugging) -func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { +// GetAssignment returns the assignment for a specific wire and instance (internal use) +func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { b.lock.Lock() defer b.lock.Unlock() var zero constraint.U64 - if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) } - // Convert fr.Element to U64 - var bigInt big.Int - b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) - return s.FromInterface(&bigInt), nil + // Copy fr.Element to U64 directly (both in Montgomery form) + var val constraint.U64 + copy(val[:], b.assignment[wireI][instanceI][:]) + return val, nil } // GetAssignments returns all assignments for proving @@ -360,3 +358,80 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } + +// BlueprintGetAssignment is a BLS24_315-specific blueprint for retrieving wire assignments. +type BlueprintGetAssignment struct { + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + + lock sync.Mutex +} + +// Ensures BlueprintGetAssignment implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) + +// Solve implements the BlueprintSolvable interface for getting assignments. +func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Read wireI and instanceI from calldata + wireI := int(inst.Calldata[0]) + instanceI := int(inst.Calldata[1]) + + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + + // Get assignment + val, err := solveBlueprint.GetAssignment(wireI, instanceI) + if err != nil { + return err + } + + // Set output wire + s.SetValue(inst.WireOffset, val) + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintGetAssignment) CalldataSize() int { + return -1 // variable size: [wireI, instanceI, dependency_linear_expression] +} + +// NbConstraints implements Blueprint +func (b *BlueprintGetAssignment) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { + return 1 // returns one assignment value +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + // Parse wireI and instanceI (skip them) + offset := 2 + + // Parse dependency linear expression + // This ensures we run after the solve instruction for this instance + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + + outputLevel := maxLevel + 1 + tree.InsertWire(inst.WireOffset, outputLevel) + return outputLevel +} diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index ceea119eed..f3c8b9af99 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -139,12 +139,10 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Set output wires (copy fr.Element to U64 in Montgomery form) - outputI := 0 - for _, outWI := range b.outputWires { + for outI, outWI := range b.outputWires { var val constraint.U64 copy(val[:], b.assignment[outWI][instanceI][:]) - s.SetValue(uint32(outputI+int(inst.WireOffset)), val) - outputI++ + s.SetValue(uint32(outI+int(inst.WireOffset)), val) } return nil @@ -200,20 +198,20 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (for debugging) -func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { +// GetAssignment returns the assignment for a specific wire and instance (internal use) +func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { b.lock.Lock() defer b.lock.Unlock() var zero constraint.U64 - if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) } - // Convert fr.Element to U64 - var bigInt big.Int - b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) - return s.FromInterface(&bigInt), nil + // Copy fr.Element to U64 directly (both in Montgomery form) + var val constraint.U64 + copy(val[:], b.assignment[wireI][instanceI][:]) + return val, nil } // GetAssignments returns all assignments for proving @@ -360,3 +358,80 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } + +// BlueprintGetAssignment is a BLS24_317-specific blueprint for retrieving wire assignments. +type BlueprintGetAssignment struct { + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + + lock sync.Mutex +} + +// Ensures BlueprintGetAssignment implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) + +// Solve implements the BlueprintSolvable interface for getting assignments. +func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Read wireI and instanceI from calldata + wireI := int(inst.Calldata[0]) + instanceI := int(inst.Calldata[1]) + + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + + // Get assignment + val, err := solveBlueprint.GetAssignment(wireI, instanceI) + if err != nil { + return err + } + + // Set output wire + s.SetValue(inst.WireOffset, val) + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintGetAssignment) CalldataSize() int { + return -1 // variable size: [wireI, instanceI, dependency_linear_expression] +} + +// NbConstraints implements Blueprint +func (b *BlueprintGetAssignment) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { + return 1 // returns one assignment value +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + // Parse wireI and instanceI (skip them) + offset := 2 + + // Parse dependency linear expression + // This ensures we run after the solve instruction for this instance + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + + outputLevel := maxLevel + 1 + tree.InsertWire(inst.WireOffset, outputLevel) + return outputLevel +} diff --git a/internal/gkr/blueprint.go b/internal/gkr/blueprint.go new file mode 100644 index 0000000000..2d66207e58 --- /dev/null +++ b/internal/gkr/blueprint.go @@ -0,0 +1,8 @@ +package gkr + +import "github.com/consensys/gnark/constraint" + +type BlueprintSolve interface { + constraint.Blueprint + SetNbInstances(nbInstances int) +} diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index fd00e92713..2e2cecdbf1 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -139,12 +139,10 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Set output wires (copy fr.Element to U64 in Montgomery form) - outputI := 0 - for _, outWI := range b.outputWires { + for outI, outWI := range b.outputWires { var val constraint.U64 copy(val[:], b.assignment[outWI][instanceI][:]) - s.SetValue(uint32(outputI+int(inst.WireOffset)), val) - outputI++ + s.SetValue(uint32(outI+int(inst.WireOffset)), val) } return nil @@ -200,20 +198,20 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (for debugging) -func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { +// GetAssignment returns the assignment for a specific wire and instance (internal use) +func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { b.lock.Lock() defer b.lock.Unlock() var zero constraint.U64 - if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) } - // Convert fr.Element to U64 - var bigInt big.Int - b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) - return s.FromInterface(&bigInt), nil + // Copy fr.Element to U64 directly (both in Montgomery form) + var val constraint.U64 + copy(val[:], b.assignment[wireI][instanceI][:]) + return val, nil } // GetAssignments returns all assignments for proving @@ -360,3 +358,80 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } + +// BlueprintGetAssignment is a BN254-specific blueprint for retrieving wire assignments. +type BlueprintGetAssignment struct { + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + + lock sync.Mutex +} + +// Ensures BlueprintGetAssignment implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) + +// Solve implements the BlueprintSolvable interface for getting assignments. +func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Read wireI and instanceI from calldata + wireI := int(inst.Calldata[0]) + instanceI := int(inst.Calldata[1]) + + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + + // Get assignment + val, err := solveBlueprint.GetAssignment(wireI, instanceI) + if err != nil { + return err + } + + // Set output wire + s.SetValue(inst.WireOffset, val) + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintGetAssignment) CalldataSize() int { + return -1 // variable size: [wireI, instanceI, dependency_linear_expression] +} + +// NbConstraints implements Blueprint +func (b *BlueprintGetAssignment) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { + return 1 // returns one assignment value +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + // Parse wireI and instanceI (skip them) + offset := 2 + + // Parse dependency linear expression + // This ensures we run after the solve instruction for this instance + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + + outputLevel := maxLevel + 1 + tree.InsertWire(inst.WireOffset, outputLevel) + return outputLevel +} diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 736ae86df1..9938e9e5ca 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -139,12 +139,10 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Set output wires (copy fr.Element to U64 in Montgomery form) - outputI := 0 - for _, outWI := range b.outputWires { + for outI, outWI := range b.outputWires { var val constraint.U64 copy(val[:], b.assignment[outWI][instanceI][:]) - s.SetValue(uint32(outputI+int(inst.WireOffset)), val) - outputI++ + s.SetValue(uint32(outI+int(inst.WireOffset)), val) } return nil @@ -200,20 +198,20 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (for debugging) -func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { +// GetAssignment returns the assignment for a specific wire and instance (internal use) +func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { b.lock.Lock() defer b.lock.Unlock() var zero constraint.U64 - if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) } - // Convert fr.Element to U64 - var bigInt big.Int - b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) - return s.FromInterface(&bigInt), nil + // Copy fr.Element to U64 directly (both in Montgomery form) + var val constraint.U64 + copy(val[:], b.assignment[wireI][instanceI][:]) + return val, nil } // GetAssignments returns all assignments for proving @@ -360,3 +358,80 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } + +// BlueprintGetAssignment is a BW6_633-specific blueprint for retrieving wire assignments. +type BlueprintGetAssignment struct { + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + + lock sync.Mutex +} + +// Ensures BlueprintGetAssignment implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) + +// Solve implements the BlueprintSolvable interface for getting assignments. +func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Read wireI and instanceI from calldata + wireI := int(inst.Calldata[0]) + instanceI := int(inst.Calldata[1]) + + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + + // Get assignment + val, err := solveBlueprint.GetAssignment(wireI, instanceI) + if err != nil { + return err + } + + // Set output wire + s.SetValue(inst.WireOffset, val) + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintGetAssignment) CalldataSize() int { + return -1 // variable size: [wireI, instanceI, dependency_linear_expression] +} + +// NbConstraints implements Blueprint +func (b *BlueprintGetAssignment) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { + return 1 // returns one assignment value +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + // Parse wireI and instanceI (skip them) + offset := 2 + + // Parse dependency linear expression + // This ensures we run after the solve instruction for this instance + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + + outputLevel := maxLevel + 1 + tree.InsertWire(inst.WireOffset, outputLevel) + return outputLevel +} diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index deee8e2d60..db17b3f0bc 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -139,12 +139,10 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Set output wires (copy fr.Element to U64 in Montgomery form) - outputI := 0 - for _, outWI := range b.outputWires { + for outI, outWI := range b.outputWires { var val constraint.U64 copy(val[:], b.assignment[outWI][instanceI][:]) - s.SetValue(uint32(outputI+int(inst.WireOffset)), val) - outputI++ + s.SetValue(uint32(outI+int(inst.WireOffset)), val) } return nil @@ -200,20 +198,20 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (for debugging) -func (b *BlueprintSolve) GetAssignment(s constraint.Solver[constraint.U64], wireIdx, instanceIdx int) (constraint.U64, error) { +// GetAssignment returns the assignment for a specific wire and instance (internal use) +func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { b.lock.Lock() defer b.lock.Unlock() var zero constraint.U64 - if wireIdx >= len(b.assignment) || instanceIdx >= len(b.assignment[wireIdx]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireIdx, instanceIdx) + if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { + return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) } - // Convert fr.Element to U64 - var bigInt big.Int - b.assignment[wireIdx][instanceIdx].BigInt(&bigInt) - return s.FromInterface(&bigInt), nil + // Copy fr.Element to U64 directly (both in Montgomery form) + var val constraint.U64 + copy(val[:], b.assignment[wireI][instanceI][:]) + return val, nil } // GetAssignments returns all assignments for proving @@ -360,3 +358,80 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } + +// BlueprintGetAssignment is a BW6_761-specific blueprint for retrieving wire assignments. +type BlueprintGetAssignment struct { + SolveBlueprintID constraint.BlueprintID + SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + + lock sync.Mutex +} + +// Ensures BlueprintGetAssignment implements BlueprintSolvable +var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) + +// Solve implements the BlueprintSolvable interface for getting assignments. +func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { + b.lock.Lock() + defer b.lock.Unlock() + + // Read wireI and instanceI from calldata + wireI := int(inst.Calldata[0]) + instanceI := int(inst.Calldata[1]) + + // Get solve blueprint from solver by ID + solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) + + // Get assignment + val, err := solveBlueprint.GetAssignment(wireI, instanceI) + if err != nil { + return err + } + + // Set output wire + s.SetValue(inst.WireOffset, val) + return nil +} + +// CalldataSize implements Blueprint +func (b *BlueprintGetAssignment) CalldataSize() int { + return -1 // variable size: [wireI, instanceI, dependency_linear_expression] +} + +// NbConstraints implements Blueprint +func (b *BlueprintGetAssignment) NbConstraints() int { + return 0 +} + +// NbOutputs implements Blueprint +func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { + return 1 // returns one assignment value +} + +// UpdateInstructionTree implements Blueprint +func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + maxLevel := constraint.LevelUnset + + // Parse wireI and instanceI (skip them) + offset := 2 + + // Parse dependency linear expression + // This ensures we run after the solve instruction for this instance + n := int(inst.Calldata[offset]) + offset++ + + for range n { + wireID := inst.Calldata[offset+1] + offset += 2 + if !tree.HasWire(wireID) { + continue + } + if level := tree.GetWireLevel(wireID); level > maxLevel { + maxLevel = level + } + } + + outputLevel := maxLevel + 1 + tree.InsertWire(inst.WireOffset, outputLevel) + return outputLevel +} diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 36010e4a60..fb8e5ee6c1 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -38,9 +38,10 @@ type Circuit struct { api frontend.API // the parent API // Blueprint-based fields - solveBlueprintID constraint.BlueprintID - proveBlueprintID constraint.BlueprintID - blueprint interface{} // actual type is *gkrbn254.BlueprintSolve + solveBlueprintID constraint.BlueprintID + proveBlueprintID constraint.BlueprintID + getAssignmentBlueprintID constraint.BlueprintID + solveBlueprint gadget.BlueprintSolve // Metadata hashName string @@ -83,9 +84,9 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C hashName: fiatshamirHashName, } - // Create and populate blueprint + // Create and populate solveBlueprint if err := res.createBlueprint(); err != nil { - return nil, fmt.Errorf("failed to create GKR blueprint: %w", err) + return nil, fmt.Errorf("failed to create GKR solveBlueprint: %w", err) } for _, opt := range options { @@ -117,7 +118,7 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C return &res, nil } -// createBlueprint creates and initializes the GKR blueprint for this circuit +// createBlueprint creates and initializes the GKR solveBlueprint for this circuit func (c *Circuit) createBlueprint() error { blueprint := &gkrbn254.BlueprintSolve{} @@ -130,11 +131,10 @@ func (c *Circuit) createBlueprint() error { blueprint.Circuit = circuit // Note: metadata and evaluatorPool are initialized lazily on first Solve() call - // Register solve blueprint with compiler + // Register solve solveBlueprint with compiler c.solveBlueprintID = c.api.Compiler().AddBlueprint(blueprint) - c.blueprint = blueprint - // Create and register prove blueprint with reference to solve blueprint + // Create and register prove solveBlueprint with reference to solve solveBlueprint proveBlueprint := &gkrbn254.BlueprintProve{ SolveBlueprintID: c.solveBlueprintID, SolveBlueprint: blueprint, @@ -142,6 +142,13 @@ func (c *Circuit) createBlueprint() error { } c.proveBlueprintID = c.api.Compiler().AddBlueprint(proveBlueprint) + // Create and register GetAssignment solveBlueprint for debugging + getAssignmentBlueprint := &gkrbn254.BlueprintGetAssignment{ + SolveBlueprintID: c.solveBlueprintID, + SolveBlueprint: blueprint, + } + c.getAssignmentBlueprintID = c.api.Compiler().AddBlueprint(getAssignmentBlueprint) + return nil } @@ -158,7 +165,7 @@ func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr } } - // Build instruction calldata for blueprint + // Build instruction calldata for solveBlueprint // Format: [0]=instanceIndex, [1...]=input values as linear expressions compiler := c.api.Compiler() calldata := make([]uint32, 1, 1+len(c.ins)*2+2) // pre-allocate roughly @@ -178,7 +185,7 @@ func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr v.Compress(&calldata) } - // Execute solve blueprint instruction + // Execute solve solveBlueprint instruction outputs := compiler.AddInstruction(c.solveBlueprintID, calldata) // Track instance count @@ -215,8 +222,8 @@ func (c *Circuit) finalize(api frontend.API) error { } } - // Set NbInstances in the solve blueprint (used for pre-allocation and proof size computation) - solveBlueprint := c.blueprint.(*gkrbn254.BlueprintSolve) + // Set NbInstances in the solve solveBlueprint (used for pre-allocation and proof size computation) + solveBlueprint := c.solveBlueprint.(*gkrbn254.BlueprintSolve) solveBlueprint.NbInstances = nbPaddedInstances // if the circuit consists of only one instance, directly solve the circuit @@ -281,7 +288,7 @@ func (c *Circuit) verify(api frontend.API, initialChallenges []frontend.Variable v.Compress(&proveCalldata) } - // Execute prove blueprint instruction + // Execute prove solveBlueprint instruction proofOutputs := compiler.AddInstruction(c.proveBlueprintID, proveCalldata) // Convert outputs to proof @@ -327,22 +334,22 @@ func newCircuitDataForSnark(curve ecc.ID, untypedCircuit gkrinfo.Circuit, assign // GetValue is a debugging utility returning the value of variable v at instance i. // While v can be an input or output variable, GetValue is most useful for querying intermediate values in the circuit. func (c *Circuit) GetValue(v gkr.Variable, i int) frontend.Variable { - // Access blueprint directly to get assignment - // The blueprint stores all wire assignments after solving - if c.blueprint == nil { - panic("blueprint not initialized") - } - - // Get blueprint (GKR only works with U64/large fields) - bp := c.blueprint.(*gkrbn254.BlueprintSolve) + // Create an instruction that will retrieve the assignment at solve time compiler := c.api.Compiler() - solver, ok := compiler.(constraint.Solver[constraint.U64]) - if !ok { - panic("compiler does not implement Solver[U64] interface") - } - val, err := bp.GetAssignment(solver, int(v), i) - if err != nil { - panic(err) + + // Build calldata: [wireI, instanceI, dependency_wire_as_linear_expression] + // The dependency ensures this instruction runs after the solve instruction for instance i + calldata := []uint32{uint32(v), uint32(i)} + + // Use the first output variable from instance i as a dependency + // This ensures the solve instruction for this instance has completed + if len(c.outs) == 0 || i >= len(c.assignments[c.outs[0]]) { + panic("GetValue called with invalid instance or before instance was added") } - return val + dependencyWire := c.assignments[c.outs[0]][i] + depVar := compiler.ToCanonicalVariable(dependencyWire) + depVar.Compress(&calldata) + + outputs := compiler.AddInstruction(c.getAssignmentBlueprintID, calldata) + return compiler.InternalVariable(outputs[0]) } From 9aaa5330b4896829a71cf44c3cabc98fdf0d7acb Mon Sep 17 00:00:00 2001 From: Tabaie Date: Thu, 22 Jan 2026 21:07:33 -0600 Subject: [PATCH 125/251] fix: interface issues --- .../generator/backend/template/gkr/blueprint.go.tmpl | 5 +++++ internal/gkr/bls12-377/blueprint.go | 5 +++++ internal/gkr/bls12-381/blueprint.go | 5 +++++ internal/gkr/bls24-315/blueprint.go | 5 +++++ internal/gkr/bls24-317/blueprint.go | 5 +++++ internal/gkr/bn254/blueprint.go | 5 +++++ internal/gkr/bw6-633/blueprint.go | 5 +++++ internal/gkr/bw6-761/blueprint.go | 5 +++++ std/gkrapi/compile.go | 12 ++++++------ 9 files changed, 46 insertions(+), 6 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index d7b3813183..22ce3a4e16 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -146,6 +146,11 @@ func (b *BlueprintSolve) Reset() { b.assignment = nil } +// SetNbInstances sets the number of instances for the blueprint +func (b *BlueprintSolve) SetNbInstances(nbInstances int) { + b.NbInstances = nbInstances +} + // CalldataSize implements Blueprint func (b *BlueprintSolve) CalldataSize() int { return -1 // variable size diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 9ae84c8770..d2c43bc64b 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -153,6 +153,11 @@ func (b *BlueprintSolve) Reset() { b.assignment = nil } +// SetNbInstances sets the number of instances for the blueprint +func (b *BlueprintSolve) SetNbInstances(nbInstances int) { + b.NbInstances = nbInstances +} + // CalldataSize implements Blueprint func (b *BlueprintSolve) CalldataSize() int { return -1 // variable size diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 4f0c059aa3..dd35850d09 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -153,6 +153,11 @@ func (b *BlueprintSolve) Reset() { b.assignment = nil } +// SetNbInstances sets the number of instances for the blueprint +func (b *BlueprintSolve) SetNbInstances(nbInstances int) { + b.NbInstances = nbInstances +} + // CalldataSize implements Blueprint func (b *BlueprintSolve) CalldataSize() int { return -1 // variable size diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index 4484279fe6..febcb23e97 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -153,6 +153,11 @@ func (b *BlueprintSolve) Reset() { b.assignment = nil } +// SetNbInstances sets the number of instances for the blueprint +func (b *BlueprintSolve) SetNbInstances(nbInstances int) { + b.NbInstances = nbInstances +} + // CalldataSize implements Blueprint func (b *BlueprintSolve) CalldataSize() int { return -1 // variable size diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index f3c8b9af99..af670318a4 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -153,6 +153,11 @@ func (b *BlueprintSolve) Reset() { b.assignment = nil } +// SetNbInstances sets the number of instances for the blueprint +func (b *BlueprintSolve) SetNbInstances(nbInstances int) { + b.NbInstances = nbInstances +} + // CalldataSize implements Blueprint func (b *BlueprintSolve) CalldataSize() int { return -1 // variable size diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 2e2cecdbf1..67f32470bf 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -153,6 +153,11 @@ func (b *BlueprintSolve) Reset() { b.assignment = nil } +// SetNbInstances sets the number of instances for the blueprint +func (b *BlueprintSolve) SetNbInstances(nbInstances int) { + b.NbInstances = nbInstances +} + // CalldataSize implements Blueprint func (b *BlueprintSolve) CalldataSize() int { return -1 // variable size diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 9938e9e5ca..f7598711ae 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -153,6 +153,11 @@ func (b *BlueprintSolve) Reset() { b.assignment = nil } +// SetNbInstances sets the number of instances for the blueprint +func (b *BlueprintSolve) SetNbInstances(nbInstances int) { + b.NbInstances = nbInstances +} + // CalldataSize implements Blueprint func (b *BlueprintSolve) CalldataSize() int { return -1 // variable size diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index db17b3f0bc..5169720037 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -153,6 +153,11 @@ func (b *BlueprintSolve) Reset() { b.assignment = nil } +// SetNbInstances sets the number of instances for the blueprint +func (b *BlueprintSolve) SetNbInstances(nbInstances int) { + b.NbInstances = nbInstances +} + // CalldataSize implements Blueprint func (b *BlueprintSolve) CalldataSize() int { return -1 // variable size diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index fb8e5ee6c1..8acdc41c48 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -120,7 +120,7 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C // createBlueprint creates and initializes the GKR solveBlueprint for this circuit func (c *Circuit) createBlueprint() error { - blueprint := &gkrbn254.BlueprintSolve{} + var blueprint gkrbn254.BlueprintSolve // Convert circuit to typed circuit circuit, err := gkrtypes.NewCircuit(c.circuit, gkrgates.Get) @@ -132,12 +132,13 @@ func (c *Circuit) createBlueprint() error { // Note: metadata and evaluatorPool are initialized lazily on first Solve() call // Register solve solveBlueprint with compiler - c.solveBlueprintID = c.api.Compiler().AddBlueprint(blueprint) + c.solveBlueprintID = c.api.Compiler().AddBlueprint(&blueprint) + c.solveBlueprint = &blueprint // Create and register prove solveBlueprint with reference to solve solveBlueprint proveBlueprint := &gkrbn254.BlueprintProve{ SolveBlueprintID: c.solveBlueprintID, - SolveBlueprint: blueprint, + SolveBlueprint: &blueprint, HashName: c.hashName, } c.proveBlueprintID = c.api.Compiler().AddBlueprint(proveBlueprint) @@ -145,7 +146,7 @@ func (c *Circuit) createBlueprint() error { // Create and register GetAssignment solveBlueprint for debugging getAssignmentBlueprint := &gkrbn254.BlueprintGetAssignment{ SolveBlueprintID: c.solveBlueprintID, - SolveBlueprint: blueprint, + SolveBlueprint: &blueprint, } c.getAssignmentBlueprintID = c.api.Compiler().AddBlueprint(getAssignmentBlueprint) @@ -223,8 +224,7 @@ func (c *Circuit) finalize(api frontend.API) error { } // Set NbInstances in the solve solveBlueprint (used for pre-allocation and proof size computation) - solveBlueprint := c.solveBlueprint.(*gkrbn254.BlueprintSolve) - solveBlueprint.NbInstances = nbPaddedInstances + c.solveBlueprint.SetNbInstances(nbPaddedInstances) // if the circuit consists of only one instance, directly solve the circuit if len(c.assignments[c.ins[0]]) == 1 { From 52007aa3ad33f7d42b68fc464320b7def5d50fb7 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Thu, 22 Jan 2026 21:32:37 -0600 Subject: [PATCH 126/251] feat: blueprints for all curves --- .../backend/template/gkr/blueprint.go.tmpl | 58 ++++++++++++----- internal/gkr/bls12-377/blueprint.go | 58 ++++++++++++----- internal/gkr/bls12-381/blueprint.go | 58 ++++++++++++----- internal/gkr/bls24-315/blueprint.go | 58 ++++++++++++----- internal/gkr/bls24-317/blueprint.go | 58 ++++++++++++----- internal/gkr/blueprint.go | 10 ++- internal/gkr/bn254/blueprint.go | 58 ++++++++++++----- internal/gkr/bw6-633/blueprint.go | 58 ++++++++++++----- internal/gkr/bw6-761/blueprint.go | 58 ++++++++++++----- std/gkrapi/compile.go | 65 ++++++++++--------- 10 files changed, 370 insertions(+), 169 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 22ce3a4e16..e838dae98f 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -22,7 +22,7 @@ type circuitEvaluator struct { type BlueprintSolve struct { // Circuit structure (serialized) Circuit gkrtypes.Circuit - NbInstances int + NbInstances uint32 // Not serialized - recreated lazily at solve time assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin @@ -147,7 +147,7 @@ func (b *BlueprintSolve) Reset() { } // SetNbInstances sets the number of instances for the blueprint -func (b *BlueprintSolve) SetNbInstances(nbInstances int) { +func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances } @@ -219,11 +219,6 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances (compile-time) -func (b *BlueprintSolve) GetNbInstances() int { - return b.NbInstances -} - // BlueprintProve is a {{.FieldID}}-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -310,17 +305,19 @@ func (b *BlueprintProve) NbConstraints() int { return 0 } -// NbOutputs implements Blueprint -func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances == 0 { +func (b *BlueprintProve) proofSize() int { + if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros(uint(nbInstances)) + logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } +// NbOutputs implements Blueprint +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { + return b.proofSize() +} + // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset @@ -345,10 +342,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree outputLevel := maxLevel + 1 // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances > 0 { - logNbInstances := bits.TrailingZeros(uint(nbInstances)) - proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + if b.SolveBlueprint.NbInstances > 0 { + proofSize := b.proofSize() for i := range proofSize { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -432,3 +427,32 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi tree.InsertWire(inst.WireOffset, outputLevel) return outputLevel } + +// NewBlueprints creates and registers all GKR blueprints for {{.FieldID}} +func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { + // Create and register solve blueprint + solve := &BlueprintSolve{Circuit: circuit} + solveID := compiler.AddBlueprint(solve) + + // Create and register prove blueprint + prove := &BlueprintProve{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + HashName: hashName, + } + proveID := compiler.AddBlueprint(prove) + + // Create and register GetAssignment blueprint + getAssignment := &BlueprintGetAssignment{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + } + getAssignmentID := compiler.AddBlueprint(getAssignment) + + return gadget.Blueprints{ + SolveID: solveID, + Solve: solve, + ProveID: proveID, + GetAssignmentID: getAssignmentID, + } +} diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index d2c43bc64b..caedcb6cb5 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -29,7 +29,7 @@ type circuitEvaluator struct { type BlueprintSolve struct { // Circuit structure (serialized) Circuit gkrtypes.Circuit - NbInstances int + NbInstances uint32 // Not serialized - recreated lazily at solve time assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin @@ -154,7 +154,7 @@ func (b *BlueprintSolve) Reset() { } // SetNbInstances sets the number of instances for the blueprint -func (b *BlueprintSolve) SetNbInstances(nbInstances int) { +func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances } @@ -226,11 +226,6 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances (compile-time) -func (b *BlueprintSolve) GetNbInstances() int { - return b.NbInstances -} - // BlueprintProve is a BLS12_377-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -317,17 +312,19 @@ func (b *BlueprintProve) NbConstraints() int { return 0 } -// NbOutputs implements Blueprint -func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances == 0 { +func (b *BlueprintProve) proofSize() int { + if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros(uint(nbInstances)) + logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } +// NbOutputs implements Blueprint +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { + return b.proofSize() +} + // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset @@ -352,10 +349,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree outputLevel := maxLevel + 1 // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances > 0 { - logNbInstances := bits.TrailingZeros(uint(nbInstances)) - proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + if b.SolveBlueprint.NbInstances > 0 { + proofSize := b.proofSize() for i := range proofSize { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -440,3 +435,32 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi tree.InsertWire(inst.WireOffset, outputLevel) return outputLevel } + +// NewBlueprints creates and registers all GKR blueprints for BLS12_377 +func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { + // Create and register solve blueprint + solve := &BlueprintSolve{Circuit: circuit} + solveID := compiler.AddBlueprint(solve) + + // Create and register prove blueprint + prove := &BlueprintProve{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + HashName: hashName, + } + proveID := compiler.AddBlueprint(prove) + + // Create and register GetAssignment blueprint + getAssignment := &BlueprintGetAssignment{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + } + getAssignmentID := compiler.AddBlueprint(getAssignment) + + return gadget.Blueprints{ + SolveID: solveID, + Solve: solve, + ProveID: proveID, + GetAssignmentID: getAssignmentID, + } +} diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index dd35850d09..c93c344d03 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -29,7 +29,7 @@ type circuitEvaluator struct { type BlueprintSolve struct { // Circuit structure (serialized) Circuit gkrtypes.Circuit - NbInstances int + NbInstances uint32 // Not serialized - recreated lazily at solve time assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin @@ -154,7 +154,7 @@ func (b *BlueprintSolve) Reset() { } // SetNbInstances sets the number of instances for the blueprint -func (b *BlueprintSolve) SetNbInstances(nbInstances int) { +func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances } @@ -226,11 +226,6 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances (compile-time) -func (b *BlueprintSolve) GetNbInstances() int { - return b.NbInstances -} - // BlueprintProve is a BLS12_381-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -317,17 +312,19 @@ func (b *BlueprintProve) NbConstraints() int { return 0 } -// NbOutputs implements Blueprint -func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances == 0 { +func (b *BlueprintProve) proofSize() int { + if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros(uint(nbInstances)) + logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } +// NbOutputs implements Blueprint +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { + return b.proofSize() +} + // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset @@ -352,10 +349,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree outputLevel := maxLevel + 1 // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances > 0 { - logNbInstances := bits.TrailingZeros(uint(nbInstances)) - proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + if b.SolveBlueprint.NbInstances > 0 { + proofSize := b.proofSize() for i := range proofSize { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -440,3 +435,32 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi tree.InsertWire(inst.WireOffset, outputLevel) return outputLevel } + +// NewBlueprints creates and registers all GKR blueprints for BLS12_381 +func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { + // Create and register solve blueprint + solve := &BlueprintSolve{Circuit: circuit} + solveID := compiler.AddBlueprint(solve) + + // Create and register prove blueprint + prove := &BlueprintProve{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + HashName: hashName, + } + proveID := compiler.AddBlueprint(prove) + + // Create and register GetAssignment blueprint + getAssignment := &BlueprintGetAssignment{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + } + getAssignmentID := compiler.AddBlueprint(getAssignment) + + return gadget.Blueprints{ + SolveID: solveID, + Solve: solve, + ProveID: proveID, + GetAssignmentID: getAssignmentID, + } +} diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index febcb23e97..dbcfe8f22d 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -29,7 +29,7 @@ type circuitEvaluator struct { type BlueprintSolve struct { // Circuit structure (serialized) Circuit gkrtypes.Circuit - NbInstances int + NbInstances uint32 // Not serialized - recreated lazily at solve time assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin @@ -154,7 +154,7 @@ func (b *BlueprintSolve) Reset() { } // SetNbInstances sets the number of instances for the blueprint -func (b *BlueprintSolve) SetNbInstances(nbInstances int) { +func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances } @@ -226,11 +226,6 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances (compile-time) -func (b *BlueprintSolve) GetNbInstances() int { - return b.NbInstances -} - // BlueprintProve is a BLS24_315-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -317,17 +312,19 @@ func (b *BlueprintProve) NbConstraints() int { return 0 } -// NbOutputs implements Blueprint -func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances == 0 { +func (b *BlueprintProve) proofSize() int { + if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros(uint(nbInstances)) + logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } +// NbOutputs implements Blueprint +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { + return b.proofSize() +} + // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset @@ -352,10 +349,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree outputLevel := maxLevel + 1 // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances > 0 { - logNbInstances := bits.TrailingZeros(uint(nbInstances)) - proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + if b.SolveBlueprint.NbInstances > 0 { + proofSize := b.proofSize() for i := range proofSize { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -440,3 +435,32 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi tree.InsertWire(inst.WireOffset, outputLevel) return outputLevel } + +// NewBlueprints creates and registers all GKR blueprints for BLS24_315 +func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { + // Create and register solve blueprint + solve := &BlueprintSolve{Circuit: circuit} + solveID := compiler.AddBlueprint(solve) + + // Create and register prove blueprint + prove := &BlueprintProve{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + HashName: hashName, + } + proveID := compiler.AddBlueprint(prove) + + // Create and register GetAssignment blueprint + getAssignment := &BlueprintGetAssignment{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + } + getAssignmentID := compiler.AddBlueprint(getAssignment) + + return gadget.Blueprints{ + SolveID: solveID, + Solve: solve, + ProveID: proveID, + GetAssignmentID: getAssignmentID, + } +} diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index af670318a4..692aa1f4f6 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -29,7 +29,7 @@ type circuitEvaluator struct { type BlueprintSolve struct { // Circuit structure (serialized) Circuit gkrtypes.Circuit - NbInstances int + NbInstances uint32 // Not serialized - recreated lazily at solve time assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin @@ -154,7 +154,7 @@ func (b *BlueprintSolve) Reset() { } // SetNbInstances sets the number of instances for the blueprint -func (b *BlueprintSolve) SetNbInstances(nbInstances int) { +func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances } @@ -226,11 +226,6 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances (compile-time) -func (b *BlueprintSolve) GetNbInstances() int { - return b.NbInstances -} - // BlueprintProve is a BLS24_317-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -317,17 +312,19 @@ func (b *BlueprintProve) NbConstraints() int { return 0 } -// NbOutputs implements Blueprint -func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances == 0 { +func (b *BlueprintProve) proofSize() int { + if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros(uint(nbInstances)) + logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } +// NbOutputs implements Blueprint +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { + return b.proofSize() +} + // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset @@ -352,10 +349,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree outputLevel := maxLevel + 1 // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances > 0 { - logNbInstances := bits.TrailingZeros(uint(nbInstances)) - proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + if b.SolveBlueprint.NbInstances > 0 { + proofSize := b.proofSize() for i := range proofSize { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -440,3 +435,32 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi tree.InsertWire(inst.WireOffset, outputLevel) return outputLevel } + +// NewBlueprints creates and registers all GKR blueprints for BLS24_317 +func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { + // Create and register solve blueprint + solve := &BlueprintSolve{Circuit: circuit} + solveID := compiler.AddBlueprint(solve) + + // Create and register prove blueprint + prove := &BlueprintProve{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + HashName: hashName, + } + proveID := compiler.AddBlueprint(prove) + + // Create and register GetAssignment blueprint + getAssignment := &BlueprintGetAssignment{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + } + getAssignmentID := compiler.AddBlueprint(getAssignment) + + return gadget.Blueprints{ + SolveID: solveID, + Solve: solve, + ProveID: proveID, + GetAssignmentID: getAssignmentID, + } +} diff --git a/internal/gkr/blueprint.go b/internal/gkr/blueprint.go index 2d66207e58..761e6deed2 100644 --- a/internal/gkr/blueprint.go +++ b/internal/gkr/blueprint.go @@ -4,5 +4,13 @@ import "github.com/consensys/gnark/constraint" type BlueprintSolve interface { constraint.Blueprint - SetNbInstances(nbInstances int) + SetNbInstances(nbInstances uint32) +} + +// Blueprints holds all GKR-related blueprint IDs and references +type Blueprints struct { + SolveID constraint.BlueprintID + Solve BlueprintSolve + ProveID constraint.BlueprintID + GetAssignmentID constraint.BlueprintID } diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 67f32470bf..71c8bc41c4 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -29,7 +29,7 @@ type circuitEvaluator struct { type BlueprintSolve struct { // Circuit structure (serialized) Circuit gkrtypes.Circuit - NbInstances int + NbInstances uint32 // Not serialized - recreated lazily at solve time assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin @@ -154,7 +154,7 @@ func (b *BlueprintSolve) Reset() { } // SetNbInstances sets the number of instances for the blueprint -func (b *BlueprintSolve) SetNbInstances(nbInstances int) { +func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances } @@ -226,11 +226,6 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances (compile-time) -func (b *BlueprintSolve) GetNbInstances() int { - return b.NbInstances -} - // BlueprintProve is a BN254-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -317,17 +312,19 @@ func (b *BlueprintProve) NbConstraints() int { return 0 } -// NbOutputs implements Blueprint -func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances == 0 { +func (b *BlueprintProve) proofSize() int { + if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros(uint(nbInstances)) + logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } +// NbOutputs implements Blueprint +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { + return b.proofSize() +} + // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset @@ -352,10 +349,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree outputLevel := maxLevel + 1 // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances > 0 { - logNbInstances := bits.TrailingZeros(uint(nbInstances)) - proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + if b.SolveBlueprint.NbInstances > 0 { + proofSize := b.proofSize() for i := range proofSize { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -440,3 +435,32 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi tree.InsertWire(inst.WireOffset, outputLevel) return outputLevel } + +// NewBlueprints creates and registers all GKR blueprints for BN254 +func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { + // Create and register solve blueprint + solve := &BlueprintSolve{Circuit: circuit} + solveID := compiler.AddBlueprint(solve) + + // Create and register prove blueprint + prove := &BlueprintProve{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + HashName: hashName, + } + proveID := compiler.AddBlueprint(prove) + + // Create and register GetAssignment blueprint + getAssignment := &BlueprintGetAssignment{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + } + getAssignmentID := compiler.AddBlueprint(getAssignment) + + return gadget.Blueprints{ + SolveID: solveID, + Solve: solve, + ProveID: proveID, + GetAssignmentID: getAssignmentID, + } +} diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index f7598711ae..df3002ab00 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -29,7 +29,7 @@ type circuitEvaluator struct { type BlueprintSolve struct { // Circuit structure (serialized) Circuit gkrtypes.Circuit - NbInstances int + NbInstances uint32 // Not serialized - recreated lazily at solve time assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin @@ -154,7 +154,7 @@ func (b *BlueprintSolve) Reset() { } // SetNbInstances sets the number of instances for the blueprint -func (b *BlueprintSolve) SetNbInstances(nbInstances int) { +func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances } @@ -226,11 +226,6 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances (compile-time) -func (b *BlueprintSolve) GetNbInstances() int { - return b.NbInstances -} - // BlueprintProve is a BW6_633-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -317,17 +312,19 @@ func (b *BlueprintProve) NbConstraints() int { return 0 } -// NbOutputs implements Blueprint -func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances == 0 { +func (b *BlueprintProve) proofSize() int { + if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros(uint(nbInstances)) + logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } +// NbOutputs implements Blueprint +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { + return b.proofSize() +} + // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset @@ -352,10 +349,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree outputLevel := maxLevel + 1 // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances > 0 { - logNbInstances := bits.TrailingZeros(uint(nbInstances)) - proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + if b.SolveBlueprint.NbInstances > 0 { + proofSize := b.proofSize() for i := range proofSize { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -440,3 +435,32 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi tree.InsertWire(inst.WireOffset, outputLevel) return outputLevel } + +// NewBlueprints creates and registers all GKR blueprints for BW6_633 +func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { + // Create and register solve blueprint + solve := &BlueprintSolve{Circuit: circuit} + solveID := compiler.AddBlueprint(solve) + + // Create and register prove blueprint + prove := &BlueprintProve{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + HashName: hashName, + } + proveID := compiler.AddBlueprint(prove) + + // Create and register GetAssignment blueprint + getAssignment := &BlueprintGetAssignment{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + } + getAssignmentID := compiler.AddBlueprint(getAssignment) + + return gadget.Blueprints{ + SolveID: solveID, + Solve: solve, + ProveID: proveID, + GetAssignmentID: getAssignmentID, + } +} diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 5169720037..c125604b1c 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -29,7 +29,7 @@ type circuitEvaluator struct { type BlueprintSolve struct { // Circuit structure (serialized) Circuit gkrtypes.Circuit - NbInstances int + NbInstances uint32 // Not serialized - recreated lazily at solve time assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin @@ -154,7 +154,7 @@ func (b *BlueprintSolve) Reset() { } // SetNbInstances sets the number of instances for the blueprint -func (b *BlueprintSolve) SetNbInstances(nbInstances int) { +func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances } @@ -226,11 +226,6 @@ func (b *BlueprintSolve) GetAssignments() WireAssignment { return b.assignment } -// GetNbInstances returns the number of instances (compile-time) -func (b *BlueprintSolve) GetNbInstances() int { - return b.NbInstances -} - // BlueprintProve is a BW6_761-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -317,17 +312,19 @@ func (b *BlueprintProve) NbConstraints() int { return 0 } -// NbOutputs implements Blueprint -func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances == 0 { +func (b *BlueprintProve) proofSize() int { + if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros(uint(nbInstances)) + logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) } +// NbOutputs implements Blueprint +func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { + return b.proofSize() +} + // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset @@ -352,10 +349,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree outputLevel := maxLevel + 1 // Compute proof size from blueprint state - nbInstances := b.SolveBlueprint.GetNbInstances() - if nbInstances > 0 { - logNbInstances := bits.TrailingZeros(uint(nbInstances)) - proofSize := gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + if b.SolveBlueprint.NbInstances > 0 { + proofSize := b.proofSize() for i := range proofSize { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } @@ -440,3 +435,32 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi tree.InsertWire(inst.WireOffset, outputLevel) return outputLevel } + +// NewBlueprints creates and registers all GKR blueprints for BW6_761 +func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { + // Create and register solve blueprint + solve := &BlueprintSolve{Circuit: circuit} + solveID := compiler.AddBlueprint(solve) + + // Create and register prove blueprint + prove := &BlueprintProve{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + HashName: hashName, + } + proveID := compiler.AddBlueprint(prove) + + // Create and register GetAssignment blueprint + getAssignment := &BlueprintGetAssignment{ + SolveBlueprintID: solveID, + SolveBlueprint: solve, + } + getAssignmentID := compiler.AddBlueprint(getAssignment) + + return gadget.Blueprints{ + SolveID: solveID, + Solve: solve, + ProveID: proveID, + GetAssignmentID: getAssignmentID, + } +} diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 8acdc41c48..dd39145429 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -5,11 +5,16 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/constraint/solver/gkrgates" "github.com/consensys/gnark/frontend" gadget "github.com/consensys/gnark/internal/gkr" + gkrbls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" + gkrbls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" + gkrbls24315 "github.com/consensys/gnark/internal/gkr/bls24-315" + gkrbls24317 "github.com/consensys/gnark/internal/gkr/bls24-317" gkrbn254 "github.com/consensys/gnark/internal/gkr/bn254" + gkrbw6633 "github.com/consensys/gnark/internal/gkr/bw6-633" + gkrbw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" @@ -38,10 +43,7 @@ type Circuit struct { api frontend.API // the parent API // Blueprint-based fields - solveBlueprintID constraint.BlueprintID - proveBlueprintID constraint.BlueprintID - getAssignmentBlueprintID constraint.BlueprintID - solveBlueprint gadget.BlueprintSolve + blueprints gadget.Blueprints // Metadata hashName string @@ -118,37 +120,36 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C return &res, nil } -// createBlueprint creates and initializes the GKR solveBlueprint for this circuit +// createBlueprint creates and initializes the GKR blueprint for this circuit func (c *Circuit) createBlueprint() error { - var blueprint gkrbn254.BlueprintSolve - - // Convert circuit to typed circuit + // Convert circuit to typed circuit (common for all curves) circuit, err := gkrtypes.NewCircuit(c.circuit, gkrgates.Get) if err != nil { return fmt.Errorf("failed to convert circuit: %w", err) } - blueprint.Circuit = circuit - // Note: metadata and evaluatorPool are initialized lazily on first Solve() call - - // Register solve solveBlueprint with compiler - c.solveBlueprintID = c.api.Compiler().AddBlueprint(&blueprint) - c.solveBlueprint = &blueprint - - // Create and register prove solveBlueprint with reference to solve solveBlueprint - proveBlueprint := &gkrbn254.BlueprintProve{ - SolveBlueprintID: c.solveBlueprintID, - SolveBlueprint: &blueprint, - HashName: c.hashName, - } - c.proveBlueprintID = c.api.Compiler().AddBlueprint(proveBlueprint) + // Dispatch to curve-specific factory + curveID := utils.FieldToCurve(c.api.Compiler().Field()) + compiler := c.api.Compiler() - // Create and register GetAssignment solveBlueprint for debugging - getAssignmentBlueprint := &gkrbn254.BlueprintGetAssignment{ - SolveBlueprintID: c.solveBlueprintID, - SolveBlueprint: &blueprint, + switch curveID { + case ecc.BN254: + c.blueprints = gkrbn254.NewBlueprints(circuit, c.hashName, compiler) + case ecc.BLS12_377: + c.blueprints = gkrbls12377.NewBlueprints(circuit, c.hashName, compiler) + case ecc.BLS12_381: + c.blueprints = gkrbls12381.NewBlueprints(circuit, c.hashName, compiler) + case ecc.BLS24_315: + c.blueprints = gkrbls24315.NewBlueprints(circuit, c.hashName, compiler) + case ecc.BLS24_317: + c.blueprints = gkrbls24317.NewBlueprints(circuit, c.hashName, compiler) + case ecc.BW6_633: + c.blueprints = gkrbw6633.NewBlueprints(circuit, c.hashName, compiler) + case ecc.BW6_761: + c.blueprints = gkrbw6761.NewBlueprints(circuit, c.hashName, compiler) + default: + return fmt.Errorf("unsupported curve: %s", curveID) } - c.getAssignmentBlueprintID = c.api.Compiler().AddBlueprint(getAssignmentBlueprint) return nil } @@ -187,7 +188,7 @@ func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr } // Execute solve solveBlueprint instruction - outputs := compiler.AddInstruction(c.solveBlueprintID, calldata) + outputs := compiler.AddInstruction(c.blueprints.SolveID, calldata) // Track instance count c.nbInstances++ @@ -224,7 +225,7 @@ func (c *Circuit) finalize(api frontend.API) error { } // Set NbInstances in the solve solveBlueprint (used for pre-allocation and proof size computation) - c.solveBlueprint.SetNbInstances(nbPaddedInstances) + c.blueprints.Solve.SetNbInstances(uint32(nbPaddedInstances)) // if the circuit consists of only one instance, directly solve the circuit if len(c.assignments[c.ins[0]]) == 1 { @@ -289,7 +290,7 @@ func (c *Circuit) verify(api frontend.API, initialChallenges []frontend.Variable } // Execute prove solveBlueprint instruction - proofOutputs := compiler.AddInstruction(c.proveBlueprintID, proveCalldata) + proofOutputs := compiler.AddInstruction(c.blueprints.ProveID, proveCalldata) // Convert outputs to proof proofSerialized := make([]frontend.Variable, len(proofOutputs)) @@ -350,6 +351,6 @@ func (c *Circuit) GetValue(v gkr.Variable, i int) frontend.Variable { depVar := compiler.ToCanonicalVariable(dependencyWire) depVar.Compress(&calldata) - outputs := compiler.AddInstruction(c.getAssignmentBlueprintID, calldata) + outputs := compiler.AddInstruction(c.blueprints.GetAssignmentID, calldata) return compiler.InternalVariable(outputs[0]) } From 04e502b4aef5a55fdf0c43cab9939edb45100e61 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Thu, 22 Jan 2026 23:04:17 -0600 Subject: [PATCH 127/251] refactor: generify circuit types --- constraint/solver/gkrgates/registry.go | 16 +- constraint/solver/gkrgates/registry_test.go | 6 +- .../backend/template/gkr/blueprint.go.tmpl | 13 +- .../backend/template/gkr/gate_testing.go.tmpl | 5 +- .../backend/template/gkr/gkr.go.tmpl | 27 +- internal/gkr/bls12-377/blueprint.go | 13 +- internal/gkr/bls12-377/gate_testing.go | 5 +- internal/gkr/bls12-377/gkr.go | 27 +- internal/gkr/bls12-381/blueprint.go | 13 +- internal/gkr/bls12-381/gate_testing.go | 5 +- internal/gkr/bls12-381/gkr.go | 27 +- internal/gkr/bls24-315/blueprint.go | 13 +- internal/gkr/bls24-315/gate_testing.go | 5 +- internal/gkr/bls24-315/gkr.go | 27 +- internal/gkr/bls24-317/blueprint.go | 13 +- internal/gkr/bls24-317/gate_testing.go | 5 +- internal/gkr/bls24-317/gkr.go | 27 +- internal/gkr/bn254/blueprint.go | 13 +- internal/gkr/bn254/gate_testing.go | 5 +- internal/gkr/bn254/gkr.go | 27 +- internal/gkr/bw6-633/blueprint.go | 13 +- internal/gkr/bw6-633/gate_testing.go | 5 +- internal/gkr/bw6-633/gkr.go | 27 +- internal/gkr/bw6-761/blueprint.go | 13 +- internal/gkr/bw6-761/gate_testing.go | 5 +- internal/gkr/bw6-761/gkr.go | 27 +- internal/gkr/gkr.go | 20 +- internal/gkr/gkrinfo/info.go | 28 -- internal/gkr/gkrtesting/gkrtesting.go | 22 +- internal/gkr/gkrtypes/compiledgate.go | 14 +- internal/gkr/gkrtypes/types.go | 239 ++++++++---------- internal/gkr/small_rational/gkr.go | 27 +- std/gkrapi/compile.go | 58 ++--- 33 files changed, 384 insertions(+), 406 deletions(-) delete mode 100644 internal/gkr/gkrinfo/info.go diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index e3dd49b797..c3e9858ee4 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -24,7 +24,7 @@ import ( ) var ( - gates = make(map[gkr.GateName]*gkrtypes.Gate) + gates = make(map[gkr.GateName]*gkrtypes.RegisteredGate) gatesLock sync.Mutex ) @@ -172,17 +172,17 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { if g, ok := gates[s.name]; ok { // gate already registered - if g.NbIn() != nbIn { - return fmt.Errorf("gate \"%s\" already registered with a different number of inputs (%d != %d)", s.name, g.NbIn(), nbIn) + if g.NbIn != nbIn { + return fmt.Errorf("gate \"%s\" already registered with a different number of inputs (%d != %d)", s.name, g.NbIn, nbIn) } for _, curve := range curvesForTesting { - gateVer, err := newGateTester(g.Compiled(), g.NbIn(), curve) + gateVer, err := newGateTester(g.Executable.Bytecode, g.NbIn, curve) if err != nil { return err } if !gateVer.Equal(compiled) { - return fmt.Errorf("mismatch with already registered gate \"%s\" (degree %d) over curve %s", s.name, g.Degree(), curve) + return fmt.Errorf("mismatch with already registered gate \"%s\" (degree %d) over curve %s", s.name, g.Degree, curve) } } @@ -231,7 +231,7 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { // Get returns a registered gate of the given name. // If not found, it will panic. // Gates can be added to the registry through Register. -func Get(name gkr.GateName) *gkrtypes.Gate { +func Get(name gkr.GateName) *gkrtypes.RegisteredGate { gatesLock.Lock() defer gatesLock.Unlock() if gate, ok := gates[name]; ok { @@ -244,10 +244,10 @@ type gateTester interface { IsAdditive(varIndex int) bool FindDegree(max int) (int, error) VerifyDegree(claimedDegree int) error - Equal(other *gkrtypes.CompiledGate) bool + Equal(other *gkrtypes.GateBytecode) bool } -func newGateTester(g *gkrtypes.CompiledGate, nbIn int, curve ecc.ID) (gateTester, error) { +func newGateTester(g *gkrtypes.GateBytecode, nbIn int, curve ecc.ID) (gateTester, error) { switch curve { case ecc.BLS12_377: diff --git a/constraint/solver/gkrgates/registry_test.go b/constraint/solver/gkrgates/registry_test.go index 350383b79e..0e47fc986f 100644 --- a/constraint/solver/gkrgates/registry_test.go +++ b/constraint/solver/gkrgates/registry_test.go @@ -35,7 +35,7 @@ func TestRegister(t *testing.T) { "no error must be returned when no degree is specified", ) - assert.Equal(t, degree, Get(name+"_no_degree").Degree(), "degree must be detected correctly") + assert.Equal(t, degree, Get(name+"_no_degree").Degree, "degree must be detected correctly") err := Register(func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Add(f(api, x...), 1) @@ -48,9 +48,9 @@ func TestRegister(t *testing.T) { return api.Add(x[0], x[1], x[2]) }, 3, 1) - testGate("mul2", gkrtypes.Mul2().Evaluate, 2, 2) + testGate("mul2", gkrtypes.Mul2().Executable.SnarkFriendly, 2, 2) - testGate("mimc", gkrtesting.NewCache().GetGate("mimc").Evaluate, 2, 7) + testGate("mimc", gkrtesting.NewCache().GetGate("mimc"), 2, 7) testGate("sub2PlusOne", func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Sub( diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index e838dae98f..7c3a0f6606 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -10,7 +10,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrinfo" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -21,7 +21,7 @@ type circuitEvaluator struct { // BlueprintSolve is a {{.FieldID}}-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.Circuit + Circuit gkrinfo.Circuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -52,11 +52,12 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.maxNbIn = b.Circuit.MaxGateNbIn() for i := range b.Circuit { if b.Circuit[i].IsInput() { b.nbInputs++ b.inputWires = append(b.inputWires, i) + } else { + b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) } } @@ -84,7 +85,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) } } return ce @@ -310,7 +311,7 @@ func (b *BlueprintProve) proofSize() int { return 0 } logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) - return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -429,7 +430,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for {{.FieldID}} -func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/generator/backend/template/gkr/gate_testing.go.tmpl b/internal/generator/backend/template/gkr/gate_testing.go.tmpl index 445ee6eb25..235608f7bc 100644 --- a/internal/generator/backend/template/gkr/gate_testing.go.tmpl +++ b/internal/generator/backend/template/gkr/gate_testing.go.tmpl @@ -1,6 +1,7 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/std/gkrapi/gkr" @@ -18,7 +19,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, @@ -165,7 +166,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { x := make({{.FieldPackageName}}.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 753bac6492..24a0e6c4b5 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -8,6 +8,7 @@ import ( "math/big" "strconv" "sync" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -438,7 +439,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } -func setup(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -549,7 +550,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]{{ .Ele } // Prove consistency of the claimed assignment -func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -603,7 +604,7 @@ func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fia // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -730,7 +731,7 @@ func frToBigInts(dst []*big.Int, src []{{ .ElementType }}) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate vars []{{ .ElementType }} frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -738,7 +739,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -783,28 +784,28 @@ func (e *gateEvaluator) evaluate(top ...{{ .ElementType }}) *{{ .ElementType }} // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrtypes.OpAdd: + case gkrinfo.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpMul: + case gkrinfo.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpSub: + case gkrinfo.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpNeg: + case gkrinfo.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrtypes.OpMulAcc: + case gkrinfo.OpMulAcc: var prod {{ .ElementType }} prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrtypes.OpSumExp17: + case gkrinfo.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum {{ .ElementType }} sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -830,14 +831,14 @@ func (e *gateEvaluator) evaluate(top ...{{ .ElementType }}) *{{ .ElementType }} // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index caedcb6cb5..cbcdc1fbab 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -17,7 +17,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrinfo" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -28,7 +28,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS12_377-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.Circuit + Circuit gkrinfo.Circuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -59,11 +59,12 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.maxNbIn = b.Circuit.MaxGateNbIn() for i := range b.Circuit { if b.Circuit[i].IsInput() { b.nbInputs++ b.inputWires = append(b.inputWires, i) + } else { + b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) } } @@ -91,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) } } return ce @@ -317,7 +318,7 @@ func (b *BlueprintProve) proofSize() int { return 0 } logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) - return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -437,7 +438,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_377 -func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bls12-377/gate_testing.go b/internal/gkr/bls12-377/gate_testing.go index 09ee174e09..99fe18756b 100644 --- a/internal/gkr/bls12-377/gate_testing.go +++ b/internal/gkr/bls12-377/gate_testing.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "slices" @@ -23,7 +24,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, @@ -152,7 +153,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 5e6532d3cb..3bd481b42c 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -16,6 +16,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -443,7 +444,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -554,7 +555,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Elem } // Prove consistency of the claimed assignment -func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -608,7 +609,7 @@ func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fia // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -735,7 +736,7 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -743,7 +744,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -788,28 +789,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrtypes.OpAdd: + case gkrinfo.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpMul: + case gkrinfo.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpSub: + case gkrinfo.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpNeg: + case gkrinfo.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrtypes.OpMulAcc: + case gkrinfo.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrtypes.OpSumExp17: + case gkrinfo.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -835,14 +836,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index c93c344d03..9351c73bbf 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -17,7 +17,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrinfo" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -28,7 +28,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS12_381-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.Circuit + Circuit gkrinfo.Circuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -59,11 +59,12 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.maxNbIn = b.Circuit.MaxGateNbIn() for i := range b.Circuit { if b.Circuit[i].IsInput() { b.nbInputs++ b.inputWires = append(b.inputWires, i) + } else { + b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) } } @@ -91,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) } } return ce @@ -317,7 +318,7 @@ func (b *BlueprintProve) proofSize() int { return 0 } logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) - return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -437,7 +438,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_381 -func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bls12-381/gate_testing.go b/internal/gkr/bls12-381/gate_testing.go index 169821fd27..4cc1779a8f 100644 --- a/internal/gkr/bls12-381/gate_testing.go +++ b/internal/gkr/bls12-381/gate_testing.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "slices" @@ -23,7 +24,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, @@ -152,7 +153,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index d6134b9b95..5de03fb8e6 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -16,6 +16,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -443,7 +444,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -554,7 +555,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Elem } // Prove consistency of the claimed assignment -func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -608,7 +609,7 @@ func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fia // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -735,7 +736,7 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -743,7 +744,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -788,28 +789,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrtypes.OpAdd: + case gkrinfo.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpMul: + case gkrinfo.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpSub: + case gkrinfo.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpNeg: + case gkrinfo.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrtypes.OpMulAcc: + case gkrinfo.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrtypes.OpSumExp17: + case gkrinfo.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -835,14 +836,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index dbcfe8f22d..32a92c4887 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -17,7 +17,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrinfo" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -28,7 +28,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS24_315-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.Circuit + Circuit gkrinfo.Circuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -59,11 +59,12 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.maxNbIn = b.Circuit.MaxGateNbIn() for i := range b.Circuit { if b.Circuit[i].IsInput() { b.nbInputs++ b.inputWires = append(b.inputWires, i) + } else { + b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) } } @@ -91,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) } } return ce @@ -317,7 +318,7 @@ func (b *BlueprintProve) proofSize() int { return 0 } logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) - return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -437,7 +438,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS24_315 -func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bls24-315/gate_testing.go b/internal/gkr/bls24-315/gate_testing.go index 3b0d415e84..5b7e29fd1d 100644 --- a/internal/gkr/bls24-315/gate_testing.go +++ b/internal/gkr/bls24-315/gate_testing.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "slices" @@ -23,7 +24,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, @@ -152,7 +153,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 2e5dbcd28b..aec736f314 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -16,6 +16,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -443,7 +444,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -554,7 +555,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Elem } // Prove consistency of the claimed assignment -func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -608,7 +609,7 @@ func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fia // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -735,7 +736,7 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -743,7 +744,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -788,28 +789,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrtypes.OpAdd: + case gkrinfo.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpMul: + case gkrinfo.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpSub: + case gkrinfo.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpNeg: + case gkrinfo.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrtypes.OpMulAcc: + case gkrinfo.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrtypes.OpSumExp17: + case gkrinfo.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -835,14 +836,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 692aa1f4f6..50a0d9ec5a 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -17,7 +17,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrinfo" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -28,7 +28,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS24_317-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.Circuit + Circuit gkrinfo.Circuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -59,11 +59,12 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.maxNbIn = b.Circuit.MaxGateNbIn() for i := range b.Circuit { if b.Circuit[i].IsInput() { b.nbInputs++ b.inputWires = append(b.inputWires, i) + } else { + b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) } } @@ -91,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) } } return ce @@ -317,7 +318,7 @@ func (b *BlueprintProve) proofSize() int { return 0 } logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) - return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -437,7 +438,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS24_317 -func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bls24-317/gate_testing.go b/internal/gkr/bls24-317/gate_testing.go index be906b0a55..b66e623b91 100644 --- a/internal/gkr/bls24-317/gate_testing.go +++ b/internal/gkr/bls24-317/gate_testing.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "slices" @@ -23,7 +24,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, @@ -152,7 +153,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 6baa34c17e..c5045076cb 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -16,6 +16,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -443,7 +444,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -554,7 +555,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Elem } // Prove consistency of the claimed assignment -func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -608,7 +609,7 @@ func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fia // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -735,7 +736,7 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -743,7 +744,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -788,28 +789,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrtypes.OpAdd: + case gkrinfo.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpMul: + case gkrinfo.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpSub: + case gkrinfo.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpNeg: + case gkrinfo.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrtypes.OpMulAcc: + case gkrinfo.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrtypes.OpSumExp17: + case gkrinfo.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -835,14 +836,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 71c8bc41c4..489b2e457c 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -17,7 +17,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrinfo" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -28,7 +28,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BN254-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.Circuit + Circuit gkrinfo.Circuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -59,11 +59,12 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.maxNbIn = b.Circuit.MaxGateNbIn() for i := range b.Circuit { if b.Circuit[i].IsInput() { b.nbInputs++ b.inputWires = append(b.inputWires, i) + } else { + b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) } } @@ -91,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) } } return ce @@ -317,7 +318,7 @@ func (b *BlueprintProve) proofSize() int { return 0 } logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) - return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -437,7 +438,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BN254 -func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bn254/gate_testing.go b/internal/gkr/bn254/gate_testing.go index 1ea0cd8a82..d2e0cd77c4 100644 --- a/internal/gkr/bn254/gate_testing.go +++ b/internal/gkr/bn254/gate_testing.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "slices" @@ -23,7 +24,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, @@ -152,7 +153,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 4efba767a9..bd73e4e6df 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -16,6 +16,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -443,7 +444,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -554,7 +555,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Elem } // Prove consistency of the claimed assignment -func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -608,7 +609,7 @@ func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fia // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -735,7 +736,7 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -743,7 +744,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -788,28 +789,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrtypes.OpAdd: + case gkrinfo.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpMul: + case gkrinfo.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpSub: + case gkrinfo.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpNeg: + case gkrinfo.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrtypes.OpMulAcc: + case gkrinfo.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrtypes.OpSumExp17: + case gkrinfo.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -835,14 +836,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index df3002ab00..57d6ba6a24 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -17,7 +17,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrinfo" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -28,7 +28,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BW6_633-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.Circuit + Circuit gkrinfo.Circuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -59,11 +59,12 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.maxNbIn = b.Circuit.MaxGateNbIn() for i := range b.Circuit { if b.Circuit[i].IsInput() { b.nbInputs++ b.inputWires = append(b.inputWires, i) + } else { + b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) } } @@ -91,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) } } return ce @@ -317,7 +318,7 @@ func (b *BlueprintProve) proofSize() int { return 0 } logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) - return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -437,7 +438,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BW6_633 -func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bw6-633/gate_testing.go b/internal/gkr/bw6-633/gate_testing.go index cb085e336a..c3b41ffe2c 100644 --- a/internal/gkr/bw6-633/gate_testing.go +++ b/internal/gkr/bw6-633/gate_testing.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "slices" @@ -23,7 +24,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, @@ -152,7 +153,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 0f1fd6a5b4..5396e3a35c 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -16,6 +16,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -443,7 +444,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -554,7 +555,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Elem } // Prove consistency of the claimed assignment -func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -608,7 +609,7 @@ func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fia // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -735,7 +736,7 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -743,7 +744,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -788,28 +789,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrtypes.OpAdd: + case gkrinfo.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpMul: + case gkrinfo.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpSub: + case gkrinfo.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpNeg: + case gkrinfo.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrtypes.OpMulAcc: + case gkrinfo.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrtypes.OpSumExp17: + case gkrinfo.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -835,14 +836,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index c125604b1c..ebe746a5bf 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -17,7 +17,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrinfo" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -28,7 +28,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BW6_761-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.Circuit + Circuit gkrinfo.Circuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -59,11 +59,12 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.maxNbIn = b.Circuit.MaxGateNbIn() for i := range b.Circuit { if b.Circuit[i].IsInput() { b.nbInputs++ b.inputWires = append(b.inputWires, i) + } else { + b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) } } @@ -91,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Compiled(), len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) } } return ce @@ -317,7 +318,7 @@ func (b *BlueprintProve) proofSize() int { return 0 } logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) - return gadget.ProofSize(b.SolveBlueprint.Circuit, logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -437,7 +438,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BW6_761 -func NewBlueprints(circuit gkrtypes.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bw6-761/gate_testing.go b/internal/gkr/bw6-761/gate_testing.go index 345421e617..a56596a5e2 100644 --- a/internal/gkr/bw6-761/gate_testing.go +++ b/internal/gkr/bw6-761/gate_testing.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "slices" @@ -23,7 +24,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrtypes.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, @@ -152,7 +153,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrtypes.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 9a58ccff40..212becdafc 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -16,6 +16,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -443,7 +444,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -554,7 +555,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Elem } // Prove consistency of the claimed assignment -func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -608,7 +609,7 @@ func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fia // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -735,7 +736,7 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -743,7 +744,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -788,28 +789,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrtypes.OpAdd: + case gkrinfo.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpMul: + case gkrinfo.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpSub: + case gkrinfo.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpNeg: + case gkrinfo.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrtypes.OpMulAcc: + case gkrinfo.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrtypes.OpSumExp17: + case gkrinfo.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -835,14 +836,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index 0a3fec372a..faffa8d303 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -8,10 +8,16 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" fiatshamir "github.com/consensys/gnark/std/fiat-shamir" + "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/consensys/gnark/std/polynomial" ) -// @tabaie TODO: Contains many things copy-pasted from gnark-crypto. Generify somehow? +// Snark-Friendly circuit types + +type ( + Wire gkrtypes.Wire[gkr.GateFunction] + Circuit gkrtypes.Circuit[gkr.GateFunction] +) // A SNARK gadget capable of verifying a GKR proof // The goal is to prove/verify evaluations of many instances of the same circuit. @@ -28,7 +34,7 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *gkrtypes.Wire { +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { return e.manager.wires[e.wireI] } @@ -143,7 +149,7 @@ func (m *claimsManager) deleteClaim(wire int) { } type settings struct { - sorted []*gkrtypes.Wire + sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -151,7 +157,7 @@ type settings struct { type Option func(*settings) -func WithSortedCircuit(sorted []*gkrtypes.Wire) Option { +func WithSortedCircuit(sorted []*Wire) Option { return func(options *settings) { options.sorted = sorted } @@ -188,13 +194,13 @@ func setup(api frontend.API, c gkrtypes.Circuit, assignment gkrtypes.WireAssignm } // ProofSize computes how large the proof for a circuit would be. It needs NbUniqueOutputs to be set -func ProofSize(c gkrtypes.Circuit, logNbInstances int) int { +func ProofSize(c Circuit, logNbInstances int) int { nbUniqueInputs := 0 nbPartialEvalPolys := 0 for i := range c { nbUniqueInputs += c[i].NbUniqueOutputs // each unique output is manifest in a finalEvalProof entry if !c[i].NoProof() { - nbPartialEvalPolys += c[i].Gate.Degree() + 1 + nbPartialEvalPolys += c[i].Gate.Degree + 1 } } return nbUniqueInputs + nbPartialEvalPolys*logNbInstances @@ -269,7 +275,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) (challenge // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(api frontend.API, c gkrtypes.Circuit, assignment gkrtypes.WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(api frontend.API, c Circuit, assignment gkrtypes.WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(api, c, assignment, transcriptSettings, options...) if err != nil { return err diff --git a/internal/gkr/gkrinfo/info.go b/internal/gkr/gkrinfo/info.go deleted file mode 100644 index 560f8ee814..0000000000 --- a/internal/gkr/gkrinfo/info.go +++ /dev/null @@ -1,28 +0,0 @@ -// Package gkrinfo contains serializable information capable of being saved in a SNARK circuit CS object. -package gkrinfo - -type ( - InputDependency struct { - OutputWire int - OutputInstance int - InputInstance int - } - - Wire struct { - Gate string - Inputs []int - } - - Circuit []Wire - - Permutations struct { - SortedInstances []int - SortedWires []int - InstancesPermutation []int - WiresPermutation []int - } -) - -func (w Wire) IsInput() bool { - return len(w.Inputs) == 0 -} diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 23100b289d..21bfa0614a 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -37,14 +37,14 @@ func NewCache() *Cache { return res } - mimcCompiled, err := gkrtypes.CompileGateFunction(mimcF, 2) + mimcCompiled, err := gkrinfo.CompileGateFunction(mimcF, 2) if err != nil { panic(err) } gates["mimc"] = gkrtypes.NewGate(mimcF, mimcCompiled, 2, 7, -1, gnark.Curves()) gates["select-input-3"] = gkrtypes.NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return in[2] - }, &gkrtypes.CompiledGate{}, 3, 1, 0, gnark.Curves()) + }, &gkrinfo.CompiledGate{}, 3, 1, 0, gnark.Curves()) return &Cache{ circuits: make(map[string]gkrtypes.Circuit), @@ -70,7 +70,23 @@ func (c *Cache) GetCircuit(path string) (circuit gkrtypes.Circuit) { if err = json.Unmarshal(bytes, &circuitInfo); err != nil { panic(err) } - if circuit, err = gkrtypes.NewCircuit(circuitInfo, c.GetGate); err != nil { + + // Build gate map from compiled gates to full gates + gateMap := make(map[*gkrinfo.CompiledGate]*gkrtypes.Gate) + for _, wire := range circuitInfo { + if wire.CompiledGate != nil { + // For each compiled gate, find the matching full gate from our cache + // This is a temporary solution - in practice, the circuit should store gate references + for _, gate := range c.gates { + if gate.Compiled() == wire.CompiledGate { + gateMap[wire.CompiledGate] = gate + break + } + } + } + } + + if circuit, err = gkrtypes.NewCircuit(circuitInfo, gateMap); err != nil { panic(err) } c.circuits[path] = circuit diff --git a/internal/gkr/gkrtypes/compiledgate.go b/internal/gkr/gkrtypes/compiledgate.go index bc634d7821..24ad63cdd8 100644 --- a/internal/gkr/gkrtypes/compiledgate.go +++ b/internal/gkr/gkrtypes/compiledgate.go @@ -24,7 +24,7 @@ const ( // GateInstruction represents a single operation in a compiled gate. // Each instruction produces a new variable (no explicit dst field). // Index space layout: -// - [0, nbConsts): constant values (from CompiledGate.Constants) +// - [0, nbConsts): constant values (from GateBytecode.Constants) // - [nbConsts, nbConsts+nbInputs): gate inputs // - [nbConsts+nbInputs, ...): instruction results type GateInstruction struct { @@ -32,17 +32,17 @@ type GateInstruction struct { Inputs []uint16 // indices into the unified value space } -// CompiledGate represents a gate function compiled into a sequence of instructions. +// GateBytecode represents a gate executable compiled into a sequence of instructions. // The compiled form is independent of curve-specific types and can be serialized. // The index space is unified: constants (0..nbConsts-1), inputs (nbConsts..nbConsts+nbInputs-1), // then instruction results. -type CompiledGate struct { +type GateBytecode struct { Instructions []GateInstruction // sequence of operations Constants []*big.Int // constant values at indices [0, nbConsts) } // NbConstants returns the number of constants in the gate -func (g *CompiledGate) NbConstants() int { +func (g *GateBytecode) NbConstants() int { return len(g.Constants) } @@ -194,9 +194,9 @@ func (gc *gateCompiler) remapIndices() { } } -// CompileGateFunction compiles a gate function into a CompiledGate. +// CompileGateFunction compiles a gate function into a GateBytecode. // The gate function should be of type gkr.GateFunction. -func CompileGateFunction(f gkr.GateFunction, nbInputs int) (*CompiledGate, error) { +func CompileGateFunction(f gkr.GateFunction, nbInputs int) (*GateBytecode, error) { // Create compiling API compiler := gateCompiler{ constantIndex: make(map[string]uint16), @@ -223,7 +223,7 @@ func CompileGateFunction(f gkr.GateFunction, nbInputs int) (*CompiledGate, error // Remap indices from temporary layout to final layout compiler.remapIndices() - return &CompiledGate{ + return &GateBytecode{ Instructions: compiler.GetInstructions(), Constants: compiler.constants, }, nil diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 6f9c236a41..4d760386b0 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -2,96 +2,108 @@ package gkrtypes import ( "errors" - "fmt" "slices" "github.com/consensys/gnark" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/consensys/gnark/std/polynomial" ) -// A Gate is a low-degree multivariate polynomial -type Gate struct { - Evaluate gkr.GateFunction // Evaluate the polynomial function defining the gate - compiled *CompiledGate // Compiled form of the gate function (used in native prover) - nbIn int // number of inputs - degree int // total degree of the polynomial - solvableVar int // if there is a variable whose value can be uniquely determined from the value of the gate and the other inputs, its index, -1 otherwise - curves []ecc.ID // curves that the gate is allowed to be used over -} +type ( + InputDependency struct { + OutputWire int + OutputInstance int + InputInstance int + } -// NewGate creates a new gate function with the given parameters: -// - f: the polynomial function defining the gate -// - compiled: the compiled form of the gate function -// - nbIn: number of inputs to the gate -// - degree: total degree of the polynomial. In case of multivariate polynomials, it is the maximum degree over all terms. -// - solvableVar: if there is a variable whose value can be uniquely determined from the value of the gate and the other inputs, its index, -1 otherwise -// - curves: curves that the gate is allowed to be used over -func NewGate(f gkr.GateFunction, compiled *CompiledGate, nbIn int, degree int, solvableVar int, curves []ecc.ID) *Gate { - - return &Gate{ - Evaluate: f, - compiled: compiled, - nbIn: nbIn, - degree: degree, - solvableVar: solvableVar, - curves: curves, + // A Gate is a low-degree multivariate polynomial + Gate[GateExecutable any] struct { + Executable GateExecutable + NbIn int // number of inputs + Degree int // total Degree of the polynomial + SolvableVar int // if there is a variable whose value can be uniquely determined from the value of the gate and the other inputs, its index, -1 otherwise + Curves []ecc.ID // Curves that the gate is allowed to be used over } -} -// SupportsCurve returns whether the gate can be used over the given curve. -func (g *Gate) SupportsCurve(curve ecc.ID) bool { - return slices.Contains(g.curves, curve) -} + Wire[GateExecutable any] struct { + Gate *Gate[GateExecutable] + Inputs []int + NbUniqueOutputs int + } -// Degree returns the total degree of the gate's polynomial e.g. Degree(xy²) = 3 -func (g *Gate) Degree() int { - return g.degree -} + Circuit[GateExecutable any] []Wire[GateExecutable] -// SolvableVar returns the index of a variable of degree 1 in the gate's -// polynomial. If there is no such variable, it returns -1. -func (g *Gate) SolvableVar() int { - return g.solvableVar -} + // Wires is a slice of pointers to Wire. It is used for propagating claim + // information through the circuit. + Wires[GateExecutable any] []*Wire[GateExecutable] -// NbIn returns the number of inputs to the gate (its fan-in) -func (g *Gate) NbIn() int { - return g.nbIn -} + SerializableWire struct { + CompiledGate *GateBytecode // Compiled gate (serializable) + Inputs []int + } + + SerializableCircuit Circuit[*GateBytecode] + + SerializableWires Wire[*GateBytecode] + + Permutations struct { + SortedInstances []int + SortedWires []int + InstancesPermutation []int + WiresPermutation []int + } -// Compiled returns the compiled form of the gate. -func (g *Gate) Compiled() *CompiledGate { - return g.compiled + BothExecutables struct { + Bytecode *GateBytecode + SnarkFriendly gkr.GateFunction + } + RegisteredGate Gate[BothExecutables] +) + +// NewGate creates a new gate function with the given parameters: +// - f: the polynomial function defining the gate +// - compiled: the compiled form of the gate function +// - NbIn: number of inputs to the gate +// - Degree: total Degree of the polynomial. In case of multivariate polynomials, it is the maximum Degree over all terms. +// - SolvableVar: if there is a variable whose value can be uniquely determined from the value of the gate and the other inputs, its index, -1 otherwise +// - Curves: Curves that the gate is allowed to be used over +func NewGate(f gkr.GateFunction, compiled *GateBytecode, nbIn int, degree int, solvableVar int, curves []ecc.ID) *RegisteredGate { + + return &RegisteredGate{ + Executable: BothExecutables{ + Bytecode: compiled, + SnarkFriendly: f, + }, + NbIn: nbIn, + Degree: degree, + SolvableVar: solvableVar, + Curves: curves, + } } -// Wire represents a wire in the GKR circuit. A wire is defined through its gate -// and its inputs. -type Wire struct { - Gate *Gate - Inputs []int - NbUniqueOutputs int +// SupportsCurve returns whether the gate can be used over the given curve. +func (g *Gate[GateExecutable]) SupportsCurve(curve ecc.ID) bool { + return slices.Contains(g.Curves, curve) } // IsInput returns whether the wire is an input wire. -func (w Wire) IsInput() bool { +func (w Wire[GateExecutable]) IsInput() bool { return len(w.Inputs) == 0 } // IsOutput returns whether the wire is an output wire. A wire is an output wire -// it it is not input to any other wire. -func (w Wire) IsOutput() bool { +// if it is not input to any other wire. +func (w Wire[GateExecutable]) IsOutput() bool { return w.NbUniqueOutputs == 0 } // NbClaims returns the number of claims to be proven about this wire. The number // of claims is the number of Wires it is input to. For output wires, there is always // one claim to be made. -func (w Wire) NbClaims() int { +func (w Wire[GateExecutable]) NbClaims() int { if w.IsOutput() { return 1 } @@ -100,12 +112,12 @@ func (w Wire) NbClaims() int { // NoProof returns whether no proof is needed for this wire. This corresponds // to input wires without any claims to be made about them. -func (w Wire) NoProof() bool { +func (w Wire[GateExecutable]) NoProof() bool { return w.IsInput() && w.NbClaims() == 1 } // NbUniqueInputs returns the number of unique input wires to this wire. -func (w Wire) NbUniqueInputs() int { +func (w Wire[GateExecutable]) NbUniqueInputs() int { set := make(map[int]struct{}, len(w.Inputs)) for _, in := range w.Inputs { set[in] = struct{}{} @@ -113,46 +125,13 @@ func (w Wire) NbUniqueInputs() int { return len(set) } -// Circuit is a GKR circuit: a list of wires. The wires are expected to be -// topologically sorted. -// -// Use [NewCircuit] to convert from [gkrinfo.Circuit] to this type. -type Circuit []Wire - -// NewCircuit converts gkrinfo.Circuit into a concrete Circuit object: -// - The gates are loaded in accordance with their names. -// - It also sets the NbUniqueOutputs fields. -// -// The gateGetter function is used to retrieve gate definitions by name. It is provided for avoiding -// import cycles and should typically be gkrgates.Get. -func NewCircuit(info gkrinfo.Circuit, gateGetter func(name gkr.GateName) *Gate) (Circuit, error) { - resCircuit := make(Circuit, len(info)) - for i := range info { - if info[i].Gate == "" && len(info[i].Inputs) == 0 { - resCircuit[i].Gate = Identity() // input wire - continue - } - resCircuit[i].Inputs = info[i].Inputs - resCircuit[i].Gate = gateGetter(gkr.GateName(info[i].Gate)) - if resCircuit[i].Gate == nil { - return nil, fmt.Errorf("gate \"%s\" not found", info[i].Gate) - } - } - resCircuit.setNbUniqueOutputs() - return resCircuit, nil -} - -// Wires is a slice of pointers to Wire. It is used for propagating claim -// information through the circuit. -type Wires []*Wire - // ClaimPropagationInfo returns sets of indices describing the pruning of claim propagation. // At the end of sumcheck for wire #wireIndex, we end up with sequences "uniqueEvaluations" and "evaluations", // the former a subsequence of the latter. // injection are the indices of the unique evaluations in the original evaluation list. // injectionRightInverse are the indices of the original evaluations in the unique evaluations list. // There are no guarantees on the non-unique choice of the semi-inverse map. -func (wires Wires) ClaimPropagationInfo(wireIndex int) (injection, injectionLeftInverse []int) { +func (wires Wires[GateExecutable]) ClaimPropagationInfo(wireIndex int) (injection, injectionLeftInverse []int) { w := wires[wireIndex] indexInProof := makeNeg1Slice(len(wires)) // O(n); use a map instead if it caused performance issues injection = make([]int, 0, len(w.Inputs)) @@ -169,11 +148,11 @@ func (wires Wires) ClaimPropagationInfo(wireIndex int) (injection, injectionLeft return } -func (c Circuit) maxGateDegree() int { +func (c Circuit[GateExecutable]) maxGateDegree() int { res := 1 for i := range c { if !c[i].IsInput() { - res = max(res, c[i].Gate.Degree()) + res = max(res, c[i].Gate.Degree) } } return res @@ -181,11 +160,11 @@ func (c Circuit) maxGateDegree() int { // MaxStackSize returns the maximum stack size needed by any gate evaluator in the circuit. // This is used to initialize universal gate evaluators that can handle any gate in the circuit. -func (c Circuit) MaxStackSize() int { +func (c SerializableCircuit) MaxStackSize() int { maxSize := 0 for i := range c { if !c[i].IsInput() { - gate := c[i].Gate.Compiled() + gate := c[i].Gate.Executable nbIn := len(c[i].Inputs) stackSize := gate.NbConstants() + nbIn + len(gate.Instructions) maxSize = max(maxSize, stackSize) @@ -195,7 +174,7 @@ func (c Circuit) MaxStackSize() int { } // MemoryRequirements returns an increasing vector of memory allocation sizes required for proving a GKR statement -func (c Circuit) MemoryRequirements(nbInstances int) []int { +func (c Circuit[GateExecutable]) MemoryRequirements(nbInstances int) []int { res := []int{256, nbInstances, nbInstances * (c.maxGateDegree() + 1)} if res[0] > res[1] { // make sure it's sorted @@ -210,14 +189,21 @@ func (c Circuit) MemoryRequirements(nbInstances int) []int { // OutputsList for each wire, returns the set of indexes of wires it is input to. // It also sets the NbUniqueOutputs fields, and sets the wire metadata. -func (c Circuit) OutputsList() [][]int { +func (c Circuit[GateExecutable]) OutputsList() [][]int { idGate := Identity() + var idGateTyped GateExecutable + switch idGateTyped.(type) { + case GateBytecode: + idGateTyped = idGate.Executable.Bytecode + case gkr.GateFunction: + idGateTyped = idGate.Executable.SnarkFriendly + } res := make([][]int, len(c)) for i := range c { res[i] = make([]int, 0) c[i].NbUniqueOutputs = 0 if c[i].IsInput() { - c[i].Gate = idGate + c[i].Gate = idGateTyped } } ins := make(map[int]struct{}, len(c)) @@ -236,7 +222,7 @@ func (c Circuit) OutputsList() [][]int { return res } -func (c Circuit) setNbUniqueOutputs() { +func (c Circuit[GateExecutable]) setNbUniqueOutputs() { for i := range c { c[i].NbUniqueOutputs = 0 @@ -263,7 +249,7 @@ func (c Circuit) setNbUniqueOutputs() { } // Inputs returns the list of input wire indices. -func (c Circuit) Inputs() []int { +func (c Circuit[GateExecutable]) Inputs() []int { res := make([]int, 0, len(c)) for i := range c { if c[i].IsInput() { @@ -274,7 +260,7 @@ func (c Circuit) Inputs() []int { } // MaxGateNbIn returns the maximum number of inputs of any gate in the circuit. -func (c Circuit) MaxGateNbIn() int { +func (c Circuit[GateExecutable]) MaxGateNbIn() int { res := 0 for i := range c { res = max(res, len(c[i].Inputs)) @@ -282,21 +268,10 @@ func (c Circuit) MaxGateNbIn() int { return res } -// SolvingInfo is a GKR circuit along with the number of instances to be solved -// and the used hash function for challenge computation. It is used during GKR -// circuit solving and proving. -// -// Use [NewSolvingInfo] to convert from [gkrinfo.StoringInfo] to this type. -type SolvingInfo struct { - Circuit Circuit - NbInstances int - HashName string -} - // WireAssignment is assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin -func (a WireAssignment) Permute(p gkrinfo.Permutations) { +func (a WireAssignment) Permute(p Permutations) { utils.Permute(a, p.WiresPermutation) for i := range a { if a[i] != nil { @@ -324,13 +299,13 @@ func (a WireAssignment) NbVars() int { } // ProofSize computes how large the proof for a circuit would be. It needs NbUniqueOutputs to be set. -func (c Circuit) ProofSize(logNbInstances int) int { +func (c Circuit[GateExecutable]) ProofSize(logNbInstances int) int { nbUniqueInputs := 0 nbPartialEvalPolys := 0 for i := range c { nbUniqueInputs += c[i].NbUniqueOutputs // each unique output is manifest in a finalEvalProof entry if !c[i].NoProof() { - nbPartialEvalPolys += c[i].Gate.Degree() + 1 + nbPartialEvalPolys += c[i].Gate.Degree + 1 } } return nbUniqueInputs + nbPartialEvalPolys*logNbInstances @@ -367,7 +342,7 @@ func (d *topSortData) markDone(i int) { } } -func statusList(c Circuit) []int { +func (c Circuit[GateExecutable]) statusList() []int { res := make([]int, len(c)) for i := range c { res[i] = len(c[i].Inputs) @@ -380,11 +355,11 @@ func statusList(c Circuit) []int { // It also sets the nbOutput flags, and a dummy IdentityGate for input wires. // Worst-case inefficient O(n^2), but that probably won't matter since the circuits are small. // Furthermore, it is efficient with already-close-to-sorted lists, which are the expected input -func (c Circuit) TopologicalSort() []*Wire { +func (c Circuit[GateExecutable]) TopologicalSort() Wires[GateExecutable] { var data topSortData data.outputs = c.OutputsList() - data.status = statusList(c) - sorted := make([]*Wire, len(c)) + data.status = c.statusList() + sorted := make(Wires[GateExecutable], len(c)) for data.leastReady = 0; data.status[data.leastReady] != 0; data.leastReady++ { } @@ -402,17 +377,17 @@ var ErrZeroFunction = errors.New("detected a zero function") // some sample gates // Identity gate: x -> x -func Identity() *Gate { +func Identity() *RegisteredGate { return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return in[0] - }, &CompiledGate{}, 1, 1, 0, gnark.Curves()) + }, &GateBytecode{}, 1, 1, 0, gnark.Curves()) } // Add2 gate: (x, y) -> x + y -func Add2() *Gate { +func Add2() *RegisteredGate { return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return api.Add(in[0], in[1]) - }, &CompiledGate{ + }, &GateBytecode{ Instructions: []GateInstruction{{ Op: OpAdd, Inputs: []uint16{0, 1}, @@ -421,10 +396,10 @@ func Add2() *Gate { } // Sub2 gate: (x, y) -> x - y -func Sub2() *Gate { +func Sub2() *RegisteredGate { return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return api.Sub(in[0], in[1]) - }, &CompiledGate{ + }, &GateBytecode{ Instructions: []GateInstruction{{ Op: OpSub, Inputs: []uint16{0, 1}, @@ -433,10 +408,10 @@ func Sub2() *Gate { } // Neg gate: x -> -x -func Neg() *Gate { +func Neg() *RegisteredGate { return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return api.Neg(in[0]) - }, &CompiledGate{ + }, &GateBytecode{ Instructions: []GateInstruction{{ Op: OpNeg, Inputs: []uint16{0}, @@ -445,10 +420,10 @@ func Neg() *Gate { } // Mul2 gate: (x, y) -> x * y -func Mul2() *Gate { +func Mul2() *RegisteredGate { return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return api.Mul(in[0], in[1]) - }, &CompiledGate{ + }, &GateBytecode{ Instructions: []GateInstruction{{ Op: OpMul, Inputs: []uint16{0, 1}, diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 71a4b38b01..107f3678c4 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -14,6 +14,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" + "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" @@ -443,7 +444,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -554,7 +555,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]small_r } // Prove consistency of the claimed assignment -func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -608,7 +609,7 @@ func Prove(c gkrtypes.Circuit, assignment WireAssignment, transcriptSettings fia // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrtypes.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -735,7 +736,7 @@ func frToBigInts(dst []*big.Int, src []small_rational.SmallRational) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate vars []small_rational.SmallRational frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -743,7 +744,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -788,28 +789,28 @@ func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rat // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrtypes.OpAdd: + case gkrinfo.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpMul: + case gkrinfo.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpSub: + case gkrinfo.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpNeg: + case gkrinfo.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrtypes.OpMulAcc: + case gkrinfo.OpMulAcc: var prod small_rational.SmallRational prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrtypes.OpSumExp17: + case gkrinfo.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum small_rational.SmallRational sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -835,14 +836,14 @@ func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rat // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.CompiledGate + gate *gkrinfo.CompiledGate nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index dd39145429..6f0ca727e4 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -86,9 +86,27 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C hashName: fiatshamirHashName, } - // Create and populate solveBlueprint - if err := res.createBlueprint(); err != nil { - return nil, fmt.Errorf("failed to create GKR solveBlueprint: %w", err) + // Dispatch to curve-specific factory + curveID := utils.FieldToCurve(api.parentApi.Compiler().Field()) + compiler := api.parentApi.Compiler() + + switch curveID { + case ecc.BN254: + res.blueprints = gkrbn254.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + case ecc.BLS12_377: + res.blueprints = gkrbls12377.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + case ecc.BLS12_381: + res.blueprints = gkrbls12381.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + case ecc.BLS24_315: + res.blueprints = gkrbls24315.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + case ecc.BLS24_317: + res.blueprints = gkrbls24317.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + case ecc.BW6_633: + res.blueprints = gkrbw6633.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + case ecc.BW6_761: + res.blueprints = gkrbw6761.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + default: + return nil, fmt.Errorf("unsupported curve: %s", curveID) } for _, opt := range options { @@ -120,40 +138,6 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C return &res, nil } -// createBlueprint creates and initializes the GKR blueprint for this circuit -func (c *Circuit) createBlueprint() error { - // Convert circuit to typed circuit (common for all curves) - circuit, err := gkrtypes.NewCircuit(c.circuit, gkrgates.Get) - if err != nil { - return fmt.Errorf("failed to convert circuit: %w", err) - } - - // Dispatch to curve-specific factory - curveID := utils.FieldToCurve(c.api.Compiler().Field()) - compiler := c.api.Compiler() - - switch curveID { - case ecc.BN254: - c.blueprints = gkrbn254.NewBlueprints(circuit, c.hashName, compiler) - case ecc.BLS12_377: - c.blueprints = gkrbls12377.NewBlueprints(circuit, c.hashName, compiler) - case ecc.BLS12_381: - c.blueprints = gkrbls12381.NewBlueprints(circuit, c.hashName, compiler) - case ecc.BLS24_315: - c.blueprints = gkrbls24315.NewBlueprints(circuit, c.hashName, compiler) - case ecc.BLS24_317: - c.blueprints = gkrbls24317.NewBlueprints(circuit, c.hashName, compiler) - case ecc.BW6_633: - c.blueprints = gkrbw6633.NewBlueprints(circuit, c.hashName, compiler) - case ecc.BW6_761: - c.blueprints = gkrbw6761.NewBlueprints(circuit, c.hashName, compiler) - default: - return fmt.Errorf("unsupported curve: %s", curveID) - } - - return nil -} - // AddInstance adds a new instance to the GKR circuit, returning the values of output variables for the instance. func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr.Variable]frontend.Variable, error) { if len(input) != len(c.ins) { From 60a167bb71a9118409053c376cf45316b16462f5 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Fri, 23 Jan 2026 00:03:39 -0600 Subject: [PATCH 128/251] chore: some cleanup --- constraint/solver/gkrgates/registry_test.go | 2 +- .../backend/template/gkr/blueprint.go.tmpl | 8 +- .../backend/template/gkr/gkr.go.tmpl | 60 ++++++++------- .../template/gkr/gkr.test.vectors.go.tmpl | 4 +- internal/gkr/bls12-377/blueprint.go | 8 +- internal/gkr/bls12-377/gkr.go | 60 ++++++++------- internal/gkr/bls12-377/gkr_test.go | 4 +- internal/gkr/bls12-381/blueprint.go | 8 +- internal/gkr/bls12-381/gkr.go | 60 ++++++++------- internal/gkr/bls12-381/gkr_test.go | 4 +- internal/gkr/bls24-315/blueprint.go | 8 +- internal/gkr/bls24-315/gkr.go | 60 ++++++++------- internal/gkr/bls24-315/gkr_test.go | 4 +- internal/gkr/bls24-317/blueprint.go | 8 +- internal/gkr/bls24-317/gkr.go | 60 ++++++++------- internal/gkr/bls24-317/gkr_test.go | 4 +- internal/gkr/bn254/blueprint.go | 8 +- internal/gkr/bn254/gkr.go | 60 ++++++++------- internal/gkr/bn254/gkr_test.go | 4 +- internal/gkr/bw6-633/blueprint.go | 8 +- internal/gkr/bw6-633/gkr.go | 60 ++++++++------- internal/gkr/bw6-633/gkr_test.go | 4 +- internal/gkr/bw6-761/blueprint.go | 8 +- internal/gkr/bw6-761/gkr.go | 60 ++++++++------- internal/gkr/bw6-761/gkr_test.go | 4 +- internal/gkr/gkrtesting/gkrtesting.go | 53 +++++++------ internal/gkr/gkrtypes/types.go | 77 +++++++++++++------ internal/gkr/small_rational/gkr.go | 60 ++++++++------- .../gkr/small_rational/test_vector_gen.go | 4 +- std/gkrapi/api.go | 15 ++-- std/gkrapi/compile.go | 50 +++++++----- 31 files changed, 471 insertions(+), 366 deletions(-) diff --git a/constraint/solver/gkrgates/registry_test.go b/constraint/solver/gkrgates/registry_test.go index 0e47fc986f..128d168c7e 100644 --- a/constraint/solver/gkrgates/registry_test.go +++ b/constraint/solver/gkrgates/registry_test.go @@ -50,7 +50,7 @@ func TestRegister(t *testing.T) { testGate("mul2", gkrtypes.Mul2().Executable.SnarkFriendly, 2, 2) - testGate("mimc", gkrtesting.NewCache().GetGate("mimc"), 2, 7) + testGate("mimc", gkrtesting.NewCache().GetGate("mimc").Executable.SnarkFriendly, 2, 7) testGate("sub2PlusOne", func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Sub( diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 7c3a0f6606..5522355683 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -10,7 +10,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrinfo" + "github.com/consensys/gnark/internal/gkr/gkrtypes" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -21,7 +21,7 @@ type circuitEvaluator struct { // BlueprintSolve is a {{.FieldID}}-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrinfo.Circuit + Circuit gkrtypes.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -85,7 +85,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) } } return ce @@ -430,7 +430,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for {{.FieldID}} -func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 24a0e6c4b5..517de25c79 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -8,10 +8,16 @@ import ( "math/big" "strconv" "sync" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) +// Type aliases for bytecode-based GKR types +type ( + Wire = gkrtypes.SerializableWire + Wires = gkrtypes.SerializableWires + Circuit = gkrtypes.SerializableCircuit +) + // The goal is to prove/verify evaluations of many instances of the same circuit @@ -30,7 +36,7 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *gkrtypes.Wire { +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { return e.manager.wires[e.wireI] } @@ -49,7 +55,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a {{ .ElementType }}) {{ } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.manager.wires[e.wireI].Gate.Degree() + return 1 + e.manager.wires[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. @@ -92,7 +98,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []{{ .ElementType e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -124,7 +130,7 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { +func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { return c.manager.wires[c.wireI] } @@ -213,7 +219,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []{{ . func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() - degGJ := 1 + wire.Gate.Degree() // guaranteed to be no smaller than the actual deg(gⱼ) + degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) nbGateIn := len(c.input) // Both E and wᵢ (the input wires and the eq table) are multilinear, thus @@ -346,10 +352,10 @@ type claimsManager struct { assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool - wires gkrtypes.Wires + wires Wires } -func newClaimsManager(wires []*gkrtypes.Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) manager.memPool = o.pool @@ -399,7 +405,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) return res } @@ -411,7 +417,7 @@ func (m *claimsManager) deleteClaim(wire int) { type settings struct { pool *polynomial.Pool - sorted []*gkrtypes.Wire + sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -426,7 +432,7 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithSortedCircuit(sorted []*gkrtypes.Wire) Option { +func WithSortedCircuit(sorted []*Wire) Option { return func(options *settings) { options.sorted = sorted } @@ -439,7 +445,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } -func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -480,7 +486,7 @@ func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat return o, err } -func ChallengeNames(sorted []*gkrtypes.Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge @@ -550,7 +556,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]{{ .Ele } // Prove consistency of the claimed assignment -func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -604,7 +610,7 @@ func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -659,7 +665,7 @@ func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcrip } // Complete the circuit evaluation from input values -func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { +func (a WireAssignment) Complete(wires Wires) WireAssignment { nbInstances := a.NumInstances() evaluators := make([]gateEvaluator, len(wires)) @@ -669,7 +675,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { a[i] = make([]{{ .ElementType }}, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) } } @@ -731,7 +737,7 @@ func frToBigInts(dst []*big.Int, src []{{ .ElementType }}) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode vars []{{ .ElementType }} frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -739,7 +745,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -784,28 +790,28 @@ func (e *gateEvaluator) evaluate(top ...{{ .ElementType }}) *{{ .ElementType }} // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrinfo.OpAdd: + case gkrtypes.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpMul: + case gkrtypes.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpSub: + case gkrtypes.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpNeg: + case gkrtypes.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrinfo.OpMulAcc: + case gkrtypes.OpMulAcc: var prod {{ .ElementType }} prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrinfo.OpSumExp17: + case gkrtypes.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum {{ .ElementType }} sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -831,14 +837,14 @@ func (e *gateEvaluator) evaluate(top ...{{ .ElementType }}) *{{ .ElementType }} // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl index 9d138b67a0..29b64252ca 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl @@ -32,7 +32,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.Circuit + Circuit gkrtypes.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -62,7 +62,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := cache.GetCircuit(filepath.Join(dir, info.Circuit)) + circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index cbcdc1fbab..95294d7bf4 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -17,7 +17,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrinfo" + "github.com/consensys/gnark/internal/gkr/gkrtypes" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -28,7 +28,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS12_377-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrinfo.Circuit + Circuit gkrtypes.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -92,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) } } return ce @@ -438,7 +438,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_377 -func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 3bd481b42c..df6860f043 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -16,10 +16,16 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) +// Type aliases for bytecode-based GKR types +type ( + Wire = gkrtypes.SerializableWire + Wires = gkrtypes.SerializableWires + Circuit = gkrtypes.SerializableCircuit +) + // The goal is to prove/verify evaluations of many instances of the same circuit // WireAssignment is assignment of values to the same wire across many instances of the circuit @@ -37,7 +43,7 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *gkrtypes.Wire { +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { return e.manager.wires[e.wireI] } @@ -56,7 +62,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a fr.Element) fr.Element } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.manager.wires[e.wireI].Gate.Degree() + return 1 + e.manager.wires[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. @@ -99,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -131,7 +137,7 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { +func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { return c.manager.wires[c.wireI] } @@ -219,7 +225,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() - degGJ := 1 + wire.Gate.Degree() // guaranteed to be no smaller than the actual deg(gⱼ) + degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) nbGateIn := len(c.input) // Both E and wᵢ (the input wires and the eq table) are multilinear, thus @@ -352,10 +358,10 @@ type claimsManager struct { assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool - wires gkrtypes.Wires + wires Wires } -func newClaimsManager(wires []*gkrtypes.Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) manager.memPool = o.pool @@ -405,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) return res } @@ -417,7 +423,7 @@ func (m *claimsManager) deleteClaim(wire int) { type settings struct { pool *polynomial.Pool - sorted []*gkrtypes.Wire + sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -432,7 +438,7 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithSortedCircuit(sorted []*gkrtypes.Wire) Option { +func WithSortedCircuit(sorted []*Wire) Option { return func(options *settings) { options.sorted = sorted } @@ -444,7 +450,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -485,7 +491,7 @@ func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat return o, err } -func ChallengeNames(sorted []*gkrtypes.Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge @@ -555,7 +561,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Elem } // Prove consistency of the claimed assignment -func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -609,7 +615,7 @@ func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -664,7 +670,7 @@ func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcrip } // Complete the circuit evaluation from input values -func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { +func (a WireAssignment) Complete(wires Wires) WireAssignment { nbInstances := a.NumInstances() evaluators := make([]gateEvaluator, len(wires)) @@ -674,7 +680,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { a[i] = make([]fr.Element, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) } } @@ -736,7 +742,7 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -744,7 +750,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -789,28 +795,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrinfo.OpAdd: + case gkrtypes.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpMul: + case gkrtypes.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpSub: + case gkrtypes.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpNeg: + case gkrtypes.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrinfo.OpMulAcc: + case gkrtypes.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrinfo.OpSumExp17: + case gkrtypes.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -836,14 +842,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index ab49d127b7..fdbcec9776 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -418,7 +418,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.Circuit + Circuit gkrtypes.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -447,7 +447,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := cache.GetCircuit(filepath.Join(dir, info.Circuit)) + circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 9351c73bbf..101a4539ac 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -17,7 +17,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrinfo" + "github.com/consensys/gnark/internal/gkr/gkrtypes" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -28,7 +28,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS12_381-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrinfo.Circuit + Circuit gkrtypes.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -92,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) } } return ce @@ -438,7 +438,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_381 -func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 5de03fb8e6..799cb00714 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -16,10 +16,16 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) +// Type aliases for bytecode-based GKR types +type ( + Wire = gkrtypes.SerializableWire + Wires = gkrtypes.SerializableWires + Circuit = gkrtypes.SerializableCircuit +) + // The goal is to prove/verify evaluations of many instances of the same circuit // WireAssignment is assignment of values to the same wire across many instances of the circuit @@ -37,7 +43,7 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *gkrtypes.Wire { +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { return e.manager.wires[e.wireI] } @@ -56,7 +62,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a fr.Element) fr.Element } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.manager.wires[e.wireI].Gate.Degree() + return 1 + e.manager.wires[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. @@ -99,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -131,7 +137,7 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { +func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { return c.manager.wires[c.wireI] } @@ -219,7 +225,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() - degGJ := 1 + wire.Gate.Degree() // guaranteed to be no smaller than the actual deg(gⱼ) + degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) nbGateIn := len(c.input) // Both E and wᵢ (the input wires and the eq table) are multilinear, thus @@ -352,10 +358,10 @@ type claimsManager struct { assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool - wires gkrtypes.Wires + wires Wires } -func newClaimsManager(wires []*gkrtypes.Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) manager.memPool = o.pool @@ -405,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) return res } @@ -417,7 +423,7 @@ func (m *claimsManager) deleteClaim(wire int) { type settings struct { pool *polynomial.Pool - sorted []*gkrtypes.Wire + sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -432,7 +438,7 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithSortedCircuit(sorted []*gkrtypes.Wire) Option { +func WithSortedCircuit(sorted []*Wire) Option { return func(options *settings) { options.sorted = sorted } @@ -444,7 +450,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -485,7 +491,7 @@ func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat return o, err } -func ChallengeNames(sorted []*gkrtypes.Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge @@ -555,7 +561,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Elem } // Prove consistency of the claimed assignment -func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -609,7 +615,7 @@ func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -664,7 +670,7 @@ func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcrip } // Complete the circuit evaluation from input values -func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { +func (a WireAssignment) Complete(wires Wires) WireAssignment { nbInstances := a.NumInstances() evaluators := make([]gateEvaluator, len(wires)) @@ -674,7 +680,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { a[i] = make([]fr.Element, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) } } @@ -736,7 +742,7 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -744,7 +750,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -789,28 +795,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrinfo.OpAdd: + case gkrtypes.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpMul: + case gkrtypes.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpSub: + case gkrtypes.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpNeg: + case gkrtypes.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrinfo.OpMulAcc: + case gkrtypes.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrinfo.OpSumExp17: + case gkrtypes.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -836,14 +842,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 85992a05d9..40c3d2ffb1 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -418,7 +418,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.Circuit + Circuit gkrtypes.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -447,7 +447,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := cache.GetCircuit(filepath.Join(dir, info.Circuit)) + circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index 32a92c4887..b38c4a4b6a 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -17,7 +17,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrinfo" + "github.com/consensys/gnark/internal/gkr/gkrtypes" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -28,7 +28,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS24_315-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrinfo.Circuit + Circuit gkrtypes.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -92,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) } } return ce @@ -438,7 +438,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS24_315 -func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index aec736f314..80e6717a47 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -16,10 +16,16 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) +// Type aliases for bytecode-based GKR types +type ( + Wire = gkrtypes.SerializableWire + Wires = gkrtypes.SerializableWires + Circuit = gkrtypes.SerializableCircuit +) + // The goal is to prove/verify evaluations of many instances of the same circuit // WireAssignment is assignment of values to the same wire across many instances of the circuit @@ -37,7 +43,7 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *gkrtypes.Wire { +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { return e.manager.wires[e.wireI] } @@ -56,7 +62,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a fr.Element) fr.Element } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.manager.wires[e.wireI].Gate.Degree() + return 1 + e.manager.wires[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. @@ -99,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -131,7 +137,7 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { +func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { return c.manager.wires[c.wireI] } @@ -219,7 +225,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() - degGJ := 1 + wire.Gate.Degree() // guaranteed to be no smaller than the actual deg(gⱼ) + degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) nbGateIn := len(c.input) // Both E and wᵢ (the input wires and the eq table) are multilinear, thus @@ -352,10 +358,10 @@ type claimsManager struct { assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool - wires gkrtypes.Wires + wires Wires } -func newClaimsManager(wires []*gkrtypes.Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) manager.memPool = o.pool @@ -405,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) return res } @@ -417,7 +423,7 @@ func (m *claimsManager) deleteClaim(wire int) { type settings struct { pool *polynomial.Pool - sorted []*gkrtypes.Wire + sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -432,7 +438,7 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithSortedCircuit(sorted []*gkrtypes.Wire) Option { +func WithSortedCircuit(sorted []*Wire) Option { return func(options *settings) { options.sorted = sorted } @@ -444,7 +450,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -485,7 +491,7 @@ func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat return o, err } -func ChallengeNames(sorted []*gkrtypes.Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge @@ -555,7 +561,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Elem } // Prove consistency of the claimed assignment -func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -609,7 +615,7 @@ func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -664,7 +670,7 @@ func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcrip } // Complete the circuit evaluation from input values -func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { +func (a WireAssignment) Complete(wires Wires) WireAssignment { nbInstances := a.NumInstances() evaluators := make([]gateEvaluator, len(wires)) @@ -674,7 +680,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { a[i] = make([]fr.Element, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) } } @@ -736,7 +742,7 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -744,7 +750,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -789,28 +795,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrinfo.OpAdd: + case gkrtypes.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpMul: + case gkrtypes.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpSub: + case gkrtypes.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpNeg: + case gkrtypes.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrinfo.OpMulAcc: + case gkrtypes.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrinfo.OpSumExp17: + case gkrtypes.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -836,14 +842,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bls24-315/gkr_test.go b/internal/gkr/bls24-315/gkr_test.go index df50bf8166..0e0fae285c 100644 --- a/internal/gkr/bls24-315/gkr_test.go +++ b/internal/gkr/bls24-315/gkr_test.go @@ -418,7 +418,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.Circuit + Circuit gkrtypes.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -447,7 +447,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := cache.GetCircuit(filepath.Join(dir, info.Circuit)) + circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 50a0d9ec5a..7053239a9b 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -17,7 +17,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrinfo" + "github.com/consensys/gnark/internal/gkr/gkrtypes" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -28,7 +28,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS24_317-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrinfo.Circuit + Circuit gkrtypes.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -92,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) } } return ce @@ -438,7 +438,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS24_317 -func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index c5045076cb..42c6ee6996 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -16,10 +16,16 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) +// Type aliases for bytecode-based GKR types +type ( + Wire = gkrtypes.SerializableWire + Wires = gkrtypes.SerializableWires + Circuit = gkrtypes.SerializableCircuit +) + // The goal is to prove/verify evaluations of many instances of the same circuit // WireAssignment is assignment of values to the same wire across many instances of the circuit @@ -37,7 +43,7 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *gkrtypes.Wire { +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { return e.manager.wires[e.wireI] } @@ -56,7 +62,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a fr.Element) fr.Element } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.manager.wires[e.wireI].Gate.Degree() + return 1 + e.manager.wires[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. @@ -99,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -131,7 +137,7 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { +func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { return c.manager.wires[c.wireI] } @@ -219,7 +225,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() - degGJ := 1 + wire.Gate.Degree() // guaranteed to be no smaller than the actual deg(gⱼ) + degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) nbGateIn := len(c.input) // Both E and wᵢ (the input wires and the eq table) are multilinear, thus @@ -352,10 +358,10 @@ type claimsManager struct { assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool - wires gkrtypes.Wires + wires Wires } -func newClaimsManager(wires []*gkrtypes.Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) manager.memPool = o.pool @@ -405,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) return res } @@ -417,7 +423,7 @@ func (m *claimsManager) deleteClaim(wire int) { type settings struct { pool *polynomial.Pool - sorted []*gkrtypes.Wire + sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -432,7 +438,7 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithSortedCircuit(sorted []*gkrtypes.Wire) Option { +func WithSortedCircuit(sorted []*Wire) Option { return func(options *settings) { options.sorted = sorted } @@ -444,7 +450,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -485,7 +491,7 @@ func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat return o, err } -func ChallengeNames(sorted []*gkrtypes.Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge @@ -555,7 +561,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Elem } // Prove consistency of the claimed assignment -func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -609,7 +615,7 @@ func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -664,7 +670,7 @@ func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcrip } // Complete the circuit evaluation from input values -func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { +func (a WireAssignment) Complete(wires Wires) WireAssignment { nbInstances := a.NumInstances() evaluators := make([]gateEvaluator, len(wires)) @@ -674,7 +680,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { a[i] = make([]fr.Element, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) } } @@ -736,7 +742,7 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -744,7 +750,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -789,28 +795,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrinfo.OpAdd: + case gkrtypes.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpMul: + case gkrtypes.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpSub: + case gkrtypes.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpNeg: + case gkrtypes.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrinfo.OpMulAcc: + case gkrtypes.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrinfo.OpSumExp17: + case gkrtypes.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -836,14 +842,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bls24-317/gkr_test.go b/internal/gkr/bls24-317/gkr_test.go index 0c3470e6d5..908ff50f52 100644 --- a/internal/gkr/bls24-317/gkr_test.go +++ b/internal/gkr/bls24-317/gkr_test.go @@ -418,7 +418,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.Circuit + Circuit gkrtypes.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -447,7 +447,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := cache.GetCircuit(filepath.Join(dir, info.Circuit)) + circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 489b2e457c..46f7684b14 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -17,7 +17,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrinfo" + "github.com/consensys/gnark/internal/gkr/gkrtypes" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -28,7 +28,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BN254-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrinfo.Circuit + Circuit gkrtypes.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -92,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) } } return ce @@ -438,7 +438,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BN254 -func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index bd73e4e6df..bc48d3a481 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -16,10 +16,16 @@ import ( "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) +// Type aliases for bytecode-based GKR types +type ( + Wire = gkrtypes.SerializableWire + Wires = gkrtypes.SerializableWires + Circuit = gkrtypes.SerializableCircuit +) + // The goal is to prove/verify evaluations of many instances of the same circuit // WireAssignment is assignment of values to the same wire across many instances of the circuit @@ -37,7 +43,7 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *gkrtypes.Wire { +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { return e.manager.wires[e.wireI] } @@ -56,7 +62,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a fr.Element) fr.Element } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.manager.wires[e.wireI].Gate.Degree() + return 1 + e.manager.wires[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. @@ -99,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -131,7 +137,7 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { +func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { return c.manager.wires[c.wireI] } @@ -219,7 +225,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() - degGJ := 1 + wire.Gate.Degree() // guaranteed to be no smaller than the actual deg(gⱼ) + degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) nbGateIn := len(c.input) // Both E and wᵢ (the input wires and the eq table) are multilinear, thus @@ -352,10 +358,10 @@ type claimsManager struct { assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool - wires gkrtypes.Wires + wires Wires } -func newClaimsManager(wires []*gkrtypes.Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) manager.memPool = o.pool @@ -405,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) return res } @@ -417,7 +423,7 @@ func (m *claimsManager) deleteClaim(wire int) { type settings struct { pool *polynomial.Pool - sorted []*gkrtypes.Wire + sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -432,7 +438,7 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithSortedCircuit(sorted []*gkrtypes.Wire) Option { +func WithSortedCircuit(sorted []*Wire) Option { return func(options *settings) { options.sorted = sorted } @@ -444,7 +450,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -485,7 +491,7 @@ func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat return o, err } -func ChallengeNames(sorted []*gkrtypes.Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge @@ -555,7 +561,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Elem } // Prove consistency of the claimed assignment -func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -609,7 +615,7 @@ func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -664,7 +670,7 @@ func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcrip } // Complete the circuit evaluation from input values -func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { +func (a WireAssignment) Complete(wires Wires) WireAssignment { nbInstances := a.NumInstances() evaluators := make([]gateEvaluator, len(wires)) @@ -674,7 +680,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { a[i] = make([]fr.Element, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) } } @@ -736,7 +742,7 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -744,7 +750,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -789,28 +795,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrinfo.OpAdd: + case gkrtypes.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpMul: + case gkrtypes.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpSub: + case gkrtypes.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpNeg: + case gkrtypes.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrinfo.OpMulAcc: + case gkrtypes.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrinfo.OpSumExp17: + case gkrtypes.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -836,14 +842,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 275803af51..bb9c746776 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -418,7 +418,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.Circuit + Circuit gkrtypes.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -447,7 +447,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := cache.GetCircuit(filepath.Join(dir, info.Circuit)) + circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 57d6ba6a24..d97dc48d65 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -17,7 +17,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrinfo" + "github.com/consensys/gnark/internal/gkr/gkrtypes" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -28,7 +28,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BW6_633-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrinfo.Circuit + Circuit gkrtypes.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -92,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) } } return ce @@ -438,7 +438,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BW6_633 -func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 5396e3a35c..8779aef0d2 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -16,10 +16,16 @@ import ( "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) +// Type aliases for bytecode-based GKR types +type ( + Wire = gkrtypes.SerializableWire + Wires = gkrtypes.SerializableWires + Circuit = gkrtypes.SerializableCircuit +) + // The goal is to prove/verify evaluations of many instances of the same circuit // WireAssignment is assignment of values to the same wire across many instances of the circuit @@ -37,7 +43,7 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *gkrtypes.Wire { +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { return e.manager.wires[e.wireI] } @@ -56,7 +62,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a fr.Element) fr.Element } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.manager.wires[e.wireI].Gate.Degree() + return 1 + e.manager.wires[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. @@ -99,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -131,7 +137,7 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { +func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { return c.manager.wires[c.wireI] } @@ -219,7 +225,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() - degGJ := 1 + wire.Gate.Degree() // guaranteed to be no smaller than the actual deg(gⱼ) + degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) nbGateIn := len(c.input) // Both E and wᵢ (the input wires and the eq table) are multilinear, thus @@ -352,10 +358,10 @@ type claimsManager struct { assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool - wires gkrtypes.Wires + wires Wires } -func newClaimsManager(wires []*gkrtypes.Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) manager.memPool = o.pool @@ -405,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) return res } @@ -417,7 +423,7 @@ func (m *claimsManager) deleteClaim(wire int) { type settings struct { pool *polynomial.Pool - sorted []*gkrtypes.Wire + sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -432,7 +438,7 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithSortedCircuit(sorted []*gkrtypes.Wire) Option { +func WithSortedCircuit(sorted []*Wire) Option { return func(options *settings) { options.sorted = sorted } @@ -444,7 +450,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -485,7 +491,7 @@ func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat return o, err } -func ChallengeNames(sorted []*gkrtypes.Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge @@ -555,7 +561,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Elem } // Prove consistency of the claimed assignment -func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -609,7 +615,7 @@ func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -664,7 +670,7 @@ func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcrip } // Complete the circuit evaluation from input values -func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { +func (a WireAssignment) Complete(wires Wires) WireAssignment { nbInstances := a.NumInstances() evaluators := make([]gateEvaluator, len(wires)) @@ -674,7 +680,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { a[i] = make([]fr.Element, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) } } @@ -736,7 +742,7 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -744,7 +750,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -789,28 +795,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrinfo.OpAdd: + case gkrtypes.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpMul: + case gkrtypes.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpSub: + case gkrtypes.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpNeg: + case gkrtypes.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrinfo.OpMulAcc: + case gkrtypes.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrinfo.OpSumExp17: + case gkrtypes.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -836,14 +842,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bw6-633/gkr_test.go b/internal/gkr/bw6-633/gkr_test.go index 508879c2f4..bfa881cfd2 100644 --- a/internal/gkr/bw6-633/gkr_test.go +++ b/internal/gkr/bw6-633/gkr_test.go @@ -418,7 +418,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.Circuit + Circuit gkrtypes.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -447,7 +447,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := cache.GetCircuit(filepath.Join(dir, info.Circuit)) + circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index ebe746a5bf..873c103ab8 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -17,7 +17,7 @@ import ( "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrinfo" + "github.com/consensys/gnark/internal/gkr/gkrtypes" ) // circuitEvaluator evaluates all gates in a circuit for one instance @@ -28,7 +28,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BW6_761-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrinfo.Circuit + Circuit gkrtypes.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -92,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.CompiledGate, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) } } return ce @@ -438,7 +438,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BW6_761 -func NewBlueprints(circuit gkrinfo.Circuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 212becdafc..debe95a206 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -16,10 +16,16 @@ import ( "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) +// Type aliases for bytecode-based GKR types +type ( + Wire = gkrtypes.SerializableWire + Wires = gkrtypes.SerializableWires + Circuit = gkrtypes.SerializableCircuit +) + // The goal is to prove/verify evaluations of many instances of the same circuit // WireAssignment is assignment of values to the same wire across many instances of the circuit @@ -37,7 +43,7 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *gkrtypes.Wire { +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { return e.manager.wires[e.wireI] } @@ -56,7 +62,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a fr.Element) fr.Element } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.manager.wires[e.wireI].Gate.Degree() + return 1 + e.manager.wires[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. @@ -99,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -131,7 +137,7 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { +func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { return c.manager.wires[c.wireI] } @@ -219,7 +225,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() - degGJ := 1 + wire.Gate.Degree() // guaranteed to be no smaller than the actual deg(gⱼ) + degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) nbGateIn := len(c.input) // Both E and wᵢ (the input wires and the eq table) are multilinear, thus @@ -352,10 +358,10 @@ type claimsManager struct { assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool - wires gkrtypes.Wires + wires Wires } -func newClaimsManager(wires []*gkrtypes.Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) manager.memPool = o.pool @@ -405,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) return res } @@ -417,7 +423,7 @@ func (m *claimsManager) deleteClaim(wire int) { type settings struct { pool *polynomial.Pool - sorted []*gkrtypes.Wire + sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -432,7 +438,7 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithSortedCircuit(sorted []*gkrtypes.Wire) Option { +func WithSortedCircuit(sorted []*Wire) Option { return func(options *settings) { options.sorted = sorted } @@ -444,7 +450,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -485,7 +491,7 @@ func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat return o, err } -func ChallengeNames(sorted []*gkrtypes.Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge @@ -555,7 +561,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Elem } // Prove consistency of the claimed assignment -func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -609,7 +615,7 @@ func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -664,7 +670,7 @@ func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcrip } // Complete the circuit evaluation from input values -func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { +func (a WireAssignment) Complete(wires Wires) WireAssignment { nbInstances := a.NumInstances() evaluators := make([]gateEvaluator, len(wires)) @@ -674,7 +680,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { a[i] = make([]fr.Element, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) } } @@ -736,7 +742,7 @@ func frToBigInts(dst []*big.Int, src []fr.Element) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -744,7 +750,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -789,28 +795,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrinfo.OpAdd: + case gkrtypes.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpMul: + case gkrtypes.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpSub: + case gkrtypes.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpNeg: + case gkrtypes.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrinfo.OpMulAcc: + case gkrtypes.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrinfo.OpSumExp17: + case gkrtypes.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -836,14 +842,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index 46ba23b0a9..7c9f3908a0 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -418,7 +418,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.Circuit + Circuit gkrtypes.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -447,7 +447,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := cache.GetCircuit(filepath.Join(dir, info.Circuit)) + circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 21bfa0614a..7947657d41 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -8,7 +8,6 @@ import ( "github.com/consensys/gnark" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/std/gkrapi/gkr" ) @@ -17,12 +16,12 @@ import ( // The main functionality is to cache whole circuits, but this package needs to use its own gate registry, in order to avoid import cycles. // Cache is used in tests for the per-curve GKR packages, but they in turn provide gate degree discovery functions to the gkrgates package. type Cache struct { - circuits map[string]gkrtypes.Circuit - gates map[gkr.GateName]*gkrtypes.Gate + circuits map[string]gkrtypes.RegisteredCircuit + gates map[gkr.GateName]*gkrtypes.RegisteredGate } func NewCache() *Cache { - gates := make(map[gkr.GateName]*gkrtypes.Gate, 7) + gates := make(map[gkr.GateName]*gkrtypes.RegisteredGate, 7) gates[gkr.Identity] = gkrtypes.Identity() gates[gkr.Add2] = gkrtypes.Add2() gates[gkr.Sub2] = gkrtypes.Sub2() @@ -37,71 +36,79 @@ func NewCache() *Cache { return res } - mimcCompiled, err := gkrinfo.CompileGateFunction(mimcF, 2) + mimcCompiled, err := gkrtypes.CompileGateFunction(mimcF, 2) if err != nil { panic(err) } gates["mimc"] = gkrtypes.NewGate(mimcF, mimcCompiled, 2, 7, -1, gnark.Curves()) gates["select-input-3"] = gkrtypes.NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return in[2] - }, &gkrinfo.CompiledGate{}, 3, 1, 0, gnark.Curves()) + }, &gkrtypes.GateBytecode{}, 3, 1, 0, gnark.Curves()) return &Cache{ - circuits: make(map[string]gkrtypes.Circuit), + circuits: make(map[string]gkrtypes.RegisteredCircuit), gates: gates, } } -func (c *Cache) GetCircuit(path string) (circuit gkrtypes.Circuit) { +func (c *Cache) GetCircuit(path string) gkrtypes.RegisteredCircuit { path, err := filepath.Abs(path) if err != nil { panic(err) } var ok bool + var circuit gkrtypes.RegisteredCircuit if circuit, ok = c.circuits[path]; ok { - return + return circuit } var bytes []byte if bytes, err = os.ReadFile(path); err != nil { panic(err) } - var circuitInfo gkrinfo.Circuit + var circuitInfo gkrtypes.SerializableCircuit if err = json.Unmarshal(bytes, &circuitInfo); err != nil { panic(err) } - // Build gate map from compiled gates to full gates - gateMap := make(map[*gkrinfo.CompiledGate]*gkrtypes.Gate) + // Build gate map from bytecode to registered gates + gateMap := make(map[*gkrtypes.GateBytecode]*gkrtypes.RegisteredGate) for _, wire := range circuitInfo { - if wire.CompiledGate != nil { - // For each compiled gate, find the matching full gate from our cache - // This is a temporary solution - in practice, the circuit should store gate references + if wire.Gate.Executable != nil { + // For each bytecode gate, find the matching registered gate from our cache for _, gate := range c.gates { - if gate.Compiled() == wire.CompiledGate { - gateMap[wire.CompiledGate] = gate + if gate.Executable.Bytecode == wire.Gate.Executable { + gateMap[wire.Gate.Executable] = gate break } } } } - if circuit, err = gkrtypes.NewCircuit(circuitInfo, gateMap); err != nil { - panic(err) - } + // Convert serializable circuit to registered circuit with both executables + circuit = gkrtypes.ConvertCircuit[*gkrtypes.GateBytecode, gkrtypes.BothExecutables]( + gkrtypes.Circuit[*gkrtypes.GateBytecode](circuitInfo), + func(bytecode *gkrtypes.GateBytecode) gkrtypes.BothExecutables { + if gate, ok := gateMap[bytecode]; ok { + return gate.Executable + } + panic("gate not found in cache") + }, + ) + c.circuits[path] = circuit - return + return circuit } -func (c *Cache) RegisterGate(name gkr.GateName, gate *gkrtypes.Gate) { +func (c *Cache) RegisterGate(name gkr.GateName, gate *gkrtypes.RegisteredGate) { if _, ok := c.gates[name]; ok { panic("gate already registered") } c.gates[name] = gate } -func (c *Cache) GetGate(name gkr.GateName) *gkrtypes.Gate { +func (c *Cache) GetGate(name gkr.GateName) *gkrtypes.RegisteredGate { if gate, ok := c.gates[name]; ok { return gate } diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 4d760386b0..f0b65b0d18 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -40,15 +40,6 @@ type ( // information through the circuit. Wires[GateExecutable any] []*Wire[GateExecutable] - SerializableWire struct { - CompiledGate *GateBytecode // Compiled gate (serializable) - Inputs []int - } - - SerializableCircuit Circuit[*GateBytecode] - - SerializableWires Wire[*GateBytecode] - Permutations struct { SortedInstances []int SortedWires []int @@ -60,7 +51,24 @@ type ( Bytecode *GateBytecode SnarkFriendly gkr.GateFunction } - RegisteredGate Gate[BothExecutables] + + // Type aliases for different circuit instantiations + + // Registered types (with both bytecode and SNARK-friendly executables) + RegisteredGate = Gate[BothExecutables] + RegisteredCircuit = Circuit[BothExecutables] + RegisteredWire = Wire[BothExecutables] + RegisteredWires = Wires[BothExecutables] + + // Serializable types (bytecode only, for native proving) + SerializableCircuit = Circuit[*GateBytecode] + SerializableWire = Wire[*GateBytecode] + SerializableWires = Wires[*GateBytecode] + + // Gadget types (gate functions only, for in-circuit verification) + GadgetCircuit = Circuit[gkr.GateFunction] + GadgetWire = Wire[gkr.GateFunction] + GadgetWires = Wires[gkr.GateFunction] ) // NewGate creates a new gate function with the given parameters: @@ -160,7 +168,7 @@ func (c Circuit[GateExecutable]) maxGateDegree() int { // MaxStackSize returns the maximum stack size needed by any gate evaluator in the circuit. // This is used to initialize universal gate evaluators that can handle any gate in the circuit. -func (c SerializableCircuit) MaxStackSize() int { +func MaxStackSize(c SerializableCircuit) int { maxSize := 0 for i := range c { if !c[i].IsInput() { @@ -188,23 +196,12 @@ func (c Circuit[GateExecutable]) MemoryRequirements(nbInstances int) []int { } // OutputsList for each wire, returns the set of indexes of wires it is input to. -// It also sets the NbUniqueOutputs fields, and sets the wire metadata. +// It also sets the NbUniqueOutputs fields. func (c Circuit[GateExecutable]) OutputsList() [][]int { - idGate := Identity() - var idGateTyped GateExecutable - switch idGateTyped.(type) { - case GateBytecode: - idGateTyped = idGate.Executable.Bytecode - case gkr.GateFunction: - idGateTyped = idGate.Executable.SnarkFriendly - } res := make([][]int, len(c)) for i := range c { res[i] = make([]int, 0) c[i].NbUniqueOutputs = 0 - if c[i].IsInput() { - c[i].Gate = idGateTyped - } } ins := make(map[int]struct{}, len(c)) for i := range c { @@ -430,3 +427,37 @@ func Mul2() *RegisteredGate { }}, }, 2, 2, -1, gnark.Curves()) } + +func ConvertCircuit[GateExecutable, TargetGateExecutable any](c Circuit[GateExecutable], gateConverter func(GateExecutable) TargetGateExecutable) Circuit[TargetGateExecutable] { + res := make(Circuit[TargetGateExecutable], len(c)) + for i := range c { + res[i] = Wire[TargetGateExecutable]{ + Gate: &Gate[TargetGateExecutable]{ + Executable: gateConverter(c[i].Gate.Executable), + NbIn: c[i].Gate.NbIn, + Degree: c[i].Gate.Degree, + SolvableVar: c[i].Gate.SolvableVar, + Curves: c[i].Gate.Curves, + }, + + Inputs: c[i].Inputs, + NbUniqueOutputs: c[i].NbUniqueOutputs, + } + } + + return res +} + +// ToSerializable converts a registered circuit (with both executables) to a serializable circuit (bytecode only). +func ToSerializable(c RegisteredCircuit) SerializableCircuit { + return ConvertCircuit(c, func(e BothExecutables) *GateBytecode { + return e.Bytecode + }) +} + +// ToGadget converts a registered circuit (with both executables) to a gadget circuit (gate functions only, for in-circuit verification). +func ToGadget(c RegisteredCircuit) GadgetCircuit { + return ConvertCircuit(c, func(e BothExecutables) gkr.GateFunction { + return e.SnarkFriendly + }) +} diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 107f3678c4..f6cb9deefb 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -14,12 +14,18 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" ) +// Type aliases for bytecode-based GKR types +type ( + Wire = gkrtypes.SerializableWire + Wires = gkrtypes.SerializableWires + Circuit = gkrtypes.SerializableCircuit +) + // The goal is to prove/verify evaluations of many instances of the same circuit // WireAssignment is assignment of values to the same wire across many instances of the circuit @@ -37,7 +43,7 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *gkrtypes.Wire { +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { return e.manager.wires[e.wireI] } @@ -56,7 +62,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a small_rational.SmallRa } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.manager.wires[e.wireI].Gate.Degree() + return 1 + e.manager.wires[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. @@ -99,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []small_rational.S e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Compiled(), len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -131,7 +137,7 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() *gkrtypes.Wire { +func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { return c.manager.wires[c.wireI] } @@ -219,7 +225,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []smal func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() - degGJ := 1 + wire.Gate.Degree() // guaranteed to be no smaller than the actual deg(gⱼ) + degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) nbGateIn := len(c.input) // Both E and wᵢ (the input wires and the eq table) are multilinear, thus @@ -352,10 +358,10 @@ type claimsManager struct { assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool - wires gkrtypes.Wires + wires Wires } -func newClaimsManager(wires []*gkrtypes.Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) manager.memPool = o.pool @@ -405,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Compiled(), len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) return res } @@ -417,7 +423,7 @@ func (m *claimsManager) deleteClaim(wire int) { type settings struct { pool *polynomial.Pool - sorted []*gkrtypes.Wire + sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -432,7 +438,7 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithSortedCircuit(sorted []*gkrtypes.Wire) Option { +func WithSortedCircuit(sorted []*Wire) Option { return func(options *settings) { options.sorted = sorted } @@ -444,7 +450,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -485,7 +491,7 @@ func setup(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat return o, err } -func ChallengeNames(sorted []*gkrtypes.Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge @@ -555,7 +561,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]small_r } // Prove consistency of the claimed assignment -func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { +func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return nil, err @@ -609,7 +615,7 @@ func Prove(c gkrinfo.Circuit, assignment WireAssignment, transcriptSettings fiat // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(c, assignment, transcriptSettings, options...) if err != nil { return err @@ -664,7 +670,7 @@ func Verify(c gkrinfo.Circuit, assignment WireAssignment, proof Proof, transcrip } // Complete the circuit evaluation from input values -func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { +func (a WireAssignment) Complete(wires Wires) WireAssignment { nbInstances := a.NumInstances() evaluators := make([]gateEvaluator, len(wires)) @@ -674,7 +680,7 @@ func (a WireAssignment) Complete(wires gkrtypes.Wires) WireAssignment { a[i] = make([]small_rational.SmallRational, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Compiled(), len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) } } @@ -736,7 +742,7 @@ func frToBigInts(dst []*big.Int, src []small_rational.SmallRational) { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode vars []small_rational.SmallRational frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -744,7 +750,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrinfo.CompiledGate, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -789,28 +795,28 @@ func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rat // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrinfo.OpAdd: + case gkrtypes.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpMul: + case gkrtypes.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpSub: + case gkrtypes.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrinfo.OpNeg: + case gkrtypes.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrinfo.OpMulAcc: + case gkrtypes.OpMulAcc: var prod small_rational.SmallRational prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrinfo.OpSumExp17: + case gkrtypes.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum small_rational.SmallRational sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -836,14 +842,14 @@ func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rat // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrinfo.CompiledGate + gate *gkrtypes.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrinfo.CompiledGate, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/small_rational/test_vector_gen.go b/internal/gkr/small_rational/test_vector_gen.go index 6f3b66959d..7fa9c4114d 100644 --- a/internal/gkr/small_rational/test_vector_gen.go +++ b/internal/gkr/small_rational/test_vector_gen.go @@ -186,7 +186,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.Circuit + Circuit gkrtypes.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -216,7 +216,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := cache.GetCircuit(filepath.Join(dir, info.Circuit)) + circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index 63ee8b5ff4..9e3199fa87 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -3,14 +3,13 @@ package gkrapi import ( "github.com/consensys/gnark/constraint/solver/gkrgates" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi/gkr" ) type API struct { - circuit gkrinfo.Circuit + circuit gkrtypes.RegisteredCircuit assignments gkrtypes.WireAssignment parentApi frontend.API } @@ -19,9 +18,15 @@ func frontendVarToInt(a gkr.Variable) int { return int(a) } -func (api *API) NamedGate(gate gkr.GateName, in ...gkr.Variable) gkr.Variable { - api.circuit = append(api.circuit, gkrinfo.Wire{ - Gate: string(gate), +func (api *API) NamedGate(gateName gkr.GateName, in ...gkr.Variable) gkr.Variable { + // Get the registered gate (with both executables) + registeredGate := gkrgates.Get(gateName) + if registeredGate == nil { + panic("gate not found: " + gateName) + } + + api.circuit = append(api.circuit, gkrtypes.RegisteredWire{ + Gate: registeredGate, Inputs: utils.Map(in, frontendVarToInt), }) api.assignments = append(api.assignments, nil) diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 6f0ca727e4..0382827987 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -15,7 +15,6 @@ import ( gkrbn254 "github.com/consensys/gnark/internal/gkr/bn254" gkrbw6633 "github.com/consensys/gnark/internal/gkr/bw6-633" gkrbw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" fiatshamir "github.com/consensys/gnark/std/fiat-shamir" @@ -25,7 +24,7 @@ import ( ) type circuitDataForSnark struct { - circuit gkrtypes.Circuit + circuit gkrtypes.GadgetCircuit assignments gkrtypes.WireAssignment } @@ -35,7 +34,7 @@ type InitialChallengeGetter func() []frontend.Variable // Circuit represents a GKR circuit. type Circuit struct { - circuit gkrinfo.Circuit // untyped circuit definition + circuit gkrtypes.RegisteredCircuit assignments gkrtypes.WireAssignment getInitialChallenges InitialChallengeGetter // optional getter for the initial Fiat-Shamir challenge ins []gkr.Variable @@ -53,7 +52,7 @@ type Circuit struct { // New creates a new GKR API func New(api frontend.API) (*API, error) { return &API{ - circuit: make(gkrinfo.Circuit, 0), + circuit: make(gkrtypes.RegisteredCircuit, 0), parentApi: api, }, nil } @@ -61,7 +60,12 @@ func New(api frontend.API) (*API, error) { // NewInput creates a new input variable. func (api *API) NewInput() gkr.Variable { i := len(api.circuit) - api.circuit = append(api.circuit, gkrinfo.Wire{}) + // Input wires have Identity gate with empty Inputs slice + api.circuit = append(api.circuit, gkrtypes.RegisteredWire{ + Gate: gkrtypes.Identity(), + Inputs: []int{}, + }) + api.assignments = append(api.assignments, nil) return gkr.Variable(i) } @@ -86,25 +90,30 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C hashName: fiatshamirHashName, } + // Convert registered circuit to serializable circuit (bytecode only) for blueprints + serializableCircuit := gkrtypes.ConvertCircuit(api.circuit, func(e gkrtypes.BothExecutables) *gkrtypes.GateBytecode { + return e.Bytecode + }) + // Dispatch to curve-specific factory curveID := utils.FieldToCurve(api.parentApi.Compiler().Field()) compiler := api.parentApi.Compiler() switch curveID { case ecc.BN254: - res.blueprints = gkrbn254.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + res.blueprints = gkrbn254.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) case ecc.BLS12_377: - res.blueprints = gkrbls12377.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + res.blueprints = gkrbls12377.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) case ecc.BLS12_381: - res.blueprints = gkrbls12381.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + res.blueprints = gkrbls12381.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) case ecc.BLS24_315: - res.blueprints = gkrbls24315.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + res.blueprints = gkrbls24315.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) case ecc.BLS24_317: - res.blueprints = gkrbls24317.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + res.blueprints = gkrbls24317.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) case ecc.BW6_633: - res.blueprints = gkrbw6633.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + res.blueprints = gkrbw6633.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) case ecc.BW6_761: - res.blueprints = gkrbw6761.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + res.blueprints = gkrbw6761.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) default: return nil, fmt.Errorf("unsupported curve: %s", curveID) } @@ -298,20 +307,19 @@ func (c *Circuit) verify(api frontend.API, initialChallenges []frontend.Variable return gadget.Verify(api, forSnark.circuit, forSnark.assignments, proof, fiatshamir.WithHash(hsh, initialChallenges...), gadget.WithSortedCircuit(forSnarkSorted)) } -func newCircuitDataForSnark(curve ecc.ID, untypedCircuit gkrinfo.Circuit, assignment gkrtypes.WireAssignment) (circuitDataForSnark, error) { - circuit, err := gkrtypes.NewCircuit(untypedCircuit, gkrgates.Get) - if err != nil { - return circuitDataForSnark{}, fmt.Errorf("failed to convert GKR info to circuit: %w", err) - } +func newCircuitDataForSnark(curve ecc.ID, registeredCircuit gkrtypes.RegisteredCircuit, assignment gkrtypes.WireAssignment) (circuitDataForSnark, error) { + // Convert registered circuit to gadget circuit (using GateFunction) + snarkCircuit := gkrtypes.ToGadget(registeredCircuit) - for i := range circuit { - if !circuit[i].Gate.SupportsCurve(curve) { - return circuitDataForSnark{}, fmt.Errorf("gate \"%s\" not usable over curve \"%s\"", untypedCircuit[i].Gate, curve) + // Check curve support + for i := range snarkCircuit { + if !snarkCircuit[i].Gate.SupportsCurve(curve) { + return circuitDataForSnark{}, fmt.Errorf("gate not usable over curve \"%s\"", curve) } } return circuitDataForSnark{ - circuit: circuit, + circuit: snarkCircuit, assignments: assignment, }, nil } From 73d6d922c99b81733df7aba328afec3dcea5c05d Mon Sep 17 00:00:00 2001 From: Tabaie Date: Fri, 23 Jan 2026 00:11:49 -0600 Subject: [PATCH 129/251] fix: json friendly circuit type --- internal/gkr/gkrtesting/gkrtesting.go | 52 ++++++++++++++------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 7947657d41..8baa62c80a 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -51,6 +51,15 @@ func NewCache() *Cache { } } +// JSONWire is the JSON serialization format for circuit wires (gate name + inputs) +type JSONWire struct { + Gate *gkr.GateName `json:"gate"` // gate name, null for input wires + Inputs []int `json:"inputs"` // indices of input wires +} + +// JSONCircuit is the JSON serialization format for circuits +type JSONCircuit []JSONWire + func (c *Cache) GetCircuit(path string) gkrtypes.RegisteredCircuit { path, err := filepath.Abs(path) if err != nil { @@ -66,35 +75,30 @@ func (c *Cache) GetCircuit(path string) gkrtypes.RegisteredCircuit { if bytes, err = os.ReadFile(path); err != nil { panic(err) } - var circuitInfo gkrtypes.SerializableCircuit - if err = json.Unmarshal(bytes, &circuitInfo); err != nil { + + // Unmarshal from JSON format (gate names as strings) + var jsonCircuit JSONCircuit + if err = json.Unmarshal(bytes, &jsonCircuit); err != nil { panic(err) } - // Build gate map from bytecode to registered gates - gateMap := make(map[*gkrtypes.GateBytecode]*gkrtypes.RegisteredGate) - for _, wire := range circuitInfo { - if wire.Gate.Executable != nil { - // For each bytecode gate, find the matching registered gate from our cache - for _, gate := range c.gates { - if gate.Executable.Bytecode == wire.Gate.Executable { - gateMap[wire.Gate.Executable] = gate - break - } - } + // Convert JSON format to RegisteredCircuit + circuit = make(gkrtypes.RegisteredCircuit, len(jsonCircuit)) + for i, wJSON := range jsonCircuit { + var gate *gkrtypes.RegisteredGate + if wJSON.Gate == nil { + // Input wire - use identity gate + gate = gkrtypes.Identity() + } else { + // Look up gate by name in cache + gate = c.GetGate(*wJSON.Gate) } - } - // Convert serializable circuit to registered circuit with both executables - circuit = gkrtypes.ConvertCircuit[*gkrtypes.GateBytecode, gkrtypes.BothExecutables]( - gkrtypes.Circuit[*gkrtypes.GateBytecode](circuitInfo), - func(bytecode *gkrtypes.GateBytecode) gkrtypes.BothExecutables { - if gate, ok := gateMap[bytecode]; ok { - return gate.Executable - } - panic("gate not found in cache") - }, - ) + circuit[i] = gkrtypes.RegisteredWire{ + Gate: gate, + Inputs: wJSON.Inputs, + } + } c.circuits[path] = circuit From e075fcc683869ede48222d79811757eb71cf0071 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Fri, 23 Jan 2026 00:33:04 -0600 Subject: [PATCH 130/251] fix: small things --- constraint/solver/gkrgates/registry.go | 2 +- constraint/solver/gkrgates/registry_test.go | 4 +- .../backend/template/gkr/blueprint.go.tmpl | 2 +- .../backend/template/gkr/gate_testing.go.tmpl | 5 +- .../backend/template/gkr/gkr.go.tmpl | 6 +- internal/gkr/bls12-377/blueprint.go | 2 +- internal/gkr/bls12-377/gate_testing.go | 3 +- internal/gkr/bls12-377/gkr.go | 6 +- internal/gkr/bls12-381/blueprint.go | 2 +- internal/gkr/bls12-381/gate_testing.go | 3 +- internal/gkr/bls12-381/gkr.go | 6 +- internal/gkr/bls24-315/blueprint.go | 2 +- internal/gkr/bls24-315/gate_testing.go | 3 +- internal/gkr/bls24-315/gkr.go | 6 +- internal/gkr/bls24-317/blueprint.go | 2 +- internal/gkr/bls24-317/gate_testing.go | 3 +- internal/gkr/bls24-317/gkr.go | 6 +- internal/gkr/bn254/blueprint.go | 2 +- internal/gkr/bn254/gate_testing.go | 5 +- internal/gkr/bn254/gkr.go | 6 +- internal/gkr/bw6-633/blueprint.go | 2 +- internal/gkr/bw6-633/gate_testing.go | 3 +- internal/gkr/bw6-633/gkr.go | 6 +- internal/gkr/bw6-761/blueprint.go | 2 +- internal/gkr/bw6-761/gate_testing.go | 3 +- internal/gkr/bw6-761/gkr.go | 6 +- internal/gkr/gkr.go | 70 ++++++++++++++----- internal/gkr/gkr_test.go | 6 +- internal/gkr/gkrtypes/types.go | 45 ++---------- internal/gkr/small_rational/gkr.go | 6 +- std/gkrapi/api.go | 3 +- std/gkrapi/compile.go | 63 +++++++---------- 32 files changed, 137 insertions(+), 154 deletions(-) diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index c3e9858ee4..99be1ecf98 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -177,7 +177,7 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { } for _, curve := range curvesForTesting { - gateVer, err := newGateTester(g.Executable.Bytecode, g.NbIn, curve) + gateVer, err := newGateTester(g.Evaluate.Bytecode, g.NbIn, curve) if err != nil { return err } diff --git a/constraint/solver/gkrgates/registry_test.go b/constraint/solver/gkrgates/registry_test.go index 128d168c7e..bb046f4360 100644 --- a/constraint/solver/gkrgates/registry_test.go +++ b/constraint/solver/gkrgates/registry_test.go @@ -48,9 +48,9 @@ func TestRegister(t *testing.T) { return api.Add(x[0], x[1], x[2]) }, 3, 1) - testGate("mul2", gkrtypes.Mul2().Executable.SnarkFriendly, 2, 2) + testGate("mul2", gkrtypes.Mul2().Evaluate.SnarkFriendly, 2, 2) - testGate("mimc", gkrtesting.NewCache().GetGate("mimc").Executable.SnarkFriendly, 2, 7) + testGate("mimc", gkrtesting.NewCache().GetGate("mimc").Evaluate.SnarkFriendly, 2, 7) testGate("sub2PlusOne", func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { return api.Sub( diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 5522355683..4f14621536 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -85,7 +85,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } } return ce diff --git a/internal/generator/backend/template/gkr/gate_testing.go.tmpl b/internal/generator/backend/template/gkr/gate_testing.go.tmpl index 235608f7bc..24888d6bce 100644 --- a/internal/generator/backend/template/gkr/gate_testing.go.tmpl +++ b/internal/generator/backend/template/gkr/gate_testing.go.tmpl @@ -1,7 +1,6 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/std/gkrapi/gkr" @@ -19,7 +18,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrtypes.GateBytecode, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, @@ -166,7 +165,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { x := make({{.FieldPackageName}}.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 517de25c79..8fa0b6e6bc 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -98,7 +98,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []{{ .ElementType e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -405,7 +405,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Evaluate, len(res.input), m.memPool) return res } @@ -675,7 +675,7 @@ func (a WireAssignment) Complete(wires Wires) WireAssignment { a[i] = make([]{{ .ElementType }}, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Evaluate, len(wires[i].Inputs)) } } diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 95294d7bf4..3d5303df7e 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -92,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } } return ce diff --git a/internal/gkr/bls12-377/gate_testing.go b/internal/gkr/bls12-377/gate_testing.go index 99fe18756b..dd4e95117d 100644 --- a/internal/gkr/bls12-377/gate_testing.go +++ b/internal/gkr/bls12-377/gate_testing.go @@ -9,7 +9,6 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "slices" @@ -24,7 +23,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrtypes.GateBytecode, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index df6860f043..5d8eeca4d3 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -105,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -411,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Evaluate, len(res.input), m.memPool) return res } @@ -680,7 +680,7 @@ func (a WireAssignment) Complete(wires Wires) WireAssignment { a[i] = make([]fr.Element, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Evaluate, len(wires[i].Inputs)) } } diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 101a4539ac..98440ae948 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -92,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } } return ce diff --git a/internal/gkr/bls12-381/gate_testing.go b/internal/gkr/bls12-381/gate_testing.go index 4cc1779a8f..fe136861e4 100644 --- a/internal/gkr/bls12-381/gate_testing.go +++ b/internal/gkr/bls12-381/gate_testing.go @@ -9,7 +9,6 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "slices" @@ -24,7 +23,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrtypes.GateBytecode, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 799cb00714..d7211aa81f 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -105,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -411,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Evaluate, len(res.input), m.memPool) return res } @@ -680,7 +680,7 @@ func (a WireAssignment) Complete(wires Wires) WireAssignment { a[i] = make([]fr.Element, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Evaluate, len(wires[i].Inputs)) } } diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index b38c4a4b6a..a1b3d966b2 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -92,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } } return ce diff --git a/internal/gkr/bls24-315/gate_testing.go b/internal/gkr/bls24-315/gate_testing.go index 5b7e29fd1d..12142148d5 100644 --- a/internal/gkr/bls24-315/gate_testing.go +++ b/internal/gkr/bls24-315/gate_testing.go @@ -9,7 +9,6 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "slices" @@ -24,7 +23,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrtypes.GateBytecode, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 80e6717a47..6e6f3d1fa0 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -105,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -411,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Evaluate, len(res.input), m.memPool) return res } @@ -680,7 +680,7 @@ func (a WireAssignment) Complete(wires Wires) WireAssignment { a[i] = make([]fr.Element, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Evaluate, len(wires[i].Inputs)) } } diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 7053239a9b..07b3abea85 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -92,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } } return ce diff --git a/internal/gkr/bls24-317/gate_testing.go b/internal/gkr/bls24-317/gate_testing.go index b66e623b91..1e3ad02681 100644 --- a/internal/gkr/bls24-317/gate_testing.go +++ b/internal/gkr/bls24-317/gate_testing.go @@ -9,7 +9,6 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "slices" @@ -24,7 +23,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrtypes.GateBytecode, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 42c6ee6996..59520ff69f 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -105,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -411,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Evaluate, len(res.input), m.memPool) return res } @@ -680,7 +680,7 @@ func (a WireAssignment) Complete(wires Wires) WireAssignment { a[i] = make([]fr.Element, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Evaluate, len(wires[i].Inputs)) } } diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 46f7684b14..5b6baeab50 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -92,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } } return ce diff --git a/internal/gkr/bn254/gate_testing.go b/internal/gkr/bn254/gate_testing.go index d2e0cd77c4..fd18ef164c 100644 --- a/internal/gkr/bn254/gate_testing.go +++ b/internal/gkr/bn254/gate_testing.go @@ -9,7 +9,6 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "slices" @@ -24,7 +23,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrtypes.GateBytecode, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, @@ -153,7 +152,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index bc48d3a481..4ca1a7e71f 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -105,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -411,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Evaluate, len(res.input), m.memPool) return res } @@ -680,7 +680,7 @@ func (a WireAssignment) Complete(wires Wires) WireAssignment { a[i] = make([]fr.Element, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Evaluate, len(wires[i].Inputs)) } } diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index d97dc48d65..1c141a8a0f 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -92,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } } return ce diff --git a/internal/gkr/bw6-633/gate_testing.go b/internal/gkr/bw6-633/gate_testing.go index c3b41ffe2c..d4f5957b33 100644 --- a/internal/gkr/bw6-633/gate_testing.go +++ b/internal/gkr/bw6-633/gate_testing.go @@ -9,7 +9,6 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "slices" @@ -24,7 +23,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrtypes.GateBytecode, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 8779aef0d2..824ad4385b 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -105,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -411,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Evaluate, len(res.input), m.memPool) return res } @@ -680,7 +680,7 @@ func (a WireAssignment) Complete(wires Wires) WireAssignment { a[i] = make([]fr.Element, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Evaluate, len(wires[i].Inputs)) } } diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 873c103ab8..3e206609d8 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -92,7 +92,7 @@ func (b *BlueprintSolve) initialize() { for wI := range b.Circuit { w := &b.Circuit[wI] if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Executable, len(w.Inputs)) + ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } } return ce diff --git a/internal/gkr/bw6-761/gate_testing.go b/internal/gkr/bw6-761/gate_testing.go index a56596a5e2..0a4686547a 100644 --- a/internal/gkr/bw6-761/gate_testing.go +++ b/internal/gkr/bw6-761/gate_testing.go @@ -9,7 +9,6 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "slices" @@ -24,7 +23,7 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrinfo.CompiledGate, nbIn int) *GateTester { +func NewGateTester(g *gkrtypes.GateBytecode, nbIn int) *GateTester { return &GateTester{ evaluator: newGateEvaluator(g, nbIn), nbIn: nbIn, diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index debe95a206..8c6d7fd4bf 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -105,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -411,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Evaluate, len(res.input), m.memPool) return res } @@ -680,7 +680,7 @@ func (a WireAssignment) Complete(wires Wires) WireAssignment { a[i] = make([]fr.Element, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Evaluate, len(wires[i].Inputs)) } } diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index faffa8d303..df1fae885d 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -7,18 +7,56 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/utils" fiatshamir "github.com/consensys/gnark/std/fiat-shamir" - "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/consensys/gnark/std/polynomial" ) -// Snark-Friendly circuit types - +// Type aliases for gadget circuit types type ( - Wire gkrtypes.Wire[gkr.GateFunction] - Circuit gkrtypes.Circuit[gkr.GateFunction] + Wire = gkrtypes.GadgetWire + Wires = gkrtypes.GadgetWires + Circuit = gkrtypes.GadgetCircuit ) +// Permutations describes how to reorder wires and instances +type Permutations struct { + SortedInstances []int + SortedWires []int + InstancesPermutation []int + WiresPermutation []int +} + +// WireAssignment is assignment of values to the same wire across many instances of the circuit +type WireAssignment []polynomial.MultiLin + +func (a WireAssignment) Permute(p Permutations) { + utils.Permute(a, p.WiresPermutation) + for i := range a { + if a[i] != nil { + utils.Permute(a[i], p.InstancesPermutation) + } + } +} + +func (a WireAssignment) NbInstances() int { + for _, aW := range a { + if aW != nil { + return len(aW) + } + } + panic("empty assignment") +} + +func (a WireAssignment) NbVars() int { + for _, aW := range a { + if aW != nil { + return aW.NumVars() + } + } + panic("empty assignment") +} + // A SNARK gadget capable of verifying a GKR proof // The goal is to prove/verify evaluations of many instances of the same circuit. @@ -106,16 +144,16 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(api frontend.API, a fron } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.getWire().Gate.Degree() + return 1 + e.getWire().Gate.Degree } type claimsManager struct { claims []*eqTimesGateEvalSumcheckLazyClaims - assignment gkrtypes.WireAssignment - wires gkrtypes.Wires + assignment WireAssignment + wires Wires } -func newClaimsManager(wires gkrtypes.Wires, assignment gkrtypes.WireAssignment) (claims claimsManager) { +func newClaimsManager(wires Wires, assignment WireAssignment) (claims claimsManager) { claims.assignment = assignment claims.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) claims.wires = wires @@ -163,7 +201,7 @@ func WithSortedCircuit(sorted []*Wire) Option { } } -func setup(api frontend.API, c gkrtypes.Circuit, assignment gkrtypes.WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(api frontend.API, c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -206,7 +244,7 @@ func ProofSize(c Circuit, logNbInstances int) int { return nbUniqueInputs + nbPartialEvalPolys*logNbInstances } -func ChallengeNames(sorted []*gkrtypes.Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(sorted Wires, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge @@ -275,7 +313,7 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) (challenge // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(api frontend.API, c Circuit, assignment gkrtypes.WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { +func Verify(api frontend.API, c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { o, err := setup(api, c, assignment, transcriptSettings, options...) if err != nil { return err @@ -350,11 +388,11 @@ func (p Proof) Serialize() []frontend.Variable { // ComputeLogNbInstances derives n such that the number of instances is 2ⁿ // from the size of the proof and the circuit structure. // It is used in proof deserialization. -func ComputeLogNbInstances(wires []*gkrtypes.Wire, serializedProofLen int) int { +func ComputeLogNbInstances(wires Wires, serializedProofLen int) int { partialEvalElemsPerVar := 0 for _, w := range wires { if !w.NoProof() { - partialEvalElemsPerVar += w.Gate.Degree() + 1 + partialEvalElemsPerVar += w.Gate.Degree + 1 } serializedProofLen -= w.NbUniqueOutputs } @@ -373,7 +411,7 @@ func (r *variablesReader) hasNextN(n int) bool { return len(*r) >= n } -func DeserializeProof(sorted []*gkrtypes.Wire, serializedProof []frontend.Variable) (Proof, error) { +func DeserializeProof(sorted Wires, serializedProof []frontend.Variable) (Proof, error) { proof := make(Proof, len(sorted)) logNbInstances := ComputeLogNbInstances(sorted, len(serializedProof)) @@ -382,7 +420,7 @@ func DeserializeProof(sorted []*gkrtypes.Wire, serializedProof []frontend.Variab if !wI.NoProof() { proof[i].PartialSumPolys = make([]polynomial.Polynomial, logNbInstances) for j := range proof[i].PartialSumPolys { - proof[i].PartialSumPolys[j] = reader.nextN(wI.Gate.Degree() + 1) + proof[i].PartialSumPolys[j] = reader.nextN(wI.Gate.Degree + 1) } } proof[i].FinalEvalProof = reader.nextN(wI.NbUniqueInputs()) diff --git a/internal/gkr/gkr_test.go b/internal/gkr/gkr_test.go index 02c6d6cac2..a3c115f8ee 100644 --- a/internal/gkr/gkr_test.go +++ b/internal/gkr/gkr_test.go @@ -133,9 +133,9 @@ func (c *GkrVerifierCircuit) Define(api frontend.API) error { return Verify(api, testCase.Circuit, assignment, proof, fiatshamir.WithHash(hsh)) } -func makeInOutAssignment(c gkrtypes.Circuit, inputValues [][]frontend.Variable, outputValues [][]frontend.Variable) gkrtypes.WireAssignment { +func makeInOutAssignment(c Circuit, inputValues [][]frontend.Variable, outputValues [][]frontend.Variable) WireAssignment { sorted := c.TopologicalSort() - res := make(gkrtypes.WireAssignment, len(c)) + res := make(WireAssignment, len(c)) inI, outI := 0, 0 for wI, w := range sorted { if w.IsInput() { @@ -156,7 +156,7 @@ func fillWithBlanks(slice [][]frontend.Variable, size int) { } type TestCase struct { - Circuit gkrtypes.Circuit + Circuit Circuit Hash HashDescription Proof Proof Input [][]frontend.Variable diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index f0b65b0d18..869103f9cc 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -21,7 +21,7 @@ type ( // A Gate is a low-degree multivariate polynomial Gate[GateExecutable any] struct { - Executable GateExecutable + Evaluate GateExecutable NbIn int // number of inputs Degree int // total Degree of the polynomial SolvableVar int // if there is a variable whose value can be uniquely determined from the value of the gate and the other inputs, its index, -1 otherwise @@ -40,13 +40,6 @@ type ( // information through the circuit. Wires[GateExecutable any] []*Wire[GateExecutable] - Permutations struct { - SortedInstances []int - SortedWires []int - InstancesPermutation []int - WiresPermutation []int - } - BothExecutables struct { Bytecode *GateBytecode SnarkFriendly gkr.GateFunction @@ -81,7 +74,7 @@ type ( func NewGate(f gkr.GateFunction, compiled *GateBytecode, nbIn int, degree int, solvableVar int, curves []ecc.ID) *RegisteredGate { return &RegisteredGate{ - Executable: BothExecutables{ + Evaluate: BothExecutables{ Bytecode: compiled, SnarkFriendly: f, }, @@ -172,7 +165,7 @@ func MaxStackSize(c SerializableCircuit) int { maxSize := 0 for i := range c { if !c[i].IsInput() { - gate := c[i].Gate.Executable + gate := c[i].Gate.Evaluate nbIn := len(c[i].Inputs) stackSize := gate.NbConstants() + nbIn + len(gate.Instructions) maxSize = max(maxSize, stackSize) @@ -265,36 +258,6 @@ func (c Circuit[GateExecutable]) MaxGateNbIn() int { return res } -// WireAssignment is assignment of values to the same wire across many instances of the circuit -type WireAssignment []polynomial.MultiLin - -func (a WireAssignment) Permute(p Permutations) { - utils.Permute(a, p.WiresPermutation) - for i := range a { - if a[i] != nil { - utils.Permute(a[i], p.InstancesPermutation) - } - } -} - -func (a WireAssignment) NbInstances() int { - for _, aW := range a { - if aW != nil { - return len(aW) - } - } - panic("empty assignment") -} - -func (a WireAssignment) NbVars() int { - for _, aW := range a { - if aW != nil { - return aW.NumVars() - } - } - panic("empty assignment") -} - // ProofSize computes how large the proof for a circuit would be. It needs NbUniqueOutputs to be set. func (c Circuit[GateExecutable]) ProofSize(logNbInstances int) int { nbUniqueInputs := 0 @@ -433,7 +396,7 @@ func ConvertCircuit[GateExecutable, TargetGateExecutable any](c Circuit[GateExec for i := range c { res[i] = Wire[TargetGateExecutable]{ Gate: &Gate[TargetGateExecutable]{ - Executable: gateConverter(c[i].Gate.Executable), + Evaluate: gateConverter(c[i].Gate.Evaluate), NbIn: c[i].Gate.NbIn, Degree: c[i].Gate.Degree, SolvableVar: c[i].Gate.SolvableVar, diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index f6cb9deefb..e26c628b93 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -105,7 +105,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []small_rational.S e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) } - evaluator := newGateEvaluator(wire.Gate.Executable, len(wire.Inputs)) + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) } @@ -411,7 +411,7 @@ func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Executable, len(res.input), m.memPool) + res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Evaluate, len(res.input), m.memPool) return res } @@ -680,7 +680,7 @@ func (a WireAssignment) Complete(wires Wires) WireAssignment { a[i] = make([]small_rational.SmallRational, nbInstances) } if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Executable, len(wires[i].Inputs)) + evaluators[i] = newGateEvaluator(wires[i].Gate.Evaluate, len(wires[i].Inputs)) } } diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index 9e3199fa87..d837528d2c 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -3,6 +3,7 @@ package gkrapi import ( "github.com/consensys/gnark/constraint/solver/gkrgates" "github.com/consensys/gnark/frontend" + gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi/gkr" @@ -10,7 +11,7 @@ import ( type API struct { circuit gkrtypes.RegisteredCircuit - assignments gkrtypes.WireAssignment + assignments gadget.WireAssignment parentApi frontend.API } diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 0382827987..4f155d22b5 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/constraint/solver/gkrgates" "github.com/consensys/gnark/frontend" gadget "github.com/consensys/gnark/internal/gkr" gkrbls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" @@ -25,7 +24,7 @@ import ( type circuitDataForSnark struct { circuit gkrtypes.GadgetCircuit - assignments gkrtypes.WireAssignment + assignments gadget.WireAssignment } // The InitialChallengeGetter provides a one-time initial Fiat-Shamir challenge for the GKR prover. @@ -35,7 +34,7 @@ type InitialChallengeGetter func() []frontend.Variable // Circuit represents a GKR circuit. type Circuit struct { circuit gkrtypes.RegisteredCircuit - assignments gkrtypes.WireAssignment + assignments gadget.WireAssignment getInitialChallenges InitialChallengeGetter // optional getter for the initial Fiat-Shamir challenge ins []gkr.Variable outs []gkr.Variable @@ -85,7 +84,7 @@ func WithInitialChallenge(getInitialChallenge InitialChallengeGetter) CompileOpt func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*Circuit, error) { res := Circuit{ circuit: api.circuit, - assignments: make(gkrtypes.WireAssignment, len(api.circuit)), + assignments: make(gadget.WireAssignment, len(api.circuit)), api: api.parentApi, hashName: fiatshamirHashName, } @@ -220,14 +219,22 @@ func (c *Circuit) finalize(api frontend.API) error { // Set NbInstances in the solve solveBlueprint (used for pre-allocation and proof size computation) c.blueprints.Solve.SetNbInstances(uint32(nbPaddedInstances)) + curve := utils.FieldToCurve(api.Compiler().Field()) + + // Convert registered circuit to gadget circuit (using GateFunction) + gadgetCircuit := gkrtypes.ToGadget(c.circuit) + + // Check curve support + for i := range gadgetCircuit { + if !gadgetCircuit[i].Gate.SupportsCurve(curve) { + return fmt.Errorf("gate not usable over curve \"%s\"", curve) + } + } + // if the circuit consists of only one instance, directly solve the circuit if len(c.assignments[c.ins[0]]) == 1 { - circuit, err := gkrtypes.NewCircuit(c.circuit, gkrgates.Get) - if err != nil { - return fmt.Errorf("failed to convert GKR info to circuit: %w", err) - } - gateIn := make([]frontend.Variable, circuit.MaxGateNbIn()) - for wI, w := range circuit { + gateIn := make([]frontend.Variable, gadgetCircuit.MaxGateNbIn()) + for wI, w := range gadgetCircuit { if w.IsInput() { continue } @@ -245,7 +252,7 @@ func (c *Circuit) finalize(api frontend.API) error { } if c.getInitialChallenges != nil { - return c.verify(api, c.getInitialChallenges()) + return c.verify(api, gadgetCircuit, c.getInitialChallenges()) } // default initial challenge is a commitment to all input and output values @@ -258,17 +265,13 @@ func (c *Circuit) finalize(api frontend.API) error { } multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { - return c.verify(api, []frontend.Variable{commitment}) + return c.verify(api, nil, []frontend.Variable{commitment}) }, insOuts...) return nil } -func (c *Circuit) verify(api frontend.API, initialChallenges []frontend.Variable) error { - forSnark, err := newCircuitDataForSnark(utils.FieldToCurve(api.Compiler().Field()), c.circuit, c.assignments) - if err != nil { - return fmt.Errorf("failed to create circuit data for snark: %w", err) - } +func (c *Circuit) verify(api frontend.API, circuit gkrtypes.GadgetCircuit, initialChallenges []frontend.Variable) error { compiler := api.Compiler() @@ -291,9 +294,12 @@ func (c *Circuit) verify(api frontend.API, initialChallenges []frontend.Variable proofSerialized[i] = compiler.InternalVariable(wireID) } - var proof gadget.Proof + var ( + proof gadget.Proof + err error + ) - forSnarkSorted := utils.SliceOfRefs(forSnark.circuit) + forSnarkSorted := utils.SliceOfRefs(circuit) if proof, err = gadget.DeserializeProof(forSnarkSorted, proofSerialized); err != nil { return err @@ -304,24 +310,7 @@ func (c *Circuit) verify(api frontend.API, initialChallenges []frontend.Variable return err } - return gadget.Verify(api, forSnark.circuit, forSnark.assignments, proof, fiatshamir.WithHash(hsh, initialChallenges...), gadget.WithSortedCircuit(forSnarkSorted)) -} - -func newCircuitDataForSnark(curve ecc.ID, registeredCircuit gkrtypes.RegisteredCircuit, assignment gkrtypes.WireAssignment) (circuitDataForSnark, error) { - // Convert registered circuit to gadget circuit (using GateFunction) - snarkCircuit := gkrtypes.ToGadget(registeredCircuit) - - // Check curve support - for i := range snarkCircuit { - if !snarkCircuit[i].Gate.SupportsCurve(curve) { - return circuitDataForSnark{}, fmt.Errorf("gate not usable over curve \"%s\"", curve) - } - } - - return circuitDataForSnark{ - circuit: snarkCircuit, - assignments: assignment, - }, nil + return gadget.Verify(api, circuit, c.assignments, proof, fiatshamir.WithHash(hsh, initialChallenges...), gadget.WithSortedCircuit(forSnarkSorted)) } // GetValue is a debugging utility returning the value of variable v at instance i. From bdb7b7de68cb87eb8f827adabd95c63cd19bc51a Mon Sep 17 00:00:00 2001 From: Tabaie Date: Fri, 23 Jan 2026 00:52:49 -0600 Subject: [PATCH 131/251] fix: test compile --- .../backend/template/gkr/gkr.test.go.tmpl | 48 +++++++++---------- internal/gkr/bls12-377/gate_testing.go | 2 +- internal/gkr/bls12-377/gkr_test.go | 48 +++++++++---------- internal/gkr/bls12-381/gate_testing.go | 2 +- internal/gkr/bls12-381/gkr_test.go | 48 +++++++++---------- internal/gkr/bls24-315/gate_testing.go | 2 +- internal/gkr/bls24-315/gkr_test.go | 48 +++++++++---------- internal/gkr/bls24-317/gate_testing.go | 2 +- internal/gkr/bls24-317/gkr_test.go | 48 +++++++++---------- internal/gkr/bn254/gkr_test.go | 48 +++++++++---------- internal/gkr/bw6-633/gate_testing.go | 2 +- internal/gkr/bw6-633/gkr_test.go | 48 +++++++++---------- internal/gkr/bw6-761/gate_testing.go | 2 +- internal/gkr/bw6-761/gkr_test.go | 48 +++++++++---------- internal/gkr/gkr_test.go | 8 +--- .../gkr/gkrtypes/topological_sort_test.go | 34 ++++--------- internal/gkr/gkrtypes/types.go | 48 ++++++++----------- 17 files changed, 227 insertions(+), 259 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index a244b97694..c403313461 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -28,44 +28,44 @@ func TestNoGateTwoInstances(t *testing.T) { } func TestNoGate(t *testing.T) { - test(t, gkrtypes.Circuit{ {} }) + test(t, Circuit{ {} }) } func TestSingleAddGate(t *testing.T) { - test(t, gkrtypes.Circuit{ {}, {}, { - Gate: gkrtypes.Add2(), + test(t, Circuit{ {}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), Inputs: []int{0, 1}, } }) } func TestSingleMulGate(t *testing.T) { - test(t, gkrtypes.Circuit{ {}, {}, { - Gate: gkrtypes.Mul2(), + test(t, Circuit{ {}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{0, 1}, }}) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, gkrtypes.Circuit{ {}, + test(t, Circuit{ {}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, gkrtypes.Circuit{ {}, + test(t, Circuit{ {}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{1}, }}) } @@ -73,11 +73,11 @@ func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { func TestAPowNTimesBCircuit(t *testing.T) { const N = 10 - c := make(gkrtypes.Circuit, N+2) + c := make(Circuit, N+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: gkrtypes.Mul2(), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{i - 1, 0}, } } @@ -86,11 +86,11 @@ func TestAPowNTimesBCircuit(t *testing.T) { } func TestSingleMimcCipherGate(t *testing.T) { - test(t, gkrtypes.Circuit{ + test(t, Circuit{ {}, {}, { Inputs: []int{0, 1}, - Gate: cache.GetGate("mimc"), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), }, }) } @@ -104,8 +104,8 @@ func TestMimc(t *testing.T) { } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := gkrtypes.Circuit{gkrtypes.Wire{ - Gate: gkrtypes.Identity(), + circuit := Circuit{Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), NbUniqueOutputs: 2, }} @@ -162,7 +162,7 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.Circuit) { +func test(t *testing.T, circuit Circuit) { wireRefs := utils.References(circuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -216,7 +216,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]{{ .ElementType }}) { - c := gkrtypes.Circuit{ + c := Circuit{ {}, } @@ -231,12 +231,12 @@ func testNoGate(t *testing.T, inputAssignments ...[]{{ .ElementType }}) { assert.NoError(t, err, "proof rejected") } -func mimcCircuit(numRounds int) gkrtypes.Circuit { - c := make(gkrtypes.Circuit, numRounds+2) +func mimcCircuit(numRounds int) Circuit { + c := make(Circuit, numRounds+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: cache.GetGate("mimc"), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } diff --git a/internal/gkr/bls12-377/gate_testing.go b/internal/gkr/bls12-377/gate_testing.go index dd4e95117d..44252c1fa5 100644 --- a/internal/gkr/bls12-377/gate_testing.go +++ b/internal/gkr/bls12-377/gate_testing.go @@ -152,7 +152,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index fdbcec9776..72fc042c7b 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -34,44 +34,44 @@ func TestNoGateTwoInstances(t *testing.T) { } func TestNoGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}}) + test(t, Circuit{{}}) } func TestSingleAddGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}, {}, { - Gate: gkrtypes.Add2(), + test(t, Circuit{{}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), Inputs: []int{0, 1}, }}) } func TestSingleMulGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}, {}, { - Gate: gkrtypes.Mul2(), + test(t, Circuit{{}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{0, 1}, }}) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, gkrtypes.Circuit{{}, + test(t, Circuit{{}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, gkrtypes.Circuit{{}, + test(t, Circuit{{}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{1}, }}) } @@ -79,11 +79,11 @@ func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { func TestAPowNTimesBCircuit(t *testing.T) { const N = 10 - c := make(gkrtypes.Circuit, N+2) + c := make(Circuit, N+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: gkrtypes.Mul2(), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{i - 1, 0}, } } @@ -92,11 +92,11 @@ func TestAPowNTimesBCircuit(t *testing.T) { } func TestSingleMimcCipherGate(t *testing.T) { - test(t, gkrtypes.Circuit{ + test(t, Circuit{ {}, {}, { Inputs: []int{0, 1}, - Gate: cache.GetGate("mimc"), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), }, }) } @@ -110,8 +110,8 @@ func TestMimc(t *testing.T) { } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := gkrtypes.Circuit{gkrtypes.Wire{ - Gate: gkrtypes.Identity(), + circuit := Circuit{Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), NbUniqueOutputs: 2, }} @@ -168,7 +168,7 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.Circuit) { +func test(t *testing.T, circuit Circuit) { wireRefs := utils.References(circuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -222,7 +222,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := gkrtypes.Circuit{ + c := Circuit{ {}, } @@ -237,12 +237,12 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { assert.NoError(t, err, "proof rejected") } -func mimcCircuit(numRounds int) gkrtypes.Circuit { - c := make(gkrtypes.Circuit, numRounds+2) +func mimcCircuit(numRounds int) Circuit { + c := make(Circuit, numRounds+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: cache.GetGate("mimc"), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } diff --git a/internal/gkr/bls12-381/gate_testing.go b/internal/gkr/bls12-381/gate_testing.go index fe136861e4..ebc9fc4fb0 100644 --- a/internal/gkr/bls12-381/gate_testing.go +++ b/internal/gkr/bls12-381/gate_testing.go @@ -152,7 +152,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 40c3d2ffb1..f5f9a731d6 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -34,44 +34,44 @@ func TestNoGateTwoInstances(t *testing.T) { } func TestNoGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}}) + test(t, Circuit{{}}) } func TestSingleAddGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}, {}, { - Gate: gkrtypes.Add2(), + test(t, Circuit{{}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), Inputs: []int{0, 1}, }}) } func TestSingleMulGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}, {}, { - Gate: gkrtypes.Mul2(), + test(t, Circuit{{}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{0, 1}, }}) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, gkrtypes.Circuit{{}, + test(t, Circuit{{}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, gkrtypes.Circuit{{}, + test(t, Circuit{{}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{1}, }}) } @@ -79,11 +79,11 @@ func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { func TestAPowNTimesBCircuit(t *testing.T) { const N = 10 - c := make(gkrtypes.Circuit, N+2) + c := make(Circuit, N+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: gkrtypes.Mul2(), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{i - 1, 0}, } } @@ -92,11 +92,11 @@ func TestAPowNTimesBCircuit(t *testing.T) { } func TestSingleMimcCipherGate(t *testing.T) { - test(t, gkrtypes.Circuit{ + test(t, Circuit{ {}, {}, { Inputs: []int{0, 1}, - Gate: cache.GetGate("mimc"), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), }, }) } @@ -110,8 +110,8 @@ func TestMimc(t *testing.T) { } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := gkrtypes.Circuit{gkrtypes.Wire{ - Gate: gkrtypes.Identity(), + circuit := Circuit{Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), NbUniqueOutputs: 2, }} @@ -168,7 +168,7 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.Circuit) { +func test(t *testing.T, circuit Circuit) { wireRefs := utils.References(circuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -222,7 +222,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := gkrtypes.Circuit{ + c := Circuit{ {}, } @@ -237,12 +237,12 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { assert.NoError(t, err, "proof rejected") } -func mimcCircuit(numRounds int) gkrtypes.Circuit { - c := make(gkrtypes.Circuit, numRounds+2) +func mimcCircuit(numRounds int) Circuit { + c := make(Circuit, numRounds+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: cache.GetGate("mimc"), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } diff --git a/internal/gkr/bls24-315/gate_testing.go b/internal/gkr/bls24-315/gate_testing.go index 12142148d5..d35b6876a0 100644 --- a/internal/gkr/bls24-315/gate_testing.go +++ b/internal/gkr/bls24-315/gate_testing.go @@ -152,7 +152,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/gkr/bls24-315/gkr_test.go b/internal/gkr/bls24-315/gkr_test.go index 0e0fae285c..175629779c 100644 --- a/internal/gkr/bls24-315/gkr_test.go +++ b/internal/gkr/bls24-315/gkr_test.go @@ -34,44 +34,44 @@ func TestNoGateTwoInstances(t *testing.T) { } func TestNoGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}}) + test(t, Circuit{{}}) } func TestSingleAddGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}, {}, { - Gate: gkrtypes.Add2(), + test(t, Circuit{{}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), Inputs: []int{0, 1}, }}) } func TestSingleMulGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}, {}, { - Gate: gkrtypes.Mul2(), + test(t, Circuit{{}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{0, 1}, }}) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, gkrtypes.Circuit{{}, + test(t, Circuit{{}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, gkrtypes.Circuit{{}, + test(t, Circuit{{}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{1}, }}) } @@ -79,11 +79,11 @@ func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { func TestAPowNTimesBCircuit(t *testing.T) { const N = 10 - c := make(gkrtypes.Circuit, N+2) + c := make(Circuit, N+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: gkrtypes.Mul2(), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{i - 1, 0}, } } @@ -92,11 +92,11 @@ func TestAPowNTimesBCircuit(t *testing.T) { } func TestSingleMimcCipherGate(t *testing.T) { - test(t, gkrtypes.Circuit{ + test(t, Circuit{ {}, {}, { Inputs: []int{0, 1}, - Gate: cache.GetGate("mimc"), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), }, }) } @@ -110,8 +110,8 @@ func TestMimc(t *testing.T) { } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := gkrtypes.Circuit{gkrtypes.Wire{ - Gate: gkrtypes.Identity(), + circuit := Circuit{Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), NbUniqueOutputs: 2, }} @@ -168,7 +168,7 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.Circuit) { +func test(t *testing.T, circuit Circuit) { wireRefs := utils.References(circuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -222,7 +222,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := gkrtypes.Circuit{ + c := Circuit{ {}, } @@ -237,12 +237,12 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { assert.NoError(t, err, "proof rejected") } -func mimcCircuit(numRounds int) gkrtypes.Circuit { - c := make(gkrtypes.Circuit, numRounds+2) +func mimcCircuit(numRounds int) Circuit { + c := make(Circuit, numRounds+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: cache.GetGate("mimc"), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } diff --git a/internal/gkr/bls24-317/gate_testing.go b/internal/gkr/bls24-317/gate_testing.go index 1e3ad02681..e75aae11e1 100644 --- a/internal/gkr/bls24-317/gate_testing.go +++ b/internal/gkr/bls24-317/gate_testing.go @@ -152,7 +152,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/gkr/bls24-317/gkr_test.go b/internal/gkr/bls24-317/gkr_test.go index 908ff50f52..31fd24c2ee 100644 --- a/internal/gkr/bls24-317/gkr_test.go +++ b/internal/gkr/bls24-317/gkr_test.go @@ -34,44 +34,44 @@ func TestNoGateTwoInstances(t *testing.T) { } func TestNoGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}}) + test(t, Circuit{{}}) } func TestSingleAddGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}, {}, { - Gate: gkrtypes.Add2(), + test(t, Circuit{{}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), Inputs: []int{0, 1}, }}) } func TestSingleMulGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}, {}, { - Gate: gkrtypes.Mul2(), + test(t, Circuit{{}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{0, 1}, }}) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, gkrtypes.Circuit{{}, + test(t, Circuit{{}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, gkrtypes.Circuit{{}, + test(t, Circuit{{}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{1}, }}) } @@ -79,11 +79,11 @@ func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { func TestAPowNTimesBCircuit(t *testing.T) { const N = 10 - c := make(gkrtypes.Circuit, N+2) + c := make(Circuit, N+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: gkrtypes.Mul2(), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{i - 1, 0}, } } @@ -92,11 +92,11 @@ func TestAPowNTimesBCircuit(t *testing.T) { } func TestSingleMimcCipherGate(t *testing.T) { - test(t, gkrtypes.Circuit{ + test(t, Circuit{ {}, {}, { Inputs: []int{0, 1}, - Gate: cache.GetGate("mimc"), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), }, }) } @@ -110,8 +110,8 @@ func TestMimc(t *testing.T) { } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := gkrtypes.Circuit{gkrtypes.Wire{ - Gate: gkrtypes.Identity(), + circuit := Circuit{Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), NbUniqueOutputs: 2, }} @@ -168,7 +168,7 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.Circuit) { +func test(t *testing.T, circuit Circuit) { wireRefs := utils.References(circuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -222,7 +222,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := gkrtypes.Circuit{ + c := Circuit{ {}, } @@ -237,12 +237,12 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { assert.NoError(t, err, "proof rejected") } -func mimcCircuit(numRounds int) gkrtypes.Circuit { - c := make(gkrtypes.Circuit, numRounds+2) +func mimcCircuit(numRounds int) Circuit { + c := make(Circuit, numRounds+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: cache.GetGate("mimc"), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index bb9c746776..3bcd1ab4fe 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -34,44 +34,44 @@ func TestNoGateTwoInstances(t *testing.T) { } func TestNoGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}}) + test(t, Circuit{{}}) } func TestSingleAddGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}, {}, { - Gate: gkrtypes.Add2(), + test(t, Circuit{{}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), Inputs: []int{0, 1}, }}) } func TestSingleMulGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}, {}, { - Gate: gkrtypes.Mul2(), + test(t, Circuit{{}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{0, 1}, }}) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, gkrtypes.Circuit{{}, + test(t, Circuit{{}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, gkrtypes.Circuit{{}, + test(t, Circuit{{}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{1}, }}) } @@ -79,11 +79,11 @@ func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { func TestAPowNTimesBCircuit(t *testing.T) { const N = 10 - c := make(gkrtypes.Circuit, N+2) + c := make(Circuit, N+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: gkrtypes.Mul2(), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{i - 1, 0}, } } @@ -92,11 +92,11 @@ func TestAPowNTimesBCircuit(t *testing.T) { } func TestSingleMimcCipherGate(t *testing.T) { - test(t, gkrtypes.Circuit{ + test(t, Circuit{ {}, {}, { Inputs: []int{0, 1}, - Gate: cache.GetGate("mimc"), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), }, }) } @@ -110,8 +110,8 @@ func TestMimc(t *testing.T) { } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := gkrtypes.Circuit{gkrtypes.Wire{ - Gate: gkrtypes.Identity(), + circuit := Circuit{Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), NbUniqueOutputs: 2, }} @@ -168,7 +168,7 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.Circuit) { +func test(t *testing.T, circuit Circuit) { wireRefs := utils.References(circuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -222,7 +222,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := gkrtypes.Circuit{ + c := Circuit{ {}, } @@ -237,12 +237,12 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { assert.NoError(t, err, "proof rejected") } -func mimcCircuit(numRounds int) gkrtypes.Circuit { - c := make(gkrtypes.Circuit, numRounds+2) +func mimcCircuit(numRounds int) Circuit { + c := make(Circuit, numRounds+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: cache.GetGate("mimc"), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } diff --git a/internal/gkr/bw6-633/gate_testing.go b/internal/gkr/bw6-633/gate_testing.go index d4f5957b33..c4bc39c831 100644 --- a/internal/gkr/bw6-633/gate_testing.go +++ b/internal/gkr/bw6-633/gate_testing.go @@ -152,7 +152,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/gkr/bw6-633/gkr_test.go b/internal/gkr/bw6-633/gkr_test.go index bfa881cfd2..914d84c2b5 100644 --- a/internal/gkr/bw6-633/gkr_test.go +++ b/internal/gkr/bw6-633/gkr_test.go @@ -34,44 +34,44 @@ func TestNoGateTwoInstances(t *testing.T) { } func TestNoGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}}) + test(t, Circuit{{}}) } func TestSingleAddGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}, {}, { - Gate: gkrtypes.Add2(), + test(t, Circuit{{}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), Inputs: []int{0, 1}, }}) } func TestSingleMulGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}, {}, { - Gate: gkrtypes.Mul2(), + test(t, Circuit{{}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{0, 1}, }}) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, gkrtypes.Circuit{{}, + test(t, Circuit{{}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, gkrtypes.Circuit{{}, + test(t, Circuit{{}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{1}, }}) } @@ -79,11 +79,11 @@ func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { func TestAPowNTimesBCircuit(t *testing.T) { const N = 10 - c := make(gkrtypes.Circuit, N+2) + c := make(Circuit, N+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: gkrtypes.Mul2(), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{i - 1, 0}, } } @@ -92,11 +92,11 @@ func TestAPowNTimesBCircuit(t *testing.T) { } func TestSingleMimcCipherGate(t *testing.T) { - test(t, gkrtypes.Circuit{ + test(t, Circuit{ {}, {}, { Inputs: []int{0, 1}, - Gate: cache.GetGate("mimc"), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), }, }) } @@ -110,8 +110,8 @@ func TestMimc(t *testing.T) { } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := gkrtypes.Circuit{gkrtypes.Wire{ - Gate: gkrtypes.Identity(), + circuit := Circuit{Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), NbUniqueOutputs: 2, }} @@ -168,7 +168,7 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.Circuit) { +func test(t *testing.T, circuit Circuit) { wireRefs := utils.References(circuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -222,7 +222,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := gkrtypes.Circuit{ + c := Circuit{ {}, } @@ -237,12 +237,12 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { assert.NoError(t, err, "proof rejected") } -func mimcCircuit(numRounds int) gkrtypes.Circuit { - c := make(gkrtypes.Circuit, numRounds+2) +func mimcCircuit(numRounds int) Circuit { + c := make(Circuit, numRounds+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: cache.GetGate("mimc"), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } diff --git a/internal/gkr/bw6-761/gate_testing.go b/internal/gkr/bw6-761/gate_testing.go index 0a4686547a..b86ac991ed 100644 --- a/internal/gkr/bw6-761/gate_testing.go +++ b/internal/gkr/bw6-761/gate_testing.go @@ -152,7 +152,7 @@ func (t *GateTester) VerifyDegree(claimedDegree int) error { } // Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrinfo.CompiledGate) bool { +func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { x := make(fr.Vector, t.nbIn) x.MustSetRandom() fAt := t.evaluator.evaluate(x...) diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index 7c9f3908a0..762c063cbd 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -34,44 +34,44 @@ func TestNoGateTwoInstances(t *testing.T) { } func TestNoGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}}) + test(t, Circuit{{}}) } func TestSingleAddGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}, {}, { - Gate: gkrtypes.Add2(), + test(t, Circuit{{}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), Inputs: []int{0, 1}, }}) } func TestSingleMulGate(t *testing.T) { - test(t, gkrtypes.Circuit{{}, {}, { - Gate: gkrtypes.Mul2(), + test(t, Circuit{{}, {}, { + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{0, 1}, }}) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, gkrtypes.Circuit{{}, + test(t, Circuit{{}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, gkrtypes.Circuit{{}, + test(t, Circuit{{}, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{0}, }, { - Gate: gkrtypes.Identity(), + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), Inputs: []int{1}, }}) } @@ -79,11 +79,11 @@ func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { func TestAPowNTimesBCircuit(t *testing.T) { const N = 10 - c := make(gkrtypes.Circuit, N+2) + c := make(Circuit, N+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: gkrtypes.Mul2(), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), Inputs: []int{i - 1, 0}, } } @@ -92,11 +92,11 @@ func TestAPowNTimesBCircuit(t *testing.T) { } func TestSingleMimcCipherGate(t *testing.T) { - test(t, gkrtypes.Circuit{ + test(t, Circuit{ {}, {}, { Inputs: []int{0, 1}, - Gate: cache.GetGate("mimc"), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), }, }) } @@ -110,8 +110,8 @@ func TestMimc(t *testing.T) { } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := gkrtypes.Circuit{gkrtypes.Wire{ - Gate: gkrtypes.Identity(), + circuit := Circuit{Wire{ + Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), NbUniqueOutputs: 2, }} @@ -168,7 +168,7 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.Circuit) { +func test(t *testing.T, circuit Circuit) { wireRefs := utils.References(circuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -222,7 +222,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := gkrtypes.Circuit{ + c := Circuit{ {}, } @@ -237,12 +237,12 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { assert.NoError(t, err, "proof rejected") } -func mimcCircuit(numRounds int) gkrtypes.Circuit { - c := make(gkrtypes.Circuit, numRounds+2) +func mimcCircuit(numRounds int) Circuit { + c := make(Circuit, numRounds+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.Wire{ - Gate: cache.GetGate("mimc"), + c[i] = Wire{ + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } diff --git a/internal/gkr/gkr_test.go b/internal/gkr/gkr_test.go index a3c115f8ee..566c43ce46 100644 --- a/internal/gkr/gkr_test.go +++ b/internal/gkr/gkr_test.go @@ -191,7 +191,7 @@ func getTestCase(path string) (*TestCase, error) { return nil, err } - cse.Circuit = cache.GetCircuit(filepath.Join(dir, info.Circuit)) + cse.Circuit = gkrtypes.ToGadget(cache.GetCircuit(filepath.Join(dir, info.Circuit))) cse.Proof = unmarshalProof(info.Proof) @@ -306,12 +306,6 @@ func NewMessageCounter(api frontend.API, startState, step int) hash.FieldHasher return transcript } -func NewMessageCounterGenerator(startState, step int) func(frontend.API) hash.FieldHasher { - return func(api frontend.API) hash.FieldHasher { - return NewMessageCounter(api, startState, step) - } -} - type constHashCircuit struct { X frontend.Variable } diff --git a/internal/gkr/gkrtypes/topological_sort_test.go b/internal/gkr/gkrtypes/topological_sort_test.go index 19d5a2fa95..2726baea55 100644 --- a/internal/gkr/gkrtypes/topological_sort_test.go +++ b/internal/gkr/gkrtypes/topological_sort_test.go @@ -4,21 +4,20 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestTopSortTrivial(t *testing.T) { - c := make(Circuit, 2) + c := make(SerializableCircuit, 2) c[0].Inputs = []int{1} sorted := c.TopologicalSort() - assert.Equal(t, []*Wire{&c[1], &c[0]}, sorted) + assert.Equal(t, SerializableWires{&c[1], &c[0]}, sorted) } func TestTopSortSingleGate(t *testing.T) { - c := make(Circuit, 3) + c := make(SerializableCircuit, 3) c[0].Inputs = []int{1, 2} sorted := c.TopologicalSort() - expected := []*Wire{&c[1], &c[2], &c[0]} + expected := SerializableWires{&c[1], &c[2], &c[0]} assert.Equal(t, expected, sorted) assert.Equal(t, c[0].NbUniqueOutputs, 0) @@ -27,17 +26,17 @@ func TestTopSortSingleGate(t *testing.T) { } func TestTopSortDeep(t *testing.T) { - c := make(Circuit, 4) + c := make(SerializableCircuit, 4) c[0].Inputs = []int{2} c[1].Inputs = []int{3} c[2].Inputs = []int{} c[3].Inputs = []int{0} sorted := c.TopologicalSort() - assert.Equal(t, []*Wire{&c[2], &c[0], &c[3], &c[1]}, sorted) + assert.Equal(t, SerializableWires{&c[2], &c[0], &c[3], &c[1]}, sorted) } func TestTopSortWide(t *testing.T) { - c := make(Circuit, 10) + c := make(SerializableCircuit, 10) c[0].Inputs = []int{3, 8} c[1].Inputs = []int{6} c[2].Inputs = []int{4} @@ -50,24 +49,7 @@ func TestTopSortWide(t *testing.T) { c[9].Inputs = []int{} sorted := c.TopologicalSort() - sortedExpected := []*Wire{&c[3], &c[4], &c[2], &c[8], &c[0], &c[9], &c[5], &c[6], &c[1], &c[7]} + sortedExpected := SerializableWires{&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 assertPermutation(t *testing.T, original Circuit, permuted []*Wire, permutationInv []int) { - for i := range permuted { - if permuted[i] != &original[permutationInv[i]] { - actualIndex := -1 - for j := range original { - if &original[j] == permuted[i] { - actualIndex = j - break - } - } - require.NotEqual(t, -1, actualIndex, "result is not a permutation. element #%d of \"permuted\" list not found in original", i) - t.Errorf("expected ") - t.Fail() - } - } -} diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 869103f9cc..d21042d9c7 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -7,9 +7,7 @@ import ( "github.com/consensys/gnark" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi/gkr" - "github.com/consensys/gnark/std/polynomial" ) type ( @@ -85,6 +83,10 @@ func NewGate(f gkr.GateFunction, compiled *GateBytecode, nbIn int, degree int, s } } +func (be BothExecutables) getByteCode() *GateBytecode { + return be.Bytecode +} + // SupportsCurve returns whether the gate can be used over the given curve. func (g *Gate[GateExecutable]) SupportsCurve(curve ecc.ID) bool { return slices.Contains(g.Curves, curve) @@ -159,21 +161,6 @@ func (c Circuit[GateExecutable]) maxGateDegree() int { return res } -// MaxStackSize returns the maximum stack size needed by any gate evaluator in the circuit. -// This is used to initialize universal gate evaluators that can handle any gate in the circuit. -func MaxStackSize(c SerializableCircuit) int { - maxSize := 0 - for i := range c { - if !c[i].IsInput() { - gate := c[i].Gate.Evaluate - nbIn := len(c[i].Inputs) - stackSize := gate.NbConstants() + nbIn + len(gate.Instructions) - maxSize = max(maxSize, stackSize) - } - } - return maxSize -} - // MemoryRequirements returns an increasing vector of memory allocation sizes required for proving a GKR statement func (c Circuit[GateExecutable]) MemoryRequirements(nbInstances int) []int { res := []int{256, nbInstances, nbInstances * (c.maxGateDegree() + 1)} @@ -391,18 +378,21 @@ func Mul2() *RegisteredGate { }, 2, 2, -1, gnark.Curves()) } +func ConvertGate[GateExecutable, TargetGateExecutable any](g *Gate[GateExecutable], converter func(GateExecutable) TargetGateExecutable) *Gate[TargetGateExecutable] { + return &Gate[TargetGateExecutable]{ + Evaluate: converter(g.Evaluate), + NbIn: g.NbIn, + Degree: g.Degree, + SolvableVar: g.SolvableVar, + Curves: g.Curves, + } +} + func ConvertCircuit[GateExecutable, TargetGateExecutable any](c Circuit[GateExecutable], gateConverter func(GateExecutable) TargetGateExecutable) Circuit[TargetGateExecutable] { res := make(Circuit[TargetGateExecutable], len(c)) for i := range c { res[i] = Wire[TargetGateExecutable]{ - Gate: &Gate[TargetGateExecutable]{ - Evaluate: gateConverter(c[i].Gate.Evaluate), - NbIn: c[i].Gate.NbIn, - Degree: c[i].Gate.Degree, - SolvableVar: c[i].Gate.SolvableVar, - Curves: c[i].Gate.Curves, - }, - + Gate: ConvertGate(c[i].Gate, gateConverter), Inputs: c[i].Inputs, NbUniqueOutputs: c[i].NbUniqueOutputs, } @@ -413,9 +403,11 @@ func ConvertCircuit[GateExecutable, TargetGateExecutable any](c Circuit[GateExec // ToSerializable converts a registered circuit (with both executables) to a serializable circuit (bytecode only). func ToSerializable(c RegisteredCircuit) SerializableCircuit { - return ConvertCircuit(c, func(e BothExecutables) *GateBytecode { - return e.Bytecode - }) + return ConvertCircuit(c, BothExecutables.getByteCode) +} + +func ToSerializableGate(g *RegisteredGate) *Gate[*GateBytecode] { + return ConvertGate(g, BothExecutables.getByteCode) } // ToGadget converts a registered circuit (with both executables) to a gadget circuit (gate functions only, for in-circuit verification). From d621873327a4cb4d61814035f27fbe52880e1e37 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Fri, 23 Jan 2026 01:04:28 -0600 Subject: [PATCH 132/251] fix: add identity gates to test inputs --- .../backend/template/gkr/gate_testing.go.tmpl | 4 +- .../backend/template/gkr/gkr.test.go.tmpl | 96 +++++++++++++++---- internal/gkr/bls12-377/gkr_test.go | 96 +++++++++++++++---- internal/gkr/bls12-381/gkr_test.go | 96 +++++++++++++++---- internal/gkr/bls24-315/gkr_test.go | 96 +++++++++++++++---- internal/gkr/bls24-317/gkr_test.go | 96 +++++++++++++++---- internal/gkr/bn254/gkr_test.go | 96 +++++++++++++++---- internal/gkr/bw6-633/gkr_test.go | 96 +++++++++++++++---- internal/gkr/bw6-761/gkr_test.go | 96 +++++++++++++++---- 9 files changed, 609 insertions(+), 163 deletions(-) diff --git a/internal/generator/backend/template/gkr/gate_testing.go.tmpl b/internal/generator/backend/template/gkr/gate_testing.go.tmpl index 24888d6bce..1ab6668d6b 100644 --- a/internal/generator/backend/template/gkr/gate_testing.go.tmpl +++ b/internal/generator/backend/template/gkr/gate_testing.go.tmpl @@ -3,11 +3,9 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" "{{.FieldPackagePath}}" {{- if .CanUseFFT }} - "{{.FieldPackagePath}}/fft" - "sync"{{- else}} + "{{.FieldPackagePath}}/fft"{{- else}} "errors"{{- end }} "{{.FieldPackagePath}}/polynomial" "slices" diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index c403313461..453e84a79f 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -22,52 +22,85 @@ import ( "time" ) +var ( + identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) +) + func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case testNoGate(t, []{{ .ElementType }}{four, three}) } func TestNoGate(t *testing.T) { - test(t, Circuit{ {} }) + test(t, Circuit{ + { + Gate: identityGate, + }, + }) } func TestSingleAddGate(t *testing.T) { - test(t, Circuit{ {}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), - Inputs: []int{0, 1}, - } }) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: add2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleMulGate(t *testing.T) { - test(t, Circuit{ {}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), - Inputs: []int{0, 1}, - }}) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: mul2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, Circuit{ {}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, Circuit{ {}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{1}, - }}) + }, + }) } func TestAPowNTimesBCircuit(t *testing.T) { @@ -75,9 +108,17 @@ func TestAPowNTimesBCircuit(t *testing.T) { c := make(Circuit, N+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), + Gate: mul2Gate, Inputs: []int{i - 1, 0}, } } @@ -87,7 +128,12 @@ func TestAPowNTimesBCircuit(t *testing.T) { func TestSingleMimcCipherGate(t *testing.T) { test(t, Circuit{ - {}, {}, + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, { Inputs: []int{0, 1}, Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), @@ -105,7 +151,7 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { circuit := Circuit{Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, NbUniqueOutputs: 2, }} @@ -217,7 +263,9 @@ func (p Proof) isEmpty() bool { func testNoGate(t *testing.T, inputAssignments ...[]{{ .ElementType }}) { c := Circuit{ - {}, + { + Gate: identityGate, + }, } assignment := WireAssignment{0: inputAssignments[0]} @@ -234,6 +282,14 @@ func testNoGate(t *testing.T, inputAssignments ...[]{{ .ElementType }}) { func mimcCircuit(numRounds int) Circuit { c := make(Circuit, numRounds+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index 72fc042c7b..8cd71627eb 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -28,52 +28,85 @@ import ( "github.com/stretchr/testify/assert" ) +var ( + identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) +) + func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case testNoGate(t, []fr.Element{four, three}) } func TestNoGate(t *testing.T) { - test(t, Circuit{{}}) + test(t, Circuit{ + { + Gate: identityGate, + }, + }) } func TestSingleAddGate(t *testing.T) { - test(t, Circuit{{}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), - Inputs: []int{0, 1}, - }}) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: add2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleMulGate(t *testing.T) { - test(t, Circuit{{}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), - Inputs: []int{0, 1}, - }}) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: mul2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, Circuit{{}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, Circuit{{}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{1}, - }}) + }, + }) } func TestAPowNTimesBCircuit(t *testing.T) { @@ -81,9 +114,17 @@ func TestAPowNTimesBCircuit(t *testing.T) { c := make(Circuit, N+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), + Gate: mul2Gate, Inputs: []int{i - 1, 0}, } } @@ -93,7 +134,12 @@ func TestAPowNTimesBCircuit(t *testing.T) { func TestSingleMimcCipherGate(t *testing.T) { test(t, Circuit{ - {}, {}, + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, { Inputs: []int{0, 1}, Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), @@ -111,7 +157,7 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { circuit := Circuit{Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, NbUniqueOutputs: 2, }} @@ -223,7 +269,9 @@ func (p Proof) isEmpty() bool { func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { c := Circuit{ - {}, + { + Gate: identityGate, + }, } assignment := WireAssignment{0: inputAssignments[0]} @@ -240,6 +288,14 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { func mimcCircuit(numRounds int) Circuit { c := make(Circuit, numRounds+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index f5f9a731d6..5f3e6041ce 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -28,52 +28,85 @@ import ( "github.com/stretchr/testify/assert" ) +var ( + identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) +) + func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case testNoGate(t, []fr.Element{four, three}) } func TestNoGate(t *testing.T) { - test(t, Circuit{{}}) + test(t, Circuit{ + { + Gate: identityGate, + }, + }) } func TestSingleAddGate(t *testing.T) { - test(t, Circuit{{}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), - Inputs: []int{0, 1}, - }}) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: add2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleMulGate(t *testing.T) { - test(t, Circuit{{}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), - Inputs: []int{0, 1}, - }}) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: mul2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, Circuit{{}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, Circuit{{}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{1}, - }}) + }, + }) } func TestAPowNTimesBCircuit(t *testing.T) { @@ -81,9 +114,17 @@ func TestAPowNTimesBCircuit(t *testing.T) { c := make(Circuit, N+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), + Gate: mul2Gate, Inputs: []int{i - 1, 0}, } } @@ -93,7 +134,12 @@ func TestAPowNTimesBCircuit(t *testing.T) { func TestSingleMimcCipherGate(t *testing.T) { test(t, Circuit{ - {}, {}, + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, { Inputs: []int{0, 1}, Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), @@ -111,7 +157,7 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { circuit := Circuit{Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, NbUniqueOutputs: 2, }} @@ -223,7 +269,9 @@ func (p Proof) isEmpty() bool { func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { c := Circuit{ - {}, + { + Gate: identityGate, + }, } assignment := WireAssignment{0: inputAssignments[0]} @@ -240,6 +288,14 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { func mimcCircuit(numRounds int) Circuit { c := make(Circuit, numRounds+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), diff --git a/internal/gkr/bls24-315/gkr_test.go b/internal/gkr/bls24-315/gkr_test.go index 175629779c..941145d4cc 100644 --- a/internal/gkr/bls24-315/gkr_test.go +++ b/internal/gkr/bls24-315/gkr_test.go @@ -28,52 +28,85 @@ import ( "github.com/stretchr/testify/assert" ) +var ( + identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) +) + func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case testNoGate(t, []fr.Element{four, three}) } func TestNoGate(t *testing.T) { - test(t, Circuit{{}}) + test(t, Circuit{ + { + Gate: identityGate, + }, + }) } func TestSingleAddGate(t *testing.T) { - test(t, Circuit{{}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), - Inputs: []int{0, 1}, - }}) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: add2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleMulGate(t *testing.T) { - test(t, Circuit{{}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), - Inputs: []int{0, 1}, - }}) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: mul2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, Circuit{{}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, Circuit{{}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{1}, - }}) + }, + }) } func TestAPowNTimesBCircuit(t *testing.T) { @@ -81,9 +114,17 @@ func TestAPowNTimesBCircuit(t *testing.T) { c := make(Circuit, N+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), + Gate: mul2Gate, Inputs: []int{i - 1, 0}, } } @@ -93,7 +134,12 @@ func TestAPowNTimesBCircuit(t *testing.T) { func TestSingleMimcCipherGate(t *testing.T) { test(t, Circuit{ - {}, {}, + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, { Inputs: []int{0, 1}, Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), @@ -111,7 +157,7 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { circuit := Circuit{Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, NbUniqueOutputs: 2, }} @@ -223,7 +269,9 @@ func (p Proof) isEmpty() bool { func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { c := Circuit{ - {}, + { + Gate: identityGate, + }, } assignment := WireAssignment{0: inputAssignments[0]} @@ -240,6 +288,14 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { func mimcCircuit(numRounds int) Circuit { c := make(Circuit, numRounds+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), diff --git a/internal/gkr/bls24-317/gkr_test.go b/internal/gkr/bls24-317/gkr_test.go index 31fd24c2ee..fd8e929e93 100644 --- a/internal/gkr/bls24-317/gkr_test.go +++ b/internal/gkr/bls24-317/gkr_test.go @@ -28,52 +28,85 @@ import ( "github.com/stretchr/testify/assert" ) +var ( + identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) +) + func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case testNoGate(t, []fr.Element{four, three}) } func TestNoGate(t *testing.T) { - test(t, Circuit{{}}) + test(t, Circuit{ + { + Gate: identityGate, + }, + }) } func TestSingleAddGate(t *testing.T) { - test(t, Circuit{{}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), - Inputs: []int{0, 1}, - }}) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: add2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleMulGate(t *testing.T) { - test(t, Circuit{{}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), - Inputs: []int{0, 1}, - }}) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: mul2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, Circuit{{}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, Circuit{{}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{1}, - }}) + }, + }) } func TestAPowNTimesBCircuit(t *testing.T) { @@ -81,9 +114,17 @@ func TestAPowNTimesBCircuit(t *testing.T) { c := make(Circuit, N+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), + Gate: mul2Gate, Inputs: []int{i - 1, 0}, } } @@ -93,7 +134,12 @@ func TestAPowNTimesBCircuit(t *testing.T) { func TestSingleMimcCipherGate(t *testing.T) { test(t, Circuit{ - {}, {}, + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, { Inputs: []int{0, 1}, Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), @@ -111,7 +157,7 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { circuit := Circuit{Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, NbUniqueOutputs: 2, }} @@ -223,7 +269,9 @@ func (p Proof) isEmpty() bool { func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { c := Circuit{ - {}, + { + Gate: identityGate, + }, } assignment := WireAssignment{0: inputAssignments[0]} @@ -240,6 +288,14 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { func mimcCircuit(numRounds int) Circuit { c := make(Circuit, numRounds+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 3bcd1ab4fe..448f545540 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -28,52 +28,85 @@ import ( "github.com/stretchr/testify/assert" ) +var ( + identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) +) + func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case testNoGate(t, []fr.Element{four, three}) } func TestNoGate(t *testing.T) { - test(t, Circuit{{}}) + test(t, Circuit{ + { + Gate: identityGate, + }, + }) } func TestSingleAddGate(t *testing.T) { - test(t, Circuit{{}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), - Inputs: []int{0, 1}, - }}) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: add2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleMulGate(t *testing.T) { - test(t, Circuit{{}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), - Inputs: []int{0, 1}, - }}) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: mul2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, Circuit{{}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, Circuit{{}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{1}, - }}) + }, + }) } func TestAPowNTimesBCircuit(t *testing.T) { @@ -81,9 +114,17 @@ func TestAPowNTimesBCircuit(t *testing.T) { c := make(Circuit, N+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), + Gate: mul2Gate, Inputs: []int{i - 1, 0}, } } @@ -93,7 +134,12 @@ func TestAPowNTimesBCircuit(t *testing.T) { func TestSingleMimcCipherGate(t *testing.T) { test(t, Circuit{ - {}, {}, + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, { Inputs: []int{0, 1}, Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), @@ -111,7 +157,7 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { circuit := Circuit{Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, NbUniqueOutputs: 2, }} @@ -223,7 +269,9 @@ func (p Proof) isEmpty() bool { func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { c := Circuit{ - {}, + { + Gate: identityGate, + }, } assignment := WireAssignment{0: inputAssignments[0]} @@ -240,6 +288,14 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { func mimcCircuit(numRounds int) Circuit { c := make(Circuit, numRounds+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), diff --git a/internal/gkr/bw6-633/gkr_test.go b/internal/gkr/bw6-633/gkr_test.go index 914d84c2b5..dd655d07b1 100644 --- a/internal/gkr/bw6-633/gkr_test.go +++ b/internal/gkr/bw6-633/gkr_test.go @@ -28,52 +28,85 @@ import ( "github.com/stretchr/testify/assert" ) +var ( + identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) +) + func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case testNoGate(t, []fr.Element{four, three}) } func TestNoGate(t *testing.T) { - test(t, Circuit{{}}) + test(t, Circuit{ + { + Gate: identityGate, + }, + }) } func TestSingleAddGate(t *testing.T) { - test(t, Circuit{{}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), - Inputs: []int{0, 1}, - }}) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: add2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleMulGate(t *testing.T) { - test(t, Circuit{{}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), - Inputs: []int{0, 1}, - }}) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: mul2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, Circuit{{}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, Circuit{{}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{1}, - }}) + }, + }) } func TestAPowNTimesBCircuit(t *testing.T) { @@ -81,9 +114,17 @@ func TestAPowNTimesBCircuit(t *testing.T) { c := make(Circuit, N+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), + Gate: mul2Gate, Inputs: []int{i - 1, 0}, } } @@ -93,7 +134,12 @@ func TestAPowNTimesBCircuit(t *testing.T) { func TestSingleMimcCipherGate(t *testing.T) { test(t, Circuit{ - {}, {}, + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, { Inputs: []int{0, 1}, Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), @@ -111,7 +157,7 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { circuit := Circuit{Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, NbUniqueOutputs: 2, }} @@ -223,7 +269,9 @@ func (p Proof) isEmpty() bool { func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { c := Circuit{ - {}, + { + Gate: identityGate, + }, } assignment := WireAssignment{0: inputAssignments[0]} @@ -240,6 +288,14 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { func mimcCircuit(numRounds int) Circuit { c := make(Circuit, numRounds+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index 762c063cbd..175a72e66b 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -28,52 +28,85 @@ import ( "github.com/stretchr/testify/assert" ) +var ( + identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) +) + func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case testNoGate(t, []fr.Element{four, three}) } func TestNoGate(t *testing.T) { - test(t, Circuit{{}}) + test(t, Circuit{ + { + Gate: identityGate, + }, + }) } func TestSingleAddGate(t *testing.T) { - test(t, Circuit{{}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Add2()), - Inputs: []int{0, 1}, - }}) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: add2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleMulGate(t *testing.T) { - test(t, Circuit{{}, {}, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), - Inputs: []int{0, 1}, - }}) + test(t, Circuit{ + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, + { + Gate: mul2Gate, + Inputs: []int{0, 1}, + }, + }) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, Circuit{{}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{0}, }, }) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, Circuit{{}, + test(t, Circuit{ { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, + }, + { + Gate: identityGate, Inputs: []int{0}, }, { - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, Inputs: []int{1}, - }}) + }, + }) } func TestAPowNTimesBCircuit(t *testing.T) { @@ -81,9 +114,17 @@ func TestAPowNTimesBCircuit(t *testing.T) { c := make(Circuit, N+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Mul2()), + Gate: mul2Gate, Inputs: []int{i - 1, 0}, } } @@ -93,7 +134,12 @@ func TestAPowNTimesBCircuit(t *testing.T) { func TestSingleMimcCipherGate(t *testing.T) { test(t, Circuit{ - {}, {}, + { + Gate: identityGate, + }, + { + Gate: identityGate, + }, { Inputs: []int{0, 1}, Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), @@ -111,7 +157,7 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { circuit := Circuit{Wire{ - Gate: gkrtypes.ToSerializableGate(gkrtypes.Identity()), + Gate: identityGate, NbUniqueOutputs: 2, }} @@ -223,7 +269,9 @@ func (p Proof) isEmpty() bool { func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { c := Circuit{ - {}, + { + Gate: identityGate, + }, } assignment := WireAssignment{0: inputAssignments[0]} @@ -240,6 +288,14 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { func mimcCircuit(numRounds int) Circuit { c := make(Circuit, numRounds+2) + // Input wires + c[0] = Wire{ + Gate: identityGate, + } + c[1] = Wire{ + Gate: identityGate, + } + for i := 2; i < len(c); i++ { c[i] = Wire{ Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), From 959305213d111957165ac2d587b56f719804262f Mon Sep 17 00:00:00 2001 From: Tabaie Date: Fri, 23 Jan 2026 01:26:15 -0600 Subject: [PATCH 133/251] fix: initialize blueprint --- .../backend/template/gkr/blueprint.go.tmpl | 24 +++----------- internal/gkr/bls12-377/blueprint.go | 24 +++----------- internal/gkr/bls12-381/blueprint.go | 24 +++----------- internal/gkr/bls24-315/blueprint.go | 24 +++----------- internal/gkr/bls24-317/blueprint.go | 24 +++----------- internal/gkr/bn254/blueprint.go | 24 +++----------- internal/gkr/bw6-633/blueprint.go | 24 +++----------- internal/gkr/bw6-761/blueprint.go | 24 +++----------- internal/gkr/gkrtypes/types.go | 11 +++++++ std/gkrapi/compile.go | 33 ++++++++----------- 10 files changed, 65 insertions(+), 171 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 4f14621536..b762284675 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -28,7 +28,6 @@ type BlueprintSolve struct { assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - nbOutputVars int `cbor:"-"` inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` maxNbIn int `cbor:"-"` // maximum number of inputs for any gate @@ -61,22 +60,7 @@ func (b *BlueprintSolve) initialize() { } } - // Identify output wires (not inputs to any other wire) - isOutput := make([]bool, len(b.Circuit)) - for i := range b.Circuit { - isOutput[i] = true - } - for _, w := range b.Circuit { - for _, inIdx := range w.Inputs { - isOutput[inIdx] = false - } - } - for i := range b.Circuit { - if isOutput[i] { - b.nbOutputVars++ - b.outputWires = append(b.outputWires, i) - } - } + b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ @@ -164,11 +148,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.nbOutputVars + return len(b.Circuit.Outputs()) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // circuit metadata + maxLevel := constraint.LevelUnset // Parse exactly b.nbInputs linear expressions @@ -190,7 +176,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.nbOutputVars { + for i := range b.outputWires { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 3d5303df7e..d040ea901a 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -35,7 +35,6 @@ type BlueprintSolve struct { assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - nbOutputVars int `cbor:"-"` inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` maxNbIn int `cbor:"-"` // maximum number of inputs for any gate @@ -68,22 +67,7 @@ func (b *BlueprintSolve) initialize() { } } - // Identify output wires (not inputs to any other wire) - isOutput := make([]bool, len(b.Circuit)) - for i := range b.Circuit { - isOutput[i] = true - } - for _, w := range b.Circuit { - for _, inIdx := range w.Inputs { - isOutput[inIdx] = false - } - } - for i := range b.Circuit { - if isOutput[i] { - b.nbOutputVars++ - b.outputWires = append(b.outputWires, i) - } - } + b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ @@ -171,11 +155,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.nbOutputVars + return len(b.Circuit.Outputs()) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // circuit metadata + maxLevel := constraint.LevelUnset // Parse exactly b.nbInputs linear expressions @@ -197,7 +183,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.nbOutputVars { + for i := range b.outputWires { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 98440ae948..f2868fdef6 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -35,7 +35,6 @@ type BlueprintSolve struct { assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - nbOutputVars int `cbor:"-"` inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` maxNbIn int `cbor:"-"` // maximum number of inputs for any gate @@ -68,22 +67,7 @@ func (b *BlueprintSolve) initialize() { } } - // Identify output wires (not inputs to any other wire) - isOutput := make([]bool, len(b.Circuit)) - for i := range b.Circuit { - isOutput[i] = true - } - for _, w := range b.Circuit { - for _, inIdx := range w.Inputs { - isOutput[inIdx] = false - } - } - for i := range b.Circuit { - if isOutput[i] { - b.nbOutputVars++ - b.outputWires = append(b.outputWires, i) - } - } + b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ @@ -171,11 +155,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.nbOutputVars + return len(b.Circuit.Outputs()) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // circuit metadata + maxLevel := constraint.LevelUnset // Parse exactly b.nbInputs linear expressions @@ -197,7 +183,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.nbOutputVars { + for i := range b.outputWires { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index a1b3d966b2..54cb307de7 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -35,7 +35,6 @@ type BlueprintSolve struct { assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - nbOutputVars int `cbor:"-"` inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` maxNbIn int `cbor:"-"` // maximum number of inputs for any gate @@ -68,22 +67,7 @@ func (b *BlueprintSolve) initialize() { } } - // Identify output wires (not inputs to any other wire) - isOutput := make([]bool, len(b.Circuit)) - for i := range b.Circuit { - isOutput[i] = true - } - for _, w := range b.Circuit { - for _, inIdx := range w.Inputs { - isOutput[inIdx] = false - } - } - for i := range b.Circuit { - if isOutput[i] { - b.nbOutputVars++ - b.outputWires = append(b.outputWires, i) - } - } + b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ @@ -171,11 +155,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.nbOutputVars + return len(b.Circuit.Outputs()) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // circuit metadata + maxLevel := constraint.LevelUnset // Parse exactly b.nbInputs linear expressions @@ -197,7 +183,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.nbOutputVars { + for i := range b.outputWires { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 07b3abea85..cab93a1c38 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -35,7 +35,6 @@ type BlueprintSolve struct { assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - nbOutputVars int `cbor:"-"` inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` maxNbIn int `cbor:"-"` // maximum number of inputs for any gate @@ -68,22 +67,7 @@ func (b *BlueprintSolve) initialize() { } } - // Identify output wires (not inputs to any other wire) - isOutput := make([]bool, len(b.Circuit)) - for i := range b.Circuit { - isOutput[i] = true - } - for _, w := range b.Circuit { - for _, inIdx := range w.Inputs { - isOutput[inIdx] = false - } - } - for i := range b.Circuit { - if isOutput[i] { - b.nbOutputVars++ - b.outputWires = append(b.outputWires, i) - } - } + b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ @@ -171,11 +155,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.nbOutputVars + return len(b.Circuit.Outputs()) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // circuit metadata + maxLevel := constraint.LevelUnset // Parse exactly b.nbInputs linear expressions @@ -197,7 +183,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.nbOutputVars { + for i := range b.outputWires { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 5b6baeab50..f1d4720dfa 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -35,7 +35,6 @@ type BlueprintSolve struct { assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - nbOutputVars int `cbor:"-"` inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` maxNbIn int `cbor:"-"` // maximum number of inputs for any gate @@ -68,22 +67,7 @@ func (b *BlueprintSolve) initialize() { } } - // Identify output wires (not inputs to any other wire) - isOutput := make([]bool, len(b.Circuit)) - for i := range b.Circuit { - isOutput[i] = true - } - for _, w := range b.Circuit { - for _, inIdx := range w.Inputs { - isOutput[inIdx] = false - } - } - for i := range b.Circuit { - if isOutput[i] { - b.nbOutputVars++ - b.outputWires = append(b.outputWires, i) - } - } + b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ @@ -171,11 +155,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.nbOutputVars + return len(b.Circuit.Outputs()) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // circuit metadata + maxLevel := constraint.LevelUnset // Parse exactly b.nbInputs linear expressions @@ -197,7 +183,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.nbOutputVars { + for i := range b.outputWires { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 1c141a8a0f..b270361b4d 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -35,7 +35,6 @@ type BlueprintSolve struct { assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - nbOutputVars int `cbor:"-"` inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` maxNbIn int `cbor:"-"` // maximum number of inputs for any gate @@ -68,22 +67,7 @@ func (b *BlueprintSolve) initialize() { } } - // Identify output wires (not inputs to any other wire) - isOutput := make([]bool, len(b.Circuit)) - for i := range b.Circuit { - isOutput[i] = true - } - for _, w := range b.Circuit { - for _, inIdx := range w.Inputs { - isOutput[inIdx] = false - } - } - for i := range b.Circuit { - if isOutput[i] { - b.nbOutputVars++ - b.outputWires = append(b.outputWires, i) - } - } + b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ @@ -171,11 +155,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.nbOutputVars + return len(b.Circuit.Outputs()) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // circuit metadata + maxLevel := constraint.LevelUnset // Parse exactly b.nbInputs linear expressions @@ -197,7 +183,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.nbOutputVars { + for i := range b.outputWires { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 3e206609d8..9c9d921a59 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -35,7 +35,6 @@ type BlueprintSolve struct { assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - nbOutputVars int `cbor:"-"` inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` maxNbIn int `cbor:"-"` // maximum number of inputs for any gate @@ -68,22 +67,7 @@ func (b *BlueprintSolve) initialize() { } } - // Identify output wires (not inputs to any other wire) - isOutput := make([]bool, len(b.Circuit)) - for i := range b.Circuit { - isOutput[i] = true - } - for _, w := range b.Circuit { - for _, inIdx := range w.Inputs { - isOutput[inIdx] = false - } - } - for i := range b.Circuit { - if isOutput[i] { - b.nbOutputVars++ - b.outputWires = append(b.outputWires, i) - } - } + b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ @@ -171,11 +155,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return b.nbOutputVars + return len(b.Circuit.Outputs()) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // circuit metadata + maxLevel := constraint.LevelUnset // Parse exactly b.nbInputs linear expressions @@ -197,7 +183,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree } outputLevel := maxLevel + 1 - for i := range b.nbOutputVars { + for i := range b.outputWires { tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index d21042d9c7..960606189c 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -236,6 +236,17 @@ func (c Circuit[GateExecutable]) Inputs() []int { return res } +// Outputs returns the list of output wire indices +func (c Circuit[GateExecutable]) Outputs() []int { + res := make([]int, 0, len(c)) + for i := range c { + if c[i].IsOutput() { + res = append(res, i) + } + } + return res +} + // MaxGateNbIn returns the maximum number of inputs of any gate in the circuit. func (c Circuit[GateExecutable]) MaxGateNbIn() int { res := 0 diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 4f155d22b5..d4aad28f5a 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -90,9 +90,7 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C } // Convert registered circuit to serializable circuit (bytecode only) for blueprints - serializableCircuit := gkrtypes.ConvertCircuit(api.circuit, func(e gkrtypes.BothExecutables) *gkrtypes.GateBytecode { - return e.Bytecode - }) + serializableCircuit := gkrtypes.ToSerializable(api.circuit) // Dispatch to curve-specific factory curveID := utils.FieldToCurve(api.parentApi.Compiler().Field()) @@ -121,24 +119,22 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C opt(&res) } - notOut := make([]bool, len(res.circuit)) - for i := range res.circuit { - if res.circuit[i].IsInput() { - res.ins = append(res.ins, gkr.Variable(i)) - } - for _, inWI := range res.circuit[i].Inputs { - notOut[inWI] = true - } - } + // Use circuit helper methods for inputs and outputs + inputIndices := res.circuit.Inputs() + outputIndices := res.circuit.Outputs() - if len(res.ins) == len(res.circuit) { + if len(inputIndices) == len(res.circuit) { return nil, errors.New("circuit has no non-input wires") } - for i := range res.circuit { - if !notOut[i] { - res.outs = append(res.outs, gkr.Variable(i)) - } + res.ins = make([]gkr.Variable, len(inputIndices)) + for i, inI := range inputIndices { + res.ins[i] = gkr.Variable(inI) + } + + res.outs = make([]gkr.Variable, len(outputIndices)) + for i, outI := range outputIndices { + res.outs[i] = gkr.Variable(outI) } res.api.Compiler().Defer(res.finalize) @@ -216,7 +212,6 @@ func (c *Circuit) finalize(api frontend.API) error { } } - // Set NbInstances in the solve solveBlueprint (used for pre-allocation and proof size computation) c.blueprints.Solve.SetNbInstances(uint32(nbPaddedInstances)) curve := utils.FieldToCurve(api.Compiler().Field()) @@ -265,7 +260,7 @@ func (c *Circuit) finalize(api frontend.API) error { } multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { - return c.verify(api, nil, []frontend.Variable{commitment}) + return c.verify(api, gadgetCircuit, []frontend.Variable{commitment}) }, insOuts...) return nil From 33d6692f223fa6a227d15582c420ef7a7ef29a6b Mon Sep 17 00:00:00 2001 From: Tabaie Date: Fri, 23 Jan 2026 01:42:35 -0600 Subject: [PATCH 134/251] fix: panic with test engine --- .../backend/template/gkr/blueprint.go.tmpl | 16 +--- internal/gkr/bls12-377/blueprint.go | 16 +--- internal/gkr/bls12-381/blueprint.go | 16 +--- internal/gkr/bls24-315/blueprint.go | 16 +--- internal/gkr/bls24-317/blueprint.go | 16 +--- internal/gkr/bn254/blueprint.go | 84 +++++++------------ internal/gkr/bw6-633/blueprint.go | 16 +--- internal/gkr/bw6-761/blueprint.go | 16 +--- std/gkrapi/compile.go | 2 +- 9 files changed, 51 insertions(+), 147 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index b762284675..a7c1fc9dbd 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -28,9 +28,7 @@ type BlueprintSolve struct { assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` - maxNbIn int `cbor:"-"` // maximum number of inputs for any gate lock sync.Mutex `cbor:"-"` } @@ -51,15 +49,7 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - for i := range b.Circuit { - if b.Circuit[i].IsInput() { - b.nbInputs++ - b.inputWires = append(b.inputWires, i) - } else { - b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) - } - } - + b.Circuit.OutputsList() // for side effects b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { @@ -148,12 +138,12 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return len(b.Circuit.Outputs()) + b.initialize() // circuit metadata + return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // circuit metadata maxLevel := constraint.LevelUnset diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index d040ea901a..2e55e43f56 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -35,9 +35,7 @@ type BlueprintSolve struct { assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` - maxNbIn int `cbor:"-"` // maximum number of inputs for any gate lock sync.Mutex `cbor:"-"` } @@ -58,15 +56,7 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - for i := range b.Circuit { - if b.Circuit[i].IsInput() { - b.nbInputs++ - b.inputWires = append(b.inputWires, i) - } else { - b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) - } - } - + b.Circuit.OutputsList() // for side effects b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { @@ -155,12 +145,12 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return len(b.Circuit.Outputs()) + b.initialize() // circuit metadata + return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // circuit metadata maxLevel := constraint.LevelUnset diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index f2868fdef6..b085b4c4eb 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -35,9 +35,7 @@ type BlueprintSolve struct { assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` - maxNbIn int `cbor:"-"` // maximum number of inputs for any gate lock sync.Mutex `cbor:"-"` } @@ -58,15 +56,7 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - for i := range b.Circuit { - if b.Circuit[i].IsInput() { - b.nbInputs++ - b.inputWires = append(b.inputWires, i) - } else { - b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) - } - } - + b.Circuit.OutputsList() // for side effects b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { @@ -155,12 +145,12 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return len(b.Circuit.Outputs()) + b.initialize() // circuit metadata + return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // circuit metadata maxLevel := constraint.LevelUnset diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index 54cb307de7..c042993226 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -35,9 +35,7 @@ type BlueprintSolve struct { assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` - maxNbIn int `cbor:"-"` // maximum number of inputs for any gate lock sync.Mutex `cbor:"-"` } @@ -58,15 +56,7 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - for i := range b.Circuit { - if b.Circuit[i].IsInput() { - b.nbInputs++ - b.inputWires = append(b.inputWires, i) - } else { - b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) - } - } - + b.Circuit.OutputsList() // for side effects b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { @@ -155,12 +145,12 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return len(b.Circuit.Outputs()) + b.initialize() // circuit metadata + return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // circuit metadata maxLevel := constraint.LevelUnset diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index cab93a1c38..7cdde6e5e5 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -35,9 +35,7 @@ type BlueprintSolve struct { assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` - maxNbIn int `cbor:"-"` // maximum number of inputs for any gate lock sync.Mutex `cbor:"-"` } @@ -58,15 +56,7 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - for i := range b.Circuit { - if b.Circuit[i].IsInput() { - b.nbInputs++ - b.inputWires = append(b.inputWires, i) - } else { - b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) - } - } - + b.Circuit.OutputsList() // for side effects b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { @@ -155,12 +145,12 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return len(b.Circuit.Outputs()) + b.initialize() // circuit metadata + return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // circuit metadata maxLevel := constraint.LevelUnset diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index f1d4720dfa..11662062a4 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -32,12 +32,10 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` - maxNbIn int `cbor:"-"` // maximum number of inputs for any gate lock sync.Mutex `cbor:"-"` } @@ -47,26 +45,18 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) func (b *BlueprintSolve) initialize() { - if b.assignment != nil { + if b.assignments != nil { return } b.lock.Lock() defer b.lock.Unlock() - if b.assignment != nil { + if b.assignments != nil { return // the unlikely event that two or more instructions were competing to initialize the blueprint } // Compute metadata from Circuit - for i := range b.Circuit { - if b.Circuit[i].IsInput() { - b.nbInputs++ - b.inputWires = append(b.inputWires, i) - } else { - b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) - } - } - + b.Circuit.OutputsList() // for side effects b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { @@ -82,9 +72,9 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments = make(WireAssignment, len(b.Circuit)) + for i := range b.assignments { + b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) } } @@ -100,6 +90,17 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra instanceI := int(inst.Calldata[0]) calldata := inst.Calldata[1:] + // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice + if len(b.assignments[0]) <= instanceI { + b.lock.Lock() + defer b.lock.Unlock() + for wI := range b.assignments { + for len(b.assignments[wI]) <= instanceI { + b.assignments[wI] = append(b.assignments[wI], fr.Element{}) + } + } + } + // Process all wires in topological order (circuit is already sorted) for wI := range b.Circuit { w := &b.Circuit[wI] @@ -108,25 +109,25 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(b.assignment[wI][instanceI][:], val[:]) + copy(b.assignments[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceI]) + evaluator.pushInput(&b.assignments[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceI].Set(evaluator.evaluate()) + b.assignments[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires (copy fr.Element to U64 in Montgomery form) for outI, outWI := range b.outputWires { var val constraint.U64 - copy(val[:], b.assignment[outWI][instanceI][:]) + copy(val[:], b.assignments[outWI][instanceI][:]) s.SetValue(uint32(outI+int(inst.WireOffset)), val) } @@ -135,7 +136,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.assignment = nil + b.assignments = nil } // SetNbInstances sets the number of instances for the blueprint @@ -155,12 +156,12 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return len(b.Circuit.Outputs()) + b.initialize() // circuit metadata + return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // circuit metadata maxLevel := constraint.LevelUnset @@ -190,29 +191,6 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (internal use) -func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { - b.lock.Lock() - defer b.lock.Unlock() - - var zero constraint.U64 - if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) - } - - // Copy fr.Element to U64 directly (both in Montgomery form) - var val constraint.U64 - copy(val[:], b.assignment[wireI][instanceI][:]) - return val, nil -} - -// GetAssignments returns all assignments for proving -func (b *BlueprintSolve) GetAssignments() WireAssignment { - b.lock.Lock() - defer b.lock.Unlock() - return b.assignment -} - // BlueprintProve is a BN254-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -234,7 +212,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) // Get assignments from solve blueprint (already in fr.Element form) - assignments := solveBlueprint.GetAssignments() + assignments := solveBlueprint.assignments if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } @@ -369,14 +347,10 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) - // Get assignment - val, err := solveBlueprint.GetAssignment(wireI, instanceI) - if err != nil { - return err - } - + var v constraint.U64 + copy(v[:], solveBlueprint.assignments[wireI][instanceI][:]) // Set output wire - s.SetValue(inst.WireOffset, val) + s.SetValue(inst.WireOffset, v) return nil } diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index b270361b4d..24ab45b9b2 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -35,9 +35,7 @@ type BlueprintSolve struct { assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` - maxNbIn int `cbor:"-"` // maximum number of inputs for any gate lock sync.Mutex `cbor:"-"` } @@ -58,15 +56,7 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - for i := range b.Circuit { - if b.Circuit[i].IsInput() { - b.nbInputs++ - b.inputWires = append(b.inputWires, i) - } else { - b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) - } - } - + b.Circuit.OutputsList() // for side effects b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { @@ -155,12 +145,12 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return len(b.Circuit.Outputs()) + b.initialize() // circuit metadata + return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // circuit metadata maxLevel := constraint.LevelUnset diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 9c9d921a59..c72c8fc31d 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -35,9 +35,7 @@ type BlueprintSolve struct { assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` - inputWires []int `cbor:"-"` outputWires []int `cbor:"-"` - maxNbIn int `cbor:"-"` // maximum number of inputs for any gate lock sync.Mutex `cbor:"-"` } @@ -58,15 +56,7 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - for i := range b.Circuit { - if b.Circuit[i].IsInput() { - b.nbInputs++ - b.inputWires = append(b.inputWires, i) - } else { - b.maxNbIn = max(b.maxNbIn, len(b.Circuit[i].Inputs)) - } - } - + b.Circuit.OutputsList() // for side effects b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { @@ -155,12 +145,12 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - return len(b.Circuit.Outputs()) + b.initialize() // circuit metadata + return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // circuit metadata maxLevel := constraint.LevelUnset diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index d4aad28f5a..50c1eef7ed 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -175,7 +175,7 @@ func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr v.Compress(&calldata) } - // Execute solve solveBlueprint instruction + // Execute solve blueprint instruction outputs := compiler.AddInstruction(c.blueprints.SolveID, calldata) // Track instance count From 5c338129f3cc54f262cb83d40e9233cfa6ef5dae Mon Sep 17 00:00:00 2001 From: Tabaie Date: Fri, 23 Jan 2026 01:53:28 -0600 Subject: [PATCH 135/251] fix: circuit outs --- .../backend/template/gkr/blueprint.go.tmpl | 68 +++++++------------ internal/gkr/bls12-377/blueprint.go | 68 +++++++------------ internal/gkr/bls12-381/blueprint.go | 68 +++++++------------ internal/gkr/bls24-315/blueprint.go | 68 +++++++------------ internal/gkr/bls24-317/blueprint.go | 68 +++++++------------ internal/gkr/bn254/blueprint.go | 2 +- internal/gkr/bw6-633/blueprint.go | 68 +++++++------------ internal/gkr/bw6-761/blueprint.go | 68 +++++++------------ internal/gkr/gkrtypes/types.go | 3 +- std/gkrapi/compile.go | 1 + 10 files changed, 186 insertions(+), 296 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index a7c1fc9dbd..cfba23930a 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -25,7 +25,7 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` @@ -38,13 +38,13 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) func (b *BlueprintSolve) initialize() { - if b.assignment != nil { + if b.assignments != nil { return } b.lock.Lock() defer b.lock.Unlock() - if b.assignment != nil { + if b.assignments != nil { return // the unlikely event that two or more instructions were competing to initialize the blueprint } @@ -65,9 +65,9 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments = make(WireAssignment, len(b.Circuit)) + for i := range b.assignments { + b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) } } @@ -83,6 +83,17 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra instanceI := int(inst.Calldata[0]) calldata := inst.Calldata[1:] + // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice + if len(b.assignments[0]) <= instanceI { + b.lock.Lock() + defer b.lock.Unlock() + for wI := range b.assignments { + for len(b.assignments[wI]) <= instanceI { + b.assignments[wI] = append(b.assignments[wI], {{ .ElementType }}{}) + } + } + } + // Process all wires in topological order (circuit is already sorted) for wI := range b.Circuit { w := &b.Circuit[wI] @@ -91,25 +102,25 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(b.assignment[wI][instanceI][:], val[:]) + copy(b.assignments[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceI]) + evaluator.pushInput(&b.assignments[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceI].Set(evaluator.evaluate()) + b.assignments[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires (copy fr.Element to U64 in Montgomery form) for outI, outWI := range b.outputWires { var val constraint.U64 - copy(val[:], b.assignment[outWI][instanceI][:]) + copy(val[:], b.assignments[outWI][instanceI][:]) s.SetValue(uint32(outI+int(inst.WireOffset)), val) } @@ -118,7 +129,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.assignment = nil + b.assignments = nil } // SetNbInstances sets the number of instances for the blueprint @@ -173,29 +184,6 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (internal use) -func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { - b.lock.Lock() - defer b.lock.Unlock() - - var zero constraint.U64 - if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) - } - - // Copy fr.Element to U64 directly (both in Montgomery form) - var val constraint.U64 - copy(val[:], b.assignment[wireI][instanceI][:]) - return val, nil -} - -// GetAssignments returns all assignments for proving -func (b *BlueprintSolve) GetAssignments() WireAssignment { - b.lock.Lock() - defer b.lock.Unlock() - return b.assignment -} - // BlueprintProve is a {{.FieldID}}-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -217,7 +205,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) // Get assignments from solve blueprint (already in {{ .ElementType }} form) - assignments := solveBlueprint.GetAssignments() + assignments := solveBlueprint.assignments if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } @@ -351,14 +339,10 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) - // Get assignment - val, err := solveBlueprint.GetAssignment(wireI, instanceI) - if err != nil { - return err - } - + var v constraint.U64 + copy(v[:], solveBlueprint.assignments[wireI][instanceI][:]) // Set output wire - s.SetValue(inst.WireOffset, val) + s.SetValue(inst.WireOffset, v) return nil } diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 2e55e43f56..0e47f41841 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -32,7 +32,7 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + assignments WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` @@ -45,13 +45,13 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) func (b *BlueprintSolve) initialize() { - if b.assignment != nil { + if b.assignments != nil { return } b.lock.Lock() defer b.lock.Unlock() - if b.assignment != nil { + if b.assignments != nil { return // the unlikely event that two or more instructions were competing to initialize the blueprint } @@ -72,9 +72,9 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments = make(WireAssignment, len(b.Circuit)) + for i := range b.assignments { + b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) } } @@ -90,6 +90,17 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra instanceI := int(inst.Calldata[0]) calldata := inst.Calldata[1:] + // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice + if len(b.assignments[0]) <= instanceI { + b.lock.Lock() + defer b.lock.Unlock() + for wI := range b.assignments { + for len(b.assignments[wI]) <= instanceI { + b.assignments[wI] = append(b.assignments[wI], fr.Element{}) + } + } + } + // Process all wires in topological order (circuit is already sorted) for wI := range b.Circuit { w := &b.Circuit[wI] @@ -98,25 +109,25 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(b.assignment[wI][instanceI][:], val[:]) + copy(b.assignments[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceI]) + evaluator.pushInput(&b.assignments[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceI].Set(evaluator.evaluate()) + b.assignments[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires (copy fr.Element to U64 in Montgomery form) for outI, outWI := range b.outputWires { var val constraint.U64 - copy(val[:], b.assignment[outWI][instanceI][:]) + copy(val[:], b.assignments[outWI][instanceI][:]) s.SetValue(uint32(outI+int(inst.WireOffset)), val) } @@ -125,7 +136,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.assignment = nil + b.assignments = nil } // SetNbInstances sets the number of instances for the blueprint @@ -180,29 +191,6 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (internal use) -func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { - b.lock.Lock() - defer b.lock.Unlock() - - var zero constraint.U64 - if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) - } - - // Copy fr.Element to U64 directly (both in Montgomery form) - var val constraint.U64 - copy(val[:], b.assignment[wireI][instanceI][:]) - return val, nil -} - -// GetAssignments returns all assignments for proving -func (b *BlueprintSolve) GetAssignments() WireAssignment { - b.lock.Lock() - defer b.lock.Unlock() - return b.assignment -} - // BlueprintProve is a BLS12_377-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -224,7 +212,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) // Get assignments from solve blueprint (already in fr.Element form) - assignments := solveBlueprint.GetAssignments() + assignments := solveBlueprint.assignments if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } @@ -359,14 +347,10 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) - // Get assignment - val, err := solveBlueprint.GetAssignment(wireI, instanceI) - if err != nil { - return err - } - + var v constraint.U64 + copy(v[:], solveBlueprint.assignments[wireI][instanceI][:]) // Set output wire - s.SetValue(inst.WireOffset, val) + s.SetValue(inst.WireOffset, v) return nil } diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index b085b4c4eb..89e42da8e0 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -32,7 +32,7 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + assignments WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` @@ -45,13 +45,13 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) func (b *BlueprintSolve) initialize() { - if b.assignment != nil { + if b.assignments != nil { return } b.lock.Lock() defer b.lock.Unlock() - if b.assignment != nil { + if b.assignments != nil { return // the unlikely event that two or more instructions were competing to initialize the blueprint } @@ -72,9 +72,9 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments = make(WireAssignment, len(b.Circuit)) + for i := range b.assignments { + b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) } } @@ -90,6 +90,17 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra instanceI := int(inst.Calldata[0]) calldata := inst.Calldata[1:] + // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice + if len(b.assignments[0]) <= instanceI { + b.lock.Lock() + defer b.lock.Unlock() + for wI := range b.assignments { + for len(b.assignments[wI]) <= instanceI { + b.assignments[wI] = append(b.assignments[wI], fr.Element{}) + } + } + } + // Process all wires in topological order (circuit is already sorted) for wI := range b.Circuit { w := &b.Circuit[wI] @@ -98,25 +109,25 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(b.assignment[wI][instanceI][:], val[:]) + copy(b.assignments[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceI]) + evaluator.pushInput(&b.assignments[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceI].Set(evaluator.evaluate()) + b.assignments[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires (copy fr.Element to U64 in Montgomery form) for outI, outWI := range b.outputWires { var val constraint.U64 - copy(val[:], b.assignment[outWI][instanceI][:]) + copy(val[:], b.assignments[outWI][instanceI][:]) s.SetValue(uint32(outI+int(inst.WireOffset)), val) } @@ -125,7 +136,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.assignment = nil + b.assignments = nil } // SetNbInstances sets the number of instances for the blueprint @@ -180,29 +191,6 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (internal use) -func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { - b.lock.Lock() - defer b.lock.Unlock() - - var zero constraint.U64 - if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) - } - - // Copy fr.Element to U64 directly (both in Montgomery form) - var val constraint.U64 - copy(val[:], b.assignment[wireI][instanceI][:]) - return val, nil -} - -// GetAssignments returns all assignments for proving -func (b *BlueprintSolve) GetAssignments() WireAssignment { - b.lock.Lock() - defer b.lock.Unlock() - return b.assignment -} - // BlueprintProve is a BLS12_381-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -224,7 +212,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) // Get assignments from solve blueprint (already in fr.Element form) - assignments := solveBlueprint.GetAssignments() + assignments := solveBlueprint.assignments if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } @@ -359,14 +347,10 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) - // Get assignment - val, err := solveBlueprint.GetAssignment(wireI, instanceI) - if err != nil { - return err - } - + var v constraint.U64 + copy(v[:], solveBlueprint.assignments[wireI][instanceI][:]) // Set output wire - s.SetValue(inst.WireOffset, val) + s.SetValue(inst.WireOffset, v) return nil } diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index c042993226..2493e511fd 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -32,7 +32,7 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + assignments WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` @@ -45,13 +45,13 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) func (b *BlueprintSolve) initialize() { - if b.assignment != nil { + if b.assignments != nil { return } b.lock.Lock() defer b.lock.Unlock() - if b.assignment != nil { + if b.assignments != nil { return // the unlikely event that two or more instructions were competing to initialize the blueprint } @@ -72,9 +72,9 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments = make(WireAssignment, len(b.Circuit)) + for i := range b.assignments { + b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) } } @@ -90,6 +90,17 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra instanceI := int(inst.Calldata[0]) calldata := inst.Calldata[1:] + // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice + if len(b.assignments[0]) <= instanceI { + b.lock.Lock() + defer b.lock.Unlock() + for wI := range b.assignments { + for len(b.assignments[wI]) <= instanceI { + b.assignments[wI] = append(b.assignments[wI], fr.Element{}) + } + } + } + // Process all wires in topological order (circuit is already sorted) for wI := range b.Circuit { w := &b.Circuit[wI] @@ -98,25 +109,25 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(b.assignment[wI][instanceI][:], val[:]) + copy(b.assignments[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceI]) + evaluator.pushInput(&b.assignments[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceI].Set(evaluator.evaluate()) + b.assignments[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires (copy fr.Element to U64 in Montgomery form) for outI, outWI := range b.outputWires { var val constraint.U64 - copy(val[:], b.assignment[outWI][instanceI][:]) + copy(val[:], b.assignments[outWI][instanceI][:]) s.SetValue(uint32(outI+int(inst.WireOffset)), val) } @@ -125,7 +136,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.assignment = nil + b.assignments = nil } // SetNbInstances sets the number of instances for the blueprint @@ -180,29 +191,6 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (internal use) -func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { - b.lock.Lock() - defer b.lock.Unlock() - - var zero constraint.U64 - if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) - } - - // Copy fr.Element to U64 directly (both in Montgomery form) - var val constraint.U64 - copy(val[:], b.assignment[wireI][instanceI][:]) - return val, nil -} - -// GetAssignments returns all assignments for proving -func (b *BlueprintSolve) GetAssignments() WireAssignment { - b.lock.Lock() - defer b.lock.Unlock() - return b.assignment -} - // BlueprintProve is a BLS24_315-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -224,7 +212,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) // Get assignments from solve blueprint (already in fr.Element form) - assignments := solveBlueprint.GetAssignments() + assignments := solveBlueprint.assignments if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } @@ -359,14 +347,10 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) - // Get assignment - val, err := solveBlueprint.GetAssignment(wireI, instanceI) - if err != nil { - return err - } - + var v constraint.U64 + copy(v[:], solveBlueprint.assignments[wireI][instanceI][:]) // Set output wire - s.SetValue(inst.WireOffset, val) + s.SetValue(inst.WireOffset, v) return nil } diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 7cdde6e5e5..6d42a2dd3c 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -32,7 +32,7 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + assignments WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` @@ -45,13 +45,13 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) func (b *BlueprintSolve) initialize() { - if b.assignment != nil { + if b.assignments != nil { return } b.lock.Lock() defer b.lock.Unlock() - if b.assignment != nil { + if b.assignments != nil { return // the unlikely event that two or more instructions were competing to initialize the blueprint } @@ -72,9 +72,9 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments = make(WireAssignment, len(b.Circuit)) + for i := range b.assignments { + b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) } } @@ -90,6 +90,17 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra instanceI := int(inst.Calldata[0]) calldata := inst.Calldata[1:] + // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice + if len(b.assignments[0]) <= instanceI { + b.lock.Lock() + defer b.lock.Unlock() + for wI := range b.assignments { + for len(b.assignments[wI]) <= instanceI { + b.assignments[wI] = append(b.assignments[wI], fr.Element{}) + } + } + } + // Process all wires in topological order (circuit is already sorted) for wI := range b.Circuit { w := &b.Circuit[wI] @@ -98,25 +109,25 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(b.assignment[wI][instanceI][:], val[:]) + copy(b.assignments[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceI]) + evaluator.pushInput(&b.assignments[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceI].Set(evaluator.evaluate()) + b.assignments[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires (copy fr.Element to U64 in Montgomery form) for outI, outWI := range b.outputWires { var val constraint.U64 - copy(val[:], b.assignment[outWI][instanceI][:]) + copy(val[:], b.assignments[outWI][instanceI][:]) s.SetValue(uint32(outI+int(inst.WireOffset)), val) } @@ -125,7 +136,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.assignment = nil + b.assignments = nil } // SetNbInstances sets the number of instances for the blueprint @@ -180,29 +191,6 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (internal use) -func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { - b.lock.Lock() - defer b.lock.Unlock() - - var zero constraint.U64 - if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) - } - - // Copy fr.Element to U64 directly (both in Montgomery form) - var val constraint.U64 - copy(val[:], b.assignment[wireI][instanceI][:]) - return val, nil -} - -// GetAssignments returns all assignments for proving -func (b *BlueprintSolve) GetAssignments() WireAssignment { - b.lock.Lock() - defer b.lock.Unlock() - return b.assignment -} - // BlueprintProve is a BLS24_317-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -224,7 +212,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) // Get assignments from solve blueprint (already in fr.Element form) - assignments := solveBlueprint.GetAssignments() + assignments := solveBlueprint.assignments if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } @@ -359,14 +347,10 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) - // Get assignment - val, err := solveBlueprint.GetAssignment(wireI, instanceI) - if err != nil { - return err - } - + var v constraint.U64 + copy(v[:], solveBlueprint.assignments[wireI][instanceI][:]) // Set output wire - s.SetValue(inst.WireOffset, val) + s.SetValue(inst.WireOffset, v) return nil } diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 11662062a4..e566bae325 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -32,7 +32,7 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` + assignments WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 24ab45b9b2..81044753c2 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -32,7 +32,7 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + assignments WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` @@ -45,13 +45,13 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) func (b *BlueprintSolve) initialize() { - if b.assignment != nil { + if b.assignments != nil { return } b.lock.Lock() defer b.lock.Unlock() - if b.assignment != nil { + if b.assignments != nil { return // the unlikely event that two or more instructions were competing to initialize the blueprint } @@ -72,9 +72,9 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments = make(WireAssignment, len(b.Circuit)) + for i := range b.assignments { + b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) } } @@ -90,6 +90,17 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra instanceI := int(inst.Calldata[0]) calldata := inst.Calldata[1:] + // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice + if len(b.assignments[0]) <= instanceI { + b.lock.Lock() + defer b.lock.Unlock() + for wI := range b.assignments { + for len(b.assignments[wI]) <= instanceI { + b.assignments[wI] = append(b.assignments[wI], fr.Element{}) + } + } + } + // Process all wires in topological order (circuit is already sorted) for wI := range b.Circuit { w := &b.Circuit[wI] @@ -98,25 +109,25 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(b.assignment[wI][instanceI][:], val[:]) + copy(b.assignments[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceI]) + evaluator.pushInput(&b.assignments[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceI].Set(evaluator.evaluate()) + b.assignments[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires (copy fr.Element to U64 in Montgomery form) for outI, outWI := range b.outputWires { var val constraint.U64 - copy(val[:], b.assignment[outWI][instanceI][:]) + copy(val[:], b.assignments[outWI][instanceI][:]) s.SetValue(uint32(outI+int(inst.WireOffset)), val) } @@ -125,7 +136,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.assignment = nil + b.assignments = nil } // SetNbInstances sets the number of instances for the blueprint @@ -180,29 +191,6 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (internal use) -func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { - b.lock.Lock() - defer b.lock.Unlock() - - var zero constraint.U64 - if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) - } - - // Copy fr.Element to U64 directly (both in Montgomery form) - var val constraint.U64 - copy(val[:], b.assignment[wireI][instanceI][:]) - return val, nil -} - -// GetAssignments returns all assignments for proving -func (b *BlueprintSolve) GetAssignments() WireAssignment { - b.lock.Lock() - defer b.lock.Unlock() - return b.assignment -} - // BlueprintProve is a BW6_633-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -224,7 +212,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) // Get assignments from solve blueprint (already in fr.Element form) - assignments := solveBlueprint.GetAssignments() + assignments := solveBlueprint.assignments if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } @@ -359,14 +347,10 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) - // Get assignment - val, err := solveBlueprint.GetAssignment(wireI, instanceI) - if err != nil { - return err - } - + var v constraint.U64 + copy(v[:], solveBlueprint.assignments[wireI][instanceI][:]) // Set output wire - s.SetValue(inst.WireOffset, val) + s.SetValue(inst.WireOffset, v) return nil } diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index c72c8fc31d..f8bbd36a86 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -32,7 +32,7 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignment WireAssignment `cbor:"-"` // []polynomial.MultiLin + assignments WireAssignment `cbor:"-"` // []polynomial.MultiLin evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` @@ -45,13 +45,13 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) func (b *BlueprintSolve) initialize() { - if b.assignment != nil { + if b.assignments != nil { return } b.lock.Lock() defer b.lock.Unlock() - if b.assignment != nil { + if b.assignments != nil { return // the unlikely event that two or more instructions were competing to initialize the blueprint } @@ -72,9 +72,9 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignment = make(WireAssignment, len(b.Circuit)) - for i := range b.assignment { - b.assignment[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments = make(WireAssignment, len(b.Circuit)) + for i := range b.assignments { + b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) } } @@ -90,6 +90,17 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra instanceI := int(inst.Calldata[0]) calldata := inst.Calldata[1:] + // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice + if len(b.assignments[0]) <= instanceI { + b.lock.Lock() + defer b.lock.Unlock() + for wI := range b.assignments { + for len(b.assignments[wI]) <= instanceI { + b.assignments[wI] = append(b.assignments[wI], fr.Element{}) + } + } + } + // Process all wires in topological order (circuit is already sorted) for wI := range b.Circuit { w := &b.Circuit[wI] @@ -98,25 +109,25 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra val, delta := s.Read(calldata) calldata = calldata[delta:] // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(b.assignment[wI][instanceI][:], val[:]) + copy(b.assignments[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator evaluator := &ce.evaluators[wI] // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignment[inWI][instanceI]) + evaluator.pushInput(&b.assignments[inWI][instanceI]) } // Evaluate the gate - b.assignment[wI][instanceI].Set(evaluator.evaluate()) + b.assignments[wI][instanceI].Set(evaluator.evaluate()) } } // Set output wires (copy fr.Element to U64 in Montgomery form) for outI, outWI := range b.outputWires { var val constraint.U64 - copy(val[:], b.assignment[outWI][instanceI][:]) + copy(val[:], b.assignments[outWI][instanceI][:]) s.SetValue(uint32(outI+int(inst.WireOffset)), val) } @@ -125,7 +136,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.assignment = nil + b.assignments = nil } // SetNbInstances sets the number of instances for the blueprint @@ -180,29 +191,6 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } -// GetAssignment returns the assignment for a specific wire and instance (internal use) -func (b *BlueprintSolve) GetAssignment(wireI, instanceI int) (constraint.U64, error) { - b.lock.Lock() - defer b.lock.Unlock() - - var zero constraint.U64 - if wireI >= len(b.assignment) || instanceI >= len(b.assignment[wireI]) { - return zero, fmt.Errorf("wire %d instance %d out of bounds", wireI, instanceI) - } - - // Copy fr.Element to U64 directly (both in Montgomery form) - var val constraint.U64 - copy(val[:], b.assignment[wireI][instanceI][:]) - return val, nil -} - -// GetAssignments returns all assignments for proving -func (b *BlueprintSolve) GetAssignments() WireAssignment { - b.lock.Lock() - defer b.lock.Unlock() - return b.assignment -} - // BlueprintProve is a BW6_761-specific blueprint for generating GKR proofs. type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID @@ -224,7 +212,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) // Get assignments from solve blueprint (already in fr.Element form) - assignments := solveBlueprint.GetAssignments() + assignments := solveBlueprint.assignments if len(assignments) == 0 { return fmt.Errorf("no assignments available for proving") } @@ -359,14 +347,10 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) - // Get assignment - val, err := solveBlueprint.GetAssignment(wireI, instanceI) - if err != nil { - return err - } - + var v constraint.U64 + copy(v[:], solveBlueprint.assignments[wireI][instanceI][:]) // Set output wire - s.SetValue(inst.WireOffset, val) + s.SetValue(inst.WireOffset, v) return nil } diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 960606189c..1eb353ad01 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -236,7 +236,8 @@ func (c Circuit[GateExecutable]) Inputs() []int { return res } -// Outputs returns the list of output wire indices +// Outputs returns the list of output wire indices. +// It requires the NbUniqueOutput values to have been set. func (c Circuit[GateExecutable]) Outputs() []int { res := make([]int, 0, len(c)) for i := range c { diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 50c1eef7ed..772f329f53 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -121,6 +121,7 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C // Use circuit helper methods for inputs and outputs inputIndices := res.circuit.Inputs() + res.circuit.OutputsList() // for side effects outputIndices := res.circuit.Outputs() if len(inputIndices) == len(res.circuit) { From f68fae7fdedfa03c26bb7991593df0770677663a Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 30 Jan 2026 17:08:26 -0600 Subject: [PATCH 136/251] style: formatting --- internal/gkr/bls12-377/blueprint.go | 2 +- internal/gkr/bls12-377/gate_testing.go | 13 +-- internal/gkr/bls12-377/gkr.go | 117 ++++++++++++------------ internal/gkr/bls12-381/blueprint.go | 2 +- internal/gkr/bls12-381/gate_testing.go | 13 +-- internal/gkr/bls12-381/gkr.go | 117 ++++++++++++------------ internal/gkr/bls24-315/blueprint.go | 2 +- internal/gkr/bls24-315/gate_testing.go | 13 +-- internal/gkr/bls24-315/gkr.go | 117 ++++++++++++------------ internal/gkr/bls24-317/blueprint.go | 2 +- internal/gkr/bls24-317/gate_testing.go | 13 +-- internal/gkr/bls24-317/gkr.go | 117 ++++++++++++------------ internal/gkr/bn254/blueprint.go | 2 +- internal/gkr/bn254/gate_testing.go | 13 +-- internal/gkr/bn254/gkr.go | 117 ++++++++++++------------ internal/gkr/bw6-633/blueprint.go | 2 +- internal/gkr/bw6-633/gate_testing.go | 13 +-- internal/gkr/bw6-633/gkr.go | 117 ++++++++++++------------ internal/gkr/bw6-761/blueprint.go | 2 +- internal/gkr/bw6-761/gate_testing.go | 13 +-- internal/gkr/bw6-761/gkr.go | 117 ++++++++++++------------ internal/gkr/gkrtypes/types.go | 15 --- internal/gkr/small_rational/gkr.go | 121 ++++++++++++------------- 23 files changed, 514 insertions(+), 546 deletions(-) diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 0e47f41841..bd6e885d5f 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -32,7 +32,7 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` // []polynomial.MultiLin + assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` diff --git a/internal/gkr/bls12-377/gate_testing.go b/internal/gkr/bls12-377/gate_testing.go index cc7c1324ab..44252c1fa5 100644 --- a/internal/gkr/bls12-377/gate_testing.go +++ b/internal/gkr/bls12-377/gate_testing.go @@ -1,20 +1,21 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Copyright 2020-2026 Consensys Software Inc. - // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Code generated by gnark DO NOT EDIT package gkr import ( "fmt" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "slices" + "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/polynomial" - "slices" ) type GateTester struct { @@ -140,7 +141,7 @@ func (t *GateTester) FindDegree(max int) (int, error) { } func (t *GateTester) VerifyDegree(claimedDegree int) error { - if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { + if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -158,4 +159,4 @@ func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) -} \ No newline at end of file +} diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 9a93845cf8..5d8eeca4d3 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -1,7 +1,6 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Copyright 2020-2026 Consensys Software Inc. - // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Code generated by gnark DO NOT EDIT package gkr @@ -9,13 +8,14 @@ package gkr import ( "errors" "fmt" + "math/big" + "strconv" + "sync" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "math/big" - "strconv" - "sync" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -28,7 +28,6 @@ type ( // The goal is to prove/verify evaluations of many instances of the same circuit - // WireAssignment is assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin @@ -38,9 +37,9 @@ type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for eac // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the checking of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckLazyClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly + wireI int // the wire for which we are making the claim, with value w + evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w + claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly manager *claimsManager // WARNING: Circular references } @@ -96,7 +95,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire injection, injectionLeftInv := - e.manager.wires.ClaimPropagationInfo(e.wireI) + e.manager.wires.ClaimPropagationInfo(e.wireI) if len(injection) != len(uniqueInputEvaluations) { return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) @@ -126,7 +125,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the proving of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckClaims struct { - wireI int // the wire for which we are making the claim, with value w + wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ) manager *claimsManager @@ -152,33 +151,33 @@ func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { // and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. // The output of combine is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) polynomial.Polynomial { -varsNum := c.varsNum() + varsNum := c.varsNum() eqLength := 1 << varsNum -claimsNum := c.claimsNum() -// initialize the eq tables ( E ) -c.eq = c.manager.memPool.Make(eqLength) + claimsNum := c.claimsNum() + // initialize the eq tables ( E ) + c.eq = c.manager.memPool.Make(eqLength) -c.eq[0].SetOne() -c.eq.Eq(c.evaluationPoints[0]) + c.eq[0].SetOne() + c.eq.Eq(c.evaluationPoints[0]) -// E := eq(x₀, -) -newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) -aI := combinationCoeff + // E := eq(x₀, -) + newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) + aI := combinationCoeff -// E += cⁱ eq(xᵢ, -) -for k := 1; k < claimsNum; k++ { -newEq[0].Set(&aI) + // E += cⁱ eq(xᵢ, -) + for k := 1; k < claimsNum; k++ { + newEq[0].Set(&aI) -c.eqAcc(c.eq, newEq,c.evaluationPoints[k]) + c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) -if k+1 < claimsNum { -aI.Mul(&aI, &combinationCoeff) -} -} + if k+1 < claimsNum { + aI.Mul(&aI, &combinationCoeff) + } + } -c.manager.memPool.Dump(newEq) + c.manager.memPool.Dump(newEq) -return c.computeGJ() + return c.computeGJ() } // eqAcc sets m to an eq table at q and then adds it to e. @@ -197,7 +196,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } } else { @@ -206,7 +205,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } }, 1024).Wait() @@ -220,7 +219,6 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E }, 512).Wait() } - // computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). // the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. @@ -234,17 +232,17 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // they are linear in Xⱼ. // So for f ∈ { E(r₁, ..., Xⱼ, h...) } ∪ {wᵢ(r₁, ..., Xⱼ, h...) }, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables. // ml are such multilinear polynomials the evaluations of which over different values of Xⱼ are computed in this stepwise manner. - ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube + ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube ml[0] = c.eq copy(ml[1:], c.input) - sumSize := len(c.eq) / 2; // the range of h, over which we sum + sumSize := len(c.eq) / 2 // the range of h, over which we sum // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called gJ := make([]fr.Element, degGJ) var mu sync.Mutex - computeAll := func(start, end int) { // compute method to allow parallelization across instances + computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element evaluator := c.gateEvaluatorPool.get() @@ -259,19 +257,19 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]fr.Element, degGJ*len(ml)) - for h := start; h < end; h++ { // h counts across instances + for h := start; h < end; h++ { // h counts across instances evalAt1Index := sumSize + h for k := range ml { // d = 0 - mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. - step.Sub(&mlEvals[k], &ml[k][h])// step = ml[k](1) - ml[k](0) + mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. + step.Sub(&mlEvals[k], &ml[k][h]) // step = ml[k](1) - ml[k](0) for d := 1; d < degGJ; d++ { mlEvals[d*len(ml)+k].Add(&mlEvals[(d-1)*len(ml)+k], &step) } } - eIndex := 0 // index for where the current eq term is + eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { // Push gate inputs @@ -286,7 +284,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { } mu.Lock() for i := range gJ { - gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum + gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum } mu.Unlock() } @@ -435,7 +433,7 @@ type settings struct { type Option func(*settings) func WithPool(pool *polynomial.Pool) Option { - return func (options *settings) { + return func(options *settings) { options.pool = pool } } @@ -449,14 +447,13 @@ func WithSortedCircuit(sorted []*Wire) Option { func WithWorkers(workers *utils.WorkerPool) Option { return func(options *settings) { options.workers = workers - } + } } - func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error - for _, option := range options { + for _, option := range options { option(&o) } @@ -659,7 +656,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } } else if err = sumcheckVerify( claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { // incorporate prover claims about w's input into the transcript + ); err == nil { // incorporate prover claims about w's input into the transcript baseChallenge = make([][]byte, len(proofW.finalEvalProof)) for j := range baseChallenge { baseChallenge[j] = proofW.finalEvalProof[j].Marshal() @@ -748,15 +745,15 @@ type gateEvaluator struct { gate *gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs - nbIn int // number of inputs expected + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, + gate: gate, + nbIn: nbIn, frameSize: gate.NbConstants(), } if len(elementPool) > 0 { @@ -845,19 +842,19 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.GateBytecode - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - elementPool *polynomial.Pool + gate *gkrtypes.GateBytecode + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - elementPool: elementPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -895,4 +892,4 @@ func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.elementPool.Dump(e.vars) } -} \ No newline at end of file +} diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 89e42da8e0..bd7aa58f11 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -32,7 +32,7 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` // []polynomial.MultiLin + assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` diff --git a/internal/gkr/bls12-381/gate_testing.go b/internal/gkr/bls12-381/gate_testing.go index b77bf8d3a7..ebc9fc4fb0 100644 --- a/internal/gkr/bls12-381/gate_testing.go +++ b/internal/gkr/bls12-381/gate_testing.go @@ -1,20 +1,21 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Copyright 2020-2026 Consensys Software Inc. - // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Code generated by gnark DO NOT EDIT package gkr import ( "fmt" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "slices" + "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/polynomial" - "slices" ) type GateTester struct { @@ -140,7 +141,7 @@ func (t *GateTester) FindDegree(max int) (int, error) { } func (t *GateTester) VerifyDegree(claimedDegree int) error { - if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { + if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -158,4 +159,4 @@ func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) -} \ No newline at end of file +} diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index c2a6da3059..d7211aa81f 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -1,7 +1,6 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Copyright 2020-2026 Consensys Software Inc. - // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Code generated by gnark DO NOT EDIT package gkr @@ -9,13 +8,14 @@ package gkr import ( "errors" "fmt" + "math/big" + "strconv" + "sync" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "math/big" - "strconv" - "sync" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -28,7 +28,6 @@ type ( // The goal is to prove/verify evaluations of many instances of the same circuit - // WireAssignment is assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin @@ -38,9 +37,9 @@ type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for eac // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the checking of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckLazyClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly + wireI int // the wire for which we are making the claim, with value w + evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w + claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly manager *claimsManager // WARNING: Circular references } @@ -96,7 +95,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire injection, injectionLeftInv := - e.manager.wires.ClaimPropagationInfo(e.wireI) + e.manager.wires.ClaimPropagationInfo(e.wireI) if len(injection) != len(uniqueInputEvaluations) { return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) @@ -126,7 +125,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the proving of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckClaims struct { - wireI int // the wire for which we are making the claim, with value w + wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ) manager *claimsManager @@ -152,33 +151,33 @@ func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { // and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. // The output of combine is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) polynomial.Polynomial { -varsNum := c.varsNum() + varsNum := c.varsNum() eqLength := 1 << varsNum -claimsNum := c.claimsNum() -// initialize the eq tables ( E ) -c.eq = c.manager.memPool.Make(eqLength) + claimsNum := c.claimsNum() + // initialize the eq tables ( E ) + c.eq = c.manager.memPool.Make(eqLength) -c.eq[0].SetOne() -c.eq.Eq(c.evaluationPoints[0]) + c.eq[0].SetOne() + c.eq.Eq(c.evaluationPoints[0]) -// E := eq(x₀, -) -newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) -aI := combinationCoeff + // E := eq(x₀, -) + newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) + aI := combinationCoeff -// E += cⁱ eq(xᵢ, -) -for k := 1; k < claimsNum; k++ { -newEq[0].Set(&aI) + // E += cⁱ eq(xᵢ, -) + for k := 1; k < claimsNum; k++ { + newEq[0].Set(&aI) -c.eqAcc(c.eq, newEq,c.evaluationPoints[k]) + c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) -if k+1 < claimsNum { -aI.Mul(&aI, &combinationCoeff) -} -} + if k+1 < claimsNum { + aI.Mul(&aI, &combinationCoeff) + } + } -c.manager.memPool.Dump(newEq) + c.manager.memPool.Dump(newEq) -return c.computeGJ() + return c.computeGJ() } // eqAcc sets m to an eq table at q and then adds it to e. @@ -197,7 +196,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } } else { @@ -206,7 +205,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } }, 1024).Wait() @@ -220,7 +219,6 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E }, 512).Wait() } - // computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). // the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. @@ -234,17 +232,17 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // they are linear in Xⱼ. // So for f ∈ { E(r₁, ..., Xⱼ, h...) } ∪ {wᵢ(r₁, ..., Xⱼ, h...) }, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables. // ml are such multilinear polynomials the evaluations of which over different values of Xⱼ are computed in this stepwise manner. - ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube + ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube ml[0] = c.eq copy(ml[1:], c.input) - sumSize := len(c.eq) / 2; // the range of h, over which we sum + sumSize := len(c.eq) / 2 // the range of h, over which we sum // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called gJ := make([]fr.Element, degGJ) var mu sync.Mutex - computeAll := func(start, end int) { // compute method to allow parallelization across instances + computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element evaluator := c.gateEvaluatorPool.get() @@ -259,19 +257,19 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]fr.Element, degGJ*len(ml)) - for h := start; h < end; h++ { // h counts across instances + for h := start; h < end; h++ { // h counts across instances evalAt1Index := sumSize + h for k := range ml { // d = 0 - mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. - step.Sub(&mlEvals[k], &ml[k][h])// step = ml[k](1) - ml[k](0) + mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. + step.Sub(&mlEvals[k], &ml[k][h]) // step = ml[k](1) - ml[k](0) for d := 1; d < degGJ; d++ { mlEvals[d*len(ml)+k].Add(&mlEvals[(d-1)*len(ml)+k], &step) } } - eIndex := 0 // index for where the current eq term is + eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { // Push gate inputs @@ -286,7 +284,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { } mu.Lock() for i := range gJ { - gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum + gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum } mu.Unlock() } @@ -435,7 +433,7 @@ type settings struct { type Option func(*settings) func WithPool(pool *polynomial.Pool) Option { - return func (options *settings) { + return func(options *settings) { options.pool = pool } } @@ -449,14 +447,13 @@ func WithSortedCircuit(sorted []*Wire) Option { func WithWorkers(workers *utils.WorkerPool) Option { return func(options *settings) { options.workers = workers - } + } } - func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error - for _, option := range options { + for _, option := range options { option(&o) } @@ -659,7 +656,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } } else if err = sumcheckVerify( claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { // incorporate prover claims about w's input into the transcript + ); err == nil { // incorporate prover claims about w's input into the transcript baseChallenge = make([][]byte, len(proofW.finalEvalProof)) for j := range baseChallenge { baseChallenge[j] = proofW.finalEvalProof[j].Marshal() @@ -748,15 +745,15 @@ type gateEvaluator struct { gate *gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs - nbIn int // number of inputs expected + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, + gate: gate, + nbIn: nbIn, frameSize: gate.NbConstants(), } if len(elementPool) > 0 { @@ -845,19 +842,19 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.GateBytecode - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - elementPool *polynomial.Pool + gate *gkrtypes.GateBytecode + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - elementPool: elementPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -895,4 +892,4 @@ func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.elementPool.Dump(e.vars) } -} \ No newline at end of file +} diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index 2493e511fd..7318a7f60f 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -32,7 +32,7 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` // []polynomial.MultiLin + assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` diff --git a/internal/gkr/bls24-315/gate_testing.go b/internal/gkr/bls24-315/gate_testing.go index a389ecb7cc..d35b6876a0 100644 --- a/internal/gkr/bls24-315/gate_testing.go +++ b/internal/gkr/bls24-315/gate_testing.go @@ -1,20 +1,21 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Copyright 2020-2026 Consensys Software Inc. - // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Code generated by gnark DO NOT EDIT package gkr import ( "fmt" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "slices" + "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/polynomial" - "slices" ) type GateTester struct { @@ -140,7 +141,7 @@ func (t *GateTester) FindDegree(max int) (int, error) { } func (t *GateTester) VerifyDegree(claimedDegree int) error { - if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { + if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -158,4 +159,4 @@ func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) -} \ No newline at end of file +} diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 1d08ce2e7e..6e6f3d1fa0 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -1,7 +1,6 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Copyright 2020-2026 Consensys Software Inc. - // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Code generated by gnark DO NOT EDIT package gkr @@ -9,13 +8,14 @@ package gkr import ( "errors" "fmt" + "math/big" + "strconv" + "sync" + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "math/big" - "strconv" - "sync" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -28,7 +28,6 @@ type ( // The goal is to prove/verify evaluations of many instances of the same circuit - // WireAssignment is assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin @@ -38,9 +37,9 @@ type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for eac // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the checking of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckLazyClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly + wireI int // the wire for which we are making the claim, with value w + evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w + claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly manager *claimsManager // WARNING: Circular references } @@ -96,7 +95,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire injection, injectionLeftInv := - e.manager.wires.ClaimPropagationInfo(e.wireI) + e.manager.wires.ClaimPropagationInfo(e.wireI) if len(injection) != len(uniqueInputEvaluations) { return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) @@ -126,7 +125,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the proving of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckClaims struct { - wireI int // the wire for which we are making the claim, with value w + wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ) manager *claimsManager @@ -152,33 +151,33 @@ func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { // and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. // The output of combine is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) polynomial.Polynomial { -varsNum := c.varsNum() + varsNum := c.varsNum() eqLength := 1 << varsNum -claimsNum := c.claimsNum() -// initialize the eq tables ( E ) -c.eq = c.manager.memPool.Make(eqLength) + claimsNum := c.claimsNum() + // initialize the eq tables ( E ) + c.eq = c.manager.memPool.Make(eqLength) -c.eq[0].SetOne() -c.eq.Eq(c.evaluationPoints[0]) + c.eq[0].SetOne() + c.eq.Eq(c.evaluationPoints[0]) -// E := eq(x₀, -) -newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) -aI := combinationCoeff + // E := eq(x₀, -) + newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) + aI := combinationCoeff -// E += cⁱ eq(xᵢ, -) -for k := 1; k < claimsNum; k++ { -newEq[0].Set(&aI) + // E += cⁱ eq(xᵢ, -) + for k := 1; k < claimsNum; k++ { + newEq[0].Set(&aI) -c.eqAcc(c.eq, newEq,c.evaluationPoints[k]) + c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) -if k+1 < claimsNum { -aI.Mul(&aI, &combinationCoeff) -} -} + if k+1 < claimsNum { + aI.Mul(&aI, &combinationCoeff) + } + } -c.manager.memPool.Dump(newEq) + c.manager.memPool.Dump(newEq) -return c.computeGJ() + return c.computeGJ() } // eqAcc sets m to an eq table at q and then adds it to e. @@ -197,7 +196,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } } else { @@ -206,7 +205,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } }, 1024).Wait() @@ -220,7 +219,6 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E }, 512).Wait() } - // computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). // the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. @@ -234,17 +232,17 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // they are linear in Xⱼ. // So for f ∈ { E(r₁, ..., Xⱼ, h...) } ∪ {wᵢ(r₁, ..., Xⱼ, h...) }, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables. // ml are such multilinear polynomials the evaluations of which over different values of Xⱼ are computed in this stepwise manner. - ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube + ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube ml[0] = c.eq copy(ml[1:], c.input) - sumSize := len(c.eq) / 2; // the range of h, over which we sum + sumSize := len(c.eq) / 2 // the range of h, over which we sum // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called gJ := make([]fr.Element, degGJ) var mu sync.Mutex - computeAll := func(start, end int) { // compute method to allow parallelization across instances + computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element evaluator := c.gateEvaluatorPool.get() @@ -259,19 +257,19 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]fr.Element, degGJ*len(ml)) - for h := start; h < end; h++ { // h counts across instances + for h := start; h < end; h++ { // h counts across instances evalAt1Index := sumSize + h for k := range ml { // d = 0 - mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. - step.Sub(&mlEvals[k], &ml[k][h])// step = ml[k](1) - ml[k](0) + mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. + step.Sub(&mlEvals[k], &ml[k][h]) // step = ml[k](1) - ml[k](0) for d := 1; d < degGJ; d++ { mlEvals[d*len(ml)+k].Add(&mlEvals[(d-1)*len(ml)+k], &step) } } - eIndex := 0 // index for where the current eq term is + eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { // Push gate inputs @@ -286,7 +284,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { } mu.Lock() for i := range gJ { - gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum + gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum } mu.Unlock() } @@ -435,7 +433,7 @@ type settings struct { type Option func(*settings) func WithPool(pool *polynomial.Pool) Option { - return func (options *settings) { + return func(options *settings) { options.pool = pool } } @@ -449,14 +447,13 @@ func WithSortedCircuit(sorted []*Wire) Option { func WithWorkers(workers *utils.WorkerPool) Option { return func(options *settings) { options.workers = workers - } + } } - func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error - for _, option := range options { + for _, option := range options { option(&o) } @@ -659,7 +656,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } } else if err = sumcheckVerify( claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { // incorporate prover claims about w's input into the transcript + ); err == nil { // incorporate prover claims about w's input into the transcript baseChallenge = make([][]byte, len(proofW.finalEvalProof)) for j := range baseChallenge { baseChallenge[j] = proofW.finalEvalProof[j].Marshal() @@ -748,15 +745,15 @@ type gateEvaluator struct { gate *gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs - nbIn int // number of inputs expected + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, + gate: gate, + nbIn: nbIn, frameSize: gate.NbConstants(), } if len(elementPool) > 0 { @@ -845,19 +842,19 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.GateBytecode - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - elementPool *polynomial.Pool + gate *gkrtypes.GateBytecode + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - elementPool: elementPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -895,4 +892,4 @@ func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.elementPool.Dump(e.vars) } -} \ No newline at end of file +} diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 6d42a2dd3c..6c413ecc94 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -32,7 +32,7 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` // []polynomial.MultiLin + assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` diff --git a/internal/gkr/bls24-317/gate_testing.go b/internal/gkr/bls24-317/gate_testing.go index 4da6af475b..e75aae11e1 100644 --- a/internal/gkr/bls24-317/gate_testing.go +++ b/internal/gkr/bls24-317/gate_testing.go @@ -1,20 +1,21 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Copyright 2020-2026 Consensys Software Inc. - // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Code generated by gnark DO NOT EDIT package gkr import ( "fmt" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "slices" + "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/polynomial" - "slices" ) type GateTester struct { @@ -140,7 +141,7 @@ func (t *GateTester) FindDegree(max int) (int, error) { } func (t *GateTester) VerifyDegree(claimedDegree int) error { - if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { + if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -158,4 +159,4 @@ func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) -} \ No newline at end of file +} diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 0a924fa73a..59520ff69f 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -1,7 +1,6 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Copyright 2020-2026 Consensys Software Inc. - // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Code generated by gnark DO NOT EDIT package gkr @@ -9,13 +8,14 @@ package gkr import ( "errors" "fmt" + "math/big" + "strconv" + "sync" + "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "math/big" - "strconv" - "sync" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -28,7 +28,6 @@ type ( // The goal is to prove/verify evaluations of many instances of the same circuit - // WireAssignment is assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin @@ -38,9 +37,9 @@ type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for eac // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the checking of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckLazyClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly + wireI int // the wire for which we are making the claim, with value w + evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w + claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly manager *claimsManager // WARNING: Circular references } @@ -96,7 +95,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire injection, injectionLeftInv := - e.manager.wires.ClaimPropagationInfo(e.wireI) + e.manager.wires.ClaimPropagationInfo(e.wireI) if len(injection) != len(uniqueInputEvaluations) { return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) @@ -126,7 +125,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the proving of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckClaims struct { - wireI int // the wire for which we are making the claim, with value w + wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ) manager *claimsManager @@ -152,33 +151,33 @@ func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { // and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. // The output of combine is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) polynomial.Polynomial { -varsNum := c.varsNum() + varsNum := c.varsNum() eqLength := 1 << varsNum -claimsNum := c.claimsNum() -// initialize the eq tables ( E ) -c.eq = c.manager.memPool.Make(eqLength) + claimsNum := c.claimsNum() + // initialize the eq tables ( E ) + c.eq = c.manager.memPool.Make(eqLength) -c.eq[0].SetOne() -c.eq.Eq(c.evaluationPoints[0]) + c.eq[0].SetOne() + c.eq.Eq(c.evaluationPoints[0]) -// E := eq(x₀, -) -newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) -aI := combinationCoeff + // E := eq(x₀, -) + newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) + aI := combinationCoeff -// E += cⁱ eq(xᵢ, -) -for k := 1; k < claimsNum; k++ { -newEq[0].Set(&aI) + // E += cⁱ eq(xᵢ, -) + for k := 1; k < claimsNum; k++ { + newEq[0].Set(&aI) -c.eqAcc(c.eq, newEq,c.evaluationPoints[k]) + c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) -if k+1 < claimsNum { -aI.Mul(&aI, &combinationCoeff) -} -} + if k+1 < claimsNum { + aI.Mul(&aI, &combinationCoeff) + } + } -c.manager.memPool.Dump(newEq) + c.manager.memPool.Dump(newEq) -return c.computeGJ() + return c.computeGJ() } // eqAcc sets m to an eq table at q and then adds it to e. @@ -197,7 +196,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } } else { @@ -206,7 +205,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } }, 1024).Wait() @@ -220,7 +219,6 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E }, 512).Wait() } - // computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). // the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. @@ -234,17 +232,17 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // they are linear in Xⱼ. // So for f ∈ { E(r₁, ..., Xⱼ, h...) } ∪ {wᵢ(r₁, ..., Xⱼ, h...) }, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables. // ml are such multilinear polynomials the evaluations of which over different values of Xⱼ are computed in this stepwise manner. - ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube + ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube ml[0] = c.eq copy(ml[1:], c.input) - sumSize := len(c.eq) / 2; // the range of h, over which we sum + sumSize := len(c.eq) / 2 // the range of h, over which we sum // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called gJ := make([]fr.Element, degGJ) var mu sync.Mutex - computeAll := func(start, end int) { // compute method to allow parallelization across instances + computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element evaluator := c.gateEvaluatorPool.get() @@ -259,19 +257,19 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]fr.Element, degGJ*len(ml)) - for h := start; h < end; h++ { // h counts across instances + for h := start; h < end; h++ { // h counts across instances evalAt1Index := sumSize + h for k := range ml { // d = 0 - mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. - step.Sub(&mlEvals[k], &ml[k][h])// step = ml[k](1) - ml[k](0) + mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. + step.Sub(&mlEvals[k], &ml[k][h]) // step = ml[k](1) - ml[k](0) for d := 1; d < degGJ; d++ { mlEvals[d*len(ml)+k].Add(&mlEvals[(d-1)*len(ml)+k], &step) } } - eIndex := 0 // index for where the current eq term is + eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { // Push gate inputs @@ -286,7 +284,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { } mu.Lock() for i := range gJ { - gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum + gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum } mu.Unlock() } @@ -435,7 +433,7 @@ type settings struct { type Option func(*settings) func WithPool(pool *polynomial.Pool) Option { - return func (options *settings) { + return func(options *settings) { options.pool = pool } } @@ -449,14 +447,13 @@ func WithSortedCircuit(sorted []*Wire) Option { func WithWorkers(workers *utils.WorkerPool) Option { return func(options *settings) { options.workers = workers - } + } } - func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error - for _, option := range options { + for _, option := range options { option(&o) } @@ -659,7 +656,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } } else if err = sumcheckVerify( claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { // incorporate prover claims about w's input into the transcript + ); err == nil { // incorporate prover claims about w's input into the transcript baseChallenge = make([][]byte, len(proofW.finalEvalProof)) for j := range baseChallenge { baseChallenge[j] = proofW.finalEvalProof[j].Marshal() @@ -748,15 +745,15 @@ type gateEvaluator struct { gate *gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs - nbIn int // number of inputs expected + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, + gate: gate, + nbIn: nbIn, frameSize: gate.NbConstants(), } if len(elementPool) > 0 { @@ -845,19 +842,19 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.GateBytecode - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - elementPool *polynomial.Pool + gate *gkrtypes.GateBytecode + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - elementPool: elementPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -895,4 +892,4 @@ func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.elementPool.Dump(e.vars) } -} \ No newline at end of file +} diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index e566bae325..11662062a4 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -32,7 +32,7 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` // []polynomial.MultiLin + assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` diff --git a/internal/gkr/bn254/gate_testing.go b/internal/gkr/bn254/gate_testing.go index ab982f95e0..fd18ef164c 100644 --- a/internal/gkr/bn254/gate_testing.go +++ b/internal/gkr/bn254/gate_testing.go @@ -1,20 +1,21 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Copyright 2020-2026 Consensys Software Inc. - // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Code generated by gnark DO NOT EDIT package gkr import ( "fmt" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "slices" + "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/polynomial" - "slices" ) type GateTester struct { @@ -140,7 +141,7 @@ func (t *GateTester) FindDegree(max int) (int, error) { } func (t *GateTester) VerifyDegree(claimedDegree int) error { - if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { + if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -158,4 +159,4 @@ func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) -} \ No newline at end of file +} diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index a1683e7fad..4ca1a7e71f 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -1,7 +1,6 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Copyright 2020-2026 Consensys Software Inc. - // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Code generated by gnark DO NOT EDIT package gkr @@ -9,13 +8,14 @@ package gkr import ( "errors" "fmt" + "math/big" + "strconv" + "sync" + "github.com/consensys/gnark-crypto/ecc/bn254/fr" "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "math/big" - "strconv" - "sync" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -28,7 +28,6 @@ type ( // The goal is to prove/verify evaluations of many instances of the same circuit - // WireAssignment is assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin @@ -38,9 +37,9 @@ type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for eac // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the checking of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckLazyClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly + wireI int // the wire for which we are making the claim, with value w + evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w + claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly manager *claimsManager // WARNING: Circular references } @@ -96,7 +95,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire injection, injectionLeftInv := - e.manager.wires.ClaimPropagationInfo(e.wireI) + e.manager.wires.ClaimPropagationInfo(e.wireI) if len(injection) != len(uniqueInputEvaluations) { return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) @@ -126,7 +125,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the proving of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckClaims struct { - wireI int // the wire for which we are making the claim, with value w + wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ) manager *claimsManager @@ -152,33 +151,33 @@ func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { // and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. // The output of combine is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) polynomial.Polynomial { -varsNum := c.varsNum() + varsNum := c.varsNum() eqLength := 1 << varsNum -claimsNum := c.claimsNum() -// initialize the eq tables ( E ) -c.eq = c.manager.memPool.Make(eqLength) + claimsNum := c.claimsNum() + // initialize the eq tables ( E ) + c.eq = c.manager.memPool.Make(eqLength) -c.eq[0].SetOne() -c.eq.Eq(c.evaluationPoints[0]) + c.eq[0].SetOne() + c.eq.Eq(c.evaluationPoints[0]) -// E := eq(x₀, -) -newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) -aI := combinationCoeff + // E := eq(x₀, -) + newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) + aI := combinationCoeff -// E += cⁱ eq(xᵢ, -) -for k := 1; k < claimsNum; k++ { -newEq[0].Set(&aI) + // E += cⁱ eq(xᵢ, -) + for k := 1; k < claimsNum; k++ { + newEq[0].Set(&aI) -c.eqAcc(c.eq, newEq,c.evaluationPoints[k]) + c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) -if k+1 < claimsNum { -aI.Mul(&aI, &combinationCoeff) -} -} + if k+1 < claimsNum { + aI.Mul(&aI, &combinationCoeff) + } + } -c.manager.memPool.Dump(newEq) + c.manager.memPool.Dump(newEq) -return c.computeGJ() + return c.computeGJ() } // eqAcc sets m to an eq table at q and then adds it to e. @@ -197,7 +196,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } } else { @@ -206,7 +205,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } }, 1024).Wait() @@ -220,7 +219,6 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E }, 512).Wait() } - // computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). // the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. @@ -234,17 +232,17 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // they are linear in Xⱼ. // So for f ∈ { E(r₁, ..., Xⱼ, h...) } ∪ {wᵢ(r₁, ..., Xⱼ, h...) }, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables. // ml are such multilinear polynomials the evaluations of which over different values of Xⱼ are computed in this stepwise manner. - ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube + ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube ml[0] = c.eq copy(ml[1:], c.input) - sumSize := len(c.eq) / 2; // the range of h, over which we sum + sumSize := len(c.eq) / 2 // the range of h, over which we sum // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called gJ := make([]fr.Element, degGJ) var mu sync.Mutex - computeAll := func(start, end int) { // compute method to allow parallelization across instances + computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element evaluator := c.gateEvaluatorPool.get() @@ -259,19 +257,19 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]fr.Element, degGJ*len(ml)) - for h := start; h < end; h++ { // h counts across instances + for h := start; h < end; h++ { // h counts across instances evalAt1Index := sumSize + h for k := range ml { // d = 0 - mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. - step.Sub(&mlEvals[k], &ml[k][h])// step = ml[k](1) - ml[k](0) + mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. + step.Sub(&mlEvals[k], &ml[k][h]) // step = ml[k](1) - ml[k](0) for d := 1; d < degGJ; d++ { mlEvals[d*len(ml)+k].Add(&mlEvals[(d-1)*len(ml)+k], &step) } } - eIndex := 0 // index for where the current eq term is + eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { // Push gate inputs @@ -286,7 +284,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { } mu.Lock() for i := range gJ { - gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum + gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum } mu.Unlock() } @@ -435,7 +433,7 @@ type settings struct { type Option func(*settings) func WithPool(pool *polynomial.Pool) Option { - return func (options *settings) { + return func(options *settings) { options.pool = pool } } @@ -449,14 +447,13 @@ func WithSortedCircuit(sorted []*Wire) Option { func WithWorkers(workers *utils.WorkerPool) Option { return func(options *settings) { options.workers = workers - } + } } - func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error - for _, option := range options { + for _, option := range options { option(&o) } @@ -659,7 +656,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } } else if err = sumcheckVerify( claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { // incorporate prover claims about w's input into the transcript + ); err == nil { // incorporate prover claims about w's input into the transcript baseChallenge = make([][]byte, len(proofW.finalEvalProof)) for j := range baseChallenge { baseChallenge[j] = proofW.finalEvalProof[j].Marshal() @@ -748,15 +745,15 @@ type gateEvaluator struct { gate *gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs - nbIn int // number of inputs expected + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, + gate: gate, + nbIn: nbIn, frameSize: gate.NbConstants(), } if len(elementPool) > 0 { @@ -845,19 +842,19 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.GateBytecode - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - elementPool *polynomial.Pool + gate *gkrtypes.GateBytecode + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - elementPool: elementPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -895,4 +892,4 @@ func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.elementPool.Dump(e.vars) } -} \ No newline at end of file +} diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 81044753c2..7005a18831 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -32,7 +32,7 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` // []polynomial.MultiLin + assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` diff --git a/internal/gkr/bw6-633/gate_testing.go b/internal/gkr/bw6-633/gate_testing.go index 57136bd9bd..c4bc39c831 100644 --- a/internal/gkr/bw6-633/gate_testing.go +++ b/internal/gkr/bw6-633/gate_testing.go @@ -1,20 +1,21 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Copyright 2020-2026 Consensys Software Inc. - // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Code generated by gnark DO NOT EDIT package gkr import ( "fmt" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "slices" + "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/polynomial" - "slices" ) type GateTester struct { @@ -140,7 +141,7 @@ func (t *GateTester) FindDegree(max int) (int, error) { } func (t *GateTester) VerifyDegree(claimedDegree int) error { - if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { + if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -158,4 +159,4 @@ func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) -} \ No newline at end of file +} diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index d5ac76e1a9..824ad4385b 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -1,7 +1,6 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Copyright 2020-2026 Consensys Software Inc. - // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Code generated by gnark DO NOT EDIT package gkr @@ -9,13 +8,14 @@ package gkr import ( "errors" "fmt" + "math/big" + "strconv" + "sync" + "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "math/big" - "strconv" - "sync" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -28,7 +28,6 @@ type ( // The goal is to prove/verify evaluations of many instances of the same circuit - // WireAssignment is assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin @@ -38,9 +37,9 @@ type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for eac // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the checking of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckLazyClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly + wireI int // the wire for which we are making the claim, with value w + evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w + claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly manager *claimsManager // WARNING: Circular references } @@ -96,7 +95,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire injection, injectionLeftInv := - e.manager.wires.ClaimPropagationInfo(e.wireI) + e.manager.wires.ClaimPropagationInfo(e.wireI) if len(injection) != len(uniqueInputEvaluations) { return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) @@ -126,7 +125,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the proving of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckClaims struct { - wireI int // the wire for which we are making the claim, with value w + wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ) manager *claimsManager @@ -152,33 +151,33 @@ func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { // and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. // The output of combine is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) polynomial.Polynomial { -varsNum := c.varsNum() + varsNum := c.varsNum() eqLength := 1 << varsNum -claimsNum := c.claimsNum() -// initialize the eq tables ( E ) -c.eq = c.manager.memPool.Make(eqLength) + claimsNum := c.claimsNum() + // initialize the eq tables ( E ) + c.eq = c.manager.memPool.Make(eqLength) -c.eq[0].SetOne() -c.eq.Eq(c.evaluationPoints[0]) + c.eq[0].SetOne() + c.eq.Eq(c.evaluationPoints[0]) -// E := eq(x₀, -) -newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) -aI := combinationCoeff + // E := eq(x₀, -) + newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) + aI := combinationCoeff -// E += cⁱ eq(xᵢ, -) -for k := 1; k < claimsNum; k++ { -newEq[0].Set(&aI) + // E += cⁱ eq(xᵢ, -) + for k := 1; k < claimsNum; k++ { + newEq[0].Set(&aI) -c.eqAcc(c.eq, newEq,c.evaluationPoints[k]) + c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) -if k+1 < claimsNum { -aI.Mul(&aI, &combinationCoeff) -} -} + if k+1 < claimsNum { + aI.Mul(&aI, &combinationCoeff) + } + } -c.manager.memPool.Dump(newEq) + c.manager.memPool.Dump(newEq) -return c.computeGJ() + return c.computeGJ() } // eqAcc sets m to an eq table at q and then adds it to e. @@ -197,7 +196,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } } else { @@ -206,7 +205,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } }, 1024).Wait() @@ -220,7 +219,6 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E }, 512).Wait() } - // computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). // the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. @@ -234,17 +232,17 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // they are linear in Xⱼ. // So for f ∈ { E(r₁, ..., Xⱼ, h...) } ∪ {wᵢ(r₁, ..., Xⱼ, h...) }, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables. // ml are such multilinear polynomials the evaluations of which over different values of Xⱼ are computed in this stepwise manner. - ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube + ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube ml[0] = c.eq copy(ml[1:], c.input) - sumSize := len(c.eq) / 2; // the range of h, over which we sum + sumSize := len(c.eq) / 2 // the range of h, over which we sum // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called gJ := make([]fr.Element, degGJ) var mu sync.Mutex - computeAll := func(start, end int) { // compute method to allow parallelization across instances + computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element evaluator := c.gateEvaluatorPool.get() @@ -259,19 +257,19 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]fr.Element, degGJ*len(ml)) - for h := start; h < end; h++ { // h counts across instances + for h := start; h < end; h++ { // h counts across instances evalAt1Index := sumSize + h for k := range ml { // d = 0 - mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. - step.Sub(&mlEvals[k], &ml[k][h])// step = ml[k](1) - ml[k](0) + mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. + step.Sub(&mlEvals[k], &ml[k][h]) // step = ml[k](1) - ml[k](0) for d := 1; d < degGJ; d++ { mlEvals[d*len(ml)+k].Add(&mlEvals[(d-1)*len(ml)+k], &step) } } - eIndex := 0 // index for where the current eq term is + eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { // Push gate inputs @@ -286,7 +284,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { } mu.Lock() for i := range gJ { - gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum + gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum } mu.Unlock() } @@ -435,7 +433,7 @@ type settings struct { type Option func(*settings) func WithPool(pool *polynomial.Pool) Option { - return func (options *settings) { + return func(options *settings) { options.pool = pool } } @@ -449,14 +447,13 @@ func WithSortedCircuit(sorted []*Wire) Option { func WithWorkers(workers *utils.WorkerPool) Option { return func(options *settings) { options.workers = workers - } + } } - func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error - for _, option := range options { + for _, option := range options { option(&o) } @@ -659,7 +656,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } } else if err = sumcheckVerify( claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { // incorporate prover claims about w's input into the transcript + ); err == nil { // incorporate prover claims about w's input into the transcript baseChallenge = make([][]byte, len(proofW.finalEvalProof)) for j := range baseChallenge { baseChallenge[j] = proofW.finalEvalProof[j].Marshal() @@ -748,15 +745,15 @@ type gateEvaluator struct { gate *gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs - nbIn int // number of inputs expected + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, + gate: gate, + nbIn: nbIn, frameSize: gate.NbConstants(), } if len(elementPool) > 0 { @@ -845,19 +842,19 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.GateBytecode - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - elementPool *polynomial.Pool + gate *gkrtypes.GateBytecode + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - elementPool: elementPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -895,4 +892,4 @@ func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.elementPool.Dump(e.vars) } -} \ No newline at end of file +} diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index f8bbd36a86..0a782291d1 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -32,7 +32,7 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` // []polynomial.MultiLin + assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` diff --git a/internal/gkr/bw6-761/gate_testing.go b/internal/gkr/bw6-761/gate_testing.go index 8e6bf8e83d..b86ac991ed 100644 --- a/internal/gkr/bw6-761/gate_testing.go +++ b/internal/gkr/bw6-761/gate_testing.go @@ -1,20 +1,21 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Copyright 2020-2026 Consensys Software Inc. - // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Code generated by gnark DO NOT EDIT package gkr import ( "fmt" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "slices" + "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/polynomial" - "slices" ) type GateTester struct { @@ -140,7 +141,7 @@ func (t *GateTester) FindDegree(max int) (int, error) { } func (t *GateTester) VerifyDegree(claimedDegree int) error { - if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { + if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { return fmt.Errorf("detected a higher degree than %d", claimedDegree) } else if len(p) == 0 { return gkrtypes.ErrZeroFunction @@ -158,4 +159,4 @@ func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { gEval := newGateEvaluator(g, t.nbIn) gAt := gEval.evaluate(x...) return fAt.Equal(gAt) -} \ No newline at end of file +} diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index eff84eb635..8c6d7fd4bf 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -1,7 +1,6 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Copyright 2020-2026 Consensys Software Inc. - // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Code generated by gnark DO NOT EDIT package gkr @@ -9,13 +8,14 @@ package gkr import ( "errors" "fmt" + "math/big" + "strconv" + "sync" + "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "math/big" - "strconv" - "sync" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -28,7 +28,6 @@ type ( // The goal is to prove/verify evaluations of many instances of the same circuit - // WireAssignment is assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin @@ -38,9 +37,9 @@ type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for eac // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the checking of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckLazyClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly + wireI int // the wire for which we are making the claim, with value w + evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w + claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly manager *claimsManager // WARNING: Circular references } @@ -96,7 +95,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire injection, injectionLeftInv := - e.manager.wires.ClaimPropagationInfo(e.wireI) + e.manager.wires.ClaimPropagationInfo(e.wireI) if len(injection) != len(uniqueInputEvaluations) { return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) @@ -126,7 +125,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the proving of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckClaims struct { - wireI int // the wire for which we are making the claim, with value w + wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ) manager *claimsManager @@ -152,33 +151,33 @@ func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { // and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. // The output of combine is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) polynomial.Polynomial { -varsNum := c.varsNum() + varsNum := c.varsNum() eqLength := 1 << varsNum -claimsNum := c.claimsNum() -// initialize the eq tables ( E ) -c.eq = c.manager.memPool.Make(eqLength) + claimsNum := c.claimsNum() + // initialize the eq tables ( E ) + c.eq = c.manager.memPool.Make(eqLength) -c.eq[0].SetOne() -c.eq.Eq(c.evaluationPoints[0]) + c.eq[0].SetOne() + c.eq.Eq(c.evaluationPoints[0]) -// E := eq(x₀, -) -newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) -aI := combinationCoeff + // E := eq(x₀, -) + newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) + aI := combinationCoeff -// E += cⁱ eq(xᵢ, -) -for k := 1; k < claimsNum; k++ { -newEq[0].Set(&aI) + // E += cⁱ eq(xᵢ, -) + for k := 1; k < claimsNum; k++ { + newEq[0].Set(&aI) -c.eqAcc(c.eq, newEq,c.evaluationPoints[k]) + c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) -if k+1 < claimsNum { -aI.Mul(&aI, &combinationCoeff) -} -} + if k+1 < claimsNum { + aI.Mul(&aI, &combinationCoeff) + } + } -c.manager.memPool.Dump(newEq) + c.manager.memPool.Dump(newEq) -return c.computeGJ() + return c.computeGJ() } // eqAcc sets m to an eq table at q and then adds it to e. @@ -197,7 +196,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } } else { @@ -206,7 +205,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } }, 1024).Wait() @@ -220,7 +219,6 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E }, 512).Wait() } - // computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). // the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. @@ -234,17 +232,17 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // they are linear in Xⱼ. // So for f ∈ { E(r₁, ..., Xⱼ, h...) } ∪ {wᵢ(r₁, ..., Xⱼ, h...) }, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables. // ml are such multilinear polynomials the evaluations of which over different values of Xⱼ are computed in this stepwise manner. - ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube + ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube ml[0] = c.eq copy(ml[1:], c.input) - sumSize := len(c.eq) / 2; // the range of h, over which we sum + sumSize := len(c.eq) / 2 // the range of h, over which we sum // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called gJ := make([]fr.Element, degGJ) var mu sync.Mutex - computeAll := func(start, end int) { // compute method to allow parallelization across instances + computeAll := func(start, end int) { // compute method to allow parallelization across instances var step fr.Element evaluator := c.gateEvaluatorPool.get() @@ -259,19 +257,19 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]fr.Element, degGJ*len(ml)) - for h := start; h < end; h++ { // h counts across instances + for h := start; h < end; h++ { // h counts across instances evalAt1Index := sumSize + h for k := range ml { // d = 0 - mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. - step.Sub(&mlEvals[k], &ml[k][h])// step = ml[k](1) - ml[k](0) + mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. + step.Sub(&mlEvals[k], &ml[k][h]) // step = ml[k](1) - ml[k](0) for d := 1; d < degGJ; d++ { mlEvals[d*len(ml)+k].Add(&mlEvals[(d-1)*len(ml)+k], &step) } } - eIndex := 0 // index for where the current eq term is + eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { // Push gate inputs @@ -286,7 +284,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { } mu.Lock() for i := range gJ { - gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum + gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum } mu.Unlock() } @@ -435,7 +433,7 @@ type settings struct { type Option func(*settings) func WithPool(pool *polynomial.Pool) Option { - return func (options *settings) { + return func(options *settings) { options.pool = pool } } @@ -449,14 +447,13 @@ func WithSortedCircuit(sorted []*Wire) Option { func WithWorkers(workers *utils.WorkerPool) Option { return func(options *settings) { options.workers = workers - } + } } - func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error - for _, option := range options { + for _, option := range options { option(&o) } @@ -659,7 +656,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } } else if err = sumcheckVerify( claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { // incorporate prover claims about w's input into the transcript + ); err == nil { // incorporate prover claims about w's input into the transcript baseChallenge = make([][]byte, len(proofW.finalEvalProof)) for j := range baseChallenge { baseChallenge[j] = proofW.finalEvalProof[j].Marshal() @@ -748,15 +745,15 @@ type gateEvaluator struct { gate *gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs - nbIn int // number of inputs expected + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, + gate: gate, + nbIn: nbIn, frameSize: gate.NbConstants(), } if len(elementPool) > 0 { @@ -845,19 +842,19 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.GateBytecode - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - elementPool *polynomial.Pool + gate *gkrtypes.GateBytecode + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - elementPool: elementPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -895,4 +892,4 @@ func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.elementPool.Dump(e.vars) } -} \ No newline at end of file +} diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 3275dc4bd6..1eb353ad01 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -161,21 +161,6 @@ func (c Circuit[GateExecutable]) maxGateDegree() int { return res } -// MaxStackSize returns the maximum stack size needed by any gate evaluator in the circuit. -// This is used to initialize universal gate evaluators that can handle any gate in the circuit. -func (c Circuit) MaxStackSize() int { - maxSize := 0 - for i := range c { - if !c[i].IsInput() { - gate := c[i].Gate.Compiled() - nbIn := len(c[i].Inputs) - stackSize := gate.NbConstants() + nbIn + len(gate.Instructions) - maxSize = max(maxSize, stackSize) - } - } - return maxSize -} - // MemoryRequirements returns an increasing vector of memory allocation sizes required for proving a GKR statement func (c Circuit[GateExecutable]) MemoryRequirements(nbInstances int) []int { res := []int{256, nbInstances, nbInstances * (c.maxGateDegree() + 1)} diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 2eafb11f1c..e26c628b93 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -1,7 +1,6 @@ +// Copyright 2020-2026 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Copyright 2020-2026 Consensys Software Inc. - // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - // Code generated by gnark DO NOT EDIT package gkr @@ -9,14 +8,15 @@ package gkr import ( "errors" "fmt" - "github.com/consensys/gnark/internal/small_rational" - "github.com/consensys/gnark/internal/small_rational/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - "github.com/consensys/gnark-crypto/utils" "math/big" "strconv" "sync" + + fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/small_rational" + "github.com/consensys/gnark/internal/small_rational/polynomial" ) // Type aliases for bytecode-based GKR types @@ -28,7 +28,6 @@ type ( // The goal is to prove/verify evaluations of many instances of the same circuit - // WireAssignment is assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin @@ -38,10 +37,10 @@ type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for eac // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the checking of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckLazyClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]small_rational.SmallRational // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []small_rational.SmallRational // yᵢ = w(xᵢ), allegedly - manager *claimsManager // WARNING: Circular references + wireI int // the wire for which we are making the claim, with value w + evaluationPoints [][]small_rational.SmallRational // xᵢ: the points at which the prover has made claims about the evaluation of w + claimedEvaluations []small_rational.SmallRational // yᵢ = w(xᵢ), allegedly + manager *claimsManager // WARNING: Circular references } func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { @@ -96,7 +95,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []small_rational.S gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire injection, injectionLeftInv := - e.manager.wires.ClaimPropagationInfo(e.wireI) + e.manager.wires.ClaimPropagationInfo(e.wireI) if len(injection) != len(uniqueInputEvaluations) { return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) @@ -126,7 +125,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []small_rational.S // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the proving of multiple evaluations of the same wire. type eqTimesGateEvalSumcheckClaims struct { - wireI int // the wire for which we are making the claim, with value w + wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]small_rational.SmallRational // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []small_rational.SmallRational // yᵢ = w(xᵢ) manager *claimsManager @@ -152,33 +151,33 @@ func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { // and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. // The output of combine is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff small_rational.SmallRational) polynomial.Polynomial { -varsNum := c.varsNum() + varsNum := c.varsNum() eqLength := 1 << varsNum -claimsNum := c.claimsNum() -// initialize the eq tables ( E ) -c.eq = c.manager.memPool.Make(eqLength) + claimsNum := c.claimsNum() + // initialize the eq tables ( E ) + c.eq = c.manager.memPool.Make(eqLength) -c.eq[0].SetOne() -c.eq.Eq(c.evaluationPoints[0]) + c.eq[0].SetOne() + c.eq.Eq(c.evaluationPoints[0]) -// E := eq(x₀, -) -newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) -aI := combinationCoeff + // E := eq(x₀, -) + newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) + aI := combinationCoeff -// E += cⁱ eq(xᵢ, -) -for k := 1; k < claimsNum; k++ { -newEq[0].Set(&aI) + // E += cⁱ eq(xᵢ, -) + for k := 1; k < claimsNum; k++ { + newEq[0].Set(&aI) -c.eqAcc(c.eq, newEq,c.evaluationPoints[k]) + c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) -if k+1 < claimsNum { -aI.Mul(&aI, &combinationCoeff) -} -} + if k+1 < claimsNum { + aI.Mul(&aI, &combinationCoeff) + } + } -c.manager.memPool.Dump(newEq) + c.manager.memPool.Dump(newEq) -return c.computeGJ() + return c.computeGJ() } // eqAcc sets m to an eq table at q and then adds it to e. @@ -197,7 +196,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []smal j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } } else { @@ -206,7 +205,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []smal j0 := j << (n - i) // bᵢ₊₁ = 0 j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ + m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) } }, 1024).Wait() @@ -220,7 +219,6 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []smal }, 512).Wait() } - // computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). // the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. @@ -234,17 +232,17 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // they are linear in Xⱼ. // So for f ∈ { E(r₁, ..., Xⱼ, h...) } ∪ {wᵢ(r₁, ..., Xⱼ, h...) }, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables. // ml are such multilinear polynomials the evaluations of which over different values of Xⱼ are computed in this stepwise manner. - ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube + ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube ml[0] = c.eq copy(ml[1:], c.input) - sumSize := len(c.eq) / 2; // the range of h, over which we sum + sumSize := len(c.eq) / 2 // the range of h, over which we sum // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called gJ := make([]small_rational.SmallRational, degGJ) var mu sync.Mutex - computeAll := func(start, end int) { // compute method to allow parallelization across instances + computeAll := func(start, end int) { // compute method to allow parallelization across instances var step small_rational.SmallRational evaluator := c.gateEvaluatorPool.get() @@ -259,19 +257,19 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) mlEvals := make([]small_rational.SmallRational, degGJ*len(ml)) - for h := start; h < end; h++ { // h counts across instances + for h := start; h < end; h++ { // h counts across instances evalAt1Index := sumSize + h for k := range ml { // d = 0 - mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. - step.Sub(&mlEvals[k], &ml[k][h])// step = ml[k](1) - ml[k](0) + mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. + step.Sub(&mlEvals[k], &ml[k][h]) // step = ml[k](1) - ml[k](0) for d := 1; d < degGJ; d++ { mlEvals[d*len(ml)+k].Add(&mlEvals[(d-1)*len(ml)+k], &step) } } - eIndex := 0 // index for where the current eq term is + eIndex := 0 // index for where the current eq term is nextEIndex := len(ml) for d := range degGJ { // Push gate inputs @@ -286,7 +284,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { } mu.Lock() for i := range gJ { - gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum + gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum } mu.Unlock() } @@ -435,7 +433,7 @@ type settings struct { type Option func(*settings) func WithPool(pool *polynomial.Pool) Option { - return func (options *settings) { + return func(options *settings) { options.pool = pool } } @@ -449,14 +447,13 @@ func WithSortedCircuit(sorted []*Wire) Option { func WithWorkers(workers *utils.WorkerPool) Option { return func(options *settings) { options.workers = workers - } + } } - func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error - for _, option := range options { + for _, option := range options { option(&o) } @@ -659,7 +656,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } } else if err = sumcheckVerify( claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { // incorporate prover claims about w's input into the transcript + ); err == nil { // incorporate prover claims about w's input into the transcript baseChallenge = make([][]byte, len(proofW.finalEvalProof)) for j := range baseChallenge { baseChallenge[j] = proofW.finalEvalProof[j].Marshal() @@ -748,15 +745,15 @@ type gateEvaluator struct { gate *gkrtypes.GateBytecode vars []small_rational.SmallRational frameSize int // number of constants plus currently pushed inputs - nbIn int // number of inputs expected + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, + gate: gate, + nbIn: nbIn, frameSize: gate.NbConstants(), } if len(elementPool) > 0 { @@ -845,19 +842,19 @@ func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rat // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.GateBytecode - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - elementPool *polynomial.Pool + gate *gkrtypes.GateBytecode + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - elementPool: elementPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } @@ -895,4 +892,4 @@ func (gep *gateEvaluatorPool) dumpAll() { for e := range gep.available { gep.elementPool.Dump(e.vars) } -} \ No newline at end of file +} From c85d57626289ba6116e7bbf01bba40deff2e5560 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 30 Jan 2026 17:10:25 -0600 Subject: [PATCH 137/251] fix: remove re-added test engine funcs --- test/blueprint_solver.go | 4 ---- test/engine.go | 1 - 2 files changed, 5 deletions(-) diff --git a/test/blueprint_solver.go b/test/blueprint_solver.go index cdb34c223b..3b2974e4d4 100644 --- a/test/blueprint_solver.go +++ b/test/blueprint_solver.go @@ -111,10 +111,6 @@ func (s *blueprintSolver[E]) IsSolved(vID uint32) bool { panic("not implemented in test.Engine") } -func (s *blueprintSolver[E]) GetBlueprint(id constraint.BlueprintID) constraint.Blueprint { - return s.blueprints[id] -} - // implements constraint.Field func (s *blueprintSolver[E]) FromInterface(i interface{}) E { diff --git a/test/engine.go b/test/engine.go index 733d7cf1be..d3af05e559 100644 --- a/test/engine.go +++ b/test/engine.go @@ -735,7 +735,6 @@ func addInstructionGeneric[E constraint.Element](e *engine, bID constraint.Bluep // solve the blueprint synchronously s := blueprintSolver[E]{ internalVariables: e.internalVariables, - blueprints: e.blueprints, modulus: newModulus[E](e.q), } if err := blueprint.Solve(&s, inst); err != nil { From 0c6dd9cf6c2abaaee264b1415aa9b5735f4f6094 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 30 Jan 2026 17:13:18 -0600 Subject: [PATCH 138/251] Revert "fix: remove re-added test engine funcs" This reverts commit c85d57626289ba6116e7bbf01bba40deff2e5560. --- test/blueprint_solver.go | 4 ++++ test/engine.go | 1 + 2 files changed, 5 insertions(+) diff --git a/test/blueprint_solver.go b/test/blueprint_solver.go index 3b2974e4d4..cdb34c223b 100644 --- a/test/blueprint_solver.go +++ b/test/blueprint_solver.go @@ -111,6 +111,10 @@ func (s *blueprintSolver[E]) IsSolved(vID uint32) bool { panic("not implemented in test.Engine") } +func (s *blueprintSolver[E]) GetBlueprint(id constraint.BlueprintID) constraint.Blueprint { + return s.blueprints[id] +} + // implements constraint.Field func (s *blueprintSolver[E]) FromInterface(i interface{}) E { diff --git a/test/engine.go b/test/engine.go index d3af05e559..733d7cf1be 100644 --- a/test/engine.go +++ b/test/engine.go @@ -735,6 +735,7 @@ func addInstructionGeneric[E constraint.Element](e *engine, bID constraint.Bluep // solve the blueprint synchronously s := blueprintSolver[E]{ internalVariables: e.internalVariables, + blueprints: e.blueprints, modulus: newModulus[E](e.q), } if err := blueprint.Solve(&s, inst); err != nil { From ae408dc7b8e275563f58cadbeded1924e2e13ab4 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 30 Jan 2026 17:17:54 -0600 Subject: [PATCH 139/251] revert: reinstate GetBlueprint mechanism --- test/blueprint_solver.go | 1 + 1 file changed, 1 insertion(+) diff --git a/test/blueprint_solver.go b/test/blueprint_solver.go index cdb34c223b..586441ff49 100644 --- a/test/blueprint_solver.go +++ b/test/blueprint_solver.go @@ -87,6 +87,7 @@ func (m *modulus[E]) bigIntToElement(b *big.Int) E { // it is a separate type to avoid method collisions with the engine. type blueprintSolver[E constraint.Element] struct { internalVariables []*big.Int + blueprints []constraint.Blueprint *modulus[E] } From 67edcf652b17d6f2adf951a7c0570bcee73eda92 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 6 Feb 2026 12:45:06 -0600 Subject: [PATCH 140/251] fix: calldata prepended by its own size --- .../backend/template/gkr/blueprint.go.tmpl | 36 +++++++++++-------- internal/gkr/bls12-377/blueprint.go | 36 +++++++++++-------- internal/gkr/bls12-381/blueprint.go | 36 +++++++++++-------- internal/gkr/bls24-315/blueprint.go | 36 +++++++++++-------- internal/gkr/bls24-317/blueprint.go | 36 +++++++++++-------- internal/gkr/bn254/blueprint.go | 36 +++++++++++-------- internal/gkr/bw6-633/blueprint.go | 36 +++++++++++-------- internal/gkr/bw6-761/blueprint.go | 36 +++++++++++-------- std/gkrapi/compile.go | 29 ++++++++++----- 9 files changed, 196 insertions(+), 121 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index cfba23930a..eb0b866c75 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -80,8 +80,9 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - instanceI := int(inst.Calldata[0]) - calldata := inst.Calldata[1:] + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + instanceI := int(inst.Calldata[1]) + calldata := inst.Calldata[2:] // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice if len(b.assignments[0]) <= instanceI { @@ -155,13 +156,16 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // ensure metadata is available maxLevel := constraint.LevelUnset - // Parse exactly b.nbInputs linear expressions - offset := 1 // skip instance index - for range b.nbInputs { - n := int(inst.Calldata[offset]) + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + offset := 2 // skip size and instance index + + // Parse input linear expressions + for offset < len(inst.Calldata) { + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -211,8 +215,9 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra } // Read initial challenges from instruction calldata (parse dynamically, no metadata) + // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges - calldata := inst.Calldata + calldata := inst.Calldata[1:] // skip size prefix for len(calldata) != 0 { val, delta := s.Read(calldata) calldata = calldata[delta:] @@ -287,10 +292,12 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse all challenges dynamically (no metadata) - offset := 0 + // Format: [0]=totalSize, [1...]=challenge linear expressions + offset := 1 // skip size prefix + + // Parse all challenge linear expressions for offset < len(inst.Calldata) { - n := int(inst.Calldata[offset]) + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -333,8 +340,9 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst defer b.lock.Unlock() // Read wireI and instanceI from calldata - wireI := int(inst.Calldata[0]) - instanceI := int(inst.Calldata[1]) + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + wireI := int(inst.Calldata[1]) + instanceI := int(inst.Calldata[2]) // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) @@ -365,8 +373,8 @@ func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse wireI and instanceI (skip them) - offset := 2 + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + offset := 3 // skip size, wireI, and instanceI // Parse dependency linear expression // This ensures we run after the solve instruction for this instance diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index bd6e885d5f..4722895791 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -87,8 +87,9 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - instanceI := int(inst.Calldata[0]) - calldata := inst.Calldata[1:] + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + instanceI := int(inst.Calldata[1]) + calldata := inst.Calldata[2:] // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice if len(b.assignments[0]) <= instanceI { @@ -162,13 +163,16 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // ensure metadata is available maxLevel := constraint.LevelUnset - // Parse exactly b.nbInputs linear expressions - offset := 1 // skip instance index - for range b.nbInputs { - n := int(inst.Calldata[offset]) + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + offset := 2 // skip size and instance index + + // Parse input linear expressions + for offset < len(inst.Calldata) { + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -218,8 +222,9 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra } // Read initial challenges from instruction calldata (parse dynamically, no metadata) + // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges - calldata := inst.Calldata + calldata := inst.Calldata[1:] // skip size prefix for len(calldata) != 0 { val, delta := s.Read(calldata) calldata = calldata[delta:] @@ -294,10 +299,12 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse all challenges dynamically (no metadata) - offset := 0 + // Format: [0]=totalSize, [1...]=challenge linear expressions + offset := 1 // skip size prefix + + // Parse all challenge linear expressions for offset < len(inst.Calldata) { - n := int(inst.Calldata[offset]) + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -341,8 +348,9 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst defer b.lock.Unlock() // Read wireI and instanceI from calldata - wireI := int(inst.Calldata[0]) - instanceI := int(inst.Calldata[1]) + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + wireI := int(inst.Calldata[1]) + instanceI := int(inst.Calldata[2]) // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) @@ -373,8 +381,8 @@ func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse wireI and instanceI (skip them) - offset := 2 + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + offset := 3 // skip size, wireI, and instanceI // Parse dependency linear expression // This ensures we run after the solve instruction for this instance diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index bd7aa58f11..a15c02f38e 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -87,8 +87,9 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - instanceI := int(inst.Calldata[0]) - calldata := inst.Calldata[1:] + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + instanceI := int(inst.Calldata[1]) + calldata := inst.Calldata[2:] // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice if len(b.assignments[0]) <= instanceI { @@ -162,13 +163,16 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // ensure metadata is available maxLevel := constraint.LevelUnset - // Parse exactly b.nbInputs linear expressions - offset := 1 // skip instance index - for range b.nbInputs { - n := int(inst.Calldata[offset]) + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + offset := 2 // skip size and instance index + + // Parse input linear expressions + for offset < len(inst.Calldata) { + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -218,8 +222,9 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra } // Read initial challenges from instruction calldata (parse dynamically, no metadata) + // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges - calldata := inst.Calldata + calldata := inst.Calldata[1:] // skip size prefix for len(calldata) != 0 { val, delta := s.Read(calldata) calldata = calldata[delta:] @@ -294,10 +299,12 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse all challenges dynamically (no metadata) - offset := 0 + // Format: [0]=totalSize, [1...]=challenge linear expressions + offset := 1 // skip size prefix + + // Parse all challenge linear expressions for offset < len(inst.Calldata) { - n := int(inst.Calldata[offset]) + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -341,8 +348,9 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst defer b.lock.Unlock() // Read wireI and instanceI from calldata - wireI := int(inst.Calldata[0]) - instanceI := int(inst.Calldata[1]) + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + wireI := int(inst.Calldata[1]) + instanceI := int(inst.Calldata[2]) // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) @@ -373,8 +381,8 @@ func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse wireI and instanceI (skip them) - offset := 2 + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + offset := 3 // skip size, wireI, and instanceI // Parse dependency linear expression // This ensures we run after the solve instruction for this instance diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index 7318a7f60f..b583a4283f 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -87,8 +87,9 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - instanceI := int(inst.Calldata[0]) - calldata := inst.Calldata[1:] + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + instanceI := int(inst.Calldata[1]) + calldata := inst.Calldata[2:] // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice if len(b.assignments[0]) <= instanceI { @@ -162,13 +163,16 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // ensure metadata is available maxLevel := constraint.LevelUnset - // Parse exactly b.nbInputs linear expressions - offset := 1 // skip instance index - for range b.nbInputs { - n := int(inst.Calldata[offset]) + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + offset := 2 // skip size and instance index + + // Parse input linear expressions + for offset < len(inst.Calldata) { + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -218,8 +222,9 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra } // Read initial challenges from instruction calldata (parse dynamically, no metadata) + // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges - calldata := inst.Calldata + calldata := inst.Calldata[1:] // skip size prefix for len(calldata) != 0 { val, delta := s.Read(calldata) calldata = calldata[delta:] @@ -294,10 +299,12 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse all challenges dynamically (no metadata) - offset := 0 + // Format: [0]=totalSize, [1...]=challenge linear expressions + offset := 1 // skip size prefix + + // Parse all challenge linear expressions for offset < len(inst.Calldata) { - n := int(inst.Calldata[offset]) + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -341,8 +348,9 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst defer b.lock.Unlock() // Read wireI and instanceI from calldata - wireI := int(inst.Calldata[0]) - instanceI := int(inst.Calldata[1]) + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + wireI := int(inst.Calldata[1]) + instanceI := int(inst.Calldata[2]) // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) @@ -373,8 +381,8 @@ func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse wireI and instanceI (skip them) - offset := 2 + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + offset := 3 // skip size, wireI, and instanceI // Parse dependency linear expression // This ensures we run after the solve instruction for this instance diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 6c413ecc94..5cce4d1b80 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -87,8 +87,9 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - instanceI := int(inst.Calldata[0]) - calldata := inst.Calldata[1:] + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + instanceI := int(inst.Calldata[1]) + calldata := inst.Calldata[2:] // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice if len(b.assignments[0]) <= instanceI { @@ -162,13 +163,16 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // ensure metadata is available maxLevel := constraint.LevelUnset - // Parse exactly b.nbInputs linear expressions - offset := 1 // skip instance index - for range b.nbInputs { - n := int(inst.Calldata[offset]) + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + offset := 2 // skip size and instance index + + // Parse input linear expressions + for offset < len(inst.Calldata) { + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -218,8 +222,9 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra } // Read initial challenges from instruction calldata (parse dynamically, no metadata) + // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges - calldata := inst.Calldata + calldata := inst.Calldata[1:] // skip size prefix for len(calldata) != 0 { val, delta := s.Read(calldata) calldata = calldata[delta:] @@ -294,10 +299,12 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse all challenges dynamically (no metadata) - offset := 0 + // Format: [0]=totalSize, [1...]=challenge linear expressions + offset := 1 // skip size prefix + + // Parse all challenge linear expressions for offset < len(inst.Calldata) { - n := int(inst.Calldata[offset]) + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -341,8 +348,9 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst defer b.lock.Unlock() // Read wireI and instanceI from calldata - wireI := int(inst.Calldata[0]) - instanceI := int(inst.Calldata[1]) + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + wireI := int(inst.Calldata[1]) + instanceI := int(inst.Calldata[2]) // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) @@ -373,8 +381,8 @@ func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse wireI and instanceI (skip them) - offset := 2 + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + offset := 3 // skip size, wireI, and instanceI // Parse dependency linear expression // This ensures we run after the solve instruction for this instance diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 11662062a4..55c8940dad 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -87,8 +87,9 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - instanceI := int(inst.Calldata[0]) - calldata := inst.Calldata[1:] + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + instanceI := int(inst.Calldata[1]) + calldata := inst.Calldata[2:] // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice if len(b.assignments[0]) <= instanceI { @@ -162,13 +163,16 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // ensure metadata is available maxLevel := constraint.LevelUnset - // Parse exactly b.nbInputs linear expressions - offset := 1 // skip instance index - for range b.nbInputs { - n := int(inst.Calldata[offset]) + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + offset := 2 // skip size and instance index + + // Parse input linear expressions + for offset < len(inst.Calldata) { + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -218,8 +222,9 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra } // Read initial challenges from instruction calldata (parse dynamically, no metadata) + // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges - calldata := inst.Calldata + calldata := inst.Calldata[1:] // skip size prefix for len(calldata) != 0 { val, delta := s.Read(calldata) calldata = calldata[delta:] @@ -294,10 +299,12 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse all challenges dynamically (no metadata) - offset := 0 + // Format: [0]=totalSize, [1...]=challenge linear expressions + offset := 1 // skip size prefix + + // Parse all challenge linear expressions for offset < len(inst.Calldata) { - n := int(inst.Calldata[offset]) + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -341,8 +348,9 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst defer b.lock.Unlock() // Read wireI and instanceI from calldata - wireI := int(inst.Calldata[0]) - instanceI := int(inst.Calldata[1]) + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + wireI := int(inst.Calldata[1]) + instanceI := int(inst.Calldata[2]) // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) @@ -373,8 +381,8 @@ func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse wireI and instanceI (skip them) - offset := 2 + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + offset := 3 // skip size, wireI, and instanceI // Parse dependency linear expression // This ensures we run after the solve instruction for this instance diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 7005a18831..900c4eafb6 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -87,8 +87,9 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - instanceI := int(inst.Calldata[0]) - calldata := inst.Calldata[1:] + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + instanceI := int(inst.Calldata[1]) + calldata := inst.Calldata[2:] // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice if len(b.assignments[0]) <= instanceI { @@ -162,13 +163,16 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // ensure metadata is available maxLevel := constraint.LevelUnset - // Parse exactly b.nbInputs linear expressions - offset := 1 // skip instance index - for range b.nbInputs { - n := int(inst.Calldata[offset]) + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + offset := 2 // skip size and instance index + + // Parse input linear expressions + for offset < len(inst.Calldata) { + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -218,8 +222,9 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra } // Read initial challenges from instruction calldata (parse dynamically, no metadata) + // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges - calldata := inst.Calldata + calldata := inst.Calldata[1:] // skip size prefix for len(calldata) != 0 { val, delta := s.Read(calldata) calldata = calldata[delta:] @@ -294,10 +299,12 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse all challenges dynamically (no metadata) - offset := 0 + // Format: [0]=totalSize, [1...]=challenge linear expressions + offset := 1 // skip size prefix + + // Parse all challenge linear expressions for offset < len(inst.Calldata) { - n := int(inst.Calldata[offset]) + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -341,8 +348,9 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst defer b.lock.Unlock() // Read wireI and instanceI from calldata - wireI := int(inst.Calldata[0]) - instanceI := int(inst.Calldata[1]) + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + wireI := int(inst.Calldata[1]) + instanceI := int(inst.Calldata[2]) // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) @@ -373,8 +381,8 @@ func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse wireI and instanceI (skip them) - offset := 2 + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + offset := 3 // skip size, wireI, and instanceI // Parse dependency linear expression // This ensures we run after the solve instruction for this instance diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 0a782291d1..4a776b0a13 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -87,8 +87,9 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) - instanceI := int(inst.Calldata[0]) - calldata := inst.Calldata[1:] + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + instanceI := int(inst.Calldata[1]) + calldata := inst.Calldata[2:] // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice if len(b.assignments[0]) <= instanceI { @@ -162,13 +163,16 @@ func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { + b.initialize() // ensure metadata is available maxLevel := constraint.LevelUnset - // Parse exactly b.nbInputs linear expressions - offset := 1 // skip instance index - for range b.nbInputs { - n := int(inst.Calldata[offset]) + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions + offset := 2 // skip size and instance index + + // Parse input linear expressions + for offset < len(inst.Calldata) { + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -218,8 +222,9 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra } // Read initial challenges from instruction calldata (parse dynamically, no metadata) + // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges - calldata := inst.Calldata + calldata := inst.Calldata[1:] // skip size prefix for len(calldata) != 0 { val, delta := s.Read(calldata) calldata = calldata[delta:] @@ -294,10 +299,12 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse all challenges dynamically (no metadata) - offset := 0 + // Format: [0]=totalSize, [1...]=challenge linear expressions + offset := 1 // skip size prefix + + // Parse all challenge linear expressions for offset < len(inst.Calldata) { - n := int(inst.Calldata[offset]) + n := int(inst.Calldata[offset]) // number of terms in this linear expression offset++ for range n { @@ -341,8 +348,9 @@ func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst defer b.lock.Unlock() // Read wireI and instanceI from calldata - wireI := int(inst.Calldata[0]) - instanceI := int(inst.Calldata[1]) + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + wireI := int(inst.Calldata[1]) + instanceI := int(inst.Calldata[2]) // Get solve blueprint from solver by ID solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) @@ -373,8 +381,8 @@ func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { maxLevel := constraint.LevelUnset - // Parse wireI and instanceI (skip them) - offset := 2 + // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression + offset := 3 // skip size, wireI, and instanceI // Parse dependency linear expression // This ensures we run after the solve instruction for this instance diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 772f329f53..ab83119250 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -157,10 +157,10 @@ func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr } // Build instruction calldata for solveBlueprint - // Format: [0]=instanceIndex, [1...]=input values as linear expressions + // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input values as linear expressions compiler := c.api.Compiler() - calldata := make([]uint32, 1, 1+len(c.ins)*2+2) // pre-allocate roughly - calldata[0] = uint32(c.nbInstances) // instance index + calldata := make([]uint32, 2, 2+len(c.ins)*3) // pre-allocate: size + instanceIndex + linear expressions + calldata[1] = uint32(c.nbInstances) // Encode input variables for _, wI := range c.ins { @@ -176,6 +176,9 @@ func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr v.Compress(&calldata) } + // Update total size + calldata[0] = uint32(len(calldata)) + // Execute solve blueprint instruction outputs := compiler.AddInstruction(c.blueprints.SolveID, calldata) @@ -272,17 +275,20 @@ func (c *Circuit) verify(api frontend.API, circuit gkrtypes.GadgetCircuit, initi compiler := api.Compiler() // Build calldata for prove instruction - // Format: [0...]=challenge linear expressions (no metadata) - proveCalldata := make([]uint32, 0, len(initialChallenges)*2+2) + // Format: [0]=totalSize, [1...]=challenge linear expressions + calldata := make([]uint32, 1, 1+len(initialChallenges)*3) // Encode initial challenges for _, challenge := range initialChallenges { v := compiler.ToCanonicalVariable(challenge) - v.Compress(&proveCalldata) + v.Compress(&calldata) } + // Update total size + calldata[0] = uint32(len(calldata)) + // Execute prove solveBlueprint instruction - proofOutputs := compiler.AddInstruction(c.blueprints.ProveID, proveCalldata) + proofOutputs := compiler.AddInstruction(c.blueprints.ProveID, calldata) // Convert outputs to proof proofSerialized := make([]frontend.Variable, len(proofOutputs)) @@ -315,9 +321,11 @@ func (c *Circuit) GetValue(v gkr.Variable, i int) frontend.Variable { // Create an instruction that will retrieve the assignment at solve time compiler := c.api.Compiler() - // Build calldata: [wireI, instanceI, dependency_wire_as_linear_expression] + // Build calldata: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency_wire_as_linear_expression // The dependency ensures this instruction runs after the solve instruction for instance i - calldata := []uint32{uint32(v), uint32(i)} + calldata := make([]uint32, 3, 6) // pre-allocate: size + wireI + instanceI + dependency linear expression (typically 3) + calldata[1] = uint32(v) + calldata[2] = uint32(i) // Use the first output variable from instance i as a dependency // This ensures the solve instruction for this instance has completed @@ -328,6 +336,9 @@ func (c *Circuit) GetValue(v gkr.Variable, i int) frontend.Variable { depVar := compiler.ToCanonicalVariable(dependencyWire) depVar.Compress(&calldata) + // Update total size + calldata[0] = uint32(len(calldata)) + outputs := compiler.AddInstruction(c.blueprints.GetAssignmentID, calldata) return compiler.InternalVariable(outputs[0]) } From 65539ce57979d237efbd239974308a2db9d772c0 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sat, 7 Feb 2026 21:15:52 -0600 Subject: [PATCH 141/251] feat: first attempt at mismatch function --- test/assert.go | 173 +++++++++++++++++++++++++++++- test/assert_mismatch_test.go | 198 +++++++++++++++++++++++++++++++++++ 2 files changed, 369 insertions(+), 2 deletions(-) create mode 100644 test/assert_mismatch_test.go diff --git a/test/assert.go b/test/assert.go index 6eadcefc5b..06c0beb39e 100644 --- a/test/assert.go +++ b/test/assert.go @@ -10,6 +10,7 @@ import ( "reflect" "strings" "testing" + "unsafe" "github.com/consensys/gnark/backend" "github.com/consensys/gnark/backend/witness" @@ -29,6 +30,165 @@ var ( ErrInvalidWitnessVerified = errors.New("invalid witness resulted in a valid proof") ) +// getUnexportedField uses unsafe to access an unexported struct field. +// Returns an invalid Value if the field is not addressable. +func getUnexportedField(v reflect.Value, i int) reflect.Value { + field := v.Field(i) + if !field.CanAddr() { + return reflect.Value{} // Return invalid value if not addressable + } + // Use unsafe to bypass the exported field check + return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem() +} + +// deepEqualMismatch finds the first mismatch between two values using reflection. +// It returns the path to the mismatch and string representations of the differing values. +// If the values are equal, it returns empty strings. +func deepEqualMismatch(a, b interface{}) (path string, aVal string, bVal string) { + return deepEqualMismatchValue(reflect.ValueOf(a), reflect.ValueOf(b), "") +} + +func deepEqualMismatchValue(a, b reflect.Value, path string) (string, string, string) { + // Handle invalid values + if !a.IsValid() && !b.IsValid() { + return "", "", "" + } + if !a.IsValid() || !b.IsValid() { + return path, fmt.Sprintf("%v", a), fmt.Sprintf("%v", b) + } + + // Types must match + if a.Type() != b.Type() { + return path, fmt.Sprintf("type %v", a.Type()), fmt.Sprintf("type %v", b.Type()) + } + + switch a.Kind() { + case reflect.Ptr: + if a.IsNil() && b.IsNil() { + return "", "", "" + } + if a.IsNil() || b.IsNil() { + return path, fmt.Sprintf("%v", a), fmt.Sprintf("%v", b) + } + return deepEqualMismatchValue(a.Elem(), b.Elem(), path) + + case reflect.Interface: + if a.IsNil() && b.IsNil() { + return "", "", "" + } + if a.IsNil() || b.IsNil() { + return path, fmt.Sprintf("%v", a), fmt.Sprintf("%v", b) + } + return deepEqualMismatchValue(a.Elem(), b.Elem(), path) + + case reflect.Struct: + for i := range a.NumField() { + field := a.Type().Field(i) + fieldName := field.Name + fieldPath := path + "." + fieldName + if path == "" { + fieldPath = fieldName + } + fmt.Println("comparing field", fieldPath) + + var aField, bField reflect.Value + if field.IsExported() { + aField, bField = a.Field(i), b.Field(i) + } else { + aField, bField = getUnexportedField(a, i), getUnexportedField(b, i) + if !aField.IsValid() || !bField.IsValid() { + // Can't access field (not addressable) - skip + fmt.Printf("DEBUG: skipping %s (struct %v, canAddr=%v)\n", fieldPath, a.Type(), a.CanAddr()) + continue + } + } + + if p, av, bv := deepEqualMismatchValue(aField, bField, fieldPath); p != "" { + return p, av, bv + } + } + return "", "", "" + + case reflect.Slice, reflect.Array: + if a.Kind() == reflect.Slice && a.IsNil() && b.IsNil() { + return "", "", "" + } + if a.Kind() == reflect.Slice && (a.IsNil() || b.IsNil()) { + return path, fmt.Sprintf("nil=%v len=%d", a.IsNil(), a.Len()), fmt.Sprintf("nil=%v len=%d", b.IsNil(), b.Len()) + } + if a.Len() != b.Len() { + return path, fmt.Sprintf("len=%d", a.Len()), fmt.Sprintf("len=%d", b.Len()) + } + for i := 0; i < a.Len(); i++ { + elem := a.Index(i) + elemType := "" + // For interface elements, show the concrete type + if elem.Kind() == reflect.Interface && !elem.IsNil() { + elemType = fmt.Sprintf("(%v)", elem.Elem().Type()) + } + elemPath := fmt.Sprintf("%s[%d]%s", path, i, elemType) + if p, av, bv := deepEqualMismatchValue(a.Index(i), b.Index(i), elemPath); p != "" { + return p, av, bv + } + } + return "", "", "" + + case reflect.Map: + if a.IsNil() && b.IsNil() { + return "", "", "" + } + if a.IsNil() || b.IsNil() { + return path, fmt.Sprintf("nil=%v", a.IsNil()), fmt.Sprintf("nil=%v", b.IsNil()) + } + if a.Len() != b.Len() { + return path, fmt.Sprintf("len=%d", a.Len()), fmt.Sprintf("len=%d", b.Len()) + } + for _, key := range a.MapKeys() { + aVal := a.MapIndex(key) + bVal := b.MapIndex(key) + if !bVal.IsValid() { + return fmt.Sprintf("%s[%v]", path, key), "exists", "missing" + } + keyPath := fmt.Sprintf("%s[%v]", path, key) + if p, av, bv := deepEqualMismatchValue(aVal, bVal, keyPath); p != "" { + return p, av, bv + } + } + return "", "", "" + + case reflect.Func: + if a.IsNil() && b.IsNil() { + return "", "", "" + } + // Functions are only equal if both are nil + if a.Pointer() != b.Pointer() { + return path, fmt.Sprintf("func@%p", a.UnsafePointer()), fmt.Sprintf("func@%p", b.UnsafePointer()) + } + return "", "", "" + + case reflect.Chan, reflect.UnsafePointer: + if a.Pointer() != b.Pointer() { + return path, fmt.Sprintf("%p", a.UnsafePointer()), fmt.Sprintf("%p", b.UnsafePointer()) + } + return "", "", "" + + default: + // For basic types, use reflect.DeepEqual + // Check if we can access the interface (not unexported) + if a.CanInterface() && b.CanInterface() { + if !reflect.DeepEqual(a.Interface(), b.Interface()) { + if path == "" { + path = fmt.Sprintf("(%v)", a.Type()) + } + return path, fmt.Sprintf("%v", a.Interface()), fmt.Sprintf("%v", b.Interface()) + } + } else { + fmt.Printf("DEBUG default: can't interface %s (type=%v, aCanIface=%v, bCanIface=%v)\n", path, a.Type(), a.CanInterface(), b.CanInterface()) + } + return "", "", "" + } +} + // Assert is a helper to test circuits type Assert struct { t *testing.T @@ -158,7 +318,12 @@ func (assert *Assert) compile(circuit frontend.Circuit, field *big.Int, backendI } if !reflect.DeepEqual(ccs, _ccs) { - return nil, ErrCompilationNotDeterministic + path, aVal, bVal := deepEqualMismatch(ccs, _ccs) + if path != "" { + return nil, fmt.Errorf("%w: mismatch at %s: first=%s, second=%s", ErrCompilationNotDeterministic, path, aVal, bVal) + } + // Debug: show types + return nil, fmt.Errorf("%w (type: %T, could not determine path)", ErrCompilationNotDeterministic, ccs) } return ccs, nil @@ -178,7 +343,11 @@ func (assert *Assert) compileU32(circuit frontend.Circuit, field *big.Int, compi } if !reflect.DeepEqual(ccs, _ccs) { - return nil, ErrCompilationNotDeterministic + path, aVal, bVal := deepEqualMismatch(ccs, _ccs) + if path != "" { + return nil, fmt.Errorf("%w: mismatch at %s: first=%s, second=%s", ErrCompilationNotDeterministic, path, aVal, bVal) + } + return nil, fmt.Errorf("%w (type: %T, could not determine path)", ErrCompilationNotDeterministic, ccs) } return ccs, nil diff --git a/test/assert_mismatch_test.go b/test/assert_mismatch_test.go new file mode 100644 index 0000000000..225117522b --- /dev/null +++ b/test/assert_mismatch_test.go @@ -0,0 +1,198 @@ +package test + +import ( + "sync" + "testing" +) + +func TestDeepEqualMismatch_BasicTypes(t *testing.T) { + // Identical ints + path, _, _ := deepEqualMismatch(1, 1) + if path != "" { + t.Errorf("expected no mismatch for identical ints, got path: %s", path) + } + + // Different ints + path, a, b := deepEqualMismatch(1, 2) + if path == "" { + t.Error("expected mismatch for different ints") + } else { + t.Logf("int mismatch: path=%s, a=%s, b=%s", path, a, b) + } +} + +func TestDeepEqualMismatch_Structs(t *testing.T) { + type Simple struct { + X int + Y string + } + + // Identical structs + path, _, _ := deepEqualMismatch(Simple{1, "a"}, Simple{1, "a"}) + if path != "" { + t.Errorf("expected no mismatch for identical structs, got path: %s", path) + } + + // Different field + path, a, b := deepEqualMismatch(Simple{1, "a"}, Simple{2, "a"}) + if path == "" { + t.Error("expected mismatch for different structs") + } else { + t.Logf("struct mismatch: path=%s, a=%s, b=%s", path, a, b) + } +} + +func TestDeepEqualMismatch_Pointers(t *testing.T) { + type Simple struct { + X int + } + + a := &Simple{1} + b := &Simple{1} + c := &Simple{2} + + // Identical pointed-to values + path, _, _ := deepEqualMismatch(a, b) + if path != "" { + t.Errorf("expected no mismatch for identical pointer targets, got path: %s", path) + } + + // Different pointed-to values + path, av, bv := deepEqualMismatch(a, c) + if path == "" { + t.Error("expected mismatch for different pointer targets") + } else { + t.Logf("pointer mismatch: path=%s, a=%s, b=%s", path, av, bv) + } +} + +func TestDeepEqualMismatch_UnexportedFields(t *testing.T) { + type withUnexported struct { + Public int + private int + } + + a := &withUnexported{Public: 1, private: 10} + b := &withUnexported{Public: 1, private: 10} + c := &withUnexported{Public: 1, private: 20} + + // Identical (including unexported) + path, _, _ := deepEqualMismatch(a, b) + if path != "" { + t.Errorf("expected no mismatch, got path: %s", path) + } + + // Different unexported field + path, av, bv := deepEqualMismatch(a, c) + if path == "" { + t.Error("expected mismatch for different unexported field") + } else { + t.Logf("unexported mismatch: path=%s, a=%s, b=%s", path, av, bv) + } +} + +func TestDeepEqualMismatch_SyncPool(t *testing.T) { + type WithPool struct { + Name string + Pool sync.Pool + } + + a := &WithPool{Name: "test"} + b := &WithPool{Name: "test"} + + // Set different New functions + a.Pool.New = func() interface{} { return "a" } + b.Pool.New = func() interface{} { return "b" } + + path, av, bv := deepEqualMismatch(a, b) + if path == "" { + t.Error("expected mismatch for different sync.Pool.New functions") + } else { + t.Logf("sync.Pool mismatch: path=%s, a=%s, b=%s", path, av, bv) + } +} + +func TestDeepEqualMismatch_Slices(t *testing.T) { + type Item struct { + Value int + } + + a := []*Item{{1}, {2}} + b := []*Item{{1}, {2}} + c := []*Item{{1}, {3}} + + // Identical slices + path, _, _ := deepEqualMismatch(a, b) + if path != "" { + t.Errorf("expected no mismatch, got path: %s", path) + } + + // Different element + path, av, bv := deepEqualMismatch(a, c) + if path == "" { + t.Error("expected mismatch for different slice element") + } else { + t.Logf("slice mismatch: path=%s, a=%s, b=%s", path, av, bv) + } +} + +func TestDeepEqualMismatch_Interface(t *testing.T) { + type Container struct { + Data interface{} + } + + a := &Container{Data: 1} + b := &Container{Data: 1} + c := &Container{Data: 2} + + // Identical + path, _, _ := deepEqualMismatch(a, b) + if path != "" { + t.Errorf("expected no mismatch, got path: %s", path) + } + + // Different + path, av, bv := deepEqualMismatch(a, c) + if path == "" { + t.Error("expected mismatch") + } else { + t.Logf("interface mismatch: path=%s, a=%s, b=%s", path, av, bv) + } +} + +// Blueprint is a simple interface mimicking constraint.Blueprint +type Blueprint interface { + Name() string +} + +// BlueprintWithPool mimics BlueprintSolve with sync.Pool +type BlueprintWithPool struct { + Data string + pool sync.Pool +} + +func (b *BlueprintWithPool) Name() string { return b.Data } + +// System mimics constraint.System with a slice of Blueprint interfaces +type System struct { + Blueprints []Blueprint +} + +func TestDeepEqualMismatch_BlueprintLike(t *testing.T) { + // Create two systems with blueprints that have different pool.New functions + bp1 := &BlueprintWithPool{Data: "test"} + bp1.pool.New = func() interface{} { return "a" } + + bp2 := &BlueprintWithPool{Data: "test"} + bp2.pool.New = func() interface{} { return "b" } + + sys1 := &System{Blueprints: []Blueprint{bp1}} + sys2 := &System{Blueprints: []Blueprint{bp2}} + + path, av, bv := deepEqualMismatch(sys1, sys2) + if path == "" { + t.Error("expected mismatch for systems with different blueprint pool.New") + } else { + t.Logf("system mismatch: path=%s, a=%s, b=%s", path, av, bv) + } +} From 2dd4d2521da3febb2944d360e739df51135f0c46 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sat, 7 Feb 2026 22:10:13 -0600 Subject: [PATCH 142/251] fix: Solve blueprint nondeterminism for bn254 --- internal/gkr/bn254/blueprint.go | 7 ++-- test/assert.go | 57 +++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 55c8940dad..87e3997639 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -56,8 +56,6 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ @@ -157,14 +155,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.initialize() // circuit metadata + b.Circuit.OutputsList() // for side effects + b.outputWires = b.Circuit.Outputs() return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // ensure metadata is available - maxLevel := constraint.LevelUnset // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions diff --git a/test/assert.go b/test/assert.go index 06c0beb39e..1cfc56040b 100644 --- a/test/assert.go +++ b/test/assert.go @@ -62,6 +62,20 @@ func deepEqualMismatchValue(a, b reflect.Value, path string) (string, string, st return path, fmt.Sprintf("type %v", a.Type()), fmt.Sprintf("type %v", b.Type()) } + // Use DeepEqual as oracle - if equal, no need to recurse + if a.CanInterface() && b.CanInterface() { + if reflect.DeepEqual(a.Interface(), b.Interface()) { + return "", "", "" + } + } + + // At this point, we know there's a mismatch at or below this path. + // Try to find a more specific path; if we can't, return this path. + bestPath := path + if bestPath == "" { + bestPath = fmt.Sprintf("(%v)", a.Type()) + } + switch a.Kind() { case reflect.Ptr: if a.IsNil() && b.IsNil() { @@ -70,7 +84,10 @@ func deepEqualMismatchValue(a, b reflect.Value, path string) (string, string, st if a.IsNil() || b.IsNil() { return path, fmt.Sprintf("%v", a), fmt.Sprintf("%v", b) } - return deepEqualMismatchValue(a.Elem(), b.Elem(), path) + if p, av, bv := deepEqualMismatchValue(a.Elem(), b.Elem(), path); p != "" { + return p, av, bv + } + return bestPath, "(pointer contents differ)", "(pointer contents differ)" case reflect.Interface: if a.IsNil() && b.IsNil() { @@ -79,7 +96,10 @@ func deepEqualMismatchValue(a, b reflect.Value, path string) (string, string, st if a.IsNil() || b.IsNil() { return path, fmt.Sprintf("%v", a), fmt.Sprintf("%v", b) } - return deepEqualMismatchValue(a.Elem(), b.Elem(), path) + if p, av, bv := deepEqualMismatchValue(a.Elem(), b.Elem(), path); p != "" { + return p, av, bv + } + return bestPath, "(interface contents differ)", "(interface contents differ)" case reflect.Struct: for i := range a.NumField() { @@ -89,7 +109,6 @@ func deepEqualMismatchValue(a, b reflect.Value, path string) (string, string, st if path == "" { fieldPath = fieldName } - fmt.Println("comparing field", fieldPath) var aField, bField reflect.Value if field.IsExported() { @@ -97,8 +116,6 @@ func deepEqualMismatchValue(a, b reflect.Value, path string) (string, string, st } else { aField, bField = getUnexportedField(a, i), getUnexportedField(b, i) if !aField.IsValid() || !bField.IsValid() { - // Can't access field (not addressable) - skip - fmt.Printf("DEBUG: skipping %s (struct %v, canAddr=%v)\n", fieldPath, a.Type(), a.CanAddr()) continue } } @@ -107,7 +124,8 @@ func deepEqualMismatchValue(a, b reflect.Value, path string) (string, string, st return p, av, bv } } - return "", "", "" + // Couldn't find specific field - return best path + return bestPath, "(struct differs)", "(struct differs)" case reflect.Slice, reflect.Array: if a.Kind() == reflect.Slice && a.IsNil() && b.IsNil() { @@ -131,7 +149,7 @@ func deepEqualMismatchValue(a, b reflect.Value, path string) (string, string, st return p, av, bv } } - return "", "", "" + return bestPath, "(slice/array differs)", "(slice/array differs)" case reflect.Map: if a.IsNil() && b.IsNil() { @@ -154,14 +172,19 @@ func deepEqualMismatchValue(a, b reflect.Value, path string) (string, string, st return p, av, bv } } - return "", "", "" + return bestPath, "(map differs)", "(map differs)" case reflect.Func: if a.IsNil() && b.IsNil() { return "", "", "" } - // Functions are only equal if both are nil - if a.Pointer() != b.Pointer() { + // Use DeepEqual for functions - it compares closure state, not just code pointer + if a.CanInterface() && b.CanInterface() { + if !reflect.DeepEqual(a.Interface(), b.Interface()) { + return path, fmt.Sprintf("func@%p", a.UnsafePointer()), fmt.Sprintf("func@%p", b.UnsafePointer()) + } + } else if a.Pointer() != b.Pointer() { + // Fallback to pointer comparison if can't interface return path, fmt.Sprintf("func@%p", a.UnsafePointer()), fmt.Sprintf("func@%p", b.UnsafePointer()) } return "", "", "" @@ -173,19 +196,11 @@ func deepEqualMismatchValue(a, b reflect.Value, path string) (string, string, st return "", "", "" default: - // For basic types, use reflect.DeepEqual - // Check if we can access the interface (not unexported) + // Leaf node that differs if a.CanInterface() && b.CanInterface() { - if !reflect.DeepEqual(a.Interface(), b.Interface()) { - if path == "" { - path = fmt.Sprintf("(%v)", a.Type()) - } - return path, fmt.Sprintf("%v", a.Interface()), fmt.Sprintf("%v", b.Interface()) - } - } else { - fmt.Printf("DEBUG default: can't interface %s (type=%v, aCanIface=%v, bCanIface=%v)\n", path, a.Type(), a.CanInterface(), b.CanInterface()) + return bestPath, fmt.Sprintf("%v", a.Interface()), fmt.Sprintf("%v", b.Interface()) } - return "", "", "" + return bestPath, "(unexported)", "(unexported)" } } From 96edfaf9fd0a83a21109ab86b952105ce73a550b Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sat, 7 Feb 2026 23:15:26 -0500 Subject: [PATCH 143/251] build: generify --- internal/generator/backend/template/gkr/blueprint.go.tmpl | 5 ++--- internal/gkr/bls12-377/blueprint.go | 5 ++--- internal/gkr/bls12-381/blueprint.go | 5 ++--- internal/gkr/bls24-315/blueprint.go | 5 ++--- internal/gkr/bls24-317/blueprint.go | 5 ++--- internal/gkr/bn254/blueprint.go | 2 ++ internal/gkr/bw6-633/blueprint.go | 5 ++--- internal/gkr/bw6-761/blueprint.go | 5 ++--- 8 files changed, 16 insertions(+), 21 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index eb0b866c75..35f64df54b 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -150,14 +150,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.initialize() // circuit metadata + b.Circuit.OutputsList() // for side effects + b.outputWires = b.Circuit.Outputs() return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // ensure metadata is available - maxLevel := constraint.LevelUnset // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 4722895791..ac2f3801eb 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -157,14 +157,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.initialize() // circuit metadata + b.Circuit.OutputsList() // for side effects + b.outputWires = b.Circuit.Outputs() return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // ensure metadata is available - maxLevel := constraint.LevelUnset // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index a15c02f38e..093f709799 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -157,14 +157,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.initialize() // circuit metadata + b.Circuit.OutputsList() // for side effects + b.outputWires = b.Circuit.Outputs() return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // ensure metadata is available - maxLevel := constraint.LevelUnset // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index b583a4283f..3e2f37fa56 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -157,14 +157,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.initialize() // circuit metadata + b.Circuit.OutputsList() // for side effects + b.outputWires = b.Circuit.Outputs() return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // ensure metadata is available - maxLevel := constraint.LevelUnset // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 5cce4d1b80..451f585f8b 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -157,14 +157,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.initialize() // circuit metadata + b.Circuit.OutputsList() // for side effects + b.outputWires = b.Circuit.Outputs() return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // ensure metadata is available - maxLevel := constraint.LevelUnset // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 87e3997639..f58b4d0350 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -56,6 +56,8 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit + b.Circuit.OutputsList() // for side effects + b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 900c4eafb6..ad9ee3125f 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -157,14 +157,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.initialize() // circuit metadata + b.Circuit.OutputsList() // for side effects + b.outputWires = b.Circuit.Outputs() return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // ensure metadata is available - maxLevel := constraint.LevelUnset // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 4a776b0a13..1dd13e98e6 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -157,14 +157,13 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.initialize() // circuit metadata + b.Circuit.OutputsList() // for side effects + b.outputWires = b.Circuit.Outputs() return len(b.outputWires) } // UpdateInstructionTree implements Blueprint func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - b.initialize() // ensure metadata is available - maxLevel := constraint.LevelUnset // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions From fa8c6c6ce2b07be2e7ea02297295af618e0f1fb8 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sat, 7 Feb 2026 23:16:22 -0500 Subject: [PATCH 144/251] fix: codegen --- internal/generator/backend/template/gkr/blueprint.go.tmpl | 2 -- internal/gkr/bls12-377/blueprint.go | 2 -- internal/gkr/bls12-381/blueprint.go | 2 -- internal/gkr/bls24-315/blueprint.go | 2 -- internal/gkr/bls24-317/blueprint.go | 2 -- internal/gkr/bn254/blueprint.go | 2 -- internal/gkr/bw6-633/blueprint.go | 2 -- internal/gkr/bw6-761/blueprint.go | 2 -- 8 files changed, 16 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 35f64df54b..1f61298013 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -49,8 +49,6 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index ac2f3801eb..6d6eaeeff1 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -56,8 +56,6 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 093f709799..2326745453 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -56,8 +56,6 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index 3e2f37fa56..0d37a68678 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -56,8 +56,6 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 451f585f8b..d6be8368b3 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -56,8 +56,6 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index f58b4d0350..87e3997639 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -56,8 +56,6 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index ad9ee3125f..1e903275bf 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -56,8 +56,6 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 1dd13e98e6..aac875979e 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -56,8 +56,6 @@ func (b *BlueprintSolve) initialize() { } // Compute metadata from Circuit - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ From 1945d67022148c413d57bdd599953f2eba8c1b0d Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sat, 7 Feb 2026 23:28:21 -0500 Subject: [PATCH 145/251] fix: Prove dependence on Solve --- .../backend/template/gkr/blueprint.go.tmpl | 19 +++++++++++-------- internal/gkr/bls12-377/blueprint.go | 18 ++++++++++-------- internal/gkr/bls12-381/blueprint.go | 18 ++++++++++-------- internal/gkr/bls24-315/blueprint.go | 18 ++++++++++-------- internal/gkr/bls24-317/blueprint.go | 18 ++++++++++-------- internal/gkr/bn254/blueprint.go | 18 ++++++++++-------- internal/gkr/bw6-633/blueprint.go | 18 ++++++++++-------- internal/gkr/bw6-761/blueprint.go | 18 ++++++++++-------- 8 files changed, 81 insertions(+), 64 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 1f61298013..9985d4ff25 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -25,10 +25,11 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` - outputWires []int `cbor:"-"` + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -182,6 +183,8 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } + b.maxOutputLevel = max(b.maxOutputLevel, outputLevel) + return outputLevel } @@ -287,7 +290,7 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := constraint.LevelUnset + maxLevel := b.SolveBlueprint.maxOutputLevel // Format: [0]=totalSize, [1...]=challenge linear expressions offset := 1 // skip size prefix @@ -303,9 +306,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree if !tree.HasWire(wireID) { continue } - if level := tree.GetWireLevel(wireID); level > maxLevel { - maxLevel = level - } + + maxLevel = max(maxLevel, tree.GetWireLevel(wireID)) } } @@ -320,6 +322,7 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree return outputLevel } + // BlueprintGetAssignment is a {{.FieldID}}-specific blueprint for retrieving wire assignments. type BlueprintGetAssignment struct { SolveBlueprintID constraint.BlueprintID diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 6d6eaeeff1..d21fccc1b3 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -32,10 +32,11 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` - outputWires []int `cbor:"-"` + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -189,6 +190,8 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } + b.maxOutputLevel = max(b.maxOutputLevel, outputLevel) + return outputLevel } @@ -294,7 +297,7 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := constraint.LevelUnset + maxLevel := b.SolveBlueprint.maxOutputLevel // Format: [0]=totalSize, [1...]=challenge linear expressions offset := 1 // skip size prefix @@ -310,9 +313,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree if !tree.HasWire(wireID) { continue } - if level := tree.GetWireLevel(wireID); level > maxLevel { - maxLevel = level - } + + maxLevel = max(maxLevel, tree.GetWireLevel(wireID)) } } diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 2326745453..ea1bfb010a 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -32,10 +32,11 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` - outputWires []int `cbor:"-"` + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -189,6 +190,8 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } + b.maxOutputLevel = max(b.maxOutputLevel, outputLevel) + return outputLevel } @@ -294,7 +297,7 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := constraint.LevelUnset + maxLevel := b.SolveBlueprint.maxOutputLevel // Format: [0]=totalSize, [1...]=challenge linear expressions offset := 1 // skip size prefix @@ -310,9 +313,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree if !tree.HasWire(wireID) { continue } - if level := tree.GetWireLevel(wireID); level > maxLevel { - maxLevel = level - } + + maxLevel = max(maxLevel, tree.GetWireLevel(wireID)) } } diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index 0d37a68678..d83b1caa6a 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -32,10 +32,11 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` - outputWires []int `cbor:"-"` + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -189,6 +190,8 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } + b.maxOutputLevel = max(b.maxOutputLevel, outputLevel) + return outputLevel } @@ -294,7 +297,7 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := constraint.LevelUnset + maxLevel := b.SolveBlueprint.maxOutputLevel // Format: [0]=totalSize, [1...]=challenge linear expressions offset := 1 // skip size prefix @@ -310,9 +313,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree if !tree.HasWire(wireID) { continue } - if level := tree.GetWireLevel(wireID); level > maxLevel { - maxLevel = level - } + + maxLevel = max(maxLevel, tree.GetWireLevel(wireID)) } } diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index d6be8368b3..27953c6426 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -32,10 +32,11 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` - outputWires []int `cbor:"-"` + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -189,6 +190,8 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } + b.maxOutputLevel = max(b.maxOutputLevel, outputLevel) + return outputLevel } @@ -294,7 +297,7 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := constraint.LevelUnset + maxLevel := b.SolveBlueprint.maxOutputLevel // Format: [0]=totalSize, [1...]=challenge linear expressions offset := 1 // skip size prefix @@ -310,9 +313,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree if !tree.HasWire(wireID) { continue } - if level := tree.GetWireLevel(wireID); level > maxLevel { - maxLevel = level - } + + maxLevel = max(maxLevel, tree.GetWireLevel(wireID)) } } diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 87e3997639..efd9ab4260 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -32,10 +32,11 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` - outputWires []int `cbor:"-"` + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -189,6 +190,8 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } + b.maxOutputLevel = max(b.maxOutputLevel, outputLevel) + return outputLevel } @@ -294,7 +297,7 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := constraint.LevelUnset + maxLevel := b.SolveBlueprint.maxOutputLevel // Format: [0]=totalSize, [1...]=challenge linear expressions offset := 1 // skip size prefix @@ -310,9 +313,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree if !tree.HasWire(wireID) { continue } - if level := tree.GetWireLevel(wireID); level > maxLevel { - maxLevel = level - } + + maxLevel = max(maxLevel, tree.GetWireLevel(wireID)) } } diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 1e903275bf..4bb0f21dfe 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -32,10 +32,11 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` - outputWires []int `cbor:"-"` + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -189,6 +190,8 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } + b.maxOutputLevel = max(b.maxOutputLevel, outputLevel) + return outputLevel } @@ -294,7 +297,7 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := constraint.LevelUnset + maxLevel := b.SolveBlueprint.maxOutputLevel // Format: [0]=totalSize, [1...]=challenge linear expressions offset := 1 // skip size prefix @@ -310,9 +313,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree if !tree.HasWire(wireID) { continue } - if level := tree.GetWireLevel(wireID); level > maxLevel { - maxLevel = level - } + + maxLevel = max(maxLevel, tree.GetWireLevel(wireID)) } } diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index aac875979e..ef89229114 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -32,10 +32,11 @@ type BlueprintSolve struct { NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` - outputWires []int `cbor:"-"` + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + nbInputs int `cbor:"-"` + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -189,6 +190,8 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } + b.maxOutputLevel = max(b.maxOutputLevel, outputLevel) + return outputLevel } @@ -294,7 +297,7 @@ func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { // UpdateInstructionTree implements Blueprint func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := constraint.LevelUnset + maxLevel := b.SolveBlueprint.maxOutputLevel // Format: [0]=totalSize, [1...]=challenge linear expressions offset := 1 // skip size prefix @@ -310,9 +313,8 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree if !tree.HasWire(wireID) { continue } - if level := tree.GetWireLevel(wireID); level > maxLevel { - maxLevel = level - } + + maxLevel = max(maxLevel, tree.GetWireLevel(wireID)) } } From 59ea7482faa4fa296965dc2dfbac315e19dc4f2e Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sat, 7 Feb 2026 23:46:52 -0500 Subject: [PATCH 146/251] remove unused field --- internal/generator/backend/template/gkr/blueprint.go.tmpl | 1 - internal/gkr/bls12-377/blueprint.go | 1 - internal/gkr/bls12-381/blueprint.go | 1 - internal/gkr/bls24-315/blueprint.go | 1 - internal/gkr/bls24-317/blueprint.go | 1 - internal/gkr/bn254/blueprint.go | 1 - internal/gkr/bw6-633/blueprint.go | 1 - internal/gkr/bw6-761/blueprint.go | 1 - 8 files changed, 8 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 9985d4ff25..ac38b5e13d 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -27,7 +27,6 @@ type BlueprintSolve struct { // Not serialized - recreated lazily at solve time assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index d21fccc1b3..78ae0a3df8 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -34,7 +34,6 @@ type BlueprintSolve struct { // Not serialized - recreated lazily at solve time assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index ea1bfb010a..7ecf5969cd 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -34,7 +34,6 @@ type BlueprintSolve struct { // Not serialized - recreated lazily at solve time assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index d83b1caa6a..17afc52e50 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -34,7 +34,6 @@ type BlueprintSolve struct { // Not serialized - recreated lazily at solve time assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 27953c6426..d3d9c25368 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -34,7 +34,6 @@ type BlueprintSolve struct { // Not serialized - recreated lazily at solve time assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index efd9ab4260..4a9164b5c5 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -34,7 +34,6 @@ type BlueprintSolve struct { // Not serialized - recreated lazily at solve time assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 4bb0f21dfe..d3f2df7dcb 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -34,7 +34,6 @@ type BlueprintSolve struct { // Not serialized - recreated lazily at solve time assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index ef89229114..028709f9d0 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -34,7 +34,6 @@ type BlueprintSolve struct { // Not serialized - recreated lazily at solve time assignments WireAssignment `cbor:"-"` evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - nbInputs int `cbor:"-"` outputWires []int `cbor:"-"` maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances From 5aa15d511768edb48ef73217cbdbc29881566ee3 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sun, 8 Feb 2026 01:31:13 -0500 Subject: [PATCH 147/251] fix: padding issues --- .../backend/template/gkr/blueprint.go.tmpl | 25 ++++++++++++++++-- internal/gkr/bls12-377/blueprint.go | 26 +++++++++++++++++-- internal/gkr/bls12-381/blueprint.go | 26 +++++++++++++++++-- internal/gkr/bls24-315/blueprint.go | 26 +++++++++++++++++-- internal/gkr/bls24-317/blueprint.go | 26 +++++++++++++++++-- internal/gkr/bn254/blueprint.go | 26 +++++++++++++++++-- internal/gkr/bw6-633/blueprint.go | 26 +++++++++++++++++-- internal/gkr/bw6-761/blueprint.go | 26 +++++++++++++++++-- std/gkrapi/compile.go | 2 +- 9 files changed, 192 insertions(+), 17 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index ac38b5e13d..4432ecb7cd 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -2,6 +2,7 @@ import ( "fmt" "math/big" "math/bits" + "slices" "sync" "{{ .FieldPackagePath }}" @@ -64,8 +65,9 @@ func (b *BlueprintSolve) initialize() { } b.assignments = make(WireAssignment, len(b.Circuit)) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } } @@ -213,6 +215,24 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } + nbPaddedInstances := uint32(ecc.NextPowerOfTwo(uint64(solveBlueprint.NbInstances))) + if nbPaddedInstances != uint32(len(assignments[0])) { // test engine path + nbPadding := nbPaddedInstances - solveBlueprint.NbInstances + for wI := range assignments { + assignments[wI] = slices.Grow(assignments[wI], int(nbPadding)) + toRepeat := assignments[wI][solveBlueprint.NbInstances-1] + for range nbPadding { + assignments[wI] = append(assignments[wI], toRepeat) + } + } + } else { + for wI := range assignments { + for i := solveBlueprint.NbInstances; i < nbPaddedInstances; i++ { + assignments[wI][i] = assignments[wI][i-1] + } + } + } + // Read initial challenges from instruction calldata (parse dynamically, no metadata) // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges @@ -278,7 +298,8 @@ func (b *BlueprintProve) proofSize() int { if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) + logNbInstances := bits.TrailingZeros64(nbPaddedInstances) return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 78ae0a3df8..eab6efefbd 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -9,8 +9,10 @@ import ( "fmt" "math/big" "math/bits" + "slices" "sync" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" @@ -71,8 +73,9 @@ func (b *BlueprintSolve) initialize() { } b.assignments = make(WireAssignment, len(b.Circuit)) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } } @@ -220,6 +223,24 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } + nbPaddedInstances := uint32(ecc.NextPowerOfTwo(uint64(solveBlueprint.NbInstances))) + if nbPaddedInstances != uint32(len(assignments[0])) { // test engine path + nbPadding := nbPaddedInstances - solveBlueprint.NbInstances + for wI := range assignments { + assignments[wI] = slices.Grow(assignments[wI], int(nbPadding)) + toRepeat := assignments[wI][solveBlueprint.NbInstances-1] + for range nbPadding { + assignments[wI] = append(assignments[wI], toRepeat) + } + } + } else { + for wI := range assignments { + for i := solveBlueprint.NbInstances; i < nbPaddedInstances; i++ { + assignments[wI][i] = assignments[wI][i-1] + } + } + } + // Read initial challenges from instruction calldata (parse dynamically, no metadata) // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges @@ -285,7 +306,8 @@ func (b *BlueprintProve) proofSize() int { if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) + logNbInstances := bits.TrailingZeros64(nbPaddedInstances) return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 7ecf5969cd..1fefca6e97 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -9,8 +9,10 @@ import ( "fmt" "math/big" "math/bits" + "slices" "sync" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" @@ -71,8 +73,9 @@ func (b *BlueprintSolve) initialize() { } b.assignments = make(WireAssignment, len(b.Circuit)) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } } @@ -220,6 +223,24 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } + nbPaddedInstances := uint32(ecc.NextPowerOfTwo(uint64(solveBlueprint.NbInstances))) + if nbPaddedInstances != uint32(len(assignments[0])) { // test engine path + nbPadding := nbPaddedInstances - solveBlueprint.NbInstances + for wI := range assignments { + assignments[wI] = slices.Grow(assignments[wI], int(nbPadding)) + toRepeat := assignments[wI][solveBlueprint.NbInstances-1] + for range nbPadding { + assignments[wI] = append(assignments[wI], toRepeat) + } + } + } else { + for wI := range assignments { + for i := solveBlueprint.NbInstances; i < nbPaddedInstances; i++ { + assignments[wI][i] = assignments[wI][i-1] + } + } + } + // Read initial challenges from instruction calldata (parse dynamically, no metadata) // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges @@ -285,7 +306,8 @@ func (b *BlueprintProve) proofSize() int { if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) + logNbInstances := bits.TrailingZeros64(nbPaddedInstances) return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index 17afc52e50..f374d5c95e 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -9,8 +9,10 @@ import ( "fmt" "math/big" "math/bits" + "slices" "sync" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" @@ -71,8 +73,9 @@ func (b *BlueprintSolve) initialize() { } b.assignments = make(WireAssignment, len(b.Circuit)) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } } @@ -220,6 +223,24 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } + nbPaddedInstances := uint32(ecc.NextPowerOfTwo(uint64(solveBlueprint.NbInstances))) + if nbPaddedInstances != uint32(len(assignments[0])) { // test engine path + nbPadding := nbPaddedInstances - solveBlueprint.NbInstances + for wI := range assignments { + assignments[wI] = slices.Grow(assignments[wI], int(nbPadding)) + toRepeat := assignments[wI][solveBlueprint.NbInstances-1] + for range nbPadding { + assignments[wI] = append(assignments[wI], toRepeat) + } + } + } else { + for wI := range assignments { + for i := solveBlueprint.NbInstances; i < nbPaddedInstances; i++ { + assignments[wI][i] = assignments[wI][i-1] + } + } + } + // Read initial challenges from instruction calldata (parse dynamically, no metadata) // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges @@ -285,7 +306,8 @@ func (b *BlueprintProve) proofSize() int { if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) + logNbInstances := bits.TrailingZeros64(nbPaddedInstances) return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index d3d9c25368..10e5c48d09 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -9,8 +9,10 @@ import ( "fmt" "math/big" "math/bits" + "slices" "sync" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" @@ -71,8 +73,9 @@ func (b *BlueprintSolve) initialize() { } b.assignments = make(WireAssignment, len(b.Circuit)) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } } @@ -220,6 +223,24 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } + nbPaddedInstances := uint32(ecc.NextPowerOfTwo(uint64(solveBlueprint.NbInstances))) + if nbPaddedInstances != uint32(len(assignments[0])) { // test engine path + nbPadding := nbPaddedInstances - solveBlueprint.NbInstances + for wI := range assignments { + assignments[wI] = slices.Grow(assignments[wI], int(nbPadding)) + toRepeat := assignments[wI][solveBlueprint.NbInstances-1] + for range nbPadding { + assignments[wI] = append(assignments[wI], toRepeat) + } + } + } else { + for wI := range assignments { + for i := solveBlueprint.NbInstances; i < nbPaddedInstances; i++ { + assignments[wI][i] = assignments[wI][i-1] + } + } + } + // Read initial challenges from instruction calldata (parse dynamically, no metadata) // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges @@ -285,7 +306,8 @@ func (b *BlueprintProve) proofSize() int { if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) + logNbInstances := bits.TrailingZeros64(nbPaddedInstances) return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 4a9164b5c5..41237e32f4 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -9,8 +9,10 @@ import ( "fmt" "math/big" "math/bits" + "slices" "sync" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bn254/fr" "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" @@ -71,8 +73,9 @@ func (b *BlueprintSolve) initialize() { } b.assignments = make(WireAssignment, len(b.Circuit)) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } } @@ -220,6 +223,24 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } + nbPaddedInstances := uint32(ecc.NextPowerOfTwo(uint64(solveBlueprint.NbInstances))) + if nbPaddedInstances != uint32(len(assignments[0])) { // test engine path + nbPadding := nbPaddedInstances - solveBlueprint.NbInstances + for wI := range assignments { + assignments[wI] = slices.Grow(assignments[wI], int(nbPadding)) + toRepeat := assignments[wI][solveBlueprint.NbInstances-1] + for range nbPadding { + assignments[wI] = append(assignments[wI], toRepeat) + } + } + } else { + for wI := range assignments { + for i := solveBlueprint.NbInstances; i < nbPaddedInstances; i++ { + assignments[wI][i] = assignments[wI][i-1] + } + } + } + // Read initial challenges from instruction calldata (parse dynamically, no metadata) // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges @@ -285,7 +306,8 @@ func (b *BlueprintProve) proofSize() int { if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) + logNbInstances := bits.TrailingZeros64(nbPaddedInstances) return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index d3f2df7dcb..14261775c3 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -9,8 +9,10 @@ import ( "fmt" "math/big" "math/bits" + "slices" "sync" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" @@ -71,8 +73,9 @@ func (b *BlueprintSolve) initialize() { } b.assignments = make(WireAssignment, len(b.Circuit)) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } } @@ -220,6 +223,24 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } + nbPaddedInstances := uint32(ecc.NextPowerOfTwo(uint64(solveBlueprint.NbInstances))) + if nbPaddedInstances != uint32(len(assignments[0])) { // test engine path + nbPadding := nbPaddedInstances - solveBlueprint.NbInstances + for wI := range assignments { + assignments[wI] = slices.Grow(assignments[wI], int(nbPadding)) + toRepeat := assignments[wI][solveBlueprint.NbInstances-1] + for range nbPadding { + assignments[wI] = append(assignments[wI], toRepeat) + } + } + } else { + for wI := range assignments { + for i := solveBlueprint.NbInstances; i < nbPaddedInstances; i++ { + assignments[wI][i] = assignments[wI][i-1] + } + } + } + // Read initial challenges from instruction calldata (parse dynamically, no metadata) // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges @@ -285,7 +306,8 @@ func (b *BlueprintProve) proofSize() int { if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) + logNbInstances := bits.TrailingZeros64(nbPaddedInstances) return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 028709f9d0..9332358211 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -9,8 +9,10 @@ import ( "fmt" "math/big" "math/bits" + "slices" "sync" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" @@ -71,8 +73,9 @@ func (b *BlueprintSolve) initialize() { } b.assignments = make(WireAssignment, len(b.Circuit)) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, b.NbInstances) + b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } } @@ -220,6 +223,24 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } + nbPaddedInstances := uint32(ecc.NextPowerOfTwo(uint64(solveBlueprint.NbInstances))) + if nbPaddedInstances != uint32(len(assignments[0])) { // test engine path + nbPadding := nbPaddedInstances - solveBlueprint.NbInstances + for wI := range assignments { + assignments[wI] = slices.Grow(assignments[wI], int(nbPadding)) + toRepeat := assignments[wI][solveBlueprint.NbInstances-1] + for range nbPadding { + assignments[wI] = append(assignments[wI], toRepeat) + } + } + } else { + for wI := range assignments { + for i := solveBlueprint.NbInstances; i < nbPaddedInstances; i++ { + assignments[wI][i] = assignments[wI][i-1] + } + } + } + // Read initial challenges from instruction calldata (parse dynamically, no metadata) // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges @@ -285,7 +306,8 @@ func (b *BlueprintProve) proofSize() int { if b.SolveBlueprint.NbInstances < 2 { return 0 } - logNbInstances := bits.TrailingZeros32(b.SolveBlueprint.NbInstances) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) + logNbInstances := bits.TrailingZeros64(nbPaddedInstances) return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index ab83119250..487f8191b1 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -216,7 +216,7 @@ func (c *Circuit) finalize(api frontend.API) error { } } - c.blueprints.Solve.SetNbInstances(uint32(nbPaddedInstances)) + c.blueprints.Solve.SetNbInstances(uint32(c.nbInstances)) curve := utils.FieldToCurve(api.Compiler().Field()) From 3fdb62a391cba41b0744796cf981abf7a27043b3 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sun, 8 Feb 2026 01:52:06 -0500 Subject: [PATCH 148/251] fix: solving race condition --- internal/generator/backend/template/gkr/blueprint.go.tmpl | 7 ++++--- internal/gkr/bls12-377/blueprint.go | 7 ++++--- internal/gkr/bls12-381/blueprint.go | 7 ++++--- internal/gkr/bls24-315/blueprint.go | 7 ++++--- internal/gkr/bls24-317/blueprint.go | 7 ++++--- internal/gkr/bn254/blueprint.go | 7 ++++--- internal/gkr/bw6-633/blueprint.go | 7 ++++--- internal/gkr/bw6-761/blueprint.go | 7 ++++--- std/hash/mimc/gkr-mimc/gkr-mimc_test.go | 4 ++-- 9 files changed, 34 insertions(+), 26 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 4432ecb7cd..96aa87c7dd 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -64,11 +64,12 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignments = make(WireAssignment, len(b.Circuit)) + assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) - for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) + for i := range assignments { + assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } + b.assignments = assignments } // Solve implements the BlueprintStateful interface. diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index eab6efefbd..45d4a8668d 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -72,11 +72,12 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignments = make(WireAssignment, len(b.Circuit)) + assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) - for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) + for i := range assignments { + assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } + b.assignments = assignments } // Solve implements the BlueprintStateful interface. diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 1fefca6e97..f407479167 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -72,11 +72,12 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignments = make(WireAssignment, len(b.Circuit)) + assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) - for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) + for i := range assignments { + assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } + b.assignments = assignments } // Solve implements the BlueprintStateful interface. diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index f374d5c95e..5f4d6b10d2 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -72,11 +72,12 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignments = make(WireAssignment, len(b.Circuit)) + assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) - for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) + for i := range assignments { + assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } + b.assignments = assignments } // Solve implements the BlueprintStateful interface. diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 10e5c48d09..ba0815bf0b 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -72,11 +72,12 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignments = make(WireAssignment, len(b.Circuit)) + assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) - for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) + for i := range assignments { + assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } + b.assignments = assignments } // Solve implements the BlueprintStateful interface. diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 41237e32f4..4518efe35c 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -72,11 +72,12 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignments = make(WireAssignment, len(b.Circuit)) + assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) - for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) + for i := range assignments { + assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } + b.assignments = assignments } // Solve implements the BlueprintStateful interface. diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 14261775c3..100702ceba 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -72,11 +72,12 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignments = make(WireAssignment, len(b.Circuit)) + assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) - for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) + for i := range assignments { + assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } + b.assignments = assignments } // Solve implements the BlueprintStateful interface. diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 9332358211..1968695193 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -72,11 +72,12 @@ func (b *BlueprintSolve) initialize() { return ce } - b.assignments = make(WireAssignment, len(b.Circuit)) + assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) - for i := range b.assignments { - b.assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) + for i := range assignments { + assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) } + b.assignments = assignments } // Solve implements the BlueprintStateful interface. diff --git a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go index e5b8ed4f83..31bd5f7384 100644 --- a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go +++ b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go @@ -83,7 +83,7 @@ type merkleTreeCircuit struct { Leaves []frontend.Variable } -func (c merkleTreeCircuit) Define(api frontend.API) error { +func (c *merkleTreeCircuit) Define(api frontend.API) error { if len(c.Leaves) == 0 { return errors.New("no hashing to do") } @@ -127,7 +127,7 @@ func BenchmarkGkrMiMC(b *testing.B) { assignment.Leaves[i] = i } - cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, circuit) + cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &circuit) require.NoError(b, err) b.ResetTimer() From efae0d763414dab94addd3d28285d8a908d0ed66 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 9 Feb 2026 08:39:09 -0500 Subject: [PATCH 149/251] refactor: proof.Flatten --- .../backend/template/gkr/blueprint.go.tmpl | 29 ++++------------ .../backend/template/gkr/gkr.go.tmpl | 34 ++++++++++--------- internal/gkr/bls12-377/blueprint.go | 28 +++------------ internal/gkr/bls12-377/gkr.go | 34 ++++++++++--------- internal/gkr/bls12-381/blueprint.go | 28 +++------------ internal/gkr/bls12-381/gkr.go | 34 ++++++++++--------- internal/gkr/bls24-315/blueprint.go | 28 +++------------ internal/gkr/bls24-315/gkr.go | 34 ++++++++++--------- internal/gkr/bls24-317/blueprint.go | 28 +++------------ internal/gkr/bls24-317/gkr.go | 34 ++++++++++--------- internal/gkr/bn254/blueprint.go | 28 +++------------ internal/gkr/bn254/gkr.go | 34 ++++++++++--------- internal/gkr/bw6-633/blueprint.go | 28 +++------------ internal/gkr/bw6-633/gkr.go | 34 ++++++++++--------- internal/gkr/bw6-761/blueprint.go | 28 +++------------ internal/gkr/bw6-761/gkr.go | 34 ++++++++++--------- internal/gkr/small_rational/gkr.go | 34 ++++++++++--------- 17 files changed, 203 insertions(+), 328 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 96aa87c7dd..a503fb010d 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -1,10 +1,10 @@ import ( "fmt" - "math/big" "math/bits" "slices" "sync" + "github.com/consensys/gnark-crypto/ecc" "{{ .FieldPackagePath }}" "{{ .FieldPackagePath }}/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" @@ -258,28 +258,11 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("{{toLower .FieldID}} prove failed: %w", err) } - // Serialize proof and convert to U64 - proofSize := 0 - for i := range proof { - for _, poly := range proof[i].partialSumPolys { - proofSize += len(poly) - } - if proof[i].finalEvalProof != nil { - proofSize += len(proof[i].finalEvalProof) - } - } - - outsBig := make([]*big.Int, proofSize) - for i := range outsBig { - outsBig[i] = new(big.Int) - } - if err := proof.SerializeToBigInts(outsBig); err != nil { - return fmt.Errorf("failed to serialize proof: %w", err) - } - - // Set output wires (convert big.Int to U64) - for i, bigVal := range outsBig { - s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + // Set output wires (copy fr.Element to U64 in Montgomery form) + for i, elem := range proof.Flatten() { + var val constraint.U64 + copy(val[:], elem[:]) + s.SetValue(uint32(i+int(inst.WireOffset)), val) } return nil diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 8fa0b6e6bc..185d6e30ff 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -5,7 +5,6 @@ import ( "{{.FieldPackagePath}}/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "math/big" "strconv" "sync" "github.com/consensys/gnark/internal/gkr/gkrtypes" @@ -707,30 +706,33 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// SerializeToBigInts flattens a proof object into the given slice of big.Ints -// useful in gnark hints. -func (p Proof) SerializeToBigInts(outs []*big.Int) error { +// Flatten returns all proof elements as a flat slice of field elements +func (p Proof) Flatten() []{{ .ElementType }} { + // Compute total size + var size int + for i := range p { + for _, poly := range p[i].partialSumPolys { + size += len(poly) + } + if p[i].finalEvalProof != nil { + size += len(p[i].finalEvalProof) + } + } + + // Flatten into slice + outs := make([]{{ .ElementType }}, size) offset := 0 for i := range p { for _, poly := range p[i].partialSumPolys { - frToBigInts(outs[offset:], poly) + copy(outs[offset:], poly) offset += len(poly) } if p[i].finalEvalProof != nil { - frToBigInts(outs[offset:], p[i].finalEvalProof) + copy(outs[offset:], p[i].finalEvalProof) offset += len(p[i].finalEvalProof) } } - if offset != len(outs) { - return fmt.Errorf("expected %d elements, got %d", offset, len(outs)) - } - return nil -} - -func frToBigInts(dst []*big.Int, src []{{ .ElementType }}) { - for i := range src { - src[i].BigInt(dst[i]) - } + return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 45d4a8668d..733cd6627e 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -7,7 +7,6 @@ package gkr import ( "fmt" - "math/big" "math/bits" "slices" "sync" @@ -266,28 +265,11 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bls12_377 prove failed: %w", err) } - // Serialize proof and convert to U64 - proofSize := 0 - for i := range proof { - for _, poly := range proof[i].partialSumPolys { - proofSize += len(poly) - } - if proof[i].finalEvalProof != nil { - proofSize += len(proof[i].finalEvalProof) - } - } - - outsBig := make([]*big.Int, proofSize) - for i := range outsBig { - outsBig[i] = new(big.Int) - } - if err := proof.SerializeToBigInts(outsBig); err != nil { - return fmt.Errorf("failed to serialize proof: %w", err) - } - - // Set output wires (convert big.Int to U64) - for i, bigVal := range outsBig { - s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + // Set output wires (copy fr.Element to U64 in Montgomery form) + for i, elem := range proof.Flatten() { + var val constraint.U64 + copy(val[:], elem[:]) + s.SetValue(uint32(i+int(inst.WireOffset)), val) } return nil diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 5d8eeca4d3..ef0b792743 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -8,7 +8,6 @@ package gkr import ( "errors" "fmt" - "math/big" "strconv" "sync" @@ -712,30 +711,33 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// SerializeToBigInts flattens a proof object into the given slice of big.Ints -// useful in gnark hints. -func (p Proof) SerializeToBigInts(outs []*big.Int) error { +// Flatten returns all proof elements as a flat slice of field elements +func (p Proof) Flatten() []fr.Element { + // Compute total size + var size int + for i := range p { + for _, poly := range p[i].partialSumPolys { + size += len(poly) + } + if p[i].finalEvalProof != nil { + size += len(p[i].finalEvalProof) + } + } + + // Flatten into slice + outs := make([]fr.Element, size) offset := 0 for i := range p { for _, poly := range p[i].partialSumPolys { - frToBigInts(outs[offset:], poly) + copy(outs[offset:], poly) offset += len(poly) } if p[i].finalEvalProof != nil { - frToBigInts(outs[offset:], p[i].finalEvalProof) + copy(outs[offset:], p[i].finalEvalProof) offset += len(p[i].finalEvalProof) } } - if offset != len(outs) { - return fmt.Errorf("expected %d elements, got %d", offset, len(outs)) - } - return nil -} - -func frToBigInts(dst []*big.Int, src []fr.Element) { - for i := range src { - src[i].BigInt(dst[i]) - } + return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index f407479167..48deb2c154 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -7,7 +7,6 @@ package gkr import ( "fmt" - "math/big" "math/bits" "slices" "sync" @@ -266,28 +265,11 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bls12_381 prove failed: %w", err) } - // Serialize proof and convert to U64 - proofSize := 0 - for i := range proof { - for _, poly := range proof[i].partialSumPolys { - proofSize += len(poly) - } - if proof[i].finalEvalProof != nil { - proofSize += len(proof[i].finalEvalProof) - } - } - - outsBig := make([]*big.Int, proofSize) - for i := range outsBig { - outsBig[i] = new(big.Int) - } - if err := proof.SerializeToBigInts(outsBig); err != nil { - return fmt.Errorf("failed to serialize proof: %w", err) - } - - // Set output wires (convert big.Int to U64) - for i, bigVal := range outsBig { - s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + // Set output wires (copy fr.Element to U64 in Montgomery form) + for i, elem := range proof.Flatten() { + var val constraint.U64 + copy(val[:], elem[:]) + s.SetValue(uint32(i+int(inst.WireOffset)), val) } return nil diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index d7211aa81f..c70f31cac0 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -8,7 +8,6 @@ package gkr import ( "errors" "fmt" - "math/big" "strconv" "sync" @@ -712,30 +711,33 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// SerializeToBigInts flattens a proof object into the given slice of big.Ints -// useful in gnark hints. -func (p Proof) SerializeToBigInts(outs []*big.Int) error { +// Flatten returns all proof elements as a flat slice of field elements +func (p Proof) Flatten() []fr.Element { + // Compute total size + var size int + for i := range p { + for _, poly := range p[i].partialSumPolys { + size += len(poly) + } + if p[i].finalEvalProof != nil { + size += len(p[i].finalEvalProof) + } + } + + // Flatten into slice + outs := make([]fr.Element, size) offset := 0 for i := range p { for _, poly := range p[i].partialSumPolys { - frToBigInts(outs[offset:], poly) + copy(outs[offset:], poly) offset += len(poly) } if p[i].finalEvalProof != nil { - frToBigInts(outs[offset:], p[i].finalEvalProof) + copy(outs[offset:], p[i].finalEvalProof) offset += len(p[i].finalEvalProof) } } - if offset != len(outs) { - return fmt.Errorf("expected %d elements, got %d", offset, len(outs)) - } - return nil -} - -func frToBigInts(dst []*big.Int, src []fr.Element) { - for i := range src { - src[i].BigInt(dst[i]) - } + return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index 5f4d6b10d2..cc16f0025e 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -7,7 +7,6 @@ package gkr import ( "fmt" - "math/big" "math/bits" "slices" "sync" @@ -266,28 +265,11 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bls24_315 prove failed: %w", err) } - // Serialize proof and convert to U64 - proofSize := 0 - for i := range proof { - for _, poly := range proof[i].partialSumPolys { - proofSize += len(poly) - } - if proof[i].finalEvalProof != nil { - proofSize += len(proof[i].finalEvalProof) - } - } - - outsBig := make([]*big.Int, proofSize) - for i := range outsBig { - outsBig[i] = new(big.Int) - } - if err := proof.SerializeToBigInts(outsBig); err != nil { - return fmt.Errorf("failed to serialize proof: %w", err) - } - - // Set output wires (convert big.Int to U64) - for i, bigVal := range outsBig { - s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + // Set output wires (copy fr.Element to U64 in Montgomery form) + for i, elem := range proof.Flatten() { + var val constraint.U64 + copy(val[:], elem[:]) + s.SetValue(uint32(i+int(inst.WireOffset)), val) } return nil diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 6e6f3d1fa0..53e154f3d7 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -8,7 +8,6 @@ package gkr import ( "errors" "fmt" - "math/big" "strconv" "sync" @@ -712,30 +711,33 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// SerializeToBigInts flattens a proof object into the given slice of big.Ints -// useful in gnark hints. -func (p Proof) SerializeToBigInts(outs []*big.Int) error { +// Flatten returns all proof elements as a flat slice of field elements +func (p Proof) Flatten() []fr.Element { + // Compute total size + var size int + for i := range p { + for _, poly := range p[i].partialSumPolys { + size += len(poly) + } + if p[i].finalEvalProof != nil { + size += len(p[i].finalEvalProof) + } + } + + // Flatten into slice + outs := make([]fr.Element, size) offset := 0 for i := range p { for _, poly := range p[i].partialSumPolys { - frToBigInts(outs[offset:], poly) + copy(outs[offset:], poly) offset += len(poly) } if p[i].finalEvalProof != nil { - frToBigInts(outs[offset:], p[i].finalEvalProof) + copy(outs[offset:], p[i].finalEvalProof) offset += len(p[i].finalEvalProof) } } - if offset != len(outs) { - return fmt.Errorf("expected %d elements, got %d", offset, len(outs)) - } - return nil -} - -func frToBigInts(dst []*big.Int, src []fr.Element) { - for i := range src { - src[i].BigInt(dst[i]) - } + return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index ba0815bf0b..9afbbb2822 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -7,7 +7,6 @@ package gkr import ( "fmt" - "math/big" "math/bits" "slices" "sync" @@ -266,28 +265,11 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bls24_317 prove failed: %w", err) } - // Serialize proof and convert to U64 - proofSize := 0 - for i := range proof { - for _, poly := range proof[i].partialSumPolys { - proofSize += len(poly) - } - if proof[i].finalEvalProof != nil { - proofSize += len(proof[i].finalEvalProof) - } - } - - outsBig := make([]*big.Int, proofSize) - for i := range outsBig { - outsBig[i] = new(big.Int) - } - if err := proof.SerializeToBigInts(outsBig); err != nil { - return fmt.Errorf("failed to serialize proof: %w", err) - } - - // Set output wires (convert big.Int to U64) - for i, bigVal := range outsBig { - s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + // Set output wires (copy fr.Element to U64 in Montgomery form) + for i, elem := range proof.Flatten() { + var val constraint.U64 + copy(val[:], elem[:]) + s.SetValue(uint32(i+int(inst.WireOffset)), val) } return nil diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 59520ff69f..188d789eb4 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -8,7 +8,6 @@ package gkr import ( "errors" "fmt" - "math/big" "strconv" "sync" @@ -712,30 +711,33 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// SerializeToBigInts flattens a proof object into the given slice of big.Ints -// useful in gnark hints. -func (p Proof) SerializeToBigInts(outs []*big.Int) error { +// Flatten returns all proof elements as a flat slice of field elements +func (p Proof) Flatten() []fr.Element { + // Compute total size + var size int + for i := range p { + for _, poly := range p[i].partialSumPolys { + size += len(poly) + } + if p[i].finalEvalProof != nil { + size += len(p[i].finalEvalProof) + } + } + + // Flatten into slice + outs := make([]fr.Element, size) offset := 0 for i := range p { for _, poly := range p[i].partialSumPolys { - frToBigInts(outs[offset:], poly) + copy(outs[offset:], poly) offset += len(poly) } if p[i].finalEvalProof != nil { - frToBigInts(outs[offset:], p[i].finalEvalProof) + copy(outs[offset:], p[i].finalEvalProof) offset += len(p[i].finalEvalProof) } } - if offset != len(outs) { - return fmt.Errorf("expected %d elements, got %d", offset, len(outs)) - } - return nil -} - -func frToBigInts(dst []*big.Int, src []fr.Element) { - for i := range src { - src[i].BigInt(dst[i]) - } + return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 4518efe35c..9cb3cb130b 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -7,7 +7,6 @@ package gkr import ( "fmt" - "math/big" "math/bits" "slices" "sync" @@ -266,28 +265,11 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bn254 prove failed: %w", err) } - // Serialize proof and convert to U64 - proofSize := 0 - for i := range proof { - for _, poly := range proof[i].partialSumPolys { - proofSize += len(poly) - } - if proof[i].finalEvalProof != nil { - proofSize += len(proof[i].finalEvalProof) - } - } - - outsBig := make([]*big.Int, proofSize) - for i := range outsBig { - outsBig[i] = new(big.Int) - } - if err := proof.SerializeToBigInts(outsBig); err != nil { - return fmt.Errorf("failed to serialize proof: %w", err) - } - - // Set output wires (convert big.Int to U64) - for i, bigVal := range outsBig { - s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + // Set output wires (copy fr.Element to U64 in Montgomery form) + for i, elem := range proof.Flatten() { + var val constraint.U64 + copy(val[:], elem[:]) + s.SetValue(uint32(i+int(inst.WireOffset)), val) } return nil diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 4ca1a7e71f..fc27b45be9 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -8,7 +8,6 @@ package gkr import ( "errors" "fmt" - "math/big" "strconv" "sync" @@ -712,30 +711,33 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// SerializeToBigInts flattens a proof object into the given slice of big.Ints -// useful in gnark hints. -func (p Proof) SerializeToBigInts(outs []*big.Int) error { +// Flatten returns all proof elements as a flat slice of field elements +func (p Proof) Flatten() []fr.Element { + // Compute total size + var size int + for i := range p { + for _, poly := range p[i].partialSumPolys { + size += len(poly) + } + if p[i].finalEvalProof != nil { + size += len(p[i].finalEvalProof) + } + } + + // Flatten into slice + outs := make([]fr.Element, size) offset := 0 for i := range p { for _, poly := range p[i].partialSumPolys { - frToBigInts(outs[offset:], poly) + copy(outs[offset:], poly) offset += len(poly) } if p[i].finalEvalProof != nil { - frToBigInts(outs[offset:], p[i].finalEvalProof) + copy(outs[offset:], p[i].finalEvalProof) offset += len(p[i].finalEvalProof) } } - if offset != len(outs) { - return fmt.Errorf("expected %d elements, got %d", offset, len(outs)) - } - return nil -} - -func frToBigInts(dst []*big.Int, src []fr.Element) { - for i := range src { - src[i].BigInt(dst[i]) - } + return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 100702ceba..450a5325ad 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -7,7 +7,6 @@ package gkr import ( "fmt" - "math/big" "math/bits" "slices" "sync" @@ -266,28 +265,11 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bw6_633 prove failed: %w", err) } - // Serialize proof and convert to U64 - proofSize := 0 - for i := range proof { - for _, poly := range proof[i].partialSumPolys { - proofSize += len(poly) - } - if proof[i].finalEvalProof != nil { - proofSize += len(proof[i].finalEvalProof) - } - } - - outsBig := make([]*big.Int, proofSize) - for i := range outsBig { - outsBig[i] = new(big.Int) - } - if err := proof.SerializeToBigInts(outsBig); err != nil { - return fmt.Errorf("failed to serialize proof: %w", err) - } - - // Set output wires (convert big.Int to U64) - for i, bigVal := range outsBig { - s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + // Set output wires (copy fr.Element to U64 in Montgomery form) + for i, elem := range proof.Flatten() { + var val constraint.U64 + copy(val[:], elem[:]) + s.SetValue(uint32(i+int(inst.WireOffset)), val) } return nil diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 824ad4385b..08d1a17c71 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -8,7 +8,6 @@ package gkr import ( "errors" "fmt" - "math/big" "strconv" "sync" @@ -712,30 +711,33 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// SerializeToBigInts flattens a proof object into the given slice of big.Ints -// useful in gnark hints. -func (p Proof) SerializeToBigInts(outs []*big.Int) error { +// Flatten returns all proof elements as a flat slice of field elements +func (p Proof) Flatten() []fr.Element { + // Compute total size + var size int + for i := range p { + for _, poly := range p[i].partialSumPolys { + size += len(poly) + } + if p[i].finalEvalProof != nil { + size += len(p[i].finalEvalProof) + } + } + + // Flatten into slice + outs := make([]fr.Element, size) offset := 0 for i := range p { for _, poly := range p[i].partialSumPolys { - frToBigInts(outs[offset:], poly) + copy(outs[offset:], poly) offset += len(poly) } if p[i].finalEvalProof != nil { - frToBigInts(outs[offset:], p[i].finalEvalProof) + copy(outs[offset:], p[i].finalEvalProof) offset += len(p[i].finalEvalProof) } } - if offset != len(outs) { - return fmt.Errorf("expected %d elements, got %d", offset, len(outs)) - } - return nil -} - -func frToBigInts(dst []*big.Int, src []fr.Element) { - for i := range src { - src[i].BigInt(dst[i]) - } + return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 1968695193..3b8957c5b1 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -7,7 +7,6 @@ package gkr import ( "fmt" - "math/big" "math/bits" "slices" "sync" @@ -266,28 +265,11 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bw6_761 prove failed: %w", err) } - // Serialize proof and convert to U64 - proofSize := 0 - for i := range proof { - for _, poly := range proof[i].partialSumPolys { - proofSize += len(poly) - } - if proof[i].finalEvalProof != nil { - proofSize += len(proof[i].finalEvalProof) - } - } - - outsBig := make([]*big.Int, proofSize) - for i := range outsBig { - outsBig[i] = new(big.Int) - } - if err := proof.SerializeToBigInts(outsBig); err != nil { - return fmt.Errorf("failed to serialize proof: %w", err) - } - - // Set output wires (convert big.Int to U64) - for i, bigVal := range outsBig { - s.SetValue(uint32(i+int(inst.WireOffset)), s.FromInterface(bigVal)) + // Set output wires (copy fr.Element to U64 in Montgomery form) + for i, elem := range proof.Flatten() { + var val constraint.U64 + copy(val[:], elem[:]) + s.SetValue(uint32(i+int(inst.WireOffset)), val) } return nil diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 8c6d7fd4bf..5c2450def6 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -8,7 +8,6 @@ package gkr import ( "errors" "fmt" - "math/big" "strconv" "sync" @@ -712,30 +711,33 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// SerializeToBigInts flattens a proof object into the given slice of big.Ints -// useful in gnark hints. -func (p Proof) SerializeToBigInts(outs []*big.Int) error { +// Flatten returns all proof elements as a flat slice of field elements +func (p Proof) Flatten() []fr.Element { + // Compute total size + var size int + for i := range p { + for _, poly := range p[i].partialSumPolys { + size += len(poly) + } + if p[i].finalEvalProof != nil { + size += len(p[i].finalEvalProof) + } + } + + // Flatten into slice + outs := make([]fr.Element, size) offset := 0 for i := range p { for _, poly := range p[i].partialSumPolys { - frToBigInts(outs[offset:], poly) + copy(outs[offset:], poly) offset += len(poly) } if p[i].finalEvalProof != nil { - frToBigInts(outs[offset:], p[i].finalEvalProof) + copy(outs[offset:], p[i].finalEvalProof) offset += len(p[i].finalEvalProof) } } - if offset != len(outs) { - return fmt.Errorf("expected %d elements, got %d", offset, len(outs)) - } - return nil -} - -func frToBigInts(dst []*big.Int, src []fr.Element) { - for i := range src { - src[i].BigInt(dst[i]) - } + return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index e26c628b93..245b748ccf 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -8,7 +8,6 @@ package gkr import ( "errors" "fmt" - "math/big" "strconv" "sync" @@ -712,30 +711,33 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// SerializeToBigInts flattens a proof object into the given slice of big.Ints -// useful in gnark hints. -func (p Proof) SerializeToBigInts(outs []*big.Int) error { +// Flatten returns all proof elements as a flat slice of field elements +func (p Proof) Flatten() []small_rational.SmallRational { + // Compute total size + var size int + for i := range p { + for _, poly := range p[i].partialSumPolys { + size += len(poly) + } + if p[i].finalEvalProof != nil { + size += len(p[i].finalEvalProof) + } + } + + // Flatten into slice + outs := make([]small_rational.SmallRational, size) offset := 0 for i := range p { for _, poly := range p[i].partialSumPolys { - frToBigInts(outs[offset:], poly) + copy(outs[offset:], poly) offset += len(poly) } if p[i].finalEvalProof != nil { - frToBigInts(outs[offset:], p[i].finalEvalProof) + copy(outs[offset:], p[i].finalEvalProof) offset += len(p[i].finalEvalProof) } } - if offset != len(outs) { - return fmt.Errorf("expected %d elements, got %d", offset, len(outs)) - } - return nil -} - -func frToBigInts(dst []*big.Int, src []small_rational.SmallRational) { - for i := range src { - src[i].BigInt(dst[i]) - } + return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. From 3f3dcf0ba1b03d2cfe6ec134d40bf7664f3d3167 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 9 Feb 2026 07:52:35 -0600 Subject: [PATCH 150/251] refactor: proof iterator --- .../backend/template/gkr/blueprint.go.tmpl | 7 ++-- .../backend/template/gkr/gkr.go.tmpl | 37 ++++++++----------- internal/gkr/bls12-377/blueprint.go | 5 +-- internal/gkr/bls12-377/gkr.go | 37 ++++++++----------- internal/gkr/bls12-381/blueprint.go | 5 +-- internal/gkr/bls12-381/gkr.go | 37 ++++++++----------- internal/gkr/bls24-315/blueprint.go | 5 +-- internal/gkr/bls24-315/gkr.go | 37 ++++++++----------- internal/gkr/bls24-317/blueprint.go | 5 +-- internal/gkr/bls24-317/gkr.go | 37 ++++++++----------- internal/gkr/bn254/blueprint.go | 5 +-- internal/gkr/bn254/gkr.go | 37 ++++++++----------- internal/gkr/bw6-633/blueprint.go | 5 +-- internal/gkr/bw6-633/gkr.go | 37 ++++++++----------- internal/gkr/bw6-761/blueprint.go | 5 +-- internal/gkr/bw6-761/gkr.go | 37 ++++++++----------- internal/gkr/small_rational/gkr.go | 37 ++++++++----------- 17 files changed, 152 insertions(+), 223 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index a503fb010d..c46785adff 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -258,12 +258,11 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("{{toLower .FieldID}} prove failed: %w", err) } - // Set output wires (copy fr.Element to U64 in Montgomery form) - for i, elem := range proof.Flatten() { + for i, elem := range proof.iterator() { var val constraint.U64 - copy(val[:], elem[:]) + copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) - } + } return nil } diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 185d6e30ff..0d1b5affb5 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -1,6 +1,7 @@ import ( "errors" "fmt" + "iter" "{{.FieldPackagePath}}" "{{.FieldPackagePath}}/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" @@ -706,33 +707,25 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// Flatten returns all proof elements as a flat slice of field elements -func (p Proof) Flatten() []{{ .ElementType }} { - // Compute total size - var size int - for i := range p { - for _, poly := range p[i].partialSumPolys { - size += len(poly) - } - if p[i].finalEvalProof != nil { - size += len(p[i].finalEvalProof) +func iterateElems(elems []{{ .ElementType }}, counter *int, yield func(int, *{{ .ElementType }}) bool) { + for i := range elems { + if !yield(*counter, &elems[i]) { + return } + *counter++ } +} - // Flatten into slice - outs := make([]{{ .ElementType }}, size) - offset := 0 - for i := range p { - for _, poly := range p[i].partialSumPolys { - copy(outs[offset:], poly) - offset += len(poly) - } - if p[i].finalEvalProof != nil { - copy(outs[offset:], p[i].finalEvalProof) - offset += len(p[i].finalEvalProof) +func (p Proof) iterator() iter.Seq2[int, *{{ .ElementType }}] { + return func(yield func(int, *{{ .ElementType }}) bool) { + var counter int + for i := range p { + for _, poly := range p[i].partialSumPolys { + iterateElems(poly, &counter, yield) + } + iterateElems(p[i].finalEvalProof, &counter, yield) } } - return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 733cd6627e..ba562721e0 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -265,10 +265,9 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bls12_377 prove failed: %w", err) } - // Set output wires (copy fr.Element to U64 in Montgomery form) - for i, elem := range proof.Flatten() { + for i, elem := range proof.iterator() { var val constraint.U64 - copy(val[:], elem[:]) + copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) } diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index ef0b792743..8a84fba766 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -8,6 +8,7 @@ package gkr import ( "errors" "fmt" + "iter" "strconv" "sync" @@ -711,33 +712,25 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// Flatten returns all proof elements as a flat slice of field elements -func (p Proof) Flatten() []fr.Element { - // Compute total size - var size int - for i := range p { - for _, poly := range p[i].partialSumPolys { - size += len(poly) - } - if p[i].finalEvalProof != nil { - size += len(p[i].finalEvalProof) +func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) { + for i := range elems { + if !yield(*counter, &elems[i]) { + return } + *counter++ } +} - // Flatten into slice - outs := make([]fr.Element, size) - offset := 0 - for i := range p { - for _, poly := range p[i].partialSumPolys { - copy(outs[offset:], poly) - offset += len(poly) - } - if p[i].finalEvalProof != nil { - copy(outs[offset:], p[i].finalEvalProof) - offset += len(p[i].finalEvalProof) +func (p Proof) iterator() iter.Seq2[int, *fr.Element] { + return func(yield func(int, *fr.Element) bool) { + var counter int + for i := range p { + for _, poly := range p[i].partialSumPolys { + iterateElems(poly, &counter, yield) + } + iterateElems(p[i].finalEvalProof, &counter, yield) } } - return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 48deb2c154..94739026ca 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -265,10 +265,9 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bls12_381 prove failed: %w", err) } - // Set output wires (copy fr.Element to U64 in Montgomery form) - for i, elem := range proof.Flatten() { + for i, elem := range proof.iterator() { var val constraint.U64 - copy(val[:], elem[:]) + copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) } diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index c70f31cac0..4b50921306 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -8,6 +8,7 @@ package gkr import ( "errors" "fmt" + "iter" "strconv" "sync" @@ -711,33 +712,25 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// Flatten returns all proof elements as a flat slice of field elements -func (p Proof) Flatten() []fr.Element { - // Compute total size - var size int - for i := range p { - for _, poly := range p[i].partialSumPolys { - size += len(poly) - } - if p[i].finalEvalProof != nil { - size += len(p[i].finalEvalProof) +func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) { + for i := range elems { + if !yield(*counter, &elems[i]) { + return } + *counter++ } +} - // Flatten into slice - outs := make([]fr.Element, size) - offset := 0 - for i := range p { - for _, poly := range p[i].partialSumPolys { - copy(outs[offset:], poly) - offset += len(poly) - } - if p[i].finalEvalProof != nil { - copy(outs[offset:], p[i].finalEvalProof) - offset += len(p[i].finalEvalProof) +func (p Proof) iterator() iter.Seq2[int, *fr.Element] { + return func(yield func(int, *fr.Element) bool) { + var counter int + for i := range p { + for _, poly := range p[i].partialSumPolys { + iterateElems(poly, &counter, yield) + } + iterateElems(p[i].finalEvalProof, &counter, yield) } } - return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index cc16f0025e..b36708ccb4 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -265,10 +265,9 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bls24_315 prove failed: %w", err) } - // Set output wires (copy fr.Element to U64 in Montgomery form) - for i, elem := range proof.Flatten() { + for i, elem := range proof.iterator() { var val constraint.U64 - copy(val[:], elem[:]) + copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) } diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 53e154f3d7..96acced3e2 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -8,6 +8,7 @@ package gkr import ( "errors" "fmt" + "iter" "strconv" "sync" @@ -711,33 +712,25 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// Flatten returns all proof elements as a flat slice of field elements -func (p Proof) Flatten() []fr.Element { - // Compute total size - var size int - for i := range p { - for _, poly := range p[i].partialSumPolys { - size += len(poly) - } - if p[i].finalEvalProof != nil { - size += len(p[i].finalEvalProof) +func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) { + for i := range elems { + if !yield(*counter, &elems[i]) { + return } + *counter++ } +} - // Flatten into slice - outs := make([]fr.Element, size) - offset := 0 - for i := range p { - for _, poly := range p[i].partialSumPolys { - copy(outs[offset:], poly) - offset += len(poly) - } - if p[i].finalEvalProof != nil { - copy(outs[offset:], p[i].finalEvalProof) - offset += len(p[i].finalEvalProof) +func (p Proof) iterator() iter.Seq2[int, *fr.Element] { + return func(yield func(int, *fr.Element) bool) { + var counter int + for i := range p { + for _, poly := range p[i].partialSumPolys { + iterateElems(poly, &counter, yield) + } + iterateElems(p[i].finalEvalProof, &counter, yield) } } - return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 9afbbb2822..17ecc89020 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -265,10 +265,9 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bls24_317 prove failed: %w", err) } - // Set output wires (copy fr.Element to U64 in Montgomery form) - for i, elem := range proof.Flatten() { + for i, elem := range proof.iterator() { var val constraint.U64 - copy(val[:], elem[:]) + copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) } diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index 188d789eb4..b3b2ae0acf 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -8,6 +8,7 @@ package gkr import ( "errors" "fmt" + "iter" "strconv" "sync" @@ -711,33 +712,25 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// Flatten returns all proof elements as a flat slice of field elements -func (p Proof) Flatten() []fr.Element { - // Compute total size - var size int - for i := range p { - for _, poly := range p[i].partialSumPolys { - size += len(poly) - } - if p[i].finalEvalProof != nil { - size += len(p[i].finalEvalProof) +func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) { + for i := range elems { + if !yield(*counter, &elems[i]) { + return } + *counter++ } +} - // Flatten into slice - outs := make([]fr.Element, size) - offset := 0 - for i := range p { - for _, poly := range p[i].partialSumPolys { - copy(outs[offset:], poly) - offset += len(poly) - } - if p[i].finalEvalProof != nil { - copy(outs[offset:], p[i].finalEvalProof) - offset += len(p[i].finalEvalProof) +func (p Proof) iterator() iter.Seq2[int, *fr.Element] { + return func(yield func(int, *fr.Element) bool) { + var counter int + for i := range p { + for _, poly := range p[i].partialSumPolys { + iterateElems(poly, &counter, yield) + } + iterateElems(p[i].finalEvalProof, &counter, yield) } } - return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 9cb3cb130b..a87b8cd2fa 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -265,10 +265,9 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bn254 prove failed: %w", err) } - // Set output wires (copy fr.Element to U64 in Montgomery form) - for i, elem := range proof.Flatten() { + for i, elem := range proof.iterator() { var val constraint.U64 - copy(val[:], elem[:]) + copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) } diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index fc27b45be9..dd7d3f7346 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -8,6 +8,7 @@ package gkr import ( "errors" "fmt" + "iter" "strconv" "sync" @@ -711,33 +712,25 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// Flatten returns all proof elements as a flat slice of field elements -func (p Proof) Flatten() []fr.Element { - // Compute total size - var size int - for i := range p { - for _, poly := range p[i].partialSumPolys { - size += len(poly) - } - if p[i].finalEvalProof != nil { - size += len(p[i].finalEvalProof) +func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) { + for i := range elems { + if !yield(*counter, &elems[i]) { + return } + *counter++ } +} - // Flatten into slice - outs := make([]fr.Element, size) - offset := 0 - for i := range p { - for _, poly := range p[i].partialSumPolys { - copy(outs[offset:], poly) - offset += len(poly) - } - if p[i].finalEvalProof != nil { - copy(outs[offset:], p[i].finalEvalProof) - offset += len(p[i].finalEvalProof) +func (p Proof) iterator() iter.Seq2[int, *fr.Element] { + return func(yield func(int, *fr.Element) bool) { + var counter int + for i := range p { + for _, poly := range p[i].partialSumPolys { + iterateElems(poly, &counter, yield) + } + iterateElems(p[i].finalEvalProof, &counter, yield) } } - return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 450a5325ad..35b1610e7c 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -265,10 +265,9 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bw6_633 prove failed: %w", err) } - // Set output wires (copy fr.Element to U64 in Montgomery form) - for i, elem := range proof.Flatten() { + for i, elem := range proof.iterator() { var val constraint.U64 - copy(val[:], elem[:]) + copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) } diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index 08d1a17c71..ed93f63986 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -8,6 +8,7 @@ package gkr import ( "errors" "fmt" + "iter" "strconv" "sync" @@ -711,33 +712,25 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// Flatten returns all proof elements as a flat slice of field elements -func (p Proof) Flatten() []fr.Element { - // Compute total size - var size int - for i := range p { - for _, poly := range p[i].partialSumPolys { - size += len(poly) - } - if p[i].finalEvalProof != nil { - size += len(p[i].finalEvalProof) +func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) { + for i := range elems { + if !yield(*counter, &elems[i]) { + return } + *counter++ } +} - // Flatten into slice - outs := make([]fr.Element, size) - offset := 0 - for i := range p { - for _, poly := range p[i].partialSumPolys { - copy(outs[offset:], poly) - offset += len(poly) - } - if p[i].finalEvalProof != nil { - copy(outs[offset:], p[i].finalEvalProof) - offset += len(p[i].finalEvalProof) +func (p Proof) iterator() iter.Seq2[int, *fr.Element] { + return func(yield func(int, *fr.Element) bool) { + var counter int + for i := range p { + for _, poly := range p[i].partialSumPolys { + iterateElems(poly, &counter, yield) + } + iterateElems(p[i].finalEvalProof, &counter, yield) } } - return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 3b8957c5b1..8b6e0b3521 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -265,10 +265,9 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bw6_761 prove failed: %w", err) } - // Set output wires (copy fr.Element to U64 in Montgomery form) - for i, elem := range proof.Flatten() { + for i, elem := range proof.iterator() { var val constraint.U64 - copy(val[:], elem[:]) + copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) } diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 5c2450def6..9ce39588c8 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -8,6 +8,7 @@ package gkr import ( "errors" "fmt" + "iter" "strconv" "sync" @@ -711,33 +712,25 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// Flatten returns all proof elements as a flat slice of field elements -func (p Proof) Flatten() []fr.Element { - // Compute total size - var size int - for i := range p { - for _, poly := range p[i].partialSumPolys { - size += len(poly) - } - if p[i].finalEvalProof != nil { - size += len(p[i].finalEvalProof) +func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) { + for i := range elems { + if !yield(*counter, &elems[i]) { + return } + *counter++ } +} - // Flatten into slice - outs := make([]fr.Element, size) - offset := 0 - for i := range p { - for _, poly := range p[i].partialSumPolys { - copy(outs[offset:], poly) - offset += len(poly) - } - if p[i].finalEvalProof != nil { - copy(outs[offset:], p[i].finalEvalProof) - offset += len(p[i].finalEvalProof) +func (p Proof) iterator() iter.Seq2[int, *fr.Element] { + return func(yield func(int, *fr.Element) bool) { + var counter int + for i := range p { + for _, poly := range p[i].partialSumPolys { + iterateElems(poly, &counter, yield) + } + iterateElems(p[i].finalEvalProof, &counter, yield) } } - return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 245b748ccf..4c336459b7 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -8,6 +8,7 @@ package gkr import ( "errors" "fmt" + "iter" "strconv" "sync" @@ -711,33 +712,25 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -// Flatten returns all proof elements as a flat slice of field elements -func (p Proof) Flatten() []small_rational.SmallRational { - // Compute total size - var size int - for i := range p { - for _, poly := range p[i].partialSumPolys { - size += len(poly) - } - if p[i].finalEvalProof != nil { - size += len(p[i].finalEvalProof) +func iterateElems(elems []small_rational.SmallRational, counter *int, yield func(int, *small_rational.SmallRational) bool) { + for i := range elems { + if !yield(*counter, &elems[i]) { + return } + *counter++ } +} - // Flatten into slice - outs := make([]small_rational.SmallRational, size) - offset := 0 - for i := range p { - for _, poly := range p[i].partialSumPolys { - copy(outs[offset:], poly) - offset += len(poly) - } - if p[i].finalEvalProof != nil { - copy(outs[offset:], p[i].finalEvalProof) - offset += len(p[i].finalEvalProof) +func (p Proof) iterator() iter.Seq2[int, *small_rational.SmallRational] { + return func(yield func(int, *small_rational.SmallRational) bool) { + var counter int + for i := range p { + for _, poly := range p[i].partialSumPolys { + iterateElems(poly, &counter, yield) + } + iterateElems(p[i].finalEvalProof, &counter, yield) } } - return outs } // gateEvaluator provides a high-level API for evaluating compiled gates efficiently. From 761a6eee62229fae5c5035b702a3d5820df6002a Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 9 Feb 2026 07:59:52 -0600 Subject: [PATCH 151/251] fix: early termination bug --- .../backend/template/gkr/blueprint.go.tmpl | 2 +- .../generator/backend/template/gkr/gkr.go.tmpl | 15 ++++++++++----- internal/gkr/bls12-377/blueprint.go | 2 +- internal/gkr/bls12-377/gkr.go | 15 ++++++++++----- internal/gkr/bls12-381/blueprint.go | 2 +- internal/gkr/bls12-381/gkr.go | 15 ++++++++++----- internal/gkr/bls24-315/blueprint.go | 2 +- internal/gkr/bls24-315/gkr.go | 15 ++++++++++----- internal/gkr/bls24-317/blueprint.go | 2 +- internal/gkr/bls24-317/gkr.go | 15 ++++++++++----- internal/gkr/bn254/blueprint.go | 2 +- internal/gkr/bn254/gkr.go | 15 ++++++++++----- internal/gkr/bw6-633/blueprint.go | 2 +- internal/gkr/bw6-633/gkr.go | 15 ++++++++++----- internal/gkr/bw6-761/blueprint.go | 2 +- internal/gkr/bw6-761/gkr.go | 15 ++++++++++----- internal/gkr/small_rational/gkr.go | 15 ++++++++++----- 17 files changed, 98 insertions(+), 53 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index c46785adff..7075919385 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -258,7 +258,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("{{toLower .FieldID}} prove failed: %w", err) } - for i, elem := range proof.iterator() { + for i, elem := range proof.flatten() { var val constraint.U64 copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 0d1b5affb5..128568717f 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -707,23 +707,28 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -func iterateElems(elems []{{ .ElementType }}, counter *int, yield func(int, *{{ .ElementType }}) bool) { +func iterateElems(elems []{{ .ElementType }}, counter *int, yield func(int, *{{ .ElementType }}) bool) bool { for i := range elems { if !yield(*counter, &elems[i]) { - return + return false } *counter++ } + return true } -func (p Proof) iterator() iter.Seq2[int, *{{ .ElementType }}] { +func (p Proof) flatten() iter.Seq2[int, *{{ .ElementType }}] { return func(yield func(int, *{{ .ElementType }}) bool) { var counter int for i := range p { for _, poly := range p[i].partialSumPolys { - iterateElems(poly, &counter, yield) + if !iterateElems(poly, &counter, yield) { + return + } + } + if !iterateElems(p[i].finalEvalProof, &counter, yield) { + return } - iterateElems(p[i].finalEvalProof, &counter, yield) } } } diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index ba562721e0..a7706f1bc9 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -265,7 +265,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bls12_377 prove failed: %w", err) } - for i, elem := range proof.iterator() { + for i, elem := range proof.flatten() { var val constraint.U64 copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 8a84fba766..eea6b8acf9 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -712,23 +712,28 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) { +func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) bool { for i := range elems { if !yield(*counter, &elems[i]) { - return + return false } *counter++ } + return true } -func (p Proof) iterator() iter.Seq2[int, *fr.Element] { +func (p Proof) flatten() iter.Seq2[int, *fr.Element] { return func(yield func(int, *fr.Element) bool) { var counter int for i := range p { for _, poly := range p[i].partialSumPolys { - iterateElems(poly, &counter, yield) + if !iterateElems(poly, &counter, yield) { + return + } + } + if !iterateElems(p[i].finalEvalProof, &counter, yield) { + return } - iterateElems(p[i].finalEvalProof, &counter, yield) } } } diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 94739026ca..b54f1c4822 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -265,7 +265,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bls12_381 prove failed: %w", err) } - for i, elem := range proof.iterator() { + for i, elem := range proof.flatten() { var val constraint.U64 copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 4b50921306..69ba8eb924 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -712,23 +712,28 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) { +func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) bool { for i := range elems { if !yield(*counter, &elems[i]) { - return + return false } *counter++ } + return true } -func (p Proof) iterator() iter.Seq2[int, *fr.Element] { +func (p Proof) flatten() iter.Seq2[int, *fr.Element] { return func(yield func(int, *fr.Element) bool) { var counter int for i := range p { for _, poly := range p[i].partialSumPolys { - iterateElems(poly, &counter, yield) + if !iterateElems(poly, &counter, yield) { + return + } + } + if !iterateElems(p[i].finalEvalProof, &counter, yield) { + return } - iterateElems(p[i].finalEvalProof, &counter, yield) } } } diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index b36708ccb4..1e11d58d1c 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -265,7 +265,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bls24_315 prove failed: %w", err) } - for i, elem := range proof.iterator() { + for i, elem := range proof.flatten() { var val constraint.U64 copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) diff --git a/internal/gkr/bls24-315/gkr.go b/internal/gkr/bls24-315/gkr.go index 96acced3e2..d64a1f4b83 100644 --- a/internal/gkr/bls24-315/gkr.go +++ b/internal/gkr/bls24-315/gkr.go @@ -712,23 +712,28 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) { +func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) bool { for i := range elems { if !yield(*counter, &elems[i]) { - return + return false } *counter++ } + return true } -func (p Proof) iterator() iter.Seq2[int, *fr.Element] { +func (p Proof) flatten() iter.Seq2[int, *fr.Element] { return func(yield func(int, *fr.Element) bool) { var counter int for i := range p { for _, poly := range p[i].partialSumPolys { - iterateElems(poly, &counter, yield) + if !iterateElems(poly, &counter, yield) { + return + } + } + if !iterateElems(p[i].finalEvalProof, &counter, yield) { + return } - iterateElems(p[i].finalEvalProof, &counter, yield) } } } diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 17ecc89020..8132438972 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -265,7 +265,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bls24_317 prove failed: %w", err) } - for i, elem := range proof.iterator() { + for i, elem := range proof.flatten() { var val constraint.U64 copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) diff --git a/internal/gkr/bls24-317/gkr.go b/internal/gkr/bls24-317/gkr.go index b3b2ae0acf..f38ed59ec0 100644 --- a/internal/gkr/bls24-317/gkr.go +++ b/internal/gkr/bls24-317/gkr.go @@ -712,23 +712,28 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) { +func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) bool { for i := range elems { if !yield(*counter, &elems[i]) { - return + return false } *counter++ } + return true } -func (p Proof) iterator() iter.Seq2[int, *fr.Element] { +func (p Proof) flatten() iter.Seq2[int, *fr.Element] { return func(yield func(int, *fr.Element) bool) { var counter int for i := range p { for _, poly := range p[i].partialSumPolys { - iterateElems(poly, &counter, yield) + if !iterateElems(poly, &counter, yield) { + return + } + } + if !iterateElems(p[i].finalEvalProof, &counter, yield) { + return } - iterateElems(p[i].finalEvalProof, &counter, yield) } } } diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index a87b8cd2fa..260581d19b 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -265,7 +265,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bn254 prove failed: %w", err) } - for i, elem := range proof.iterator() { + for i, elem := range proof.flatten() { var val constraint.U64 copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index dd7d3f7346..e9b70130f9 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -712,23 +712,28 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) { +func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) bool { for i := range elems { if !yield(*counter, &elems[i]) { - return + return false } *counter++ } + return true } -func (p Proof) iterator() iter.Seq2[int, *fr.Element] { +func (p Proof) flatten() iter.Seq2[int, *fr.Element] { return func(yield func(int, *fr.Element) bool) { var counter int for i := range p { for _, poly := range p[i].partialSumPolys { - iterateElems(poly, &counter, yield) + if !iterateElems(poly, &counter, yield) { + return + } + } + if !iterateElems(p[i].finalEvalProof, &counter, yield) { + return } - iterateElems(p[i].finalEvalProof, &counter, yield) } } } diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 35b1610e7c..29e0a4d17e 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -265,7 +265,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bw6_633 prove failed: %w", err) } - for i, elem := range proof.iterator() { + for i, elem := range proof.flatten() { var val constraint.U64 copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) diff --git a/internal/gkr/bw6-633/gkr.go b/internal/gkr/bw6-633/gkr.go index ed93f63986..7720978013 100644 --- a/internal/gkr/bw6-633/gkr.go +++ b/internal/gkr/bw6-633/gkr.go @@ -712,23 +712,28 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) { +func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) bool { for i := range elems { if !yield(*counter, &elems[i]) { - return + return false } *counter++ } + return true } -func (p Proof) iterator() iter.Seq2[int, *fr.Element] { +func (p Proof) flatten() iter.Seq2[int, *fr.Element] { return func(yield func(int, *fr.Element) bool) { var counter int for i := range p { for _, poly := range p[i].partialSumPolys { - iterateElems(poly, &counter, yield) + if !iterateElems(poly, &counter, yield) { + return + } + } + if !iterateElems(p[i].finalEvalProof, &counter, yield) { + return } - iterateElems(p[i].finalEvalProof, &counter, yield) } } } diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 8b6e0b3521..4666b8cd6a 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -265,7 +265,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("bw6_761 prove failed: %w", err) } - for i, elem := range proof.iterator() { + for i, elem := range proof.flatten() { var val constraint.U64 copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 9ce39588c8..c03a2c667d 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -712,23 +712,28 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) { +func iterateElems(elems []fr.Element, counter *int, yield func(int, *fr.Element) bool) bool { for i := range elems { if !yield(*counter, &elems[i]) { - return + return false } *counter++ } + return true } -func (p Proof) iterator() iter.Seq2[int, *fr.Element] { +func (p Proof) flatten() iter.Seq2[int, *fr.Element] { return func(yield func(int, *fr.Element) bool) { var counter int for i := range p { for _, poly := range p[i].partialSumPolys { - iterateElems(poly, &counter, yield) + if !iterateElems(poly, &counter, yield) { + return + } + } + if !iterateElems(p[i].finalEvalProof, &counter, yield) { + return } - iterateElems(p[i].finalEvalProof, &counter, yield) } } } diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 4c336459b7..1b87fa3b56 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -712,23 +712,28 @@ func (a WireAssignment) NumVars() int { panic("empty assignment") } -func iterateElems(elems []small_rational.SmallRational, counter *int, yield func(int, *small_rational.SmallRational) bool) { +func iterateElems(elems []small_rational.SmallRational, counter *int, yield func(int, *small_rational.SmallRational) bool) bool { for i := range elems { if !yield(*counter, &elems[i]) { - return + return false } *counter++ } + return true } -func (p Proof) iterator() iter.Seq2[int, *small_rational.SmallRational] { +func (p Proof) flatten() iter.Seq2[int, *small_rational.SmallRational] { return func(yield func(int, *small_rational.SmallRational) bool) { var counter int for i := range p { for _, poly := range p[i].partialSumPolys { - iterateElems(poly, &counter, yield) + if !iterateElems(poly, &counter, yield) { + return + } + } + if !iterateElems(p[i].finalEvalProof, &counter, yield) { + return } - iterateElems(p[i].finalEvalProof, &counter, yield) } } } From 43756239bbe166fb93f30b5ea7bc0403abff591a Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 9 Feb 2026 09:39:58 -0600 Subject: [PATCH 152/251] refactor: use Reset to initialize --- internal/gkr/bn254/blueprint.go | 57 +++++++++++---------------------- 1 file changed, 19 insertions(+), 38 deletions(-) diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 260581d19b..c85da7cee1 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -44,46 +44,9 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -func (b *BlueprintSolve) initialize() { - - if b.assignments != nil { - return - } - b.lock.Lock() - defer b.lock.Unlock() - - if b.assignments != nil { - return // the unlikely event that two or more instructions were competing to initialize the blueprint - } - - // Compute metadata from Circuit - - b.evaluatorPool.New = func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), - } - for wI := range b.Circuit { - w := &b.Circuit[wI] - if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) - } - } - return ce - } - - assignments := make(WireAssignment, len(b.Circuit)) - nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) - for i := range assignments { - assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) - } - b.assignments = assignments -} - // Solve implements the BlueprintStateful interface. func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - b.initialize() - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) @@ -138,7 +101,25 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Reset implements BlueprintStateful func (b *BlueprintSolve) Reset() { - b.assignments = nil + b.evaluatorPool.New = func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(b.Circuit)), + } + for wI := range b.Circuit { + w := &b.Circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) + } + } + return ce + } + + assignments := make(WireAssignment, len(b.Circuit)) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) + for i := range assignments { + assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) + } + b.assignments = assignments } // SetNbInstances sets the number of instances for the blueprint From 5fc5a4802cc3b9eb4e1bde089b393c6d5188b72a Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 9 Feb 2026 09:41:50 -0600 Subject: [PATCH 153/251] build: generify --- .../backend/template/gkr/blueprint.go.tmpl | 24 ++-------- internal/gkr/bls12-377/blueprint.go | 24 ++-------- internal/gkr/bls12-381/blueprint.go | 24 ++-------- internal/gkr/bls24-315/blueprint.go | 24 ++-------- internal/gkr/bls24-317/blueprint.go | 24 ++-------- internal/gkr/bn254/blueprint.go | 47 ++++++++++--------- internal/gkr/bw6-633/blueprint.go | 24 ++-------- internal/gkr/bw6-761/blueprint.go | 24 ++-------- 8 files changed, 45 insertions(+), 170 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 7075919385..a30868ac3f 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -37,20 +37,9 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -func (b *BlueprintSolve) initialize() { - - if b.assignments != nil { - return - } - b.lock.Lock() - defer b.lock.Unlock() - - if b.assignments != nil { - return // the unlikely event that two or more instructions were competing to initialize the blueprint - } - - // Compute metadata from Circuit - +// Reset implements BlueprintStateful. +// It is used to initialize the blueprint for the current circuit. +func (b *BlueprintSolve) Reset() { b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ evaluators: make([]gateEvaluator, len(b.Circuit)), @@ -75,8 +64,6 @@ func (b *BlueprintSolve) initialize() { // Solve implements the BlueprintStateful interface. func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - b.initialize() - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) @@ -129,11 +116,6 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra return nil } -// Reset implements BlueprintStateful -func (b *BlueprintSolve) Reset() { - b.assignments = nil -} - // SetNbInstances sets the number of instances for the blueprint func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index a7706f1bc9..21553f920e 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -44,20 +44,9 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -func (b *BlueprintSolve) initialize() { - - if b.assignments != nil { - return - } - b.lock.Lock() - defer b.lock.Unlock() - - if b.assignments != nil { - return // the unlikely event that two or more instructions were competing to initialize the blueprint - } - - // Compute metadata from Circuit - +// Reset implements BlueprintStateful. +// It is used to initialize the blueprint for the current circuit. +func (b *BlueprintSolve) Reset() { b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ evaluators: make([]gateEvaluator, len(b.Circuit)), @@ -82,8 +71,6 @@ func (b *BlueprintSolve) initialize() { // Solve implements the BlueprintStateful interface. func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - b.initialize() - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) @@ -136,11 +123,6 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra return nil } -// Reset implements BlueprintStateful -func (b *BlueprintSolve) Reset() { - b.assignments = nil -} - // SetNbInstances sets the number of instances for the blueprint func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index b54f1c4822..7c461bf9d7 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -44,20 +44,9 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -func (b *BlueprintSolve) initialize() { - - if b.assignments != nil { - return - } - b.lock.Lock() - defer b.lock.Unlock() - - if b.assignments != nil { - return // the unlikely event that two or more instructions were competing to initialize the blueprint - } - - // Compute metadata from Circuit - +// Reset implements BlueprintStateful. +// It is used to initialize the blueprint for the current circuit. +func (b *BlueprintSolve) Reset() { b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ evaluators: make([]gateEvaluator, len(b.Circuit)), @@ -82,8 +71,6 @@ func (b *BlueprintSolve) initialize() { // Solve implements the BlueprintStateful interface. func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - b.initialize() - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) @@ -136,11 +123,6 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra return nil } -// Reset implements BlueprintStateful -func (b *BlueprintSolve) Reset() { - b.assignments = nil -} - // SetNbInstances sets the number of instances for the blueprint func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go index 1e11d58d1c..a3ca0bb13f 100644 --- a/internal/gkr/bls24-315/blueprint.go +++ b/internal/gkr/bls24-315/blueprint.go @@ -44,20 +44,9 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -func (b *BlueprintSolve) initialize() { - - if b.assignments != nil { - return - } - b.lock.Lock() - defer b.lock.Unlock() - - if b.assignments != nil { - return // the unlikely event that two or more instructions were competing to initialize the blueprint - } - - // Compute metadata from Circuit - +// Reset implements BlueprintStateful. +// It is used to initialize the blueprint for the current circuit. +func (b *BlueprintSolve) Reset() { b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ evaluators: make([]gateEvaluator, len(b.Circuit)), @@ -82,8 +71,6 @@ func (b *BlueprintSolve) initialize() { // Solve implements the BlueprintStateful interface. func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - b.initialize() - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) @@ -136,11 +123,6 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra return nil } -// Reset implements BlueprintStateful -func (b *BlueprintSolve) Reset() { - b.assignments = nil -} - // SetNbInstances sets the number of instances for the blueprint func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go index 8132438972..8d97250b1f 100644 --- a/internal/gkr/bls24-317/blueprint.go +++ b/internal/gkr/bls24-317/blueprint.go @@ -44,20 +44,9 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -func (b *BlueprintSolve) initialize() { - - if b.assignments != nil { - return - } - b.lock.Lock() - defer b.lock.Unlock() - - if b.assignments != nil { - return // the unlikely event that two or more instructions were competing to initialize the blueprint - } - - // Compute metadata from Circuit - +// Reset implements BlueprintStateful. +// It is used to initialize the blueprint for the current circuit. +func (b *BlueprintSolve) Reset() { b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ evaluators: make([]gateEvaluator, len(b.Circuit)), @@ -82,8 +71,6 @@ func (b *BlueprintSolve) initialize() { // Solve implements the BlueprintStateful interface. func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - b.initialize() - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) @@ -136,11 +123,6 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra return nil } -// Reset implements BlueprintStateful -func (b *BlueprintSolve) Reset() { - b.assignments = nil -} - // SetNbInstances sets the number of instances for the blueprint func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index c85da7cee1..a725a52f5f 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -44,6 +44,30 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) +// Reset implements BlueprintStateful. +// It is used to initialize the blueprint for the current circuit. +func (b *BlueprintSolve) Reset() { + b.evaluatorPool.New = func() interface{} { + ce := &circuitEvaluator{ + evaluators: make([]gateEvaluator, len(b.Circuit)), + } + for wI := range b.Circuit { + w := &b.Circuit[wI] + if !w.IsInput() { + ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) + } + } + return ce + } + + assignments := make(WireAssignment, len(b.Circuit)) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) + for i := range assignments { + assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) + } + b.assignments = assignments +} + // Solve implements the BlueprintStateful interface. func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { @@ -99,29 +123,6 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra return nil } -// Reset implements BlueprintStateful -func (b *BlueprintSolve) Reset() { - b.evaluatorPool.New = func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), - } - for wI := range b.Circuit { - w := &b.Circuit[wI] - if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) - } - } - return ce - } - - assignments := make(WireAssignment, len(b.Circuit)) - nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) - for i := range assignments { - assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) - } - b.assignments = assignments -} - // SetNbInstances sets the number of instances for the blueprint func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go index 29e0a4d17e..c94c8ec5e5 100644 --- a/internal/gkr/bw6-633/blueprint.go +++ b/internal/gkr/bw6-633/blueprint.go @@ -44,20 +44,9 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -func (b *BlueprintSolve) initialize() { - - if b.assignments != nil { - return - } - b.lock.Lock() - defer b.lock.Unlock() - - if b.assignments != nil { - return // the unlikely event that two or more instructions were competing to initialize the blueprint - } - - // Compute metadata from Circuit - +// Reset implements BlueprintStateful. +// It is used to initialize the blueprint for the current circuit. +func (b *BlueprintSolve) Reset() { b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ evaluators: make([]gateEvaluator, len(b.Circuit)), @@ -82,8 +71,6 @@ func (b *BlueprintSolve) initialize() { // Solve implements the BlueprintStateful interface. func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - b.initialize() - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) @@ -136,11 +123,6 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra return nil } -// Reset implements BlueprintStateful -func (b *BlueprintSolve) Reset() { - b.assignments = nil -} - // SetNbInstances sets the number of instances for the blueprint func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 4666b8cd6a..73bce2984f 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -44,20 +44,9 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -func (b *BlueprintSolve) initialize() { - - if b.assignments != nil { - return - } - b.lock.Lock() - defer b.lock.Unlock() - - if b.assignments != nil { - return // the unlikely event that two or more instructions were competing to initialize the blueprint - } - - // Compute metadata from Circuit - +// Reset implements BlueprintStateful. +// It is used to initialize the blueprint for the current circuit. +func (b *BlueprintSolve) Reset() { b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ evaluators: make([]gateEvaluator, len(b.Circuit)), @@ -82,8 +71,6 @@ func (b *BlueprintSolve) initialize() { // Solve implements the BlueprintStateful interface. func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - b.initialize() - // Get a circuit evaluator from the pool ce := b.evaluatorPool.Get().(*circuitEvaluator) defer b.evaluatorPool.Put(ce) @@ -136,11 +123,6 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra return nil } -// Reset implements BlueprintStateful -func (b *BlueprintSolve) Reset() { - b.assignments = nil -} - // SetNbInstances sets the number of instances for the blueprint func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { b.NbInstances = nbInstances From db9b2da9228a75fdfa198c3c14aaaddc6efe6bdb Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Tue, 10 Feb 2026 10:32:12 -0600 Subject: [PATCH 154/251] fix: Test engine to run reset --- std/gkrapi/compile.go | 5 ----- test/engine.go | 21 +++++++++------------ 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 487f8191b1..29ceaa620c 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -22,11 +22,6 @@ import ( "github.com/consensys/gnark/std/multicommit" ) -type circuitDataForSnark struct { - circuit gkrtypes.GadgetCircuit - assignments gadget.WireAssignment -} - // The InitialChallengeGetter provides a one-time initial Fiat-Shamir challenge for the GKR prover. // Normally, these should include a unique circuit identifier and all input-output pairs. type InitialChallengeGetter func() []frontend.Variable diff --git a/test/engine.go b/test/engine.go index 733d7cf1be..a0c667dbf4 100644 --- a/test/engine.go +++ b/test/engine.go @@ -116,18 +116,6 @@ func IsSolved(circuit, witness frontend.Circuit, field *big.Int, opts ...TestEng log.Debug().Msg("running circuit in test engine") cptAdd, cptMul, cptSub, cptToBinary, cptFromBinary, cptAssertIsEqual = 0, 0, 0, 0, 0, 0 - // XXX(@ivokub): commented out - this seems to match the implementation of native solver, - // but we always create new test engine when calling `IsSolved`, so this slice is always empty. - // Skipping this allows us to avoid making test engine generic. - /* - // first we reset the stateful blueprints - for i := range e.blueprints { - if b, ok := e.blueprints[i].(constraint.BlueprintStateful); ok { - b.Reset() - } - } - */ - var apiEngine frontend.API if smallfields.IsSmallField(e.modulus()) && !e.noSmallFieldCompatibility { apiEngine = &smallfieldEngine{engine: e} @@ -769,7 +757,16 @@ func (e *engine) AddBlueprint(b constraint.Blueprint) constraint.BlueprintID { panic("unsupported blueprint in test engine") } } + e.blueprints = append(e.blueprints, b) + + if stateful, ok := b.(constraint.BlueprintStateful[constraint.U32]); ok { + stateful.Reset() + } + if stateful, ok := b.(constraint.BlueprintStateful[constraint.U64]); ok { + stateful.Reset() + } + return constraint.BlueprintID(len(e.blueprints) - 1) } From 14b592b3bd4048e85d40017b0694e62cd4446d9e Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 11 Feb 2026 11:29:12 -0600 Subject: [PATCH 155/251] fix: remove decommissioned curves --- internal/gkr/bls24-315/blueprint.go | 419 ---------------------------- internal/gkr/bls24-317/blueprint.go | 419 ---------------------------- internal/gkr/bw6-633/blueprint.go | 419 ---------------------------- std/gkrapi/compile.go | 9 - 4 files changed, 1266 deletions(-) delete mode 100644 internal/gkr/bls24-315/blueprint.go delete mode 100644 internal/gkr/bls24-317/blueprint.go delete mode 100644 internal/gkr/bw6-633/blueprint.go diff --git a/internal/gkr/bls24-315/blueprint.go b/internal/gkr/bls24-315/blueprint.go deleted file mode 100644 index a3ca0bb13f..0000000000 --- a/internal/gkr/bls24-315/blueprint.go +++ /dev/null @@ -1,419 +0,0 @@ -// Copyright 2020-2026 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -// Code generated by gnark DO NOT EDIT - -package gkr - -import ( - "fmt" - "math/bits" - "slices" - "sync" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" - "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - "github.com/consensys/gnark-crypto/hash" - "github.com/consensys/gnark/constraint" - gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrtypes" -) - -// circuitEvaluator evaluates all gates in a circuit for one instance -type circuitEvaluator struct { - evaluators []gateEvaluator // one evaluator per wire -} - -// BlueprintSolve is a BLS24_315-specific blueprint for solving GKR circuit instances. -type BlueprintSolve struct { - // Circuit structure (serialized) - Circuit gkrtypes.SerializableCircuit - NbInstances uint32 - - // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - outputWires []int `cbor:"-"` - maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances - - lock sync.Mutex `cbor:"-"` -} - -// Ensures BlueprintSolve implements BlueprintStateful -var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) - -// Reset implements BlueprintStateful. -// It is used to initialize the blueprint for the current circuit. -func (b *BlueprintSolve) Reset() { - b.evaluatorPool.New = func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), - } - for wI := range b.Circuit { - w := &b.Circuit[wI] - if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) - } - } - return ce - } - - assignments := make(WireAssignment, len(b.Circuit)) - nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) - for i := range assignments { - assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) - } - b.assignments = assignments -} - -// Solve implements the BlueprintStateful interface. -func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - - // Get a circuit evaluator from the pool - ce := b.evaluatorPool.Get().(*circuitEvaluator) - defer b.evaluatorPool.Put(ce) - - // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions - instanceI := int(inst.Calldata[1]) - calldata := inst.Calldata[2:] - - // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice - if len(b.assignments[0]) <= instanceI { - b.lock.Lock() - defer b.lock.Unlock() - for wI := range b.assignments { - for len(b.assignments[wI]) <= instanceI { - b.assignments[wI] = append(b.assignments[wI], fr.Element{}) - } - } - } - - // Process all wires in topological order (circuit is already sorted) - for wI := range b.Circuit { - w := &b.Circuit[wI] - - if w.IsInput() { - val, delta := s.Read(calldata) - calldata = calldata[delta:] - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(b.assignments[wI][instanceI][:], val[:]) - } else { - // Get evaluator for this wire from the circuit evaluator - evaluator := &ce.evaluators[wI] - - // Push gate inputs - for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignments[inWI][instanceI]) - } - - // Evaluate the gate - b.assignments[wI][instanceI].Set(evaluator.evaluate()) - } - } - - // Set output wires (copy fr.Element to U64 in Montgomery form) - for outI, outWI := range b.outputWires { - var val constraint.U64 - copy(val[:], b.assignments[outWI][instanceI][:]) - s.SetValue(uint32(outI+int(inst.WireOffset)), val) - } - - return nil -} - -// SetNbInstances sets the number of instances for the blueprint -func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { - b.NbInstances = nbInstances -} - -// CalldataSize implements Blueprint -func (b *BlueprintSolve) CalldataSize() int { - return -1 // variable size -} - -// NbConstraints implements Blueprint -func (b *BlueprintSolve) NbConstraints() int { - return 0 -} - -// NbOutputs implements Blueprint -func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() - return len(b.outputWires) -} - -// UpdateInstructionTree implements Blueprint -func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := constraint.LevelUnset - - // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions - offset := 2 // skip size and instance index - - // Parse input linear expressions - for offset < len(inst.Calldata) { - n := int(inst.Calldata[offset]) // number of terms in this linear expression - offset++ - - for range n { - wireID := inst.Calldata[offset+1] - offset += 2 - if !tree.HasWire(wireID) { - continue - } - if level := tree.GetWireLevel(wireID); level > maxLevel { - maxLevel = level - } - } - } - - outputLevel := maxLevel + 1 - for i := range b.outputWires { - tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) - } - - b.maxOutputLevel = max(b.maxOutputLevel, outputLevel) - - return outputLevel -} - -// BlueprintProve is a BLS24_315-specific blueprint for generating GKR proofs. -type BlueprintProve struct { - SolveBlueprintID constraint.BlueprintID - SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time - HashName string - - lock sync.Mutex -} - -// Ensures BlueprintProve implements BlueprintSolvable -var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) - -// Solve implements the BlueprintSolvable interface for proving. -func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - b.lock.Lock() - defer b.lock.Unlock() - - // Get solve blueprint from solver by ID - solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) - - // Get assignments from solve blueprint (already in fr.Element form) - assignments := solveBlueprint.assignments - if len(assignments) == 0 { - return fmt.Errorf("no assignments available for proving") - } - - nbPaddedInstances := uint32(ecc.NextPowerOfTwo(uint64(solveBlueprint.NbInstances))) - if nbPaddedInstances != uint32(len(assignments[0])) { // test engine path - nbPadding := nbPaddedInstances - solveBlueprint.NbInstances - for wI := range assignments { - assignments[wI] = slices.Grow(assignments[wI], int(nbPadding)) - toRepeat := assignments[wI][solveBlueprint.NbInstances-1] - for range nbPadding { - assignments[wI] = append(assignments[wI], toRepeat) - } - } - } else { - for wI := range assignments { - for i := solveBlueprint.NbInstances; i < nbPaddedInstances; i++ { - assignments[wI][i] = assignments[wI][i-1] - } - } - } - - // Read initial challenges from instruction calldata (parse dynamically, no metadata) - // Format: [0]=totalSize, [1...]=challenge linear expressions - insBytes := make([][]byte, 0) // first challenges - calldata := inst.Calldata[1:] // skip size prefix - for len(calldata) != 0 { - val, delta := s.Read(calldata) - calldata = calldata[delta:] - - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - var challenge fr.Element - copy(challenge[:], val[:]) - insBytes = append(insBytes, challenge.Marshal()) - } - - // Create Fiat-Shamir settings - hsh := hash.NewHash(b.HashName + "_BLS24_315") - fsSettings := fiatshamir.WithHash(hsh, insBytes...) - - // Call the BLS24_315-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) - if err != nil { - return fmt.Errorf("bls24_315 prove failed: %w", err) - } - - for i, elem := range proof.flatten() { - var val constraint.U64 - copy(val[:], (*elem)[:]) - s.SetValue(uint32(i+int(inst.WireOffset)), val) - } - - return nil -} - -// CalldataSize implements Blueprint -func (b *BlueprintProve) CalldataSize() int { - return -1 // variable size -} - -// NbConstraints implements Blueprint -func (b *BlueprintProve) NbConstraints() int { - return 0 -} - -func (b *BlueprintProve) proofSize() int { - if b.SolveBlueprint.NbInstances < 2 { - return 0 - } - nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) - logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) -} - -// NbOutputs implements Blueprint -func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - return b.proofSize() -} - -// UpdateInstructionTree implements Blueprint -func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := b.SolveBlueprint.maxOutputLevel - - // Format: [0]=totalSize, [1...]=challenge linear expressions - offset := 1 // skip size prefix - - // Parse all challenge linear expressions - for offset < len(inst.Calldata) { - n := int(inst.Calldata[offset]) // number of terms in this linear expression - offset++ - - for range n { - wireID := inst.Calldata[offset+1] - offset += 2 - if !tree.HasWire(wireID) { - continue - } - - maxLevel = max(maxLevel, tree.GetWireLevel(wireID)) - } - } - - outputLevel := maxLevel + 1 - // Compute proof size from blueprint state - if b.SolveBlueprint.NbInstances > 0 { - proofSize := b.proofSize() - for i := range proofSize { - tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) - } - } - - return outputLevel -} - -// BlueprintGetAssignment is a BLS24_315-specific blueprint for retrieving wire assignments. -type BlueprintGetAssignment struct { - SolveBlueprintID constraint.BlueprintID - SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time - - lock sync.Mutex -} - -// Ensures BlueprintGetAssignment implements BlueprintSolvable -var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) - -// Solve implements the BlueprintSolvable interface for getting assignments. -func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - b.lock.Lock() - defer b.lock.Unlock() - - // Read wireI and instanceI from calldata - // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression - wireI := int(inst.Calldata[1]) - instanceI := int(inst.Calldata[2]) - - // Get solve blueprint from solver by ID - solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) - - var v constraint.U64 - copy(v[:], solveBlueprint.assignments[wireI][instanceI][:]) - // Set output wire - s.SetValue(inst.WireOffset, v) - return nil -} - -// CalldataSize implements Blueprint -func (b *BlueprintGetAssignment) CalldataSize() int { - return -1 // variable size: [wireI, instanceI, dependency_linear_expression] -} - -// NbConstraints implements Blueprint -func (b *BlueprintGetAssignment) NbConstraints() int { - return 0 -} - -// NbOutputs implements Blueprint -func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { - return 1 // returns one assignment value -} - -// UpdateInstructionTree implements Blueprint -func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := constraint.LevelUnset - - // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression - offset := 3 // skip size, wireI, and instanceI - - // Parse dependency linear expression - // This ensures we run after the solve instruction for this instance - n := int(inst.Calldata[offset]) - offset++ - - for range n { - wireID := inst.Calldata[offset+1] - offset += 2 - if !tree.HasWire(wireID) { - continue - } - if level := tree.GetWireLevel(wireID); level > maxLevel { - maxLevel = level - } - } - - outputLevel := maxLevel + 1 - tree.InsertWire(inst.WireOffset, outputLevel) - return outputLevel -} - -// NewBlueprints creates and registers all GKR blueprints for BLS24_315 -func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { - // Create and register solve blueprint - solve := &BlueprintSolve{Circuit: circuit} - solveID := compiler.AddBlueprint(solve) - - // Create and register prove blueprint - prove := &BlueprintProve{ - SolveBlueprintID: solveID, - SolveBlueprint: solve, - HashName: hashName, - } - proveID := compiler.AddBlueprint(prove) - - // Create and register GetAssignment blueprint - getAssignment := &BlueprintGetAssignment{ - SolveBlueprintID: solveID, - SolveBlueprint: solve, - } - getAssignmentID := compiler.AddBlueprint(getAssignment) - - return gadget.Blueprints{ - SolveID: solveID, - Solve: solve, - ProveID: proveID, - GetAssignmentID: getAssignmentID, - } -} diff --git a/internal/gkr/bls24-317/blueprint.go b/internal/gkr/bls24-317/blueprint.go deleted file mode 100644 index 8d97250b1f..0000000000 --- a/internal/gkr/bls24-317/blueprint.go +++ /dev/null @@ -1,419 +0,0 @@ -// Copyright 2020-2026 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -// Code generated by gnark DO NOT EDIT - -package gkr - -import ( - "fmt" - "math/bits" - "slices" - "sync" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" - "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - "github.com/consensys/gnark-crypto/hash" - "github.com/consensys/gnark/constraint" - gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrtypes" -) - -// circuitEvaluator evaluates all gates in a circuit for one instance -type circuitEvaluator struct { - evaluators []gateEvaluator // one evaluator per wire -} - -// BlueprintSolve is a BLS24_317-specific blueprint for solving GKR circuit instances. -type BlueprintSolve struct { - // Circuit structure (serialized) - Circuit gkrtypes.SerializableCircuit - NbInstances uint32 - - // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - outputWires []int `cbor:"-"` - maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances - - lock sync.Mutex `cbor:"-"` -} - -// Ensures BlueprintSolve implements BlueprintStateful -var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) - -// Reset implements BlueprintStateful. -// It is used to initialize the blueprint for the current circuit. -func (b *BlueprintSolve) Reset() { - b.evaluatorPool.New = func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), - } - for wI := range b.Circuit { - w := &b.Circuit[wI] - if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) - } - } - return ce - } - - assignments := make(WireAssignment, len(b.Circuit)) - nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) - for i := range assignments { - assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) - } - b.assignments = assignments -} - -// Solve implements the BlueprintStateful interface. -func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - - // Get a circuit evaluator from the pool - ce := b.evaluatorPool.Get().(*circuitEvaluator) - defer b.evaluatorPool.Put(ce) - - // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions - instanceI := int(inst.Calldata[1]) - calldata := inst.Calldata[2:] - - // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice - if len(b.assignments[0]) <= instanceI { - b.lock.Lock() - defer b.lock.Unlock() - for wI := range b.assignments { - for len(b.assignments[wI]) <= instanceI { - b.assignments[wI] = append(b.assignments[wI], fr.Element{}) - } - } - } - - // Process all wires in topological order (circuit is already sorted) - for wI := range b.Circuit { - w := &b.Circuit[wI] - - if w.IsInput() { - val, delta := s.Read(calldata) - calldata = calldata[delta:] - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(b.assignments[wI][instanceI][:], val[:]) - } else { - // Get evaluator for this wire from the circuit evaluator - evaluator := &ce.evaluators[wI] - - // Push gate inputs - for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignments[inWI][instanceI]) - } - - // Evaluate the gate - b.assignments[wI][instanceI].Set(evaluator.evaluate()) - } - } - - // Set output wires (copy fr.Element to U64 in Montgomery form) - for outI, outWI := range b.outputWires { - var val constraint.U64 - copy(val[:], b.assignments[outWI][instanceI][:]) - s.SetValue(uint32(outI+int(inst.WireOffset)), val) - } - - return nil -} - -// SetNbInstances sets the number of instances for the blueprint -func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { - b.NbInstances = nbInstances -} - -// CalldataSize implements Blueprint -func (b *BlueprintSolve) CalldataSize() int { - return -1 // variable size -} - -// NbConstraints implements Blueprint -func (b *BlueprintSolve) NbConstraints() int { - return 0 -} - -// NbOutputs implements Blueprint -func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() - return len(b.outputWires) -} - -// UpdateInstructionTree implements Blueprint -func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := constraint.LevelUnset - - // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions - offset := 2 // skip size and instance index - - // Parse input linear expressions - for offset < len(inst.Calldata) { - n := int(inst.Calldata[offset]) // number of terms in this linear expression - offset++ - - for range n { - wireID := inst.Calldata[offset+1] - offset += 2 - if !tree.HasWire(wireID) { - continue - } - if level := tree.GetWireLevel(wireID); level > maxLevel { - maxLevel = level - } - } - } - - outputLevel := maxLevel + 1 - for i := range b.outputWires { - tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) - } - - b.maxOutputLevel = max(b.maxOutputLevel, outputLevel) - - return outputLevel -} - -// BlueprintProve is a BLS24_317-specific blueprint for generating GKR proofs. -type BlueprintProve struct { - SolveBlueprintID constraint.BlueprintID - SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time - HashName string - - lock sync.Mutex -} - -// Ensures BlueprintProve implements BlueprintSolvable -var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) - -// Solve implements the BlueprintSolvable interface for proving. -func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - b.lock.Lock() - defer b.lock.Unlock() - - // Get solve blueprint from solver by ID - solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) - - // Get assignments from solve blueprint (already in fr.Element form) - assignments := solveBlueprint.assignments - if len(assignments) == 0 { - return fmt.Errorf("no assignments available for proving") - } - - nbPaddedInstances := uint32(ecc.NextPowerOfTwo(uint64(solveBlueprint.NbInstances))) - if nbPaddedInstances != uint32(len(assignments[0])) { // test engine path - nbPadding := nbPaddedInstances - solveBlueprint.NbInstances - for wI := range assignments { - assignments[wI] = slices.Grow(assignments[wI], int(nbPadding)) - toRepeat := assignments[wI][solveBlueprint.NbInstances-1] - for range nbPadding { - assignments[wI] = append(assignments[wI], toRepeat) - } - } - } else { - for wI := range assignments { - for i := solveBlueprint.NbInstances; i < nbPaddedInstances; i++ { - assignments[wI][i] = assignments[wI][i-1] - } - } - } - - // Read initial challenges from instruction calldata (parse dynamically, no metadata) - // Format: [0]=totalSize, [1...]=challenge linear expressions - insBytes := make([][]byte, 0) // first challenges - calldata := inst.Calldata[1:] // skip size prefix - for len(calldata) != 0 { - val, delta := s.Read(calldata) - calldata = calldata[delta:] - - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - var challenge fr.Element - copy(challenge[:], val[:]) - insBytes = append(insBytes, challenge.Marshal()) - } - - // Create Fiat-Shamir settings - hsh := hash.NewHash(b.HashName + "_BLS24_317") - fsSettings := fiatshamir.WithHash(hsh, insBytes...) - - // Call the BLS24_317-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) - if err != nil { - return fmt.Errorf("bls24_317 prove failed: %w", err) - } - - for i, elem := range proof.flatten() { - var val constraint.U64 - copy(val[:], (*elem)[:]) - s.SetValue(uint32(i+int(inst.WireOffset)), val) - } - - return nil -} - -// CalldataSize implements Blueprint -func (b *BlueprintProve) CalldataSize() int { - return -1 // variable size -} - -// NbConstraints implements Blueprint -func (b *BlueprintProve) NbConstraints() int { - return 0 -} - -func (b *BlueprintProve) proofSize() int { - if b.SolveBlueprint.NbInstances < 2 { - return 0 - } - nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) - logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) -} - -// NbOutputs implements Blueprint -func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - return b.proofSize() -} - -// UpdateInstructionTree implements Blueprint -func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := b.SolveBlueprint.maxOutputLevel - - // Format: [0]=totalSize, [1...]=challenge linear expressions - offset := 1 // skip size prefix - - // Parse all challenge linear expressions - for offset < len(inst.Calldata) { - n := int(inst.Calldata[offset]) // number of terms in this linear expression - offset++ - - for range n { - wireID := inst.Calldata[offset+1] - offset += 2 - if !tree.HasWire(wireID) { - continue - } - - maxLevel = max(maxLevel, tree.GetWireLevel(wireID)) - } - } - - outputLevel := maxLevel + 1 - // Compute proof size from blueprint state - if b.SolveBlueprint.NbInstances > 0 { - proofSize := b.proofSize() - for i := range proofSize { - tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) - } - } - - return outputLevel -} - -// BlueprintGetAssignment is a BLS24_317-specific blueprint for retrieving wire assignments. -type BlueprintGetAssignment struct { - SolveBlueprintID constraint.BlueprintID - SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time - - lock sync.Mutex -} - -// Ensures BlueprintGetAssignment implements BlueprintSolvable -var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) - -// Solve implements the BlueprintSolvable interface for getting assignments. -func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - b.lock.Lock() - defer b.lock.Unlock() - - // Read wireI and instanceI from calldata - // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression - wireI := int(inst.Calldata[1]) - instanceI := int(inst.Calldata[2]) - - // Get solve blueprint from solver by ID - solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) - - var v constraint.U64 - copy(v[:], solveBlueprint.assignments[wireI][instanceI][:]) - // Set output wire - s.SetValue(inst.WireOffset, v) - return nil -} - -// CalldataSize implements Blueprint -func (b *BlueprintGetAssignment) CalldataSize() int { - return -1 // variable size: [wireI, instanceI, dependency_linear_expression] -} - -// NbConstraints implements Blueprint -func (b *BlueprintGetAssignment) NbConstraints() int { - return 0 -} - -// NbOutputs implements Blueprint -func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { - return 1 // returns one assignment value -} - -// UpdateInstructionTree implements Blueprint -func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := constraint.LevelUnset - - // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression - offset := 3 // skip size, wireI, and instanceI - - // Parse dependency linear expression - // This ensures we run after the solve instruction for this instance - n := int(inst.Calldata[offset]) - offset++ - - for range n { - wireID := inst.Calldata[offset+1] - offset += 2 - if !tree.HasWire(wireID) { - continue - } - if level := tree.GetWireLevel(wireID); level > maxLevel { - maxLevel = level - } - } - - outputLevel := maxLevel + 1 - tree.InsertWire(inst.WireOffset, outputLevel) - return outputLevel -} - -// NewBlueprints creates and registers all GKR blueprints for BLS24_317 -func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { - // Create and register solve blueprint - solve := &BlueprintSolve{Circuit: circuit} - solveID := compiler.AddBlueprint(solve) - - // Create and register prove blueprint - prove := &BlueprintProve{ - SolveBlueprintID: solveID, - SolveBlueprint: solve, - HashName: hashName, - } - proveID := compiler.AddBlueprint(prove) - - // Create and register GetAssignment blueprint - getAssignment := &BlueprintGetAssignment{ - SolveBlueprintID: solveID, - SolveBlueprint: solve, - } - getAssignmentID := compiler.AddBlueprint(getAssignment) - - return gadget.Blueprints{ - SolveID: solveID, - Solve: solve, - ProveID: proveID, - GetAssignmentID: getAssignmentID, - } -} diff --git a/internal/gkr/bw6-633/blueprint.go b/internal/gkr/bw6-633/blueprint.go deleted file mode 100644 index c94c8ec5e5..0000000000 --- a/internal/gkr/bw6-633/blueprint.go +++ /dev/null @@ -1,419 +0,0 @@ -// Copyright 2020-2026 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -// Code generated by gnark DO NOT EDIT - -package gkr - -import ( - "fmt" - "math/bits" - "slices" - "sync" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - "github.com/consensys/gnark-crypto/hash" - "github.com/consensys/gnark/constraint" - gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrtypes" -) - -// circuitEvaluator evaluates all gates in a circuit for one instance -type circuitEvaluator struct { - evaluators []gateEvaluator // one evaluator per wire -} - -// BlueprintSolve is a BW6_633-specific blueprint for solving GKR circuit instances. -type BlueprintSolve struct { - // Circuit structure (serialized) - Circuit gkrtypes.SerializableCircuit - NbInstances uint32 - - // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - outputWires []int `cbor:"-"` - maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances - - lock sync.Mutex `cbor:"-"` -} - -// Ensures BlueprintSolve implements BlueprintStateful -var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) - -// Reset implements BlueprintStateful. -// It is used to initialize the blueprint for the current circuit. -func (b *BlueprintSolve) Reset() { - b.evaluatorPool.New = func() interface{} { - ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), - } - for wI := range b.Circuit { - w := &b.Circuit[wI] - if !w.IsInput() { - ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) - } - } - return ce - } - - assignments := make(WireAssignment, len(b.Circuit)) - nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) - for i := range assignments { - assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) - } - b.assignments = assignments -} - -// Solve implements the BlueprintStateful interface. -func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - - // Get a circuit evaluator from the pool - ce := b.evaluatorPool.Get().(*circuitEvaluator) - defer b.evaluatorPool.Put(ce) - - // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions - instanceI := int(inst.Calldata[1]) - calldata := inst.Calldata[2:] - - // The test engine runs the instruction before "finalize" is called. We need to avoid attempting to access an uninitialized slice - if len(b.assignments[0]) <= instanceI { - b.lock.Lock() - defer b.lock.Unlock() - for wI := range b.assignments { - for len(b.assignments[wI]) <= instanceI { - b.assignments[wI] = append(b.assignments[wI], fr.Element{}) - } - } - } - - // Process all wires in topological order (circuit is already sorted) - for wI := range b.Circuit { - w := &b.Circuit[wI] - - if w.IsInput() { - val, delta := s.Read(calldata) - calldata = calldata[delta:] - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - copy(b.assignments[wI][instanceI][:], val[:]) - } else { - // Get evaluator for this wire from the circuit evaluator - evaluator := &ce.evaluators[wI] - - // Push gate inputs - for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignments[inWI][instanceI]) - } - - // Evaluate the gate - b.assignments[wI][instanceI].Set(evaluator.evaluate()) - } - } - - // Set output wires (copy fr.Element to U64 in Montgomery form) - for outI, outWI := range b.outputWires { - var val constraint.U64 - copy(val[:], b.assignments[outWI][instanceI][:]) - s.SetValue(uint32(outI+int(inst.WireOffset)), val) - } - - return nil -} - -// SetNbInstances sets the number of instances for the blueprint -func (b *BlueprintSolve) SetNbInstances(nbInstances uint32) { - b.NbInstances = nbInstances -} - -// CalldataSize implements Blueprint -func (b *BlueprintSolve) CalldataSize() int { - return -1 // variable size -} - -// NbConstraints implements Blueprint -func (b *BlueprintSolve) NbConstraints() int { - return 0 -} - -// NbOutputs implements Blueprint -func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() - return len(b.outputWires) -} - -// UpdateInstructionTree implements Blueprint -func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := constraint.LevelUnset - - // Format: [0]=totalSize, [1]=instanceIndex, [2...]=input linear expressions - offset := 2 // skip size and instance index - - // Parse input linear expressions - for offset < len(inst.Calldata) { - n := int(inst.Calldata[offset]) // number of terms in this linear expression - offset++ - - for range n { - wireID := inst.Calldata[offset+1] - offset += 2 - if !tree.HasWire(wireID) { - continue - } - if level := tree.GetWireLevel(wireID); level > maxLevel { - maxLevel = level - } - } - } - - outputLevel := maxLevel + 1 - for i := range b.outputWires { - tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) - } - - b.maxOutputLevel = max(b.maxOutputLevel, outputLevel) - - return outputLevel -} - -// BlueprintProve is a BW6_633-specific blueprint for generating GKR proofs. -type BlueprintProve struct { - SolveBlueprintID constraint.BlueprintID - SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time - HashName string - - lock sync.Mutex -} - -// Ensures BlueprintProve implements BlueprintSolvable -var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) - -// Solve implements the BlueprintSolvable interface for proving. -func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - b.lock.Lock() - defer b.lock.Unlock() - - // Get solve blueprint from solver by ID - solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) - - // Get assignments from solve blueprint (already in fr.Element form) - assignments := solveBlueprint.assignments - if len(assignments) == 0 { - return fmt.Errorf("no assignments available for proving") - } - - nbPaddedInstances := uint32(ecc.NextPowerOfTwo(uint64(solveBlueprint.NbInstances))) - if nbPaddedInstances != uint32(len(assignments[0])) { // test engine path - nbPadding := nbPaddedInstances - solveBlueprint.NbInstances - for wI := range assignments { - assignments[wI] = slices.Grow(assignments[wI], int(nbPadding)) - toRepeat := assignments[wI][solveBlueprint.NbInstances-1] - for range nbPadding { - assignments[wI] = append(assignments[wI], toRepeat) - } - } - } else { - for wI := range assignments { - for i := solveBlueprint.NbInstances; i < nbPaddedInstances; i++ { - assignments[wI][i] = assignments[wI][i-1] - } - } - } - - // Read initial challenges from instruction calldata (parse dynamically, no metadata) - // Format: [0]=totalSize, [1...]=challenge linear expressions - insBytes := make([][]byte, 0) // first challenges - calldata := inst.Calldata[1:] // skip size prefix - for len(calldata) != 0 { - val, delta := s.Read(calldata) - calldata = calldata[delta:] - - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) - var challenge fr.Element - copy(challenge[:], val[:]) - insBytes = append(insBytes, challenge.Marshal()) - } - - // Create Fiat-Shamir settings - hsh := hash.NewHash(b.HashName + "_BW6_633") - fsSettings := fiatshamir.WithHash(hsh, insBytes...) - - // Call the BW6_633-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) - if err != nil { - return fmt.Errorf("bw6_633 prove failed: %w", err) - } - - for i, elem := range proof.flatten() { - var val constraint.U64 - copy(val[:], (*elem)[:]) - s.SetValue(uint32(i+int(inst.WireOffset)), val) - } - - return nil -} - -// CalldataSize implements Blueprint -func (b *BlueprintProve) CalldataSize() int { - return -1 // variable size -} - -// NbConstraints implements Blueprint -func (b *BlueprintProve) NbConstraints() int { - return 0 -} - -func (b *BlueprintProve) proofSize() int { - if b.SolveBlueprint.NbInstances < 2 { - return 0 - } - nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) - logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) -} - -// NbOutputs implements Blueprint -func (b *BlueprintProve) NbOutputs(inst constraint.Instruction) int { - return b.proofSize() -} - -// UpdateInstructionTree implements Blueprint -func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := b.SolveBlueprint.maxOutputLevel - - // Format: [0]=totalSize, [1...]=challenge linear expressions - offset := 1 // skip size prefix - - // Parse all challenge linear expressions - for offset < len(inst.Calldata) { - n := int(inst.Calldata[offset]) // number of terms in this linear expression - offset++ - - for range n { - wireID := inst.Calldata[offset+1] - offset += 2 - if !tree.HasWire(wireID) { - continue - } - - maxLevel = max(maxLevel, tree.GetWireLevel(wireID)) - } - } - - outputLevel := maxLevel + 1 - // Compute proof size from blueprint state - if b.SolveBlueprint.NbInstances > 0 { - proofSize := b.proofSize() - for i := range proofSize { - tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) - } - } - - return outputLevel -} - -// BlueprintGetAssignment is a BW6_633-specific blueprint for retrieving wire assignments. -type BlueprintGetAssignment struct { - SolveBlueprintID constraint.BlueprintID - SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time - - lock sync.Mutex -} - -// Ensures BlueprintGetAssignment implements BlueprintSolvable -var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) - -// Solve implements the BlueprintSolvable interface for getting assignments. -func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { - b.lock.Lock() - defer b.lock.Unlock() - - // Read wireI and instanceI from calldata - // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression - wireI := int(inst.Calldata[1]) - instanceI := int(inst.Calldata[2]) - - // Get solve blueprint from solver by ID - solveBlueprint := s.GetBlueprint(b.SolveBlueprintID).(*BlueprintSolve) - - var v constraint.U64 - copy(v[:], solveBlueprint.assignments[wireI][instanceI][:]) - // Set output wire - s.SetValue(inst.WireOffset, v) - return nil -} - -// CalldataSize implements Blueprint -func (b *BlueprintGetAssignment) CalldataSize() int { - return -1 // variable size: [wireI, instanceI, dependency_linear_expression] -} - -// NbConstraints implements Blueprint -func (b *BlueprintGetAssignment) NbConstraints() int { - return 0 -} - -// NbOutputs implements Blueprint -func (b *BlueprintGetAssignment) NbOutputs(inst constraint.Instruction) int { - return 1 // returns one assignment value -} - -// UpdateInstructionTree implements Blueprint -func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instruction, tree constraint.InstructionTree) constraint.Level { - maxLevel := constraint.LevelUnset - - // Format: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency linear expression - offset := 3 // skip size, wireI, and instanceI - - // Parse dependency linear expression - // This ensures we run after the solve instruction for this instance - n := int(inst.Calldata[offset]) - offset++ - - for range n { - wireID := inst.Calldata[offset+1] - offset += 2 - if !tree.HasWire(wireID) { - continue - } - if level := tree.GetWireLevel(wireID); level > maxLevel { - maxLevel = level - } - } - - outputLevel := maxLevel + 1 - tree.InsertWire(inst.WireOffset, outputLevel) - return outputLevel -} - -// NewBlueprints creates and registers all GKR blueprints for BW6_633 -func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { - // Create and register solve blueprint - solve := &BlueprintSolve{Circuit: circuit} - solveID := compiler.AddBlueprint(solve) - - // Create and register prove blueprint - prove := &BlueprintProve{ - SolveBlueprintID: solveID, - SolveBlueprint: solve, - HashName: hashName, - } - proveID := compiler.AddBlueprint(prove) - - // Create and register GetAssignment blueprint - getAssignment := &BlueprintGetAssignment{ - SolveBlueprintID: solveID, - SolveBlueprint: solve, - } - getAssignmentID := compiler.AddBlueprint(getAssignment) - - return gadget.Blueprints{ - SolveID: solveID, - Solve: solve, - ProveID: proveID, - GetAssignmentID: getAssignmentID, - } -} diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 29ceaa620c..4f4a2316de 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -9,10 +9,7 @@ import ( gadget "github.com/consensys/gnark/internal/gkr" gkrbls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" gkrbls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" - gkrbls24315 "github.com/consensys/gnark/internal/gkr/bls24-315" - gkrbls24317 "github.com/consensys/gnark/internal/gkr/bls24-317" gkrbn254 "github.com/consensys/gnark/internal/gkr/bn254" - gkrbw6633 "github.com/consensys/gnark/internal/gkr/bw6-633" gkrbw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" @@ -98,12 +95,6 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C res.blueprints = gkrbls12377.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) case ecc.BLS12_381: res.blueprints = gkrbls12381.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) - case ecc.BLS24_315: - res.blueprints = gkrbls24315.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) - case ecc.BLS24_317: - res.blueprints = gkrbls24317.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) - case ecc.BW6_633: - res.blueprints = gkrbw6633.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) case ecc.BW6_761: res.blueprints = gkrbw6761.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) default: From 339668e2ef225df32e8c9423e0db2f314d87d34b Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 11 Feb 2026 11:42:28 -0600 Subject: [PATCH 156/251] fix: remove newWitness out of bench loop --- std/hash/mimc/gkr-mimc/gkr-mimc_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go index 31bd5f7384..471d06f960 100644 --- a/std/hash/mimc/gkr-mimc/gkr-mimc_test.go +++ b/std/hash/mimc/gkr-mimc/gkr-mimc_test.go @@ -130,12 +130,13 @@ func BenchmarkGkrMiMC(b *testing.B) { cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &circuit) require.NoError(b, err) + w, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField()) + require.NoError(b, err) + b.ResetTimer() for b.Loop() { - w, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField()) + _, err = cs.Solve(w) require.NoError(b, err) - - require.NoError(b, cs.IsSolved(w)) } } From d892646bb948f9939a9865be08d6eca75c6ac756 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 11 Feb 2026 11:48:13 -0600 Subject: [PATCH 157/251] remove unused field --- internal/generator/backend/template/gkr/blueprint.go.tmpl | 2 -- internal/gkr/bls12-377/blueprint.go | 2 -- internal/gkr/bls12-381/blueprint.go | 2 -- internal/gkr/bn254/blueprint.go | 2 -- internal/gkr/bw6-761/blueprint.go | 2 -- 5 files changed, 10 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index a30868ac3f..55e50b14ef 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -311,7 +311,6 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree // BlueprintGetAssignment is a {{.FieldID}}-specific blueprint for retrieving wire assignments. type BlueprintGetAssignment struct { SolveBlueprintID constraint.BlueprintID - SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time lock sync.Mutex } @@ -399,7 +398,6 @@ func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compil // Create and register GetAssignment blueprint getAssignment := &BlueprintGetAssignment{ SolveBlueprintID: solveID, - SolveBlueprint: solve, } getAssignmentID := compiler.AddBlueprint(getAssignment) diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 21553f920e..3bfafa0e48 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -318,7 +318,6 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree // BlueprintGetAssignment is a BLS12_377-specific blueprint for retrieving wire assignments. type BlueprintGetAssignment struct { SolveBlueprintID constraint.BlueprintID - SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time lock sync.Mutex } @@ -406,7 +405,6 @@ func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compil // Create and register GetAssignment blueprint getAssignment := &BlueprintGetAssignment{ SolveBlueprintID: solveID, - SolveBlueprint: solve, } getAssignmentID := compiler.AddBlueprint(getAssignment) diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 7c461bf9d7..20e70e307f 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -318,7 +318,6 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree // BlueprintGetAssignment is a BLS12_381-specific blueprint for retrieving wire assignments. type BlueprintGetAssignment struct { SolveBlueprintID constraint.BlueprintID - SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time lock sync.Mutex } @@ -406,7 +405,6 @@ func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compil // Create and register GetAssignment blueprint getAssignment := &BlueprintGetAssignment{ SolveBlueprintID: solveID, - SolveBlueprint: solve, } getAssignmentID := compiler.AddBlueprint(getAssignment) diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index a725a52f5f..9250bbf7d5 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -318,7 +318,6 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree // BlueprintGetAssignment is a BN254-specific blueprint for retrieving wire assignments. type BlueprintGetAssignment struct { SolveBlueprintID constraint.BlueprintID - SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time lock sync.Mutex } @@ -406,7 +405,6 @@ func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compil // Create and register GetAssignment blueprint getAssignment := &BlueprintGetAssignment{ SolveBlueprintID: solveID, - SolveBlueprint: solve, } getAssignmentID := compiler.AddBlueprint(getAssignment) diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 73bce2984f..4b8c52e986 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -318,7 +318,6 @@ func (b *BlueprintProve) UpdateInstructionTree(inst constraint.Instruction, tree // BlueprintGetAssignment is a BW6_761-specific blueprint for retrieving wire assignments. type BlueprintGetAssignment struct { SolveBlueprintID constraint.BlueprintID - SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time lock sync.Mutex } @@ -406,7 +405,6 @@ func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compil // Create and register GetAssignment blueprint getAssignment := &BlueprintGetAssignment{ SolveBlueprintID: solveID, - SolveBlueprint: solve, } getAssignmentID := compiler.AddBlueprint(getAssignment) From 207b16f71d9f254ad5ac1d3a6b9cd5909ce90fa4 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 11 Feb 2026 12:31:18 -0600 Subject: [PATCH 158/251] refactor: stub API changes --- std/gkrapi/api.go | 4 + std/gkrapi/example_test.go | 19 +-- std/permutation/gkr-mimc/gkr-mimc.go | 76 ++++-------- .../poseidon2/gkr-poseidon2/gkr-poseidon2.go | 111 +----------------- 4 files changed, 38 insertions(+), 172 deletions(-) diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index d837528d2c..ae71a9ab15 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -34,7 +34,11 @@ func (api *API) NamedGate(gateName gkr.GateName, in ...gkr.Variable) gkr.Variabl return gkr.Variable(len(api.circuit) - 1) } +// Gate compiles and caches a gate function, then adds a wire using it. +// The gate is identified by its compiled bytecode, so gates with identical +// behavior (same operations and constants) are automatically deduplicated. func (api *API) Gate(gate gkr.GateFunction, in ...gkr.Variable) gkr.Variable { + // TODO: replace global registry with local bytecode-based cache if err := gkrgates.Register(gate, len(in)); err != nil { panic(err) } diff --git a/std/gkrapi/example_test.go b/std/gkrapi/example_test.go index 1de0222dcc..f3a90d4249 100644 --- a/std/gkrapi/example_test.go +++ b/std/gkrapi/example_test.go @@ -6,7 +6,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377" - "github.com/consensys/gnark/constraint/solver/gkrgates" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/gkrapi" "github.com/consensys/gnark/std/gkrapi/gkr" @@ -20,17 +19,6 @@ func Example() { // The implementation is based on the function DoubleAssign() of type G1Jac in gnark-crypto v0.17.0. // github.com/consensys/gnark-crypto/ecc/bls12-377 - // register the gates: Doing so is not needed here because - // the proof is being computed in the same session as the - // SNARK circuit being compiled. - // But in production applications it would be necessary. - - assertNoError(gkrgates.Register(squareGate, 1)) - assertNoError(gkrgates.Register(sGate, 4)) - assertNoError(gkrgates.Register(zGate, 4)) - assertNoError(gkrgates.Register(xGate, 2)) - assertNoError(gkrgates.Register(yGate, 4)) - const nbInstances = 2 // create instances assignment := exampleCircuit{ @@ -110,7 +98,7 @@ func (c *exampleCircuit) Define(api frontend.API) error { YOut := gkrApi.Gate(yGate, S, XOut, XX, YYYY) // 423 - 426 // have to duplicate X for it to be considered an output variable; this is an implementation detail and will be fixed in the future [https://github.com/Consensys/gnark/issues/1452] - XOut = gkrApi.NamedGate(gkr.Identity, XOut) + XOut = gkrApi.Gate(identityGate, XOut) gkrCircuit, err := gkrApi.Compile("MIMC") if err != nil { @@ -137,6 +125,11 @@ func (c *exampleCircuit) Define(api frontend.API) error { // custom gates +// identityGate x -> x +func identityGate(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { + return api.Add(input[0], 0) +} + // squareGate x -> x² func squareGate(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { return api.Mul(input[0], input[0]) diff --git a/std/permutation/gkr-mimc/gkr-mimc.go b/std/permutation/gkr-mimc/gkr-mimc.go index a9a09bd5f5..14270b8135 100644 --- a/std/permutation/gkr-mimc/gkr-mimc.go +++ b/std/permutation/gkr-mimc/gkr-mimc.go @@ -1,7 +1,6 @@ package gkr_mimc import ( - "errors" "fmt" "math/big" @@ -10,7 +9,6 @@ import ( bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/mimc" bn254 "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/mimc" - "github.com/consensys/gnark/constraint/solver/gkrgates" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/kvstore" "github.com/consensys/gnark/internal/utils" @@ -61,20 +59,32 @@ func NewCompressor(api frontend.API) (hash.Compressor, error) { y := in1 curve := utils.FieldToCurve(api.Compiler().Field()) - params, _, err := getParams(curve) // params is only used for its length + constants, deg, err := getConstants(curve) if err != nil { return nil, err } - if err = RegisterGates(curve); err != nil { - return nil, err + + // Select sBox functions based on degree + var lastLayerSBox, nonLastLayerSBox func(*big.Int) gkr.GateFunction + switch deg { + case 5: + lastLayerSBox = addPow5Add + nonLastLayerSBox = addPow5 + case 7: + lastLayerSBox = addPow7Add + nonLastLayerSBox = addPow7 + case 17: + lastLayerSBox = addPow17Add + nonLastLayerSBox = addPow17 + default: + return nil, fmt.Errorf("s-Box of degree %d not supported", deg) } - gateNamer := newGateNamer(curve) - for i := range len(params) - 1 { - y = gkrApi.NamedGate(gateNamer.round(i), in0, y) + for i := range len(constants) - 1 { + y = gkrApi.Gate(nonLastLayerSBox(&constants[i]), in0, y) } - y = gkrApi.NamedGate(gateNamer.round(len(params)-1), in0, y, in1) + y = gkrApi.Gate(lastLayerSBox(&constants[len(constants)-1]), in0, y, in1) gkrCircuit, err := gkrApi.Compile("POSEIDON2") if err != nil { @@ -93,47 +103,14 @@ func NewCompressor(api frontend.API) (hash.Compressor, error) { return res, nil } +// Deprecated: Gate registration now happens automatically via api.Gate(). func RegisterGates(curves ...ecc.ID) error { - if len(curves) == 0 { - return errors.New("expected at least one curve") - } - for _, curve := range curves { - constants, deg, err := getParams(curve) - if err != nil { - return err - } - gateNamer := newGateNamer(curve) - var lastLayerSBox, nonLastLayerSBox func(*big.Int) gkr.GateFunction - switch deg { - case 5: - lastLayerSBox = addPow5Add - nonLastLayerSBox = addPow5 - case 7: - lastLayerSBox = addPow7Add - nonLastLayerSBox = addPow7 - case 17: - lastLayerSBox = addPow17Add - nonLastLayerSBox = addPow17 - default: - return fmt.Errorf("s-Box of degree %d not supported", deg) - } - - for i := range len(constants) - 1 { - if err = gkrgates.Register(nonLastLayerSBox(&constants[i]), 2, gkrgates.WithName(gateNamer.round(i)), gkrgates.WithUnverifiedDegree(deg), gkrgates.WithCurves(curve)); err != nil { - return fmt.Errorf("failed to register keyed GKR gate for round %d of MiMC on curve %s: %w", i, curve, err) - } - } - - if err = gkrgates.Register(lastLayerSBox(&constants[len(constants)-1]), 3, gkrgates.WithName(gateNamer.round(len(constants)-1)), gkrgates.WithUnverifiedDegree(deg), gkrgates.WithCurves(curve)); err != nil { - return fmt.Errorf("failed to register keyed GKR gate for round %d of MiMC on curve %s: %w", len(constants)-1, curve, err) - } - } return nil } -// getParams returns the parameters for the MiMC encryption function for the given curve. +// getConstants returns the parameters for the MiMC encryption function for the given curve. // It also returns the degree of the s-Box -func getParams(curve ecc.ID) ([]big.Int, int, error) { +func getConstants(curve ecc.ID) ([]big.Int, int, error) { switch curve { case ecc.BN254: return bn254.GetConstants(), 5, nil @@ -148,15 +125,6 @@ func getParams(curve ecc.ID) ([]big.Int, int, error) { } } -type gateNamer string - -func newGateNamer(o fmt.Stringer) gateNamer { - return gateNamer("MiMC-" + o.String() + "-round-") -} -func (n gateNamer) round(i int) gkr.GateName { - return gkr.GateName(fmt.Sprintf("%s%d", string(n), i)) -} - func addPow5(key *big.Int) gkr.GateFunction { return func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { if len(in) != 2 { diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go index b8901c4e5d..164c850d36 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go @@ -1,19 +1,16 @@ package gkr_poseidon2 import ( - "errors" "fmt" - "github.com/consensys/gnark/constraint/solver/gkrgates" + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/kvstore" "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi" "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/consensys/gnark/std/hash" "github.com/consensys/gnark/std/permutation/poseidon2" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/frontend" ) // extKeyGate applies the external matrix mul, then adds the round key @@ -182,11 +179,6 @@ func defineCircuit(api frontend.API) (gkrCircuit *gkrapi.Circuit, in1, in2, out if err != nil { return } - gateNamer := newRoundGateNamer(&p, curve) - - if err = registerGates(&p, curve); err != nil { - return - } gkrApi, err := gkrapi.New(api) if err != nil { @@ -233,15 +225,14 @@ func defineCircuit(api frontend.API) (gkrCircuit *gkrapi.Circuit, in1, in2, out // apply external matrix multiplication and round key addition // round dependent due to the round key extKeySBox := func(round, varI int, a, b gkr.Variable) gkr.Variable { - return sBox(gkrApi.NamedGate(gateNamer.linear(varI, round), a, b)) + return sBox(gkrApi.Gate(extKeyGate(&p.RoundKeys[round][varI]), a, b)) } - // apply external matrix multiplication and round key addition + // apply internal matrix multiplication and round key addition // then apply the s-Box // for the second variable - // round independent due to the round key intKeySBox2 := func(round int, a, b gkr.Variable) gkr.Variable { - return sBox(gkrApi.NamedGate(gateNamer.linear(yI, round), a, b)) + return sBox(gkrApi.Gate(intKeyGate2(&p.RoundKeys[round][1]), a, b)) } // apply a full round @@ -289,99 +280,9 @@ func defineCircuit(api frontend.API) (gkrCircuit *gkrapi.Circuit, in1, in2, out return } -// RegisterGates registers the GKR gates corresponding to the given curves for the solver. +// Deprecated: Gate registration now happens automatically via api.Gate(). func RegisterGates(curves ...ecc.ID) error { - if len(curves) == 0 { - return errors.New("expected at least one curve") - } - for _, curve := range curves { - p, err := poseidon2.GetDefaultParameters(curve) - if err != nil { - return fmt.Errorf("failed to get default parameters for curve %s: %w", curve, err) - } - if err = registerGates(&p, curve); err != nil { - return fmt.Errorf("failed to register gates for curve %s: %w", curve, err) - } - } return nil } -func registerGates(p *poseidon2.Parameters, curve ecc.ID) error { - const ( - x = iota - y - ) - - gateNames := newRoundGateNamer(p, curve) - halfRf := p.NbFullRounds / 2 - - extKeySBox := func(round int, varIndex int) error { - return gkrgates.Register(extKeyGate(&p.RoundKeys[round][varIndex]), 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(varIndex, round)), gkrgates.WithCurves(curve)) - } - - intKeySBox2 := func(round int) error { - return gkrgates.Register(intKeyGate2(&p.RoundKeys[round][1]), 2, gkrgates.WithUnverifiedDegree(1), gkrgates.WithUnverifiedSolvableVar(0), gkrgates.WithName(gateNames.linear(y, round)), gkrgates.WithCurves(curve)) - } - - fullRound := func(i int) error { - if err := extKeySBox(i, x); err != nil { - return err - } - return extKeySBox(i, y) - } - - for round := range halfRf { - if err := fullRound(round); err != nil { - return err - } - } - - { // round = halfRf: first partial one - if err := extKeySBox(halfRf, x); err != nil { - return err - } - } - - for round := halfRf + 1; round < halfRf+p.NbPartialRounds; round++ { - if err := extKeySBox(round, x); err != nil { // for x1, intKeySBox is identical to extKeySBox - return err - } - } - - { - round := halfRf + p.NbPartialRounds - if err := extKeySBox(round, x); err != nil { - return err - } - if err := intKeySBox2(round); err != nil { - return err - } - } - - for round := halfRf + p.NbPartialRounds + 1; round < p.NbPartialRounds+p.NbFullRounds; round++ { - if err := fullRound(round); err != nil { - return err - } - } - - return nil -} - -type roundGateNamer string - -// newRoundGateNamer returns an object that returns standardized names for gates in the GKR circuit -func newRoundGateNamer(p *poseidon2.Parameters, curve ecc.ID) roundGateNamer { - return roundGateNamer(fmt.Sprintf("Poseidon2-%s[t=%d,rF=%d,rP=%d,d=%d]", curve.String(), p.Width, p.NbFullRounds, p.NbPartialRounds, p.DegreeSBox)) -} - -// linear is the name of a gate where a polynomial of total degree 1 is applied to the input -func (n roundGateNamer) linear(varIndex, round int) gkr.GateName { - return gkr.GateName(fmt.Sprintf("x%d-l-op-round=%d;%s", varIndex, round, n)) -} - -// integrated is the name of a gate where a polynomial of total degree 1 is applied to the input, followed by an S-box -func (n roundGateNamer) integrated(varIndex, round int) gkr.GateName { - return gkr.GateName(fmt.Sprintf("x%d-i-op-round=%d;%s", varIndex, round, n)) -} - type gkrPoseidon2Key struct{} From c7629d677c5665777a1a6c6c723cc2c785d0fae8 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 11 Feb 2026 13:41:05 -0600 Subject: [PATCH 159/251] refactor: move gate registry to API --- constraint/solver/gkrgates/registry.go | 299 ------------------------- internal/gkr/bls12-381/gate_testing.go | 47 +--- internal/gkr/gkrtypes/compiledgate.go | 31 +++ internal/gkr/gkrtypes/types.go | 15 +- std/gkrapi/api.go | 16 +- std/gkrapi/api_test.go | 2 +- std/gkrapi/example_test.go | 2 +- std/gkrapi/gateregistry.go | 114 ++++++++++ 8 files changed, 165 insertions(+), 361 deletions(-) delete mode 100644 constraint/solver/gkrgates/registry.go create mode 100644 std/gkrapi/gateregistry.go diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go deleted file mode 100644 index c5c3b008fb..0000000000 --- a/constraint/solver/gkrgates/registry.go +++ /dev/null @@ -1,299 +0,0 @@ -// Package gkrgates contains the registry of GKR gates. -package gkrgates - -import ( - "errors" - "fmt" - "reflect" - "runtime" - "sync" - - "github.com/consensys/gnark" - "github.com/consensys/gnark-crypto/ecc" - - bls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" - bls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" - bn254 "github.com/consensys/gnark/internal/gkr/bn254" - bw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - - "github.com/consensys/gnark/std/gkrapi/gkr" -) - -var ( - gates = make(map[gkr.GateName]*gkrtypes.RegisteredGate) - gatesLock sync.Mutex -) - -type registerSettings struct { - solvableVar int - noSolvableVarVerification bool - noDegreeVerification bool - degree int - name gkr.GateName - curves []ecc.ID -} - -type RegisterOption func(*registerSettings) error - -// WithSolvableVar gives the index of a variable whose value can be uniquely determined from that of the other variables along with the gate's output. -// RegisterGate will return an error if it cannot verify that this claim is correct. -func WithSolvableVar(solvableVar int) RegisterOption { - return func(settings *registerSettings) error { - if settings.solvableVar != -1 { - return fmt.Errorf("solvable variable already set to %d", settings.solvableVar) - } - if settings.noSolvableVarVerification { - return errors.New("solvable variable already set to NONE") - } - settings.solvableVar = solvableVar - return nil - } -} - -// WithUnverifiedSolvableVar sets the index of a variable whose value can be uniquely determined from that of the other variables along with the gate's output. -// RegisterGate will not verify that the given index is correct. -func WithUnverifiedSolvableVar(solvableVar int) RegisterOption { - return func(settings *registerSettings) error { - if settings.solvableVar != -1 { - return fmt.Errorf("solvable variable already set to %d", settings.solvableVar) - } - if settings.noSolvableVarVerification { - return errors.New("solvable variable already set to NONE") - } - settings.noSolvableVarVerification = true - settings.solvableVar = solvableVar - return nil - } -} - -// WithNoSolvableVar sets the gate as having no variable whose value can be uniquely determined from that of the other variables along with the gate's output. -// RegisterGate will not check the correctness of this claim. -func WithNoSolvableVar() RegisterOption { - return func(settings *registerSettings) error { - if settings.solvableVar != -1 { - return fmt.Errorf("solvable variable already set to %d", settings.solvableVar) - } - if settings.noSolvableVarVerification { - return errors.New("solvable variable already set to NONE") - } - settings.solvableVar = -1 - settings.noSolvableVarVerification = true - return nil - } -} - -// WithUnverifiedDegree sets the degree of the gate. RegisterGate will not verify that the given degree is correct. -func WithUnverifiedDegree(degree int) RegisterOption { - return func(settings *registerSettings) error { - if settings.degree != -1 { - return fmt.Errorf("gate degree already set to %d", settings.degree) - } - settings.noDegreeVerification = true - settings.degree = degree - return nil - } -} - -// WithDegree sets the degree of the gate. RegisterGate will return an error if the degree is not correct. -func WithDegree(degree int) RegisterOption { - return func(settings *registerSettings) error { - if settings.degree != -1 { - return fmt.Errorf("gate degree already set to %d", settings.degree) - } - settings.degree = degree - return nil - } -} - -// WithName can be used to set a human-readable name for the gate. -func WithName(name gkr.GateName) RegisterOption { - return func(settings *registerSettings) error { - if name == "" { - return errors.New("gate name must not be empty") - } - if settings.name != "" { - return fmt.Errorf("gate name already set to \"%s\"", settings.name) - } - settings.name = name - return nil - } -} - -// WithCurves determines on which curves the gate is validated and allowed to be used. -// By default, the gate can be used on any curve, and is only validated on BN254. -// This works for most gates, unless the leading coefficient is divided by -// the curve's order, in which case the degree will be computed incorrectly. -func WithCurves(curves ...ecc.ID) RegisterOption { - return func(settings *registerSettings) error { - if settings.curves != nil { - return errors.New("gate curves already set") - } - settings.curves = curves - return nil - } -} - -// Register creates a gate object and stores it in the gates registry. -// - name is a human-readable name for the gate. -// - f is the polynomial function defining the gate. -// - nbIn is the number of inputs to the gate. -// -// If the gate is already registered, it will return false and no error. -func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { - s := registerSettings{degree: -1, solvableVar: -1} - for _, option := range options { - if err := option(&s); err != nil { - return err - } - } - if s.name == "" { - s.name = GetDefaultGateName(f) - } - - curvesForTesting := s.curves - allowedCurves := s.curves - if len(curvesForTesting) == 0 { - // no restriction on curves, but only test on BN254 - curvesForTesting = []ecc.ID{ecc.BN254} - allowedCurves = gnark.Curves() - } - - compiled, err := gkrtypes.CompileGateFunction(f, nbIn) - if err != nil { - return err - } - - gatesLock.Lock() - defer gatesLock.Unlock() - - if g, ok := gates[s.name]; ok { - // gate already registered - if g.NbIn != nbIn { - return fmt.Errorf("gate \"%s\" already registered with a different number of inputs (%d != %d)", s.name, g.NbIn, nbIn) - } - - for _, curve := range curvesForTesting { - gateVer, err := newGateTester(g.Evaluate.Bytecode, g.NbIn, curve) - if err != nil { - return err - } - if !gateVer.Equal(compiled) { - return fmt.Errorf("mismatch with already registered gate \"%s\" (degree %d) over curve %s", s.name, g.Degree, curve) - } - } - - return nil // gate already registered - } - - for _, curve := range curvesForTesting { - t, err := newGateTester(compiled, nbIn, curve) - if err != nil { - return err - } - - if s.degree == -1 { // find a degree - if s.noDegreeVerification { - panic("invalid settings") - } - const maxAutoDegreeBound = 32 - if s.degree, err = t.FindDegree(maxAutoDegreeBound); err != nil { - return fmt.Errorf("for gate \"%s\": %v", s.name, err) - } - } else { - if !s.noDegreeVerification { // check that the given degree is correct - if err = t.VerifyDegree(s.degree); err != nil { - return fmt.Errorf("for gate \"%s\": %v", s.name, err) - } - } - } - - if s.solvableVar == -1 { - if !s.noSolvableVarVerification { // find a solvable variable - s.solvableVar = findSolvableVar(t, nbIn) - } - } else { - // solvable variable given - if !s.noSolvableVarVerification && !isVarSolvable(t, s.solvableVar, nbIn) { - return fmt.Errorf("cannot verify the solvability of variable %d in gate \"%s\"", s.solvableVar, s.name) - } - } - - } - - gates[s.name] = gkrtypes.NewGate(f, compiled, nbIn, s.degree, s.solvableVar, allowedCurves) - return nil -} - -// Get returns a registered gate of the given name. -// If not found, it will panic. -// Gates can be added to the registry through Register. -func Get(name gkr.GateName) *gkrtypes.RegisteredGate { - gatesLock.Lock() - defer gatesLock.Unlock() - if gate, ok := gates[name]; ok { - return gate - } - panic(fmt.Sprintf("gate \"%s\" not found", name)) -} - -type gateTester interface { - IsAdditive(varIndex int) bool - FindDegree(max int) (int, error) - VerifyDegree(claimedDegree int) error - Equal(other *gkrtypes.GateBytecode) bool -} - -func newGateTester(g *gkrtypes.GateBytecode, nbIn int, curve ecc.ID) (gateTester, error) { - - switch curve { - case ecc.BLS12_377: - return bls12377.NewGateTester(g, nbIn), nil - case ecc.BLS12_381: - return bls12381.NewGateTester(g, nbIn), nil - case ecc.BN254: - return bn254.NewGateTester(g, nbIn), nil - case ecc.BW6_761: - return bw6761.NewGateTester(g, nbIn), nil - } - return nil, fmt.Errorf("unsupported curve %s", curve) -} - -// GetDefaultGateName provides a standardized name for a gate function, depending on its package and name. -// NB: For anonymous functions, the name is the same no matter the implicit arguments provided. -func GetDefaultGateName(fn gkr.GateFunction) gkr.GateName { - fnptr := reflect.ValueOf(fn).Pointer() - return gkr.GateName(runtime.FuncForPC(fnptr).Name()) -} - -// findSolvableVar returns the index of a variable whose value can be uniquely determined from that of the other variables along with the gate's output. -// It returns -1 if it fails to find one. -// nbIn is the number of inputs to the gate -func findSolvableVar(t gateTester, nbIn int) int { - for i := range nbIn { - if t.IsAdditive(i) { - return i - } - } - return -1 -} - -// isVarSolvable returns whether claimedSolvableVar is a variable whose value can be uniquely determined from that of the other variables along with the gate's output. -// It returns false if it fails to verify this claim. -// nbIn is the number of inputs to the gate. -func isVarSolvable(t gateTester, claimedSolvableVar, nbIn int) bool { - return t.IsAdditive(claimedSolvableVar) -} - -func init() { - // register some basic gates - gatesLock.Lock() - - gates[gkr.Identity] = gkrtypes.Identity() - gates[gkr.Add2] = gkrtypes.Add2() - gates[gkr.Sub2] = gkrtypes.Sub2() - gates[gkr.Neg] = gkrtypes.Neg() - gates[gkr.Mul2] = gkrtypes.Mul2() - - gatesLock.Unlock() -} diff --git a/internal/gkr/bls12-381/gate_testing.go b/internal/gkr/bls12-381/gate_testing.go index ebc9fc4fb0..cc54c91e8c 100644 --- a/internal/gkr/bls12-381/gate_testing.go +++ b/internal/gkr/bls12-381/gate_testing.go @@ -6,8 +6,6 @@ package gkr import ( - "fmt" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" @@ -23,11 +21,9 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrtypes.GateBytecode, nbIn int) *GateTester { - return &GateTester{ - evaluator: newGateEvaluator(g, nbIn), - nbIn: nbIn, - } +func (t *GateTester) SetGate(g *gkrtypes.GateBytecode, nbIn int) { + t.evaluator = newGateEvaluator(g, nbIn) + t.nbIn = nbIn } // IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e @@ -126,37 +122,8 @@ func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { } // FindDegree returns the degree of the gate function, or -1 if it fails. -// Failure could be due to the degree being higher than max or the function not being a polynomial at all. -func (t *GateTester) FindDegree(max int) (int, error) { - bound := uint64(max) + 1 - for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := t.fitPoly(degreeBound); p != nil { - if len(p) == 0 { - return -1, gkrtypes.ErrZeroFunction - } - return len(p) - 1, nil - } - } - return -1, fmt.Errorf("could not find a degree: tried up to %d", max) -} - -func (t *GateTester) VerifyDegree(claimedDegree int) error { - if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { - return fmt.Errorf("detected a higher degree than %d", claimedDegree) - } else if len(p) == 0 { - return gkrtypes.ErrZeroFunction - } else if len(p)-1 != claimedDegree { - return fmt.Errorf("detected degree %d, claimed %d", len(p)-1, claimedDegree) - } - return nil -} - -// Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { - x := make(fr.Vector, t.nbIn) - x.MustSetRandom() - fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(g, t.nbIn) - gAt := gEval.evaluate(x...) - return fAt.Equal(gAt) +// Failure could be due to the function not being a polynomial at all, or being constantly zero. +func (t *GateTester) FindDegree() int { + p := t.fitPoly(ecc.NextPowerOfTwo(uint64(t.evaluator.gate.EstimateDegree(t.nbIn)) + 1)) + return len(p) - 1 } diff --git a/internal/gkr/gkrtypes/compiledgate.go b/internal/gkr/gkrtypes/compiledgate.go index 24ad63cdd8..d2107e83cb 100644 --- a/internal/gkr/gkrtypes/compiledgate.go +++ b/internal/gkr/gkrtypes/compiledgate.go @@ -46,6 +46,37 @@ func (g *GateBytecode) NbConstants() int { return len(g.Constants) } +// EstimateDegree returns an upper bound on the degree of the gate +func (g *GateBytecode) EstimateDegree(nbIn int) int { + frameSize := len(g.Constants) + nbIn + deg := make([]int, frameSize+len(g.Instructions)) + for i := range nbIn { + deg[i+len(g.Constants)] = 1 + } + for i, inst := range g.Instructions { + var curr int + switch inst.Op { + case OpAdd | OpSub | OpNeg | OpSumExp17: + for _, in := range inst.Inputs { + curr = max(curr, deg[in]) + } + case OpMul: + for _, in := range inst.Inputs { + curr += deg[in] + } + case OpMulAcc: + curr = max(deg[inst.Inputs[0]], deg[inst.Inputs[1]]) + deg[inst.Inputs[2]] + default: + panic("unknown operation") + } + if inst.Op == OpSumExp17 { + curr *= 17 + } + deg[frameSize+i] = curr + } + return deg[len(deg)-1] +} + // String returns a human-readable representation of the operation func (op GateOp) String() string { switch op { diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 1eb353ad01..354f62027a 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -2,7 +2,6 @@ package gkrtypes import ( "errors" - "slices" "github.com/consensys/gnark" "github.com/consensys/gnark-crypto/ecc" @@ -20,10 +19,9 @@ type ( // A Gate is a low-degree multivariate polynomial Gate[GateExecutable any] struct { Evaluate GateExecutable - NbIn int // number of inputs - Degree int // total Degree of the polynomial - SolvableVar int // if there is a variable whose value can be uniquely determined from the value of the gate and the other inputs, its index, -1 otherwise - Curves []ecc.ID // Curves that the gate is allowed to be used over + NbIn int // number of inputs + Degree int // total Degree of the polynomial + SolvableVar int // if there is a variable whose value can be uniquely determined from the value of the gate and the other inputs, its index, -1 otherwise } Wire[GateExecutable any] struct { @@ -79,7 +77,6 @@ func NewGate(f gkr.GateFunction, compiled *GateBytecode, nbIn int, degree int, s NbIn: nbIn, Degree: degree, SolvableVar: solvableVar, - Curves: curves, } } @@ -87,11 +84,6 @@ func (be BothExecutables) getByteCode() *GateBytecode { return be.Bytecode } -// SupportsCurve returns whether the gate can be used over the given curve. -func (g *Gate[GateExecutable]) SupportsCurve(curve ecc.ID) bool { - return slices.Contains(g.Curves, curve) -} - // IsInput returns whether the wire is an input wire. func (w Wire[GateExecutable]) IsInput() bool { return len(w.Inputs) == 0 @@ -396,7 +388,6 @@ func ConvertGate[GateExecutable, TargetGateExecutable any](g *Gate[GateExecutabl NbIn: g.NbIn, Degree: g.Degree, SolvableVar: g.SolvableVar, - Curves: g.Curves, } } diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index ae71a9ab15..fda3f094e1 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -13,6 +13,7 @@ type API struct { circuit gkrtypes.RegisteredCircuit assignments gadget.WireAssignment parentApi frontend.API + gates map[string]gkr.GateFunction } func frontendVarToInt(a gkr.Variable) int { @@ -34,15 +35,14 @@ func (api *API) NamedGate(gateName gkr.GateName, in ...gkr.Variable) gkr.Variabl return gkr.Variable(len(api.circuit) - 1) } -// Gate compiles and caches a gate function, then adds a wire using it. -// The gate is identified by its compiled bytecode, so gates with identical -// behavior (same operations and constants) are automatically deduplicated. -func (api *API) Gate(gate gkr.GateFunction, in ...gkr.Variable) gkr.Variable { - // TODO: replace global registry with local bytecode-based cache - if err := gkrgates.Register(gate, len(in)); err != nil { +// Gate adds the given gate with the given inputs and returns its output wire. +func (api *API) Gate(gate gkr.GateFunction, inputs ...gkr.Variable) gkr.Variable { + + // TODO: replace global registry with local bytecode-based ids + if err := gkrgates.Register(gate, len(inputs)); err != nil { panic(err) } - return api.NamedGate(gkrgates.GetDefaultGateName(gate), in...) + return api.NamedGate(gkrgates.GetDefaultGateName(gate), inputs...) } func (api *API) namedGate2PlusIn(gate gkr.GateName, in1, in2 gkr.Variable, in ...gkr.Variable) gkr.Variable { @@ -69,4 +69,4 @@ func (api *API) Sub(i1, i2 gkr.Variable) gkr.Variable { func (api *API) Mul(i1, i2 gkr.Variable) gkr.Variable { return api.namedGate2PlusIn(gkr.Mul2, i1, i2) -} +} \ No newline at end of file diff --git a/std/gkrapi/api_test.go b/std/gkrapi/api_test.go index 66624be498..e53a66ecd7 100644 --- a/std/gkrapi/api_test.go +++ b/std/gkrapi/api_test.go @@ -597,7 +597,7 @@ func newConstBytesPseudoHash(c int64, mod *big.Int) constBytesPseudoHash { } func init() { - // register custom (constant) "hash" functions + // getID custom (constant) "hash" functions for _, v := range []int64{-1, -20} { name := strconv.Itoa(int(v)) stdHash.RegisterCustomHash(name, func(api frontend.API) (stdHash.FieldHasher, error) { diff --git a/std/gkrapi/example_test.go b/std/gkrapi/example_test.go index f3a90d4249..9122572feb 100644 --- a/std/gkrapi/example_test.go +++ b/std/gkrapi/example_test.go @@ -9,7 +9,7 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/gkrapi" "github.com/consensys/gnark/std/gkrapi/gkr" - _ "github.com/consensys/gnark/std/hash/all" // import all hash functions to register them + _ "github.com/consensys/gnark/std/hash/all" // import all hash functions to getID them "github.com/consensys/gnark/test" ) diff --git a/std/gkrapi/gateregistry.go b/std/gkrapi/gateregistry.go new file mode 100644 index 0000000000..552496cda7 --- /dev/null +++ b/std/gkrapi/gateregistry.go @@ -0,0 +1,114 @@ +package gkrapi + +import ( + "fmt" + "reflect" + "slices" + + "github.com/consensys/gnark-crypto/ecc" + bls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" + bls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" + bn254 "github.com/consensys/gnark/internal/gkr/bn254" + bw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" + "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/std/gkrapi/gkr" +) + +type ( + gateID uint16 + gateRegistry struct { + ids map[uintptr][]gateID // ids maps a gate function to gates defined using it. + gates []*gkrtypes.RegisteredGate + tester gateTester + } +) + +const ( + idIdentity gateID = iota + idAdd2 + idSub2 + idNeg + idMul2 +) + +var builtinGates = [...]*gkrtypes.RegisteredGate{ + idIdentity: gkrtypes.Identity(), + idAdd2: gkrtypes.Add2(), + idSub2: gkrtypes.Sub2(), + idNeg: gkrtypes.Neg(), + idMul2: gkrtypes.Mul2(), +} + +func newGateRegistry(curve ecc.ID) gateRegistry { + res := gateRegistry{ + ids: make(map[uintptr][]gateID), + gates: slices.Clone(builtinGates[:]), + } + + switch curve { + case ecc.BLS12_377: + res.tester = &bls12377.GateTester{} + case ecc.BLS12_381: + res.tester = &bls12381.GateTester{} + case ecc.BN254: + res.tester = &bn254.GateTester{} + case ecc.BW6_761: + res.tester = &bw6761.GateTester{} + default: + panic(fmt.Errorf("unsupported curve %s", curve)) + } + + return res +} + +// getID looks up f in the cache, adding it if necessary. +// Gate ID is returned. +func (r *gateRegistry) getID(f gkr.GateFunction, nbIn int) gateID { + bytecode, err := gkrtypes.CompileGateFunction(f, nbIn) + if err != nil { + panic(err) + } + + ptr := reflect.ValueOf(f).Pointer() + + for _, id := range r.ids[ptr] { + if reflect.DeepEqual(r.gates[id].Evaluate.Bytecode, bytecode) { + return id + } + } + + r.tester.SetGate(bytecode, nbIn) + + g := gkrtypes.RegisteredGate{ + Evaluate: gkrtypes.BothExecutables{}, + NbIn: nbIn, + Degree: r.tester.FindDegree(), + SolvableVar: -1, + } + + for i := range nbIn { + if r.tester.IsAdditive(i) { + g.SolvableVar = i + break + } + } + + if g.Degree == -1 { + panic("cannot find degree for gate") + } + if g.SolvableVar == -1 { + panic("cannot find solvable variable for gate") + } + + id := gateID(len(r.gates)) + r.gates = append(r.gates) + r.ids[ptr] = append(r.ids[ptr], id) + + return id +} + +type gateTester interface { + IsAdditive(varIndex int) bool + FindDegree() int + SetGate(g *gkrtypes.GateBytecode, nbIn int) +} From f77393e29a07cd5ebab35ced74d3b9adac9bb98e Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 11 Feb 2026 13:49:11 -0600 Subject: [PATCH 160/251] fix minor bugs --- std/gkrapi/gateregistry.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/std/gkrapi/gateregistry.go b/std/gkrapi/gateregistry.go index 552496cda7..b23189e2d0 100644 --- a/std/gkrapi/gateregistry.go +++ b/std/gkrapi/gateregistry.go @@ -80,7 +80,10 @@ func (r *gateRegistry) getID(f gkr.GateFunction, nbIn int) gateID { r.tester.SetGate(bytecode, nbIn) g := gkrtypes.RegisteredGate{ - Evaluate: gkrtypes.BothExecutables{}, + Evaluate: gkrtypes.BothExecutables{ + Bytecode: bytecode, + SnarkFriendly: f, + }, NbIn: nbIn, Degree: r.tester.FindDegree(), SolvableVar: -1, @@ -101,7 +104,7 @@ func (r *gateRegistry) getID(f gkr.GateFunction, nbIn int) gateID { } id := gateID(len(r.gates)) - r.gates = append(r.gates) + r.gates = append(r.gates, &g) r.ids[ptr] = append(r.ids[ptr], id) return id From 2154298fbd44d66072fe157e35d18dfa58b727a3 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 11 Feb 2026 13:52:18 -0600 Subject: [PATCH 161/251] build: generify gate testing changes --- .../backend/template/gkr/gate_testing.go.tmpl | 48 ++++--------------- internal/gkr/bls12-377/gate_testing.go | 47 +++--------------- internal/gkr/bls12-381/gate_testing.go | 2 +- internal/gkr/bn254/gate_testing.go | 47 +++--------------- internal/gkr/bw6-761/gate_testing.go | 47 +++--------------- std/gkrapi/api.go | 2 +- 6 files changed, 31 insertions(+), 162 deletions(-) diff --git a/internal/generator/backend/template/gkr/gate_testing.go.tmpl b/internal/generator/backend/template/gkr/gate_testing.go.tmpl index 1ab6668d6b..db231d8285 100644 --- a/internal/generator/backend/template/gkr/gate_testing.go.tmpl +++ b/internal/generator/backend/template/gkr/gate_testing.go.tmpl @@ -1,5 +1,4 @@ import ( - "fmt" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" @@ -16,11 +15,9 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrtypes.GateBytecode, nbIn int) *GateTester { - return &GateTester{ - evaluator: newGateEvaluator(g, nbIn), - nbIn: nbIn, - } +func (t *GateTester) SetGate(g *gkrtypes.GateBytecode, nbIn int) { + t.evaluator = newGateEvaluator(g, nbIn) + t.nbIn = nbIn } // IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e @@ -137,39 +134,10 @@ func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { } // FindDegree returns the degree of the gate function, or -1 if it fails. -// Failure could be due to the degree being higher than max or the function not being a polynomial at all. -func (t *GateTester) FindDegree(max int) (int, error) { - bound := uint64(max) + 1 - for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := t.fitPoly(degreeBound); p != nil { - if len(p) == 0 { - return -1, gkrtypes.ErrZeroFunction - } - return len(p) - 1, nil - } - } - return -1, fmt.Errorf("could not find a degree: tried up to %d", max) -} - -func (t *GateTester) VerifyDegree(claimedDegree int) error { - if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree)+1)); p == nil { - return fmt.Errorf("detected a higher degree than %d", claimedDegree) - } else if len(p) == 0 { - return gkrtypes.ErrZeroFunction - } else if len(p)-1 != claimedDegree { - return fmt.Errorf("detected degree %d, claimed %d", len(p)-1, claimedDegree) - } - return nil -} - -// Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { - x := make({{.FieldPackageName}}.Vector, t.nbIn) - x.MustSetRandom() - fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(g, t.nbIn) - gAt := gEval.evaluate(x...) - return fAt.Equal(gAt) +// Failure can be due to the function not being a polynomial at all, or being constantly zero. +func (t *GateTester) FindDegree() int { + p := t.fitPoly(ecc.NextPowerOfTwo(uint64(t.evaluator.gate.EstimateDegree(t.nbIn)) + 1)) + return len(p) - 1 } {{- if not .CanUseFFT }} @@ -227,4 +195,4 @@ func interpolate(X, Y []{{.ElementType}}) (polynomial.Polynomial, error) { return res, nil } -{{- end }} \ No newline at end of file +{{- end }} diff --git a/internal/gkr/bls12-377/gate_testing.go b/internal/gkr/bls12-377/gate_testing.go index 44252c1fa5..0e4f18bcda 100644 --- a/internal/gkr/bls12-377/gate_testing.go +++ b/internal/gkr/bls12-377/gate_testing.go @@ -6,8 +6,6 @@ package gkr import ( - "fmt" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" @@ -23,11 +21,9 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrtypes.GateBytecode, nbIn int) *GateTester { - return &GateTester{ - evaluator: newGateEvaluator(g, nbIn), - nbIn: nbIn, - } +func (t *GateTester) SetGate(g *gkrtypes.GateBytecode, nbIn int) { + t.evaluator = newGateEvaluator(g, nbIn) + t.nbIn = nbIn } // IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e @@ -126,37 +122,8 @@ func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { } // FindDegree returns the degree of the gate function, or -1 if it fails. -// Failure could be due to the degree being higher than max or the function not being a polynomial at all. -func (t *GateTester) FindDegree(max int) (int, error) { - bound := uint64(max) + 1 - for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := t.fitPoly(degreeBound); p != nil { - if len(p) == 0 { - return -1, gkrtypes.ErrZeroFunction - } - return len(p) - 1, nil - } - } - return -1, fmt.Errorf("could not find a degree: tried up to %d", max) -} - -func (t *GateTester) VerifyDegree(claimedDegree int) error { - if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { - return fmt.Errorf("detected a higher degree than %d", claimedDegree) - } else if len(p) == 0 { - return gkrtypes.ErrZeroFunction - } else if len(p)-1 != claimedDegree { - return fmt.Errorf("detected degree %d, claimed %d", len(p)-1, claimedDegree) - } - return nil -} - -// Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { - x := make(fr.Vector, t.nbIn) - x.MustSetRandom() - fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(g, t.nbIn) - gAt := gEval.evaluate(x...) - return fAt.Equal(gAt) +// Failure can be due to the function not being a polynomial at all, or being constantly zero. +func (t *GateTester) FindDegree() int { + p := t.fitPoly(ecc.NextPowerOfTwo(uint64(t.evaluator.gate.EstimateDegree(t.nbIn)) + 1)) + return len(p) - 1 } diff --git a/internal/gkr/bls12-381/gate_testing.go b/internal/gkr/bls12-381/gate_testing.go index cc54c91e8c..6ab08e3e4d 100644 --- a/internal/gkr/bls12-381/gate_testing.go +++ b/internal/gkr/bls12-381/gate_testing.go @@ -122,7 +122,7 @@ func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { } // FindDegree returns the degree of the gate function, or -1 if it fails. -// Failure could be due to the function not being a polynomial at all, or being constantly zero. +// Failure can be due to the function not being a polynomial at all, or being constantly zero. func (t *GateTester) FindDegree() int { p := t.fitPoly(ecc.NextPowerOfTwo(uint64(t.evaluator.gate.EstimateDegree(t.nbIn)) + 1)) return len(p) - 1 diff --git a/internal/gkr/bn254/gate_testing.go b/internal/gkr/bn254/gate_testing.go index fd18ef164c..0a1714b345 100644 --- a/internal/gkr/bn254/gate_testing.go +++ b/internal/gkr/bn254/gate_testing.go @@ -6,8 +6,6 @@ package gkr import ( - "fmt" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" @@ -23,11 +21,9 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrtypes.GateBytecode, nbIn int) *GateTester { - return &GateTester{ - evaluator: newGateEvaluator(g, nbIn), - nbIn: nbIn, - } +func (t *GateTester) SetGate(g *gkrtypes.GateBytecode, nbIn int) { + t.evaluator = newGateEvaluator(g, nbIn) + t.nbIn = nbIn } // IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e @@ -126,37 +122,8 @@ func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { } // FindDegree returns the degree of the gate function, or -1 if it fails. -// Failure could be due to the degree being higher than max or the function not being a polynomial at all. -func (t *GateTester) FindDegree(max int) (int, error) { - bound := uint64(max) + 1 - for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := t.fitPoly(degreeBound); p != nil { - if len(p) == 0 { - return -1, gkrtypes.ErrZeroFunction - } - return len(p) - 1, nil - } - } - return -1, fmt.Errorf("could not find a degree: tried up to %d", max) -} - -func (t *GateTester) VerifyDegree(claimedDegree int) error { - if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { - return fmt.Errorf("detected a higher degree than %d", claimedDegree) - } else if len(p) == 0 { - return gkrtypes.ErrZeroFunction - } else if len(p)-1 != claimedDegree { - return fmt.Errorf("detected degree %d, claimed %d", len(p)-1, claimedDegree) - } - return nil -} - -// Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { - x := make(fr.Vector, t.nbIn) - x.MustSetRandom() - fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(g, t.nbIn) - gAt := gEval.evaluate(x...) - return fAt.Equal(gAt) +// Failure can be due to the function not being a polynomial at all, or being constantly zero. +func (t *GateTester) FindDegree() int { + p := t.fitPoly(ecc.NextPowerOfTwo(uint64(t.evaluator.gate.EstimateDegree(t.nbIn)) + 1)) + return len(p) - 1 } diff --git a/internal/gkr/bw6-761/gate_testing.go b/internal/gkr/bw6-761/gate_testing.go index b86ac991ed..6b933d41e5 100644 --- a/internal/gkr/bw6-761/gate_testing.go +++ b/internal/gkr/bw6-761/gate_testing.go @@ -6,8 +6,6 @@ package gkr import ( - "fmt" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/gkr/gkrtypes" @@ -23,11 +21,9 @@ type GateTester struct { nbIn int } -func NewGateTester(g *gkrtypes.GateBytecode, nbIn int) *GateTester { - return &GateTester{ - evaluator: newGateEvaluator(g, nbIn), - nbIn: nbIn, - } +func (t *GateTester) SetGate(g *gkrtypes.GateBytecode, nbIn int) { + t.evaluator = newGateEvaluator(g, nbIn) + t.nbIn = nbIn } // IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e @@ -126,37 +122,8 @@ func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { } // FindDegree returns the degree of the gate function, or -1 if it fails. -// Failure could be due to the degree being higher than max or the function not being a polynomial at all. -func (t *GateTester) FindDegree(max int) (int, error) { - bound := uint64(max) + 1 - for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := t.fitPoly(degreeBound); p != nil { - if len(p) == 0 { - return -1, gkrtypes.ErrZeroFunction - } - return len(p) - 1, nil - } - } - return -1, fmt.Errorf("could not find a degree: tried up to %d", max) -} - -func (t *GateTester) VerifyDegree(claimedDegree int) error { - if p := t.fitPoly(ecc.NextPowerOfTwo(uint64(claimedDegree) + 1)); p == nil { - return fmt.Errorf("detected a higher degree than %d", claimedDegree) - } else if len(p) == 0 { - return gkrtypes.ErrZeroFunction - } else if len(p)-1 != claimedDegree { - return fmt.Errorf("detected degree %d, claimed %d", len(p)-1, claimedDegree) - } - return nil -} - -// Equal checks if two gate functions are equal, by testing the same at a random point. -func (t *GateTester) Equal(g *gkrtypes.GateBytecode) bool { - x := make(fr.Vector, t.nbIn) - x.MustSetRandom() - fAt := t.evaluator.evaluate(x...) - gEval := newGateEvaluator(g, t.nbIn) - gAt := gEval.evaluate(x...) - return fAt.Equal(gAt) +// Failure can be due to the function not being a polynomial at all, or being constantly zero. +func (t *GateTester) FindDegree() int { + p := t.fitPoly(ecc.NextPowerOfTwo(uint64(t.evaluator.gate.EstimateDegree(t.nbIn)) + 1)) + return len(p) - 1 } diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index fda3f094e1..891d0a686c 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -69,4 +69,4 @@ func (api *API) Sub(i1, i2 gkr.Variable) gkr.Variable { func (api *API) Mul(i1, i2 gkr.Variable) gkr.Variable { return api.namedGate2PlusIn(gkr.Mul2, i1, i2) -} \ No newline at end of file +} From 7bc6d17dc0770218dfc3d969c7debb89c2c2b4f5 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 11 Feb 2026 14:11:45 -0600 Subject: [PATCH 162/251] fix static degree detection bug --- constraint/babybear/solver.go | 2 +- constraint/bls12-377/solver.go | 2 +- constraint/bls12-381/solver.go | 2 +- constraint/bn254/solver.go | 2 +- constraint/bw6-761/solver.go | 2 +- constraint/koalabear/solver.go | 2 +- constraint/tinyfield/solver.go | 2 +- .../backend/template/representations/solver.go.tmpl | 2 +- internal/gkr/gkrtypes/compiledgate.go | 6 +++--- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/constraint/babybear/solver.go b/constraint/babybear/solver.go index b2d5159fd4..f32953e511 100644 --- a/constraint/babybear/solver.go +++ b/constraint/babybear/solver.go @@ -409,7 +409,7 @@ func (s *solver) processInstruction(pi constraint.PackedInstruction, scratch *sc return nil } -// run runs the solver. it return an error if a constraint is not satisfied or if not all wires +// run runs the solver. it returns an error if a constraint is not satisfied or if not all wires // were instantiated. func (s *solver) run() error { // minWorkPerCPU is the minimum target number of constraint a task should hold diff --git a/constraint/bls12-377/solver.go b/constraint/bls12-377/solver.go index 3a129b659d..d714a129e0 100644 --- a/constraint/bls12-377/solver.go +++ b/constraint/bls12-377/solver.go @@ -409,7 +409,7 @@ func (s *solver) processInstruction(pi constraint.PackedInstruction, scratch *sc return nil } -// run runs the solver. it return an error if a constraint is not satisfied or if not all wires +// run runs the solver. it returns an error if a constraint is not satisfied or if not all wires // were instantiated. func (s *solver) run() error { // minWorkPerCPU is the minimum target number of constraint a task should hold diff --git a/constraint/bls12-381/solver.go b/constraint/bls12-381/solver.go index 132d7d88c8..63835dab4b 100644 --- a/constraint/bls12-381/solver.go +++ b/constraint/bls12-381/solver.go @@ -409,7 +409,7 @@ func (s *solver) processInstruction(pi constraint.PackedInstruction, scratch *sc return nil } -// run runs the solver. it return an error if a constraint is not satisfied or if not all wires +// run runs the solver. it returns an error if a constraint is not satisfied or if not all wires // were instantiated. func (s *solver) run() error { // minWorkPerCPU is the minimum target number of constraint a task should hold diff --git a/constraint/bn254/solver.go b/constraint/bn254/solver.go index 8c658f0939..33b66686e2 100644 --- a/constraint/bn254/solver.go +++ b/constraint/bn254/solver.go @@ -409,7 +409,7 @@ func (s *solver) processInstruction(pi constraint.PackedInstruction, scratch *sc return nil } -// run runs the solver. it return an error if a constraint is not satisfied or if not all wires +// run runs the solver. it returns an error if a constraint is not satisfied or if not all wires // were instantiated. func (s *solver) run() error { // minWorkPerCPU is the minimum target number of constraint a task should hold diff --git a/constraint/bw6-761/solver.go b/constraint/bw6-761/solver.go index 5f6d007638..e1df5f42b4 100644 --- a/constraint/bw6-761/solver.go +++ b/constraint/bw6-761/solver.go @@ -409,7 +409,7 @@ func (s *solver) processInstruction(pi constraint.PackedInstruction, scratch *sc return nil } -// run runs the solver. it return an error if a constraint is not satisfied or if not all wires +// run runs the solver. it returns an error if a constraint is not satisfied or if not all wires // were instantiated. func (s *solver) run() error { // minWorkPerCPU is the minimum target number of constraint a task should hold diff --git a/constraint/koalabear/solver.go b/constraint/koalabear/solver.go index dc44c9f198..d5e7942878 100644 --- a/constraint/koalabear/solver.go +++ b/constraint/koalabear/solver.go @@ -409,7 +409,7 @@ func (s *solver) processInstruction(pi constraint.PackedInstruction, scratch *sc return nil } -// run runs the solver. it return an error if a constraint is not satisfied or if not all wires +// run runs the solver. it returns an error if a constraint is not satisfied or if not all wires // were instantiated. func (s *solver) run() error { // minWorkPerCPU is the minimum target number of constraint a task should hold diff --git a/constraint/tinyfield/solver.go b/constraint/tinyfield/solver.go index a8c3127b0d..20444fde0b 100644 --- a/constraint/tinyfield/solver.go +++ b/constraint/tinyfield/solver.go @@ -409,7 +409,7 @@ func (s *solver) processInstruction(pi constraint.PackedInstruction, scratch *sc return nil } -// run runs the solver. it return an error if a constraint is not satisfied or if not all wires +// run runs the solver. it returns an error if a constraint is not satisfied or if not all wires // were instantiated. func (s *solver) run() error { // minWorkPerCPU is the minimum target number of constraint a task should hold diff --git a/internal/generator/backend/template/representations/solver.go.tmpl b/internal/generator/backend/template/representations/solver.go.tmpl index 93b7fdf871..455c3f0705 100644 --- a/internal/generator/backend/template/representations/solver.go.tmpl +++ b/internal/generator/backend/template/representations/solver.go.tmpl @@ -414,7 +414,7 @@ func (s *solver) processInstruction(pi constraint.PackedInstruction, scratch *sc } -// run runs the solver. it return an error if a constraint is not satisfied or if not all wires +// run runs the solver. it returns an error if a constraint is not satisfied or if not all wires // were instantiated. func (s *solver) run() error { // minWorkPerCPU is the minimum target number of constraint a task should hold diff --git a/internal/gkr/gkrtypes/compiledgate.go b/internal/gkr/gkrtypes/compiledgate.go index d2107e83cb..a4441f21ed 100644 --- a/internal/gkr/gkrtypes/compiledgate.go +++ b/internal/gkr/gkrtypes/compiledgate.go @@ -56,7 +56,7 @@ func (g *GateBytecode) EstimateDegree(nbIn int) int { for i, inst := range g.Instructions { var curr int switch inst.Op { - case OpAdd | OpSub | OpNeg | OpSumExp17: + case OpAdd, OpSub, OpNeg, OpSumExp17: for _, in := range inst.Inputs { curr = max(curr, deg[in]) } @@ -64,8 +64,8 @@ func (g *GateBytecode) EstimateDegree(nbIn int) int { for _, in := range inst.Inputs { curr += deg[in] } - case OpMulAcc: - curr = max(deg[inst.Inputs[0]], deg[inst.Inputs[1]]) + deg[inst.Inputs[2]] + case OpMulAcc: // a + b*c + curr = max(deg[inst.Inputs[0]], deg[inst.Inputs[1]]+deg[inst.Inputs[2]]) default: panic("unknown operation") } From f38d074d988ba0e90ad915831c1914b6794eceb2 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 11 Feb 2026 14:58:56 -0600 Subject: [PATCH 163/251] fix compilation --- .../backend/template/gkr/gkr.go.tmpl | 6 +-- .../backend/template/gkr/gkr.test.go.tmpl | 10 ++-- .../template/gkr/gkr.test.vectors.go.tmpl | 4 +- internal/gkr/bls12-377/gkr.go | 6 +-- internal/gkr/bls12-377/gkr_test.go | 14 +++--- internal/gkr/bls12-381/gkr.go | 6 +-- internal/gkr/bls12-381/gkr_test.go | 14 +++--- internal/gkr/bn254/gkr.go | 6 +-- internal/gkr/bn254/gkr_test.go | 14 +++--- internal/gkr/bw6-761/gkr.go | 6 +-- internal/gkr/bw6-761/gkr_test.go | 14 +++--- .../gkr/gkrtypes/topological_sort_test.go | 16 +++---- internal/gkr/gkrtypes/types.go | 38 ++++++++++++--- internal/gkr/small_rational/gkr.go | 6 +-- .../gkr/small_rational/test_vector_gen.go | 4 +- std/gkrapi/api.go | 42 ++++++----------- std/gkrapi/compile.go | 46 ++++++------------- std/gkrapi/gateregistry.go | 24 +++++++--- 18 files changed, 141 insertions(+), 135 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index f48acb843b..e7e84300bd 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -13,9 +13,9 @@ import ( // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.SerializableWire - Wires = gkrtypes.SerializableWires - Circuit = gkrtypes.SerializableCircuit + Wire = gkrtypes.ExecutableWire + Wires = gkrtypes.ExecutableWires + Circuit = gkrtypes.ExecutableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index 453e84a79f..a074d7a047 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -23,9 +23,9 @@ import ( ) var ( - identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) - add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) - mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) + identityGate = gkrtypes.ToExecutableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToExecutableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToExecutableGate(gkrtypes.Mul2()) ) func TestNoGateTwoInstances(t *testing.T) { @@ -136,7 +136,7 @@ func TestSingleMimcCipherGate(t *testing.T) { }, { Inputs: []int{0, 1}, - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), }, }) } @@ -292,7 +292,7 @@ func mimcCircuit(numRounds int) Circuit { for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl index 29b64252ca..3d06655ffe 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl @@ -32,7 +32,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.SerializableCircuit + Circuit gkrtypes.ExecutableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -62,7 +62,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := gkrtypes.ToExecutable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 6493a8499a..987e43c1af 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -21,9 +21,9 @@ import ( // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.SerializableWire - Wires = gkrtypes.SerializableWires - Circuit = gkrtypes.SerializableCircuit + Wire = gkrtypes.ExecutableWire + Wires = gkrtypes.ExecutableWires + Circuit = gkrtypes.ExecutableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index 8cd71627eb..ed8dc32361 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -29,9 +29,9 @@ import ( ) var ( - identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) - add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) - mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) + identityGate = gkrtypes.ToExecutableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToExecutableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToExecutableGate(gkrtypes.Mul2()) ) func TestNoGateTwoInstances(t *testing.T) { @@ -142,7 +142,7 @@ func TestSingleMimcCipherGate(t *testing.T) { }, { Inputs: []int{0, 1}, - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), }, }) } @@ -298,7 +298,7 @@ func mimcCircuit(numRounds int) Circuit { for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } @@ -474,7 +474,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.SerializableCircuit + Circuit gkrtypes.ExecutableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -503,7 +503,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := gkrtypes.ToExecutable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 39a4197e09..f336d620cd 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -21,9 +21,9 @@ import ( // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.SerializableWire - Wires = gkrtypes.SerializableWires - Circuit = gkrtypes.SerializableCircuit + Wire = gkrtypes.ExecutableWire + Wires = gkrtypes.ExecutableWires + Circuit = gkrtypes.ExecutableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 5f3e6041ce..a9b60efd28 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -29,9 +29,9 @@ import ( ) var ( - identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) - add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) - mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) + identityGate = gkrtypes.ToExecutableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToExecutableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToExecutableGate(gkrtypes.Mul2()) ) func TestNoGateTwoInstances(t *testing.T) { @@ -142,7 +142,7 @@ func TestSingleMimcCipherGate(t *testing.T) { }, { Inputs: []int{0, 1}, - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), }, }) } @@ -298,7 +298,7 @@ func mimcCircuit(numRounds int) Circuit { for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } @@ -474,7 +474,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.SerializableCircuit + Circuit gkrtypes.ExecutableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -503,7 +503,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := gkrtypes.ToExecutable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index a57ea2dbab..46d0c5f441 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -21,9 +21,9 @@ import ( // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.SerializableWire - Wires = gkrtypes.SerializableWires - Circuit = gkrtypes.SerializableCircuit + Wire = gkrtypes.ExecutableWire + Wires = gkrtypes.ExecutableWires + Circuit = gkrtypes.ExecutableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 448f545540..93cc84a362 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -29,9 +29,9 @@ import ( ) var ( - identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) - add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) - mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) + identityGate = gkrtypes.ToExecutableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToExecutableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToExecutableGate(gkrtypes.Mul2()) ) func TestNoGateTwoInstances(t *testing.T) { @@ -142,7 +142,7 @@ func TestSingleMimcCipherGate(t *testing.T) { }, { Inputs: []int{0, 1}, - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), }, }) } @@ -298,7 +298,7 @@ func mimcCircuit(numRounds int) Circuit { for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } @@ -474,7 +474,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.SerializableCircuit + Circuit gkrtypes.ExecutableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -503,7 +503,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := gkrtypes.ToExecutable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 41296ede56..d166a4a1db 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -21,9 +21,9 @@ import ( // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.SerializableWire - Wires = gkrtypes.SerializableWires - Circuit = gkrtypes.SerializableCircuit + Wire = gkrtypes.ExecutableWire + Wires = gkrtypes.ExecutableWires + Circuit = gkrtypes.ExecutableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index 175a72e66b..653e5045e8 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -29,9 +29,9 @@ import ( ) var ( - identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) - add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) - mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) + identityGate = gkrtypes.ToExecutableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToExecutableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToExecutableGate(gkrtypes.Mul2()) ) func TestNoGateTwoInstances(t *testing.T) { @@ -142,7 +142,7 @@ func TestSingleMimcCipherGate(t *testing.T) { }, { Inputs: []int{0, 1}, - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), }, }) } @@ -298,7 +298,7 @@ func mimcCircuit(numRounds int) Circuit { for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } @@ -474,7 +474,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.SerializableCircuit + Circuit gkrtypes.ExecutableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -503,7 +503,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := gkrtypes.ToExecutable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/gkrtypes/topological_sort_test.go b/internal/gkr/gkrtypes/topological_sort_test.go index 2726baea55..3a3e38d811 100644 --- a/internal/gkr/gkrtypes/topological_sort_test.go +++ b/internal/gkr/gkrtypes/topological_sort_test.go @@ -7,17 +7,17 @@ import ( ) func TestTopSortTrivial(t *testing.T) { - c := make(SerializableCircuit, 2) + c := make(ExecutableCircuit, 2) c[0].Inputs = []int{1} sorted := c.TopologicalSort() - assert.Equal(t, SerializableWires{&c[1], &c[0]}, sorted) + assert.Equal(t, ExecutableWires{&c[1], &c[0]}, sorted) } func TestTopSortSingleGate(t *testing.T) { - c := make(SerializableCircuit, 3) + c := make(ExecutableCircuit, 3) c[0].Inputs = []int{1, 2} sorted := c.TopologicalSort() - expected := SerializableWires{&c[1], &c[2], &c[0]} + expected := ExecutableWires{&c[1], &c[2], &c[0]} assert.Equal(t, expected, sorted) assert.Equal(t, c[0].NbUniqueOutputs, 0) @@ -26,17 +26,17 @@ func TestTopSortSingleGate(t *testing.T) { } func TestTopSortDeep(t *testing.T) { - c := make(SerializableCircuit, 4) + c := make(ExecutableCircuit, 4) c[0].Inputs = []int{2} c[1].Inputs = []int{3} c[2].Inputs = []int{} c[3].Inputs = []int{0} sorted := c.TopologicalSort() - assert.Equal(t, SerializableWires{&c[2], &c[0], &c[3], &c[1]}, sorted) + assert.Equal(t, ExecutableWires{&c[2], &c[0], &c[3], &c[1]}, sorted) } func TestTopSortWide(t *testing.T) { - c := make(SerializableCircuit, 10) + c := make(ExecutableCircuit, 10) c[0].Inputs = []int{3, 8} c[1].Inputs = []int{6} c[2].Inputs = []int{4} @@ -49,7 +49,7 @@ func TestTopSortWide(t *testing.T) { c[9].Inputs = []int{} sorted := c.TopologicalSort() - sortedExpected := SerializableWires{&c[3], &c[4], &c[2], &c[8], &c[0], &c[9], &c[5], &c[6], &c[1], &c[7]} + sortedExpected := ExecutableWires{&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) } diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 354f62027a..8af7c1d221 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -16,6 +16,13 @@ type ( InputInstance int } + GateID uint16 + SerializableWire struct { + Gate GateID + Inputs []int + } + SerializableCircuit []SerializableWire + // A Gate is a low-degree multivariate polynomial Gate[GateExecutable any] struct { Evaluate GateExecutable @@ -49,10 +56,10 @@ type ( RegisteredWire = Wire[BothExecutables] RegisteredWires = Wires[BothExecutables] - // Serializable types (bytecode only, for native proving) - SerializableCircuit = Circuit[*GateBytecode] - SerializableWire = Wire[*GateBytecode] - SerializableWires = Wires[*GateBytecode] + // Executable types (bytecode only, for native proving) + ExecutableCircuit = Circuit[*GateBytecode] + ExecutableWire = Wire[*GateBytecode] + ExecutableWires = Wires[*GateBytecode] // Gadget types (gate functions only, for in-circuit verification) GadgetCircuit = Circuit[gkr.GateFunction] @@ -80,6 +87,10 @@ func NewGate(f gkr.GateFunction, compiled *GateBytecode, nbIn int, degree int, s } } +func (be BothExecutables) getGateFunction() gkr.GateFunction { + return be.SnarkFriendly +} + func (be BothExecutables) getByteCode() *GateBytecode { return be.Bytecode } @@ -404,18 +415,31 @@ func ConvertCircuit[GateExecutable, TargetGateExecutable any](c Circuit[GateExec return res } -// ToSerializable converts a registered circuit (with both executables) to a serializable circuit (bytecode only). -func ToSerializable(c RegisteredCircuit) SerializableCircuit { +// ToExecutable converts a registered circuit (with both executables) to an executable circuit (bytecode only). +func ToExecutable(c RegisteredCircuit) ExecutableCircuit { return ConvertCircuit(c, BothExecutables.getByteCode) } -func ToSerializableGate(g *RegisteredGate) *Gate[*GateBytecode] { +func ToExecutableGate(g *RegisteredGate) *Gate[*GateBytecode] { return ConvertGate(g, BothExecutables.getByteCode) } +func ToGadgetGate(g *RegisteredGate) *Gate[gkr.GateFunction] { + return ConvertGate(g, BothExecutables.getGateFunction) +} + // ToGadget converts a registered circuit (with both executables) to a gadget circuit (gate functions only, for in-circuit verification). func ToGadget(c RegisteredCircuit) GadgetCircuit { return ConvertCircuit(c, func(e BothExecutables) gkr.GateFunction { return e.SnarkFriendly }) } + +func (c SerializableCircuit) ToExecutable(gates []*Gate[*GateBytecode]) ExecutableCircuit { + res := make(ExecutableCircuit, len(c)) + for i := range c { + res[i].Inputs = c[i].Inputs + res[i].Gate = gates[c[i].Gate] + } + return res +} diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 9d1b62dfb6..57516ebce8 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -21,9 +21,9 @@ import ( // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.SerializableWire - Wires = gkrtypes.SerializableWires - Circuit = gkrtypes.SerializableCircuit + Wire = gkrtypes.ExecutableWire + Wires = gkrtypes.ExecutableWires + Circuit = gkrtypes.ExecutableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/small_rational/test_vector_gen.go b/internal/gkr/small_rational/test_vector_gen.go index 7fa9c4114d..38b771b9a5 100644 --- a/internal/gkr/small_rational/test_vector_gen.go +++ b/internal/gkr/small_rational/test_vector_gen.go @@ -186,7 +186,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.SerializableCircuit + Circuit gkrtypes.ExecutableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -216,7 +216,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := gkrtypes.ToExecutable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index 891d0a686c..77c1dde979 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -1,7 +1,6 @@ package gkrapi import ( - "github.com/consensys/gnark/constraint/solver/gkrgates" "github.com/consensys/gnark/frontend" gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" @@ -10,26 +9,20 @@ import ( ) type API struct { - circuit gkrtypes.RegisteredCircuit - assignments gadget.WireAssignment - parentApi frontend.API - gates map[string]gkr.GateFunction + circuit gkrtypes.SerializableCircuit + assignments gadget.WireAssignment + parentApi frontend.API + gateRegistry gateRegistry } func frontendVarToInt(a gkr.Variable) int { return int(a) } -func (api *API) NamedGate(gateName gkr.GateName, in ...gkr.Variable) gkr.Variable { - // Get the registered gate (with both executables) - registeredGate := gkrgates.Get(gateName) - if registeredGate == nil { - panic("gate not found: " + gateName) - } - - api.circuit = append(api.circuit, gkrtypes.RegisteredWire{ - Gate: registeredGate, - Inputs: utils.Map(in, frontendVarToInt), +func (api *API) gate(id gkrtypes.GateID, inputs ...gkr.Variable) gkr.Variable { + api.circuit = append(api.circuit, gkrtypes.SerializableWire{ + Gate: id, + Inputs: utils.Map(inputs, frontendVarToInt), }) api.assignments = append(api.assignments, nil) return gkr.Variable(len(api.circuit) - 1) @@ -37,36 +30,31 @@ func (api *API) NamedGate(gateName gkr.GateName, in ...gkr.Variable) gkr.Variabl // Gate adds the given gate with the given inputs and returns its output wire. func (api *API) Gate(gate gkr.GateFunction, inputs ...gkr.Variable) gkr.Variable { - - // TODO: replace global registry with local bytecode-based ids - if err := gkrgates.Register(gate, len(inputs)); err != nil { - panic(err) - } - return api.NamedGate(gkrgates.GetDefaultGateName(gate), inputs...) + return api.gate(api.gateRegistry.getID(gate, len(inputs)), inputs...) } -func (api *API) namedGate2PlusIn(gate gkr.GateName, in1, in2 gkr.Variable, in ...gkr.Variable) gkr.Variable { +func (api *API) gate2PlusIn(gate gkrtypes.GateID, in1, in2 gkr.Variable, in ...gkr.Variable) gkr.Variable { inCombined := make([]gkr.Variable, 2+len(in)) inCombined[0] = in1 inCombined[1] = in2 for i := range in { inCombined[i+2] = in[i] } - return api.NamedGate(gate, inCombined...) + return api.gate(gate, inCombined...) } func (api *API) Add(i1, i2 gkr.Variable) gkr.Variable { - return api.namedGate2PlusIn(gkr.Add2, i1, i2) + return api.gate2PlusIn(idAdd2, i1, i2) } func (api *API) Neg(i1 gkr.Variable) gkr.Variable { - return api.NamedGate("neg", i1) + return api.gate(idNeg, i1) } func (api *API) Sub(i1, i2 gkr.Variable) gkr.Variable { - return api.namedGate2PlusIn(gkr.Sub2, i1, i2) + return api.gate2PlusIn(idSub2, i1, i2) } func (api *API) Mul(i1, i2 gkr.Variable) gkr.Variable { - return api.namedGate2PlusIn(gkr.Mul2, i1, i2) + return api.gate2PlusIn(idMul2, i1, i2) } diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 4f4a2316de..95c55c6cde 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -25,7 +25,8 @@ type InitialChallengeGetter func() []frontend.Variable // Circuit represents a GKR circuit. type Circuit struct { - circuit gkrtypes.RegisteredCircuit + circuit gkrtypes.GadgetCircuit + gates []gkrtypes.GateBytecode assignments gadget.WireAssignment getInitialChallenges InitialChallengeGetter // optional getter for the initial Fiat-Shamir challenge ins []gkr.Variable @@ -43,19 +44,15 @@ type Circuit struct { // New creates a new GKR API func New(api frontend.API) (*API, error) { return &API{ - circuit: make(gkrtypes.RegisteredCircuit, 0), - parentApi: api, + parentApi: api, + gateRegistry: newGateRegistry(utils.FieldToCurve(api.Compiler().Field())), }, nil } // NewInput creates a new input variable. func (api *API) NewInput() gkr.Variable { i := len(api.circuit) - // Input wires have Identity gate with empty Inputs slice - api.circuit = append(api.circuit, gkrtypes.RegisteredWire{ - Gate: gkrtypes.Identity(), - Inputs: []int{}, - }) + api.circuit = append(api.circuit, gkrtypes.SerializableWire{}) api.assignments = append(api.assignments, nil) return gkr.Variable(i) } @@ -75,28 +72,25 @@ func WithInitialChallenge(getInitialChallenge InitialChallengeGetter) CompileOpt // but instances can be added to it. func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*Circuit, error) { res := Circuit{ - circuit: api.circuit, + circuit: api.gateRegistry.toGadgetCircuit(api.circuit), assignments: make(gadget.WireAssignment, len(api.circuit)), api: api.parentApi, hashName: fiatshamirHashName, } - // Convert registered circuit to serializable circuit (bytecode only) for blueprints - serializableCircuit := gkrtypes.ToSerializable(api.circuit) - // Dispatch to curve-specific factory curveID := utils.FieldToCurve(api.parentApi.Compiler().Field()) compiler := api.parentApi.Compiler() switch curveID { case ecc.BN254: - res.blueprints = gkrbn254.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) + res.blueprints = gkrbn254.NewBlueprints(api.circuit, fiatshamirHashName, compiler) case ecc.BLS12_377: - res.blueprints = gkrbls12377.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) + res.blueprints = gkrbls12377.NewBlueprints(api.circuit, fiatshamirHashName, compiler) case ecc.BLS12_381: - res.blueprints = gkrbls12381.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) + res.blueprints = gkrbls12381.NewBlueprints(api.circuit, fiatshamirHashName, compiler) case ecc.BW6_761: - res.blueprints = gkrbw6761.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) + res.blueprints = gkrbw6761.NewBlueprints(api.circuit, fiatshamirHashName, compiler) default: return nil, fmt.Errorf("unsupported curve: %s", curveID) } @@ -204,22 +198,10 @@ func (c *Circuit) finalize(api frontend.API) error { c.blueprints.Solve.SetNbInstances(uint32(c.nbInstances)) - curve := utils.FieldToCurve(api.Compiler().Field()) - - // Convert registered circuit to gadget circuit (using GateFunction) - gadgetCircuit := gkrtypes.ToGadget(c.circuit) - - // Check curve support - for i := range gadgetCircuit { - if !gadgetCircuit[i].Gate.SupportsCurve(curve) { - return fmt.Errorf("gate not usable over curve \"%s\"", curve) - } - } - // if the circuit consists of only one instance, directly solve the circuit if len(c.assignments[c.ins[0]]) == 1 { - gateIn := make([]frontend.Variable, gadgetCircuit.MaxGateNbIn()) - for wI, w := range gadgetCircuit { + gateIn := make([]frontend.Variable, c.circuit.MaxGateNbIn()) + for wI, w := range c.circuit { if w.IsInput() { continue } @@ -237,7 +219,7 @@ func (c *Circuit) finalize(api frontend.API) error { } if c.getInitialChallenges != nil { - return c.verify(api, gadgetCircuit, c.getInitialChallenges()) + return c.verify(api, c.circuit, c.getInitialChallenges()) } // default initial challenge is a commitment to all input and output values @@ -250,7 +232,7 @@ func (c *Circuit) finalize(api frontend.API) error { } multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { - return c.verify(api, gadgetCircuit, []frontend.Variable{commitment}) + return c.verify(api, c.circuit, []frontend.Variable{commitment}) }, insOuts...) return nil diff --git a/std/gkrapi/gateregistry.go b/std/gkrapi/gateregistry.go index b23189e2d0..f775263cc7 100644 --- a/std/gkrapi/gateregistry.go +++ b/std/gkrapi/gateregistry.go @@ -15,16 +15,15 @@ import ( ) type ( - gateID uint16 gateRegistry struct { - ids map[uintptr][]gateID // ids maps a gate function to gates defined using it. + ids map[uintptr][]gkrtypes.GateID // ids maps a gate function to gates defined using it. gates []*gkrtypes.RegisteredGate tester gateTester } ) const ( - idIdentity gateID = iota + idIdentity gkrtypes.GateID = iota idAdd2 idSub2 idNeg @@ -41,7 +40,7 @@ var builtinGates = [...]*gkrtypes.RegisteredGate{ func newGateRegistry(curve ecc.ID) gateRegistry { res := gateRegistry{ - ids: make(map[uintptr][]gateID), + ids: make(map[uintptr][]gkrtypes.GateID), gates: slices.Clone(builtinGates[:]), } @@ -63,7 +62,7 @@ func newGateRegistry(curve ecc.ID) gateRegistry { // getID looks up f in the cache, adding it if necessary. // Gate ID is returned. -func (r *gateRegistry) getID(f gkr.GateFunction, nbIn int) gateID { +func (r *gateRegistry) getID(f gkr.GateFunction, nbIn int) gkrtypes.GateID { bytecode, err := gkrtypes.CompileGateFunction(f, nbIn) if err != nil { panic(err) @@ -103,13 +102,26 @@ func (r *gateRegistry) getID(f gkr.GateFunction, nbIn int) gateID { panic("cannot find solvable variable for gate") } - id := gateID(len(r.gates)) + id := gkrtypes.GateID(len(r.gates)) r.gates = append(r.gates, &g) r.ids[ptr] = append(r.ids[ptr], id) return id } +func (r *gateRegistry) getGateFunction(id gkrtypes.GateID) gkr.GateFunction { + return r.gates[id].Evaluate.SnarkFriendly +} + +func (r *gateRegistry) toGadgetCircuit(c gkrtypes.SerializableCircuit) gkrtypes.GadgetCircuit { + res := make(gkrtypes.GadgetCircuit, len(c)) + for i := range c { + res[i].Inputs = c[i].Inputs + res[i].Gate = gkrtypes.ToGadgetGate(r.gates[c[i].Gate]) + } + return res +} + type gateTester interface { IsAdditive(varIndex int) bool FindDegree() int From b156730aa24b71f88e9d4f4d65dc58643a308f13 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 11 Feb 2026 15:09:31 -0600 Subject: [PATCH 164/251] refactor: serializable circuits for blueprints --- internal/gkr/bn254/blueprint.go | 41 ++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 9250bbf7d5..545548eb81 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -30,13 +30,15 @@ type circuitEvaluator struct { type BlueprintSolve struct { // Circuit structure (serialized) Circuit gkrtypes.SerializableCircuit + Gates []*gkrtypes.Gate[*gkrtypes.GateBytecode] NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - outputWires []int `cbor:"-"` - maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances + circuit gkrtypes.ExecutableCircuit `cbor:"-"` // converted from Circuit using Gates + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -47,12 +49,16 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { + b.circuit = b.Circuit.ToExecutable(b.Gates) + b.circuit.OutputsList() + b.outputWires = b.circuit.Outputs() + b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), + evaluators: make([]gateEvaluator, len(b.circuit)), } - for wI := range b.Circuit { - w := &b.Circuit[wI] + for wI := range b.circuit { + w := &b.circuit[wI] if !w.IsInput() { ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } @@ -91,8 +97,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Process all wires in topological order (circuit is already sorted) - for wI := range b.Circuit { - w := &b.Circuit[wI] + for wI := range b.circuit { + w := &b.circuit[wI] if w.IsInput() { val, delta := s.Read(calldata) @@ -139,9 +145,12 @@ func (b *BlueprintSolve) NbConstraints() int { } // NbOutputs implements Blueprint -func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() +func (b *BlueprintSolve) NbOutputs(constraint.Instruction) int { + + execCircuit := b.Circuit.ToExecutable(b.Gates) + execCircuit.OutputsList() + b.outputWires = b.circuit.Outputs() + return len(b.outputWires) } @@ -242,7 +251,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BN254-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("bn254 prove failed: %w", err) } @@ -272,7 +281,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) + return b.SolveBlueprint.circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -389,9 +398,9 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BN254 -func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, gates []*gkrtypes.Gate[*gkrtypes.GateBytecode], hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint - solve := &BlueprintSolve{Circuit: circuit} + solve := &BlueprintSolve{Circuit: circuit, Gates: gates} solveID := compiler.AddBlueprint(solve) // Create and register prove blueprint From f91a9fba773f40333515c0e18b65ff2f8eab1a27 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 11 Feb 2026 15:11:48 -0600 Subject: [PATCH 165/251] build: generify --- .../backend/template/gkr/blueprint.go.tmpl | 79 +++++++++++-------- internal/gkr/bls12-377/blueprint.go | 41 ++++++---- internal/gkr/bls12-381/blueprint.go | 41 ++++++---- internal/gkr/bw6-761/blueprint.go | 41 ++++++---- 4 files changed, 119 insertions(+), 83 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 55e50b14ef..fb88a3f755 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -23,13 +23,15 @@ type circuitEvaluator struct { type BlueprintSolve struct { // Circuit structure (serialized) Circuit gkrtypes.SerializableCircuit + Gates []*gkrtypes.Gate[*gkrtypes.GateBytecode] NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - outputWires []int `cbor:"-"` - maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances + circuit gkrtypes.ExecutableCircuit `cbor:"-"` // converted from Circuit using Gates + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -40,12 +42,16 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { + b.circuit = b.Circuit.ToExecutable(b.Gates) + b.circuit.OutputsList() + b.outputWires = b.circuit.Outputs() + b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), + evaluators: make([]gateEvaluator, len(b.circuit)), } - for wI := range b.Circuit { - w := &b.Circuit[wI] + for wI := range b.circuit { + w := &b.circuit[wI] if !w.IsInput() { ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } @@ -53,7 +59,7 @@ func (b *BlueprintSolve) Reset() { return ce } - assignments := make(WireAssignment, len(b.Circuit)) + assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) for i := range assignments { assignments[i] = make(polynomial.MultiLin, nbPaddedInstances) @@ -84,8 +90,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Process all wires in topological order (circuit is already sorted) - for wI := range b.Circuit { - w := &b.Circuit[wI] + for wI := range b.circuit { + w := &b.circuit[wI] if w.IsInput() { val, delta := s.Read(calldata) @@ -132,9 +138,12 @@ func (b *BlueprintSolve) NbConstraints() int { } // NbOutputs implements Blueprint -func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() +func (b *BlueprintSolve) NbOutputs(constraint.Instruction) int { + + execCircuit := b.Circuit.ToExecutable(b.Gates) + execCircuit.OutputsList() + b.outputWires = b.circuit.Outputs() + return len(b.outputWires) } @@ -167,7 +176,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree tree.InsertWire(uint32(i+int(inst.WireOffset)), outputLevel) } - b.maxOutputLevel = max(b.maxOutputLevel, outputLevel) + b.maxOutputLevel = max(b.maxOutputLevel, outputLevel) return outputLevel } @@ -198,28 +207,28 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra return fmt.Errorf("no assignments available for proving") } - nbPaddedInstances := uint32(ecc.NextPowerOfTwo(uint64(solveBlueprint.NbInstances))) - if nbPaddedInstances != uint32(len(assignments[0])) { // test engine path - nbPadding := nbPaddedInstances - solveBlueprint.NbInstances + nbPaddedInstances := uint32(ecc.NextPowerOfTwo(uint64(solveBlueprint.NbInstances))) + if nbPaddedInstances != uint32(len(assignments[0])) { // test engine path + nbPadding := nbPaddedInstances - solveBlueprint.NbInstances for wI := range assignments { - assignments[wI] = slices.Grow(assignments[wI], int(nbPadding)) - toRepeat := assignments[wI][solveBlueprint.NbInstances-1] - for range nbPadding { - assignments[wI] = append(assignments[wI], toRepeat) - } + assignments[wI] = slices.Grow(assignments[wI], int(nbPadding)) + toRepeat := assignments[wI][solveBlueprint.NbInstances-1] + for range nbPadding { + assignments[wI] = append(assignments[wI], toRepeat) + } } } else { - for wI := range assignments { - for i := solveBlueprint.NbInstances; i < nbPaddedInstances; i++ { - assignments[wI][i] = assignments[wI][i-1] - } - } + for wI := range assignments { + for i := solveBlueprint.NbInstances; i < nbPaddedInstances; i++ { + assignments[wI][i] = assignments[wI][i-1] + } + } } // Read initial challenges from instruction calldata (parse dynamically, no metadata) // Format: [0]=totalSize, [1...]=challenge linear expressions insBytes := make([][]byte, 0) // first challenges - calldata := inst.Calldata[1:] // skip size prefix + calldata := inst.Calldata[1:] // skip size prefix for len(calldata) != 0 { val, delta := s.Read(calldata) calldata = calldata[delta:] @@ -235,16 +244,16 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the {{.FieldID}}-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("{{toLower .FieldID}} prove failed: %w", err) } - for i, elem := range proof.flatten() { + for i, elem := range proof.flatten() { var val constraint.U64 copy(val[:], (*elem)[:]) s.SetValue(uint32(i+int(inst.WireOffset)), val) - } + } return nil } @@ -263,9 +272,9 @@ func (b *BlueprintProve) proofSize() int { if b.SolveBlueprint.NbInstances < 2 { return 0 } - nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) + nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) + return b.SolveBlueprint.circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -382,9 +391,9 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for {{.FieldID}} -func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, gates []*gkrtypes.Gate[*gkrtypes.GateBytecode], hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint - solve := &BlueprintSolve{Circuit: circuit} + solve := &BlueprintSolve{Circuit: circuit, Gates: gates} solveID := compiler.AddBlueprint(solve) // Create and register prove blueprint diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 3bfafa0e48..9794a00057 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -30,13 +30,15 @@ type circuitEvaluator struct { type BlueprintSolve struct { // Circuit structure (serialized) Circuit gkrtypes.SerializableCircuit + Gates []*gkrtypes.Gate[*gkrtypes.GateBytecode] NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - outputWires []int `cbor:"-"` - maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances + circuit gkrtypes.ExecutableCircuit `cbor:"-"` // converted from Circuit using Gates + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -47,12 +49,16 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { + b.circuit = b.Circuit.ToExecutable(b.Gates) + b.circuit.OutputsList() + b.outputWires = b.circuit.Outputs() + b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), + evaluators: make([]gateEvaluator, len(b.circuit)), } - for wI := range b.Circuit { - w := &b.Circuit[wI] + for wI := range b.circuit { + w := &b.circuit[wI] if !w.IsInput() { ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } @@ -91,8 +97,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Process all wires in topological order (circuit is already sorted) - for wI := range b.Circuit { - w := &b.Circuit[wI] + for wI := range b.circuit { + w := &b.circuit[wI] if w.IsInput() { val, delta := s.Read(calldata) @@ -139,9 +145,12 @@ func (b *BlueprintSolve) NbConstraints() int { } // NbOutputs implements Blueprint -func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() +func (b *BlueprintSolve) NbOutputs(constraint.Instruction) int { + + execCircuit := b.Circuit.ToExecutable(b.Gates) + execCircuit.OutputsList() + b.outputWires = b.circuit.Outputs() + return len(b.outputWires) } @@ -242,7 +251,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BLS12_377-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("bls12_377 prove failed: %w", err) } @@ -272,7 +281,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) + return b.SolveBlueprint.circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -389,9 +398,9 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_377 -func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, gates []*gkrtypes.Gate[*gkrtypes.GateBytecode], hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint - solve := &BlueprintSolve{Circuit: circuit} + solve := &BlueprintSolve{Circuit: circuit, Gates: gates} solveID := compiler.AddBlueprint(solve) // Create and register prove blueprint diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 20e70e307f..d422996b40 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -30,13 +30,15 @@ type circuitEvaluator struct { type BlueprintSolve struct { // Circuit structure (serialized) Circuit gkrtypes.SerializableCircuit + Gates []*gkrtypes.Gate[*gkrtypes.GateBytecode] NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - outputWires []int `cbor:"-"` - maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances + circuit gkrtypes.ExecutableCircuit `cbor:"-"` // converted from Circuit using Gates + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -47,12 +49,16 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { + b.circuit = b.Circuit.ToExecutable(b.Gates) + b.circuit.OutputsList() + b.outputWires = b.circuit.Outputs() + b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), + evaluators: make([]gateEvaluator, len(b.circuit)), } - for wI := range b.Circuit { - w := &b.Circuit[wI] + for wI := range b.circuit { + w := &b.circuit[wI] if !w.IsInput() { ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } @@ -91,8 +97,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Process all wires in topological order (circuit is already sorted) - for wI := range b.Circuit { - w := &b.Circuit[wI] + for wI := range b.circuit { + w := &b.circuit[wI] if w.IsInput() { val, delta := s.Read(calldata) @@ -139,9 +145,12 @@ func (b *BlueprintSolve) NbConstraints() int { } // NbOutputs implements Blueprint -func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() +func (b *BlueprintSolve) NbOutputs(constraint.Instruction) int { + + execCircuit := b.Circuit.ToExecutable(b.Gates) + execCircuit.OutputsList() + b.outputWires = b.circuit.Outputs() + return len(b.outputWires) } @@ -242,7 +251,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BLS12_381-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("bls12_381 prove failed: %w", err) } @@ -272,7 +281,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) + return b.SolveBlueprint.circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -389,9 +398,9 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_381 -func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, gates []*gkrtypes.Gate[*gkrtypes.GateBytecode], hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint - solve := &BlueprintSolve{Circuit: circuit} + solve := &BlueprintSolve{Circuit: circuit, Gates: gates} solveID := compiler.AddBlueprint(solve) // Create and register prove blueprint diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 4b8c52e986..f45177e645 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -30,13 +30,15 @@ type circuitEvaluator struct { type BlueprintSolve struct { // Circuit structure (serialized) Circuit gkrtypes.SerializableCircuit + Gates []*gkrtypes.Gate[*gkrtypes.GateBytecode] NbInstances uint32 // Not serialized - recreated lazily at solve time - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - outputWires []int `cbor:"-"` - maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances + circuit gkrtypes.ExecutableCircuit `cbor:"-"` // converted from Circuit using Gates + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -47,12 +49,16 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { + b.circuit = b.Circuit.ToExecutable(b.Gates) + b.circuit.OutputsList() + b.outputWires = b.circuit.Outputs() + b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.Circuit)), + evaluators: make([]gateEvaluator, len(b.circuit)), } - for wI := range b.Circuit { - w := &b.Circuit[wI] + for wI := range b.circuit { + w := &b.circuit[wI] if !w.IsInput() { ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } @@ -91,8 +97,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Process all wires in topological order (circuit is already sorted) - for wI := range b.Circuit { - w := &b.Circuit[wI] + for wI := range b.circuit { + w := &b.circuit[wI] if w.IsInput() { val, delta := s.Read(calldata) @@ -139,9 +145,12 @@ func (b *BlueprintSolve) NbConstraints() int { } // NbOutputs implements Blueprint -func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() +func (b *BlueprintSolve) NbOutputs(constraint.Instruction) int { + + execCircuit := b.Circuit.ToExecutable(b.Gates) + execCircuit.OutputsList() + b.outputWires = b.circuit.Outputs() + return len(b.outputWires) } @@ -242,7 +251,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BW6_761-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("bw6_761 prove failed: %w", err) } @@ -272,7 +281,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) + return b.SolveBlueprint.circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -389,9 +398,9 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BW6_761 -func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, gates []*gkrtypes.Gate[*gkrtypes.GateBytecode], hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint - solve := &BlueprintSolve{Circuit: circuit} + solve := &BlueprintSolve{Circuit: circuit, Gates: gates} solveID := compiler.AddBlueprint(solve) // Create and register prove blueprint From 9948b29a9fdb6b665c3cdababb741b8284a945ec Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 11 Feb 2026 15:28:27 -0600 Subject: [PATCH 166/251] revert: blueprint to use executable circuit --- .../backend/template/gkr/blueprint.go.tmpl | 42 ++++++++----------- internal/gkr/bls12-377/blueprint.go | 42 ++++++++----------- internal/gkr/bls12-381/blueprint.go | 42 ++++++++----------- internal/gkr/bn254/blueprint.go | 42 ++++++++----------- internal/gkr/bw6-761/blueprint.go | 42 ++++++++----------- internal/gkr/gkrtypes/types.go | 9 ---- std/gkrapi/compile.go | 10 +++-- std/gkrapi/gateregistry.go | 9 ++++ 8 files changed, 105 insertions(+), 133 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index fb88a3f755..56cdcca699 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -22,16 +22,14 @@ type circuitEvaluator struct { // BlueprintSolve is a {{.FieldID}}-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.SerializableCircuit - Gates []*gkrtypes.Gate[*gkrtypes.GateBytecode] + Circuit gkrtypes.ExecutableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time - circuit gkrtypes.ExecutableCircuit `cbor:"-"` // converted from Circuit using Gates - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - outputWires []int `cbor:"-"` - maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -42,16 +40,15 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { - b.circuit = b.Circuit.ToExecutable(b.Gates) - b.circuit.OutputsList() - b.outputWires = b.circuit.Outputs() + b.Circuit.OutputsList() + b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.circuit)), + evaluators: make([]gateEvaluator, len(b.Circuit)), } - for wI := range b.circuit { - w := &b.circuit[wI] + for wI := range b.Circuit { + w := &b.Circuit[wI] if !w.IsInput() { ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } @@ -90,8 +87,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Process all wires in topological order (circuit is already sorted) - for wI := range b.circuit { - w := &b.circuit[wI] + for wI := range b.Circuit { + w := &b.Circuit[wI] if w.IsInput() { val, delta := s.Read(calldata) @@ -139,11 +136,8 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(constraint.Instruction) int { - - execCircuit := b.Circuit.ToExecutable(b.Gates) - execCircuit.OutputsList() - b.outputWires = b.circuit.Outputs() - + b.Circuit.OutputsList() + b.outputWires = b.Circuit.Outputs() return len(b.outputWires) } @@ -244,7 +238,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the {{.FieldID}}-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("{{toLower .FieldID}} prove failed: %w", err) } @@ -274,7 +268,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.circuit.ProofSize(logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -391,9 +385,9 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for {{.FieldID}} -func NewBlueprints(circuit gkrtypes.SerializableCircuit, gates []*gkrtypes.Gate[*gkrtypes.GateBytecode], hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint - solve := &BlueprintSolve{Circuit: circuit, Gates: gates} + solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) // Create and register prove blueprint diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 9794a00057..f1daeb12fb 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -29,16 +29,14 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS12_377-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.SerializableCircuit - Gates []*gkrtypes.Gate[*gkrtypes.GateBytecode] + Circuit gkrtypes.ExecutableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time - circuit gkrtypes.ExecutableCircuit `cbor:"-"` // converted from Circuit using Gates - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - outputWires []int `cbor:"-"` - maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -49,16 +47,15 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { - b.circuit = b.Circuit.ToExecutable(b.Gates) - b.circuit.OutputsList() - b.outputWires = b.circuit.Outputs() + b.Circuit.OutputsList() + b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.circuit)), + evaluators: make([]gateEvaluator, len(b.Circuit)), } - for wI := range b.circuit { - w := &b.circuit[wI] + for wI := range b.Circuit { + w := &b.Circuit[wI] if !w.IsInput() { ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } @@ -97,8 +94,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Process all wires in topological order (circuit is already sorted) - for wI := range b.circuit { - w := &b.circuit[wI] + for wI := range b.Circuit { + w := &b.Circuit[wI] if w.IsInput() { val, delta := s.Read(calldata) @@ -146,11 +143,8 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(constraint.Instruction) int { - - execCircuit := b.Circuit.ToExecutable(b.Gates) - execCircuit.OutputsList() - b.outputWires = b.circuit.Outputs() - + b.Circuit.OutputsList() + b.outputWires = b.Circuit.Outputs() return len(b.outputWires) } @@ -251,7 +245,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BLS12_377-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("bls12_377 prove failed: %w", err) } @@ -281,7 +275,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.circuit.ProofSize(logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -398,9 +392,9 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_377 -func NewBlueprints(circuit gkrtypes.SerializableCircuit, gates []*gkrtypes.Gate[*gkrtypes.GateBytecode], hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint - solve := &BlueprintSolve{Circuit: circuit, Gates: gates} + solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) // Create and register prove blueprint diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index d422996b40..66a2e520a5 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -29,16 +29,14 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS12_381-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.SerializableCircuit - Gates []*gkrtypes.Gate[*gkrtypes.GateBytecode] + Circuit gkrtypes.ExecutableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time - circuit gkrtypes.ExecutableCircuit `cbor:"-"` // converted from Circuit using Gates - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - outputWires []int `cbor:"-"` - maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -49,16 +47,15 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { - b.circuit = b.Circuit.ToExecutable(b.Gates) - b.circuit.OutputsList() - b.outputWires = b.circuit.Outputs() + b.Circuit.OutputsList() + b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.circuit)), + evaluators: make([]gateEvaluator, len(b.Circuit)), } - for wI := range b.circuit { - w := &b.circuit[wI] + for wI := range b.Circuit { + w := &b.Circuit[wI] if !w.IsInput() { ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } @@ -97,8 +94,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Process all wires in topological order (circuit is already sorted) - for wI := range b.circuit { - w := &b.circuit[wI] + for wI := range b.Circuit { + w := &b.Circuit[wI] if w.IsInput() { val, delta := s.Read(calldata) @@ -146,11 +143,8 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(constraint.Instruction) int { - - execCircuit := b.Circuit.ToExecutable(b.Gates) - execCircuit.OutputsList() - b.outputWires = b.circuit.Outputs() - + b.Circuit.OutputsList() + b.outputWires = b.Circuit.Outputs() return len(b.outputWires) } @@ -251,7 +245,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BLS12_381-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("bls12_381 prove failed: %w", err) } @@ -281,7 +275,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.circuit.ProofSize(logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -398,9 +392,9 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_381 -func NewBlueprints(circuit gkrtypes.SerializableCircuit, gates []*gkrtypes.Gate[*gkrtypes.GateBytecode], hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint - solve := &BlueprintSolve{Circuit: circuit, Gates: gates} + solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) // Create and register prove blueprint diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 545548eb81..d8e5085b9b 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -29,16 +29,14 @@ type circuitEvaluator struct { // BlueprintSolve is a BN254-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.SerializableCircuit - Gates []*gkrtypes.Gate[*gkrtypes.GateBytecode] + Circuit gkrtypes.ExecutableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time - circuit gkrtypes.ExecutableCircuit `cbor:"-"` // converted from Circuit using Gates - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - outputWires []int `cbor:"-"` - maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -49,16 +47,15 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { - b.circuit = b.Circuit.ToExecutable(b.Gates) - b.circuit.OutputsList() - b.outputWires = b.circuit.Outputs() + b.Circuit.OutputsList() + b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.circuit)), + evaluators: make([]gateEvaluator, len(b.Circuit)), } - for wI := range b.circuit { - w := &b.circuit[wI] + for wI := range b.Circuit { + w := &b.Circuit[wI] if !w.IsInput() { ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } @@ -97,8 +94,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Process all wires in topological order (circuit is already sorted) - for wI := range b.circuit { - w := &b.circuit[wI] + for wI := range b.Circuit { + w := &b.Circuit[wI] if w.IsInput() { val, delta := s.Read(calldata) @@ -146,11 +143,8 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(constraint.Instruction) int { - - execCircuit := b.Circuit.ToExecutable(b.Gates) - execCircuit.OutputsList() - b.outputWires = b.circuit.Outputs() - + b.Circuit.OutputsList() + b.outputWires = b.Circuit.Outputs() return len(b.outputWires) } @@ -251,7 +245,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BN254-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("bn254 prove failed: %w", err) } @@ -281,7 +275,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.circuit.ProofSize(logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -398,9 +392,9 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BN254 -func NewBlueprints(circuit gkrtypes.SerializableCircuit, gates []*gkrtypes.Gate[*gkrtypes.GateBytecode], hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint - solve := &BlueprintSolve{Circuit: circuit, Gates: gates} + solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) // Create and register prove blueprint diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index f45177e645..f62cc3d9a2 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -29,16 +29,14 @@ type circuitEvaluator struct { // BlueprintSolve is a BW6_761-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.SerializableCircuit - Gates []*gkrtypes.Gate[*gkrtypes.GateBytecode] + Circuit gkrtypes.ExecutableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time - circuit gkrtypes.ExecutableCircuit `cbor:"-"` // converted from Circuit using Gates - assignments WireAssignment `cbor:"-"` - evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized - outputWires []int `cbor:"-"` - maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances + assignments WireAssignment `cbor:"-"` + evaluatorPool sync.Pool `cbor:"-"` // pool of circuitEvaluator, lazy-initialized + outputWires []int `cbor:"-"` + maxOutputLevel constraint.Level `cbor:"-"` // highest output level across all solve instances lock sync.Mutex `cbor:"-"` } @@ -49,16 +47,15 @@ var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { - b.circuit = b.Circuit.ToExecutable(b.Gates) - b.circuit.OutputsList() - b.outputWires = b.circuit.Outputs() + b.Circuit.OutputsList() + b.outputWires = b.Circuit.Outputs() b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ - evaluators: make([]gateEvaluator, len(b.circuit)), + evaluators: make([]gateEvaluator, len(b.Circuit)), } - for wI := range b.circuit { - w := &b.circuit[wI] + for wI := range b.Circuit { + w := &b.Circuit[wI] if !w.IsInput() { ce.evaluators[wI] = newGateEvaluator(w.Gate.Evaluate, len(w.Inputs)) } @@ -97,8 +94,8 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } // Process all wires in topological order (circuit is already sorted) - for wI := range b.circuit { - w := &b.circuit[wI] + for wI := range b.Circuit { + w := &b.Circuit[wI] if w.IsInput() { val, delta := s.Read(calldata) @@ -146,11 +143,8 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(constraint.Instruction) int { - - execCircuit := b.Circuit.ToExecutable(b.Gates) - execCircuit.OutputsList() - b.outputWires = b.circuit.Outputs() - + b.Circuit.OutputsList() + b.outputWires = b.Circuit.Outputs() return len(b.outputWires) } @@ -251,7 +245,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BW6_761-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) if err != nil { return fmt.Errorf("bw6_761 prove failed: %w", err) } @@ -281,7 +275,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.circuit.ProofSize(logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) } // NbOutputs implements Blueprint @@ -398,9 +392,9 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BW6_761 -func NewBlueprints(circuit gkrtypes.SerializableCircuit, gates []*gkrtypes.Gate[*gkrtypes.GateBytecode], hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { // Create and register solve blueprint - solve := &BlueprintSolve{Circuit: circuit, Gates: gates} + solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) // Create and register prove blueprint diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 8af7c1d221..5a5beddadd 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -434,12 +434,3 @@ func ToGadget(c RegisteredCircuit) GadgetCircuit { return e.SnarkFriendly }) } - -func (c SerializableCircuit) ToExecutable(gates []*Gate[*GateBytecode]) ExecutableCircuit { - res := make(ExecutableCircuit, len(c)) - for i := range c { - res[i].Inputs = c[i].Inputs - res[i].Gate = gates[c[i].Gate] - } - return res -} diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 95c55c6cde..d07c7196c5 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -82,15 +82,17 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C curveID := utils.FieldToCurve(api.parentApi.Compiler().Field()) compiler := api.parentApi.Compiler() + eCircuit := api.gateRegistry.toExecutableCircuit(api.circuit) + switch curveID { case ecc.BN254: - res.blueprints = gkrbn254.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + res.blueprints = gkrbn254.NewBlueprints(eCircuit, fiatshamirHashName, compiler) case ecc.BLS12_377: - res.blueprints = gkrbls12377.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + res.blueprints = gkrbls12377.NewBlueprints(eCircuit, fiatshamirHashName, compiler) case ecc.BLS12_381: - res.blueprints = gkrbls12381.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + res.blueprints = gkrbls12381.NewBlueprints(eCircuit, fiatshamirHashName, compiler) case ecc.BW6_761: - res.blueprints = gkrbw6761.NewBlueprints(api.circuit, fiatshamirHashName, compiler) + res.blueprints = gkrbw6761.NewBlueprints(eCircuit, fiatshamirHashName, compiler) default: return nil, fmt.Errorf("unsupported curve: %s", curveID) } diff --git a/std/gkrapi/gateregistry.go b/std/gkrapi/gateregistry.go index f775263cc7..8e27d0a018 100644 --- a/std/gkrapi/gateregistry.go +++ b/std/gkrapi/gateregistry.go @@ -109,6 +109,15 @@ func (r *gateRegistry) getID(f gkr.GateFunction, nbIn int) gkrtypes.GateID { return id } +func (r *gateRegistry) toExecutableCircuit(c gkrtypes.SerializableCircuit) gkrtypes.ExecutableCircuit { + res := make(gkrtypes.ExecutableCircuit, len(c)) + for i := range c { + res[i].Inputs = c[i].Inputs + res[i].Gate = gkrtypes.ToExecutableGate(r.gates[i]) + } + return res +} + func (r *gateRegistry) getGateFunction(id gkrtypes.GateID) gkr.GateFunction { return r.gates[id].Evaluate.SnarkFriendly } From d3992f7f30e10b4ca71fecc92b6316c98bcab0bf Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 11 Feb 2026 15:37:52 -0600 Subject: [PATCH 167/251] remove registry test --- constraint/solver/gkrgates/registry_test.go | 80 --------------------- 1 file changed, 80 deletions(-) delete mode 100644 constraint/solver/gkrgates/registry_test.go diff --git a/constraint/solver/gkrgates/registry_test.go b/constraint/solver/gkrgates/registry_test.go deleted file mode 100644 index bb046f4360..0000000000 --- a/constraint/solver/gkrgates/registry_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package gkrgates - -import ( - "testing" - - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrtesting" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" - "github.com/stretchr/testify/assert" -) - -func TestRegister(t *testing.T) { - testGate := func(name gkr.GateName, f gkr.GateFunction, nbIn, degree int) { - t.Run(string(name), func(t *testing.T) { - name = name + "-register-gate-test" - - assert.NoError(t, - Register(f, nbIn, WithDegree(degree), WithName(name+"_given")), - "given degree must be accepted", - ) - - assert.Error(t, - Register(f, nbIn, WithDegree(degree-1), WithName(name+"_lower")), - "error must be returned for lower degree", - ) - - assert.Error(t, - Register(f, nbIn, WithDegree(degree+1), WithName(name+"_higher")), - "error must be returned for higher degree", - ) - - assert.NoError(t, - Register(f, nbIn, WithName(name+"_no_degree")), - "no error must be returned when no degree is specified", - ) - - assert.Equal(t, degree, Get(name+"_no_degree").Degree, "degree must be detected correctly") - - err := Register(func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - return api.Add(f(api, x...), 1) - }, nbIn, WithDegree(degree), WithName(name+"_given")) - assert.Error(t, err, "registering another function under the same name must fail") - }) - } - - testGate("add3", func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - return api.Add(x[0], x[1], x[2]) - }, 3, 1) - - testGate("mul2", gkrtypes.Mul2().Evaluate.SnarkFriendly, 2, 2) - - testGate("mimc", gkrtesting.NewCache().GetGate("mimc").Evaluate.SnarkFriendly, 2, 7) - - testGate("sub2PlusOne", func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - return api.Sub( - api.Add(1, x[0]), - x[1], - ) - }, 2, 1) - - t.Run("zero", func(t *testing.T) { - const gateName gkr.GateName = "zero-register-gate-test" - zeroGate := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - return api.Sub(x[0], x[0]) - } - - // Attempt to register the zero gate without specifying a degree - assert.Error(t, - Register(zeroGate, 1, WithName(gateName)), - "error must be returned for zero polynomial", - ) - - // Attempt to register the zero gate with a specified degree - assert.Error(t, - Register(zeroGate, 1, WithName(gateName), WithDegree(2)), - "error must be returned for zero polynomial with degree", - ) - }) -} From 8f4c207498ca1ba1102c8806d7a8d6d20e7c3001 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 11 Feb 2026 15:44:45 -0600 Subject: [PATCH 168/251] fix conversion bug --- std/gkrapi/gateregistry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/gkrapi/gateregistry.go b/std/gkrapi/gateregistry.go index 8e27d0a018..68083eb3ec 100644 --- a/std/gkrapi/gateregistry.go +++ b/std/gkrapi/gateregistry.go @@ -113,7 +113,7 @@ func (r *gateRegistry) toExecutableCircuit(c gkrtypes.SerializableCircuit) gkrty res := make(gkrtypes.ExecutableCircuit, len(c)) for i := range c { res[i].Inputs = c[i].Inputs - res[i].Gate = gkrtypes.ToExecutableGate(r.gates[i]) + res[i].Gate = gkrtypes.ToExecutableGate(r.gates[c[i].Gate]) } return res } From 3ef6c9937690e05c7388e5ef9aebeee8e744a17a Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 11 Feb 2026 15:45:32 -0600 Subject: [PATCH 169/251] decriminalize no solvable vars --- std/gkrapi/gateregistry.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/std/gkrapi/gateregistry.go b/std/gkrapi/gateregistry.go index 68083eb3ec..837336804f 100644 --- a/std/gkrapi/gateregistry.go +++ b/std/gkrapi/gateregistry.go @@ -98,9 +98,6 @@ func (r *gateRegistry) getID(f gkr.GateFunction, nbIn int) gkrtypes.GateID { if g.Degree == -1 { panic("cannot find degree for gate") } - if g.SolvableVar == -1 { - panic("cannot find solvable variable for gate") - } id := gkrtypes.GateID(len(r.gates)) r.gates = append(r.gates, &g) From 021a1e53b5aa120957781eea1c2a77ce6352d93c Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Thu, 12 Feb 2026 20:03:31 -0600 Subject: [PATCH 170/251] revert reverting blueprints --- .../backend/template/gkr/blueprint.go.tmpl | 20 +++++++------- .../backend/template/gkr/gkr.test.go.tmpl | 4 ++- internal/gkr/bls12-377/blueprint.go | 25 +++++++++++------- internal/gkr/bls12-377/gkr_test.go | 4 ++- internal/gkr/bls12-381/blueprint.go | 25 +++++++++++------- internal/gkr/bls12-381/gkr_test.go | 4 ++- internal/gkr/blueprint.go | 19 +++++-------- internal/gkr/bn254/blueprint.go | 25 +++++++++++------- internal/gkr/bn254/gkr_test.go | 4 ++- internal/gkr/bw6-761/blueprint.go | 25 +++++++++++------- internal/gkr/bw6-761/gkr_test.go | 4 ++- internal/gkr/gkrtypes/types.go | 15 +++++++++++ .../testdata/gkr_circuit_bn254.scs | Bin 69711 -> 69687 bytes std/gkrapi/compile.go | 2 +- 14 files changed, 112 insertions(+), 64 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 83db048bfd..6161370f8a 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -10,7 +10,6 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" - gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) @@ -42,12 +41,14 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) +func (b *BlueprintSolve) setOutputWires() { + b.Circuit.OutputsList() // for side effects + b.outputWires = b.Circuit.Outputs() +} + // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { - b.Circuit.OutputsList() - b.outputWires = b.Circuit.Outputs() - b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ evaluators: make([]gateEvaluator, len(b.Circuit)), @@ -61,6 +62,8 @@ func (b *BlueprintSolve) Reset() { return ce } + b.setOutputWires() + assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) for i := range assignments { @@ -140,9 +143,8 @@ func (b *BlueprintSolve) NbConstraints() int { } // NbOutputs implements Blueprint -func (b *BlueprintSolve) NbOutputs(constraint.Instruction) int { - b.Circuit.OutputsList() - b.outputWires = b.Circuit.Outputs() +func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { + b.setOutputWires() return len(b.outputWires) } @@ -390,7 +392,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for {{.FieldID}} -func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) @@ -409,7 +411,7 @@ func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler } getAssignmentID := compiler.AddBlueprint(getAssignment) - return gadget.Blueprints{ + return gkrtypes.Blueprints{ SolveID: solveID, Solve: solve, ProveID: proveID, diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index a074d7a047..10b725d389 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -326,7 +326,9 @@ func TestIsAdditive(t *testing.T) { newTester := func(f gkr.GateFunction, nbIn int) *GateTester { cg, err := gkrtypes.CompileGateFunction(f, nbIn) assert.NoError(t, err) - return NewGateTester(cg, nbIn) + var gt GateTester + gt.SetGate(cg, nbIn) + return > } tester := newTester(f, 2) diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index f1daeb12fb..79f980e309 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -17,10 +17,14 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" - gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) +func init() { + // Register GKR blueprint types for CBOR serialization with explicit tag numbers + constraint.RegisterGkrBlueprintTypes(ecc.BLS12_377, BlueprintSolve{}, BlueprintProve{}, BlueprintGetAssignment{}) +} + // circuitEvaluator evaluates all gates in a circuit for one instance type circuitEvaluator struct { evaluators []gateEvaluator // one evaluator per wire @@ -44,12 +48,14 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) +func (b *BlueprintSolve) setOutputWires() { + b.Circuit.OutputsList() // for side effects + b.outputWires = b.Circuit.Outputs() +} + // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { - b.Circuit.OutputsList() - b.outputWires = b.Circuit.Outputs() - b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ evaluators: make([]gateEvaluator, len(b.Circuit)), @@ -63,6 +69,8 @@ func (b *BlueprintSolve) Reset() { return ce } + b.setOutputWires() + assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) for i := range assignments { @@ -142,9 +150,8 @@ func (b *BlueprintSolve) NbConstraints() int { } // NbOutputs implements Blueprint -func (b *BlueprintSolve) NbOutputs(constraint.Instruction) int { - b.Circuit.OutputsList() - b.outputWires = b.Circuit.Outputs() +func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { + b.setOutputWires() return len(b.outputWires) } @@ -392,7 +399,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_377 -func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) @@ -411,7 +418,7 @@ func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler } getAssignmentID := compiler.AddBlueprint(getAssignment) - return gadget.Blueprints{ + return gkrtypes.Blueprints{ SolveID: solveID, Solve: solve, ProveID: proveID, diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index ed8dc32361..060a76bba2 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -331,7 +331,9 @@ func TestIsAdditive(t *testing.T) { newTester := func(f gkr.GateFunction, nbIn int) *GateTester { cg, err := gkrtypes.CompileGateFunction(f, nbIn) assert.NoError(t, err) - return NewGateTester(cg, nbIn) + var gt GateTester + gt.SetGate(cg, nbIn) + return > } tester := newTester(f, 2) diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 66a2e520a5..5501b4c14b 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -17,10 +17,14 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" - gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) +func init() { + // Register GKR blueprint types for CBOR serialization with explicit tag numbers + constraint.RegisterGkrBlueprintTypes(ecc.BLS12_381, BlueprintSolve{}, BlueprintProve{}, BlueprintGetAssignment{}) +} + // circuitEvaluator evaluates all gates in a circuit for one instance type circuitEvaluator struct { evaluators []gateEvaluator // one evaluator per wire @@ -44,12 +48,14 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) +func (b *BlueprintSolve) setOutputWires() { + b.Circuit.OutputsList() // for side effects + b.outputWires = b.Circuit.Outputs() +} + // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { - b.Circuit.OutputsList() - b.outputWires = b.Circuit.Outputs() - b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ evaluators: make([]gateEvaluator, len(b.Circuit)), @@ -63,6 +69,8 @@ func (b *BlueprintSolve) Reset() { return ce } + b.setOutputWires() + assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) for i := range assignments { @@ -142,9 +150,8 @@ func (b *BlueprintSolve) NbConstraints() int { } // NbOutputs implements Blueprint -func (b *BlueprintSolve) NbOutputs(constraint.Instruction) int { - b.Circuit.OutputsList() - b.outputWires = b.Circuit.Outputs() +func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { + b.setOutputWires() return len(b.outputWires) } @@ -392,7 +399,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_381 -func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) @@ -411,7 +418,7 @@ func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler } getAssignmentID := compiler.AddBlueprint(getAssignment) - return gadget.Blueprints{ + return gkrtypes.Blueprints{ SolveID: solveID, Solve: solve, ProveID: proveID, diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index a9b60efd28..5e7995ed6d 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -331,7 +331,9 @@ func TestIsAdditive(t *testing.T) { newTester := func(f gkr.GateFunction, nbIn int) *GateTester { cg, err := gkrtypes.CompileGateFunction(f, nbIn) assert.NoError(t, err) - return NewGateTester(cg, nbIn) + var gt GateTester + gt.SetGate(cg, nbIn) + return > } tester := newTester(f, 2) diff --git a/internal/gkr/blueprint.go b/internal/gkr/blueprint.go index 761e6deed2..9f47e01d01 100644 --- a/internal/gkr/blueprint.go +++ b/internal/gkr/blueprint.go @@ -1,16 +1,9 @@ package gkr -import "github.com/consensys/gnark/constraint" +import "github.com/consensys/gnark/internal/gkr/gkrtypes" -type BlueprintSolve interface { - constraint.Blueprint - SetNbInstances(nbInstances uint32) -} - -// Blueprints holds all GKR-related blueprint IDs and references -type Blueprints struct { - SolveID constraint.BlueprintID - Solve BlueprintSolve - ProveID constraint.BlueprintID - GetAssignmentID constraint.BlueprintID -} +// Type aliases for backwards compatibility - types are now defined in gkrtypes +type ( + BlueprintSolve = gkrtypes.BlueprintSolve + Blueprints = gkrtypes.Blueprints +) diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index d8e5085b9b..8b3dd66177 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -17,10 +17,14 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" - gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) +func init() { + // Register GKR blueprint types for CBOR serialization with explicit tag numbers + constraint.RegisterGkrBlueprintTypes(ecc.BN254, BlueprintSolve{}, BlueprintProve{}, BlueprintGetAssignment{}) +} + // circuitEvaluator evaluates all gates in a circuit for one instance type circuitEvaluator struct { evaluators []gateEvaluator // one evaluator per wire @@ -44,12 +48,14 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) +func (b *BlueprintSolve) setOutputWires() { + b.Circuit.OutputsList() // for side effects + b.outputWires = b.Circuit.Outputs() +} + // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { - b.Circuit.OutputsList() - b.outputWires = b.Circuit.Outputs() - b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ evaluators: make([]gateEvaluator, len(b.Circuit)), @@ -63,6 +69,8 @@ func (b *BlueprintSolve) Reset() { return ce } + b.setOutputWires() + assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) for i := range assignments { @@ -142,9 +150,8 @@ func (b *BlueprintSolve) NbConstraints() int { } // NbOutputs implements Blueprint -func (b *BlueprintSolve) NbOutputs(constraint.Instruction) int { - b.Circuit.OutputsList() - b.outputWires = b.Circuit.Outputs() +func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { + b.setOutputWires() return len(b.outputWires) } @@ -392,7 +399,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BN254 -func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) @@ -411,7 +418,7 @@ func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler } getAssignmentID := compiler.AddBlueprint(getAssignment) - return gadget.Blueprints{ + return gkrtypes.Blueprints{ SolveID: solveID, Solve: solve, ProveID: proveID, diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 93cc84a362..a775590a82 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -331,7 +331,9 @@ func TestIsAdditive(t *testing.T) { newTester := func(f gkr.GateFunction, nbIn int) *GateTester { cg, err := gkrtypes.CompileGateFunction(f, nbIn) assert.NoError(t, err) - return NewGateTester(cg, nbIn) + var gt GateTester + gt.SetGate(cg, nbIn) + return > } tester := newTester(f, 2) diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index f62cc3d9a2..17c1c338c5 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -17,10 +17,14 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" - gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) +func init() { + // Register GKR blueprint types for CBOR serialization with explicit tag numbers + constraint.RegisterGkrBlueprintTypes(ecc.BW6_761, BlueprintSolve{}, BlueprintProve{}, BlueprintGetAssignment{}) +} + // circuitEvaluator evaluates all gates in a circuit for one instance type circuitEvaluator struct { evaluators []gateEvaluator // one evaluator per wire @@ -44,12 +48,14 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) +func (b *BlueprintSolve) setOutputWires() { + b.Circuit.OutputsList() // for side effects + b.outputWires = b.Circuit.Outputs() +} + // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { - b.Circuit.OutputsList() - b.outputWires = b.Circuit.Outputs() - b.evaluatorPool.New = func() interface{} { ce := &circuitEvaluator{ evaluators: make([]gateEvaluator, len(b.Circuit)), @@ -63,6 +69,8 @@ func (b *BlueprintSolve) Reset() { return ce } + b.setOutputWires() + assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) for i := range assignments { @@ -142,9 +150,8 @@ func (b *BlueprintSolve) NbConstraints() int { } // NbOutputs implements Blueprint -func (b *BlueprintSolve) NbOutputs(constraint.Instruction) int { - b.Circuit.OutputsList() - b.outputWires = b.Circuit.Outputs() +func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { + b.setOutputWires() return len(b.outputWires) } @@ -392,7 +399,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BW6_761 -func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gadget.Blueprints { +func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) @@ -411,7 +418,7 @@ func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler } getAssignmentID := compiler.AddBlueprint(getAssignment) - return gadget.Blueprints{ + return gkrtypes.Blueprints{ SolveID: solveID, Solve: solve, ProveID: proveID, diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index 653e5045e8..e136ef8f7e 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -331,7 +331,9 @@ func TestIsAdditive(t *testing.T) { newTester := func(f gkr.GateFunction, nbIn int) *GateTester { cg, err := gkrtypes.CompileGateFunction(f, nbIn) assert.NoError(t, err) - return NewGateTester(cg, nbIn) + var gt GateTester + gt.SetGate(cg, nbIn) + return > } tester := newTester(f, 2) diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 5a5beddadd..b9fb46c0ba 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -5,6 +5,7 @@ import ( "github.com/consensys/gnark" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/gkrapi/gkr" ) @@ -434,3 +435,17 @@ func ToGadget(c RegisteredCircuit) GadgetCircuit { return e.SnarkFriendly }) } + +// BlueprintSolve is the interface for GKR solve blueprints +type BlueprintSolve interface { + constraint.Blueprint + SetNbInstances(nbInstances uint32) +} + +// Blueprints holds all GKR-related blueprint IDs and references +type Blueprints struct { + SolveID constraint.BlueprintID + Solve BlueprintSolve + ProveID constraint.BlueprintID + GetAssignmentID constraint.BlueprintID +} diff --git a/internal/gkr/test_vectors/testdata/gkr_circuit_bn254.scs b/internal/gkr/test_vectors/testdata/gkr_circuit_bn254.scs index ca413effc935a9de57e56271b02aa7bbacbdcfde..d2a7f3029a9c6b477e0c7880218c387a47ca17d2 100644 GIT binary patch delta 70 zcmX@VfMxpv7Dn-jjOL7wHd^{WW?z!xm*knpIC=JCd--qqeo3KunT4gP{-q@ar6t9T Wi&NYaOHv_fn1E`uHtRo8&;bDA*BopB delta 76 zcmdnKfaUxG7DoMvjOL8LHd^{W7G9d-m*knpnC4tsRF+!Y!pOwTHo5 Date: Thu, 12 Feb 2026 20:21:20 -0600 Subject: [PATCH 171/251] revert: executable -> serializable --- .../backend/template/gkr/blueprint.go.tmpl | 4 +-- .../backend/template/gkr/gkr.go.tmpl | 6 ++-- .../backend/template/gkr/gkr.test.go.tmpl | 10 +++---- .../template/gkr/gkr.test.vectors.go.tmpl | 4 +-- internal/gkr/bls12-377/blueprint.go | 4 +-- internal/gkr/bls12-377/gkr.go | 6 ++-- internal/gkr/bls12-377/gkr_test.go | 14 ++++----- internal/gkr/bls12-381/blueprint.go | 4 +-- internal/gkr/bls12-381/gkr.go | 6 ++-- internal/gkr/bls12-381/gkr_test.go | 14 ++++----- internal/gkr/blueprint.go | 9 ------ internal/gkr/bn254/blueprint.go | 4 +-- internal/gkr/bn254/gkr.go | 6 ++-- internal/gkr/bn254/gkr_test.go | 14 ++++----- internal/gkr/bw6-761/blueprint.go | 4 +-- internal/gkr/bw6-761/gkr.go | 6 ++-- internal/gkr/bw6-761/gkr_test.go | 14 ++++----- .../gkr/gkrtypes/topological_sort_test.go | 16 +++++----- internal/gkr/gkrtypes/types.go | 21 +++++--------- internal/gkr/small_rational/gkr.go | 6 ++-- .../gkr/small_rational/test_vector_gen.go | 4 +-- std/gkrapi/api.go | 29 ++++++++++++------- std/gkrapi/compile.go | 4 +-- std/gkrapi/gateregistry.go | 20 ++++++------- 24 files changed, 111 insertions(+), 118 deletions(-) delete mode 100644 internal/gkr/blueprint.go diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 6161370f8a..aa186aea13 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -26,7 +26,7 @@ type circuitEvaluator struct { // BlueprintSolve is a {{.FieldID}}-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.ExecutableCircuit + Circuit gkrtypes.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -392,7 +392,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for {{.FieldID}} -func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index e7e84300bd..f48acb843b 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -13,9 +13,9 @@ import ( // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.ExecutableWire - Wires = gkrtypes.ExecutableWires - Circuit = gkrtypes.ExecutableCircuit + Wire = gkrtypes.SerializableWire + Wires = gkrtypes.SerializableWires + Circuit = gkrtypes.SerializableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index 10b725d389..eddde66842 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -23,9 +23,9 @@ import ( ) var ( - identityGate = gkrtypes.ToExecutableGate(gkrtypes.Identity()) - add2Gate = gkrtypes.ToExecutableGate(gkrtypes.Add2()) - mul2Gate = gkrtypes.ToExecutableGate(gkrtypes.Mul2()) + identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) ) func TestNoGateTwoInstances(t *testing.T) { @@ -136,7 +136,7 @@ func TestSingleMimcCipherGate(t *testing.T) { }, { Inputs: []int{0, 1}, - Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), }, }) } @@ -292,7 +292,7 @@ func mimcCircuit(numRounds int) Circuit { for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl index 3d06655ffe..29b64252ca 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl @@ -32,7 +32,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.ExecutableCircuit + Circuit gkrtypes.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -62,7 +62,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToExecutable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 79f980e309..2a9b2de2d0 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -33,7 +33,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS12_377-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.ExecutableCircuit + Circuit gkrtypes.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -399,7 +399,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_377 -func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 987e43c1af..6493a8499a 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -21,9 +21,9 @@ import ( // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.ExecutableWire - Wires = gkrtypes.ExecutableWires - Circuit = gkrtypes.ExecutableCircuit + Wire = gkrtypes.SerializableWire + Wires = gkrtypes.SerializableWires + Circuit = gkrtypes.SerializableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index 060a76bba2..e93bc24589 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -29,9 +29,9 @@ import ( ) var ( - identityGate = gkrtypes.ToExecutableGate(gkrtypes.Identity()) - add2Gate = gkrtypes.ToExecutableGate(gkrtypes.Add2()) - mul2Gate = gkrtypes.ToExecutableGate(gkrtypes.Mul2()) + identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) ) func TestNoGateTwoInstances(t *testing.T) { @@ -142,7 +142,7 @@ func TestSingleMimcCipherGate(t *testing.T) { }, { Inputs: []int{0, 1}, - Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), }, }) } @@ -298,7 +298,7 @@ func mimcCircuit(numRounds int) Circuit { for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } @@ -476,7 +476,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.ExecutableCircuit + Circuit gkrtypes.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -505,7 +505,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToExecutable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 5501b4c14b..3c37e9a2ab 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -33,7 +33,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS12_381-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.ExecutableCircuit + Circuit gkrtypes.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -399,7 +399,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_381 -func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index f336d620cd..39a4197e09 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -21,9 +21,9 @@ import ( // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.ExecutableWire - Wires = gkrtypes.ExecutableWires - Circuit = gkrtypes.ExecutableCircuit + Wire = gkrtypes.SerializableWire + Wires = gkrtypes.SerializableWires + Circuit = gkrtypes.SerializableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 5e7995ed6d..e1ecf58916 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -29,9 +29,9 @@ import ( ) var ( - identityGate = gkrtypes.ToExecutableGate(gkrtypes.Identity()) - add2Gate = gkrtypes.ToExecutableGate(gkrtypes.Add2()) - mul2Gate = gkrtypes.ToExecutableGate(gkrtypes.Mul2()) + identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) ) func TestNoGateTwoInstances(t *testing.T) { @@ -142,7 +142,7 @@ func TestSingleMimcCipherGate(t *testing.T) { }, { Inputs: []int{0, 1}, - Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), }, }) } @@ -298,7 +298,7 @@ func mimcCircuit(numRounds int) Circuit { for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } @@ -476,7 +476,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.ExecutableCircuit + Circuit gkrtypes.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -505,7 +505,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToExecutable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/blueprint.go b/internal/gkr/blueprint.go deleted file mode 100644 index 9f47e01d01..0000000000 --- a/internal/gkr/blueprint.go +++ /dev/null @@ -1,9 +0,0 @@ -package gkr - -import "github.com/consensys/gnark/internal/gkr/gkrtypes" - -// Type aliases for backwards compatibility - types are now defined in gkrtypes -type ( - BlueprintSolve = gkrtypes.BlueprintSolve - Blueprints = gkrtypes.Blueprints -) diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 8b3dd66177..35cf6f9ed5 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -33,7 +33,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BN254-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.ExecutableCircuit + Circuit gkrtypes.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -399,7 +399,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BN254 -func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 46d0c5f441..a57ea2dbab 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -21,9 +21,9 @@ import ( // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.ExecutableWire - Wires = gkrtypes.ExecutableWires - Circuit = gkrtypes.ExecutableCircuit + Wire = gkrtypes.SerializableWire + Wires = gkrtypes.SerializableWires + Circuit = gkrtypes.SerializableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index a775590a82..3a4048dc26 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -29,9 +29,9 @@ import ( ) var ( - identityGate = gkrtypes.ToExecutableGate(gkrtypes.Identity()) - add2Gate = gkrtypes.ToExecutableGate(gkrtypes.Add2()) - mul2Gate = gkrtypes.ToExecutableGate(gkrtypes.Mul2()) + identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) ) func TestNoGateTwoInstances(t *testing.T) { @@ -142,7 +142,7 @@ func TestSingleMimcCipherGate(t *testing.T) { }, { Inputs: []int{0, 1}, - Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), }, }) } @@ -298,7 +298,7 @@ func mimcCircuit(numRounds int) Circuit { for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } @@ -476,7 +476,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.ExecutableCircuit + Circuit gkrtypes.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -505,7 +505,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToExecutable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 17c1c338c5..ce3d16c520 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -33,7 +33,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BW6_761-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.ExecutableCircuit + Circuit gkrtypes.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -399,7 +399,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BW6_761 -func NewBlueprints(circuit gkrtypes.ExecutableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { +func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index d166a4a1db..41296ede56 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -21,9 +21,9 @@ import ( // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.ExecutableWire - Wires = gkrtypes.ExecutableWires - Circuit = gkrtypes.ExecutableCircuit + Wire = gkrtypes.SerializableWire + Wires = gkrtypes.SerializableWires + Circuit = gkrtypes.SerializableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index e136ef8f7e..cf2ae43c4d 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -29,9 +29,9 @@ import ( ) var ( - identityGate = gkrtypes.ToExecutableGate(gkrtypes.Identity()) - add2Gate = gkrtypes.ToExecutableGate(gkrtypes.Add2()) - mul2Gate = gkrtypes.ToExecutableGate(gkrtypes.Mul2()) + identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) + add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) + mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) ) func TestNoGateTwoInstances(t *testing.T) { @@ -142,7 +142,7 @@ func TestSingleMimcCipherGate(t *testing.T) { }, { Inputs: []int{0, 1}, - Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), }, }) } @@ -298,7 +298,7 @@ func mimcCircuit(numRounds int) Circuit { for i := 2; i < len(c); i++ { c[i] = Wire{ - Gate: gkrtypes.ToExecutableGate(cache.GetGate("mimc")), + Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), Inputs: []int{i - 1, 0}, } } @@ -476,7 +476,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.ExecutableCircuit + Circuit gkrtypes.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -505,7 +505,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToExecutable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/gkrtypes/topological_sort_test.go b/internal/gkr/gkrtypes/topological_sort_test.go index 3a3e38d811..2726baea55 100644 --- a/internal/gkr/gkrtypes/topological_sort_test.go +++ b/internal/gkr/gkrtypes/topological_sort_test.go @@ -7,17 +7,17 @@ import ( ) func TestTopSortTrivial(t *testing.T) { - c := make(ExecutableCircuit, 2) + c := make(SerializableCircuit, 2) c[0].Inputs = []int{1} sorted := c.TopologicalSort() - assert.Equal(t, ExecutableWires{&c[1], &c[0]}, sorted) + assert.Equal(t, SerializableWires{&c[1], &c[0]}, sorted) } func TestTopSortSingleGate(t *testing.T) { - c := make(ExecutableCircuit, 3) + c := make(SerializableCircuit, 3) c[0].Inputs = []int{1, 2} sorted := c.TopologicalSort() - expected := ExecutableWires{&c[1], &c[2], &c[0]} + expected := SerializableWires{&c[1], &c[2], &c[0]} assert.Equal(t, expected, sorted) assert.Equal(t, c[0].NbUniqueOutputs, 0) @@ -26,17 +26,17 @@ func TestTopSortSingleGate(t *testing.T) { } func TestTopSortDeep(t *testing.T) { - c := make(ExecutableCircuit, 4) + c := make(SerializableCircuit, 4) c[0].Inputs = []int{2} c[1].Inputs = []int{3} c[2].Inputs = []int{} c[3].Inputs = []int{0} sorted := c.TopologicalSort() - assert.Equal(t, ExecutableWires{&c[2], &c[0], &c[3], &c[1]}, sorted) + assert.Equal(t, SerializableWires{&c[2], &c[0], &c[3], &c[1]}, sorted) } func TestTopSortWide(t *testing.T) { - c := make(ExecutableCircuit, 10) + c := make(SerializableCircuit, 10) c[0].Inputs = []int{3, 8} c[1].Inputs = []int{6} c[2].Inputs = []int{4} @@ -49,7 +49,7 @@ func TestTopSortWide(t *testing.T) { c[9].Inputs = []int{} sorted := c.TopologicalSort() - sortedExpected := ExecutableWires{&c[3], &c[4], &c[2], &c[8], &c[0], &c[9], &c[5], &c[6], &c[1], &c[7]} + sortedExpected := SerializableWires{&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) } diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index b9fb46c0ba..f6bbd3b0c8 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -17,13 +17,6 @@ type ( InputInstance int } - GateID uint16 - SerializableWire struct { - Gate GateID - Inputs []int - } - SerializableCircuit []SerializableWire - // A Gate is a low-degree multivariate polynomial Gate[GateExecutable any] struct { Evaluate GateExecutable @@ -57,10 +50,10 @@ type ( RegisteredWire = Wire[BothExecutables] RegisteredWires = Wires[BothExecutables] - // Executable types (bytecode only, for native proving) - ExecutableCircuit = Circuit[*GateBytecode] - ExecutableWire = Wire[*GateBytecode] - ExecutableWires = Wires[*GateBytecode] + // Serializable types (bytecode only, for native proving) + SerializableCircuit = Circuit[*GateBytecode] + SerializableWire = Wire[*GateBytecode] + SerializableWires = Wires[*GateBytecode] // Gadget types (gate functions only, for in-circuit verification) GadgetCircuit = Circuit[gkr.GateFunction] @@ -416,12 +409,12 @@ func ConvertCircuit[GateExecutable, TargetGateExecutable any](c Circuit[GateExec return res } -// ToExecutable converts a registered circuit (with both executables) to an executable circuit (bytecode only). -func ToExecutable(c RegisteredCircuit) ExecutableCircuit { +// ToSerializable converts a registered circuit (with both executables) to a serializable circuit (bytecode only). +func ToSerializable(c RegisteredCircuit) SerializableCircuit { return ConvertCircuit(c, BothExecutables.getByteCode) } -func ToExecutableGate(g *RegisteredGate) *Gate[*GateBytecode] { +func ToSerializableGate(g *RegisteredGate) *Gate[*GateBytecode] { return ConvertGate(g, BothExecutables.getByteCode) } diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 57516ebce8..9d1b62dfb6 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -21,9 +21,9 @@ import ( // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.ExecutableWire - Wires = gkrtypes.ExecutableWires - Circuit = gkrtypes.ExecutableCircuit + Wire = gkrtypes.SerializableWire + Wires = gkrtypes.SerializableWires + Circuit = gkrtypes.SerializableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit diff --git a/internal/gkr/small_rational/test_vector_gen.go b/internal/gkr/small_rational/test_vector_gen.go index 38b771b9a5..7fa9c4114d 100644 --- a/internal/gkr/small_rational/test_vector_gen.go +++ b/internal/gkr/small_rational/test_vector_gen.go @@ -186,7 +186,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.ExecutableCircuit + Circuit gkrtypes.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment @@ -216,7 +216,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToExecutable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index 77c1dde979..c376fb7b79 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -3,24 +3,33 @@ package gkrapi import ( "github.com/consensys/gnark/frontend" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi/gkr" ) -type API struct { - circuit gkrtypes.SerializableCircuit - assignments gadget.WireAssignment - parentApi frontend.API - gateRegistry gateRegistry -} +type ( + gateID uint16 + wire struct { + Gate gateID + Inputs []int + } + + circuit []wire + + API struct { + circuit circuit + assignments gadget.WireAssignment + parentApi frontend.API + gateRegistry gateRegistry + } +) func frontendVarToInt(a gkr.Variable) int { return int(a) } -func (api *API) gate(id gkrtypes.GateID, inputs ...gkr.Variable) gkr.Variable { - api.circuit = append(api.circuit, gkrtypes.SerializableWire{ +func (api *API) gate(id gateID, inputs ...gkr.Variable) gkr.Variable { + api.circuit = append(api.circuit, wire{ Gate: id, Inputs: utils.Map(inputs, frontendVarToInt), }) @@ -33,7 +42,7 @@ func (api *API) Gate(gate gkr.GateFunction, inputs ...gkr.Variable) gkr.Variable return api.gate(api.gateRegistry.getID(gate, len(inputs)), inputs...) } -func (api *API) gate2PlusIn(gate gkrtypes.GateID, in1, in2 gkr.Variable, in ...gkr.Variable) gkr.Variable { +func (api *API) gate2PlusIn(gate gateID, in1, in2 gkr.Variable, in ...gkr.Variable) gkr.Variable { inCombined := make([]gkr.Variable, 2+len(in)) inCombined[0] = in1 inCombined[1] = in2 diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 5a4600b96b..c61424295a 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -52,7 +52,7 @@ func New(api frontend.API) (*API, error) { // NewInput creates a new input variable. func (api *API) NewInput() gkr.Variable { i := len(api.circuit) - api.circuit = append(api.circuit, gkrtypes.SerializableWire{}) + api.circuit = append(api.circuit, wire{}) api.assignments = append(api.assignments, nil) return gkr.Variable(i) } @@ -82,7 +82,7 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C curveID := utils.FieldToCurve(api.parentApi.Compiler().Field()) compiler := api.parentApi.Compiler() - eCircuit := api.gateRegistry.toExecutableCircuit(api.circuit) + eCircuit := api.gateRegistry.toSerializableCircuit(api.circuit) switch curveID { case ecc.BN254: diff --git a/std/gkrapi/gateregistry.go b/std/gkrapi/gateregistry.go index 837336804f..6af703f322 100644 --- a/std/gkrapi/gateregistry.go +++ b/std/gkrapi/gateregistry.go @@ -16,14 +16,14 @@ import ( type ( gateRegistry struct { - ids map[uintptr][]gkrtypes.GateID // ids maps a gate function to gates defined using it. + ids map[uintptr][]gateID // ids maps a gate function to gates defined using it. gates []*gkrtypes.RegisteredGate tester gateTester } ) const ( - idIdentity gkrtypes.GateID = iota + idIdentity gateID = iota idAdd2 idSub2 idNeg @@ -40,7 +40,7 @@ var builtinGates = [...]*gkrtypes.RegisteredGate{ func newGateRegistry(curve ecc.ID) gateRegistry { res := gateRegistry{ - ids: make(map[uintptr][]gkrtypes.GateID), + ids: make(map[uintptr][]gateID), gates: slices.Clone(builtinGates[:]), } @@ -62,7 +62,7 @@ func newGateRegistry(curve ecc.ID) gateRegistry { // getID looks up f in the cache, adding it if necessary. // Gate ID is returned. -func (r *gateRegistry) getID(f gkr.GateFunction, nbIn int) gkrtypes.GateID { +func (r *gateRegistry) getID(f gkr.GateFunction, nbIn int) gateID { bytecode, err := gkrtypes.CompileGateFunction(f, nbIn) if err != nil { panic(err) @@ -99,27 +99,27 @@ func (r *gateRegistry) getID(f gkr.GateFunction, nbIn int) gkrtypes.GateID { panic("cannot find degree for gate") } - id := gkrtypes.GateID(len(r.gates)) + id := gateID(len(r.gates)) r.gates = append(r.gates, &g) r.ids[ptr] = append(r.ids[ptr], id) return id } -func (r *gateRegistry) toExecutableCircuit(c gkrtypes.SerializableCircuit) gkrtypes.ExecutableCircuit { - res := make(gkrtypes.ExecutableCircuit, len(c)) +func (r *gateRegistry) toSerializableCircuit(c circuit) gkrtypes.SerializableCircuit { + res := make(gkrtypes.SerializableCircuit, len(c)) for i := range c { res[i].Inputs = c[i].Inputs - res[i].Gate = gkrtypes.ToExecutableGate(r.gates[c[i].Gate]) + res[i].Gate = gkrtypes.ToSerializableGate(r.gates[c[i].Gate]) } return res } -func (r *gateRegistry) getGateFunction(id gkrtypes.GateID) gkr.GateFunction { +func (r *gateRegistry) getGateFunction(id gateID) gkr.GateFunction { return r.gates[id].Evaluate.SnarkFriendly } -func (r *gateRegistry) toGadgetCircuit(c gkrtypes.SerializableCircuit) gkrtypes.GadgetCircuit { +func (r *gateRegistry) toGadgetCircuit(c circuit) gkrtypes.GadgetCircuit { res := make(gkrtypes.GadgetCircuit, len(c)) for i := range c { res[i].Inputs = c[i].Inputs From 2455effab6d33785b13bf3a641efd57c461ea14f Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Thu, 12 Feb 2026 21:51:36 -0600 Subject: [PATCH 172/251] snapshot: insoluble import cycle --- internal/gkr/bls12-381/gkr_test.go | 153 ++++--------------- internal/gkr/compile.go | 70 +++++++++ internal/gkr/gkrtesting/gkrtesting.go | 208 ++++++++++++++++++++------ internal/gkr/gkrtypes/types.go | 57 ++----- std/gkrapi/api.go | 34 ++--- std/gkrapi/api_test.go | 2 +- std/gkrapi/compile.go | 18 +-- std/gkrapi/example_test.go | 10 +- std/gkrapi/gateregistry.go | 135 ----------------- std/gkrapi/gkr/types.go | 20 --- 10 files changed, 297 insertions(+), 410 deletions(-) create mode 100644 internal/gkr/compile.go delete mode 100644 std/gkrapi/gateregistry.go diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index e1ecf58916..0b3fb627c7 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -15,12 +15,14 @@ import ( "testing" "time" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/mimc" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" gcUtils "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/frontend" + gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" @@ -28,11 +30,9 @@ import ( "github.com/stretchr/testify/assert" ) -var ( - identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) - add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) - mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) -) +func toSerializableCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { + return gadget.ToSerializableCircuit(ecc.BLS12_381, circuit) +} func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case @@ -40,126 +40,49 @@ func TestNoGateTwoInstances(t *testing.T) { } func TestNoGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - }) + test(t, gkrtesting.NoGateCircuit()) } func TestSingleAddGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - }, - { - Gate: add2Gate, - Inputs: []int{0, 1}, - }, - }) + test(t, gkrtesting.SingleAddGateCircuit()) } func TestSingleMulGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - }, - { - Gate: mul2Gate, - Inputs: []int{0, 1}, - }, - }) + test(t, gkrtesting.SingleMulGateCircuit()) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - Inputs: []int{0}, - }, - { - Gate: identityGate, - Inputs: []int{0}, - }, - }) + test(t, gkrtesting.SingleInputTwoIdentityGatesCircuit()) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - Inputs: []int{0}, - }, - { - Gate: identityGate, - Inputs: []int{1}, - }, - }) + test(t, gkrtesting.SingleInputTwoIdentityGatesComposedCircuit()) } func TestAPowNTimesBCircuit(t *testing.T) { - const N = 10 - - c := make(Circuit, N+2) - - // Input wires - c[0] = Wire{ - Gate: identityGate, - } - c[1] = Wire{ - Gate: identityGate, - } - - for i := 2; i < len(c); i++ { - c[i] = Wire{ - Gate: mul2Gate, - Inputs: []int{i - 1, 0}, - } - } - - test(t, c) + test(t, gkrtesting.APowNTimesBCircuit(10)) } func TestSingleMimcCipherGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - }, - { - Inputs: []int{0, 1}, - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), - }, - }) + test(t, gkrtesting.SingleMimcCipherGateCircuit()) } func TestShallowMimcTwoInstances(t *testing.T) { - test(t, mimcCircuit(2)) + test(t, gkrtesting.MiMCCircuit(2)) } func TestMimc(t *testing.T) { - test(t, mimcCircuit(93)) + test(t, gkrtesting.MiMCCircuit(93)) } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := Circuit{Wire{ - Gate: identityGate, + gCircuit := gkrtypes.GadgetCircuit{{ + Gate: &gkrtypes.GadgetGate{ + Evaluate: gkrtypes.Identity, + }, NbUniqueOutputs: 2, }} + circuit := toSerializableCircuit(gCircuit) assignment := WireAssignment{[]fr.Element{two, three}} var o settings @@ -214,8 +137,9 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit Circuit) { - wireRefs := utils.References(circuit) +func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { + sCircuit := toSerializableCircuit(circuit) + wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -235,19 +159,19 @@ func test(t *testing.T, circuit Circuit) { t.Log("Selected inputs for test") - proof, err := Prove(circuit, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(sCircuit, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(circuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) assert.NoError(t, err, "proof rejected") if proof.isEmpty() { // special case for TestNoGate: continue // there's no way to make a trivial proof fail } - err = Verify(circuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) + err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) assert.NotNil(t, err, "bad proof accepted") } @@ -270,7 +194,7 @@ func (p Proof) isEmpty() bool { func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { c := Circuit{ { - Gate: identityGate, + Gate: gkrtesting.IdentityGate, }, } @@ -285,26 +209,6 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { assert.NoError(t, err, "proof rejected") } -func mimcCircuit(numRounds int) Circuit { - c := make(Circuit, numRounds+2) - - // Input wires - c[0] = Wire{ - Gate: identityGate, - } - c[1] = Wire{ - Gate: identityGate, - } - - for i := 2; i < len(c); i++ { - c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), - Inputs: []int{i - 1, 0}, - } - } - return c -} - func TestIsAdditive(t *testing.T) { // f: x,y -> x² + xy @@ -416,7 +320,8 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := mimcCircuit(mimcDepth) + gc := gkrtesting.MiMCCircuit(mimcDepth) + c := toSerializableCircuit(gc) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) @@ -505,7 +410,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := toSerializableCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/compile.go b/internal/gkr/compile.go new file mode 100644 index 0000000000..09926d2d9c --- /dev/null +++ b/internal/gkr/compile.go @@ -0,0 +1,70 @@ +package gkr + +import ( + "fmt" + + "github.com/consensys/gnark-crypto/ecc" + bls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" + bls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" + bn254 "github.com/consensys/gnark/internal/gkr/bn254" + bw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" + "github.com/consensys/gnark/internal/gkr/gkrtypes" +) + +// GateTester is an interface for testing gate properties (degree, additivity). +type GateTester interface { + IsAdditive(varIndex int) bool + FindDegree() int + SetGate(g *gkrtypes.GateBytecode, nbIn int) +} + +// NewGateTester returns a curve-specific GateTester. +func NewGateTester(curve ecc.ID) GateTester { + switch curve { + case ecc.BLS12_377: + return &bls12377.GateTester{} + case ecc.BLS12_381: + return &bls12381.GateTester{} + case ecc.BN254: + return &bn254.GateTester{} + case ecc.BW6_761: + return &bw6761.GateTester{} + default: + panic(fmt.Errorf("unsupported curve %s", curve)) + } +} + +// ToSerializableCircuit converts a gadget circuit to a serializable circuit by compiling the gate functions. +// It also sets the gate metadata (Degree, SolvableVar) for both the input and output circuits. +func ToSerializableCircuit(curve ecc.ID, c gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { + tester := NewGateTester(curve) + + var err error + res := make(gkrtypes.SerializableCircuit, len(c)) + for i := range c { + res[i].Inputs = c[i].Inputs + + c[i].Gate.NbIn = len(c[i].Inputs) + res[i].Gate.NbIn = c[i].Gate.NbIn + + if res[i].Gate.Evaluate, err = gkrtypes.CompileGateFunction(c[i].Gate.Evaluate, c[i].Gate.NbIn); err != nil { + panic(err) + } + + tester.SetGate(res[i].Gate.Evaluate, c[i].Gate.NbIn) + + if c[i].Gate.Degree = tester.FindDegree(); c[i].Gate.Degree == -1 { + panic("cannot find degree for gate") + } + res[i].Gate.Degree = c[i].Gate.Degree + + for j := range c[i].Inputs { + if tester.IsAdditive(j) { + c[i].Gate.SolvableVar = j + break + } + } + res[i].Gate.SolvableVar = c[i].Gate.SolvableVar + } + return res +} diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 8baa62c80a..cfe52c1d06 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -3,71 +3,109 @@ package gkrtesting import ( "encoding/json" "errors" + "fmt" "os" "path/filepath" - "github.com/consensys/gnark" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" + gkrbls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" + gkrbls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" + gkrbn254 "github.com/consensys/gnark/internal/gkr/bn254" + gkrbw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/std/gkrapi/gkr" ) +func must(g *gkrtypes.GateBytecode, err error) *gkrtypes.GateBytecode { + if err != nil { + panic(err) + } + return g +} + +// Exported compiled gates for tests that need SerializableGate (bytecode gates) +var ( + IdentityGate *gkrtypes.SerializableGate + Add2Gate *gkrtypes.SerializableGate + Mul2Gate *gkrtypes.SerializableGate +) + +func init() { + IdentityGate = &gkrtypes.SerializableGate{ + Evaluate: must(gkrtypes.CompileGateFunction(gkrtypes.Identity, 1)), + NbIn: 1, + Degree: 1, + SolvableVar: 0, + } + Add2Gate = &gkrtypes.SerializableGate{ + Evaluate: must(gkrtypes.CompileGateFunction(gkrtypes.Add2, 2)), + NbIn: 2, + Degree: 1, + SolvableVar: 0, + } + Mul2Gate = &gkrtypes.SerializableGate{ + Evaluate: must(gkrtypes.CompileGateFunction(gkrtypes.Mul2, 2)), + NbIn: 2, + Degree: 2, + SolvableVar: -1, + } +} + // Cache for circuits and gates. // The main functionality is to cache whole circuits, but this package needs to use its own gate registry, in order to avoid import cycles. // Cache is used in tests for the per-curve GKR packages, but they in turn provide gate degree discovery functions to the gkrgates package. type Cache struct { - circuits map[string]gkrtypes.RegisteredCircuit - gates map[gkr.GateName]*gkrtypes.RegisteredGate + circuits map[string]gkrtypes.GadgetCircuit + gates map[string]gkr.GateFunction +} + +func mimcGate(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { + sum := api.Add(input[0], input[1]) //.Add(&sum, &m.ark) TODO: add ark + res := api.Mul(sum, sum) // sum^2 + res = api.Mul(res, sum) // sum^3 + res = api.Mul(res, res) // sum^6 + res = api.Mul(res, sum) // sum^7 + + return res +} + +func selectInput3Gate(_ gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return in[2] } func NewCache() *Cache { - gates := make(map[gkr.GateName]*gkrtypes.RegisteredGate, 7) - gates[gkr.Identity] = gkrtypes.Identity() - gates[gkr.Add2] = gkrtypes.Add2() - gates[gkr.Sub2] = gkrtypes.Sub2() - gates[gkr.Neg] = gkrtypes.Neg() - gates[gkr.Mul2] = gkrtypes.Mul2() - mimcF := func(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { - sum := api.Add(input[0], input[1]) //.Add(&sum, &m.ark) TODO: add ark - res := api.Mul(sum, sum) // sum^2 - res = api.Mul(res, sum) // sum^3 - res = api.Mul(res, res) // sum^6 - res = api.Mul(res, sum) // sum^7 - - return res - } - mimcCompiled, err := gkrtypes.CompileGateFunction(mimcF, 2) - if err != nil { - panic(err) - } - gates["mimc"] = gkrtypes.NewGate(mimcF, mimcCompiled, 2, 7, -1, gnark.Curves()) - gates["select-input-3"] = gkrtypes.NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - return in[2] - }, &gkrtypes.GateBytecode{}, 3, 1, 0, gnark.Curves()) + gates := make(map[string]gkr.GateFunction, 7) + gates[""] = gkrtypes.Identity + gates["identity"] = gkrtypes.Identity + gates["add2"] = gkrtypes.Add2 + gates["sub2"] = gkrtypes.Sub2 + gates["neg"] = gkrtypes.Neg + gates["mul2"] = gkrtypes.Mul2 + gates["mimc"] = mimcGate + gates["select-input-3"] = selectInput3Gate return &Cache{ - circuits: make(map[string]gkrtypes.RegisteredCircuit), + circuits: make(map[string]gkrtypes.GadgetCircuit), gates: gates, } } // JSONWire is the JSON serialization format for circuit wires (gate name + inputs) type JSONWire struct { - Gate *gkr.GateName `json:"gate"` // gate name, null for input wires - Inputs []int `json:"inputs"` // indices of input wires + Gate string `json:"gate"` // gate name, empty for input wires + Inputs []int `json:"inputs"` // indices of input wires } // JSONCircuit is the JSON serialization format for circuits type JSONCircuit []JSONWire -func (c *Cache) GetCircuit(path string) gkrtypes.RegisteredCircuit { +func (c *Cache) GetCircuit(path string) gkrtypes.GadgetCircuit { path, err := filepath.Abs(path) if err != nil { panic(err) } - var ok bool - var circuit gkrtypes.RegisteredCircuit - if circuit, ok = c.circuits[path]; ok { + if circuit, ok := c.circuits[path]; ok { return circuit } @@ -82,20 +120,13 @@ func (c *Cache) GetCircuit(path string) gkrtypes.RegisteredCircuit { panic(err) } - // Convert JSON format to RegisteredCircuit - circuit = make(gkrtypes.RegisteredCircuit, len(jsonCircuit)) + // Convert JSON format to GadgetCircuit + circuit := make(gkrtypes.GadgetCircuit, len(jsonCircuit)) for i, wJSON := range jsonCircuit { - var gate *gkrtypes.RegisteredGate - if wJSON.Gate == nil { - // Input wire - use identity gate - gate = gkrtypes.Identity() - } else { - // Look up gate by name in cache - gate = c.GetGate(*wJSON.Gate) - } + gate := c.GetGate(wJSON.Gate) - circuit[i] = gkrtypes.RegisteredWire{ - Gate: gate, + circuit[i] = gkrtypes.GadgetWire{ + Gate: &gkrtypes.Gate[gkr.GateFunction]{Evaluate: gate}, Inputs: wJSON.Inputs, } } @@ -105,18 +136,32 @@ func (c *Cache) GetCircuit(path string) gkrtypes.RegisteredCircuit { return circuit } -func (c *Cache) RegisterGate(name gkr.GateName, gate *gkrtypes.RegisteredGate) { +func (c *Cache) RegisterGate(name string, gate gkr.GateFunction) { if _, ok := c.gates[name]; ok { panic("gate already registered") } c.gates[name] = gate } -func (c *Cache) GetGate(name gkr.GateName) *gkrtypes.RegisteredGate { +func (c *Cache) GetGate(name string) gkr.GateFunction { if gate, ok := c.gates[name]; ok { return gate } - panic("gate not found") + panic("gate not found: " + name) +} + +func MiMCCircuit(numRounds int) gkrtypes.GadgetCircuit { + c := make(gkrtypes.GadgetCircuit, numRounds+2) + idGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} + mimc := &gkrtypes.GadgetGate{Evaluate: mimcGate} + + c[0] = gkrtypes.GadgetWire{Gate: idGate} + c[1] = gkrtypes.GadgetWire{Gate: idGate} + + for i := 2; i < len(c); i++ { + c[i] = gkrtypes.GadgetWire{Gate: mimc, Inputs: []int{i - 1, 0}} + } + return c } type PrintableProof []PrintableSumcheckProof @@ -146,3 +191,70 @@ func (c *Cache) ReadTestCaseInfo(filePath string) (info TestCaseInfo, err error) err = json.NewDecoder(f).Decode(&info) return } + +func NoGateCircuit() gkrtypes.GadgetCircuit { + return gkrtypes.GadgetCircuit{ + { + Gate: &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity}, + }, + } +} + +func SingleAddGateCircuit() gkrtypes.GadgetCircuit { + idGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} + return gkrtypes.GadgetCircuit{ + {Gate: idGate}, + {Gate: idGate}, + {Gate: &gkrtypes.GadgetGate{Evaluate: gkrtypes.Add2}, Inputs: []int{0, 1}}, + } +} + +func SingleMulGateCircuit() gkrtypes.GadgetCircuit { + idGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} + return gkrtypes.GadgetCircuit{ + {Gate: idGate}, + {Gate: idGate}, + {Gate: &gkrtypes.GadgetGate{Evaluate: gkrtypes.Mul2}, Inputs: []int{0, 1}}, + } +} + +func SingleInputTwoIdentityGatesCircuit() gkrtypes.GadgetCircuit { + idGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} + return gkrtypes.GadgetCircuit{ + {Gate: idGate}, + {Gate: idGate, Inputs: []int{0}}, + {Gate: idGate, Inputs: []int{0}}, + } +} + +func SingleInputTwoIdentityGatesComposedCircuit() gkrtypes.GadgetCircuit { + idGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} + return gkrtypes.GadgetCircuit{ + {Gate: idGate}, + {Gate: idGate, Inputs: []int{0}}, + {Gate: idGate, Inputs: []int{1}}, + } +} + +func APowNTimesBCircuit(n int) gkrtypes.GadgetCircuit { + c := make(gkrtypes.GadgetCircuit, n+2) + idGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} + mulGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Mul2} + + c[0] = gkrtypes.GadgetWire{Gate: idGate} + c[1] = gkrtypes.GadgetWire{Gate: idGate} + + for i := 2; i < len(c); i++ { + c[i] = gkrtypes.GadgetWire{Gate: mulGate, Inputs: []int{i - 1, 0}} + } + return c +} + +func SingleMimcCipherGateCircuit() gkrtypes.GadgetCircuit { + idGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} + return gkrtypes.GadgetCircuit{ + {Gate: idGate}, + {Gate: idGate}, + {Gate: &gkrtypes.GadgetGate{Evaluate: mimcGate}, Inputs: []int{0, 1}}, + } +} diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index f6bbd3b0c8..69d44f4520 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -1,9 +1,6 @@ package gkrtypes import ( - "errors" - - "github.com/consensys/gnark" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" @@ -51,11 +48,13 @@ type ( RegisteredWires = Wires[BothExecutables] // Serializable types (bytecode only, for native proving) + SerializableGate = Gate[*GateBytecode] SerializableCircuit = Circuit[*GateBytecode] SerializableWire = Wire[*GateBytecode] SerializableWires = Wires[*GateBytecode] // Gadget types (gate functions only, for in-circuit verification) + GadgetGate = Gate[gkr.GateFunction] GadgetCircuit = Circuit[gkr.GateFunction] GadgetWire = Wire[gkr.GateFunction] GadgetWires = Wires[gkr.GateFunction] @@ -328,63 +327,31 @@ func (c Circuit[GateExecutable]) TopologicalSort() Wires[GateExecutable] { return sorted } -var ErrZeroFunction = errors.New("detected a zero function") - // some sample gates // Identity gate: x -> x -func Identity() *RegisteredGate { - return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - return in[0] - }, &GateBytecode{}, 1, 1, 0, gnark.Curves()) +func Identity(_ gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return in[0] } // Add2 gate: (x, y) -> x + y -func Add2() *RegisteredGate { - return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - return api.Add(in[0], in[1]) - }, &GateBytecode{ - Instructions: []GateInstruction{{ - Op: OpAdd, - Inputs: []uint16{0, 1}, - }}, - }, 2, 1, 0, gnark.Curves()) +func Add2(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Add(in[0], in[1]) } // Sub2 gate: (x, y) -> x - y -func Sub2() *RegisteredGate { - return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - return api.Sub(in[0], in[1]) - }, &GateBytecode{ - Instructions: []GateInstruction{{ - Op: OpSub, - Inputs: []uint16{0, 1}, - }}, - }, 2, 1, 0, gnark.Curves()) +func Sub2(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Sub(in[0], in[1]) } // Neg gate: x -> -x -func Neg() *RegisteredGate { - return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - return api.Neg(in[0]) - }, &GateBytecode{ - Instructions: []GateInstruction{{ - Op: OpNeg, - Inputs: []uint16{0}, - }}, - }, 1, 1, 0, gnark.Curves()) +func Neg(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Neg(in[0]) } // Mul2 gate: (x, y) -> x * y -func Mul2() *RegisteredGate { - return NewGate(func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - return api.Mul(in[0], in[1]) - }, &GateBytecode{ - Instructions: []GateInstruction{{ - Op: OpMul, - Inputs: []uint16{0, 1}, - }}, - }, 2, 2, -1, gnark.Curves()) +func Mul2(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Mul(in[0], in[1]) } func ConvertGate[GateExecutable, TargetGateExecutable any](g *Gate[GateExecutable], converter func(GateExecutable) TargetGateExecutable) *Gate[TargetGateExecutable] { diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index c376fb7b79..573ce12532 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -3,6 +3,7 @@ package gkrapi import ( "github.com/consensys/gnark/frontend" gadget "github.com/consensys/gnark/internal/gkr" + "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi/gkr" ) @@ -14,13 +15,10 @@ type ( Inputs []int } - circuit []wire - API struct { - circuit circuit - assignments gadget.WireAssignment - parentApi frontend.API - gateRegistry gateRegistry + circuit gkrtypes.GadgetCircuit + assignments gadget.WireAssignment + parentApi frontend.API } ) @@ -28,42 +26,38 @@ func frontendVarToInt(a gkr.Variable) int { return int(a) } -func (api *API) gate(id gateID, inputs ...gkr.Variable) gkr.Variable { - api.circuit = append(api.circuit, wire{ - Gate: id, +// Gate adds the given gate with the given inputs and returns its output wire. +func (api *API) Gate(gate gkr.GateFunction, inputs ...gkr.Variable) gkr.Variable { + api.circuit = append(api.circuit, gkrtypes.GadgetWire{ + Gate: &gkrtypes.Gate[gkr.GateFunction]{Evaluate: gate}, Inputs: utils.Map(inputs, frontendVarToInt), }) api.assignments = append(api.assignments, nil) return gkr.Variable(len(api.circuit) - 1) } -// Gate adds the given gate with the given inputs and returns its output wire. -func (api *API) Gate(gate gkr.GateFunction, inputs ...gkr.Variable) gkr.Variable { - return api.gate(api.gateRegistry.getID(gate, len(inputs)), inputs...) -} - -func (api *API) gate2PlusIn(gate gateID, in1, in2 gkr.Variable, in ...gkr.Variable) gkr.Variable { +func (api *API) gate2PlusIn(gate gkr.GateFunction, in1, in2 gkr.Variable, in ...gkr.Variable) gkr.Variable { inCombined := make([]gkr.Variable, 2+len(in)) inCombined[0] = in1 inCombined[1] = in2 for i := range in { inCombined[i+2] = in[i] } - return api.gate(gate, inCombined...) + return api.Gate(gate, inCombined...) } func (api *API) Add(i1, i2 gkr.Variable) gkr.Variable { - return api.gate2PlusIn(idAdd2, i1, i2) + return api.gate2PlusIn(gkrtypes.Add2, i1, i2) } func (api *API) Neg(i1 gkr.Variable) gkr.Variable { - return api.gate(idNeg, i1) + return api.Gate(gkrtypes.Neg, i1) } func (api *API) Sub(i1, i2 gkr.Variable) gkr.Variable { - return api.gate2PlusIn(idSub2, i1, i2) + return api.gate2PlusIn(gkrtypes.Sub2, i1, i2) } func (api *API) Mul(i1, i2 gkr.Variable) gkr.Variable { - return api.gate2PlusIn(idMul2, i1, i2) + return api.gate2PlusIn(gkrtypes.Mul2, i1, i2) } diff --git a/std/gkrapi/api_test.go b/std/gkrapi/api_test.go index e53a66ecd7..f360567d94 100644 --- a/std/gkrapi/api_test.go +++ b/std/gkrapi/api_test.go @@ -597,7 +597,7 @@ func newConstBytesPseudoHash(c int64, mod *big.Int) constBytesPseudoHash { } func init() { - // getID custom (constant) "hash" functions + // Register custom (constant) "hash" functions for _, v := range []int64{-1, -20} { name := strconv.Itoa(int(v)) stdHash.RegisterCustomHash(name, func(api frontend.API) (stdHash.FieldHasher, error) { diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index c61424295a..94380c8e7a 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -44,15 +44,14 @@ type Circuit struct { // New creates a new GKR API func New(api frontend.API) (*API, error) { return &API{ - parentApi: api, - gateRegistry: newGateRegistry(utils.FieldToCurve(api.Compiler().Field())), + parentApi: api, }, nil } // NewInput creates a new input variable. func (api *API) NewInput() gkr.Variable { i := len(api.circuit) - api.circuit = append(api.circuit, wire{}) + api.circuit = append(api.circuit, gkrtypes.GadgetWire{Gate: &gkrtypes.Gate[gkr.GateFunction]{Evaluate: gkrtypes.Identity}}) api.assignments = append(api.assignments, nil) return gkr.Variable(i) } @@ -72,7 +71,7 @@ func WithInitialChallenge(getInitialChallenge InitialChallengeGetter) CompileOpt // but instances can be added to it. func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*Circuit, error) { res := Circuit{ - circuit: api.gateRegistry.toGadgetCircuit(api.circuit), + circuit: api.circuit, assignments: make(gadget.WireAssignment, len(api.circuit)), api: api.parentApi, hashName: fiatshamirHashName, @@ -81,18 +80,17 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C // Dispatch to curve-specific factory curveID := utils.FieldToCurve(api.parentApi.Compiler().Field()) compiler := api.parentApi.Compiler() - - eCircuit := api.gateRegistry.toSerializableCircuit(api.circuit) + serializableCircuit := gadget.ToSerializableCircuit(curveID, api.circuit) switch curveID { case ecc.BN254: - res.blueprints = gkrbn254.NewBlueprints(eCircuit, fiatshamirHashName, compiler) + res.blueprints = gkrbn254.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) case ecc.BLS12_377: - res.blueprints = gkrbls12377.NewBlueprints(eCircuit, fiatshamirHashName, compiler) + res.blueprints = gkrbls12377.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) case ecc.BLS12_381: - res.blueprints = gkrbls12381.NewBlueprints(eCircuit, fiatshamirHashName, compiler) + res.blueprints = gkrbls12381.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) case ecc.BW6_761: - res.blueprints = gkrbw6761.NewBlueprints(eCircuit, fiatshamirHashName, compiler) + res.blueprints = gkrbw6761.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) default: return nil, fmt.Errorf("unsupported curve: %s", curveID) } diff --git a/std/gkrapi/example_test.go b/std/gkrapi/example_test.go index 9122572feb..d6a5fa7f54 100644 --- a/std/gkrapi/example_test.go +++ b/std/gkrapi/example_test.go @@ -7,9 +7,10 @@ import ( "github.com/consensys/gnark-crypto/ecc" bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/std/gkrapi" "github.com/consensys/gnark/std/gkrapi/gkr" - _ "github.com/consensys/gnark/std/hash/all" // import all hash functions to getID them + _ "github.com/consensys/gnark/std/hash/all" // import all hash functions to register them "github.com/consensys/gnark/test" ) @@ -98,7 +99,7 @@ func (c *exampleCircuit) Define(api frontend.API) error { YOut := gkrApi.Gate(yGate, S, XOut, XX, YYYY) // 423 - 426 // have to duplicate X for it to be considered an output variable; this is an implementation detail and will be fixed in the future [https://github.com/Consensys/gnark/issues/1452] - XOut = gkrApi.Gate(identityGate, XOut) + XOut = gkrApi.Gate(gkrtypes.Identity, XOut) gkrCircuit, err := gkrApi.Compile("MIMC") if err != nil { @@ -125,11 +126,6 @@ func (c *exampleCircuit) Define(api frontend.API) error { // custom gates -// identityGate x -> x -func identityGate(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { - return api.Add(input[0], 0) -} - // squareGate x -> x² func squareGate(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { return api.Mul(input[0], input[0]) diff --git a/std/gkrapi/gateregistry.go b/std/gkrapi/gateregistry.go deleted file mode 100644 index 6af703f322..0000000000 --- a/std/gkrapi/gateregistry.go +++ /dev/null @@ -1,135 +0,0 @@ -package gkrapi - -import ( - "fmt" - "reflect" - "slices" - - "github.com/consensys/gnark-crypto/ecc" - bls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" - bls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" - bn254 "github.com/consensys/gnark/internal/gkr/bn254" - bw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/std/gkrapi/gkr" -) - -type ( - gateRegistry struct { - ids map[uintptr][]gateID // ids maps a gate function to gates defined using it. - gates []*gkrtypes.RegisteredGate - tester gateTester - } -) - -const ( - idIdentity gateID = iota - idAdd2 - idSub2 - idNeg - idMul2 -) - -var builtinGates = [...]*gkrtypes.RegisteredGate{ - idIdentity: gkrtypes.Identity(), - idAdd2: gkrtypes.Add2(), - idSub2: gkrtypes.Sub2(), - idNeg: gkrtypes.Neg(), - idMul2: gkrtypes.Mul2(), -} - -func newGateRegistry(curve ecc.ID) gateRegistry { - res := gateRegistry{ - ids: make(map[uintptr][]gateID), - gates: slices.Clone(builtinGates[:]), - } - - switch curve { - case ecc.BLS12_377: - res.tester = &bls12377.GateTester{} - case ecc.BLS12_381: - res.tester = &bls12381.GateTester{} - case ecc.BN254: - res.tester = &bn254.GateTester{} - case ecc.BW6_761: - res.tester = &bw6761.GateTester{} - default: - panic(fmt.Errorf("unsupported curve %s", curve)) - } - - return res -} - -// getID looks up f in the cache, adding it if necessary. -// Gate ID is returned. -func (r *gateRegistry) getID(f gkr.GateFunction, nbIn int) gateID { - bytecode, err := gkrtypes.CompileGateFunction(f, nbIn) - if err != nil { - panic(err) - } - - ptr := reflect.ValueOf(f).Pointer() - - for _, id := range r.ids[ptr] { - if reflect.DeepEqual(r.gates[id].Evaluate.Bytecode, bytecode) { - return id - } - } - - r.tester.SetGate(bytecode, nbIn) - - g := gkrtypes.RegisteredGate{ - Evaluate: gkrtypes.BothExecutables{ - Bytecode: bytecode, - SnarkFriendly: f, - }, - NbIn: nbIn, - Degree: r.tester.FindDegree(), - SolvableVar: -1, - } - - for i := range nbIn { - if r.tester.IsAdditive(i) { - g.SolvableVar = i - break - } - } - - if g.Degree == -1 { - panic("cannot find degree for gate") - } - - id := gateID(len(r.gates)) - r.gates = append(r.gates, &g) - r.ids[ptr] = append(r.ids[ptr], id) - - return id -} - -func (r *gateRegistry) toSerializableCircuit(c circuit) gkrtypes.SerializableCircuit { - res := make(gkrtypes.SerializableCircuit, len(c)) - for i := range c { - res[i].Inputs = c[i].Inputs - res[i].Gate = gkrtypes.ToSerializableGate(r.gates[c[i].Gate]) - } - return res -} - -func (r *gateRegistry) getGateFunction(id gateID) gkr.GateFunction { - return r.gates[id].Evaluate.SnarkFriendly -} - -func (r *gateRegistry) toGadgetCircuit(c circuit) gkrtypes.GadgetCircuit { - res := make(gkrtypes.GadgetCircuit, len(c)) - for i := range c { - res[i].Inputs = c[i].Inputs - res[i].Gate = gkrtypes.ToGadgetGate(r.gates[c[i].Gate]) - } - return res -} - -type gateTester interface { - IsAdditive(varIndex int) bool - FindDegree() int - SetGate(g *gkrtypes.GateBytecode, nbIn int) -} diff --git a/std/gkrapi/gkr/types.go b/std/gkrapi/gkr/types.go index f5235055ba..4610c70899 100644 --- a/std/gkrapi/gkr/types.go +++ b/std/gkrapi/gkr/types.go @@ -42,23 +42,3 @@ type GateAPI interface { // using the given GateAPI. // It is used to define custom gates in GKR circuits. type GateFunction func(GateAPI, ...frontend.Variable) frontend.Variable - -// GateName is a string representing a (human-readable) name for a GKR gate. -type GateName string - -const ( - // Identity gate: x -> x - Identity GateName = "identity" - - // Add2 gate: (x, y) -> x + y - Add2 GateName = "add2" - - // Sub2 gate: (x, y) -> x - y - Sub2 GateName = "sub2" - - // Neg gate: x -> -x - Neg GateName = "neg" - - // Mul2 gate: (x, y) -> x * y - Mul2 GateName = "mul2" -) From 1443d0ab635e25700a9da5d097eeb8d784ff4ad8 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Thu, 12 Feb 2026 22:59:14 -0600 Subject: [PATCH 173/251] refactor: isAdditive --- internal/gkr/compile.go | 58 ------ internal/gkr/gkrtesting/gkrtesting.go | 6 - internal/gkr/gkrtypes/compiledgate.go | 274 ++++++++++++++++++++++++++ 3 files changed, 274 insertions(+), 64 deletions(-) diff --git a/internal/gkr/compile.go b/internal/gkr/compile.go index 09926d2d9c..718ae2aaac 100644 --- a/internal/gkr/compile.go +++ b/internal/gkr/compile.go @@ -10,61 +10,3 @@ import ( bw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" "github.com/consensys/gnark/internal/gkr/gkrtypes" ) - -// GateTester is an interface for testing gate properties (degree, additivity). -type GateTester interface { - IsAdditive(varIndex int) bool - FindDegree() int - SetGate(g *gkrtypes.GateBytecode, nbIn int) -} - -// NewGateTester returns a curve-specific GateTester. -func NewGateTester(curve ecc.ID) GateTester { - switch curve { - case ecc.BLS12_377: - return &bls12377.GateTester{} - case ecc.BLS12_381: - return &bls12381.GateTester{} - case ecc.BN254: - return &bn254.GateTester{} - case ecc.BW6_761: - return &bw6761.GateTester{} - default: - panic(fmt.Errorf("unsupported curve %s", curve)) - } -} - -// ToSerializableCircuit converts a gadget circuit to a serializable circuit by compiling the gate functions. -// It also sets the gate metadata (Degree, SolvableVar) for both the input and output circuits. -func ToSerializableCircuit(curve ecc.ID, c gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - tester := NewGateTester(curve) - - var err error - res := make(gkrtypes.SerializableCircuit, len(c)) - for i := range c { - res[i].Inputs = c[i].Inputs - - c[i].Gate.NbIn = len(c[i].Inputs) - res[i].Gate.NbIn = c[i].Gate.NbIn - - if res[i].Gate.Evaluate, err = gkrtypes.CompileGateFunction(c[i].Gate.Evaluate, c[i].Gate.NbIn); err != nil { - panic(err) - } - - tester.SetGate(res[i].Gate.Evaluate, c[i].Gate.NbIn) - - if c[i].Gate.Degree = tester.FindDegree(); c[i].Gate.Degree == -1 { - panic("cannot find degree for gate") - } - res[i].Gate.Degree = c[i].Gate.Degree - - for j := range c[i].Inputs { - if tester.IsAdditive(j) { - c[i].Gate.SolvableVar = j - break - } - } - res[i].Gate.SolvableVar = c[i].Gate.SolvableVar - } - return res -} diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index cfe52c1d06..92abaef677 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -3,16 +3,10 @@ package gkrtesting import ( "encoding/json" "errors" - "fmt" "os" "path/filepath" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" - gkrbls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" - gkrbls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" - gkrbn254 "github.com/consensys/gnark/internal/gkr/bn254" - gkrbw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/std/gkrapi/gkr" ) diff --git a/internal/gkr/gkrtypes/compiledgate.go b/internal/gkr/gkrtypes/compiledgate.go index a4441f21ed..9c6c448f28 100644 --- a/internal/gkr/gkrtypes/compiledgate.go +++ b/internal/gkr/gkrtypes/compiledgate.go @@ -1,12 +1,21 @@ package gkrtypes import ( + crand "crypto/rand" "errors" + "fmt" "math/big" + "slices" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" + bls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" + bls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" + bn254 "github.com/consensys/gnark/internal/gkr/bn254" + bw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/consensys/gnark/std/polynomial" ) // GateOp represents an arithmetic operation in a compiled gate. @@ -225,6 +234,80 @@ func (gc *gateCompiler) remapIndices() { } } + +func (t *gateTester) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := new(big.Int).Set(i1.(*big.Int)) + res.Add(res, i2.(*big.Int)) + for _, v := range in { + res.Add(res, v.(*big.Int)) + } + return res.Mod(res, t.mod) +} + +func (t *gateTester) MulAcc(a, b, c frontend.Variable) frontend.Variable { + prod := new(big.Int).Mul(b.(*big.Int), c.(*big.Int)) + res := new(big.Int).Add(a.(*big.Int), prod) + return res.Mod(res, t.mod) +} + +func (t *gateTester) Neg(i1 frontend.Variable) frontend.Variable { + res := new(big.Int).Neg(i1.(*big.Int)) + return res.Mod(res, t.mod) +} + +func (t *gateTester) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := new(big.Int).Set(i1.(*big.Int)) + res.Sub(res, i2.(*big.Int)) + for _, v := range in { + res.Sub(res, v.(*big.Int)) + } + return res.Mod(res, t.mod) +} + +func (t *gateTester) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { + res := new(big.Int).Mul(i1.(*big.Int), i2.(*big.Int)) + res.Mod(res, t.mod) + for _, v := range in { + res.Mul(res, v.(*big.Int)) + res.Mod(res, t.mod) + } + return res +} + +func (t *gateTester) SumExp17(a, b, c frontend.Variable) frontend.Variable { + sum := new(big.Int).Add(a.(*big.Int), b.(*big.Int)) + sum.Add(sum, c.(*big.Int)) + sum.Mod(sum, t.mod) + res := new(big.Int).Exp(sum, big.NewInt(17), t.mod) + return res +} + +func (t *gateTester) IsZero(a frontend.Variable) bool { + v := new(big.Int).Mod(a.(*big.Int), t.mod) + return v.BitLen() == 0 +} + +func (t *gateTester) Equal(a, b frontend.Variable) bool { + diff := t.Sub(a, b).(*big.Int) + return diff.BitLen() == 0 +} + +func (t *gateTester) randomElement() *big.Int { + res, err := crand.Int(crand.Reader, t.mod) + if err != nil { + panic(err) + } + return res +} + +func (t *gateTester) randomVector(n int) polynomial.Polynomial { + res := make([]frontend.Variable, n) + for i := range res { + res[i] = t.randomElement() + } + return res +} + // CompileGateFunction compiles a gate function into a GateBytecode. // The gate function should be of type gkr.GateFunction. func CompileGateFunction(f gkr.GateFunction, nbInputs int) (*GateBytecode, error) { @@ -259,3 +342,194 @@ func CompileGateFunction(f gkr.GateFunction, nbInputs int) (*GateBytecode, error Constants: compiler.constants, }, nil } + +// ToSerializableCircuit converts a gadget circuit to a serializable circuit by compiling the gate functions. +// It also sets the gate metadata (Degree, SolvableVar) for both the input and output circuits. +func ToSerializableCircuit(mod *big.Int, c GadgetCircuit) SerializableCircuit { + tester := gateTester{mod} + + var err error + res := make(SerializableCircuit, len(c)) + for i := range c { + res[i].Inputs = c[i].Inputs + + c[i].Gate.NbIn = len(c[i].Inputs) + res[i].Gate.NbIn = c[i].Gate.NbIn + + if res[i].Gate.Evaluate, err = CompileGateFunction(c[i].Gate.Evaluate, c[i].Gate.NbIn); err != nil { + panic(err) + } + + c[i].Gate.Degree = len(tester.fitPoly(res[i].Gate.Evaluate.EstimateDegree(len(c[i].Inputs))))-1 + if res[i].Gate.Degree = tester.SetDegree(c[i].Gate); c[i].Gate.Degree == -1 { + panic("cannot find degree for gate") + } + + res[i].Gate.SolvableVar = -1 + for j := range c[i].Gate.NbIn { + if tester.isAdditive(c[i].Gate, j) { + res[i].Gate.SolvableVar = j + break + } + } + c[i].Gate.SolvableVar = res[i].Gate.SolvableVar + + tester.SetGate(res[i].Gate.Evaluate, c[i].Gate.NbIn) + + + res[i].Gate.SolvableVar = c[i].Gate.SolvableVar + } + return res +} + +type gateTester struct { + mod *big.Int +} + +// isAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in g +func (t *gateTester) isAdditive(g *Gate[gkr.GateFunction], i int) bool { + // fix all variables except the i-th one at random points + // pick random value x1 for the i-th variable + // check if f(-, 0, -) + f(-, 2*x1, -) = 2*f(-, x1, -) + in := t.randomVector(g.NbIn) + + x := t.randomElement() + in[i] = x + + y1 := g.Evaluate(t, in...) + + zero := new(big.Int) + in[i] = zero + y0 := g.Evaluate(t, in...) + + xDbl := t.Add(x, x) + in[i] = xDbl + y2 := g.Evaluate(t, in...) + + y2 = t.Sub(y2, y1) + y1 = t.Sub(y1, y0) + + if !t.Equal(y1, y2) { + return false // not linear + } + + // check if the coefficient of xᵢ is nonzero and independent of the other variables + if t.IsZero(y1) { + return false + } + + // compute the slope with another assignment for the other variables + in = t.randomVector(g.NbIn) + in[i] = zero + y0 = g.Evaluate(t, in...) + + in[i] = x + y1 = g.Evaluate(t, in...) + y1 = t.Sub(y1, y0) + + return t.Equal(y2, y1) +} + +// fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. +// degreeBound must be a power of 2. +// It returns the polynomial if successful, nil otherwise +func (t *gateTester) fitPoly(g *GadgetGate,degreeBound int) polynomial.Polynomial { + + // turn f univariate by defining p(x) as f(x, rx, ..., sx) + // where r, s, ... are random constants + fIn := make(polynomial.Polynomial, g.NbIn) + consts := t.randomVector(g.NbIn-1) + + p := make(polynomial.Polynomial, degreeBound) + + x := t.randomVector(degreeBound) + + for i := range x { + fIn[0] = x[i] + for j := range consts { + fIn[j+1].Mul(x[i], consts[j]) + } + + p[i].Set(t.f..evaluator.evaluate(fIn...)) + } + + // obtain p's coefficients + p, err := interpolate(x, p) + if err != nil { + panic(err) + } + + // check if p is equal to f. This not being the case means that f is of a degree higher than degreeBound + fIn[0].MustSetRandom() + for i := range consts { + fIn[i+1].Mul(&fIn[0], &consts[i]) + } + pAt := p.Eval(&fIn[0]) + fAt := *t.evaluator.evaluate(fIn...) + if !pAt.Equal(&fAt) { + return nil + } + + // trim p + lastNonZero := len(p) - 1 + for lastNonZero >= 0 && p[lastNonZero].IsZero() { + lastNonZero-- + } + return p[:lastNonZero+1] +} + +// interpolate fits a polynomial of degree len(X) - 1 = len(Y) - 1 to the points (X[i], Y[i]) +// Note that the runtime is O(len(X)³) +func interpolate(X, Y polynomial.Polynomial) (polynomial.Polynomial, error) { +if len(X) != len(Y) { +return nil, errors.New("same length expected for X and Y") +} + +// solve the system of equations by Gaussian elimination +augmentedRows := make([]polynomial.Polynomial, len(X)) // the last column is the Y values +for i := range augmentedRows { +augmentedRows[i] = make(polynomial.Polynomial, len(X)+1) +augmentedRows[i][0].SetOne() +augmentedRows[i][1].Set(&X[i]) +for j := 2; j < len(augmentedRows[i])-1; j++ { +augmentedRows[i][j].Mul(&augmentedRows[i][j-1], &X[i]) +} +augmentedRows[i][len(augmentedRows[i])-1].Set(&Y[i]) +} + +// make the upper triangle +for i := range len(augmentedRows) - 1 { +// use row i to eliminate the ith element in all rows below +var negInv *big.Int +if augmentedRows[i][i].IsZero() { +return nil, errors.New("singular matrix") +} +negInv.Inverse(&augmentedRows[i][i]) +negInv.Neg(negInv) +for j := i + 1; j < len(augmentedRows); j++ { +var c big.Int +c.Mul(augmentedRows[j][i], negInv) +// augmentedRows[j][i].SetZero() omitted +for k := i + 1; k < len(augmentedRows[i]); k++ { +var t *big.Int +t.Mul(augmentedRows[i][k], c) +augmentedRows[j][k].Add(augmentedRows[j][k], &) +} +} +} + +// back substitution +res := make(polynomial.Polynomial, len(X)) +for i := len(augmentedRows) - 1; i >= 0; i-- { +res[i] = augmentedRows[i][len(augmentedRows[i])-1] +for j := i + 1; j < len(augmentedRows[i])-1; j++ { +var t big.Int +t.Mul(res[j], augmentedRows[i][j]) +res[i].Sub(res[i], &t) +} +res[i].Div(res[i], augmentedRows[i][i]) +} + +return res, nil +} + From 158bb94d43a617666884d65c1a70cd38e85efd2c Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 00:11:30 -0600 Subject: [PATCH 174/251] feat: big.Int based gate testing --- debug_test.go | 2 +- .../gkr/gkrtypes/{compiledgate.go => gate.go} | 397 +++++++++--------- .../{compiledgate_test.go => gate_test.go} | 0 internal/gkr/gkrtypes/types.go | 43 +- 4 files changed, 238 insertions(+), 204 deletions(-) rename internal/gkr/gkrtypes/{compiledgate.go => gate.go} (60%) rename internal/gkr/gkrtypes/{compiledgate_test.go => gate_test.go} (100%) diff --git a/debug_test.go b/debug_test.go index 0b1548e66f..e5b543d9ee 100644 --- a/debug_test.go +++ b/debug_test.go @@ -111,7 +111,7 @@ func TestTraceDivBy0(t *testing.T) { } // ------------------------------------------------------------------------------------------------- -// Not Equal +// Not equal type notEqualTrace struct { A, B, C frontend.Variable } diff --git a/internal/gkr/gkrtypes/compiledgate.go b/internal/gkr/gkrtypes/gate.go similarity index 60% rename from internal/gkr/gkrtypes/compiledgate.go rename to internal/gkr/gkrtypes/gate.go index 9c6c448f28..0a56e073fa 100644 --- a/internal/gkr/gkrtypes/compiledgate.go +++ b/internal/gkr/gkrtypes/gate.go @@ -1,21 +1,14 @@ package gkrtypes import ( - crand "crypto/rand" + "crypto/rand" "errors" - "fmt" "math/big" - "slices" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" - bls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" - bls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" - bn254 "github.com/consensys/gnark/internal/gkr/bn254" - bw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" + "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi/gkr" - "github.com/consensys/gnark/std/polynomial" ) // GateOp represents an arithmetic operation in a compiled gate. @@ -234,80 +227,6 @@ func (gc *gateCompiler) remapIndices() { } } - -func (t *gateTester) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := new(big.Int).Set(i1.(*big.Int)) - res.Add(res, i2.(*big.Int)) - for _, v := range in { - res.Add(res, v.(*big.Int)) - } - return res.Mod(res, t.mod) -} - -func (t *gateTester) MulAcc(a, b, c frontend.Variable) frontend.Variable { - prod := new(big.Int).Mul(b.(*big.Int), c.(*big.Int)) - res := new(big.Int).Add(a.(*big.Int), prod) - return res.Mod(res, t.mod) -} - -func (t *gateTester) Neg(i1 frontend.Variable) frontend.Variable { - res := new(big.Int).Neg(i1.(*big.Int)) - return res.Mod(res, t.mod) -} - -func (t *gateTester) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := new(big.Int).Set(i1.(*big.Int)) - res.Sub(res, i2.(*big.Int)) - for _, v := range in { - res.Sub(res, v.(*big.Int)) - } - return res.Mod(res, t.mod) -} - -func (t *gateTester) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { - res := new(big.Int).Mul(i1.(*big.Int), i2.(*big.Int)) - res.Mod(res, t.mod) - for _, v := range in { - res.Mul(res, v.(*big.Int)) - res.Mod(res, t.mod) - } - return res -} - -func (t *gateTester) SumExp17(a, b, c frontend.Variable) frontend.Variable { - sum := new(big.Int).Add(a.(*big.Int), b.(*big.Int)) - sum.Add(sum, c.(*big.Int)) - sum.Mod(sum, t.mod) - res := new(big.Int).Exp(sum, big.NewInt(17), t.mod) - return res -} - -func (t *gateTester) IsZero(a frontend.Variable) bool { - v := new(big.Int).Mod(a.(*big.Int), t.mod) - return v.BitLen() == 0 -} - -func (t *gateTester) Equal(a, b frontend.Variable) bool { - diff := t.Sub(a, b).(*big.Int) - return diff.BitLen() == 0 -} - -func (t *gateTester) randomElement() *big.Int { - res, err := crand.Int(crand.Reader, t.mod) - if err != nil { - panic(err) - } - return res -} - -func (t *gateTester) randomVector(n int) polynomial.Polynomial { - res := make([]frontend.Variable, n) - for i := range res { - res[i] = t.randomElement() - } - return res -} - // CompileGateFunction compiles a gate function into a GateBytecode. // The gate function should be of type gkr.GateFunction. func CompileGateFunction(f gkr.GateFunction, nbInputs int) (*GateBytecode, error) { @@ -343,136 +262,212 @@ func CompileGateFunction(f gkr.GateFunction, nbInputs int) (*GateBytecode, error }, nil } -// ToSerializableCircuit converts a gadget circuit to a serializable circuit by compiling the gate functions. -// It also sets the gate metadata (Degree, SolvableVar) for both the input and output circuits. -func ToSerializableCircuit(mod *big.Int, c GadgetCircuit) SerializableCircuit { - tester := gateTester{mod} +type gateTester struct { + mod *big.Int + gate *GateBytecode + vars []*big.Int + frameSize int + nbIn int +} - var err error - res := make(SerializableCircuit, len(c)) - for i := range c { - res[i].Inputs = c[i].Inputs +func (t *gateTester) setGate(g *GateBytecode, nbIn int) { + t.gate = g + t.vars = make([]*big.Int, g.NbConstants()+nbIn+len(g.Instructions)) + t.frameSize = g.NbConstants() + t.nbIn = nbIn + copy(t.vars, g.Constants) +} - c[i].Gate.NbIn = len(c[i].Inputs) - res[i].Gate.NbIn = c[i].Gate.NbIn +func (t *gateTester) isZero(a *big.Int) bool { + v := new(big.Int).Mod(a, t.mod) + return v.BitLen() == 0 +} - if res[i].Gate.Evaluate, err = CompileGateFunction(c[i].Gate.Evaluate, c[i].Gate.NbIn); err != nil { - panic(err) - } +func (t *gateTester) equal(a, b *big.Int) bool { + return a.Cmp(b) == 0 +} - c[i].Gate.Degree = len(tester.fitPoly(res[i].Gate.Evaluate.EstimateDegree(len(c[i].Inputs))))-1 - if res[i].Gate.Degree = tester.SetDegree(c[i].Gate); c[i].Gate.Degree == -1 { - panic("cannot find degree for gate") - } +func (t *gateTester) add(a, b *big.Int) *big.Int { + res := new(big.Int).Add(a, b) + return res.Mod(res, t.mod) +} - res[i].Gate.SolvableVar = -1 - for j := range c[i].Gate.NbIn { - if tester.isAdditive(c[i].Gate, j) { - res[i].Gate.SolvableVar = j - break - } - } - c[i].Gate.SolvableVar = res[i].Gate.SolvableVar +func (t *gateTester) sub(a, b *big.Int) *big.Int { + res := new(big.Int).Sub(a, b) + return res.Mod(res, t.mod) +} - tester.SetGate(res[i].Gate.Evaluate, c[i].Gate.NbIn) +func (t *gateTester) mul(a, b *big.Int) *big.Int { + res := new(big.Int).Mul(a, b) + return res.Mod(res, t.mod) +} +func (t *gateTester) neg(a *big.Int) *big.Int { + res := new(big.Int).Neg(a) + return res.Mod(res, t.mod) +} + +func (t *gateTester) inverse(a *big.Int) *big.Int { + return new(big.Int).ModInverse(a, t.mod) +} - res[i].Gate.SolvableVar = c[i].Gate.SolvableVar +func (t *gateTester) div(a, b *big.Int) *big.Int { + res := new(big.Int).ModInverse(b, t.mod) + return res.Mul(a, res).Mod(res, t.mod) +} + +func (t *gateTester) randomElement() *big.Int { + res, err := rand.Int(rand.Reader, t.mod) + if err != nil { + panic(err) } return res } -type gateTester struct { - mod *big.Int +func (t *gateTester) randomElements(n int) []*big.Int { + res := make([]*big.Int, n) + for i := range res { + res[i] = t.randomElement() + } + return res +} + +func (t *gateTester) evalPoly(p []*big.Int, x *big.Int) *big.Int { + res := p[len(p)-1] + for i := len(p) - 2; i >= 0; i-- { + res = t.mul(res, x) + res = t.add(res, p[i]) + } + return res +} + +// evaluate executes the gate bytecode with the given inputs. +func (t *gateTester) evaluate(inputs ...*big.Int) *big.Int { + // Copy inputs into frame + copy(t.vars[t.frameSize:], inputs) + t.frameSize += len(inputs) + + // Execute instructions + for i, inst := range t.gate.Instructions { + dst := t.vars[t.frameSize+i] + if dst == nil { + dst = new(big.Int) + t.vars[t.frameSize+i] = dst + } + switch inst.Op { + case OpAdd: + dst.Set(t.vars[inst.Inputs[0]]) + for _, idx := range inst.Inputs[1:] { + dst.Add(dst, t.vars[idx]) + } + case OpSub: + dst.Set(t.vars[inst.Inputs[0]]) + for _, idx := range inst.Inputs[1:] { + dst.Sub(dst, t.vars[idx]) + } + case OpMul: + dst.Set(t.vars[inst.Inputs[0]]) + for _, idx := range inst.Inputs[1:] { + dst.Mul(dst, t.vars[idx]) + } + case OpNeg: + dst.Neg(t.vars[inst.Inputs[0]]) + case OpMulAcc: // a + b*c + dst.Mul(t.vars[inst.Inputs[1]], t.vars[inst.Inputs[2]]) + dst.Add(dst, t.vars[inst.Inputs[0]]) + case OpSumExp17: // (a + b + c)^17 + dst.Add(t.vars[inst.Inputs[0]], t.vars[inst.Inputs[1]]) + dst.Add(dst, t.vars[inst.Inputs[2]]) + dst.Exp(dst, big.NewInt(17), t.mod) + default: + panic("unknown operation") + } + dst.Mod(dst, t.mod) + } + + // Get result and reset frame + t.frameSize = t.gate.NbConstants() + return new(big.Int).Set(t.vars[t.frameSize+len(t.gate.Instructions)-1]) } -// isAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in g -func (t *gateTester) isAdditive(g *Gate[gkr.GateFunction], i int) bool { - // fix all variables except the i-th one at random points - // pick random value x1 for the i-th variable - // check if f(-, 0, -) + f(-, 2*x1, -) = 2*f(-, x1, -) - in := t.randomVector(g.NbIn) +// isAdditive returns whether xᵢ occurs only in a monomial of total degree 1 +func (t *gateTester) isAdditive(i int) bool { + in := t.randomElements(t.nbIn) x := t.randomElement() in[i] = x - - y1 := g.Evaluate(t, in...) + y1 := t.evaluate(in...) zero := new(big.Int) in[i] = zero - y0 := g.Evaluate(t, in...) + y0 := t.evaluate(in...) - xDbl := t.Add(x, x) - in[i] = xDbl - y2 := g.Evaluate(t, in...) + in[i] = t.add(x, x) + y2 := t.evaluate(in...) - y2 = t.Sub(y2, y1) - y1 = t.Sub(y1, y0) + // f(2x) - f(x) == f(x) - f(0) ? + y2 = t.sub(y2, y1) + y1 = t.sub(y1, y0) - if !t.Equal(y1, y2) { + if !t.equal(y1, y2) { return false // not linear } - // check if the coefficient of xᵢ is nonzero and independent of the other variables - if t.IsZero(y1) { - return false + if t.isZero(y1) { + return false // zero coefficient } - // compute the slope with another assignment for the other variables - in = t.randomVector(g.NbIn) + // check slope is independent of other variables + in = t.randomElements(t.nbIn) in[i] = zero - y0 = g.Evaluate(t, in...) + y0 = t.evaluate(in...) in[i] = x - y1 = g.Evaluate(t, in...) - y1 = t.Sub(y1, y0) + y1 = t.sub(t.evaluate(in...), y0) - return t.Equal(y2, y1) + return t.equal(y2, y1) } // fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. -// degreeBound must be a power of 2. -// It returns the polynomial if successful, nil otherwise -func (t *gateTester) fitPoly(g *GadgetGate,degreeBound int) polynomial.Polynomial { +// It returns the polynomial if successful, nil otherwise. +func (t *gateTester) fitPoly(degreeBound int) []*big.Int { // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants - fIn := make(polynomial.Polynomial, g.NbIn) - consts := t.randomVector(g.NbIn-1) - - p := make(polynomial.Polynomial, degreeBound) + fIn := make([]*big.Int, t.nbIn) + consts := t.randomElements(t.nbIn - 1) - x := t.randomVector(degreeBound) + p := make([]*big.Int, degreeBound) + x := t.randomElements(degreeBound) for i := range x { fIn[0] = x[i] for j := range consts { - fIn[j+1].Mul(x[i], consts[j]) + fIn[j+1] = t.mul(x[i], consts[j]) } - - p[i].Set(t.f..evaluator.evaluate(fIn...)) + p[i] = t.evaluate(fIn...) } // obtain p's coefficients - p, err := interpolate(x, p) + p, err := t.interpolate(x, p) if err != nil { panic(err) } // check if p is equal to f. This not being the case means that f is of a degree higher than degreeBound - fIn[0].MustSetRandom() + fIn[0] = t.randomElement() for i := range consts { - fIn[i+1].Mul(&fIn[0], &consts[i]) + fIn[i+1] = t.mul(fIn[0], consts[i]) } - pAt := p.Eval(&fIn[0]) - fAt := *t.evaluator.evaluate(fIn...) - if !pAt.Equal(&fAt) { + pAt := t.evalPoly(p, fIn[0]) + fAt := t.evaluate(fIn...) + if !t.equal(pAt, fAt) { return nil } // trim p lastNonZero := len(p) - 1 - for lastNonZero >= 0 && p[lastNonZero].IsZero() { + for lastNonZero >= 0 && t.isZero(p[lastNonZero]) { lastNonZero-- } return p[:lastNonZero+1] @@ -480,56 +475,54 @@ func (t *gateTester) fitPoly(g *GadgetGate,degreeBound int) polynomial.Polynomia // interpolate fits a polynomial of degree len(X) - 1 = len(Y) - 1 to the points (X[i], Y[i]) // Note that the runtime is O(len(X)³) -func interpolate(X, Y polynomial.Polynomial) (polynomial.Polynomial, error) { -if len(X) != len(Y) { -return nil, errors.New("same length expected for X and Y") -} +func (t *gateTester) interpolate(X, Y []*big.Int) ([]*big.Int, error) { + if len(X) != len(Y) { + return nil, errors.New("same length expected for X and Y") + } -// solve the system of equations by Gaussian elimination -augmentedRows := make([]polynomial.Polynomial, len(X)) // the last column is the Y values -for i := range augmentedRows { -augmentedRows[i] = make(polynomial.Polynomial, len(X)+1) -augmentedRows[i][0].SetOne() -augmentedRows[i][1].Set(&X[i]) -for j := 2; j < len(augmentedRows[i])-1; j++ { -augmentedRows[i][j].Mul(&augmentedRows[i][j-1], &X[i]) -} -augmentedRows[i][len(augmentedRows[i])-1].Set(&Y[i]) -} + one := big.NewInt(1) -// make the upper triangle -for i := range len(augmentedRows) - 1 { -// use row i to eliminate the ith element in all rows below -var negInv *big.Int -if augmentedRows[i][i].IsZero() { -return nil, errors.New("singular matrix") -} -negInv.Inverse(&augmentedRows[i][i]) -negInv.Neg(negInv) -for j := i + 1; j < len(augmentedRows); j++ { -var c big.Int -c.Mul(augmentedRows[j][i], negInv) -// augmentedRows[j][i].SetZero() omitted -for k := i + 1; k < len(augmentedRows[i]); k++ { -var t *big.Int -t.Mul(augmentedRows[i][k], c) -augmentedRows[j][k].Add(augmentedRows[j][k], &) -} -} -} + // solve the system of equations by Gaussian elimination + augmentedRows := make([][]*big.Int, len(X)) // the last column is the Y values + for i := range augmentedRows { + augmentedRows[i] = make([]*big.Int, len(X)+1) + augmentedRows[i][0] = one + augmentedRows[i][1] = X[i] + for j := 2; j < len(augmentedRows[i])-1; j++ { + augmentedRows[i][j] = t.mul(augmentedRows[i][j-1], X[i]) + } + augmentedRows[i][len(augmentedRows[i])-1] = Y[i] + } -// back substitution -res := make(polynomial.Polynomial, len(X)) -for i := len(augmentedRows) - 1; i >= 0; i-- { -res[i] = augmentedRows[i][len(augmentedRows[i])-1] -for j := i + 1; j < len(augmentedRows[i])-1; j++ { -var t big.Int -t.Mul(res[j], augmentedRows[i][j]) -res[i].Sub(res[i], &t) -} -res[i].Div(res[i], augmentedRows[i][i]) -} + // make the upper triangle + for i := range len(augmentedRows) - 1 { + // use row i to eliminate the ith element in all rows below + var negInv *big.Int + if t.isZero(augmentedRows[i][i]) { + return nil, errors.New("singular matrix") + } + negInv = t.inverse(augmentedRows[i][i]) + negInv = t.neg(negInv) + for j := i + 1; j < len(augmentedRows); j++ { + c := t.mul(augmentedRows[j][i], negInv) + // augmentedRows[j][i].SetZero() omitted + for k := i + 1; k < len(augmentedRows[i]); k++ { + z := t.mul(augmentedRows[i][k], c) + augmentedRows[j][k] = t.add(augmentedRows[j][k], z) + } + } + } -return res, nil -} + // back substitution + res := make([]*big.Int, len(X)) + for i := len(augmentedRows) - 1; i >= 0; i-- { + res[i] = augmentedRows[i][len(augmentedRows[i])-1] + for j := i + 1; j < len(augmentedRows[i])-1; j++ { + z := t.mul(res[j], augmentedRows[i][j]) + res[i] = t.sub(res[i], z) + } + res[i] = t.div(res[i], augmentedRows[i][i]) + } + return res, nil +} diff --git a/internal/gkr/gkrtypes/compiledgate_test.go b/internal/gkr/gkrtypes/gate_test.go similarity index 100% rename from internal/gkr/gkrtypes/compiledgate_test.go rename to internal/gkr/gkrtypes/gate_test.go diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 69d44f4520..8f77f3df52 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -1,6 +1,8 @@ package gkrtypes import ( + "math/big" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" @@ -48,7 +50,7 @@ type ( RegisteredWires = Wires[BothExecutables] // Serializable types (bytecode only, for native proving) - SerializableGate = Gate[*GateBytecode] + SerializableGate = Gate[*GateBytecode] SerializableCircuit = Circuit[*GateBytecode] SerializableWire = Wire[*GateBytecode] SerializableWires = Wires[*GateBytecode] @@ -409,3 +411,42 @@ type Blueprints struct { ProveID constraint.BlueprintID GetAssignmentID constraint.BlueprintID } + +// ToSerializableCircuit converts a gadget circuit to a serializable circuit by compiling the gate functions. +// It also sets the gate metadata (Degree, SolvableVar) for both the input and output circuits. +func ToSerializableCircuit(mod *big.Int, c GadgetCircuit) SerializableCircuit { + + tester := gateTester{mod: mod} + + var err error + res := make(SerializableCircuit, len(c)) + for i := range c { + res[i].Inputs = c[i].Inputs + + c[i].Gate.NbIn = len(c[i].Inputs) + res[i].Gate.NbIn = c[i].Gate.NbIn + + if res[i].Gate.Evaluate, err = CompileGateFunction(c[i].Gate.Evaluate, c[i].Gate.NbIn); err != nil { + panic(err) + } + + tester.setGate(res[i].Gate.Evaluate, c[i].Gate.NbIn) + + c[i].Gate.Degree = len(tester.fitPoly(res[i].Gate.Evaluate.EstimateDegree(len(c[i].Inputs)))) - 1 + if c[i].Gate.Degree == -1 { + panic("cannot find degree for gate") + } + res[i].Gate.Degree = c[i].Gate.Degree + + res[i].Gate.SolvableVar = -1 + for j := range c[i].Gate.NbIn { + if tester.isAdditive(j) { + res[i].Gate.SolvableVar = j + break + } + } + c[i].Gate.SolvableVar = res[i].Gate.SolvableVar + res[i].Gate.SolvableVar = c[i].Gate.SolvableVar + } + return res +} From 141294188cce1c2b357203c8e11cfe1991dec363 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 00:13:25 -0600 Subject: [PATCH 175/251] remove per curve gate testing --- internal/generator/backend/main.go | 1 - .../backend/template/gkr/gate_testing.go.tmpl | 198 ------------------ internal/gkr/bls12-377/gate_testing.go | 129 ------------ internal/gkr/bls12-381/gate_testing.go | 129 ------------ internal/gkr/bn254/gate_testing.go | 129 ------------ internal/gkr/bw6-761/gate_testing.go | 129 ------------ 6 files changed, 715 deletions(-) delete mode 100644 internal/generator/backend/template/gkr/gate_testing.go.tmpl delete mode 100644 internal/gkr/bls12-377/gate_testing.go delete mode 100644 internal/gkr/bls12-381/gate_testing.go delete mode 100644 internal/gkr/bn254/gate_testing.go delete mode 100644 internal/gkr/bw6-761/gate_testing.go diff --git a/internal/generator/backend/main.go b/internal/generator/backend/main.go index 53b55228d5..a7f60e4c40 100644 --- a/internal/generator/backend/main.go +++ b/internal/generator/backend/main.go @@ -307,7 +307,6 @@ func generateGkrBackend(cfg gkrConfig) error { } else { entries = append(entries, []bavard.Entry{ {File: filepath.Join(packageDir, "blueprint.go"), Templates: []string{"blueprint.go.tmpl"}}, - {File: filepath.Join(packageDir, "gate_testing.go"), Templates: []string{"gate_testing.go.tmpl"}}, }...) } diff --git a/internal/generator/backend/template/gkr/gate_testing.go.tmpl b/internal/generator/backend/template/gkr/gate_testing.go.tmpl deleted file mode 100644 index db231d8285..0000000000 --- a/internal/generator/backend/template/gkr/gate_testing.go.tmpl +++ /dev/null @@ -1,198 +0,0 @@ -import ( - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - - "{{.FieldPackagePath}}" - {{- if .CanUseFFT }} - "{{.FieldPackagePath}}/fft"{{- else}} - "errors"{{- end }} - "{{.FieldPackagePath}}/polynomial" - "slices" -) - -type GateTester struct { - evaluator gateEvaluator - nbIn int -} - -func (t *GateTester) SetGate(g *gkrtypes.GateBytecode, nbIn int) { - t.evaluator = newGateEvaluator(g, nbIn) - t.nbIn = nbIn -} - -// IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e -func (t *GateTester) IsAdditive(i int) bool { - - // fix all variables except the i-th one at random points - // pick random value x1 for the i-th variable - // check if f(-, 0, -) + f(-, 2*x1, -) = 2*f(-, x1, -) - x := make({{.FieldPackageName}}.Vector, t.nbIn) - x.MustSetRandom() - x0 := x[i] - x[i].SetZero() - in := slices.Clone(x) - y0 := *t.evaluator.evaluate(in...) - - x[i] = x0 - copy(in, x) - y1 := *t.evaluator.evaluate(in...) - - x[i].Double(&x[i]) - copy(in, x) - y2 := *t.evaluator.evaluate(in...) - - y2.Sub(&y2, &y1) - y1.Sub(&y1, &y0) - - if !y2.Equal(&y1) { - return false // not linear - } - - // check if the coefficient of x_i is nonzero and independent of the other variables (so that we know it is ALWAYS nonzero) - if y1.IsZero() { // f(-, x1, -) = f(-, 0, -), so the coefficient of x_i is 0 - return false - } - - // compute the slope with another assignment for the other variables - x.MustSetRandom() - x[i].SetZero() - copy(in, x) - y0 = *t.evaluator.evaluate(in...) - - x[i] = x0 - copy(in, x) - y1 = *t.evaluator.evaluate(in...) - y1.Sub(&y1, &y0) - - return y1.Equal(&y2) -} - -// fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. -// degreeBound must be a power of 2. -// It returns the polynomial if successful, nil otherwise -func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { - - // turn f univariate by defining p(x) as f(x, rx, ..., sx) - // where r, s, ... are random constants - fIn := make([]{{ .ElementType }}, t.nbIn) - consts := make({{.FieldPackageName}}.Vector, t.nbIn-1) - consts.MustSetRandom() - - p := make(polynomial.Polynomial, degreeBound) - {{- if .CanUseFFT }} - domain := fft.NewDomain(degreeBound) - // evaluate p on the unit circle (first filling p with evaluations rather than coefficients) - x := {{.FieldPackageName}}.One() - for i := range p { - fIn[0] = x - for j := range consts { - fIn[j+1].Mul(&x, &consts[j]) - } - p[i].Set(t.evaluator.evaluate(fIn...)) - - x.Mul(&x, &domain.Generator) - } - - // obtain p's coefficients - domain.FFTInverse(p, fft.DIF) - fft.BitReverse(p) //nolint:staticcheck // method is backwards compatible - {{- else }} - x := make({{.FieldPackageName}}.Vector, degreeBound) - x.MustSetRandom() - for i := range x { - fIn[0] = x[i] - for j := range consts { - fIn[j+1].Mul(&x[i], &consts[j]) - } - p[i].Set(t.evaluator.evaluate(fIn...)) - } - - // obtain p's coefficients - p, err := interpolate(x, p) - if err != nil { - panic(err) - } - {{- end }} - - // check if p is equal to f. This not being the case means that f is of a degree higher than degreeBound - fIn[0].MustSetRandom() - for i := range consts { - fIn[i+1].Mul(&fIn[0], &consts[i]) - } - pAt := p.Eval(&fIn[0]) - fAt := *t.evaluator.evaluate(fIn...) - if !pAt.Equal(&fAt) { - return nil - } - - // trim p - lastNonZero := len(p) - 1 - for lastNonZero >= 0 && p[lastNonZero].IsZero() { - lastNonZero-- - } - return p[:lastNonZero+1] -} - -// FindDegree returns the degree of the gate function, or -1 if it fails. -// Failure can be due to the function not being a polynomial at all, or being constantly zero. -func (t *GateTester) FindDegree() int { - p := t.fitPoly(ecc.NextPowerOfTwo(uint64(t.evaluator.gate.EstimateDegree(t.nbIn)) + 1)) - return len(p) - 1 -} - -{{- if not .CanUseFFT }} -// interpolate fits a polynomial of degree len(X) - 1 = len(Y) - 1 to the points (X[i], Y[i]) -// Note that the runtime is O(len(X)³) -func interpolate(X, Y []{{.ElementType}}) (polynomial.Polynomial, error) { - if len(X) != len(Y) { - return nil, errors.New("same length expected for X and Y") - } - - // solve the system of equations by Gaussian elimination - augmentedRows := make([][]{{.ElementType}}, len(X)) // the last column is the Y values - for i := range augmentedRows { - augmentedRows[i] = make([]{{.ElementType}}, len(X)+1) - augmentedRows[i][0].SetOne() - augmentedRows[i][1].Set(&X[i]) - for j := 2; j < len(augmentedRows[i])-1; j++ { - augmentedRows[i][j].Mul(&augmentedRows[i][j-1], &X[i]) - } - augmentedRows[i][len(augmentedRows[i])-1].Set(&Y[i]) - } - - // make the upper triangle - for i := range len(augmentedRows) - 1 { - // use row i to eliminate the ith element in all rows below - var negInv {{.ElementType}} - if augmentedRows[i][i].IsZero() { - return nil, errors.New("singular matrix") - } - negInv.Inverse(&augmentedRows[i][i]) - negInv.Neg(&negInv) - for j := i + 1; j < len(augmentedRows); j++ { - var c {{.ElementType}} - c.Mul(&augmentedRows[j][i], &negInv) - // augmentedRows[j][i].SetZero() omitted - for k := i + 1; k < len(augmentedRows[i]); k++ { - var t {{.ElementType}} - t.Mul(&augmentedRows[i][k], &c) - augmentedRows[j][k].Add(&augmentedRows[j][k], &t) - } - } - } - - // back substitution - res := make(polynomial.Polynomial, len(X)) - for i := len(augmentedRows) - 1; i >= 0; i-- { - res[i] = augmentedRows[i][len(augmentedRows[i])-1] - for j := i + 1; j < len(augmentedRows[i])-1; j++ { - var t {{.ElementType}} - t.Mul(&res[j], &augmentedRows[i][j]) - res[i].Sub(&res[i], &t) - } - res[i].Div(&res[i], &augmentedRows[i][i]) - } - - return res, nil -} -{{- end }} diff --git a/internal/gkr/bls12-377/gate_testing.go b/internal/gkr/bls12-377/gate_testing.go deleted file mode 100644 index 0e4f18bcda..0000000000 --- a/internal/gkr/bls12-377/gate_testing.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2020-2026 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -// Code generated by gnark DO NOT EDIT - -package gkr - -import ( - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - - "slices" - - "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/polynomial" -) - -type GateTester struct { - evaluator gateEvaluator - nbIn int -} - -func (t *GateTester) SetGate(g *gkrtypes.GateBytecode, nbIn int) { - t.evaluator = newGateEvaluator(g, nbIn) - t.nbIn = nbIn -} - -// IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e -func (t *GateTester) IsAdditive(i int) bool { - - // fix all variables except the i-th one at random points - // pick random value x1 for the i-th variable - // check if f(-, 0, -) + f(-, 2*x1, -) = 2*f(-, x1, -) - x := make(fr.Vector, t.nbIn) - x.MustSetRandom() - x0 := x[i] - x[i].SetZero() - in := slices.Clone(x) - y0 := *t.evaluator.evaluate(in...) - - x[i] = x0 - copy(in, x) - y1 := *t.evaluator.evaluate(in...) - - x[i].Double(&x[i]) - copy(in, x) - y2 := *t.evaluator.evaluate(in...) - - y2.Sub(&y2, &y1) - y1.Sub(&y1, &y0) - - if !y2.Equal(&y1) { - return false // not linear - } - - // check if the coefficient of x_i is nonzero and independent of the other variables (so that we know it is ALWAYS nonzero) - if y1.IsZero() { // f(-, x1, -) = f(-, 0, -), so the coefficient of x_i is 0 - return false - } - - // compute the slope with another assignment for the other variables - x.MustSetRandom() - x[i].SetZero() - copy(in, x) - y0 = *t.evaluator.evaluate(in...) - - x[i] = x0 - copy(in, x) - y1 = *t.evaluator.evaluate(in...) - y1.Sub(&y1, &y0) - - return y1.Equal(&y2) -} - -// fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. -// degreeBound must be a power of 2. -// It returns the polynomial if successful, nil otherwise -func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { - - // turn f univariate by defining p(x) as f(x, rx, ..., sx) - // where r, s, ... are random constants - fIn := make([]fr.Element, t.nbIn) - consts := make(fr.Vector, t.nbIn-1) - consts.MustSetRandom() - - p := make(polynomial.Polynomial, degreeBound) - domain := fft.NewDomain(degreeBound) - // evaluate p on the unit circle (first filling p with evaluations rather than coefficients) - x := fr.One() - for i := range p { - fIn[0] = x - for j := range consts { - fIn[j+1].Mul(&x, &consts[j]) - } - p[i].Set(t.evaluator.evaluate(fIn...)) - - x.Mul(&x, &domain.Generator) - } - - // obtain p's coefficients - domain.FFTInverse(p, fft.DIF) - fft.BitReverse(p) //nolint:staticcheck // method is backwards compatible - - // check if p is equal to f. This not being the case means that f is of a degree higher than degreeBound - fIn[0].MustSetRandom() - for i := range consts { - fIn[i+1].Mul(&fIn[0], &consts[i]) - } - pAt := p.Eval(&fIn[0]) - fAt := *t.evaluator.evaluate(fIn...) - if !pAt.Equal(&fAt) { - return nil - } - - // trim p - lastNonZero := len(p) - 1 - for lastNonZero >= 0 && p[lastNonZero].IsZero() { - lastNonZero-- - } - return p[:lastNonZero+1] -} - -// FindDegree returns the degree of the gate function, or -1 if it fails. -// Failure can be due to the function not being a polynomial at all, or being constantly zero. -func (t *GateTester) FindDegree() int { - p := t.fitPoly(ecc.NextPowerOfTwo(uint64(t.evaluator.gate.EstimateDegree(t.nbIn)) + 1)) - return len(p) - 1 -} diff --git a/internal/gkr/bls12-381/gate_testing.go b/internal/gkr/bls12-381/gate_testing.go deleted file mode 100644 index 6ab08e3e4d..0000000000 --- a/internal/gkr/bls12-381/gate_testing.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2020-2026 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -// Code generated by gnark DO NOT EDIT - -package gkr - -import ( - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - - "slices" - - "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/polynomial" -) - -type GateTester struct { - evaluator gateEvaluator - nbIn int -} - -func (t *GateTester) SetGate(g *gkrtypes.GateBytecode, nbIn int) { - t.evaluator = newGateEvaluator(g, nbIn) - t.nbIn = nbIn -} - -// IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e -func (t *GateTester) IsAdditive(i int) bool { - - // fix all variables except the i-th one at random points - // pick random value x1 for the i-th variable - // check if f(-, 0, -) + f(-, 2*x1, -) = 2*f(-, x1, -) - x := make(fr.Vector, t.nbIn) - x.MustSetRandom() - x0 := x[i] - x[i].SetZero() - in := slices.Clone(x) - y0 := *t.evaluator.evaluate(in...) - - x[i] = x0 - copy(in, x) - y1 := *t.evaluator.evaluate(in...) - - x[i].Double(&x[i]) - copy(in, x) - y2 := *t.evaluator.evaluate(in...) - - y2.Sub(&y2, &y1) - y1.Sub(&y1, &y0) - - if !y2.Equal(&y1) { - return false // not linear - } - - // check if the coefficient of x_i is nonzero and independent of the other variables (so that we know it is ALWAYS nonzero) - if y1.IsZero() { // f(-, x1, -) = f(-, 0, -), so the coefficient of x_i is 0 - return false - } - - // compute the slope with another assignment for the other variables - x.MustSetRandom() - x[i].SetZero() - copy(in, x) - y0 = *t.evaluator.evaluate(in...) - - x[i] = x0 - copy(in, x) - y1 = *t.evaluator.evaluate(in...) - y1.Sub(&y1, &y0) - - return y1.Equal(&y2) -} - -// fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. -// degreeBound must be a power of 2. -// It returns the polynomial if successful, nil otherwise -func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { - - // turn f univariate by defining p(x) as f(x, rx, ..., sx) - // where r, s, ... are random constants - fIn := make([]fr.Element, t.nbIn) - consts := make(fr.Vector, t.nbIn-1) - consts.MustSetRandom() - - p := make(polynomial.Polynomial, degreeBound) - domain := fft.NewDomain(degreeBound) - // evaluate p on the unit circle (first filling p with evaluations rather than coefficients) - x := fr.One() - for i := range p { - fIn[0] = x - for j := range consts { - fIn[j+1].Mul(&x, &consts[j]) - } - p[i].Set(t.evaluator.evaluate(fIn...)) - - x.Mul(&x, &domain.Generator) - } - - // obtain p's coefficients - domain.FFTInverse(p, fft.DIF) - fft.BitReverse(p) //nolint:staticcheck // method is backwards compatible - - // check if p is equal to f. This not being the case means that f is of a degree higher than degreeBound - fIn[0].MustSetRandom() - for i := range consts { - fIn[i+1].Mul(&fIn[0], &consts[i]) - } - pAt := p.Eval(&fIn[0]) - fAt := *t.evaluator.evaluate(fIn...) - if !pAt.Equal(&fAt) { - return nil - } - - // trim p - lastNonZero := len(p) - 1 - for lastNonZero >= 0 && p[lastNonZero].IsZero() { - lastNonZero-- - } - return p[:lastNonZero+1] -} - -// FindDegree returns the degree of the gate function, or -1 if it fails. -// Failure can be due to the function not being a polynomial at all, or being constantly zero. -func (t *GateTester) FindDegree() int { - p := t.fitPoly(ecc.NextPowerOfTwo(uint64(t.evaluator.gate.EstimateDegree(t.nbIn)) + 1)) - return len(p) - 1 -} diff --git a/internal/gkr/bn254/gate_testing.go b/internal/gkr/bn254/gate_testing.go deleted file mode 100644 index 0a1714b345..0000000000 --- a/internal/gkr/bn254/gate_testing.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2020-2026 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -// Code generated by gnark DO NOT EDIT - -package gkr - -import ( - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - - "slices" - - "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/polynomial" -) - -type GateTester struct { - evaluator gateEvaluator - nbIn int -} - -func (t *GateTester) SetGate(g *gkrtypes.GateBytecode, nbIn int) { - t.evaluator = newGateEvaluator(g, nbIn) - t.nbIn = nbIn -} - -// IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e -func (t *GateTester) IsAdditive(i int) bool { - - // fix all variables except the i-th one at random points - // pick random value x1 for the i-th variable - // check if f(-, 0, -) + f(-, 2*x1, -) = 2*f(-, x1, -) - x := make(fr.Vector, t.nbIn) - x.MustSetRandom() - x0 := x[i] - x[i].SetZero() - in := slices.Clone(x) - y0 := *t.evaluator.evaluate(in...) - - x[i] = x0 - copy(in, x) - y1 := *t.evaluator.evaluate(in...) - - x[i].Double(&x[i]) - copy(in, x) - y2 := *t.evaluator.evaluate(in...) - - y2.Sub(&y2, &y1) - y1.Sub(&y1, &y0) - - if !y2.Equal(&y1) { - return false // not linear - } - - // check if the coefficient of x_i is nonzero and independent of the other variables (so that we know it is ALWAYS nonzero) - if y1.IsZero() { // f(-, x1, -) = f(-, 0, -), so the coefficient of x_i is 0 - return false - } - - // compute the slope with another assignment for the other variables - x.MustSetRandom() - x[i].SetZero() - copy(in, x) - y0 = *t.evaluator.evaluate(in...) - - x[i] = x0 - copy(in, x) - y1 = *t.evaluator.evaluate(in...) - y1.Sub(&y1, &y0) - - return y1.Equal(&y2) -} - -// fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. -// degreeBound must be a power of 2. -// It returns the polynomial if successful, nil otherwise -func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { - - // turn f univariate by defining p(x) as f(x, rx, ..., sx) - // where r, s, ... are random constants - fIn := make([]fr.Element, t.nbIn) - consts := make(fr.Vector, t.nbIn-1) - consts.MustSetRandom() - - p := make(polynomial.Polynomial, degreeBound) - domain := fft.NewDomain(degreeBound) - // evaluate p on the unit circle (first filling p with evaluations rather than coefficients) - x := fr.One() - for i := range p { - fIn[0] = x - for j := range consts { - fIn[j+1].Mul(&x, &consts[j]) - } - p[i].Set(t.evaluator.evaluate(fIn...)) - - x.Mul(&x, &domain.Generator) - } - - // obtain p's coefficients - domain.FFTInverse(p, fft.DIF) - fft.BitReverse(p) //nolint:staticcheck // method is backwards compatible - - // check if p is equal to f. This not being the case means that f is of a degree higher than degreeBound - fIn[0].MustSetRandom() - for i := range consts { - fIn[i+1].Mul(&fIn[0], &consts[i]) - } - pAt := p.Eval(&fIn[0]) - fAt := *t.evaluator.evaluate(fIn...) - if !pAt.Equal(&fAt) { - return nil - } - - // trim p - lastNonZero := len(p) - 1 - for lastNonZero >= 0 && p[lastNonZero].IsZero() { - lastNonZero-- - } - return p[:lastNonZero+1] -} - -// FindDegree returns the degree of the gate function, or -1 if it fails. -// Failure can be due to the function not being a polynomial at all, or being constantly zero. -func (t *GateTester) FindDegree() int { - p := t.fitPoly(ecc.NextPowerOfTwo(uint64(t.evaluator.gate.EstimateDegree(t.nbIn)) + 1)) - return len(p) - 1 -} diff --git a/internal/gkr/bw6-761/gate_testing.go b/internal/gkr/bw6-761/gate_testing.go deleted file mode 100644 index 6b933d41e5..0000000000 --- a/internal/gkr/bw6-761/gate_testing.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2020-2026 Consensys Software Inc. -// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. - -// Code generated by gnark DO NOT EDIT - -package gkr - -import ( - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/internal/gkr/gkrtypes" - - "slices" - - "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/polynomial" -) - -type GateTester struct { - evaluator gateEvaluator - nbIn int -} - -func (t *GateTester) SetGate(g *gkrtypes.GateBytecode, nbIn int) { - t.evaluator = newGateEvaluator(g, nbIn) - t.nbIn = nbIn -} - -// IsAdditive returns whether xᵢ occurs only in a monomial of total degree 1 in e -func (t *GateTester) IsAdditive(i int) bool { - - // fix all variables except the i-th one at random points - // pick random value x1 for the i-th variable - // check if f(-, 0, -) + f(-, 2*x1, -) = 2*f(-, x1, -) - x := make(fr.Vector, t.nbIn) - x.MustSetRandom() - x0 := x[i] - x[i].SetZero() - in := slices.Clone(x) - y0 := *t.evaluator.evaluate(in...) - - x[i] = x0 - copy(in, x) - y1 := *t.evaluator.evaluate(in...) - - x[i].Double(&x[i]) - copy(in, x) - y2 := *t.evaluator.evaluate(in...) - - y2.Sub(&y2, &y1) - y1.Sub(&y1, &y0) - - if !y2.Equal(&y1) { - return false // not linear - } - - // check if the coefficient of x_i is nonzero and independent of the other variables (so that we know it is ALWAYS nonzero) - if y1.IsZero() { // f(-, x1, -) = f(-, 0, -), so the coefficient of x_i is 0 - return false - } - - // compute the slope with another assignment for the other variables - x.MustSetRandom() - x[i].SetZero() - copy(in, x) - y0 = *t.evaluator.evaluate(in...) - - x[i] = x0 - copy(in, x) - y1 = *t.evaluator.evaluate(in...) - y1.Sub(&y1, &y0) - - return y1.Equal(&y2) -} - -// fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. -// degreeBound must be a power of 2. -// It returns the polynomial if successful, nil otherwise -func (t *GateTester) fitPoly(degreeBound uint64) polynomial.Polynomial { - - // turn f univariate by defining p(x) as f(x, rx, ..., sx) - // where r, s, ... are random constants - fIn := make([]fr.Element, t.nbIn) - consts := make(fr.Vector, t.nbIn-1) - consts.MustSetRandom() - - p := make(polynomial.Polynomial, degreeBound) - domain := fft.NewDomain(degreeBound) - // evaluate p on the unit circle (first filling p with evaluations rather than coefficients) - x := fr.One() - for i := range p { - fIn[0] = x - for j := range consts { - fIn[j+1].Mul(&x, &consts[j]) - } - p[i].Set(t.evaluator.evaluate(fIn...)) - - x.Mul(&x, &domain.Generator) - } - - // obtain p's coefficients - domain.FFTInverse(p, fft.DIF) - fft.BitReverse(p) //nolint:staticcheck // method is backwards compatible - - // check if p is equal to f. This not being the case means that f is of a degree higher than degreeBound - fIn[0].MustSetRandom() - for i := range consts { - fIn[i+1].Mul(&fIn[0], &consts[i]) - } - pAt := p.Eval(&fIn[0]) - fAt := *t.evaluator.evaluate(fIn...) - if !pAt.Equal(&fAt) { - return nil - } - - // trim p - lastNonZero := len(p) - 1 - for lastNonZero >= 0 && p[lastNonZero].IsZero() { - lastNonZero-- - } - return p[:lastNonZero+1] -} - -// FindDegree returns the degree of the gate function, or -1 if it fails. -// Failure can be due to the function not being a polynomial at all, or being constantly zero. -func (t *GateTester) FindDegree() int { - p := t.fitPoly(ecc.NextPowerOfTwo(uint64(t.evaluator.gate.EstimateDegree(t.nbIn)) + 1)) - return len(p) - 1 -} From ca19a847371476a27cae248898093792f8d22bed Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 10:11:06 -0600 Subject: [PATCH 176/251] fix: degree detection --- internal/gkr/gkrtypes/gate.go | 46 ++++++++++-------- internal/gkr/gkrtypes/gate_test.go | 75 ++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 20 deletions(-) diff --git a/internal/gkr/gkrtypes/gate.go b/internal/gkr/gkrtypes/gate.go index 0a56e073fa..c1e56436e1 100644 --- a/internal/gkr/gkrtypes/gate.go +++ b/internal/gkr/gkrtypes/gate.go @@ -245,7 +245,13 @@ func CompileGateFunction(f gkr.GateFunction, nbInputs int) (*GateBytecode, error // Execute the gate function to record operations out := f(&compiler, inputs...) if len(compiler.instructions) == 0 { - return nil, errors.New("every gate must perform a non-trivial operation") + // No operations recorded, but not all is lost. + // If the output simply mirrors the last input, we can still represent + // it in bytecode, as the evaluator returns the last stack frame element. + if int(out.(compilationVar).id) == nbInputs-1 { + return &GateBytecode{}, nil + } + return nil, errors.New("cannot compile no-op gate function") } // All instructions after the output are no-ops. Prune them and the corresponding variables. @@ -263,17 +269,15 @@ func CompileGateFunction(f gkr.GateFunction, nbInputs int) (*GateBytecode, error } type gateTester struct { - mod *big.Int - gate *GateBytecode - vars []*big.Int - frameSize int - nbIn int + mod *big.Int + gate *GateBytecode + vars []*big.Int + nbIn int } func (t *gateTester) setGate(g *GateBytecode, nbIn int) { t.gate = g t.vars = make([]*big.Int, g.NbConstants()+nbIn+len(g.Instructions)) - t.frameSize = g.NbConstants() t.nbIn = nbIn copy(t.vars, g.Constants) } @@ -343,16 +347,19 @@ func (t *gateTester) evalPoly(p []*big.Int, x *big.Int) *big.Int { // evaluate executes the gate bytecode with the given inputs. func (t *gateTester) evaluate(inputs ...*big.Int) *big.Int { + frameSize := t.gate.NbConstants() + // Copy inputs into frame - copy(t.vars[t.frameSize:], inputs) - t.frameSize += len(inputs) + copy(t.vars[t.gate.NbConstants():], inputs) + + frameSize += len(inputs) // Execute instructions - for i, inst := range t.gate.Instructions { - dst := t.vars[t.frameSize+i] + for _, inst := range t.gate.Instructions { + dst := t.vars[frameSize] if dst == nil { dst = new(big.Int) - t.vars[t.frameSize+i] = dst + t.vars[frameSize] = dst } switch inst.Op { case OpAdd: @@ -383,11 +390,10 @@ func (t *gateTester) evaluate(inputs ...*big.Int) *big.Int { panic("unknown operation") } dst.Mod(dst, t.mod) + frameSize++ } - // Get result and reset frame - t.frameSize = t.gate.NbConstants() - return new(big.Int).Set(t.vars[t.frameSize+len(t.gate.Instructions)-1]) + return new(big.Int).Set(t.vars[frameSize-1]) } // isAdditive returns whether xᵢ occurs only in a monomial of total degree 1 @@ -428,18 +434,18 @@ func (t *gateTester) isAdditive(i int) bool { return t.equal(y2, y1) } -// fitPoly tries to fit a polynomial of degree less than degreeBound to the gate. +// fitPoly tries to fit a polynomial of degree no more than degreeBound to the gate. // It returns the polynomial if successful, nil otherwise. -func (t *gateTester) fitPoly(degreeBound int) []*big.Int { +func (t *gateTester) fitPoly(maxDegree int) []*big.Int { // turn f univariate by defining p(x) as f(x, rx, ..., sx) // where r, s, ... are random constants fIn := make([]*big.Int, t.nbIn) consts := t.randomElements(t.nbIn - 1) - p := make([]*big.Int, degreeBound) + p := make([]*big.Int, maxDegree+1) - x := t.randomElements(degreeBound) + x := t.randomElements(maxDegree + 1) for i := range x { fIn[0] = x[i] for j := range consts { @@ -454,7 +460,7 @@ func (t *gateTester) fitPoly(degreeBound int) []*big.Int { panic(err) } - // check if p is equal to f. This not being the case means that f is of a degree higher than degreeBound + // check if p is equal to f. This not being the case means that f is of a degree higher than maxDegree fIn[0] = t.randomElement() for i := range consts { fIn[i+1] = t.mul(fIn[0], consts[i]) diff --git a/internal/gkr/gkrtypes/gate_test.go b/internal/gkr/gkrtypes/gate_test.go index 01a32a3ff2..1d7c6ab998 100644 --- a/internal/gkr/gkrtypes/gate_test.go +++ b/internal/gkr/gkrtypes/gate_test.go @@ -4,6 +4,7 @@ import ( "math/big" "testing" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/stretchr/testify/assert" @@ -108,3 +109,77 @@ func TestConstantDeduplication(t *testing.T) { t.Logf("Successfully deduplicated constants: %d unique constant(s)", len(compiled.Constants)) } + +func testFitPoly(t *testing.T, name string, f gkr.GateFunction, nbIn, degree, maxDegree int) { + t.Run(name, func(t *testing.T) { + tester := gateTester{mod: ecc.BN254.ScalarField()} + g, err := CompileGateFunction(f, nbIn) + require.NoError(t, err) + tester.setGate(g, nbIn) + require.Equal(t, degree, len(tester.fitPoly(maxDegree))-1) + }) +} + +func TestFitPoly(t *testing.T) { + testFitPoly(t, "identity", Identity, 1, 1, 3) + testFitPoly(t, "add", Add2, 2, 1, 2) + testFitPoly(t, "sub", Sub2, 2, 1, 4) + testFitPoly(t, "mul", Mul2, 2, 2, 2) + + // x * y * z has degree 3 + mul3 := func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Mul(api.Mul(in[0], in[1]), in[2]) + } + testFitPoly(t, "mul3", mul3, 3, 3, 4) + + // x + y + z has degree 1 + add3 := func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Add(api.Add(in[0], in[1]), in[2]) + } + testFitPoly(t, "add3", add3, 3, 1, 4) + + // (x + y) * z has degree 2 + addMul := func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Mul(api.Add(in[0], in[1]), in[2]) + } + testFitPoly(t, "addMul", addMul, 3, 2, 4) +} + +func testIsAdditive(t *testing.T, name string, f gkr.GateFunction, isAdditive ...bool) { + t.Run(name, func(t *testing.T) { + tester := gateTester{mod: ecc.BN254.ScalarField()} + g, err := CompileGateFunction(f, len(isAdditive)) + require.NoError(t, err) + tester.setGate(g, len(isAdditive)) + for i := range isAdditive { + assert.Equal(t, isAdditive[i], tester.isAdditive(i)) + } + }) +} + +func TestIsAdditive(t *testing.T) { + testIsAdditive(t, "x+y", Add2, true, true) + testIsAdditive(t, "x-y", Sub2, true, true) + testIsAdditive(t, "x*y", Mul2, false, false) // neither additive (degree 2 monomial) + + // x additive, y and z not + testIsAdditive(t, "x+y*z", + func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Add(in[0], api.Mul(in[1], in[2])) + }, + true, false, false) + + // x not additive (degree 2), y additive + testIsAdditive(t, "x²+y", + func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Add(api.Mul(in[0], in[0]), in[1]) + }, + false, true) + + // y appears in both degree-1 and degree-2 terms, so not additive + testIsAdditive(t, "x*y+y", + func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Add(api.Mul(in[0], in[1]), in[1]) + }, + false, false) +} From ce8b9c2966dbfa8fb2401310aaa2f2bbbaac0cee Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 10:16:25 -0600 Subject: [PATCH 177/251] fix: remove TestIsAdditive in curve package --- internal/gkr/bls12-381/gkr_test.go | 48 +----------------------------- internal/gkr/compile.go | 11 ------- internal/gkr/gkrtypes/gate_test.go | 4 +-- 3 files changed, 3 insertions(+), 60 deletions(-) diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 0b3fb627c7..4691b4b826 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -21,17 +21,14 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" gcUtils "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/frontend" - gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/stretchr/testify/assert" ) func toSerializableCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - return gadget.ToSerializableCircuit(ecc.BLS12_381, circuit) + return gkrtypes.ToSerializableCircuit(ecc.BLS12_381.ScalarField(), circuit) } func TestNoGateTwoInstances(t *testing.T) { @@ -209,49 +206,6 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { assert.NoError(t, err, "proof rejected") } -func TestIsAdditive(t *testing.T) { - - // f: x,y -> x² + xy - f := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - if len(x) != 2 { - panic("bivariate input needed") - } - res := api.Add(x[0], x[1]) - return api.Mul(res, x[0]) - } - - // g: x,y -> x² + 3y - g := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - res := api.Mul(x[0], x[0]) - y3 := api.Mul(x[1], 3) - return api.Add(res, y3) - } - - // h: x -> 2x - h := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - return api.Add(x[0], x[0]) - } - - newTester := func(f gkr.GateFunction, nbIn int) *GateTester { - cg, err := gkrtypes.CompileGateFunction(f, nbIn) - assert.NoError(t, err) - var gt GateTester - gt.SetGate(cg, nbIn) - return > - } - - tester := newTester(f, 2) - assert.False(t, tester.IsAdditive(1)) - assert.False(t, tester.IsAdditive(0)) - - tester = newTester(g, 2) - assert.False(t, tester.IsAdditive(0)) - assert.True(t, tester.IsAdditive(1)) - - tester = newTester(h, 1) - assert.True(t, tester.IsAdditive(0)) -} - func generateTestProver(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) diff --git a/internal/gkr/compile.go b/internal/gkr/compile.go index 718ae2aaac..10cd5e6a54 100644 --- a/internal/gkr/compile.go +++ b/internal/gkr/compile.go @@ -1,12 +1 @@ package gkr - -import ( - "fmt" - - "github.com/consensys/gnark-crypto/ecc" - bls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" - bls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" - bn254 "github.com/consensys/gnark/internal/gkr/bn254" - bw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" - "github.com/consensys/gnark/internal/gkr/gkrtypes" -) diff --git a/internal/gkr/gkrtypes/gate_test.go b/internal/gkr/gkrtypes/gate_test.go index 1d7c6ab998..0bbf6eb0d2 100644 --- a/internal/gkr/gkrtypes/gate_test.go +++ b/internal/gkr/gkrtypes/gate_test.go @@ -170,9 +170,9 @@ func TestIsAdditive(t *testing.T) { true, false, false) // x not additive (degree 2), y additive - testIsAdditive(t, "x²+y", + testIsAdditive(t, "x²+2y", func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - return api.Add(api.Mul(in[0], in[0]), in[1]) + return api.Add(api.Mul(in[0], in[0]), in[1], in[1]) }, false, true) From f445071411ad9a67cea56cee5d75614d1f12f941 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 10:47:44 -0600 Subject: [PATCH 178/251] build: generify --- .../backend/template/gkr/gkr.test.go.tmpl | 208 +++--------------- internal/gkr/bls12-377/gkr_test.go | 204 +++-------------- internal/gkr/bls12-381/gkr_test.go | 21 +- internal/gkr/bn254/gkr_test.go | 204 +++-------------- internal/gkr/bw6-761/gkr_test.go | 204 +++-------------- 5 files changed, 130 insertions(+), 711 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index eddde66842..7e2abbd762 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -4,12 +4,11 @@ import ( "{{.FieldPackagePath}}/mimc" "{{.FieldPackagePath}}/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + "github.com/consensys/gnark-crypto/ecc" gcUtils "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/stretchr/testify/assert" "fmt" "hash" @@ -17,16 +16,13 @@ import ( "strconv" "testing" "path/filepath" - "encoding/json" "reflect" "time" ) -var ( - identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) - add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) - mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) -) +func toSerializableCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { + return gkrtypes.ToSerializableCircuit(ecc.{{.FieldID}}.ScalarField(), circuit) +} func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case @@ -34,126 +30,51 @@ func TestNoGateTwoInstances(t *testing.T) { } func TestNoGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - }) + test(t, gkrtesting.NoGateCircuit()) } func TestSingleAddGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - }, - { - Gate: add2Gate, - Inputs: []int{0, 1}, - }, - }) + test(t, gkrtesting.SingleAddGateCircuit()) } func TestSingleMulGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - }, - { - Gate: mul2Gate, - Inputs: []int{0, 1}, - }, - }) + test(t, gkrtesting.SingleMulGateCircuit()) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - Inputs: []int{0}, - }, - { - Gate: identityGate, - Inputs: []int{0}, - }, - }) + test(t, gkrtesting.SingleInputTwoIdentityGatesCircuit()) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - Inputs: []int{0}, - }, - { - Gate: identityGate, - Inputs: []int{1}, - }, - }) + test(t, gkrtesting.SingleInputTwoIdentityGatesComposedCircuit()) } func TestAPowNTimesBCircuit(t *testing.T) { - const N = 10 - - c := make(Circuit, N+2) - - // Input wires - c[0] = Wire{ - Gate: identityGate, - } - c[1] = Wire{ - Gate: identityGate, - } - - for i := 2; i < len(c); i++ { - c[i] = Wire{ - Gate: mul2Gate, - Inputs: []int{i - 1, 0}, - } - } - - test(t, c) + test(t, gkrtesting.APowNTimesBCircuit(10)) } func TestSingleMimcCipherGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - }, - { - Inputs: []int{0, 1}, - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), - }, - }) + test(t, gkrtesting.SingleMimcCipherGateCircuit()) } func TestShallowMimcTwoInstances(t *testing.T) { - test(t, mimcCircuit(2)) + test(t, gkrtesting.MiMCCircuit(2)) } func TestMimc(t *testing.T) { - test(t, mimcCircuit(93)) + test(t, gkrtesting.MiMCCircuit(93)) } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := Circuit{Wire{ - Gate: identityGate, - NbUniqueOutputs: 2, - }} + gCircuit := gkrtypes.GadgetCircuit{ + { + Gate: &gkrtypes.GadgetGate{ + Evaluate: gkrtypes.Identity, + }, + NbUniqueOutputs: 2, + }, + } + circuit := toSerializableCircuit(gCircuit) assignment := WireAssignment{[]{{ .ElementType }}{two, three}} var o settings @@ -208,8 +129,9 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit Circuit) { - wireRefs := utils.References(circuit) +func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { + sCircuit := toSerializableCircuit(circuit) + wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -229,19 +151,19 @@ func test(t *testing.T, circuit Circuit) { t.Log("Selected inputs for test") - proof, err := Prove(circuit, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(sCircuit, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(circuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) assert.NoError(t, err, "proof rejected") if proof.isEmpty() { // special case for TestNoGate: continue // there's no way to make a trivial proof fail } - err = Verify(circuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) + err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) assert.NotNil(t, err, "bad proof accepted") } @@ -262,11 +184,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]{{ .ElementType }}) { - c := Circuit{ - { - Gate: identityGate, - }, - } + c := toSerializableCircuit(gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -279,70 +197,6 @@ func testNoGate(t *testing.T, inputAssignments ...[]{{ .ElementType }}) { assert.NoError(t, err, "proof rejected") } -func mimcCircuit(numRounds int) Circuit { - c := make(Circuit, numRounds+2) - - // Input wires - c[0] = Wire{ - Gate: identityGate, - } - c[1] = Wire{ - Gate: identityGate, - } - - for i := 2; i < len(c); i++ { - c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), - Inputs: []int{i - 1, 0}, - } - } - return c -} - - -func TestIsAdditive(t *testing.T) { - - // f: x,y -> x² + xy - f := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - if len(x) != 2 { - panic("bivariate input needed") - } - res := api.Add(x[0], x[1]) - return api.Mul(res, x[0]) - } - - // g: x,y -> x² + 3y - g := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - res := api.Mul(x[0], x[0]) - y3 := api.Mul(x[1], 3) - return api.Add(res, y3) - } - - // h: x -> 2x - h := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - return api.Add(x[0], x[0]) - } - - newTester := func(f gkr.GateFunction, nbIn int) *GateTester { - cg, err := gkrtypes.CompileGateFunction(f, nbIn) - assert.NoError(t, err) - var gt GateTester - gt.SetGate(cg, nbIn) - return > - } - - tester := newTester(f, 2) - assert.False(t, tester.IsAdditive(1)) - assert.False(t, tester.IsAdditive(0)) - - tester = newTester(g, 2) - assert.False(t, tester.IsAdditive(0)) - assert.True(t, tester.IsAdditive(1)) - - tester = newTester(h, 1) - assert.True(t, tester.IsAdditive(0)) -} - func generateTestProver(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) @@ -411,7 +265,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := mimcCircuit(mimcDepth) + c := toSerializableCircuit(gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]{{ .ElementType }}, nbInstances) in1 := make([]{{ .ElementType }}, nbInstances) @@ -441,4 +295,4 @@ func BenchmarkGkrMimc17(b *testing.B) { benchmarkGkrMiMC(b, 1<<17, 91) } -{{template "gkrTestVectors" .}} \ No newline at end of file +{{template "gkrTestVectors" .}} diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index e93bc24589..5995f25b54 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -15,24 +15,21 @@ import ( "testing" "time" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/mimc" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" gcUtils "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/stretchr/testify/assert" ) -var ( - identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) - add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) - mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) -) +func toSerializableCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { + return gkrtypes.ToSerializableCircuit(ecc.BLS12_377.ScalarField(), circuit) +} func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case @@ -40,126 +37,51 @@ func TestNoGateTwoInstances(t *testing.T) { } func TestNoGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - }) + test(t, gkrtesting.NoGateCircuit()) } func TestSingleAddGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - }, - { - Gate: add2Gate, - Inputs: []int{0, 1}, - }, - }) + test(t, gkrtesting.SingleAddGateCircuit()) } func TestSingleMulGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - }, - { - Gate: mul2Gate, - Inputs: []int{0, 1}, - }, - }) + test(t, gkrtesting.SingleMulGateCircuit()) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - Inputs: []int{0}, - }, - { - Gate: identityGate, - Inputs: []int{0}, - }, - }) + test(t, gkrtesting.SingleInputTwoIdentityGatesCircuit()) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - Inputs: []int{0}, - }, - { - Gate: identityGate, - Inputs: []int{1}, - }, - }) + test(t, gkrtesting.SingleInputTwoIdentityGatesComposedCircuit()) } func TestAPowNTimesBCircuit(t *testing.T) { - const N = 10 - - c := make(Circuit, N+2) - - // Input wires - c[0] = Wire{ - Gate: identityGate, - } - c[1] = Wire{ - Gate: identityGate, - } - - for i := 2; i < len(c); i++ { - c[i] = Wire{ - Gate: mul2Gate, - Inputs: []int{i - 1, 0}, - } - } - - test(t, c) + test(t, gkrtesting.APowNTimesBCircuit(10)) } func TestSingleMimcCipherGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - }, - { - Inputs: []int{0, 1}, - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), - }, - }) + test(t, gkrtesting.SingleMimcCipherGateCircuit()) } func TestShallowMimcTwoInstances(t *testing.T) { - test(t, mimcCircuit(2)) + test(t, gkrtesting.MiMCCircuit(2)) } func TestMimc(t *testing.T) { - test(t, mimcCircuit(93)) + test(t, gkrtesting.MiMCCircuit(93)) } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := Circuit{Wire{ - Gate: identityGate, - NbUniqueOutputs: 2, - }} + gCircuit := gkrtypes.GadgetCircuit{ + { + Gate: &gkrtypes.GadgetGate{ + Evaluate: gkrtypes.Identity, + }, + NbUniqueOutputs: 2, + }, + } + circuit := toSerializableCircuit(gCircuit) assignment := WireAssignment{[]fr.Element{two, three}} var o settings @@ -214,8 +136,9 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit Circuit) { - wireRefs := utils.References(circuit) +func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { + sCircuit := toSerializableCircuit(circuit) + wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -235,19 +158,19 @@ func test(t *testing.T, circuit Circuit) { t.Log("Selected inputs for test") - proof, err := Prove(circuit, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(sCircuit, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(circuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) assert.NoError(t, err, "proof rejected") if proof.isEmpty() { // special case for TestNoGate: continue // there's no way to make a trivial proof fail } - err = Verify(circuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) + err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) assert.NotNil(t, err, "bad proof accepted") } @@ -268,11 +191,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := Circuit{ - { - Gate: identityGate, - }, - } + c := toSerializableCircuit(gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -285,69 +204,6 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { assert.NoError(t, err, "proof rejected") } -func mimcCircuit(numRounds int) Circuit { - c := make(Circuit, numRounds+2) - - // Input wires - c[0] = Wire{ - Gate: identityGate, - } - c[1] = Wire{ - Gate: identityGate, - } - - for i := 2; i < len(c); i++ { - c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), - Inputs: []int{i - 1, 0}, - } - } - return c -} - -func TestIsAdditive(t *testing.T) { - - // f: x,y -> x² + xy - f := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - if len(x) != 2 { - panic("bivariate input needed") - } - res := api.Add(x[0], x[1]) - return api.Mul(res, x[0]) - } - - // g: x,y -> x² + 3y - g := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - res := api.Mul(x[0], x[0]) - y3 := api.Mul(x[1], 3) - return api.Add(res, y3) - } - - // h: x -> 2x - h := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - return api.Add(x[0], x[0]) - } - - newTester := func(f gkr.GateFunction, nbIn int) *GateTester { - cg, err := gkrtypes.CompileGateFunction(f, nbIn) - assert.NoError(t, err) - var gt GateTester - gt.SetGate(cg, nbIn) - return > - } - - tester := newTester(f, 2) - assert.False(t, tester.IsAdditive(1)) - assert.False(t, tester.IsAdditive(0)) - - tester = newTester(g, 2) - assert.False(t, tester.IsAdditive(0)) - assert.True(t, tester.IsAdditive(1)) - - tester = newTester(h, 1) - assert.True(t, tester.IsAdditive(0)) -} - func generateTestProver(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) @@ -416,7 +272,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := mimcCircuit(mimcDepth) + c := toSerializableCircuit(gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 4691b4b826..b996d031ad 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -73,12 +73,14 @@ func TestMimc(t *testing.T) { } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - gCircuit := gkrtypes.GadgetCircuit{{ - Gate: &gkrtypes.GadgetGate{ - Evaluate: gkrtypes.Identity, + gCircuit := gkrtypes.GadgetCircuit{ + { + Gate: &gkrtypes.GadgetGate{ + Evaluate: gkrtypes.Identity, + }, + NbUniqueOutputs: 2, }, - NbUniqueOutputs: 2, - }} + } circuit := toSerializableCircuit(gCircuit) assignment := WireAssignment{[]fr.Element{two, three}} @@ -189,11 +191,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := Circuit{ - { - Gate: gkrtesting.IdentityGate, - }, - } + c := toSerializableCircuit(gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -274,8 +272,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - gc := gkrtesting.MiMCCircuit(mimcDepth) - c := toSerializableCircuit(gc) + c := toSerializableCircuit(gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 3a4048dc26..c1a8c168c9 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -15,24 +15,21 @@ import ( "testing" "time" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bn254/fr" "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" gcUtils "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/stretchr/testify/assert" ) -var ( - identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) - add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) - mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) -) +func toSerializableCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { + return gkrtypes.ToSerializableCircuit(ecc.BN254.ScalarField(), circuit) +} func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case @@ -40,126 +37,51 @@ func TestNoGateTwoInstances(t *testing.T) { } func TestNoGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - }) + test(t, gkrtesting.NoGateCircuit()) } func TestSingleAddGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - }, - { - Gate: add2Gate, - Inputs: []int{0, 1}, - }, - }) + test(t, gkrtesting.SingleAddGateCircuit()) } func TestSingleMulGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - }, - { - Gate: mul2Gate, - Inputs: []int{0, 1}, - }, - }) + test(t, gkrtesting.SingleMulGateCircuit()) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - Inputs: []int{0}, - }, - { - Gate: identityGate, - Inputs: []int{0}, - }, - }) + test(t, gkrtesting.SingleInputTwoIdentityGatesCircuit()) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - Inputs: []int{0}, - }, - { - Gate: identityGate, - Inputs: []int{1}, - }, - }) + test(t, gkrtesting.SingleInputTwoIdentityGatesComposedCircuit()) } func TestAPowNTimesBCircuit(t *testing.T) { - const N = 10 - - c := make(Circuit, N+2) - - // Input wires - c[0] = Wire{ - Gate: identityGate, - } - c[1] = Wire{ - Gate: identityGate, - } - - for i := 2; i < len(c); i++ { - c[i] = Wire{ - Gate: mul2Gate, - Inputs: []int{i - 1, 0}, - } - } - - test(t, c) + test(t, gkrtesting.APowNTimesBCircuit(10)) } func TestSingleMimcCipherGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - }, - { - Inputs: []int{0, 1}, - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), - }, - }) + test(t, gkrtesting.SingleMimcCipherGateCircuit()) } func TestShallowMimcTwoInstances(t *testing.T) { - test(t, mimcCircuit(2)) + test(t, gkrtesting.MiMCCircuit(2)) } func TestMimc(t *testing.T) { - test(t, mimcCircuit(93)) + test(t, gkrtesting.MiMCCircuit(93)) } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := Circuit{Wire{ - Gate: identityGate, - NbUniqueOutputs: 2, - }} + gCircuit := gkrtypes.GadgetCircuit{ + { + Gate: &gkrtypes.GadgetGate{ + Evaluate: gkrtypes.Identity, + }, + NbUniqueOutputs: 2, + }, + } + circuit := toSerializableCircuit(gCircuit) assignment := WireAssignment{[]fr.Element{two, three}} var o settings @@ -214,8 +136,9 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit Circuit) { - wireRefs := utils.References(circuit) +func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { + sCircuit := toSerializableCircuit(circuit) + wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -235,19 +158,19 @@ func test(t *testing.T, circuit Circuit) { t.Log("Selected inputs for test") - proof, err := Prove(circuit, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(sCircuit, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(circuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) assert.NoError(t, err, "proof rejected") if proof.isEmpty() { // special case for TestNoGate: continue // there's no way to make a trivial proof fail } - err = Verify(circuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) + err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) assert.NotNil(t, err, "bad proof accepted") } @@ -268,11 +191,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := Circuit{ - { - Gate: identityGate, - }, - } + c := toSerializableCircuit(gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -285,69 +204,6 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { assert.NoError(t, err, "proof rejected") } -func mimcCircuit(numRounds int) Circuit { - c := make(Circuit, numRounds+2) - - // Input wires - c[0] = Wire{ - Gate: identityGate, - } - c[1] = Wire{ - Gate: identityGate, - } - - for i := 2; i < len(c); i++ { - c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), - Inputs: []int{i - 1, 0}, - } - } - return c -} - -func TestIsAdditive(t *testing.T) { - - // f: x,y -> x² + xy - f := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - if len(x) != 2 { - panic("bivariate input needed") - } - res := api.Add(x[0], x[1]) - return api.Mul(res, x[0]) - } - - // g: x,y -> x² + 3y - g := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - res := api.Mul(x[0], x[0]) - y3 := api.Mul(x[1], 3) - return api.Add(res, y3) - } - - // h: x -> 2x - h := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - return api.Add(x[0], x[0]) - } - - newTester := func(f gkr.GateFunction, nbIn int) *GateTester { - cg, err := gkrtypes.CompileGateFunction(f, nbIn) - assert.NoError(t, err) - var gt GateTester - gt.SetGate(cg, nbIn) - return > - } - - tester := newTester(f, 2) - assert.False(t, tester.IsAdditive(1)) - assert.False(t, tester.IsAdditive(0)) - - tester = newTester(g, 2) - assert.False(t, tester.IsAdditive(0)) - assert.True(t, tester.IsAdditive(1)) - - tester = newTester(h, 1) - assert.True(t, tester.IsAdditive(0)) -} - func generateTestProver(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) @@ -416,7 +272,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := mimcCircuit(mimcDepth) + c := toSerializableCircuit(gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index cf2ae43c4d..44b074e8ec 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -15,24 +15,21 @@ import ( "testing" "time" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/mimc" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" gcUtils "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" - "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/stretchr/testify/assert" ) -var ( - identityGate = gkrtypes.ToSerializableGate(gkrtypes.Identity()) - add2Gate = gkrtypes.ToSerializableGate(gkrtypes.Add2()) - mul2Gate = gkrtypes.ToSerializableGate(gkrtypes.Mul2()) -) +func toSerializableCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { + return gkrtypes.ToSerializableCircuit(ecc.BW6_761.ScalarField(), circuit) +} func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case @@ -40,126 +37,51 @@ func TestNoGateTwoInstances(t *testing.T) { } func TestNoGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - }) + test(t, gkrtesting.NoGateCircuit()) } func TestSingleAddGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - }, - { - Gate: add2Gate, - Inputs: []int{0, 1}, - }, - }) + test(t, gkrtesting.SingleAddGateCircuit()) } func TestSingleMulGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - }, - { - Gate: mul2Gate, - Inputs: []int{0, 1}, - }, - }) + test(t, gkrtesting.SingleMulGateCircuit()) } func TestSingleInputTwoIdentityGates(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - Inputs: []int{0}, - }, - { - Gate: identityGate, - Inputs: []int{0}, - }, - }) + test(t, gkrtesting.SingleInputTwoIdentityGatesCircuit()) } func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - Inputs: []int{0}, - }, - { - Gate: identityGate, - Inputs: []int{1}, - }, - }) + test(t, gkrtesting.SingleInputTwoIdentityGatesComposedCircuit()) } func TestAPowNTimesBCircuit(t *testing.T) { - const N = 10 - - c := make(Circuit, N+2) - - // Input wires - c[0] = Wire{ - Gate: identityGate, - } - c[1] = Wire{ - Gate: identityGate, - } - - for i := 2; i < len(c); i++ { - c[i] = Wire{ - Gate: mul2Gate, - Inputs: []int{i - 1, 0}, - } - } - - test(t, c) + test(t, gkrtesting.APowNTimesBCircuit(10)) } func TestSingleMimcCipherGate(t *testing.T) { - test(t, Circuit{ - { - Gate: identityGate, - }, - { - Gate: identityGate, - }, - { - Inputs: []int{0, 1}, - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), - }, - }) + test(t, gkrtesting.SingleMimcCipherGateCircuit()) } func TestShallowMimcTwoInstances(t *testing.T) { - test(t, mimcCircuit(2)) + test(t, gkrtesting.MiMCCircuit(2)) } func TestMimc(t *testing.T) { - test(t, mimcCircuit(93)) + test(t, gkrtesting.MiMCCircuit(93)) } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := Circuit{Wire{ - Gate: identityGate, - NbUniqueOutputs: 2, - }} + gCircuit := gkrtypes.GadgetCircuit{ + { + Gate: &gkrtypes.GadgetGate{ + Evaluate: gkrtypes.Identity, + }, + NbUniqueOutputs: 2, + }, + } + circuit := toSerializableCircuit(gCircuit) assignment := WireAssignment{[]fr.Element{two, three}} var o settings @@ -214,8 +136,9 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit Circuit) { - wireRefs := utils.References(circuit) +func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { + sCircuit := toSerializableCircuit(circuit) + wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -235,19 +158,19 @@ func test(t *testing.T, circuit Circuit) { t.Log("Selected inputs for test") - proof, err := Prove(circuit, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(sCircuit, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(circuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) assert.NoError(t, err, "proof rejected") if proof.isEmpty() { // special case for TestNoGate: continue // there's no way to make a trivial proof fail } - err = Verify(circuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) + err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) assert.NotNil(t, err, "bad proof accepted") } @@ -268,11 +191,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := Circuit{ - { - Gate: identityGate, - }, - } + c := toSerializableCircuit(gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -285,69 +204,6 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { assert.NoError(t, err, "proof rejected") } -func mimcCircuit(numRounds int) Circuit { - c := make(Circuit, numRounds+2) - - // Input wires - c[0] = Wire{ - Gate: identityGate, - } - c[1] = Wire{ - Gate: identityGate, - } - - for i := 2; i < len(c); i++ { - c[i] = Wire{ - Gate: gkrtypes.ToSerializableGate(cache.GetGate("mimc")), - Inputs: []int{i - 1, 0}, - } - } - return c -} - -func TestIsAdditive(t *testing.T) { - - // f: x,y -> x² + xy - f := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - if len(x) != 2 { - panic("bivariate input needed") - } - res := api.Add(x[0], x[1]) - return api.Mul(res, x[0]) - } - - // g: x,y -> x² + 3y - g := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - res := api.Mul(x[0], x[0]) - y3 := api.Mul(x[1], 3) - return api.Add(res, y3) - } - - // h: x -> 2x - h := func(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { - return api.Add(x[0], x[0]) - } - - newTester := func(f gkr.GateFunction, nbIn int) *GateTester { - cg, err := gkrtypes.CompileGateFunction(f, nbIn) - assert.NoError(t, err) - var gt GateTester - gt.SetGate(cg, nbIn) - return > - } - - tester := newTester(f, 2) - assert.False(t, tester.IsAdditive(1)) - assert.False(t, tester.IsAdditive(0)) - - tester = newTester(g, 2) - assert.False(t, tester.IsAdditive(0)) - assert.True(t, tester.IsAdditive(1)) - - tester = newTester(h, 1) - assert.True(t, tester.IsAdditive(0)) -} - func generateTestProver(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) @@ -416,7 +272,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := mimcCircuit(mimcDepth) + c := toSerializableCircuit(gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) From 0eac4e62fe5509cd9438a5a29f4b0ec7ee57c9c4 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 11:03:43 -0600 Subject: [PATCH 179/251] build: generify --- .../template/gkr/gkr.test.vectors.go.tmpl | 2 +- internal/gkr/bls12-377/gkr_test.go | 2 +- internal/gkr/bn254/gkr_test.go | 2 +- internal/gkr/bw6-761/gkr_test.go | 2 +- .../gkr/small_rational/test_vector_gen.go | 2 +- std/gkrapi/compile.go | 28 +++++++------------ 6 files changed, 15 insertions(+), 23 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl index 29b64252ca..168e81e140 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl @@ -62,7 +62,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := toSerializableCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index 5995f25b54..ba67544b0b 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -361,7 +361,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := toSerializableCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index c1a8c168c9..b8f08586a5 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -361,7 +361,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := toSerializableCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index 44b074e8ec..d6bb6d3efe 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -361,7 +361,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := toSerializableCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/small_rational/test_vector_gen.go b/internal/gkr/small_rational/test_vector_gen.go index 7fa9c4114d..3c2d25aaae 100644 --- a/internal/gkr/small_rational/test_vector_gen.go +++ b/internal/gkr/small_rational/test_vector_gen.go @@ -216,7 +216,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := gkrtypes.ToSerializable(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := toSerializableCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 94380c8e7a..993f7a50e7 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" gadget "github.com/consensys/gnark/internal/gkr" gkrbls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" @@ -70,29 +71,20 @@ func WithInitialChallenge(getInitialChallenge InitialChallengeGetter) CompileOpt // From this point on, the circuit cannot be modified, // but instances can be added to it. func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*Circuit, error) { + compiler := api.parentApi.Compiler() + field := compiler.Field() + res := Circuit{ circuit: api.circuit, assignments: make(gadget.WireAssignment, len(api.circuit)), api: api.parentApi, hashName: fiatshamirHashName, - } - - // Dispatch to curve-specific factory - curveID := utils.FieldToCurve(api.parentApi.Compiler().Field()) - compiler := api.parentApi.Compiler() - serializableCircuit := gadget.ToSerializableCircuit(curveID, api.circuit) - - switch curveID { - case ecc.BN254: - res.blueprints = gkrbn254.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) - case ecc.BLS12_377: - res.blueprints = gkrbls12377.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) - case ecc.BLS12_381: - res.blueprints = gkrbls12381.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) - case ecc.BW6_761: - res.blueprints = gkrbw6761.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) - default: - return nil, fmt.Errorf("unsupported curve: %s", curveID) + blueprints: [...]func(gkrtypes.SerializableCircuit, string, constraint.CustomizableSystem) gkrtypes.Blueprints{ + ecc.BN254: gkrbn254.NewBlueprints, + ecc.BLS12_377: gkrbls12377.NewBlueprints, + ecc.BLS12_381: gkrbls12381.NewBlueprints, + ecc.BW6_761: gkrbw6761.NewBlueprints, + }[utils.FieldToCurve(field)](gkrtypes.ToSerializableCircuit(field, api.circuit), fiatshamirHashName, compiler), } for _, opt := range options { From e1ed95a21bdd1d219a8db3f0c1b8723750f09a7e Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 11:09:27 -0600 Subject: [PATCH 180/251] feat: toSerializableCircuit for tests --- .../backend/template/gkr/gkr.test.vectors.gen.go.tmpl | 8 ++++++++ internal/gkr/small_rational/test_vector_gen.go | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl index 2d305103ab..9f0457e9b8 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl @@ -2,6 +2,7 @@ import ( "encoding/json" "fmt" "github.com/consensys/bavard" + "github.com/consensys/gnark-crypto/ecc" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" @@ -14,6 +15,13 @@ import ( "reflect" ) +func toSerializableCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { + // The properties of test gates are expected to be the same across all relevant fields. + // We can therefore use the gate testing functions for any curve rather that reimplementing + // for small_rational. + return gkrtypes.ToSerializableCircuit(ecc.BN254.ScalarField(), circuit) +} + func GenerateVectors() error { testDirPath, err := filepath.Abs("../../gkr/test_vectors") if err != nil { diff --git a/internal/gkr/small_rational/test_vector_gen.go b/internal/gkr/small_rational/test_vector_gen.go index 3c2d25aaae..b658ab8e20 100644 --- a/internal/gkr/small_rational/test_vector_gen.go +++ b/internal/gkr/small_rational/test_vector_gen.go @@ -14,6 +14,7 @@ import ( "reflect" "github.com/consensys/bavard" + "github.com/consensys/gnark-crypto/ecc" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" @@ -22,6 +23,13 @@ import ( "github.com/consensys/gnark/internal/utils" ) +func toSerializableCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { + // The properties of test gates are expected to be the same across all relevant fields. + // We can therefore use the gate testing functions for any curve rather that reimplementing + // for small_rational. + return gkrtypes.ToSerializableCircuit(ecc.BN254.ScalarField(), circuit) +} + func GenerateVectors() error { testDirPath, err := filepath.Abs("../../gkr/test_vectors") if err != nil { From 16bc2bb4ea006cb8c2ab7b647b9c7b551513b0de Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 14:19:33 -0600 Subject: [PATCH 181/251] refactor: remove pointer types --- .../backend/template/gkr/gkr.go.tmpl | 8 +- .../backend/template/gkr/gkr.test.go.tmpl | 2 +- internal/gkr/bls12-377/gkr.go | 8 +- internal/gkr/bls12-377/gkr_test.go | 2 +- internal/gkr/bls12-381/gkr.go | 8 +- internal/gkr/bls12-381/gkr_test.go | 2 +- internal/gkr/bn254/gkr.go | 8 +- internal/gkr/bn254/gkr_test.go | 2 +- internal/gkr/bw6-761/gkr.go | 8 +- internal/gkr/bw6-761/gkr_test.go | 2 +- internal/gkr/gkrtesting/gkrtesting.go | 63 +++----------- internal/gkr/gkrtypes/gate.go | 12 +-- internal/gkr/gkrtypes/types.go | 86 ++----------------- internal/gkr/small_rational/gkr.go | 8 +- std/gkrapi/api.go | 2 +- std/gkrapi/compile.go | 31 ++++--- 16 files changed, 78 insertions(+), 174 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index f48acb843b..f747b83815 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -740,7 +740,7 @@ func (p Proof) flatten() iter.Seq2[int, *{{ .ElementType }}] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.GateBytecode + gate gkrtypes.GateBytecode vars []{{ .ElementType }} frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -748,7 +748,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -840,14 +840,14 @@ func (e *gateEvaluator) evaluate(top ...{{ .ElementType }}) *{{ .ElementType }} // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.GateBytecode + gate gkrtypes.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index 7e2abbd762..ca3aee1af0 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -68,7 +68,7 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { gCircuit := gkrtypes.GadgetCircuit{ { - Gate: &gkrtypes.GadgetGate{ + Gate: gkrtypes.GadgetGate{ Evaluate: gkrtypes.Identity, }, NbUniqueOutputs: 2, diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 6493a8499a..78451d0d31 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -745,7 +745,7 @@ func (p Proof) flatten() iter.Seq2[int, *fr.Element] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.GateBytecode + gate gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -753,7 +753,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -845,14 +845,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.GateBytecode + gate gkrtypes.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index ba67544b0b..d09a6911cc 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -75,7 +75,7 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { gCircuit := gkrtypes.GadgetCircuit{ { - Gate: &gkrtypes.GadgetGate{ + Gate: gkrtypes.GadgetGate{ Evaluate: gkrtypes.Identity, }, NbUniqueOutputs: 2, diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 39a4197e09..ce73fa2662 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -745,7 +745,7 @@ func (p Proof) flatten() iter.Seq2[int, *fr.Element] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.GateBytecode + gate gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -753,7 +753,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -845,14 +845,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.GateBytecode + gate gkrtypes.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index b996d031ad..41897c634c 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -75,7 +75,7 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { gCircuit := gkrtypes.GadgetCircuit{ { - Gate: &gkrtypes.GadgetGate{ + Gate: gkrtypes.GadgetGate{ Evaluate: gkrtypes.Identity, }, NbUniqueOutputs: 2, diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index a57ea2dbab..086f9988b8 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -745,7 +745,7 @@ func (p Proof) flatten() iter.Seq2[int, *fr.Element] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.GateBytecode + gate gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -753,7 +753,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -845,14 +845,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.GateBytecode + gate gkrtypes.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index b8f08586a5..832c5625b0 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -75,7 +75,7 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { gCircuit := gkrtypes.GadgetCircuit{ { - Gate: &gkrtypes.GadgetGate{ + Gate: gkrtypes.GadgetGate{ Evaluate: gkrtypes.Identity, }, NbUniqueOutputs: 2, diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 41296ede56..d80a456da0 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -745,7 +745,7 @@ func (p Proof) flatten() iter.Seq2[int, *fr.Element] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.GateBytecode + gate gkrtypes.GateBytecode vars []fr.Element frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -753,7 +753,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -845,14 +845,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.GateBytecode + gate gkrtypes.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index d6bb6d3efe..352001652b 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -75,7 +75,7 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { gCircuit := gkrtypes.GadgetCircuit{ { - Gate: &gkrtypes.GadgetGate{ + Gate: gkrtypes.GadgetGate{ Evaluate: gkrtypes.Identity, }, NbUniqueOutputs: 2, diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 92abaef677..790bfef32d 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -11,41 +11,6 @@ import ( "github.com/consensys/gnark/std/gkrapi/gkr" ) -func must(g *gkrtypes.GateBytecode, err error) *gkrtypes.GateBytecode { - if err != nil { - panic(err) - } - return g -} - -// Exported compiled gates for tests that need SerializableGate (bytecode gates) -var ( - IdentityGate *gkrtypes.SerializableGate - Add2Gate *gkrtypes.SerializableGate - Mul2Gate *gkrtypes.SerializableGate -) - -func init() { - IdentityGate = &gkrtypes.SerializableGate{ - Evaluate: must(gkrtypes.CompileGateFunction(gkrtypes.Identity, 1)), - NbIn: 1, - Degree: 1, - SolvableVar: 0, - } - Add2Gate = &gkrtypes.SerializableGate{ - Evaluate: must(gkrtypes.CompileGateFunction(gkrtypes.Add2, 2)), - NbIn: 2, - Degree: 1, - SolvableVar: 0, - } - Mul2Gate = &gkrtypes.SerializableGate{ - Evaluate: must(gkrtypes.CompileGateFunction(gkrtypes.Mul2, 2)), - NbIn: 2, - Degree: 2, - SolvableVar: -1, - } -} - // Cache for circuits and gates. // The main functionality is to cache whole circuits, but this package needs to use its own gate registry, in order to avoid import cycles. // Cache is used in tests for the per-curve GKR packages, but they in turn provide gate degree discovery functions to the gkrgates package. @@ -120,7 +85,7 @@ func (c *Cache) GetCircuit(path string) gkrtypes.GadgetCircuit { gate := c.GetGate(wJSON.Gate) circuit[i] = gkrtypes.GadgetWire{ - Gate: &gkrtypes.Gate[gkr.GateFunction]{Evaluate: gate}, + Gate: gkrtypes.Gate[gkr.GateFunction]{Evaluate: gate}, Inputs: wJSON.Inputs, } } @@ -146,8 +111,8 @@ func (c *Cache) GetGate(name string) gkr.GateFunction { func MiMCCircuit(numRounds int) gkrtypes.GadgetCircuit { c := make(gkrtypes.GadgetCircuit, numRounds+2) - idGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} - mimc := &gkrtypes.GadgetGate{Evaluate: mimcGate} + idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} + mimc := gkrtypes.GadgetGate{Evaluate: mimcGate} c[0] = gkrtypes.GadgetWire{Gate: idGate} c[1] = gkrtypes.GadgetWire{Gate: idGate} @@ -189,31 +154,31 @@ func (c *Cache) ReadTestCaseInfo(filePath string) (info TestCaseInfo, err error) func NoGateCircuit() gkrtypes.GadgetCircuit { return gkrtypes.GadgetCircuit{ { - Gate: &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity}, + Gate: gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity}, }, } } func SingleAddGateCircuit() gkrtypes.GadgetCircuit { - idGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} + idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} return gkrtypes.GadgetCircuit{ {Gate: idGate}, {Gate: idGate}, - {Gate: &gkrtypes.GadgetGate{Evaluate: gkrtypes.Add2}, Inputs: []int{0, 1}}, + {Gate: gkrtypes.GadgetGate{Evaluate: gkrtypes.Add2}, Inputs: []int{0, 1}}, } } func SingleMulGateCircuit() gkrtypes.GadgetCircuit { - idGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} + idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} return gkrtypes.GadgetCircuit{ {Gate: idGate}, {Gate: idGate}, - {Gate: &gkrtypes.GadgetGate{Evaluate: gkrtypes.Mul2}, Inputs: []int{0, 1}}, + {Gate: gkrtypes.GadgetGate{Evaluate: gkrtypes.Mul2}, Inputs: []int{0, 1}}, } } func SingleInputTwoIdentityGatesCircuit() gkrtypes.GadgetCircuit { - idGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} + idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} return gkrtypes.GadgetCircuit{ {Gate: idGate}, {Gate: idGate, Inputs: []int{0}}, @@ -222,7 +187,7 @@ func SingleInputTwoIdentityGatesCircuit() gkrtypes.GadgetCircuit { } func SingleInputTwoIdentityGatesComposedCircuit() gkrtypes.GadgetCircuit { - idGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} + idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} return gkrtypes.GadgetCircuit{ {Gate: idGate}, {Gate: idGate, Inputs: []int{0}}, @@ -232,8 +197,8 @@ func SingleInputTwoIdentityGatesComposedCircuit() gkrtypes.GadgetCircuit { func APowNTimesBCircuit(n int) gkrtypes.GadgetCircuit { c := make(gkrtypes.GadgetCircuit, n+2) - idGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} - mulGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Mul2} + idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} + mulGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Mul2} c[0] = gkrtypes.GadgetWire{Gate: idGate} c[1] = gkrtypes.GadgetWire{Gate: idGate} @@ -245,10 +210,10 @@ func APowNTimesBCircuit(n int) gkrtypes.GadgetCircuit { } func SingleMimcCipherGateCircuit() gkrtypes.GadgetCircuit { - idGate := &gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} + idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} return gkrtypes.GadgetCircuit{ {Gate: idGate}, {Gate: idGate}, - {Gate: &gkrtypes.GadgetGate{Evaluate: mimcGate}, Inputs: []int{0, 1}}, + {Gate: gkrtypes.GadgetGate{Evaluate: mimcGate}, Inputs: []int{0, 1}}, } } diff --git a/internal/gkr/gkrtypes/gate.go b/internal/gkr/gkrtypes/gate.go index c1e56436e1..016f0fba9f 100644 --- a/internal/gkr/gkrtypes/gate.go +++ b/internal/gkr/gkrtypes/gate.go @@ -229,7 +229,7 @@ func (gc *gateCompiler) remapIndices() { // CompileGateFunction compiles a gate function into a GateBytecode. // The gate function should be of type gkr.GateFunction. -func CompileGateFunction(f gkr.GateFunction, nbInputs int) (*GateBytecode, error) { +func CompileGateFunction(f gkr.GateFunction, nbInputs int) (GateBytecode, error) { // Create compiling API compiler := gateCompiler{ constantIndex: make(map[string]uint16), @@ -249,9 +249,9 @@ func CompileGateFunction(f gkr.GateFunction, nbInputs int) (*GateBytecode, error // If the output simply mirrors the last input, we can still represent // it in bytecode, as the evaluator returns the last stack frame element. if int(out.(compilationVar).id) == nbInputs-1 { - return &GateBytecode{}, nil + return GateBytecode{}, nil } - return nil, errors.New("cannot compile no-op gate function") + return GateBytecode{}, errors.New("cannot compile no-op gate function") } // All instructions after the output are no-ops. Prune them and the corresponding variables. @@ -262,7 +262,7 @@ func CompileGateFunction(f gkr.GateFunction, nbInputs int) (*GateBytecode, error // Remap indices from temporary layout to final layout compiler.remapIndices() - return &GateBytecode{ + return GateBytecode{ Instructions: compiler.GetInstructions(), Constants: compiler.constants, }, nil @@ -270,12 +270,12 @@ func CompileGateFunction(f gkr.GateFunction, nbInputs int) (*GateBytecode, error type gateTester struct { mod *big.Int - gate *GateBytecode + gate GateBytecode vars []*big.Int nbIn int } -func (t *gateTester) setGate(g *GateBytecode, nbIn int) { +func (t *gateTester) setGate(g GateBytecode, nbIn int) { t.gate = g t.vars = make([]*big.Int, g.NbConstants()+nbIn+len(g.Instructions)) t.nbIn = nbIn diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 8f77f3df52..6f796bed38 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -3,7 +3,6 @@ package gkrtypes import ( "math/big" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/gkrapi/gkr" @@ -25,7 +24,7 @@ type ( } Wire[GateExecutable any] struct { - Gate *Gate[GateExecutable] + Gate Gate[GateExecutable] Inputs []int NbUniqueOutputs int } @@ -43,17 +42,11 @@ type ( // Type aliases for different circuit instantiations - // Registered types (with both bytecode and SNARK-friendly executables) - RegisteredGate = Gate[BothExecutables] - RegisteredCircuit = Circuit[BothExecutables] - RegisteredWire = Wire[BothExecutables] - RegisteredWires = Wires[BothExecutables] - // Serializable types (bytecode only, for native proving) - SerializableGate = Gate[*GateBytecode] - SerializableCircuit = Circuit[*GateBytecode] - SerializableWire = Wire[*GateBytecode] - SerializableWires = Wires[*GateBytecode] + SerializableGate = Gate[GateBytecode] + SerializableCircuit = Circuit[GateBytecode] + SerializableWire = Wire[GateBytecode] + SerializableWires = Wires[GateBytecode] // Gadget types (gate functions only, for in-circuit verification) GadgetGate = Gate[gkr.GateFunction] @@ -62,32 +55,12 @@ type ( GadgetWires = Wires[gkr.GateFunction] ) -// NewGate creates a new gate function with the given parameters: -// - f: the polynomial function defining the gate -// - compiled: the compiled form of the gate function -// - NbIn: number of inputs to the gate -// - Degree: total Degree of the polynomial. In case of multivariate polynomials, it is the maximum Degree over all terms. -// - SolvableVar: if there is a variable whose value can be uniquely determined from the value of the gate and the other inputs, its index, -1 otherwise -// - Curves: Curves that the gate is allowed to be used over -func NewGate(f gkr.GateFunction, compiled *GateBytecode, nbIn int, degree int, solvableVar int, curves []ecc.ID) *RegisteredGate { - - return &RegisteredGate{ - Evaluate: BothExecutables{ - Bytecode: compiled, - SnarkFriendly: f, - }, - NbIn: nbIn, - Degree: degree, - SolvableVar: solvableVar, - } -} - func (be BothExecutables) getGateFunction() gkr.GateFunction { return be.SnarkFriendly } -func (be BothExecutables) getByteCode() *GateBytecode { - return be.Bytecode +func (be BothExecutables) getByteCode() GateBytecode { + return *be.Bytecode } // IsInput returns whether the wire is an input wire. @@ -356,48 +329,6 @@ func Mul2(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { return api.Mul(in[0], in[1]) } -func ConvertGate[GateExecutable, TargetGateExecutable any](g *Gate[GateExecutable], converter func(GateExecutable) TargetGateExecutable) *Gate[TargetGateExecutable] { - return &Gate[TargetGateExecutable]{ - Evaluate: converter(g.Evaluate), - NbIn: g.NbIn, - Degree: g.Degree, - SolvableVar: g.SolvableVar, - } -} - -func ConvertCircuit[GateExecutable, TargetGateExecutable any](c Circuit[GateExecutable], gateConverter func(GateExecutable) TargetGateExecutable) Circuit[TargetGateExecutable] { - res := make(Circuit[TargetGateExecutable], len(c)) - for i := range c { - res[i] = Wire[TargetGateExecutable]{ - Gate: ConvertGate(c[i].Gate, gateConverter), - Inputs: c[i].Inputs, - NbUniqueOutputs: c[i].NbUniqueOutputs, - } - } - - return res -} - -// ToSerializable converts a registered circuit (with both executables) to a serializable circuit (bytecode only). -func ToSerializable(c RegisteredCircuit) SerializableCircuit { - return ConvertCircuit(c, BothExecutables.getByteCode) -} - -func ToSerializableGate(g *RegisteredGate) *Gate[*GateBytecode] { - return ConvertGate(g, BothExecutables.getByteCode) -} - -func ToGadgetGate(g *RegisteredGate) *Gate[gkr.GateFunction] { - return ConvertGate(g, BothExecutables.getGateFunction) -} - -// ToGadget converts a registered circuit (with both executables) to a gadget circuit (gate functions only, for in-circuit verification). -func ToGadget(c RegisteredCircuit) GadgetCircuit { - return ConvertCircuit(c, func(e BothExecutables) gkr.GateFunction { - return e.SnarkFriendly - }) -} - // BlueprintSolve is the interface for GKR solve blueprints type BlueprintSolve interface { constraint.Blueprint @@ -417,9 +348,8 @@ type Blueprints struct { func ToSerializableCircuit(mod *big.Int, c GadgetCircuit) SerializableCircuit { tester := gateTester{mod: mod} - - var err error res := make(SerializableCircuit, len(c)) + var err error for i := range c { res[i].Inputs = c[i].Inputs diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 9d1b62dfb6..ceff2cf3c2 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -745,7 +745,7 @@ func (p Proof) flatten() iter.Seq2[int, *small_rational.SmallRational] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate *gkrtypes.GateBytecode + gate gkrtypes.GateBytecode vars []small_rational.SmallRational frameSize int // number of constants plus currently pushed inputs nbIn int // number of inputs expected @@ -753,7 +753,7 @@ type gateEvaluator struct { // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate *gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -845,14 +845,14 @@ func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rat // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate *gkrtypes.GateBytecode + gate gkrtypes.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate *gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index 573ce12532..933f75fa1a 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -29,7 +29,7 @@ func frontendVarToInt(a gkr.Variable) int { // Gate adds the given gate with the given inputs and returns its output wire. func (api *API) Gate(gate gkr.GateFunction, inputs ...gkr.Variable) gkr.Variable { api.circuit = append(api.circuit, gkrtypes.GadgetWire{ - Gate: &gkrtypes.Gate[gkr.GateFunction]{Evaluate: gate}, + Gate: gkrtypes.GadgetGate{Evaluate: gate}, Inputs: utils.Map(inputs, frontendVarToInt), }) api.assignments = append(api.assignments, nil) diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 993f7a50e7..f4a0bbed7e 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" gadget "github.com/consensys/gnark/internal/gkr" gkrbls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" @@ -52,7 +51,7 @@ func New(api frontend.API) (*API, error) { // NewInput creates a new input variable. func (api *API) NewInput() gkr.Variable { i := len(api.circuit) - api.circuit = append(api.circuit, gkrtypes.GadgetWire{Gate: &gkrtypes.Gate[gkr.GateFunction]{Evaluate: gkrtypes.Identity}}) + api.circuit = append(api.circuit, gkrtypes.GadgetWire{Gate: gkrtypes.Gate[gkr.GateFunction]{Evaluate: gkrtypes.Identity}}) api.assignments = append(api.assignments, nil) return gkr.Variable(i) } @@ -71,20 +70,30 @@ func WithInitialChallenge(getInitialChallenge InitialChallengeGetter) CompileOpt // From this point on, the circuit cannot be modified, // but instances can be added to it. func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*Circuit, error) { - compiler := api.parentApi.Compiler() - field := compiler.Field() - res := Circuit{ circuit: api.circuit, assignments: make(gadget.WireAssignment, len(api.circuit)), api: api.parentApi, hashName: fiatshamirHashName, - blueprints: [...]func(gkrtypes.SerializableCircuit, string, constraint.CustomizableSystem) gkrtypes.Blueprints{ - ecc.BN254: gkrbn254.NewBlueprints, - ecc.BLS12_377: gkrbls12377.NewBlueprints, - ecc.BLS12_381: gkrbls12381.NewBlueprints, - ecc.BW6_761: gkrbw6761.NewBlueprints, - }[utils.FieldToCurve(field)](gkrtypes.ToSerializableCircuit(field, api.circuit), fiatshamirHashName, compiler), + } + + // Dispatch to curve-specific factory + compiler := api.parentApi.Compiler() + field := compiler.Field() + curveID := utils.FieldToCurve(field) + serializableCircuit := gkrtypes.ToSerializableCircuit(field, api.circuit) + + switch curveID { + case ecc.BN254: + res.blueprints = gkrbn254.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) + case ecc.BLS12_377: + res.blueprints = gkrbls12377.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) + case ecc.BLS12_381: + res.blueprints = gkrbls12381.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) + case ecc.BW6_761: + res.blueprints = gkrbw6761.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) + default: + return nil, fmt.Errorf("unsupported curve: %s", curveID) } for _, opt := range options { From becb46aa8dbbbf7a8b41646f858a188de3b8f05d Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 15:47:11 -0600 Subject: [PATCH 182/251] fix: gate compilation bug in ToSerializableCircuit --- internal/gkr/gkrtypes/types.go | 70 ++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 6f796bed38..f61cf57df1 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -2,6 +2,7 @@ package gkrtypes import ( "math/big" + "reflect" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" @@ -170,29 +171,6 @@ func (c Circuit[GateExecutable]) OutputsList() [][]int { return res } -func (c Circuit[GateExecutable]) setNbUniqueOutputs() { - - for i := range c { - c[i].NbUniqueOutputs = 0 - } - - curWireIn := make([]bool, len(c)) - uniqueIns := make([]int, 0, len(c)) - for i := range c { - // clear the caches - for j := range uniqueIns { - curWireIn[uniqueIns[j]] = false - } - uniqueIns = uniqueIns[:0] - - // count! - for _, in := range c[i].Inputs { - if !curWireIn[in] { - c[in].NbUniqueOutputs++ - curWireIn[in] = true - uniqueIns = append(uniqueIns, in) - } - } } } @@ -345,17 +323,38 @@ type Blueprints struct { // ToSerializableCircuit converts a gadget circuit to a serializable circuit by compiling the gate functions. // It also sets the gate metadata (Degree, SolvableVar) for both the input and output circuits. -func ToSerializableCircuit(mod *big.Int, c GadgetCircuit) SerializableCircuit { +// Compile converts a gadget circuit to a serializable circuit by compiling the gate functions. +// It also sets wire and gate metadata (Degree, SolvableVar, NbUniqueOutputs) for both the input and output circuits. +func (c GadgetCircuit) Compile(mod *big.Int) SerializableCircuit { - tester := gateTester{mod: mod} + for i := range c { + } + curWireIn := make([]bool, len(c)) // curWireIn[j] = true iff i takes j as input. + tester := gateTester{mod: mod} // tester computes the gate's degree res := make(SerializableCircuit, len(c)) var err error for i := range c { - res[i].Inputs = c[i].Inputs + // Compute NbUniqueOutputs as we go. + for j := range curWireIn { + curWireIn[j] = false + } - c[i].Gate.NbIn = len(c[i].Inputs) - res[i].Gate.NbIn = c[i].Gate.NbIn + // count! + for _, in := range c[i].Inputs { + if !curWireIn[in] { + c[in].NbUniqueOutputs++ + curWireIn[in] = true + } + } + + if c[i].IsInput() { + if !reflect.DeepEqual(c[i].Gate, GadgetGate{}) { + panic("empty gate expected for input wire") + } + break + } + c[i].Gate.NbIn = len(c[i].Inputs) if res[i].Gate.Evaluate, err = CompileGateFunction(c[i].Gate.Evaluate, c[i].Gate.NbIn); err != nil { panic(err) } @@ -366,17 +365,24 @@ func ToSerializableCircuit(mod *big.Int, c GadgetCircuit) SerializableCircuit { if c[i].Gate.Degree == -1 { panic("cannot find degree for gate") } - res[i].Gate.Degree = c[i].Gate.Degree - res[i].Gate.SolvableVar = -1 + c[i].Gate.SolvableVar = -1 for j := range c[i].Gate.NbIn { if tester.isAdditive(j) { - res[i].Gate.SolvableVar = j + c[i].Gate.SolvableVar = j break } } - c[i].Gate.SolvableVar = res[i].Gate.SolvableVar + } + + // copy metadata from c to res + for i := range c { + res[i].Inputs = c[i].Inputs + res[i].NbUniqueOutputs = c[i].NbUniqueOutputs + res[i].Gate.Degree = c[i].Gate.Degree res[i].Gate.SolvableVar = c[i].Gate.SolvableVar + res[i].Gate.NbIn = c[i].Gate.NbIn } + return res } From 88a5f8c60a3f46359f7ca2e34c78cf7cac04031f Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 15:47:52 -0600 Subject: [PATCH 183/251] fix: strange git behavior --- internal/gkr/gkrtypes/types.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index f61cf57df1..ca7ce14cf3 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -171,9 +171,6 @@ func (c Circuit[GateExecutable]) OutputsList() [][]int { return res } - } -} - // Inputs returns the list of input wire indices. func (c Circuit[GateExecutable]) Inputs() []int { res := make([]int, 0, len(c)) @@ -321,14 +318,15 @@ type Blueprints struct { GetAssignmentID constraint.BlueprintID } -// ToSerializableCircuit converts a gadget circuit to a serializable circuit by compiling the gate functions. -// It also sets the gate metadata (Degree, SolvableVar) for both the input and output circuits. // Compile converts a gadget circuit to a serializable circuit by compiling the gate functions. // It also sets wire and gate metadata (Degree, SolvableVar, NbUniqueOutputs) for both the input and output circuits. func (c GadgetCircuit) Compile(mod *big.Int) SerializableCircuit { for i := range c { + c[i].NbUniqueOutputs = 0 } + + // compile the gate and compute metadata curWireIn := make([]bool, len(c)) // curWireIn[j] = true iff i takes j as input. tester := gateTester{mod: mod} // tester computes the gate's degree res := make(SerializableCircuit, len(c)) From a4801df9cfcac3bb0308791bda1c742b0c7f1663 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 15:54:11 -0600 Subject: [PATCH 184/251] propagate changes --- .../generator/backend/template/gkr/gkr.test.go.tmpl | 12 ++++++------ .../template/gkr/gkr.test.vectors.gen.go.tmpl | 6 +++--- .../backend/template/gkr/gkr.test.vectors.go.tmpl | 2 +- internal/gkr/gkrtypes/types.go | 13 ------------- 4 files changed, 10 insertions(+), 23 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index ca3aee1af0..408d4e41ef 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -20,8 +20,8 @@ import ( "time" ) -func toSerializableCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - return gkrtypes.ToSerializableCircuit(ecc.{{.FieldID}}.ScalarField(), circuit) +func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { + return circuit.Compile(ecc.{{.FieldID}}.ScalarField()) } func TestNoGateTwoInstances(t *testing.T) { @@ -74,7 +74,7 @@ func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { NbUniqueOutputs: 2, }, } - circuit := toSerializableCircuit(gCircuit) + circuit := compileCircuit(gCircuit) assignment := WireAssignment{[]{{ .ElementType }}{two, three}} var o settings @@ -130,7 +130,7 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := toSerializableCircuit(circuit) + sCircuit := compileCircuit(circuit) wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -184,7 +184,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]{{ .ElementType }}) { - c := toSerializableCircuit(gkrtesting.NoGateCircuit()) + c := compileCircuit(gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -265,7 +265,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := toSerializableCircuit(gkrtesting.MiMCCircuit(mimcDepth)) + c := compileCircuit(gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]{{ .ElementType }}, nbInstances) in1 := make([]{{ .ElementType }}, nbInstances) diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl index 9f0457e9b8..0791eed463 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl @@ -15,11 +15,11 @@ import ( "reflect" ) -func toSerializableCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { +func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { // The properties of test gates are expected to be the same across all relevant fields. - // We can therefore use the gate testing functions for any curve rather that reimplementing + // We can therefore use the gate testing functions for any curve rather than reimplementing // for small_rational. - return gkrtypes.ToSerializableCircuit(ecc.BN254.ScalarField(), circuit) + return circuit.Compile(ecc.BN254.ScalarField()) } func GenerateVectors() error { diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl index 168e81e140..0dd60f3b82 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl @@ -62,7 +62,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := toSerializableCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := compileCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index ca7ce14cf3..1fb035c7e6 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -36,11 +36,6 @@ type ( // information through the circuit. Wires[GateExecutable any] []*Wire[GateExecutable] - BothExecutables struct { - Bytecode *GateBytecode - SnarkFriendly gkr.GateFunction - } - // Type aliases for different circuit instantiations // Serializable types (bytecode only, for native proving) @@ -56,14 +51,6 @@ type ( GadgetWires = Wires[gkr.GateFunction] ) -func (be BothExecutables) getGateFunction() gkr.GateFunction { - return be.SnarkFriendly -} - -func (be BothExecutables) getByteCode() GateBytecode { - return *be.Bytecode -} - // IsInput returns whether the wire is an input wire. func (w Wire[GateExecutable]) IsInput() bool { return len(w.Inputs) == 0 From 7cf27866141329c977069028b61d931d03869779 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 16:03:32 -0600 Subject: [PATCH 185/251] remove id gates in test circuits --- .../backend/template/gkr/blueprint.go.tmpl | 11 +++---- .../backend/template/gkr/gkr.test.go.tmpl | 2 +- .../template/gkr/gkr.test.vectors.gen.go.tmpl | 2 +- internal/gkr/bls12-377/blueprint.go | 11 +++---- internal/gkr/bls12-377/gkr_test.go | 14 ++++----- internal/gkr/bls12-381/blueprint.go | 11 +++---- internal/gkr/bls12-381/gkr_test.go | 14 ++++----- internal/gkr/bn254/blueprint.go | 11 +++---- internal/gkr/bn254/gkr_test.go | 14 ++++----- internal/gkr/bw6-761/blueprint.go | 11 +++---- internal/gkr/bw6-761/gkr_test.go | 14 ++++----- internal/gkr/gkrtesting/gkrtesting.go | 31 ++++++------------- internal/gkr/gkrtypes/types.go | 4 +-- .../gkr/small_rational/test_vector_gen.go | 8 ++--- std/gkrapi/compile.go | 3 +- 15 files changed, 66 insertions(+), 95 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index aa186aea13..adae37f4ad 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -41,11 +41,6 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -func (b *BlueprintSolve) setOutputWires() { - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() -} - // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { @@ -62,7 +57,7 @@ func (b *BlueprintSolve) Reset() { return ce } - b.setOutputWires() + b.outputWires = b.Circuit.Outputs() assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) @@ -144,7 +139,9 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.setOutputWires() + if b.outputWires == nil { + b.outputWires = b.Circuit.Outputs() + } return len(b.outputWires) } diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index 408d4e41ef..38a4bbb010 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -21,7 +21,7 @@ import ( ) func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - return circuit.Compile(ecc.{{.FieldID}}.ScalarField()) + return gkrtypes.CompileCircuit(circuit, ecc.{{.FieldID}}.ScalarField()) } func TestNoGateTwoInstances(t *testing.T) { diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl index 0791eed463..10eed5287b 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl @@ -19,7 +19,7 @@ func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit // The properties of test gates are expected to be the same across all relevant fields. // We can therefore use the gate testing functions for any curve rather than reimplementing // for small_rational. - return circuit.Compile(ecc.BN254.ScalarField()) + return gkrtypes.CompileCircuit(circuit, ecc.BN254.ScalarField()) } func GenerateVectors() error { diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 2a9b2de2d0..f4ecfd0b2a 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -48,11 +48,6 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -func (b *BlueprintSolve) setOutputWires() { - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() -} - // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { @@ -69,7 +64,7 @@ func (b *BlueprintSolve) Reset() { return ce } - b.setOutputWires() + b.outputWires = b.Circuit.Outputs() assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) @@ -151,7 +146,9 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.setOutputWires() + if b.outputWires == nil { + b.outputWires = b.Circuit.Outputs() + } return len(b.outputWires) } diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index d09a6911cc..b74cc6323e 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -27,8 +27,8 @@ import ( "github.com/stretchr/testify/assert" ) -func toSerializableCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - return gkrtypes.ToSerializableCircuit(ecc.BLS12_377.ScalarField(), circuit) +func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { + return gkrtypes.CompileCircuit(circuit, ecc.BLS12_377.ScalarField()) } func TestNoGateTwoInstances(t *testing.T) { @@ -81,7 +81,7 @@ func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { NbUniqueOutputs: 2, }, } - circuit := toSerializableCircuit(gCircuit) + circuit := compileCircuit(gCircuit) assignment := WireAssignment{[]fr.Element{two, three}} var o settings @@ -137,7 +137,7 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := toSerializableCircuit(circuit) + sCircuit := compileCircuit(circuit) wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -191,7 +191,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := toSerializableCircuit(gkrtesting.NoGateCircuit()) + c := compileCircuit(gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -272,7 +272,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := toSerializableCircuit(gkrtesting.MiMCCircuit(mimcDepth)) + c := compileCircuit(gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) @@ -361,7 +361,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := toSerializableCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := compileCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 3c37e9a2ab..a931934faf 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -48,11 +48,6 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -func (b *BlueprintSolve) setOutputWires() { - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() -} - // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { @@ -69,7 +64,7 @@ func (b *BlueprintSolve) Reset() { return ce } - b.setOutputWires() + b.outputWires = b.Circuit.Outputs() assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) @@ -151,7 +146,9 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.setOutputWires() + if b.outputWires == nil { + b.outputWires = b.Circuit.Outputs() + } return len(b.outputWires) } diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 41897c634c..147d7d8e51 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -27,8 +27,8 @@ import ( "github.com/stretchr/testify/assert" ) -func toSerializableCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - return gkrtypes.ToSerializableCircuit(ecc.BLS12_381.ScalarField(), circuit) +func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { + return gkrtypes.CompileCircuit(circuit, ecc.BLS12_381.ScalarField()) } func TestNoGateTwoInstances(t *testing.T) { @@ -81,7 +81,7 @@ func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { NbUniqueOutputs: 2, }, } - circuit := toSerializableCircuit(gCircuit) + circuit := compileCircuit(gCircuit) assignment := WireAssignment{[]fr.Element{two, three}} var o settings @@ -137,7 +137,7 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := toSerializableCircuit(circuit) + sCircuit := compileCircuit(circuit) wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -191,7 +191,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := toSerializableCircuit(gkrtesting.NoGateCircuit()) + c := compileCircuit(gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -272,7 +272,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := toSerializableCircuit(gkrtesting.MiMCCircuit(mimcDepth)) + c := compileCircuit(gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) @@ -361,7 +361,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := toSerializableCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := compileCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 35cf6f9ed5..29d583ae85 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -48,11 +48,6 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -func (b *BlueprintSolve) setOutputWires() { - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() -} - // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { @@ -69,7 +64,7 @@ func (b *BlueprintSolve) Reset() { return ce } - b.setOutputWires() + b.outputWires = b.Circuit.Outputs() assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) @@ -151,7 +146,9 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.setOutputWires() + if b.outputWires == nil { + b.outputWires = b.Circuit.Outputs() + } return len(b.outputWires) } diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 832c5625b0..f4366d79f4 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -27,8 +27,8 @@ import ( "github.com/stretchr/testify/assert" ) -func toSerializableCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - return gkrtypes.ToSerializableCircuit(ecc.BN254.ScalarField(), circuit) +func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { + return gkrtypes.CompileCircuit(circuit, ecc.BN254.ScalarField()) } func TestNoGateTwoInstances(t *testing.T) { @@ -81,7 +81,7 @@ func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { NbUniqueOutputs: 2, }, } - circuit := toSerializableCircuit(gCircuit) + circuit := compileCircuit(gCircuit) assignment := WireAssignment{[]fr.Element{two, three}} var o settings @@ -137,7 +137,7 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := toSerializableCircuit(circuit) + sCircuit := compileCircuit(circuit) wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -191,7 +191,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := toSerializableCircuit(gkrtesting.NoGateCircuit()) + c := compileCircuit(gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -272,7 +272,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := toSerializableCircuit(gkrtesting.MiMCCircuit(mimcDepth)) + c := compileCircuit(gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) @@ -361,7 +361,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := toSerializableCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := compileCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index ce3d16c520..c1275ff9c8 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -48,11 +48,6 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) -func (b *BlueprintSolve) setOutputWires() { - b.Circuit.OutputsList() // for side effects - b.outputWires = b.Circuit.Outputs() -} - // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { @@ -69,7 +64,7 @@ func (b *BlueprintSolve) Reset() { return ce } - b.setOutputWires() + b.outputWires = b.Circuit.Outputs() assignments := make(WireAssignment, len(b.Circuit)) nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.NbInstances)) @@ -151,7 +146,9 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - b.setOutputWires() + if b.outputWires == nil { + b.outputWires = b.Circuit.Outputs() + } return len(b.outputWires) } diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index 352001652b..a7aa3622c7 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -27,8 +27,8 @@ import ( "github.com/stretchr/testify/assert" ) -func toSerializableCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - return gkrtypes.ToSerializableCircuit(ecc.BW6_761.ScalarField(), circuit) +func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { + return gkrtypes.CompileCircuit(circuit, ecc.BW6_761.ScalarField()) } func TestNoGateTwoInstances(t *testing.T) { @@ -81,7 +81,7 @@ func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { NbUniqueOutputs: 2, }, } - circuit := toSerializableCircuit(gCircuit) + circuit := compileCircuit(gCircuit) assignment := WireAssignment{[]fr.Element{two, three}} var o settings @@ -137,7 +137,7 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := toSerializableCircuit(circuit) + sCircuit := compileCircuit(circuit) wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -191,7 +191,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := toSerializableCircuit(gkrtesting.NoGateCircuit()) + c := compileCircuit(gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -272,7 +272,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := toSerializableCircuit(gkrtesting.MiMCCircuit(mimcDepth)) + c := compileCircuit(gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) @@ -361,7 +361,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := toSerializableCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := compileCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 790bfef32d..2cfb820f80 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -111,12 +111,8 @@ func (c *Cache) GetGate(name string) gkr.GateFunction { func MiMCCircuit(numRounds int) gkrtypes.GadgetCircuit { c := make(gkrtypes.GadgetCircuit, numRounds+2) - idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} mimc := gkrtypes.GadgetGate{Evaluate: mimcGate} - c[0] = gkrtypes.GadgetWire{Gate: idGate} - c[1] = gkrtypes.GadgetWire{Gate: idGate} - for i := 2; i < len(c); i++ { c[i] = gkrtypes.GadgetWire{Gate: mimc, Inputs: []int{i - 1, 0}} } @@ -153,26 +149,22 @@ func (c *Cache) ReadTestCaseInfo(filePath string) (info TestCaseInfo, err error) func NoGateCircuit() gkrtypes.GadgetCircuit { return gkrtypes.GadgetCircuit{ - { - Gate: gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity}, - }, + {}, } } func SingleAddGateCircuit() gkrtypes.GadgetCircuit { - idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} return gkrtypes.GadgetCircuit{ - {Gate: idGate}, - {Gate: idGate}, + {}, + {}, {Gate: gkrtypes.GadgetGate{Evaluate: gkrtypes.Add2}, Inputs: []int{0, 1}}, } } func SingleMulGateCircuit() gkrtypes.GadgetCircuit { - idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} return gkrtypes.GadgetCircuit{ - {Gate: idGate}, - {Gate: idGate}, + {}, + {}, {Gate: gkrtypes.GadgetGate{Evaluate: gkrtypes.Mul2}, Inputs: []int{0, 1}}, } } @@ -180,7 +172,7 @@ func SingleMulGateCircuit() gkrtypes.GadgetCircuit { func SingleInputTwoIdentityGatesCircuit() gkrtypes.GadgetCircuit { idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} return gkrtypes.GadgetCircuit{ - {Gate: idGate}, + {}, {Gate: idGate, Inputs: []int{0}}, {Gate: idGate, Inputs: []int{0}}, } @@ -189,7 +181,7 @@ func SingleInputTwoIdentityGatesCircuit() gkrtypes.GadgetCircuit { func SingleInputTwoIdentityGatesComposedCircuit() gkrtypes.GadgetCircuit { idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} return gkrtypes.GadgetCircuit{ - {Gate: idGate}, + {}, {Gate: idGate, Inputs: []int{0}}, {Gate: idGate, Inputs: []int{1}}, } @@ -197,12 +189,8 @@ func SingleInputTwoIdentityGatesComposedCircuit() gkrtypes.GadgetCircuit { func APowNTimesBCircuit(n int) gkrtypes.GadgetCircuit { c := make(gkrtypes.GadgetCircuit, n+2) - idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} mulGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Mul2} - c[0] = gkrtypes.GadgetWire{Gate: idGate} - c[1] = gkrtypes.GadgetWire{Gate: idGate} - for i := 2; i < len(c); i++ { c[i] = gkrtypes.GadgetWire{Gate: mulGate, Inputs: []int{i - 1, 0}} } @@ -210,10 +198,9 @@ func APowNTimesBCircuit(n int) gkrtypes.GadgetCircuit { } func SingleMimcCipherGateCircuit() gkrtypes.GadgetCircuit { - idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} return gkrtypes.GadgetCircuit{ - {Gate: idGate}, - {Gate: idGate}, + {}, + {}, {Gate: gkrtypes.GadgetGate{Evaluate: mimcGate}, Inputs: []int{0, 1}}, } } diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 1fb035c7e6..b13dd84ae0 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -305,9 +305,9 @@ type Blueprints struct { GetAssignmentID constraint.BlueprintID } -// Compile converts a gadget circuit to a serializable circuit by compiling the gate functions. +// CompileCircuit converts a gadget circuit to a serializable circuit by compiling the gate functions. // It also sets wire and gate metadata (Degree, SolvableVar, NbUniqueOutputs) for both the input and output circuits. -func (c GadgetCircuit) Compile(mod *big.Int) SerializableCircuit { +func CompileCircuit(c GadgetCircuit, mod *big.Int) SerializableCircuit { for i := range c { c[i].NbUniqueOutputs = 0 diff --git a/internal/gkr/small_rational/test_vector_gen.go b/internal/gkr/small_rational/test_vector_gen.go index b658ab8e20..405e11bf5f 100644 --- a/internal/gkr/small_rational/test_vector_gen.go +++ b/internal/gkr/small_rational/test_vector_gen.go @@ -23,11 +23,11 @@ import ( "github.com/consensys/gnark/internal/utils" ) -func toSerializableCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { +func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { // The properties of test gates are expected to be the same across all relevant fields. - // We can therefore use the gate testing functions for any curve rather that reimplementing + // We can therefore use the gate testing functions for any curve rather than reimplementing // for small_rational. - return gkrtypes.ToSerializableCircuit(ecc.BN254.ScalarField(), circuit) + return gkrtypes.CompileCircuit(circuit, ecc.BN254.ScalarField()) } func GenerateVectors() error { @@ -224,7 +224,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := toSerializableCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit := compileCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index f4a0bbed7e..14a00f0685 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -81,7 +81,7 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C compiler := api.parentApi.Compiler() field := compiler.Field() curveID := utils.FieldToCurve(field) - serializableCircuit := gkrtypes.ToSerializableCircuit(field, api.circuit) + serializableCircuit := gkrtypes.CompileCircuit(api.circuit, field) switch curveID { case ecc.BN254: @@ -102,7 +102,6 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C // Use circuit helper methods for inputs and outputs inputIndices := res.circuit.Inputs() - res.circuit.OutputsList() // for side effects outputIndices := res.circuit.Outputs() if len(inputIndices) == len(res.circuit) { From 079e61e4404b7993529963c16e5ba957957b079d Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 16:10:12 -0600 Subject: [PATCH 186/251] fix circuit compilation bug --- internal/generator/backend/template/gkr/gkr.test.go.tmpl | 3 --- internal/gkr/bls12-377/gkr_test.go | 3 --- internal/gkr/bls12-381/gkr_test.go | 3 --- internal/gkr/bn254/gkr_test.go | 3 --- internal/gkr/bw6-761/gkr_test.go | 3 --- internal/gkr/gkrtypes/types.go | 2 +- std/gkrapi/compile.go | 2 +- 7 files changed, 2 insertions(+), 17 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index 38a4bbb010..5b9a26e7e6 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -68,9 +68,6 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { gCircuit := gkrtypes.GadgetCircuit{ { - Gate: gkrtypes.GadgetGate{ - Evaluate: gkrtypes.Identity, - }, NbUniqueOutputs: 2, }, } diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index b74cc6323e..da16138c8d 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -75,9 +75,6 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { gCircuit := gkrtypes.GadgetCircuit{ { - Gate: gkrtypes.GadgetGate{ - Evaluate: gkrtypes.Identity, - }, NbUniqueOutputs: 2, }, } diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 147d7d8e51..e73988ed8f 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -75,9 +75,6 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { gCircuit := gkrtypes.GadgetCircuit{ { - Gate: gkrtypes.GadgetGate{ - Evaluate: gkrtypes.Identity, - }, NbUniqueOutputs: 2, }, } diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index f4366d79f4..50d4650e9e 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -75,9 +75,6 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { gCircuit := gkrtypes.GadgetCircuit{ { - Gate: gkrtypes.GadgetGate{ - Evaluate: gkrtypes.Identity, - }, NbUniqueOutputs: 2, }, } diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index a7aa3622c7..006dae8c99 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -75,9 +75,6 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { gCircuit := gkrtypes.GadgetCircuit{ { - Gate: gkrtypes.GadgetGate{ - Evaluate: gkrtypes.Identity, - }, NbUniqueOutputs: 2, }, } diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index b13dd84ae0..52971a74b9 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -336,7 +336,7 @@ func CompileCircuit(c GadgetCircuit, mod *big.Int) SerializableCircuit { if !reflect.DeepEqual(c[i].Gate, GadgetGate{}) { panic("empty gate expected for input wire") } - break + continue } c[i].Gate.NbIn = len(c[i].Inputs) diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 14a00f0685..3ee3cd0e54 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -51,7 +51,7 @@ func New(api frontend.API) (*API, error) { // NewInput creates a new input variable. func (api *API) NewInput() gkr.Variable { i := len(api.circuit) - api.circuit = append(api.circuit, gkrtypes.GadgetWire{Gate: gkrtypes.Gate[gkr.GateFunction]{Evaluate: gkrtypes.Identity}}) + api.circuit = append(api.circuit, gkrtypes.GadgetWire{}) api.assignments = append(api.assignments, nil) return gkr.Variable(i) } From 00b1a9d3d846fdfc94213c07cd543468e0b235a2 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 16:37:07 -0600 Subject: [PATCH 187/251] fix: bn254 tests pass --- internal/generator/backend/template/gkr/gkr.test.go.tmpl | 6 ++++-- internal/gkr/bn254/gkr.go | 3 +++ internal/gkr/bn254/gkr_test.go | 1 - internal/gkr/gkrtesting/gkrtesting.go | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index 5b9a26e7e6..2f5a5c856c 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -66,12 +66,14 @@ func TestMimc(t *testing.T) { } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - gCircuit := gkrtypes.GadgetCircuit{ + // Construct SerializableCircuit directly, bypassing CompileCircuit + // which would reset NbUniqueOutputs based on actual topology + circuit := gkrtypes.SerializableCircuit{ { NbUniqueOutputs: 2, + Gate: gkrtypes.SerializableGate{Degree: 1}, }, } - circuit := compileCircuit(gCircuit) assignment := WireAssignment{[]{{ .ElementType }}{two, three}} var o settings diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 086f9988b8..8309f457e4 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -370,6 +370,9 @@ func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (man for i, wire := range wires { + if wire.IsInput() { + wire.Gate.Degree = 1 + } manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ wireI: i, evaluationPoints: make([][]fr.Element, 0, wire.NbClaims()), diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 50d4650e9e..2678224f8a 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -78,7 +78,6 @@ func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { NbUniqueOutputs: 2, }, } - circuit := compileCircuit(gCircuit) assignment := WireAssignment{[]fr.Element{two, three}} var o settings diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 2cfb820f80..fe0f95e7d9 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -35,7 +35,7 @@ func selectInput3Gate(_ gkr.GateAPI, in ...frontend.Variable) frontend.Variable func NewCache() *Cache { gates := make(map[string]gkr.GateFunction, 7) - gates[""] = gkrtypes.Identity + gates[""] = nil gates["identity"] = gkrtypes.Identity gates["add2"] = gkrtypes.Add2 gates["sub2"] = gkrtypes.Sub2 From 108464a60157ab2e9d6b41e245621554c1ee7adf Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 16:37:21 -0600 Subject: [PATCH 188/251] fix strange git behavior --- internal/gkr/bn254/gkr_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 2678224f8a..bf1d0ad53e 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -73,7 +73,9 @@ func TestMimc(t *testing.T) { } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - gCircuit := gkrtypes.GadgetCircuit{ + // Construct SerializableCircuit directly, bypassing CompileCircuit + // which would reset NbUniqueOutputs based on actual topology + circuit := gkrtypes.SerializableCircuit{ { NbUniqueOutputs: 2, }, From c493a920beb3386fe9ba4320d49ba451e6108c0c Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 16:40:21 -0600 Subject: [PATCH 189/251] build: generify --- .../backend/template/gkr/gkr.go.tmpl | 3 +++ internal/gkr/bls12-377/gkr.go | 3 +++ internal/gkr/bls12-377/gkr_test.go | 6 ++++-- internal/gkr/bls12-381/gkr.go | 3 +++ internal/gkr/bls12-381/gkr_test.go | 6 ++++-- internal/gkr/bn254/gkr_test.go | 1 + internal/gkr/bw6-761/gkr.go | 3 +++ internal/gkr/bw6-761/gkr_test.go | 6 ++++-- internal/gkr/small_rational/gkr.go | 3 +++ .../testdata/gkr_circuit_bn254.scs | Bin 69687 -> 69687 bytes 10 files changed, 28 insertions(+), 6 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index f747b83815..7c7f0d7d16 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -364,6 +364,9 @@ func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (man for i, wire := range wires { + if wire.IsInput() { + wire.Gate.Degree = 1 + } manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ wireI: i, evaluationPoints: make([][]{{ .ElementType }}, 0, wire.NbClaims()), diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 78451d0d31..52d71c1217 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -370,6 +370,9 @@ func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (man for i, wire := range wires { + if wire.IsInput() { + wire.Gate.Degree = 1 + } manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ wireI: i, evaluationPoints: make([][]fr.Element, 0, wire.NbClaims()), diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index da16138c8d..73d4577922 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -73,12 +73,14 @@ func TestMimc(t *testing.T) { } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - gCircuit := gkrtypes.GadgetCircuit{ + // Construct SerializableCircuit directly, bypassing CompileCircuit + // which would reset NbUniqueOutputs based on actual topology + circuit := gkrtypes.SerializableCircuit{ { NbUniqueOutputs: 2, + Gate: gkrtypes.SerializableGate{Degree: 1}, }, } - circuit := compileCircuit(gCircuit) assignment := WireAssignment{[]fr.Element{two, three}} var o settings diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index ce73fa2662..92a27216b5 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -370,6 +370,9 @@ func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (man for i, wire := range wires { + if wire.IsInput() { + wire.Gate.Degree = 1 + } manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ wireI: i, evaluationPoints: make([][]fr.Element, 0, wire.NbClaims()), diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index e73988ed8f..4e523cee05 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -73,12 +73,14 @@ func TestMimc(t *testing.T) { } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - gCircuit := gkrtypes.GadgetCircuit{ + // Construct SerializableCircuit directly, bypassing CompileCircuit + // which would reset NbUniqueOutputs based on actual topology + circuit := gkrtypes.SerializableCircuit{ { NbUniqueOutputs: 2, + Gate: gkrtypes.SerializableGate{Degree: 1}, }, } - circuit := compileCircuit(gCircuit) assignment := WireAssignment{[]fr.Element{two, three}} var o settings diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index bf1d0ad53e..c40da67403 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -78,6 +78,7 @@ func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { circuit := gkrtypes.SerializableCircuit{ { NbUniqueOutputs: 2, + Gate: gkrtypes.SerializableGate{Degree: 1}, }, } diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index d80a456da0..c849a8681a 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -370,6 +370,9 @@ func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (man for i, wire := range wires { + if wire.IsInput() { + wire.Gate.Degree = 1 + } manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ wireI: i, evaluationPoints: make([][]fr.Element, 0, wire.NbClaims()), diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index 006dae8c99..c71d42f5a5 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -73,12 +73,14 @@ func TestMimc(t *testing.T) { } func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - gCircuit := gkrtypes.GadgetCircuit{ + // Construct SerializableCircuit directly, bypassing CompileCircuit + // which would reset NbUniqueOutputs based on actual topology + circuit := gkrtypes.SerializableCircuit{ { NbUniqueOutputs: 2, + Gate: gkrtypes.SerializableGate{Degree: 1}, }, } - circuit := compileCircuit(gCircuit) assignment := WireAssignment{[]fr.Element{two, three}} var o settings diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index ceff2cf3c2..0eb4e1b412 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -370,6 +370,9 @@ func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (man for i, wire := range wires { + if wire.IsInput() { + wire.Gate.Degree = 1 + } manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ wireI: i, evaluationPoints: make([][]small_rational.SmallRational, 0, wire.NbClaims()), diff --git a/internal/gkr/test_vectors/testdata/gkr_circuit_bn254.scs b/internal/gkr/test_vectors/testdata/gkr_circuit_bn254.scs index d2a7f3029a9c6b477e0c7880218c387a47ca17d2..3c1f05f5bb59902608c5a033b098829d80e709ec 100644 GIT binary patch delta 24 gcmdnKfMxpvmJNN6IT_MiQqzl4QyDf-f4o2k0EFZUi~s-t delta 20 ccmdnKfMxpvmJNN685t+PeXO;4=i@pZ0BUjyU;qFB From 417e72f34ebe975b260f59b9b5368f9690bee015 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 21:03:11 -0600 Subject: [PATCH 190/251] fix: input gate degree --- internal/gkr/gkr.go | 20 +++++--------------- internal/gkr/gkr_test.go | 3 +-- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index df1fae885d..21c780e4c2 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -158,8 +158,11 @@ func newClaimsManager(wires Wires, assignment WireAssignment) (claims claimsMana claims.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) claims.wires = wires - for i := range wires { - wire := wires[i] + for i, wire := range wires { + if wire.IsInput() { + wire.Gate.Degree = 1 + wire.Gate.Evaluate = gkrtypes.Identity + } claims.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ wireI: i, evaluationPoints: make([][]frontend.Variable, 0, wire.NbClaims()), @@ -231,19 +234,6 @@ func setup(api frontend.API, c Circuit, assignment WireAssignment, transcriptSet return o, err } -// ProofSize computes how large the proof for a circuit would be. It needs NbUniqueOutputs to be set -func ProofSize(c Circuit, logNbInstances int) int { - nbUniqueInputs := 0 - nbPartialEvalPolys := 0 - for i := range c { - nbUniqueInputs += c[i].NbUniqueOutputs // each unique output is manifest in a finalEvalProof entry - if !c[i].NoProof() { - nbPartialEvalPolys += c[i].Gate.Degree + 1 - } - } - return nbUniqueInputs + nbPartialEvalPolys*logNbInstances -} - func ChallengeNames(sorted Wires, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending diff --git a/internal/gkr/gkr_test.go b/internal/gkr/gkr_test.go index 566c43ce46..1b27d3cb7f 100644 --- a/internal/gkr/gkr_test.go +++ b/internal/gkr/gkr_test.go @@ -11,7 +11,6 @@ import ( "github.com/consensys/gnark/backend" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtesting" - "github.com/consensys/gnark/internal/gkr/gkrtypes" fiatshamir "github.com/consensys/gnark/std/fiat-shamir" "github.com/consensys/gnark/std/hash" "github.com/consensys/gnark/std/polynomial" @@ -191,7 +190,7 @@ func getTestCase(path string) (*TestCase, error) { return nil, err } - cse.Circuit = gkrtypes.ToGadget(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + cse.Circuit = cache.GetCircuit(filepath.Join(dir, info.Circuit)) cse.Proof = unmarshalProof(info.Proof) From 181c537afc32a359cc713eeaa4b5f291b3eb1e13 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 22:22:30 -0600 Subject: [PATCH 191/251] feat: cache to return both circuits --- internal/gkr/gkr.go | 26 ++++++++++++---------- internal/gkr/gkr_test.go | 18 +++++++++++----- internal/gkr/gkrtesting/gkrtesting.go | 31 +++++++++++++++++++++------ internal/gkr/gkrtypes/types.go | 15 ++++++++++--- 4 files changed, 64 insertions(+), 26 deletions(-) diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index 21c780e4c2..3c19812712 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -377,16 +377,20 @@ func (p Proof) Serialize() []frontend.Variable { // ComputeLogNbInstances derives n such that the number of instances is 2ⁿ // from the size of the proof and the circuit structure. +// The number actually computed is that of rounds in each ZeroCheck instance, which is equal +// to the desired result. // It is used in proof deserialization. -func ComputeLogNbInstances(wires Wires, serializedProofLen int) int { +func ComputeLogNbInstances(circuit Circuit, serializedProofLen int) int { partialEvalElemsPerVar := 0 - for _, w := range wires { - if !w.NoProof() { - partialEvalElemsPerVar += w.Gate.Degree + 1 - } + for _, w := range circuit { + partialEvalElemsPerVar += w.ProofPolyLength() serializedProofLen -= w.NbUniqueOutputs } - return serializedProofLen / partialEvalElemsPerVar + res := serializedProofLen / partialEvalElemsPerVar + if res*partialEvalElemsPerVar != serializedProofLen { + panic("cannot compute logNbInstances") + } + return res } type variablesReader []frontend.Variable @@ -401,16 +405,16 @@ func (r *variablesReader) hasNextN(n int) bool { return len(*r) >= n } -func DeserializeProof(sorted Wires, serializedProof []frontend.Variable) (Proof, error) { - proof := make(Proof, len(sorted)) - logNbInstances := ComputeLogNbInstances(sorted, len(serializedProof)) +func DeserializeProof(circuit Circuit, serializedProof []frontend.Variable) (Proof, error) { + proof := make(Proof, len(circuit)) + logNbInstances := ComputeLogNbInstances(circuit, len(serializedProof)) reader := variablesReader(serializedProof) - for i, wI := range sorted { + for i, wI := range circuit { if !wI.NoProof() { proof[i].PartialSumPolys = make([]polynomial.Polynomial, logNbInstances) for j := range proof[i].PartialSumPolys { - proof[i].PartialSumPolys[j] = reader.nextN(wI.Gate.Degree + 1) + proof[i].PartialSumPolys[j] = reader.nextN(wI.ProofPolyLength()) } } proof[i].FinalEvalProof = reader.nextN(wI.NbUniqueInputs()) diff --git a/internal/gkr/gkr_test.go b/internal/gkr/gkr_test.go index 1b27d3cb7f..3e3bbdf908 100644 --- a/internal/gkr/gkr_test.go +++ b/internal/gkr/gkr_test.go @@ -8,9 +8,11 @@ import ( "reflect" "testing" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/backend" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtesting" + "github.com/consensys/gnark/internal/gkr/gkrtypes" fiatshamir "github.com/consensys/gnark/std/fiat-shamir" "github.com/consensys/gnark/std/hash" "github.com/consensys/gnark/std/polynomial" @@ -109,13 +111,13 @@ func (c *GkrVerifierCircuit) Define(api frontend.API) error { var testCase *TestCase var proof Proof var err error - //var proofRef Proof if testCase, err = getTestCase(c.TestCaseName); err != nil { return err } - sorted := testCase.Circuit.TopologicalSort() - if proof, err = DeserializeProof(sorted, c.SerializedProof); err != nil { + compile(testCase.Circuit) + + if proof, err = DeserializeProof(testCase.Circuit, c.SerializedProof); err != nil { return err } assignment := makeInOutAssignment(testCase.Circuit, c.Input, c.Output) @@ -242,9 +244,9 @@ func TestLogNbInstances(t *testing.T) { return func(t *testing.T) { testCase, err := getTestCase(path) assert.NoError(t, err) - wires := testCase.Circuit.TopologicalSort() + compile(testCase.Circuit) serializedProof := testCase.Proof.Serialize() - logNbInstances := ComputeLogNbInstances(wires, len(serializedProof)) + logNbInstances := ComputeLogNbInstances(testCase.Circuit, len(serializedProof)) assert.Equal(t, 1, logNbInstances) } } @@ -332,3 +334,9 @@ var cache *gkrtesting.Cache func init() { cache = gkrtesting.NewCache() } + +func compile(circuit Circuit) { + // The gate testing process needs a field. + // The simple gates used in these tests have the same properties across all curve scalar fields. + gkrtypes.CompileCircuit(circuit, ecc.BN254.ScalarField()) +} diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index fe0f95e7d9..2565fd99e7 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -3,8 +3,10 @@ package gkrtesting import ( "encoding/json" "errors" + "math/big" "os" "path/filepath" + "sync" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" @@ -15,8 +17,15 @@ import ( // The main functionality is to cache whole circuits, but this package needs to use its own gate registry, in order to avoid import cycles. // Cache is used in tests for the per-curve GKR packages, but they in turn provide gate degree discovery functions to the gkrgates package. type Cache struct { - circuits map[string]gkrtypes.GadgetCircuit + field *big.Int + circuits map[string]circuits gates map[string]gkr.GateFunction + lock sync.Mutex +} + +type circuits struct { + proverCircuit gkrtypes.SerializableCircuit + verifierCircuit gkrtypes.GadgetCircuit } func mimcGate(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { @@ -33,7 +42,7 @@ func selectInput3Gate(_ gkr.GateAPI, in ...frontend.Variable) frontend.Variable return in[2] } -func NewCache() *Cache { +func NewCache(field *big.Int) *Cache { gates := make(map[string]gkr.GateFunction, 7) gates[""] = nil gates["identity"] = gkrtypes.Identity @@ -45,7 +54,8 @@ func NewCache() *Cache { gates["select-input-3"] = selectInput3Gate return &Cache{ - circuits: make(map[string]gkrtypes.GadgetCircuit), + field: field, + circuits: make(map[string]circuits), gates: gates, } } @@ -59,13 +69,16 @@ type JSONWire struct { // JSONCircuit is the JSON serialization format for circuits type JSONCircuit []JSONWire -func (c *Cache) GetCircuit(path string) gkrtypes.GadgetCircuit { +func (c *Cache) GetCircuit(path string) (gkrtypes.SerializableCircuit, gkrtypes.GadgetCircuit) { + c.lock.Lock() + defer c.lock.Unlock() + path, err := filepath.Abs(path) if err != nil { panic(err) } if circuit, ok := c.circuits[path]; ok { - return circuit + return circuit.proverCircuit, circuit.verifierCircuit } var bytes []byte @@ -89,10 +102,14 @@ func (c *Cache) GetCircuit(path string) gkrtypes.GadgetCircuit { Inputs: wJSON.Inputs, } } + pCircuit := gkrtypes.CompileCircuit(circuit, c.field) - c.circuits[path] = circuit + c.circuits[path] = circuits{ + proverCircuit: pCircuit, + verifierCircuit: circuit, + } - return circuit + return pCircuit, circuit } func (c *Cache) RegisterGate(name string, gate gkr.GateFunction) { diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 52971a74b9..736cc39f8a 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -134,6 +134,17 @@ func (c Circuit[GateExecutable]) MemoryRequirements(nbInstances int) []int { return res } +// ProofPolyLength returns the size (degree+1) of each polynomial in the ZeroCheck subproof associated with w. +func (w Wire[GateExecutable]) ProofPolyLength() int { + if w.NoProof() { + return 0 + } + if w.IsInput() { + return 2 // Input gate with multiple claims treated as a degree 1 gate. + } + return w.Gate.Degree + 1 +} + // OutputsList for each wire, returns the set of indexes of wires it is input to. // It also sets the NbUniqueOutputs fields. func (c Circuit[GateExecutable]) OutputsList() [][]int { @@ -196,9 +207,7 @@ func (c Circuit[GateExecutable]) ProofSize(logNbInstances int) int { nbPartialEvalPolys := 0 for i := range c { nbUniqueInputs += c[i].NbUniqueOutputs // each unique output is manifest in a finalEvalProof entry - if !c[i].NoProof() { - nbPartialEvalPolys += c[i].Gate.Degree + 1 - } + nbPartialEvalPolys += c[i].ProofPolyLength() } return nbUniqueInputs + nbPartialEvalPolys*logNbInstances } From e8d15e6f55b277048a165bf44709316e8e4ec9d3 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 22:40:26 -0600 Subject: [PATCH 192/251] all tests pass --- .../backend/template/gkr/gkr.test.go.tmpl | 10 ++++------ .../template/gkr/gkr.test.vectors.gen.go.tmpl | 10 ++++------ .../template/gkr/gkr.test.vectors.go.tmpl | 8 +++----- internal/gkr/bls12-377/gkr_test.go | 17 ++++++----------- internal/gkr/bls12-381/gkr_test.go | 17 ++++++----------- internal/gkr/bn254/gkr_test.go | 17 ++++++----------- internal/gkr/bw6-761/gkr_test.go | 17 ++++++----------- internal/gkr/gkr_test.go | 18 ++---------------- internal/gkr/gkrtesting/gkrtesting.go | 5 +++++ internal/gkr/gkrtesting/gkrtesting_test.go | 5 +++-- internal/gkr/small_rational/test_vector_gen.go | 17 ++++++----------- std/gkrapi/compile.go | 6 ++---- 12 files changed, 53 insertions(+), 94 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index 2f5a5c856c..bf57c046bb 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -20,9 +20,7 @@ import ( "time" ) -func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - return gkrtypes.CompileCircuit(circuit, ecc.{{.FieldID}}.ScalarField()) -} +var cache = gkrtesting.NewCache(ecc.{{.FieldID}}.ScalarField()) func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case @@ -129,7 +127,7 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := compileCircuit(circuit) + sCircuit := cache.Compile(circuit) wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -183,7 +181,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]{{ .ElementType }}) { - c := compileCircuit(gkrtesting.NoGateCircuit()) + c := cache.Compile(gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -264,7 +262,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := compileCircuit(gkrtesting.MiMCCircuit(mimcDepth)) + c := cache.Compile(gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]{{ .ElementType }}, nbInstances) in1 := make([]{{ .ElementType }}, nbInstances) diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl index 10eed5287b..2f07bca1b5 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl @@ -15,12 +15,10 @@ import ( "reflect" ) -func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - // The properties of test gates are expected to be the same across all relevant fields. - // We can therefore use the gate testing functions for any curve rather than reimplementing - // for small_rational. - return gkrtypes.CompileCircuit(circuit, ecc.BN254.ScalarField()) -} +// The properties of test gates are expected to be the same across all relevant fields. +// We can therefore use the gate testing functions for any curve rather than reimplementing + // for small_rational. +var cache = gkrtesting.NewCache(ecc.BN254.ScalarField()) func GenerateVectors() error { testDirPath, err := filepath.Abs("../../gkr/test_vectors") diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl index 0dd60f3b82..5e54d3383f 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl @@ -40,10 +40,8 @@ type TestCase struct { {{if .GenerateTestVectors}}Info gkrtesting.TestCaseInfo // we are generating the test vectors, so we need to keep the circuit instance info to ADD the proof to it and resave it{{end}} } -var ( - testCases = make(map[string]*TestCase) - cache = gkrtesting.NewCache() -) +var testCases = make(map[string]*TestCase) + func newTestCase(path string) (*TestCase, error) { path, err := filepath.Abs(path) @@ -62,7 +60,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := compileCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit, _ := cache.GetCircuit(filepath.Join(dir, info.Circuit)) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index 73d4577922..09362877a4 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -27,9 +27,7 @@ import ( "github.com/stretchr/testify/assert" ) -func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - return gkrtypes.CompileCircuit(circuit, ecc.BLS12_377.ScalarField()) -} +var cache = gkrtesting.NewCache(ecc.BLS12_377.ScalarField()) func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case @@ -136,7 +134,7 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := compileCircuit(circuit) + sCircuit := cache.Compile(circuit) wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -190,7 +188,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := compileCircuit(gkrtesting.NoGateCircuit()) + c := cache.Compile(gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -271,7 +269,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := compileCircuit(gkrtesting.MiMCCircuit(mimcDepth)) + c := cache.Compile(gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) @@ -338,10 +336,7 @@ type TestCase struct { InOutAssignment WireAssignment } -var ( - testCases = make(map[string]*TestCase) - cache = gkrtesting.NewCache() -) +var testCases = make(map[string]*TestCase) func newTestCase(path string) (*TestCase, error) { path, err := filepath.Abs(path) @@ -360,7 +355,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := compileCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit, _ := cache.GetCircuit(filepath.Join(dir, info.Circuit)) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 4e523cee05..0cce17250d 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -27,9 +27,7 @@ import ( "github.com/stretchr/testify/assert" ) -func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - return gkrtypes.CompileCircuit(circuit, ecc.BLS12_381.ScalarField()) -} +var cache = gkrtesting.NewCache(ecc.BLS12_381.ScalarField()) func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case @@ -136,7 +134,7 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := compileCircuit(circuit) + sCircuit := cache.Compile(circuit) wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -190,7 +188,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := compileCircuit(gkrtesting.NoGateCircuit()) + c := cache.Compile(gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -271,7 +269,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := compileCircuit(gkrtesting.MiMCCircuit(mimcDepth)) + c := cache.Compile(gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) @@ -338,10 +336,7 @@ type TestCase struct { InOutAssignment WireAssignment } -var ( - testCases = make(map[string]*TestCase) - cache = gkrtesting.NewCache() -) +var testCases = make(map[string]*TestCase) func newTestCase(path string) (*TestCase, error) { path, err := filepath.Abs(path) @@ -360,7 +355,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := compileCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit, _ := cache.GetCircuit(filepath.Join(dir, info.Circuit)) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index c40da67403..404d8eded2 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -27,9 +27,7 @@ import ( "github.com/stretchr/testify/assert" ) -func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - return gkrtypes.CompileCircuit(circuit, ecc.BN254.ScalarField()) -} +var cache = gkrtesting.NewCache(ecc.BN254.ScalarField()) func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case @@ -136,7 +134,7 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := compileCircuit(circuit) + sCircuit := cache.Compile(circuit) wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -190,7 +188,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := compileCircuit(gkrtesting.NoGateCircuit()) + c := cache.Compile(gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -271,7 +269,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := compileCircuit(gkrtesting.MiMCCircuit(mimcDepth)) + c := cache.Compile(gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) @@ -338,10 +336,7 @@ type TestCase struct { InOutAssignment WireAssignment } -var ( - testCases = make(map[string]*TestCase) - cache = gkrtesting.NewCache() -) +var testCases = make(map[string]*TestCase) func newTestCase(path string) (*TestCase, error) { path, err := filepath.Abs(path) @@ -360,7 +355,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := compileCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit, _ := cache.GetCircuit(filepath.Join(dir, info.Circuit)) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index c71d42f5a5..8920732a23 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -27,9 +27,7 @@ import ( "github.com/stretchr/testify/assert" ) -func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - return gkrtypes.CompileCircuit(circuit, ecc.BW6_761.ScalarField()) -} +var cache = gkrtesting.NewCache(ecc.BW6_761.ScalarField()) func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case @@ -136,7 +134,7 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := compileCircuit(circuit) + sCircuit := cache.Compile(circuit) wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -190,7 +188,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := compileCircuit(gkrtesting.NoGateCircuit()) + c := cache.Compile(gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -271,7 +269,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := compileCircuit(gkrtesting.MiMCCircuit(mimcDepth)) + c := cache.Compile(gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) @@ -338,10 +336,7 @@ type TestCase struct { InOutAssignment WireAssignment } -var ( - testCases = make(map[string]*TestCase) - cache = gkrtesting.NewCache() -) +var testCases = make(map[string]*TestCase) func newTestCase(path string) (*TestCase, error) { path, err := filepath.Abs(path) @@ -360,7 +355,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := compileCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit, _ := cache.GetCircuit(filepath.Join(dir, info.Circuit)) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/internal/gkr/gkr_test.go b/internal/gkr/gkr_test.go index 3e3bbdf908..341b0eccf6 100644 --- a/internal/gkr/gkr_test.go +++ b/internal/gkr/gkr_test.go @@ -12,7 +12,6 @@ import ( "github.com/consensys/gnark/backend" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtesting" - "github.com/consensys/gnark/internal/gkr/gkrtypes" fiatshamir "github.com/consensys/gnark/std/fiat-shamir" "github.com/consensys/gnark/std/hash" "github.com/consensys/gnark/std/polynomial" @@ -115,8 +114,6 @@ func (c *GkrVerifierCircuit) Define(api frontend.API) error { return err } - compile(testCase.Circuit) - if proof, err = DeserializeProof(testCase.Circuit, c.SerializedProof); err != nil { return err } @@ -192,7 +189,7 @@ func getTestCase(path string) (*TestCase, error) { return nil, err } - cse.Circuit = cache.GetCircuit(filepath.Join(dir, info.Circuit)) + _, cse.Circuit = cache.GetCircuit(filepath.Join(dir, info.Circuit)) cse.Proof = unmarshalProof(info.Proof) @@ -244,7 +241,6 @@ func TestLogNbInstances(t *testing.T) { return func(t *testing.T) { testCase, err := getTestCase(path) assert.NoError(t, err) - compile(testCase.Circuit) serializedProof := testCase.Proof.Serialize() logNbInstances := ComputeLogNbInstances(testCase.Circuit, len(serializedProof)) assert.Equal(t, 1, logNbInstances) @@ -329,14 +325,4 @@ func TestConstHash(t *testing.T) { ) } -var cache *gkrtesting.Cache - -func init() { - cache = gkrtesting.NewCache() -} - -func compile(circuit Circuit) { - // The gate testing process needs a field. - // The simple gates used in these tests have the same properties across all curve scalar fields. - gkrtypes.CompileCircuit(circuit, ecc.BN254.ScalarField()) -} +var cache = gkrtesting.NewCache(ecc.BN254.ScalarField()) diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 2565fd99e7..0012e80158 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -69,6 +69,11 @@ type JSONWire struct { // JSONCircuit is the JSON serialization format for circuits type JSONCircuit []JSONWire +// Compile compiles a programmatic GadgetCircuit into a SerializableCircuit. +func (c *Cache) Compile(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { + return gkrtypes.CompileCircuit(circuit, c.field) +} + func (c *Cache) GetCircuit(path string) (gkrtypes.SerializableCircuit, gkrtypes.GadgetCircuit) { c.lock.Lock() defer c.lock.Unlock() diff --git a/internal/gkr/gkrtesting/gkrtesting_test.go b/internal/gkr/gkrtesting/gkrtesting_test.go index 8081343cb1..56a976367a 100644 --- a/internal/gkr/gkrtesting/gkrtesting_test.go +++ b/internal/gkr/gkrtesting/gkrtesting_test.go @@ -3,12 +3,13 @@ package gkrtesting import ( "testing" + "github.com/consensys/gnark-crypto/ecc" "github.com/stretchr/testify/assert" ) func TestLoadCircuit(t *testing.T) { - cache := NewCache() - c := cache.GetCircuit("../test_vectors/circuits/two_identity_gates_composed_single_input.json") + cache := NewCache(ecc.BN254.ScalarField()) + _, c := cache.GetCircuit("../test_vectors/circuits/two_identity_gates_composed_single_input.json") assert.Equal(t, 0, len(c[0].Inputs)) assert.Equal(t, []int{0}, c[1].Inputs) assert.Equal(t, []int{1}, c[2].Inputs) diff --git a/internal/gkr/small_rational/test_vector_gen.go b/internal/gkr/small_rational/test_vector_gen.go index 405e11bf5f..3efae37acd 100644 --- a/internal/gkr/small_rational/test_vector_gen.go +++ b/internal/gkr/small_rational/test_vector_gen.go @@ -23,12 +23,10 @@ import ( "github.com/consensys/gnark/internal/utils" ) -func compileCircuit(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - // The properties of test gates are expected to be the same across all relevant fields. - // We can therefore use the gate testing functions for any curve rather than reimplementing - // for small_rational. - return gkrtypes.CompileCircuit(circuit, ecc.BN254.ScalarField()) -} +// The properties of test gates are expected to be the same across all relevant fields. +// We can therefore use the gate testing functions for any curve rather than reimplementing +// for small_rational. +var cache = gkrtesting.NewCache(ecc.BN254.ScalarField()) func GenerateVectors() error { testDirPath, err := filepath.Abs("../../gkr/test_vectors") @@ -202,10 +200,7 @@ type TestCase struct { Info gkrtesting.TestCaseInfo // we are generating the test vectors, so we need to keep the circuit instance info to ADD the proof to it and resave it } -var ( - testCases = make(map[string]*TestCase) - cache = gkrtesting.NewCache() -) +var testCases = make(map[string]*TestCase) func newTestCase(path string) (*TestCase, error) { path, err := filepath.Abs(path) @@ -224,7 +219,7 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } - circuit := compileCircuit(cache.GetCircuit(filepath.Join(dir, info.Circuit))) + circuit, _ := cache.GetCircuit(filepath.Join(dir, info.Circuit)) var _hash hash.Hash if _hash, err = hashFromDescription(info.Hash); err != nil { return nil, err diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 3ee3cd0e54..6635eb61d7 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -269,9 +269,7 @@ func (c *Circuit) verify(api frontend.API, circuit gkrtypes.GadgetCircuit, initi err error ) - forSnarkSorted := utils.SliceOfRefs(circuit) - - if proof, err = gadget.DeserializeProof(forSnarkSorted, proofSerialized); err != nil { + if proof, err = gadget.DeserializeProof(circuit, proofSerialized); err != nil { return err } @@ -280,7 +278,7 @@ func (c *Circuit) verify(api frontend.API, circuit gkrtypes.GadgetCircuit, initi return err } - return gadget.Verify(api, circuit, c.assignments, proof, fiatshamir.WithHash(hsh, initialChallenges...), gadget.WithSortedCircuit(forSnarkSorted)) + return gadget.Verify(api, circuit, c.assignments, proof, fiatshamir.WithHash(hsh, initialChallenges...)) } // GetValue is a debugging utility returning the value of variable v at instance i. From e2a0aa358e8e9d9ed2f91341d4ed286a8b1cc972 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 22:58:34 -0600 Subject: [PATCH 193/251] remove topological sort --- .../backend/template/gkr/gkr.go.tmpl | 91 ++++++++----------- .../backend/template/gkr/gkr.test.go.tmpl | 8 +- .../template/gkr/gkr.test.vectors.gen.go.tmpl | 1 - .../template/gkr/gkr.test.vectors.go.tmpl | 14 ++- internal/gkr/bls12-377/gkr.go | 91 ++++++++----------- internal/gkr/bls12-377/gkr_test.go | 22 ++--- internal/gkr/bls12-381/gkr.go | 91 ++++++++----------- internal/gkr/bls12-381/gkr_test.go | 22 ++--- internal/gkr/bn254/gkr.go | 91 ++++++++----------- internal/gkr/bn254/gkr_test.go | 22 ++--- internal/gkr/bw6-761/gkr.go | 91 ++++++++----------- internal/gkr/bw6-761/gkr_test.go | 22 ++--- internal/gkr/gkr.go | 62 +++++-------- internal/gkr/gkr_test.go | 11 +-- .../gkr/gkrtypes/topological_sort_test.go | 55 ----------- internal/gkr/gkrtypes/types.go | 86 +++--------------- internal/gkr/small_rational/gkr.go | 91 ++++++++----------- .../gkr/small_rational/test_vector_gen.go | 15 ++- 18 files changed, 328 insertions(+), 558 deletions(-) delete mode 100644 internal/gkr/gkrtypes/topological_sort_test.go diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 7c7f0d7d16..b585e454fc 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -14,7 +14,6 @@ import ( // Type aliases for bytecode-based GKR types type ( Wire = gkrtypes.SerializableWire - Wires = gkrtypes.SerializableWires Circuit = gkrtypes.SerializableCircuit ) @@ -36,8 +35,8 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { - return e.manager.wires[e.wireI] +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() Wire { + return e.manager.circuit[e.wireI] } func (e *eqTimesGateEvalSumcheckLazyClaims) claimsNum() int { @@ -55,7 +54,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a {{ .ElementType }}) {{ } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.manager.wires[e.wireI].Gate.Degree + return 1 + e.manager.circuit[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. @@ -80,7 +79,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []{{ .ElementType evaluation.Add(&evaluation, &eq) } - wire := e.manager.wires[e.wireI] + wire := e.manager.circuit[e.wireI] // the w(...) term var gateEvaluation {{ .ElementType }} @@ -88,7 +87,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []{{ .ElementType gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire injection, injectionLeftInv := - e.manager.wires.ClaimPropagationInfo(e.wireI) + e.manager.circuit.ClaimPropagationInfo(e.wireI) if len(injection) != len(uniqueInputEvaluations) { return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) @@ -130,8 +129,8 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { - return c.manager.wires[c.wireI] +func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { + return c.manager.circuit[c.wireI] } // combine the multiple claims into one claim using a random combination (combinationCoeff or c). @@ -332,7 +331,7 @@ func (c *eqTimesGateEvalSumcheckClaims) claimsNum() int { func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []{{ .ElementType }}) []{{ .ElementType }} { //defer the proof, return list of claims - injection, _ := c.manager.wires.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. + injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. evaluations := make([]{{ .ElementType }}, len(injection)) for i, gateInputI := range injection { wI := c.input[gateInputI] @@ -352,25 +351,24 @@ type claimsManager struct { assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool - wires Wires + circuit Circuit } -func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment - manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) + manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(circuit)) manager.memPool = o.pool manager.workers = o.workers - manager.wires = wires + manager.circuit = circuit - for i, wire := range wires { - - if wire.IsInput() { - wire.Gate.Degree = 1 + for i := range circuit { + if circuit[i].IsInput() { + circuit[i].Gate.Degree = 1 } manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ wireI: i, - evaluationPoints: make([][]{{ .ElementType }}, 0, wire.NbClaims()), - claimedEvaluations: manager.memPool.Make(wire.NbClaims()), + evaluationPoints: make([][]{{ .ElementType }}, 0, circuit[i].NbClaims()), + claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), manager: &manager, } } @@ -390,7 +388,7 @@ func (m *claimsManager) getLazyClaim(wire int) *eqTimesGateEvalSumcheckLazyClaim func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { lazy := m.claims[wireI] - wire := m.wires[wireI] + wire := m.circuit[wireI] res := &eqTimesGateEvalSumcheckClaims{ wireI: wireI, evaluationPoints: lazy.evaluationPoints, @@ -420,7 +418,6 @@ func (m *claimsManager) deleteClaim(wire int) { type settings struct { pool *polynomial.Pool - sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -435,12 +432,6 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithSortedCircuit(sorted []*Wire) Option { - return func(options *settings) { - options.sorted = sorted - } -} - func WithWorkers(workers *utils.WorkerPool) Option { return func(options *settings) { options.workers = workers @@ -470,12 +461,8 @@ func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S o.workers = utils.NewWorkerPool() } - if o.sorted == nil { - o.sorted = c.TopologicalSort() - } - if transcriptSettings.Transcript == nil { - challengeNames := ChallengeNames(o.sorted, o.nbVars, transcriptSettings.Prefix) + challengeNames := ChallengeNames(c, o.nbVars, transcriptSettings.Prefix) o.transcript = fiatshamir.NewTranscript(transcriptSettings.Hash, challengeNames...) for i := range transcriptSettings.BaseChallenges { if err = o.transcript.Bind(challengeNames[0], transcriptSettings.BaseChallenges[i]); err != nil { @@ -489,22 +476,22 @@ func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S return o, err } -func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge - for _, w := range sorted { - if w.NoProof() { // no proof, no challenge + for i := range c { + if c[i].NoProof() { // no proof, no challenge continue } - if w.NbClaims() > 1 { //combine the claims + if c[i].NbClaims() > 1 { //combine the claims size++ } size += logNbInstances // full run of sumcheck on logNbInstances variables } - nums := make([]string, max(len(sorted), logNbInstances)) + nums := make([]string, max(len(c), logNbInstances)) for i := range nums { nums[i] = strconv.Itoa(i) } @@ -517,13 +504,13 @@ func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string challenges[j] = firstChallengePrefix + nums[j] } j := logNbInstances - for i := len(sorted) - 1; i >= 0; i-- { - if sorted[i].NoProof() { + for i := len(c) - 1; i >= 0; i-- { + if c[i].NoProof() { continue } wirePrefix := prefix + "w" + nums[i] + "." - if sorted[i].NbClaims() > 1 { + if c[i].NbClaims() > 1 { challenges[j] = wirePrefix + "comb" j++ } @@ -566,7 +553,7 @@ func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S } defer o.workers.Stop() - claims := newClaimsManager(o.sorted, assignment, o) + claims := newClaimsManager(c, assignment, o) proof := make(Proof, len(c)) // firstChallenge called rho in the paper @@ -580,7 +567,7 @@ func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S var baseChallenge [][]byte for i := len(c) - 1; i >= 0; i-- { - wire := o.sorted[i] + wire := c[i] if wire.IsOutput() { claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) @@ -620,7 +607,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } defer o.workers.Stop() - claims := newClaimsManager(o.sorted, assignment, o) + claims := newClaimsManager(c, assignment, o) var firstChallenge []{{ .ElementType }} firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) @@ -631,7 +618,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting wirePrefix := o.transcriptPrefix + "w" var baseChallenge [][]byte for i := len(c) - 1; i >= 0; i-- { - wire := o.sorted[i] + wire := c[i] if wire.IsOutput() { claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) @@ -671,24 +658,24 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } // Complete the circuit evaluation from input values -func (a WireAssignment) Complete(wires Wires) WireAssignment { +func (a WireAssignment) Complete(circuit Circuit) WireAssignment { nbInstances := a.NumInstances() - evaluators := make([]gateEvaluator, len(wires)) + evaluators := make([]gateEvaluator, len(circuit)) - for i := range wires { + for i := range circuit { if len(a[i]) != nbInstances { a[i] = make([]{{ .ElementType }}, nbInstances) } - if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Evaluate, len(wires[i].Inputs)) + if !circuit[i].IsInput() { + evaluators[i] = newGateEvaluator(circuit[i].Gate.Evaluate, len(circuit[i].Inputs)) } } for i := range nbInstances { - for wI, w := range wires { - if !w.IsInput() { - for _, in := range w.Inputs { + for wI := range circuit { + if !circuit[wI].IsInput() { + for _, in := range circuit[wI].Inputs { evaluators[wI].pushInput(&a[in][i]) } a[wI][i].Set(evaluators[wI].evaluate()) diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index bf57c046bb..5a34218ee4 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -8,7 +8,6 @@ import ( gcUtils "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/internal/utils" "github.com/stretchr/testify/assert" "fmt" "hash" @@ -81,7 +80,7 @@ func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { o.workers = workers claimsManagerGen := func() *claimsManager { - manager := newClaimsManager(utils.References(circuit), assignment, o) + manager := newClaimsManager(circuit, assignment, o) manager.add(0, []{{ .ElementType }}{three}, five) manager.add(0, []{{ .ElementType }}{four}, six) return &manager @@ -128,7 +127,6 @@ func getLogMaxInstances(t *testing.T) int { func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { sCircuit := cache.Compile(circuit) - wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -144,7 +142,7 @@ func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { fullAssignment[ins[i]] = insAssignment[i][:numEvals] } - fullAssignment.Complete(wireRefs) + fullAssignment.Complete(sCircuit) t.Log("Selected inputs for test") @@ -271,7 +269,7 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("evaluating circuit") start := time.Now().UnixMicro() - assignment := WireAssignment{in0, in1}.Complete(utils.References(c)) + assignment := WireAssignment{in0, in1}.Complete(c) solved := time.Now().UnixMicro() - start fmt.Println("solved in", solved, "μs") diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl index 2f07bca1b5..49f2d3eb7c 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl @@ -8,7 +8,6 @@ import ( "github.com/consensys/gnark/internal/small_rational/polynomial" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/internal/utils" "hash" "os" "path/filepath" diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl index 5e54d3383f..e0778641bd 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl @@ -73,18 +73,16 @@ func newTestCase(path string) (*TestCase, error) { fullAssignment := make(WireAssignment, len(circuit)) inOutAssignment := make(WireAssignment, len(circuit)) - sorted := circuit.TopologicalSort() - inI, outI := 0, 0 - for i, w := range sorted { + for i := range circuit { var assignmentRaw []interface{} - if w.IsInput() { + if circuit[i].IsInput() { if inI == len(info.Input) { return nil, fmt.Errorf("fewer input in vector than in circuit") } assignmentRaw = info.Input[inI] inI++ - } else if w.IsOutput() { + } else if circuit[i].IsOutput() { if outI == len(info.Output) { return nil, fmt.Errorf("fewer output in vector than in circuit") } @@ -102,10 +100,10 @@ func newTestCase(path string) (*TestCase, error) { } } - fullAssignment.Complete(utils.References(circuit)) + fullAssignment.Complete(circuit) - for i, w := range sorted { - if w.IsOutput() { + for i := range circuit { + if circuit[i].IsOutput() { if err = sliceEquals(inOutAssignment[i], fullAssignment[i]); err != nil { return nil, fmt.Errorf("assignment mismatch: %v", err) } diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 52d71c1217..226f9dcaaa 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -22,7 +22,6 @@ import ( // Type aliases for bytecode-based GKR types type ( Wire = gkrtypes.SerializableWire - Wires = gkrtypes.SerializableWires Circuit = gkrtypes.SerializableCircuit ) @@ -43,8 +42,8 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { - return e.manager.wires[e.wireI] +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() Wire { + return e.manager.circuit[e.wireI] } func (e *eqTimesGateEvalSumcheckLazyClaims) claimsNum() int { @@ -62,7 +61,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a fr.Element) fr.Element } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.manager.wires[e.wireI].Gate.Degree + return 1 + e.manager.circuit[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. @@ -87,7 +86,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb evaluation.Add(&evaluation, &eq) } - wire := e.manager.wires[e.wireI] + wire := e.manager.circuit[e.wireI] // the w(...) term var gateEvaluation fr.Element @@ -95,7 +94,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire injection, injectionLeftInv := - e.manager.wires.ClaimPropagationInfo(e.wireI) + e.manager.circuit.ClaimPropagationInfo(e.wireI) if len(injection) != len(uniqueInputEvaluations) { return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) @@ -137,8 +136,8 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { - return c.manager.wires[c.wireI] +func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { + return c.manager.circuit[c.wireI] } // combine the multiple claims into one claim using a random combination (combinationCoeff or c). @@ -338,7 +337,7 @@ func (c *eqTimesGateEvalSumcheckClaims) claimsNum() int { func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Element { //defer the proof, return list of claims - injection, _ := c.manager.wires.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. + injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. evaluations := make([]fr.Element, len(injection)) for i, gateInputI := range injection { wI := c.input[gateInputI] @@ -358,25 +357,24 @@ type claimsManager struct { assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool - wires Wires + circuit Circuit } -func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment - manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) + manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(circuit)) manager.memPool = o.pool manager.workers = o.workers - manager.wires = wires + manager.circuit = circuit - for i, wire := range wires { - - if wire.IsInput() { - wire.Gate.Degree = 1 + for i := range circuit { + if circuit[i].IsInput() { + circuit[i].Gate.Degree = 1 } manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ wireI: i, - evaluationPoints: make([][]fr.Element, 0, wire.NbClaims()), - claimedEvaluations: manager.memPool.Make(wire.NbClaims()), + evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), + claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), manager: &manager, } } @@ -396,7 +394,7 @@ func (m *claimsManager) getLazyClaim(wire int) *eqTimesGateEvalSumcheckLazyClaim func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { lazy := m.claims[wireI] - wire := m.wires[wireI] + wire := m.circuit[wireI] res := &eqTimesGateEvalSumcheckClaims{ wireI: wireI, evaluationPoints: lazy.evaluationPoints, @@ -426,7 +424,6 @@ func (m *claimsManager) deleteClaim(wire int) { type settings struct { pool *polynomial.Pool - sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -441,12 +438,6 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithSortedCircuit(sorted []*Wire) Option { - return func(options *settings) { - options.sorted = sorted - } -} - func WithWorkers(workers *utils.WorkerPool) Option { return func(options *settings) { options.workers = workers @@ -475,12 +466,8 @@ func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S o.workers = utils.NewWorkerPool() } - if o.sorted == nil { - o.sorted = c.TopologicalSort() - } - if transcriptSettings.Transcript == nil { - challengeNames := ChallengeNames(o.sorted, o.nbVars, transcriptSettings.Prefix) + challengeNames := ChallengeNames(c, o.nbVars, transcriptSettings.Prefix) o.transcript = fiatshamir.NewTranscript(transcriptSettings.Hash, challengeNames...) for i := range transcriptSettings.BaseChallenges { if err = o.transcript.Bind(challengeNames[0], transcriptSettings.BaseChallenges[i]); err != nil { @@ -494,22 +481,22 @@ func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S return o, err } -func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge - for _, w := range sorted { - if w.NoProof() { // no proof, no challenge + for i := range c { + if c[i].NoProof() { // no proof, no challenge continue } - if w.NbClaims() > 1 { //combine the claims + if c[i].NbClaims() > 1 { //combine the claims size++ } size += logNbInstances // full run of sumcheck on logNbInstances variables } - nums := make([]string, max(len(sorted), logNbInstances)) + nums := make([]string, max(len(c), logNbInstances)) for i := range nums { nums[i] = strconv.Itoa(i) } @@ -522,13 +509,13 @@ func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string challenges[j] = firstChallengePrefix + nums[j] } j := logNbInstances - for i := len(sorted) - 1; i >= 0; i-- { - if sorted[i].NoProof() { + for i := len(c) - 1; i >= 0; i-- { + if c[i].NoProof() { continue } wirePrefix := prefix + "w" + nums[i] + "." - if sorted[i].NbClaims() > 1 { + if c[i].NbClaims() > 1 { challenges[j] = wirePrefix + "comb" j++ } @@ -571,7 +558,7 @@ func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S } defer o.workers.Stop() - claims := newClaimsManager(o.sorted, assignment, o) + claims := newClaimsManager(c, assignment, o) proof := make(Proof, len(c)) // firstChallenge called rho in the paper @@ -585,7 +572,7 @@ func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S var baseChallenge [][]byte for i := len(c) - 1; i >= 0; i-- { - wire := o.sorted[i] + wire := c[i] if wire.IsOutput() { claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) @@ -625,7 +612,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } defer o.workers.Stop() - claims := newClaimsManager(o.sorted, assignment, o) + claims := newClaimsManager(c, assignment, o) var firstChallenge []fr.Element firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) @@ -636,7 +623,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting wirePrefix := o.transcriptPrefix + "w" var baseChallenge [][]byte for i := len(c) - 1; i >= 0; i-- { - wire := o.sorted[i] + wire := c[i] if wire.IsOutput() { claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) @@ -676,24 +663,24 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } // Complete the circuit evaluation from input values -func (a WireAssignment) Complete(wires Wires) WireAssignment { +func (a WireAssignment) Complete(circuit Circuit) WireAssignment { nbInstances := a.NumInstances() - evaluators := make([]gateEvaluator, len(wires)) + evaluators := make([]gateEvaluator, len(circuit)) - for i := range wires { + for i := range circuit { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Evaluate, len(wires[i].Inputs)) + if !circuit[i].IsInput() { + evaluators[i] = newGateEvaluator(circuit[i].Gate.Evaluate, len(circuit[i].Inputs)) } } for i := range nbInstances { - for wI, w := range wires { - if !w.IsInput() { - for _, in := range w.Inputs { + for wI := range circuit { + if !circuit[wI].IsInput() { + for _, in := range circuit[wI].Inputs { evaluators[wI].pushInput(&a[in][i]) } a[wI][i].Set(evaluators[wI].evaluate()) diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index 09362877a4..3dc8ce5179 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -23,7 +23,6 @@ import ( gcUtils "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/internal/utils" "github.com/stretchr/testify/assert" ) @@ -88,7 +87,7 @@ func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { o.workers = workers claimsManagerGen := func() *claimsManager { - manager := newClaimsManager(utils.References(circuit), assignment, o) + manager := newClaimsManager(circuit, assignment, o) manager.add(0, []fr.Element{three}, five) manager.add(0, []fr.Element{four}, six) return &manager @@ -135,7 +134,6 @@ func getLogMaxInstances(t *testing.T) int { func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { sCircuit := cache.Compile(circuit) - wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -151,7 +149,7 @@ func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { fullAssignment[ins[i]] = insAssignment[i][:numEvals] } - fullAssignment.Complete(wireRefs) + fullAssignment.Complete(sCircuit) t.Log("Selected inputs for test") @@ -278,7 +276,7 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("evaluating circuit") start := time.Now().UnixMicro() - assignment := WireAssignment{in0, in1}.Complete(utils.References(c)) + assignment := WireAssignment{in0, in1}.Complete(c) solved := time.Now().UnixMicro() - start fmt.Println("solved in", solved, "μs") @@ -368,18 +366,16 @@ func newTestCase(path string) (*TestCase, error) { fullAssignment := make(WireAssignment, len(circuit)) inOutAssignment := make(WireAssignment, len(circuit)) - sorted := circuit.TopologicalSort() - inI, outI := 0, 0 - for i, w := range sorted { + for i := range circuit { var assignmentRaw []interface{} - if w.IsInput() { + if circuit[i].IsInput() { if inI == len(info.Input) { return nil, fmt.Errorf("fewer input in vector than in circuit") } assignmentRaw = info.Input[inI] inI++ - } else if w.IsOutput() { + } else if circuit[i].IsOutput() { if outI == len(info.Output) { return nil, fmt.Errorf("fewer output in vector than in circuit") } @@ -397,10 +393,10 @@ func newTestCase(path string) (*TestCase, error) { } } - fullAssignment.Complete(utils.References(circuit)) + fullAssignment.Complete(circuit) - for i, w := range sorted { - if w.IsOutput() { + for i := range circuit { + if circuit[i].IsOutput() { if err = sliceEquals(inOutAssignment[i], fullAssignment[i]); err != nil { return nil, fmt.Errorf("assignment mismatch: %v", err) } diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 92a27216b5..c48acd3d03 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -22,7 +22,6 @@ import ( // Type aliases for bytecode-based GKR types type ( Wire = gkrtypes.SerializableWire - Wires = gkrtypes.SerializableWires Circuit = gkrtypes.SerializableCircuit ) @@ -43,8 +42,8 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { - return e.manager.wires[e.wireI] +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() Wire { + return e.manager.circuit[e.wireI] } func (e *eqTimesGateEvalSumcheckLazyClaims) claimsNum() int { @@ -62,7 +61,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a fr.Element) fr.Element } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.manager.wires[e.wireI].Gate.Degree + return 1 + e.manager.circuit[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. @@ -87,7 +86,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb evaluation.Add(&evaluation, &eq) } - wire := e.manager.wires[e.wireI] + wire := e.manager.circuit[e.wireI] // the w(...) term var gateEvaluation fr.Element @@ -95,7 +94,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire injection, injectionLeftInv := - e.manager.wires.ClaimPropagationInfo(e.wireI) + e.manager.circuit.ClaimPropagationInfo(e.wireI) if len(injection) != len(uniqueInputEvaluations) { return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) @@ -137,8 +136,8 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { - return c.manager.wires[c.wireI] +func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { + return c.manager.circuit[c.wireI] } // combine the multiple claims into one claim using a random combination (combinationCoeff or c). @@ -338,7 +337,7 @@ func (c *eqTimesGateEvalSumcheckClaims) claimsNum() int { func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Element { //defer the proof, return list of claims - injection, _ := c.manager.wires.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. + injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. evaluations := make([]fr.Element, len(injection)) for i, gateInputI := range injection { wI := c.input[gateInputI] @@ -358,25 +357,24 @@ type claimsManager struct { assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool - wires Wires + circuit Circuit } -func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment - manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) + manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(circuit)) manager.memPool = o.pool manager.workers = o.workers - manager.wires = wires + manager.circuit = circuit - for i, wire := range wires { - - if wire.IsInput() { - wire.Gate.Degree = 1 + for i := range circuit { + if circuit[i].IsInput() { + circuit[i].Gate.Degree = 1 } manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ wireI: i, - evaluationPoints: make([][]fr.Element, 0, wire.NbClaims()), - claimedEvaluations: manager.memPool.Make(wire.NbClaims()), + evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), + claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), manager: &manager, } } @@ -396,7 +394,7 @@ func (m *claimsManager) getLazyClaim(wire int) *eqTimesGateEvalSumcheckLazyClaim func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { lazy := m.claims[wireI] - wire := m.wires[wireI] + wire := m.circuit[wireI] res := &eqTimesGateEvalSumcheckClaims{ wireI: wireI, evaluationPoints: lazy.evaluationPoints, @@ -426,7 +424,6 @@ func (m *claimsManager) deleteClaim(wire int) { type settings struct { pool *polynomial.Pool - sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -441,12 +438,6 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithSortedCircuit(sorted []*Wire) Option { - return func(options *settings) { - options.sorted = sorted - } -} - func WithWorkers(workers *utils.WorkerPool) Option { return func(options *settings) { options.workers = workers @@ -475,12 +466,8 @@ func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S o.workers = utils.NewWorkerPool() } - if o.sorted == nil { - o.sorted = c.TopologicalSort() - } - if transcriptSettings.Transcript == nil { - challengeNames := ChallengeNames(o.sorted, o.nbVars, transcriptSettings.Prefix) + challengeNames := ChallengeNames(c, o.nbVars, transcriptSettings.Prefix) o.transcript = fiatshamir.NewTranscript(transcriptSettings.Hash, challengeNames...) for i := range transcriptSettings.BaseChallenges { if err = o.transcript.Bind(challengeNames[0], transcriptSettings.BaseChallenges[i]); err != nil { @@ -494,22 +481,22 @@ func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S return o, err } -func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge - for _, w := range sorted { - if w.NoProof() { // no proof, no challenge + for i := range c { + if c[i].NoProof() { // no proof, no challenge continue } - if w.NbClaims() > 1 { //combine the claims + if c[i].NbClaims() > 1 { //combine the claims size++ } size += logNbInstances // full run of sumcheck on logNbInstances variables } - nums := make([]string, max(len(sorted), logNbInstances)) + nums := make([]string, max(len(c), logNbInstances)) for i := range nums { nums[i] = strconv.Itoa(i) } @@ -522,13 +509,13 @@ func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string challenges[j] = firstChallengePrefix + nums[j] } j := logNbInstances - for i := len(sorted) - 1; i >= 0; i-- { - if sorted[i].NoProof() { + for i := len(c) - 1; i >= 0; i-- { + if c[i].NoProof() { continue } wirePrefix := prefix + "w" + nums[i] + "." - if sorted[i].NbClaims() > 1 { + if c[i].NbClaims() > 1 { challenges[j] = wirePrefix + "comb" j++ } @@ -571,7 +558,7 @@ func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S } defer o.workers.Stop() - claims := newClaimsManager(o.sorted, assignment, o) + claims := newClaimsManager(c, assignment, o) proof := make(Proof, len(c)) // firstChallenge called rho in the paper @@ -585,7 +572,7 @@ func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S var baseChallenge [][]byte for i := len(c) - 1; i >= 0; i-- { - wire := o.sorted[i] + wire := c[i] if wire.IsOutput() { claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) @@ -625,7 +612,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } defer o.workers.Stop() - claims := newClaimsManager(o.sorted, assignment, o) + claims := newClaimsManager(c, assignment, o) var firstChallenge []fr.Element firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) @@ -636,7 +623,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting wirePrefix := o.transcriptPrefix + "w" var baseChallenge [][]byte for i := len(c) - 1; i >= 0; i-- { - wire := o.sorted[i] + wire := c[i] if wire.IsOutput() { claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) @@ -676,24 +663,24 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } // Complete the circuit evaluation from input values -func (a WireAssignment) Complete(wires Wires) WireAssignment { +func (a WireAssignment) Complete(circuit Circuit) WireAssignment { nbInstances := a.NumInstances() - evaluators := make([]gateEvaluator, len(wires)) + evaluators := make([]gateEvaluator, len(circuit)) - for i := range wires { + for i := range circuit { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Evaluate, len(wires[i].Inputs)) + if !circuit[i].IsInput() { + evaluators[i] = newGateEvaluator(circuit[i].Gate.Evaluate, len(circuit[i].Inputs)) } } for i := range nbInstances { - for wI, w := range wires { - if !w.IsInput() { - for _, in := range w.Inputs { + for wI := range circuit { + if !circuit[wI].IsInput() { + for _, in := range circuit[wI].Inputs { evaluators[wI].pushInput(&a[in][i]) } a[wI][i].Set(evaluators[wI].evaluate()) diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 0cce17250d..9e3c583ac4 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -23,7 +23,6 @@ import ( gcUtils "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/internal/utils" "github.com/stretchr/testify/assert" ) @@ -88,7 +87,7 @@ func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { o.workers = workers claimsManagerGen := func() *claimsManager { - manager := newClaimsManager(utils.References(circuit), assignment, o) + manager := newClaimsManager(circuit, assignment, o) manager.add(0, []fr.Element{three}, five) manager.add(0, []fr.Element{four}, six) return &manager @@ -135,7 +134,6 @@ func getLogMaxInstances(t *testing.T) int { func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { sCircuit := cache.Compile(circuit) - wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -151,7 +149,7 @@ func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { fullAssignment[ins[i]] = insAssignment[i][:numEvals] } - fullAssignment.Complete(wireRefs) + fullAssignment.Complete(sCircuit) t.Log("Selected inputs for test") @@ -278,7 +276,7 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("evaluating circuit") start := time.Now().UnixMicro() - assignment := WireAssignment{in0, in1}.Complete(utils.References(c)) + assignment := WireAssignment{in0, in1}.Complete(c) solved := time.Now().UnixMicro() - start fmt.Println("solved in", solved, "μs") @@ -368,18 +366,16 @@ func newTestCase(path string) (*TestCase, error) { fullAssignment := make(WireAssignment, len(circuit)) inOutAssignment := make(WireAssignment, len(circuit)) - sorted := circuit.TopologicalSort() - inI, outI := 0, 0 - for i, w := range sorted { + for i := range circuit { var assignmentRaw []interface{} - if w.IsInput() { + if circuit[i].IsInput() { if inI == len(info.Input) { return nil, fmt.Errorf("fewer input in vector than in circuit") } assignmentRaw = info.Input[inI] inI++ - } else if w.IsOutput() { + } else if circuit[i].IsOutput() { if outI == len(info.Output) { return nil, fmt.Errorf("fewer output in vector than in circuit") } @@ -397,10 +393,10 @@ func newTestCase(path string) (*TestCase, error) { } } - fullAssignment.Complete(utils.References(circuit)) + fullAssignment.Complete(circuit) - for i, w := range sorted { - if w.IsOutput() { + for i := range circuit { + if circuit[i].IsOutput() { if err = sliceEquals(inOutAssignment[i], fullAssignment[i]); err != nil { return nil, fmt.Errorf("assignment mismatch: %v", err) } diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 8309f457e4..dde007a960 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -22,7 +22,6 @@ import ( // Type aliases for bytecode-based GKR types type ( Wire = gkrtypes.SerializableWire - Wires = gkrtypes.SerializableWires Circuit = gkrtypes.SerializableCircuit ) @@ -43,8 +42,8 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { - return e.manager.wires[e.wireI] +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() Wire { + return e.manager.circuit[e.wireI] } func (e *eqTimesGateEvalSumcheckLazyClaims) claimsNum() int { @@ -62,7 +61,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a fr.Element) fr.Element } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.manager.wires[e.wireI].Gate.Degree + return 1 + e.manager.circuit[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. @@ -87,7 +86,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb evaluation.Add(&evaluation, &eq) } - wire := e.manager.wires[e.wireI] + wire := e.manager.circuit[e.wireI] // the w(...) term var gateEvaluation fr.Element @@ -95,7 +94,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire injection, injectionLeftInv := - e.manager.wires.ClaimPropagationInfo(e.wireI) + e.manager.circuit.ClaimPropagationInfo(e.wireI) if len(injection) != len(uniqueInputEvaluations) { return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) @@ -137,8 +136,8 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { - return c.manager.wires[c.wireI] +func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { + return c.manager.circuit[c.wireI] } // combine the multiple claims into one claim using a random combination (combinationCoeff or c). @@ -338,7 +337,7 @@ func (c *eqTimesGateEvalSumcheckClaims) claimsNum() int { func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Element { //defer the proof, return list of claims - injection, _ := c.manager.wires.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. + injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. evaluations := make([]fr.Element, len(injection)) for i, gateInputI := range injection { wI := c.input[gateInputI] @@ -358,25 +357,24 @@ type claimsManager struct { assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool - wires Wires + circuit Circuit } -func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment - manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) + manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(circuit)) manager.memPool = o.pool manager.workers = o.workers - manager.wires = wires + manager.circuit = circuit - for i, wire := range wires { - - if wire.IsInput() { - wire.Gate.Degree = 1 + for i := range circuit { + if circuit[i].IsInput() { + circuit[i].Gate.Degree = 1 } manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ wireI: i, - evaluationPoints: make([][]fr.Element, 0, wire.NbClaims()), - claimedEvaluations: manager.memPool.Make(wire.NbClaims()), + evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), + claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), manager: &manager, } } @@ -396,7 +394,7 @@ func (m *claimsManager) getLazyClaim(wire int) *eqTimesGateEvalSumcheckLazyClaim func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { lazy := m.claims[wireI] - wire := m.wires[wireI] + wire := m.circuit[wireI] res := &eqTimesGateEvalSumcheckClaims{ wireI: wireI, evaluationPoints: lazy.evaluationPoints, @@ -426,7 +424,6 @@ func (m *claimsManager) deleteClaim(wire int) { type settings struct { pool *polynomial.Pool - sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -441,12 +438,6 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithSortedCircuit(sorted []*Wire) Option { - return func(options *settings) { - options.sorted = sorted - } -} - func WithWorkers(workers *utils.WorkerPool) Option { return func(options *settings) { options.workers = workers @@ -475,12 +466,8 @@ func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S o.workers = utils.NewWorkerPool() } - if o.sorted == nil { - o.sorted = c.TopologicalSort() - } - if transcriptSettings.Transcript == nil { - challengeNames := ChallengeNames(o.sorted, o.nbVars, transcriptSettings.Prefix) + challengeNames := ChallengeNames(c, o.nbVars, transcriptSettings.Prefix) o.transcript = fiatshamir.NewTranscript(transcriptSettings.Hash, challengeNames...) for i := range transcriptSettings.BaseChallenges { if err = o.transcript.Bind(challengeNames[0], transcriptSettings.BaseChallenges[i]); err != nil { @@ -494,22 +481,22 @@ func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S return o, err } -func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge - for _, w := range sorted { - if w.NoProof() { // no proof, no challenge + for i := range c { + if c[i].NoProof() { // no proof, no challenge continue } - if w.NbClaims() > 1 { //combine the claims + if c[i].NbClaims() > 1 { //combine the claims size++ } size += logNbInstances // full run of sumcheck on logNbInstances variables } - nums := make([]string, max(len(sorted), logNbInstances)) + nums := make([]string, max(len(c), logNbInstances)) for i := range nums { nums[i] = strconv.Itoa(i) } @@ -522,13 +509,13 @@ func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string challenges[j] = firstChallengePrefix + nums[j] } j := logNbInstances - for i := len(sorted) - 1; i >= 0; i-- { - if sorted[i].NoProof() { + for i := len(c) - 1; i >= 0; i-- { + if c[i].NoProof() { continue } wirePrefix := prefix + "w" + nums[i] + "." - if sorted[i].NbClaims() > 1 { + if c[i].NbClaims() > 1 { challenges[j] = wirePrefix + "comb" j++ } @@ -571,7 +558,7 @@ func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S } defer o.workers.Stop() - claims := newClaimsManager(o.sorted, assignment, o) + claims := newClaimsManager(c, assignment, o) proof := make(Proof, len(c)) // firstChallenge called rho in the paper @@ -585,7 +572,7 @@ func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S var baseChallenge [][]byte for i := len(c) - 1; i >= 0; i-- { - wire := o.sorted[i] + wire := c[i] if wire.IsOutput() { claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) @@ -625,7 +612,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } defer o.workers.Stop() - claims := newClaimsManager(o.sorted, assignment, o) + claims := newClaimsManager(c, assignment, o) var firstChallenge []fr.Element firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) @@ -636,7 +623,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting wirePrefix := o.transcriptPrefix + "w" var baseChallenge [][]byte for i := len(c) - 1; i >= 0; i-- { - wire := o.sorted[i] + wire := c[i] if wire.IsOutput() { claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) @@ -676,24 +663,24 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } // Complete the circuit evaluation from input values -func (a WireAssignment) Complete(wires Wires) WireAssignment { +func (a WireAssignment) Complete(circuit Circuit) WireAssignment { nbInstances := a.NumInstances() - evaluators := make([]gateEvaluator, len(wires)) + evaluators := make([]gateEvaluator, len(circuit)) - for i := range wires { + for i := range circuit { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Evaluate, len(wires[i].Inputs)) + if !circuit[i].IsInput() { + evaluators[i] = newGateEvaluator(circuit[i].Gate.Evaluate, len(circuit[i].Inputs)) } } for i := range nbInstances { - for wI, w := range wires { - if !w.IsInput() { - for _, in := range w.Inputs { + for wI := range circuit { + if !circuit[wI].IsInput() { + for _, in := range circuit[wI].Inputs { evaluators[wI].pushInput(&a[in][i]) } a[wI][i].Set(evaluators[wI].evaluate()) diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 404d8eded2..04ae48263c 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -23,7 +23,6 @@ import ( gcUtils "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/internal/utils" "github.com/stretchr/testify/assert" ) @@ -88,7 +87,7 @@ func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { o.workers = workers claimsManagerGen := func() *claimsManager { - manager := newClaimsManager(utils.References(circuit), assignment, o) + manager := newClaimsManager(circuit, assignment, o) manager.add(0, []fr.Element{three}, five) manager.add(0, []fr.Element{four}, six) return &manager @@ -135,7 +134,6 @@ func getLogMaxInstances(t *testing.T) int { func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { sCircuit := cache.Compile(circuit) - wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -151,7 +149,7 @@ func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { fullAssignment[ins[i]] = insAssignment[i][:numEvals] } - fullAssignment.Complete(wireRefs) + fullAssignment.Complete(sCircuit) t.Log("Selected inputs for test") @@ -278,7 +276,7 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("evaluating circuit") start := time.Now().UnixMicro() - assignment := WireAssignment{in0, in1}.Complete(utils.References(c)) + assignment := WireAssignment{in0, in1}.Complete(c) solved := time.Now().UnixMicro() - start fmt.Println("solved in", solved, "μs") @@ -368,18 +366,16 @@ func newTestCase(path string) (*TestCase, error) { fullAssignment := make(WireAssignment, len(circuit)) inOutAssignment := make(WireAssignment, len(circuit)) - sorted := circuit.TopologicalSort() - inI, outI := 0, 0 - for i, w := range sorted { + for i := range circuit { var assignmentRaw []interface{} - if w.IsInput() { + if circuit[i].IsInput() { if inI == len(info.Input) { return nil, fmt.Errorf("fewer input in vector than in circuit") } assignmentRaw = info.Input[inI] inI++ - } else if w.IsOutput() { + } else if circuit[i].IsOutput() { if outI == len(info.Output) { return nil, fmt.Errorf("fewer output in vector than in circuit") } @@ -397,10 +393,10 @@ func newTestCase(path string) (*TestCase, error) { } } - fullAssignment.Complete(utils.References(circuit)) + fullAssignment.Complete(circuit) - for i, w := range sorted { - if w.IsOutput() { + for i := range circuit { + if circuit[i].IsOutput() { if err = sliceEquals(inOutAssignment[i], fullAssignment[i]); err != nil { return nil, fmt.Errorf("assignment mismatch: %v", err) } diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index c849a8681a..8b40d5dfff 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -22,7 +22,6 @@ import ( // Type aliases for bytecode-based GKR types type ( Wire = gkrtypes.SerializableWire - Wires = gkrtypes.SerializableWires Circuit = gkrtypes.SerializableCircuit ) @@ -43,8 +42,8 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { - return e.manager.wires[e.wireI] +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() Wire { + return e.manager.circuit[e.wireI] } func (e *eqTimesGateEvalSumcheckLazyClaims) claimsNum() int { @@ -62,7 +61,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a fr.Element) fr.Element } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.manager.wires[e.wireI].Gate.Degree + return 1 + e.manager.circuit[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. @@ -87,7 +86,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb evaluation.Add(&evaluation, &eq) } - wire := e.manager.wires[e.wireI] + wire := e.manager.circuit[e.wireI] // the w(...) term var gateEvaluation fr.Element @@ -95,7 +94,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire injection, injectionLeftInv := - e.manager.wires.ClaimPropagationInfo(e.wireI) + e.manager.circuit.ClaimPropagationInfo(e.wireI) if len(injection) != len(uniqueInputEvaluations) { return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) @@ -137,8 +136,8 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { - return c.manager.wires[c.wireI] +func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { + return c.manager.circuit[c.wireI] } // combine the multiple claims into one claim using a random combination (combinationCoeff or c). @@ -338,7 +337,7 @@ func (c *eqTimesGateEvalSumcheckClaims) claimsNum() int { func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Element { //defer the proof, return list of claims - injection, _ := c.manager.wires.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. + injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. evaluations := make([]fr.Element, len(injection)) for i, gateInputI := range injection { wI := c.input[gateInputI] @@ -358,25 +357,24 @@ type claimsManager struct { assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool - wires Wires + circuit Circuit } -func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment - manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) + manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(circuit)) manager.memPool = o.pool manager.workers = o.workers - manager.wires = wires + manager.circuit = circuit - for i, wire := range wires { - - if wire.IsInput() { - wire.Gate.Degree = 1 + for i := range circuit { + if circuit[i].IsInput() { + circuit[i].Gate.Degree = 1 } manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ wireI: i, - evaluationPoints: make([][]fr.Element, 0, wire.NbClaims()), - claimedEvaluations: manager.memPool.Make(wire.NbClaims()), + evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), + claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), manager: &manager, } } @@ -396,7 +394,7 @@ func (m *claimsManager) getLazyClaim(wire int) *eqTimesGateEvalSumcheckLazyClaim func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { lazy := m.claims[wireI] - wire := m.wires[wireI] + wire := m.circuit[wireI] res := &eqTimesGateEvalSumcheckClaims{ wireI: wireI, evaluationPoints: lazy.evaluationPoints, @@ -426,7 +424,6 @@ func (m *claimsManager) deleteClaim(wire int) { type settings struct { pool *polynomial.Pool - sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -441,12 +438,6 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithSortedCircuit(sorted []*Wire) Option { - return func(options *settings) { - options.sorted = sorted - } -} - func WithWorkers(workers *utils.WorkerPool) Option { return func(options *settings) { options.workers = workers @@ -475,12 +466,8 @@ func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S o.workers = utils.NewWorkerPool() } - if o.sorted == nil { - o.sorted = c.TopologicalSort() - } - if transcriptSettings.Transcript == nil { - challengeNames := ChallengeNames(o.sorted, o.nbVars, transcriptSettings.Prefix) + challengeNames := ChallengeNames(c, o.nbVars, transcriptSettings.Prefix) o.transcript = fiatshamir.NewTranscript(transcriptSettings.Hash, challengeNames...) for i := range transcriptSettings.BaseChallenges { if err = o.transcript.Bind(challengeNames[0], transcriptSettings.BaseChallenges[i]); err != nil { @@ -494,22 +481,22 @@ func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S return o, err } -func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge - for _, w := range sorted { - if w.NoProof() { // no proof, no challenge + for i := range c { + if c[i].NoProof() { // no proof, no challenge continue } - if w.NbClaims() > 1 { //combine the claims + if c[i].NbClaims() > 1 { //combine the claims size++ } size += logNbInstances // full run of sumcheck on logNbInstances variables } - nums := make([]string, max(len(sorted), logNbInstances)) + nums := make([]string, max(len(c), logNbInstances)) for i := range nums { nums[i] = strconv.Itoa(i) } @@ -522,13 +509,13 @@ func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string challenges[j] = firstChallengePrefix + nums[j] } j := logNbInstances - for i := len(sorted) - 1; i >= 0; i-- { - if sorted[i].NoProof() { + for i := len(c) - 1; i >= 0; i-- { + if c[i].NoProof() { continue } wirePrefix := prefix + "w" + nums[i] + "." - if sorted[i].NbClaims() > 1 { + if c[i].NbClaims() > 1 { challenges[j] = wirePrefix + "comb" j++ } @@ -571,7 +558,7 @@ func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S } defer o.workers.Stop() - claims := newClaimsManager(o.sorted, assignment, o) + claims := newClaimsManager(c, assignment, o) proof := make(Proof, len(c)) // firstChallenge called rho in the paper @@ -585,7 +572,7 @@ func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S var baseChallenge [][]byte for i := len(c) - 1; i >= 0; i-- { - wire := o.sorted[i] + wire := c[i] if wire.IsOutput() { claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) @@ -625,7 +612,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } defer o.workers.Stop() - claims := newClaimsManager(o.sorted, assignment, o) + claims := newClaimsManager(c, assignment, o) var firstChallenge []fr.Element firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) @@ -636,7 +623,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting wirePrefix := o.transcriptPrefix + "w" var baseChallenge [][]byte for i := len(c) - 1; i >= 0; i-- { - wire := o.sorted[i] + wire := c[i] if wire.IsOutput() { claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) @@ -676,24 +663,24 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } // Complete the circuit evaluation from input values -func (a WireAssignment) Complete(wires Wires) WireAssignment { +func (a WireAssignment) Complete(circuit Circuit) WireAssignment { nbInstances := a.NumInstances() - evaluators := make([]gateEvaluator, len(wires)) + evaluators := make([]gateEvaluator, len(circuit)) - for i := range wires { + for i := range circuit { if len(a[i]) != nbInstances { a[i] = make([]fr.Element, nbInstances) } - if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Evaluate, len(wires[i].Inputs)) + if !circuit[i].IsInput() { + evaluators[i] = newGateEvaluator(circuit[i].Gate.Evaluate, len(circuit[i].Inputs)) } } for i := range nbInstances { - for wI, w := range wires { - if !w.IsInput() { - for _, in := range w.Inputs { + for wI := range circuit { + if !circuit[wI].IsInput() { + for _, in := range circuit[wI].Inputs { evaluators[wI].pushInput(&a[in][i]) } a[wI][i].Set(evaluators[wI].evaluate()) diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index 8920732a23..1cf4cf0bda 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -23,7 +23,6 @@ import ( gcUtils "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/internal/utils" "github.com/stretchr/testify/assert" ) @@ -88,7 +87,7 @@ func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { o.workers = workers claimsManagerGen := func() *claimsManager { - manager := newClaimsManager(utils.References(circuit), assignment, o) + manager := newClaimsManager(circuit, assignment, o) manager.add(0, []fr.Element{three}, five) manager.add(0, []fr.Element{four}, six) return &manager @@ -135,7 +134,6 @@ func getLogMaxInstances(t *testing.T) int { func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { sCircuit := cache.Compile(circuit) - wireRefs := utils.References(sCircuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -151,7 +149,7 @@ func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { fullAssignment[ins[i]] = insAssignment[i][:numEvals] } - fullAssignment.Complete(wireRefs) + fullAssignment.Complete(sCircuit) t.Log("Selected inputs for test") @@ -278,7 +276,7 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("evaluating circuit") start := time.Now().UnixMicro() - assignment := WireAssignment{in0, in1}.Complete(utils.References(c)) + assignment := WireAssignment{in0, in1}.Complete(c) solved := time.Now().UnixMicro() - start fmt.Println("solved in", solved, "μs") @@ -368,18 +366,16 @@ func newTestCase(path string) (*TestCase, error) { fullAssignment := make(WireAssignment, len(circuit)) inOutAssignment := make(WireAssignment, len(circuit)) - sorted := circuit.TopologicalSort() - inI, outI := 0, 0 - for i, w := range sorted { + for i := range circuit { var assignmentRaw []interface{} - if w.IsInput() { + if circuit[i].IsInput() { if inI == len(info.Input) { return nil, fmt.Errorf("fewer input in vector than in circuit") } assignmentRaw = info.Input[inI] inI++ - } else if w.IsOutput() { + } else if circuit[i].IsOutput() { if outI == len(info.Output) { return nil, fmt.Errorf("fewer output in vector than in circuit") } @@ -397,10 +393,10 @@ func newTestCase(path string) (*TestCase, error) { } } - fullAssignment.Complete(utils.References(circuit)) + fullAssignment.Complete(circuit) - for i, w := range sorted { - if w.IsOutput() { + for i := range circuit { + if circuit[i].IsOutput() { if err = sliceEquals(inOutAssignment[i], fullAssignment[i]); err != nil { return nil, fmt.Errorf("assignment mismatch: %v", err) } diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index 3c19812712..0a23de81a8 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -15,7 +15,6 @@ import ( // Type aliases for gadget circuit types type ( Wire = gkrtypes.GadgetWire - Wires = gkrtypes.GadgetWires Circuit = gkrtypes.GadgetCircuit ) @@ -72,8 +71,8 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { - return e.manager.wires[e.wireI] +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() Wire { + return e.manager.circuit[e.wireI] } // verifyFinalEval finalizes the verification of w. @@ -107,7 +106,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(api frontend.API, r } else { injection, injectionLeftInv := - e.manager.wires.ClaimPropagationInfo(e.wireI) + e.manager.circuit.ClaimPropagationInfo(e.wireI) if len(injection) != len(uniqueInputEvaluations) { return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) @@ -150,23 +149,23 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { type claimsManager struct { claims []*eqTimesGateEvalSumcheckLazyClaims assignment WireAssignment - wires Wires + circuit Circuit } -func newClaimsManager(wires Wires, assignment WireAssignment) (claims claimsManager) { +func newClaimsManager(circuit Circuit, assignment WireAssignment) (claims claimsManager) { claims.assignment = assignment - claims.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) - claims.wires = wires + claims.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(circuit)) + claims.circuit = circuit - for i, wire := range wires { - if wire.IsInput() { - wire.Gate.Degree = 1 - wire.Gate.Evaluate = gkrtypes.Identity + for i := range circuit { + if circuit[i].IsInput() { + circuit[i].Gate.Degree = 1 + circuit[i].Gate.Evaluate = gkrtypes.Identity } claims.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ wireI: i, - evaluationPoints: make([][]frontend.Variable, 0, wire.NbClaims()), - claimedEvaluations: make(polynomial.Polynomial, wire.NbClaims()), + evaluationPoints: make([][]frontend.Variable, 0, circuit[i].NbClaims()), + claimedEvaluations: make(polynomial.Polynomial, circuit[i].NbClaims()), manager: &claims, } } @@ -190,7 +189,6 @@ func (m *claimsManager) deleteClaim(wire int) { } type settings struct { - sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -198,12 +196,6 @@ type settings struct { type Option func(*settings) -func WithSortedCircuit(sorted []*Wire) Option { - return func(options *settings) { - options.sorted = sorted - } -} - func setup(api frontend.API, c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error @@ -217,12 +209,8 @@ func setup(api frontend.API, c Circuit, assignment WireAssignment, transcriptSet return o, errors.New("number of instances must be power of 2") } - if o.sorted == nil { - o.sorted = c.TopologicalSort() - } - if transcriptSettings.Transcript == nil { - challengeNames := ChallengeNames(o.sorted, o.nbVars, transcriptSettings.Prefix) + challengeNames := ChallengeNames(c, o.nbVars, transcriptSettings.Prefix) o.transcript = fiatshamir.NewTranscript(api, transcriptSettings.Hash, challengeNames) if err = o.transcript.Bind(challengeNames[0], transcriptSettings.BaseChallenges); err != nil { return o, err @@ -234,22 +222,22 @@ func setup(api frontend.API, c Circuit, assignment WireAssignment, transcriptSet return o, err } -func ChallengeNames(sorted Wires, logNbInstances int, prefix string) []string { +func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge - for _, w := range sorted { - if w.NoProof() { // no proof, no challenge + for i := range c { + if c[i].NoProof() { // no proof, no challenge continue } - if w.NbClaims() > 1 { //combine the claims + if c[i].NbClaims() > 1 { //combine the claims size++ } size += logNbInstances // full run of sumcheck on logNbInstances variables } - nums := make([]string, max(len(sorted), logNbInstances)) + nums := make([]string, max(len(c), logNbInstances)) for i := range nums { nums[i] = strconv.Itoa(i) } @@ -262,13 +250,13 @@ func ChallengeNames(sorted Wires, logNbInstances int, prefix string) []string { challenges[j] = firstChallengePrefix + nums[j] } j := logNbInstances - for i := len(sorted) - 1; i >= 0; i-- { - if sorted[i].NoProof() { + for i := len(c) - 1; i >= 0; i-- { + if c[i].NoProof() { continue } wirePrefix := prefix + "w" + nums[i] + "." - if sorted[i].NbClaims() > 1 { + if c[i].NbClaims() > 1 { challenges[j] = wirePrefix + "comb" j++ } @@ -309,7 +297,7 @@ func Verify(api frontend.API, c Circuit, assignment WireAssignment, proof Proof, return err } - claims := newClaimsManager(o.sorted, assignment) + claims := newClaimsManager(c, assignment) var firstChallenge []frontend.Variable firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) @@ -320,7 +308,7 @@ func Verify(api frontend.API, c Circuit, assignment WireAssignment, proof Proof, wirePrefix := o.transcriptPrefix + "w" var baseChallenge []frontend.Variable for i := len(c) - 1; i >= 0; i-- { - wire := o.sorted[i] + wire := c[i] if wire.IsOutput() { claims.add(i, firstChallenge, assignment[i].Evaluate(api, firstChallenge)) @@ -351,8 +339,6 @@ func Verify(api frontend.API, c Circuit, assignment WireAssignment, proof Proof, return nil } -// TODO: Have this use algo_utils.TopologicalSort underneath - func (p Proof) Serialize() []frontend.Variable { size := 0 for i := range p { diff --git a/internal/gkr/gkr_test.go b/internal/gkr/gkr_test.go index 341b0eccf6..6b0ecd1b67 100644 --- a/internal/gkr/gkr_test.go +++ b/internal/gkr/gkr_test.go @@ -132,15 +132,14 @@ func (c *GkrVerifierCircuit) Define(api frontend.API) error { } func makeInOutAssignment(c Circuit, inputValues [][]frontend.Variable, outputValues [][]frontend.Variable) WireAssignment { - sorted := c.TopologicalSort() res := make(WireAssignment, len(c)) inI, outI := 0, 0 - for wI, w := range sorted { - if w.IsInput() { - res[wI] = inputValues[inI] + for i := range c { + if c[i].IsInput() { + res[i] = inputValues[inI] inI++ - } else if w.IsOutput() { - res[wI] = outputValues[outI] + } else if c[i].IsOutput() { + res[i] = outputValues[outI] outI++ } } diff --git a/internal/gkr/gkrtypes/topological_sort_test.go b/internal/gkr/gkrtypes/topological_sort_test.go deleted file mode 100644 index 2726baea55..0000000000 --- a/internal/gkr/gkrtypes/topological_sort_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package gkrtypes - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestTopSortTrivial(t *testing.T) { - c := make(SerializableCircuit, 2) - c[0].Inputs = []int{1} - sorted := c.TopologicalSort() - assert.Equal(t, SerializableWires{&c[1], &c[0]}, sorted) -} - -func TestTopSortSingleGate(t *testing.T) { - c := make(SerializableCircuit, 3) - c[0].Inputs = []int{1, 2} - sorted := c.TopologicalSort() - expected := SerializableWires{&c[1], &c[2], &c[0]} - - assert.Equal(t, expected, sorted) - 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(SerializableCircuit, 4) - c[0].Inputs = []int{2} - c[1].Inputs = []int{3} - c[2].Inputs = []int{} - c[3].Inputs = []int{0} - sorted := c.TopologicalSort() - assert.Equal(t, SerializableWires{&c[2], &c[0], &c[3], &c[1]}, sorted) -} - -func TestTopSortWide(t *testing.T) { - c := make(SerializableCircuit, 10) - c[0].Inputs = []int{3, 8} - c[1].Inputs = []int{6} - c[2].Inputs = []int{4} - c[3].Inputs = []int{} - c[4].Inputs = []int{} - c[5].Inputs = []int{9} - c[6].Inputs = []int{9} - c[7].Inputs = []int{9, 5, 2} - c[8].Inputs = []int{4, 3} - c[9].Inputs = []int{} - - sorted := c.TopologicalSort() - sortedExpected := SerializableWires{&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) -} diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 736cc39f8a..b26f8be2b2 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -32,23 +32,17 @@ type ( Circuit[GateExecutable any] []Wire[GateExecutable] - // Wires is a slice of pointers to Wire. It is used for propagating claim - // information through the circuit. - Wires[GateExecutable any] []*Wire[GateExecutable] - // Type aliases for different circuit instantiations // Serializable types (bytecode only, for native proving) SerializableGate = Gate[GateBytecode] SerializableCircuit = Circuit[GateBytecode] SerializableWire = Wire[GateBytecode] - SerializableWires = Wires[GateBytecode] // Gadget types (gate functions only, for in-circuit verification) GadgetGate = Gate[gkr.GateFunction] GadgetCircuit = Circuit[gkr.GateFunction] GadgetWire = Wire[gkr.GateFunction] - GadgetWires = Wires[gkr.GateFunction] ) // IsInput returns whether the wire is an input wire. @@ -87,15 +81,26 @@ func (w Wire[GateExecutable]) NbUniqueInputs() int { return len(set) } +// ProofPolyLength returns the size (degree+1) of each polynomial in the ZeroCheck subproof associated with w. +func (w Wire[GateExecutable]) ProofPolyLength() int { + if w.NoProof() { + return 0 + } + if w.IsInput() { + return 2 // Input gate with multiple claims treated as a degree 1 gate. + } + return w.Gate.Degree + 1 +} + // ClaimPropagationInfo returns sets of indices describing the pruning of claim propagation. // At the end of sumcheck for wire #wireIndex, we end up with sequences "uniqueEvaluations" and "evaluations", // the former a subsequence of the latter. // injection are the indices of the unique evaluations in the original evaluation list. // injectionRightInverse are the indices of the original evaluations in the unique evaluations list. // There are no guarantees on the non-unique choice of the semi-inverse map. -func (wires Wires[GateExecutable]) ClaimPropagationInfo(wireIndex int) (injection, injectionLeftInverse []int) { - w := wires[wireIndex] - indexInProof := makeNeg1Slice(len(wires)) // O(n); use a map instead if it caused performance issues +func (c Circuit[GateExecutable]) ClaimPropagationInfo(wireIndex int) (injection, injectionLeftInverse []int) { + w := c[wireIndex] + indexInProof := makeNeg1Slice(len(c)) // O(n); use a map instead if it caused performance issues injection = make([]int, 0, len(w.Inputs)) injectionLeftInverse = make([]int, len(w.Inputs)) @@ -134,17 +139,6 @@ func (c Circuit[GateExecutable]) MemoryRequirements(nbInstances int) []int { return res } -// ProofPolyLength returns the size (degree+1) of each polynomial in the ZeroCheck subproof associated with w. -func (w Wire[GateExecutable]) ProofPolyLength() int { - if w.NoProof() { - return 0 - } - if w.IsInput() { - return 2 // Input gate with multiple claims treated as a degree 1 gate. - } - return w.Gate.Degree + 1 -} - // OutputsList for each wire, returns the set of indexes of wires it is input to. // It also sets the NbUniqueOutputs fields. func (c Circuit[GateExecutable]) OutputsList() [][]int { @@ -221,58 +215,6 @@ func makeNeg1Slice(n int) []int { return res } -type topSortData struct { - outputs [][]int - status []int // status > 0 indicates number of inputs left to be ready. status = 0 means ready. status = -1 means done - leastReady int -} - -func (d *topSortData) markDone(i int) { - - d.status[i] = -1 - - for _, outI := range d.outputs[i] { - d.status[outI]-- - if d.status[outI] == 0 && outI < d.leastReady { - d.leastReady = outI - } - } - - for d.leastReady < len(d.status) && d.status[d.leastReady] != 0 { - d.leastReady++ - } -} - -func (c Circuit[GateExecutable]) statusList() []int { - res := make([]int, len(c)) - for i := range c { - res[i] = len(c[i].Inputs) - } - return res -} - -// 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. -// Worst-case inefficient O(n^2), but that probably won't matter since the circuits are small. -// Furthermore, it is efficient with already-close-to-sorted lists, which are the expected input -func (c Circuit[GateExecutable]) TopologicalSort() Wires[GateExecutable] { - var data topSortData - data.outputs = c.OutputsList() - data.status = c.statusList() - sorted := make(Wires[GateExecutable], len(c)) - - for data.leastReady = 0; data.status[data.leastReady] != 0; data.leastReady++ { - } - - for i := range c { - sorted[i] = &c[data.leastReady] - data.markDone(data.leastReady) - } - - return sorted -} - // some sample gates // Identity gate: x -> x diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 0eb4e1b412..42e371638e 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -22,7 +22,6 @@ import ( // Type aliases for bytecode-based GKR types type ( Wire = gkrtypes.SerializableWire - Wires = gkrtypes.SerializableWires Circuit = gkrtypes.SerializableCircuit ) @@ -43,8 +42,8 @@ type eqTimesGateEvalSumcheckLazyClaims struct { manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() *Wire { - return e.manager.wires[e.wireI] +func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() Wire { + return e.manager.circuit[e.wireI] } func (e *eqTimesGateEvalSumcheckLazyClaims) claimsNum() int { @@ -62,7 +61,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a small_rational.SmallRa } func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { - return 1 + e.manager.wires[e.wireI].Gate.Degree + return 1 + e.manager.circuit[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. @@ -87,7 +86,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []small_rational.S evaluation.Add(&evaluation, &eq) } - wire := e.manager.wires[e.wireI] + wire := e.manager.circuit[e.wireI] // the w(...) term var gateEvaluation small_rational.SmallRational @@ -95,7 +94,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []small_rational.S gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire injection, injectionLeftInv := - e.manager.wires.ClaimPropagationInfo(e.wireI) + e.manager.circuit.ClaimPropagationInfo(e.wireI) if len(injection) != len(uniqueInputEvaluations) { return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) @@ -137,8 +136,8 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() *Wire { - return c.manager.wires[c.wireI] +func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { + return c.manager.circuit[c.wireI] } // combine the multiple claims into one claim using a random combination (combinationCoeff or c). @@ -338,7 +337,7 @@ func (c *eqTimesGateEvalSumcheckClaims) claimsNum() int { func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []small_rational.SmallRational) []small_rational.SmallRational { //defer the proof, return list of claims - injection, _ := c.manager.wires.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. + injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. evaluations := make([]small_rational.SmallRational, len(injection)) for i, gateInputI := range injection { wI := c.input[gateInputI] @@ -358,25 +357,24 @@ type claimsManager struct { assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool - wires Wires + circuit Circuit } -func newClaimsManager(wires []*Wire, assignment WireAssignment, o settings) (manager claimsManager) { +func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment - manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(wires)) + manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(circuit)) manager.memPool = o.pool manager.workers = o.workers - manager.wires = wires + manager.circuit = circuit - for i, wire := range wires { - - if wire.IsInput() { - wire.Gate.Degree = 1 + for i := range circuit { + if circuit[i].IsInput() { + circuit[i].Gate.Degree = 1 } manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ wireI: i, - evaluationPoints: make([][]small_rational.SmallRational, 0, wire.NbClaims()), - claimedEvaluations: manager.memPool.Make(wire.NbClaims()), + evaluationPoints: make([][]small_rational.SmallRational, 0, circuit[i].NbClaims()), + claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), manager: &manager, } } @@ -396,7 +394,7 @@ func (m *claimsManager) getLazyClaim(wire int) *eqTimesGateEvalSumcheckLazyClaim func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { lazy := m.claims[wireI] - wire := m.wires[wireI] + wire := m.circuit[wireI] res := &eqTimesGateEvalSumcheckClaims{ wireI: wireI, evaluationPoints: lazy.evaluationPoints, @@ -426,7 +424,6 @@ func (m *claimsManager) deleteClaim(wire int) { type settings struct { pool *polynomial.Pool - sorted []*Wire transcript *fiatshamir.Transcript transcriptPrefix string nbVars int @@ -441,12 +438,6 @@ func WithPool(pool *polynomial.Pool) Option { } } -func WithSortedCircuit(sorted []*Wire) Option { - return func(options *settings) { - options.sorted = sorted - } -} - func WithWorkers(workers *utils.WorkerPool) Option { return func(options *settings) { options.workers = workers @@ -475,12 +466,8 @@ func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S o.workers = utils.NewWorkerPool() } - if o.sorted == nil { - o.sorted = c.TopologicalSort() - } - if transcriptSettings.Transcript == nil { - challengeNames := ChallengeNames(o.sorted, o.nbVars, transcriptSettings.Prefix) + challengeNames := ChallengeNames(c, o.nbVars, transcriptSettings.Prefix) o.transcript = fiatshamir.NewTranscript(transcriptSettings.Hash, challengeNames...) for i := range transcriptSettings.BaseChallenges { if err = o.transcript.Bind(challengeNames[0], transcriptSettings.BaseChallenges[i]); err != nil { @@ -494,22 +481,22 @@ func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S return o, err } -func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string { +func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { // Pre-compute the size TODO: Consider not doing this and just grow the list by appending size := logNbInstances // first challenge - for _, w := range sorted { - if w.NoProof() { // no proof, no challenge + for i := range c { + if c[i].NoProof() { // no proof, no challenge continue } - if w.NbClaims() > 1 { //combine the claims + if c[i].NbClaims() > 1 { //combine the claims size++ } size += logNbInstances // full run of sumcheck on logNbInstances variables } - nums := make([]string, max(len(sorted), logNbInstances)) + nums := make([]string, max(len(c), logNbInstances)) for i := range nums { nums[i] = strconv.Itoa(i) } @@ -522,13 +509,13 @@ func ChallengeNames(sorted []*Wire, logNbInstances int, prefix string) []string challenges[j] = firstChallengePrefix + nums[j] } j := logNbInstances - for i := len(sorted) - 1; i >= 0; i-- { - if sorted[i].NoProof() { + for i := len(c) - 1; i >= 0; i-- { + if c[i].NoProof() { continue } wirePrefix := prefix + "w" + nums[i] + "." - if sorted[i].NbClaims() > 1 { + if c[i].NbClaims() > 1 { challenges[j] = wirePrefix + "comb" j++ } @@ -571,7 +558,7 @@ func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S } defer o.workers.Stop() - claims := newClaimsManager(o.sorted, assignment, o) + claims := newClaimsManager(c, assignment, o) proof := make(Proof, len(c)) // firstChallenge called rho in the paper @@ -585,7 +572,7 @@ func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S var baseChallenge [][]byte for i := len(c) - 1; i >= 0; i-- { - wire := o.sorted[i] + wire := c[i] if wire.IsOutput() { claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) @@ -625,7 +612,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } defer o.workers.Stop() - claims := newClaimsManager(o.sorted, assignment, o) + claims := newClaimsManager(c, assignment, o) var firstChallenge []small_rational.SmallRational firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) @@ -636,7 +623,7 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting wirePrefix := o.transcriptPrefix + "w" var baseChallenge [][]byte for i := len(c) - 1; i >= 0; i-- { - wire := o.sorted[i] + wire := c[i] if wire.IsOutput() { claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) @@ -676,24 +663,24 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting } // Complete the circuit evaluation from input values -func (a WireAssignment) Complete(wires Wires) WireAssignment { +func (a WireAssignment) Complete(circuit Circuit) WireAssignment { nbInstances := a.NumInstances() - evaluators := make([]gateEvaluator, len(wires)) + evaluators := make([]gateEvaluator, len(circuit)) - for i := range wires { + for i := range circuit { if len(a[i]) != nbInstances { a[i] = make([]small_rational.SmallRational, nbInstances) } - if !wires[i].IsInput() { - evaluators[i] = newGateEvaluator(wires[i].Gate.Evaluate, len(wires[i].Inputs)) + if !circuit[i].IsInput() { + evaluators[i] = newGateEvaluator(circuit[i].Gate.Evaluate, len(circuit[i].Inputs)) } } for i := range nbInstances { - for wI, w := range wires { - if !w.IsInput() { - for _, in := range w.Inputs { + for wI := range circuit { + if !circuit[wI].IsInput() { + for _, in := range circuit[wI].Inputs { evaluators[wI].pushInput(&a[in][i]) } a[wI][i].Set(evaluators[wI].evaluate()) diff --git a/internal/gkr/small_rational/test_vector_gen.go b/internal/gkr/small_rational/test_vector_gen.go index 3efae37acd..40f4beaf0e 100644 --- a/internal/gkr/small_rational/test_vector_gen.go +++ b/internal/gkr/small_rational/test_vector_gen.go @@ -20,7 +20,6 @@ import ( "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" - "github.com/consensys/gnark/internal/utils" ) // The properties of test gates are expected to be the same across all relevant fields. @@ -232,18 +231,16 @@ func newTestCase(path string) (*TestCase, error) { fullAssignment := make(WireAssignment, len(circuit)) inOutAssignment := make(WireAssignment, len(circuit)) - sorted := circuit.TopologicalSort() - inI, outI := 0, 0 - for i, w := range sorted { + for i := range circuit { var assignmentRaw []interface{} - if w.IsInput() { + if circuit[i].IsInput() { if inI == len(info.Input) { return nil, fmt.Errorf("fewer input in vector than in circuit") } assignmentRaw = info.Input[inI] inI++ - } else if w.IsOutput() { + } else if circuit[i].IsOutput() { if outI == len(info.Output) { return nil, fmt.Errorf("fewer output in vector than in circuit") } @@ -261,10 +258,10 @@ func newTestCase(path string) (*TestCase, error) { } } - fullAssignment.Complete(utils.References(circuit)) + fullAssignment.Complete(circuit) - for i, w := range sorted { - if w.IsOutput() { + for i := range circuit { + if circuit[i].IsOutput() { if err = sliceEquals(inOutAssignment[i], fullAssignment[i]); err != nil { return nil, fmt.Errorf("assignment mismatch: %v", err) } From b30cab704b235a59efe0019da6520352161a137d Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Feb 2026 23:20:52 -0600 Subject: [PATCH 194/251] minor naming improvements --- .../generator/backend/template/gkr/gkr.go.tmpl | 3 --- internal/gkr/bls12-377/gkr.go | 3 --- internal/gkr/bls12-381/gkr.go | 3 --- internal/gkr/bn254/gkr.go | 3 --- internal/gkr/bw6-761/gkr.go | 3 --- internal/gkr/gkrtesting/gkrtesting.go | 18 +++++++++--------- internal/gkr/gkrtypes/types.go | 2 ++ internal/gkr/small_rational/gkr.go | 3 --- 8 files changed, 11 insertions(+), 27 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index b585e454fc..c8d9a37932 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -865,9 +865,6 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() - // Reset evaluator state (but keep the vars slice for reuse) - e.frameSize = gep.gate.NbConstants() - // Return evaluator to pool (it keeps its vars slice from polynomial pool) gep.available[e] = struct{}{} } diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 226f9dcaaa..47085ae51c 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -870,9 +870,6 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() - // Reset evaluator state (but keep the vars slice for reuse) - e.frameSize = gep.gate.NbConstants() - // Return evaluator to pool (it keeps its vars slice from polynomial pool) gep.available[e] = struct{}{} } diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index c48acd3d03..d384b4acf7 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -870,9 +870,6 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() - // Reset evaluator state (but keep the vars slice for reuse) - e.frameSize = gep.gate.NbConstants() - // Return evaluator to pool (it keeps its vars slice from polynomial pool) gep.available[e] = struct{}{} } diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index dde007a960..4444586d17 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -870,9 +870,6 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() - // Reset evaluator state (but keep the vars slice for reuse) - e.frameSize = gep.gate.NbConstants() - // Return evaluator to pool (it keeps its vars slice from polynomial pool) gep.available[e] = struct{}{} } diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 8b40d5dfff..3d0de7c350 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -870,9 +870,6 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() - // Reset evaluator state (but keep the vars slice for reuse) - e.frameSize = gep.gate.NbConstants() - // Return evaluator to pool (it keeps its vars slice from polynomial pool) gep.available[e] = struct{}{} } diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 0012e80158..5576dea233 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -24,8 +24,8 @@ type Cache struct { } type circuits struct { - proverCircuit gkrtypes.SerializableCircuit - verifierCircuit gkrtypes.GadgetCircuit + serializable gkrtypes.SerializableCircuit + gadget gkrtypes.GadgetCircuit } func mimcGate(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { @@ -83,7 +83,7 @@ func (c *Cache) GetCircuit(path string) (gkrtypes.SerializableCircuit, gkrtypes. panic(err) } if circuit, ok := c.circuits[path]; ok { - return circuit.proverCircuit, circuit.verifierCircuit + return circuit.serializable, circuit.gadget } var bytes []byte @@ -98,23 +98,23 @@ func (c *Cache) GetCircuit(path string) (gkrtypes.SerializableCircuit, gkrtypes. } // Convert JSON format to GadgetCircuit - circuit := make(gkrtypes.GadgetCircuit, len(jsonCircuit)) + gCircuit := make(gkrtypes.GadgetCircuit, len(jsonCircuit)) for i, wJSON := range jsonCircuit { gate := c.GetGate(wJSON.Gate) - circuit[i] = gkrtypes.GadgetWire{ + gCircuit[i] = gkrtypes.GadgetWire{ Gate: gkrtypes.Gate[gkr.GateFunction]{Evaluate: gate}, Inputs: wJSON.Inputs, } } - pCircuit := gkrtypes.CompileCircuit(circuit, c.field) + sCircuit := gkrtypes.CompileCircuit(gCircuit, c.field) c.circuits[path] = circuits{ - proverCircuit: pCircuit, - verifierCircuit: circuit, + serializable: sCircuit, + gadget: gCircuit, } - return pCircuit, circuit + return sCircuit, gCircuit } func (c *Cache) RegisterGate(name string, gate gkr.GateFunction) { diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index b26f8be2b2..9edf960fff 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -35,11 +35,13 @@ type ( // Type aliases for different circuit instantiations // Serializable types (bytecode only, for native proving) + SerializableGate = Gate[GateBytecode] SerializableCircuit = Circuit[GateBytecode] SerializableWire = Wire[GateBytecode] // Gadget types (gate functions only, for in-circuit verification) + GadgetGate = Gate[gkr.GateFunction] GadgetCircuit = Circuit[gkr.GateFunction] GadgetWire = Wire[gkr.GateFunction] diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 42e371638e..8f3158dcda 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -870,9 +870,6 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() - // Reset evaluator state (but keep the vars slice for reuse) - e.frameSize = gep.gate.NbConstants() - // Return evaluator to pool (it keeps its vars slice from polynomial pool) gep.available[e] = struct{}{} } From fa7acf24325426fb81f528b778c3b895e56125a5 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sat, 14 Feb 2026 06:23:43 +0000 Subject: [PATCH 195/251] simplify gateEvaluator --- .../backend/template/gkr/blueprint.go.tmpl | 2 +- .../backend/template/gkr/gkr.go.tmpl | 36 ++++++++--------- internal/gkr/bls12-377/blueprint.go | 2 +- internal/gkr/bls12-377/gkr.go | 40 +++++++++---------- internal/gkr/bls12-381/blueprint.go | 2 +- internal/gkr/bls12-381/gkr.go | 40 +++++++++---------- internal/gkr/bn254/blueprint.go | 2 +- internal/gkr/bn254/gkr.go | 40 +++++++++---------- internal/gkr/bw6-761/blueprint.go | 2 +- internal/gkr/bw6-761/gkr.go | 40 +++++++++---------- internal/gkr/small_rational/gkr.go | 40 +++++++++---------- 11 files changed, 117 insertions(+), 129 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index adae37f4ad..0d80fc29d6 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -104,7 +104,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignments[inWI][instanceI]) + evaluator.pushInput(b.assignments[inWI][instanceI]) } // Evaluate the gate diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index c8d9a37932..e7231d9218 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -99,7 +99,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []{{ .ElementType evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique - evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) + evaluator.pushInput(uniqueInputEvaluations[uniqueI]) } gateEvaluation.Set(evaluator.evaluate()) @@ -267,7 +267,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { for d := range degGJ { // Push gate inputs for i := range nbGateIn { - evaluator.pushInput(&mlEvals[eIndex+1+i]) + evaluator.pushInput(mlEvals[eIndex+1+i]) } summand := evaluator.evaluate() summand.Mul(summand, &mlEvals[eIndex]) @@ -676,7 +676,7 @@ func (a WireAssignment) Complete(circuit Circuit) WireAssignment { for wI := range circuit { if !circuit[wI].IsInput() { for _, in := range circuit[wI].Inputs { - evaluators[wI].pushInput(&a[in][i]) + evaluators[wI].pushInput(a[in][i]) } a[wI][i].Set(evaluators[wI].evaluate()) } @@ -730,10 +730,9 @@ func (p Proof) flatten() iter.Seq2[int, *{{ .ElementType }}] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate gkrtypes.GateBytecode - vars []{{ .ElementType }} - frameSize int // number of constants plus currently pushed inputs - nbIn int // number of inputs expected + gate gkrtypes.GateBytecode + vars []{{ .ElementType }} + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. @@ -742,13 +741,13 @@ func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*poly e := gateEvaluator{ gate: gate, nbIn: nbIn, - frameSize: gate.NbConstants(), } if len(elementPool) > 0 { e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) } else { e.vars = make([]{{.ElementType}}, gate.NbConstants()+nbIn+len(gate.Instructions)) } + e.vars = e.vars[:gate.NbConstants()] for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } @@ -757,9 +756,8 @@ func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*poly // pushInput adds an input to the evaluator's input buffer. // Inputs must be added in order, and the number of inputs must match the gate's NbInputs. -func (e *gateEvaluator) pushInput(input *{{ .ElementType }}) { - e.vars[e.frameSize].Set(input) - e.frameSize++ +func (e *gateEvaluator) pushInput(input {{ .ElementType }}) { + e.vars = append(e.vars, input) } // evaluate adds top to the top of the stack, executes the gate on it and returns the result. @@ -767,19 +765,19 @@ func (e *gateEvaluator) pushInput(input *{{ .ElementType }}) { // making the evaluator ready for the next evaluation. // NB! The result is short-lived. It will be overwritten the next time evaluate is called. func (e *gateEvaluator) evaluate(top ...{{ .ElementType }}) *{{ .ElementType }} { - for i := range top { - e.pushInput(&top[i]) - } + e.vars = append(e.vars, top...) - if e.frameSize != e.nbIn+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) + if len(e.vars) != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, len(e.vars))) } // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { inst := &e.gate.Instructions[i] - dst := &e.vars[i+e.frameSize] + // Grow len by 1 within the pre-allocated capacity + e.vars = e.vars[:len(e.vars)+1] + dst := &e.vars[len(e.vars)-1] // Use switch instead of function pointer for better inlining switch inst.Op { @@ -819,10 +817,10 @@ func (e *gateEvaluator) evaluate(top ...{{ .ElementType }}) *{{ .ElementType }} } } - res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] + res := &e.vars[len(e.vars)-1] // Reset for next evaluation - e.frameSize = e.gate.NbConstants() + e.vars = e.vars[:e.gate.NbConstants()] return res } diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index f4ecfd0b2a..72ac7530fd 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -111,7 +111,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignments[inWI][instanceI]) + evaluator.pushInput(b.assignments[inWI][instanceI]) } // Evaluate the gate diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 47085ae51c..8870dbd30a 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -106,7 +106,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique - evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) + evaluator.pushInput(uniqueInputEvaluations[uniqueI]) } gateEvaluation.Set(evaluator.evaluate()) @@ -273,7 +273,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { for d := range degGJ { // Push gate inputs for i := range nbGateIn { - evaluator.pushInput(&mlEvals[eIndex+1+i]) + evaluator.pushInput(mlEvals[eIndex+1+i]) } summand := evaluator.evaluate() summand.Mul(summand, &mlEvals[eIndex]) @@ -681,7 +681,7 @@ func (a WireAssignment) Complete(circuit Circuit) WireAssignment { for wI := range circuit { if !circuit[wI].IsInput() { for _, in := range circuit[wI].Inputs { - evaluators[wI].pushInput(&a[in][i]) + evaluators[wI].pushInput(a[in][i]) } a[wI][i].Set(evaluators[wI].evaluate()) } @@ -735,25 +735,24 @@ func (p Proof) flatten() iter.Seq2[int, *fr.Element] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate gkrtypes.GateBytecode - vars []fr.Element - frameSize int // number of constants plus currently pushed inputs - nbIn int // number of inputs expected + gate gkrtypes.GateBytecode + vars []fr.Element + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, - frameSize: gate.NbConstants(), + gate: gate, + nbIn: nbIn, } if len(elementPool) > 0 { e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) } else { e.vars = make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)) } + e.vars = e.vars[:gate.NbConstants()] for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } @@ -762,9 +761,8 @@ func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*poly // pushInput adds an input to the evaluator's input buffer. // Inputs must be added in order, and the number of inputs must match the gate's NbInputs. -func (e *gateEvaluator) pushInput(input *fr.Element) { - e.vars[e.frameSize].Set(input) - e.frameSize++ +func (e *gateEvaluator) pushInput(input fr.Element) { + e.vars = append(e.vars, input) } // evaluate adds top to the top of the stack, executes the gate on it and returns the result. @@ -772,19 +770,19 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { // making the evaluator ready for the next evaluation. // NB! The result is short-lived. It will be overwritten the next time evaluate is called. func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { - for i := range top { - e.pushInput(&top[i]) - } + e.vars = append(e.vars, top...) - if e.frameSize != e.nbIn+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) + if len(e.vars) != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, len(e.vars))) } // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { inst := &e.gate.Instructions[i] - dst := &e.vars[i+e.frameSize] + // Grow len by 1 within the pre-allocated capacity (safe because cap was set in newGateEvaluator) + e.vars = e.vars[:len(e.vars)+1] + dst := &e.vars[len(e.vars)-1] // Use switch instead of function pointer for better inlining switch inst.Op { @@ -824,10 +822,10 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { } } - res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] + res := &e.vars[len(e.vars)-1] // Reset for next evaluation - e.frameSize = e.gate.NbConstants() + e.vars = e.vars[:e.gate.NbConstants()] return res } diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index a931934faf..4b5a4fce03 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -111,7 +111,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignments[inWI][instanceI]) + evaluator.pushInput(b.assignments[inWI][instanceI]) } // Evaluate the gate diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index d384b4acf7..7cf12cf9cf 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -106,7 +106,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique - evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) + evaluator.pushInput(uniqueInputEvaluations[uniqueI]) } gateEvaluation.Set(evaluator.evaluate()) @@ -273,7 +273,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { for d := range degGJ { // Push gate inputs for i := range nbGateIn { - evaluator.pushInput(&mlEvals[eIndex+1+i]) + evaluator.pushInput(mlEvals[eIndex+1+i]) } summand := evaluator.evaluate() summand.Mul(summand, &mlEvals[eIndex]) @@ -681,7 +681,7 @@ func (a WireAssignment) Complete(circuit Circuit) WireAssignment { for wI := range circuit { if !circuit[wI].IsInput() { for _, in := range circuit[wI].Inputs { - evaluators[wI].pushInput(&a[in][i]) + evaluators[wI].pushInput(a[in][i]) } a[wI][i].Set(evaluators[wI].evaluate()) } @@ -735,25 +735,24 @@ func (p Proof) flatten() iter.Seq2[int, *fr.Element] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate gkrtypes.GateBytecode - vars []fr.Element - frameSize int // number of constants plus currently pushed inputs - nbIn int // number of inputs expected + gate gkrtypes.GateBytecode + vars []fr.Element + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, - frameSize: gate.NbConstants(), + gate: gate, + nbIn: nbIn, } if len(elementPool) > 0 { e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) } else { e.vars = make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)) } + e.vars = e.vars[:gate.NbConstants()] for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } @@ -762,9 +761,8 @@ func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*poly // pushInput adds an input to the evaluator's input buffer. // Inputs must be added in order, and the number of inputs must match the gate's NbInputs. -func (e *gateEvaluator) pushInput(input *fr.Element) { - e.vars[e.frameSize].Set(input) - e.frameSize++ +func (e *gateEvaluator) pushInput(input fr.Element) { + e.vars = append(e.vars, input) } // evaluate adds top to the top of the stack, executes the gate on it and returns the result. @@ -772,19 +770,19 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { // making the evaluator ready for the next evaluation. // NB! The result is short-lived. It will be overwritten the next time evaluate is called. func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { - for i := range top { - e.pushInput(&top[i]) - } + e.vars = append(e.vars, top...) - if e.frameSize != e.nbIn+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) + if len(e.vars) != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, len(e.vars))) } // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { inst := &e.gate.Instructions[i] - dst := &e.vars[i+e.frameSize] + // Grow len by 1 within the pre-allocated capacity (safe because cap was set in newGateEvaluator) + e.vars = e.vars[:len(e.vars)+1] + dst := &e.vars[len(e.vars)-1] // Use switch instead of function pointer for better inlining switch inst.Op { @@ -824,10 +822,10 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { } } - res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] + res := &e.vars[len(e.vars)-1] // Reset for next evaluation - e.frameSize = e.gate.NbConstants() + e.vars = e.vars[:e.gate.NbConstants()] return res } diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 29d583ae85..7c4336bef4 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -111,7 +111,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignments[inWI][instanceI]) + evaluator.pushInput(b.assignments[inWI][instanceI]) } // Evaluate the gate diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 4444586d17..8466054667 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -106,7 +106,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique - evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) + evaluator.pushInput(uniqueInputEvaluations[uniqueI]) } gateEvaluation.Set(evaluator.evaluate()) @@ -273,7 +273,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { for d := range degGJ { // Push gate inputs for i := range nbGateIn { - evaluator.pushInput(&mlEvals[eIndex+1+i]) + evaluator.pushInput(mlEvals[eIndex+1+i]) } summand := evaluator.evaluate() summand.Mul(summand, &mlEvals[eIndex]) @@ -681,7 +681,7 @@ func (a WireAssignment) Complete(circuit Circuit) WireAssignment { for wI := range circuit { if !circuit[wI].IsInput() { for _, in := range circuit[wI].Inputs { - evaluators[wI].pushInput(&a[in][i]) + evaluators[wI].pushInput(a[in][i]) } a[wI][i].Set(evaluators[wI].evaluate()) } @@ -735,25 +735,24 @@ func (p Proof) flatten() iter.Seq2[int, *fr.Element] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate gkrtypes.GateBytecode - vars []fr.Element - frameSize int // number of constants plus currently pushed inputs - nbIn int // number of inputs expected + gate gkrtypes.GateBytecode + vars []fr.Element + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, - frameSize: gate.NbConstants(), + gate: gate, + nbIn: nbIn, } if len(elementPool) > 0 { e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) } else { e.vars = make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)) } + e.vars = e.vars[:gate.NbConstants()] for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } @@ -762,9 +761,8 @@ func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*poly // pushInput adds an input to the evaluator's input buffer. // Inputs must be added in order, and the number of inputs must match the gate's NbInputs. -func (e *gateEvaluator) pushInput(input *fr.Element) { - e.vars[e.frameSize].Set(input) - e.frameSize++ +func (e *gateEvaluator) pushInput(input fr.Element) { + e.vars = append(e.vars, input) } // evaluate adds top to the top of the stack, executes the gate on it and returns the result. @@ -772,19 +770,19 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { // making the evaluator ready for the next evaluation. // NB! The result is short-lived. It will be overwritten the next time evaluate is called. func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { - for i := range top { - e.pushInput(&top[i]) - } + e.vars = append(e.vars, top...) - if e.frameSize != e.nbIn+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) + if len(e.vars) != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, len(e.vars))) } // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { inst := &e.gate.Instructions[i] - dst := &e.vars[i+e.frameSize] + // Grow len by 1 within the pre-allocated capacity (safe because cap was set in newGateEvaluator) + e.vars = e.vars[:len(e.vars)+1] + dst := &e.vars[len(e.vars)-1] // Use switch instead of function pointer for better inlining switch inst.Op { @@ -824,10 +822,10 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { } } - res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] + res := &e.vars[len(e.vars)-1] // Reset for next evaluation - e.frameSize = e.gate.NbConstants() + e.vars = e.vars[:e.gate.NbConstants()] return res } diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index c1275ff9c8..8093ea08d0 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -111,7 +111,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra // Push gate inputs for _, inWI := range w.Inputs { - evaluator.pushInput(&b.assignments[inWI][instanceI]) + evaluator.pushInput(b.assignments[inWI][instanceI]) } // Evaluate the gate diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 3d0de7c350..161547bd4a 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -106,7 +106,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique - evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) + evaluator.pushInput(uniqueInputEvaluations[uniqueI]) } gateEvaluation.Set(evaluator.evaluate()) @@ -273,7 +273,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { for d := range degGJ { // Push gate inputs for i := range nbGateIn { - evaluator.pushInput(&mlEvals[eIndex+1+i]) + evaluator.pushInput(mlEvals[eIndex+1+i]) } summand := evaluator.evaluate() summand.Mul(summand, &mlEvals[eIndex]) @@ -681,7 +681,7 @@ func (a WireAssignment) Complete(circuit Circuit) WireAssignment { for wI := range circuit { if !circuit[wI].IsInput() { for _, in := range circuit[wI].Inputs { - evaluators[wI].pushInput(&a[in][i]) + evaluators[wI].pushInput(a[in][i]) } a[wI][i].Set(evaluators[wI].evaluate()) } @@ -735,25 +735,24 @@ func (p Proof) flatten() iter.Seq2[int, *fr.Element] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate gkrtypes.GateBytecode - vars []fr.Element - frameSize int // number of constants plus currently pushed inputs - nbIn int // number of inputs expected + gate gkrtypes.GateBytecode + vars []fr.Element + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, - frameSize: gate.NbConstants(), + gate: gate, + nbIn: nbIn, } if len(elementPool) > 0 { e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) } else { e.vars = make([]fr.Element, gate.NbConstants()+nbIn+len(gate.Instructions)) } + e.vars = e.vars[:gate.NbConstants()] for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } @@ -762,9 +761,8 @@ func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*poly // pushInput adds an input to the evaluator's input buffer. // Inputs must be added in order, and the number of inputs must match the gate's NbInputs. -func (e *gateEvaluator) pushInput(input *fr.Element) { - e.vars[e.frameSize].Set(input) - e.frameSize++ +func (e *gateEvaluator) pushInput(input fr.Element) { + e.vars = append(e.vars, input) } // evaluate adds top to the top of the stack, executes the gate on it and returns the result. @@ -772,19 +770,19 @@ func (e *gateEvaluator) pushInput(input *fr.Element) { // making the evaluator ready for the next evaluation. // NB! The result is short-lived. It will be overwritten the next time evaluate is called. func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { - for i := range top { - e.pushInput(&top[i]) - } + e.vars = append(e.vars, top...) - if e.frameSize != e.nbIn+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) + if len(e.vars) != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, len(e.vars))) } // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { inst := &e.gate.Instructions[i] - dst := &e.vars[i+e.frameSize] + // Grow len by 1 within the pre-allocated capacity (safe because cap was set in newGateEvaluator) + e.vars = e.vars[:len(e.vars)+1] + dst := &e.vars[len(e.vars)-1] // Use switch instead of function pointer for better inlining switch inst.Op { @@ -824,10 +822,10 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { } } - res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] + res := &e.vars[len(e.vars)-1] // Reset for next evaluation - e.frameSize = e.gate.NbConstants() + e.vars = e.vars[:e.gate.NbConstants()] return res } diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 8f3158dcda..994e4f89e3 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -106,7 +106,7 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []small_rational.S evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, uniqueI := range injectionLeftInv { // map from all to unique - evaluator.pushInput(&uniqueInputEvaluations[uniqueI]) + evaluator.pushInput(uniqueInputEvaluations[uniqueI]) } gateEvaluation.Set(evaluator.evaluate()) @@ -273,7 +273,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { for d := range degGJ { // Push gate inputs for i := range nbGateIn { - evaluator.pushInput(&mlEvals[eIndex+1+i]) + evaluator.pushInput(mlEvals[eIndex+1+i]) } summand := evaluator.evaluate() summand.Mul(summand, &mlEvals[eIndex]) @@ -681,7 +681,7 @@ func (a WireAssignment) Complete(circuit Circuit) WireAssignment { for wI := range circuit { if !circuit[wI].IsInput() { for _, in := range circuit[wI].Inputs { - evaluators[wI].pushInput(&a[in][i]) + evaluators[wI].pushInput(a[in][i]) } a[wI][i].Set(evaluators[wI].evaluate()) } @@ -735,25 +735,24 @@ func (p Proof) flatten() iter.Seq2[int, *small_rational.SmallRational] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate gkrtypes.GateBytecode - vars []small_rational.SmallRational - frameSize int // number of constants plus currently pushed inputs - nbIn int // number of inputs expected + gate gkrtypes.GateBytecode + vars []small_rational.SmallRational + nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ - gate: gate, - nbIn: nbIn, - frameSize: gate.NbConstants(), + gate: gate, + nbIn: nbIn, } if len(elementPool) > 0 { e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) } else { e.vars = make([]small_rational.SmallRational, gate.NbConstants()+nbIn+len(gate.Instructions)) } + e.vars = e.vars[:gate.NbConstants()] for i, constVal := range gate.Constants { e.vars[i].SetBigInt(constVal) } @@ -762,9 +761,8 @@ func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*poly // pushInput adds an input to the evaluator's input buffer. // Inputs must be added in order, and the number of inputs must match the gate's NbInputs. -func (e *gateEvaluator) pushInput(input *small_rational.SmallRational) { - e.vars[e.frameSize].Set(input) - e.frameSize++ +func (e *gateEvaluator) pushInput(input small_rational.SmallRational) { + e.vars = append(e.vars, input) } // evaluate adds top to the top of the stack, executes the gate on it and returns the result. @@ -772,19 +770,19 @@ func (e *gateEvaluator) pushInput(input *small_rational.SmallRational) { // making the evaluator ready for the next evaluation. // NB! The result is short-lived. It will be overwritten the next time evaluate is called. func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rational.SmallRational { - for i := range top { - e.pushInput(&top[i]) - } + e.vars = append(e.vars, top...) - if e.frameSize != e.nbIn+e.gate.NbConstants() { - panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, e.frameSize)) + if len(e.vars) != e.nbIn+e.gate.NbConstants() { + panic(fmt.Sprintf("expected a stack size of %d representing %d constants and %d inputs, got %d", e.nbIn+e.gate.NbConstants(), e.gate.NbConstants(), e.nbIn, len(e.vars))) } // Execute instructions, appending results to stack // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { inst := &e.gate.Instructions[i] - dst := &e.vars[i+e.frameSize] + // Grow len by 1 within the pre-allocated capacity (safe because cap was set in newGateEvaluator) + e.vars = e.vars[:len(e.vars)+1] + dst := &e.vars[len(e.vars)-1] // Use switch instead of function pointer for better inlining switch inst.Op { @@ -824,10 +822,10 @@ func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rat } } - res := &e.vars[e.frameSize+len(e.gate.Instructions)-1] + res := &e.vars[len(e.vars)-1] // Reset for next evaluation - e.frameSize = e.gate.NbConstants() + e.vars = e.vars[:e.gate.NbConstants()] return res } From 52339184cc0091329d7dd82ed4c6689788fa6dd8 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sat, 14 Feb 2026 06:24:11 +0000 Subject: [PATCH 196/251] build: go generate --- internal/gkr/bls12-377/gkr.go | 2 +- internal/gkr/bls12-381/gkr.go | 2 +- internal/gkr/bn254/gkr.go | 2 +- internal/gkr/bw6-761/gkr.go | 2 +- internal/gkr/small_rational/gkr.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 8870dbd30a..9c120fb20d 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -780,7 +780,7 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { inst := &e.gate.Instructions[i] - // Grow len by 1 within the pre-allocated capacity (safe because cap was set in newGateEvaluator) + // Grow len by 1 within the pre-allocated capacity e.vars = e.vars[:len(e.vars)+1] dst := &e.vars[len(e.vars)-1] diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 7cf12cf9cf..3b49c98e75 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -780,7 +780,7 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { inst := &e.gate.Instructions[i] - // Grow len by 1 within the pre-allocated capacity (safe because cap was set in newGateEvaluator) + // Grow len by 1 within the pre-allocated capacity e.vars = e.vars[:len(e.vars)+1] dst := &e.vars[len(e.vars)-1] diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 8466054667..724ad2abaa 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -780,7 +780,7 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { inst := &e.gate.Instructions[i] - // Grow len by 1 within the pre-allocated capacity (safe because cap was set in newGateEvaluator) + // Grow len by 1 within the pre-allocated capacity e.vars = e.vars[:len(e.vars)+1] dst := &e.vars[len(e.vars)-1] diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 161547bd4a..906cda398d 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -780,7 +780,7 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { inst := &e.gate.Instructions[i] - // Grow len by 1 within the pre-allocated capacity (safe because cap was set in newGateEvaluator) + // Grow len by 1 within the pre-allocated capacity e.vars = e.vars[:len(e.vars)+1] dst := &e.vars[len(e.vars)-1] diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 994e4f89e3..6b39d5d7bb 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -780,7 +780,7 @@ func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rat // The stack grows to: [constants | inputs | results] for i := range e.gate.Instructions { inst := &e.gate.Instructions[i] - // Grow len by 1 within the pre-allocated capacity (safe because cap was set in newGateEvaluator) + // Grow len by 1 within the pre-allocated capacity e.vars = e.vars[:len(e.vars)+1] dst := &e.vars[len(e.vars)-1] From 138c5fa95df5b01ffe1d7269d485a653ea2f48a4 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sat, 14 Feb 2026 00:33:12 -0600 Subject: [PATCH 197/251] remove empty file --- internal/gkr/compile.go | 1 - 1 file changed, 1 deletion(-) delete mode 100644 internal/gkr/compile.go diff --git a/internal/gkr/compile.go b/internal/gkr/compile.go deleted file mode 100644 index 10cd5e6a54..0000000000 --- a/internal/gkr/compile.go +++ /dev/null @@ -1 +0,0 @@ -package gkr From 52abacd464b8bdeada1a5bed5c3c868a553d2d03 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 18 Feb 2026 15:05:40 -0600 Subject: [PATCH 198/251] refactor: better name for ProofPolyLength --- internal/gkr/gkr.go | 4 ++-- internal/gkr/gkrtypes/types.go | 20 +++++++++++++------- std/gkrapi/api_test.go | 4 ++-- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index 0a23de81a8..974c9da7ef 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -369,7 +369,7 @@ func (p Proof) Serialize() []frontend.Variable { func ComputeLogNbInstances(circuit Circuit, serializedProofLen int) int { partialEvalElemsPerVar := 0 for _, w := range circuit { - partialEvalElemsPerVar += w.ProofPolyLength() + partialEvalElemsPerVar += w.ZeroCheckDegree() serializedProofLen -= w.NbUniqueOutputs } res := serializedProofLen / partialEvalElemsPerVar @@ -400,7 +400,7 @@ func DeserializeProof(circuit Circuit, serializedProof []frontend.Variable) (Pro if !wI.NoProof() { proof[i].PartialSumPolys = make([]polynomial.Polynomial, logNbInstances) for j := range proof[i].PartialSumPolys { - proof[i].PartialSumPolys[j] = reader.nextN(wI.ProofPolyLength()) + proof[i].PartialSumPolys[j] = reader.nextN(wI.ZeroCheckDegree()) } } proof[i].FinalEvalProof = reader.nextN(wI.NbUniqueInputs()) diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 9edf960fff..011cc58d73 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -83,13 +83,19 @@ func (w Wire[GateExecutable]) NbUniqueInputs() int { return len(set) } -// ProofPolyLength returns the size (degree+1) of each polynomial in the ZeroCheck subproof associated with w. -func (w Wire[GateExecutable]) ProofPolyLength() int { - if w.NoProof() { - return 0 - } +// ZeroCheckDegree returns the degree in each variable of the zero-check polynomial +// associated with this gate, if any. If this wire is not subject to zero-check, it will return 0. +func (w Wire[GateExecutable]) ZeroCheckDegree() int { if w.IsInput() { - return 2 // Input gate with multiple claims treated as a degree 1 gate. + switch w.NbClaims() { + case 0: + panic("should be unreachable") + case 1: + return 0 + default: + // Input gate with multiple claims treated as a degree 1 gate. + return 2 + } } return w.Gate.Degree + 1 } @@ -203,7 +209,7 @@ func (c Circuit[GateExecutable]) ProofSize(logNbInstances int) int { nbPartialEvalPolys := 0 for i := range c { nbUniqueInputs += c[i].NbUniqueOutputs // each unique output is manifest in a finalEvalProof entry - nbPartialEvalPolys += c[i].ProofPolyLength() + nbPartialEvalPolys += c[i].ZeroCheckDegree() } return nbUniqueInputs + nbPartialEvalPolys*logNbInstances } diff --git a/std/gkrapi/api_test.go b/std/gkrapi/api_test.go index f360567d94..5f839f6f6a 100644 --- a/std/gkrapi/api_test.go +++ b/std/gkrapi/api_test.go @@ -25,8 +25,8 @@ import ( ) // compressThreshold → if linear expressions are larger than this, the frontend will introduce -// intermediate constraints. The lower this number is, the faster compile time should be (to a point) -// but resulting circuit will have more constraints (slower proving time). +// intermediate constraints. The lower this number is, the faster compile time should be (to a point). +// However, the resulting circuit will have more constraints (slower proving time). const compressThreshold = 1000 type doubleNoDependencyCircuit struct { From 47aa74475b2443db02ac6a7f3b1f0c0904192b7e Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 18 Feb 2026 15:15:45 -0600 Subject: [PATCH 199/251] refactor: name changes for bn254 --- internal/gkr/bn254/gkr.go | 66 ++++++++++++++--------------- internal/gkr/bn254/sumcheck.go | 26 ++++++------ internal/gkr/bn254/sumcheck_test.go | 6 +-- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 724ad2abaa..0529bab6e6 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -32,41 +32,41 @@ type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) -// eqTimesGateEvalSumcheckLazyClaims is a lazy claim for sumcheck (verifier side). +// zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the checking of multiple evaluations of the same wire. -type eqTimesGateEvalSumcheckLazyClaims struct { +type zeroCheckLazyClaims struct { wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() Wire { +func (e *zeroCheckLazyClaims) getWire() Wire { return e.manager.circuit[e.wireI] } -func (e *eqTimesGateEvalSumcheckLazyClaims) claimsNum() int { +func (e *zeroCheckLazyClaims) claimsNum() int { return len(e.evaluationPoints) } -func (e *eqTimesGateEvalSumcheckLazyClaims) varsNum() int { +func (e *zeroCheckLazyClaims) varsNum() int { return len(e.evaluationPoints[0]) } -// combinedSum returns ∑ᵢ aⁱ yᵢ -func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a fr.Element) fr.Element { +// foldedSum returns ∑ᵢ aⁱ yᵢ +func (e *zeroCheckLazyClaims) foldedSum(a fr.Element) fr.Element { evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) return evalsAsPoly.Eval(&a) } -func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { +func (e *zeroCheckLazyClaims) degree(int) int { return 1 + e.manager.circuit[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. // The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is combinationCoeff ) +// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is foldingCoeff ) // Both purportedValue and the vector r have been randomized during the sumcheck protocol. // By taking the w term out of the sum we get the equivalent claim that // for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. @@ -76,12 +76,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { // The claims are communicated through the proof parameter. // The verifier checks here if the claimed evaluations of wᵢ(r) are consistent with // the main claim, by checking E w(wᵢ(r)...) = purportedValue. -func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, combinationCoeff, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { +func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, foldingCoeff, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { // the eq terms ( E ) numClaims := len(e.evaluationPoints) evaluation := polynomial.EvalEq(e.evaluationPoints[numClaims-1], r) for i := numClaims - 2; i >= 0; i-- { - evaluation.Mul(&evaluation, &combinationCoeff) + evaluation.Mul(&evaluation, &foldingCoeff) eq := polynomial.EvalEq(e.evaluationPoints[i], r) evaluation.Add(&evaluation, &eq) } @@ -120,10 +120,10 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb return errors.New("incompatible evaluations") } -// eqTimesGateEvalSumcheckClaims is a claim for sumcheck (prover side). +// zeroCheckClaims is a claim for sumcheck (prover side). // eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). // Its purpose is to batch the proving of multiple evaluations of the same wire. -type eqTimesGateEvalSumcheckClaims struct { +type zeroCheckClaims struct { wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ) @@ -136,11 +136,11 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { +func (c *zeroCheckClaims) getWire() Wire { return c.manager.circuit[c.wireI] } -// combine the multiple claims into one claim using a random combination (combinationCoeff or c). +// fold the multiple claims into one claim using a random combination (foldingCoeff or c). // From the original multiple claims of w(xᵢ) = yᵢ, we get a single claim // ∑ᵢ,ₕ cⁱ eq(xᵢ, h) w(h) = ∑ᵢ cⁱ yᵢ, where h iterates over the hypercube (circuit instances) and // i iterates over the claims. @@ -148,8 +148,8 @@ func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { // Thus if we initially compute E := ∑ᵢ cⁱ eq(xᵢ, -), our claim will find the simpler form // ∑ᵢ cⁱ yᵢ = ∑ₕ w(h) E(h), where the sum-checked polynomial is of degree deg(g) + 1, // and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. -// The output of combine is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. -func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) polynomial.Polynomial { +// The output of fold is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. +func (c *zeroCheckClaims) fold(foldingCoeff fr.Element) polynomial.Polynomial { varsNum := c.varsNum() eqLength := 1 << varsNum claimsNum := c.claimsNum() @@ -161,7 +161,7 @@ func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) pol // E := eq(x₀, -) newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) - aI := combinationCoeff + aI := foldingCoeff // E += cⁱ eq(xᵢ, -) for k := 1; k < claimsNum; k++ { @@ -170,7 +170,7 @@ func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) pol c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) if k+1 < claimsNum { - aI.Mul(&aI, &combinationCoeff) + aI.Mul(&aI, &foldingCoeff) } } @@ -182,7 +182,7 @@ func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) pol // eqAcc sets m to an eq table at q and then adds it to e. // m <- eq(q, -). // e <- e + m -func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { +func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { n := len(q) //At the end of each iteration, m(h₁, ..., hₙ) = eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) @@ -221,7 +221,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E // computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). // the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. -func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { +func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) @@ -237,7 +237,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { sumSize := len(c.eq) / 2 // the range of h, over which we sum - // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called + // Perf-TODO: Collate once at claim "folding" time and not again. then, even folding can be done in one operation every time "next" is called gJ := make([]fr.Element, degGJ) var mu sync.Mutex @@ -302,7 +302,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // next first folds the input and E polynomials at the given verifier challenge then computes the new gⱼ. // Thus, j <- j+1 and rⱼ = challenge. -func (c *eqTimesGateEvalSumcheckClaims) next(challenge fr.Element) polynomial.Polynomial { +func (c *zeroCheckClaims) next(challenge fr.Element) polynomial.Polynomial { const minBlockSize = 512 n := len(c.eq) / 2 if n < minBlockSize { @@ -325,16 +325,16 @@ func (c *eqTimesGateEvalSumcheckClaims) next(challenge fr.Element) polynomial.Po return c.computeGJ() } -func (c *eqTimesGateEvalSumcheckClaims) varsNum() int { +func (c *zeroCheckClaims) varsNum() int { return len(c.evaluationPoints[0]) } -func (c *eqTimesGateEvalSumcheckClaims) claimsNum() int { +func (c *zeroCheckClaims) claimsNum() int { return len(c.claimedEvaluations) } // proveFinalEval provides the values wᵢ(r₁, ..., rₙ) -func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Element { +func (c *zeroCheckClaims) proveFinalEval(r []fr.Element) []fr.Element { //defer the proof, return list of claims injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. @@ -353,7 +353,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } type claimsManager struct { - claims []*eqTimesGateEvalSumcheckLazyClaims + claims []*zeroCheckLazyClaims assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool @@ -362,7 +362,7 @@ type claimsManager struct { func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment - manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(circuit)) + manager.claims = make([]*zeroCheckLazyClaims, len(circuit)) manager.memPool = o.pool manager.workers = o.workers manager.circuit = circuit @@ -371,7 +371,7 @@ func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (m if circuit[i].IsInput() { circuit[i].Gate.Degree = 1 } - manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ + manager.claims[i] = &zeroCheckLazyClaims{ wireI: i, evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), @@ -388,14 +388,14 @@ func (m *claimsManager) add(wire int, evaluationPoint []fr.Element, evaluation f claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) } -func (m *claimsManager) getLazyClaim(wire int) *eqTimesGateEvalSumcheckLazyClaims { +func (m *claimsManager) getLazyClaim(wire int) *zeroCheckLazyClaims { return m.claims[wire] } -func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { +func (m *claimsManager) getClaim(wireI int) *zeroCheckClaims { lazy := m.claims[wireI] wire := m.circuit[wireI] - res := &eqTimesGateEvalSumcheckClaims{ + res := &zeroCheckClaims{ wireI: wireI, evaluationPoints: lazy.evaluationPoints, claimedEvaluations: lazy.claimedEvaluations, @@ -490,7 +490,7 @@ func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { if c[i].NoProof() { // no proof, no challenge continue } - if c[i].NbClaims() > 1 { //combine the claims + if c[i].NbClaims() > 1 { //fold the claims size++ } size += logNbInstances // full run of sumcheck on logNbInstances variables diff --git a/internal/gkr/bn254/sumcheck.go b/internal/gkr/bn254/sumcheck.go index 957c7ef7f4..aaadc50d7a 100644 --- a/internal/gkr/bn254/sumcheck.go +++ b/internal/gkr/bn254/sumcheck.go @@ -20,7 +20,7 @@ import ( // sumcheckClaims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. // Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) type sumcheckClaims interface { - combine(a fr.Element) polynomial.Polynomial // combine into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. + fold(a fr.Element) polynomial.Polynomial // fold into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. next(fr.Element) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ varsNum() int // number of variables claimsNum() int // number of claims @@ -29,11 +29,11 @@ type sumcheckClaims interface { // sumcheckLazyClaims is the sumcheckClaims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. type sumcheckLazyClaims interface { - claimsNum() int // claimsNum = m - varsNum() int // varsNum = n - combinedSum(a fr.Element) fr.Element // combinedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - degree(i int) int // degree of the total claim in the i'th variable - verifyFinalEval(r []fr.Element, combinationCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error + claimsNum() int // claimsNum = m + varsNum() int // varsNum = n + foldedSum(a fr.Element) fr.Element // foldedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ + degree(i int) int // degree of the total claim in the i'th variable + verifyFinalEval(r []fr.Element, foldingCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error } // sumcheckProof of a multi-statement. @@ -95,16 +95,16 @@ func sumcheckProve(claims sumcheckClaims, transcriptSettings fiatshamir.Settings return proof, err } - var combinationCoeff fr.Element + var foldingCoeff fr.Element if claims.claimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { + if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { return proof, err } } varsNum := claims.varsNum() proof.partialSumPolys = make([]polynomial.Polynomial, varsNum) - proof.partialSumPolys[0] = claims.combine(combinationCoeff) + proof.partialSumPolys[0] = claims.fold(foldingCoeff) challenges := make([]fr.Element, varsNum) for j := 0; j+1 < varsNum; j++ { @@ -130,10 +130,10 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe return err } - var combinationCoeff fr.Element + var foldingCoeff fr.Element if claims.claimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { + if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { return err } } @@ -148,7 +148,7 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe } } gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() - gJR := claims.combinedSum(combinationCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) + gJR := claims.foldedSum(foldingCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) for j := range claims.varsNum() { if len(proof.partialSumPolys[j]) != claims.degree(j) { @@ -167,5 +167,5 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe gJR = gJCoeffs.Eval(&r[j]) } - return claims.verifyFinalEval(r, combinationCoeff, gJR, proof.finalEvalProof) + return claims.verifyFinalEval(r, foldingCoeff, gJR, proof.finalEvalProof) } diff --git a/internal/gkr/bn254/sumcheck_test.go b/internal/gkr/bn254/sumcheck_test.go index 5eddf9e4a8..5f68f7c039 100644 --- a/internal/gkr/bn254/sumcheck_test.go +++ b/internal/gkr/bn254/sumcheck_test.go @@ -113,7 +113,7 @@ func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { return []fr.Element{sum} } -func (c singleMultilinClaim) combine(fr.Element) polynomial.Polynomial { +func (c singleMultilinClaim) fold(fr.Element) polynomial.Polynomial { return sumForX1One(c.g) } @@ -127,7 +127,7 @@ type singleMultilinLazyClaim struct { claimedSum fr.Element } -func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, combinationCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error { +func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, _ fr.Element, purportedValue fr.Element, proof []fr.Element) error { val := c.g.Evaluate(r, nil) if val.Equal(&purportedValue) { return nil @@ -135,7 +135,7 @@ func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, combinationCoef return fmt.Errorf("mismatch") } -func (c singleMultilinLazyClaim) combinedSum(combinationCoeffs fr.Element) fr.Element { +func (c singleMultilinLazyClaim) foldedSum(_ fr.Element) fr.Element { return c.claimedSum } From 64b496fa60f9c9f57efcab3aff67b4b577112fe8 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 18 Feb 2026 15:21:42 -0600 Subject: [PATCH 200/251] refactor: name changes in the gadget package --- internal/gkr/bn254/gkr.go | 6 ++++-- internal/gkr/gkr.go | 33 +++++++++++++++++---------------- internal/gkr/sumcheck.go | 4 ++-- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 0529bab6e6..7fbc4e6c9c 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -33,7 +33,8 @@ type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) // zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). -// eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear +// extension of the values of w across all instances. // Its purpose is to batch the checking of multiple evaluations of the same wire. type zeroCheckLazyClaims struct { wireI int // the wire for which we are making the claim, with value w @@ -121,7 +122,8 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, foldingCoeff, purp } // zeroCheckClaims is a claim for sumcheck (prover side). -// eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear +// extension of the values of w across all instances. // Its purpose is to batch the proving of multiple evaluations of the same wire. type zeroCheckClaims struct { wireI int // the wire for which we are making the claim, with value w diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index 974c9da7ef..f42f2fe8b5 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -61,23 +61,24 @@ func (a WireAssignment) NbVars() int { type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) -// eqTimesGateEvalSumcheckLazyClaims is a lazy claim for sumcheck (verifier side). -// eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). +// zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear +// extension of the values of w across all instances. // Its purpose is to batch the checking of multiple evaluations of the same wire. -type eqTimesGateEvalSumcheckLazyClaims struct { +type zeroCheckLazyClaims struct { wireI int evaluationPoints [][]frontend.Variable claimedEvaluations []frontend.Variable manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() Wire { +func (e *zeroCheckLazyClaims) getWire() Wire { return e.manager.circuit[e.wireI] } // verifyFinalEval finalizes the verification of w. // The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is combinationCoeff ) +// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is foldingCoeff ) // Both purportedValue and the vector r have been randomized during the sumcheck protocol. // By taking the w term out of the sum we get the equivalent claim that // for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. @@ -87,12 +88,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() Wire { // The claims are communicated through the proof parameter. // The verifier checks here if the claimed evaluations of wᵢ(r) are consistent with // the main claim, by checking E w(wᵢ(r)...) = purportedValue. -func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(api frontend.API, r []frontend.Variable, combinationCoeff, purportedValue frontend.Variable, uniqueInputEvaluations []frontend.Variable) error { +func (e *zeroCheckLazyClaims) verifyFinalEval(api frontend.API, r []frontend.Variable, foldingCoeff, purportedValue frontend.Variable, uniqueInputEvaluations []frontend.Variable) error { // the eq terms ( E ) numClaims := len(e.evaluationPoints) evaluation := polynomial.EvalEq(api, e.evaluationPoints[numClaims-1], r) for i := numClaims - 2; i >= 0; i-- { - evaluation = api.Mul(evaluation, combinationCoeff) + evaluation = api.Mul(evaluation, foldingCoeff) eq := polynomial.EvalEq(api, e.evaluationPoints[i], r) evaluation = api.Add(evaluation, eq) } @@ -129,32 +130,32 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(api frontend.API, r return nil } -func (e *eqTimesGateEvalSumcheckLazyClaims) claimsNum() int { +func (e *zeroCheckLazyClaims) claimsNum() int { return len(e.evaluationPoints) } -func (e *eqTimesGateEvalSumcheckLazyClaims) varsNum() int { +func (e *zeroCheckLazyClaims) varsNum() int { return len(e.evaluationPoints[0]) } -func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(api frontend.API, a frontend.Variable) frontend.Variable { +func (e *zeroCheckLazyClaims) foldedSum(api frontend.API, a frontend.Variable) frontend.Variable { evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) return evalsAsPoly.Eval(api, a) } -func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { +func (e *zeroCheckLazyClaims) degree(int) int { return 1 + e.getWire().Gate.Degree } type claimsManager struct { - claims []*eqTimesGateEvalSumcheckLazyClaims + claims []*zeroCheckLazyClaims assignment WireAssignment circuit Circuit } func newClaimsManager(circuit Circuit, assignment WireAssignment) (claims claimsManager) { claims.assignment = assignment - claims.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(circuit)) + claims.claims = make([]*zeroCheckLazyClaims, len(circuit)) claims.circuit = circuit for i := range circuit { @@ -162,7 +163,7 @@ func newClaimsManager(circuit Circuit, assignment WireAssignment) (claims claims circuit[i].Gate.Degree = 1 circuit[i].Gate.Evaluate = gkrtypes.Identity } - claims.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ + claims.claims[i] = &zeroCheckLazyClaims{ wireI: i, evaluationPoints: make([][]frontend.Variable, 0, circuit[i].NbClaims()), claimedEvaluations: make(polynomial.Polynomial, circuit[i].NbClaims()), @@ -179,7 +180,7 @@ func (m *claimsManager) add(wire int, evaluationPoint []frontend.Variable, evalu claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) } -func (m *claimsManager) getLazyClaim(wire int) *eqTimesGateEvalSumcheckLazyClaims { +func (m *claimsManager) getLazyClaim(wire int) *zeroCheckLazyClaims { return m.claims[wire] } @@ -231,7 +232,7 @@ func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { if c[i].NoProof() { // no proof, no challenge continue } - if c[i].NbClaims() > 1 { //combine the claims + if c[i].NbClaims() > 1 { //fold the claims size++ } size += logNbInstances // full run of sumcheck on logNbInstances variables diff --git a/internal/gkr/sumcheck.go b/internal/gkr/sumcheck.go index 77e327ccb6..67e489459c 100644 --- a/internal/gkr/sumcheck.go +++ b/internal/gkr/sumcheck.go @@ -15,7 +15,7 @@ import ( type sumcheckLazyClaims interface { claimsNum() int // claimsNum = m varsNum() int // varsNum = n - combinedSum(api frontend.API, a frontend.Variable) frontend.Variable // combinedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ + foldedSum(api frontend.API, a frontend.Variable) frontend.Variable // foldedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ degree(i int) int // degree of the total claim in the i'th variable verifyFinalEval(api frontend.API, r []frontend.Variable, combinationCoeff, purportedValue frontend.Variable, proof []frontend.Variable) error } @@ -84,7 +84,7 @@ func verifySumcheck(api frontend.API, claims sumcheckLazyClaims, proof sumcheckP } gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() - gJR := claims.combinedSum(api, combinationCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) + gJR := claims.foldedSum(api, combinationCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) for j := 0; j < claims.varsNum(); j++ { partialSumPoly := proof.PartialSumPolys[j] //proof.PartialSumPolys(j) From 84467face70eafd4fde3075afa0b43eeb79ed29a Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 18 Feb 2026 15:35:41 -0600 Subject: [PATCH 201/251] build: generify --- .../backend/template/gkr/gkr.go.tmpl | 74 ++++++++++--------- .../backend/template/gkr/sumcheck.go.tmpl | 28 +++---- .../template/gkr/sumcheck.test.defs.go.tmpl | 6 +- internal/gkr/bls12-377/gkr.go | 74 ++++++++++--------- internal/gkr/bls12-377/sumcheck.go | 28 +++---- internal/gkr/bls12-377/sumcheck_test.go | 6 +- internal/gkr/bls12-381/gkr.go | 74 ++++++++++--------- internal/gkr/bls12-381/sumcheck.go | 28 +++---- internal/gkr/bls12-381/sumcheck_test.go | 6 +- internal/gkr/bn254/gkr.go | 2 +- internal/gkr/bn254/sumcheck.go | 2 +- internal/gkr/bw6-761/gkr.go | 74 ++++++++++--------- internal/gkr/bw6-761/sumcheck.go | 28 +++---- internal/gkr/bw6-761/sumcheck_test.go | 6 +- internal/gkr/gkr.go | 2 +- internal/gkr/small_rational/gkr.go | 74 ++++++++++--------- internal/gkr/small_rational/sumcheck.go | 28 +++---- .../sumcheck_test_vector_gen.go | 6 +- internal/gkr/sumcheck.go | 20 ++--- 19 files changed, 288 insertions(+), 278 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index e7231d9218..93e30c7294 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -25,41 +25,42 @@ type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) -// eqTimesGateEvalSumcheckLazyClaims is a lazy claim for sumcheck (verifier side). -// eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). +// zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear +// extension of the values of w across all instances. // Its purpose is to batch the checking of multiple evaluations of the same wire. -type eqTimesGateEvalSumcheckLazyClaims struct { +type zeroCheckLazyClaims struct { wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]{{ .ElementType }} // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []{{ .ElementType }} // yᵢ = w(xᵢ), allegedly manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() Wire { +func (e *zeroCheckLazyClaims) getWire() Wire { return e.manager.circuit[e.wireI] } -func (e *eqTimesGateEvalSumcheckLazyClaims) claimsNum() int { +func (e *zeroCheckLazyClaims) claimsNum() int { return len(e.evaluationPoints) } -func (e *eqTimesGateEvalSumcheckLazyClaims) varsNum() int { +func (e *zeroCheckLazyClaims) varsNum() int { return len(e.evaluationPoints[0]) } -// combinedSum returns ∑ᵢ aⁱ yᵢ -func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a {{ .ElementType }}) {{ .ElementType }} { +// foldedSum returns ∑ᵢ aⁱ yᵢ +func (e *zeroCheckLazyClaims) foldedSum(a {{ .ElementType }}) {{ .ElementType }} { evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) return evalsAsPoly.Eval(&a) } -func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { +func (e *zeroCheckLazyClaims) degree(int) int { return 1 + e.manager.circuit[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. // The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is combinationCoeff ) +// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is foldingCoeff ) // Both purportedValue and the vector r have been randomized during the sumcheck protocol. // By taking the w term out of the sum we get the equivalent claim that // for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. @@ -69,12 +70,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { // The claims are communicated through the proof parameter. // The verifier checks here if the claimed evaluations of wᵢ(r) are consistent with // the main claim, by checking E w(wᵢ(r)...) = purportedValue. -func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []{{ .ElementType }}, combinationCoeff, purportedValue {{ .ElementType }}, uniqueInputEvaluations []{{ .ElementType }}) error { +func (e *zeroCheckLazyClaims) verifyFinalEval(r []{{ .ElementType }}, foldingCoeff, purportedValue {{ .ElementType }}, uniqueInputEvaluations []{{ .ElementType }}) error { // the eq terms ( E ) numClaims := len(e.evaluationPoints) evaluation := polynomial.EvalEq(e.evaluationPoints[numClaims-1], r) for i := numClaims - 2; i >= 0; i-- { - evaluation.Mul(&evaluation, &combinationCoeff) + evaluation.Mul(&evaluation, &foldingCoeff) eq := polynomial.EvalEq(e.evaluationPoints[i], r) evaluation.Add(&evaluation, &eq) } @@ -113,10 +114,11 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []{{ .ElementType return errors.New("incompatible evaluations") } -// eqTimesGateEvalSumcheckClaims is a claim for sumcheck (prover side). -// eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). +// zeroCheckClaims is a claim for sumcheck (prover side). +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear +// extension of the values of w across all instances. // Its purpose is to batch the proving of multiple evaluations of the same wire. -type eqTimesGateEvalSumcheckClaims struct { +type zeroCheckClaims struct { wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]{{ .ElementType }} // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []{{ .ElementType }} // yᵢ = w(xᵢ) @@ -129,11 +131,11 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { +func (c *zeroCheckClaims) getWire() Wire { return c.manager.circuit[c.wireI] } -// combine the multiple claims into one claim using a random combination (combinationCoeff or c). +// fold the multiple claims into one claim using a random combination (foldingCoeff or c). // From the original multiple claims of w(xᵢ) = yᵢ, we get a single claim // ∑ᵢ,ₕ cⁱ eq(xᵢ, h) w(h) = ∑ᵢ cⁱ yᵢ, where h iterates over the hypercube (circuit instances) and // i iterates over the claims. @@ -141,8 +143,8 @@ func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { // Thus if we initially compute E := ∑ᵢ cⁱ eq(xᵢ, -), our claim will find the simpler form // ∑ᵢ cⁱ yᵢ = ∑ₕ w(h) E(h), where the sum-checked polynomial is of degree deg(g) + 1, // and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. -// The output of combine is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. -func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff {{ .ElementType }}) polynomial.Polynomial { +// The output of fold is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. +func (c *zeroCheckClaims) fold(foldingCoeff {{ .ElementType }}) polynomial.Polynomial { varsNum := c.varsNum() eqLength := 1 << varsNum claimsNum := c.claimsNum() @@ -154,7 +156,7 @@ c.eq.Eq(c.evaluationPoints[0]) // E := eq(x₀, -) newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) -aI := combinationCoeff +aI := foldingCoeff // E += cⁱ eq(xᵢ, -) for k := 1; k < claimsNum; k++ { @@ -163,7 +165,7 @@ newEq[0].Set(&aI) c.eqAcc(c.eq, newEq,c.evaluationPoints[k]) if k+1 < claimsNum { -aI.Mul(&aI, &combinationCoeff) +aI.Mul(&aI, &foldingCoeff) } } @@ -175,7 +177,7 @@ return c.computeGJ() // eqAcc sets m to an eq table at q and then adds it to e. // m <- eq(q, -). // e <- e + m -func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []{{ .ElementType }}) { +func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []{{ .ElementType }}) { n := len(q) //At the end of each iteration, m(h₁, ..., hₙ) = eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) @@ -215,7 +217,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []{{ . // computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). // the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. -func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { +func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) @@ -231,7 +233,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { sumSize := len(c.eq) / 2; // the range of h, over which we sum - // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called + // Perf-TODO: Collate once at claim "folding" time and not again. then, even folding can be done in one operation every time "next" is called gJ := make([]{{ .ElementType }}, degGJ) var mu sync.Mutex @@ -296,7 +298,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // next first folds the input and E polynomials at the given verifier challenge then computes the new gⱼ. // Thus, j <- j+1 and rⱼ = challenge. -func (c *eqTimesGateEvalSumcheckClaims) next(challenge {{ .ElementType }}) polynomial.Polynomial { +func (c *zeroCheckClaims) next(challenge {{ .ElementType }}) polynomial.Polynomial { const minBlockSize = 512 n := len(c.eq) / 2 if n < minBlockSize { @@ -319,16 +321,16 @@ func (c *eqTimesGateEvalSumcheckClaims) next(challenge {{ .ElementType }}) polyn return c.computeGJ() } -func (c *eqTimesGateEvalSumcheckClaims) varsNum() int { +func (c *zeroCheckClaims) varsNum() int { return len(c.evaluationPoints[0]) } -func (c *eqTimesGateEvalSumcheckClaims) claimsNum() int { +func (c *zeroCheckClaims) claimsNum() int { return len(c.claimedEvaluations) } // proveFinalEval provides the values wᵢ(r₁, ..., rₙ) -func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []{{ .ElementType }}) []{{ .ElementType }} { +func (c *zeroCheckClaims) proveFinalEval(r []{{ .ElementType }}) []{{ .ElementType }} { //defer the proof, return list of claims injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. @@ -347,7 +349,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []{{ .ElementType }}) [ } type claimsManager struct { - claims []*eqTimesGateEvalSumcheckLazyClaims + claims []*zeroCheckLazyClaims assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool @@ -356,7 +358,7 @@ type claimsManager struct { func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment - manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(circuit)) + manager.claims = make([]*zeroCheckLazyClaims, len(circuit)) manager.memPool = o.pool manager.workers = o.workers manager.circuit = circuit @@ -365,7 +367,7 @@ func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (m if circuit[i].IsInput() { circuit[i].Gate.Degree = 1 } - manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ + manager.claims[i] = &zeroCheckLazyClaims{ wireI: i, evaluationPoints: make([][]{{ .ElementType }}, 0, circuit[i].NbClaims()), claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), @@ -382,14 +384,14 @@ func (m *claimsManager) add(wire int, evaluationPoint []{{ .ElementType }}, eval claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) } -func (m *claimsManager) getLazyClaim(wire int) *eqTimesGateEvalSumcheckLazyClaims { +func (m *claimsManager) getLazyClaim(wire int) *zeroCheckLazyClaims { return m.claims[wire] } -func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { +func (m *claimsManager) getClaim(wireI int) *zeroCheckClaims { lazy := m.claims[wireI] wire := m.circuit[wireI] - res := &eqTimesGateEvalSumcheckClaims{ + res := &zeroCheckClaims{ wireI: wireI, evaluationPoints: lazy.evaluationPoints, claimedEvaluations: lazy.claimedEvaluations, @@ -485,7 +487,7 @@ func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { if c[i].NoProof() { // no proof, no challenge continue } - if c[i].NbClaims() > 1 { //combine the claims + if c[i].NbClaims() > 1 { //fold the claims size++ } size += logNbInstances // full run of sumcheck on logNbInstances variables @@ -511,7 +513,7 @@ func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { wirePrefix := prefix + "w" + nums[i] + "." if c[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "comb" + challenges[j] = wirePrefix + "fold" j++ } diff --git a/internal/generator/backend/template/gkr/sumcheck.go.tmpl b/internal/generator/backend/template/gkr/sumcheck.go.tmpl index 7a351fc284..41ff58b325 100644 --- a/internal/generator/backend/template/gkr/sumcheck.go.tmpl +++ b/internal/generator/backend/template/gkr/sumcheck.go.tmpl @@ -12,7 +12,7 @@ import ( // sumcheckClaims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. // Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) type sumcheckClaims interface { - combine(a {{.ElementType}}) polynomial.Polynomial // combine into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. + fold(a {{.ElementType}}) polynomial.Polynomial // fold into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. next({{.ElementType}}) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ varsNum() int // number of variables claimsNum() int // number of claims @@ -21,11 +21,11 @@ type sumcheckClaims interface { // sumcheckLazyClaims is the sumcheckClaims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. type sumcheckLazyClaims interface { - claimsNum() int // claimsNum = m - varsNum() int // varsNum = n - combinedSum(a {{.ElementType}}) {{.ElementType}} // combinedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - degree(i int) int // degree of the total claim in the i'th variable - verifyFinalEval(r []{{.ElementType}}, combinationCoeff {{.ElementType}}, purportedValue {{.ElementType}}, proof []{{.ElementType}}) error + claimsNum() int // claimsNum = m + varsNum() int // varsNum = n + foldedSum(a {{.ElementType}}) {{.ElementType}} // foldedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ + degree(i int) int // degree of the total claim in the i'th variable + verifyFinalEval(r []{{.ElementType}}, foldingCoeff {{.ElementType}}, purportedValue {{.ElementType}}, proof []{{.ElementType}}) error } // sumcheckProof of a multi-statement. @@ -41,7 +41,7 @@ func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) } challengeNames = make([]string, numChallenges) if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "comb" + challengeNames[0] = settings.Prefix + "fold" } prefix := settings.Prefix + "pSP." for i := 0; i < varsNum; i++ { @@ -87,16 +87,16 @@ func sumcheckProve(claims sumcheckClaims, transcriptSettings fiatshamir.Settings return proof, err } - var combinationCoeff {{.ElementType}} + var foldingCoeff {{.ElementType}} if claims.claimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []{{.ElementType}}{}, &remainingChallengeNames); err != nil { + if foldingCoeff, err = next(transcript, []{{.ElementType}}{}, &remainingChallengeNames); err != nil { return proof, err } } varsNum := claims.varsNum() proof.partialSumPolys = make([]polynomial.Polynomial, varsNum) - proof.partialSumPolys[0] = claims.combine(combinationCoeff) + proof.partialSumPolys[0] = claims.fold(foldingCoeff) challenges := make([]{{.ElementType}}, varsNum) for j := 0; j+1 < varsNum; j++ { @@ -122,10 +122,10 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe return err } - var combinationCoeff {{.ElementType}} + var foldingCoeff {{.ElementType}} if claims.claimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []{{.ElementType}}{}, &remainingChallengeNames); err != nil { + if foldingCoeff, err = next(transcript, []{{.ElementType}}{}, &remainingChallengeNames); err != nil { return err } } @@ -140,7 +140,7 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe } } gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() - gJR := claims.combinedSum(combinationCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) + gJR := claims.foldedSum(foldingCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) for j := range claims.varsNum() { if len(proof.partialSumPolys[j]) != claims.degree(j) { @@ -159,5 +159,5 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe gJR = gJCoeffs.Eval(&r[j]) } - return claims.verifyFinalEval(r, combinationCoeff, gJR, proof.finalEvalProof) + return claims.verifyFinalEval(r, foldingCoeff, gJR, proof.finalEvalProof) } diff --git a/internal/generator/backend/template/gkr/sumcheck.test.defs.go.tmpl b/internal/generator/backend/template/gkr/sumcheck.test.defs.go.tmpl index 227d0c7052..472898669c 100644 --- a/internal/generator/backend/template/gkr/sumcheck.test.defs.go.tmpl +++ b/internal/generator/backend/template/gkr/sumcheck.test.defs.go.tmpl @@ -24,7 +24,7 @@ func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { return []{{.ElementType}}{sum} } -func (c singleMultilinClaim) combine({{.ElementType}}) polynomial.Polynomial { +func (c singleMultilinClaim) fold({{.ElementType}}) polynomial.Polynomial { return sumForX1One(c.g) } @@ -38,7 +38,7 @@ type singleMultilinLazyClaim struct { claimedSum {{.ElementType}} } -func (c singleMultilinLazyClaim) verifyFinalEval(r []{{.ElementType}}, combinationCoeff {{.ElementType}}, purportedValue {{.ElementType}}, proof []{{.ElementType}}) error { +func (c singleMultilinLazyClaim) verifyFinalEval(r []{{.ElementType}}, _ {{.ElementType}}, purportedValue {{.ElementType}}, proof []{{.ElementType}}) error { val := c.g.Evaluate(r, nil) if val.Equal(&purportedValue) { return nil @@ -46,7 +46,7 @@ func (c singleMultilinLazyClaim) verifyFinalEval(r []{{.ElementType}}, combinati return fmt.Errorf("mismatch") } -func (c singleMultilinLazyClaim) combinedSum(combinationCoeffs {{.ElementType}}) {{.ElementType}} { +func (c singleMultilinLazyClaim) foldedSum(_ {{.ElementType}}) {{.ElementType}} { return c.claimedSum } diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 9c120fb20d..0753606376 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -32,41 +32,42 @@ type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) -// eqTimesGateEvalSumcheckLazyClaims is a lazy claim for sumcheck (verifier side). -// eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). +// zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear +// extension of the values of w across all instances. // Its purpose is to batch the checking of multiple evaluations of the same wire. -type eqTimesGateEvalSumcheckLazyClaims struct { +type zeroCheckLazyClaims struct { wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() Wire { +func (e *zeroCheckLazyClaims) getWire() Wire { return e.manager.circuit[e.wireI] } -func (e *eqTimesGateEvalSumcheckLazyClaims) claimsNum() int { +func (e *zeroCheckLazyClaims) claimsNum() int { return len(e.evaluationPoints) } -func (e *eqTimesGateEvalSumcheckLazyClaims) varsNum() int { +func (e *zeroCheckLazyClaims) varsNum() int { return len(e.evaluationPoints[0]) } -// combinedSum returns ∑ᵢ aⁱ yᵢ -func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a fr.Element) fr.Element { +// foldedSum returns ∑ᵢ aⁱ yᵢ +func (e *zeroCheckLazyClaims) foldedSum(a fr.Element) fr.Element { evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) return evalsAsPoly.Eval(&a) } -func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { +func (e *zeroCheckLazyClaims) degree(int) int { return 1 + e.manager.circuit[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. // The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is combinationCoeff ) +// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is foldingCoeff ) // Both purportedValue and the vector r have been randomized during the sumcheck protocol. // By taking the w term out of the sum we get the equivalent claim that // for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. @@ -76,12 +77,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { // The claims are communicated through the proof parameter. // The verifier checks here if the claimed evaluations of wᵢ(r) are consistent with // the main claim, by checking E w(wᵢ(r)...) = purportedValue. -func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, combinationCoeff, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { +func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, foldingCoeff, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { // the eq terms ( E ) numClaims := len(e.evaluationPoints) evaluation := polynomial.EvalEq(e.evaluationPoints[numClaims-1], r) for i := numClaims - 2; i >= 0; i-- { - evaluation.Mul(&evaluation, &combinationCoeff) + evaluation.Mul(&evaluation, &foldingCoeff) eq := polynomial.EvalEq(e.evaluationPoints[i], r) evaluation.Add(&evaluation, &eq) } @@ -120,10 +121,11 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb return errors.New("incompatible evaluations") } -// eqTimesGateEvalSumcheckClaims is a claim for sumcheck (prover side). -// eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). +// zeroCheckClaims is a claim for sumcheck (prover side). +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear +// extension of the values of w across all instances. // Its purpose is to batch the proving of multiple evaluations of the same wire. -type eqTimesGateEvalSumcheckClaims struct { +type zeroCheckClaims struct { wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ) @@ -136,11 +138,11 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { +func (c *zeroCheckClaims) getWire() Wire { return c.manager.circuit[c.wireI] } -// combine the multiple claims into one claim using a random combination (combinationCoeff or c). +// fold the multiple claims into one claim using a random combination (foldingCoeff or c). // From the original multiple claims of w(xᵢ) = yᵢ, we get a single claim // ∑ᵢ,ₕ cⁱ eq(xᵢ, h) w(h) = ∑ᵢ cⁱ yᵢ, where h iterates over the hypercube (circuit instances) and // i iterates over the claims. @@ -148,8 +150,8 @@ func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { // Thus if we initially compute E := ∑ᵢ cⁱ eq(xᵢ, -), our claim will find the simpler form // ∑ᵢ cⁱ yᵢ = ∑ₕ w(h) E(h), where the sum-checked polynomial is of degree deg(g) + 1, // and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. -// The output of combine is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. -func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) polynomial.Polynomial { +// The output of fold is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. +func (c *zeroCheckClaims) fold(foldingCoeff fr.Element) polynomial.Polynomial { varsNum := c.varsNum() eqLength := 1 << varsNum claimsNum := c.claimsNum() @@ -161,7 +163,7 @@ func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) pol // E := eq(x₀, -) newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) - aI := combinationCoeff + aI := foldingCoeff // E += cⁱ eq(xᵢ, -) for k := 1; k < claimsNum; k++ { @@ -170,7 +172,7 @@ func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) pol c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) if k+1 < claimsNum { - aI.Mul(&aI, &combinationCoeff) + aI.Mul(&aI, &foldingCoeff) } } @@ -182,7 +184,7 @@ func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) pol // eqAcc sets m to an eq table at q and then adds it to e. // m <- eq(q, -). // e <- e + m -func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { +func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { n := len(q) //At the end of each iteration, m(h₁, ..., hₙ) = eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) @@ -221,7 +223,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E // computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). // the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. -func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { +func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) @@ -237,7 +239,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { sumSize := len(c.eq) / 2 // the range of h, over which we sum - // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called + // Perf-TODO: Collate once at claim "folding" time and not again. then, even folding can be done in one operation every time "next" is called gJ := make([]fr.Element, degGJ) var mu sync.Mutex @@ -302,7 +304,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // next first folds the input and E polynomials at the given verifier challenge then computes the new gⱼ. // Thus, j <- j+1 and rⱼ = challenge. -func (c *eqTimesGateEvalSumcheckClaims) next(challenge fr.Element) polynomial.Polynomial { +func (c *zeroCheckClaims) next(challenge fr.Element) polynomial.Polynomial { const minBlockSize = 512 n := len(c.eq) / 2 if n < minBlockSize { @@ -325,16 +327,16 @@ func (c *eqTimesGateEvalSumcheckClaims) next(challenge fr.Element) polynomial.Po return c.computeGJ() } -func (c *eqTimesGateEvalSumcheckClaims) varsNum() int { +func (c *zeroCheckClaims) varsNum() int { return len(c.evaluationPoints[0]) } -func (c *eqTimesGateEvalSumcheckClaims) claimsNum() int { +func (c *zeroCheckClaims) claimsNum() int { return len(c.claimedEvaluations) } // proveFinalEval provides the values wᵢ(r₁, ..., rₙ) -func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Element { +func (c *zeroCheckClaims) proveFinalEval(r []fr.Element) []fr.Element { //defer the proof, return list of claims injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. @@ -353,7 +355,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } type claimsManager struct { - claims []*eqTimesGateEvalSumcheckLazyClaims + claims []*zeroCheckLazyClaims assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool @@ -362,7 +364,7 @@ type claimsManager struct { func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment - manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(circuit)) + manager.claims = make([]*zeroCheckLazyClaims, len(circuit)) manager.memPool = o.pool manager.workers = o.workers manager.circuit = circuit @@ -371,7 +373,7 @@ func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (m if circuit[i].IsInput() { circuit[i].Gate.Degree = 1 } - manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ + manager.claims[i] = &zeroCheckLazyClaims{ wireI: i, evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), @@ -388,14 +390,14 @@ func (m *claimsManager) add(wire int, evaluationPoint []fr.Element, evaluation f claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) } -func (m *claimsManager) getLazyClaim(wire int) *eqTimesGateEvalSumcheckLazyClaims { +func (m *claimsManager) getLazyClaim(wire int) *zeroCheckLazyClaims { return m.claims[wire] } -func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { +func (m *claimsManager) getClaim(wireI int) *zeroCheckClaims { lazy := m.claims[wireI] wire := m.circuit[wireI] - res := &eqTimesGateEvalSumcheckClaims{ + res := &zeroCheckClaims{ wireI: wireI, evaluationPoints: lazy.evaluationPoints, claimedEvaluations: lazy.claimedEvaluations, @@ -490,7 +492,7 @@ func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { if c[i].NoProof() { // no proof, no challenge continue } - if c[i].NbClaims() > 1 { //combine the claims + if c[i].NbClaims() > 1 { //fold the claims size++ } size += logNbInstances // full run of sumcheck on logNbInstances variables @@ -516,7 +518,7 @@ func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { wirePrefix := prefix + "w" + nums[i] + "." if c[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "comb" + challenges[j] = wirePrefix + "fold" j++ } diff --git a/internal/gkr/bls12-377/sumcheck.go b/internal/gkr/bls12-377/sumcheck.go index 3df2db665d..b05e319932 100644 --- a/internal/gkr/bls12-377/sumcheck.go +++ b/internal/gkr/bls12-377/sumcheck.go @@ -20,7 +20,7 @@ import ( // sumcheckClaims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. // Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) type sumcheckClaims interface { - combine(a fr.Element) polynomial.Polynomial // combine into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. + fold(a fr.Element) polynomial.Polynomial // fold into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. next(fr.Element) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ varsNum() int // number of variables claimsNum() int // number of claims @@ -29,11 +29,11 @@ type sumcheckClaims interface { // sumcheckLazyClaims is the sumcheckClaims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. type sumcheckLazyClaims interface { - claimsNum() int // claimsNum = m - varsNum() int // varsNum = n - combinedSum(a fr.Element) fr.Element // combinedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - degree(i int) int // degree of the total claim in the i'th variable - verifyFinalEval(r []fr.Element, combinationCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error + claimsNum() int // claimsNum = m + varsNum() int // varsNum = n + foldedSum(a fr.Element) fr.Element // foldedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ + degree(i int) int // degree of the total claim in the i'th variable + verifyFinalEval(r []fr.Element, foldingCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error } // sumcheckProof of a multi-statement. @@ -49,7 +49,7 @@ func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) } challengeNames = make([]string, numChallenges) if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "comb" + challengeNames[0] = settings.Prefix + "fold" } prefix := settings.Prefix + "pSP." for i := 0; i < varsNum; i++ { @@ -95,16 +95,16 @@ func sumcheckProve(claims sumcheckClaims, transcriptSettings fiatshamir.Settings return proof, err } - var combinationCoeff fr.Element + var foldingCoeff fr.Element if claims.claimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { + if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { return proof, err } } varsNum := claims.varsNum() proof.partialSumPolys = make([]polynomial.Polynomial, varsNum) - proof.partialSumPolys[0] = claims.combine(combinationCoeff) + proof.partialSumPolys[0] = claims.fold(foldingCoeff) challenges := make([]fr.Element, varsNum) for j := 0; j+1 < varsNum; j++ { @@ -130,10 +130,10 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe return err } - var combinationCoeff fr.Element + var foldingCoeff fr.Element if claims.claimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { + if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { return err } } @@ -148,7 +148,7 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe } } gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() - gJR := claims.combinedSum(combinationCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) + gJR := claims.foldedSum(foldingCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) for j := range claims.varsNum() { if len(proof.partialSumPolys[j]) != claims.degree(j) { @@ -167,5 +167,5 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe gJR = gJCoeffs.Eval(&r[j]) } - return claims.verifyFinalEval(r, combinationCoeff, gJR, proof.finalEvalProof) + return claims.verifyFinalEval(r, foldingCoeff, gJR, proof.finalEvalProof) } diff --git a/internal/gkr/bls12-377/sumcheck_test.go b/internal/gkr/bls12-377/sumcheck_test.go index d6f1f37a2b..f4b8d524b3 100644 --- a/internal/gkr/bls12-377/sumcheck_test.go +++ b/internal/gkr/bls12-377/sumcheck_test.go @@ -113,7 +113,7 @@ func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { return []fr.Element{sum} } -func (c singleMultilinClaim) combine(fr.Element) polynomial.Polynomial { +func (c singleMultilinClaim) fold(fr.Element) polynomial.Polynomial { return sumForX1One(c.g) } @@ -127,7 +127,7 @@ type singleMultilinLazyClaim struct { claimedSum fr.Element } -func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, combinationCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error { +func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, _ fr.Element, purportedValue fr.Element, proof []fr.Element) error { val := c.g.Evaluate(r, nil) if val.Equal(&purportedValue) { return nil @@ -135,7 +135,7 @@ func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, combinationCoef return fmt.Errorf("mismatch") } -func (c singleMultilinLazyClaim) combinedSum(combinationCoeffs fr.Element) fr.Element { +func (c singleMultilinLazyClaim) foldedSum(_ fr.Element) fr.Element { return c.claimedSum } diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index 3b49c98e75..a55a1335a0 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -32,41 +32,42 @@ type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) -// eqTimesGateEvalSumcheckLazyClaims is a lazy claim for sumcheck (verifier side). -// eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). +// zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear +// extension of the values of w across all instances. // Its purpose is to batch the checking of multiple evaluations of the same wire. -type eqTimesGateEvalSumcheckLazyClaims struct { +type zeroCheckLazyClaims struct { wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() Wire { +func (e *zeroCheckLazyClaims) getWire() Wire { return e.manager.circuit[e.wireI] } -func (e *eqTimesGateEvalSumcheckLazyClaims) claimsNum() int { +func (e *zeroCheckLazyClaims) claimsNum() int { return len(e.evaluationPoints) } -func (e *eqTimesGateEvalSumcheckLazyClaims) varsNum() int { +func (e *zeroCheckLazyClaims) varsNum() int { return len(e.evaluationPoints[0]) } -// combinedSum returns ∑ᵢ aⁱ yᵢ -func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a fr.Element) fr.Element { +// foldedSum returns ∑ᵢ aⁱ yᵢ +func (e *zeroCheckLazyClaims) foldedSum(a fr.Element) fr.Element { evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) return evalsAsPoly.Eval(&a) } -func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { +func (e *zeroCheckLazyClaims) degree(int) int { return 1 + e.manager.circuit[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. // The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is combinationCoeff ) +// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is foldingCoeff ) // Both purportedValue and the vector r have been randomized during the sumcheck protocol. // By taking the w term out of the sum we get the equivalent claim that // for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. @@ -76,12 +77,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { // The claims are communicated through the proof parameter. // The verifier checks here if the claimed evaluations of wᵢ(r) are consistent with // the main claim, by checking E w(wᵢ(r)...) = purportedValue. -func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, combinationCoeff, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { +func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, foldingCoeff, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { // the eq terms ( E ) numClaims := len(e.evaluationPoints) evaluation := polynomial.EvalEq(e.evaluationPoints[numClaims-1], r) for i := numClaims - 2; i >= 0; i-- { - evaluation.Mul(&evaluation, &combinationCoeff) + evaluation.Mul(&evaluation, &foldingCoeff) eq := polynomial.EvalEq(e.evaluationPoints[i], r) evaluation.Add(&evaluation, &eq) } @@ -120,10 +121,11 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb return errors.New("incompatible evaluations") } -// eqTimesGateEvalSumcheckClaims is a claim for sumcheck (prover side). -// eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). +// zeroCheckClaims is a claim for sumcheck (prover side). +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear +// extension of the values of w across all instances. // Its purpose is to batch the proving of multiple evaluations of the same wire. -type eqTimesGateEvalSumcheckClaims struct { +type zeroCheckClaims struct { wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ) @@ -136,11 +138,11 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { +func (c *zeroCheckClaims) getWire() Wire { return c.manager.circuit[c.wireI] } -// combine the multiple claims into one claim using a random combination (combinationCoeff or c). +// fold the multiple claims into one claim using a random combination (foldingCoeff or c). // From the original multiple claims of w(xᵢ) = yᵢ, we get a single claim // ∑ᵢ,ₕ cⁱ eq(xᵢ, h) w(h) = ∑ᵢ cⁱ yᵢ, where h iterates over the hypercube (circuit instances) and // i iterates over the claims. @@ -148,8 +150,8 @@ func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { // Thus if we initially compute E := ∑ᵢ cⁱ eq(xᵢ, -), our claim will find the simpler form // ∑ᵢ cⁱ yᵢ = ∑ₕ w(h) E(h), where the sum-checked polynomial is of degree deg(g) + 1, // and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. -// The output of combine is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. -func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) polynomial.Polynomial { +// The output of fold is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. +func (c *zeroCheckClaims) fold(foldingCoeff fr.Element) polynomial.Polynomial { varsNum := c.varsNum() eqLength := 1 << varsNum claimsNum := c.claimsNum() @@ -161,7 +163,7 @@ func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) pol // E := eq(x₀, -) newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) - aI := combinationCoeff + aI := foldingCoeff // E += cⁱ eq(xᵢ, -) for k := 1; k < claimsNum; k++ { @@ -170,7 +172,7 @@ func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) pol c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) if k+1 < claimsNum { - aI.Mul(&aI, &combinationCoeff) + aI.Mul(&aI, &foldingCoeff) } } @@ -182,7 +184,7 @@ func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) pol // eqAcc sets m to an eq table at q and then adds it to e. // m <- eq(q, -). // e <- e + m -func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { +func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { n := len(q) //At the end of each iteration, m(h₁, ..., hₙ) = eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) @@ -221,7 +223,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E // computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). // the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. -func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { +func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) @@ -237,7 +239,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { sumSize := len(c.eq) / 2 // the range of h, over which we sum - // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called + // Perf-TODO: Collate once at claim "folding" time and not again. then, even folding can be done in one operation every time "next" is called gJ := make([]fr.Element, degGJ) var mu sync.Mutex @@ -302,7 +304,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // next first folds the input and E polynomials at the given verifier challenge then computes the new gⱼ. // Thus, j <- j+1 and rⱼ = challenge. -func (c *eqTimesGateEvalSumcheckClaims) next(challenge fr.Element) polynomial.Polynomial { +func (c *zeroCheckClaims) next(challenge fr.Element) polynomial.Polynomial { const minBlockSize = 512 n := len(c.eq) / 2 if n < minBlockSize { @@ -325,16 +327,16 @@ func (c *eqTimesGateEvalSumcheckClaims) next(challenge fr.Element) polynomial.Po return c.computeGJ() } -func (c *eqTimesGateEvalSumcheckClaims) varsNum() int { +func (c *zeroCheckClaims) varsNum() int { return len(c.evaluationPoints[0]) } -func (c *eqTimesGateEvalSumcheckClaims) claimsNum() int { +func (c *zeroCheckClaims) claimsNum() int { return len(c.claimedEvaluations) } // proveFinalEval provides the values wᵢ(r₁, ..., rₙ) -func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Element { +func (c *zeroCheckClaims) proveFinalEval(r []fr.Element) []fr.Element { //defer the proof, return list of claims injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. @@ -353,7 +355,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } type claimsManager struct { - claims []*eqTimesGateEvalSumcheckLazyClaims + claims []*zeroCheckLazyClaims assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool @@ -362,7 +364,7 @@ type claimsManager struct { func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment - manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(circuit)) + manager.claims = make([]*zeroCheckLazyClaims, len(circuit)) manager.memPool = o.pool manager.workers = o.workers manager.circuit = circuit @@ -371,7 +373,7 @@ func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (m if circuit[i].IsInput() { circuit[i].Gate.Degree = 1 } - manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ + manager.claims[i] = &zeroCheckLazyClaims{ wireI: i, evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), @@ -388,14 +390,14 @@ func (m *claimsManager) add(wire int, evaluationPoint []fr.Element, evaluation f claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) } -func (m *claimsManager) getLazyClaim(wire int) *eqTimesGateEvalSumcheckLazyClaims { +func (m *claimsManager) getLazyClaim(wire int) *zeroCheckLazyClaims { return m.claims[wire] } -func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { +func (m *claimsManager) getClaim(wireI int) *zeroCheckClaims { lazy := m.claims[wireI] wire := m.circuit[wireI] - res := &eqTimesGateEvalSumcheckClaims{ + res := &zeroCheckClaims{ wireI: wireI, evaluationPoints: lazy.evaluationPoints, claimedEvaluations: lazy.claimedEvaluations, @@ -490,7 +492,7 @@ func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { if c[i].NoProof() { // no proof, no challenge continue } - if c[i].NbClaims() > 1 { //combine the claims + if c[i].NbClaims() > 1 { //fold the claims size++ } size += logNbInstances // full run of sumcheck on logNbInstances variables @@ -516,7 +518,7 @@ func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { wirePrefix := prefix + "w" + nums[i] + "." if c[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "comb" + challenges[j] = wirePrefix + "fold" j++ } diff --git a/internal/gkr/bls12-381/sumcheck.go b/internal/gkr/bls12-381/sumcheck.go index 2fa45ed33d..4ff812a8ce 100644 --- a/internal/gkr/bls12-381/sumcheck.go +++ b/internal/gkr/bls12-381/sumcheck.go @@ -20,7 +20,7 @@ import ( // sumcheckClaims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. // Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) type sumcheckClaims interface { - combine(a fr.Element) polynomial.Polynomial // combine into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. + fold(a fr.Element) polynomial.Polynomial // fold into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. next(fr.Element) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ varsNum() int // number of variables claimsNum() int // number of claims @@ -29,11 +29,11 @@ type sumcheckClaims interface { // sumcheckLazyClaims is the sumcheckClaims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. type sumcheckLazyClaims interface { - claimsNum() int // claimsNum = m - varsNum() int // varsNum = n - combinedSum(a fr.Element) fr.Element // combinedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - degree(i int) int // degree of the total claim in the i'th variable - verifyFinalEval(r []fr.Element, combinationCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error + claimsNum() int // claimsNum = m + varsNum() int // varsNum = n + foldedSum(a fr.Element) fr.Element // foldedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ + degree(i int) int // degree of the total claim in the i'th variable + verifyFinalEval(r []fr.Element, foldingCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error } // sumcheckProof of a multi-statement. @@ -49,7 +49,7 @@ func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) } challengeNames = make([]string, numChallenges) if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "comb" + challengeNames[0] = settings.Prefix + "fold" } prefix := settings.Prefix + "pSP." for i := 0; i < varsNum; i++ { @@ -95,16 +95,16 @@ func sumcheckProve(claims sumcheckClaims, transcriptSettings fiatshamir.Settings return proof, err } - var combinationCoeff fr.Element + var foldingCoeff fr.Element if claims.claimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { + if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { return proof, err } } varsNum := claims.varsNum() proof.partialSumPolys = make([]polynomial.Polynomial, varsNum) - proof.partialSumPolys[0] = claims.combine(combinationCoeff) + proof.partialSumPolys[0] = claims.fold(foldingCoeff) challenges := make([]fr.Element, varsNum) for j := 0; j+1 < varsNum; j++ { @@ -130,10 +130,10 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe return err } - var combinationCoeff fr.Element + var foldingCoeff fr.Element if claims.claimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { + if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { return err } } @@ -148,7 +148,7 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe } } gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() - gJR := claims.combinedSum(combinationCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) + gJR := claims.foldedSum(foldingCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) for j := range claims.varsNum() { if len(proof.partialSumPolys[j]) != claims.degree(j) { @@ -167,5 +167,5 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe gJR = gJCoeffs.Eval(&r[j]) } - return claims.verifyFinalEval(r, combinationCoeff, gJR, proof.finalEvalProof) + return claims.verifyFinalEval(r, foldingCoeff, gJR, proof.finalEvalProof) } diff --git a/internal/gkr/bls12-381/sumcheck_test.go b/internal/gkr/bls12-381/sumcheck_test.go index d31d35e0c8..70574588a9 100644 --- a/internal/gkr/bls12-381/sumcheck_test.go +++ b/internal/gkr/bls12-381/sumcheck_test.go @@ -113,7 +113,7 @@ func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { return []fr.Element{sum} } -func (c singleMultilinClaim) combine(fr.Element) polynomial.Polynomial { +func (c singleMultilinClaim) fold(fr.Element) polynomial.Polynomial { return sumForX1One(c.g) } @@ -127,7 +127,7 @@ type singleMultilinLazyClaim struct { claimedSum fr.Element } -func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, combinationCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error { +func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, _ fr.Element, purportedValue fr.Element, proof []fr.Element) error { val := c.g.Evaluate(r, nil) if val.Equal(&purportedValue) { return nil @@ -135,7 +135,7 @@ func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, combinationCoef return fmt.Errorf("mismatch") } -func (c singleMultilinLazyClaim) combinedSum(combinationCoeffs fr.Element) fr.Element { +func (c singleMultilinLazyClaim) foldedSum(_ fr.Element) fr.Element { return c.claimedSum } diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 7fbc4e6c9c..41bcb4c673 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -518,7 +518,7 @@ func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { wirePrefix := prefix + "w" + nums[i] + "." if c[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "comb" + challenges[j] = wirePrefix + "fold" j++ } diff --git a/internal/gkr/bn254/sumcheck.go b/internal/gkr/bn254/sumcheck.go index aaadc50d7a..4c9dc2ca3c 100644 --- a/internal/gkr/bn254/sumcheck.go +++ b/internal/gkr/bn254/sumcheck.go @@ -49,7 +49,7 @@ func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) } challengeNames = make([]string, numChallenges) if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "comb" + challengeNames[0] = settings.Prefix + "fold" } prefix := settings.Prefix + "pSP." for i := 0; i < varsNum; i++ { diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 906cda398d..80a4b1313f 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -32,41 +32,42 @@ type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) -// eqTimesGateEvalSumcheckLazyClaims is a lazy claim for sumcheck (verifier side). -// eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). +// zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear +// extension of the values of w across all instances. // Its purpose is to batch the checking of multiple evaluations of the same wire. -type eqTimesGateEvalSumcheckLazyClaims struct { +type zeroCheckLazyClaims struct { wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() Wire { +func (e *zeroCheckLazyClaims) getWire() Wire { return e.manager.circuit[e.wireI] } -func (e *eqTimesGateEvalSumcheckLazyClaims) claimsNum() int { +func (e *zeroCheckLazyClaims) claimsNum() int { return len(e.evaluationPoints) } -func (e *eqTimesGateEvalSumcheckLazyClaims) varsNum() int { +func (e *zeroCheckLazyClaims) varsNum() int { return len(e.evaluationPoints[0]) } -// combinedSum returns ∑ᵢ aⁱ yᵢ -func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a fr.Element) fr.Element { +// foldedSum returns ∑ᵢ aⁱ yᵢ +func (e *zeroCheckLazyClaims) foldedSum(a fr.Element) fr.Element { evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) return evalsAsPoly.Eval(&a) } -func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { +func (e *zeroCheckLazyClaims) degree(int) int { return 1 + e.manager.circuit[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. // The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is combinationCoeff ) +// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is foldingCoeff ) // Both purportedValue and the vector r have been randomized during the sumcheck protocol. // By taking the w term out of the sum we get the equivalent claim that // for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. @@ -76,12 +77,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { // The claims are communicated through the proof parameter. // The verifier checks here if the claimed evaluations of wᵢ(r) are consistent with // the main claim, by checking E w(wᵢ(r)...) = purportedValue. -func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, combinationCoeff, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { +func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, foldingCoeff, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { // the eq terms ( E ) numClaims := len(e.evaluationPoints) evaluation := polynomial.EvalEq(e.evaluationPoints[numClaims-1], r) for i := numClaims - 2; i >= 0; i-- { - evaluation.Mul(&evaluation, &combinationCoeff) + evaluation.Mul(&evaluation, &foldingCoeff) eq := polynomial.EvalEq(e.evaluationPoints[i], r) evaluation.Add(&evaluation, &eq) } @@ -120,10 +121,11 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []fr.Element, comb return errors.New("incompatible evaluations") } -// eqTimesGateEvalSumcheckClaims is a claim for sumcheck (prover side). -// eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). +// zeroCheckClaims is a claim for sumcheck (prover side). +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear +// extension of the values of w across all instances. // Its purpose is to batch the proving of multiple evaluations of the same wire. -type eqTimesGateEvalSumcheckClaims struct { +type zeroCheckClaims struct { wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ) @@ -136,11 +138,11 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { +func (c *zeroCheckClaims) getWire() Wire { return c.manager.circuit[c.wireI] } -// combine the multiple claims into one claim using a random combination (combinationCoeff or c). +// fold the multiple claims into one claim using a random combination (foldingCoeff or c). // From the original multiple claims of w(xᵢ) = yᵢ, we get a single claim // ∑ᵢ,ₕ cⁱ eq(xᵢ, h) w(h) = ∑ᵢ cⁱ yᵢ, where h iterates over the hypercube (circuit instances) and // i iterates over the claims. @@ -148,8 +150,8 @@ func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { // Thus if we initially compute E := ∑ᵢ cⁱ eq(xᵢ, -), our claim will find the simpler form // ∑ᵢ cⁱ yᵢ = ∑ₕ w(h) E(h), where the sum-checked polynomial is of degree deg(g) + 1, // and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. -// The output of combine is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. -func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) polynomial.Polynomial { +// The output of fold is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. +func (c *zeroCheckClaims) fold(foldingCoeff fr.Element) polynomial.Polynomial { varsNum := c.varsNum() eqLength := 1 << varsNum claimsNum := c.claimsNum() @@ -161,7 +163,7 @@ func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) pol // E := eq(x₀, -) newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) - aI := combinationCoeff + aI := foldingCoeff // E += cⁱ eq(xᵢ, -) for k := 1; k < claimsNum; k++ { @@ -170,7 +172,7 @@ func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) pol c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) if k+1 < claimsNum { - aI.Mul(&aI, &combinationCoeff) + aI.Mul(&aI, &foldingCoeff) } } @@ -182,7 +184,7 @@ func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff fr.Element) pol // eqAcc sets m to an eq table at q and then adds it to e. // m <- eq(q, -). // e <- e + m -func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { +func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { n := len(q) //At the end of each iteration, m(h₁, ..., hₙ) = eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) @@ -221,7 +223,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.E // computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). // the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. -func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { +func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) @@ -237,7 +239,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { sumSize := len(c.eq) / 2 // the range of h, over which we sum - // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called + // Perf-TODO: Collate once at claim "folding" time and not again. then, even folding can be done in one operation every time "next" is called gJ := make([]fr.Element, degGJ) var mu sync.Mutex @@ -302,7 +304,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // next first folds the input and E polynomials at the given verifier challenge then computes the new gⱼ. // Thus, j <- j+1 and rⱼ = challenge. -func (c *eqTimesGateEvalSumcheckClaims) next(challenge fr.Element) polynomial.Polynomial { +func (c *zeroCheckClaims) next(challenge fr.Element) polynomial.Polynomial { const minBlockSize = 512 n := len(c.eq) / 2 if n < minBlockSize { @@ -325,16 +327,16 @@ func (c *eqTimesGateEvalSumcheckClaims) next(challenge fr.Element) polynomial.Po return c.computeGJ() } -func (c *eqTimesGateEvalSumcheckClaims) varsNum() int { +func (c *zeroCheckClaims) varsNum() int { return len(c.evaluationPoints[0]) } -func (c *eqTimesGateEvalSumcheckClaims) claimsNum() int { +func (c *zeroCheckClaims) claimsNum() int { return len(c.claimedEvaluations) } // proveFinalEval provides the values wᵢ(r₁, ..., rₙ) -func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Element { +func (c *zeroCheckClaims) proveFinalEval(r []fr.Element) []fr.Element { //defer the proof, return list of claims injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. @@ -353,7 +355,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []fr.Element) []fr.Elem } type claimsManager struct { - claims []*eqTimesGateEvalSumcheckLazyClaims + claims []*zeroCheckLazyClaims assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool @@ -362,7 +364,7 @@ type claimsManager struct { func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment - manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(circuit)) + manager.claims = make([]*zeroCheckLazyClaims, len(circuit)) manager.memPool = o.pool manager.workers = o.workers manager.circuit = circuit @@ -371,7 +373,7 @@ func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (m if circuit[i].IsInput() { circuit[i].Gate.Degree = 1 } - manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ + manager.claims[i] = &zeroCheckLazyClaims{ wireI: i, evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), @@ -388,14 +390,14 @@ func (m *claimsManager) add(wire int, evaluationPoint []fr.Element, evaluation f claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) } -func (m *claimsManager) getLazyClaim(wire int) *eqTimesGateEvalSumcheckLazyClaims { +func (m *claimsManager) getLazyClaim(wire int) *zeroCheckLazyClaims { return m.claims[wire] } -func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { +func (m *claimsManager) getClaim(wireI int) *zeroCheckClaims { lazy := m.claims[wireI] wire := m.circuit[wireI] - res := &eqTimesGateEvalSumcheckClaims{ + res := &zeroCheckClaims{ wireI: wireI, evaluationPoints: lazy.evaluationPoints, claimedEvaluations: lazy.claimedEvaluations, @@ -490,7 +492,7 @@ func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { if c[i].NoProof() { // no proof, no challenge continue } - if c[i].NbClaims() > 1 { //combine the claims + if c[i].NbClaims() > 1 { //fold the claims size++ } size += logNbInstances // full run of sumcheck on logNbInstances variables @@ -516,7 +518,7 @@ func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { wirePrefix := prefix + "w" + nums[i] + "." if c[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "comb" + challenges[j] = wirePrefix + "fold" j++ } diff --git a/internal/gkr/bw6-761/sumcheck.go b/internal/gkr/bw6-761/sumcheck.go index aea8e656b3..f083550198 100644 --- a/internal/gkr/bw6-761/sumcheck.go +++ b/internal/gkr/bw6-761/sumcheck.go @@ -20,7 +20,7 @@ import ( // sumcheckClaims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. // Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) type sumcheckClaims interface { - combine(a fr.Element) polynomial.Polynomial // combine into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. + fold(a fr.Element) polynomial.Polynomial // fold into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. next(fr.Element) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ varsNum() int // number of variables claimsNum() int // number of claims @@ -29,11 +29,11 @@ type sumcheckClaims interface { // sumcheckLazyClaims is the sumcheckClaims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. type sumcheckLazyClaims interface { - claimsNum() int // claimsNum = m - varsNum() int // varsNum = n - combinedSum(a fr.Element) fr.Element // combinedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - degree(i int) int // degree of the total claim in the i'th variable - verifyFinalEval(r []fr.Element, combinationCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error + claimsNum() int // claimsNum = m + varsNum() int // varsNum = n + foldedSum(a fr.Element) fr.Element // foldedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ + degree(i int) int // degree of the total claim in the i'th variable + verifyFinalEval(r []fr.Element, foldingCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error } // sumcheckProof of a multi-statement. @@ -49,7 +49,7 @@ func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) } challengeNames = make([]string, numChallenges) if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "comb" + challengeNames[0] = settings.Prefix + "fold" } prefix := settings.Prefix + "pSP." for i := 0; i < varsNum; i++ { @@ -95,16 +95,16 @@ func sumcheckProve(claims sumcheckClaims, transcriptSettings fiatshamir.Settings return proof, err } - var combinationCoeff fr.Element + var foldingCoeff fr.Element if claims.claimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { + if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { return proof, err } } varsNum := claims.varsNum() proof.partialSumPolys = make([]polynomial.Polynomial, varsNum) - proof.partialSumPolys[0] = claims.combine(combinationCoeff) + proof.partialSumPolys[0] = claims.fold(foldingCoeff) challenges := make([]fr.Element, varsNum) for j := 0; j+1 < varsNum; j++ { @@ -130,10 +130,10 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe return err } - var combinationCoeff fr.Element + var foldingCoeff fr.Element if claims.claimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { + if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { return err } } @@ -148,7 +148,7 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe } } gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() - gJR := claims.combinedSum(combinationCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) + gJR := claims.foldedSum(foldingCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) for j := range claims.varsNum() { if len(proof.partialSumPolys[j]) != claims.degree(j) { @@ -167,5 +167,5 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe gJR = gJCoeffs.Eval(&r[j]) } - return claims.verifyFinalEval(r, combinationCoeff, gJR, proof.finalEvalProof) + return claims.verifyFinalEval(r, foldingCoeff, gJR, proof.finalEvalProof) } diff --git a/internal/gkr/bw6-761/sumcheck_test.go b/internal/gkr/bw6-761/sumcheck_test.go index c61d420cb7..375652e4ec 100644 --- a/internal/gkr/bw6-761/sumcheck_test.go +++ b/internal/gkr/bw6-761/sumcheck_test.go @@ -113,7 +113,7 @@ func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { return []fr.Element{sum} } -func (c singleMultilinClaim) combine(fr.Element) polynomial.Polynomial { +func (c singleMultilinClaim) fold(fr.Element) polynomial.Polynomial { return sumForX1One(c.g) } @@ -127,7 +127,7 @@ type singleMultilinLazyClaim struct { claimedSum fr.Element } -func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, combinationCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error { +func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, _ fr.Element, purportedValue fr.Element, proof []fr.Element) error { val := c.g.Evaluate(r, nil) if val.Equal(&purportedValue) { return nil @@ -135,7 +135,7 @@ func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, combinationCoef return fmt.Errorf("mismatch") } -func (c singleMultilinLazyClaim) combinedSum(combinationCoeffs fr.Element) fr.Element { +func (c singleMultilinLazyClaim) foldedSum(_ fr.Element) fr.Element { return c.claimedSum } diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index f42f2fe8b5..d4b28277e4 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -258,7 +258,7 @@ func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { wirePrefix := prefix + "w" + nums[i] + "." if c[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "comb" + challenges[j] = wirePrefix + "fold" j++ } diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 6b39d5d7bb..40c67050d0 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -32,41 +32,42 @@ type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) -// eqTimesGateEvalSumcheckLazyClaims is a lazy claim for sumcheck (verifier side). -// eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). +// zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear +// extension of the values of w across all instances. // Its purpose is to batch the checking of multiple evaluations of the same wire. -type eqTimesGateEvalSumcheckLazyClaims struct { +type zeroCheckLazyClaims struct { wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]small_rational.SmallRational // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []small_rational.SmallRational // yᵢ = w(xᵢ), allegedly manager *claimsManager // WARNING: Circular references } -func (e *eqTimesGateEvalSumcheckLazyClaims) getWire() Wire { +func (e *zeroCheckLazyClaims) getWire() Wire { return e.manager.circuit[e.wireI] } -func (e *eqTimesGateEvalSumcheckLazyClaims) claimsNum() int { +func (e *zeroCheckLazyClaims) claimsNum() int { return len(e.evaluationPoints) } -func (e *eqTimesGateEvalSumcheckLazyClaims) varsNum() int { +func (e *zeroCheckLazyClaims) varsNum() int { return len(e.evaluationPoints[0]) } -// combinedSum returns ∑ᵢ aⁱ yᵢ -func (e *eqTimesGateEvalSumcheckLazyClaims) combinedSum(a small_rational.SmallRational) small_rational.SmallRational { +// foldedSum returns ∑ᵢ aⁱ yᵢ +func (e *zeroCheckLazyClaims) foldedSum(a small_rational.SmallRational) small_rational.SmallRational { evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) return evalsAsPoly.Eval(&a) } -func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { +func (e *zeroCheckLazyClaims) degree(int) int { return 1 + e.manager.circuit[e.wireI].Gate.Degree } // verifyFinalEval finalizes the verification of w. // The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is combinationCoeff ) +// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is foldingCoeff ) // Both purportedValue and the vector r have been randomized during the sumcheck protocol. // By taking the w term out of the sum we get the equivalent claim that // for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. @@ -76,12 +77,12 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) degree(int) int { // The claims are communicated through the proof parameter. // The verifier checks here if the claimed evaluations of wᵢ(r) are consistent with // the main claim, by checking E w(wᵢ(r)...) = purportedValue. -func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []small_rational.SmallRational, combinationCoeff, purportedValue small_rational.SmallRational, uniqueInputEvaluations []small_rational.SmallRational) error { +func (e *zeroCheckLazyClaims) verifyFinalEval(r []small_rational.SmallRational, foldingCoeff, purportedValue small_rational.SmallRational, uniqueInputEvaluations []small_rational.SmallRational) error { // the eq terms ( E ) numClaims := len(e.evaluationPoints) evaluation := polynomial.EvalEq(e.evaluationPoints[numClaims-1], r) for i := numClaims - 2; i >= 0; i-- { - evaluation.Mul(&evaluation, &combinationCoeff) + evaluation.Mul(&evaluation, &foldingCoeff) eq := polynomial.EvalEq(e.evaluationPoints[i], r) evaluation.Add(&evaluation, &eq) } @@ -120,10 +121,11 @@ func (e *eqTimesGateEvalSumcheckLazyClaims) verifyFinalEval(r []small_rational.S return errors.New("incompatible evaluations") } -// eqTimesGateEvalSumcheckClaims is a claim for sumcheck (prover side). -// eqTimesGateEval is a polynomial consisting of ∑ᵢ cⁱ eq(-, xᵢ) w(-). +// zeroCheckClaims is a claim for sumcheck (prover side). +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear +// extension of the values of w across all instances. // Its purpose is to batch the proving of multiple evaluations of the same wire. -type eqTimesGateEvalSumcheckClaims struct { +type zeroCheckClaims struct { wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]small_rational.SmallRational // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []small_rational.SmallRational // yᵢ = w(xᵢ) @@ -136,11 +138,11 @@ type eqTimesGateEvalSumcheckClaims struct { gateEvaluatorPool *gateEvaluatorPool } -func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { +func (c *zeroCheckClaims) getWire() Wire { return c.manager.circuit[c.wireI] } -// combine the multiple claims into one claim using a random combination (combinationCoeff or c). +// fold the multiple claims into one claim using a random combination (foldingCoeff or c). // From the original multiple claims of w(xᵢ) = yᵢ, we get a single claim // ∑ᵢ,ₕ cⁱ eq(xᵢ, h) w(h) = ∑ᵢ cⁱ yᵢ, where h iterates over the hypercube (circuit instances) and // i iterates over the claims. @@ -148,8 +150,8 @@ func (c *eqTimesGateEvalSumcheckClaims) getWire() Wire { // Thus if we initially compute E := ∑ᵢ cⁱ eq(xᵢ, -), our claim will find the simpler form // ∑ᵢ cⁱ yᵢ = ∑ₕ w(h) E(h), where the sum-checked polynomial is of degree deg(g) + 1, // and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. -// The output of combine is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. -func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff small_rational.SmallRational) polynomial.Polynomial { +// The output of fold is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. +func (c *zeroCheckClaims) fold(foldingCoeff small_rational.SmallRational) polynomial.Polynomial { varsNum := c.varsNum() eqLength := 1 << varsNum claimsNum := c.claimsNum() @@ -161,7 +163,7 @@ func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff small_rational. // E := eq(x₀, -) newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) - aI := combinationCoeff + aI := foldingCoeff // E += cⁱ eq(xᵢ, -) for k := 1; k < claimsNum; k++ { @@ -170,7 +172,7 @@ func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff small_rational. c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) if k+1 < claimsNum { - aI.Mul(&aI, &combinationCoeff) + aI.Mul(&aI, &foldingCoeff) } } @@ -182,7 +184,7 @@ func (c *eqTimesGateEvalSumcheckClaims) combine(combinationCoeff small_rational. // eqAcc sets m to an eq table at q and then adds it to e. // m <- eq(q, -). // e <- e + m -func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []small_rational.SmallRational) { +func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []small_rational.SmallRational) { n := len(q) //At the end of each iteration, m(h₁, ..., hₙ) = eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) @@ -221,7 +223,7 @@ func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []smal // computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). // the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. -func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { +func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) @@ -237,7 +239,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { sumSize := len(c.eq) / 2 // the range of h, over which we sum - // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called + // Perf-TODO: Collate once at claim "folding" time and not again. then, even folding can be done in one operation every time "next" is called gJ := make([]small_rational.SmallRational, degGJ) var mu sync.Mutex @@ -302,7 +304,7 @@ func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { // next first folds the input and E polynomials at the given verifier challenge then computes the new gⱼ. // Thus, j <- j+1 and rⱼ = challenge. -func (c *eqTimesGateEvalSumcheckClaims) next(challenge small_rational.SmallRational) polynomial.Polynomial { +func (c *zeroCheckClaims) next(challenge small_rational.SmallRational) polynomial.Polynomial { const minBlockSize = 512 n := len(c.eq) / 2 if n < minBlockSize { @@ -325,16 +327,16 @@ func (c *eqTimesGateEvalSumcheckClaims) next(challenge small_rational.SmallRatio return c.computeGJ() } -func (c *eqTimesGateEvalSumcheckClaims) varsNum() int { +func (c *zeroCheckClaims) varsNum() int { return len(c.evaluationPoints[0]) } -func (c *eqTimesGateEvalSumcheckClaims) claimsNum() int { +func (c *zeroCheckClaims) claimsNum() int { return len(c.claimedEvaluations) } // proveFinalEval provides the values wᵢ(r₁, ..., rₙ) -func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []small_rational.SmallRational) []small_rational.SmallRational { +func (c *zeroCheckClaims) proveFinalEval(r []small_rational.SmallRational) []small_rational.SmallRational { //defer the proof, return list of claims injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. @@ -353,7 +355,7 @@ func (c *eqTimesGateEvalSumcheckClaims) proveFinalEval(r []small_rational.SmallR } type claimsManager struct { - claims []*eqTimesGateEvalSumcheckLazyClaims + claims []*zeroCheckLazyClaims assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool @@ -362,7 +364,7 @@ type claimsManager struct { func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { manager.assignment = assignment - manager.claims = make([]*eqTimesGateEvalSumcheckLazyClaims, len(circuit)) + manager.claims = make([]*zeroCheckLazyClaims, len(circuit)) manager.memPool = o.pool manager.workers = o.workers manager.circuit = circuit @@ -371,7 +373,7 @@ func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (m if circuit[i].IsInput() { circuit[i].Gate.Degree = 1 } - manager.claims[i] = &eqTimesGateEvalSumcheckLazyClaims{ + manager.claims[i] = &zeroCheckLazyClaims{ wireI: i, evaluationPoints: make([][]small_rational.SmallRational, 0, circuit[i].NbClaims()), claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), @@ -388,14 +390,14 @@ func (m *claimsManager) add(wire int, evaluationPoint []small_rational.SmallRati claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) } -func (m *claimsManager) getLazyClaim(wire int) *eqTimesGateEvalSumcheckLazyClaims { +func (m *claimsManager) getLazyClaim(wire int) *zeroCheckLazyClaims { return m.claims[wire] } -func (m *claimsManager) getClaim(wireI int) *eqTimesGateEvalSumcheckClaims { +func (m *claimsManager) getClaim(wireI int) *zeroCheckClaims { lazy := m.claims[wireI] wire := m.circuit[wireI] - res := &eqTimesGateEvalSumcheckClaims{ + res := &zeroCheckClaims{ wireI: wireI, evaluationPoints: lazy.evaluationPoints, claimedEvaluations: lazy.claimedEvaluations, @@ -490,7 +492,7 @@ func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { if c[i].NoProof() { // no proof, no challenge continue } - if c[i].NbClaims() > 1 { //combine the claims + if c[i].NbClaims() > 1 { //fold the claims size++ } size += logNbInstances // full run of sumcheck on logNbInstances variables @@ -516,7 +518,7 @@ func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { wirePrefix := prefix + "w" + nums[i] + "." if c[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "comb" + challenges[j] = wirePrefix + "fold" j++ } diff --git a/internal/gkr/small_rational/sumcheck.go b/internal/gkr/small_rational/sumcheck.go index 9aeecf49e0..19d2c86c77 100644 --- a/internal/gkr/small_rational/sumcheck.go +++ b/internal/gkr/small_rational/sumcheck.go @@ -20,7 +20,7 @@ import ( // sumcheckClaims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. // Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) type sumcheckClaims interface { - combine(a small_rational.SmallRational) polynomial.Polynomial // combine into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. + fold(a small_rational.SmallRational) polynomial.Polynomial // fold into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. next(small_rational.SmallRational) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ varsNum() int // number of variables claimsNum() int // number of claims @@ -29,11 +29,11 @@ type sumcheckClaims interface { // sumcheckLazyClaims is the sumcheckClaims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. type sumcheckLazyClaims interface { - claimsNum() int // claimsNum = m - varsNum() int // varsNum = n - combinedSum(a small_rational.SmallRational) small_rational.SmallRational // combinedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - degree(i int) int // degree of the total claim in the i'th variable - verifyFinalEval(r []small_rational.SmallRational, combinationCoeff small_rational.SmallRational, purportedValue small_rational.SmallRational, proof []small_rational.SmallRational) error + claimsNum() int // claimsNum = m + varsNum() int // varsNum = n + foldedSum(a small_rational.SmallRational) small_rational.SmallRational // foldedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ + degree(i int) int // degree of the total claim in the i'th variable + verifyFinalEval(r []small_rational.SmallRational, foldingCoeff small_rational.SmallRational, purportedValue small_rational.SmallRational, proof []small_rational.SmallRational) error } // sumcheckProof of a multi-statement. @@ -49,7 +49,7 @@ func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) } challengeNames = make([]string, numChallenges) if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "comb" + challengeNames[0] = settings.Prefix + "fold" } prefix := settings.Prefix + "pSP." for i := 0; i < varsNum; i++ { @@ -95,16 +95,16 @@ func sumcheckProve(claims sumcheckClaims, transcriptSettings fiatshamir.Settings return proof, err } - var combinationCoeff small_rational.SmallRational + var foldingCoeff small_rational.SmallRational if claims.claimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []small_rational.SmallRational{}, &remainingChallengeNames); err != nil { + if foldingCoeff, err = next(transcript, []small_rational.SmallRational{}, &remainingChallengeNames); err != nil { return proof, err } } varsNum := claims.varsNum() proof.partialSumPolys = make([]polynomial.Polynomial, varsNum) - proof.partialSumPolys[0] = claims.combine(combinationCoeff) + proof.partialSumPolys[0] = claims.fold(foldingCoeff) challenges := make([]small_rational.SmallRational, varsNum) for j := 0; j+1 < varsNum; j++ { @@ -130,10 +130,10 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe return err } - var combinationCoeff small_rational.SmallRational + var foldingCoeff small_rational.SmallRational if claims.claimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []small_rational.SmallRational{}, &remainingChallengeNames); err != nil { + if foldingCoeff, err = next(transcript, []small_rational.SmallRational{}, &remainingChallengeNames); err != nil { return err } } @@ -148,7 +148,7 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe } } gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() - gJR := claims.combinedSum(combinationCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) + gJR := claims.foldedSum(foldingCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) for j := range claims.varsNum() { if len(proof.partialSumPolys[j]) != claims.degree(j) { @@ -167,5 +167,5 @@ func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSe gJR = gJCoeffs.Eval(&r[j]) } - return claims.verifyFinalEval(r, combinationCoeff, gJR, proof.finalEvalProof) + return claims.verifyFinalEval(r, foldingCoeff, gJR, proof.finalEvalProof) } diff --git a/internal/gkr/small_rational/sumcheck_test_vector_gen.go b/internal/gkr/small_rational/sumcheck_test_vector_gen.go index d8364fbd89..ea969d171e 100644 --- a/internal/gkr/small_rational/sumcheck_test_vector_gen.go +++ b/internal/gkr/small_rational/sumcheck_test_vector_gen.go @@ -170,7 +170,7 @@ func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { return []small_rational.SmallRational{sum} } -func (c singleMultilinClaim) combine(small_rational.SmallRational) polynomial.Polynomial { +func (c singleMultilinClaim) fold(small_rational.SmallRational) polynomial.Polynomial { return sumForX1One(c.g) } @@ -184,7 +184,7 @@ type singleMultilinLazyClaim struct { claimedSum small_rational.SmallRational } -func (c singleMultilinLazyClaim) verifyFinalEval(r []small_rational.SmallRational, combinationCoeff small_rational.SmallRational, purportedValue small_rational.SmallRational, proof []small_rational.SmallRational) error { +func (c singleMultilinLazyClaim) verifyFinalEval(r []small_rational.SmallRational, _ small_rational.SmallRational, purportedValue small_rational.SmallRational, proof []small_rational.SmallRational) error { val := c.g.Evaluate(r, nil) if val.Equal(&purportedValue) { return nil @@ -192,7 +192,7 @@ func (c singleMultilinLazyClaim) verifyFinalEval(r []small_rational.SmallRationa return fmt.Errorf("mismatch") } -func (c singleMultilinLazyClaim) combinedSum(combinationCoeffs small_rational.SmallRational) small_rational.SmallRational { +func (c singleMultilinLazyClaim) foldedSum(_ small_rational.SmallRational) small_rational.SmallRational { return c.claimedSum } diff --git a/internal/gkr/sumcheck.go b/internal/gkr/sumcheck.go index 67e489459c..e963509686 100644 --- a/internal/gkr/sumcheck.go +++ b/internal/gkr/sumcheck.go @@ -13,11 +13,11 @@ import ( // sumcheckLazyClaims is the Claims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. type sumcheckLazyClaims interface { - claimsNum() int // claimsNum = m - varsNum() int // varsNum = n + claimsNum() int // claimsNum = m + varsNum() int // varsNum = n foldedSum(api frontend.API, a frontend.Variable) frontend.Variable // foldedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - degree(i int) int // degree of the total claim in the i'th variable - verifyFinalEval(api frontend.API, r []frontend.Variable, combinationCoeff, purportedValue frontend.Variable, proof []frontend.Variable) error + degree(i int) int // degree of the total claim in the i'th variable + verifyFinalEval(api frontend.API, r []frontend.Variable, foldingCoeff, purportedValue frontend.Variable, proof []frontend.Variable) error } // sumcheckProof of a multi-sumcheck statement. @@ -33,7 +33,7 @@ func setupTranscript(api frontend.API, claimsNum int, varsNum int, settings *fia } challengeNames := make([]string, numChallenges) if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "comb" + challengeNames[0] = settings.Prefix + "fold" } prefix := settings.Prefix + "pSP." for i := 0; i < varsNum; i++ { @@ -65,10 +65,10 @@ func verifySumcheck(api frontend.API, claims sumcheckLazyClaims, proof sumcheckP return err } - var combinationCoeff frontend.Variable + var foldingCoeff frontend.Variable if claims.claimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []frontend.Variable{}, &remainingChallengeNames); err != nil { + if foldingCoeff, err = next(transcript, []frontend.Variable{}, &remainingChallengeNames); err != nil { return err } } @@ -83,8 +83,8 @@ func verifySumcheck(api frontend.API, claims sumcheckLazyClaims, proof sumcheckP } } - gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() - gJR := claims.foldedSum(api, combinationCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) + gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() + gJR := claims.foldedSum(api, foldingCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) for j := 0; j < claims.varsNum(); j++ { partialSumPoly := proof.PartialSumPolys[j] //proof.PartialSumPolys(j) @@ -103,6 +103,6 @@ func verifySumcheck(api frontend.API, claims sumcheckLazyClaims, proof sumcheckP gJR = polynomial.InterpolateLDE(api, r[j], gJ[:(claims.degree(j)+1)]) } - return claims.verifyFinalEval(api, r, combinationCoeff, gJR, proof.FinalEvalProof) + return claims.verifyFinalEval(api, r, foldingCoeff, gJR, proof.FinalEvalProof) } From 8a5acffa809589adc30ee0ef089d6b7725ccab87 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 18 Feb 2026 16:15:43 -0600 Subject: [PATCH 202/251] refactor: remove permutations --- internal/gkr/gkr.go | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index d4b28277e4..b19cd2f478 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -7,7 +7,6 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" - "github.com/consensys/gnark/internal/utils" fiatshamir "github.com/consensys/gnark/std/fiat-shamir" "github.com/consensys/gnark/std/polynomial" ) @@ -18,26 +17,9 @@ type ( Circuit = gkrtypes.GadgetCircuit ) -// Permutations describes how to reorder wires and instances -type Permutations struct { - SortedInstances []int - SortedWires []int - InstancesPermutation []int - WiresPermutation []int -} - -// WireAssignment is assignment of values to the same wire across many instances of the circuit +// WireAssignment is an assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin -func (a WireAssignment) Permute(p Permutations) { - utils.Permute(a, p.WiresPermutation) - for i := range a { - if a[i] != nil { - utils.Permute(a[i], p.InstancesPermutation) - } - } -} - func (a WireAssignment) NbInstances() int { for _, aW := range a { if aW != nil { From a8be3ad7c33352918fc9c0641583f041a4b997fb Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 18 Feb 2026 16:16:42 -0600 Subject: [PATCH 203/251] style: remove unnecessary space --- internal/generator/backend/template/gkr/gkr.go.tmpl | 2 +- internal/gkr/bls12-377/gkr.go | 2 +- internal/gkr/bls12-381/gkr.go | 2 +- internal/gkr/bn254/gkr.go | 2 +- internal/gkr/bw6-761/gkr.go | 2 +- internal/gkr/small_rational/gkr.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 93e30c7294..31663e1982 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -60,7 +60,7 @@ func (e *zeroCheckLazyClaims) degree(int) int { // verifyFinalEval finalizes the verification of w. // The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is foldingCoeff ) +// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. (c is foldingCoeff) // Both purportedValue and the vector r have been randomized during the sumcheck protocol. // By taking the w term out of the sum we get the equivalent claim that // for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 0753606376..5b85606092 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -67,7 +67,7 @@ func (e *zeroCheckLazyClaims) degree(int) int { // verifyFinalEval finalizes the verification of w. // The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is foldingCoeff ) +// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. (c is foldingCoeff) // Both purportedValue and the vector r have been randomized during the sumcheck protocol. // By taking the w term out of the sum we get the equivalent claim that // for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index a55a1335a0..cb831e29f2 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -67,7 +67,7 @@ func (e *zeroCheckLazyClaims) degree(int) int { // verifyFinalEval finalizes the verification of w. // The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is foldingCoeff ) +// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. (c is foldingCoeff) // Both purportedValue and the vector r have been randomized during the sumcheck protocol. // By taking the w term out of the sum we get the equivalent claim that // for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 41bcb4c673..5e7d29e4ee 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -67,7 +67,7 @@ func (e *zeroCheckLazyClaims) degree(int) int { // verifyFinalEval finalizes the verification of w. // The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is foldingCoeff ) +// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. (c is foldingCoeff) // Both purportedValue and the vector r have been randomized during the sumcheck protocol. // By taking the w term out of the sum we get the equivalent claim that // for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 80a4b1313f..515a38c7b7 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -67,7 +67,7 @@ func (e *zeroCheckLazyClaims) degree(int) int { // verifyFinalEval finalizes the verification of w. // The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is foldingCoeff ) +// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. (c is foldingCoeff) // Both purportedValue and the vector r have been randomized during the sumcheck protocol. // By taking the w term out of the sum we get the equivalent claim that // for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 40c67050d0..4c7406e138 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -67,7 +67,7 @@ func (e *zeroCheckLazyClaims) degree(int) int { // verifyFinalEval finalizes the verification of w. // The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is foldingCoeff ) +// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. (c is foldingCoeff) // Both purportedValue and the vector r have been randomized during the sumcheck protocol. // By taking the w term out of the sum we get the equivalent claim that // for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. From c141874009d874fb1663e4c3344e7c3c412a372d Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Thu, 19 Feb 2026 16:27:39 -0600 Subject: [PATCH 204/251] fix: improved error message for "constant" gate --- .../backend/template/gkr/gkr.test.go.tmpl | 6 +++--- internal/gkr/bls12-377/gkr_test.go | 6 +++--- internal/gkr/bls12-381/gkr_test.go | 6 +++--- internal/gkr/bn254/gkr_test.go | 6 +++--- internal/gkr/bw6-761/gkr_test.go | 6 +++--- internal/gkr/gkrtesting/gkrtesting.go | 12 ++++++++--- internal/gkr/gkrtypes/gate.go | 14 ++++++++----- internal/gkr/gkrtypes/types.go | 20 +++++++++---------- std/gkrapi/compile.go | 5 ++++- 9 files changed, 47 insertions(+), 34 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index 5a34218ee4..729c28948c 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -126,7 +126,7 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := cache.Compile(circuit) + sCircuit := cache.Compile(t, circuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -179,7 +179,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]{{ .ElementType }}) { - c := cache.Compile(gkrtesting.NoGateCircuit()) + c := cache.Compile(t, gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -260,7 +260,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := cache.Compile(gkrtesting.MiMCCircuit(mimcDepth)) + c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]{{ .ElementType }}, nbInstances) in1 := make([]{{ .ElementType }}, nbInstances) diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index 3dc8ce5179..95b45cc654 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -133,7 +133,7 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := cache.Compile(circuit) + sCircuit := cache.Compile(t, circuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -186,7 +186,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := cache.Compile(gkrtesting.NoGateCircuit()) + c := cache.Compile(t, gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -267,7 +267,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := cache.Compile(gkrtesting.MiMCCircuit(mimcDepth)) + c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 9e3c583ac4..012683657a 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -133,7 +133,7 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := cache.Compile(circuit) + sCircuit := cache.Compile(t, circuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -186,7 +186,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := cache.Compile(gkrtesting.NoGateCircuit()) + c := cache.Compile(t, gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -267,7 +267,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := cache.Compile(gkrtesting.MiMCCircuit(mimcDepth)) + c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 04ae48263c..6f6f0b5432 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -133,7 +133,7 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := cache.Compile(circuit) + sCircuit := cache.Compile(t, circuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -186,7 +186,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := cache.Compile(gkrtesting.NoGateCircuit()) + c := cache.Compile(t, gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -267,7 +267,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := cache.Compile(gkrtesting.MiMCCircuit(mimcDepth)) + c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index 1cf4cf0bda..b8c4d5a422 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -133,7 +133,7 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := cache.Compile(circuit) + sCircuit := cache.Compile(t, circuit) ins := circuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -186,7 +186,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := cache.Compile(gkrtesting.NoGateCircuit()) + c := cache.Compile(t, gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -267,7 +267,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := cache.Compile(gkrtesting.MiMCCircuit(mimcDepth)) + c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 5576dea233..56c2c16bd1 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -11,6 +11,7 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/std/gkrapi/gkr" + "github.com/stretchr/testify/require" ) // Cache for circuits and gates. @@ -70,8 +71,10 @@ type JSONWire struct { type JSONCircuit []JSONWire // Compile compiles a programmatic GadgetCircuit into a SerializableCircuit. -func (c *Cache) Compile(circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - return gkrtypes.CompileCircuit(circuit, c.field) +func (c *Cache) Compile(t require.TestingT, circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { + res, err := gkrtypes.CompileCircuit(circuit, c.field) + require.NoError(t, err) + return res } func (c *Cache) GetCircuit(path string) (gkrtypes.SerializableCircuit, gkrtypes.GadgetCircuit) { @@ -107,7 +110,10 @@ func (c *Cache) GetCircuit(path string) (gkrtypes.SerializableCircuit, gkrtypes. Inputs: wJSON.Inputs, } } - sCircuit := gkrtypes.CompileCircuit(gCircuit, c.field) + sCircuit, err := gkrtypes.CompileCircuit(gCircuit, c.field) + if err != nil { + panic(err) + } c.circuits[path] = circuits{ serializable: sCircuit, diff --git a/internal/gkr/gkrtypes/gate.go b/internal/gkr/gkrtypes/gate.go index 016f0fba9f..565b9cb1d7 100644 --- a/internal/gkr/gkrtypes/gate.go +++ b/internal/gkr/gkrtypes/gate.go @@ -244,19 +244,23 @@ func CompileGateFunction(f gkr.GateFunction, nbInputs int) (GateBytecode, error) // Execute the gate function to record operations out := f(&compiler, inputs...) + outVar, ok := out.(compilationVar) + if !ok { + return GateBytecode{}, errors.New("gate function must return a variable; constant values must be hard-coded into the gates that use them") + } if len(compiler.instructions) == 0 { - // No operations recorded, but not all is lost. + // No operations recorded, but not all is lost yet. // If the output simply mirrors the last input, we can still represent // it in bytecode, as the evaluator returns the last stack frame element. - if int(out.(compilationVar).id) == nbInputs-1 { + if int(outVar.id) == len(compiler.constants)+nbInputs-1 { return GateBytecode{}, nil } - return GateBytecode{}, errors.New("cannot compile no-op gate function") + return GateBytecode{}, errors.New("only non-trivial or last-reflective gate functions supported") } // All instructions after the output are no-ops. Prune them and the corresponding variables. - // Henceforth we guarantee that the variable with the highest index is the gate output. - lastEffectiveInstructionIndex := int(out.(compilationVar).id) - compiler.nbInputs + // Henceforth, we guarantee that the variable with the highest index is the gate output. + lastEffectiveInstructionIndex := int(outVar.id) - compiler.nbInputs compiler.instructions = compiler.instructions[:lastEffectiveInstructionIndex+1] // Remap indices from temporary layout to final layout diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 011cc58d73..e9265cf41e 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -1,6 +1,7 @@ package gkrtypes import ( + "errors" "math/big" "reflect" @@ -266,7 +267,7 @@ type Blueprints struct { // CompileCircuit converts a gadget circuit to a serializable circuit by compiling the gate functions. // It also sets wire and gate metadata (Degree, SolvableVar, NbUniqueOutputs) for both the input and output circuits. -func CompileCircuit(c GadgetCircuit, mod *big.Int) SerializableCircuit { +func CompileCircuit(c GadgetCircuit, mod *big.Int) (SerializableCircuit, error) { for i := range c { c[i].NbUniqueOutputs = 0 @@ -279,35 +280,34 @@ func CompileCircuit(c GadgetCircuit, mod *big.Int) SerializableCircuit { var err error for i := range c { // Compute NbUniqueOutputs as we go. - for j := range curWireIn { - curWireIn[j] = false - } - - // count! for _, in := range c[i].Inputs { if !curWireIn[in] { c[in].NbUniqueOutputs++ curWireIn[in] = true } } + // clear curWireIn + for _, in := range c[i].Inputs { + curWireIn[in] = false + } if c[i].IsInput() { if !reflect.DeepEqual(c[i].Gate, GadgetGate{}) { - panic("empty gate expected for input wire") + return nil, errors.New("empty gate expected for input wire") } continue } c[i].Gate.NbIn = len(c[i].Inputs) if res[i].Gate.Evaluate, err = CompileGateFunction(c[i].Gate.Evaluate, c[i].Gate.NbIn); err != nil { - panic(err) + return nil, err } tester.setGate(res[i].Gate.Evaluate, c[i].Gate.NbIn) c[i].Gate.Degree = len(tester.fitPoly(res[i].Gate.Evaluate.EstimateDegree(len(c[i].Inputs)))) - 1 if c[i].Gate.Degree == -1 { - panic("cannot find degree for gate") + return nil, errors.New("cannot find degree for gate") } c[i].Gate.SolvableVar = -1 @@ -328,5 +328,5 @@ func CompileCircuit(c GadgetCircuit, mod *big.Int) SerializableCircuit { res[i].Gate.NbIn = c[i].Gate.NbIn } - return res + return res, nil } diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 6635eb61d7..3132493baa 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -81,7 +81,10 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C compiler := api.parentApi.Compiler() field := compiler.Field() curveID := utils.FieldToCurve(field) - serializableCircuit := gkrtypes.CompileCircuit(api.circuit, field) + serializableCircuit, err := gkrtypes.CompileCircuit(api.circuit, field) + if err != nil { + return nil, err + } switch curveID { case ecc.BN254: From 15e8b2a1c21e920c7b4e6f056cfcb1c70276264d Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Thu, 19 Feb 2026 16:52:02 -0600 Subject: [PATCH 205/251] feat: Can now export intermediate wires as output --- internal/gkr/gkrtypes/types.go | 15 +++++++++------ std/gkrapi/api.go | 8 ++++++++ std/gkrapi/example_test.go | 5 ++--- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index e9265cf41e..0e415ad712 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -29,6 +29,7 @@ type ( Gate Gate[GateExecutable] Inputs []int NbUniqueOutputs int + Exported bool } Circuit[GateExecutable any] []Wire[GateExecutable] @@ -56,17 +57,18 @@ func (w Wire[GateExecutable]) IsInput() bool { // IsOutput returns whether the wire is an output wire. A wire is an output wire // if it is not input to any other wire. func (w Wire[GateExecutable]) IsOutput() bool { - return w.NbUniqueOutputs == 0 + return w.NbUniqueOutputs == 0 || w.Exported } // NbClaims returns the number of claims to be proven about this wire. The number -// of claims is the number of Wires it is input to. For output wires, there is always -// one claim to be made. +// of claims is the number of Wires it is input to, except for an output wire, which +// has an extra claim. func (w Wire[GateExecutable]) NbClaims() int { + res := w.NbUniqueOutputs if w.IsOutput() { - return 1 + res++ } - return w.NbUniqueOutputs + return res } // NoProof returns whether no proof is needed for this wire. This corresponds @@ -322,10 +324,11 @@ func CompileCircuit(c GadgetCircuit, mod *big.Int) (SerializableCircuit, error) // copy metadata from c to res for i := range c { res[i].Inputs = c[i].Inputs + res[i].Exported = c[i].Exported res[i].NbUniqueOutputs = c[i].NbUniqueOutputs res[i].Gate.Degree = c[i].Gate.Degree - res[i].Gate.SolvableVar = c[i].Gate.SolvableVar res[i].Gate.NbIn = c[i].Gate.NbIn + res[i].Gate.SolvableVar = c[i].Gate.SolvableVar } return res, nil diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index 933f75fa1a..be14e485d3 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -61,3 +61,11 @@ func (api *API) Sub(i1, i2 gkr.Variable) gkr.Variable { func (api *API) Mul(i1, i2 gkr.Variable) gkr.Variable { return api.gate2PlusIn(gkrtypes.Mul2, i1, i2) } + +// Export explicitly designates a wire as output. +// Wires that are not used as input to another are considered output by default. +func (api *API) Export(in ...gkr.Variable) { + for _, v := range in { + api.circuit[v].Exported = true + } +} diff --git a/std/gkrapi/example_test.go b/std/gkrapi/example_test.go index d6a5fa7f54..63f680e912 100644 --- a/std/gkrapi/example_test.go +++ b/std/gkrapi/example_test.go @@ -7,7 +7,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/std/gkrapi" "github.com/consensys/gnark/std/gkrapi/gkr" _ "github.com/consensys/gnark/std/hash/all" // import all hash functions to register them @@ -98,8 +97,8 @@ func (c *exampleCircuit) Define(api frontend.API) error { XOut := gkrApi.Gate(xGate, XX, S) // 419-422 YOut := gkrApi.Gate(yGate, S, XOut, XX, YYYY) // 423 - 426 - // have to duplicate X for it to be considered an output variable; this is an implementation detail and will be fixed in the future [https://github.com/Consensys/gnark/issues/1452] - XOut = gkrApi.Gate(gkrtypes.Identity, XOut) + // Mark XOut as an output, even though it is fed into another wire (YOut) + gkrApi.Export(XOut) gkrCircuit, err := gkrApi.Compile("MIMC") if err != nil { From 797262773049c92837905b18fc30420adc32b3f9 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Thu, 19 Feb 2026 17:00:41 -0600 Subject: [PATCH 206/251] fix: unused import --- std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go | 1 - 1 file changed, 1 deletion(-) diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go index 8f26cbbbef..ecec151e8a 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2.go @@ -2,7 +2,6 @@ package gkr_poseidon2 import ( "fmt" - "sync" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" From 1e1209123731ff6414850166d4cfccb1704e491b Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Thu, 19 Feb 2026 17:11:38 -0600 Subject: [PATCH 207/251] fix: regen constraint testdata --- .../testdata/gkr_circuit_bn254.scs | Bin 69687 -> 69707 bytes .../poseidon2/gkr-poseidon2/gkr-poseidon2.go | 5 ++--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/gkr/test_vectors/testdata/gkr_circuit_bn254.scs b/internal/gkr/test_vectors/testdata/gkr_circuit_bn254.scs index 3c1f05f5bb59902608c5a033b098829d80e709ec..503d00e477b3fd4fac56c2d744d6668021e4ab09 100644 GIT binary patch delta 72 zcmdnKfaUZ87DnxfjOL8rHd=Z Date: Fri, 20 Feb 2026 09:15:24 -0600 Subject: [PATCH 208/251] fix: address cursor comment: do not modify circuit in backend --- internal/generator/backend/template/gkr/gkr.go.tmpl | 7 ++----- internal/gkr/bls12-377/gkr.go | 7 ++----- internal/gkr/bls12-381/gkr.go | 7 ++----- internal/gkr/bn254/gkr.go | 7 ++----- internal/gkr/bw6-761/gkr.go | 7 ++----- internal/gkr/small_rational/gkr.go | 7 ++----- 6 files changed, 12 insertions(+), 30 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 31663e1982..bcf85cecdf 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -55,7 +55,7 @@ func (e *zeroCheckLazyClaims) foldedSum(a {{ .ElementType }}) {{ .ElementType }} } func (e *zeroCheckLazyClaims) degree(int) int { - return 1 + e.manager.circuit[e.wireI].Gate.Degree + return e.manager.circuit[e.wireI].ZeroCheckDegree() } // verifyFinalEval finalizes the verification of w. @@ -220,7 +220,7 @@ func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []{{ .ElementType }} func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() - degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) + degGJ := wire.ZeroCheckDegree() // guaranteed to be no smaller than the actual deg(gⱼ) nbGateIn := len(c.input) // Both E and wᵢ (the input wires and the eq table) are multilinear, thus @@ -364,9 +364,6 @@ func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (m manager.circuit = circuit for i := range circuit { - if circuit[i].IsInput() { - circuit[i].Gate.Degree = 1 - } manager.claims[i] = &zeroCheckLazyClaims{ wireI: i, evaluationPoints: make([][]{{ .ElementType }}, 0, circuit[i].NbClaims()), diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 5b85606092..e9cc5a10c9 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -62,7 +62,7 @@ func (e *zeroCheckLazyClaims) foldedSum(a fr.Element) fr.Element { } func (e *zeroCheckLazyClaims) degree(int) int { - return 1 + e.manager.circuit[e.wireI].Gate.Degree + return e.manager.circuit[e.wireI].ZeroCheckDegree() } // verifyFinalEval finalizes the verification of w. @@ -226,7 +226,7 @@ func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() - degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) + degGJ := wire.ZeroCheckDegree() // guaranteed to be no smaller than the actual deg(gⱼ) nbGateIn := len(c.input) // Both E and wᵢ (the input wires and the eq table) are multilinear, thus @@ -370,9 +370,6 @@ func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (m manager.circuit = circuit for i := range circuit { - if circuit[i].IsInput() { - circuit[i].Gate.Degree = 1 - } manager.claims[i] = &zeroCheckLazyClaims{ wireI: i, evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index cb831e29f2..dad2117fc3 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -62,7 +62,7 @@ func (e *zeroCheckLazyClaims) foldedSum(a fr.Element) fr.Element { } func (e *zeroCheckLazyClaims) degree(int) int { - return 1 + e.manager.circuit[e.wireI].Gate.Degree + return e.manager.circuit[e.wireI].ZeroCheckDegree() } // verifyFinalEval finalizes the verification of w. @@ -226,7 +226,7 @@ func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() - degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) + degGJ := wire.ZeroCheckDegree() // guaranteed to be no smaller than the actual deg(gⱼ) nbGateIn := len(c.input) // Both E and wᵢ (the input wires and the eq table) are multilinear, thus @@ -370,9 +370,6 @@ func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (m manager.circuit = circuit for i := range circuit { - if circuit[i].IsInput() { - circuit[i].Gate.Degree = 1 - } manager.claims[i] = &zeroCheckLazyClaims{ wireI: i, evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 5e7d29e4ee..1658dced5d 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -62,7 +62,7 @@ func (e *zeroCheckLazyClaims) foldedSum(a fr.Element) fr.Element { } func (e *zeroCheckLazyClaims) degree(int) int { - return 1 + e.manager.circuit[e.wireI].Gate.Degree + return e.manager.circuit[e.wireI].ZeroCheckDegree() } // verifyFinalEval finalizes the verification of w. @@ -226,7 +226,7 @@ func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() - degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) + degGJ := wire.ZeroCheckDegree() // guaranteed to be no smaller than the actual deg(gⱼ) nbGateIn := len(c.input) // Both E and wᵢ (the input wires and the eq table) are multilinear, thus @@ -370,9 +370,6 @@ func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (m manager.circuit = circuit for i := range circuit { - if circuit[i].IsInput() { - circuit[i].Gate.Degree = 1 - } manager.claims[i] = &zeroCheckLazyClaims{ wireI: i, evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 515a38c7b7..df503a96c7 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -62,7 +62,7 @@ func (e *zeroCheckLazyClaims) foldedSum(a fr.Element) fr.Element { } func (e *zeroCheckLazyClaims) degree(int) int { - return 1 + e.manager.circuit[e.wireI].Gate.Degree + return e.manager.circuit[e.wireI].ZeroCheckDegree() } // verifyFinalEval finalizes the verification of w. @@ -226,7 +226,7 @@ func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() - degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) + degGJ := wire.ZeroCheckDegree() // guaranteed to be no smaller than the actual deg(gⱼ) nbGateIn := len(c.input) // Both E and wᵢ (the input wires and the eq table) are multilinear, thus @@ -370,9 +370,6 @@ func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (m manager.circuit = circuit for i := range circuit { - if circuit[i].IsInput() { - circuit[i].Gate.Degree = 1 - } manager.claims[i] = &zeroCheckLazyClaims{ wireI: i, evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 4c7406e138..c21d26c21f 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -62,7 +62,7 @@ func (e *zeroCheckLazyClaims) foldedSum(a small_rational.SmallRational) small_ra } func (e *zeroCheckLazyClaims) degree(int) int { - return 1 + e.manager.circuit[e.wireI].Gate.Degree + return e.manager.circuit[e.wireI].ZeroCheckDegree() } // verifyFinalEval finalizes the verification of w. @@ -226,7 +226,7 @@ func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []small_rational.Sma func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { wire := c.getWire() - degGJ := 1 + wire.Gate.Degree // guaranteed to be no smaller than the actual deg(gⱼ) + degGJ := wire.ZeroCheckDegree() // guaranteed to be no smaller than the actual deg(gⱼ) nbGateIn := len(c.input) // Both E and wᵢ (the input wires and the eq table) are multilinear, thus @@ -370,9 +370,6 @@ func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (m manager.circuit = circuit for i := range circuit { - if circuit[i].IsInput() { - circuit[i].Gate.Degree = 1 - } manager.claims[i] = &zeroCheckLazyClaims{ wireI: i, evaluationPoints: make([][]small_rational.SmallRational, 0, circuit[i].NbClaims()), From 08f6c4df5aaa4b48f625bfdbc98ddbceec4a97c6 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 20 Feb 2026 14:19:27 -0600 Subject: [PATCH 209/251] feat: shim for the old gate registry --- constraint/solver/gkrgates/registry.go | 185 ++++++++++++++++++++ internal/gkr/gkrtypes/gate.go | 45 ++++- internal/gkr/gkrtypes/gate_test.go | 232 +++++++++++-------------- std/gkrapi/api.go | 14 +- std/gkrapi/gkr/types.go | 5 + 5 files changed, 342 insertions(+), 139 deletions(-) create mode 100644 constraint/solver/gkrgates/registry.go diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go new file mode 100644 index 0000000000..dced0d3a4b --- /dev/null +++ b/constraint/solver/gkrgates/registry.go @@ -0,0 +1,185 @@ +// Package gkrgates contains the registry of GKR gates. +// +// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). +package gkrgates + +import ( + "errors" + "fmt" + "reflect" + "runtime" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/std/gkrapi/gkr" +) + +type registerSettings struct { + solvableVar int + degree int + name gkr.GateName + curves []ecc.ID +} + +const unset = -2 + +func newRegisterSettings() registerSettings { + return registerSettings{ + solvableVar: unset, + degree: unset, + } +} + +// RegisterOption is a functional option for Register. +// +// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). +type RegisterOption func(*registerSettings) error + +func noopRegisterOption(*registerSettings) error { return nil } + +// WithSolvableVar gives the index of a variable whose value can be uniquely determined +// from that of the other variables along with the gate's output. +// +// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). +func WithSolvableVar(solvableVar int) RegisterOption { + return func(s *registerSettings) error { + if s.solvableVar != unset { + return fmt.Errorf("solvable variable already set to %d", s.solvableVar) + } + s.solvableVar = solvableVar + return nil + } +} + +// WithUnverifiedSolvableVar sets the index of a variable whose value can be uniquely determined +// from that of the other variables along with the gate's output. +// +// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). +// This now functions identically to WithSolvableVar. +func WithUnverifiedSolvableVar(solvableVar int) RegisterOption { + return WithSolvableVar(solvableVar) +} + +// WithNoSolvableVar sets the gate as having no variable whose value can be uniquely determined +// from that of the other variables along with the gate's output. +// +// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). +func WithNoSolvableVar() RegisterOption { + return WithSolvableVar(-1) +} + +// WithUnverifiedDegree sets the degree of the gate. +// +// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). +// This now functions identically to WithDegree. +func WithUnverifiedDegree(degree int) RegisterOption { + return WithDegree(degree) +} + +// WithDegree sets the degree of the gate. +// +// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). +func WithDegree(degree int) RegisterOption { + return func(s *registerSettings) error { + if s.degree != unset { + return fmt.Errorf("gate degree already set to %d", s.degree) + } + s.degree = degree + return nil + } +} + +// WithName can be used to set a human-readable name for the gate. +// +// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). +func WithName(name gkr.GateName) RegisterOption { + return func(s *registerSettings) error { + if name == "" { + return errors.New("gate name must not be empty") + } + if s.name != "" { + return fmt.Errorf("gate name already set to %q", s.name) + } + s.name = name + return nil + } +} + +// WithCurves determines on which curves the gate is validated and allowed to be used. +// +// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). +func WithCurves(curves ...ecc.ID) RegisterOption { + return func(s *registerSettings) error { + if s.curves != nil { + return errors.New("gate curves already set") + } + s.curves = curves + return nil + } +} + +// gates is the deprecated gate registry. +var gates = make(map[gkr.GateName]gkr.GateFunction) + +// Register creates a gate object and stores it in the gates registry. +// +// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). +func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { + s := newRegisterSettings() + for _, opt := range options { + if err := opt(&s); err != nil { + return err + } + } + if s.name == "" { + s.name = GetDefaultGateName(f) + } + + if s.curves == nil { + s.curves = []ecc.ID{ecc.BN254} + } + + for _, curve := range s.curves { + compiled, err := gkrtypes.CompileGateFunction(f, nbIn, curve.ScalarField()) + if err != nil { + return err + } + + if s.degree == unset { + s.degree = compiled.Degree + } + if s.degree != compiled.Degree { + return fmt.Errorf("gate degree mismatch: expected %d, got %d on %s", s.degree, compiled.Degree, curve) + } + + if s.solvableVar == unset { + s.solvableVar = compiled.SolvableVar + } + if s.solvableVar != compiled.SolvableVar { + return fmt.Errorf("solvable variable mismatch: expected %d, got %d on %s", s.solvableVar, compiled.SolvableVar, curve) + } + } + + gates[s.name] = f + return nil +} + +// Get returns a registered gate function by name. +// If not found, it panics. +// +// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). +func Get(name gkr.GateName) gkr.GateFunction { + if f, ok := gates[name]; ok { + return f + } + panic("gate not found: " + string(name)) +} + +// GetDefaultGateName provides a standardized name for a gate function, depending on its package and name. +// NB: For anonymous functions, the name is the same no matter the implicit arguments provided. +// +// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). +func GetDefaultGateName(fn gkr.GateFunction) gkr.GateName { + fnptr := reflect.ValueOf(fn).Pointer() + return gkr.GateName(runtime.FuncForPC(fnptr).Name()) +} diff --git a/internal/gkr/gkrtypes/gate.go b/internal/gkr/gkrtypes/gate.go index 565b9cb1d7..2b6100db9a 100644 --- a/internal/gkr/gkrtypes/gate.go +++ b/internal/gkr/gkrtypes/gate.go @@ -227,9 +227,10 @@ func (gc *gateCompiler) remapIndices() { } } -// CompileGateFunction compiles a gate function into a GateBytecode. -// The gate function should be of type gkr.GateFunction. -func CompileGateFunction(f gkr.GateFunction, nbInputs int) (GateBytecode, error) { +// CompileGateFunction converts a gate function into a SerializableGate. +// This consists of compiling into bytecode as well as computing gate metadata +// such as degree and solvable var index for the given field. +func CompileGateFunction(f gkr.GateFunction, nbInputs int, field *big.Int) (SerializableGate, error) { // Create compiling API compiler := gateCompiler{ constantIndex: make(map[string]uint16), @@ -246,16 +247,22 @@ func CompileGateFunction(f gkr.GateFunction, nbInputs int) (GateBytecode, error) out := f(&compiler, inputs...) outVar, ok := out.(compilationVar) if !ok { - return GateBytecode{}, errors.New("gate function must return a variable; constant values must be hard-coded into the gates that use them") + return SerializableGate{}, errors.New("gate function must return a variable; constant values must be hard-coded into the gates that use them") } if len(compiler.instructions) == 0 { // No operations recorded, but not all is lost yet. // If the output simply mirrors the last input, we can still represent // it in bytecode, as the evaluator returns the last stack frame element. if int(outVar.id) == len(compiler.constants)+nbInputs-1 { - return GateBytecode{}, nil + // Identity-like gate: returns last input unchanged + // Degree is 1, and the returned variable is solvable + return SerializableGate{ + NbIn: nbInputs, + Degree: 1, + SolvableVar: nbInputs - 1, + }, nil } - return GateBytecode{}, errors.New("only non-trivial or last-reflective gate functions supported") + return SerializableGate{}, errors.New("only non-trivial or last-reflective gate functions supported") } // All instructions after the output are no-ops. Prune them and the corresponding variables. @@ -266,9 +273,33 @@ func CompileGateFunction(f gkr.GateFunction, nbInputs int) (GateBytecode, error) // Remap indices from temporary layout to final layout compiler.remapIndices() - return GateBytecode{ + bytecode := GateBytecode{ Instructions: compiler.GetInstructions(), Constants: compiler.constants, + } + + // Compute degree and solvable variable + tester := gateTester{mod: field} + tester.setGate(bytecode, nbInputs) + + degree := len(tester.fitPoly(bytecode.EstimateDegree(nbInputs))) - 1 + if degree == -1 { + return SerializableGate{}, errors.New("cannot find degree for gate") + } + + solvableVar := -1 + for j := range nbInputs { + if tester.isAdditive(j) { + solvableVar = j + break + } + } + + return SerializableGate{ + Evaluate: bytecode, + NbIn: nbInputs, + Degree: degree, + SolvableVar: solvableVar, }, nil } diff --git a/internal/gkr/gkrtypes/gate_test.go b/internal/gkr/gkrtypes/gate_test.go index 0bbf6eb0d2..3bc044959d 100644 --- a/internal/gkr/gkrtypes/gate_test.go +++ b/internal/gkr/gkrtypes/gate_test.go @@ -11,111 +11,124 @@ import ( "github.com/stretchr/testify/require" ) -// TestCompiledGateWithConstants tests that gates can use constant values -func TestCompiledGateWithConstants(t *testing.T) { - // Create a gate that adds a constant: f(x) = x + 5 - addConstantGate := func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - five := big.NewInt(5) - return api.Add(in[0], five) - } +func field() *big.Int { return ecc.BN254.ScalarField() } - const nbIn = 1 - // Compile the gate - compiled, err := CompileGateFunction(addConstantGate, nbIn) - require.NoError(t, err) - - // Verify the gate has constants - assert.NotEmpty(t, compiled.Constants, "Expected compiled gate to have constants") - - // Verify the constant value - assert.Equal(t, 0, compiled.Constants[0].Cmp(big.NewInt(5)), "Expected constant to be 5, got %s", compiled.Constants[0].String()) - - // Verify instructions reference the constant - assert.NotEmpty(t, compiled.Instructions, "Expected compiled gate to have instructions") - - inst := compiled.Instructions[0] - assert.Equal(t, OpAdd, inst.Op, "Expected OpAdd, got %v", inst.Op) - - // Verify index layout: constants at [0, nbConsts), inputs at [nbConsts, nbConsts+nbInputs) - nbConsts := len(compiled.Constants) - hasConstant := false - hasInput := false - for _, idx := range inst.Inputs { - if idx < uint16(nbConsts) { - hasConstant = true - assert.Equal(t, uint16(0), idx, "Expected constant index 0, got %d", idx) - } else if idx >= uint16(nbConsts) && idx < uint16(nbConsts+nbIn) { - hasInput = true - inputIdx := idx - uint16(nbConsts) - assert.Equal(t, uint16(0), inputIdx, "Expected input index 0 (remapped to %d), got %d", nbConsts, idx) - } - } +// test gate functions - assert.True(t, hasConstant, "Expected instruction to reference a constant") - assert.True(t, hasInput, "Expected instruction to reference an input") +func mul3(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Mul(api.Mul(in[0], in[1]), in[2]) } -// TestCompiledGateWithMultipleConstants tests gates with multiple different constants -func TestCompiledGateWithMultipleConstants(t *testing.T) { - // Create a gate: f(x) = (x + 3) * 7 - complexGate := func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - three := big.NewInt(3) - seven := big.NewInt(7) - sum := api.Add(in[0], three) - return api.Mul(sum, seven) - } - - // Compile the gate - const nbIn = 1 - compiled, err := CompileGateFunction(complexGate, nbIn) - require.NoError(t, err) +func add3(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Add(api.Add(in[0], in[1]), in[2]) +} - // Verify we have two constants - assert.Equal(t, 2, len(compiled.Constants), "Expected 2 constants, got %d", len(compiled.Constants)) +func addMul(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Mul(api.Add(in[0], in[1]), in[2]) +} - // Verify the constants are 3 and 7 - constantValues := make(map[int64]bool) - for _, c := range compiled.Constants { - constantValues[c.Int64()] = true - } +func addMul2(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Add(in[0], api.Mul(in[1], in[2])) +} - assert.True(t, constantValues[3] && constantValues[7], "Expected constants to be 3 and 7") +func sqrAdd2(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Add(api.Mul(in[0], in[0]), in[1], in[1]) +} - t.Logf("Successfully compiled gate with %d constants: %v", - len(compiled.Constants), compiled.Constants) +func mulAdd(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Add(api.Mul(in[0], in[1]), in[1]) } -// TestConstantDeduplication tests that identical constants are deduplicated -func TestConstantDeduplication(t *testing.T) { - // Create a gate that uses the same constant multiple times: f(x, y) = (x + 5) + (y + 5) - gateWithDuplicates := func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - five1 := big.NewInt(5) - five2 := big.NewInt(5) // Same value, should be deduplicated - sum1 := api.Add(in[0], five1) - sum2 := api.Add(in[1], five2) - return api.Add(sum1, sum2) +func TestConstants(t *testing.T) { + tests := []struct { + name string + f gkr.GateFunction + nbIn int + constants []int64 // expected constant values (duplicates should be deduplicated) + }{ + { + "x+5", + func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Add(in[0], big.NewInt(5)) + }, + 1, + []int64{5}, + }, + { + "(x+3)*7", + func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + return api.Mul(api.Add(in[0], big.NewInt(3)), big.NewInt(7)) + }, + 1, + []int64{3, 7}, + }, + { + "(x+5)+(y+5)", // tests deduplication + func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { + sum1 := api.Add(in[0], big.NewInt(5)) + sum2 := api.Add(in[1], big.NewInt(5)) + return api.Add(sum1, sum2) + }, + 2, + []int64{5}, + }, } - const nbIn = 2 - // Compile the gate - compiled, err := CompileGateFunction(gateWithDuplicates, nbIn) - require.NoError(t, err) - - // Verify only one constant is stored (deduplication) - assert.Equal(t, 1, len(compiled.Constants), "Expected 1 deduplicated constant, got %d", len(compiled.Constants)) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + compiled, err := CompileGateFunction(tt.f, tt.nbIn, field()) + require.NoError(t, err) + + assert.Equal(t, len(tt.constants), len(compiled.Evaluate.Constants)) + + gotValues := make(map[int64]bool) + for _, c := range compiled.Evaluate.Constants { + gotValues[c.Int64()] = true + } + for _, want := range tt.constants { + assert.True(t, gotValues[want], "expected constant %d", want) + } + }) + } +} - assert.Equal(t, 0, compiled.Constants[0].Cmp(big.NewInt(5)), "Expected constant to be 5, got %s", compiled.Constants[0].String()) +func TestCompileGateFunction(t *testing.T) { + tests := []struct { + name string + f gkr.GateFunction + nbIn int + degree int + solvableVar int // -1 means none, otherwise first additive var + }{ + {"identity", Identity, 1, 1, 0}, + {"x+y", Add2, 2, 1, 0}, + {"x-y", Sub2, 2, 1, 0}, + {"x*y", Mul2, 2, 2, -1}, + {"x*y*z", mul3, 3, 3, -1}, + {"x+y+z", add3, 3, 1, 0}, + {"(x+y)*z", addMul, 3, 2, -1}, + {"x+y*z", addMul2, 3, 2, 0}, + {"x²+2y", sqrAdd2, 2, 2, 1}, + {"x*y+y", mulAdd, 2, 2, -1}, + } - t.Logf("Successfully deduplicated constants: %d unique constant(s)", - len(compiled.Constants)) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + compiled, err := CompileGateFunction(tt.f, tt.nbIn, field()) + require.NoError(t, err) + assert.Equal(t, tt.nbIn, compiled.NbIn) + assert.Equal(t, tt.degree, compiled.Degree) + assert.Equal(t, tt.solvableVar, compiled.SolvableVar) + }) + } } func testFitPoly(t *testing.T, name string, f gkr.GateFunction, nbIn, degree, maxDegree int) { t.Run(name, func(t *testing.T) { - tester := gateTester{mod: ecc.BN254.ScalarField()} - g, err := CompileGateFunction(f, nbIn) + tester := gateTester{mod: field()} + g, err := CompileGateFunction(f, nbIn, field()) require.NoError(t, err) - tester.setGate(g, nbIn) + tester.setGate(g.Evaluate, nbIn) require.Equal(t, degree, len(tester.fitPoly(maxDegree))-1) }) } @@ -125,32 +138,17 @@ func TestFitPoly(t *testing.T) { testFitPoly(t, "add", Add2, 2, 1, 2) testFitPoly(t, "sub", Sub2, 2, 1, 4) testFitPoly(t, "mul", Mul2, 2, 2, 2) - - // x * y * z has degree 3 - mul3 := func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - return api.Mul(api.Mul(in[0], in[1]), in[2]) - } testFitPoly(t, "mul3", mul3, 3, 3, 4) - - // x + y + z has degree 1 - add3 := func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - return api.Add(api.Add(in[0], in[1]), in[2]) - } testFitPoly(t, "add3", add3, 3, 1, 4) - - // (x + y) * z has degree 2 - addMul := func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - return api.Mul(api.Add(in[0], in[1]), in[2]) - } testFitPoly(t, "addMul", addMul, 3, 2, 4) } func testIsAdditive(t *testing.T, name string, f gkr.GateFunction, isAdditive ...bool) { t.Run(name, func(t *testing.T) { - tester := gateTester{mod: ecc.BN254.ScalarField()} - g, err := CompileGateFunction(f, len(isAdditive)) + tester := gateTester{mod: field()} + g, err := CompileGateFunction(f, len(isAdditive), field()) require.NoError(t, err) - tester.setGate(g, len(isAdditive)) + tester.setGate(g.Evaluate, len(isAdditive)) for i := range isAdditive { assert.Equal(t, isAdditive[i], tester.isAdditive(i)) } @@ -160,26 +158,8 @@ func testIsAdditive(t *testing.T, name string, f gkr.GateFunction, isAdditive .. func TestIsAdditive(t *testing.T) { testIsAdditive(t, "x+y", Add2, true, true) testIsAdditive(t, "x-y", Sub2, true, true) - testIsAdditive(t, "x*y", Mul2, false, false) // neither additive (degree 2 monomial) - - // x additive, y and z not - testIsAdditive(t, "x+y*z", - func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - return api.Add(in[0], api.Mul(in[1], in[2])) - }, - true, false, false) - - // x not additive (degree 2), y additive - testIsAdditive(t, "x²+2y", - func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - return api.Add(api.Mul(in[0], in[0]), in[1], in[1]) - }, - false, true) - - // y appears in both degree-1 and degree-2 terms, so not additive - testIsAdditive(t, "x*y+y", - func(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { - return api.Add(api.Mul(in[0], in[1]), in[1]) - }, - false, false) + testIsAdditive(t, "x*y", Mul2, false, false) + testIsAdditive(t, "x+y*z", addMul2, true, false, false) + testIsAdditive(t, "x²+2y", sqrAdd2, false, true) + testIsAdditive(t, "x*y+y", mulAdd, false, false) } diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index be14e485d3..49557cd133 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -1,6 +1,7 @@ package gkrapi import ( + "github.com/consensys/gnark/constraint/solver/gkrgates" "github.com/consensys/gnark/frontend" gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" @@ -9,12 +10,6 @@ import ( ) type ( - gateID uint16 - wire struct { - Gate gateID - Inputs []int - } - API struct { circuit gkrtypes.GadgetCircuit assignments gadget.WireAssignment @@ -36,6 +31,13 @@ func (api *API) Gate(gate gkr.GateFunction, inputs ...gkr.Variable) gkr.Variable return gkr.Variable(len(api.circuit) - 1) } +// NamedGate adds a gate looked up by name from the registry. +// +// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). +func (api *API) NamedGate(gate gkr.GateName, inputs ...gkr.Variable) gkr.Variable { + return api.Gate(gkrgates.Get(gate), inputs...) +} + func (api *API) gate2PlusIn(gate gkr.GateFunction, in1, in2 gkr.Variable, in ...gkr.Variable) gkr.Variable { inCombined := make([]gkr.Variable, 2+len(in)) inCombined[0] = in1 diff --git a/std/gkrapi/gkr/types.go b/std/gkrapi/gkr/types.go index 4610c70899..1006bbff2e 100644 --- a/std/gkrapi/gkr/types.go +++ b/std/gkrapi/gkr/types.go @@ -42,3 +42,8 @@ type GateAPI interface { // using the given GateAPI. // It is used to define custom gates in GKR circuits. type GateFunction func(GateAPI, ...frontend.Variable) frontend.Variable + +// GateName is a string representing a (human-readable) name for a GKR gate. +// +// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). +type GateName string From 36179644717b784232ae15db6e536fe49aa975a0 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 20 Feb 2026 14:19:59 -0600 Subject: [PATCH 210/251] fix: commit all changes --- internal/gkr/gkrtypes/types.go | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 0e415ad712..9089de41b3 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -255,7 +255,7 @@ func Mul2(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { // BlueprintSolve is the interface for GKR solve blueprints type BlueprintSolve interface { - constraint.Blueprint + constraint.BlueprintStateful[constraint.U64] SetNbInstances(nbInstances uint32) } @@ -277,7 +277,6 @@ func CompileCircuit(c GadgetCircuit, mod *big.Int) (SerializableCircuit, error) // compile the gate and compute metadata curWireIn := make([]bool, len(c)) // curWireIn[j] = true iff i takes j as input. - tester := gateTester{mod: mod} // tester computes the gate's degree res := make(SerializableCircuit, len(c)) var err error for i := range c { @@ -301,24 +300,11 @@ func CompileCircuit(c GadgetCircuit, mod *big.Int) (SerializableCircuit, error) } c[i].Gate.NbIn = len(c[i].Inputs) - if res[i].Gate.Evaluate, err = CompileGateFunction(c[i].Gate.Evaluate, c[i].Gate.NbIn); err != nil { + if res[i].Gate, err = CompileGateFunction(c[i].Gate.Evaluate, c[i].Gate.NbIn, mod); err != nil { return nil, err } - - tester.setGate(res[i].Gate.Evaluate, c[i].Gate.NbIn) - - c[i].Gate.Degree = len(tester.fitPoly(res[i].Gate.Evaluate.EstimateDegree(len(c[i].Inputs)))) - 1 - if c[i].Gate.Degree == -1 { - return nil, errors.New("cannot find degree for gate") - } - - c[i].Gate.SolvableVar = -1 - for j := range c[i].Gate.NbIn { - if tester.isAdditive(j) { - c[i].Gate.SolvableVar = j - break - } - } + c[i].Gate.Degree = res[i].Gate.Degree + c[i].Gate.SolvableVar = res[i].Gate.SolvableVar } // copy metadata from c to res From 27710025b4d0510d05e9f071b66b81f28f90f157 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 20 Feb 2026 15:11:00 -0600 Subject: [PATCH 211/251] fix: lint errors --- constraint/solver/gkrgates/registry.go | 37 +++++--------------------- std/gkrapi/api.go | 2 +- 2 files changed, 7 insertions(+), 32 deletions(-) diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index dced0d3a4b..71f7de9122 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -17,7 +17,7 @@ import ( type registerSettings struct { solvableVar int degree int - name gkr.GateName + name gkr.GateName // nolint SA1019 curves []ecc.ID } @@ -31,16 +31,10 @@ func newRegisterSettings() registerSettings { } // RegisterOption is a functional option for Register. -// -// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). type RegisterOption func(*registerSettings) error -func noopRegisterOption(*registerSettings) error { return nil } - // WithSolvableVar gives the index of a variable whose value can be uniquely determined // from that of the other variables along with the gate's output. -// -// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). func WithSolvableVar(solvableVar int) RegisterOption { return func(s *registerSettings) error { if s.solvableVar != unset { @@ -53,8 +47,6 @@ func WithSolvableVar(solvableVar int) RegisterOption { // WithUnverifiedSolvableVar sets the index of a variable whose value can be uniquely determined // from that of the other variables along with the gate's output. -// -// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). // This now functions identically to WithSolvableVar. func WithUnverifiedSolvableVar(solvableVar int) RegisterOption { return WithSolvableVar(solvableVar) @@ -62,23 +54,17 @@ func WithUnverifiedSolvableVar(solvableVar int) RegisterOption { // WithNoSolvableVar sets the gate as having no variable whose value can be uniquely determined // from that of the other variables along with the gate's output. -// -// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). func WithNoSolvableVar() RegisterOption { return WithSolvableVar(-1) } // WithUnverifiedDegree sets the degree of the gate. -// -// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). // This now functions identically to WithDegree. func WithUnverifiedDegree(degree int) RegisterOption { return WithDegree(degree) } // WithDegree sets the degree of the gate. -// -// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). func WithDegree(degree int) RegisterOption { return func(s *registerSettings) error { if s.degree != unset { @@ -90,9 +76,7 @@ func WithDegree(degree int) RegisterOption { } // WithName can be used to set a human-readable name for the gate. -// -// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). -func WithName(name gkr.GateName) RegisterOption { +func WithName(name gkr.GateName) RegisterOption { // nolint SA1019 return func(s *registerSettings) error { if name == "" { return errors.New("gate name must not be empty") @@ -106,8 +90,6 @@ func WithName(name gkr.GateName) RegisterOption { } // WithCurves determines on which curves the gate is validated and allowed to be used. -// -// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). func WithCurves(curves ...ecc.ID) RegisterOption { return func(s *registerSettings) error { if s.curves != nil { @@ -118,12 +100,9 @@ func WithCurves(curves ...ecc.ID) RegisterOption { } } -// gates is the deprecated gate registry. -var gates = make(map[gkr.GateName]gkr.GateFunction) +var gates = make(map[gkr.GateName]gkr.GateFunction) // nolint SA1019 // Register creates a gate object and stores it in the gates registry. -// -// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { s := newRegisterSettings() for _, opt := range options { @@ -166,9 +145,7 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { // Get returns a registered gate function by name. // If not found, it panics. -// -// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). -func Get(name gkr.GateName) gkr.GateFunction { +func Get(name gkr.GateName) gkr.GateFunction { // nolint SA1019 if f, ok := gates[name]; ok { return f } @@ -177,9 +154,7 @@ func Get(name gkr.GateName) gkr.GateFunction { // GetDefaultGateName provides a standardized name for a gate function, depending on its package and name. // NB: For anonymous functions, the name is the same no matter the implicit arguments provided. -// -// Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate(). -func GetDefaultGateName(fn gkr.GateFunction) gkr.GateName { +func GetDefaultGateName(fn gkr.GateFunction) gkr.GateName { // nolint SA1019 fnptr := reflect.ValueOf(fn).Pointer() - return gkr.GateName(runtime.FuncForPC(fnptr).Name()) + return gkr.GateName(runtime.FuncForPC(fnptr).Name()) // nolint SA1019 } diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index 49557cd133..68c481e343 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -1,7 +1,7 @@ package gkrapi import ( - "github.com/consensys/gnark/constraint/solver/gkrgates" + "github.com/consensys/gnark/constraint/solver/gkrgates" // nolint SA1019 "github.com/consensys/gnark/frontend" gadget "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtypes" From a540ffe98ee028b8994adb06bb2f2ebc157f6e40 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 20 Feb 2026 15:43:44 -0600 Subject: [PATCH 212/251] test: r1cs roundtrip for gkr --- constraint/babybear/r1cs_test.go | 6 ++ constraint/bls12-377/r1cs_test.go | 6 ++ constraint/bls12-381/r1cs_test.go | 6 ++ constraint/bn254/r1cs_test.go | 6 ++ constraint/bw6-761/r1cs_test.go | 6 ++ constraint/koalabear/r1cs_test.go | 6 ++ constraint/tinyfield/r1cs_test.go | 6 ++ internal/backend/circuits/gkr.go | 57 +++++++++++++++++++ .../representations/tests/r1cs.go.tmpl | 11 +++- 9 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 internal/backend/circuits/gkr.go diff --git a/constraint/babybear/r1cs_test.go b/constraint/babybear/r1cs_test.go index 57ea65f93a..278f762c87 100644 --- a/constraint/babybear/r1cs_test.go +++ b/constraint/babybear/r1cs_test.go @@ -71,7 +71,13 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed + // Blueprint implementations may have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. + blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { + return reflect.TypeOf(a) == reflect.TypeOf(b) + }) if diff := cmp.Diff(r1cs1, &reconstructed, + blueprintComparer, cmpopts.IgnoreFields(cs.R1CS{}, "System.q", "field", diff --git a/constraint/bls12-377/r1cs_test.go b/constraint/bls12-377/r1cs_test.go index 9bed2c335a..f95a92fa40 100644 --- a/constraint/bls12-377/r1cs_test.go +++ b/constraint/bls12-377/r1cs_test.go @@ -66,7 +66,13 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed + // Blueprint implementations may have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. + blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { + return reflect.TypeOf(a) == reflect.TypeOf(b) + }) if diff := cmp.Diff(r1cs1, &reconstructed, + blueprintComparer, cmpopts.IgnoreFields(cs.R1CS{}, "System.q", "field", diff --git a/constraint/bls12-381/r1cs_test.go b/constraint/bls12-381/r1cs_test.go index 9b03b96934..e2013c184e 100644 --- a/constraint/bls12-381/r1cs_test.go +++ b/constraint/bls12-381/r1cs_test.go @@ -66,7 +66,13 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed + // Blueprint implementations may have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. + blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { + return reflect.TypeOf(a) == reflect.TypeOf(b) + }) if diff := cmp.Diff(r1cs1, &reconstructed, + blueprintComparer, cmpopts.IgnoreFields(cs.R1CS{}, "System.q", "field", diff --git a/constraint/bn254/r1cs_test.go b/constraint/bn254/r1cs_test.go index 612323e758..a564e81764 100644 --- a/constraint/bn254/r1cs_test.go +++ b/constraint/bn254/r1cs_test.go @@ -66,7 +66,13 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed + // Blueprint implementations may have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. + blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { + return reflect.TypeOf(a) == reflect.TypeOf(b) + }) if diff := cmp.Diff(r1cs1, &reconstructed, + blueprintComparer, cmpopts.IgnoreFields(cs.R1CS{}, "System.q", "field", diff --git a/constraint/bw6-761/r1cs_test.go b/constraint/bw6-761/r1cs_test.go index 31036bd7d3..0b9cc60a90 100644 --- a/constraint/bw6-761/r1cs_test.go +++ b/constraint/bw6-761/r1cs_test.go @@ -69,7 +69,13 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed + // Blueprint implementations may have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. + blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { + return reflect.TypeOf(a) == reflect.TypeOf(b) + }) if diff := cmp.Diff(r1cs1, &reconstructed, + blueprintComparer, cmpopts.IgnoreFields(cs.R1CS{}, "System.q", "field", diff --git a/constraint/koalabear/r1cs_test.go b/constraint/koalabear/r1cs_test.go index beb38e4893..50dec1f1f0 100644 --- a/constraint/koalabear/r1cs_test.go +++ b/constraint/koalabear/r1cs_test.go @@ -71,7 +71,13 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed + // Blueprint implementations may have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. + blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { + return reflect.TypeOf(a) == reflect.TypeOf(b) + }) if diff := cmp.Diff(r1cs1, &reconstructed, + blueprintComparer, cmpopts.IgnoreFields(cs.R1CS{}, "System.q", "field", diff --git a/constraint/tinyfield/r1cs_test.go b/constraint/tinyfield/r1cs_test.go index 08b772c6b1..9ff0c680c8 100644 --- a/constraint/tinyfield/r1cs_test.go +++ b/constraint/tinyfield/r1cs_test.go @@ -74,7 +74,13 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed + // Blueprint implementations may have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. + blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { + return reflect.TypeOf(a) == reflect.TypeOf(b) + }) if diff := cmp.Diff(r1cs1, &reconstructed, + blueprintComparer, cmpopts.IgnoreFields(cs.R1CS{}, "System.q", "field", diff --git a/internal/backend/circuits/gkr.go b/internal/backend/circuits/gkr.go new file mode 100644 index 0000000000..42aa699070 --- /dev/null +++ b/internal/backend/circuits/gkr.go @@ -0,0 +1,57 @@ +package circuits + +import ( + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/gkrapi" + "github.com/consensys/gnark/std/gkrapi/gkr" + _ "github.com/consensys/gnark/std/hash/all" // register hash functions for GKR +) + +type gkrCubeCircuit struct { + X []frontend.Variable + Square []frontend.Variable `gnark:",public"` + Cube []frontend.Variable `gnark:",public"` +} + +func (c *gkrCubeCircuit) Define(api frontend.API) error { + gkrApi, err := gkrapi.New(api) + if err != nil { + return err + } + x := gkrApi.NewInput() + y := gkrApi.Mul(x, x) + z := gkrApi.Mul(x, y) + + gkrApi.Export(y) + + gkrCircuit, err := gkrApi.Compile("MIMC") + if err != nil { + return err + } + + for i := range c.X { + out, err := gkrCircuit.AddInstance(map[gkr.Variable]frontend.Variable{x: c.X[i]}) + if err != nil { + return err + } + api.AssertIsEqual(out[y], c.Square[i]) + api.AssertIsEqual(out[z], c.Cube[i]) + } + return nil +} + +func init() { + // GKR only supported on these curves + curves := []ecc.ID{ecc.BN254, ecc.BLS12_381, ecc.BLS12_377, ecc.BW6_761} + + circuit := &gkrCubeCircuit{ + X: make([]frontend.Variable, 2), + Square: make([]frontend.Variable, 2), + Cube: make([]frontend.Variable, 2), + } + good := &gkrCubeCircuit{X: []frontend.Variable{3, 5}, Square: []frontend.Variable{9, 25}, Cube: []frontend.Variable{27, 125}} + bad := &gkrCubeCircuit{X: []frontend.Variable{3, 5}, Square: []frontend.Variable{9, 25}, Cube: []frontend.Variable{27, 126}} + + addEntry("gkr_cube", circuit, good, bad, curves) +} diff --git a/internal/generator/backend/template/representations/tests/r1cs.go.tmpl b/internal/generator/backend/template/representations/tests/r1cs.go.tmpl index 636c9cc7f9..0d296693fa 100644 --- a/internal/generator/backend/template/representations/tests/r1cs.go.tmpl +++ b/internal/generator/backend/template/representations/tests/r1cs.go.tmpl @@ -1,8 +1,9 @@ import ( "bytes" - "testing" "reflect" + "testing" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -76,7 +77,13 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed - if diff := cmp.Diff(r1cs1, &reconstructed, + // Blueprint implementations may have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. + blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { + return reflect.TypeOf(a) == reflect.TypeOf(b) + }) + if diff := cmp.Diff(r1cs1, &reconstructed, + blueprintComparer, cmpopts.IgnoreFields(cs.R1CS{}, "System.q", "field", From 9f6257fd25e6505f86d992b387d727ccc263274e Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 20 Feb 2026 15:57:33 -0600 Subject: [PATCH 213/251] style: concise --- internal/backend/circuits/gkr.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/internal/backend/circuits/gkr.go b/internal/backend/circuits/gkr.go index 42aa699070..56698e2bdf 100644 --- a/internal/backend/circuits/gkr.go +++ b/internal/backend/circuits/gkr.go @@ -1,7 +1,7 @@ package circuits import ( - "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/gkrapi" "github.com/consensys/gnark/std/gkrapi/gkr" @@ -42,9 +42,6 @@ func (c *gkrCubeCircuit) Define(api frontend.API) error { } func init() { - // GKR only supported on these curves - curves := []ecc.ID{ecc.BN254, ecc.BLS12_381, ecc.BLS12_377, ecc.BW6_761} - circuit := &gkrCubeCircuit{ X: make([]frontend.Variable, 2), Square: make([]frontend.Variable, 2), @@ -53,5 +50,5 @@ func init() { good := &gkrCubeCircuit{X: []frontend.Variable{3, 5}, Square: []frontend.Variable{9, 25}, Cube: []frontend.Variable{27, 125}} bad := &gkrCubeCircuit{X: []frontend.Variable{3, 5}, Square: []frontend.Variable{9, 25}, Cube: []frontend.Variable{27, 126}} - addEntry("gkr_cube", circuit, good, bad, curves) + addEntry("gkr_cube", circuit, good, bad, gnark.Curves()) } From 16c01a324db2f0c933660054c3db6d74a1fb5dfe Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sun, 22 Feb 2026 23:08:55 -0600 Subject: [PATCH 214/251] fix: blueprint comparison --- constraint/babybear/r1cs_test.go | 15 ++++++-- constraint/bls12-377/r1cs_test.go | 15 ++++++-- constraint/bls12-381/r1cs_test.go | 15 ++++++-- constraint/blueprint.go | 7 ++++ constraint/bn254/r1cs_test.go | 15 ++++++-- constraint/bw6-761/r1cs_test.go | 15 ++++++-- constraint/koalabear/r1cs_test.go | 15 ++++++-- constraint/tinyfield/r1cs_test.go | 15 ++++++-- internal/backend/circuits/circuits.go | 14 +++++++ .../backend/template/gkr/blueprint.go.tmpl | 29 ++++++++++++++ .../representations/tests/r1cs.go.tmpl | 17 +++++++-- internal/gkr/bls12-377/blueprint.go | 38 +++++++++++++++++++ internal/gkr/bls12-381/blueprint.go | 38 +++++++++++++++++++ internal/gkr/bn254/blueprint.go | 38 +++++++++++++++++++ internal/gkr/bw6-761/blueprint.go | 38 +++++++++++++++++++ 15 files changed, 292 insertions(+), 32 deletions(-) diff --git a/constraint/babybear/r1cs_test.go b/constraint/babybear/r1cs_test.go index 278f762c87..b5d49c6a16 100644 --- a/constraint/babybear/r1cs_test.go +++ b/constraint/babybear/r1cs_test.go @@ -10,6 +10,7 @@ import ( "reflect" "testing" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -32,6 +33,9 @@ func TestSerialization(t *testing.T) { for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] + if !tc.SupportsCurve(ecc.UNKNOWN) { + t.Skip("circuit does not support small fields") + } builder := r1cs.NewBuilder[constraint.U32] if name == "commit" { // smallfield builders do not support commitment. We use the wrapper which has the methods @@ -71,10 +75,13 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed - // Blueprint implementations may have unexported fields that are not serialized - // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. - blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { - return reflect.TypeOf(a) == reflect.TypeOf(b) + // Some blueprints have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Use Equal method if available. + blueprintComparer := cmp.Comparer(func(a, b constraint.BlueprintComparable) bool { + if a == nil { + return b == nil + } + return a.Equal(b) }) if diff := cmp.Diff(r1cs1, &reconstructed, blueprintComparer, diff --git a/constraint/bls12-377/r1cs_test.go b/constraint/bls12-377/r1cs_test.go index f95a92fa40..bed336f0f9 100644 --- a/constraint/bls12-377/r1cs_test.go +++ b/constraint/bls12-377/r1cs_test.go @@ -10,6 +10,7 @@ import ( "reflect" "testing" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -31,6 +32,9 @@ func TestSerialization(t *testing.T) { for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] + if !tc.SupportsCurve(ecc.BLS12_377) { + t.Skip("circuit does not support this curve") + } builder := r1cs.NewBuilder[constraint.U64] r1cs1, err := frontend.CompileGeneric(fr.Modulus(), builder, tc.Circuit) @@ -66,10 +70,13 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed - // Blueprint implementations may have unexported fields that are not serialized - // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. - blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { - return reflect.TypeOf(a) == reflect.TypeOf(b) + // Some blueprints have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Use Equal method if available. + blueprintComparer := cmp.Comparer(func(a, b constraint.BlueprintComparable) bool { + if a == nil { + return b == nil + } + return a.Equal(b) }) if diff := cmp.Diff(r1cs1, &reconstructed, blueprintComparer, diff --git a/constraint/bls12-381/r1cs_test.go b/constraint/bls12-381/r1cs_test.go index e2013c184e..c31db79430 100644 --- a/constraint/bls12-381/r1cs_test.go +++ b/constraint/bls12-381/r1cs_test.go @@ -10,6 +10,7 @@ import ( "reflect" "testing" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -31,6 +32,9 @@ func TestSerialization(t *testing.T) { for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] + if !tc.SupportsCurve(ecc.BLS12_381) { + t.Skip("circuit does not support this curve") + } builder := r1cs.NewBuilder[constraint.U64] r1cs1, err := frontend.CompileGeneric(fr.Modulus(), builder, tc.Circuit) @@ -66,10 +70,13 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed - // Blueprint implementations may have unexported fields that are not serialized - // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. - blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { - return reflect.TypeOf(a) == reflect.TypeOf(b) + // Some blueprints have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Use Equal method if available. + blueprintComparer := cmp.Comparer(func(a, b constraint.BlueprintComparable) bool { + if a == nil { + return b == nil + } + return a.Equal(b) }) if diff := cmp.Diff(r1cs1, &reconstructed, blueprintComparer, diff --git a/constraint/blueprint.go b/constraint/blueprint.go index eea0265a7a..4342d961af 100644 --- a/constraint/blueprint.go +++ b/constraint/blueprint.go @@ -84,3 +84,10 @@ type Compressible interface { // Compress interprets the objects as a LinearExpression and encodes it as a []uint32. Compress(to *[]uint32) } + +// BlueprintComparable is implemented by blueprints that support equality comparison +// for serialization round-trip testing. +type BlueprintComparable interface { + Blueprint + Equal(BlueprintComparable) bool +} diff --git a/constraint/bn254/r1cs_test.go b/constraint/bn254/r1cs_test.go index a564e81764..e5ed27f169 100644 --- a/constraint/bn254/r1cs_test.go +++ b/constraint/bn254/r1cs_test.go @@ -10,6 +10,7 @@ import ( "reflect" "testing" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -31,6 +32,9 @@ func TestSerialization(t *testing.T) { for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] + if !tc.SupportsCurve(ecc.BN254) { + t.Skip("circuit does not support this curve") + } builder := r1cs.NewBuilder[constraint.U64] r1cs1, err := frontend.CompileGeneric(fr.Modulus(), builder, tc.Circuit) @@ -66,10 +70,13 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed - // Blueprint implementations may have unexported fields that are not serialized - // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. - blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { - return reflect.TypeOf(a) == reflect.TypeOf(b) + // Some blueprints have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Use Equal method if available. + blueprintComparer := cmp.Comparer(func(a, b constraint.BlueprintComparable) bool { + if a == nil { + return b == nil + } + return a.Equal(b) }) if diff := cmp.Diff(r1cs1, &reconstructed, blueprintComparer, diff --git a/constraint/bw6-761/r1cs_test.go b/constraint/bw6-761/r1cs_test.go index 0b9cc60a90..ac6ca44547 100644 --- a/constraint/bw6-761/r1cs_test.go +++ b/constraint/bw6-761/r1cs_test.go @@ -10,6 +10,7 @@ import ( "reflect" "testing" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -31,6 +32,9 @@ func TestSerialization(t *testing.T) { for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] + if !tc.SupportsCurve(ecc.BW6_761) { + t.Skip("circuit does not support this curve") + } if testing.Short() && name != "reference_small" { return } @@ -69,10 +73,13 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed - // Blueprint implementations may have unexported fields that are not serialized - // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. - blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { - return reflect.TypeOf(a) == reflect.TypeOf(b) + // Some blueprints have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Use Equal method if available. + blueprintComparer := cmp.Comparer(func(a, b constraint.BlueprintComparable) bool { + if a == nil { + return b == nil + } + return a.Equal(b) }) if diff := cmp.Diff(r1cs1, &reconstructed, blueprintComparer, diff --git a/constraint/koalabear/r1cs_test.go b/constraint/koalabear/r1cs_test.go index 50dec1f1f0..61f960bdc6 100644 --- a/constraint/koalabear/r1cs_test.go +++ b/constraint/koalabear/r1cs_test.go @@ -10,6 +10,7 @@ import ( "reflect" "testing" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -32,6 +33,9 @@ func TestSerialization(t *testing.T) { for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] + if !tc.SupportsCurve(ecc.UNKNOWN) { + t.Skip("circuit does not support small fields") + } builder := r1cs.NewBuilder[constraint.U32] if name == "commit" { // smallfield builders do not support commitment. We use the wrapper which has the methods @@ -71,10 +75,13 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed - // Blueprint implementations may have unexported fields that are not serialized - // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. - blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { - return reflect.TypeOf(a) == reflect.TypeOf(b) + // Some blueprints have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Use Equal method if available. + blueprintComparer := cmp.Comparer(func(a, b constraint.BlueprintComparable) bool { + if a == nil { + return b == nil + } + return a.Equal(b) }) if diff := cmp.Diff(r1cs1, &reconstructed, blueprintComparer, diff --git a/constraint/tinyfield/r1cs_test.go b/constraint/tinyfield/r1cs_test.go index 9ff0c680c8..dafeb5a730 100644 --- a/constraint/tinyfield/r1cs_test.go +++ b/constraint/tinyfield/r1cs_test.go @@ -10,6 +10,7 @@ import ( "reflect" "testing" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -32,6 +33,9 @@ func TestSerialization(t *testing.T) { for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] + if !tc.SupportsCurve(ecc.UNKNOWN) { + t.Skip("circuit does not support small fields") + } if name == "range_constant" { return } @@ -74,10 +78,13 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed - // Blueprint implementations may have unexported fields that are not serialized - // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. - blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { - return reflect.TypeOf(a) == reflect.TypeOf(b) + // Some blueprints have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Use Equal method if available. + blueprintComparer := cmp.Comparer(func(a, b constraint.BlueprintComparable) bool { + if a == nil { + return b == nil + } + return a.Equal(b) }) if diff := cmp.Diff(r1cs1, &reconstructed, blueprintComparer, diff --git a/internal/backend/circuits/circuits.go b/internal/backend/circuits/circuits.go index 3424a61750..e72ff22576 100644 --- a/internal/backend/circuits/circuits.go +++ b/internal/backend/circuits/circuits.go @@ -15,6 +15,20 @@ type TestCircuit struct { Curves []ecc.ID } +// SupportsCurve returns true if the test circuit supports the given curve. +// If no curves are specified, all curves are supported. +func (tc TestCircuit) SupportsCurve(curve ecc.ID) bool { + if len(tc.Curves) == 0 { + return true + } + for _, c := range tc.Curves { + if c == curve { + return true + } + } + return false +} + // Circuits are used for test purposes (backend.Groth16 and gnark/integration_test.go) var Circuits map[string]TestCircuit diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 0d80fc29d6..3b148b4ee2 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -1,6 +1,7 @@ import ( "fmt" "math/bits" + "reflect" "slices" "sync" @@ -40,6 +41,16 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) +// Equal returns true if the serialized fields of two BlueprintSolve are equal. +// Used for testing serialization round-trips. +func (b *BlueprintSolve) Equal(other constraint.BlueprintComparable) bool { + if other == nil { return false } + o, ok := other.(*BlueprintSolve) + if !ok { + return false + } + return b.NbInstances == o.NbInstances && reflect.DeepEqual(b.Circuit, o.Circuit) && reflect.DeepEqual(b.assignments, o.assignments) +} // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. @@ -190,6 +201,15 @@ type BlueprintProve struct { // Ensures BlueprintProve implements BlueprintSolvable var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) +// Equal returns true if the serialized fields of two BlueprintProve are equal. +func (b *BlueprintProve) Equal(other constraint.BlueprintComparable) bool { + if other == nil { return false } + o, ok := other.(*BlueprintProve) + if !ok { + return false + } + return b.SolveBlueprintID == o.SolveBlueprintID && b.HashName == o.HashName +} // Solve implements the BlueprintSolvable interface for proving. func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { @@ -324,6 +344,15 @@ type BlueprintGetAssignment struct { // Ensures BlueprintGetAssignment implements BlueprintSolvable var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) +// Equal returns true if the serialized fields of two BlueprintGetAssignment are equal. +func (b *BlueprintGetAssignment) Equal(other constraint.BlueprintComparable) bool { + if other == nil { return false } + o, ok := other.(*BlueprintGetAssignment) + if !ok { + return false + } + return b.SolveBlueprintID == o.SolveBlueprintID +} // Solve implements the BlueprintSolvable interface for getting assignments. func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { diff --git a/internal/generator/backend/template/representations/tests/r1cs.go.tmpl b/internal/generator/backend/template/representations/tests/r1cs.go.tmpl index 0d296693fa..e72905871e 100644 --- a/internal/generator/backend/template/representations/tests/r1cs.go.tmpl +++ b/internal/generator/backend/template/representations/tests/r1cs.go.tmpl @@ -4,6 +4,7 @@ import ( "reflect" "testing" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -27,6 +28,13 @@ func TestSerialization(t *testing.T) { for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] + if !tc.SupportsCurve(ecc.{{.CurveID}}) { + {{- if eq .ElementType "U32"}} + t.Skip("circuit does not support small fields") + {{- else}} + t.Skip("circuit does not support this curve") + {{- end}} + } {{- if eq .Curve "BW6-761"}} if testing.Short() && name != "reference_small" { return @@ -77,10 +85,11 @@ func TestSerialization(t *testing.T) { } // compare original and reconstructed - // Blueprint implementations may have unexported fields that are not serialized - // (e.g., sync.Mutex, lazy-initialized caches). Compare by type only. - blueprintComparer := cmp.Comparer(func(a, b constraint.Blueprint) bool { - return reflect.TypeOf(a) == reflect.TypeOf(b) + // Some blueprints have unexported fields that are not serialized + // (e.g., sync.Mutex, lazy-initialized caches). Use Equal method if available. + blueprintComparer := cmp.Comparer(func(a, b constraint.BlueprintComparable) bool { + if a == nil { return b == nil } + return a.Equal(b) }) if diff := cmp.Diff(r1cs1, &reconstructed, blueprintComparer, diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 72ac7530fd..67b337ea1e 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/bits" + "reflect" "slices" "sync" @@ -48,6 +49,19 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) +// Equal returns true if the serialized fields of two BlueprintSolve are equal. +// Used for testing serialization round-trips. +func (b *BlueprintSolve) Equal(other constraint.BlueprintComparable) bool { + if other == nil { + return false + } + o, ok := other.(*BlueprintSolve) + if !ok { + return false + } + return b.NbInstances == o.NbInstances && reflect.DeepEqual(b.Circuit, o.Circuit) && reflect.DeepEqual(b.assignments, o.assignments) +} + // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { @@ -198,6 +212,18 @@ type BlueprintProve struct { // Ensures BlueprintProve implements BlueprintSolvable var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) +// Equal returns true if the serialized fields of two BlueprintProve are equal. +func (b *BlueprintProve) Equal(other constraint.BlueprintComparable) bool { + if other == nil { + return false + } + o, ok := other.(*BlueprintProve) + if !ok { + return false + } + return b.SolveBlueprintID == o.SolveBlueprintID && b.HashName == o.HashName +} + // Solve implements the BlueprintSolvable interface for proving. func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { b.lock.Lock() @@ -332,6 +358,18 @@ type BlueprintGetAssignment struct { // Ensures BlueprintGetAssignment implements BlueprintSolvable var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) +// Equal returns true if the serialized fields of two BlueprintGetAssignment are equal. +func (b *BlueprintGetAssignment) Equal(other constraint.BlueprintComparable) bool { + if other == nil { + return false + } + o, ok := other.(*BlueprintGetAssignment) + if !ok { + return false + } + return b.SolveBlueprintID == o.SolveBlueprintID +} + // Solve implements the BlueprintSolvable interface for getting assignments. func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { b.lock.Lock() diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 4b5a4fce03..6ab93347a9 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/bits" + "reflect" "slices" "sync" @@ -48,6 +49,19 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) +// Equal returns true if the serialized fields of two BlueprintSolve are equal. +// Used for testing serialization round-trips. +func (b *BlueprintSolve) Equal(other constraint.BlueprintComparable) bool { + if other == nil { + return false + } + o, ok := other.(*BlueprintSolve) + if !ok { + return false + } + return b.NbInstances == o.NbInstances && reflect.DeepEqual(b.Circuit, o.Circuit) && reflect.DeepEqual(b.assignments, o.assignments) +} + // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { @@ -198,6 +212,18 @@ type BlueprintProve struct { // Ensures BlueprintProve implements BlueprintSolvable var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) +// Equal returns true if the serialized fields of two BlueprintProve are equal. +func (b *BlueprintProve) Equal(other constraint.BlueprintComparable) bool { + if other == nil { + return false + } + o, ok := other.(*BlueprintProve) + if !ok { + return false + } + return b.SolveBlueprintID == o.SolveBlueprintID && b.HashName == o.HashName +} + // Solve implements the BlueprintSolvable interface for proving. func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { b.lock.Lock() @@ -332,6 +358,18 @@ type BlueprintGetAssignment struct { // Ensures BlueprintGetAssignment implements BlueprintSolvable var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) +// Equal returns true if the serialized fields of two BlueprintGetAssignment are equal. +func (b *BlueprintGetAssignment) Equal(other constraint.BlueprintComparable) bool { + if other == nil { + return false + } + o, ok := other.(*BlueprintGetAssignment) + if !ok { + return false + } + return b.SolveBlueprintID == o.SolveBlueprintID +} + // Solve implements the BlueprintSolvable interface for getting assignments. func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { b.lock.Lock() diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 7c4336bef4..eefb3dee8b 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/bits" + "reflect" "slices" "sync" @@ -48,6 +49,19 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) +// Equal returns true if the serialized fields of two BlueprintSolve are equal. +// Used for testing serialization round-trips. +func (b *BlueprintSolve) Equal(other constraint.BlueprintComparable) bool { + if other == nil { + return false + } + o, ok := other.(*BlueprintSolve) + if !ok { + return false + } + return b.NbInstances == o.NbInstances && reflect.DeepEqual(b.Circuit, o.Circuit) && reflect.DeepEqual(b.assignments, o.assignments) +} + // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { @@ -198,6 +212,18 @@ type BlueprintProve struct { // Ensures BlueprintProve implements BlueprintSolvable var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) +// Equal returns true if the serialized fields of two BlueprintProve are equal. +func (b *BlueprintProve) Equal(other constraint.BlueprintComparable) bool { + if other == nil { + return false + } + o, ok := other.(*BlueprintProve) + if !ok { + return false + } + return b.SolveBlueprintID == o.SolveBlueprintID && b.HashName == o.HashName +} + // Solve implements the BlueprintSolvable interface for proving. func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { b.lock.Lock() @@ -332,6 +358,18 @@ type BlueprintGetAssignment struct { // Ensures BlueprintGetAssignment implements BlueprintSolvable var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) +// Equal returns true if the serialized fields of two BlueprintGetAssignment are equal. +func (b *BlueprintGetAssignment) Equal(other constraint.BlueprintComparable) bool { + if other == nil { + return false + } + o, ok := other.(*BlueprintGetAssignment) + if !ok { + return false + } + return b.SolveBlueprintID == o.SolveBlueprintID +} + // Solve implements the BlueprintSolvable interface for getting assignments. func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { b.lock.Lock() diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 8093ea08d0..c760545c78 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -8,6 +8,7 @@ package gkr import ( "fmt" "math/bits" + "reflect" "slices" "sync" @@ -48,6 +49,19 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) +// Equal returns true if the serialized fields of two BlueprintSolve are equal. +// Used for testing serialization round-trips. +func (b *BlueprintSolve) Equal(other constraint.BlueprintComparable) bool { + if other == nil { + return false + } + o, ok := other.(*BlueprintSolve) + if !ok { + return false + } + return b.NbInstances == o.NbInstances && reflect.DeepEqual(b.Circuit, o.Circuit) && reflect.DeepEqual(b.assignments, o.assignments) +} + // Reset implements BlueprintStateful. // It is used to initialize the blueprint for the current circuit. func (b *BlueprintSolve) Reset() { @@ -198,6 +212,18 @@ type BlueprintProve struct { // Ensures BlueprintProve implements BlueprintSolvable var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) +// Equal returns true if the serialized fields of two BlueprintProve are equal. +func (b *BlueprintProve) Equal(other constraint.BlueprintComparable) bool { + if other == nil { + return false + } + o, ok := other.(*BlueprintProve) + if !ok { + return false + } + return b.SolveBlueprintID == o.SolveBlueprintID && b.HashName == o.HashName +} + // Solve implements the BlueprintSolvable interface for proving. func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { b.lock.Lock() @@ -332,6 +358,18 @@ type BlueprintGetAssignment struct { // Ensures BlueprintGetAssignment implements BlueprintSolvable var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) +// Equal returns true if the serialized fields of two BlueprintGetAssignment are equal. +func (b *BlueprintGetAssignment) Equal(other constraint.BlueprintComparable) bool { + if other == nil { + return false + } + o, ok := other.(*BlueprintGetAssignment) + if !ok { + return false + } + return b.SolveBlueprintID == o.SolveBlueprintID +} + // Solve implements the BlueprintSolvable interface for getting assignments. func (b *BlueprintGetAssignment) Solve(s constraint.Solver[constraint.U64], inst constraint.Instruction) error { b.lock.Lock() From 658cf25849fdd0c8550e3873b43e2c8d085c40af Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sun, 22 Feb 2026 23:17:27 -0600 Subject: [PATCH 215/251] test: U64Only option --- constraint/babybear/r1cs_test.go | 3 +- constraint/bls12-377/r1cs_test.go | 4 --- constraint/bls12-381/r1cs_test.go | 4 --- constraint/bn254/r1cs_test.go | 4 --- constraint/bw6-761/r1cs_test.go | 4 --- constraint/koalabear/r1cs_test.go | 3 +- constraint/tinyfield/r1cs_test.go | 3 +- internal/backend/circuits/circuits.go | 31 +++++++++---------- internal/backend/circuits/gkr.go | 2 +- .../representations/tests/r1cs.go.tmpl | 13 +++----- 10 files changed, 24 insertions(+), 47 deletions(-) diff --git a/constraint/babybear/r1cs_test.go b/constraint/babybear/r1cs_test.go index b5d49c6a16..17d987d0ce 100644 --- a/constraint/babybear/r1cs_test.go +++ b/constraint/babybear/r1cs_test.go @@ -10,7 +10,6 @@ import ( "reflect" "testing" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -33,7 +32,7 @@ func TestSerialization(t *testing.T) { for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] - if !tc.SupportsCurve(ecc.UNKNOWN) { + if tc.U64Only { t.Skip("circuit does not support small fields") } builder := r1cs.NewBuilder[constraint.U32] diff --git a/constraint/bls12-377/r1cs_test.go b/constraint/bls12-377/r1cs_test.go index bed336f0f9..9d95eb5920 100644 --- a/constraint/bls12-377/r1cs_test.go +++ b/constraint/bls12-377/r1cs_test.go @@ -10,7 +10,6 @@ import ( "reflect" "testing" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -32,9 +31,6 @@ func TestSerialization(t *testing.T) { for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] - if !tc.SupportsCurve(ecc.BLS12_377) { - t.Skip("circuit does not support this curve") - } builder := r1cs.NewBuilder[constraint.U64] r1cs1, err := frontend.CompileGeneric(fr.Modulus(), builder, tc.Circuit) diff --git a/constraint/bls12-381/r1cs_test.go b/constraint/bls12-381/r1cs_test.go index c31db79430..d8bbf1b57e 100644 --- a/constraint/bls12-381/r1cs_test.go +++ b/constraint/bls12-381/r1cs_test.go @@ -10,7 +10,6 @@ import ( "reflect" "testing" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -32,9 +31,6 @@ func TestSerialization(t *testing.T) { for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] - if !tc.SupportsCurve(ecc.BLS12_381) { - t.Skip("circuit does not support this curve") - } builder := r1cs.NewBuilder[constraint.U64] r1cs1, err := frontend.CompileGeneric(fr.Modulus(), builder, tc.Circuit) diff --git a/constraint/bn254/r1cs_test.go b/constraint/bn254/r1cs_test.go index e5ed27f169..748b8cc24f 100644 --- a/constraint/bn254/r1cs_test.go +++ b/constraint/bn254/r1cs_test.go @@ -10,7 +10,6 @@ import ( "reflect" "testing" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -32,9 +31,6 @@ func TestSerialization(t *testing.T) { for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] - if !tc.SupportsCurve(ecc.BN254) { - t.Skip("circuit does not support this curve") - } builder := r1cs.NewBuilder[constraint.U64] r1cs1, err := frontend.CompileGeneric(fr.Modulus(), builder, tc.Circuit) diff --git a/constraint/bw6-761/r1cs_test.go b/constraint/bw6-761/r1cs_test.go index ac6ca44547..ec86db39fb 100644 --- a/constraint/bw6-761/r1cs_test.go +++ b/constraint/bw6-761/r1cs_test.go @@ -10,7 +10,6 @@ import ( "reflect" "testing" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -32,9 +31,6 @@ func TestSerialization(t *testing.T) { for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] - if !tc.SupportsCurve(ecc.BW6_761) { - t.Skip("circuit does not support this curve") - } if testing.Short() && name != "reference_small" { return } diff --git a/constraint/koalabear/r1cs_test.go b/constraint/koalabear/r1cs_test.go index 61f960bdc6..b66184fa29 100644 --- a/constraint/koalabear/r1cs_test.go +++ b/constraint/koalabear/r1cs_test.go @@ -10,7 +10,6 @@ import ( "reflect" "testing" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -33,7 +32,7 @@ func TestSerialization(t *testing.T) { for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] - if !tc.SupportsCurve(ecc.UNKNOWN) { + if tc.U64Only { t.Skip("circuit does not support small fields") } builder := r1cs.NewBuilder[constraint.U32] diff --git a/constraint/tinyfield/r1cs_test.go b/constraint/tinyfield/r1cs_test.go index dafeb5a730..074454190c 100644 --- a/constraint/tinyfield/r1cs_test.go +++ b/constraint/tinyfield/r1cs_test.go @@ -10,7 +10,6 @@ import ( "reflect" "testing" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -33,7 +32,7 @@ func TestSerialization(t *testing.T) { for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] - if !tc.SupportsCurve(ecc.UNKNOWN) { + if tc.U64Only { t.Skip("circuit does not support small fields") } if name == "range_constant" { diff --git a/internal/backend/circuits/circuits.go b/internal/backend/circuits/circuits.go index e72ff22576..5697ee8ddc 100644 --- a/internal/backend/circuits/circuits.go +++ b/internal/backend/circuits/circuits.go @@ -13,26 +13,21 @@ type TestCircuit struct { ValidAssignments, InvalidAssignments []frontend.Circuit // good and bad witness for the prover + public verifier data HintFunctions []solver.Hint Curves []ecc.ID -} - -// SupportsCurve returns true if the test circuit supports the given curve. -// If no curves are specified, all curves are supported. -func (tc TestCircuit) SupportsCurve(curve ecc.ID) bool { - if len(tc.Curves) == 0 { - return true - } - for _, c := range tc.Curves { - if c == curve { - return true - } - } - return false + U64Only bool // if true, circuit only works with U64 element types (not small fields) } // Circuits are used for test purposes (backend.Groth16 and gnark/integration_test.go) var Circuits map[string]TestCircuit -func addEntry(name string, circuit, proverGood, proverBad frontend.Circuit, curves []ecc.ID) { +type entryOption func(*TestCircuit) + +func withU64Only() entryOption { + return func(tc *TestCircuit) { + tc.U64Only = true + } +} + +func addEntry(name string, circuit, proverGood, proverBad frontend.Circuit, curves []ecc.ID, opts ...entryOption) { if Circuits == nil { Circuits = make(map[string]TestCircuit) @@ -41,7 +36,11 @@ func addEntry(name string, circuit, proverGood, proverBad frontend.Circuit, curv panic("name " + name + "already taken by another test circuit ") } - Circuits[name] = TestCircuit{Circuit: circuit, ValidAssignments: []frontend.Circuit{proverGood}, InvalidAssignments: []frontend.Circuit{proverBad}, HintFunctions: nil, Curves: curves} + tc := TestCircuit{Circuit: circuit, ValidAssignments: []frontend.Circuit{proverGood}, InvalidAssignments: []frontend.Circuit{proverBad}, HintFunctions: nil, Curves: curves} + for _, opt := range opts { + opt(&tc) + } + Circuits[name] = tc } func addNewEntry(name string, circuit frontend.Circuit, proverGood, proverBad []frontend.Circuit, curves []ecc.ID, hintFunctions ...solver.Hint) { diff --git a/internal/backend/circuits/gkr.go b/internal/backend/circuits/gkr.go index 56698e2bdf..cd96ffc972 100644 --- a/internal/backend/circuits/gkr.go +++ b/internal/backend/circuits/gkr.go @@ -50,5 +50,5 @@ func init() { good := &gkrCubeCircuit{X: []frontend.Variable{3, 5}, Square: []frontend.Variable{9, 25}, Cube: []frontend.Variable{27, 125}} bad := &gkrCubeCircuit{X: []frontend.Variable{3, 5}, Square: []frontend.Variable{9, 25}, Cube: []frontend.Variable{27, 126}} - addEntry("gkr_cube", circuit, good, bad, gnark.Curves()) + addEntry("gkr_cube", circuit, good, bad, gnark.Curves(), withU64Only()) } diff --git a/internal/generator/backend/template/representations/tests/r1cs.go.tmpl b/internal/generator/backend/template/representations/tests/r1cs.go.tmpl index e72905871e..48fcab43a1 100644 --- a/internal/generator/backend/template/representations/tests/r1cs.go.tmpl +++ b/internal/generator/backend/template/representations/tests/r1cs.go.tmpl @@ -4,7 +4,6 @@ import ( "reflect" "testing" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" @@ -22,19 +21,17 @@ import ( ) func TestSerialization(t *testing.T) { - + var buffer, buffer2 bytes.Buffer - + for name := range circuits.Circuits { t.Run(name, func(t *testing.T) { tc := circuits.Circuits[name] - if !tc.SupportsCurve(ecc.{{.CurveID}}) { - {{- if eq .ElementType "U32"}} + {{- if eq .ElementType "U32"}} + if tc.U64Only { t.Skip("circuit does not support small fields") - {{- else}} - t.Skip("circuit does not support this curve") - {{- end}} } + {{- end}} {{- if eq .Curve "BW6-761"}} if testing.Short() && name != "reference_small" { return From b58e0c140071ce8dbb0597bfba33963009dd88bb Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Thu, 26 Feb 2026 09:01:46 -0600 Subject: [PATCH 216/251] bench: gkrposeidon2 --- std/permutation/gkr-mimc/gkr-mimc_test.go | 6 +- .../gkr-poseidon2/gkr-poseidon2_test.go | 72 ++++++++++++++++++- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/std/permutation/gkr-mimc/gkr-mimc_test.go b/std/permutation/gkr-mimc/gkr-mimc_test.go index e236540996..2ce31acfdd 100644 --- a/std/permutation/gkr-mimc/gkr-mimc_test.go +++ b/std/permutation/gkr-mimc/gkr-mimc_test.go @@ -2,7 +2,6 @@ package gkr_mimc import ( "errors" - "slices" "testing" "github.com/consensys/gnark-crypto/ecc" @@ -25,7 +24,10 @@ func (c merkleTreeCircuit) Define(api frontend.API) error { return err } - layer := slices.Clone(c.Leaves) + layer := make([]frontend.Variable, len(c.Leaves)/2) + for i := range layer { + layer[i] = hsh.Compress(c.Leaves[2*i], c.Leaves[2*i+1]) + } for len(layer) > 1 { if len(layer)%2 == 1 { diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go index 8d6cbb1099..10520bb7cb 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr-poseidon2_test.go @@ -1,6 +1,7 @@ package gkr_poseidon2 import ( + "errors" "fmt" "testing" @@ -76,8 +77,12 @@ func BenchmarkGkrCompressions(b *testing.B) { witness, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField()) require.NoError(b, err) - _, err = cs.Solve(witness) - require.NoError(b, err) + b.ResetTimer() + + for b.Loop() { + _, err = cs.Solve(witness) + require.NoError(b, err) + } } // directPoseidon2Circuit uses direct poseidon2 calls (no GKR) @@ -112,6 +117,69 @@ func (c *gkrPoseidon2Circuit) Define(api frontend.API) error { return nil } +type poseidon2MerkleTreeCircuit struct { + Leaves []frontend.Variable +} + +func (c *poseidon2MerkleTreeCircuit) Define(api frontend.API) error { + if len(c.Leaves) == 0 { + return errors.New("no hashing to do") + } + + comp, err := NewCompressor(api) + if err != nil { + return err + } + + layer := make([]frontend.Variable, len(c.Leaves)/2) + for i := range layer { + layer[i] = comp.Compress(c.Leaves[2*i], c.Leaves[2*i+1]) + } + + for len(layer) > 1 { + if len(layer)%2 == 1 { + layer = append(layer, 0) // pad with zero + } + + for i := range len(layer) / 2 { + layer[i] = comp.Compress(layer[2*i], layer[2*i+1]) + } + + layer = layer[:len(layer)/2] + } + + api.AssertIsDifferent(layer[0], 0) + return nil +} + +func BenchmarkGkrPoseidon2(b *testing.B) { + const size = 1 << 15 // about 2 ^ 16 total hashes + + circuit := poseidon2MerkleTreeCircuit{ + Leaves: make([]frontend.Variable, size), + } + assignment := poseidon2MerkleTreeCircuit{ + Leaves: make([]frontend.Variable, size), + } + + for i := range assignment.Leaves { + assignment.Leaves[i] = i + } + + cs, err := frontend.Compile(ecc.BLS12_377.ScalarField(), scs.NewBuilder, &circuit) + require.NoError(b, err) + + w, err := frontend.NewWitness(&assignment, ecc.BLS12_377.ScalarField()) + require.NoError(b, err) + + b.ResetTimer() + + for b.Loop() { + _, err = cs.Solve(w) + require.NoError(b, err) + } +} + // BenchmarkConstraintComparison compares constraint counts between direct poseidon2 and GKR-based poseidon2 // at various scales to identify the crossover point where GKR becomes beneficial. func BenchmarkConstraintComparison(b *testing.B) { From dc47db2069a716c68c630a9be2b9135e4a978e24 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Thu, 26 Feb 2026 09:43:46 -0600 Subject: [PATCH 217/251] refactor: rawcircuit --- .../backend/template/gkr/gkr.test.go.tmpl | 10 +-- internal/gkr/bls12-377/gkr_test.go | 10 +-- internal/gkr/bls12-381/gkr_test.go | 10 +-- internal/gkr/bn254/gkr_test.go | 10 +-- internal/gkr/bw6-761/gkr_test.go | 10 +-- internal/gkr/gkrtesting/gkrtesting.go | 76 ++++++++---------- internal/gkr/gkrtypes/types.go | 80 +++++++++++-------- std/gkrapi/api.go | 6 +- std/gkrapi/compile.go | 19 ++--- 9 files changed, 120 insertions(+), 111 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index 729c28948c..1be78c2cad 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -125,9 +125,9 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := cache.Compile(t, circuit) - ins := circuit.Inputs() +func test(t *testing.T, circuit gkrtypes.RawCircuit) { + gCircuit, sCircuit := cache.Compile(t, circuit) + ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -179,7 +179,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]{{ .ElementType }}) { - c := cache.Compile(t, gkrtesting.NoGateCircuit()) + _, c := cache.Compile(t, gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -260,7 +260,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) + _, c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]{{ .ElementType }}, nbInstances) in1 := make([]{{ .ElementType }}, nbInstances) diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index 95b45cc654..670db1b9d7 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -132,9 +132,9 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := cache.Compile(t, circuit) - ins := circuit.Inputs() +func test(t *testing.T, circuit gkrtypes.RawCircuit) { + gCircuit, sCircuit := cache.Compile(t, circuit) + ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -186,7 +186,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := cache.Compile(t, gkrtesting.NoGateCircuit()) + _, c := cache.Compile(t, gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -267,7 +267,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) + _, c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 012683657a..effbeac26b 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -132,9 +132,9 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := cache.Compile(t, circuit) - ins := circuit.Inputs() +func test(t *testing.T, circuit gkrtypes.RawCircuit) { + gCircuit, sCircuit := cache.Compile(t, circuit) + ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -186,7 +186,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := cache.Compile(t, gkrtesting.NoGateCircuit()) + _, c := cache.Compile(t, gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -267,7 +267,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) + _, c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 6f6f0b5432..ed5e7a6087 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -132,9 +132,9 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := cache.Compile(t, circuit) - ins := circuit.Inputs() +func test(t *testing.T, circuit gkrtypes.RawCircuit) { + gCircuit, sCircuit := cache.Compile(t, circuit) + ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -186,7 +186,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := cache.Compile(t, gkrtesting.NoGateCircuit()) + _, c := cache.Compile(t, gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -267,7 +267,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) + _, c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index b8c4d5a422..e32e256250 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -132,9 +132,9 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.GadgetCircuit) { - sCircuit := cache.Compile(t, circuit) - ins := circuit.Inputs() +func test(t *testing.T, circuit gkrtypes.RawCircuit) { + gCircuit, sCircuit := cache.Compile(t, circuit) + ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -186,7 +186,7 @@ func (p Proof) isEmpty() bool { } func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := cache.Compile(t, gkrtesting.NoGateCircuit()) + _, c := cache.Compile(t, gkrtesting.NoGateCircuit()) assignment := WireAssignment{0: inputAssignments[0]} @@ -267,7 +267,7 @@ func proofEquals(expected Proof, seen Proof) error { func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") - c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) + _, c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 56c2c16bd1..b2bdca5d62 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -70,11 +70,11 @@ type JSONWire struct { // JSONCircuit is the JSON serialization format for circuits type JSONCircuit []JSONWire -// Compile compiles a programmatic GadgetCircuit into a SerializableCircuit. -func (c *Cache) Compile(t require.TestingT, circuit gkrtypes.GadgetCircuit) gkrtypes.SerializableCircuit { - res, err := gkrtypes.CompileCircuit(circuit, c.field) +// Compile compiles a RawCircuit into a SerializableCircuit. +func (c *Cache) Compile(t require.TestingT, circuit gkrtypes.RawCircuit) (gkrtypes.GadgetCircuit, gkrtypes.SerializableCircuit) { + gadget, serializable, err := circuit.Compile(c.field) require.NoError(t, err) - return res + return gadget, serializable } func (c *Cache) GetCircuit(path string) (gkrtypes.SerializableCircuit, gkrtypes.GadgetCircuit) { @@ -100,17 +100,15 @@ func (c *Cache) GetCircuit(path string) (gkrtypes.SerializableCircuit, gkrtypes. panic(err) } - // Convert JSON format to GadgetCircuit - gCircuit := make(gkrtypes.GadgetCircuit, len(jsonCircuit)) + // Convert JSON format to RawCircuit + rawCircuit := make(gkrtypes.RawCircuit, len(jsonCircuit)) for i, wJSON := range jsonCircuit { - gate := c.GetGate(wJSON.Gate) - - gCircuit[i] = gkrtypes.GadgetWire{ - Gate: gkrtypes.Gate[gkr.GateFunction]{Evaluate: gate}, + rawCircuit[i] = gkrtypes.RawWire{ + Gate: c.GetGate(wJSON.Gate), Inputs: wJSON.Inputs, } } - sCircuit, err := gkrtypes.CompileCircuit(gCircuit, c.field) + gCircuit, sCircuit, err := rawCircuit.Compile(c.field) if err != nil { panic(err) } @@ -137,12 +135,10 @@ func (c *Cache) GetGate(name string) gkr.GateFunction { panic("gate not found: " + name) } -func MiMCCircuit(numRounds int) gkrtypes.GadgetCircuit { - c := make(gkrtypes.GadgetCircuit, numRounds+2) - mimc := gkrtypes.GadgetGate{Evaluate: mimcGate} - +func MiMCCircuit(numRounds int) gkrtypes.RawCircuit { + c := make(gkrtypes.RawCircuit, numRounds+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.GadgetWire{Gate: mimc, Inputs: []int{i - 1, 0}} + c[i] = gkrtypes.RawWire{Gate: mimcGate, Inputs: []int{i - 1, 0}} } return c } @@ -175,60 +171,56 @@ func (c *Cache) ReadTestCaseInfo(filePath string) (info TestCaseInfo, err error) return } -func NoGateCircuit() gkrtypes.GadgetCircuit { - return gkrtypes.GadgetCircuit{ +func NoGateCircuit() gkrtypes.RawCircuit { + return gkrtypes.RawCircuit{ {}, } } -func SingleAddGateCircuit() gkrtypes.GadgetCircuit { - return gkrtypes.GadgetCircuit{ +func SingleAddGateCircuit() gkrtypes.RawCircuit { + return gkrtypes.RawCircuit{ {}, {}, - {Gate: gkrtypes.GadgetGate{Evaluate: gkrtypes.Add2}, Inputs: []int{0, 1}}, + {Gate: gkrtypes.Add2, Inputs: []int{0, 1}}, } } -func SingleMulGateCircuit() gkrtypes.GadgetCircuit { - return gkrtypes.GadgetCircuit{ +func SingleMulGateCircuit() gkrtypes.RawCircuit { + return gkrtypes.RawCircuit{ {}, {}, - {Gate: gkrtypes.GadgetGate{Evaluate: gkrtypes.Mul2}, Inputs: []int{0, 1}}, + {Gate: gkrtypes.Mul2, Inputs: []int{0, 1}}, } } -func SingleInputTwoIdentityGatesCircuit() gkrtypes.GadgetCircuit { - idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} - return gkrtypes.GadgetCircuit{ +func SingleInputTwoIdentityGatesCircuit() gkrtypes.RawCircuit { + return gkrtypes.RawCircuit{ {}, - {Gate: idGate, Inputs: []int{0}}, - {Gate: idGate, Inputs: []int{0}}, + {Gate: gkrtypes.Identity, Inputs: []int{0}}, + {Gate: gkrtypes.Identity, Inputs: []int{0}}, } } -func SingleInputTwoIdentityGatesComposedCircuit() gkrtypes.GadgetCircuit { - idGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Identity} - return gkrtypes.GadgetCircuit{ +func SingleInputTwoIdentityGatesComposedCircuit() gkrtypes.RawCircuit { + return gkrtypes.RawCircuit{ {}, - {Gate: idGate, Inputs: []int{0}}, - {Gate: idGate, Inputs: []int{1}}, + {Gate: gkrtypes.Identity, Inputs: []int{0}}, + {Gate: gkrtypes.Identity, Inputs: []int{1}}, } } -func APowNTimesBCircuit(n int) gkrtypes.GadgetCircuit { - c := make(gkrtypes.GadgetCircuit, n+2) - mulGate := gkrtypes.GadgetGate{Evaluate: gkrtypes.Mul2} - +func APowNTimesBCircuit(n int) gkrtypes.RawCircuit { + c := make(gkrtypes.RawCircuit, n+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.GadgetWire{Gate: mulGate, Inputs: []int{i - 1, 0}} + c[i] = gkrtypes.RawWire{Gate: gkrtypes.Mul2, Inputs: []int{i - 1, 0}} } return c } -func SingleMimcCipherGateCircuit() gkrtypes.GadgetCircuit { - return gkrtypes.GadgetCircuit{ +func SingleMimcCipherGateCircuit() gkrtypes.RawCircuit { + return gkrtypes.RawCircuit{ {}, {}, - {Gate: gkrtypes.GadgetGate{Evaluate: mimcGate}, Inputs: []int{0, 1}}, + {Gate: mimcGate, Inputs: []int{0, 1}}, } } diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 9089de41b3..0e62d1d51a 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -3,7 +3,6 @@ package gkrtypes import ( "errors" "math/big" - "reflect" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" @@ -17,6 +16,17 @@ type ( InputInstance int } + // RawWire is a minimal wire representation with only inputs and gate function. + RawWire struct { + Gate gkr.GateFunction + Inputs []int + Exported bool + } + + // RawCircuit is a minimal circuit representation for API-level circuit construction. + // It contains only the essential topology (inputs) and gate functions. + RawCircuit []RawWire + // A Gate is a low-degree multivariate polynomial Gate[GateExecutable any] struct { Evaluate GateExecutable @@ -267,55 +277,61 @@ type Blueprints struct { GetAssignmentID constraint.BlueprintID } -// CompileCircuit converts a gadget circuit to a serializable circuit by compiling the gate functions. -// It also sets wire and gate metadata (Degree, SolvableVar, NbUniqueOutputs) for both the input and output circuits. -func CompileCircuit(c GadgetCircuit, mod *big.Int) (SerializableCircuit, error) { +// Compile compiles a raw circuit into both a gadget circuit and a serializable circuit. +// It computes all wire and gate metadata (Degree, SolvableVar, NbUniqueOutputs). +func (c RawCircuit) Compile(mod *big.Int) (GadgetCircuit, SerializableCircuit, error) { + gadget := make(GadgetCircuit, len(c)) + serializable := make(SerializableCircuit, len(c)) + // First pass: copy inputs, gates, and exported flags, compute NbUniqueOutputs + curWireIn := make([]bool, len(c)) for i := range c { - c[i].NbUniqueOutputs = 0 - } + gadget[i].Inputs = c[i].Inputs + gadget[i].Exported = c[i].Exported + serializable[i].Inputs = c[i].Inputs + serializable[i].Exported = c[i].Exported - // compile the gate and compute metadata - curWireIn := make([]bool, len(c)) // curWireIn[j] = true iff i takes j as input. - res := make(SerializableCircuit, len(c)) - var err error - for i := range c { - // Compute NbUniqueOutputs as we go. + // Compute NbUniqueOutputs for input wires for _, in := range c[i].Inputs { if !curWireIn[in] { - c[in].NbUniqueOutputs++ + gadget[in].NbUniqueOutputs++ + serializable[in].NbUniqueOutputs++ curWireIn[in] = true } } - // clear curWireIn + // clear curWireIn for next iteration for _, in := range c[i].Inputs { curWireIn[in] = false } + } - if c[i].IsInput() { - if !reflect.DeepEqual(c[i].Gate, GadgetGate{}) { - return nil, errors.New("empty gate expected for input wire") + // Second pass: compile gates and set metadata + for i := range c { + if len(c[i].Inputs) == 0 { // input wire + if c[i].Gate != nil { + return nil, nil, errors.New("nil gate expected for input wire") } continue } - c[i].Gate.NbIn = len(c[i].Inputs) - if res[i].Gate, err = CompileGateFunction(c[i].Gate.Evaluate, c[i].Gate.NbIn, mod); err != nil { - return nil, err + if c[i].Gate == nil { + return nil, nil, errors.New("gate function required for non-input wire") } - c[i].Gate.Degree = res[i].Gate.Degree - c[i].Gate.SolvableVar = res[i].Gate.SolvableVar - } - // copy metadata from c to res - for i := range c { - res[i].Inputs = c[i].Inputs - res[i].Exported = c[i].Exported - res[i].NbUniqueOutputs = c[i].NbUniqueOutputs - res[i].Gate.Degree = c[i].Gate.Degree - res[i].Gate.NbIn = c[i].Gate.NbIn - res[i].Gate.SolvableVar = c[i].Gate.SolvableVar + nbIn := len(c[i].Inputs) + compiledGate, err := CompileGateFunction(c[i].Gate, nbIn, mod) + if err != nil { + return nil, nil, err + } + + gadget[i].Gate = GadgetGate{ + Evaluate: c[i].Gate, + NbIn: nbIn, + Degree: compiledGate.Degree, + SolvableVar: compiledGate.SolvableVar, + } + serializable[i].Gate = compiledGate } - return res, nil + return gadget, serializable, nil } diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index 68c481e343..444ac8cbe8 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -11,7 +11,7 @@ import ( type ( API struct { - circuit gkrtypes.GadgetCircuit + circuit gkrtypes.RawCircuit assignments gadget.WireAssignment parentApi frontend.API } @@ -23,8 +23,8 @@ func frontendVarToInt(a gkr.Variable) int { // Gate adds the given gate with the given inputs and returns its output wire. func (api *API) Gate(gate gkr.GateFunction, inputs ...gkr.Variable) gkr.Variable { - api.circuit = append(api.circuit, gkrtypes.GadgetWire{ - Gate: gkrtypes.GadgetGate{Evaluate: gate}, + api.circuit = append(api.circuit, gkrtypes.RawWire{ + Gate: gate, Inputs: utils.Map(inputs, frontendVarToInt), }) api.assignments = append(api.assignments, nil) diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 3132493baa..e1604a15e1 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -51,7 +51,7 @@ func New(api frontend.API) (*API, error) { // NewInput creates a new input variable. func (api *API) NewInput() gkr.Variable { i := len(api.circuit) - api.circuit = append(api.circuit, gkrtypes.GadgetWire{}) + api.circuit = append(api.circuit, gkrtypes.RawWire{}) api.assignments = append(api.assignments, nil) return gkr.Variable(i) } @@ -70,22 +70,23 @@ func WithInitialChallenge(getInitialChallenge InitialChallengeGetter) CompileOpt // From this point on, the circuit cannot be modified, // but instances can be added to it. func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*Circuit, error) { - res := Circuit{ - circuit: api.circuit, - assignments: make(gadget.WireAssignment, len(api.circuit)), - api: api.parentApi, - hashName: fiatshamirHashName, - } - // Dispatch to curve-specific factory compiler := api.parentApi.Compiler() field := compiler.Field() curveID := utils.FieldToCurve(field) - serializableCircuit, err := gkrtypes.CompileCircuit(api.circuit, field) + + gadgetCircuit, serializableCircuit, err := api.circuit.Compile(field) if err != nil { return nil, err } + res := Circuit{ + circuit: gadgetCircuit, + assignments: make(gadget.WireAssignment, len(api.circuit)), + api: api.parentApi, + hashName: fiatshamirHashName, + } + switch curveID { case ecc.BN254: res.blueprints = gkrbn254.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) From 64f73f9366317684a8dd6a55c8cf197a387d8f65 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Tue, 3 Mar 2026 11:37:49 -0600 Subject: [PATCH 218/251] feat: basic schedule objects --- internal/gkr/gkrtypes/types.go | 76 ++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index 0e62d1d51a..450bf4710c 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -277,6 +277,82 @@ type Blueprints struct { GetAssignmentID constraint.BlueprintID } +// ProvingSchedule types for GKR proving +// These define how claims are grouped and processed during the proving protocol. + +type ( + // ClaimGroup represents a set of wires with their claim sources. + // It is agnostic of the protocol - it only describes which wires have claims + // from which sources, not what to do with them. + ClaimGroup struct { + Wires []int `json:"wires"` + ClaimSources []int `json:"claimSources"` // indices of steps that produced these claims + } + + // ProvingStep is the interface for a single step in the proving schedule. + // A step is either a SkipStep or a SumcheckStep. + ProvingStep interface { + + } + + // SkipStep represents a step where zerocheck is skipped. + // Claims propagate through at their existing evaluation points. + SkipStep ClaimGroup + + // SumcheckStep represents a step where one or more zerochecks are batched + // together in a single sumcheck. Each ClaimGroup within may have different + // claim sources (sumcheck-level batching), or the same source (enabling + // zerocheck-level batching with shared eq tables). + SumcheckStep []ClaimGroup + + // ProvingSchedule is a sequence of steps defining how to prove a GKR circuit. + ProvingSchedule []ProvingStep +) + +// DefaultProvingSchedule generates a simple schedule for a circuit where each non-input +// wire gets its own sumcheck step, processed in reverse topological order. +// This matches the original GKR prover behavior before schedule support was added. +func DefaultProvingSchedule[T any](c Circuit[T]) ProvingSchedule { + var steps ProvingSchedule + wireToStep := make(map[int]int) // wire -> step index that processes it + + // Process wires in reverse order (outputs first) + for i := len(c) - 1; i >= 0; i-- { + if c[i].IsInput() { + continue + } + + stepI := len(steps) + + // Collect claim sources from input wires + var claimSources []int + seen := make(map[int]bool) + for _, inputWire := range c[i].Inputs { + if srcStep, ok := wireToStep[inputWire]; ok && !seen[srcStep] { + claimSources = append(claimSources, srcStep) + seen[srcStep] = true + } + } + + // For output wires, the initial claim comes from step -1 (the verifier's initial challenge) + // We represent this as having no claim sources for output wires + if c[i].IsOutput() && len(claimSources) == 0 { + claimSources = nil // initial challenge, no prior step + } + + steps = append(steps, SumcheckStep{ + ClaimGroup{ + Wires: []int{i}, + ClaimSources: claimSources, + }, + }) + + wireToStep[i] = stepI + } + + return steps +} + // Compile compiles a raw circuit into both a gadget circuit and a serializable circuit. // It computes all wire and gate metadata (Degree, SolvableVar, NbUniqueOutputs). func (c RawCircuit) Compile(mod *big.Int) (GadgetCircuit, SerializableCircuit, error) { From 03ad0f494af2d1f4fa6402a7d66cd87783ce0214 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Tue, 3 Mar 2026 11:59:20 -0600 Subject: [PATCH 219/251] refactor: gkrtypes -> gkrcore --- constraint/solver/gkrgates/registry.go | 4 +- .../backend/template/gkr/blueprint.go.tmpl | 8 +- .../backend/template/gkr/gkr.go.tmpl | 26 +-- .../backend/template/gkr/gkr.test.go.tmpl | 8 +- .../template/gkr/gkr.test.vectors.gen.go.tmpl | 2 +- .../template/gkr/gkr.test.vectors.go.tmpl | 2 +- internal/gkr/bls12-377/blueprint.go | 8 +- internal/gkr/bls12-377/gkr.go | 26 +-- internal/gkr/bls12-377/gkr_test.go | 10 +- internal/gkr/bls12-381/blueprint.go | 8 +- internal/gkr/bls12-381/gkr.go | 26 +-- internal/gkr/bls12-381/gkr_test.go | 10 +- internal/gkr/bn254/blueprint.go | 10 +- internal/gkr/bn254/gkr.go | 220 +++++++++++------- internal/gkr/bn254/gkr_test.go | 78 +++++-- internal/gkr/bw6-761/blueprint.go | 8 +- internal/gkr/bw6-761/gkr.go | 26 +-- internal/gkr/bw6-761/gkr_test.go | 10 +- internal/gkr/gkr.go | 8 +- internal/gkr/{gkrtypes => gkrcore}/gate.go | 2 +- .../gkr/{gkrtypes => gkrcore}/gate_test.go | 2 +- internal/gkr/{gkrtypes => gkrcore}/types.go | 2 +- internal/gkr/gkrtesting/gkrtesting.go | 185 +++++++++++---- internal/gkr/small_rational/gkr.go | 26 +-- .../gkr/small_rational/test_vector_gen.go | 4 +- std/gkrapi/api.go | 14 +- std/gkrapi/compile.go | 12 +- 27 files changed, 470 insertions(+), 275 deletions(-) rename internal/gkr/{gkrtypes => gkrcore}/gate.go (99%) rename internal/gkr/{gkrtypes => gkrcore}/gate_test.go (99%) rename internal/gkr/{gkrtypes => gkrcore}/types.go (99%) diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index 71f7de9122..6178eeb6ab 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -10,7 +10,7 @@ import ( "runtime" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/consensys/gnark/std/gkrapi/gkr" ) @@ -119,7 +119,7 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { } for _, curve := range s.curves { - compiled, err := gkrtypes.CompileGateFunction(f, nbIn, curve.ScalarField()) + compiled, err := gkrcore.CompileGateFunction(f, nbIn, curve.ScalarField()) if err != nil { return err } diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 3b148b4ee2..7a45be56bc 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -11,7 +11,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" ) func init() { @@ -27,7 +27,7 @@ type circuitEvaluator struct { // BlueprintSolve is a {{.FieldID}}-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.SerializableCircuit + Circuit gkrcore.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -418,7 +418,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for {{.FieldID}} -func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { +func NewBlueprints(circuit gkrcore.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrcore.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) @@ -437,7 +437,7 @@ func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compil } getAssignmentID := compiler.AddBlueprint(getAssignment) - return gkrtypes.Blueprints{ + return gkrcore.Blueprints{ SolveID: solveID, Solve: solve, ProveID: proveID, diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index bcf85cecdf..5e2d3396d1 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -8,13 +8,13 @@ import ( "github.com/consensys/gnark-crypto/utils" "strconv" "sync" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" ) // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.SerializableWire - Circuit = gkrtypes.SerializableCircuit + Wire = gkrcore.SerializableWire + Circuit = gkrcore.SerializableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit @@ -729,14 +729,14 @@ func (p Proof) flatten() iter.Seq2[int, *{{ .ElementType }}] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate gkrtypes.GateBytecode + gate gkrcore.GateBytecode vars []{{ .ElementType }} nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate gkrcore.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -780,28 +780,28 @@ func (e *gateEvaluator) evaluate(top ...{{ .ElementType }}) *{{ .ElementType }} // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrtypes.OpAdd: + case gkrcore.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpMul: + case gkrcore.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpSub: + case gkrcore.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpNeg: + case gkrcore.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrtypes.OpMulAcc: + case gkrcore.OpMulAcc: var prod {{ .ElementType }} prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrtypes.OpSumExp17: + case gkrcore.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum {{ .ElementType }} sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -827,14 +827,14 @@ func (e *gateEvaluator) evaluate(top ...{{ .ElementType }}) *{{ .ElementType }} // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate gkrtypes.GateBytecode + gate gkrcore.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate gkrcore.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index 1be78c2cad..4fc4dac89f 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -7,7 +7,7 @@ import ( "github.com/consensys/gnark-crypto/ecc" gcUtils "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/internal/gkr/gkrtesting" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/stretchr/testify/assert" "fmt" "hash" @@ -65,10 +65,10 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { // Construct SerializableCircuit directly, bypassing CompileCircuit // which would reset NbUniqueOutputs based on actual topology - circuit := gkrtypes.SerializableCircuit{ + circuit := gkrcore.SerializableCircuit{ { NbUniqueOutputs: 2, - Gate: gkrtypes.SerializableGate{Degree: 1}, + Gate: gkrcore.SerializableGate{Degree: 1}, }, } @@ -125,7 +125,7 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.RawCircuit) { +func test(t *testing.T, circuit gkrcore.RawCircuit) { gCircuit, sCircuit := cache.Compile(t, circuit) ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl index 49f2d3eb7c..3326e8b35d 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl @@ -7,7 +7,7 @@ import ( "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" "github.com/consensys/gnark/internal/gkr/gkrtesting" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" "hash" "os" "path/filepath" diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl index e0778641bd..25084e8d0f 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl @@ -32,7 +32,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.SerializableCircuit + Circuit gkrcore.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 67b337ea1e..b15707a087 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -18,7 +18,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" ) func init() { @@ -34,7 +34,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS12_377-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.SerializableCircuit + Circuit gkrcore.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -434,7 +434,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_377 -func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { +func NewBlueprints(circuit gkrcore.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrcore.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) @@ -453,7 +453,7 @@ func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compil } getAssignmentID := compiler.AddBlueprint(getAssignment) - return gkrtypes.Blueprints{ + return gkrcore.Blueprints{ SolveID: solveID, Solve: solve, ProveID: proveID, diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index e9cc5a10c9..62dd4b4879 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -16,13 +16,13 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" ) // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.SerializableWire - Circuit = gkrtypes.SerializableCircuit + Wire = gkrcore.SerializableWire + Circuit = gkrcore.SerializableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit @@ -734,14 +734,14 @@ func (p Proof) flatten() iter.Seq2[int, *fr.Element] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate gkrtypes.GateBytecode + gate gkrcore.GateBytecode vars []fr.Element nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate gkrcore.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -785,28 +785,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrtypes.OpAdd: + case gkrcore.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpMul: + case gkrcore.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpSub: + case gkrcore.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpNeg: + case gkrcore.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrtypes.OpMulAcc: + case gkrcore.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrtypes.OpSumExp17: + case gkrcore.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -832,14 +832,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate gkrtypes.GateBytecode + gate gkrcore.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate gkrcore.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index 670db1b9d7..9f98d97454 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -22,7 +22,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" gcUtils "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/internal/gkr/gkrtesting" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/stretchr/testify/assert" ) @@ -72,10 +72,10 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { // Construct SerializableCircuit directly, bypassing CompileCircuit // which would reset NbUniqueOutputs based on actual topology - circuit := gkrtypes.SerializableCircuit{ + circuit := gkrcore.SerializableCircuit{ { NbUniqueOutputs: 2, - Gate: gkrtypes.SerializableGate{Degree: 1}, + Gate: gkrcore.SerializableGate{Degree: 1}, }, } @@ -132,7 +132,7 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.RawCircuit) { +func test(t *testing.T, circuit gkrcore.RawCircuit) { gCircuit, sCircuit := cache.Compile(t, circuit) ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -327,7 +327,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.SerializableCircuit + Circuit gkrcore.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 6ab93347a9..974c7d18b3 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -18,7 +18,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" ) func init() { @@ -34,7 +34,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BLS12_381-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.SerializableCircuit + Circuit gkrcore.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -434,7 +434,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_381 -func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { +func NewBlueprints(circuit gkrcore.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrcore.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) @@ -453,7 +453,7 @@ func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compil } getAssignmentID := compiler.AddBlueprint(getAssignment) - return gkrtypes.Blueprints{ + return gkrcore.Blueprints{ SolveID: solveID, Solve: solve, ProveID: proveID, diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index dad2117fc3..d41aa8d248 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -16,13 +16,13 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" ) // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.SerializableWire - Circuit = gkrtypes.SerializableCircuit + Wire = gkrcore.SerializableWire + Circuit = gkrcore.SerializableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit @@ -734,14 +734,14 @@ func (p Proof) flatten() iter.Seq2[int, *fr.Element] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate gkrtypes.GateBytecode + gate gkrcore.GateBytecode vars []fr.Element nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate gkrcore.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -785,28 +785,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrtypes.OpAdd: + case gkrcore.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpMul: + case gkrcore.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpSub: + case gkrcore.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpNeg: + case gkrcore.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrtypes.OpMulAcc: + case gkrcore.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrtypes.OpSumExp17: + case gkrcore.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -832,14 +832,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate gkrtypes.GateBytecode + gate gkrcore.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate gkrcore.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index effbeac26b..08f76de14a 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -22,7 +22,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" gcUtils "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/internal/gkr/gkrtesting" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/stretchr/testify/assert" ) @@ -72,10 +72,10 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { // Construct SerializableCircuit directly, bypassing CompileCircuit // which would reset NbUniqueOutputs based on actual topology - circuit := gkrtypes.SerializableCircuit{ + circuit := gkrcore.SerializableCircuit{ { NbUniqueOutputs: 2, - Gate: gkrtypes.SerializableGate{Degree: 1}, + Gate: gkrcore.SerializableGate{Degree: 1}, }, } @@ -132,7 +132,7 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.RawCircuit) { +func test(t *testing.T, circuit gkrcore.RawCircuit) { gCircuit, sCircuit := cache.Compile(t, circuit) ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -327,7 +327,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.SerializableCircuit + Circuit gkrcore.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index eefb3dee8b..bbe45ecdac 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -18,7 +18,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" ) func init() { @@ -34,7 +34,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BN254-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.SerializableCircuit + Circuit gkrcore.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -275,7 +275,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BN254-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, nil, assignments, fsSettings) if err != nil { return fmt.Errorf("bn254 prove failed: %w", err) } @@ -434,7 +434,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BN254 -func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { +func NewBlueprints(circuit gkrcore.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrcore.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) @@ -453,7 +453,7 @@ func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compil } getAssignmentID := compiler.AddBlueprint(getAssignment) - return gkrtypes.Blueprints{ + return gkrcore.Blueprints{ SolveID: solveID, Solve: solve, ProveID: proveID, diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 1658dced5d..28396649d3 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -16,13 +16,13 @@ import ( "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" ) // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.SerializableWire - Circuit = gkrtypes.SerializableCircuit + Wire = gkrcore.SerializableWire + Circuit = gkrcore.SerializableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit @@ -427,6 +427,7 @@ type settings struct { transcriptPrefix string nbVars int workers *utils.WorkerPool + schedule gkrcore.ProvingSchedule } type Option func(*settings) @@ -443,13 +444,18 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c Circuit, schedule gkrcore.ProvingSchedule, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { option(&o) } + if schedule == nil { + schedule = gkrcore.DefaultProvingSchedule(c) + } + o.schedule = schedule + o.nbVars = assignment.NumVars() nbInstances := assignment.NumInstances() if 1< 1 { //fold the claims - size++ + nbClaims := 0 + for _, g := range s { + for _, w := range g.Wires { + nbClaims += c[w].NbClaims() + } + } + if nbClaims > 1 { + size++ // fold challenge } - size += logNbInstances // full run of sumcheck on logNbInstances variables + size += logNbInstances // sumcheck rounds } nums := make([]string, max(len(c), logNbInstances)) @@ -508,18 +524,25 @@ func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { challenges[j] = firstChallengePrefix + nums[j] } j := logNbInstances - for i := len(c) - 1; i >= 0; i-- { - if c[i].NoProof() { + for stepI, step := range schedule { + s, ok := step.(gkrcore.SumcheckStep) + if !ok { continue } - wirePrefix := prefix + "w" + nums[i] + "." + stepPrefix := prefix + "s" + nums[stepI] + "." - if c[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "fold" + nbClaims := 0 + for _, g := range s { + for _, w := range g.Wires { + nbClaims += c[w].NbClaims() + } + } + if nbClaims > 1 { + challenges[j] = stepPrefix + "fold" j++ } - partialSumPrefix := wirePrefix + "pSP." + partialSumPrefix := stepPrefix + "pSP." for k := 0; k < logNbInstances; k++ { challenges[j] = partialSumPrefix + nums[k] j++ @@ -550,8 +573,8 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Elem } // Prove consistency of the claimed assignment -func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { - o, err := setup(c, assignment, transcriptSettings, options...) +func Prove(c Circuit, schedule gkrcore.ProvingSchedule, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { + o, err := setup(c, schedule, assignment, transcriptSettings, options...) if err != nil { return nil, err } @@ -559,7 +582,7 @@ func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S claims := newClaimsManager(c, assignment, o) - proof := make(Proof, len(c)) + proof := make(Proof, len(o.schedule)) // firstChallenge called rho in the paper var firstChallenge []fr.Element firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) @@ -567,36 +590,48 @@ func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S return nil, err } - wirePrefix := o.transcriptPrefix + "w" + stepPrefix := o.transcriptPrefix + "s" var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - - wire := c[i] - - if wire.IsOutput() { - claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) - } - - claim := claims.getClaim(i) - if wire.NoProof() { // input wires with one claim only - proof[i] = sumcheckProof{ - partialSumPolys: []polynomial.Polynomial{}, - finalEvalProof: []fr.Element{}, - } - } else { - if proof[i], err = sumcheckProve( - claim, fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err != nil { - return proof, err + for stepI, step := range o.schedule { + switch s := step.(type) { + case gkrcore.SkipStep: + // No sumcheck; claims propagate at existing evaluation points. + // TODO: implement claim passthrough for SkipStep + _ = s + case gkrcore.SumcheckStep: + // Currently only single-wire single-group steps are supported. + // Multi-wire batching is future work. + // Seed output claims for all wires in this step. + for _, g := range s { + for _, wireI := range g.Wires { + if c[wireI].IsOutput() { + claims.add(wireI, firstChallenge, assignment[wireI].Evaluate(firstChallenge, claims.memPool)) + } + } } - baseChallenge = make([][]byte, len(proof[i].finalEvalProof)) - for j := range proof[i].finalEvalProof { - baseChallenge[j] = proof[i].finalEvalProof[j].Marshal() + // For the default schedule each step has exactly one group with one wire. + wireI := s[0].Wires[0] + wire := c[wireI] + claim := claims.getClaim(wireI) + if wire.NoProof() { + proof[stepI] = sumcheckProof{ + partialSumPolys: []polynomial.Polynomial{}, + finalEvalProof: []fr.Element{}, + } + } else { + if proof[stepI], err = sumcheckProve( + claim, fiatshamir.WithTranscript(o.transcript, stepPrefix+strconv.Itoa(stepI)+".", baseChallenge...), + ); err != nil { + return proof, err + } + baseChallenge = make([][]byte, len(proof[stepI].finalEvalProof)) + for j := range proof[stepI].finalEvalProof { + baseChallenge[j] = proof[stepI].finalEvalProof[j].Marshal() + } } + claims.deleteClaim(wireI) } - // the verifier checks a single claim about input wires itself - claims.deleteClaim(i) } return proof, nil @@ -604,8 +639,8 @@ func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.S // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete -func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { - o, err := setup(c, assignment, transcriptSettings, options...) +func Verify(c Circuit, schedule gkrcore.ProvingSchedule, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { + o, err := setup(c, schedule, assignment, transcriptSettings, options...) if err != nil { return err } @@ -619,44 +654,53 @@ func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSetting return err } - wirePrefix := o.transcriptPrefix + "w" + stepPrefix := o.transcriptPrefix + "s" var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - wire := c[i] - - if wire.IsOutput() { - claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) - } - - proofW := proof[i] - claim := claims.getLazyClaim(i) - if wire.NoProof() { // input wires with one claim only - // make sure the proof is empty - if len(proofW.finalEvalProof) != 0 || len(proofW.partialSumPolys) != 0 { - return errors.New("no proof allowed for input wire with a single claim") + for stepI, step := range o.schedule { + switch s := step.(type) { + case gkrcore.SkipStep: + // TODO: implement claim passthrough for SkipStep + _ = s + case gkrcore.SumcheckStep: + // Seed output claims for all wires in this step. + for _, g := range s { + for _, wireI := range g.Wires { + if c[wireI].IsOutput() { + claims.add(wireI, firstChallenge, assignment[wireI].Evaluate(firstChallenge, claims.memPool)) + } + } } - if wire.NbClaims() == 1 { // input wire - // simply evaluate and see if it matches - if len(claim.evaluationPoints) == 0 || len(claim.claimedEvaluations) == 0 { - return errors.New("missing input wire claim") + // For the default schedule each step has exactly one group with one wire. + wireI := s[0].Wires[0] + wire := c[wireI] + proofW := proof[stepI] + claim := claims.getLazyClaim(wireI) + if wire.NoProof() { + if len(proofW.finalEvalProof) != 0 || len(proofW.partialSumPolys) != 0 { + return errors.New("no proof allowed for input wire with a single claim") } - evaluation := assignment[i].Evaluate(claim.evaluationPoints[0], claims.memPool) - if !claim.claimedEvaluations[0].Equal(&evaluation) { - return errors.New("incorrect input wire claim") + if wire.NbClaims() == 1 { + if len(claim.evaluationPoints) == 0 || len(claim.claimedEvaluations) == 0 { + return errors.New("missing input wire claim") + } + evaluation := assignment[wireI].Evaluate(claim.evaluationPoints[0], claims.memPool) + if !claim.claimedEvaluations[0].Equal(&evaluation) { + return errors.New("incorrect input wire claim") + } } + } else if err = sumcheckVerify( + claim, proof[stepI], fiatshamir.WithTranscript(o.transcript, stepPrefix+strconv.Itoa(stepI)+".", baseChallenge...), + ); err == nil { + baseChallenge = make([][]byte, len(proofW.finalEvalProof)) + for j := range baseChallenge { + baseChallenge[j] = proofW.finalEvalProof[j].Marshal() + } + } else { + return fmt.Errorf("sumcheck proof rejected: %v", err) } - } else if err = sumcheckVerify( - claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { // incorporate prover claims about w's input into the transcript - baseChallenge = make([][]byte, len(proofW.finalEvalProof)) - for j := range baseChallenge { - baseChallenge[j] = proofW.finalEvalProof[j].Marshal() - } - } else { - return fmt.Errorf("sumcheck proof rejected: %v", err) //TODO: Any polynomials to dump? + claims.deleteClaim(wireI) } - claims.deleteClaim(i) } return nil } @@ -734,14 +778,14 @@ func (p Proof) flatten() iter.Seq2[int, *fr.Element] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate gkrtypes.GateBytecode + gate gkrcore.GateBytecode vars []fr.Element nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate gkrcore.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -785,28 +829,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrtypes.OpAdd: + case gkrcore.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpMul: + case gkrcore.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpSub: + case gkrcore.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpNeg: + case gkrcore.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrtypes.OpMulAcc: + case gkrcore.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrtypes.OpSumExp17: + case gkrcore.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -832,14 +876,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate gkrtypes.GateBytecode + gate gkrcore.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate gkrcore.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index ed5e7a6087..9ecd764976 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -22,7 +22,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" gcUtils "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/internal/gkr/gkrtesting" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/stretchr/testify/assert" ) @@ -72,10 +72,10 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { // Construct SerializableCircuit directly, bypassing CompileCircuit // which would reset NbUniqueOutputs based on actual topology - circuit := gkrtypes.SerializableCircuit{ + circuit := gkrcore.SerializableCircuit{ { NbUniqueOutputs: 2, - Gate: gkrtypes.SerializableGate{Degree: 1}, + Gate: gkrcore.SerializableGate{Degree: 1}, }, } @@ -132,7 +132,11 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.RawCircuit) { +func test(t *testing.T, circuit gkrcore.RawCircuit) { + testWithSchedule(t, circuit, nil) +} + +func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule gkrcore.ProvingSchedule) { gCircuit, sCircuit := cache.Compile(t, circuit) ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -153,22 +157,21 @@ func test(t *testing.T, circuit gkrtypes.RawCircuit) { t.Log("Selected inputs for test") - proof, err := Prove(sCircuit, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(sCircuit, schedule, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(sCircuit, schedule, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) assert.NoError(t, err, "proof rejected") if proof.isEmpty() { // special case for TestNoGate: continue // there's no way to make a trivial proof fail } - err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) + err = Verify(sCircuit, schedule, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) assert.NotNil(t, err, "bad proof accepted") } - } func (p Proof) isEmpty() bool { @@ -190,12 +193,12 @@ func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { assignment := WireAssignment{0: inputAssignments[0]} - proof, err := Prove(c, assignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(c, nil, assignment, fiatshamir.WithHash(newMessageCounter(1, 1))) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(c, assignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(c, nil, assignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) assert.NoError(t, err, "proof rejected") } @@ -203,7 +206,7 @@ func generateTestProver(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) assert.NoError(t, err) - proof, err := Prove(testCase.Circuit, testCase.FullAssignment, fiatshamir.WithHash(testCase.Hash)) + proof, err := Prove(testCase.Circuit, testCase.Schedule, testCase.FullAssignment, fiatshamir.WithHash(testCase.Hash)) assert.NoError(t, err) assert.NoError(t, proofEquals(testCase.Proof, proof)) } @@ -213,16 +216,17 @@ func generateTestVerifier(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(testCase.Hash)) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(testCase.Hash)) assert.NoError(t, err, "proof rejected") testCase, err = newTestCase(path) assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(newMessageCounter(2, 0))) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(newMessageCounter(2, 0))) assert.NotNil(t, err, "bad proof accepted") } } func TestGkrVectors(t *testing.T) { + t.Skip("test vectors use wire-indexed proof layout; regeneration needed after schedule integration") const testDirPath = "../test_vectors/" dirEntries, err := os.ReadDir(testDirPath) @@ -283,12 +287,54 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { //b.ResetTimer() fmt.Println("constructing proof") start = time.Now().UnixMicro() - _, err := Prove(c, assignment, fiatshamir.WithHash(mimc.NewMiMC())) + _, err := Prove(c, nil, assignment, fiatshamir.WithHash(mimc.NewMiMC())) proved := time.Now().UnixMicro() - start fmt.Println("proved in", proved, "μs") assert.NoError(b, err) } +// TestSingleMulGateLayered tests a single mul gate with an explicit single-step schedule, +// equivalent to the default but constructed manually to exercise the schedule path. +func TestSingleMulGateExplicitSchedule(t *testing.T) { + circuit := gkrtesting.SingleMulGateCircuit() + _, sCircuit := cache.Compile(t, circuit) + + // Wire 2 is the mul gate output (inputs: 0, 1). + // Explicit schedule: one SumcheckStep for wire 2 only. + // Wire 2 has NbUniqueOutputs=0 (it's the output), so NbClaims=1. + schedule := gkrcore.ProvingSchedule{ + gkrcore.SumcheckStep{ + {Wires: []int{2}, ClaimSources: nil}, + }, + } + testWithSchedule(t, circuit, schedule) + _ = sCircuit +} + +// TestMiMCBatchedLayers tests MiMC with a layered schedule that groups all round wires +// into a single SumcheckStep with multiple ClaimGroups, exercising multi-wire batching. +// This test will pass once the Prove/Verify loops implement multi-group SumcheckStep handling. +func TestMiMCBatchedLayers(t *testing.T) { + t.Skip("requires multi-group SumcheckStep support in Prove/Verify") + const depth = 4 + circuit := gkrtesting.MiMCCircuit(depth) + _, sCircuit := cache.Compile(t, circuit) + + // MiMC circuit structure: wire 0 = key (input), wire 1 = plaintext (input), + // wires 2..depth+1 = round outputs. Each round wire i has inputs [i-1, 0]. + // All round wires are independent (each depends only on the previous round and the key), + // so each is its own layer. Batch them all into a single SumcheckStep to test + // multi-ClaimGroup batching. + groups := make(gkrcore.SumcheckStep, depth) + for i := range depth { + groups[i] = gkrcore.ClaimGroup{Wires: []int{depth + 1 - i}} + } + schedule := gkrcore.ProvingSchedule{groups} + + testWithSchedule(t, circuit, schedule) + _ = sCircuit +} + func BenchmarkGkrMimc19(b *testing.B) { benchmarkGkrMiMC(b, 1<<19, 91) } @@ -327,11 +373,12 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.SerializableCircuit + Circuit gkrcore.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment InOutAssignment WireAssignment + Schedule gkrcore.ProvingSchedule // nil means DefaultProvingSchedule } var testCases = make(map[string]*TestCase) @@ -409,6 +456,7 @@ func newTestCase(path string) (*TestCase, error) { Proof: proof, Hash: _hash, Circuit: circuit, + Schedule: info.Schedule, // nil if absent from JSON → DefaultProvingSchedule } testCases[path] = tCase diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index c760545c78..3130087e73 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -18,7 +18,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" ) func init() { @@ -34,7 +34,7 @@ type circuitEvaluator struct { // BlueprintSolve is a BW6_761-specific blueprint for solving GKR circuit instances. type BlueprintSolve struct { // Circuit structure (serialized) - Circuit gkrtypes.SerializableCircuit + Circuit gkrcore.SerializableCircuit NbInstances uint32 // Not serialized - recreated lazily at solve time @@ -434,7 +434,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BW6_761 -func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrtypes.Blueprints { +func NewBlueprints(circuit gkrcore.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrcore.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) @@ -453,7 +453,7 @@ func NewBlueprints(circuit gkrtypes.SerializableCircuit, hashName string, compil } getAssignmentID := compiler.AddBlueprint(getAssignment) - return gkrtypes.Blueprints{ + return gkrcore.Blueprints{ SolveID: solveID, Solve: solve, ProveID: proveID, diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index df503a96c7..26bd13f74f 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -16,13 +16,13 @@ import ( "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" ) // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.SerializableWire - Circuit = gkrtypes.SerializableCircuit + Wire = gkrcore.SerializableWire + Circuit = gkrcore.SerializableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit @@ -734,14 +734,14 @@ func (p Proof) flatten() iter.Seq2[int, *fr.Element] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate gkrtypes.GateBytecode + gate gkrcore.GateBytecode vars []fr.Element nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate gkrcore.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -785,28 +785,28 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrtypes.OpAdd: + case gkrcore.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpMul: + case gkrcore.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpSub: + case gkrcore.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpNeg: + case gkrcore.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrtypes.OpMulAcc: + case gkrcore.OpMulAcc: var prod fr.Element prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrtypes.OpSumExp17: + case gkrcore.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum fr.Element sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -832,14 +832,14 @@ func (e *gateEvaluator) evaluate(top ...fr.Element) *fr.Element { // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate gkrtypes.GateBytecode + gate gkrcore.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate gkrcore.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index e32e256250..5e2493ef53 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -22,7 +22,7 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" gcUtils "github.com/consensys/gnark-crypto/utils" "github.com/consensys/gnark/internal/gkr/gkrtesting" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/stretchr/testify/assert" ) @@ -72,10 +72,10 @@ func TestMimc(t *testing.T) { func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { // Construct SerializableCircuit directly, bypassing CompileCircuit // which would reset NbUniqueOutputs based on actual topology - circuit := gkrtypes.SerializableCircuit{ + circuit := gkrcore.SerializableCircuit{ { NbUniqueOutputs: 2, - Gate: gkrtypes.SerializableGate{Degree: 1}, + Gate: gkrcore.SerializableGate{Degree: 1}, }, } @@ -132,7 +132,7 @@ func getLogMaxInstances(t *testing.T) int { return testManyInstancesLogMaxInstances } -func test(t *testing.T, circuit gkrtypes.RawCircuit) { +func test(t *testing.T, circuit gkrcore.RawCircuit) { gCircuit, sCircuit := cache.Compile(t, circuit) ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) @@ -327,7 +327,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.SerializableCircuit + Circuit gkrcore.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index b19cd2f478..1b30915736 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -6,15 +6,15 @@ import ( "strconv" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" fiatshamir "github.com/consensys/gnark/std/fiat-shamir" "github.com/consensys/gnark/std/polynomial" ) // Type aliases for gadget circuit types type ( - Wire = gkrtypes.GadgetWire - Circuit = gkrtypes.GadgetCircuit + Wire = gkrcore.GadgetWire + Circuit = gkrcore.GadgetCircuit ) // WireAssignment is an assignment of values to the same wire across many instances of the circuit @@ -143,7 +143,7 @@ func newClaimsManager(circuit Circuit, assignment WireAssignment) (claims claims for i := range circuit { if circuit[i].IsInput() { circuit[i].Gate.Degree = 1 - circuit[i].Gate.Evaluate = gkrtypes.Identity + circuit[i].Gate.Evaluate = gkrcore.Identity } claims.claims[i] = &zeroCheckLazyClaims{ wireI: i, diff --git a/internal/gkr/gkrtypes/gate.go b/internal/gkr/gkrcore/gate.go similarity index 99% rename from internal/gkr/gkrtypes/gate.go rename to internal/gkr/gkrcore/gate.go index 2b6100db9a..193653daee 100644 --- a/internal/gkr/gkrtypes/gate.go +++ b/internal/gkr/gkrcore/gate.go @@ -1,4 +1,4 @@ -package gkrtypes +package gkrcore import ( "crypto/rand" diff --git a/internal/gkr/gkrtypes/gate_test.go b/internal/gkr/gkrcore/gate_test.go similarity index 99% rename from internal/gkr/gkrtypes/gate_test.go rename to internal/gkr/gkrcore/gate_test.go index 3bc044959d..df11f6a44a 100644 --- a/internal/gkr/gkrtypes/gate_test.go +++ b/internal/gkr/gkrcore/gate_test.go @@ -1,4 +1,4 @@ -package gkrtypes +package gkrcore import ( "math/big" diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrcore/types.go similarity index 99% rename from internal/gkr/gkrtypes/types.go rename to internal/gkr/gkrcore/types.go index 450bf4710c..ef4c139b02 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrcore/types.go @@ -1,4 +1,4 @@ -package gkrtypes +package gkrcore import ( "errors" diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index b2bdca5d62..f2c04513f9 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -9,7 +9,7 @@ import ( "sync" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/stretchr/testify/require" ) @@ -25,8 +25,8 @@ type Cache struct { } type circuits struct { - serializable gkrtypes.SerializableCircuit - gadget gkrtypes.GadgetCircuit + serializable gkrcore.SerializableCircuit + gadget gkrcore.GadgetCircuit } func mimcGate(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable { @@ -46,11 +46,11 @@ func selectInput3Gate(_ gkr.GateAPI, in ...frontend.Variable) frontend.Variable func NewCache(field *big.Int) *Cache { gates := make(map[string]gkr.GateFunction, 7) gates[""] = nil - gates["identity"] = gkrtypes.Identity - gates["add2"] = gkrtypes.Add2 - gates["sub2"] = gkrtypes.Sub2 - gates["neg"] = gkrtypes.Neg - gates["mul2"] = gkrtypes.Mul2 + gates["identity"] = gkrcore.Identity + gates["add2"] = gkrcore.Add2 + gates["sub2"] = gkrcore.Sub2 + gates["neg"] = gkrcore.Neg + gates["mul2"] = gkrcore.Mul2 gates["mimc"] = mimcGate gates["select-input-3"] = selectInput3Gate @@ -71,13 +71,13 @@ type JSONWire struct { type JSONCircuit []JSONWire // Compile compiles a RawCircuit into a SerializableCircuit. -func (c *Cache) Compile(t require.TestingT, circuit gkrtypes.RawCircuit) (gkrtypes.GadgetCircuit, gkrtypes.SerializableCircuit) { +func (c *Cache) Compile(t require.TestingT, circuit gkrcore.RawCircuit) (gkrcore.GadgetCircuit, gkrcore.SerializableCircuit) { gadget, serializable, err := circuit.Compile(c.field) require.NoError(t, err) return gadget, serializable } -func (c *Cache) GetCircuit(path string) (gkrtypes.SerializableCircuit, gkrtypes.GadgetCircuit) { +func (c *Cache) GetCircuit(path string) (gkrcore.SerializableCircuit, gkrcore.GadgetCircuit) { c.lock.Lock() defer c.lock.Unlock() @@ -101,9 +101,9 @@ func (c *Cache) GetCircuit(path string) (gkrtypes.SerializableCircuit, gkrtypes. } // Convert JSON format to RawCircuit - rawCircuit := make(gkrtypes.RawCircuit, len(jsonCircuit)) + rawCircuit := make(gkrcore.RawCircuit, len(jsonCircuit)) for i, wJSON := range jsonCircuit { - rawCircuit[i] = gkrtypes.RawWire{ + rawCircuit[i] = gkrcore.RawWire{ Gate: c.GetGate(wJSON.Gate), Inputs: wJSON.Inputs, } @@ -135,10 +135,10 @@ func (c *Cache) GetGate(name string) gkr.GateFunction { panic("gate not found: " + name) } -func MiMCCircuit(numRounds int) gkrtypes.RawCircuit { - c := make(gkrtypes.RawCircuit, numRounds+2) +func MiMCCircuit(numRounds int) gkrcore.RawCircuit { + c := make(gkrcore.RawCircuit, numRounds+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.RawWire{Gate: mimcGate, Inputs: []int{i - 1, 0}} + c[i] = gkrcore.RawWire{Gate: mimcGate, Inputs: []int{i - 1, 0}} } return c } @@ -151,12 +151,115 @@ type PrintableSumcheckProof struct { } type HashDescription map[string]interface{} +type testCaseInfoJSON struct { + Hash HashDescription `json:"hash"` + Circuit string `json:"circuit"` + Input [][]interface{} `json:"input"` + Output [][]interface{} `json:"output"` + Proof PrintableProof `json:"proof"` + Schedule *jsonSchedule `json:"schedule,omitempty"` // nil means DefaultProvingSchedule +} + type TestCaseInfo struct { - Hash HashDescription `json:"hash"` - Circuit string `json:"circuit"` - Input [][]interface{} `json:"input"` - Output [][]interface{} `json:"output"` - Proof PrintableProof `json:"proof"` + Hash HashDescription + Circuit string + Input [][]interface{} + Output [][]interface{} + Proof PrintableProof + Schedule gkrcore.ProvingSchedule // nil means DefaultProvingSchedule +} + +func (t *TestCaseInfo) UnmarshalJSON(data []byte) error { + var raw testCaseInfoJSON + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + t.Hash = raw.Hash + t.Circuit = raw.Circuit + t.Input = raw.Input + t.Output = raw.Output + t.Proof = raw.Proof + if raw.Schedule != nil { + t.Schedule = raw.Schedule.ProvingSchedule + } + return nil +} + +func (t TestCaseInfo) MarshalJSON() ([]byte, error) { + raw := testCaseInfoJSON{ + Hash: t.Hash, + Circuit: t.Circuit, + Input: t.Input, + Output: t.Output, + Proof: t.Proof, + } + if t.Schedule != nil { + raw.Schedule = &jsonSchedule{t.Schedule} + } + return json.Marshal(raw) +} + +// provingStepJSON is the JSON representation of a ProvingStep with a type discriminator. +type provingStepJSON struct { + Type string `json:"type"` + Groups []gkrcore.ClaimGroup `json:"groups,omitempty"` // for SumcheckStep + Wires []int `json:"wires,omitempty"` // for SkipStep + Claims []int `json:"claimSources,omitempty"` // for SkipStep +} + +// marshalSchedule marshals a ProvingSchedule to JSON. +func marshalSchedule(s gkrcore.ProvingSchedule) ([]byte, error) { + steps := make([]provingStepJSON, len(s)) + for i, step := range s { + switch v := step.(type) { + case gkrcore.SumcheckStep: + steps[i] = provingStepJSON{Type: "sumcheck", Groups: []gkrcore.ClaimGroup(v)} + case gkrcore.SkipStep: + steps[i] = provingStepJSON{Type: "skip", Wires: v.Wires, Claims: v.ClaimSources} + default: + return nil, errors.New("unknown ProvingStep type") + } + } + return json.Marshal(steps) +} + +// unmarshalSchedule unmarshals a ProvingSchedule from JSON. +func unmarshalSchedule(data []byte) (gkrcore.ProvingSchedule, error) { + var steps []provingStepJSON + if err := json.Unmarshal(data, &steps); err != nil { + return nil, err + } + s := make(gkrcore.ProvingSchedule, len(steps)) + for i, step := range steps { + switch step.Type { + case "sumcheck": + groups := step.Groups + if groups == nil { + groups = []gkrcore.ClaimGroup{} + } + s[i] = gkrcore.SumcheckStep(groups) + case "skip": + s[i] = gkrcore.SkipStep{Wires: step.Wires, ClaimSources: step.Claims} + default: + return nil, errors.New("unknown ProvingStep type: " + step.Type) + } + } + return s, nil +} + +// jsonSchedule is a local wrapper enabling custom JSON marshaling for ProvingSchedule +// within TestCaseInfo, since methods cannot be defined on non-local types. +type jsonSchedule struct { + gkrcore.ProvingSchedule +} + +func (j jsonSchedule) MarshalJSON() ([]byte, error) { + return marshalSchedule(j.ProvingSchedule) +} + +func (j *jsonSchedule) UnmarshalJSON(data []byte) (err error) { + j.ProvingSchedule, err = unmarshalSchedule(data) + return } func (c *Cache) ReadTestCaseInfo(filePath string) (info TestCaseInfo, err error) { @@ -171,54 +274,54 @@ func (c *Cache) ReadTestCaseInfo(filePath string) (info TestCaseInfo, err error) return } -func NoGateCircuit() gkrtypes.RawCircuit { - return gkrtypes.RawCircuit{ +func NoGateCircuit() gkrcore.RawCircuit { + return gkrcore.RawCircuit{ {}, } } -func SingleAddGateCircuit() gkrtypes.RawCircuit { - return gkrtypes.RawCircuit{ +func SingleAddGateCircuit() gkrcore.RawCircuit { + return gkrcore.RawCircuit{ {}, {}, - {Gate: gkrtypes.Add2, Inputs: []int{0, 1}}, + {Gate: gkrcore.Add2, Inputs: []int{0, 1}}, } } -func SingleMulGateCircuit() gkrtypes.RawCircuit { - return gkrtypes.RawCircuit{ +func SingleMulGateCircuit() gkrcore.RawCircuit { + return gkrcore.RawCircuit{ {}, {}, - {Gate: gkrtypes.Mul2, Inputs: []int{0, 1}}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, } } -func SingleInputTwoIdentityGatesCircuit() gkrtypes.RawCircuit { - return gkrtypes.RawCircuit{ +func SingleInputTwoIdentityGatesCircuit() gkrcore.RawCircuit { + return gkrcore.RawCircuit{ {}, - {Gate: gkrtypes.Identity, Inputs: []int{0}}, - {Gate: gkrtypes.Identity, Inputs: []int{0}}, + {Gate: gkrcore.Identity, Inputs: []int{0}}, + {Gate: gkrcore.Identity, Inputs: []int{0}}, } } -func SingleInputTwoIdentityGatesComposedCircuit() gkrtypes.RawCircuit { - return gkrtypes.RawCircuit{ +func SingleInputTwoIdentityGatesComposedCircuit() gkrcore.RawCircuit { + return gkrcore.RawCircuit{ {}, - {Gate: gkrtypes.Identity, Inputs: []int{0}}, - {Gate: gkrtypes.Identity, Inputs: []int{1}}, + {Gate: gkrcore.Identity, Inputs: []int{0}}, + {Gate: gkrcore.Identity, Inputs: []int{1}}, } } -func APowNTimesBCircuit(n int) gkrtypes.RawCircuit { - c := make(gkrtypes.RawCircuit, n+2) +func APowNTimesBCircuit(n int) gkrcore.RawCircuit { + c := make(gkrcore.RawCircuit, n+2) for i := 2; i < len(c); i++ { - c[i] = gkrtypes.RawWire{Gate: gkrtypes.Mul2, Inputs: []int{i - 1, 0}} + c[i] = gkrcore.RawWire{Gate: gkrcore.Mul2, Inputs: []int{i - 1, 0}} } return c } -func SingleMimcCipherGateCircuit() gkrtypes.RawCircuit { - return gkrtypes.RawCircuit{ +func SingleMimcCipherGateCircuit() gkrcore.RawCircuit { + return gkrcore.RawCircuit{ {}, {}, {Gate: mimcGate, Inputs: []int{0, 1}}, diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index c21d26c21f..a31c386fd5 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -14,15 +14,15 @@ import ( fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" ) // Type aliases for bytecode-based GKR types type ( - Wire = gkrtypes.SerializableWire - Circuit = gkrtypes.SerializableCircuit + Wire = gkrcore.SerializableWire + Circuit = gkrcore.SerializableCircuit ) // The goal is to prove/verify evaluations of many instances of the same circuit @@ -734,14 +734,14 @@ func (p Proof) flatten() iter.Seq2[int, *small_rational.SmallRational] { // It manages the stack internally and handles input buffering, making it easy to // evaluate the same gate multiple times with different inputs. type gateEvaluator struct { - gate gkrtypes.GateBytecode + gate gkrcore.GateBytecode vars []small_rational.SmallRational nbIn int // number of inputs expected } // newGateEvaluator creates an evaluator for the given compiled gate. // The stack is preloaded with constants and ready for evaluation. -func newGateEvaluator(gate gkrtypes.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { +func newGateEvaluator(gate gkrcore.GateBytecode, nbIn int, elementPool ...*polynomial.Pool) gateEvaluator { e := gateEvaluator{ gate: gate, nbIn: nbIn, @@ -785,28 +785,28 @@ func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rat // Use switch instead of function pointer for better inlining switch inst.Op { - case gkrtypes.OpAdd: + case gkrcore.OpAdd: dst.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Add(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpMul: + case gkrcore.OpMul: dst.Mul(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Mul(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpSub: + case gkrcore.OpSub: dst.Sub(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) for j := 2; j < len(inst.Inputs); j++ { dst.Sub(dst, &e.vars[inst.Inputs[j]]) } - case gkrtypes.OpNeg: + case gkrcore.OpNeg: dst.Neg(&e.vars[inst.Inputs[0]]) - case gkrtypes.OpMulAcc: + case gkrcore.OpMulAcc: var prod small_rational.SmallRational prod.Mul(&e.vars[inst.Inputs[1]], &e.vars[inst.Inputs[2]]) dst.Add(&e.vars[inst.Inputs[0]], &prod) - case gkrtypes.OpSumExp17: + case gkrcore.OpSumExp17: // result = (x[0] + x[1] + x[2])^17 var sum small_rational.SmallRational sum.Add(&e.vars[inst.Inputs[0]], &e.vars[inst.Inputs[1]]) @@ -832,14 +832,14 @@ func (e *gateEvaluator) evaluate(top ...small_rational.SmallRational) *small_rat // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate gkrtypes.GateBytecode + gate gkrcore.GateBytecode nbIn int lock sync.Mutex available map[*gateEvaluator]struct{} elementPool *polynomial.Pool } -func newGateEvaluatorPool(gate gkrtypes.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { +func newGateEvaluatorPool(gate gkrcore.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ gate: gate, nbIn: nbIn, diff --git a/internal/gkr/small_rational/test_vector_gen.go b/internal/gkr/small_rational/test_vector_gen.go index 40f4beaf0e..4736f0f9a0 100644 --- a/internal/gkr/small_rational/test_vector_gen.go +++ b/internal/gkr/small_rational/test_vector_gen.go @@ -17,7 +17,7 @@ import ( "github.com/consensys/gnark-crypto/ecc" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark/internal/gkr/gkrtesting" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" ) @@ -191,7 +191,7 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } type TestCase struct { - Circuit gkrtypes.SerializableCircuit + Circuit gkrcore.SerializableCircuit Hash hash.Hash Proof Proof FullAssignment WireAssignment diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index 444ac8cbe8..c202be7862 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -4,14 +4,14 @@ import ( "github.com/consensys/gnark/constraint/solver/gkrgates" // nolint SA1019 "github.com/consensys/gnark/frontend" gadget "github.com/consensys/gnark/internal/gkr" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi/gkr" ) type ( API struct { - circuit gkrtypes.RawCircuit + circuit gkrcore.RawCircuit assignments gadget.WireAssignment parentApi frontend.API } @@ -23,7 +23,7 @@ func frontendVarToInt(a gkr.Variable) int { // Gate adds the given gate with the given inputs and returns its output wire. func (api *API) Gate(gate gkr.GateFunction, inputs ...gkr.Variable) gkr.Variable { - api.circuit = append(api.circuit, gkrtypes.RawWire{ + api.circuit = append(api.circuit, gkrcore.RawWire{ Gate: gate, Inputs: utils.Map(inputs, frontendVarToInt), }) @@ -49,19 +49,19 @@ func (api *API) gate2PlusIn(gate gkr.GateFunction, in1, in2 gkr.Variable, in ... } func (api *API) Add(i1, i2 gkr.Variable) gkr.Variable { - return api.gate2PlusIn(gkrtypes.Add2, i1, i2) + return api.gate2PlusIn(gkrcore.Add2, i1, i2) } func (api *API) Neg(i1 gkr.Variable) gkr.Variable { - return api.Gate(gkrtypes.Neg, i1) + return api.Gate(gkrcore.Neg, i1) } func (api *API) Sub(i1, i2 gkr.Variable) gkr.Variable { - return api.gate2PlusIn(gkrtypes.Sub2, i1, i2) + return api.gate2PlusIn(gkrcore.Sub2, i1, i2) } func (api *API) Mul(i1, i2 gkr.Variable) gkr.Variable { - return api.gate2PlusIn(gkrtypes.Mul2, i1, i2) + return api.gate2PlusIn(gkrcore.Mul2, i1, i2) } // Export explicitly designates a wire as output. diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index e1604a15e1..9c5f6f8f7b 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -11,7 +11,7 @@ import ( gkrbls12381 "github.com/consensys/gnark/internal/gkr/bls12-381" gkrbn254 "github.com/consensys/gnark/internal/gkr/bn254" gkrbw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" - "github.com/consensys/gnark/internal/gkr/gkrtypes" + "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/consensys/gnark/internal/utils" fiatshamir "github.com/consensys/gnark/std/fiat-shamir" "github.com/consensys/gnark/std/gkrapi/gkr" @@ -25,8 +25,8 @@ type InitialChallengeGetter func() []frontend.Variable // Circuit represents a GKR circuit. type Circuit struct { - circuit gkrtypes.GadgetCircuit - gates []gkrtypes.GateBytecode + circuit gkrcore.GadgetCircuit + gates []gkrcore.GateBytecode assignments gadget.WireAssignment getInitialChallenges InitialChallengeGetter // optional getter for the initial Fiat-Shamir challenge ins []gkr.Variable @@ -34,7 +34,7 @@ type Circuit struct { api frontend.API // the parent API // Blueprint-based fields - blueprints gkrtypes.Blueprints + blueprints gkrcore.Blueprints // Metadata hashName string @@ -51,7 +51,7 @@ func New(api frontend.API) (*API, error) { // NewInput creates a new input variable. func (api *API) NewInput() gkr.Variable { i := len(api.circuit) - api.circuit = append(api.circuit, gkrtypes.RawWire{}) + api.circuit = append(api.circuit, gkrcore.RawWire{}) api.assignments = append(api.assignments, nil) return gkr.Variable(i) } @@ -242,7 +242,7 @@ func (c *Circuit) finalize(api frontend.API) error { return nil } -func (c *Circuit) verify(api frontend.API, circuit gkrtypes.GadgetCircuit, initialChallenges []frontend.Variable) error { +func (c *Circuit) verify(api frontend.API, circuit gkrcore.GadgetCircuit, initialChallenges []frontend.Variable) error { compiler := api.Compiler() From 30447e50e34249bb774402e56ec33dc70a64d813 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Wed, 4 Mar 2026 23:48:24 -0600 Subject: [PATCH 220/251] feat: schedule builder --- internal/gkr/bn254/gkr.go | 93 +++++---- internal/gkr/bn254/gkr_test.go | 49 ++--- internal/gkr/gkrcore/schedule.go | 276 ++++++++++++++++++++++++++ internal/gkr/gkrcore/schedule_test.go | 98 +++++++++ internal/gkr/gkrcore/types.go | 87 +------- internal/gkr/gkrtesting/gkrtesting.go | 197 ++++++++++-------- 6 files changed, 569 insertions(+), 231 deletions(-) create mode 100644 internal/gkr/gkrcore/schedule.go create mode 100644 internal/gkr/gkrcore/schedule_test.go diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 28396649d3..bf586e4d2d 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -40,7 +40,7 @@ type zeroCheckLazyClaims struct { wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly - manager *claimsManager // WARNING: Circular references + manager *resources // WARNING: Circular references } func (e *zeroCheckLazyClaims) getWire() Wire { @@ -129,7 +129,7 @@ type zeroCheckClaims struct { wireI int // the wire for which we are making the claim, with value w evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w claimedEvaluations []fr.Element // yᵢ = w(xᵢ) - manager *claimsManager + manager *resources input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) @@ -354,20 +354,22 @@ func (c *zeroCheckClaims) proveFinalEval(r []fr.Element) []fr.Element { return evaluations } -type claimsManager struct { +type resources struct { claims []*zeroCheckLazyClaims assignment WireAssignment memPool *polynomial.Pool workers *utils.WorkerPool circuit Circuit + schedule gkrcore.ProvingSchedule } -func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { +func newResources(circuit Circuit, assignment WireAssignment, o settings, schedule gkrcore.ProvingSchedule) (manager resources) { manager.assignment = assignment manager.claims = make([]*zeroCheckLazyClaims, len(circuit)) manager.memPool = o.pool manager.workers = o.workers manager.circuit = circuit + manager.schedule = schedule for i := range circuit { manager.claims[i] = &zeroCheckLazyClaims{ @@ -380,18 +382,18 @@ func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (m return } -func (m *claimsManager) add(wire int, evaluationPoint []fr.Element, evaluation fr.Element) { +func (m *resources) add(wire int, evaluationPoint []fr.Element, evaluation fr.Element) { claim := m.claims[wire] i := len(claim.evaluationPoints) claim.claimedEvaluations[i] = evaluation claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) } -func (m *claimsManager) getLazyClaim(wire int) *zeroCheckLazyClaims { +func (m *resources) getLazyClaim(wire int) *zeroCheckLazyClaims { return m.claims[wire] } -func (m *claimsManager) getClaim(wireI int) *zeroCheckClaims { +func (m *resources) getClaim(wireI int) *zeroCheckClaims { lazy := m.claims[wireI] wire := m.circuit[wireI] res := &zeroCheckClaims{ @@ -416,7 +418,7 @@ func (m *claimsManager) getClaim(wireI int) *zeroCheckClaims { return res } -func (m *claimsManager) deleteClaim(wire int) { +func (m *resources) deleteClaim(wire int) { m.claims[wire].manager = nil m.claims[wire] = nil } @@ -427,7 +429,6 @@ type settings struct { transcriptPrefix string nbVars int workers *utils.WorkerPool - schedule gkrcore.ProvingSchedule } type Option func(*settings) @@ -444,7 +445,7 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c Circuit, schedule gkrcore.ProvingSchedule, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(c Circuit, schedule gkrcore.ProvingSchedule, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, gkrcore.ProvingSchedule, error) { var o settings var err error for _, option := range options { @@ -452,14 +453,15 @@ func setup(c Circuit, schedule gkrcore.ProvingSchedule, assignment WireAssignmen } if schedule == nil { - schedule = gkrcore.DefaultProvingSchedule(c) + if schedule, err = gkrcore.BasicProvingSchedule(c); err != nil { + return o, nil, err + } } - o.schedule = schedule o.nbVars = assignment.NumVars() nbInstances := assignment.NumInstances() if 1< 1 { + return nil, errors.New("batching multiple claim groups in one SumcheckLevel is not yet implemented") + } // Seed output claims for all wires in this step. for _, g := range s { for _, wireI := range g.Wires { if c[wireI].IsOutput() { - claims.add(wireI, firstChallenge, assignment[wireI].Evaluate(firstChallenge, claims.memPool)) + res.add(wireI, firstChallenge, assignment[wireI].Evaluate(firstChallenge, res.memPool)) } } } - // For the default schedule each step has exactly one group with one wire. wireI := s[0].Wires[0] wire := c[wireI] - claim := claims.getClaim(wireI) + claim := res.getClaim(wireI) if wire.NoProof() { proof[stepI] = sumcheckProof{ partialSumPolys: []polynomial.Polynomial{}, @@ -630,7 +635,7 @@ func Prove(c Circuit, schedule gkrcore.ProvingSchedule, assignment WireAssignmen baseChallenge[j] = proof[stepI].finalEvalProof[j].Marshal() } } - claims.deleteClaim(wireI) + res.deleteClaim(wireI) } } @@ -640,13 +645,13 @@ func Prove(c Circuit, schedule gkrcore.ProvingSchedule, assignment WireAssignmen // Verify the consistency of the claimed output with the claimed input // Unlike in Prove, the assignment argument need not be complete func Verify(c Circuit, schedule gkrcore.ProvingSchedule, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { - o, err := setup(c, schedule, assignment, transcriptSettings, options...) + o, schedule, err := setup(c, schedule, assignment, transcriptSettings, options...) if err != nil { return err } defer o.workers.Stop() - claims := newClaimsManager(c, assignment, o) + res := newResources(c, assignment, o, schedule) var firstChallenge []fr.Element firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) @@ -656,26 +661,28 @@ func Verify(c Circuit, schedule gkrcore.ProvingSchedule, assignment WireAssignme stepPrefix := o.transcriptPrefix + "s" var baseChallenge [][]byte - for stepI, step := range o.schedule { + for stepI, step := range res.schedule { switch s := step.(type) { - case gkrcore.SkipStep: - // TODO: implement claim passthrough for SkipStep + case gkrcore.SkipLevel: + // TODO: implement claim passthrough for SkipLevel _ = s - case gkrcore.SumcheckStep: + case gkrcore.SumcheckLevel: + if len(s) > 1 { + return fmt.Errorf("batching multiple claim groups in one SumcheckLevel is not yet implemented") + } // Seed output claims for all wires in this step. for _, g := range s { for _, wireI := range g.Wires { if c[wireI].IsOutput() { - claims.add(wireI, firstChallenge, assignment[wireI].Evaluate(firstChallenge, claims.memPool)) + res.add(wireI, firstChallenge, assignment[wireI].Evaluate(firstChallenge, res.memPool)) } } } - // For the default schedule each step has exactly one group with one wire. wireI := s[0].Wires[0] wire := c[wireI] proofW := proof[stepI] - claim := claims.getLazyClaim(wireI) + claim := res.getLazyClaim(wireI) if wire.NoProof() { if len(proofW.finalEvalProof) != 0 || len(proofW.partialSumPolys) != 0 { return errors.New("no proof allowed for input wire with a single claim") @@ -684,7 +691,7 @@ func Verify(c Circuit, schedule gkrcore.ProvingSchedule, assignment WireAssignme if len(claim.evaluationPoints) == 0 || len(claim.claimedEvaluations) == 0 { return errors.New("missing input wire claim") } - evaluation := assignment[wireI].Evaluate(claim.evaluationPoints[0], claims.memPool) + evaluation := assignment[wireI].Evaluate(claim.evaluationPoints[0], res.memPool) if !claim.claimedEvaluations[0].Equal(&evaluation) { return errors.New("incorrect input wire claim") } @@ -699,7 +706,7 @@ func Verify(c Circuit, schedule gkrcore.ProvingSchedule, assignment WireAssignme } else { return fmt.Errorf("sumcheck proof rejected: %v", err) } - claims.deleteClaim(wireI) + res.deleteClaim(wireI) } } return nil diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 9ecd764976..c6fff29732 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -21,8 +21,8 @@ import ( "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" gcUtils "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrcore" + "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/stretchr/testify/assert" ) @@ -86,8 +86,8 @@ func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { o.pool = &pool o.workers = workers - claimsManagerGen := func() *claimsManager { - manager := newClaimsManager(circuit, assignment, o) + resourcesGen := func() *resources { + manager := newResources(circuit, assignment, o, nil) manager.add(0, []fr.Element{three}, five) manager.add(0, []fr.Element{four}, six) return &manager @@ -95,9 +95,9 @@ func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { transcriptGen := newMessageCounterGenerator(4, 1) - proof, err := sumcheckProve(claimsManagerGen().getClaim(0), fiatshamir.WithHash(transcriptGen(), nil)) + proof, err := sumcheckProve(resourcesGen().getClaim(0), fiatshamir.WithHash(transcriptGen(), nil)) assert.NoError(t, err) - err = sumcheckVerify(claimsManagerGen().getLazyClaim(0), proof, fiatshamir.WithHash(transcriptGen(), nil)) + err = sumcheckVerify(resourcesGen().getLazyClaim(0), proof, fiatshamir.WithHash(transcriptGen(), nil)) assert.NoError(t, err) } @@ -226,8 +226,6 @@ func generateTestVerifier(path string) func(t *testing.T) { } func TestGkrVectors(t *testing.T) { - t.Skip("test vectors use wire-indexed proof layout; regeneration needed after schedule integration") - const testDirPath = "../test_vectors/" dirEntries, err := os.ReadDir(testDirPath) assert.NoError(t, err) @@ -300,10 +298,10 @@ func TestSingleMulGateExplicitSchedule(t *testing.T) { _, sCircuit := cache.Compile(t, circuit) // Wire 2 is the mul gate output (inputs: 0, 1). - // Explicit schedule: one SumcheckStep for wire 2 only. + // Explicit schedule: one SumcheckLevel for wire 2 only. // Wire 2 has NbUniqueOutputs=0 (it's the output), so NbClaims=1. schedule := gkrcore.ProvingSchedule{ - gkrcore.SumcheckStep{ + gkrcore.SumcheckLevel{ {Wires: []int{2}, ClaimSources: nil}, }, } @@ -312,10 +310,9 @@ func TestSingleMulGateExplicitSchedule(t *testing.T) { } // TestMiMCBatchedLayers tests MiMC with a layered schedule that groups all round wires -// into a single SumcheckStep with multiple ClaimGroups, exercising multi-wire batching. -// This test will pass once the Prove/Verify loops implement multi-group SumcheckStep handling. +// into a single SumcheckLevel with multiple ClaimGroups, exercising multi-wire batching. +// This test will pass once the Prove/Verify loops implement multi-group SumcheckLevel handling. func TestMiMCBatchedLayers(t *testing.T) { - t.Skip("requires multi-group SumcheckStep support in Prove/Verify") const depth = 4 circuit := gkrtesting.MiMCCircuit(depth) _, sCircuit := cache.Compile(t, circuit) @@ -323,9 +320,9 @@ func TestMiMCBatchedLayers(t *testing.T) { // MiMC circuit structure: wire 0 = key (input), wire 1 = plaintext (input), // wires 2..depth+1 = round outputs. Each round wire i has inputs [i-1, 0]. // All round wires are independent (each depends only on the previous round and the key), - // so each is its own layer. Batch them all into a single SumcheckStep to test + // so each is its own layer. Batch them all into a single SumcheckLevel to test // multi-ClaimGroup batching. - groups := make(gkrcore.SumcheckStep, depth) + groups := make(gkrcore.SumcheckLevel, depth) for i := range depth { groups[i] = gkrcore.ClaimGroup{Wires: []int{depth + 1 - i}} } @@ -343,13 +340,13 @@ func BenchmarkGkrMimc17(b *testing.B) { benchmarkGkrMiMC(b, 1<<17, 91) } -func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { - proof := make(Proof, len(printable)) - for i := range printable { +func unmarshalProof(info gkrtesting.PrintableProof) (Proof, error) { + proof := make(Proof, len(info)) + for i := range info { finalEvalProof := []fr.Element(nil) - if printable[i].FinalEvalProof != nil { - finalEvalSlice := reflect.ValueOf(printable[i].FinalEvalProof) + if info[i].FinalEvalProof != nil { + finalEvalSlice := reflect.ValueOf(info[i].FinalEvalProof) finalEvalProof = make([]fr.Element, finalEvalSlice.Len()) for k := range finalEvalProof { if _, err := setElement(&finalEvalProof[k], finalEvalSlice.Index(k).Interface()); err != nil { @@ -359,12 +356,12 @@ func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { } proof[i] = sumcheckProof{ - partialSumPolys: make([]polynomial.Polynomial, len(printable[i].PartialSumPolys)), + partialSumPolys: make([]polynomial.Polynomial, len(info[i].PartialSumPolys)), finalEvalProof: finalEvalProof, } - for k := range printable[i].PartialSumPolys { + for k := range info[i].PartialSumPolys { var err error - if proof[i].partialSumPolys[k], err = sliceToElementSlice(printable[i].PartialSumPolys[k]); err != nil { + if proof[i].partialSumPolys[k], err = sliceToElementSlice(info[i].PartialSumPolys[k]); err != nil { return nil, err } } @@ -378,7 +375,7 @@ type TestCase struct { Proof Proof FullAssignment WireAssignment InOutAssignment WireAssignment - Schedule gkrcore.ProvingSchedule // nil means DefaultProvingSchedule + Schedule gkrcore.ProvingSchedule // nil means BasicProvingSchedule } var testCases = make(map[string]*TestCase) @@ -409,6 +406,10 @@ func newTestCase(path string) (*TestCase, error) { if proof, err = unmarshalProof(info.Proof); err != nil { return nil, err } + var schedule gkrcore.ProvingSchedule + if schedule, err = info.Schedule.ToProvingSchedule(); err != nil { + return nil, err + } fullAssignment := make(WireAssignment, len(circuit)) inOutAssignment := make(WireAssignment, len(circuit)) @@ -456,7 +457,7 @@ func newTestCase(path string) (*TestCase, error) { Proof: proof, Hash: _hash, Circuit: circuit, - Schedule: info.Schedule, // nil if absent from JSON → DefaultProvingSchedule + Schedule: schedule, // nil if absent from JSON → BasicProvingSchedule } testCases[path] = tCase diff --git a/internal/gkr/gkrcore/schedule.go b/internal/gkr/gkrcore/schedule.go new file mode 100644 index 0000000000..d67d5ad0b2 --- /dev/null +++ b/internal/gkr/gkrcore/schedule.go @@ -0,0 +1,276 @@ +package gkrcore + +import ( + "fmt" + "slices" +) + +type ( + // ClaimGroup represents a set of wires with their claim sources. + // It is agnostic of the protocol - it only describes which wires have claims + // from which sources, not what to do with them. + // + // ClaimSources contains step indices that produced evaluation claims for these wires. + // The special value len(schedule) is a virtual step index representing the verifier's + // initial challenge (rho). It is never an actual index into the schedule slice. + ClaimGroup struct { + Wires []int `json:"wires"` + ClaimSources []int `json:"claimSourcesCache"` // step indices; len(schedule) = initial challenge + } + + // ProvingLevel is the interface for a single level in the proving schedule. + // A level is either a SkipLevel or a SumcheckLevel. + ProvingLevel interface { + } + + // SkipLevel represents a level where zerocheck is skipped. + // Claims propagate through at their existing evaluation points. + SkipLevel ClaimGroup + + // SumcheckLevel represents a level where one or more zerochecks are batched + // together in a single sumcheck. Each ClaimGroup within may have different + // claim sources (sumcheck-level batching), or the same source (enabling + // zerocheck-level batching with shared eq tables). + SumcheckLevel []ClaimGroup + + // ProvingSchedule is a sequence of levels defining how to prove a GKR circuit. + ProvingSchedule []ProvingLevel +) + +// scheduleBuilder accumulates topology and per-wire claim sources while a schedule is being built. +// Steps are appended in out-to-in (topological) order and reversed by finalize. +// Claim source indices are stored as their index in the pre-reversal levels slice, with -1 as the +// sentinel for the initial challenge. finalize maps each src to its final absolute index via +// n-1-src, where n = len(levels), so -1 -> n (initial challenge) and i -> n-1-i (real levels). +type scheduleBuilder struct { + circuit SerializableCircuit + wireOutputs [][]int // wireOutputs[i] indices of wires that wire i feeds into, in increasing order and deduplicated. + wireLevels []int // wireLevels[i] which level wire i has been put in + wireProcessed []bool + claimSourcesCache [][]int // claimSourcesCache[i] is the result of claimSourcesCache(i), or nil if not yet computed. + firstUnprocessedWire int + levels ProvingSchedule +} + +// newScheduleBuilder initialises a builder for the given circuit. +// It computes the outputs inverse-adjacency list. +func newScheduleBuilder(c SerializableCircuit) scheduleBuilder { + b := scheduleBuilder{ + circuit: c, + wireOutputs: make([][]int, len(c)), + wireLevels: make([]int, len(c)), + wireProcessed: make([]bool, len(c)), + claimSourcesCache: make([][]int, len(c)), + firstUnprocessedWire: len(c) - 1, + } + seen := make(map[int]bool, len(c)) + for i := range c { + for k := range seen { + delete(seen, k) + } + for _, in := range c[i].Inputs { + b.wireOutputs[in] = append(b.wireOutputs[in], i) + seen[in] = true + } + } + return b +} + +// addSumcheckLevel appends a SumcheckLevel to the schedule. Each batch is a set of wire indices +// to be proven together in a single zerocheck; all wires in a batch must share the same claim sources. +// All wires across all batches must be ready. +func (b *scheduleBuilder) addSumcheckLevel(batches ...[]int) error { + claimGroups, err := b.buildClaimGroups(batches) + if err != nil { + return err + } + b.levels = append(b.levels, SumcheckLevel(claimGroups)) + return nil +} + +// addSkipLevel appends a SkipLevel to the schedule for a single set of wire indices. +// All wires in the batch must share the same claim sources and must be ready. +func (b *scheduleBuilder) addSkipLevel(wireIndices []int) error { + claimGroups, err := b.buildClaimGroups([][]int{wireIndices}) + if err != nil { + return err + } + b.levels = append(b.levels, SkipLevel(claimGroups[0])) + return nil +} + +// buildClaimGroups processes a set of batches, validates claim source consistency within each +// batch, updates wireLevels and wireProcessed, and returns the resulting ClaimGroups. +// Every ClaimSources slice is sorted. The user may reorder it to optimize eq handling. +func (b *scheduleBuilder) buildClaimGroups(batches [][]int) ([]ClaimGroup, error) { + levelIdx := len(b.levels) + claimGroups := make([]ClaimGroup, len(batches)) + for i, wireIndices := range batches { + var claimSources []int + for j, wI := range wireIndices { + wireClaims, ok := b.claimSources(wI) + if !ok { + return nil, fmt.Errorf("wire %d is not ready", wI) + } + if j == 0 { + claimSources = wireClaims + } else if !slices.Equal(claimSources, wireClaims) { + return nil, fmt.Errorf("wires %d and %d in the same batch have different claim sources", wireIndices[0], wI) + } + b.wireLevels[wI] = levelIdx + b.wireProcessed[wI] = true + if wI == b.firstUnprocessedWire { + for b.firstUnprocessedWire--; b.firstUnprocessedWire >= 0 && b.wireProcessed[b.firstUnprocessedWire]; b.firstUnprocessedWire-- { + } + } + } + claimGroups[i] = ClaimGroup{Wires: slices.Clone(wireIndices), ClaimSources: claimSources} + } + return claimGroups, nil +} + +// nextReady returns the largest i such that wires [i, len(circuit)) are all ready, +// along with their claim sources. Wires are considered in reverse index order (high-to-low). +// Returns len(circuit) if no wires are ready. +// nextReady returns the highest wire index in the contiguous ready suffix starting at +// firstUnprocessedWire, along with each wire's claim sources in wire-index order. +// Returns firstUnprocessedWire, nil if no wires are ready. +func (b *scheduleBuilder) nextReady() (highestWireI int, sources [][]int) { + for lowestWireI := b.firstUnprocessedWire; lowestWireI >= 0; lowestWireI-- { + if b.wireProcessed[lowestWireI] { + break + } + src, ok := b.claimSources(lowestWireI) + if !ok { + break + } + sources = append(sources, src) + } + slices.Reverse(sources) + return b.firstUnprocessedWire, sources +} + +// claimSourcesCache checks whether all consumers of wire wI have already been processed. +// If so, it returns the deduplicated sorted claim sources for wI and true. +// If not, it returns nil and false. Results are cached. +func (b *scheduleBuilder) claimSources(wI int) ([]int, bool) { + if b.claimSourcesCache[wI] != nil { + return b.claimSourcesCache[wI], true + } + var wireClaims []int + if b.circuit[wI].Exported || len(b.wireOutputs[wI]) == 0 { + wireClaims = append(wireClaims, -1) + } + for _, consumerWI := range b.wireOutputs[wI] { + if !b.wireProcessed[consumerWI] { + return nil, false + } + consumerLevel := b.wireLevels[consumerWI] + if skip, ok := b.levels[consumerLevel].(SkipLevel); ok { + wireClaims = append(wireClaims, skip.ClaimSources...) + } else { + wireClaims = append(wireClaims, consumerLevel) + } + } + slices.Sort(wireClaims) + wireClaims = slices.Compact(wireClaims) + b.claimSourcesCache[wI] = wireClaims + return wireClaims, true +} + +// finalize reverses the schedule into in-to-out order and returns the completed schedule. +// It errors if any wire has not been processed, meaning the caller did not schedule a layer for every wire. +func (b *scheduleBuilder) finalize() (ProvingSchedule, error) { + for i, processed := range b.wireProcessed { + if !processed { + return nil, fmt.Errorf("wire %d has not been processed", i) + } + } + + n := len(b.levels) + slices.Reverse(b.levels) + // Fix up ClaimSources in every ClaimGroup: pre-reversal index src maps to n-1-src, + // and the initial-challenge sentinel -1 maps to n. + for _, layer := range b.levels { + switch l := layer.(type) { + case SkipLevel: + mirror(l.ClaimSources, n) + case SumcheckLevel: + for _, cg := range l { + mirror(cg.ClaimSources, n) + } + } + } + return b.levels, nil +} + +// mirror maps each pre-reversal index src in-place to its post-reversal absolute index n-1-src. +// The initial-challenge sentinel -1 maps to n. +func mirror(s []int, n int) { + n-- + for j := range s { + s[j] = n - s[j] + } +} + +// DefaultProvingSchedule generates a schedule that greedily batches input wires with the same +// single claim source into the same SkipLevel. Non-input wires, and input wires with multiple +// claim sources, each get their own SumcheckLevel. +func DefaultProvingSchedule(c SerializableCircuit) (ProvingSchedule, error) { + b := newScheduleBuilder(c) + + for b.firstUnprocessedWire >= 0 { + highWI, claimSources := b.nextReady() + // try and make a homogenous (same degree, same claims) batch + w := c[highWI] + batchClaimSources := claimSources[0] + if w.IsInput() && len(batchClaimSources) == 1 { + if err := b.addSkipLevel([]int{highWI}); err != nil { + return nil, err + } + continue + } + + // there is an actual "gate" in question + batch := []int{highWI} + for len(batch) < len(claimSources) { + if w.Gate.Degree != c[highWI-len(batch)].Gate.Degree || !slices.Equal(claimSources[0], claimSources[len(batch)]) { + break + } + batch = append(batch, highWI-len(batch)) + } + if w.Gate.Degree == 1 && len(batchClaimSources) == 1 { // certain that skipping won't cause a claim blowup + if err := b.addSkipLevel(batch); err != nil { + return nil, err + } + } else { + if err := b.addSumcheckLevel(batch); err != nil { + return nil, err + } + } + } + return b.finalize() +} + +// BasicProvingSchedule generates a schedule for a circuit where every wire gets its own level: +// Non-input wires get a SumcheckLevel. Input wires with multiple consumers get a SumcheckLevel +// to consolidate claims; input wires with a single consumer get a SkipLevel. +func BasicProvingSchedule(c SerializableCircuit) (ProvingSchedule, error) { + b := newScheduleBuilder(c) + for wI := len(c) - 1; wI >= 0; wI-- { + src, ready := b.claimSources(wI) + if !ready { + return nil, fmt.Errorf("circuit is not topologically sorted: wire %d not ready", wI) + } + var err error + if !c[wI].IsInput() || len(src) > 1 { + err = b.addSumcheckLevel([]int{wI}) + } else { + err = b.addSkipLevel([]int{wI}) + } + if err != nil { + return nil, err + } + } + return b.finalize() +} diff --git a/internal/gkr/gkrcore/schedule_test.go b/internal/gkr/gkrcore/schedule_test.go new file mode 100644 index 0000000000..877551ba9e --- /dev/null +++ b/internal/gkr/gkrcore/schedule_test.go @@ -0,0 +1,98 @@ +package gkrcore_test + +import ( + "testing" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/internal/gkr/gkrcore" + "github.com/consensys/gnark/internal/gkr/gkrtesting" + "github.com/stretchr/testify/require" +) + +var scheduleTestCache = gkrtesting.NewCache(ecc.BN254.ScalarField()) + +func TestDefaultProvingSchedulePoseidon2(t *testing.T) { + _, c := scheduleTestCache.Compile(t, gkrtesting.Poseidon2Circuit(4, 2)) + schedule, err := gkrcore.DefaultProvingSchedule(c) + require.NoError(t, err) + + // Wire layout for Poseidon2Circuit(4, 2) — 25 wires total: + // 0, 1 inputs + // 2–5 full-round 0 (lin0, lin1, sBox0, sBox1) + // 6–9 full-round 1 (lin0, lin1, sBox0, sBox1) + // 10–12 partial-round 0 (lin0, lin1, sBox0) + // 13–15 partial-round 1 (lin0, lin1, sBox0) + // 16–19 full-round 2 (lin0, lin1, sBox0, sBox1) + // 20–23 full-round 3 (lin0, lin1, sBox0, sBox1) + // 24 feed-forward output + // + // Claim source indices refer to levels (post-reversal); 17 = len(schedule) = initial challenge. + // + // Linear wires (degree 1, single claim source) become SkipLevels. + // S-box wires (degree 2) become SumcheckLevels. + // The two s-boxes of a full round share identical claim sources and are batched together. + // Full-round 1 s-boxes (wires 8, 9) feed three downstream sumchecks: partial-round 0 s-box + // (level 8), partial-round 1 s-box (level 11), and full-round 2 s-boxes (level 13). + expected := gkrcore.ProvingSchedule{ + + // Level 0: input[0] — single claim source: full-round 0 s-boxes at level 3. + gkrcore.SkipLevel{Wires: []int{0}, ClaimSources: []int{3}}, + // Level 1: input[1] — feeds full-round 0 s-boxes (level 3) and the feed-forward (level 17 = initial challenge). + gkrcore.SkipLevel{Wires: []int{1}, ClaimSources: []int{17, 3}}, + + // Level 2: full-round 0 lin1+lin0 (degree 1, batched) — claim source: full-round 0 s-boxes at level 3. + gkrcore.SkipLevel{Wires: []int{3, 2}, ClaimSources: []int{3}}, + // Level 3: full-round 0 sBox1+sBox0 (degree 2, batched) — claim source: full-round 1 s-boxes at level 5. + gkrcore.SumcheckLevel{{Wires: []int{5, 4}, ClaimSources: []int{5}}}, + + // Level 4: full-round 1 lin1+lin0 (degree 1, batched) — claim source: full-round 1 s-boxes at level 5. + gkrcore.SkipLevel{Wires: []int{7, 6}, ClaimSources: []int{5}}, + // Level 5: full-round 1 sBox1+sBox0 (degree 2, batched) — three downstream sumchecks. + gkrcore.SumcheckLevel{{Wires: []int{9, 8}, ClaimSources: []int{13, 11, 8}}}, + + // Level 6: partial-round 0 lin0 (degree 1) — claim source: partial-round 0 s-box at level 8. + gkrcore.SkipLevel{Wires: []int{10}, ClaimSources: []int{8}}, + // Level 7: partial-round 0 lin1 (degree 1) — feeds partial-round 1 s-box (level 11) and full-round 2 s-boxes (level 13). + gkrcore.SkipLevel{Wires: []int{11}, ClaimSources: []int{13, 11}}, + // Level 8: partial-round 0 sBox0 (degree 2) — feeds partial-round 1 lin0+lin1. + gkrcore.SumcheckLevel{{Wires: []int{12}, ClaimSources: []int{13, 11}}}, + + // Level 9: partial-round 1 lin0 (degree 1) — claim source: partial-round 1 s-box at level 11. + gkrcore.SkipLevel{Wires: []int{13}, ClaimSources: []int{11}}, + // Level 10: partial-round 1 lin1 (degree 1) — claim source: full-round 2 s-boxes at level 13. + gkrcore.SkipLevel{Wires: []int{14}, ClaimSources: []int{13}}, + // Level 11: partial-round 1 sBox0 (degree 2) — claim source: full-round 2 s-boxes at level 13. + gkrcore.SumcheckLevel{{Wires: []int{15}, ClaimSources: []int{13}}}, + + // Level 12: full-round 2 lin1+lin0 (degree 1, batched) — claim source: full-round 2 s-boxes at level 13. + gkrcore.SkipLevel{Wires: []int{17, 16}, ClaimSources: []int{13}}, + // Level 13: full-round 2 sBox1+sBox0 (degree 2, batched) — claim source: full-round 3 s-boxes at level 15. + gkrcore.SumcheckLevel{{Wires: []int{19, 18}, ClaimSources: []int{15}}}, + + // Level 14: full-round 3 lin1+lin0 (degree 1, batched) — claim source: full-round 3 s-boxes at level 15. + gkrcore.SkipLevel{Wires: []int{21, 20}, ClaimSources: []int{15}}, + // Level 15: full-round 3 sBox1+sBox0 (degree 2, batched) — claim source: initial challenge (17). + gkrcore.SumcheckLevel{{Wires: []int{23, 22}, ClaimSources: []int{17}}}, + + // Level 16: feed-forward output (degree 1) — claim source: initial challenge (17). + gkrcore.SkipLevel{Wires: []int{24}, ClaimSources: []int{17}}, + } + require.Equal(t, expected, schedule) +} + +func TestBasicProvingSchedule(t *testing.T) { + _, c := scheduleTestCache.Compile(t, gkrtesting.SingleMulGateCircuit()) + schedule, err := gkrcore.BasicProvingSchedule(c) + require.NoError(t, err) + + // SingleMulGateCircuit has wires 0, 1 (inputs) and 2 (mul gate). + // Claim source indices refer to levels; 3 = len(schedule) = initial challenge. + require.Equal(t, gkrcore.ProvingSchedule{ + // Level 0: input[0] — claim source: mul gate at level 2. + gkrcore.SkipLevel{Wires: []int{0}, ClaimSources: []int{2}}, + // Level 1: input[1] — claim source: mul gate at level 2. + gkrcore.SkipLevel{Wires: []int{1}, ClaimSources: []int{2}}, + // Level 2: mul gate output (degree 2) — claim source: initial challenge (3). + gkrcore.SumcheckLevel{{Wires: []int{2}, ClaimSources: []int{3}}}, + }, schedule) +} diff --git a/internal/gkr/gkrcore/types.go b/internal/gkr/gkrcore/types.go index ef4c139b02..ac0584f4e7 100644 --- a/internal/gkr/gkrcore/types.go +++ b/internal/gkr/gkrcore/types.go @@ -116,7 +116,7 @@ func (w Wire[GateExecutable]) ZeroCheckDegree() int { // ClaimPropagationInfo returns sets of indices describing the pruning of claim propagation. // At the end of sumcheck for wire #wireIndex, we end up with sequences "uniqueEvaluations" and "evaluations", // the former a subsequence of the latter. -// injection are the indices of the unique evaluations in the original evaluation list. +// injection consists of the indices of the unique evaluations in the original evaluation list. // injectionRightInverse are the indices of the original evaluations in the unique evaluations list. // There are no guarantees on the non-unique choice of the semi-inverse map. func (c Circuit[GateExecutable]) ClaimPropagationInfo(wireIndex int) (injection, injectionLeftInverse []int) { @@ -277,82 +277,6 @@ type Blueprints struct { GetAssignmentID constraint.BlueprintID } -// ProvingSchedule types for GKR proving -// These define how claims are grouped and processed during the proving protocol. - -type ( - // ClaimGroup represents a set of wires with their claim sources. - // It is agnostic of the protocol - it only describes which wires have claims - // from which sources, not what to do with them. - ClaimGroup struct { - Wires []int `json:"wires"` - ClaimSources []int `json:"claimSources"` // indices of steps that produced these claims - } - - // ProvingStep is the interface for a single step in the proving schedule. - // A step is either a SkipStep or a SumcheckStep. - ProvingStep interface { - - } - - // SkipStep represents a step where zerocheck is skipped. - // Claims propagate through at their existing evaluation points. - SkipStep ClaimGroup - - // SumcheckStep represents a step where one or more zerochecks are batched - // together in a single sumcheck. Each ClaimGroup within may have different - // claim sources (sumcheck-level batching), or the same source (enabling - // zerocheck-level batching with shared eq tables). - SumcheckStep []ClaimGroup - - // ProvingSchedule is a sequence of steps defining how to prove a GKR circuit. - ProvingSchedule []ProvingStep -) - -// DefaultProvingSchedule generates a simple schedule for a circuit where each non-input -// wire gets its own sumcheck step, processed in reverse topological order. -// This matches the original GKR prover behavior before schedule support was added. -func DefaultProvingSchedule[T any](c Circuit[T]) ProvingSchedule { - var steps ProvingSchedule - wireToStep := make(map[int]int) // wire -> step index that processes it - - // Process wires in reverse order (outputs first) - for i := len(c) - 1; i >= 0; i-- { - if c[i].IsInput() { - continue - } - - stepI := len(steps) - - // Collect claim sources from input wires - var claimSources []int - seen := make(map[int]bool) - for _, inputWire := range c[i].Inputs { - if srcStep, ok := wireToStep[inputWire]; ok && !seen[srcStep] { - claimSources = append(claimSources, srcStep) - seen[srcStep] = true - } - } - - // For output wires, the initial claim comes from step -1 (the verifier's initial challenge) - // We represent this as having no claim sources for output wires - if c[i].IsOutput() && len(claimSources) == 0 { - claimSources = nil // initial challenge, no prior step - } - - steps = append(steps, SumcheckStep{ - ClaimGroup{ - Wires: []int{i}, - ClaimSources: claimSources, - }, - }) - - wireToStep[i] = stepI - } - - return steps -} - // Compile compiles a raw circuit into both a gadget circuit and a serializable circuit. // It computes all wire and gate metadata (Degree, SolvableVar, NbUniqueOutputs). func (c RawCircuit) Compile(mod *big.Int) (GadgetCircuit, SerializableCircuit, error) { @@ -381,12 +305,21 @@ func (c RawCircuit) Compile(mod *big.Int) (GadgetCircuit, SerializableCircuit, e } } + // Compile identity gate for input wires. + compiledIdentity, err := CompileGateFunction(Identity, 1, mod) + if err != nil { + return nil, nil, err + } + identityGadgetGate := GadgetGate{Evaluate: Identity, NbIn: 1, Degree: compiledIdentity.Degree, SolvableVar: compiledIdentity.SolvableVar} + // Second pass: compile gates and set metadata for i := range c { if len(c[i].Inputs) == 0 { // input wire if c[i].Gate != nil { return nil, nil, errors.New("nil gate expected for input wire") } + gadget[i].Gate = identityGadgetGate + serializable[i].Gate = compiledIdentity continue } diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index f2c04513f9..e66a6f2e7a 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -151,117 +151,53 @@ type PrintableSumcheckProof struct { } type HashDescription map[string]interface{} -type testCaseInfoJSON struct { + +// TestCaseInfo is the serializable form of a GKR test case, matching the JSON file format. +// Schedule is nil when absent from JSON, which means BasicProvingSchedule. +type TestCaseInfo struct { Hash HashDescription `json:"hash"` Circuit string `json:"circuit"` Input [][]interface{} `json:"input"` Output [][]interface{} `json:"output"` Proof PrintableProof `json:"proof"` - Schedule *jsonSchedule `json:"schedule,omitempty"` // nil means DefaultProvingSchedule + Schedule ScheduleInfo `json:"schedule,omitempty"` } -type TestCaseInfo struct { - Hash HashDescription - Circuit string - Input [][]interface{} - Output [][]interface{} - Proof PrintableProof - Schedule gkrcore.ProvingSchedule // nil means DefaultProvingSchedule +// ScheduleStepInfo is the JSON representation of a ProvingLevel with a type discriminator. +type ScheduleStepInfo struct { + Type string `json:"type"` + Groups []gkrcore.ClaimGroup `json:"groups,omitempty"` // for SumcheckLevel + Wires []int `json:"wires,omitempty"` // for SkipLevel + Claims []int `json:"claimSources,omitempty"` // for SkipLevel } -func (t *TestCaseInfo) UnmarshalJSON(data []byte) error { - var raw testCaseInfoJSON - if err := json.Unmarshal(data, &raw); err != nil { - return err - } - t.Hash = raw.Hash - t.Circuit = raw.Circuit - t.Input = raw.Input - t.Output = raw.Output - t.Proof = raw.Proof - if raw.Schedule != nil { - t.Schedule = raw.Schedule.ProvingSchedule - } - return nil -} +// ScheduleInfo is the JSON-serializable form of a ProvingSchedule. +type ScheduleInfo []ScheduleStepInfo -func (t TestCaseInfo) MarshalJSON() ([]byte, error) { - raw := testCaseInfoJSON{ - Hash: t.Hash, - Circuit: t.Circuit, - Input: t.Input, - Output: t.Output, - Proof: t.Proof, +// ToProvingSchedule converts a ScheduleInfo to a gkrcore.ProvingSchedule. +// A nil ScheduleInfo returns nil, which callers should interpret as BasicProvingSchedule. +func (p ScheduleInfo) ToProvingSchedule() (gkrcore.ProvingSchedule, error) { + if p == nil { + return nil, nil } - if t.Schedule != nil { - raw.Schedule = &jsonSchedule{t.Schedule} - } - return json.Marshal(raw) -} - -// provingStepJSON is the JSON representation of a ProvingStep with a type discriminator. -type provingStepJSON struct { - Type string `json:"type"` - Groups []gkrcore.ClaimGroup `json:"groups,omitempty"` // for SumcheckStep - Wires []int `json:"wires,omitempty"` // for SkipStep - Claims []int `json:"claimSources,omitempty"` // for SkipStep -} - -// marshalSchedule marshals a ProvingSchedule to JSON. -func marshalSchedule(s gkrcore.ProvingSchedule) ([]byte, error) { - steps := make([]provingStepJSON, len(s)) - for i, step := range s { - switch v := step.(type) { - case gkrcore.SumcheckStep: - steps[i] = provingStepJSON{Type: "sumcheck", Groups: []gkrcore.ClaimGroup(v)} - case gkrcore.SkipStep: - steps[i] = provingStepJSON{Type: "skip", Wires: v.Wires, Claims: v.ClaimSources} - default: - return nil, errors.New("unknown ProvingStep type") - } - } - return json.Marshal(steps) -} - -// unmarshalSchedule unmarshals a ProvingSchedule from JSON. -func unmarshalSchedule(data []byte) (gkrcore.ProvingSchedule, error) { - var steps []provingStepJSON - if err := json.Unmarshal(data, &steps); err != nil { - return nil, err - } - s := make(gkrcore.ProvingSchedule, len(steps)) - for i, step := range steps { + s := make(gkrcore.ProvingSchedule, len(p)) + for i, step := range p { switch step.Type { case "sumcheck": groups := step.Groups if groups == nil { groups = []gkrcore.ClaimGroup{} } - s[i] = gkrcore.SumcheckStep(groups) + s[i] = gkrcore.SumcheckLevel(groups) case "skip": - s[i] = gkrcore.SkipStep{Wires: step.Wires, ClaimSources: step.Claims} + s[i] = gkrcore.SkipLevel{Wires: step.Wires, ClaimSources: step.Claims} default: - return nil, errors.New("unknown ProvingStep type: " + step.Type) + return nil, errors.New("unknown ProvingLevel type: " + step.Type) } } return s, nil } -// jsonSchedule is a local wrapper enabling custom JSON marshaling for ProvingSchedule -// within TestCaseInfo, since methods cannot be defined on non-local types. -type jsonSchedule struct { - gkrcore.ProvingSchedule -} - -func (j jsonSchedule) MarshalJSON() ([]byte, error) { - return marshalSchedule(j.ProvingSchedule) -} - -func (j *jsonSchedule) UnmarshalJSON(data []byte) (err error) { - j.ProvingSchedule, err = unmarshalSchedule(data) - return -} - func (c *Cache) ReadTestCaseInfo(filePath string) (info TestCaseInfo, err error) { f, err := os.Open(filePath) if err != nil { @@ -327,3 +263,90 @@ func SingleMimcCipherGateCircuit() gkrcore.RawCircuit { {Gate: mimcGate, Inputs: []int{0, 1}}, } } + +// poseidon2ExtLinear0 computes 2*x[0] + x[1] (external matrix, state[0] row). +func poseidon2ExtLinear0(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { + return api.Add(x[0], x[0], x[1]) +} + +// poseidon2ExtLinear1 computes x[0] + 2*x[1] (external matrix, state[1] row). +func poseidon2ExtLinear1(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { + return api.Add(x[0], x[1], x[1]) +} + +// poseidon2IntLinear1 computes x[0] + 3*x[1] (internal matrix, state[1] row; state[0] row = external). +func poseidon2IntLinear1(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { + return api.Add(x[0], x[1], x[1], x[1]) +} + +// poseidon2SBox computes x[0]^2 (simplified s-box). +func poseidon2SBox(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { + return api.Mul(x[0], x[0]) +} + +// poseidon2FeedForward computes 2*x[0] + x[1] + x[2] (external matrix row with feed-forward). +func poseidon2FeedForward(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { + return api.Add(x[0], x[0], x[1], x[2]) +} + +// Poseidon2Circuit returns a 2-state Poseidon2-like GKR circuit with the given number of +// full and partial rounds, followed by a feed-forward output wire. +// Each full round applies the external linear layer (degree 1, skip) to both state elements +// followed by the s-box x^2 (degree 2, sumcheck) to both. +// Each partial round applies the external linear layer to state[0] and the internal linear +// layer to state[1] (both skip), then the s-box to state[0] only (sumcheck). +// The final output wire is 2*s0 + s1 + in1 (external matrix row with the second input fed forward). +// +// Wire layout per full round (wires s0, s1 are the current state): +// +// +0 = 2*s0 + s1 external linear, state[0] (skip) +// +1 = s0 + 2*s1 external linear, state[1] (skip) +// +2 = lin0^2 s-box, state[0] (sumcheck) +// +3 = lin1^2 s-box, state[1] (sumcheck) +// +// Wire layout per partial round: +// +// +0 = 2*s0 + s1 external linear, state[0] (skip) +// +1 = s0 + 3*s1 internal linear, state[1] (skip) +// +2 = lin0^2 s-box, state[0] only (sumcheck) +// +// Final wire: 2*s0 + s1 + in1 (feed-forward, sumcheck output) +func Poseidon2Circuit(nbFullRounds, nbPartialRounds int) gkrcore.RawCircuit { + // 2 inputs + 4 wires per full round + 3 wires per partial round + 1 feed-forward output + nbWires := 2 + 4*nbFullRounds + 3*nbPartialRounds + 1 + c := make(gkrcore.RawCircuit, nbWires) + // wires 0, 1 are inputs + s0, s1 := 0, 1 + + w := 2 + appendFullRound := func() { + c[w] = gkrcore.RawWire{Gate: poseidon2ExtLinear0, Inputs: []int{s0, s1}} + c[w+1] = gkrcore.RawWire{Gate: poseidon2ExtLinear1, Inputs: []int{s0, s1}} + c[w+2] = gkrcore.RawWire{Gate: poseidon2SBox, Inputs: []int{w}} + c[w+3] = gkrcore.RawWire{Gate: poseidon2SBox, Inputs: []int{w + 1}} + s0, s1 = w+2, w+3 + w += 4 + } + appendPartialRound := func() { + c[w] = gkrcore.RawWire{Gate: poseidon2ExtLinear0, Inputs: []int{s0, s1}} + c[w+1] = gkrcore.RawWire{Gate: poseidon2IntLinear1, Inputs: []int{s0, s1}} + c[w+2] = gkrcore.RawWire{Gate: poseidon2SBox, Inputs: []int{w}} + s0, s1 = w+2, w+1 + w += 3 + } + + for range nbFullRounds / 2 { + appendFullRound() + } + for range nbPartialRounds { + appendPartialRound() + } + for range nbFullRounds - nbFullRounds/2 { + appendFullRound() + } + + // feed-forward: 2*s0 + s1 + in1 + c[w] = gkrcore.RawWire{Gate: poseidon2FeedForward, Inputs: []int{s0, s1, 1}} + + return c +} From c1e3be4ea61739e16a2aba3e6a2fdf3fba2ce8cc Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 9 Mar 2026 21:11:52 -0500 Subject: [PATCH 221/251] feat: functions for level based scheduling --- constraint/gkr.go | 37 +++++++ constraint/marshal.go | 3 + internal/gkr/bn254/blueprint.go | 8 +- internal/gkr/gkrcore/gate.go | 6 ++ internal/gkr/gkrcore/schedule.go | 165 +++++++++++++++++++++---------- internal/gkr/gkrcore/types.go | 134 +++---------------------- 6 files changed, 178 insertions(+), 175 deletions(-) create mode 100644 constraint/gkr.go diff --git a/constraint/gkr.go b/constraint/gkr.go new file mode 100644 index 0000000000..650d0efe04 --- /dev/null +++ b/constraint/gkr.go @@ -0,0 +1,37 @@ +package constraint + +type ( + // GkrClaimGroup represents a set of wires with their claim sources. + // It is agnostic of the protocol - it only describes which wires have claims + // from which sources, not what to do with them. + // + // ClaimSources contains step indices that produced evaluation claims for these wires. + // The special value len(schedule) is a virtual step index representing the verifier's + // initial challenge (rho). It is never an actual index into the schedule slice. + GkrClaimGroup struct { + Wires []int `json:"wires"` + ClaimSources []int `json:"claimSourcesCache"` // step indices; len(schedule) = initial challenge + } + + // GkrProvingLevel is a sealed interface for a single level in the proving schedule. + // A level is either a GkrSkipLevel or a GkrSumcheckLevel. + GkrProvingLevel interface { + gkrProvingLevel() // marker method restricting implementations to GkrSkipLevel and GkrSumcheckLevel + } + + // GkrSkipLevel represents a level where zerocheck is skipped. + // Claims propagate through at their existing evaluation points. + GkrSkipLevel GkrClaimGroup + + // GkrSumcheckLevel represents a level where one or more zerochecks are batched + // together in a single sumcheck. Each GkrClaimGroup within may have different + // claim sources (sumcheck-level batching), or the same source (enabling + // zerocheck-level batching with shared eq tables). + GkrSumcheckLevel []GkrClaimGroup + + // GkrProvingSchedule is a sequence of levels defining how to prove a GKR circuit. + GkrProvingSchedule []GkrProvingLevel +) + +func (GkrSumcheckLevel) gkrProvingLevel() {} +func (GkrSkipLevel) gkrProvingLevel() {} diff --git a/constraint/marshal.go b/constraint/marshal.go index 92c4b0b593..4c03ff54dc 100644 --- a/constraint/marshal.go +++ b/constraint/marshal.go @@ -395,6 +395,9 @@ func getTagSet() cbor.TagSet { addType(reflect.TypeOf(BlueprintSparseR1CBool[U64]{})) addType(reflect.TypeOf(BlueprintLookupHint[U64]{})) + addType(reflect.TypeOf(GkrSkipLevel{})) + addType(reflect.TypeOf(GkrSumcheckLevel{})) + // Add types registered by external packages (e.g., GKR blueprints) // These use explicit tag numbers to ensure stability regardless of init() order for _, rt := range registeredBlueprintTypes { diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index bbe45ecdac..7332eb77fc 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -204,6 +204,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + Schedule constraint.GkrProvingSchedule HashName string lock sync.Mutex @@ -275,7 +276,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra fsSettings := fiatshamir.WithHash(hsh, insBytes...) // Call the BN254-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.Circuit, nil, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, b.Schedule, assignments, fsSettings) if err != nil { return fmt.Errorf("bn254 prove failed: %w", err) } @@ -305,7 +306,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) + return gkrcore.ProofSize(b.Schedule, b.SolveBlueprint.Circuit, logNbInstances) } // NbOutputs implements Blueprint @@ -434,7 +435,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BN254 -func NewBlueprints(circuit gkrcore.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrcore.Blueprints { +func NewBlueprints(circuit gkrcore.SerializableCircuit, schedule constraint.GkrProvingSchedule, hashName string, compiler constraint.CustomizableSystem) gkrcore.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) @@ -443,6 +444,7 @@ func NewBlueprints(circuit gkrcore.SerializableCircuit, hashName string, compile prove := &BlueprintProve{ SolveBlueprintID: solveID, SolveBlueprint: solve, + Schedule: schedule, HashName: hashName, } proveID := compiler.AddBlueprint(prove) diff --git a/internal/gkr/gkrcore/gate.go b/internal/gkr/gkrcore/gate.go index 193653daee..8e3be2a773 100644 --- a/internal/gkr/gkrcore/gate.go +++ b/internal/gkr/gkrcore/gate.go @@ -43,6 +43,12 @@ type GateBytecode struct { Constants []*big.Int // constant values at indices [0, nbConsts) } +// IdentityBytecode returns the compiled form of the identity gate (x → x). +// A GateBytecode with no instructions returns its sole input directly. +func IdentityBytecode() GateBytecode { + return GateBytecode{} +} + // NbConstants returns the number of constants in the gate func (g *GateBytecode) NbConstants() int { return len(g.Constants) diff --git a/internal/gkr/gkrcore/schedule.go b/internal/gkr/gkrcore/schedule.go index d67d5ad0b2..bf4084aa7a 100644 --- a/internal/gkr/gkrcore/schedule.go +++ b/internal/gkr/gkrcore/schedule.go @@ -3,39 +3,107 @@ package gkrcore import ( "fmt" "slices" + + "github.com/consensys/gnark/constraint" ) -type ( - // ClaimGroup represents a set of wires with their claim sources. - // It is agnostic of the protocol - it only describes which wires have claims - // from which sources, not what to do with them. - // - // ClaimSources contains step indices that produced evaluation claims for these wires. - // The special value len(schedule) is a virtual step index representing the verifier's - // initial challenge (rho). It is never an actual index into the schedule slice. - ClaimGroup struct { - Wires []int `json:"wires"` - ClaimSources []int `json:"claimSourcesCache"` // step indices; len(schedule) = initial challenge +// UniqueGateInputs returns the unique gate input wire indices for all wires in the level, +// deduplicated in batch-then-wire-then-input order (first occurrence wins). +// For input wires (no gate inputs), the wire itself is returned. +func UniqueGateInputs[G any](level constraint.GkrProvingLevel, c Circuit[G]) []int { + uniqueInputs, _ := InputMapping(level, c) + return uniqueInputs +} + +// InputMapping computes the unique gate inputs and the per-wire index mapping in a single pass. +// inputIndices[wireInLevel][gateInputJ] → position in uniqueInputs. +// For input wires: inputIndices[i] = []int{position of wI in uniqueInputs}. +func InputMapping[G any](level constraint.GkrProvingLevel, c Circuit[G]) (uniqueInputs []int, inputIndices [][]int) { + var groups []constraint.GkrClaimGroup + switch l := level.(type) { + case constraint.GkrSumcheckLevel: + groups = l + case constraint.GkrSkipLevel: + groups = []constraint.GkrClaimGroup{constraint.GkrClaimGroup(l)} } - // ProvingLevel is the interface for a single level in the proving schedule. - // A level is either a SkipLevel or a SumcheckLevel. - ProvingLevel interface { + seen := make(map[int]int) // wire index → position in uniqueInputs + for _, group := range groups { + for _, wI := range group.Wires { + wire := c[wI] + inputs := wire.Inputs + if wire.IsInput() { + inputs = []int{wI} + } + + indices := make([]int, len(inputs)) + for inWI, inW := range inputs { + pos, ok := seen[inW] + if !ok { + pos = len(uniqueInputs) + seen[inW] = pos + uniqueInputs = append(uniqueInputs, inW) + } + indices[inWI] = pos + } + inputIndices = append(inputIndices, indices) + } } + return +} - // SkipLevel represents a level where zerocheck is skipped. - // Claims propagate through at their existing evaluation points. - SkipLevel ClaimGroup +// ReduplicateInputs expands unique evaluations to per-wire gate input evaluation lists. +func ReduplicateInputs[G, F any](level constraint.GkrProvingLevel, c Circuit[G], uniqueEvals []F) [][]F { + _, inputIndices := InputMapping(level, c) + result := make([][]F, len(inputIndices)) + for wireInLevel := range inputIndices { + wireInputs := make([]F, len(inputIndices[wireInLevel])) + for gateInputJ, uniqueI := range inputIndices[wireInLevel] { + wireInputs[gateInputJ] = uniqueEvals[uniqueI] + } + result[wireInLevel] = wireInputs + } + return result +} - // SumcheckLevel represents a level where one or more zerochecks are batched - // together in a single sumcheck. Each ClaimGroup within may have different - // claim sources (sumcheck-level batching), or the same source (enabling - // zerocheck-level batching with shared eq tables). - SumcheckLevel []ClaimGroup +// Degree returns max(Gate.Degree across all wires in all groups) + 1. +func Degree[G any](level constraint.GkrSumcheckLevel, c Circuit[G]) int { + maxDeg := 0 + for _, group := range level { + for _, wI := range group.Wires { + w := &c[wI] + curr := 1 + if !w.IsInput() { + curr = w.Gate.Degree + } + maxDeg = max(maxDeg, curr) + } + } + return maxDeg + 1 +} - // ProvingSchedule is a sequence of levels defining how to prove a GKR circuit. - ProvingSchedule []ProvingLevel -) +// NbClaims returns the total number of claims in a sumcheck level. +func NbClaims(level constraint.GkrSumcheckLevel) int { + n := 0 + for _, g := range level { + n += len(g.Wires) * len(g.ClaimSources) + } + return n +} + +// ProofSize returns the total number of field elements in a GKR proof. +func ProofSize[G any](schedule constraint.GkrProvingSchedule, c Circuit[G], logNbInstances int) int { + size := 0 + for _, step := range schedule { + s, ok := step.(constraint.GkrSumcheckLevel) + if !ok { + continue + } + size += logNbInstances * Degree(s, c) // partialSumPolys + size += len(UniqueGateInputs(s, c)) // finalEvalProof + } + return size +} // scheduleBuilder accumulates topology and per-wire claim sources while a schedule is being built. // Steps are appended in out-to-in (topological) order and reversed by finalize. @@ -49,7 +117,7 @@ type scheduleBuilder struct { wireProcessed []bool claimSourcesCache [][]int // claimSourcesCache[i] is the result of claimSourcesCache(i), or nil if not yet computed. firstUnprocessedWire int - levels ProvingSchedule + levels constraint.GkrProvingSchedule } // newScheduleBuilder initialises a builder for the given circuit. @@ -76,7 +144,7 @@ func newScheduleBuilder(c SerializableCircuit) scheduleBuilder { return b } -// addSumcheckLevel appends a SumcheckLevel to the schedule. Each batch is a set of wire indices +// addSumcheckLevel appends a GkrSumcheckLevel to the schedule. Each batch is a set of wire indices // to be proven together in a single zerocheck; all wires in a batch must share the same claim sources. // All wires across all batches must be ready. func (b *scheduleBuilder) addSumcheckLevel(batches ...[]int) error { @@ -84,27 +152,27 @@ func (b *scheduleBuilder) addSumcheckLevel(batches ...[]int) error { if err != nil { return err } - b.levels = append(b.levels, SumcheckLevel(claimGroups)) + b.levels = append(b.levels, constraint.GkrSumcheckLevel(claimGroups)) return nil } -// addSkipLevel appends a SkipLevel to the schedule for a single set of wire indices. +// addSkipLevel appends a GkrSkipLevel to the schedule for a single set of wire indices. // All wires in the batch must share the same claim sources and must be ready. func (b *scheduleBuilder) addSkipLevel(wireIndices []int) error { claimGroups, err := b.buildClaimGroups([][]int{wireIndices}) if err != nil { return err } - b.levels = append(b.levels, SkipLevel(claimGroups[0])) + b.levels = append(b.levels, constraint.GkrSkipLevel(claimGroups[0])) return nil } // buildClaimGroups processes a set of batches, validates claim source consistency within each -// batch, updates wireLevels and wireProcessed, and returns the resulting ClaimGroups. +// batch, updates wireLevels and wireProcessed, and returns the resulting GkrClaimGroups. // Every ClaimSources slice is sorted. The user may reorder it to optimize eq handling. -func (b *scheduleBuilder) buildClaimGroups(batches [][]int) ([]ClaimGroup, error) { +func (b *scheduleBuilder) buildClaimGroups(batches [][]int) ([]constraint.GkrClaimGroup, error) { levelIdx := len(b.levels) - claimGroups := make([]ClaimGroup, len(batches)) + claimGroups := make([]constraint.GkrClaimGroup, len(batches)) for i, wireIndices := range batches { var claimSources []int for j, wI := range wireIndices { @@ -124,14 +192,11 @@ func (b *scheduleBuilder) buildClaimGroups(batches [][]int) ([]ClaimGroup, error } } } - claimGroups[i] = ClaimGroup{Wires: slices.Clone(wireIndices), ClaimSources: claimSources} + claimGroups[i] = constraint.GkrClaimGroup{Wires: slices.Clone(wireIndices), ClaimSources: claimSources} } return claimGroups, nil } -// nextReady returns the largest i such that wires [i, len(circuit)) are all ready, -// along with their claim sources. Wires are considered in reverse index order (high-to-low). -// Returns len(circuit) if no wires are ready. // nextReady returns the highest wire index in the contiguous ready suffix starting at // firstUnprocessedWire, along with each wire's claim sources in wire-index order. // Returns firstUnprocessedWire, nil if no wires are ready. @@ -150,7 +215,7 @@ func (b *scheduleBuilder) nextReady() (highestWireI int, sources [][]int) { return b.firstUnprocessedWire, sources } -// claimSourcesCache checks whether all consumers of wire wI have already been processed. +// claimSources checks whether all consumers of wire wI have already been processed. // If so, it returns the deduplicated sorted claim sources for wI and true. // If not, it returns nil and false. Results are cached. func (b *scheduleBuilder) claimSources(wI int) ([]int, bool) { @@ -166,7 +231,7 @@ func (b *scheduleBuilder) claimSources(wI int) ([]int, bool) { return nil, false } consumerLevel := b.wireLevels[consumerWI] - if skip, ok := b.levels[consumerLevel].(SkipLevel); ok { + if skip, ok := b.levels[consumerLevel].(constraint.GkrSkipLevel); ok { wireClaims = append(wireClaims, skip.ClaimSources...) } else { wireClaims = append(wireClaims, consumerLevel) @@ -180,7 +245,7 @@ func (b *scheduleBuilder) claimSources(wI int) ([]int, bool) { // finalize reverses the schedule into in-to-out order and returns the completed schedule. // It errors if any wire has not been processed, meaning the caller did not schedule a layer for every wire. -func (b *scheduleBuilder) finalize() (ProvingSchedule, error) { +func (b *scheduleBuilder) finalize() (constraint.GkrProvingSchedule, error) { for i, processed := range b.wireProcessed { if !processed { return nil, fmt.Errorf("wire %d has not been processed", i) @@ -189,13 +254,13 @@ func (b *scheduleBuilder) finalize() (ProvingSchedule, error) { n := len(b.levels) slices.Reverse(b.levels) - // Fix up ClaimSources in every ClaimGroup: pre-reversal index src maps to n-1-src, + // Fix up ClaimSources in every GkrClaimGroup: pre-reversal index src maps to n-1-src, // and the initial-challenge sentinel -1 maps to n. for _, layer := range b.levels { switch l := layer.(type) { - case SkipLevel: + case constraint.GkrSkipLevel: mirror(l.ClaimSources, n) - case SumcheckLevel: + case constraint.GkrSumcheckLevel: for _, cg := range l { mirror(cg.ClaimSources, n) } @@ -214,9 +279,9 @@ func mirror(s []int, n int) { } // DefaultProvingSchedule generates a schedule that greedily batches input wires with the same -// single claim source into the same SkipLevel. Non-input wires, and input wires with multiple -// claim sources, each get their own SumcheckLevel. -func DefaultProvingSchedule(c SerializableCircuit) (ProvingSchedule, error) { +// single claim source into the same GkrSkipLevel. Non-input wires, and input wires with multiple +// claim sources, each get their own GkrSumcheckLevel. +func DefaultProvingSchedule(c SerializableCircuit) (constraint.GkrProvingSchedule, error) { b := newScheduleBuilder(c) for b.firstUnprocessedWire >= 0 { @@ -239,7 +304,7 @@ func DefaultProvingSchedule(c SerializableCircuit) (ProvingSchedule, error) { } batch = append(batch, highWI-len(batch)) } - if w.Gate.Degree == 1 && len(batchClaimSources) == 1 { // certain that skipping won't cause a claim blowup + if w.Gate.Degree == 1 && len(batchClaimSources) == 1 { // certain that skipping won't cause a claim blowup if err := b.addSkipLevel(batch); err != nil { return nil, err } @@ -253,9 +318,9 @@ func DefaultProvingSchedule(c SerializableCircuit) (ProvingSchedule, error) { } // BasicProvingSchedule generates a schedule for a circuit where every wire gets its own level: -// Non-input wires get a SumcheckLevel. Input wires with multiple consumers get a SumcheckLevel -// to consolidate claims; input wires with a single consumer get a SkipLevel. -func BasicProvingSchedule(c SerializableCircuit) (ProvingSchedule, error) { +// Non-input wires get a GkrSumcheckLevel. Input wires with multiple consumers get a GkrSumcheckLevel +// to consolidate claims; input wires with a single consumer get a GkrSkipLevel. +func BasicProvingSchedule(c SerializableCircuit) (constraint.GkrProvingSchedule, error) { b := newScheduleBuilder(c) for wI := len(c) - 1; wI >= 0; wI-- { src, ready := b.claimSources(wI) diff --git a/internal/gkr/gkrcore/types.go b/internal/gkr/gkrcore/types.go index ac0584f4e7..c0b62d4637 100644 --- a/internal/gkr/gkrcore/types.go +++ b/internal/gkr/gkrcore/types.go @@ -36,10 +36,9 @@ type ( } Wire[GateExecutable any] struct { - Gate Gate[GateExecutable] - Inputs []int - NbUniqueOutputs int - Exported bool + Gate Gate[GateExecutable] + Inputs []int + Exported bool } Circuit[GateExecutable any] []Wire[GateExecutable] @@ -64,55 +63,6 @@ func (w Wire[GateExecutable]) IsInput() bool { return len(w.Inputs) == 0 } -// IsOutput returns whether the wire is an output wire. A wire is an output wire -// if it is not input to any other wire. -func (w Wire[GateExecutable]) IsOutput() bool { - return w.NbUniqueOutputs == 0 || w.Exported -} - -// NbClaims returns the number of claims to be proven about this wire. The number -// of claims is the number of Wires it is input to, except for an output wire, which -// has an extra claim. -func (w Wire[GateExecutable]) NbClaims() int { - res := w.NbUniqueOutputs - if w.IsOutput() { - res++ - } - return res -} - -// NoProof returns whether no proof is needed for this wire. This corresponds -// to input wires without any claims to be made about them. -func (w Wire[GateExecutable]) NoProof() bool { - return w.IsInput() && w.NbClaims() == 1 -} - -// NbUniqueInputs returns the number of unique input wires to this wire. -func (w Wire[GateExecutable]) NbUniqueInputs() int { - set := make(map[int]struct{}, len(w.Inputs)) - for _, in := range w.Inputs { - set[in] = struct{}{} - } - return len(set) -} - -// ZeroCheckDegree returns the degree in each variable of the zero-check polynomial -// associated with this gate, if any. If this wire is not subject to zero-check, it will return 0. -func (w Wire[GateExecutable]) ZeroCheckDegree() int { - if w.IsInput() { - switch w.NbClaims() { - case 0: - panic("should be unreachable") - case 1: - return 0 - default: - // Input gate with multiple claims treated as a degree 1 gate. - return 2 - } - } - return w.Gate.Degree + 1 -} - // ClaimPropagationInfo returns sets of indices describing the pruning of claim propagation. // At the end of sumcheck for wire #wireIndex, we end up with sequences "uniqueEvaluations" and "evaluations", // the former a subsequence of the latter. @@ -160,30 +110,6 @@ func (c Circuit[GateExecutable]) MemoryRequirements(nbInstances int) []int { return res } -// OutputsList for each wire, returns the set of indexes of wires it is input to. -// It also sets the NbUniqueOutputs fields. -func (c Circuit[GateExecutable]) OutputsList() [][]int { - res := make([][]int, len(c)) - for i := range c { - res[i] = make([]int, 0) - c[i].NbUniqueOutputs = 0 - } - ins := make(map[int]struct{}, len(c)) - for i := range c { - for k := range ins { // clear map - delete(ins, k) - } - for _, in := range c[i].Inputs { - res[in] = append(res[in], i) - if _, ok := ins[in]; !ok { - c[in].NbUniqueOutputs++ - ins[in] = struct{}{} - } - } - } - return res -} - // Inputs returns the list of input wire indices. func (c Circuit[GateExecutable]) Inputs() []int { res := make([]int, 0, len(c)) @@ -196,11 +122,16 @@ func (c Circuit[GateExecutable]) Inputs() []int { } // Outputs returns the list of output wire indices. -// It requires the NbUniqueOutput values to have been set. func (c Circuit[GateExecutable]) Outputs() []int { + isOutput := make([]bool, len(c)) + for i := range c { + for _, in := range c[i].Inputs { + isOutput[in] = true + } + } res := make([]int, 0, len(c)) for i := range c { - if c[i].IsOutput() { + if !isOutput[i] { res = append(res, i) } } @@ -216,17 +147,6 @@ func (c Circuit[GateExecutable]) MaxGateNbIn() int { return res } -// ProofSize computes how large the proof for a circuit would be. It needs NbUniqueOutputs to be set. -func (c Circuit[GateExecutable]) ProofSize(logNbInstances int) int { - nbUniqueInputs := 0 - nbPartialEvalPolys := 0 - for i := range c { - nbUniqueInputs += c[i].NbUniqueOutputs // each unique output is manifest in a finalEvalProof entry - nbPartialEvalPolys += c[i].ZeroCheckDegree() - } - return nbUniqueInputs + nbPartialEvalPolys*logNbInstances -} - // makeNeg1Slice returns a slice of size n with all elements set to -1. func makeNeg1Slice(n int) []int { res := make([]int, n) @@ -278,48 +198,18 @@ type Blueprints struct { } // Compile compiles a raw circuit into both a gadget circuit and a serializable circuit. -// It computes all wire and gate metadata (Degree, SolvableVar, NbUniqueOutputs). +// It computes all wire and gate metadata (Degree, SolvableVar). func (c RawCircuit) Compile(mod *big.Int) (GadgetCircuit, SerializableCircuit, error) { gadget := make(GadgetCircuit, len(c)) serializable := make(SerializableCircuit, len(c)) - // First pass: copy inputs, gates, and exported flags, compute NbUniqueOutputs - curWireIn := make([]bool, len(c)) for i := range c { gadget[i].Inputs = c[i].Inputs gadget[i].Exported = c[i].Exported serializable[i].Inputs = c[i].Inputs serializable[i].Exported = c[i].Exported - // Compute NbUniqueOutputs for input wires - for _, in := range c[i].Inputs { - if !curWireIn[in] { - gadget[in].NbUniqueOutputs++ - serializable[in].NbUniqueOutputs++ - curWireIn[in] = true - } - } - // clear curWireIn for next iteration - for _, in := range c[i].Inputs { - curWireIn[in] = false - } - } - - // Compile identity gate for input wires. - compiledIdentity, err := CompileGateFunction(Identity, 1, mod) - if err != nil { - return nil, nil, err - } - identityGadgetGate := GadgetGate{Evaluate: Identity, NbIn: 1, Degree: compiledIdentity.Degree, SolvableVar: compiledIdentity.SolvableVar} - - // Second pass: compile gates and set metadata - for i := range c { - if len(c[i].Inputs) == 0 { // input wire - if c[i].Gate != nil { - return nil, nil, errors.New("nil gate expected for input wire") - } - gadget[i].Gate = identityGadgetGate - serializable[i].Gate = compiledIdentity + if gadget[i].IsInput() { continue } From eb3a0b683a3534e7d011bd812ffbb81fc813387e Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 9 Mar 2026 22:47:02 -0500 Subject: [PATCH 222/251] refactor: remove use of the fiatshamir package --- internal/gkr/bn254/blueprint.go | 14 +- internal/gkr/bn254/sumcheck.go | 183 +++++++++---------------- internal/gkr/gkrcore/schedule.go | 2 +- internal/gkr/gkrcore/serialize.go | 219 ++++++++++++++++++++++++++++++ 4 files changed, 290 insertions(+), 128 deletions(-) create mode 100644 internal/gkr/gkrcore/serialize.go diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 7332eb77fc..545d98f65d 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -15,7 +15,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bn254/fr" "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" @@ -257,9 +256,11 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra } } + // Create hasher and write base challenges + hsh := hash.NewHash(b.HashName + "_BN254") + // Read initial challenges from instruction calldata (parse dynamically, no metadata) // Format: [0]=totalSize, [1...]=challenge linear expressions - insBytes := make([][]byte, 0) // first challenges calldata := inst.Calldata[1:] // skip size prefix for len(calldata) != 0 { val, delta := s.Read(calldata) @@ -268,15 +269,12 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) var challenge fr.Element copy(challenge[:], val[:]) - insBytes = append(insBytes, challenge.Marshal()) + challengeBytes := challenge.Bytes() + hsh.Write(challengeBytes[:]) } - // Create Fiat-Shamir settings - hsh := hash.NewHash(b.HashName + "_BN254") - fsSettings := fiatshamir.WithHash(hsh, insBytes...) - // Call the BN254-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.Circuit, b.Schedule, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, b.Schedule, assignments, hsh) if err != nil { return fmt.Errorf("bn254 prove failed: %w", err) } diff --git a/internal/gkr/bn254/sumcheck.go b/internal/gkr/bn254/sumcheck.go index 4c9dc2ca3c..cdf37feb95 100644 --- a/internal/gkr/bn254/sumcheck.go +++ b/internal/gkr/bn254/sumcheck.go @@ -7,33 +7,62 @@ package gkr import ( "errors" - "strconv" + "hash" "github.com/consensys/gnark-crypto/ecc/bn254/fr" "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) -// This does not make use of parallelism and represents polynomials as lists of coefficients -// It is currently geared towards arithmetic hashes. Once we have a more unified hash function interface, this can be generified. +// This does not make use of parallelism and represents polynomials as lists of coefficients. + +// transcript is a Fiat-Shamir transcript backed by a running hash. +// Field elements are written via bind; challenges are derived via getChallenge. +// The hash is never reset — all previous data is implicitly part of future challenges. +type transcript struct { + h hash.Hash + bound bool // whether bind was called since the last getChallenge +} + +// bind writes field elements to the transcript as bindings for the next challenge. +func (t *transcript) bind(elements ...fr.Element) { + if len(elements) == 0 { + return + } + for i := range elements { + bytes := elements[i].Bytes() + t.h.Write(bytes[:]) + } + t.bound = true +} + +// getChallenge binds optional elements, then squeezes a challenge from the current hash state. +// If no bindings were added since the last squeeze, a separator byte is written first +// to advance the state and prevent repeated values. +func (t *transcript) getChallenge(bindings ...fr.Element) fr.Element { + t.bind(bindings...) + if !t.bound { + t.h.Write([]byte{0}) + } + t.bound = false + var res fr.Element + res.SetBytes(t.h.Sum(nil)) + return res +} // sumcheckClaims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. // Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) type sumcheckClaims interface { - fold(a fr.Element) polynomial.Polynomial // fold into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. - next(fr.Element) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ + roundPolynomial() polynomial.Polynomial // compute gⱼ polynomial for current round + roundFold(r fr.Element) // fold inputs and eq at challenge r varsNum() int // number of variables - claimsNum() int // number of claims proveFinalEval(r []fr.Element) []fr.Element // in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof } // sumcheckLazyClaims is the sumcheckClaims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. type sumcheckLazyClaims interface { - claimsNum() int // claimsNum = m - varsNum() int // varsNum = n - foldedSum(a fr.Element) fr.Element // foldedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - degree(i int) int // degree of the total claim in the i'th variable - verifyFinalEval(r []fr.Element, foldingCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error + varsNum() int // varsNum = n + degree(i int) int // degree of the total claim in the i'th variable + verifyFinalEval(r []fr.Element, purportedValue fr.Element, proof []fr.Element) error } // sumcheckProof of a multi-statement. @@ -42,130 +71,46 @@ type sumcheckProof struct { finalEvalProof []fr.Element //in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof } -func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) (challengeNames []string, err error) { - numChallenges := varsNum - if claimsNum >= 2 { - numChallenges++ - } - challengeNames = make([]string, numChallenges) - if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "fold" - } - prefix := settings.Prefix + "pSP." - for i := 0; i < varsNum; i++ { - challengeNames[i+numChallenges-varsNum] = prefix + strconv.Itoa(i) - } - if settings.Transcript == nil { - transcript := fiatshamir.NewTranscript(settings.Hash, challengeNames...) - settings.Transcript = transcript - } - - for i := range settings.BaseChallenges { - if err = settings.Transcript.Bind(challengeNames[0], settings.BaseChallenges[i]); err != nil { - return - } - } - return -} - -func next(transcript *fiatshamir.Transcript, bindings []fr.Element, remainingChallengeNames *[]string) (fr.Element, error) { - challengeName := (*remainingChallengeNames)[0] - for i := range bindings { - bytes := bindings[i].Bytes() - if err := transcript.Bind(challengeName, bytes[:]); err != nil { - return fr.Element{}, err - } - } - var res fr.Element - bytes, err := transcript.ComputeChallenge(challengeName) - res.SetBytes(bytes) - - *remainingChallengeNames = (*remainingChallengeNames)[1:] - - return res, err -} - -// sumcheckProve create a non-interactive proof -func sumcheckProve(claims sumcheckClaims, transcriptSettings fiatshamir.Settings) (sumcheckProof, error) { - - var proof sumcheckProof - remainingChallengeNames, err := setupTranscript(claims.claimsNum(), claims.varsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return proof, err - } - - var foldingCoeff fr.Element - if claims.claimsNum() >= 2 { - if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { - return proof, err - } - } - +// sumcheckProve creates a non-interactive sumcheck proof. +// The fold challenge is derived by the caller (proveLevel). +// Pattern: roundPolynomial, [roundFold, roundPolynomial]*, proveFinalEval. +func sumcheckProve(claims sumcheckClaims, t *transcript) sumcheckProof { varsNum := claims.varsNum() - proof.partialSumPolys = make([]polynomial.Polynomial, varsNum) - proof.partialSumPolys[0] = claims.fold(foldingCoeff) + proof := sumcheckProof{partialSumPolys: make([]polynomial.Polynomial, varsNum)} + proof.partialSumPolys[0] = claims.roundPolynomial() challenges := make([]fr.Element, varsNum) - for j := 0; j+1 < varsNum; j++ { - if challenges[j], err = next(transcript, proof.partialSumPolys[j], &remainingChallengeNames); err != nil { - return proof, err - } - proof.partialSumPolys[j+1] = claims.next(challenges[j]) - } - - if challenges[varsNum-1], err = next(transcript, proof.partialSumPolys[varsNum-1], &remainingChallengeNames); err != nil { - return proof, err + for j := range varsNum - 1 { + challenges[j] = t.getChallenge(proof.partialSumPolys[j]...) + claims.roundFold(challenges[j]) + proof.partialSumPolys[j+1] = claims.roundPolynomial() } + challenges[varsNum-1] = t.getChallenge(proof.partialSumPolys[varsNum-1]...) proof.finalEvalProof = claims.proveFinalEval(challenges) - - return proof, nil + return proof } -func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSettings fiatshamir.Settings) error { - remainingChallengeNames, err := setupTranscript(claims.claimsNum(), claims.varsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return err - } - - var foldingCoeff fr.Element - - if claims.claimsNum() >= 2 { - if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { - return err - } - } - +// sumcheckVerify verifies a non-interactive sumcheck proof. +// The fold challenge is derived by the caller (verifyLevel). +// claimedSum is the expected sum; degree is the polynomial's degree in each variable. +func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, claimedSum fr.Element, degree int, t *transcript) error { r := make([]fr.Element, claims.varsNum()) - // Just so that there is enough room for gJ to be reused - maxDegree := claims.degree(0) - for j := 1; j < claims.varsNum(); j++ { - if d := claims.degree(j); d > maxDegree { - maxDegree = d - } - } - gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() - gJR := claims.foldedSum(foldingCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) + gJ := make(polynomial.Polynomial, degree+1) + gJR := claimedSum for j := range claims.varsNum() { - if len(proof.partialSumPolys[j]) != claims.degree(j) { + if len(proof.partialSumPolys[j]) != degree { return errors.New("malformed proof") } copy(gJ[1:], proof.partialSumPolys[j]) - gJ[0].Sub(&gJR, &proof.partialSumPolys[j][0]) // Requirement that gⱼ(0) + gⱼ(1) = gⱼ₋₁(r) - // gJ is ready + gJ[0].Sub(&gJR, &proof.partialSumPolys[j][0]) - //Prepare for the next iteration - if r[j], err = next(transcript, proof.partialSumPolys[j], &remainingChallengeNames); err != nil { - return err - } - // This is an extremely inefficient way of interpolating. TODO: Interpolate without symbolically computing a polynomial - gJCoeffs := polynomial.InterpolateOnRange(gJ[:(claims.degree(j) + 1)]) + r[j] = t.getChallenge(proof.partialSumPolys[j]...) + gJCoeffs := polynomial.InterpolateOnRange(gJ[:(degree + 1)]) gJR = gJCoeffs.Eval(&r[j]) } - return claims.verifyFinalEval(r, foldingCoeff, gJR, proof.finalEvalProof) + return claims.verifyFinalEval(r, gJR, proof.finalEvalProof) } diff --git a/internal/gkr/gkrcore/schedule.go b/internal/gkr/gkrcore/schedule.go index bf4084aa7a..d27c0a6c23 100644 --- a/internal/gkr/gkrcore/schedule.go +++ b/internal/gkr/gkrcore/schedule.go @@ -338,4 +338,4 @@ func BasicProvingSchedule(c SerializableCircuit) (constraint.GkrProvingSchedule, } } return b.finalize() -} +} \ No newline at end of file diff --git a/internal/gkr/gkrcore/serialize.go b/internal/gkr/gkrcore/serialize.go new file mode 100644 index 0000000000..8fc56d0160 --- /dev/null +++ b/internal/gkr/gkrcore/serialize.go @@ -0,0 +1,219 @@ +package gkrcore + +import ( + "encoding/binary" + "fmt" + "io" + "math/big" + + "github.com/consensys/gnark/constraint" +) + +// Common serialization utilities + +func writeUint8(w io.Writer, x int) error { + if x >= 256 || x < 0 { + return fmt.Errorf("%d out of range", x) + } + _, err := w.Write([]byte{byte(x)}) + return err +} + +func writeUint16(w io.Writer, x int) error { + var buf [2]byte + if x >= 65536 || x < 0 { + return fmt.Errorf("%d out of range", x) + } + binary.LittleEndian.PutUint16(buf[:], uint16(x)) + _, err := w.Write(buf[:]) + return err +} + +func writeBool(w io.Writer, b bool) error { + var v byte + if b { + v = 1 + } + _, err := w.Write([]byte{v}) + return err +} + +func writeBigInt(w io.Writer, x *big.Int) error { + bytes := x.Bytes() + if err := writeUint8(w, len(bytes)); err != nil { + return err + } + _, err := w.Write(bytes) + return err +} + +// SerializeCircuit writes a SerializableCircuit to w in deterministic binary format, +// primarily for hashing circuits to create unique identifiers. +// +// The encoding is compact (uint16 for counts/indices, uint8 for bigint byte lengths) and +// uses little-endian throughout. Gate metadata (NbIn, Degree, SolvableVar) is omitted +// since it can be recomputed from bytecode on deserialization. +// +// Format: +// +// Circuit: [wire_count:u16] [wire...] +// Wire: [input_count:u16] [input_indices:u16...] [exported:bool] [gate?] +// Gate (non-input only): [const_count:u16] [constants...] [inst_count:u16] [instructions...] +// Constant: [byte_len:u8] [bytes...] +// Instruction: [op:u8] [input_count:u16] [input_indices:u16...] +func SerializeCircuit(w io.Writer, c SerializableCircuit) error { + if len(c) >= 1<<32 { + return fmt.Errorf("circuit length too large: %d", len(c)) + } + + // Write number of wires + if err := writeUint16(w, len(c)); err != nil { + return err + } + + // Write each wire + for i := range c { + wire := &c[i] + + // Write number of inputs + if err := writeUint16(w, len(wire.Inputs)); err != nil { + return err + } + + // Write each input index + for _, input := range wire.Inputs { + if err := writeUint16(w, input); err != nil { + return err + } + } + + // Write exported flag + if err := writeBool(w, wire.Exported); err != nil { + return err + } + + // If not an input wire, write gate information + if !wire.IsInput() { + gate := &wire.Gate + + // Write bytecode + bytecode := &gate.Evaluate + + // Write constants + if err := writeUint16(w, len(bytecode.Constants)); err != nil { + return err + } + for _, constant := range bytecode.Constants { + if err := writeBigInt(w, constant); err != nil { + return err + } + } + + // Write instructions + if err := writeUint16(w, len(bytecode.Instructions)); err != nil { + return err + } + for _, inst := range bytecode.Instructions { + // Write operation + if _, err := w.Write([]byte{byte(inst.Op)}); err != nil { + return err + } + + // Write number of instruction inputs + if err := writeUint16(w, len(inst.Inputs)); err != nil { + return err + } + + // Write each instruction input + for _, input := range inst.Inputs { + if err := writeUint16(w, int(input)); err != nil { + return err + } + } + } + } + } + + return nil +} + +// SerializeSchedule writes a GkrProvingSchedule to w in deterministic binary format, +// primarily for hashing schedules to create unique identifiers. +// +// The encoding uses uint16 for counts/indices and little-endian throughout. +// +// Format: +// +// Schedule: [level_count:u16] [level...] +// Level: [type:u8] [skip_group | sumcheck_groups] +// SkipLevel: [claim_group] +// SumcheckLevel: [group_count:u16] [claim_group...] +// ClaimGroup: [wire_count:u16] [wire_indices:u16...] [source_count:u16] [source_indices:u16...] +func SerializeSchedule(w io.Writer, s constraint.GkrProvingSchedule) error { + if len(s) >= 65536 { + return fmt.Errorf("schedule length too large: %d", len(s)) + } + + writeClaimGroup := func(cg constraint.GkrClaimGroup) error { + // Write wire count and indices + if err := writeUint16(w, len(cg.Wires)); err != nil { + return err + } + for _, wireIdx := range cg.Wires { + if err := writeUint16(w, wireIdx); err != nil { + return err + } + } + + // Write claim source count and indices + if err := writeUint16(w, len(cg.ClaimSources)); err != nil { + return err + } + for _, srcIdx := range cg.ClaimSources { + if err := writeUint16(w, srcIdx); err != nil { + return err + } + } + return nil + } + + // Write number of levels + if err := writeUint16(w, len(s)); err != nil { + return err + } + + // Write each level + for _, level := range s { + switch l := level.(type) { + case constraint.GkrSkipLevel: + // Type: 0 for skip + if _, err := w.Write([]byte{0}); err != nil { + return err + } + if err := writeClaimGroup(constraint.GkrClaimGroup(l)); err != nil { + return err + } + + case constraint.GkrSumcheckLevel: + // Type: 1 for sumcheck + if _, err := w.Write([]byte{1}); err != nil { + return err + } + // Write number of groups + if err := writeUint16(w, len(l)); err != nil { + return err + } + // Write each group + for _, cg := range l { + if err := writeClaimGroup(cg); err != nil { + return err + } + } + + default: + return fmt.Errorf("unknown level type: %T", level) + } + } + + return nil +} From 5e878c7b13675cf29a15dcf1caca657173353b23 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Tue, 10 Mar 2026 15:34:37 -0500 Subject: [PATCH 223/251] feat: levels for bn254 --- internal/gkr/bn254/gkr.go | 882 +++++++++++--------------- internal/gkr/gkrcore/schedule.go | 22 +- internal/gkr/gkrcore/schedule_test.go | 45 +- 3 files changed, 409 insertions(+), 540 deletions(-) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index bf586e4d2d..f07c1f5e35 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -8,14 +8,14 @@ package gkr import ( "errors" "fmt" + "hash" "iter" - "strconv" "sync" "github.com/consensys/gnark-crypto/ecc/bn254/fr" "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" ) @@ -33,402 +33,423 @@ type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) // zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). -// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear -// extension of the values of w across all instances. -// Its purpose is to batch the checking of multiple evaluations of the same wire. +// It checks that the polynomial ∑ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, +// where the sum runs over all wᵢ and evaluation point xᵢ in the level. +// Its purpose is to batch the checking of multiple wire evaluations at evaluation points. type zeroCheckLazyClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly - manager *resources // WARNING: Circular references -} - -func (e *zeroCheckLazyClaims) getWire() Wire { - return e.manager.circuit[e.wireI] -} - -func (e *zeroCheckLazyClaims) claimsNum() int { - return len(e.evaluationPoints) + foldingCoeff fr.Element // the coefficient used to fold claims, conventionally 0 if there is only one claim + resources *resources + levelI int } func (e *zeroCheckLazyClaims) varsNum() int { - return len(e.evaluationPoints[0]) -} - -// foldedSum returns ∑ᵢ aⁱ yᵢ -func (e *zeroCheckLazyClaims) foldedSum(a fr.Element) fr.Element { - evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) - return evalsAsPoly.Eval(&a) + return e.resources.nbVars } func (e *zeroCheckLazyClaims) degree(int) int { - return e.manager.circuit[e.wireI].ZeroCheckDegree() -} - -// verifyFinalEval finalizes the verification of w. -// The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. (c is foldingCoeff) -// Both purportedValue and the vector r have been randomized during the sumcheck protocol. -// By taking the w term out of the sum we get the equivalent claim that -// for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. -// If w is an input wire, the verifier can directly check its evaluation at r. -// Otherwise, the prover makes claims about the evaluation of w's input wires, -// wᵢ, at r, to be verified later. -// The claims are communicated through the proof parameter. -// The verifier checks here if the claimed evaluations of wᵢ(r) are consistent with -// the main claim, by checking E w(wᵢ(r)...) = purportedValue. -func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, foldingCoeff, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { - // the eq terms ( E ) - numClaims := len(e.evaluationPoints) - evaluation := polynomial.EvalEq(e.evaluationPoints[numClaims-1], r) - for i := numClaims - 2; i >= 0; i-- { - evaluation.Mul(&evaluation, &foldingCoeff) - eq := polynomial.EvalEq(e.evaluationPoints[i], r) - evaluation.Add(&evaluation, &eq) - } - - wire := e.manager.circuit[e.wireI] - - // the w(...) term - var gateEvaluation fr.Element - if wire.IsInput() { // just compute w(r) - gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) - } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire - injection, injectionLeftInv := - e.manager.circuit.ClaimPropagationInfo(e.wireI) - - if len(injection) != len(uniqueInputEvaluations) { - return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) - } - - for uniqueI, i := range injection { // map from unique to all - e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) - } + return gkrcore.Degree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel), e.resources.circuit) +} + +// verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. +// The sumcheck protocol has already reduced the per-wire claims w(xᵢ) = yᵢ to verifying +// ∑ cⁱ eq(xᵢ, r) · gateᵥ(r) = purportedValue, where the sum runs over all +// (wire v, claim source s) pairs and c is foldingCoeff. +// Both purportedValue and the vector r have been randomized during sumcheck. +// +// For input wires, gateᵥ(r) is computed directly from the assignment. +// For non-input wires, the prover claims evaluations of the input wires at r, +// communicated through uniqueInputEvaluations; those claims are verified later. +// The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming +// that the full sum matches purportedValue. +func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { + e.resources.levelPoints[e.levelI] = r + level := e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel) + perWireInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) + + var terms []fr.Element + flatW := 0 + for _, group := range level { + for _, wI := range group.Wires { + wire := e.resources.circuit[wI] + + var gateEval fr.Element + if wire.IsInput() { + gateEval = e.resources.assignment[wI].Evaluate(r, e.resources.memPool) + } else { + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) + for _, v := range perWireInputEvals[flatW] { + evaluator.pushInput(v) + } + gateEval.Set(evaluator.evaluate()) + } - evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) - for _, uniqueI := range injectionLeftInv { // map from all to unique - evaluator.pushInput(uniqueInputEvaluations[uniqueI]) + for _, src := range group.ClaimSources { + eq := polynomial.EvalEq(e.resources.levelPoints[src], r) + var term fr.Element + term.Mul(&eq, &gateEval) + terms = append(terms, term) + } + flatW++ } - - gateEvaluation.Set(evaluator.evaluate()) } - evaluation.Mul(&evaluation, &gateEvaluation) - - if evaluation.Equal(&purportedValue) { + ys := polynomial.Polynomial(terms) + if total := ys.Eval(&e.foldingCoeff); total.Equal(&purportedValue) { return nil } return errors.New("incompatible evaluations") } // zeroCheckClaims is a claim for sumcheck (prover side). -// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear -// extension of the values of w across all instances. -// Its purpose is to batch the proving of multiple evaluations of the same wire. +// It checks that the polynomial ∑ cⁱ eq(-, xᵢ) gateᵥ(-) sums to the expected value, +// where the sum runs over all (wire v, claim source s) pairs in the level. +// Each wire has its own eq table with the batching coefficients baked in. type zeroCheckClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []fr.Element // yᵢ = w(xᵢ) - manager *resources - - input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) - - eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) - - gateEvaluatorPool *gateEvaluatorPool + level constraint.GkrSumcheckLevel + levelI int + resources *resources + input []polynomial.MultiLin // UniqueGateInputs order + inputIndices [][]int // [wireInLevel][gateInputJ] → index in input + eqs []polynomial.MultiLin // per-wire interpolation bases for evaluating wire assignments at the challenge point + gateEvaluatorPools []*gateEvaluatorPool } -func (c *zeroCheckClaims) getWire() Wire { - return c.manager.circuit[c.wireI] -} - -// fold the multiple claims into one claim using a random combination (foldingCoeff or c). -// From the original multiple claims of w(xᵢ) = yᵢ, we get a single claim -// ∑ᵢ,ₕ cⁱ eq(xᵢ, h) w(h) = ∑ᵢ cⁱ yᵢ, where h iterates over the hypercube (circuit instances) and -// i iterates over the claims. -// Equivalently, we could say ∑ᵢ cⁱ yᵢ = ∑ₕ,ᵢ cⁱ eq(xᵢ, h) w(h) = ∑ₕ w(h) ∑ᵢ cⁱ eq(xᵢ, h). -// Thus if we initially compute E := ∑ᵢ cⁱ eq(xᵢ, -), our claim will find the simpler form -// ∑ᵢ cⁱ yᵢ = ∑ₕ w(h) E(h), where the sum-checked polynomial is of degree deg(g) + 1, -// and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. -// The output of fold is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. -func (c *zeroCheckClaims) fold(foldingCoeff fr.Element) polynomial.Polynomial { - varsNum := c.varsNum() - eqLength := 1 << varsNum - claimsNum := c.claimsNum() - // initialize the eq tables ( E ) - c.eq = c.manager.memPool.Make(eqLength) - - c.eq[0].SetOne() - c.eq.Eq(c.evaluationPoints[0]) - - // E := eq(x₀, -) - newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) - aI := foldingCoeff - - // E += cⁱ eq(xᵢ, -) - for k := 1; k < claimsNum; k++ { - newEq[0].Set(&aI) - - c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) - - if k+1 < claimsNum { - aI.Mul(&aI, &foldingCoeff) - } - } - - c.manager.memPool.Dump(newEq) - - return c.computeGJ() -} - -// eqAcc sets m to an eq table at q and then adds it to e. -// m <- eq(q, -). -// e <- e + m -func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { - n := len(q) - - //At the end of each iteration, m(h₁, ..., hₙ) = eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) - for i := range q { // In the comments we use a 1-based index so q[i] = qᵢ₊₁ - // go through all assignments of (b₁, ..., bᵢ) ∈ {0,1}ⁱ - const threshold = 1 << 6 - k := 1 << i - if k < threshold { - for j := 0; j < k; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - } else { - c.manager.workers.Submit(k, func(start, end int) { - for j := start; j < end; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - }, 1024).Wait() - } - - } - c.manager.workers.Submit(len(e), func(start, end int) { - for i := start; i < end; i++ { - e[i].Add(&e[i], &m[i]) - } - }, 512).Wait() +func (c *zeroCheckClaims) varsNum() int { + return len(c.resources.levelPoints[c.level[0].ClaimSources[0]]) } -// computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). -// the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). -// The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. -func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { +// roundPolynomial computes gⱼ = ∑_h ∑_w eqs[w](Xⱼ, h...) · gateᵥ(inputs(Xⱼ, h...)). +// The polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). +// The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). +// By convention, g₀ is a constant polynomial equal to the claimed sum. +func (c *zeroCheckClaims) roundPolynomial() polynomial.Polynomial { + degree := gkrcore.Degree(c.level, c.resources.circuit) + nbUniqueInputs := len(c.input) + nbWires := len(c.eqs) - wire := c.getWire() - degGJ := wire.ZeroCheckDegree() // guaranteed to be no smaller than the actual deg(gⱼ) - nbGateIn := len(c.input) + // Both eqs and input are multilinear, thus linear in Xⱼ. + // For any such f, f(m) = m·(f(1) - f(0)) + f(0), and f(0), f(1) are read directly + // from the bookkeeping tables. This allows stepwise evaluation at Xⱼ = 1, 2, ..., degree. + // Layout: [eq₀, eq₁, ..., eq_{nbWires-1}, input₀, input₁, ..., input_{nbUniqueInputs-1}] + ml := make([]polynomial.MultiLin, nbWires+nbUniqueInputs) + copy(ml, c.eqs) + copy(ml[nbWires:], c.input) - // Both E and wᵢ (the input wires and the eq table) are multilinear, thus - // they are linear in Xⱼ. - // So for f ∈ { E(r₁, ..., Xⱼ, h...) } ∪ {wᵢ(r₁, ..., Xⱼ, h...) }, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables. - // ml are such multilinear polynomials the evaluations of which over different values of Xⱼ are computed in this stepwise manner. - ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube - ml[0] = c.eq - copy(ml[1:], c.input) + sumSize := len(c.eqs[0]) / 2 - sumSize := len(c.eq) / 2 // the range of h, over which we sum - - // Perf-TODO: Collate once at claim "folding" time and not again. then, even folding can be done in one operation every time "next" is called - - gJ := make([]fr.Element, degGJ) + p := make([]fr.Element, degree) var mu sync.Mutex - computeAll := func(start, end int) { // compute method to allow parallelization across instances + computeAll := func(start, end int) { var step fr.Element - evaluator := c.gateEvaluatorPool.get() - defer c.gateEvaluatorPool.put(evaluator) + evaluators := make([]*gateEvaluator, nbWires) + for w := range nbWires { + evaluators[w] = c.gateEvaluatorPools[w].get() + } + defer func() { + for w := range nbWires { + c.gateEvaluatorPools[w].put(evaluators[w]) + } + }() - res := make([]fr.Element, degGJ) + res := make([]fr.Element, degree) // evaluations of ml, laid out as: // ml[0](1, h...), ml[1](1, h...), ..., ml[len(ml)-1](1, h...), // ml[0](2, h...), ml[1](2, h...), ..., ml[len(ml)-1](2, h...), // ... - // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) - mlEvals := make([]fr.Element, degGJ*len(ml)) - - for h := start; h < end; h++ { // h counts across instances + // ml[0](degree, h...), ml[1](degree, h...), ..., ml[len(ml)-1](degree, h...) + mlEvals := make([]fr.Element, degree*len(ml)) + for h := start; h < end; h++ { evalAt1Index := sumSize + h for k := range ml { - // d = 0 - mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. + mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1, taken directly from the table step.Sub(&mlEvals[k], &ml[k][h]) // step = ml[k](1) - ml[k](0) - for d := 1; d < degGJ; d++ { + for d := 1; d < degree; d++ { mlEvals[d*len(ml)+k].Add(&mlEvals[(d-1)*len(ml)+k], &step) } } - eIndex := 0 // index for where the current eq term is + eIndex := 0 // start of the current row's eq evaluations nextEIndex := len(ml) - for d := range degGJ { - // Push gate inputs - for i := range nbGateIn { - evaluator.pushInput(mlEvals[eIndex+1+i]) + for d := range degree { + for w := range nbWires { + for _, inputI := range c.inputIndices[w] { + evaluators[w].pushInput(mlEvals[eIndex+nbWires+inputI]) + } + summand := evaluators[w].evaluate() + summand.Mul(summand, &mlEvals[eIndex+w]) + res[d].Add(&res[d], summand) // collect contributions into the sum from start to end } - summand := evaluator.evaluate() - summand.Mul(summand, &mlEvals[eIndex]) - res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } } mu.Lock() - for i := range gJ { - gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum + for i := range p { + p[i].Add(&p[i], &res[i]) // collect into the complete sum } mu.Unlock() } const minBlockSize = 64 - if sumSize < minBlockSize { - // no parallelization computeAll(0, sumSize) } else { - c.manager.workers.Submit(sumSize, computeAll, minBlockSize).Wait() + c.resources.workers.Submit(sumSize, computeAll, minBlockSize).Wait() } - return gJ + return p } -// next first folds the input and E polynomials at the given verifier challenge then computes the new gⱼ. -// Thus, j <- j+1 and rⱼ = challenge. -func (c *zeroCheckClaims) next(challenge fr.Element) polynomial.Polynomial { +// roundFold folds all input and eq polynomials at the verifier challenge r. +// After this call, j ← j+1 and rⱼ = r. +func (c *zeroCheckClaims) roundFold(r fr.Element) { const minBlockSize = 512 - n := len(c.eq) / 2 + n := len(c.eqs[0]) / 2 if n < minBlockSize { - // no parallelization for i := range c.input { - c.input[i].Fold(challenge) + c.input[i].Fold(r) + } + for i := range c.eqs { + c.eqs[i].Fold(r) } - c.eq.Fold(challenge) } else { - wgs := make([]*sync.WaitGroup, len(c.input)) + wgs := make([]*sync.WaitGroup, len(c.input)+len(c.eqs)) for i := range c.input { - wgs[i] = c.manager.workers.Submit(n, c.input[i].FoldParallel(challenge), minBlockSize) + wgs[i] = c.resources.workers.Submit(n, c.input[i].FoldParallel(r), minBlockSize) + } + for i := range c.eqs { + wgs[len(c.input)+i] = c.resources.workers.Submit(n, c.eqs[i].FoldParallel(r), minBlockSize) } - c.manager.workers.Submit(n, c.eq.FoldParallel(challenge), minBlockSize).Wait() for _, wg := range wgs { wg.Wait() } } - - return c.computeGJ() } -func (c *zeroCheckClaims) varsNum() int { - return len(c.evaluationPoints[0]) -} - -func (c *zeroCheckClaims) claimsNum() int { - return len(c.claimedEvaluations) -} - -// proveFinalEval provides the values wᵢ(r₁, ..., rₙ) +// proveFinalEval provides the unique input wire values wᵢ(r₁, ..., rₙ). func (c *zeroCheckClaims) proveFinalEval(r []fr.Element) []fr.Element { - //defer the proof, return list of claims - - injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. - evaluations := make([]fr.Element, len(injection)) - for i, gateInputI := range injection { - wI := c.input[gateInputI] - wI.Fold(r[len(r)-1]) // We already have wᵢ(r₁, ..., rₙ₋₁, hₙ) in a table. Only one more fold required. - c.manager.add(c.getWire().Inputs[gateInputI], r, wI[0]) - evaluations[i] = wI[0] + c.resources.levelPoints[c.levelI] = r + evaluations := make([]fr.Element, len(c.input)) + for i := range c.input { + c.input[i].Fold(r[len(r)-1]) + evaluations[i] = c.input[i][0] + } + for i := range c.input { + c.resources.memPool.Dump(c.input[i]) + } + for i := range c.eqs { + c.resources.memPool.Dump(c.eqs[i]) + } + for _, pool := range c.gateEvaluatorPools { + pool.dumpAll() } - - c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.dumpAll() - return evaluations } -type resources struct { - claims []*zeroCheckLazyClaims - assignment WireAssignment - memPool *polynomial.Pool - workers *utils.WorkerPool - circuit Circuit - schedule gkrcore.ProvingSchedule -} - -func newResources(circuit Circuit, assignment WireAssignment, o settings, schedule gkrcore.ProvingSchedule) (manager resources) { - manager.assignment = assignment - manager.claims = make([]*zeroCheckLazyClaims, len(circuit)) - manager.memPool = o.pool - manager.workers = o.workers - manager.circuit = circuit - manager.schedule = schedule +// eqAcc sets m to an eq table at q and then adds it to e. +// m <- m[0] · eq(q, -). +// e <- e + m +func (r *resources) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { + n := len(q) - for i := range circuit { - manager.claims[i] = &zeroCheckLazyClaims{ - wireI: i, - evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), - claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), - manager: &manager, + // At the end of each iteration, m(h₁, ..., hₙ) = m[0] · eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) + for i := range q { // 1-based in comments: q[i] = qᵢ₊₁ + // go through all assignments of (b₁, ..., bᵢ) ∈ {0,1}ⁱ + const threshold = 1 << 6 + k := 1 << i + if k < threshold { + for j := 0; j < k; j++ { + j0 := j << (n - i) // bᵢ₊₁ = 0 + j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 + + m[j1].Mul(&q[i], &m[j0]) // m(b₁,...,bᵢ,1) = m(b₁,...,bᵢ) · qᵢ₊₁ + m[j0].Sub(&m[j0], &m[j1]) // m(b₁,...,bᵢ,0) = m(b₁,...,bᵢ) · (1 - qᵢ₊₁) + } + } else { + r.workers.Submit(k, func(start, end int) { + for j := start; j < end; j++ { + j0 := j << (n - i) // bᵢ₊₁ = 0 + j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 + + m[j1].Mul(&q[i], &m[j0]) // m(b₁,...,bᵢ,1) = m(b₁,...,bᵢ) · qᵢ₊₁ + m[j0].Sub(&m[j0], &m[j1]) // m(b₁,...,bᵢ,0) = m(b₁,...,bᵢ) · (1 - qᵢ₊₁) + } + }, 1024).Wait() } } - return -} - -func (m *resources) add(wire int, evaluationPoint []fr.Element, evaluation fr.Element) { - claim := m.claims[wire] - i := len(claim.evaluationPoints) - claim.claimedEvaluations[i] = evaluation - claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) + r.workers.Submit(len(e), func(start, end int) { + for i := start; i < end; i++ { + e[i].Add(&e[i], &m[i]) + } + }, 512).Wait() } -func (m *resources) getLazyClaim(wire int) *zeroCheckLazyClaims { - return m.claims[wire] -} +type resources struct { + // levelPoints[i] is the sumcheck evaluation point r produced at schedule level i. + // levelPoints[len(schedule)] holds the initial challenge (firstChallenge / rho). + // SkipLevels produce no point; their slot remains nil. + levelPoints [][]fr.Element + evalPositions [][]int // [wireI][evalI] → position in source level's finalEvalProof + nbVars int + assignment WireAssignment + memPool *polynomial.Pool + workers *utils.WorkerPool + circuit Circuit + schedule constraint.GkrProvingSchedule +} + +func newResources(circuit Circuit, assignment WireAssignment, o settings, schedule constraint.GkrProvingSchedule) resources { + return resources{ + levelPoints: make([][]fr.Element, len(schedule)+1), + evalPositions: gkrcore.BuildFinalEvalPositions(schedule, circuit), + nbVars: o.nbVars, + assignment: assignment, + memPool: o.pool, + workers: o.workers, + circuit: circuit, + schedule: schedule, + } +} + +// proveLevel runs the sumcheck for a SumcheckLevel: derives the fold challenge, +// builds per-wire eq tables, constructs zeroCheckClaims, and calls sumcheckProve. +func (r *resources) proveLevel(levelI int, t *transcript) sumcheckProof { + level := r.schedule[levelI].(constraint.GkrSumcheckLevel) + + nbClaims := gkrcore.NbClaims(level) + var foldingCoeff fr.Element + if nbClaims >= 2 { + foldingCoeff = t.getChallenge() + } + + uniqueInputs, inputIndices := gkrcore.InputMapping(level, r.circuit) + input := make([]polynomial.MultiLin, len(uniqueInputs)) + for i, inW := range uniqueInputs { + input[i] = r.memPool.Clone(r.assignment[inW]) + } + + nbWires := 0 + for _, group := range level { + nbWires += len(group.Wires) + } + + pools := make([]*gateEvaluatorPool, nbWires) + flatW := 0 + for _, group := range level { + for _, wI := range group.Wires { + wire := r.circuit[wI] + gate := wire.Gate.Evaluate + if wire.IsInput() { + gate = gkrcore.IdentityBytecode() + } + pools[flatW] = newGateEvaluatorPool(gate, len(inputIndices[flatW]), r.memPool) + flatW++ + } + } + + eqLength := 1 << r.nbVars + eqs := make([]polynomial.MultiLin, nbWires) + var alpha fr.Element + alpha.SetOne() + flatW = 0 + for _, group := range level { + nbSources := len(group.ClaimSources) + + groupEq := polynomial.MultiLin(r.memPool.Make(eqLength)) + groupEq[0].Set(&alpha) + groupEq.Eq(r.levelPoints[group.ClaimSources[0]]) + + if nbSources > 1 { + newEq := polynomial.MultiLin(r.memPool.Make(eqLength)) + aI := alpha + for k := 1; k < nbSources; k++ { + aI.Mul(&aI, &foldingCoeff) + newEq[0].Set(&aI) + r.eqAcc(groupEq, newEq, r.levelPoints[group.ClaimSources[k]]) + } + r.memPool.Dump(newEq) + } -func (m *resources) getClaim(wireI int) *zeroCheckClaims { - lazy := m.claims[wireI] - wire := m.circuit[wireI] - res := &zeroCheckClaims{ - wireI: wireI, - evaluationPoints: lazy.evaluationPoints, - claimedEvaluations: lazy.claimedEvaluations, - manager: m, - } + var stride fr.Element + stride.Set(&foldingCoeff) + for range nbSources - 1 { + stride.Mul(&stride, &foldingCoeff) + } - if wire.IsInput() { - res.input = []polynomial.MultiLin{m.memPool.Clone(m.assignment[wireI])} - } else { - res.input = make([]polynomial.MultiLin, len(wire.Inputs)) + eqs[flatW] = groupEq + flatW++ + alpha.Mul(&alpha, &stride) - for inputI, inputW := range wire.Inputs { - res.input[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied + for w := 1; w < len(group.Wires); w++ { + eqs[flatW] = polynomial.MultiLin(r.memPool.Make(eqLength)) + r.workers.Submit(eqLength, func(start, end int) { + for i := start; i < end; i++ { + eqs[flatW][i].Mul(&eqs[flatW-1][i], &stride) + } + }, 512).Wait() + flatW++ + alpha.Mul(&alpha, &stride) + } + } + + claims := &zeroCheckClaims{ + level: level, + levelI: levelI, + resources: r, + input: input, + inputIndices: inputIndices, + eqs: eqs, + gateEvaluatorPools: pools, + } + return sumcheckProve(claims, t) +} + +// verifyLevel runs the sumcheck verification for a SumcheckLevel: derives the fold challenge, +// computes the claimed sum, and calls sumcheckVerify. +func (r *resources) verifyLevel(levelI int, proof Proof, t *transcript) error { + level := r.schedule[levelI].(constraint.GkrSumcheckLevel) + initialChallengeI := len(r.schedule) + + nbClaims := gkrcore.NbClaims(level) + var foldingCoeff fr.Element + if nbClaims >= 2 { + foldingCoeff = t.getChallenge() + } + + var ys []fr.Element + for _, group := range level { + for _, wI := range group.Wires { + evalI := 0 + for _, src := range group.ClaimSources { + var y fr.Element + if src == initialChallengeI { + y = r.assignment[wI].Evaluate(r.levelPoints[src], r.memPool) + } else { + y = proof[src].finalEvalProof[r.evalPositions[wI][evalI]] + evalI++ + } + ys = append(ys, y) + } } } - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Evaluate, len(res.input), m.memPool) - - return res -} + ysPoly := polynomial.Polynomial(ys) + claimedSum := ysPoly.Eval(&foldingCoeff) -func (m *resources) deleteClaim(wire int) { - m.claims[wire].manager = nil - m.claims[wire] = nil + lazyClaims := &zeroCheckLazyClaims{ + foldingCoeff: foldingCoeff, + resources: r, + levelI: levelI, + } + return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, gkrcore.Degree(level, r.circuit), t) } type settings struct { - pool *polynomial.Pool - transcript *fiatshamir.Transcript - transcriptPrefix string - nbVars int - workers *utils.WorkerPool + pool *polynomial.Pool + nbVars int + workers *utils.WorkerPool } type Option func(*settings) @@ -445,23 +466,16 @@ func WithWorkers(workers *utils.WorkerPool) Option { } } -func setup(c Circuit, schedule gkrcore.ProvingSchedule, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, gkrcore.ProvingSchedule, error) { +func setup(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash, options ...Option) (settings, error) { var o settings - var err error for _, option := range options { option(&o) } - if schedule == nil { - if schedule, err = gkrcore.BasicProvingSchedule(c); err != nil { - return o, nil, err - } - } - o.nbVars = assignment.NumVars() nbInstances := assignment.NumInstances() if 1< 1 { - size++ // fold challenge - } - size += logNbInstances // sumcheck rounds - } - - nums := make([]string, max(len(c), logNbInstances)) - for i := range nums { - nums[i] = strconv.Itoa(i) - } - - challenges := make([]string, size) - - // output wire claims - firstChallengePrefix := prefix + "fC." - for j := 0; j < logNbInstances; j++ { - challenges[j] = firstChallengePrefix + nums[j] - } - j := logNbInstances - for stepI, step := range schedule { - s, ok := step.(gkrcore.SumcheckLevel) - if !ok { - continue - } - stepPrefix := prefix + "s" + nums[stepI] + "." - - nbClaims := 0 - for _, g := range s { - for _, w := range g.Wires { - nbClaims += c[w].NbClaims() - } - } - if nbClaims > 1 { - challenges[j] = stepPrefix + "fold" - j++ - } - - partialSumPrefix := stepPrefix + "pSP." - for k := 0; k < logNbInstances; k++ { - challenges[j] = partialSumPrefix + nums[k] - j++ - } - } - return challenges -} - -func getFirstChallengeNames(logNbInstances int, prefix string) []string { - res := make([]string, logNbInstances) - firstChallengePrefix := prefix + "fC." - for i := 0; i < logNbInstances; i++ { - res[i] = firstChallengePrefix + strconv.Itoa(i) - } - return res -} - -func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Element, error) { - res := make([]fr.Element, len(names)) - for i, name := range names { - if bytes, err := transcript.ComputeChallenge(name); err != nil { - return nil, err - } else if err = res[i].SetBytesCanonical(bytes); err != nil { - return nil, err - } - } - return res, nil + return o, nil } // Prove consistency of the claimed assignment -func Prove(c Circuit, schedule gkrcore.ProvingSchedule, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { - o, schedule, err := setup(c, schedule, assignment, transcriptSettings, options...) +func Prove(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash, options ...Option) (Proof, error) { + o, err := setup(c, schedule, assignment, hasher, options...) if err != nil { return nil, err } defer o.workers.Stop() res := newResources(c, assignment, o, schedule) + t := transcript{h: hasher} - proof := make(Proof, len(res.schedule)) - // firstChallenge called rho in the paper - var firstChallenge []fr.Element - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return nil, err + proof := make(Proof, len(schedule)) + + // Derive initial challenge point + firstChallenge := make([]fr.Element, o.nbVars) + for j := range o.nbVars { + firstChallenge[j] = t.getChallenge() } + res.levelPoints[len(schedule)] = firstChallenge - stepPrefix := o.transcriptPrefix + "s" - var baseChallenge [][]byte - for stepI, step := range res.schedule { - switch s := step.(type) { - case gkrcore.SkipLevel: - // No sumcheck; claims propagate at existing evaluation points. - // TODO: implement claim passthrough for SkipLevel - _ = s - case gkrcore.SumcheckLevel: - if len(s) > 1 { - return nil, errors.New("batching multiple claim groups in one SumcheckLevel is not yet implemented") - } - // Seed output claims for all wires in this step. - for _, g := range s { - for _, wireI := range g.Wires { - if c[wireI].IsOutput() { - res.add(wireI, firstChallenge, assignment[wireI].Evaluate(firstChallenge, res.memPool)) - } - } - } + for levelI, level := range schedule { + switch s := level.(type) { + case constraint.GkrSkipLevel: + res.levelPoints[levelI] = res.levelPoints[s.ClaimSources[0]] - wireI := s[0].Wires[0] - wire := c[wireI] - claim := res.getClaim(wireI) - if wire.NoProof() { - proof[stepI] = sumcheckProof{ - partialSumPolys: []polynomial.Polynomial{}, - finalEvalProof: []fr.Element{}, - } - } else { - if proof[stepI], err = sumcheckProve( - claim, fiatshamir.WithTranscript(o.transcript, stepPrefix+strconv.Itoa(stepI)+".", baseChallenge...), - ); err != nil { - return proof, err - } - baseChallenge = make([][]byte, len(proof[stepI].finalEvalProof)) - for j := range proof[stepI].finalEvalProof { - baseChallenge[j] = proof[stepI].finalEvalProof[j].Marshal() - } - } - res.deleteClaim(wireI) + case constraint.GkrSumcheckLevel: + proof[levelI] = res.proveLevel(levelI, &t) + // Bind finalEvalProof for next level's challenge derivation + t.bind(proof[levelI].finalEvalProof...) } } return proof, nil } -// Verify the consistency of the claimed output with the claimed input -// Unlike in Prove, the assignment argument need not be complete -func Verify(c Circuit, schedule gkrcore.ProvingSchedule, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { - o, schedule, err := setup(c, schedule, assignment, transcriptSettings, options...) +// Verify the consistency of the claimed output with the claimed input. +// Unlike in Prove, the assignment argument need not be complete. +func Verify(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, proof Proof, hasher hash.Hash, options ...Option) error { + o, err := setup(c, schedule, assignment, hasher, options...) if err != nil { return err } defer o.workers.Stop() res := newResources(c, assignment, o, schedule) + t := transcript{h: hasher} - var firstChallenge []fr.Element - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return err + // Derive initial challenge point + firstChallenge := make([]fr.Element, o.nbVars) + for j := range o.nbVars { + firstChallenge[j] = t.getChallenge() } + res.levelPoints[len(schedule)] = firstChallenge - stepPrefix := o.transcriptPrefix + "s" - var baseChallenge [][]byte - for stepI, step := range res.schedule { - switch s := step.(type) { - case gkrcore.SkipLevel: - // TODO: implement claim passthrough for SkipLevel - _ = s - case gkrcore.SumcheckLevel: - if len(s) > 1 { - return fmt.Errorf("batching multiple claim groups in one SumcheckLevel is not yet implemented") - } - // Seed output claims for all wires in this step. - for _, g := range s { - for _, wireI := range g.Wires { - if c[wireI].IsOutput() { - res.add(wireI, firstChallenge, assignment[wireI].Evaluate(firstChallenge, res.memPool)) - } - } - } + for levelI, level := range schedule { + switch s := level.(type) { + case constraint.GkrSkipLevel: + res.levelPoints[levelI] = res.levelPoints[s.ClaimSources[0]] - wireI := s[0].Wires[0] - wire := c[wireI] - proofW := proof[stepI] - claim := res.getLazyClaim(wireI) - if wire.NoProof() { - if len(proofW.finalEvalProof) != 0 || len(proofW.partialSumPolys) != 0 { - return errors.New("no proof allowed for input wire with a single claim") - } - if wire.NbClaims() == 1 { - if len(claim.evaluationPoints) == 0 || len(claim.claimedEvaluations) == 0 { - return errors.New("missing input wire claim") - } - evaluation := assignment[wireI].Evaluate(claim.evaluationPoints[0], res.memPool) - if !claim.claimedEvaluations[0].Equal(&evaluation) { - return errors.New("incorrect input wire claim") - } - } - } else if err = sumcheckVerify( - claim, proof[stepI], fiatshamir.WithTranscript(o.transcript, stepPrefix+strconv.Itoa(stepI)+".", baseChallenge...), - ); err == nil { - baseChallenge = make([][]byte, len(proofW.finalEvalProof)) - for j := range baseChallenge { - baseChallenge[j] = proofW.finalEvalProof[j].Marshal() - } - } else { + case constraint.GkrSumcheckLevel: + if err = res.verifyLevel(levelI, proof, &t); err != nil { return fmt.Errorf("sumcheck proof rejected: %v", err) } - res.deleteClaim(wireI) + // Bind finalEvalProof for next level's challenge derivation + t.bind(proof[levelI].finalEvalProof...) } } return nil diff --git a/internal/gkr/gkrcore/schedule.go b/internal/gkr/gkrcore/schedule.go index d27c0a6c23..ba24736e7e 100644 --- a/internal/gkr/gkrcore/schedule.go +++ b/internal/gkr/gkrcore/schedule.go @@ -278,6 +278,26 @@ func mirror(s []int, n int) { } } +// BuildFinalEvalPositions returns, for each wire, its position in each source level's +// UniqueGateInputs. Indexed as [wireI][evalI] → position in finalEvalProof. +// Claim sources referencing the initial challenge (index len(schedule)) have no entry; +// they are always first in descending ClaimSources and handled separately by the verifier. +// NB! This function assumes all ClaimSources slices are in decreasing order. +func BuildFinalEvalPositions[G any](schedule constraint.GkrProvingSchedule, c Circuit[G]) [][]int { + positions := make([][]int, len(c)) + for i := len(schedule) - 1; i >= 0; i-- { + sc, ok := schedule[i].(constraint.GkrSumcheckLevel) + if !ok { + continue + } + uniqueInputs := UniqueGateInputs(sc, c) + for j, wI := range uniqueInputs { + positions[wI] = append(positions[wI], j) + } + } + return positions +} + // DefaultProvingSchedule generates a schedule that greedily batches input wires with the same // single claim source into the same GkrSkipLevel. Non-input wires, and input wires with multiple // claim sources, each get their own GkrSumcheckLevel. @@ -338,4 +358,4 @@ func BasicProvingSchedule(c SerializableCircuit) (constraint.GkrProvingSchedule, } } return b.finalize() -} \ No newline at end of file +} diff --git a/internal/gkr/gkrcore/schedule_test.go b/internal/gkr/gkrcore/schedule_test.go index 877551ba9e..a4116a3bc6 100644 --- a/internal/gkr/gkrcore/schedule_test.go +++ b/internal/gkr/gkrcore/schedule_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/stretchr/testify/require" @@ -33,49 +34,49 @@ func TestDefaultProvingSchedulePoseidon2(t *testing.T) { // The two s-boxes of a full round share identical claim sources and are batched together. // Full-round 1 s-boxes (wires 8, 9) feed three downstream sumchecks: partial-round 0 s-box // (level 8), partial-round 1 s-box (level 11), and full-round 2 s-boxes (level 13). - expected := gkrcore.ProvingSchedule{ + expected := constraint.GkrProvingSchedule{ // Level 0: input[0] — single claim source: full-round 0 s-boxes at level 3. - gkrcore.SkipLevel{Wires: []int{0}, ClaimSources: []int{3}}, + constraint.GkrSkipLevel{Wires: []int{0}, ClaimSources: []int{3}}, // Level 1: input[1] — feeds full-round 0 s-boxes (level 3) and the feed-forward (level 17 = initial challenge). - gkrcore.SkipLevel{Wires: []int{1}, ClaimSources: []int{17, 3}}, + constraint.GkrSkipLevel{Wires: []int{1}, ClaimSources: []int{17, 3}}, // Level 2: full-round 0 lin1+lin0 (degree 1, batched) — claim source: full-round 0 s-boxes at level 3. - gkrcore.SkipLevel{Wires: []int{3, 2}, ClaimSources: []int{3}}, + constraint.GkrSkipLevel{Wires: []int{3, 2}, ClaimSources: []int{3}}, // Level 3: full-round 0 sBox1+sBox0 (degree 2, batched) — claim source: full-round 1 s-boxes at level 5. - gkrcore.SumcheckLevel{{Wires: []int{5, 4}, ClaimSources: []int{5}}}, + constraint.GkrSumcheckLevel{{Wires: []int{5, 4}, ClaimSources: []int{5}}}, // Level 4: full-round 1 lin1+lin0 (degree 1, batched) — claim source: full-round 1 s-boxes at level 5. - gkrcore.SkipLevel{Wires: []int{7, 6}, ClaimSources: []int{5}}, + constraint.GkrSkipLevel{Wires: []int{7, 6}, ClaimSources: []int{5}}, // Level 5: full-round 1 sBox1+sBox0 (degree 2, batched) — three downstream sumchecks. - gkrcore.SumcheckLevel{{Wires: []int{9, 8}, ClaimSources: []int{13, 11, 8}}}, + constraint.GkrSumcheckLevel{{Wires: []int{9, 8}, ClaimSources: []int{13, 11, 8}}}, // Level 6: partial-round 0 lin0 (degree 1) — claim source: partial-round 0 s-box at level 8. - gkrcore.SkipLevel{Wires: []int{10}, ClaimSources: []int{8}}, + constraint.GkrSkipLevel{Wires: []int{10}, ClaimSources: []int{8}}, // Level 7: partial-round 0 lin1 (degree 1) — feeds partial-round 1 s-box (level 11) and full-round 2 s-boxes (level 13). - gkrcore.SkipLevel{Wires: []int{11}, ClaimSources: []int{13, 11}}, + constraint.GkrSkipLevel{Wires: []int{11}, ClaimSources: []int{13, 11}}, // Level 8: partial-round 0 sBox0 (degree 2) — feeds partial-round 1 lin0+lin1. - gkrcore.SumcheckLevel{{Wires: []int{12}, ClaimSources: []int{13, 11}}}, + constraint.GkrSumcheckLevel{{Wires: []int{12}, ClaimSources: []int{13, 11}}}, // Level 9: partial-round 1 lin0 (degree 1) — claim source: partial-round 1 s-box at level 11. - gkrcore.SkipLevel{Wires: []int{13}, ClaimSources: []int{11}}, + constraint.GkrSkipLevel{Wires: []int{13}, ClaimSources: []int{11}}, // Level 10: partial-round 1 lin1 (degree 1) — claim source: full-round 2 s-boxes at level 13. - gkrcore.SkipLevel{Wires: []int{14}, ClaimSources: []int{13}}, + constraint.GkrSkipLevel{Wires: []int{14}, ClaimSources: []int{13}}, // Level 11: partial-round 1 sBox0 (degree 2) — claim source: full-round 2 s-boxes at level 13. - gkrcore.SumcheckLevel{{Wires: []int{15}, ClaimSources: []int{13}}}, + constraint.GkrSumcheckLevel{{Wires: []int{15}, ClaimSources: []int{13}}}, // Level 12: full-round 2 lin1+lin0 (degree 1, batched) — claim source: full-round 2 s-boxes at level 13. - gkrcore.SkipLevel{Wires: []int{17, 16}, ClaimSources: []int{13}}, + constraint.GkrSkipLevel{Wires: []int{17, 16}, ClaimSources: []int{13}}, // Level 13: full-round 2 sBox1+sBox0 (degree 2, batched) — claim source: full-round 3 s-boxes at level 15. - gkrcore.SumcheckLevel{{Wires: []int{19, 18}, ClaimSources: []int{15}}}, + constraint.GkrSumcheckLevel{{Wires: []int{19, 18}, ClaimSources: []int{15}}}, // Level 14: full-round 3 lin1+lin0 (degree 1, batched) — claim source: full-round 3 s-boxes at level 15. - gkrcore.SkipLevel{Wires: []int{21, 20}, ClaimSources: []int{15}}, + constraint.GkrSkipLevel{Wires: []int{21, 20}, ClaimSources: []int{15}}, // Level 15: full-round 3 sBox1+sBox0 (degree 2, batched) — claim source: initial challenge (17). - gkrcore.SumcheckLevel{{Wires: []int{23, 22}, ClaimSources: []int{17}}}, + constraint.GkrSumcheckLevel{{Wires: []int{23, 22}, ClaimSources: []int{17}}}, // Level 16: feed-forward output (degree 1) — claim source: initial challenge (17). - gkrcore.SkipLevel{Wires: []int{24}, ClaimSources: []int{17}}, + constraint.GkrSkipLevel{Wires: []int{24}, ClaimSources: []int{17}}, } require.Equal(t, expected, schedule) } @@ -87,12 +88,12 @@ func TestBasicProvingSchedule(t *testing.T) { // SingleMulGateCircuit has wires 0, 1 (inputs) and 2 (mul gate). // Claim source indices refer to levels; 3 = len(schedule) = initial challenge. - require.Equal(t, gkrcore.ProvingSchedule{ + require.Equal(t, constraint.GkrProvingSchedule{ // Level 0: input[0] — claim source: mul gate at level 2. - gkrcore.SkipLevel{Wires: []int{0}, ClaimSources: []int{2}}, + constraint.GkrSkipLevel{Wires: []int{0}, ClaimSources: []int{2}}, // Level 1: input[1] — claim source: mul gate at level 2. - gkrcore.SkipLevel{Wires: []int{1}, ClaimSources: []int{2}}, + constraint.GkrSkipLevel{Wires: []int{1}, ClaimSources: []int{2}}, // Level 2: mul gate output (degree 2) — claim source: initial challenge (3). - gkrcore.SumcheckLevel{{Wires: []int{2}, ClaimSources: []int{3}}}, + constraint.GkrSumcheckLevel{{Wires: []int{2}, ClaimSources: []int{3}}}, }, schedule) } From 8d8c282b958acd86f825e8c286e3174762eace9c Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Tue, 10 Mar 2026 18:51:53 -0500 Subject: [PATCH 224/251] test: sumcheck --- internal/gkr/bn254/sumcheck_test.go | 38 +++++++++------------------ internal/gkr/gkrtesting/gkrtesting.go | 13 ++++----- internal/utils/slices.go | 9 ------- 3 files changed, 19 insertions(+), 41 deletions(-) diff --git a/internal/gkr/bn254/sumcheck_test.go b/internal/gkr/bn254/sumcheck_test.go index 5f68f7c039..15cff2d307 100644 --- a/internal/gkr/bn254/sumcheck_test.go +++ b/internal/gkr/bn254/sumcheck_test.go @@ -10,7 +10,6 @@ import ( "hash" "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/stretchr/testify/assert" "math/bits" @@ -28,11 +27,9 @@ func testSumcheckSingleClaimMultilin(polyInt []uint64, hashGenerator func() hash } claim := singleMultilinClaim{g: poly.Clone()} + t := transcript{h: hashGenerator()} - proof, err := sumcheckProve(&claim, fiatshamir.WithHash(hashGenerator())) - if err != nil { - return err - } + proof := sumcheckProve(&claim, &t) var sb strings.Builder for _, p := range proof.partialSumPolys { @@ -48,13 +45,15 @@ func testSumcheckSingleClaimMultilin(polyInt []uint64, hashGenerator func() hash } lazyClaim := singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if err = sumcheckVerify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())); err != nil { + t = transcript{h: hashGenerator()} + if err := sumcheckVerify(lazyClaim, proof, lazyClaim.claimedSum, 1, &t); err != nil { return err } proof.partialSumPolys[0][0].Add(&proof.partialSumPolys[0][0], toElement(1)) lazyClaim = singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if sumcheckVerify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())) == nil { + t = transcript{h: hashGenerator()} + if sumcheckVerify(lazyClaim, proof, lazyClaim.claimedSum, 1, &t) == nil { return fmt.Errorf("bad proof accepted") } return nil @@ -93,18 +92,14 @@ type singleMultilinClaim struct { g polynomial.MultiLin } -func (c singleMultilinClaim) proveFinalEval(r []fr.Element) []fr.Element { +func (c *singleMultilinClaim) proveFinalEval(r []fr.Element) []fr.Element { return nil // verifier can compute the final eval itself } -func (c singleMultilinClaim) varsNum() int { +func (c *singleMultilinClaim) varsNum() int { return bits.TrailingZeros(uint(len(c.g))) } -func (c singleMultilinClaim) claimsNum() int { - return 1 -} - func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { sum := g[len(g)/2] for i := len(g)/2 + 1; i < len(g); i++ { @@ -113,13 +108,12 @@ func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { return []fr.Element{sum} } -func (c singleMultilinClaim) fold(fr.Element) polynomial.Polynomial { +func (c *singleMultilinClaim) roundPolynomial() polynomial.Polynomial { return sumForX1One(c.g) } -func (c *singleMultilinClaim) next(r fr.Element) polynomial.Polynomial { +func (c *singleMultilinClaim) roundFold(r fr.Element) { c.g.Fold(r) - return sumForX1One(c.g) } type singleMultilinLazyClaim struct { @@ -127,7 +121,7 @@ type singleMultilinLazyClaim struct { claimedSum fr.Element } -func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, _ fr.Element, purportedValue fr.Element, proof []fr.Element) error { +func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, purportedValue fr.Element, proof []fr.Element) error { val := c.g.Evaluate(r, nil) if val.Equal(&purportedValue) { return nil @@ -135,15 +129,7 @@ func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, _ fr.Element, p return fmt.Errorf("mismatch") } -func (c singleMultilinLazyClaim) foldedSum(_ fr.Element) fr.Element { - return c.claimedSum -} - -func (c singleMultilinLazyClaim) degree(i int) int { - return 1 -} - -func (c singleMultilinLazyClaim) claimsNum() int { +func (c singleMultilinLazyClaim) degree(int) int { return 1 } diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index e66a6f2e7a..556a33df04 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -8,6 +8,7 @@ import ( "path/filepath" "sync" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/consensys/gnark/std/gkrapi/gkr" @@ -166,7 +167,7 @@ type TestCaseInfo struct { // ScheduleStepInfo is the JSON representation of a ProvingLevel with a type discriminator. type ScheduleStepInfo struct { Type string `json:"type"` - Groups []gkrcore.ClaimGroup `json:"groups,omitempty"` // for SumcheckLevel + Groups []constraint.GkrClaimGroup `json:"groups,omitempty"` // for SumcheckLevel Wires []int `json:"wires,omitempty"` // for SkipLevel Claims []int `json:"claimSources,omitempty"` // for SkipLevel } @@ -176,21 +177,21 @@ type ScheduleInfo []ScheduleStepInfo // ToProvingSchedule converts a ScheduleInfo to a gkrcore.ProvingSchedule. // A nil ScheduleInfo returns nil, which callers should interpret as BasicProvingSchedule. -func (p ScheduleInfo) ToProvingSchedule() (gkrcore.ProvingSchedule, error) { +func (p ScheduleInfo) ToProvingSchedule() (constraint.GkrProvingSchedule, error) { if p == nil { return nil, nil } - s := make(gkrcore.ProvingSchedule, len(p)) + s := make(constraint.GkrProvingSchedule, len(p)) for i, step := range p { switch step.Type { case "sumcheck": groups := step.Groups if groups == nil { - groups = []gkrcore.ClaimGroup{} + groups = []constraint.GkrClaimGroup{} } - s[i] = gkrcore.SumcheckLevel(groups) + s[i] = constraint.GkrSumcheckLevel(groups) case "skip": - s[i] = gkrcore.SkipLevel{Wires: step.Wires, ClaimSources: step.Claims} + s[i] = constraint.GkrSkipLevel{Wires: step.Wires, ClaimSources: step.Claims} default: return nil, errors.New("unknown ProvingLevel type: " + step.Type) } diff --git a/internal/utils/slices.go b/internal/utils/slices.go index bdd86119fa..f6fcd943d6 100644 --- a/internal/utils/slices.go +++ b/internal/utils/slices.go @@ -8,15 +8,6 @@ func AppendRefs[T any](s []any, v []T) []any { return s } -// References returns a slice of references to the elements of v. -func References[T any](v []T) []*T { - res := make([]*T, len(v)) - for i := range v { - res[i] = &v[i] - } - return res -} - // ExtendRepeatLast extends a non-empty slice s by repeating the last element until it reaches the length n. func ExtendRepeatLast[T any](s []T, n int) []T { if n <= len(s) { From 68c7cdd4a04e68d32d6f00e73f864e30b24101d1 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Tue, 10 Mar 2026 20:52:53 -0500 Subject: [PATCH 225/251] most tests passing --- internal/gkr/bn254/gkr.go | 134 +++++++++++++------------------------- 1 file changed, 46 insertions(+), 88 deletions(-) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index f07c1f5e35..77507c4ce8 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -53,7 +53,7 @@ func (e *zeroCheckLazyClaims) degree(int) int { // verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. // The sumcheck protocol has already reduced the per-wire claims w(xᵢ) = yᵢ to verifying // ∑ cⁱ eq(xᵢ, r) · gateᵥ(r) = purportedValue, where the sum runs over all -// (wire v, claim source s) pairs and c is foldingCoeff. +// claims on each wire and c is foldingCoeff. // Both purportedValue and the vector r have been randomized during sumcheck. // // For input wires, gateᵥ(r) is computed directly from the assignment. @@ -74,7 +74,7 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. var gateEval fr.Element if wire.IsInput() { - gateEval = e.resources.assignment[wI].Evaluate(r, e.resources.memPool) + gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) } else { evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, v := range perWireInputEvals[flatW] { @@ -294,34 +294,42 @@ type resources struct { evalPositions [][]int // [wireI][evalI] → position in source level's finalEvalProof nbVars int assignment WireAssignment - memPool *polynomial.Pool + memPool polynomial.Pool workers *utils.WorkerPool circuit Circuit schedule constraint.GkrProvingSchedule + transcript transcript } -func newResources(circuit Circuit, assignment WireAssignment, o settings, schedule constraint.GkrProvingSchedule) resources { +func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (resources, error) { + nbVars := assignment.NumVars() + nbInstances := assignment.NumInstances() + if 1<= 2 { - foldingCoeff = t.getChallenge() + foldingCoeff = r.transcript.getChallenge() } uniqueInputs, inputIndices := gkrcore.InputMapping(level, r.circuit) @@ -344,7 +352,7 @@ func (r *resources) proveLevel(levelI int, t *transcript) sumcheckProof { if wire.IsInput() { gate = gkrcore.IdentityBytecode() } - pools[flatW] = newGateEvaluatorPool(gate, len(inputIndices[flatW]), r.memPool) + pools[flatW] = newGateEvaluatorPool(gate, len(inputIndices[flatW]), &r.memPool) flatW++ } } @@ -403,19 +411,19 @@ func (r *resources) proveLevel(levelI int, t *transcript) sumcheckProof { eqs: eqs, gateEvaluatorPools: pools, } - return sumcheckProve(claims, t) + return sumcheckProve(claims, &r.transcript) } // verifyLevel runs the sumcheck verification for a SumcheckLevel: derives the fold challenge, // computes the claimed sum, and calls sumcheckVerify. -func (r *resources) verifyLevel(levelI int, proof Proof, t *transcript) error { +func (r *resources) verifyLevel(levelI int, proof Proof) error { level := r.schedule[levelI].(constraint.GkrSumcheckLevel) initialChallengeI := len(r.schedule) nbClaims := gkrcore.NbClaims(level) var foldingCoeff fr.Element if nbClaims >= 2 { - foldingCoeff = t.getChallenge() + foldingCoeff = r.transcript.getChallenge() } var ys []fr.Element @@ -425,7 +433,7 @@ func (r *resources) verifyLevel(levelI int, proof Proof, t *transcript) error { for _, src := range group.ClaimSources { var y fr.Element if src == initialChallengeI { - y = r.assignment[wI].Evaluate(r.levelPoints[src], r.memPool) + y = r.assignment[wI].Evaluate(r.levelPoints[src], &r.memPool) } else { y = proof[src].finalEvalProof[r.evalPositions[wI][evalI]] evalI++ @@ -443,82 +451,35 @@ func (r *resources) verifyLevel(levelI int, proof Proof, t *transcript) error { resources: r, levelI: levelI, } - return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, gkrcore.Degree(level, r.circuit), t) -} - -type settings struct { - pool *polynomial.Pool - nbVars int - workers *utils.WorkerPool -} - -type Option func(*settings) - -func WithPool(pool *polynomial.Pool) Option { - return func(options *settings) { - options.pool = pool - } -} - -func WithWorkers(workers *utils.WorkerPool) Option { - return func(options *settings) { - options.workers = workers - } -} - -func setup(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash, options ...Option) (settings, error) { - var o settings - for _, option := range options { - option(&o) - } - - o.nbVars = assignment.NumVars() - nbInstances := assignment.NumInstances() - if 1< Date: Tue, 10 Mar 2026 20:57:14 -0500 Subject: [PATCH 226/251] non-vector tests pass --- internal/gkr/bn254/gkr.go | 8 +- internal/gkr/bn254/gkr_test.go | 179 ++++++++++++++++---------- internal/gkr/gkrcore/schedule.go | 22 ---- internal/gkr/gkrcore/schedule_test.go | 4 +- internal/gkr/gkrtesting/gkrtesting.go | 4 +- 5 files changed, 119 insertions(+), 98 deletions(-) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 77507c4ce8..f087e519be 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -471,8 +471,8 @@ func Prove(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAss } r.levelPoints[len(schedule)] = firstChallenge - for levelI, level := range schedule { - switch s := level.(type) { + for levelI := len(schedule) - 1; levelI >= 0; levelI-- { + switch s := schedule[levelI].(type) { case constraint.GkrSkipLevel: r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] @@ -502,8 +502,8 @@ func Verify(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAs } r.levelPoints[len(schedule)] = firstChallenge - for levelI, level := range schedule { - switch s := level.(type) { + for levelI := len(schedule) - 1; levelI >= 0; levelI-- { + switch s := schedule[levelI].(type) { case constraint.GkrSkipLevel: r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index c6fff29732..3116515156 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -19,8 +19,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bn254/fr" "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - gcUtils "github.com/consensys/gnark-crypto/utils" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/stretchr/testify/assert" @@ -69,36 +68,82 @@ func TestMimc(t *testing.T) { test(t, gkrtesting.MiMCCircuit(93)) } -func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - // Construct SerializableCircuit directly, bypassing CompileCircuit - // which would reset NbUniqueOutputs based on actual topology - circuit := gkrcore.SerializableCircuit{ - { - NbUniqueOutputs: 2, - Gate: gkrcore.SerializableGate{Degree: 1}, - }, - } +// testLevel exercises proveLevel/verifyLevel for a single sumcheck level. +func testLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrSumcheckLevel) { + t.Helper() + _, sCircuit := cache.Compile(t, circuit) - assignment := WireAssignment{[]fr.Element{two, three}} - var o settings - pool := polynomial.NewPool(256, 1<<11) - workers := gcUtils.NewWorkerPool() - o.pool = &pool - o.workers = workers - - resourcesGen := func() *resources { - manager := newResources(circuit, assignment, o, nil) - manager.add(0, []fr.Element{three}, five) - manager.add(0, []fr.Element{four}, six) - return &manager + ins := sCircuit.Inputs() + assignment := make(WireAssignment, len(sCircuit)) + for _, i := range ins { + assignment[i] = make([]fr.Element, 2) + fr.Vector(assignment[i]).MustSetRandom() } - transcriptGen := newMessageCounterGenerator(4, 1) + assignment.Complete(sCircuit) + + schedule := constraint.GkrProvingSchedule{level} + challenge := []fr.Element{five} - proof, err := sumcheckProve(resourcesGen().getClaim(0), fiatshamir.WithHash(transcriptGen(), nil)) + // Prove + proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) - err = sumcheckVerify(resourcesGen().getLazyClaim(0), proof, fiatshamir.WithHash(transcriptGen(), nil)) + defer proveR.workers.Stop() + + proveR.levelPoints[1] = challenge + proof := Proof{proveR.proveLevel(0)} + + // Verify + verifyR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) + defer verifyR.workers.Stop() + + verifyR.levelPoints[1] = challenge + assert.NoError(t, verifyR.verifyLevel(0, proof)) +} + +func TestLevel(t *testing.T) { + // Wires 0,1 = inputs; wires 2,3,4 = mul(0,1). All gates are independent outputs. + circuit := gkrcore.RawCircuit{ + {}, + {}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, + } + // All levels have initial challenge at index 1 (len(schedule) = 1). + tests := []struct { + name string + level constraint.GkrSumcheckLevel + }{ + { + name: "single wire", + level: constraint.GkrSumcheckLevel{{Wires: []int{4}, ClaimSources: []int{1}}}, + }, + { + name: "two groups", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []int{1}}, + {Wires: []int{3}, ClaimSources: []int{1}}, + }, + }, + { + name: "one group with two wires", + level: constraint.GkrSumcheckLevel{{Wires: []int{4, 3}, ClaimSources: []int{1}}}, + }, + { + name: "mixed: single + multi-wire group", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []int{1}}, + {Wires: []int{3, 2}, ClaimSources: []int{1}}, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testLevel(t, circuit, tc.level) + }) + } } var one, two, three, four, five, six fr.Element @@ -136,8 +181,13 @@ func test(t *testing.T, circuit gkrcore.RawCircuit) { testWithSchedule(t, circuit, nil) } -func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule gkrcore.ProvingSchedule) { +func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constraint.GkrProvingSchedule) { gCircuit, sCircuit := cache.Compile(t, circuit) + if schedule == nil { + var err error + schedule, err = gkrcore.DefaultProvingSchedule(sCircuit) + assert.NoError(t, err) + } ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -157,19 +207,19 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule gkrcore t.Log("Selected inputs for test") - proof, err := Prove(sCircuit, schedule, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(sCircuit, schedule, fullAssignment, newMessageCounter(1, 1)) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(sCircuit, schedule, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(1, 1)) assert.NoError(t, err, "proof rejected") if proof.isEmpty() { // special case for TestNoGate: continue // there's no way to make a trivial proof fail } - err = Verify(sCircuit, schedule, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) + err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(0, 1)) assert.NotNil(t, err, "bad proof accepted") } } @@ -191,14 +241,17 @@ func (p Proof) isEmpty() bool { func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { _, c := cache.Compile(t, gkrtesting.NoGateCircuit()) + schedule, err := gkrcore.DefaultProvingSchedule(c) + assert.NoError(t, err) + assignment := WireAssignment{0: inputAssignments[0]} - proof, err := Prove(c, nil, assignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(c, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(c, nil, assignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(c, schedule, assignment, proof, newMessageCounter(1, 1)) assert.NoError(t, err, "proof rejected") } @@ -206,7 +259,7 @@ func generateTestProver(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) assert.NoError(t, err) - proof, err := Prove(testCase.Circuit, testCase.Schedule, testCase.FullAssignment, fiatshamir.WithHash(testCase.Hash)) + proof, err := Prove(testCase.Circuit, testCase.Schedule, testCase.FullAssignment, testCase.Hash) assert.NoError(t, err) assert.NoError(t, proofEquals(testCase.Proof, proof)) } @@ -216,11 +269,11 @@ func generateTestVerifier(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(testCase.Hash)) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) assert.NoError(t, err, "proof rejected") testCase, err = newTestCase(path) assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(newMessageCounter(2, 0))) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, newMessageCounter(2, 0)) assert.NotNil(t, err, "bad proof accepted") } } @@ -271,6 +324,9 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") _, c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) + schedule, err := gkrcore.DefaultProvingSchedule(c) + assert.NoError(b, err) + in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) fr.Vector(in0).MustSetRandom() @@ -285,7 +341,7 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { //b.ResetTimer() fmt.Println("constructing proof") start = time.Now().UnixMicro() - _, err := Prove(c, nil, assignment, fiatshamir.WithHash(mimc.NewMiMC())) + _, err = Prove(c, schedule, assignment, mimc.NewMiMC()) proved := time.Now().UnixMicro() - start fmt.Println("proved in", proved, "μs") assert.NoError(b, err) @@ -299,39 +355,16 @@ func TestSingleMulGateExplicitSchedule(t *testing.T) { // Wire 2 is the mul gate output (inputs: 0, 1). // Explicit schedule: one SumcheckLevel for wire 2 only. - // Wire 2 has NbUniqueOutputs=0 (it's the output), so NbClaims=1. - schedule := gkrcore.ProvingSchedule{ - gkrcore.SumcheckLevel{ - {Wires: []int{2}, ClaimSources: nil}, + // ClaimSources: [1] = initial challenge (index len(schedule) = 1). + schedule := constraint.GkrProvingSchedule{ + constraint.GkrSumcheckLevel{ + {Wires: []int{2}, ClaimSources: []int{1}}, }, } testWithSchedule(t, circuit, schedule) _ = sCircuit } -// TestMiMCBatchedLayers tests MiMC with a layered schedule that groups all round wires -// into a single SumcheckLevel with multiple ClaimGroups, exercising multi-wire batching. -// This test will pass once the Prove/Verify loops implement multi-group SumcheckLevel handling. -func TestMiMCBatchedLayers(t *testing.T) { - const depth = 4 - circuit := gkrtesting.MiMCCircuit(depth) - _, sCircuit := cache.Compile(t, circuit) - - // MiMC circuit structure: wire 0 = key (input), wire 1 = plaintext (input), - // wires 2..depth+1 = round outputs. Each round wire i has inputs [i-1, 0]. - // All round wires are independent (each depends only on the previous round and the key), - // so each is its own layer. Batch them all into a single SumcheckLevel to test - // multi-ClaimGroup batching. - groups := make(gkrcore.SumcheckLevel, depth) - for i := range depth { - groups[i] = gkrcore.ClaimGroup{Wires: []int{depth + 1 - i}} - } - schedule := gkrcore.ProvingSchedule{groups} - - testWithSchedule(t, circuit, schedule) - _ = sCircuit -} - func BenchmarkGkrMimc19(b *testing.B) { benchmarkGkrMiMC(b, 1<<19, 91) } @@ -375,7 +408,7 @@ type TestCase struct { Proof Proof FullAssignment WireAssignment InOutAssignment WireAssignment - Schedule gkrcore.ProvingSchedule // nil means BasicProvingSchedule + Schedule constraint.GkrProvingSchedule } var testCases = make(map[string]*TestCase) @@ -406,10 +439,20 @@ func newTestCase(path string) (*TestCase, error) { if proof, err = unmarshalProof(info.Proof); err != nil { return nil, err } - var schedule gkrcore.ProvingSchedule + var schedule constraint.GkrProvingSchedule if schedule, err = info.Schedule.ToProvingSchedule(); err != nil { return nil, err } + if schedule == nil { + if schedule, err = gkrcore.DefaultProvingSchedule(circuit); err != nil { + return nil, err + } + } + + outputSet := make(map[int]bool, len(circuit)) + for _, o := range circuit.Outputs() { + outputSet[o] = true + } fullAssignment := make(WireAssignment, len(circuit)) inOutAssignment := make(WireAssignment, len(circuit)) @@ -423,7 +466,7 @@ func newTestCase(path string) (*TestCase, error) { } assignmentRaw = info.Input[inI] inI++ - } else if circuit[i].IsOutput() { + } else if outputSet[i] { if outI == len(info.Output) { return nil, fmt.Errorf("fewer output in vector than in circuit") } @@ -444,7 +487,7 @@ func newTestCase(path string) (*TestCase, error) { fullAssignment.Complete(circuit) for i := range circuit { - if circuit[i].IsOutput() { + if outputSet[i] { if err = sliceEquals(inOutAssignment[i], fullAssignment[i]); err != nil { return nil, fmt.Errorf("assignment mismatch: %v", err) } @@ -457,7 +500,7 @@ func newTestCase(path string) (*TestCase, error) { Proof: proof, Hash: _hash, Circuit: circuit, - Schedule: schedule, // nil if absent from JSON → BasicProvingSchedule + Schedule: schedule, } testCases[path] = tCase diff --git a/internal/gkr/gkrcore/schedule.go b/internal/gkr/gkrcore/schedule.go index ba24736e7e..d3838db6b7 100644 --- a/internal/gkr/gkrcore/schedule.go +++ b/internal/gkr/gkrcore/schedule.go @@ -337,25 +337,3 @@ func DefaultProvingSchedule(c SerializableCircuit) (constraint.GkrProvingSchedul return b.finalize() } -// BasicProvingSchedule generates a schedule for a circuit where every wire gets its own level: -// Non-input wires get a GkrSumcheckLevel. Input wires with multiple consumers get a GkrSumcheckLevel -// to consolidate claims; input wires with a single consumer get a GkrSkipLevel. -func BasicProvingSchedule(c SerializableCircuit) (constraint.GkrProvingSchedule, error) { - b := newScheduleBuilder(c) - for wI := len(c) - 1; wI >= 0; wI-- { - src, ready := b.claimSources(wI) - if !ready { - return nil, fmt.Errorf("circuit is not topologically sorted: wire %d not ready", wI) - } - var err error - if !c[wI].IsInput() || len(src) > 1 { - err = b.addSumcheckLevel([]int{wI}) - } else { - err = b.addSkipLevel([]int{wI}) - } - if err != nil { - return nil, err - } - } - return b.finalize() -} diff --git a/internal/gkr/gkrcore/schedule_test.go b/internal/gkr/gkrcore/schedule_test.go index a4116a3bc6..3481726a1a 100644 --- a/internal/gkr/gkrcore/schedule_test.go +++ b/internal/gkr/gkrcore/schedule_test.go @@ -81,9 +81,9 @@ func TestDefaultProvingSchedulePoseidon2(t *testing.T) { require.Equal(t, expected, schedule) } -func TestBasicProvingSchedule(t *testing.T) { +func TestDefaultProvingSchedule(t *testing.T) { _, c := scheduleTestCache.Compile(t, gkrtesting.SingleMulGateCircuit()) - schedule, err := gkrcore.BasicProvingSchedule(c) + schedule, err := gkrcore.DefaultProvingSchedule(c) require.NoError(t, err) // SingleMulGateCircuit has wires 0, 1 (inputs) and 2 (mul gate). diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 556a33df04..6f6ea7c4be 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -154,7 +154,7 @@ type PrintableSumcheckProof struct { type HashDescription map[string]interface{} // TestCaseInfo is the serializable form of a GKR test case, matching the JSON file format. -// Schedule is nil when absent from JSON, which means BasicProvingSchedule. +// Schedule is nil when absent from JSON, which means DefaultProvingSchedule. type TestCaseInfo struct { Hash HashDescription `json:"hash"` Circuit string `json:"circuit"` @@ -176,7 +176,7 @@ type ScheduleStepInfo struct { type ScheduleInfo []ScheduleStepInfo // ToProvingSchedule converts a ScheduleInfo to a gkrcore.ProvingSchedule. -// A nil ScheduleInfo returns nil, which callers should interpret as BasicProvingSchedule. +// A nil ScheduleInfo returns nil, which callers should interpret as DefaultProvingSchedule. func (p ScheduleInfo) ToProvingSchedule() (constraint.GkrProvingSchedule, error) { if p == nil { return nil, nil From e795d2dbe53ac42a78af3105c6931284266972da Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Tue, 10 Mar 2026 21:56:20 -0500 Subject: [PATCH 227/251] build: generify --- .../backend/template/gkr/blueprint.go.tmpl | 49 +- .../backend/template/gkr/gkr.go.tmpl | 863 ++++++++---------- .../backend/template/gkr/gkr.test.go.tmpl | 183 ++-- .../template/gkr/gkr.test.vectors.gen.go.tmpl | 16 +- .../template/gkr/gkr.test.vectors.go.tmpl | 34 +- .../backend/template/gkr/sumcheck.go.tmpl | 194 ++-- .../template/gkr/sumcheck.test.defs.go.tmpl | 31 +- .../template/gkr/sumcheck.test.go.tmpl | 19 +- .../gkr/sumcheck.test.vectors.gen.go.tmpl | 23 +- internal/gkr/bls12-377/blueprint.go | 22 +- internal/gkr/bls12-377/gkr.go | 831 +++++++---------- internal/gkr/bls12-377/gkr_test.go | 176 +++- internal/gkr/bls12-377/sumcheck.go | 183 ++-- internal/gkr/bls12-377/sumcheck_test.go | 38 +- internal/gkr/bls12-381/blueprint.go | 22 +- internal/gkr/bls12-381/gkr.go | 831 +++++++---------- internal/gkr/bls12-381/gkr_test.go | 176 +++- internal/gkr/bls12-381/sumcheck.go | 183 ++-- internal/gkr/bls12-381/sumcheck_test.go | 38 +- internal/gkr/bn254/blueprint.go | 2 +- internal/gkr/bn254/gkr.go | 12 +- internal/gkr/bn254/gkr_test.go | 30 +- internal/gkr/bw6-761/blueprint.go | 22 +- internal/gkr/bw6-761/gkr.go | 831 +++++++---------- internal/gkr/bw6-761/gkr_test.go | 176 +++- internal/gkr/bw6-761/sumcheck.go | 183 ++-- internal/gkr/bw6-761/sumcheck_test.go | 38 +- internal/gkr/gkrcore/schedule.go | 1 - internal/gkr/gkrtesting/gkrtesting.go | 6 +- internal/gkr/small_rational/gkr.go | 831 +++++++---------- internal/gkr/small_rational/sumcheck.go | 183 ++-- internal/gkr/small_rational/sumcheck_test.go | 13 +- .../sumcheck_test_vector_gen.go | 46 +- .../gkr/small_rational/test_vector_gen.go | 32 +- 34 files changed, 2837 insertions(+), 3481 deletions(-) diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 7a45be56bc..3558aba064 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -8,7 +8,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" "{{ .FieldPackagePath }}" "{{ .FieldPackagePath }}/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" @@ -41,10 +40,13 @@ type BlueprintSolve struct { // Ensures BlueprintSolve implements BlueprintStateful var _ constraint.BlueprintStateful[constraint.U64] = (*BlueprintSolve)(nil) + // Equal returns true if the serialized fields of two BlueprintSolve are equal. // Used for testing serialization round-trips. func (b *BlueprintSolve) Equal(other constraint.BlueprintComparable) bool { - if other == nil { return false } + if other == nil { + return false + } o, ok := other.(*BlueprintSolve) if !ok { return false @@ -107,7 +109,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra if w.IsInput() { val, delta := s.Read(calldata) calldata = calldata[delta:] - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + // Copy directly from constraint.U64 to {{ .ElementType }} (both in Montgomery form) copy(b.assignments[wI][instanceI][:], val[:]) } else { // Get evaluator for this wire from the circuit evaluator @@ -123,7 +125,7 @@ func (b *BlueprintSolve) Solve(s constraint.Solver[constraint.U64], inst constra } } - // Set output wires (copy fr.Element to U64 in Montgomery form) + // Set output wires (copy {{ .ElementType }} to U64 in Montgomery form) for outI, outWI := range b.outputWires { var val constraint.U64 copy(val[:], b.assignments[outWI][instanceI][:]) @@ -150,9 +152,9 @@ func (b *BlueprintSolve) NbConstraints() int { // NbOutputs implements Blueprint func (b *BlueprintSolve) NbOutputs(inst constraint.Instruction) int { - if b.outputWires == nil { - b.outputWires = b.Circuit.Outputs() - } + if b.outputWires == nil { + b.outputWires = b.Circuit.Outputs() + } return len(b.outputWires) } @@ -194,6 +196,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + Schedule constraint.GkrProvingSchedule HashName string lock sync.Mutex @@ -201,9 +204,12 @@ type BlueprintProve struct { // Ensures BlueprintProve implements BlueprintSolvable var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintProve)(nil) + // Equal returns true if the serialized fields of two BlueprintProve are equal. func (b *BlueprintProve) Equal(other constraint.BlueprintComparable) bool { - if other == nil { return false } + if other == nil { + return false + } o, ok := other.(*BlueprintProve) if !ok { return false @@ -243,28 +249,27 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra } } + // Create hasher and write base challenges + hsh := hash.NewHash(b.HashName + "_{{.FieldID}}") + // Read initial challenges from instruction calldata (parse dynamically, no metadata) // Format: [0]=totalSize, [1...]=challenge linear expressions - insBytes := make([][]byte, 0) // first challenges calldata := inst.Calldata[1:] // skip size prefix for len(calldata) != 0 { val, delta := s.Read(calldata) calldata = calldata[delta:] - // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) + // Copy directly from constraint.U64 to {{ .ElementType }} (both in Montgomery form) var challenge {{ .ElementType }} copy(challenge[:], val[:]) - insBytes = append(insBytes, challenge.Marshal()) + challengeBytes := challenge.Bytes() + hsh.Write(challengeBytes[:]) } - // Create Fiat-Shamir settings - hsh := hash.NewHash(b.HashName + "_{{.FieldID}}") - fsSettings := fiatshamir.WithHash(hsh, insBytes...) - // Call the {{.FieldID}}-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, b.Schedule, assignments, hsh) if err != nil { - return fmt.Errorf("{{toLower .FieldID}} prove failed: %w", err) + return fmt.Errorf("{{.FieldID}} prove failed: %w", err) } for i, elem := range proof.flatten() { @@ -292,7 +297,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) + return gkrcore.ProofSize(b.Schedule, b.SolveBlueprint.Circuit, logNbInstances) } // NbOutputs implements Blueprint @@ -344,9 +349,12 @@ type BlueprintGetAssignment struct { // Ensures BlueprintGetAssignment implements BlueprintSolvable var _ constraint.BlueprintSolvable[constraint.U64] = (*BlueprintGetAssignment)(nil) + // Equal returns true if the serialized fields of two BlueprintGetAssignment are equal. func (b *BlueprintGetAssignment) Equal(other constraint.BlueprintComparable) bool { - if other == nil { return false } + if other == nil { + return false + } o, ok := other.(*BlueprintGetAssignment) if !ok { return false @@ -418,7 +426,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for {{.FieldID}} -func NewBlueprints(circuit gkrcore.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrcore.Blueprints { +func NewBlueprints(circuit gkrcore.SerializableCircuit, schedule constraint.GkrProvingSchedule, hashName string, compiler constraint.CustomizableSystem) gkrcore.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) @@ -427,6 +435,7 @@ func NewBlueprints(circuit gkrcore.SerializableCircuit, hashName string, compile prove := &BlueprintProve{ SolveBlueprintID: solveID, SolveBlueprint: solve, + Schedule: schedule, HashName: hashName, } proveID := compiler.AddBlueprint(prove) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 5e2d3396d1..85891ce98b 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -1,13 +1,14 @@ import ( "errors" "fmt" + "hash" "iter" + "sync" + "{{.FieldPackagePath}}" "{{.FieldPackagePath}}/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" - "strconv" - "sync" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" ) @@ -19,639 +20,493 @@ type ( // The goal is to prove/verify evaluations of many instances of the same circuit - // WireAssignment is assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) // zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). -// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear -// extension of the values of w across all instances. -// Its purpose is to batch the checking of multiple evaluations of the same wire. +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, +// where the sum runs over all wᵢ and evaluation point xᵢ in the level. +// Its purpose is to batch the checking of multiple wire evaluations at evaluation points. type zeroCheckLazyClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]{{ .ElementType }} // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []{{ .ElementType }} // yᵢ = w(xᵢ), allegedly - manager *claimsManager // WARNING: Circular references -} - -func (e *zeroCheckLazyClaims) getWire() Wire { - return e.manager.circuit[e.wireI] -} - -func (e *zeroCheckLazyClaims) claimsNum() int { - return len(e.evaluationPoints) + foldingCoeff {{ .ElementType }} // the coefficient used to fold claims, conventionally 0 if there is only one claim + resources *resources + levelI int } func (e *zeroCheckLazyClaims) varsNum() int { - return len(e.evaluationPoints[0]) -} - -// foldedSum returns ∑ᵢ aⁱ yᵢ -func (e *zeroCheckLazyClaims) foldedSum(a {{ .ElementType }}) {{ .ElementType }} { - evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) - return evalsAsPoly.Eval(&a) + return e.resources.nbVars } func (e *zeroCheckLazyClaims) degree(int) int { - return e.manager.circuit[e.wireI].ZeroCheckDegree() -} - -// verifyFinalEval finalizes the verification of w. -// The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. (c is foldingCoeff) -// Both purportedValue and the vector r have been randomized during the sumcheck protocol. -// By taking the w term out of the sum we get the equivalent claim that -// for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. -// If w is an input wire, the verifier can directly check its evaluation at r. -// Otherwise, the prover makes claims about the evaluation of w's input wires, -// wᵢ, at r, to be verified later. -// The claims are communicated through the proof parameter. -// The verifier checks here if the claimed evaluations of wᵢ(r) are consistent with -// the main claim, by checking E w(wᵢ(r)...) = purportedValue. -func (e *zeroCheckLazyClaims) verifyFinalEval(r []{{ .ElementType }}, foldingCoeff, purportedValue {{ .ElementType }}, uniqueInputEvaluations []{{ .ElementType }}) error { - // the eq terms ( E ) - numClaims := len(e.evaluationPoints) - evaluation := polynomial.EvalEq(e.evaluationPoints[numClaims-1], r) - for i := numClaims - 2; i >= 0; i-- { - evaluation.Mul(&evaluation, &foldingCoeff) - eq := polynomial.EvalEq(e.evaluationPoints[i], r) - evaluation.Add(&evaluation, &eq) - } - - wire := e.manager.circuit[e.wireI] - - // the w(...) term - var gateEvaluation {{ .ElementType }} - if wire.IsInput() { // just compute w(r) - gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) - } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire - injection, injectionLeftInv := - e.manager.circuit.ClaimPropagationInfo(e.wireI) - - if len(injection) != len(uniqueInputEvaluations) { - return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) - } - - for uniqueI, i := range injection { // map from unique to all - e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) - } + return gkrcore.Degree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel), e.resources.circuit) +} + +// verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. +// The sumcheck protocol has already reduced the per-wire claims w(xᵢ) = yᵢ to verifying +// ∑ᵢ cⁱ eq(xᵢ, r) · wᵢ(r) = purportedValue, where the sum runs over all +// claims on each wire and c is foldingCoeff. +// Both purportedValue and the vector r have been randomized during sumcheck. +// +// For input wires, w(r) is computed directly from the assignment. +// For non-input wires, the prover claims evaluations of the input wires at r, +// communicated through uniqueInputEvaluations; those claims are verified later. +// The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming +// that the full sum matches purportedValue. +func (e *zeroCheckLazyClaims) verifyFinalEval(r []{{ .ElementType }}, purportedValue {{ .ElementType }}, uniqueInputEvaluations []{{ .ElementType }}) error { + e.resources.levelPoints[e.levelI] = r + level := e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel) + perWireInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) + + var terms []{{ .ElementType }} + flatW := 0 + for _, group := range level { + for _, wI := range group.Wires { + wire := e.resources.circuit[wI] + + var gateEval {{ .ElementType }} + if wire.IsInput() { + gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) + } else { + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) + for _, v := range perWireInputEvals[flatW] { + evaluator.pushInput(v) + } + gateEval.Set(evaluator.evaluate()) + } - evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) - for _, uniqueI := range injectionLeftInv { // map from all to unique - evaluator.pushInput(uniqueInputEvaluations[uniqueI]) + for _, src := range group.ClaimSources { + eq := polynomial.EvalEq(e.resources.levelPoints[src], r) + var term {{ .ElementType }} + term.Mul(&eq, &gateEval) + terms = append(terms, term) + } + flatW++ } - - gateEvaluation.Set(evaluator.evaluate()) } - evaluation.Mul(&evaluation, &gateEvaluation) - - if evaluation.Equal(&purportedValue) { + ys := polynomial.Polynomial(terms) + if total := ys.Eval(&e.foldingCoeff); total.Equal(&purportedValue) { return nil } return errors.New("incompatible evaluations") } // zeroCheckClaims is a claim for sumcheck (prover side). -// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear -// extension of the values of w across all instances. -// Its purpose is to batch the proving of multiple evaluations of the same wire. +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, +// where the sum runs over all (wire v, claim source s) pairs in the level. +// Each wire has its own eq table with the batching coefficients baked in. type zeroCheckClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]{{ .ElementType }} // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []{{ .ElementType }} // yᵢ = w(xᵢ) - manager *claimsManager - - input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) - - eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) - - gateEvaluatorPool *gateEvaluatorPool -} - -func (c *zeroCheckClaims) getWire() Wire { - return c.manager.circuit[c.wireI] -} - -// fold the multiple claims into one claim using a random combination (foldingCoeff or c). -// From the original multiple claims of w(xᵢ) = yᵢ, we get a single claim -// ∑ᵢ,ₕ cⁱ eq(xᵢ, h) w(h) = ∑ᵢ cⁱ yᵢ, where h iterates over the hypercube (circuit instances) and -// i iterates over the claims. -// Equivalently, we could say ∑ᵢ cⁱ yᵢ = ∑ₕ,ᵢ cⁱ eq(xᵢ, h) w(h) = ∑ₕ w(h) ∑ᵢ cⁱ eq(xᵢ, h). -// Thus if we initially compute E := ∑ᵢ cⁱ eq(xᵢ, -), our claim will find the simpler form -// ∑ᵢ cⁱ yᵢ = ∑ₕ w(h) E(h), where the sum-checked polynomial is of degree deg(g) + 1, -// and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. -// The output of fold is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. -func (c *zeroCheckClaims) fold(foldingCoeff {{ .ElementType }}) polynomial.Polynomial { -varsNum := c.varsNum() - eqLength := 1 << varsNum -claimsNum := c.claimsNum() -// initialize the eq tables ( E ) -c.eq = c.manager.memPool.Make(eqLength) - -c.eq[0].SetOne() -c.eq.Eq(c.evaluationPoints[0]) - -// E := eq(x₀, -) -newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) -aI := foldingCoeff - -// E += cⁱ eq(xᵢ, -) -for k := 1; k < claimsNum; k++ { -newEq[0].Set(&aI) - -c.eqAcc(c.eq, newEq,c.evaluationPoints[k]) - -if k+1 < claimsNum { -aI.Mul(&aI, &foldingCoeff) -} + level constraint.GkrSumcheckLevel + levelI int + resources *resources + input []polynomial.MultiLin // UniqueGateInputs order + inputIndices [][]int // [wireInLevel][gateInputJ] → index in input + eqs []polynomial.MultiLin // per-wire interpolation bases for evaluating wire assignments at challenge points + gateEvaluatorPools []*gateEvaluatorPool } -c.manager.memPool.Dump(newEq) - -return c.computeGJ() -} - -// eqAcc sets m to an eq table at q and then adds it to e. -// m <- eq(q, -). -// e <- e + m -func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []{{ .ElementType }}) { - n := len(q) - - //At the end of each iteration, m(h₁, ..., hₙ) = eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) - for i := range q { // In the comments we use a 1-based index so q[i] = qᵢ₊₁ - // go through all assignments of (b₁, ..., bᵢ) ∈ {0,1}ⁱ - const threshold = 1 << 6 - k := 1 << i - if k < threshold { - for j := 0; j < k; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - } else { - c.manager.workers.Submit(k, func(start, end int) { - for j := start; j < end; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - }, 1024).Wait() - } - - } - c.manager.workers.Submit(len(e), func(start, end int) { - for i := start; i < end; i++ { - e[i].Add(&e[i], &m[i]) - } - }, 512).Wait() +func (c *zeroCheckClaims) varsNum() int { + return len(c.resources.levelPoints[c.level[0].ClaimSources[0]]) } +// roundPolynomial computes gⱼ = ∑ₕ ∑ᵥ eqs[v](Xⱼ, h...) · gateᵥ(inputs(Xⱼ, h...)). +// The polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). +// The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). +// By convention, g₀ is a constant polynomial equal to the claimed sum. +func (c *zeroCheckClaims) roundPolynomial() polynomial.Polynomial { + degree := gkrcore.Degree(c.level, c.resources.circuit) + nbUniqueInputs := len(c.input) + nbWires := len(c.eqs) -// computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). -// the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). -// The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. -func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { + // Both eqs and input are multilinear, thus linear in Xⱼ. + // For any such f, f(m) = m·(f(1) - f(0)) + f(0), and f(0), f(1) are read directly + // from the bookkeeping tables. This allows stepwise evaluation at Xⱼ = 1, 2, ..., degree. + // Layout: [eq₀, eq₁, ..., eq_{nbWires-1}, input₀, input₁, ..., input_{nbUniqueInputs-1}] + ml := make([]polynomial.MultiLin, nbWires+nbUniqueInputs) + copy(ml, c.eqs) + copy(ml[nbWires:], c.input) - wire := c.getWire() - degGJ := wire.ZeroCheckDegree() // guaranteed to be no smaller than the actual deg(gⱼ) - nbGateIn := len(c.input) + sumSize := len(c.eqs[0]) / 2 - // Both E and wᵢ (the input wires and the eq table) are multilinear, thus - // they are linear in Xⱼ. - // So for f ∈ { E(r₁, ..., Xⱼ, h...) } ∪ {wᵢ(r₁, ..., Xⱼ, h...) }, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables. - // ml are such multilinear polynomials the evaluations of which over different values of Xⱼ are computed in this stepwise manner. - ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube - ml[0] = c.eq - copy(ml[1:], c.input) - - sumSize := len(c.eq) / 2; // the range of h, over which we sum - - // Perf-TODO: Collate once at claim "folding" time and not again. then, even folding can be done in one operation every time "next" is called - - gJ := make([]{{ .ElementType }}, degGJ) + p := make([]{{ .ElementType }}, degree) var mu sync.Mutex - computeAll := func(start, end int) { // compute method to allow parallelization across instances + computeAll := func(start, end int) { var step {{ .ElementType }} - evaluator := c.gateEvaluatorPool.get() - defer c.gateEvaluatorPool.put(evaluator) + evaluators := make([]*gateEvaluator, nbWires) + for w := range nbWires { + evaluators[w] = c.gateEvaluatorPools[w].get() + } + defer func() { + for w := range nbWires { + c.gateEvaluatorPools[w].put(evaluators[w]) + } + }() - res := make([]{{ .ElementType }}, degGJ) + res := make([]{{ .ElementType }}, degree) // evaluations of ml, laid out as: // ml[0](1, h...), ml[1](1, h...), ..., ml[len(ml)-1](1, h...), // ml[0](2, h...), ml[1](2, h...), ..., ml[len(ml)-1](2, h...), // ... - // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) - mlEvals := make([]{{ .ElementType }}, degGJ*len(ml)) - - for h := start; h < end; h++ { // h counts across instances + // ml[0](degree, h...), ml[1](degree, h...), ..., ml[len(ml)-1](degree, h...) + mlEvals := make([]{{ .ElementType }}, degree*len(ml)) + for h := start; h < end; h++ { evalAt1Index := sumSize + h for k := range ml { - // d = 0 - mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. - step.Sub(&mlEvals[k], &ml[k][h])// step = ml[k](1) - ml[k](0) - for d := 1; d < degGJ; d++ { + mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1, taken directly from the table + step.Sub(&mlEvals[k], &ml[k][h]) // step = ml[k](1) - ml[k](0) + for d := 1; d < degree; d++ { mlEvals[d*len(ml)+k].Add(&mlEvals[(d-1)*len(ml)+k], &step) } } - eIndex := 0 // index for where the current eq term is + eIndex := 0 // start of the current row's eq evaluations nextEIndex := len(ml) - for d := range degGJ { - // Push gate inputs - for i := range nbGateIn { - evaluator.pushInput(mlEvals[eIndex+1+i]) + for d := range degree { + for w := range nbWires { + for _, inputI := range c.inputIndices[w] { + evaluators[w].pushInput(mlEvals[eIndex+nbWires+inputI]) + } + summand := evaluators[w].evaluate() + summand.Mul(summand, &mlEvals[eIndex+w]) + res[d].Add(&res[d], summand) // collect contributions into the sum from start to end } - summand := evaluator.evaluate() - summand.Mul(summand, &mlEvals[eIndex]) - res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } } mu.Lock() - for i := range gJ { - gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum + for i := range p { + p[i].Add(&p[i], &res[i]) // collect into the complete sum } mu.Unlock() } const minBlockSize = 64 - if sumSize < minBlockSize { - // no parallelization computeAll(0, sumSize) } else { - c.manager.workers.Submit(sumSize, computeAll, minBlockSize).Wait() + c.resources.workers.Submit(sumSize, computeAll, minBlockSize).Wait() } - return gJ + return p } -// next first folds the input and E polynomials at the given verifier challenge then computes the new gⱼ. -// Thus, j <- j+1 and rⱼ = challenge. -func (c *zeroCheckClaims) next(challenge {{ .ElementType }}) polynomial.Polynomial { +// roundFold folds all input and eq polynomials at the verifier challenge r. +// After this call, j ← j+1 and rⱼ = r. +func (c *zeroCheckClaims) roundFold(r {{ .ElementType }}) { const minBlockSize = 512 - n := len(c.eq) / 2 + n := len(c.eqs[0]) / 2 if n < minBlockSize { - // no parallelization for i := range c.input { - c.input[i].Fold(challenge) + c.input[i].Fold(r) + } + for i := range c.eqs { + c.eqs[i].Fold(r) } - c.eq.Fold(challenge) } else { - wgs := make([]*sync.WaitGroup, len(c.input)) + wgs := make([]*sync.WaitGroup, len(c.input)+len(c.eqs)) for i := range c.input { - wgs[i] = c.manager.workers.Submit(n, c.input[i].FoldParallel(challenge), minBlockSize) + wgs[i] = c.resources.workers.Submit(n, c.input[i].FoldParallel(r), minBlockSize) + } + for i := range c.eqs { + wgs[len(c.input)+i] = c.resources.workers.Submit(n, c.eqs[i].FoldParallel(r), minBlockSize) } - c.manager.workers.Submit(n, c.eq.FoldParallel(challenge), minBlockSize).Wait() for _, wg := range wgs { wg.Wait() } } - - return c.computeGJ() } -func (c *zeroCheckClaims) varsNum() int { - return len(c.evaluationPoints[0]) -} - -func (c *zeroCheckClaims) claimsNum() int { - return len(c.claimedEvaluations) -} - -// proveFinalEval provides the values wᵢ(r₁, ..., rₙ) +// proveFinalEval provides the unique input wire values wᵢ(r₁, ..., rₙ). func (c *zeroCheckClaims) proveFinalEval(r []{{ .ElementType }}) []{{ .ElementType }} { - //defer the proof, return list of claims - - injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. - evaluations := make([]{{ .ElementType }}, len(injection)) - for i, gateInputI := range injection { - wI := c.input[gateInputI] - wI.Fold(r[len(r)-1]) // We already have wᵢ(r₁, ..., rₙ₋₁, hₙ) in a table. Only one more fold required. - c.manager.add(c.getWire().Inputs[gateInputI], r, wI[0]) - evaluations[i] = wI[0] + c.resources.levelPoints[c.levelI] = r + evaluations := make([]{{ .ElementType }}, len(c.input)) + for i := range c.input { + c.input[i].Fold(r[len(r)-1]) + evaluations[i] = c.input[i][0] } - - c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.dumpAll() - - return evaluations -} - -type claimsManager struct { - claims []*zeroCheckLazyClaims - assignment WireAssignment - memPool *polynomial.Pool - workers *utils.WorkerPool - circuit Circuit -} - -func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { - manager.assignment = assignment - manager.claims = make([]*zeroCheckLazyClaims, len(circuit)) - manager.memPool = o.pool - manager.workers = o.workers - manager.circuit = circuit - - for i := range circuit { - manager.claims[i] = &zeroCheckLazyClaims{ - wireI: i, - evaluationPoints: make([][]{{ .ElementType }}, 0, circuit[i].NbClaims()), - claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), - manager: &manager, - } + for i := range c.input { + c.resources.memPool.Dump(c.input[i]) } - return -} - -func (m *claimsManager) add(wire int, evaluationPoint []{{ .ElementType }}, evaluation {{ .ElementType }}) { - claim := m.claims[wire] - i := len(claim.evaluationPoints) - claim.claimedEvaluations[i] = evaluation - claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) + for i := range c.eqs { + c.resources.memPool.Dump(c.eqs[i]) + } + for _, pool := range c.gateEvaluatorPools { + pool.dumpAll() + } + return evaluations } -func (m *claimsManager) getLazyClaim(wire int) *zeroCheckLazyClaims { - return m.claims[wire] -} +// eqAcc sets m to an eq table at q and then adds it to e. +// m <- m[0] · eq(q, -). +// e <- e + m +func (r *resources) eqAcc(e, m polynomial.MultiLin, q []{{ .ElementType }}) { + n := len(q) -func (m *claimsManager) getClaim(wireI int) *zeroCheckClaims { - lazy := m.claims[wireI] - wire := m.circuit[wireI] - res := &zeroCheckClaims{ - wireI: wireI, - evaluationPoints: lazy.evaluationPoints, - claimedEvaluations: lazy.claimedEvaluations, - manager: m, - } + // At the end of each iteration, m(h₁, ..., hₙ) = m[0] · eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) + for i := range q { // 1-based in comments: q[i] = qᵢ₊₁ + // go through all assignments of (b₁, ..., bᵢ) ∈ {0,1}ⁱ + const threshold = 1 << 6 + k := 1 << i + if k < threshold { + for j := 0; j < k; j++ { + j0 := j << (n - i) // bᵢ₊₁ = 0 + j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - if wire.IsInput() { - res.input = []polynomial.MultiLin{m.memPool.Clone(m.assignment[wireI])} - } else { - res.input = make([]polynomial.MultiLin, len(wire.Inputs)) + m[j1].Mul(&q[i], &m[j0]) // m(b₁,...,bᵢ,1) = m(b₁,...,bᵢ) · qᵢ₊₁ + m[j0].Sub(&m[j0], &m[j1]) // m(b₁,...,bᵢ,0) = m(b₁,...,bᵢ) · (1 - qᵢ₊₁) + } + } else { + r.workers.Submit(k, func(start, end int) { + for j := start; j < end; j++ { + j0 := j << (n - i) // bᵢ₊₁ = 0 + j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - for inputI, inputW := range wire.Inputs { - res.input[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied + m[j1].Mul(&q[i], &m[j0]) // m(b₁,...,bᵢ,1) = m(b₁,...,bᵢ) · qᵢ₊₁ + m[j0].Sub(&m[j0], &m[j1]) // m(b₁,...,bᵢ,0) = m(b₁,...,bᵢ) · (1 - qᵢ₊₁) + } + }, 1024).Wait() } } - - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Evaluate, len(res.input), m.memPool) - - return res -} - -func (m *claimsManager) deleteClaim(wire int) { - m.claims[wire].manager = nil - m.claims[wire] = nil + r.workers.Submit(len(e), func(start, end int) { + for i := start; i < end; i++ { + e[i].Add(&e[i], &m[i]) + } + }, 512).Wait() } -type settings struct { - pool *polynomial.Pool - transcript *fiatshamir.Transcript - transcriptPrefix string - nbVars int - workers *utils.WorkerPool +type resources struct { + // levelPoints[i] is the sumcheck evaluation point r produced at schedule level i. + // levelPoints[len(schedule)] holds the initial challenge (firstChallenge / rho). + // SkipLevels produce no point; their slot remains nil. + levelPoints [][]{{ .ElementType }} + evalPositions [][]int // [wireI][evalI] → position in source level's finalEvalProof + nbVars int + assignment WireAssignment + memPool polynomial.Pool + workers *utils.WorkerPool + circuit Circuit + schedule constraint.GkrProvingSchedule + transcript transcript } -type Option func(*settings) - -func WithPool(pool *polynomial.Pool) Option { - return func (options *settings) { - options.pool = pool +func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (resources, error) { + nbVars := assignment.NumVars() + nbInstances := assignment.NumInstances() + if 1<= 2 { + foldingCoeff = r.transcript.getChallenge() } - o.nbVars = assignment.NumVars() - nbInstances := assignment.NumInstances() - if 1< 1 { //fold the claims - size++ + eqLength := 1 << r.nbVars + eqs := make([]polynomial.MultiLin, nbWires) + var alpha {{ .ElementType }} + alpha.SetOne() + flatW = 0 + for _, group := range level { + nbSources := len(group.ClaimSources) + + groupEq := polynomial.MultiLin(r.memPool.Make(eqLength)) + groupEq[0].Set(&alpha) + groupEq.Eq(r.levelPoints[group.ClaimSources[0]]) + + if nbSources > 1 { + newEq := polynomial.MultiLin(r.memPool.Make(eqLength)) + aI := alpha + for k := 1; k < nbSources; k++ { + aI.Mul(&aI, &foldingCoeff) + newEq[0].Set(&aI) + r.eqAcc(groupEq, newEq, r.levelPoints[group.ClaimSources[k]]) + } + r.memPool.Dump(newEq) } - size += logNbInstances // full run of sumcheck on logNbInstances variables - } - - nums := make([]string, max(len(c), logNbInstances)) - for i := range nums { - nums[i] = strconv.Itoa(i) - } - challenges := make([]string, size) - - // output wire claims - firstChallengePrefix := prefix + "fC." - for j := 0; j < logNbInstances; j++ { - challenges[j] = firstChallengePrefix + nums[j] - } - j := logNbInstances - for i := len(c) - 1; i >= 0; i-- { - if c[i].NoProof() { - continue + var stride {{ .ElementType }} + stride.Set(&foldingCoeff) + for range nbSources - 1 { + stride.Mul(&stride, &foldingCoeff) } - wirePrefix := prefix + "w" + nums[i] + "." - if c[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "fold" - j++ - } + eqs[flatW] = groupEq + flatW++ + alpha.Mul(&alpha, &stride) - partialSumPrefix := wirePrefix + "pSP." - for k := 0; k < logNbInstances; k++ { - challenges[j] = partialSumPrefix + nums[k] - j++ + for w := 1; w < len(group.Wires); w++ { + eqs[flatW] = polynomial.MultiLin(r.memPool.Make(eqLength)) + r.workers.Submit(eqLength, func(start, end int) { + for i := start; i < end; i++ { + eqs[flatW][i].Mul(&eqs[flatW-1][i], &stride) + } + }, 512).Wait() + flatW++ + alpha.Mul(&alpha, &stride) } } - return challenges -} -func getFirstChallengeNames(logNbInstances int, prefix string) []string { - res := make([]string, logNbInstances) - firstChallengePrefix := prefix + "fC." - for i := 0; i < logNbInstances; i++ { - res[i] = firstChallengePrefix + strconv.Itoa(i) + claims := &zeroCheckClaims{ + level: level, + levelI: levelI, + resources: r, + input: input, + inputIndices: inputIndices, + eqs: eqs, + gateEvaluatorPools: pools, + } + return sumcheckProve(claims, &r.transcript) +} + +// verifyLevel runs the sumcheck verification for a SumcheckLevel: derives the fold challenge, +// computes the claimed sum, and calls sumcheckVerify. +func (r *resources) verifyLevel(levelI int, proof Proof) error { + level := r.schedule[levelI].(constraint.GkrSumcheckLevel) + initialChallengeI := len(r.schedule) + + nbClaims := gkrcore.NbClaims(level) + var foldingCoeff {{ .ElementType }} + if nbClaims >= 2 { + foldingCoeff = r.transcript.getChallenge() + } + + var ys []{{ .ElementType }} + for _, group := range level { + for _, wI := range group.Wires { + evalI := 0 + for _, src := range group.ClaimSources { + var y {{ .ElementType }} + if src == initialChallengeI { + y = r.assignment[wI].Evaluate(r.levelPoints[src], &r.memPool) + } else { + y = proof[src].finalEvalProof[r.evalPositions[wI][evalI]] + evalI++ + } + ys = append(ys, y) + } + } } - return res -} -func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]{{ .ElementType }}, error) { - res := make([]{{ .ElementType }}, len(names)) - for i, name := range names { - if bytes, err := transcript.ComputeChallenge(name); err != nil { - return nil, err - } else if err = res[i].SetBytesCanonical(bytes); err != nil { - return nil, err - } + ysPoly := polynomial.Polynomial(ys) + claimedSum := ysPoly.Eval(&foldingCoeff) + + lazyClaims := &zeroCheckLazyClaims{ + foldingCoeff: foldingCoeff, + resources: r, + levelI: levelI, } - return res, nil + return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, gkrcore.Degree(level, r.circuit), &r.transcript) } // Prove consistency of the claimed assignment -func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { - o, err := setup(c, assignment, transcriptSettings, options...) +func Prove(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (Proof, error) { + r, err := newResources(c, schedule, assignment, hasher) if err != nil { return nil, err } - defer o.workers.Stop() + defer r.workers.Stop() - claims := newClaimsManager(c, assignment, o) + proof := make(Proof, len(schedule)) - proof := make(Proof, len(c)) - // firstChallenge called rho in the paper - var firstChallenge []{{ .ElementType }} - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return nil, err + // Derive initial challenge point + firstChallenge := make([]{{ .ElementType }}, r.nbVars) + for j := range r.nbVars { + firstChallenge[j] = r.transcript.getChallenge() } + r.levelPoints[len(schedule)] = firstChallenge - wirePrefix := o.transcriptPrefix + "w" - var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - - wire := c[i] + for levelI := len(schedule) - 1; levelI >= 0; levelI-- { + switch s := schedule[levelI].(type) { + case constraint.GkrSkipLevel: + r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - if wire.IsOutput() { - claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) + case constraint.GkrSumcheckLevel: + proof[levelI] = r.proveLevel(levelI) + // Bind finalEvalProof for next level's challenge derivation + r.transcript.bind(proof[levelI].finalEvalProof...) } - - claim := claims.getClaim(i) - if wire.NoProof() { // input wires with one claim only - proof[i] = sumcheckProof{ - partialSumPolys: []polynomial.Polynomial{}, - finalEvalProof: []{{ .ElementType }}{}, - } - } else { - if proof[i], err = sumcheckProve( - claim, fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err != nil { - return proof, err - } - - baseChallenge = make([][]byte, len(proof[i].finalEvalProof)) - for j := range proof[i].finalEvalProof { - baseChallenge[j] = proof[i].finalEvalProof[j].Marshal() - } - } - // the verifier checks a single claim about input wires itself - claims.deleteClaim(i) } return proof, nil } -// Verify the consistency of the claimed output with the claimed input -// Unlike in Prove, the assignment argument need not be complete -func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { - o, err := setup(c, assignment, transcriptSettings, options...) +// Verify the consistency of the claimed output with the claimed input. +// Unlike in Prove, the assignment argument need not be complete. +func Verify(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, proof Proof, hasher hash.Hash) error { + r, err := newResources(c, schedule, assignment, hasher) if err != nil { return err } - defer o.workers.Stop() - - claims := newClaimsManager(c, assignment, o) + defer r.workers.Stop() - var firstChallenge []{{ .ElementType }} - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return err + // Derive initial challenge point + firstChallenge := make([]{{ .ElementType }}, r.nbVars) + for j := range r.nbVars { + firstChallenge[j] = r.transcript.getChallenge() } + r.levelPoints[len(schedule)] = firstChallenge - wirePrefix := o.transcriptPrefix + "w" - var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - wire := c[i] - - if wire.IsOutput() { - claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) - } - - proofW := proof[i] - claim := claims.getLazyClaim(i) - if wire.NoProof() { // input wires with one claim only - // make sure the proof is empty - if len(proofW.finalEvalProof) != 0 || len(proofW.partialSumPolys) != 0 { - return errors.New("no proof allowed for input wire with a single claim") - } + for levelI := len(schedule) - 1; levelI >= 0; levelI-- { + switch s := schedule[levelI].(type) { + case constraint.GkrSkipLevel: + r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - if wire.NbClaims() == 1 { // input wire - // simply evaluate and see if it matches - if len(claim.evaluationPoints) == 0 || len(claim.claimedEvaluations) == 0 { - return errors.New("missing input wire claim") - } - evaluation := assignment[i].Evaluate(claim.evaluationPoints[0], claims.memPool) - if !claim.claimedEvaluations[0].Equal(&evaluation) { - return errors.New("incorrect input wire claim") - } - } - } else if err = sumcheckVerify( - claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { // incorporate prover claims about w's input into the transcript - baseChallenge = make([][]byte, len(proofW.finalEvalProof)) - for j := range baseChallenge { - baseChallenge[j] = proofW.finalEvalProof[j].Marshal() + case constraint.GkrSumcheckLevel: + if err = r.verifyLevel(levelI, proof); err != nil { + return fmt.Errorf("sumcheck proof rejected: %v", err) } - } else { - return fmt.Errorf("sumcheck proof rejected: %v", err) //TODO: Any polynomials to dump? + // Bind finalEvalProof for next level's challenge derivation + r.transcript.bind(proof[levelI].finalEvalProof...) } - claims.deleteClaim(i) } return nil } @@ -711,15 +566,15 @@ func iterateElems(elems []{{ .ElementType }}, counter *int, yield func(int, *{{ func (p Proof) flatten() iter.Seq2[int, *{{ .ElementType }}] { return func(yield func(int, *{{ .ElementType }}) bool) { - var counter int + var counter int for i := range p { for _, poly := range p[i].partialSumPolys { if !iterateElems(poly, &counter, yield) { - return + return } } if !iterateElems(p[i].finalEvalProof, &counter, yield) { - return + return } } } @@ -827,19 +682,19 @@ func (e *gateEvaluator) evaluate(top ...{{ .ElementType }}) *{{ .ElementType }} // gateEvaluatorPool manages a pool of gate evaluators for a specific gate type // All evaluators share the same underlying polynomial.Pool for element slices type gateEvaluatorPool struct { - gate gkrcore.GateBytecode - nbIn int - lock sync.Mutex - available map[*gateEvaluator]struct{} - elementPool *polynomial.Pool + gate gkrcore.GateBytecode + nbIn int + lock sync.Mutex + available map[*gateEvaluator]struct{} + elementPool *polynomial.Pool } func newGateEvaluatorPool(gate gkrcore.GateBytecode, nbIn int, elementPool *polynomial.Pool) *gateEvaluatorPool { gep := &gateEvaluatorPool{ - gate: gate, - nbIn: nbIn, - elementPool: elementPool, - available: make(map[*gateEvaluator]struct{}), + gate: gate, + nbIn: nbIn, + elementPool: elementPool, + available: make(map[*gateEvaluator]struct{}), } return gep } diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index 4fc4dac89f..7da946057a 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -1,25 +1,25 @@ import ( - "{{.FieldPackagePath}}" - "{{.FieldPackagePath}}/mimc" - "{{.FieldPackagePath}}/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - "github.com/consensys/gnark-crypto/ecc" - gcUtils "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrtesting" - "github.com/consensys/gnark/internal/gkr/gkrcore" - "github.com/stretchr/testify/assert" "fmt" "hash" "os" - "strconv" - "testing" "path/filepath" "reflect" + "strconv" + "testing" "time" + + "github.com/consensys/gnark-crypto/ecc" + "{{ .FieldPackagePath }}" + "{{ .FieldPackagePath }}/mimc" + "{{ .FieldPackagePath }}/polynomial" + "github.com/consensys/gnark/constraint" + "github.com/consensys/gnark/internal/gkr/gkrcore" + "github.com/consensys/gnark/internal/gkr/gkrtesting" + "github.com/stretchr/testify/assert" ) -var cache = gkrtesting.NewCache(ecc.{{.FieldID}}.ScalarField()) +var cache = gkrtesting.NewCache(ecc.{{ .FieldID }}.ScalarField()) func TestNoGateTwoInstances(t *testing.T) { // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case @@ -62,36 +62,86 @@ func TestMimc(t *testing.T) { test(t, gkrtesting.MiMCCircuit(93)) } -func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - // Construct SerializableCircuit directly, bypassing CompileCircuit - // which would reset NbUniqueOutputs based on actual topology - circuit := gkrcore.SerializableCircuit{ - { - NbUniqueOutputs: 2, - Gate: gkrcore.SerializableGate{Degree: 1}, - }, - } +// testLevel exercises proveLevel/verifyLevel for a single sumcheck level. +func testLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrSumcheckLevel) { + t.Helper() + _, sCircuit := cache.Compile(t, circuit) - assignment := WireAssignment{[]{{ .ElementType }}{two, three}} - var o settings - pool := polynomial.NewPool(256, 1<<11) - workers := gcUtils.NewWorkerPool() - o.pool = &pool - o.workers = workers - - claimsManagerGen := func() *claimsManager { - manager := newClaimsManager(circuit, assignment, o) - manager.add(0, []{{ .ElementType }}{three}, five) - manager.add(0, []{{ .ElementType }}{four}, six) - return &manager + ins := sCircuit.Inputs() + assignment := make(WireAssignment, len(sCircuit)) + for _, i := range ins { + assignment[i] = make([]{{ .ElementType }}, 2) + {{ .FieldPackageName }}.Vector(assignment[i]).MustSetRandom() } - transcriptGen := newMessageCounterGenerator(4, 1) + assignment.Complete(sCircuit) + + schedule := constraint.GkrProvingSchedule{level} + challenge := []{{ .ElementType }}{five} - proof, err := sumcheckProve(claimsManagerGen().getClaim(0), fiatshamir.WithHash(transcriptGen(), nil)) + // Prove + proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) - err = sumcheckVerify(claimsManagerGen().getLazyClaim(0), proof, fiatshamir.WithHash(transcriptGen(), nil)) + defer proveR.workers.Stop() + + proveR.levelPoints[1] = challenge + proof := Proof{proveR.proveLevel(0)} + + // Verify + verifyR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) + defer verifyR.workers.Stop() + + verifyR.levelPoints[1] = challenge + assert.NoError(t, verifyR.verifyLevel(0, proof)) +} + +func TestLevel(t *testing.T) { + // Wires 0,1 = inputs; wires 2,3,4 = mul(0,1). All gates are independent outputs. + circuit := gkrcore.RawCircuit{ + {}, + {}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, + } + // All levels have initial challenge at index 1 (len(schedule) = 1). + tests := []struct { + name string + level constraint.GkrSumcheckLevel + }{ + { + name: "single wire", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []int{1}}, + }, + }, + { + name: "two groups", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []int{1}}, + {Wires: []int{3}, ClaimSources: []int{1}}, + }, + }, + { + name: "one group with two wires", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4, 3}, ClaimSources: []int{1}}, + }, + }, + { + name: "mixed: single + multi-wire group", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []int{1}}, + {Wires: []int{3, 2}, ClaimSources: []int{1}}, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testLevel(t, circuit, tc.level) + }) + } } var one, two, three, four, five, six {{ .ElementType }} @@ -126,14 +176,23 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrcore.RawCircuit) { + testWithSchedule(t, circuit, nil) +} + +func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constraint.GkrProvingSchedule) { gCircuit, sCircuit := cache.Compile(t, circuit) + if schedule == nil { + var err error + schedule, err = gkrcore.DefaultProvingSchedule(sCircuit) + assert.NoError(t, err) + } ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) for i := range ins { insAssignment[i] = make([]{{ .ElementType }}, maxSize) - fr.Vector(insAssignment[i]).MustSetRandom() + {{ .FieldPackageName }}.Vector(insAssignment[i]).MustSetRandom() } fullAssignment := make(WireAssignment, len(circuit)) @@ -146,22 +205,21 @@ func test(t *testing.T, circuit gkrcore.RawCircuit) { t.Log("Selected inputs for test") - proof, err := Prove(sCircuit, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(sCircuit, schedule, fullAssignment, newMessageCounter(1, 1)) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(1, 1)) assert.NoError(t, err, "proof rejected") if proof.isEmpty() { // special case for TestNoGate: continue // there's no way to make a trivial proof fail } - err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) + err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(0, 1)) assert.NotNil(t, err, "bad proof accepted") } - } func (p Proof) isEmpty() bool { @@ -181,14 +239,17 @@ func (p Proof) isEmpty() bool { func testNoGate(t *testing.T, inputAssignments ...[]{{ .ElementType }}) { _, c := cache.Compile(t, gkrtesting.NoGateCircuit()) + schedule, err := gkrcore.DefaultProvingSchedule(c) + assert.NoError(t, err) + assignment := WireAssignment{0: inputAssignments[0]} - proof, err := Prove(c, assignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(c, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(c, assignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(c, schedule, assignment, proof, newMessageCounter(1, 1)) assert.NoError(t, err, "proof rejected") } @@ -196,7 +257,7 @@ func generateTestProver(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) assert.NoError(t, err) - proof, err := Prove(testCase.Circuit, testCase.FullAssignment, fiatshamir.WithHash(testCase.Hash)) + proof, err := Prove(testCase.Circuit, testCase.Schedule, testCase.FullAssignment, testCase.Hash) assert.NoError(t, err) assert.NoError(t, proofEquals(testCase.Proof, proof)) } @@ -206,17 +267,16 @@ func generateTestVerifier(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(testCase.Hash)) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) assert.NoError(t, err, "proof rejected") testCase, err = newTestCase(path) assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(newMessageCounter(2, 0))) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, newMessageCounter(2, 0)) assert.NotNil(t, err, "bad proof accepted") } } func TestGkrVectors(t *testing.T) { - const testDirPath = "../test_vectors/" dirEntries, err := os.ReadDir(testDirPath) assert.NoError(t, err) @@ -262,10 +322,13 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") _, c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) + schedule, err := gkrcore.DefaultProvingSchedule(c) + assert.NoError(b, err) + in0 := make([]{{ .ElementType }}, nbInstances) in1 := make([]{{ .ElementType }}, nbInstances) - {{.FieldPackageName}}.Vector(in0).MustSetRandom() - {{.FieldPackageName}}.Vector(in1).MustSetRandom() + {{ .FieldPackageName }}.Vector(in0).MustSetRandom() + {{ .FieldPackageName }}.Vector(in1).MustSetRandom() fmt.Println("evaluating circuit") start := time.Now().UnixMicro() @@ -276,12 +339,30 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { //b.ResetTimer() fmt.Println("constructing proof") start = time.Now().UnixMicro() - _, err := Prove(c, assignment, fiatshamir.WithHash(mimc.NewMiMC())) + _, err = Prove(c, schedule, assignment, mimc.NewMiMC()) proved := time.Now().UnixMicro() - start fmt.Println("proved in", proved, "μs") assert.NoError(b, err) } +// TestSingleMulGateExplicitSchedule tests a single mul gate with an explicit single-step schedule, +// equivalent to the default but constructed manually to exercise the schedule path. +func TestSingleMulGateExplicitSchedule(t *testing.T) { + circuit := gkrtesting.SingleMulGateCircuit() + _, sCircuit := cache.Compile(t, circuit) + + // Wire 2 is the mul gate output (inputs: 0, 1). + // Explicit schedule: one SumcheckLevel for wire 2 only. + // ClaimSources: [1] = initial challenge (index len(schedule) = 1). + schedule := constraint.GkrProvingSchedule{ + constraint.GkrSumcheckLevel{ + {Wires: []int{2}, ClaimSources: []int{1}}, + }, + } + testWithSchedule(t, circuit, schedule) + _ = sCircuit +} + func BenchmarkGkrMimc19(b *testing.B) { benchmarkGkrMiMC(b, 1<<19, 91) } @@ -290,4 +371,4 @@ func BenchmarkGkrMimc17(b *testing.B) { benchmarkGkrMiMC(b, 1<<17, 91) } -{{template "gkrTestVectors" .}} +{{ template "gkrTestVectors" .}} diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl index 3326e8b35d..7cbc8ed2f3 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.gen.go.tmpl @@ -3,7 +3,7 @@ import ( "fmt" "github.com/consensys/bavard" "github.com/consensys/gnark-crypto/ecc" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" "github.com/consensys/gnark/internal/gkr/gkrtesting" @@ -56,10 +56,8 @@ func run(absPath string) error { return err } - transcriptSetting := fiatshamir.WithHash(testCase.Hash) - var proof Proof - proof, err = Prove(testCase.Circuit, testCase.FullAssignment, transcriptSetting) + proof, err = Prove(testCase.Circuit, testCase.Schedule, testCase.FullAssignment, testCase.Hash) if err != nil { return err } @@ -81,7 +79,7 @@ func run(absPath string) error { return err } - err = Verify(testCase.Circuit, testCase.InOutAssignment, proof, transcriptSetting) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, proof, testCase.Hash) if err != nil { return err } @@ -91,7 +89,7 @@ func run(absPath string) error { return err } - err = Verify(testCase.Circuit, testCase.InOutAssignment, proof, fiatshamir.WithHash(newMessageCounter(2, 0))) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, proof, newMessageCounter(2, 0)) if err == nil { return fmt.Errorf("bad proof accepted") } @@ -116,7 +114,7 @@ func toPrintableProof(proof Proof) (gkrtesting.PrintableProof, error) { return res, nil } -func elementToInterface(x *{{.ElementType}}) interface{} { +func elementToInterface(x *{{ .ElementType }}) interface{} { if i := x.BigInt(nil); i != nil { return i } @@ -132,7 +130,7 @@ func elementSliceToInterfaceSlice(x interface{}) []interface{} { res := make([]interface{}, X.Len()) for i := range res { - xI := X.Index(i).Interface().({{.ElementType}}) + xI := X.Index(i).Interface().({{ .ElementType }}) res[i] = elementToInterface(&xI) } return res @@ -153,4 +151,4 @@ func elementSliceSliceToInterfaceSliceSlice(x interface{}) [][]interface{} { return res } -{{template "gkrTestVectors" .}} \ No newline at end of file +{{ template "gkrTestVectors" .}} diff --git a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl index 25084e8d0f..92370ece26 100644 --- a/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.vectors.go.tmpl @@ -1,15 +1,13 @@ -{{define "gkrTestVectors"}} - -{{$CheckOutputCorrectness := true}} +{{ define "gkrTestVectors" }} func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { proof := make(Proof, len(printable)) for i := range printable { - finalEvalProof := []{{.ElementType}}(nil) + finalEvalProof := []{{ .ElementType }}(nil) if printable[i].FinalEvalProof != nil { finalEvalSlice := reflect.ValueOf(printable[i].FinalEvalProof) - finalEvalProof = make([]{{.ElementType}}, finalEvalSlice.Len()) + finalEvalProof = make([]{{ .ElementType }}, finalEvalSlice.Len()) for k := range finalEvalProof { if _, err := {{ setElement "finalEvalProof[k]" "finalEvalSlice.Index(k).Interface()" .ElementType}}; err != nil { return nil, err @@ -37,7 +35,8 @@ type TestCase struct { Proof Proof FullAssignment WireAssignment InOutAssignment WireAssignment - {{if .GenerateTestVectors}}Info gkrtesting.TestCaseInfo // we are generating the test vectors, so we need to keep the circuit instance info to ADD the proof to it and resave it{{end}} + Schedule constraint.GkrProvingSchedule + {{ if .GenerateTestVectors }}Info gkrtesting.TestCaseInfo // we are generating the test vectors, so we need to keep the circuit instance info to ADD the proof to it and resave it{{ end }} } var testCases = make(map[string]*TestCase) @@ -69,6 +68,20 @@ func newTestCase(path string) (*TestCase, error) { if proof, err = unmarshalProof(info.Proof); err != nil { return nil, err } + var schedule constraint.GkrProvingSchedule + if schedule, err = info.Schedule.ToProvingSchedule(); err != nil { + return nil, err + } + if schedule == nil { + if schedule, err = gkrcore.DefaultProvingSchedule(circuit); err != nil { + return nil, err + } + } + + outputSet := make(map[int]bool, len(circuit)) + for _, o := range circuit.Outputs() { + outputSet[o] = true + } fullAssignment := make(WireAssignment, len(circuit)) inOutAssignment := make(WireAssignment, len(circuit)) @@ -82,7 +95,7 @@ func newTestCase(path string) (*TestCase, error) { } assignmentRaw = info.Input[inI] inI++ - } else if circuit[i].IsOutput() { + } else if outputSet[i] { if outI == len(info.Output) { return nil, fmt.Errorf("fewer output in vector than in circuit") } @@ -103,7 +116,7 @@ func newTestCase(path string) (*TestCase, error) { fullAssignment.Complete(circuit) for i := range circuit { - if circuit[i].IsOutput() { + if outputSet[i] { if err = sliceEquals(inOutAssignment[i], fullAssignment[i]); err != nil { return nil, fmt.Errorf("assignment mismatch: %v", err) } @@ -116,6 +129,7 @@ func newTestCase(path string) (*TestCase, error) { Proof: proof, Hash: _hash, Circuit: circuit, + Schedule: schedule, {{ if .GenerateTestVectors }} Info: info, {{ end }} } @@ -124,7 +138,7 @@ func newTestCase(path string) (*TestCase, error) { return tCase, nil } -{{end}} +{{ end }} {{- define "setElement element value elementType"}} {{- if eq .elementType "fr.Element"}} setElement(&{{.element}}, {{.value}}) @@ -132,4 +146,4 @@ func newTestCase(path string) (*TestCase, error) { {{- else}} {{print "\"UNEXPECTED TYPE" .elementType "\""}} {{- end}} -{{- end}} \ No newline at end of file +{{- end}} diff --git a/internal/generator/backend/template/gkr/sumcheck.go.tmpl b/internal/generator/backend/template/gkr/sumcheck.go.tmpl index 41ff58b325..60b3c75664 100644 --- a/internal/generator/backend/template/gkr/sumcheck.go.tmpl +++ b/internal/generator/backend/template/gkr/sumcheck.go.tmpl @@ -1,163 +1,109 @@ import ( "errors" + "hash" + "{{.FieldPackagePath}}" "{{.FieldPackagePath}}/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - "strconv" ) -// This does not make use of parallelism and represents polynomials as lists of coefficients -// It is currently geared towards arithmetic hashes. Once we have a more unified hash function interface, this can be generified. +// This does not make use of parallelism and represents polynomials as lists of coefficients. + +// transcript is a Fiat-Shamir transcript backed by a running hash. +// Field elements are written via bind; challenges are derived via getChallenge. +// The hash is never reset — all previous data is implicitly part of future challenges. +type transcript struct { + h hash.Hash + bound bool // whether bind was called since the last getChallenge +} + +// bind writes field elements to the transcript as bindings for the next challenge. +func (t *transcript) bind(elements ...{{ .ElementType }}) { + if len(elements) == 0 { + return + } + for i := range elements { + bytes := elements[i].Bytes() + t.h.Write(bytes[:]) + } + t.bound = true +} + +// getChallenge binds optional elements, then squeezes a challenge from the current hash state. +// If no bindings were added since the last squeeze, a separator byte is written first +// to advance the state and prevent repeated values. +func (t *transcript) getChallenge(bindings ...{{ .ElementType }}) {{ .ElementType }} { + t.bind(bindings...) + if !t.bound { + t.h.Write([]byte{0}) + } + t.bound = false + var res {{ .ElementType }} + res.SetBytes(t.h.Sum(nil)) + return res +} // sumcheckClaims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. // Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) type sumcheckClaims interface { - fold(a {{.ElementType}}) polynomial.Polynomial // fold into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. - next({{.ElementType}}) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ + roundPolynomial() polynomial.Polynomial // compute gⱼ polynomial for current round + roundFold(r {{ .ElementType }}) // fold inputs and eq at challenge r varsNum() int // number of variables - claimsNum() int // number of claims - proveFinalEval(r []{{.ElementType}}) []{{.ElementType}} // in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof + proveFinalEval(r []{{ .ElementType }}) []{{ .ElementType }} // in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof } // sumcheckLazyClaims is the sumcheckClaims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. type sumcheckLazyClaims interface { - claimsNum() int // claimsNum = m - varsNum() int // varsNum = n - foldedSum(a {{.ElementType}}) {{.ElementType}} // foldedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - degree(i int) int // degree of the total claim in the i'th variable - verifyFinalEval(r []{{.ElementType}}, foldingCoeff {{.ElementType}}, purportedValue {{.ElementType}}, proof []{{.ElementType}}) error + varsNum() int // varsNum = n + degree(i int) int // degree of the total claim in the i'th variable + verifyFinalEval(r []{{ .ElementType }}, purportedValue {{ .ElementType }}, proof []{{ .ElementType }}) error } // sumcheckProof of a multi-statement. type sumcheckProof struct { partialSumPolys []polynomial.Polynomial - finalEvalProof []{{.ElementType}} //in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof -} - -func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) (challengeNames []string, err error) { - numChallenges := varsNum - if claimsNum >= 2 { - numChallenges++ - } - challengeNames = make([]string, numChallenges) - if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "fold" - } - prefix := settings.Prefix + "pSP." - for i := 0; i < varsNum; i++ { - challengeNames[i+numChallenges-varsNum] = prefix + strconv.Itoa(i) - } - if settings.Transcript == nil { - transcript := fiatshamir.NewTranscript(settings.Hash, challengeNames...) - settings.Transcript = transcript - } - - for i := range settings.BaseChallenges { - if err = settings.Transcript.Bind(challengeNames[0], settings.BaseChallenges[i]); err != nil { - return - } - } - return -} - -func next(transcript *fiatshamir.Transcript, bindings []{{.ElementType}}, remainingChallengeNames *[]string) ({{.ElementType}}, error) { - challengeName := (*remainingChallengeNames)[0] - for i := range bindings { - bytes := bindings[i].Bytes() - if err := transcript.Bind(challengeName, bytes[:]); err != nil { - return {{.ElementType}}{}, err - } - } - var res {{.ElementType}} - bytes, err := transcript.ComputeChallenge(challengeName) - res.SetBytes(bytes) - - *remainingChallengeNames = (*remainingChallengeNames)[1:] - - return res, err + finalEvalProof []{{ .ElementType }} //in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof } -// sumcheckProve create a non-interactive proof -func sumcheckProve(claims sumcheckClaims, transcriptSettings fiatshamir.Settings) (sumcheckProof, error) { - - var proof sumcheckProof - remainingChallengeNames, err := setupTranscript(claims.claimsNum(), claims.varsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return proof, err - } - - var foldingCoeff {{.ElementType}} - if claims.claimsNum() >= 2 { - if foldingCoeff, err = next(transcript, []{{.ElementType}}{}, &remainingChallengeNames); err != nil { - return proof, err - } - } - +// sumcheckProve creates a non-interactive sumcheck proof. +// The fold challenge is derived by the caller (proveLevel). +// Pattern: roundPolynomial, [roundFold, roundPolynomial]*, proveFinalEval. +func sumcheckProve(claims sumcheckClaims, t *transcript) sumcheckProof { varsNum := claims.varsNum() - proof.partialSumPolys = make([]polynomial.Polynomial, varsNum) - proof.partialSumPolys[0] = claims.fold(foldingCoeff) - challenges := make([]{{.ElementType}}, varsNum) - - for j := 0; j+1 < varsNum; j++ { - if challenges[j], err = next(transcript, proof.partialSumPolys[j], &remainingChallengeNames); err != nil { - return proof, err - } - proof.partialSumPolys[j+1] = claims.next(challenges[j]) - } - - if challenges[varsNum-1], err = next(transcript, proof.partialSumPolys[varsNum-1], &remainingChallengeNames); err != nil { - return proof, err + proof := sumcheckProof{partialSumPolys: make([]polynomial.Polynomial, varsNum)} + proof.partialSumPolys[0] = claims.roundPolynomial() + challenges := make([]{{ .ElementType }}, varsNum) + + for j := range varsNum - 1 { + challenges[j] = t.getChallenge(proof.partialSumPolys[j]...) + claims.roundFold(challenges[j]) + proof.partialSumPolys[j+1] = claims.roundPolynomial() } + challenges[varsNum-1] = t.getChallenge(proof.partialSumPolys[varsNum-1]...) proof.finalEvalProof = claims.proveFinalEval(challenges) - - return proof, nil + return proof } -func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSettings fiatshamir.Settings) error { - remainingChallengeNames, err := setupTranscript(claims.claimsNum(), claims.varsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return err - } - - var foldingCoeff {{.ElementType}} +// sumcheckVerify verifies a non-interactive sumcheck proof. +// The fold challenge is derived by the caller (verifyLevel). +// claimedSum is the expected sum; degree is the polynomial's degree in each variable. +func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, claimedSum {{ .ElementType }}, degree int, t *transcript) error { + r := make([]{{ .ElementType }}, claims.varsNum()) - if claims.claimsNum() >= 2 { - if foldingCoeff, err = next(transcript, []{{.ElementType}}{}, &remainingChallengeNames); err != nil { - return err - } - } - - r := make([]{{.ElementType}}, claims.varsNum()) - - // Just so that there is enough room for gJ to be reused - maxDegree := claims.degree(0) - for j := 1; j < claims.varsNum(); j++ { - if d := claims.degree(j); d > maxDegree { - maxDegree = d - } - } - gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() - gJR := claims.foldedSum(foldingCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) + gJ := make(polynomial.Polynomial, degree+1) + gJR := claimedSum for j := range claims.varsNum() { - if len(proof.partialSumPolys[j]) != claims.degree(j) { + if len(proof.partialSumPolys[j]) != degree { return errors.New("malformed proof") } copy(gJ[1:], proof.partialSumPolys[j]) - gJ[0].Sub(&gJR, &proof.partialSumPolys[j][0]) // Requirement that gⱼ(0) + gⱼ(1) = gⱼ₋₁(r) - // gJ is ready + gJ[0].Sub(&gJR, &proof.partialSumPolys[j][0]) - //Prepare for the next iteration - if r[j], err = next(transcript, proof.partialSumPolys[j], &remainingChallengeNames); err != nil { - return err - } - // This is an extremely inefficient way of interpolating. TODO: Interpolate without symbolically computing a polynomial - gJCoeffs := polynomial.InterpolateOnRange(gJ[:(claims.degree(j) + 1)]) + r[j] = t.getChallenge(proof.partialSumPolys[j]...) + gJCoeffs := polynomial.InterpolateOnRange(gJ[:(degree + 1)]) gJR = gJCoeffs.Eval(&r[j]) } - return claims.verifyFinalEval(r, foldingCoeff, gJR, proof.finalEvalProof) + return claims.verifyFinalEval(r, gJR, proof.finalEvalProof) } diff --git a/internal/generator/backend/template/gkr/sumcheck.test.defs.go.tmpl b/internal/generator/backend/template/gkr/sumcheck.test.defs.go.tmpl index 472898669c..54a5c00b85 100644 --- a/internal/generator/backend/template/gkr/sumcheck.test.defs.go.tmpl +++ b/internal/generator/backend/template/gkr/sumcheck.test.defs.go.tmpl @@ -4,41 +4,36 @@ type singleMultilinClaim struct { g polynomial.MultiLin } -func (c singleMultilinClaim) proveFinalEval(r []{{.ElementType}}) []{{.ElementType}} { +func (c *singleMultilinClaim) proveFinalEval(r []{{ .ElementType }}) []{{ .ElementType }} { return nil // verifier can compute the final eval itself } -func (c singleMultilinClaim) varsNum() int { +func (c *singleMultilinClaim) varsNum() int { return bits.TrailingZeros(uint(len(c.g))) } -func (c singleMultilinClaim) claimsNum() int { - return 1 -} - func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { sum := g[len(g)/2] for i := len(g)/2 + 1; i < len(g); i++ { sum.Add(&sum, &g[i]) } - return []{{.ElementType}}{sum} + return []{{ .ElementType }}{sum} } -func (c singleMultilinClaim) fold({{.ElementType}}) polynomial.Polynomial { +func (c *singleMultilinClaim) roundPolynomial() polynomial.Polynomial { return sumForX1One(c.g) } -func (c *singleMultilinClaim) next(r {{.ElementType}}) polynomial.Polynomial { +func (c *singleMultilinClaim) roundFold(r {{ .ElementType }}) { c.g.Fold(r) - return sumForX1One(c.g) } type singleMultilinLazyClaim struct { g polynomial.MultiLin - claimedSum {{.ElementType}} + claimedSum {{ .ElementType }} } -func (c singleMultilinLazyClaim) verifyFinalEval(r []{{.ElementType}}, _ {{.ElementType}}, purportedValue {{.ElementType}}, proof []{{.ElementType}}) error { +func (c singleMultilinLazyClaim) verifyFinalEval(r []{{ .ElementType }}, purportedValue {{ .ElementType }}, proof []{{ .ElementType }}) error { val := c.g.Evaluate(r, nil) if val.Equal(&purportedValue) { return nil @@ -46,15 +41,7 @@ func (c singleMultilinLazyClaim) verifyFinalEval(r []{{.ElementType}}, _ {{.Elem return fmt.Errorf("mismatch") } -func (c singleMultilinLazyClaim) foldedSum(_ {{.ElementType}}) {{.ElementType}} { - return c.claimedSum -} - -func (c singleMultilinLazyClaim) degree(i int) int { - return 1 -} - -func (c singleMultilinLazyClaim) claimsNum() int { +func (c singleMultilinLazyClaim) degree(int) int { return 1 } @@ -62,4 +49,4 @@ func (c singleMultilinLazyClaim) varsNum() int { return bits.TrailingZeros(uint(len(c.g))) } -{{ end }} \ No newline at end of file +{{ end }} diff --git a/internal/generator/backend/template/gkr/sumcheck.test.go.tmpl b/internal/generator/backend/template/gkr/sumcheck.test.go.tmpl index 72e0f76326..146a67727d 100644 --- a/internal/generator/backend/template/gkr/sumcheck.test.go.tmpl +++ b/internal/generator/backend/template/gkr/sumcheck.test.go.tmpl @@ -1,11 +1,10 @@ import ( "fmt" - "{{.FieldPackagePath}}/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + "{{ .FieldPackagePath }}/polynomial" "github.com/stretchr/testify/assert" "hash" {{ if not .GenerateTestVectors}} - "{{.FieldPackagePath}}" + "{{ .FieldPackagePath }}" "math/bits" {{ end }} "strings" @@ -19,11 +18,9 @@ func testSumcheckSingleClaimMultilin(polyInt []uint64, hashGenerator func() hash } claim := singleMultilinClaim{g: poly.Clone()} + t := transcript{h: hashGenerator()} - proof, err := sumcheckProve(&claim, fiatshamir.WithHash(hashGenerator())) - if err != nil { - return err - } + proof := sumcheckProve(&claim, &t) var sb strings.Builder for _, p := range proof.partialSumPolys { @@ -39,13 +36,15 @@ func testSumcheckSingleClaimMultilin(polyInt []uint64, hashGenerator func() hash } lazyClaim := singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if err = sumcheckVerify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())); err != nil { + t = transcript{h: hashGenerator()} + if err := sumcheckVerify(lazyClaim, proof, lazyClaim.claimedSum, 1, &t); err != nil { return err } proof.partialSumPolys[0][0].Add(&proof.partialSumPolys[0][0], toElement(1)) lazyClaim = singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if sumcheckVerify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())) == nil { + t = transcript{h: hashGenerator()} + if sumcheckVerify(lazyClaim, proof, lazyClaim.claimedSum, 1, &t) == nil { return fmt.Errorf("bad proof accepted") } return nil @@ -82,4 +81,4 @@ func TestSumcheckDeterministicHashSingleClaimMultilin(t *testing.T) { {{ if not .GenerateTestVectors }} {{ template "sumcheckTestDefs" .}} -{{ end }} \ No newline at end of file +{{ end }} diff --git a/internal/generator/backend/template/gkr/sumcheck.test.vectors.gen.go.tmpl b/internal/generator/backend/template/gkr/sumcheck.test.vectors.gen.go.tmpl index d47ce5bc3f..de9055e2f4 100644 --- a/internal/generator/backend/template/gkr/sumcheck.test.vectors.gen.go.tmpl +++ b/internal/generator/backend/template/gkr/sumcheck.test.vectors.gen.go.tmpl @@ -1,7 +1,6 @@ import ( "encoding/json" "fmt" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" "github.com/consensys/gnark/internal/gkr/gkrtesting" @@ -30,11 +29,9 @@ func runMultilin(testCaseInfo *sumcheckTestCaseInfo) error { return err } - proof, err := sumcheckProve( - &singleMultilinClaim{poly}, fiatshamir.WithHash(hsh)) - if err != nil { - return err - } + claim := singleMultilinClaim{poly} + t := transcript{h: hsh} + proof := sumcheckProve(&claim, &t) testCaseInfo.Proof = sumcheckToPrintableProof(proof) // Verification @@ -48,12 +45,20 @@ func runMultilin(testCaseInfo *sumcheckTestCaseInfo) error { return err } - if err = sumcheckVerify(singleMultilinLazyClaim{g: poly, claimedSum: claimedSum}, proof, fiatshamir.WithHash(hsh)); err != nil { + if hsh, err = hashFromDescription(testCaseInfo.Hash); err != nil { + return err + } + t = transcript{h: hsh} + if err = sumcheckVerify(singleMultilinLazyClaim{g: poly, claimedSum: claimedSum}, proof, claimedSum, 1, &t); err != nil { return fmt.Errorf("proof rejected: %v", err) } proof.partialSumPolys[0][0].Add(&proof.partialSumPolys[0][0], toElement(1)) - if err = sumcheckVerify(singleMultilinLazyClaim{g: poly, claimedSum: claimedSum}, proof, fiatshamir.WithHash(hsh)); err == nil { + if hsh, err = hashFromDescription(testCaseInfo.Hash); err != nil { + return err + } + t = transcript{h: hsh} + if err = sumcheckVerify(singleMultilinLazyClaim{g: poly, claimedSum: claimedSum}, proof, claimedSum, 1, &t); err == nil { return fmt.Errorf("bad proof accepted") } @@ -138,4 +143,4 @@ func sumcheckToPrintableProof(proof sumcheckProof) (printable SumcheckPrintableP return } -{{ template "sumcheckTestDefs" .}} \ No newline at end of file +{{ template "sumcheckTestDefs" .}} diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index b15707a087..9461400e99 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -15,7 +15,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" @@ -204,6 +203,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + Schedule constraint.GkrProvingSchedule HashName string lock sync.Mutex @@ -256,9 +256,11 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra } } + // Create hasher and write base challenges + hsh := hash.NewHash(b.HashName + "_BLS12_377") + // Read initial challenges from instruction calldata (parse dynamically, no metadata) // Format: [0]=totalSize, [1...]=challenge linear expressions - insBytes := make([][]byte, 0) // first challenges calldata := inst.Calldata[1:] // skip size prefix for len(calldata) != 0 { val, delta := s.Read(calldata) @@ -267,17 +269,14 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) var challenge fr.Element copy(challenge[:], val[:]) - insBytes = append(insBytes, challenge.Marshal()) + challengeBytes := challenge.Bytes() + hsh.Write(challengeBytes[:]) } - // Create Fiat-Shamir settings - hsh := hash.NewHash(b.HashName + "_BLS12_377") - fsSettings := fiatshamir.WithHash(hsh, insBytes...) - // Call the BLS12_377-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, b.Schedule, assignments, hsh) if err != nil { - return fmt.Errorf("bls12_377 prove failed: %w", err) + return fmt.Errorf("BLS12_377 prove failed: %w", err) } for i, elem := range proof.flatten() { @@ -305,7 +304,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) + return gkrcore.ProofSize(b.Schedule, b.SolveBlueprint.Circuit, logNbInstances) } // NbOutputs implements Blueprint @@ -434,7 +433,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_377 -func NewBlueprints(circuit gkrcore.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrcore.Blueprints { +func NewBlueprints(circuit gkrcore.SerializableCircuit, schedule constraint.GkrProvingSchedule, hashName string, compiler constraint.CustomizableSystem) gkrcore.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) @@ -443,6 +442,7 @@ func NewBlueprints(circuit gkrcore.SerializableCircuit, hashName string, compile prove := &BlueprintProve{ SolveBlueprintID: solveID, SolveBlueprint: solve, + Schedule: schedule, HashName: hashName, } proveID := compiler.AddBlueprint(prove) diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 62dd4b4879..3ff62f2c0a 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -8,14 +8,14 @@ package gkr import ( "errors" "fmt" + "hash" "iter" - "strconv" "sync" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" ) @@ -33,630 +33,487 @@ type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) // zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). -// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear -// extension of the values of w across all instances. -// Its purpose is to batch the checking of multiple evaluations of the same wire. +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, +// where the sum runs over all wᵢ and evaluation point xᵢ in the level. +// Its purpose is to batch the checking of multiple wire evaluations at evaluation points. type zeroCheckLazyClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly - manager *claimsManager // WARNING: Circular references -} - -func (e *zeroCheckLazyClaims) getWire() Wire { - return e.manager.circuit[e.wireI] -} - -func (e *zeroCheckLazyClaims) claimsNum() int { - return len(e.evaluationPoints) + foldingCoeff fr.Element // the coefficient used to fold claims, conventionally 0 if there is only one claim + resources *resources + levelI int } func (e *zeroCheckLazyClaims) varsNum() int { - return len(e.evaluationPoints[0]) -} - -// foldedSum returns ∑ᵢ aⁱ yᵢ -func (e *zeroCheckLazyClaims) foldedSum(a fr.Element) fr.Element { - evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) - return evalsAsPoly.Eval(&a) + return e.resources.nbVars } func (e *zeroCheckLazyClaims) degree(int) int { - return e.manager.circuit[e.wireI].ZeroCheckDegree() -} - -// verifyFinalEval finalizes the verification of w. -// The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. (c is foldingCoeff) -// Both purportedValue and the vector r have been randomized during the sumcheck protocol. -// By taking the w term out of the sum we get the equivalent claim that -// for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. -// If w is an input wire, the verifier can directly check its evaluation at r. -// Otherwise, the prover makes claims about the evaluation of w's input wires, -// wᵢ, at r, to be verified later. -// The claims are communicated through the proof parameter. -// The verifier checks here if the claimed evaluations of wᵢ(r) are consistent with -// the main claim, by checking E w(wᵢ(r)...) = purportedValue. -func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, foldingCoeff, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { - // the eq terms ( E ) - numClaims := len(e.evaluationPoints) - evaluation := polynomial.EvalEq(e.evaluationPoints[numClaims-1], r) - for i := numClaims - 2; i >= 0; i-- { - evaluation.Mul(&evaluation, &foldingCoeff) - eq := polynomial.EvalEq(e.evaluationPoints[i], r) - evaluation.Add(&evaluation, &eq) - } - - wire := e.manager.circuit[e.wireI] - - // the w(...) term - var gateEvaluation fr.Element - if wire.IsInput() { // just compute w(r) - gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) - } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire - injection, injectionLeftInv := - e.manager.circuit.ClaimPropagationInfo(e.wireI) - - if len(injection) != len(uniqueInputEvaluations) { - return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) - } - - for uniqueI, i := range injection { // map from unique to all - e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) - } + return gkrcore.Degree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel), e.resources.circuit) +} + +// verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. +// The sumcheck protocol has already reduced the per-wire claims w(xᵢ) = yᵢ to verifying +// ∑ᵢ cⁱ eq(xᵢ, r) · wᵢ(r) = purportedValue, where the sum runs over all +// claims on each wire and c is foldingCoeff. +// Both purportedValue and the vector r have been randomized during sumcheck. +// +// For input wires, w(r) is computed directly from the assignment. +// For non-input wires, the prover claims evaluations of the input wires at r, +// communicated through uniqueInputEvaluations; those claims are verified later. +// The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming +// that the full sum matches purportedValue. +func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { + e.resources.levelPoints[e.levelI] = r + level := e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel) + perWireInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) + + var terms []fr.Element + flatW := 0 + for _, group := range level { + for _, wI := range group.Wires { + wire := e.resources.circuit[wI] + + var gateEval fr.Element + if wire.IsInput() { + gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) + } else { + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) + for _, v := range perWireInputEvals[flatW] { + evaluator.pushInput(v) + } + gateEval.Set(evaluator.evaluate()) + } - evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) - for _, uniqueI := range injectionLeftInv { // map from all to unique - evaluator.pushInput(uniqueInputEvaluations[uniqueI]) + for _, src := range group.ClaimSources { + eq := polynomial.EvalEq(e.resources.levelPoints[src], r) + var term fr.Element + term.Mul(&eq, &gateEval) + terms = append(terms, term) + } + flatW++ } - - gateEvaluation.Set(evaluator.evaluate()) } - evaluation.Mul(&evaluation, &gateEvaluation) - - if evaluation.Equal(&purportedValue) { + ys := polynomial.Polynomial(terms) + if total := ys.Eval(&e.foldingCoeff); total.Equal(&purportedValue) { return nil } return errors.New("incompatible evaluations") } // zeroCheckClaims is a claim for sumcheck (prover side). -// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear -// extension of the values of w across all instances. -// Its purpose is to batch the proving of multiple evaluations of the same wire. +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, +// where the sum runs over all (wire v, claim source s) pairs in the level. +// Each wire has its own eq table with the batching coefficients baked in. type zeroCheckClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []fr.Element // yᵢ = w(xᵢ) - manager *claimsManager - - input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) - - eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) - - gateEvaluatorPool *gateEvaluatorPool -} - -func (c *zeroCheckClaims) getWire() Wire { - return c.manager.circuit[c.wireI] + level constraint.GkrSumcheckLevel + levelI int + resources *resources + input []polynomial.MultiLin // UniqueGateInputs order + inputIndices [][]int // [wireInLevel][gateInputJ] → index in input + eqs []polynomial.MultiLin // per-wire interpolation bases for evaluating wire assignments at challenge points + gateEvaluatorPools []*gateEvaluatorPool } -// fold the multiple claims into one claim using a random combination (foldingCoeff or c). -// From the original multiple claims of w(xᵢ) = yᵢ, we get a single claim -// ∑ᵢ,ₕ cⁱ eq(xᵢ, h) w(h) = ∑ᵢ cⁱ yᵢ, where h iterates over the hypercube (circuit instances) and -// i iterates over the claims. -// Equivalently, we could say ∑ᵢ cⁱ yᵢ = ∑ₕ,ᵢ cⁱ eq(xᵢ, h) w(h) = ∑ₕ w(h) ∑ᵢ cⁱ eq(xᵢ, h). -// Thus if we initially compute E := ∑ᵢ cⁱ eq(xᵢ, -), our claim will find the simpler form -// ∑ᵢ cⁱ yᵢ = ∑ₕ w(h) E(h), where the sum-checked polynomial is of degree deg(g) + 1, -// and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. -// The output of fold is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. -func (c *zeroCheckClaims) fold(foldingCoeff fr.Element) polynomial.Polynomial { - varsNum := c.varsNum() - eqLength := 1 << varsNum - claimsNum := c.claimsNum() - // initialize the eq tables ( E ) - c.eq = c.manager.memPool.Make(eqLength) - - c.eq[0].SetOne() - c.eq.Eq(c.evaluationPoints[0]) - - // E := eq(x₀, -) - newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) - aI := foldingCoeff - - // E += cⁱ eq(xᵢ, -) - for k := 1; k < claimsNum; k++ { - newEq[0].Set(&aI) - - c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) - - if k+1 < claimsNum { - aI.Mul(&aI, &foldingCoeff) - } - } - - c.manager.memPool.Dump(newEq) - - return c.computeGJ() -} - -// eqAcc sets m to an eq table at q and then adds it to e. -// m <- eq(q, -). -// e <- e + m -func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { - n := len(q) - - //At the end of each iteration, m(h₁, ..., hₙ) = eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) - for i := range q { // In the comments we use a 1-based index so q[i] = qᵢ₊₁ - // go through all assignments of (b₁, ..., bᵢ) ∈ {0,1}ⁱ - const threshold = 1 << 6 - k := 1 << i - if k < threshold { - for j := 0; j < k; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - } else { - c.manager.workers.Submit(k, func(start, end int) { - for j := start; j < end; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - }, 1024).Wait() - } - - } - c.manager.workers.Submit(len(e), func(start, end int) { - for i := start; i < end; i++ { - e[i].Add(&e[i], &m[i]) - } - }, 512).Wait() +func (c *zeroCheckClaims) varsNum() int { + return len(c.resources.levelPoints[c.level[0].ClaimSources[0]]) } -// computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). -// the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). -// The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. -func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { - - wire := c.getWire() - degGJ := wire.ZeroCheckDegree() // guaranteed to be no smaller than the actual deg(gⱼ) - nbGateIn := len(c.input) - - // Both E and wᵢ (the input wires and the eq table) are multilinear, thus - // they are linear in Xⱼ. - // So for f ∈ { E(r₁, ..., Xⱼ, h...) } ∪ {wᵢ(r₁, ..., Xⱼ, h...) }, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables. - // ml are such multilinear polynomials the evaluations of which over different values of Xⱼ are computed in this stepwise manner. - ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube - ml[0] = c.eq - copy(ml[1:], c.input) +// roundPolynomial computes gⱼ = ∑ₕ ∑ᵥ eqs[v](Xⱼ, h...) · gateᵥ(inputs(Xⱼ, h...)). +// The polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). +// The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). +// By convention, g₀ is a constant polynomial equal to the claimed sum. +func (c *zeroCheckClaims) roundPolynomial() polynomial.Polynomial { + degree := gkrcore.Degree(c.level, c.resources.circuit) + nbUniqueInputs := len(c.input) + nbWires := len(c.eqs) - sumSize := len(c.eq) / 2 // the range of h, over which we sum + // Both eqs and input are multilinear, thus linear in Xⱼ. + // For any such f, f(m) = m·(f(1) - f(0)) + f(0), and f(0), f(1) are read directly + // from the bookkeeping tables. This allows stepwise evaluation at Xⱼ = 1, 2, ..., degree. + // Layout: [eq₀, eq₁, ..., eq_{nbWires-1}, input₀, input₁, ..., input_{nbUniqueInputs-1}] + ml := make([]polynomial.MultiLin, nbWires+nbUniqueInputs) + copy(ml, c.eqs) + copy(ml[nbWires:], c.input) - // Perf-TODO: Collate once at claim "folding" time and not again. then, even folding can be done in one operation every time "next" is called + sumSize := len(c.eqs[0]) / 2 - gJ := make([]fr.Element, degGJ) + p := make([]fr.Element, degree) var mu sync.Mutex - computeAll := func(start, end int) { // compute method to allow parallelization across instances + computeAll := func(start, end int) { var step fr.Element - evaluator := c.gateEvaluatorPool.get() - defer c.gateEvaluatorPool.put(evaluator) + evaluators := make([]*gateEvaluator, nbWires) + for w := range nbWires { + evaluators[w] = c.gateEvaluatorPools[w].get() + } + defer func() { + for w := range nbWires { + c.gateEvaluatorPools[w].put(evaluators[w]) + } + }() - res := make([]fr.Element, degGJ) + res := make([]fr.Element, degree) // evaluations of ml, laid out as: // ml[0](1, h...), ml[1](1, h...), ..., ml[len(ml)-1](1, h...), // ml[0](2, h...), ml[1](2, h...), ..., ml[len(ml)-1](2, h...), // ... - // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) - mlEvals := make([]fr.Element, degGJ*len(ml)) - - for h := start; h < end; h++ { // h counts across instances + // ml[0](degree, h...), ml[1](degree, h...), ..., ml[len(ml)-1](degree, h...) + mlEvals := make([]fr.Element, degree*len(ml)) + for h := start; h < end; h++ { evalAt1Index := sumSize + h for k := range ml { - // d = 0 - mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. + mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1, taken directly from the table step.Sub(&mlEvals[k], &ml[k][h]) // step = ml[k](1) - ml[k](0) - for d := 1; d < degGJ; d++ { + for d := 1; d < degree; d++ { mlEvals[d*len(ml)+k].Add(&mlEvals[(d-1)*len(ml)+k], &step) } } - eIndex := 0 // index for where the current eq term is + eIndex := 0 // start of the current row's eq evaluations nextEIndex := len(ml) - for d := range degGJ { - // Push gate inputs - for i := range nbGateIn { - evaluator.pushInput(mlEvals[eIndex+1+i]) + for d := range degree { + for w := range nbWires { + for _, inputI := range c.inputIndices[w] { + evaluators[w].pushInput(mlEvals[eIndex+nbWires+inputI]) + } + summand := evaluators[w].evaluate() + summand.Mul(summand, &mlEvals[eIndex+w]) + res[d].Add(&res[d], summand) // collect contributions into the sum from start to end } - summand := evaluator.evaluate() - summand.Mul(summand, &mlEvals[eIndex]) - res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } } mu.Lock() - for i := range gJ { - gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum + for i := range p { + p[i].Add(&p[i], &res[i]) // collect into the complete sum } mu.Unlock() } const minBlockSize = 64 - if sumSize < minBlockSize { - // no parallelization computeAll(0, sumSize) } else { - c.manager.workers.Submit(sumSize, computeAll, minBlockSize).Wait() + c.resources.workers.Submit(sumSize, computeAll, minBlockSize).Wait() } - return gJ + return p } -// next first folds the input and E polynomials at the given verifier challenge then computes the new gⱼ. -// Thus, j <- j+1 and rⱼ = challenge. -func (c *zeroCheckClaims) next(challenge fr.Element) polynomial.Polynomial { +// roundFold folds all input and eq polynomials at the verifier challenge r. +// After this call, j ← j+1 and rⱼ = r. +func (c *zeroCheckClaims) roundFold(r fr.Element) { const minBlockSize = 512 - n := len(c.eq) / 2 + n := len(c.eqs[0]) / 2 if n < minBlockSize { - // no parallelization for i := range c.input { - c.input[i].Fold(challenge) + c.input[i].Fold(r) + } + for i := range c.eqs { + c.eqs[i].Fold(r) } - c.eq.Fold(challenge) } else { - wgs := make([]*sync.WaitGroup, len(c.input)) + wgs := make([]*sync.WaitGroup, len(c.input)+len(c.eqs)) for i := range c.input { - wgs[i] = c.manager.workers.Submit(n, c.input[i].FoldParallel(challenge), minBlockSize) + wgs[i] = c.resources.workers.Submit(n, c.input[i].FoldParallel(r), minBlockSize) + } + for i := range c.eqs { + wgs[len(c.input)+i] = c.resources.workers.Submit(n, c.eqs[i].FoldParallel(r), minBlockSize) } - c.manager.workers.Submit(n, c.eq.FoldParallel(challenge), minBlockSize).Wait() for _, wg := range wgs { wg.Wait() } } - - return c.computeGJ() -} - -func (c *zeroCheckClaims) varsNum() int { - return len(c.evaluationPoints[0]) -} - -func (c *zeroCheckClaims) claimsNum() int { - return len(c.claimedEvaluations) } -// proveFinalEval provides the values wᵢ(r₁, ..., rₙ) +// proveFinalEval provides the unique input wire values wᵢ(r₁, ..., rₙ). func (c *zeroCheckClaims) proveFinalEval(r []fr.Element) []fr.Element { - //defer the proof, return list of claims - - injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. - evaluations := make([]fr.Element, len(injection)) - for i, gateInputI := range injection { - wI := c.input[gateInputI] - wI.Fold(r[len(r)-1]) // We already have wᵢ(r₁, ..., rₙ₋₁, hₙ) in a table. Only one more fold required. - c.manager.add(c.getWire().Inputs[gateInputI], r, wI[0]) - evaluations[i] = wI[0] + c.resources.levelPoints[c.levelI] = r + evaluations := make([]fr.Element, len(c.input)) + for i := range c.input { + c.input[i].Fold(r[len(r)-1]) + evaluations[i] = c.input[i][0] } - - c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.dumpAll() - - return evaluations -} - -type claimsManager struct { - claims []*zeroCheckLazyClaims - assignment WireAssignment - memPool *polynomial.Pool - workers *utils.WorkerPool - circuit Circuit -} - -func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { - manager.assignment = assignment - manager.claims = make([]*zeroCheckLazyClaims, len(circuit)) - manager.memPool = o.pool - manager.workers = o.workers - manager.circuit = circuit - - for i := range circuit { - manager.claims[i] = &zeroCheckLazyClaims{ - wireI: i, - evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), - claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), - manager: &manager, - } + for i := range c.input { + c.resources.memPool.Dump(c.input[i]) } - return -} - -func (m *claimsManager) add(wire int, evaluationPoint []fr.Element, evaluation fr.Element) { - claim := m.claims[wire] - i := len(claim.evaluationPoints) - claim.claimedEvaluations[i] = evaluation - claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) + for i := range c.eqs { + c.resources.memPool.Dump(c.eqs[i]) + } + for _, pool := range c.gateEvaluatorPools { + pool.dumpAll() + } + return evaluations } -func (m *claimsManager) getLazyClaim(wire int) *zeroCheckLazyClaims { - return m.claims[wire] -} +// eqAcc sets m to an eq table at q and then adds it to e. +// m <- m[0] · eq(q, -). +// e <- e + m +func (r *resources) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { + n := len(q) -func (m *claimsManager) getClaim(wireI int) *zeroCheckClaims { - lazy := m.claims[wireI] - wire := m.circuit[wireI] - res := &zeroCheckClaims{ - wireI: wireI, - evaluationPoints: lazy.evaluationPoints, - claimedEvaluations: lazy.claimedEvaluations, - manager: m, - } + // At the end of each iteration, m(h₁, ..., hₙ) = m[0] · eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) + for i := range q { // 1-based in comments: q[i] = qᵢ₊₁ + // go through all assignments of (b₁, ..., bᵢ) ∈ {0,1}ⁱ + const threshold = 1 << 6 + k := 1 << i + if k < threshold { + for j := 0; j < k; j++ { + j0 := j << (n - i) // bᵢ₊₁ = 0 + j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - if wire.IsInput() { - res.input = []polynomial.MultiLin{m.memPool.Clone(m.assignment[wireI])} - } else { - res.input = make([]polynomial.MultiLin, len(wire.Inputs)) + m[j1].Mul(&q[i], &m[j0]) // m(b₁,...,bᵢ,1) = m(b₁,...,bᵢ) · qᵢ₊₁ + m[j0].Sub(&m[j0], &m[j1]) // m(b₁,...,bᵢ,0) = m(b₁,...,bᵢ) · (1 - qᵢ₊₁) + } + } else { + r.workers.Submit(k, func(start, end int) { + for j := start; j < end; j++ { + j0 := j << (n - i) // bᵢ₊₁ = 0 + j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - for inputI, inputW := range wire.Inputs { - res.input[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied + m[j1].Mul(&q[i], &m[j0]) // m(b₁,...,bᵢ,1) = m(b₁,...,bᵢ) · qᵢ₊₁ + m[j0].Sub(&m[j0], &m[j1]) // m(b₁,...,bᵢ,0) = m(b₁,...,bᵢ) · (1 - qᵢ₊₁) + } + }, 1024).Wait() } } - - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Evaluate, len(res.input), m.memPool) - - return res -} - -func (m *claimsManager) deleteClaim(wire int) { - m.claims[wire].manager = nil - m.claims[wire] = nil + r.workers.Submit(len(e), func(start, end int) { + for i := start; i < end; i++ { + e[i].Add(&e[i], &m[i]) + } + }, 512).Wait() } -type settings struct { - pool *polynomial.Pool - transcript *fiatshamir.Transcript - transcriptPrefix string - nbVars int - workers *utils.WorkerPool +type resources struct { + // levelPoints[i] is the sumcheck evaluation point r produced at schedule level i. + // levelPoints[len(schedule)] holds the initial challenge (firstChallenge / rho). + // SkipLevels produce no point; their slot remains nil. + levelPoints [][]fr.Element + evalPositions [][]int // [wireI][evalI] → position in source level's finalEvalProof + nbVars int + assignment WireAssignment + memPool polynomial.Pool + workers *utils.WorkerPool + circuit Circuit + schedule constraint.GkrProvingSchedule + transcript transcript } -type Option func(*settings) - -func WithPool(pool *polynomial.Pool) Option { - return func(options *settings) { - options.pool = pool +func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (resources, error) { + nbVars := assignment.NumVars() + nbInstances := assignment.NumInstances() + if 1<= 2 { + foldingCoeff = r.transcript.getChallenge() } - if o.pool == nil { - pool := polynomial.NewPool(c.MemoryRequirements(nbInstances)...) - o.pool = &pool + uniqueInputs, inputIndices := gkrcore.InputMapping(level, r.circuit) + input := make([]polynomial.MultiLin, len(uniqueInputs)) + for i, inW := range uniqueInputs { + input[i] = r.memPool.Clone(r.assignment[inW]) } - if o.workers == nil { - o.workers = utils.NewWorkerPool() + nbWires := 0 + for _, group := range level { + nbWires += len(group.Wires) } - if transcriptSettings.Transcript == nil { - challengeNames := ChallengeNames(c, o.nbVars, transcriptSettings.Prefix) - o.transcript = fiatshamir.NewTranscript(transcriptSettings.Hash, challengeNames...) - for i := range transcriptSettings.BaseChallenges { - if err = o.transcript.Bind(challengeNames[0], transcriptSettings.BaseChallenges[i]); err != nil { - return o, err + pools := make([]*gateEvaluatorPool, nbWires) + flatW := 0 + for _, group := range level { + for _, wI := range group.Wires { + wire := r.circuit[wI] + gate := wire.Gate.Evaluate + if wire.IsInput() { + gate = gkrcore.IdentityBytecode() } + pools[flatW] = newGateEvaluatorPool(gate, len(inputIndices[flatW]), &r.memPool) + flatW++ } - } else { - o.transcript, o.transcriptPrefix = transcriptSettings.Transcript, transcriptSettings.Prefix } - return o, err -} - -func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { - - // Pre-compute the size TODO: Consider not doing this and just grow the list by appending - size := logNbInstances // first challenge - - for i := range c { - if c[i].NoProof() { // no proof, no challenge - continue - } - if c[i].NbClaims() > 1 { //fold the claims - size++ + eqLength := 1 << r.nbVars + eqs := make([]polynomial.MultiLin, nbWires) + var alpha fr.Element + alpha.SetOne() + flatW = 0 + for _, group := range level { + nbSources := len(group.ClaimSources) + + groupEq := polynomial.MultiLin(r.memPool.Make(eqLength)) + groupEq[0].Set(&alpha) + groupEq.Eq(r.levelPoints[group.ClaimSources[0]]) + + if nbSources > 1 { + newEq := polynomial.MultiLin(r.memPool.Make(eqLength)) + aI := alpha + for k := 1; k < nbSources; k++ { + aI.Mul(&aI, &foldingCoeff) + newEq[0].Set(&aI) + r.eqAcc(groupEq, newEq, r.levelPoints[group.ClaimSources[k]]) + } + r.memPool.Dump(newEq) } - size += logNbInstances // full run of sumcheck on logNbInstances variables - } - nums := make([]string, max(len(c), logNbInstances)) - for i := range nums { - nums[i] = strconv.Itoa(i) - } - - challenges := make([]string, size) - - // output wire claims - firstChallengePrefix := prefix + "fC." - for j := 0; j < logNbInstances; j++ { - challenges[j] = firstChallengePrefix + nums[j] - } - j := logNbInstances - for i := len(c) - 1; i >= 0; i-- { - if c[i].NoProof() { - continue + var stride fr.Element + stride.Set(&foldingCoeff) + for range nbSources - 1 { + stride.Mul(&stride, &foldingCoeff) } - wirePrefix := prefix + "w" + nums[i] + "." - if c[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "fold" - j++ - } + eqs[flatW] = groupEq + flatW++ + alpha.Mul(&alpha, &stride) - partialSumPrefix := wirePrefix + "pSP." - for k := 0; k < logNbInstances; k++ { - challenges[j] = partialSumPrefix + nums[k] - j++ + for w := 1; w < len(group.Wires); w++ { + eqs[flatW] = polynomial.MultiLin(r.memPool.Make(eqLength)) + r.workers.Submit(eqLength, func(start, end int) { + for i := start; i < end; i++ { + eqs[flatW][i].Mul(&eqs[flatW-1][i], &stride) + } + }, 512).Wait() + flatW++ + alpha.Mul(&alpha, &stride) } } - return challenges -} -func getFirstChallengeNames(logNbInstances int, prefix string) []string { - res := make([]string, logNbInstances) - firstChallengePrefix := prefix + "fC." - for i := 0; i < logNbInstances; i++ { - res[i] = firstChallengePrefix + strconv.Itoa(i) + claims := &zeroCheckClaims{ + level: level, + levelI: levelI, + resources: r, + input: input, + inputIndices: inputIndices, + eqs: eqs, + gateEvaluatorPools: pools, + } + return sumcheckProve(claims, &r.transcript) +} + +// verifyLevel runs the sumcheck verification for a SumcheckLevel: derives the fold challenge, +// computes the claimed sum, and calls sumcheckVerify. +func (r *resources) verifyLevel(levelI int, proof Proof) error { + level := r.schedule[levelI].(constraint.GkrSumcheckLevel) + initialChallengeI := len(r.schedule) + + nbClaims := gkrcore.NbClaims(level) + var foldingCoeff fr.Element + if nbClaims >= 2 { + foldingCoeff = r.transcript.getChallenge() + } + + var ys []fr.Element + for _, group := range level { + for _, wI := range group.Wires { + evalI := 0 + for _, src := range group.ClaimSources { + var y fr.Element + if src == initialChallengeI { + y = r.assignment[wI].Evaluate(r.levelPoints[src], &r.memPool) + } else { + y = proof[src].finalEvalProof[r.evalPositions[wI][evalI]] + evalI++ + } + ys = append(ys, y) + } + } } - return res -} -func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Element, error) { - res := make([]fr.Element, len(names)) - for i, name := range names { - if bytes, err := transcript.ComputeChallenge(name); err != nil { - return nil, err - } else if err = res[i].SetBytesCanonical(bytes); err != nil { - return nil, err - } + ysPoly := polynomial.Polynomial(ys) + claimedSum := ysPoly.Eval(&foldingCoeff) + + lazyClaims := &zeroCheckLazyClaims{ + foldingCoeff: foldingCoeff, + resources: r, + levelI: levelI, } - return res, nil + return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, gkrcore.Degree(level, r.circuit), &r.transcript) } // Prove consistency of the claimed assignment -func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { - o, err := setup(c, assignment, transcriptSettings, options...) +func Prove(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (Proof, error) { + r, err := newResources(c, schedule, assignment, hasher) if err != nil { return nil, err } - defer o.workers.Stop() + defer r.workers.Stop() - claims := newClaimsManager(c, assignment, o) + proof := make(Proof, len(schedule)) - proof := make(Proof, len(c)) - // firstChallenge called rho in the paper - var firstChallenge []fr.Element - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return nil, err + // Derive initial challenge point + firstChallenge := make([]fr.Element, r.nbVars) + for j := range r.nbVars { + firstChallenge[j] = r.transcript.getChallenge() } + r.levelPoints[len(schedule)] = firstChallenge - wirePrefix := o.transcriptPrefix + "w" - var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - - wire := c[i] - - if wire.IsOutput() { - claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) - } - - claim := claims.getClaim(i) - if wire.NoProof() { // input wires with one claim only - proof[i] = sumcheckProof{ - partialSumPolys: []polynomial.Polynomial{}, - finalEvalProof: []fr.Element{}, - } - } else { - if proof[i], err = sumcheckProve( - claim, fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err != nil { - return proof, err - } + for levelI := len(schedule) - 1; levelI >= 0; levelI-- { + switch s := schedule[levelI].(type) { + case constraint.GkrSkipLevel: + r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - baseChallenge = make([][]byte, len(proof[i].finalEvalProof)) - for j := range proof[i].finalEvalProof { - baseChallenge[j] = proof[i].finalEvalProof[j].Marshal() - } + case constraint.GkrSumcheckLevel: + proof[levelI] = r.proveLevel(levelI) + // Bind finalEvalProof for next level's challenge derivation + r.transcript.bind(proof[levelI].finalEvalProof...) } - // the verifier checks a single claim about input wires itself - claims.deleteClaim(i) } return proof, nil } -// Verify the consistency of the claimed output with the claimed input -// Unlike in Prove, the assignment argument need not be complete -func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { - o, err := setup(c, assignment, transcriptSettings, options...) +// Verify the consistency of the claimed output with the claimed input. +// Unlike in Prove, the assignment argument need not be complete. +func Verify(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, proof Proof, hasher hash.Hash) error { + r, err := newResources(c, schedule, assignment, hasher) if err != nil { return err } - defer o.workers.Stop() - - claims := newClaimsManager(c, assignment, o) + defer r.workers.Stop() - var firstChallenge []fr.Element - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return err + // Derive initial challenge point + firstChallenge := make([]fr.Element, r.nbVars) + for j := range r.nbVars { + firstChallenge[j] = r.transcript.getChallenge() } + r.levelPoints[len(schedule)] = firstChallenge - wirePrefix := o.transcriptPrefix + "w" - var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - wire := c[i] - - if wire.IsOutput() { - claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) - } - - proofW := proof[i] - claim := claims.getLazyClaim(i) - if wire.NoProof() { // input wires with one claim only - // make sure the proof is empty - if len(proofW.finalEvalProof) != 0 || len(proofW.partialSumPolys) != 0 { - return errors.New("no proof allowed for input wire with a single claim") - } + for levelI := len(schedule) - 1; levelI >= 0; levelI-- { + switch s := schedule[levelI].(type) { + case constraint.GkrSkipLevel: + r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - if wire.NbClaims() == 1 { // input wire - // simply evaluate and see if it matches - if len(claim.evaluationPoints) == 0 || len(claim.claimedEvaluations) == 0 { - return errors.New("missing input wire claim") - } - evaluation := assignment[i].Evaluate(claim.evaluationPoints[0], claims.memPool) - if !claim.claimedEvaluations[0].Equal(&evaluation) { - return errors.New("incorrect input wire claim") - } + case constraint.GkrSumcheckLevel: + if err = r.verifyLevel(levelI, proof); err != nil { + return fmt.Errorf("sumcheck proof rejected: %v", err) } - } else if err = sumcheckVerify( - claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { // incorporate prover claims about w's input into the transcript - baseChallenge = make([][]byte, len(proofW.finalEvalProof)) - for j := range baseChallenge { - baseChallenge[j] = proofW.finalEvalProof[j].Marshal() - } - } else { - return fmt.Errorf("sumcheck proof rejected: %v", err) //TODO: Any polynomials to dump? + // Bind finalEvalProof for next level's challenge derivation + r.transcript.bind(proof[levelI].finalEvalProof...) } - claims.deleteClaim(i) } return nil } diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index 9f98d97454..0c42aff870 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -19,10 +19,9 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/mimc" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - gcUtils "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrtesting" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" + "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/stretchr/testify/assert" ) @@ -69,36 +68,86 @@ func TestMimc(t *testing.T) { test(t, gkrtesting.MiMCCircuit(93)) } -func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - // Construct SerializableCircuit directly, bypassing CompileCircuit - // which would reset NbUniqueOutputs based on actual topology - circuit := gkrcore.SerializableCircuit{ - { - NbUniqueOutputs: 2, - Gate: gkrcore.SerializableGate{Degree: 1}, - }, - } +// testLevel exercises proveLevel/verifyLevel for a single sumcheck level. +func testLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrSumcheckLevel) { + t.Helper() + _, sCircuit := cache.Compile(t, circuit) - assignment := WireAssignment{[]fr.Element{two, three}} - var o settings - pool := polynomial.NewPool(256, 1<<11) - workers := gcUtils.NewWorkerPool() - o.pool = &pool - o.workers = workers - - claimsManagerGen := func() *claimsManager { - manager := newClaimsManager(circuit, assignment, o) - manager.add(0, []fr.Element{three}, five) - manager.add(0, []fr.Element{four}, six) - return &manager + ins := sCircuit.Inputs() + assignment := make(WireAssignment, len(sCircuit)) + for _, i := range ins { + assignment[i] = make([]fr.Element, 2) + fr.Vector(assignment[i]).MustSetRandom() } - transcriptGen := newMessageCounterGenerator(4, 1) + assignment.Complete(sCircuit) + + schedule := constraint.GkrProvingSchedule{level} + challenge := []fr.Element{five} - proof, err := sumcheckProve(claimsManagerGen().getClaim(0), fiatshamir.WithHash(transcriptGen(), nil)) + // Prove + proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) - err = sumcheckVerify(claimsManagerGen().getLazyClaim(0), proof, fiatshamir.WithHash(transcriptGen(), nil)) + defer proveR.workers.Stop() + + proveR.levelPoints[1] = challenge + proof := Proof{proveR.proveLevel(0)} + + // Verify + verifyR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) + defer verifyR.workers.Stop() + + verifyR.levelPoints[1] = challenge + assert.NoError(t, verifyR.verifyLevel(0, proof)) +} + +func TestLevel(t *testing.T) { + // Wires 0,1 = inputs; wires 2,3,4 = mul(0,1). All gates are independent outputs. + circuit := gkrcore.RawCircuit{ + {}, + {}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, + } + // All levels have initial challenge at index 1 (len(schedule) = 1). + tests := []struct { + name string + level constraint.GkrSumcheckLevel + }{ + { + name: "single wire", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []int{1}}, + }, + }, + { + name: "two groups", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []int{1}}, + {Wires: []int{3}, ClaimSources: []int{1}}, + }, + }, + { + name: "one group with two wires", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4, 3}, ClaimSources: []int{1}}, + }, + }, + { + name: "mixed: single + multi-wire group", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []int{1}}, + {Wires: []int{3, 2}, ClaimSources: []int{1}}, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testLevel(t, circuit, tc.level) + }) + } } var one, two, three, four, five, six fr.Element @@ -133,7 +182,16 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrcore.RawCircuit) { + testWithSchedule(t, circuit, nil) +} + +func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constraint.GkrProvingSchedule) { gCircuit, sCircuit := cache.Compile(t, circuit) + if schedule == nil { + var err error + schedule, err = gkrcore.DefaultProvingSchedule(sCircuit) + assert.NoError(t, err) + } ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -153,22 +211,21 @@ func test(t *testing.T, circuit gkrcore.RawCircuit) { t.Log("Selected inputs for test") - proof, err := Prove(sCircuit, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(sCircuit, schedule, fullAssignment, newMessageCounter(1, 1)) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(1, 1)) assert.NoError(t, err, "proof rejected") if proof.isEmpty() { // special case for TestNoGate: continue // there's no way to make a trivial proof fail } - err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) + err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(0, 1)) assert.NotNil(t, err, "bad proof accepted") } - } func (p Proof) isEmpty() bool { @@ -188,14 +245,17 @@ func (p Proof) isEmpty() bool { func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { _, c := cache.Compile(t, gkrtesting.NoGateCircuit()) + schedule, err := gkrcore.DefaultProvingSchedule(c) + assert.NoError(t, err) + assignment := WireAssignment{0: inputAssignments[0]} - proof, err := Prove(c, assignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(c, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(c, assignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(c, schedule, assignment, proof, newMessageCounter(1, 1)) assert.NoError(t, err, "proof rejected") } @@ -203,7 +263,7 @@ func generateTestProver(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) assert.NoError(t, err) - proof, err := Prove(testCase.Circuit, testCase.FullAssignment, fiatshamir.WithHash(testCase.Hash)) + proof, err := Prove(testCase.Circuit, testCase.Schedule, testCase.FullAssignment, testCase.Hash) assert.NoError(t, err) assert.NoError(t, proofEquals(testCase.Proof, proof)) } @@ -213,17 +273,16 @@ func generateTestVerifier(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(testCase.Hash)) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) assert.NoError(t, err, "proof rejected") testCase, err = newTestCase(path) assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(newMessageCounter(2, 0))) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, newMessageCounter(2, 0)) assert.NotNil(t, err, "bad proof accepted") } } func TestGkrVectors(t *testing.T) { - const testDirPath = "../test_vectors/" dirEntries, err := os.ReadDir(testDirPath) assert.NoError(t, err) @@ -269,6 +328,9 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") _, c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) + schedule, err := gkrcore.DefaultProvingSchedule(c) + assert.NoError(b, err) + in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) fr.Vector(in0).MustSetRandom() @@ -283,12 +345,30 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { //b.ResetTimer() fmt.Println("constructing proof") start = time.Now().UnixMicro() - _, err := Prove(c, assignment, fiatshamir.WithHash(mimc.NewMiMC())) + _, err = Prove(c, schedule, assignment, mimc.NewMiMC()) proved := time.Now().UnixMicro() - start fmt.Println("proved in", proved, "μs") assert.NoError(b, err) } +// TestSingleMulGateExplicitSchedule tests a single mul gate with an explicit single-step schedule, +// equivalent to the default but constructed manually to exercise the schedule path. +func TestSingleMulGateExplicitSchedule(t *testing.T) { + circuit := gkrtesting.SingleMulGateCircuit() + _, sCircuit := cache.Compile(t, circuit) + + // Wire 2 is the mul gate output (inputs: 0, 1). + // Explicit schedule: one SumcheckLevel for wire 2 only. + // ClaimSources: [1] = initial challenge (index len(schedule) = 1). + schedule := constraint.GkrProvingSchedule{ + constraint.GkrSumcheckLevel{ + {Wires: []int{2}, ClaimSources: []int{1}}, + }, + } + testWithSchedule(t, circuit, schedule) + _ = sCircuit +} + func BenchmarkGkrMimc19(b *testing.B) { benchmarkGkrMiMC(b, 1<<19, 91) } @@ -332,6 +412,7 @@ type TestCase struct { Proof Proof FullAssignment WireAssignment InOutAssignment WireAssignment + Schedule constraint.GkrProvingSchedule } var testCases = make(map[string]*TestCase) @@ -362,6 +443,20 @@ func newTestCase(path string) (*TestCase, error) { if proof, err = unmarshalProof(info.Proof); err != nil { return nil, err } + var schedule constraint.GkrProvingSchedule + if schedule, err = info.Schedule.ToProvingSchedule(); err != nil { + return nil, err + } + if schedule == nil { + if schedule, err = gkrcore.DefaultProvingSchedule(circuit); err != nil { + return nil, err + } + } + + outputSet := make(map[int]bool, len(circuit)) + for _, o := range circuit.Outputs() { + outputSet[o] = true + } fullAssignment := make(WireAssignment, len(circuit)) inOutAssignment := make(WireAssignment, len(circuit)) @@ -375,7 +470,7 @@ func newTestCase(path string) (*TestCase, error) { } assignmentRaw = info.Input[inI] inI++ - } else if circuit[i].IsOutput() { + } else if outputSet[i] { if outI == len(info.Output) { return nil, fmt.Errorf("fewer output in vector than in circuit") } @@ -396,7 +491,7 @@ func newTestCase(path string) (*TestCase, error) { fullAssignment.Complete(circuit) for i := range circuit { - if circuit[i].IsOutput() { + if outputSet[i] { if err = sliceEquals(inOutAssignment[i], fullAssignment[i]); err != nil { return nil, fmt.Errorf("assignment mismatch: %v", err) } @@ -409,6 +504,7 @@ func newTestCase(path string) (*TestCase, error) { Proof: proof, Hash: _hash, Circuit: circuit, + Schedule: schedule, } testCases[path] = tCase diff --git a/internal/gkr/bls12-377/sumcheck.go b/internal/gkr/bls12-377/sumcheck.go index b05e319932..f8b77a197c 100644 --- a/internal/gkr/bls12-377/sumcheck.go +++ b/internal/gkr/bls12-377/sumcheck.go @@ -7,33 +7,62 @@ package gkr import ( "errors" - "strconv" + "hash" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) -// This does not make use of parallelism and represents polynomials as lists of coefficients -// It is currently geared towards arithmetic hashes. Once we have a more unified hash function interface, this can be generified. +// This does not make use of parallelism and represents polynomials as lists of coefficients. + +// transcript is a Fiat-Shamir transcript backed by a running hash. +// Field elements are written via bind; challenges are derived via getChallenge. +// The hash is never reset — all previous data is implicitly part of future challenges. +type transcript struct { + h hash.Hash + bound bool // whether bind was called since the last getChallenge +} + +// bind writes field elements to the transcript as bindings for the next challenge. +func (t *transcript) bind(elements ...fr.Element) { + if len(elements) == 0 { + return + } + for i := range elements { + bytes := elements[i].Bytes() + t.h.Write(bytes[:]) + } + t.bound = true +} + +// getChallenge binds optional elements, then squeezes a challenge from the current hash state. +// If no bindings were added since the last squeeze, a separator byte is written first +// to advance the state and prevent repeated values. +func (t *transcript) getChallenge(bindings ...fr.Element) fr.Element { + t.bind(bindings...) + if !t.bound { + t.h.Write([]byte{0}) + } + t.bound = false + var res fr.Element + res.SetBytes(t.h.Sum(nil)) + return res +} // sumcheckClaims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. // Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) type sumcheckClaims interface { - fold(a fr.Element) polynomial.Polynomial // fold into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. - next(fr.Element) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ + roundPolynomial() polynomial.Polynomial // compute gⱼ polynomial for current round + roundFold(r fr.Element) // fold inputs and eq at challenge r varsNum() int // number of variables - claimsNum() int // number of claims proveFinalEval(r []fr.Element) []fr.Element // in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof } // sumcheckLazyClaims is the sumcheckClaims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. type sumcheckLazyClaims interface { - claimsNum() int // claimsNum = m - varsNum() int // varsNum = n - foldedSum(a fr.Element) fr.Element // foldedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - degree(i int) int // degree of the total claim in the i'th variable - verifyFinalEval(r []fr.Element, foldingCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error + varsNum() int // varsNum = n + degree(i int) int // degree of the total claim in the i'th variable + verifyFinalEval(r []fr.Element, purportedValue fr.Element, proof []fr.Element) error } // sumcheckProof of a multi-statement. @@ -42,130 +71,46 @@ type sumcheckProof struct { finalEvalProof []fr.Element //in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof } -func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) (challengeNames []string, err error) { - numChallenges := varsNum - if claimsNum >= 2 { - numChallenges++ - } - challengeNames = make([]string, numChallenges) - if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "fold" - } - prefix := settings.Prefix + "pSP." - for i := 0; i < varsNum; i++ { - challengeNames[i+numChallenges-varsNum] = prefix + strconv.Itoa(i) - } - if settings.Transcript == nil { - transcript := fiatshamir.NewTranscript(settings.Hash, challengeNames...) - settings.Transcript = transcript - } - - for i := range settings.BaseChallenges { - if err = settings.Transcript.Bind(challengeNames[0], settings.BaseChallenges[i]); err != nil { - return - } - } - return -} - -func next(transcript *fiatshamir.Transcript, bindings []fr.Element, remainingChallengeNames *[]string) (fr.Element, error) { - challengeName := (*remainingChallengeNames)[0] - for i := range bindings { - bytes := bindings[i].Bytes() - if err := transcript.Bind(challengeName, bytes[:]); err != nil { - return fr.Element{}, err - } - } - var res fr.Element - bytes, err := transcript.ComputeChallenge(challengeName) - res.SetBytes(bytes) - - *remainingChallengeNames = (*remainingChallengeNames)[1:] - - return res, err -} - -// sumcheckProve create a non-interactive proof -func sumcheckProve(claims sumcheckClaims, transcriptSettings fiatshamir.Settings) (sumcheckProof, error) { - - var proof sumcheckProof - remainingChallengeNames, err := setupTranscript(claims.claimsNum(), claims.varsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return proof, err - } - - var foldingCoeff fr.Element - if claims.claimsNum() >= 2 { - if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { - return proof, err - } - } - +// sumcheckProve creates a non-interactive sumcheck proof. +// The fold challenge is derived by the caller (proveLevel). +// Pattern: roundPolynomial, [roundFold, roundPolynomial]*, proveFinalEval. +func sumcheckProve(claims sumcheckClaims, t *transcript) sumcheckProof { varsNum := claims.varsNum() - proof.partialSumPolys = make([]polynomial.Polynomial, varsNum) - proof.partialSumPolys[0] = claims.fold(foldingCoeff) + proof := sumcheckProof{partialSumPolys: make([]polynomial.Polynomial, varsNum)} + proof.partialSumPolys[0] = claims.roundPolynomial() challenges := make([]fr.Element, varsNum) - for j := 0; j+1 < varsNum; j++ { - if challenges[j], err = next(transcript, proof.partialSumPolys[j], &remainingChallengeNames); err != nil { - return proof, err - } - proof.partialSumPolys[j+1] = claims.next(challenges[j]) - } - - if challenges[varsNum-1], err = next(transcript, proof.partialSumPolys[varsNum-1], &remainingChallengeNames); err != nil { - return proof, err + for j := range varsNum - 1 { + challenges[j] = t.getChallenge(proof.partialSumPolys[j]...) + claims.roundFold(challenges[j]) + proof.partialSumPolys[j+1] = claims.roundPolynomial() } + challenges[varsNum-1] = t.getChallenge(proof.partialSumPolys[varsNum-1]...) proof.finalEvalProof = claims.proveFinalEval(challenges) - - return proof, nil + return proof } -func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSettings fiatshamir.Settings) error { - remainingChallengeNames, err := setupTranscript(claims.claimsNum(), claims.varsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return err - } - - var foldingCoeff fr.Element - - if claims.claimsNum() >= 2 { - if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { - return err - } - } - +// sumcheckVerify verifies a non-interactive sumcheck proof. +// The fold challenge is derived by the caller (verifyLevel). +// claimedSum is the expected sum; degree is the polynomial's degree in each variable. +func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, claimedSum fr.Element, degree int, t *transcript) error { r := make([]fr.Element, claims.varsNum()) - // Just so that there is enough room for gJ to be reused - maxDegree := claims.degree(0) - for j := 1; j < claims.varsNum(); j++ { - if d := claims.degree(j); d > maxDegree { - maxDegree = d - } - } - gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() - gJR := claims.foldedSum(foldingCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) + gJ := make(polynomial.Polynomial, degree+1) + gJR := claimedSum for j := range claims.varsNum() { - if len(proof.partialSumPolys[j]) != claims.degree(j) { + if len(proof.partialSumPolys[j]) != degree { return errors.New("malformed proof") } copy(gJ[1:], proof.partialSumPolys[j]) - gJ[0].Sub(&gJR, &proof.partialSumPolys[j][0]) // Requirement that gⱼ(0) + gⱼ(1) = gⱼ₋₁(r) - // gJ is ready + gJ[0].Sub(&gJR, &proof.partialSumPolys[j][0]) - //Prepare for the next iteration - if r[j], err = next(transcript, proof.partialSumPolys[j], &remainingChallengeNames); err != nil { - return err - } - // This is an extremely inefficient way of interpolating. TODO: Interpolate without symbolically computing a polynomial - gJCoeffs := polynomial.InterpolateOnRange(gJ[:(claims.degree(j) + 1)]) + r[j] = t.getChallenge(proof.partialSumPolys[j]...) + gJCoeffs := polynomial.InterpolateOnRange(gJ[:(degree + 1)]) gJR = gJCoeffs.Eval(&r[j]) } - return claims.verifyFinalEval(r, foldingCoeff, gJR, proof.finalEvalProof) + return claims.verifyFinalEval(r, gJR, proof.finalEvalProof) } diff --git a/internal/gkr/bls12-377/sumcheck_test.go b/internal/gkr/bls12-377/sumcheck_test.go index f4b8d524b3..395a75bff3 100644 --- a/internal/gkr/bls12-377/sumcheck_test.go +++ b/internal/gkr/bls12-377/sumcheck_test.go @@ -10,7 +10,6 @@ import ( "hash" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/stretchr/testify/assert" "math/bits" @@ -28,11 +27,9 @@ func testSumcheckSingleClaimMultilin(polyInt []uint64, hashGenerator func() hash } claim := singleMultilinClaim{g: poly.Clone()} + t := transcript{h: hashGenerator()} - proof, err := sumcheckProve(&claim, fiatshamir.WithHash(hashGenerator())) - if err != nil { - return err - } + proof := sumcheckProve(&claim, &t) var sb strings.Builder for _, p := range proof.partialSumPolys { @@ -48,13 +45,15 @@ func testSumcheckSingleClaimMultilin(polyInt []uint64, hashGenerator func() hash } lazyClaim := singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if err = sumcheckVerify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())); err != nil { + t = transcript{h: hashGenerator()} + if err := sumcheckVerify(lazyClaim, proof, lazyClaim.claimedSum, 1, &t); err != nil { return err } proof.partialSumPolys[0][0].Add(&proof.partialSumPolys[0][0], toElement(1)) lazyClaim = singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if sumcheckVerify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())) == nil { + t = transcript{h: hashGenerator()} + if sumcheckVerify(lazyClaim, proof, lazyClaim.claimedSum, 1, &t) == nil { return fmt.Errorf("bad proof accepted") } return nil @@ -93,18 +92,14 @@ type singleMultilinClaim struct { g polynomial.MultiLin } -func (c singleMultilinClaim) proveFinalEval(r []fr.Element) []fr.Element { +func (c *singleMultilinClaim) proveFinalEval(r []fr.Element) []fr.Element { return nil // verifier can compute the final eval itself } -func (c singleMultilinClaim) varsNum() int { +func (c *singleMultilinClaim) varsNum() int { return bits.TrailingZeros(uint(len(c.g))) } -func (c singleMultilinClaim) claimsNum() int { - return 1 -} - func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { sum := g[len(g)/2] for i := len(g)/2 + 1; i < len(g); i++ { @@ -113,13 +108,12 @@ func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { return []fr.Element{sum} } -func (c singleMultilinClaim) fold(fr.Element) polynomial.Polynomial { +func (c *singleMultilinClaim) roundPolynomial() polynomial.Polynomial { return sumForX1One(c.g) } -func (c *singleMultilinClaim) next(r fr.Element) polynomial.Polynomial { +func (c *singleMultilinClaim) roundFold(r fr.Element) { c.g.Fold(r) - return sumForX1One(c.g) } type singleMultilinLazyClaim struct { @@ -127,7 +121,7 @@ type singleMultilinLazyClaim struct { claimedSum fr.Element } -func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, _ fr.Element, purportedValue fr.Element, proof []fr.Element) error { +func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, purportedValue fr.Element, proof []fr.Element) error { val := c.g.Evaluate(r, nil) if val.Equal(&purportedValue) { return nil @@ -135,15 +129,7 @@ func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, _ fr.Element, p return fmt.Errorf("mismatch") } -func (c singleMultilinLazyClaim) foldedSum(_ fr.Element) fr.Element { - return c.claimedSum -} - -func (c singleMultilinLazyClaim) degree(i int) int { - return 1 -} - -func (c singleMultilinLazyClaim) claimsNum() int { +func (c singleMultilinLazyClaim) degree(int) int { return 1 } diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 974c7d18b3..7c150c6f52 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -15,7 +15,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" @@ -204,6 +203,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + Schedule constraint.GkrProvingSchedule HashName string lock sync.Mutex @@ -256,9 +256,11 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra } } + // Create hasher and write base challenges + hsh := hash.NewHash(b.HashName + "_BLS12_381") + // Read initial challenges from instruction calldata (parse dynamically, no metadata) // Format: [0]=totalSize, [1...]=challenge linear expressions - insBytes := make([][]byte, 0) // first challenges calldata := inst.Calldata[1:] // skip size prefix for len(calldata) != 0 { val, delta := s.Read(calldata) @@ -267,17 +269,14 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) var challenge fr.Element copy(challenge[:], val[:]) - insBytes = append(insBytes, challenge.Marshal()) + challengeBytes := challenge.Bytes() + hsh.Write(challengeBytes[:]) } - // Create Fiat-Shamir settings - hsh := hash.NewHash(b.HashName + "_BLS12_381") - fsSettings := fiatshamir.WithHash(hsh, insBytes...) - // Call the BLS12_381-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, b.Schedule, assignments, hsh) if err != nil { - return fmt.Errorf("bls12_381 prove failed: %w", err) + return fmt.Errorf("BLS12_381 prove failed: %w", err) } for i, elem := range proof.flatten() { @@ -305,7 +304,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) + return gkrcore.ProofSize(b.Schedule, b.SolveBlueprint.Circuit, logNbInstances) } // NbOutputs implements Blueprint @@ -434,7 +433,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BLS12_381 -func NewBlueprints(circuit gkrcore.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrcore.Blueprints { +func NewBlueprints(circuit gkrcore.SerializableCircuit, schedule constraint.GkrProvingSchedule, hashName string, compiler constraint.CustomizableSystem) gkrcore.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) @@ -443,6 +442,7 @@ func NewBlueprints(circuit gkrcore.SerializableCircuit, hashName string, compile prove := &BlueprintProve{ SolveBlueprintID: solveID, SolveBlueprint: solve, + Schedule: schedule, HashName: hashName, } proveID := compiler.AddBlueprint(prove) diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index d41aa8d248..e5bb7c7676 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -8,14 +8,14 @@ package gkr import ( "errors" "fmt" + "hash" "iter" - "strconv" "sync" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" ) @@ -33,630 +33,487 @@ type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) // zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). -// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear -// extension of the values of w across all instances. -// Its purpose is to batch the checking of multiple evaluations of the same wire. +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, +// where the sum runs over all wᵢ and evaluation point xᵢ in the level. +// Its purpose is to batch the checking of multiple wire evaluations at evaluation points. type zeroCheckLazyClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly - manager *claimsManager // WARNING: Circular references -} - -func (e *zeroCheckLazyClaims) getWire() Wire { - return e.manager.circuit[e.wireI] -} - -func (e *zeroCheckLazyClaims) claimsNum() int { - return len(e.evaluationPoints) + foldingCoeff fr.Element // the coefficient used to fold claims, conventionally 0 if there is only one claim + resources *resources + levelI int } func (e *zeroCheckLazyClaims) varsNum() int { - return len(e.evaluationPoints[0]) -} - -// foldedSum returns ∑ᵢ aⁱ yᵢ -func (e *zeroCheckLazyClaims) foldedSum(a fr.Element) fr.Element { - evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) - return evalsAsPoly.Eval(&a) + return e.resources.nbVars } func (e *zeroCheckLazyClaims) degree(int) int { - return e.manager.circuit[e.wireI].ZeroCheckDegree() -} - -// verifyFinalEval finalizes the verification of w. -// The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. (c is foldingCoeff) -// Both purportedValue and the vector r have been randomized during the sumcheck protocol. -// By taking the w term out of the sum we get the equivalent claim that -// for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. -// If w is an input wire, the verifier can directly check its evaluation at r. -// Otherwise, the prover makes claims about the evaluation of w's input wires, -// wᵢ, at r, to be verified later. -// The claims are communicated through the proof parameter. -// The verifier checks here if the claimed evaluations of wᵢ(r) are consistent with -// the main claim, by checking E w(wᵢ(r)...) = purportedValue. -func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, foldingCoeff, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { - // the eq terms ( E ) - numClaims := len(e.evaluationPoints) - evaluation := polynomial.EvalEq(e.evaluationPoints[numClaims-1], r) - for i := numClaims - 2; i >= 0; i-- { - evaluation.Mul(&evaluation, &foldingCoeff) - eq := polynomial.EvalEq(e.evaluationPoints[i], r) - evaluation.Add(&evaluation, &eq) - } - - wire := e.manager.circuit[e.wireI] - - // the w(...) term - var gateEvaluation fr.Element - if wire.IsInput() { // just compute w(r) - gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) - } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire - injection, injectionLeftInv := - e.manager.circuit.ClaimPropagationInfo(e.wireI) - - if len(injection) != len(uniqueInputEvaluations) { - return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) - } - - for uniqueI, i := range injection { // map from unique to all - e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) - } + return gkrcore.Degree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel), e.resources.circuit) +} + +// verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. +// The sumcheck protocol has already reduced the per-wire claims w(xᵢ) = yᵢ to verifying +// ∑ᵢ cⁱ eq(xᵢ, r) · wᵢ(r) = purportedValue, where the sum runs over all +// claims on each wire and c is foldingCoeff. +// Both purportedValue and the vector r have been randomized during sumcheck. +// +// For input wires, w(r) is computed directly from the assignment. +// For non-input wires, the prover claims evaluations of the input wires at r, +// communicated through uniqueInputEvaluations; those claims are verified later. +// The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming +// that the full sum matches purportedValue. +func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { + e.resources.levelPoints[e.levelI] = r + level := e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel) + perWireInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) + + var terms []fr.Element + flatW := 0 + for _, group := range level { + for _, wI := range group.Wires { + wire := e.resources.circuit[wI] + + var gateEval fr.Element + if wire.IsInput() { + gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) + } else { + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) + for _, v := range perWireInputEvals[flatW] { + evaluator.pushInput(v) + } + gateEval.Set(evaluator.evaluate()) + } - evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) - for _, uniqueI := range injectionLeftInv { // map from all to unique - evaluator.pushInput(uniqueInputEvaluations[uniqueI]) + for _, src := range group.ClaimSources { + eq := polynomial.EvalEq(e.resources.levelPoints[src], r) + var term fr.Element + term.Mul(&eq, &gateEval) + terms = append(terms, term) + } + flatW++ } - - gateEvaluation.Set(evaluator.evaluate()) } - evaluation.Mul(&evaluation, &gateEvaluation) - - if evaluation.Equal(&purportedValue) { + ys := polynomial.Polynomial(terms) + if total := ys.Eval(&e.foldingCoeff); total.Equal(&purportedValue) { return nil } return errors.New("incompatible evaluations") } // zeroCheckClaims is a claim for sumcheck (prover side). -// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear -// extension of the values of w across all instances. -// Its purpose is to batch the proving of multiple evaluations of the same wire. +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, +// where the sum runs over all (wire v, claim source s) pairs in the level. +// Each wire has its own eq table with the batching coefficients baked in. type zeroCheckClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []fr.Element // yᵢ = w(xᵢ) - manager *claimsManager - - input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) - - eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) - - gateEvaluatorPool *gateEvaluatorPool -} - -func (c *zeroCheckClaims) getWire() Wire { - return c.manager.circuit[c.wireI] + level constraint.GkrSumcheckLevel + levelI int + resources *resources + input []polynomial.MultiLin // UniqueGateInputs order + inputIndices [][]int // [wireInLevel][gateInputJ] → index in input + eqs []polynomial.MultiLin // per-wire interpolation bases for evaluating wire assignments at challenge points + gateEvaluatorPools []*gateEvaluatorPool } -// fold the multiple claims into one claim using a random combination (foldingCoeff or c). -// From the original multiple claims of w(xᵢ) = yᵢ, we get a single claim -// ∑ᵢ,ₕ cⁱ eq(xᵢ, h) w(h) = ∑ᵢ cⁱ yᵢ, where h iterates over the hypercube (circuit instances) and -// i iterates over the claims. -// Equivalently, we could say ∑ᵢ cⁱ yᵢ = ∑ₕ,ᵢ cⁱ eq(xᵢ, h) w(h) = ∑ₕ w(h) ∑ᵢ cⁱ eq(xᵢ, h). -// Thus if we initially compute E := ∑ᵢ cⁱ eq(xᵢ, -), our claim will find the simpler form -// ∑ᵢ cⁱ yᵢ = ∑ₕ w(h) E(h), where the sum-checked polynomial is of degree deg(g) + 1, -// and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. -// The output of fold is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. -func (c *zeroCheckClaims) fold(foldingCoeff fr.Element) polynomial.Polynomial { - varsNum := c.varsNum() - eqLength := 1 << varsNum - claimsNum := c.claimsNum() - // initialize the eq tables ( E ) - c.eq = c.manager.memPool.Make(eqLength) - - c.eq[0].SetOne() - c.eq.Eq(c.evaluationPoints[0]) - - // E := eq(x₀, -) - newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) - aI := foldingCoeff - - // E += cⁱ eq(xᵢ, -) - for k := 1; k < claimsNum; k++ { - newEq[0].Set(&aI) - - c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) - - if k+1 < claimsNum { - aI.Mul(&aI, &foldingCoeff) - } - } - - c.manager.memPool.Dump(newEq) - - return c.computeGJ() -} - -// eqAcc sets m to an eq table at q and then adds it to e. -// m <- eq(q, -). -// e <- e + m -func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { - n := len(q) - - //At the end of each iteration, m(h₁, ..., hₙ) = eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) - for i := range q { // In the comments we use a 1-based index so q[i] = qᵢ₊₁ - // go through all assignments of (b₁, ..., bᵢ) ∈ {0,1}ⁱ - const threshold = 1 << 6 - k := 1 << i - if k < threshold { - for j := 0; j < k; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - } else { - c.manager.workers.Submit(k, func(start, end int) { - for j := start; j < end; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - }, 1024).Wait() - } - - } - c.manager.workers.Submit(len(e), func(start, end int) { - for i := start; i < end; i++ { - e[i].Add(&e[i], &m[i]) - } - }, 512).Wait() +func (c *zeroCheckClaims) varsNum() int { + return len(c.resources.levelPoints[c.level[0].ClaimSources[0]]) } -// computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). -// the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). -// The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. -func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { - - wire := c.getWire() - degGJ := wire.ZeroCheckDegree() // guaranteed to be no smaller than the actual deg(gⱼ) - nbGateIn := len(c.input) - - // Both E and wᵢ (the input wires and the eq table) are multilinear, thus - // they are linear in Xⱼ. - // So for f ∈ { E(r₁, ..., Xⱼ, h...) } ∪ {wᵢ(r₁, ..., Xⱼ, h...) }, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables. - // ml are such multilinear polynomials the evaluations of which over different values of Xⱼ are computed in this stepwise manner. - ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube - ml[0] = c.eq - copy(ml[1:], c.input) +// roundPolynomial computes gⱼ = ∑ₕ ∑ᵥ eqs[v](Xⱼ, h...) · gateᵥ(inputs(Xⱼ, h...)). +// The polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). +// The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). +// By convention, g₀ is a constant polynomial equal to the claimed sum. +func (c *zeroCheckClaims) roundPolynomial() polynomial.Polynomial { + degree := gkrcore.Degree(c.level, c.resources.circuit) + nbUniqueInputs := len(c.input) + nbWires := len(c.eqs) - sumSize := len(c.eq) / 2 // the range of h, over which we sum + // Both eqs and input are multilinear, thus linear in Xⱼ. + // For any such f, f(m) = m·(f(1) - f(0)) + f(0), and f(0), f(1) are read directly + // from the bookkeeping tables. This allows stepwise evaluation at Xⱼ = 1, 2, ..., degree. + // Layout: [eq₀, eq₁, ..., eq_{nbWires-1}, input₀, input₁, ..., input_{nbUniqueInputs-1}] + ml := make([]polynomial.MultiLin, nbWires+nbUniqueInputs) + copy(ml, c.eqs) + copy(ml[nbWires:], c.input) - // Perf-TODO: Collate once at claim "folding" time and not again. then, even folding can be done in one operation every time "next" is called + sumSize := len(c.eqs[0]) / 2 - gJ := make([]fr.Element, degGJ) + p := make([]fr.Element, degree) var mu sync.Mutex - computeAll := func(start, end int) { // compute method to allow parallelization across instances + computeAll := func(start, end int) { var step fr.Element - evaluator := c.gateEvaluatorPool.get() - defer c.gateEvaluatorPool.put(evaluator) + evaluators := make([]*gateEvaluator, nbWires) + for w := range nbWires { + evaluators[w] = c.gateEvaluatorPools[w].get() + } + defer func() { + for w := range nbWires { + c.gateEvaluatorPools[w].put(evaluators[w]) + } + }() - res := make([]fr.Element, degGJ) + res := make([]fr.Element, degree) // evaluations of ml, laid out as: // ml[0](1, h...), ml[1](1, h...), ..., ml[len(ml)-1](1, h...), // ml[0](2, h...), ml[1](2, h...), ..., ml[len(ml)-1](2, h...), // ... - // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) - mlEvals := make([]fr.Element, degGJ*len(ml)) - - for h := start; h < end; h++ { // h counts across instances + // ml[0](degree, h...), ml[1](degree, h...), ..., ml[len(ml)-1](degree, h...) + mlEvals := make([]fr.Element, degree*len(ml)) + for h := start; h < end; h++ { evalAt1Index := sumSize + h for k := range ml { - // d = 0 - mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. + mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1, taken directly from the table step.Sub(&mlEvals[k], &ml[k][h]) // step = ml[k](1) - ml[k](0) - for d := 1; d < degGJ; d++ { + for d := 1; d < degree; d++ { mlEvals[d*len(ml)+k].Add(&mlEvals[(d-1)*len(ml)+k], &step) } } - eIndex := 0 // index for where the current eq term is + eIndex := 0 // start of the current row's eq evaluations nextEIndex := len(ml) - for d := range degGJ { - // Push gate inputs - for i := range nbGateIn { - evaluator.pushInput(mlEvals[eIndex+1+i]) + for d := range degree { + for w := range nbWires { + for _, inputI := range c.inputIndices[w] { + evaluators[w].pushInput(mlEvals[eIndex+nbWires+inputI]) + } + summand := evaluators[w].evaluate() + summand.Mul(summand, &mlEvals[eIndex+w]) + res[d].Add(&res[d], summand) // collect contributions into the sum from start to end } - summand := evaluator.evaluate() - summand.Mul(summand, &mlEvals[eIndex]) - res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } } mu.Lock() - for i := range gJ { - gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum + for i := range p { + p[i].Add(&p[i], &res[i]) // collect into the complete sum } mu.Unlock() } const minBlockSize = 64 - if sumSize < minBlockSize { - // no parallelization computeAll(0, sumSize) } else { - c.manager.workers.Submit(sumSize, computeAll, minBlockSize).Wait() + c.resources.workers.Submit(sumSize, computeAll, minBlockSize).Wait() } - return gJ + return p } -// next first folds the input and E polynomials at the given verifier challenge then computes the new gⱼ. -// Thus, j <- j+1 and rⱼ = challenge. -func (c *zeroCheckClaims) next(challenge fr.Element) polynomial.Polynomial { +// roundFold folds all input and eq polynomials at the verifier challenge r. +// After this call, j ← j+1 and rⱼ = r. +func (c *zeroCheckClaims) roundFold(r fr.Element) { const minBlockSize = 512 - n := len(c.eq) / 2 + n := len(c.eqs[0]) / 2 if n < minBlockSize { - // no parallelization for i := range c.input { - c.input[i].Fold(challenge) + c.input[i].Fold(r) + } + for i := range c.eqs { + c.eqs[i].Fold(r) } - c.eq.Fold(challenge) } else { - wgs := make([]*sync.WaitGroup, len(c.input)) + wgs := make([]*sync.WaitGroup, len(c.input)+len(c.eqs)) for i := range c.input { - wgs[i] = c.manager.workers.Submit(n, c.input[i].FoldParallel(challenge), minBlockSize) + wgs[i] = c.resources.workers.Submit(n, c.input[i].FoldParallel(r), minBlockSize) + } + for i := range c.eqs { + wgs[len(c.input)+i] = c.resources.workers.Submit(n, c.eqs[i].FoldParallel(r), minBlockSize) } - c.manager.workers.Submit(n, c.eq.FoldParallel(challenge), minBlockSize).Wait() for _, wg := range wgs { wg.Wait() } } - - return c.computeGJ() -} - -func (c *zeroCheckClaims) varsNum() int { - return len(c.evaluationPoints[0]) -} - -func (c *zeroCheckClaims) claimsNum() int { - return len(c.claimedEvaluations) } -// proveFinalEval provides the values wᵢ(r₁, ..., rₙ) +// proveFinalEval provides the unique input wire values wᵢ(r₁, ..., rₙ). func (c *zeroCheckClaims) proveFinalEval(r []fr.Element) []fr.Element { - //defer the proof, return list of claims - - injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. - evaluations := make([]fr.Element, len(injection)) - for i, gateInputI := range injection { - wI := c.input[gateInputI] - wI.Fold(r[len(r)-1]) // We already have wᵢ(r₁, ..., rₙ₋₁, hₙ) in a table. Only one more fold required. - c.manager.add(c.getWire().Inputs[gateInputI], r, wI[0]) - evaluations[i] = wI[0] + c.resources.levelPoints[c.levelI] = r + evaluations := make([]fr.Element, len(c.input)) + for i := range c.input { + c.input[i].Fold(r[len(r)-1]) + evaluations[i] = c.input[i][0] } - - c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.dumpAll() - - return evaluations -} - -type claimsManager struct { - claims []*zeroCheckLazyClaims - assignment WireAssignment - memPool *polynomial.Pool - workers *utils.WorkerPool - circuit Circuit -} - -func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { - manager.assignment = assignment - manager.claims = make([]*zeroCheckLazyClaims, len(circuit)) - manager.memPool = o.pool - manager.workers = o.workers - manager.circuit = circuit - - for i := range circuit { - manager.claims[i] = &zeroCheckLazyClaims{ - wireI: i, - evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), - claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), - manager: &manager, - } + for i := range c.input { + c.resources.memPool.Dump(c.input[i]) } - return -} - -func (m *claimsManager) add(wire int, evaluationPoint []fr.Element, evaluation fr.Element) { - claim := m.claims[wire] - i := len(claim.evaluationPoints) - claim.claimedEvaluations[i] = evaluation - claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) + for i := range c.eqs { + c.resources.memPool.Dump(c.eqs[i]) + } + for _, pool := range c.gateEvaluatorPools { + pool.dumpAll() + } + return evaluations } -func (m *claimsManager) getLazyClaim(wire int) *zeroCheckLazyClaims { - return m.claims[wire] -} +// eqAcc sets m to an eq table at q and then adds it to e. +// m <- m[0] · eq(q, -). +// e <- e + m +func (r *resources) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { + n := len(q) -func (m *claimsManager) getClaim(wireI int) *zeroCheckClaims { - lazy := m.claims[wireI] - wire := m.circuit[wireI] - res := &zeroCheckClaims{ - wireI: wireI, - evaluationPoints: lazy.evaluationPoints, - claimedEvaluations: lazy.claimedEvaluations, - manager: m, - } + // At the end of each iteration, m(h₁, ..., hₙ) = m[0] · eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) + for i := range q { // 1-based in comments: q[i] = qᵢ₊₁ + // go through all assignments of (b₁, ..., bᵢ) ∈ {0,1}ⁱ + const threshold = 1 << 6 + k := 1 << i + if k < threshold { + for j := 0; j < k; j++ { + j0 := j << (n - i) // bᵢ₊₁ = 0 + j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - if wire.IsInput() { - res.input = []polynomial.MultiLin{m.memPool.Clone(m.assignment[wireI])} - } else { - res.input = make([]polynomial.MultiLin, len(wire.Inputs)) + m[j1].Mul(&q[i], &m[j0]) // m(b₁,...,bᵢ,1) = m(b₁,...,bᵢ) · qᵢ₊₁ + m[j0].Sub(&m[j0], &m[j1]) // m(b₁,...,bᵢ,0) = m(b₁,...,bᵢ) · (1 - qᵢ₊₁) + } + } else { + r.workers.Submit(k, func(start, end int) { + for j := start; j < end; j++ { + j0 := j << (n - i) // bᵢ₊₁ = 0 + j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - for inputI, inputW := range wire.Inputs { - res.input[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied + m[j1].Mul(&q[i], &m[j0]) // m(b₁,...,bᵢ,1) = m(b₁,...,bᵢ) · qᵢ₊₁ + m[j0].Sub(&m[j0], &m[j1]) // m(b₁,...,bᵢ,0) = m(b₁,...,bᵢ) · (1 - qᵢ₊₁) + } + }, 1024).Wait() } } - - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Evaluate, len(res.input), m.memPool) - - return res -} - -func (m *claimsManager) deleteClaim(wire int) { - m.claims[wire].manager = nil - m.claims[wire] = nil + r.workers.Submit(len(e), func(start, end int) { + for i := start; i < end; i++ { + e[i].Add(&e[i], &m[i]) + } + }, 512).Wait() } -type settings struct { - pool *polynomial.Pool - transcript *fiatshamir.Transcript - transcriptPrefix string - nbVars int - workers *utils.WorkerPool +type resources struct { + // levelPoints[i] is the sumcheck evaluation point r produced at schedule level i. + // levelPoints[len(schedule)] holds the initial challenge (firstChallenge / rho). + // SkipLevels produce no point; their slot remains nil. + levelPoints [][]fr.Element + evalPositions [][]int // [wireI][evalI] → position in source level's finalEvalProof + nbVars int + assignment WireAssignment + memPool polynomial.Pool + workers *utils.WorkerPool + circuit Circuit + schedule constraint.GkrProvingSchedule + transcript transcript } -type Option func(*settings) - -func WithPool(pool *polynomial.Pool) Option { - return func(options *settings) { - options.pool = pool +func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (resources, error) { + nbVars := assignment.NumVars() + nbInstances := assignment.NumInstances() + if 1<= 2 { + foldingCoeff = r.transcript.getChallenge() } - if o.pool == nil { - pool := polynomial.NewPool(c.MemoryRequirements(nbInstances)...) - o.pool = &pool + uniqueInputs, inputIndices := gkrcore.InputMapping(level, r.circuit) + input := make([]polynomial.MultiLin, len(uniqueInputs)) + for i, inW := range uniqueInputs { + input[i] = r.memPool.Clone(r.assignment[inW]) } - if o.workers == nil { - o.workers = utils.NewWorkerPool() + nbWires := 0 + for _, group := range level { + nbWires += len(group.Wires) } - if transcriptSettings.Transcript == nil { - challengeNames := ChallengeNames(c, o.nbVars, transcriptSettings.Prefix) - o.transcript = fiatshamir.NewTranscript(transcriptSettings.Hash, challengeNames...) - for i := range transcriptSettings.BaseChallenges { - if err = o.transcript.Bind(challengeNames[0], transcriptSettings.BaseChallenges[i]); err != nil { - return o, err + pools := make([]*gateEvaluatorPool, nbWires) + flatW := 0 + for _, group := range level { + for _, wI := range group.Wires { + wire := r.circuit[wI] + gate := wire.Gate.Evaluate + if wire.IsInput() { + gate = gkrcore.IdentityBytecode() } + pools[flatW] = newGateEvaluatorPool(gate, len(inputIndices[flatW]), &r.memPool) + flatW++ } - } else { - o.transcript, o.transcriptPrefix = transcriptSettings.Transcript, transcriptSettings.Prefix } - return o, err -} - -func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { - - // Pre-compute the size TODO: Consider not doing this and just grow the list by appending - size := logNbInstances // first challenge - - for i := range c { - if c[i].NoProof() { // no proof, no challenge - continue - } - if c[i].NbClaims() > 1 { //fold the claims - size++ + eqLength := 1 << r.nbVars + eqs := make([]polynomial.MultiLin, nbWires) + var alpha fr.Element + alpha.SetOne() + flatW = 0 + for _, group := range level { + nbSources := len(group.ClaimSources) + + groupEq := polynomial.MultiLin(r.memPool.Make(eqLength)) + groupEq[0].Set(&alpha) + groupEq.Eq(r.levelPoints[group.ClaimSources[0]]) + + if nbSources > 1 { + newEq := polynomial.MultiLin(r.memPool.Make(eqLength)) + aI := alpha + for k := 1; k < nbSources; k++ { + aI.Mul(&aI, &foldingCoeff) + newEq[0].Set(&aI) + r.eqAcc(groupEq, newEq, r.levelPoints[group.ClaimSources[k]]) + } + r.memPool.Dump(newEq) } - size += logNbInstances // full run of sumcheck on logNbInstances variables - } - nums := make([]string, max(len(c), logNbInstances)) - for i := range nums { - nums[i] = strconv.Itoa(i) - } - - challenges := make([]string, size) - - // output wire claims - firstChallengePrefix := prefix + "fC." - for j := 0; j < logNbInstances; j++ { - challenges[j] = firstChallengePrefix + nums[j] - } - j := logNbInstances - for i := len(c) - 1; i >= 0; i-- { - if c[i].NoProof() { - continue + var stride fr.Element + stride.Set(&foldingCoeff) + for range nbSources - 1 { + stride.Mul(&stride, &foldingCoeff) } - wirePrefix := prefix + "w" + nums[i] + "." - if c[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "fold" - j++ - } + eqs[flatW] = groupEq + flatW++ + alpha.Mul(&alpha, &stride) - partialSumPrefix := wirePrefix + "pSP." - for k := 0; k < logNbInstances; k++ { - challenges[j] = partialSumPrefix + nums[k] - j++ + for w := 1; w < len(group.Wires); w++ { + eqs[flatW] = polynomial.MultiLin(r.memPool.Make(eqLength)) + r.workers.Submit(eqLength, func(start, end int) { + for i := start; i < end; i++ { + eqs[flatW][i].Mul(&eqs[flatW-1][i], &stride) + } + }, 512).Wait() + flatW++ + alpha.Mul(&alpha, &stride) } } - return challenges -} -func getFirstChallengeNames(logNbInstances int, prefix string) []string { - res := make([]string, logNbInstances) - firstChallengePrefix := prefix + "fC." - for i := 0; i < logNbInstances; i++ { - res[i] = firstChallengePrefix + strconv.Itoa(i) + claims := &zeroCheckClaims{ + level: level, + levelI: levelI, + resources: r, + input: input, + inputIndices: inputIndices, + eqs: eqs, + gateEvaluatorPools: pools, + } + return sumcheckProve(claims, &r.transcript) +} + +// verifyLevel runs the sumcheck verification for a SumcheckLevel: derives the fold challenge, +// computes the claimed sum, and calls sumcheckVerify. +func (r *resources) verifyLevel(levelI int, proof Proof) error { + level := r.schedule[levelI].(constraint.GkrSumcheckLevel) + initialChallengeI := len(r.schedule) + + nbClaims := gkrcore.NbClaims(level) + var foldingCoeff fr.Element + if nbClaims >= 2 { + foldingCoeff = r.transcript.getChallenge() + } + + var ys []fr.Element + for _, group := range level { + for _, wI := range group.Wires { + evalI := 0 + for _, src := range group.ClaimSources { + var y fr.Element + if src == initialChallengeI { + y = r.assignment[wI].Evaluate(r.levelPoints[src], &r.memPool) + } else { + y = proof[src].finalEvalProof[r.evalPositions[wI][evalI]] + evalI++ + } + ys = append(ys, y) + } + } } - return res -} -func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Element, error) { - res := make([]fr.Element, len(names)) - for i, name := range names { - if bytes, err := transcript.ComputeChallenge(name); err != nil { - return nil, err - } else if err = res[i].SetBytesCanonical(bytes); err != nil { - return nil, err - } + ysPoly := polynomial.Polynomial(ys) + claimedSum := ysPoly.Eval(&foldingCoeff) + + lazyClaims := &zeroCheckLazyClaims{ + foldingCoeff: foldingCoeff, + resources: r, + levelI: levelI, } - return res, nil + return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, gkrcore.Degree(level, r.circuit), &r.transcript) } // Prove consistency of the claimed assignment -func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { - o, err := setup(c, assignment, transcriptSettings, options...) +func Prove(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (Proof, error) { + r, err := newResources(c, schedule, assignment, hasher) if err != nil { return nil, err } - defer o.workers.Stop() + defer r.workers.Stop() - claims := newClaimsManager(c, assignment, o) + proof := make(Proof, len(schedule)) - proof := make(Proof, len(c)) - // firstChallenge called rho in the paper - var firstChallenge []fr.Element - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return nil, err + // Derive initial challenge point + firstChallenge := make([]fr.Element, r.nbVars) + for j := range r.nbVars { + firstChallenge[j] = r.transcript.getChallenge() } + r.levelPoints[len(schedule)] = firstChallenge - wirePrefix := o.transcriptPrefix + "w" - var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - - wire := c[i] - - if wire.IsOutput() { - claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) - } - - claim := claims.getClaim(i) - if wire.NoProof() { // input wires with one claim only - proof[i] = sumcheckProof{ - partialSumPolys: []polynomial.Polynomial{}, - finalEvalProof: []fr.Element{}, - } - } else { - if proof[i], err = sumcheckProve( - claim, fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err != nil { - return proof, err - } + for levelI := len(schedule) - 1; levelI >= 0; levelI-- { + switch s := schedule[levelI].(type) { + case constraint.GkrSkipLevel: + r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - baseChallenge = make([][]byte, len(proof[i].finalEvalProof)) - for j := range proof[i].finalEvalProof { - baseChallenge[j] = proof[i].finalEvalProof[j].Marshal() - } + case constraint.GkrSumcheckLevel: + proof[levelI] = r.proveLevel(levelI) + // Bind finalEvalProof for next level's challenge derivation + r.transcript.bind(proof[levelI].finalEvalProof...) } - // the verifier checks a single claim about input wires itself - claims.deleteClaim(i) } return proof, nil } -// Verify the consistency of the claimed output with the claimed input -// Unlike in Prove, the assignment argument need not be complete -func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { - o, err := setup(c, assignment, transcriptSettings, options...) +// Verify the consistency of the claimed output with the claimed input. +// Unlike in Prove, the assignment argument need not be complete. +func Verify(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, proof Proof, hasher hash.Hash) error { + r, err := newResources(c, schedule, assignment, hasher) if err != nil { return err } - defer o.workers.Stop() - - claims := newClaimsManager(c, assignment, o) + defer r.workers.Stop() - var firstChallenge []fr.Element - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return err + // Derive initial challenge point + firstChallenge := make([]fr.Element, r.nbVars) + for j := range r.nbVars { + firstChallenge[j] = r.transcript.getChallenge() } + r.levelPoints[len(schedule)] = firstChallenge - wirePrefix := o.transcriptPrefix + "w" - var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - wire := c[i] - - if wire.IsOutput() { - claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) - } - - proofW := proof[i] - claim := claims.getLazyClaim(i) - if wire.NoProof() { // input wires with one claim only - // make sure the proof is empty - if len(proofW.finalEvalProof) != 0 || len(proofW.partialSumPolys) != 0 { - return errors.New("no proof allowed for input wire with a single claim") - } + for levelI := len(schedule) - 1; levelI >= 0; levelI-- { + switch s := schedule[levelI].(type) { + case constraint.GkrSkipLevel: + r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - if wire.NbClaims() == 1 { // input wire - // simply evaluate and see if it matches - if len(claim.evaluationPoints) == 0 || len(claim.claimedEvaluations) == 0 { - return errors.New("missing input wire claim") - } - evaluation := assignment[i].Evaluate(claim.evaluationPoints[0], claims.memPool) - if !claim.claimedEvaluations[0].Equal(&evaluation) { - return errors.New("incorrect input wire claim") - } + case constraint.GkrSumcheckLevel: + if err = r.verifyLevel(levelI, proof); err != nil { + return fmt.Errorf("sumcheck proof rejected: %v", err) } - } else if err = sumcheckVerify( - claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { // incorporate prover claims about w's input into the transcript - baseChallenge = make([][]byte, len(proofW.finalEvalProof)) - for j := range baseChallenge { - baseChallenge[j] = proofW.finalEvalProof[j].Marshal() - } - } else { - return fmt.Errorf("sumcheck proof rejected: %v", err) //TODO: Any polynomials to dump? + // Bind finalEvalProof for next level's challenge derivation + r.transcript.bind(proof[levelI].finalEvalProof...) } - claims.deleteClaim(i) } return nil } diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 08f76de14a..02ab912447 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -19,10 +19,9 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/mimc" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - gcUtils "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrtesting" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" + "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/stretchr/testify/assert" ) @@ -69,36 +68,86 @@ func TestMimc(t *testing.T) { test(t, gkrtesting.MiMCCircuit(93)) } -func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - // Construct SerializableCircuit directly, bypassing CompileCircuit - // which would reset NbUniqueOutputs based on actual topology - circuit := gkrcore.SerializableCircuit{ - { - NbUniqueOutputs: 2, - Gate: gkrcore.SerializableGate{Degree: 1}, - }, - } +// testLevel exercises proveLevel/verifyLevel for a single sumcheck level. +func testLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrSumcheckLevel) { + t.Helper() + _, sCircuit := cache.Compile(t, circuit) - assignment := WireAssignment{[]fr.Element{two, three}} - var o settings - pool := polynomial.NewPool(256, 1<<11) - workers := gcUtils.NewWorkerPool() - o.pool = &pool - o.workers = workers - - claimsManagerGen := func() *claimsManager { - manager := newClaimsManager(circuit, assignment, o) - manager.add(0, []fr.Element{three}, five) - manager.add(0, []fr.Element{four}, six) - return &manager + ins := sCircuit.Inputs() + assignment := make(WireAssignment, len(sCircuit)) + for _, i := range ins { + assignment[i] = make([]fr.Element, 2) + fr.Vector(assignment[i]).MustSetRandom() } - transcriptGen := newMessageCounterGenerator(4, 1) + assignment.Complete(sCircuit) + + schedule := constraint.GkrProvingSchedule{level} + challenge := []fr.Element{five} - proof, err := sumcheckProve(claimsManagerGen().getClaim(0), fiatshamir.WithHash(transcriptGen(), nil)) + // Prove + proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) - err = sumcheckVerify(claimsManagerGen().getLazyClaim(0), proof, fiatshamir.WithHash(transcriptGen(), nil)) + defer proveR.workers.Stop() + + proveR.levelPoints[1] = challenge + proof := Proof{proveR.proveLevel(0)} + + // Verify + verifyR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) + defer verifyR.workers.Stop() + + verifyR.levelPoints[1] = challenge + assert.NoError(t, verifyR.verifyLevel(0, proof)) +} + +func TestLevel(t *testing.T) { + // Wires 0,1 = inputs; wires 2,3,4 = mul(0,1). All gates are independent outputs. + circuit := gkrcore.RawCircuit{ + {}, + {}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, + } + // All levels have initial challenge at index 1 (len(schedule) = 1). + tests := []struct { + name string + level constraint.GkrSumcheckLevel + }{ + { + name: "single wire", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []int{1}}, + }, + }, + { + name: "two groups", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []int{1}}, + {Wires: []int{3}, ClaimSources: []int{1}}, + }, + }, + { + name: "one group with two wires", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4, 3}, ClaimSources: []int{1}}, + }, + }, + { + name: "mixed: single + multi-wire group", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []int{1}}, + {Wires: []int{3, 2}, ClaimSources: []int{1}}, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testLevel(t, circuit, tc.level) + }) + } } var one, two, three, four, five, six fr.Element @@ -133,7 +182,16 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrcore.RawCircuit) { + testWithSchedule(t, circuit, nil) +} + +func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constraint.GkrProvingSchedule) { gCircuit, sCircuit := cache.Compile(t, circuit) + if schedule == nil { + var err error + schedule, err = gkrcore.DefaultProvingSchedule(sCircuit) + assert.NoError(t, err) + } ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -153,22 +211,21 @@ func test(t *testing.T, circuit gkrcore.RawCircuit) { t.Log("Selected inputs for test") - proof, err := Prove(sCircuit, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(sCircuit, schedule, fullAssignment, newMessageCounter(1, 1)) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(1, 1)) assert.NoError(t, err, "proof rejected") if proof.isEmpty() { // special case for TestNoGate: continue // there's no way to make a trivial proof fail } - err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) + err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(0, 1)) assert.NotNil(t, err, "bad proof accepted") } - } func (p Proof) isEmpty() bool { @@ -188,14 +245,17 @@ func (p Proof) isEmpty() bool { func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { _, c := cache.Compile(t, gkrtesting.NoGateCircuit()) + schedule, err := gkrcore.DefaultProvingSchedule(c) + assert.NoError(t, err) + assignment := WireAssignment{0: inputAssignments[0]} - proof, err := Prove(c, assignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(c, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(c, assignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(c, schedule, assignment, proof, newMessageCounter(1, 1)) assert.NoError(t, err, "proof rejected") } @@ -203,7 +263,7 @@ func generateTestProver(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) assert.NoError(t, err) - proof, err := Prove(testCase.Circuit, testCase.FullAssignment, fiatshamir.WithHash(testCase.Hash)) + proof, err := Prove(testCase.Circuit, testCase.Schedule, testCase.FullAssignment, testCase.Hash) assert.NoError(t, err) assert.NoError(t, proofEquals(testCase.Proof, proof)) } @@ -213,17 +273,16 @@ func generateTestVerifier(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(testCase.Hash)) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) assert.NoError(t, err, "proof rejected") testCase, err = newTestCase(path) assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(newMessageCounter(2, 0))) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, newMessageCounter(2, 0)) assert.NotNil(t, err, "bad proof accepted") } } func TestGkrVectors(t *testing.T) { - const testDirPath = "../test_vectors/" dirEntries, err := os.ReadDir(testDirPath) assert.NoError(t, err) @@ -269,6 +328,9 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") _, c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) + schedule, err := gkrcore.DefaultProvingSchedule(c) + assert.NoError(b, err) + in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) fr.Vector(in0).MustSetRandom() @@ -283,12 +345,30 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { //b.ResetTimer() fmt.Println("constructing proof") start = time.Now().UnixMicro() - _, err := Prove(c, assignment, fiatshamir.WithHash(mimc.NewMiMC())) + _, err = Prove(c, schedule, assignment, mimc.NewMiMC()) proved := time.Now().UnixMicro() - start fmt.Println("proved in", proved, "μs") assert.NoError(b, err) } +// TestSingleMulGateExplicitSchedule tests a single mul gate with an explicit single-step schedule, +// equivalent to the default but constructed manually to exercise the schedule path. +func TestSingleMulGateExplicitSchedule(t *testing.T) { + circuit := gkrtesting.SingleMulGateCircuit() + _, sCircuit := cache.Compile(t, circuit) + + // Wire 2 is the mul gate output (inputs: 0, 1). + // Explicit schedule: one SumcheckLevel for wire 2 only. + // ClaimSources: [1] = initial challenge (index len(schedule) = 1). + schedule := constraint.GkrProvingSchedule{ + constraint.GkrSumcheckLevel{ + {Wires: []int{2}, ClaimSources: []int{1}}, + }, + } + testWithSchedule(t, circuit, schedule) + _ = sCircuit +} + func BenchmarkGkrMimc19(b *testing.B) { benchmarkGkrMiMC(b, 1<<19, 91) } @@ -332,6 +412,7 @@ type TestCase struct { Proof Proof FullAssignment WireAssignment InOutAssignment WireAssignment + Schedule constraint.GkrProvingSchedule } var testCases = make(map[string]*TestCase) @@ -362,6 +443,20 @@ func newTestCase(path string) (*TestCase, error) { if proof, err = unmarshalProof(info.Proof); err != nil { return nil, err } + var schedule constraint.GkrProvingSchedule + if schedule, err = info.Schedule.ToProvingSchedule(); err != nil { + return nil, err + } + if schedule == nil { + if schedule, err = gkrcore.DefaultProvingSchedule(circuit); err != nil { + return nil, err + } + } + + outputSet := make(map[int]bool, len(circuit)) + for _, o := range circuit.Outputs() { + outputSet[o] = true + } fullAssignment := make(WireAssignment, len(circuit)) inOutAssignment := make(WireAssignment, len(circuit)) @@ -375,7 +470,7 @@ func newTestCase(path string) (*TestCase, error) { } assignmentRaw = info.Input[inI] inI++ - } else if circuit[i].IsOutput() { + } else if outputSet[i] { if outI == len(info.Output) { return nil, fmt.Errorf("fewer output in vector than in circuit") } @@ -396,7 +491,7 @@ func newTestCase(path string) (*TestCase, error) { fullAssignment.Complete(circuit) for i := range circuit { - if circuit[i].IsOutput() { + if outputSet[i] { if err = sliceEquals(inOutAssignment[i], fullAssignment[i]); err != nil { return nil, fmt.Errorf("assignment mismatch: %v", err) } @@ -409,6 +504,7 @@ func newTestCase(path string) (*TestCase, error) { Proof: proof, Hash: _hash, Circuit: circuit, + Schedule: schedule, } testCases[path] = tCase diff --git a/internal/gkr/bls12-381/sumcheck.go b/internal/gkr/bls12-381/sumcheck.go index 4ff812a8ce..c00902fe0e 100644 --- a/internal/gkr/bls12-381/sumcheck.go +++ b/internal/gkr/bls12-381/sumcheck.go @@ -7,33 +7,62 @@ package gkr import ( "errors" - "strconv" + "hash" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) -// This does not make use of parallelism and represents polynomials as lists of coefficients -// It is currently geared towards arithmetic hashes. Once we have a more unified hash function interface, this can be generified. +// This does not make use of parallelism and represents polynomials as lists of coefficients. + +// transcript is a Fiat-Shamir transcript backed by a running hash. +// Field elements are written via bind; challenges are derived via getChallenge. +// The hash is never reset — all previous data is implicitly part of future challenges. +type transcript struct { + h hash.Hash + bound bool // whether bind was called since the last getChallenge +} + +// bind writes field elements to the transcript as bindings for the next challenge. +func (t *transcript) bind(elements ...fr.Element) { + if len(elements) == 0 { + return + } + for i := range elements { + bytes := elements[i].Bytes() + t.h.Write(bytes[:]) + } + t.bound = true +} + +// getChallenge binds optional elements, then squeezes a challenge from the current hash state. +// If no bindings were added since the last squeeze, a separator byte is written first +// to advance the state and prevent repeated values. +func (t *transcript) getChallenge(bindings ...fr.Element) fr.Element { + t.bind(bindings...) + if !t.bound { + t.h.Write([]byte{0}) + } + t.bound = false + var res fr.Element + res.SetBytes(t.h.Sum(nil)) + return res +} // sumcheckClaims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. // Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) type sumcheckClaims interface { - fold(a fr.Element) polynomial.Polynomial // fold into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. - next(fr.Element) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ + roundPolynomial() polynomial.Polynomial // compute gⱼ polynomial for current round + roundFold(r fr.Element) // fold inputs and eq at challenge r varsNum() int // number of variables - claimsNum() int // number of claims proveFinalEval(r []fr.Element) []fr.Element // in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof } // sumcheckLazyClaims is the sumcheckClaims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. type sumcheckLazyClaims interface { - claimsNum() int // claimsNum = m - varsNum() int // varsNum = n - foldedSum(a fr.Element) fr.Element // foldedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - degree(i int) int // degree of the total claim in the i'th variable - verifyFinalEval(r []fr.Element, foldingCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error + varsNum() int // varsNum = n + degree(i int) int // degree of the total claim in the i'th variable + verifyFinalEval(r []fr.Element, purportedValue fr.Element, proof []fr.Element) error } // sumcheckProof of a multi-statement. @@ -42,130 +71,46 @@ type sumcheckProof struct { finalEvalProof []fr.Element //in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof } -func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) (challengeNames []string, err error) { - numChallenges := varsNum - if claimsNum >= 2 { - numChallenges++ - } - challengeNames = make([]string, numChallenges) - if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "fold" - } - prefix := settings.Prefix + "pSP." - for i := 0; i < varsNum; i++ { - challengeNames[i+numChallenges-varsNum] = prefix + strconv.Itoa(i) - } - if settings.Transcript == nil { - transcript := fiatshamir.NewTranscript(settings.Hash, challengeNames...) - settings.Transcript = transcript - } - - for i := range settings.BaseChallenges { - if err = settings.Transcript.Bind(challengeNames[0], settings.BaseChallenges[i]); err != nil { - return - } - } - return -} - -func next(transcript *fiatshamir.Transcript, bindings []fr.Element, remainingChallengeNames *[]string) (fr.Element, error) { - challengeName := (*remainingChallengeNames)[0] - for i := range bindings { - bytes := bindings[i].Bytes() - if err := transcript.Bind(challengeName, bytes[:]); err != nil { - return fr.Element{}, err - } - } - var res fr.Element - bytes, err := transcript.ComputeChallenge(challengeName) - res.SetBytes(bytes) - - *remainingChallengeNames = (*remainingChallengeNames)[1:] - - return res, err -} - -// sumcheckProve create a non-interactive proof -func sumcheckProve(claims sumcheckClaims, transcriptSettings fiatshamir.Settings) (sumcheckProof, error) { - - var proof sumcheckProof - remainingChallengeNames, err := setupTranscript(claims.claimsNum(), claims.varsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return proof, err - } - - var foldingCoeff fr.Element - if claims.claimsNum() >= 2 { - if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { - return proof, err - } - } - +// sumcheckProve creates a non-interactive sumcheck proof. +// The fold challenge is derived by the caller (proveLevel). +// Pattern: roundPolynomial, [roundFold, roundPolynomial]*, proveFinalEval. +func sumcheckProve(claims sumcheckClaims, t *transcript) sumcheckProof { varsNum := claims.varsNum() - proof.partialSumPolys = make([]polynomial.Polynomial, varsNum) - proof.partialSumPolys[0] = claims.fold(foldingCoeff) + proof := sumcheckProof{partialSumPolys: make([]polynomial.Polynomial, varsNum)} + proof.partialSumPolys[0] = claims.roundPolynomial() challenges := make([]fr.Element, varsNum) - for j := 0; j+1 < varsNum; j++ { - if challenges[j], err = next(transcript, proof.partialSumPolys[j], &remainingChallengeNames); err != nil { - return proof, err - } - proof.partialSumPolys[j+1] = claims.next(challenges[j]) - } - - if challenges[varsNum-1], err = next(transcript, proof.partialSumPolys[varsNum-1], &remainingChallengeNames); err != nil { - return proof, err + for j := range varsNum - 1 { + challenges[j] = t.getChallenge(proof.partialSumPolys[j]...) + claims.roundFold(challenges[j]) + proof.partialSumPolys[j+1] = claims.roundPolynomial() } + challenges[varsNum-1] = t.getChallenge(proof.partialSumPolys[varsNum-1]...) proof.finalEvalProof = claims.proveFinalEval(challenges) - - return proof, nil + return proof } -func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSettings fiatshamir.Settings) error { - remainingChallengeNames, err := setupTranscript(claims.claimsNum(), claims.varsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return err - } - - var foldingCoeff fr.Element - - if claims.claimsNum() >= 2 { - if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { - return err - } - } - +// sumcheckVerify verifies a non-interactive sumcheck proof. +// The fold challenge is derived by the caller (verifyLevel). +// claimedSum is the expected sum; degree is the polynomial's degree in each variable. +func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, claimedSum fr.Element, degree int, t *transcript) error { r := make([]fr.Element, claims.varsNum()) - // Just so that there is enough room for gJ to be reused - maxDegree := claims.degree(0) - for j := 1; j < claims.varsNum(); j++ { - if d := claims.degree(j); d > maxDegree { - maxDegree = d - } - } - gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() - gJR := claims.foldedSum(foldingCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) + gJ := make(polynomial.Polynomial, degree+1) + gJR := claimedSum for j := range claims.varsNum() { - if len(proof.partialSumPolys[j]) != claims.degree(j) { + if len(proof.partialSumPolys[j]) != degree { return errors.New("malformed proof") } copy(gJ[1:], proof.partialSumPolys[j]) - gJ[0].Sub(&gJR, &proof.partialSumPolys[j][0]) // Requirement that gⱼ(0) + gⱼ(1) = gⱼ₋₁(r) - // gJ is ready + gJ[0].Sub(&gJR, &proof.partialSumPolys[j][0]) - //Prepare for the next iteration - if r[j], err = next(transcript, proof.partialSumPolys[j], &remainingChallengeNames); err != nil { - return err - } - // This is an extremely inefficient way of interpolating. TODO: Interpolate without symbolically computing a polynomial - gJCoeffs := polynomial.InterpolateOnRange(gJ[:(claims.degree(j) + 1)]) + r[j] = t.getChallenge(proof.partialSumPolys[j]...) + gJCoeffs := polynomial.InterpolateOnRange(gJ[:(degree + 1)]) gJR = gJCoeffs.Eval(&r[j]) } - return claims.verifyFinalEval(r, foldingCoeff, gJR, proof.finalEvalProof) + return claims.verifyFinalEval(r, gJR, proof.finalEvalProof) } diff --git a/internal/gkr/bls12-381/sumcheck_test.go b/internal/gkr/bls12-381/sumcheck_test.go index 70574588a9..5f8a12fc5a 100644 --- a/internal/gkr/bls12-381/sumcheck_test.go +++ b/internal/gkr/bls12-381/sumcheck_test.go @@ -10,7 +10,6 @@ import ( "hash" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/stretchr/testify/assert" "math/bits" @@ -28,11 +27,9 @@ func testSumcheckSingleClaimMultilin(polyInt []uint64, hashGenerator func() hash } claim := singleMultilinClaim{g: poly.Clone()} + t := transcript{h: hashGenerator()} - proof, err := sumcheckProve(&claim, fiatshamir.WithHash(hashGenerator())) - if err != nil { - return err - } + proof := sumcheckProve(&claim, &t) var sb strings.Builder for _, p := range proof.partialSumPolys { @@ -48,13 +45,15 @@ func testSumcheckSingleClaimMultilin(polyInt []uint64, hashGenerator func() hash } lazyClaim := singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if err = sumcheckVerify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())); err != nil { + t = transcript{h: hashGenerator()} + if err := sumcheckVerify(lazyClaim, proof, lazyClaim.claimedSum, 1, &t); err != nil { return err } proof.partialSumPolys[0][0].Add(&proof.partialSumPolys[0][0], toElement(1)) lazyClaim = singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if sumcheckVerify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())) == nil { + t = transcript{h: hashGenerator()} + if sumcheckVerify(lazyClaim, proof, lazyClaim.claimedSum, 1, &t) == nil { return fmt.Errorf("bad proof accepted") } return nil @@ -93,18 +92,14 @@ type singleMultilinClaim struct { g polynomial.MultiLin } -func (c singleMultilinClaim) proveFinalEval(r []fr.Element) []fr.Element { +func (c *singleMultilinClaim) proveFinalEval(r []fr.Element) []fr.Element { return nil // verifier can compute the final eval itself } -func (c singleMultilinClaim) varsNum() int { +func (c *singleMultilinClaim) varsNum() int { return bits.TrailingZeros(uint(len(c.g))) } -func (c singleMultilinClaim) claimsNum() int { - return 1 -} - func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { sum := g[len(g)/2] for i := len(g)/2 + 1; i < len(g); i++ { @@ -113,13 +108,12 @@ func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { return []fr.Element{sum} } -func (c singleMultilinClaim) fold(fr.Element) polynomial.Polynomial { +func (c *singleMultilinClaim) roundPolynomial() polynomial.Polynomial { return sumForX1One(c.g) } -func (c *singleMultilinClaim) next(r fr.Element) polynomial.Polynomial { +func (c *singleMultilinClaim) roundFold(r fr.Element) { c.g.Fold(r) - return sumForX1One(c.g) } type singleMultilinLazyClaim struct { @@ -127,7 +121,7 @@ type singleMultilinLazyClaim struct { claimedSum fr.Element } -func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, _ fr.Element, purportedValue fr.Element, proof []fr.Element) error { +func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, purportedValue fr.Element, proof []fr.Element) error { val := c.g.Evaluate(r, nil) if val.Equal(&purportedValue) { return nil @@ -135,15 +129,7 @@ func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, _ fr.Element, p return fmt.Errorf("mismatch") } -func (c singleMultilinLazyClaim) foldedSum(_ fr.Element) fr.Element { - return c.claimedSum -} - -func (c singleMultilinLazyClaim) degree(i int) int { - return 1 -} - -func (c singleMultilinLazyClaim) claimsNum() int { +func (c singleMultilinLazyClaim) degree(int) int { return 1 } diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 545d98f65d..5fd64ac5ed 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -276,7 +276,7 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra // Call the BN254-specific Prove function (assignments already WireAssignment type) proof, err := Prove(solveBlueprint.Circuit, b.Schedule, assignments, hsh) if err != nil { - return fmt.Errorf("bn254 prove failed: %w", err) + return fmt.Errorf("BN254 prove failed: %w", err) } for i, elem := range proof.flatten() { diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index f087e519be..0b056132e4 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -33,7 +33,7 @@ type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) // zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). -// It checks that the polynomial ∑ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, // where the sum runs over all wᵢ and evaluation point xᵢ in the level. // Its purpose is to batch the checking of multiple wire evaluations at evaluation points. type zeroCheckLazyClaims struct { @@ -52,11 +52,11 @@ func (e *zeroCheckLazyClaims) degree(int) int { // verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. // The sumcheck protocol has already reduced the per-wire claims w(xᵢ) = yᵢ to verifying -// ∑ cⁱ eq(xᵢ, r) · gateᵥ(r) = purportedValue, where the sum runs over all +// ∑ᵢ cⁱ eq(xᵢ, r) · wᵢ(r) = purportedValue, where the sum runs over all // claims on each wire and c is foldingCoeff. // Both purportedValue and the vector r have been randomized during sumcheck. // -// For input wires, gateᵥ(r) is computed directly from the assignment. +// For input wires, w(r) is computed directly from the assignment. // For non-input wires, the prover claims evaluations of the input wires at r, // communicated through uniqueInputEvaluations; those claims are verified later. // The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming @@ -101,7 +101,7 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. } // zeroCheckClaims is a claim for sumcheck (prover side). -// It checks that the polynomial ∑ cⁱ eq(-, xᵢ) gateᵥ(-) sums to the expected value, +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, // where the sum runs over all (wire v, claim source s) pairs in the level. // Each wire has its own eq table with the batching coefficients baked in. type zeroCheckClaims struct { @@ -110,7 +110,7 @@ type zeroCheckClaims struct { resources *resources input []polynomial.MultiLin // UniqueGateInputs order inputIndices [][]int // [wireInLevel][gateInputJ] → index in input - eqs []polynomial.MultiLin // per-wire interpolation bases for evaluating wire assignments at the challenge point + eqs []polynomial.MultiLin // per-wire interpolation bases for evaluating wire assignments at challenge points gateEvaluatorPools []*gateEvaluatorPool } @@ -118,7 +118,7 @@ func (c *zeroCheckClaims) varsNum() int { return len(c.resources.levelPoints[c.level[0].ClaimSources[0]]) } -// roundPolynomial computes gⱼ = ∑_h ∑_w eqs[w](Xⱼ, h...) · gateᵥ(inputs(Xⱼ, h...)). +// roundPolynomial computes gⱼ = ∑ₕ ∑ᵥ eqs[v](Xⱼ, h...) · gateᵥ(inputs(Xⱼ, h...)). // The polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). // By convention, g₀ is a constant polynomial equal to the claimed sum. diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 3116515156..b12ad775b9 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -117,8 +117,10 @@ func TestLevel(t *testing.T) { level constraint.GkrSumcheckLevel }{ { - name: "single wire", - level: constraint.GkrSumcheckLevel{{Wires: []int{4}, ClaimSources: []int{1}}}, + name: "single wire", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []int{1}}, + }, }, { name: "two groups", @@ -128,8 +130,10 @@ func TestLevel(t *testing.T) { }, }, { - name: "one group with two wires", - level: constraint.GkrSumcheckLevel{{Wires: []int{4, 3}, ClaimSources: []int{1}}}, + name: "one group with two wires", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4, 3}, ClaimSources: []int{1}}, + }, }, { name: "mixed: single + multi-wire group", @@ -347,7 +351,7 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { assert.NoError(b, err) } -// TestSingleMulGateLayered tests a single mul gate with an explicit single-step schedule, +// TestSingleMulGateExplicitSchedule tests a single mul gate with an explicit single-step schedule, // equivalent to the default but constructed manually to exercise the schedule path. func TestSingleMulGateExplicitSchedule(t *testing.T) { circuit := gkrtesting.SingleMulGateCircuit() @@ -373,13 +377,13 @@ func BenchmarkGkrMimc17(b *testing.B) { benchmarkGkrMiMC(b, 1<<17, 91) } -func unmarshalProof(info gkrtesting.PrintableProof) (Proof, error) { - proof := make(Proof, len(info)) - for i := range info { +func unmarshalProof(printable gkrtesting.PrintableProof) (Proof, error) { + proof := make(Proof, len(printable)) + for i := range printable { finalEvalProof := []fr.Element(nil) - if info[i].FinalEvalProof != nil { - finalEvalSlice := reflect.ValueOf(info[i].FinalEvalProof) + if printable[i].FinalEvalProof != nil { + finalEvalSlice := reflect.ValueOf(printable[i].FinalEvalProof) finalEvalProof = make([]fr.Element, finalEvalSlice.Len()) for k := range finalEvalProof { if _, err := setElement(&finalEvalProof[k], finalEvalSlice.Index(k).Interface()); err != nil { @@ -389,12 +393,12 @@ func unmarshalProof(info gkrtesting.PrintableProof) (Proof, error) { } proof[i] = sumcheckProof{ - partialSumPolys: make([]polynomial.Polynomial, len(info[i].PartialSumPolys)), + partialSumPolys: make([]polynomial.Polynomial, len(printable[i].PartialSumPolys)), finalEvalProof: finalEvalProof, } - for k := range info[i].PartialSumPolys { + for k := range printable[i].PartialSumPolys { var err error - if proof[i].partialSumPolys[k], err = sliceToElementSlice(info[i].PartialSumPolys[k]); err != nil { + if proof[i].partialSumPolys[k], err = sliceToElementSlice(printable[i].PartialSumPolys[k]); err != nil { return nil, err } } diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 3130087e73..8430fb44db 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -15,7 +15,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" @@ -204,6 +203,7 @@ func (b *BlueprintSolve) UpdateInstructionTree(inst constraint.Instruction, tree type BlueprintProve struct { SolveBlueprintID constraint.BlueprintID SolveBlueprint *BlueprintSolve `cbor:"-"` // not serialized, set at compile time + Schedule constraint.GkrProvingSchedule HashName string lock sync.Mutex @@ -256,9 +256,11 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra } } + // Create hasher and write base challenges + hsh := hash.NewHash(b.HashName + "_BW6_761") + // Read initial challenges from instruction calldata (parse dynamically, no metadata) // Format: [0]=totalSize, [1...]=challenge linear expressions - insBytes := make([][]byte, 0) // first challenges calldata := inst.Calldata[1:] // skip size prefix for len(calldata) != 0 { val, delta := s.Read(calldata) @@ -267,17 +269,14 @@ func (b *BlueprintProve) Solve(s constraint.Solver[constraint.U64], inst constra // Copy directly from constraint.U64 to fr.Element (both in Montgomery form) var challenge fr.Element copy(challenge[:], val[:]) - insBytes = append(insBytes, challenge.Marshal()) + challengeBytes := challenge.Bytes() + hsh.Write(challengeBytes[:]) } - // Create Fiat-Shamir settings - hsh := hash.NewHash(b.HashName + "_BW6_761") - fsSettings := fiatshamir.WithHash(hsh, insBytes...) - // Call the BW6_761-specific Prove function (assignments already WireAssignment type) - proof, err := Prove(solveBlueprint.Circuit, assignments, fsSettings) + proof, err := Prove(solveBlueprint.Circuit, b.Schedule, assignments, hsh) if err != nil { - return fmt.Errorf("bw6_761 prove failed: %w", err) + return fmt.Errorf("BW6_761 prove failed: %w", err) } for i, elem := range proof.flatten() { @@ -305,7 +304,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return b.SolveBlueprint.Circuit.ProofSize(logNbInstances) + return gkrcore.ProofSize(b.Schedule, b.SolveBlueprint.Circuit, logNbInstances) } // NbOutputs implements Blueprint @@ -434,7 +433,7 @@ func (b *BlueprintGetAssignment) UpdateInstructionTree(inst constraint.Instructi } // NewBlueprints creates and registers all GKR blueprints for BW6_761 -func NewBlueprints(circuit gkrcore.SerializableCircuit, hashName string, compiler constraint.CustomizableSystem) gkrcore.Blueprints { +func NewBlueprints(circuit gkrcore.SerializableCircuit, schedule constraint.GkrProvingSchedule, hashName string, compiler constraint.CustomizableSystem) gkrcore.Blueprints { // Create and register solve blueprint solve := &BlueprintSolve{Circuit: circuit} solveID := compiler.AddBlueprint(solve) @@ -443,6 +442,7 @@ func NewBlueprints(circuit gkrcore.SerializableCircuit, hashName string, compile prove := &BlueprintProve{ SolveBlueprintID: solveID, SolveBlueprint: solve, + Schedule: schedule, HashName: hashName, } proveID := compiler.AddBlueprint(prove) diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 26bd13f74f..f24adababa 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -8,14 +8,14 @@ package gkr import ( "errors" "fmt" + "hash" "iter" - "strconv" "sync" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" ) @@ -33,630 +33,487 @@ type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) // zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). -// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear -// extension of the values of w across all instances. -// Its purpose is to batch the checking of multiple evaluations of the same wire. +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, +// where the sum runs over all wᵢ and evaluation point xᵢ in the level. +// Its purpose is to batch the checking of multiple wire evaluations at evaluation points. type zeroCheckLazyClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []fr.Element // yᵢ = w(xᵢ), allegedly - manager *claimsManager // WARNING: Circular references -} - -func (e *zeroCheckLazyClaims) getWire() Wire { - return e.manager.circuit[e.wireI] -} - -func (e *zeroCheckLazyClaims) claimsNum() int { - return len(e.evaluationPoints) + foldingCoeff fr.Element // the coefficient used to fold claims, conventionally 0 if there is only one claim + resources *resources + levelI int } func (e *zeroCheckLazyClaims) varsNum() int { - return len(e.evaluationPoints[0]) -} - -// foldedSum returns ∑ᵢ aⁱ yᵢ -func (e *zeroCheckLazyClaims) foldedSum(a fr.Element) fr.Element { - evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) - return evalsAsPoly.Eval(&a) + return e.resources.nbVars } func (e *zeroCheckLazyClaims) degree(int) int { - return e.manager.circuit[e.wireI].ZeroCheckDegree() -} - -// verifyFinalEval finalizes the verification of w. -// The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. (c is foldingCoeff) -// Both purportedValue and the vector r have been randomized during the sumcheck protocol. -// By taking the w term out of the sum we get the equivalent claim that -// for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. -// If w is an input wire, the verifier can directly check its evaluation at r. -// Otherwise, the prover makes claims about the evaluation of w's input wires, -// wᵢ, at r, to be verified later. -// The claims are communicated through the proof parameter. -// The verifier checks here if the claimed evaluations of wᵢ(r) are consistent with -// the main claim, by checking E w(wᵢ(r)...) = purportedValue. -func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, foldingCoeff, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { - // the eq terms ( E ) - numClaims := len(e.evaluationPoints) - evaluation := polynomial.EvalEq(e.evaluationPoints[numClaims-1], r) - for i := numClaims - 2; i >= 0; i-- { - evaluation.Mul(&evaluation, &foldingCoeff) - eq := polynomial.EvalEq(e.evaluationPoints[i], r) - evaluation.Add(&evaluation, &eq) - } - - wire := e.manager.circuit[e.wireI] - - // the w(...) term - var gateEvaluation fr.Element - if wire.IsInput() { // just compute w(r) - gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) - } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire - injection, injectionLeftInv := - e.manager.circuit.ClaimPropagationInfo(e.wireI) - - if len(injection) != len(uniqueInputEvaluations) { - return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) - } - - for uniqueI, i := range injection { // map from unique to all - e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) - } + return gkrcore.Degree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel), e.resources.circuit) +} + +// verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. +// The sumcheck protocol has already reduced the per-wire claims w(xᵢ) = yᵢ to verifying +// ∑ᵢ cⁱ eq(xᵢ, r) · wᵢ(r) = purportedValue, where the sum runs over all +// claims on each wire and c is foldingCoeff. +// Both purportedValue and the vector r have been randomized during sumcheck. +// +// For input wires, w(r) is computed directly from the assignment. +// For non-input wires, the prover claims evaluations of the input wires at r, +// communicated through uniqueInputEvaluations; those claims are verified later. +// The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming +// that the full sum matches purportedValue. +func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { + e.resources.levelPoints[e.levelI] = r + level := e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel) + perWireInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) + + var terms []fr.Element + flatW := 0 + for _, group := range level { + for _, wI := range group.Wires { + wire := e.resources.circuit[wI] + + var gateEval fr.Element + if wire.IsInput() { + gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) + } else { + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) + for _, v := range perWireInputEvals[flatW] { + evaluator.pushInput(v) + } + gateEval.Set(evaluator.evaluate()) + } - evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) - for _, uniqueI := range injectionLeftInv { // map from all to unique - evaluator.pushInput(uniqueInputEvaluations[uniqueI]) + for _, src := range group.ClaimSources { + eq := polynomial.EvalEq(e.resources.levelPoints[src], r) + var term fr.Element + term.Mul(&eq, &gateEval) + terms = append(terms, term) + } + flatW++ } - - gateEvaluation.Set(evaluator.evaluate()) } - evaluation.Mul(&evaluation, &gateEvaluation) - - if evaluation.Equal(&purportedValue) { + ys := polynomial.Polynomial(terms) + if total := ys.Eval(&e.foldingCoeff); total.Equal(&purportedValue) { return nil } return errors.New("incompatible evaluations") } // zeroCheckClaims is a claim for sumcheck (prover side). -// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear -// extension of the values of w across all instances. -// Its purpose is to batch the proving of multiple evaluations of the same wire. +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, +// where the sum runs over all (wire v, claim source s) pairs in the level. +// Each wire has its own eq table with the batching coefficients baked in. type zeroCheckClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]fr.Element // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []fr.Element // yᵢ = w(xᵢ) - manager *claimsManager - - input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) - - eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) - - gateEvaluatorPool *gateEvaluatorPool -} - -func (c *zeroCheckClaims) getWire() Wire { - return c.manager.circuit[c.wireI] + level constraint.GkrSumcheckLevel + levelI int + resources *resources + input []polynomial.MultiLin // UniqueGateInputs order + inputIndices [][]int // [wireInLevel][gateInputJ] → index in input + eqs []polynomial.MultiLin // per-wire interpolation bases for evaluating wire assignments at challenge points + gateEvaluatorPools []*gateEvaluatorPool } -// fold the multiple claims into one claim using a random combination (foldingCoeff or c). -// From the original multiple claims of w(xᵢ) = yᵢ, we get a single claim -// ∑ᵢ,ₕ cⁱ eq(xᵢ, h) w(h) = ∑ᵢ cⁱ yᵢ, where h iterates over the hypercube (circuit instances) and -// i iterates over the claims. -// Equivalently, we could say ∑ᵢ cⁱ yᵢ = ∑ₕ,ᵢ cⁱ eq(xᵢ, h) w(h) = ∑ₕ w(h) ∑ᵢ cⁱ eq(xᵢ, h). -// Thus if we initially compute E := ∑ᵢ cⁱ eq(xᵢ, -), our claim will find the simpler form -// ∑ᵢ cⁱ yᵢ = ∑ₕ w(h) E(h), where the sum-checked polynomial is of degree deg(g) + 1, -// and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. -// The output of fold is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. -func (c *zeroCheckClaims) fold(foldingCoeff fr.Element) polynomial.Polynomial { - varsNum := c.varsNum() - eqLength := 1 << varsNum - claimsNum := c.claimsNum() - // initialize the eq tables ( E ) - c.eq = c.manager.memPool.Make(eqLength) - - c.eq[0].SetOne() - c.eq.Eq(c.evaluationPoints[0]) - - // E := eq(x₀, -) - newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) - aI := foldingCoeff - - // E += cⁱ eq(xᵢ, -) - for k := 1; k < claimsNum; k++ { - newEq[0].Set(&aI) - - c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) - - if k+1 < claimsNum { - aI.Mul(&aI, &foldingCoeff) - } - } - - c.manager.memPool.Dump(newEq) - - return c.computeGJ() -} - -// eqAcc sets m to an eq table at q and then adds it to e. -// m <- eq(q, -). -// e <- e + m -func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { - n := len(q) - - //At the end of each iteration, m(h₁, ..., hₙ) = eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) - for i := range q { // In the comments we use a 1-based index so q[i] = qᵢ₊₁ - // go through all assignments of (b₁, ..., bᵢ) ∈ {0,1}ⁱ - const threshold = 1 << 6 - k := 1 << i - if k < threshold { - for j := 0; j < k; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - } else { - c.manager.workers.Submit(k, func(start, end int) { - for j := start; j < end; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - }, 1024).Wait() - } - - } - c.manager.workers.Submit(len(e), func(start, end int) { - for i := start; i < end; i++ { - e[i].Add(&e[i], &m[i]) - } - }, 512).Wait() +func (c *zeroCheckClaims) varsNum() int { + return len(c.resources.levelPoints[c.level[0].ClaimSources[0]]) } -// computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). -// the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). -// The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. -func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { - - wire := c.getWire() - degGJ := wire.ZeroCheckDegree() // guaranteed to be no smaller than the actual deg(gⱼ) - nbGateIn := len(c.input) - - // Both E and wᵢ (the input wires and the eq table) are multilinear, thus - // they are linear in Xⱼ. - // So for f ∈ { E(r₁, ..., Xⱼ, h...) } ∪ {wᵢ(r₁, ..., Xⱼ, h...) }, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables. - // ml are such multilinear polynomials the evaluations of which over different values of Xⱼ are computed in this stepwise manner. - ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube - ml[0] = c.eq - copy(ml[1:], c.input) +// roundPolynomial computes gⱼ = ∑ₕ ∑ᵥ eqs[v](Xⱼ, h...) · gateᵥ(inputs(Xⱼ, h...)). +// The polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). +// The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). +// By convention, g₀ is a constant polynomial equal to the claimed sum. +func (c *zeroCheckClaims) roundPolynomial() polynomial.Polynomial { + degree := gkrcore.Degree(c.level, c.resources.circuit) + nbUniqueInputs := len(c.input) + nbWires := len(c.eqs) - sumSize := len(c.eq) / 2 // the range of h, over which we sum + // Both eqs and input are multilinear, thus linear in Xⱼ. + // For any such f, f(m) = m·(f(1) - f(0)) + f(0), and f(0), f(1) are read directly + // from the bookkeeping tables. This allows stepwise evaluation at Xⱼ = 1, 2, ..., degree. + // Layout: [eq₀, eq₁, ..., eq_{nbWires-1}, input₀, input₁, ..., input_{nbUniqueInputs-1}] + ml := make([]polynomial.MultiLin, nbWires+nbUniqueInputs) + copy(ml, c.eqs) + copy(ml[nbWires:], c.input) - // Perf-TODO: Collate once at claim "folding" time and not again. then, even folding can be done in one operation every time "next" is called + sumSize := len(c.eqs[0]) / 2 - gJ := make([]fr.Element, degGJ) + p := make([]fr.Element, degree) var mu sync.Mutex - computeAll := func(start, end int) { // compute method to allow parallelization across instances + computeAll := func(start, end int) { var step fr.Element - evaluator := c.gateEvaluatorPool.get() - defer c.gateEvaluatorPool.put(evaluator) + evaluators := make([]*gateEvaluator, nbWires) + for w := range nbWires { + evaluators[w] = c.gateEvaluatorPools[w].get() + } + defer func() { + for w := range nbWires { + c.gateEvaluatorPools[w].put(evaluators[w]) + } + }() - res := make([]fr.Element, degGJ) + res := make([]fr.Element, degree) // evaluations of ml, laid out as: // ml[0](1, h...), ml[1](1, h...), ..., ml[len(ml)-1](1, h...), // ml[0](2, h...), ml[1](2, h...), ..., ml[len(ml)-1](2, h...), // ... - // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) - mlEvals := make([]fr.Element, degGJ*len(ml)) - - for h := start; h < end; h++ { // h counts across instances + // ml[0](degree, h...), ml[1](degree, h...), ..., ml[len(ml)-1](degree, h...) + mlEvals := make([]fr.Element, degree*len(ml)) + for h := start; h < end; h++ { evalAt1Index := sumSize + h for k := range ml { - // d = 0 - mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. + mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1, taken directly from the table step.Sub(&mlEvals[k], &ml[k][h]) // step = ml[k](1) - ml[k](0) - for d := 1; d < degGJ; d++ { + for d := 1; d < degree; d++ { mlEvals[d*len(ml)+k].Add(&mlEvals[(d-1)*len(ml)+k], &step) } } - eIndex := 0 // index for where the current eq term is + eIndex := 0 // start of the current row's eq evaluations nextEIndex := len(ml) - for d := range degGJ { - // Push gate inputs - for i := range nbGateIn { - evaluator.pushInput(mlEvals[eIndex+1+i]) + for d := range degree { + for w := range nbWires { + for _, inputI := range c.inputIndices[w] { + evaluators[w].pushInput(mlEvals[eIndex+nbWires+inputI]) + } + summand := evaluators[w].evaluate() + summand.Mul(summand, &mlEvals[eIndex+w]) + res[d].Add(&res[d], summand) // collect contributions into the sum from start to end } - summand := evaluator.evaluate() - summand.Mul(summand, &mlEvals[eIndex]) - res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } } mu.Lock() - for i := range gJ { - gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum + for i := range p { + p[i].Add(&p[i], &res[i]) // collect into the complete sum } mu.Unlock() } const minBlockSize = 64 - if sumSize < minBlockSize { - // no parallelization computeAll(0, sumSize) } else { - c.manager.workers.Submit(sumSize, computeAll, minBlockSize).Wait() + c.resources.workers.Submit(sumSize, computeAll, minBlockSize).Wait() } - return gJ + return p } -// next first folds the input and E polynomials at the given verifier challenge then computes the new gⱼ. -// Thus, j <- j+1 and rⱼ = challenge. -func (c *zeroCheckClaims) next(challenge fr.Element) polynomial.Polynomial { +// roundFold folds all input and eq polynomials at the verifier challenge r. +// After this call, j ← j+1 and rⱼ = r. +func (c *zeroCheckClaims) roundFold(r fr.Element) { const minBlockSize = 512 - n := len(c.eq) / 2 + n := len(c.eqs[0]) / 2 if n < minBlockSize { - // no parallelization for i := range c.input { - c.input[i].Fold(challenge) + c.input[i].Fold(r) + } + for i := range c.eqs { + c.eqs[i].Fold(r) } - c.eq.Fold(challenge) } else { - wgs := make([]*sync.WaitGroup, len(c.input)) + wgs := make([]*sync.WaitGroup, len(c.input)+len(c.eqs)) for i := range c.input { - wgs[i] = c.manager.workers.Submit(n, c.input[i].FoldParallel(challenge), minBlockSize) + wgs[i] = c.resources.workers.Submit(n, c.input[i].FoldParallel(r), minBlockSize) + } + for i := range c.eqs { + wgs[len(c.input)+i] = c.resources.workers.Submit(n, c.eqs[i].FoldParallel(r), minBlockSize) } - c.manager.workers.Submit(n, c.eq.FoldParallel(challenge), minBlockSize).Wait() for _, wg := range wgs { wg.Wait() } } - - return c.computeGJ() -} - -func (c *zeroCheckClaims) varsNum() int { - return len(c.evaluationPoints[0]) -} - -func (c *zeroCheckClaims) claimsNum() int { - return len(c.claimedEvaluations) } -// proveFinalEval provides the values wᵢ(r₁, ..., rₙ) +// proveFinalEval provides the unique input wire values wᵢ(r₁, ..., rₙ). func (c *zeroCheckClaims) proveFinalEval(r []fr.Element) []fr.Element { - //defer the proof, return list of claims - - injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. - evaluations := make([]fr.Element, len(injection)) - for i, gateInputI := range injection { - wI := c.input[gateInputI] - wI.Fold(r[len(r)-1]) // We already have wᵢ(r₁, ..., rₙ₋₁, hₙ) in a table. Only one more fold required. - c.manager.add(c.getWire().Inputs[gateInputI], r, wI[0]) - evaluations[i] = wI[0] + c.resources.levelPoints[c.levelI] = r + evaluations := make([]fr.Element, len(c.input)) + for i := range c.input { + c.input[i].Fold(r[len(r)-1]) + evaluations[i] = c.input[i][0] } - - c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.dumpAll() - - return evaluations -} - -type claimsManager struct { - claims []*zeroCheckLazyClaims - assignment WireAssignment - memPool *polynomial.Pool - workers *utils.WorkerPool - circuit Circuit -} - -func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { - manager.assignment = assignment - manager.claims = make([]*zeroCheckLazyClaims, len(circuit)) - manager.memPool = o.pool - manager.workers = o.workers - manager.circuit = circuit - - for i := range circuit { - manager.claims[i] = &zeroCheckLazyClaims{ - wireI: i, - evaluationPoints: make([][]fr.Element, 0, circuit[i].NbClaims()), - claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), - manager: &manager, - } + for i := range c.input { + c.resources.memPool.Dump(c.input[i]) } - return -} - -func (m *claimsManager) add(wire int, evaluationPoint []fr.Element, evaluation fr.Element) { - claim := m.claims[wire] - i := len(claim.evaluationPoints) - claim.claimedEvaluations[i] = evaluation - claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) + for i := range c.eqs { + c.resources.memPool.Dump(c.eqs[i]) + } + for _, pool := range c.gateEvaluatorPools { + pool.dumpAll() + } + return evaluations } -func (m *claimsManager) getLazyClaim(wire int) *zeroCheckLazyClaims { - return m.claims[wire] -} +// eqAcc sets m to an eq table at q and then adds it to e. +// m <- m[0] · eq(q, -). +// e <- e + m +func (r *resources) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { + n := len(q) -func (m *claimsManager) getClaim(wireI int) *zeroCheckClaims { - lazy := m.claims[wireI] - wire := m.circuit[wireI] - res := &zeroCheckClaims{ - wireI: wireI, - evaluationPoints: lazy.evaluationPoints, - claimedEvaluations: lazy.claimedEvaluations, - manager: m, - } + // At the end of each iteration, m(h₁, ..., hₙ) = m[0] · eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) + for i := range q { // 1-based in comments: q[i] = qᵢ₊₁ + // go through all assignments of (b₁, ..., bᵢ) ∈ {0,1}ⁱ + const threshold = 1 << 6 + k := 1 << i + if k < threshold { + for j := 0; j < k; j++ { + j0 := j << (n - i) // bᵢ₊₁ = 0 + j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - if wire.IsInput() { - res.input = []polynomial.MultiLin{m.memPool.Clone(m.assignment[wireI])} - } else { - res.input = make([]polynomial.MultiLin, len(wire.Inputs)) + m[j1].Mul(&q[i], &m[j0]) // m(b₁,...,bᵢ,1) = m(b₁,...,bᵢ) · qᵢ₊₁ + m[j0].Sub(&m[j0], &m[j1]) // m(b₁,...,bᵢ,0) = m(b₁,...,bᵢ) · (1 - qᵢ₊₁) + } + } else { + r.workers.Submit(k, func(start, end int) { + for j := start; j < end; j++ { + j0 := j << (n - i) // bᵢ₊₁ = 0 + j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - for inputI, inputW := range wire.Inputs { - res.input[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied + m[j1].Mul(&q[i], &m[j0]) // m(b₁,...,bᵢ,1) = m(b₁,...,bᵢ) · qᵢ₊₁ + m[j0].Sub(&m[j0], &m[j1]) // m(b₁,...,bᵢ,0) = m(b₁,...,bᵢ) · (1 - qᵢ₊₁) + } + }, 1024).Wait() } } - - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Evaluate, len(res.input), m.memPool) - - return res -} - -func (m *claimsManager) deleteClaim(wire int) { - m.claims[wire].manager = nil - m.claims[wire] = nil + r.workers.Submit(len(e), func(start, end int) { + for i := start; i < end; i++ { + e[i].Add(&e[i], &m[i]) + } + }, 512).Wait() } -type settings struct { - pool *polynomial.Pool - transcript *fiatshamir.Transcript - transcriptPrefix string - nbVars int - workers *utils.WorkerPool +type resources struct { + // levelPoints[i] is the sumcheck evaluation point r produced at schedule level i. + // levelPoints[len(schedule)] holds the initial challenge (firstChallenge / rho). + // SkipLevels produce no point; their slot remains nil. + levelPoints [][]fr.Element + evalPositions [][]int // [wireI][evalI] → position in source level's finalEvalProof + nbVars int + assignment WireAssignment + memPool polynomial.Pool + workers *utils.WorkerPool + circuit Circuit + schedule constraint.GkrProvingSchedule + transcript transcript } -type Option func(*settings) - -func WithPool(pool *polynomial.Pool) Option { - return func(options *settings) { - options.pool = pool +func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (resources, error) { + nbVars := assignment.NumVars() + nbInstances := assignment.NumInstances() + if 1<= 2 { + foldingCoeff = r.transcript.getChallenge() } - if o.pool == nil { - pool := polynomial.NewPool(c.MemoryRequirements(nbInstances)...) - o.pool = &pool + uniqueInputs, inputIndices := gkrcore.InputMapping(level, r.circuit) + input := make([]polynomial.MultiLin, len(uniqueInputs)) + for i, inW := range uniqueInputs { + input[i] = r.memPool.Clone(r.assignment[inW]) } - if o.workers == nil { - o.workers = utils.NewWorkerPool() + nbWires := 0 + for _, group := range level { + nbWires += len(group.Wires) } - if transcriptSettings.Transcript == nil { - challengeNames := ChallengeNames(c, o.nbVars, transcriptSettings.Prefix) - o.transcript = fiatshamir.NewTranscript(transcriptSettings.Hash, challengeNames...) - for i := range transcriptSettings.BaseChallenges { - if err = o.transcript.Bind(challengeNames[0], transcriptSettings.BaseChallenges[i]); err != nil { - return o, err + pools := make([]*gateEvaluatorPool, nbWires) + flatW := 0 + for _, group := range level { + for _, wI := range group.Wires { + wire := r.circuit[wI] + gate := wire.Gate.Evaluate + if wire.IsInput() { + gate = gkrcore.IdentityBytecode() } + pools[flatW] = newGateEvaluatorPool(gate, len(inputIndices[flatW]), &r.memPool) + flatW++ } - } else { - o.transcript, o.transcriptPrefix = transcriptSettings.Transcript, transcriptSettings.Prefix } - return o, err -} - -func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { - - // Pre-compute the size TODO: Consider not doing this and just grow the list by appending - size := logNbInstances // first challenge - - for i := range c { - if c[i].NoProof() { // no proof, no challenge - continue - } - if c[i].NbClaims() > 1 { //fold the claims - size++ + eqLength := 1 << r.nbVars + eqs := make([]polynomial.MultiLin, nbWires) + var alpha fr.Element + alpha.SetOne() + flatW = 0 + for _, group := range level { + nbSources := len(group.ClaimSources) + + groupEq := polynomial.MultiLin(r.memPool.Make(eqLength)) + groupEq[0].Set(&alpha) + groupEq.Eq(r.levelPoints[group.ClaimSources[0]]) + + if nbSources > 1 { + newEq := polynomial.MultiLin(r.memPool.Make(eqLength)) + aI := alpha + for k := 1; k < nbSources; k++ { + aI.Mul(&aI, &foldingCoeff) + newEq[0].Set(&aI) + r.eqAcc(groupEq, newEq, r.levelPoints[group.ClaimSources[k]]) + } + r.memPool.Dump(newEq) } - size += logNbInstances // full run of sumcheck on logNbInstances variables - } - nums := make([]string, max(len(c), logNbInstances)) - for i := range nums { - nums[i] = strconv.Itoa(i) - } - - challenges := make([]string, size) - - // output wire claims - firstChallengePrefix := prefix + "fC." - for j := 0; j < logNbInstances; j++ { - challenges[j] = firstChallengePrefix + nums[j] - } - j := logNbInstances - for i := len(c) - 1; i >= 0; i-- { - if c[i].NoProof() { - continue + var stride fr.Element + stride.Set(&foldingCoeff) + for range nbSources - 1 { + stride.Mul(&stride, &foldingCoeff) } - wirePrefix := prefix + "w" + nums[i] + "." - if c[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "fold" - j++ - } + eqs[flatW] = groupEq + flatW++ + alpha.Mul(&alpha, &stride) - partialSumPrefix := wirePrefix + "pSP." - for k := 0; k < logNbInstances; k++ { - challenges[j] = partialSumPrefix + nums[k] - j++ + for w := 1; w < len(group.Wires); w++ { + eqs[flatW] = polynomial.MultiLin(r.memPool.Make(eqLength)) + r.workers.Submit(eqLength, func(start, end int) { + for i := start; i < end; i++ { + eqs[flatW][i].Mul(&eqs[flatW-1][i], &stride) + } + }, 512).Wait() + flatW++ + alpha.Mul(&alpha, &stride) } } - return challenges -} -func getFirstChallengeNames(logNbInstances int, prefix string) []string { - res := make([]string, logNbInstances) - firstChallengePrefix := prefix + "fC." - for i := 0; i < logNbInstances; i++ { - res[i] = firstChallengePrefix + strconv.Itoa(i) + claims := &zeroCheckClaims{ + level: level, + levelI: levelI, + resources: r, + input: input, + inputIndices: inputIndices, + eqs: eqs, + gateEvaluatorPools: pools, + } + return sumcheckProve(claims, &r.transcript) +} + +// verifyLevel runs the sumcheck verification for a SumcheckLevel: derives the fold challenge, +// computes the claimed sum, and calls sumcheckVerify. +func (r *resources) verifyLevel(levelI int, proof Proof) error { + level := r.schedule[levelI].(constraint.GkrSumcheckLevel) + initialChallengeI := len(r.schedule) + + nbClaims := gkrcore.NbClaims(level) + var foldingCoeff fr.Element + if nbClaims >= 2 { + foldingCoeff = r.transcript.getChallenge() + } + + var ys []fr.Element + for _, group := range level { + for _, wI := range group.Wires { + evalI := 0 + for _, src := range group.ClaimSources { + var y fr.Element + if src == initialChallengeI { + y = r.assignment[wI].Evaluate(r.levelPoints[src], &r.memPool) + } else { + y = proof[src].finalEvalProof[r.evalPositions[wI][evalI]] + evalI++ + } + ys = append(ys, y) + } + } } - return res -} -func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Element, error) { - res := make([]fr.Element, len(names)) - for i, name := range names { - if bytes, err := transcript.ComputeChallenge(name); err != nil { - return nil, err - } else if err = res[i].SetBytesCanonical(bytes); err != nil { - return nil, err - } + ysPoly := polynomial.Polynomial(ys) + claimedSum := ysPoly.Eval(&foldingCoeff) + + lazyClaims := &zeroCheckLazyClaims{ + foldingCoeff: foldingCoeff, + resources: r, + levelI: levelI, } - return res, nil + return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, gkrcore.Degree(level, r.circuit), &r.transcript) } // Prove consistency of the claimed assignment -func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { - o, err := setup(c, assignment, transcriptSettings, options...) +func Prove(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (Proof, error) { + r, err := newResources(c, schedule, assignment, hasher) if err != nil { return nil, err } - defer o.workers.Stop() + defer r.workers.Stop() - claims := newClaimsManager(c, assignment, o) + proof := make(Proof, len(schedule)) - proof := make(Proof, len(c)) - // firstChallenge called rho in the paper - var firstChallenge []fr.Element - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return nil, err + // Derive initial challenge point + firstChallenge := make([]fr.Element, r.nbVars) + for j := range r.nbVars { + firstChallenge[j] = r.transcript.getChallenge() } + r.levelPoints[len(schedule)] = firstChallenge - wirePrefix := o.transcriptPrefix + "w" - var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - - wire := c[i] - - if wire.IsOutput() { - claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) - } - - claim := claims.getClaim(i) - if wire.NoProof() { // input wires with one claim only - proof[i] = sumcheckProof{ - partialSumPolys: []polynomial.Polynomial{}, - finalEvalProof: []fr.Element{}, - } - } else { - if proof[i], err = sumcheckProve( - claim, fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err != nil { - return proof, err - } + for levelI := len(schedule) - 1; levelI >= 0; levelI-- { + switch s := schedule[levelI].(type) { + case constraint.GkrSkipLevel: + r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - baseChallenge = make([][]byte, len(proof[i].finalEvalProof)) - for j := range proof[i].finalEvalProof { - baseChallenge[j] = proof[i].finalEvalProof[j].Marshal() - } + case constraint.GkrSumcheckLevel: + proof[levelI] = r.proveLevel(levelI) + // Bind finalEvalProof for next level's challenge derivation + r.transcript.bind(proof[levelI].finalEvalProof...) } - // the verifier checks a single claim about input wires itself - claims.deleteClaim(i) } return proof, nil } -// Verify the consistency of the claimed output with the claimed input -// Unlike in Prove, the assignment argument need not be complete -func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { - o, err := setup(c, assignment, transcriptSettings, options...) +// Verify the consistency of the claimed output with the claimed input. +// Unlike in Prove, the assignment argument need not be complete. +func Verify(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, proof Proof, hasher hash.Hash) error { + r, err := newResources(c, schedule, assignment, hasher) if err != nil { return err } - defer o.workers.Stop() - - claims := newClaimsManager(c, assignment, o) + defer r.workers.Stop() - var firstChallenge []fr.Element - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return err + // Derive initial challenge point + firstChallenge := make([]fr.Element, r.nbVars) + for j := range r.nbVars { + firstChallenge[j] = r.transcript.getChallenge() } + r.levelPoints[len(schedule)] = firstChallenge - wirePrefix := o.transcriptPrefix + "w" - var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - wire := c[i] - - if wire.IsOutput() { - claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) - } - - proofW := proof[i] - claim := claims.getLazyClaim(i) - if wire.NoProof() { // input wires with one claim only - // make sure the proof is empty - if len(proofW.finalEvalProof) != 0 || len(proofW.partialSumPolys) != 0 { - return errors.New("no proof allowed for input wire with a single claim") - } + for levelI := len(schedule) - 1; levelI >= 0; levelI-- { + switch s := schedule[levelI].(type) { + case constraint.GkrSkipLevel: + r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - if wire.NbClaims() == 1 { // input wire - // simply evaluate and see if it matches - if len(claim.evaluationPoints) == 0 || len(claim.claimedEvaluations) == 0 { - return errors.New("missing input wire claim") - } - evaluation := assignment[i].Evaluate(claim.evaluationPoints[0], claims.memPool) - if !claim.claimedEvaluations[0].Equal(&evaluation) { - return errors.New("incorrect input wire claim") - } + case constraint.GkrSumcheckLevel: + if err = r.verifyLevel(levelI, proof); err != nil { + return fmt.Errorf("sumcheck proof rejected: %v", err) } - } else if err = sumcheckVerify( - claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { // incorporate prover claims about w's input into the transcript - baseChallenge = make([][]byte, len(proofW.finalEvalProof)) - for j := range baseChallenge { - baseChallenge[j] = proofW.finalEvalProof[j].Marshal() - } - } else { - return fmt.Errorf("sumcheck proof rejected: %v", err) //TODO: Any polynomials to dump? + // Bind finalEvalProof for next level's challenge derivation + r.transcript.bind(proof[levelI].finalEvalProof...) } - claims.deleteClaim(i) } return nil } diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index 5e2493ef53..90bc302571 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -19,10 +19,9 @@ import ( "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/mimc" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - gcUtils "github.com/consensys/gnark-crypto/utils" - "github.com/consensys/gnark/internal/gkr/gkrtesting" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" + "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/stretchr/testify/assert" ) @@ -69,36 +68,86 @@ func TestMimc(t *testing.T) { test(t, gkrtesting.MiMCCircuit(93)) } -func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - // Construct SerializableCircuit directly, bypassing CompileCircuit - // which would reset NbUniqueOutputs based on actual topology - circuit := gkrcore.SerializableCircuit{ - { - NbUniqueOutputs: 2, - Gate: gkrcore.SerializableGate{Degree: 1}, - }, - } +// testLevel exercises proveLevel/verifyLevel for a single sumcheck level. +func testLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrSumcheckLevel) { + t.Helper() + _, sCircuit := cache.Compile(t, circuit) - assignment := WireAssignment{[]fr.Element{two, three}} - var o settings - pool := polynomial.NewPool(256, 1<<11) - workers := gcUtils.NewWorkerPool() - o.pool = &pool - o.workers = workers - - claimsManagerGen := func() *claimsManager { - manager := newClaimsManager(circuit, assignment, o) - manager.add(0, []fr.Element{three}, five) - manager.add(0, []fr.Element{four}, six) - return &manager + ins := sCircuit.Inputs() + assignment := make(WireAssignment, len(sCircuit)) + for _, i := range ins { + assignment[i] = make([]fr.Element, 2) + fr.Vector(assignment[i]).MustSetRandom() } - transcriptGen := newMessageCounterGenerator(4, 1) + assignment.Complete(sCircuit) + + schedule := constraint.GkrProvingSchedule{level} + challenge := []fr.Element{five} - proof, err := sumcheckProve(claimsManagerGen().getClaim(0), fiatshamir.WithHash(transcriptGen(), nil)) + // Prove + proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) - err = sumcheckVerify(claimsManagerGen().getLazyClaim(0), proof, fiatshamir.WithHash(transcriptGen(), nil)) + defer proveR.workers.Stop() + + proveR.levelPoints[1] = challenge + proof := Proof{proveR.proveLevel(0)} + + // Verify + verifyR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) + defer verifyR.workers.Stop() + + verifyR.levelPoints[1] = challenge + assert.NoError(t, verifyR.verifyLevel(0, proof)) +} + +func TestLevel(t *testing.T) { + // Wires 0,1 = inputs; wires 2,3,4 = mul(0,1). All gates are independent outputs. + circuit := gkrcore.RawCircuit{ + {}, + {}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, + {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, + } + // All levels have initial challenge at index 1 (len(schedule) = 1). + tests := []struct { + name string + level constraint.GkrSumcheckLevel + }{ + { + name: "single wire", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []int{1}}, + }, + }, + { + name: "two groups", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []int{1}}, + {Wires: []int{3}, ClaimSources: []int{1}}, + }, + }, + { + name: "one group with two wires", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4, 3}, ClaimSources: []int{1}}, + }, + }, + { + name: "mixed: single + multi-wire group", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []int{1}}, + {Wires: []int{3, 2}, ClaimSources: []int{1}}, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testLevel(t, circuit, tc.level) + }) + } } var one, two, three, four, five, six fr.Element @@ -133,7 +182,16 @@ func getLogMaxInstances(t *testing.T) int { } func test(t *testing.T, circuit gkrcore.RawCircuit) { + testWithSchedule(t, circuit, nil) +} + +func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constraint.GkrProvingSchedule) { gCircuit, sCircuit := cache.Compile(t, circuit) + if schedule == nil { + var err error + schedule, err = gkrcore.DefaultProvingSchedule(sCircuit) + assert.NoError(t, err) + } ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) maxSize := 1 << getLogMaxInstances(t) @@ -153,22 +211,21 @@ func test(t *testing.T, circuit gkrcore.RawCircuit) { t.Log("Selected inputs for test") - proof, err := Prove(sCircuit, fullAssignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(sCircuit, schedule, fullAssignment, newMessageCounter(1, 1)) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(1, 1)) assert.NoError(t, err, "proof rejected") if proof.isEmpty() { // special case for TestNoGate: continue // there's no way to make a trivial proof fail } - err = Verify(sCircuit, fullAssignment, proof, fiatshamir.WithHash(newMessageCounter(0, 1))) + err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(0, 1)) assert.NotNil(t, err, "bad proof accepted") } - } func (p Proof) isEmpty() bool { @@ -188,14 +245,17 @@ func (p Proof) isEmpty() bool { func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { _, c := cache.Compile(t, gkrtesting.NoGateCircuit()) + schedule, err := gkrcore.DefaultProvingSchedule(c) + assert.NoError(t, err) + assignment := WireAssignment{0: inputAssignments[0]} - proof, err := Prove(c, assignment, fiatshamir.WithHash(newMessageCounter(1, 1))) + proof, err := Prove(c, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) // Even though a hash is called here, the proof is empty - err = Verify(c, assignment, proof, fiatshamir.WithHash(newMessageCounter(1, 1))) + err = Verify(c, schedule, assignment, proof, newMessageCounter(1, 1)) assert.NoError(t, err, "proof rejected") } @@ -203,7 +263,7 @@ func generateTestProver(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) assert.NoError(t, err) - proof, err := Prove(testCase.Circuit, testCase.FullAssignment, fiatshamir.WithHash(testCase.Hash)) + proof, err := Prove(testCase.Circuit, testCase.Schedule, testCase.FullAssignment, testCase.Hash) assert.NoError(t, err) assert.NoError(t, proofEquals(testCase.Proof, proof)) } @@ -213,17 +273,16 @@ func generateTestVerifier(path string) func(t *testing.T) { return func(t *testing.T) { testCase, err := newTestCase(path) assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(testCase.Hash)) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) assert.NoError(t, err, "proof rejected") testCase, err = newTestCase(path) assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(newMessageCounter(2, 0))) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, newMessageCounter(2, 0)) assert.NotNil(t, err, "bad proof accepted") } } func TestGkrVectors(t *testing.T) { - const testDirPath = "../test_vectors/" dirEntries, err := os.ReadDir(testDirPath) assert.NoError(t, err) @@ -269,6 +328,9 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { fmt.Println("creating circuit structure") _, c := cache.Compile(b, gkrtesting.MiMCCircuit(mimcDepth)) + schedule, err := gkrcore.DefaultProvingSchedule(c) + assert.NoError(b, err) + in0 := make([]fr.Element, nbInstances) in1 := make([]fr.Element, nbInstances) fr.Vector(in0).MustSetRandom() @@ -283,12 +345,30 @@ func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { //b.ResetTimer() fmt.Println("constructing proof") start = time.Now().UnixMicro() - _, err := Prove(c, assignment, fiatshamir.WithHash(mimc.NewMiMC())) + _, err = Prove(c, schedule, assignment, mimc.NewMiMC()) proved := time.Now().UnixMicro() - start fmt.Println("proved in", proved, "μs") assert.NoError(b, err) } +// TestSingleMulGateExplicitSchedule tests a single mul gate with an explicit single-step schedule, +// equivalent to the default but constructed manually to exercise the schedule path. +func TestSingleMulGateExplicitSchedule(t *testing.T) { + circuit := gkrtesting.SingleMulGateCircuit() + _, sCircuit := cache.Compile(t, circuit) + + // Wire 2 is the mul gate output (inputs: 0, 1). + // Explicit schedule: one SumcheckLevel for wire 2 only. + // ClaimSources: [1] = initial challenge (index len(schedule) = 1). + schedule := constraint.GkrProvingSchedule{ + constraint.GkrSumcheckLevel{ + {Wires: []int{2}, ClaimSources: []int{1}}, + }, + } + testWithSchedule(t, circuit, schedule) + _ = sCircuit +} + func BenchmarkGkrMimc19(b *testing.B) { benchmarkGkrMiMC(b, 1<<19, 91) } @@ -332,6 +412,7 @@ type TestCase struct { Proof Proof FullAssignment WireAssignment InOutAssignment WireAssignment + Schedule constraint.GkrProvingSchedule } var testCases = make(map[string]*TestCase) @@ -362,6 +443,20 @@ func newTestCase(path string) (*TestCase, error) { if proof, err = unmarshalProof(info.Proof); err != nil { return nil, err } + var schedule constraint.GkrProvingSchedule + if schedule, err = info.Schedule.ToProvingSchedule(); err != nil { + return nil, err + } + if schedule == nil { + if schedule, err = gkrcore.DefaultProvingSchedule(circuit); err != nil { + return nil, err + } + } + + outputSet := make(map[int]bool, len(circuit)) + for _, o := range circuit.Outputs() { + outputSet[o] = true + } fullAssignment := make(WireAssignment, len(circuit)) inOutAssignment := make(WireAssignment, len(circuit)) @@ -375,7 +470,7 @@ func newTestCase(path string) (*TestCase, error) { } assignmentRaw = info.Input[inI] inI++ - } else if circuit[i].IsOutput() { + } else if outputSet[i] { if outI == len(info.Output) { return nil, fmt.Errorf("fewer output in vector than in circuit") } @@ -396,7 +491,7 @@ func newTestCase(path string) (*TestCase, error) { fullAssignment.Complete(circuit) for i := range circuit { - if circuit[i].IsOutput() { + if outputSet[i] { if err = sliceEquals(inOutAssignment[i], fullAssignment[i]); err != nil { return nil, fmt.Errorf("assignment mismatch: %v", err) } @@ -409,6 +504,7 @@ func newTestCase(path string) (*TestCase, error) { Proof: proof, Hash: _hash, Circuit: circuit, + Schedule: schedule, } testCases[path] = tCase diff --git a/internal/gkr/bw6-761/sumcheck.go b/internal/gkr/bw6-761/sumcheck.go index f083550198..d6c227ad68 100644 --- a/internal/gkr/bw6-761/sumcheck.go +++ b/internal/gkr/bw6-761/sumcheck.go @@ -7,33 +7,62 @@ package gkr import ( "errors" - "strconv" + "hash" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" ) -// This does not make use of parallelism and represents polynomials as lists of coefficients -// It is currently geared towards arithmetic hashes. Once we have a more unified hash function interface, this can be generified. +// This does not make use of parallelism and represents polynomials as lists of coefficients. + +// transcript is a Fiat-Shamir transcript backed by a running hash. +// Field elements are written via bind; challenges are derived via getChallenge. +// The hash is never reset — all previous data is implicitly part of future challenges. +type transcript struct { + h hash.Hash + bound bool // whether bind was called since the last getChallenge +} + +// bind writes field elements to the transcript as bindings for the next challenge. +func (t *transcript) bind(elements ...fr.Element) { + if len(elements) == 0 { + return + } + for i := range elements { + bytes := elements[i].Bytes() + t.h.Write(bytes[:]) + } + t.bound = true +} + +// getChallenge binds optional elements, then squeezes a challenge from the current hash state. +// If no bindings were added since the last squeeze, a separator byte is written first +// to advance the state and prevent repeated values. +func (t *transcript) getChallenge(bindings ...fr.Element) fr.Element { + t.bind(bindings...) + if !t.bound { + t.h.Write([]byte{0}) + } + t.bound = false + var res fr.Element + res.SetBytes(t.h.Sum(nil)) + return res +} // sumcheckClaims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. // Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) type sumcheckClaims interface { - fold(a fr.Element) polynomial.Polynomial // fold into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. - next(fr.Element) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ + roundPolynomial() polynomial.Polynomial // compute gⱼ polynomial for current round + roundFold(r fr.Element) // fold inputs and eq at challenge r varsNum() int // number of variables - claimsNum() int // number of claims proveFinalEval(r []fr.Element) []fr.Element // in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof } // sumcheckLazyClaims is the sumcheckClaims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. type sumcheckLazyClaims interface { - claimsNum() int // claimsNum = m - varsNum() int // varsNum = n - foldedSum(a fr.Element) fr.Element // foldedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - degree(i int) int // degree of the total claim in the i'th variable - verifyFinalEval(r []fr.Element, foldingCoeff fr.Element, purportedValue fr.Element, proof []fr.Element) error + varsNum() int // varsNum = n + degree(i int) int // degree of the total claim in the i'th variable + verifyFinalEval(r []fr.Element, purportedValue fr.Element, proof []fr.Element) error } // sumcheckProof of a multi-statement. @@ -42,130 +71,46 @@ type sumcheckProof struct { finalEvalProof []fr.Element //in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof } -func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) (challengeNames []string, err error) { - numChallenges := varsNum - if claimsNum >= 2 { - numChallenges++ - } - challengeNames = make([]string, numChallenges) - if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "fold" - } - prefix := settings.Prefix + "pSP." - for i := 0; i < varsNum; i++ { - challengeNames[i+numChallenges-varsNum] = prefix + strconv.Itoa(i) - } - if settings.Transcript == nil { - transcript := fiatshamir.NewTranscript(settings.Hash, challengeNames...) - settings.Transcript = transcript - } - - for i := range settings.BaseChallenges { - if err = settings.Transcript.Bind(challengeNames[0], settings.BaseChallenges[i]); err != nil { - return - } - } - return -} - -func next(transcript *fiatshamir.Transcript, bindings []fr.Element, remainingChallengeNames *[]string) (fr.Element, error) { - challengeName := (*remainingChallengeNames)[0] - for i := range bindings { - bytes := bindings[i].Bytes() - if err := transcript.Bind(challengeName, bytes[:]); err != nil { - return fr.Element{}, err - } - } - var res fr.Element - bytes, err := transcript.ComputeChallenge(challengeName) - res.SetBytes(bytes) - - *remainingChallengeNames = (*remainingChallengeNames)[1:] - - return res, err -} - -// sumcheckProve create a non-interactive proof -func sumcheckProve(claims sumcheckClaims, transcriptSettings fiatshamir.Settings) (sumcheckProof, error) { - - var proof sumcheckProof - remainingChallengeNames, err := setupTranscript(claims.claimsNum(), claims.varsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return proof, err - } - - var foldingCoeff fr.Element - if claims.claimsNum() >= 2 { - if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { - return proof, err - } - } - +// sumcheckProve creates a non-interactive sumcheck proof. +// The fold challenge is derived by the caller (proveLevel). +// Pattern: roundPolynomial, [roundFold, roundPolynomial]*, proveFinalEval. +func sumcheckProve(claims sumcheckClaims, t *transcript) sumcheckProof { varsNum := claims.varsNum() - proof.partialSumPolys = make([]polynomial.Polynomial, varsNum) - proof.partialSumPolys[0] = claims.fold(foldingCoeff) + proof := sumcheckProof{partialSumPolys: make([]polynomial.Polynomial, varsNum)} + proof.partialSumPolys[0] = claims.roundPolynomial() challenges := make([]fr.Element, varsNum) - for j := 0; j+1 < varsNum; j++ { - if challenges[j], err = next(transcript, proof.partialSumPolys[j], &remainingChallengeNames); err != nil { - return proof, err - } - proof.partialSumPolys[j+1] = claims.next(challenges[j]) - } - - if challenges[varsNum-1], err = next(transcript, proof.partialSumPolys[varsNum-1], &remainingChallengeNames); err != nil { - return proof, err + for j := range varsNum - 1 { + challenges[j] = t.getChallenge(proof.partialSumPolys[j]...) + claims.roundFold(challenges[j]) + proof.partialSumPolys[j+1] = claims.roundPolynomial() } + challenges[varsNum-1] = t.getChallenge(proof.partialSumPolys[varsNum-1]...) proof.finalEvalProof = claims.proveFinalEval(challenges) - - return proof, nil + return proof } -func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSettings fiatshamir.Settings) error { - remainingChallengeNames, err := setupTranscript(claims.claimsNum(), claims.varsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return err - } - - var foldingCoeff fr.Element - - if claims.claimsNum() >= 2 { - if foldingCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { - return err - } - } - +// sumcheckVerify verifies a non-interactive sumcheck proof. +// The fold challenge is derived by the caller (verifyLevel). +// claimedSum is the expected sum; degree is the polynomial's degree in each variable. +func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, claimedSum fr.Element, degree int, t *transcript) error { r := make([]fr.Element, claims.varsNum()) - // Just so that there is enough room for gJ to be reused - maxDegree := claims.degree(0) - for j := 1; j < claims.varsNum(); j++ { - if d := claims.degree(j); d > maxDegree { - maxDegree = d - } - } - gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() - gJR := claims.foldedSum(foldingCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) + gJ := make(polynomial.Polynomial, degree+1) + gJR := claimedSum for j := range claims.varsNum() { - if len(proof.partialSumPolys[j]) != claims.degree(j) { + if len(proof.partialSumPolys[j]) != degree { return errors.New("malformed proof") } copy(gJ[1:], proof.partialSumPolys[j]) - gJ[0].Sub(&gJR, &proof.partialSumPolys[j][0]) // Requirement that gⱼ(0) + gⱼ(1) = gⱼ₋₁(r) - // gJ is ready + gJ[0].Sub(&gJR, &proof.partialSumPolys[j][0]) - //Prepare for the next iteration - if r[j], err = next(transcript, proof.partialSumPolys[j], &remainingChallengeNames); err != nil { - return err - } - // This is an extremely inefficient way of interpolating. TODO: Interpolate without symbolically computing a polynomial - gJCoeffs := polynomial.InterpolateOnRange(gJ[:(claims.degree(j) + 1)]) + r[j] = t.getChallenge(proof.partialSumPolys[j]...) + gJCoeffs := polynomial.InterpolateOnRange(gJ[:(degree + 1)]) gJR = gJCoeffs.Eval(&r[j]) } - return claims.verifyFinalEval(r, foldingCoeff, gJR, proof.finalEvalProof) + return claims.verifyFinalEval(r, gJR, proof.finalEvalProof) } diff --git a/internal/gkr/bw6-761/sumcheck_test.go b/internal/gkr/bw6-761/sumcheck_test.go index 375652e4ec..d5fd5d305a 100644 --- a/internal/gkr/bw6-761/sumcheck_test.go +++ b/internal/gkr/bw6-761/sumcheck_test.go @@ -10,7 +10,6 @@ import ( "hash" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/stretchr/testify/assert" "math/bits" @@ -28,11 +27,9 @@ func testSumcheckSingleClaimMultilin(polyInt []uint64, hashGenerator func() hash } claim := singleMultilinClaim{g: poly.Clone()} + t := transcript{h: hashGenerator()} - proof, err := sumcheckProve(&claim, fiatshamir.WithHash(hashGenerator())) - if err != nil { - return err - } + proof := sumcheckProve(&claim, &t) var sb strings.Builder for _, p := range proof.partialSumPolys { @@ -48,13 +45,15 @@ func testSumcheckSingleClaimMultilin(polyInt []uint64, hashGenerator func() hash } lazyClaim := singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if err = sumcheckVerify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())); err != nil { + t = transcript{h: hashGenerator()} + if err := sumcheckVerify(lazyClaim, proof, lazyClaim.claimedSum, 1, &t); err != nil { return err } proof.partialSumPolys[0][0].Add(&proof.partialSumPolys[0][0], toElement(1)) lazyClaim = singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if sumcheckVerify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())) == nil { + t = transcript{h: hashGenerator()} + if sumcheckVerify(lazyClaim, proof, lazyClaim.claimedSum, 1, &t) == nil { return fmt.Errorf("bad proof accepted") } return nil @@ -93,18 +92,14 @@ type singleMultilinClaim struct { g polynomial.MultiLin } -func (c singleMultilinClaim) proveFinalEval(r []fr.Element) []fr.Element { +func (c *singleMultilinClaim) proveFinalEval(r []fr.Element) []fr.Element { return nil // verifier can compute the final eval itself } -func (c singleMultilinClaim) varsNum() int { +func (c *singleMultilinClaim) varsNum() int { return bits.TrailingZeros(uint(len(c.g))) } -func (c singleMultilinClaim) claimsNum() int { - return 1 -} - func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { sum := g[len(g)/2] for i := len(g)/2 + 1; i < len(g); i++ { @@ -113,13 +108,12 @@ func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { return []fr.Element{sum} } -func (c singleMultilinClaim) fold(fr.Element) polynomial.Polynomial { +func (c *singleMultilinClaim) roundPolynomial() polynomial.Polynomial { return sumForX1One(c.g) } -func (c *singleMultilinClaim) next(r fr.Element) polynomial.Polynomial { +func (c *singleMultilinClaim) roundFold(r fr.Element) { c.g.Fold(r) - return sumForX1One(c.g) } type singleMultilinLazyClaim struct { @@ -127,7 +121,7 @@ type singleMultilinLazyClaim struct { claimedSum fr.Element } -func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, _ fr.Element, purportedValue fr.Element, proof []fr.Element) error { +func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, purportedValue fr.Element, proof []fr.Element) error { val := c.g.Evaluate(r, nil) if val.Equal(&purportedValue) { return nil @@ -135,15 +129,7 @@ func (c singleMultilinLazyClaim) verifyFinalEval(r []fr.Element, _ fr.Element, p return fmt.Errorf("mismatch") } -func (c singleMultilinLazyClaim) foldedSum(_ fr.Element) fr.Element { - return c.claimedSum -} - -func (c singleMultilinLazyClaim) degree(i int) int { - return 1 -} - -func (c singleMultilinLazyClaim) claimsNum() int { +func (c singleMultilinLazyClaim) degree(int) int { return 1 } diff --git a/internal/gkr/gkrcore/schedule.go b/internal/gkr/gkrcore/schedule.go index d3838db6b7..d9d413692f 100644 --- a/internal/gkr/gkrcore/schedule.go +++ b/internal/gkr/gkrcore/schedule.go @@ -336,4 +336,3 @@ func DefaultProvingSchedule(c SerializableCircuit) (constraint.GkrProvingSchedul } return b.finalize() } - diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 6f6ea7c4be..47702ba2f6 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -166,10 +166,10 @@ type TestCaseInfo struct { // ScheduleStepInfo is the JSON representation of a ProvingLevel with a type discriminator. type ScheduleStepInfo struct { - Type string `json:"type"` + Type string `json:"type"` Groups []constraint.GkrClaimGroup `json:"groups,omitempty"` // for SumcheckLevel - Wires []int `json:"wires,omitempty"` // for SkipLevel - Claims []int `json:"claimSources,omitempty"` // for SkipLevel + Wires []int `json:"wires,omitempty"` // for SkipLevel + Claims []int `json:"claimSources,omitempty"` // for SkipLevel } // ScheduleInfo is the JSON-serializable form of a ProvingSchedule. diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index a31c386fd5..828fb0122a 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -8,12 +8,12 @@ package gkr import ( "errors" "fmt" + "hash" "iter" - "strconv" "sync" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/utils" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" @@ -33,630 +33,487 @@ type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) // zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). -// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear -// extension of the values of w across all instances. -// Its purpose is to batch the checking of multiple evaluations of the same wire. +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, +// where the sum runs over all wᵢ and evaluation point xᵢ in the level. +// Its purpose is to batch the checking of multiple wire evaluations at evaluation points. type zeroCheckLazyClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]small_rational.SmallRational // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []small_rational.SmallRational // yᵢ = w(xᵢ), allegedly - manager *claimsManager // WARNING: Circular references -} - -func (e *zeroCheckLazyClaims) getWire() Wire { - return e.manager.circuit[e.wireI] -} - -func (e *zeroCheckLazyClaims) claimsNum() int { - return len(e.evaluationPoints) + foldingCoeff small_rational.SmallRational // the coefficient used to fold claims, conventionally 0 if there is only one claim + resources *resources + levelI int } func (e *zeroCheckLazyClaims) varsNum() int { - return len(e.evaluationPoints[0]) -} - -// foldedSum returns ∑ᵢ aⁱ yᵢ -func (e *zeroCheckLazyClaims) foldedSum(a small_rational.SmallRational) small_rational.SmallRational { - evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) - return evalsAsPoly.Eval(&a) + return e.resources.nbVars } func (e *zeroCheckLazyClaims) degree(int) int { - return e.manager.circuit[e.wireI].ZeroCheckDegree() -} - -// verifyFinalEval finalizes the verification of w. -// The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. (c is foldingCoeff) -// Both purportedValue and the vector r have been randomized during the sumcheck protocol. -// By taking the w term out of the sum we get the equivalent claim that -// for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. -// If w is an input wire, the verifier can directly check its evaluation at r. -// Otherwise, the prover makes claims about the evaluation of w's input wires, -// wᵢ, at r, to be verified later. -// The claims are communicated through the proof parameter. -// The verifier checks here if the claimed evaluations of wᵢ(r) are consistent with -// the main claim, by checking E w(wᵢ(r)...) = purportedValue. -func (e *zeroCheckLazyClaims) verifyFinalEval(r []small_rational.SmallRational, foldingCoeff, purportedValue small_rational.SmallRational, uniqueInputEvaluations []small_rational.SmallRational) error { - // the eq terms ( E ) - numClaims := len(e.evaluationPoints) - evaluation := polynomial.EvalEq(e.evaluationPoints[numClaims-1], r) - for i := numClaims - 2; i >= 0; i-- { - evaluation.Mul(&evaluation, &foldingCoeff) - eq := polynomial.EvalEq(e.evaluationPoints[i], r) - evaluation.Add(&evaluation, &eq) - } - - wire := e.manager.circuit[e.wireI] - - // the w(...) term - var gateEvaluation small_rational.SmallRational - if wire.IsInput() { // just compute w(r) - gateEvaluation = e.manager.assignment[e.wireI].Evaluate(r, e.manager.memPool) - } else { // proof contains the evaluations of the inputs, but avoids repetition in case multiple inputs come from the same wire - injection, injectionLeftInv := - e.manager.circuit.ClaimPropagationInfo(e.wireI) - - if len(injection) != len(uniqueInputEvaluations) { - return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) - } - - for uniqueI, i := range injection { // map from unique to all - e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) - } + return gkrcore.Degree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel), e.resources.circuit) +} + +// verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. +// The sumcheck protocol has already reduced the per-wire claims w(xᵢ) = yᵢ to verifying +// ∑ᵢ cⁱ eq(xᵢ, r) · wᵢ(r) = purportedValue, where the sum runs over all +// claims on each wire and c is foldingCoeff. +// Both purportedValue and the vector r have been randomized during sumcheck. +// +// For input wires, w(r) is computed directly from the assignment. +// For non-input wires, the prover claims evaluations of the input wires at r, +// communicated through uniqueInputEvaluations; those claims are verified later. +// The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming +// that the full sum matches purportedValue. +func (e *zeroCheckLazyClaims) verifyFinalEval(r []small_rational.SmallRational, purportedValue small_rational.SmallRational, uniqueInputEvaluations []small_rational.SmallRational) error { + e.resources.levelPoints[e.levelI] = r + level := e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel) + perWireInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) + + var terms []small_rational.SmallRational + flatW := 0 + for _, group := range level { + for _, wI := range group.Wires { + wire := e.resources.circuit[wI] + + var gateEval small_rational.SmallRational + if wire.IsInput() { + gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) + } else { + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) + for _, v := range perWireInputEvals[flatW] { + evaluator.pushInput(v) + } + gateEval.Set(evaluator.evaluate()) + } - evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) - for _, uniqueI := range injectionLeftInv { // map from all to unique - evaluator.pushInput(uniqueInputEvaluations[uniqueI]) + for _, src := range group.ClaimSources { + eq := polynomial.EvalEq(e.resources.levelPoints[src], r) + var term small_rational.SmallRational + term.Mul(&eq, &gateEval) + terms = append(terms, term) + } + flatW++ } - - gateEvaluation.Set(evaluator.evaluate()) } - evaluation.Mul(&evaluation, &gateEvaluation) - - if evaluation.Equal(&purportedValue) { + ys := polynomial.Polynomial(terms) + if total := ys.Eval(&e.foldingCoeff); total.Equal(&purportedValue) { return nil } return errors.New("incompatible evaluations") } // zeroCheckClaims is a claim for sumcheck (prover side). -// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear -// extension of the values of w across all instances. -// Its purpose is to batch the proving of multiple evaluations of the same wire. +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, +// where the sum runs over all (wire v, claim source s) pairs in the level. +// Each wire has its own eq table with the batching coefficients baked in. type zeroCheckClaims struct { - wireI int // the wire for which we are making the claim, with value w - evaluationPoints [][]small_rational.SmallRational // xᵢ: the points at which the prover has made claims about the evaluation of w - claimedEvaluations []small_rational.SmallRational // yᵢ = w(xᵢ) - manager *claimsManager - - input []polynomial.MultiLin // input[i](h₁, ..., hₘ₋ⱼ) = wᵢ(r₁, r₂, ..., rⱼ₋₁, h₁, ..., hₘ₋ⱼ) - - eq polynomial.MultiLin // E := ∑ᵢ cⁱ eq(xᵢ, -) - - gateEvaluatorPool *gateEvaluatorPool -} - -func (c *zeroCheckClaims) getWire() Wire { - return c.manager.circuit[c.wireI] + level constraint.GkrSumcheckLevel + levelI int + resources *resources + input []polynomial.MultiLin // UniqueGateInputs order + inputIndices [][]int // [wireInLevel][gateInputJ] → index in input + eqs []polynomial.MultiLin // per-wire interpolation bases for evaluating wire assignments at challenge points + gateEvaluatorPools []*gateEvaluatorPool } -// fold the multiple claims into one claim using a random combination (foldingCoeff or c). -// From the original multiple claims of w(xᵢ) = yᵢ, we get a single claim -// ∑ᵢ,ₕ cⁱ eq(xᵢ, h) w(h) = ∑ᵢ cⁱ yᵢ, where h iterates over the hypercube (circuit instances) and -// i iterates over the claims. -// Equivalently, we could say ∑ᵢ cⁱ yᵢ = ∑ₕ,ᵢ cⁱ eq(xᵢ, h) w(h) = ∑ₕ w(h) ∑ᵢ cⁱ eq(xᵢ, h). -// Thus if we initially compute E := ∑ᵢ cⁱ eq(xᵢ, -), our claim will find the simpler form -// ∑ᵢ cⁱ yᵢ = ∑ₕ w(h) E(h), where the sum-checked polynomial is of degree deg(g) + 1, -// and deg(g) is the total degree of the polynomial defining the gate g of which w is the output. -// The output of fold is the first sumcheck claim, i.e. ∑₍ₕ₁,ₕ₂,...₎ w(X, h₁, h₂, ...) E(X, h₁, h₂, ...).. -func (c *zeroCheckClaims) fold(foldingCoeff small_rational.SmallRational) polynomial.Polynomial { - varsNum := c.varsNum() - eqLength := 1 << varsNum - claimsNum := c.claimsNum() - // initialize the eq tables ( E ) - c.eq = c.manager.memPool.Make(eqLength) - - c.eq[0].SetOne() - c.eq.Eq(c.evaluationPoints[0]) - - // E := eq(x₀, -) - newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) - aI := foldingCoeff - - // E += cⁱ eq(xᵢ, -) - for k := 1; k < claimsNum; k++ { - newEq[0].Set(&aI) - - c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) - - if k+1 < claimsNum { - aI.Mul(&aI, &foldingCoeff) - } - } - - c.manager.memPool.Dump(newEq) - - return c.computeGJ() -} - -// eqAcc sets m to an eq table at q and then adds it to e. -// m <- eq(q, -). -// e <- e + m -func (c *zeroCheckClaims) eqAcc(e, m polynomial.MultiLin, q []small_rational.SmallRational) { - n := len(q) - - //At the end of each iteration, m(h₁, ..., hₙ) = eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) - for i := range q { // In the comments we use a 1-based index so q[i] = qᵢ₊₁ - // go through all assignments of (b₁, ..., bᵢ) ∈ {0,1}ⁱ - const threshold = 1 << 6 - k := 1 << i - if k < threshold { - for j := 0; j < k; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - } else { - c.manager.workers.Submit(k, func(start, end int) { - for j := start; j < end; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 1) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) eq(qᵢ₊₁, 0) = eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - }, 1024).Wait() - } - - } - c.manager.workers.Submit(len(e), func(start, end int) { - for i := start; i < end; i++ { - e[i].Add(&e[i], &m[i]) - } - }, 512).Wait() +func (c *zeroCheckClaims) varsNum() int { + return len(c.resources.levelPoints[c.level[0].ClaimSources[0]]) } -// computeGJ: gⱼ = ∑_{0≤h<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, h...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., Xⱼ, h...) g( w₀(r₁, ..., Xⱼ, h...), ... ). -// the polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). -// The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. -func (c *zeroCheckClaims) computeGJ() polynomial.Polynomial { - - wire := c.getWire() - degGJ := wire.ZeroCheckDegree() // guaranteed to be no smaller than the actual deg(gⱼ) - nbGateIn := len(c.input) - - // Both E and wᵢ (the input wires and the eq table) are multilinear, thus - // they are linear in Xⱼ. - // So for f ∈ { E(r₁, ..., Xⱼ, h...) } ∪ {wᵢ(r₁, ..., Xⱼ, h...) }, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables. - // ml are such multilinear polynomials the evaluations of which over different values of Xⱼ are computed in this stepwise manner. - ml := make([]polynomial.MultiLin, nbGateIn+1) // shortcut to the evaluations of the multilinear polynomials over the hypercube - ml[0] = c.eq - copy(ml[1:], c.input) +// roundPolynomial computes gⱼ = ∑ₕ ∑ᵥ eqs[v](Xⱼ, h...) · gateᵥ(inputs(Xⱼ, h...)). +// The polynomial is represented by the evaluations gⱼ(1), gⱼ(2), ..., gⱼ(deg(gⱼ)). +// The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). +// By convention, g₀ is a constant polynomial equal to the claimed sum. +func (c *zeroCheckClaims) roundPolynomial() polynomial.Polynomial { + degree := gkrcore.Degree(c.level, c.resources.circuit) + nbUniqueInputs := len(c.input) + nbWires := len(c.eqs) - sumSize := len(c.eq) / 2 // the range of h, over which we sum + // Both eqs and input are multilinear, thus linear in Xⱼ. + // For any such f, f(m) = m·(f(1) - f(0)) + f(0), and f(0), f(1) are read directly + // from the bookkeeping tables. This allows stepwise evaluation at Xⱼ = 1, 2, ..., degree. + // Layout: [eq₀, eq₁, ..., eq_{nbWires-1}, input₀, input₁, ..., input_{nbUniqueInputs-1}] + ml := make([]polynomial.MultiLin, nbWires+nbUniqueInputs) + copy(ml, c.eqs) + copy(ml[nbWires:], c.input) - // Perf-TODO: Collate once at claim "folding" time and not again. then, even folding can be done in one operation every time "next" is called + sumSize := len(c.eqs[0]) / 2 - gJ := make([]small_rational.SmallRational, degGJ) + p := make([]small_rational.SmallRational, degree) var mu sync.Mutex - computeAll := func(start, end int) { // compute method to allow parallelization across instances + computeAll := func(start, end int) { var step small_rational.SmallRational - evaluator := c.gateEvaluatorPool.get() - defer c.gateEvaluatorPool.put(evaluator) + evaluators := make([]*gateEvaluator, nbWires) + for w := range nbWires { + evaluators[w] = c.gateEvaluatorPools[w].get() + } + defer func() { + for w := range nbWires { + c.gateEvaluatorPools[w].put(evaluators[w]) + } + }() - res := make([]small_rational.SmallRational, degGJ) + res := make([]small_rational.SmallRational, degree) // evaluations of ml, laid out as: // ml[0](1, h...), ml[1](1, h...), ..., ml[len(ml)-1](1, h...), // ml[0](2, h...), ml[1](2, h...), ..., ml[len(ml)-1](2, h...), // ... - // ml[0](degGJ, h...), ml[2](degGJ, h...), ..., ml[len(ml)-1](degGJ, h...) - mlEvals := make([]small_rational.SmallRational, degGJ*len(ml)) - - for h := start; h < end; h++ { // h counts across instances + // ml[0](degree, h...), ml[1](degree, h...), ..., ml[len(ml)-1](degree, h...) + mlEvals := make([]small_rational.SmallRational, degree*len(ml)) + for h := start; h < end; h++ { evalAt1Index := sumSize + h for k := range ml { - // d = 0 - mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1. Can be taken directly from the table. + mlEvals[k].Set(&ml[k][evalAt1Index]) // evaluation at Xⱼ = 1, taken directly from the table step.Sub(&mlEvals[k], &ml[k][h]) // step = ml[k](1) - ml[k](0) - for d := 1; d < degGJ; d++ { + for d := 1; d < degree; d++ { mlEvals[d*len(ml)+k].Add(&mlEvals[(d-1)*len(ml)+k], &step) } } - eIndex := 0 // index for where the current eq term is + eIndex := 0 // start of the current row's eq evaluations nextEIndex := len(ml) - for d := range degGJ { - // Push gate inputs - for i := range nbGateIn { - evaluator.pushInput(mlEvals[eIndex+1+i]) + for d := range degree { + for w := range nbWires { + for _, inputI := range c.inputIndices[w] { + evaluators[w].pushInput(mlEvals[eIndex+nbWires+inputI]) + } + summand := evaluators[w].evaluate() + summand.Mul(summand, &mlEvals[eIndex+w]) + res[d].Add(&res[d], summand) // collect contributions into the sum from start to end } - summand := evaluator.evaluate() - summand.Mul(summand, &mlEvals[eIndex]) - res[d].Add(&res[d], summand) // collect contributions into the sum from start to end eIndex, nextEIndex = nextEIndex, nextEIndex+len(ml) } } mu.Lock() - for i := range gJ { - gJ[i].Add(&gJ[i], &res[i]) // collect into the complete sum + for i := range p { + p[i].Add(&p[i], &res[i]) // collect into the complete sum } mu.Unlock() } const minBlockSize = 64 - if sumSize < minBlockSize { - // no parallelization computeAll(0, sumSize) } else { - c.manager.workers.Submit(sumSize, computeAll, minBlockSize).Wait() + c.resources.workers.Submit(sumSize, computeAll, minBlockSize).Wait() } - return gJ + return p } -// next first folds the input and E polynomials at the given verifier challenge then computes the new gⱼ. -// Thus, j <- j+1 and rⱼ = challenge. -func (c *zeroCheckClaims) next(challenge small_rational.SmallRational) polynomial.Polynomial { +// roundFold folds all input and eq polynomials at the verifier challenge r. +// After this call, j ← j+1 and rⱼ = r. +func (c *zeroCheckClaims) roundFold(r small_rational.SmallRational) { const minBlockSize = 512 - n := len(c.eq) / 2 + n := len(c.eqs[0]) / 2 if n < minBlockSize { - // no parallelization for i := range c.input { - c.input[i].Fold(challenge) + c.input[i].Fold(r) + } + for i := range c.eqs { + c.eqs[i].Fold(r) } - c.eq.Fold(challenge) } else { - wgs := make([]*sync.WaitGroup, len(c.input)) + wgs := make([]*sync.WaitGroup, len(c.input)+len(c.eqs)) for i := range c.input { - wgs[i] = c.manager.workers.Submit(n, c.input[i].FoldParallel(challenge), minBlockSize) + wgs[i] = c.resources.workers.Submit(n, c.input[i].FoldParallel(r), minBlockSize) + } + for i := range c.eqs { + wgs[len(c.input)+i] = c.resources.workers.Submit(n, c.eqs[i].FoldParallel(r), minBlockSize) } - c.manager.workers.Submit(n, c.eq.FoldParallel(challenge), minBlockSize).Wait() for _, wg := range wgs { wg.Wait() } } - - return c.computeGJ() -} - -func (c *zeroCheckClaims) varsNum() int { - return len(c.evaluationPoints[0]) -} - -func (c *zeroCheckClaims) claimsNum() int { - return len(c.claimedEvaluations) } -// proveFinalEval provides the values wᵢ(r₁, ..., rₙ) +// proveFinalEval provides the unique input wire values wᵢ(r₁, ..., rₙ). func (c *zeroCheckClaims) proveFinalEval(r []small_rational.SmallRational) []small_rational.SmallRational { - //defer the proof, return list of claims - - injection, _ := c.manager.circuit.ClaimPropagationInfo(c.wireI) // TODO @Tabaie: Instead of doing this last, we could just have fewer input in the first place; not that likely to happen with single gates, but more so with layers. - evaluations := make([]small_rational.SmallRational, len(injection)) - for i, gateInputI := range injection { - wI := c.input[gateInputI] - wI.Fold(r[len(r)-1]) // We already have wᵢ(r₁, ..., rₙ₋₁, hₙ) in a table. Only one more fold required. - c.manager.add(c.getWire().Inputs[gateInputI], r, wI[0]) - evaluations[i] = wI[0] + c.resources.levelPoints[c.levelI] = r + evaluations := make([]small_rational.SmallRational, len(c.input)) + for i := range c.input { + c.input[i].Fold(r[len(r)-1]) + evaluations[i] = c.input[i][0] } - - c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - c.gateEvaluatorPool.dumpAll() - - return evaluations -} - -type claimsManager struct { - claims []*zeroCheckLazyClaims - assignment WireAssignment - memPool *polynomial.Pool - workers *utils.WorkerPool - circuit Circuit -} - -func newClaimsManager(circuit Circuit, assignment WireAssignment, o settings) (manager claimsManager) { - manager.assignment = assignment - manager.claims = make([]*zeroCheckLazyClaims, len(circuit)) - manager.memPool = o.pool - manager.workers = o.workers - manager.circuit = circuit - - for i := range circuit { - manager.claims[i] = &zeroCheckLazyClaims{ - wireI: i, - evaluationPoints: make([][]small_rational.SmallRational, 0, circuit[i].NbClaims()), - claimedEvaluations: manager.memPool.Make(circuit[i].NbClaims()), - manager: &manager, - } + for i := range c.input { + c.resources.memPool.Dump(c.input[i]) } - return -} - -func (m *claimsManager) add(wire int, evaluationPoint []small_rational.SmallRational, evaluation small_rational.SmallRational) { - claim := m.claims[wire] - i := len(claim.evaluationPoints) - claim.claimedEvaluations[i] = evaluation - claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) + for i := range c.eqs { + c.resources.memPool.Dump(c.eqs[i]) + } + for _, pool := range c.gateEvaluatorPools { + pool.dumpAll() + } + return evaluations } -func (m *claimsManager) getLazyClaim(wire int) *zeroCheckLazyClaims { - return m.claims[wire] -} +// eqAcc sets m to an eq table at q and then adds it to e. +// m <- m[0] · eq(q, -). +// e <- e + m +func (r *resources) eqAcc(e, m polynomial.MultiLin, q []small_rational.SmallRational) { + n := len(q) -func (m *claimsManager) getClaim(wireI int) *zeroCheckClaims { - lazy := m.claims[wireI] - wire := m.circuit[wireI] - res := &zeroCheckClaims{ - wireI: wireI, - evaluationPoints: lazy.evaluationPoints, - claimedEvaluations: lazy.claimedEvaluations, - manager: m, - } + // At the end of each iteration, m(h₁, ..., hₙ) = m[0] · eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) + for i := range q { // 1-based in comments: q[i] = qᵢ₊₁ + // go through all assignments of (b₁, ..., bᵢ) ∈ {0,1}ⁱ + const threshold = 1 << 6 + k := 1 << i + if k < threshold { + for j := 0; j < k; j++ { + j0 := j << (n - i) // bᵢ₊₁ = 0 + j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - if wire.IsInput() { - res.input = []polynomial.MultiLin{m.memPool.Clone(m.assignment[wireI])} - } else { - res.input = make([]polynomial.MultiLin, len(wire.Inputs)) + m[j1].Mul(&q[i], &m[j0]) // m(b₁,...,bᵢ,1) = m(b₁,...,bᵢ) · qᵢ₊₁ + m[j0].Sub(&m[j0], &m[j1]) // m(b₁,...,bᵢ,0) = m(b₁,...,bᵢ) · (1 - qᵢ₊₁) + } + } else { + r.workers.Submit(k, func(start, end int) { + for j := start; j < end; j++ { + j0 := j << (n - i) // bᵢ₊₁ = 0 + j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - for inputI, inputW := range wire.Inputs { - res.input[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied + m[j1].Mul(&q[i], &m[j0]) // m(b₁,...,bᵢ,1) = m(b₁,...,bᵢ) · qᵢ₊₁ + m[j0].Sub(&m[j0], &m[j1]) // m(b₁,...,bᵢ,0) = m(b₁,...,bᵢ) · (1 - qᵢ₊₁) + } + }, 1024).Wait() } } - - res.gateEvaluatorPool = newGateEvaluatorPool(wire.Gate.Evaluate, len(res.input), m.memPool) - - return res -} - -func (m *claimsManager) deleteClaim(wire int) { - m.claims[wire].manager = nil - m.claims[wire] = nil + r.workers.Submit(len(e), func(start, end int) { + for i := start; i < end; i++ { + e[i].Add(&e[i], &m[i]) + } + }, 512).Wait() } -type settings struct { - pool *polynomial.Pool - transcript *fiatshamir.Transcript - transcriptPrefix string - nbVars int - workers *utils.WorkerPool +type resources struct { + // levelPoints[i] is the sumcheck evaluation point r produced at schedule level i. + // levelPoints[len(schedule)] holds the initial challenge (firstChallenge / rho). + // SkipLevels produce no point; their slot remains nil. + levelPoints [][]small_rational.SmallRational + evalPositions [][]int // [wireI][evalI] → position in source level's finalEvalProof + nbVars int + assignment WireAssignment + memPool polynomial.Pool + workers *utils.WorkerPool + circuit Circuit + schedule constraint.GkrProvingSchedule + transcript transcript } -type Option func(*settings) - -func WithPool(pool *polynomial.Pool) Option { - return func(options *settings) { - options.pool = pool +func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (resources, error) { + nbVars := assignment.NumVars() + nbInstances := assignment.NumInstances() + if 1<= 2 { + foldingCoeff = r.transcript.getChallenge() } - if o.pool == nil { - pool := polynomial.NewPool(c.MemoryRequirements(nbInstances)...) - o.pool = &pool + uniqueInputs, inputIndices := gkrcore.InputMapping(level, r.circuit) + input := make([]polynomial.MultiLin, len(uniqueInputs)) + for i, inW := range uniqueInputs { + input[i] = r.memPool.Clone(r.assignment[inW]) } - if o.workers == nil { - o.workers = utils.NewWorkerPool() + nbWires := 0 + for _, group := range level { + nbWires += len(group.Wires) } - if transcriptSettings.Transcript == nil { - challengeNames := ChallengeNames(c, o.nbVars, transcriptSettings.Prefix) - o.transcript = fiatshamir.NewTranscript(transcriptSettings.Hash, challengeNames...) - for i := range transcriptSettings.BaseChallenges { - if err = o.transcript.Bind(challengeNames[0], transcriptSettings.BaseChallenges[i]); err != nil { - return o, err + pools := make([]*gateEvaluatorPool, nbWires) + flatW := 0 + for _, group := range level { + for _, wI := range group.Wires { + wire := r.circuit[wI] + gate := wire.Gate.Evaluate + if wire.IsInput() { + gate = gkrcore.IdentityBytecode() } + pools[flatW] = newGateEvaluatorPool(gate, len(inputIndices[flatW]), &r.memPool) + flatW++ } - } else { - o.transcript, o.transcriptPrefix = transcriptSettings.Transcript, transcriptSettings.Prefix } - return o, err -} - -func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { - - // Pre-compute the size TODO: Consider not doing this and just grow the list by appending - size := logNbInstances // first challenge - - for i := range c { - if c[i].NoProof() { // no proof, no challenge - continue - } - if c[i].NbClaims() > 1 { //fold the claims - size++ + eqLength := 1 << r.nbVars + eqs := make([]polynomial.MultiLin, nbWires) + var alpha small_rational.SmallRational + alpha.SetOne() + flatW = 0 + for _, group := range level { + nbSources := len(group.ClaimSources) + + groupEq := polynomial.MultiLin(r.memPool.Make(eqLength)) + groupEq[0].Set(&alpha) + groupEq.Eq(r.levelPoints[group.ClaimSources[0]]) + + if nbSources > 1 { + newEq := polynomial.MultiLin(r.memPool.Make(eqLength)) + aI := alpha + for k := 1; k < nbSources; k++ { + aI.Mul(&aI, &foldingCoeff) + newEq[0].Set(&aI) + r.eqAcc(groupEq, newEq, r.levelPoints[group.ClaimSources[k]]) + } + r.memPool.Dump(newEq) } - size += logNbInstances // full run of sumcheck on logNbInstances variables - } - nums := make([]string, max(len(c), logNbInstances)) - for i := range nums { - nums[i] = strconv.Itoa(i) - } - - challenges := make([]string, size) - - // output wire claims - firstChallengePrefix := prefix + "fC." - for j := 0; j < logNbInstances; j++ { - challenges[j] = firstChallengePrefix + nums[j] - } - j := logNbInstances - for i := len(c) - 1; i >= 0; i-- { - if c[i].NoProof() { - continue + var stride small_rational.SmallRational + stride.Set(&foldingCoeff) + for range nbSources - 1 { + stride.Mul(&stride, &foldingCoeff) } - wirePrefix := prefix + "w" + nums[i] + "." - if c[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "fold" - j++ - } + eqs[flatW] = groupEq + flatW++ + alpha.Mul(&alpha, &stride) - partialSumPrefix := wirePrefix + "pSP." - for k := 0; k < logNbInstances; k++ { - challenges[j] = partialSumPrefix + nums[k] - j++ + for w := 1; w < len(group.Wires); w++ { + eqs[flatW] = polynomial.MultiLin(r.memPool.Make(eqLength)) + r.workers.Submit(eqLength, func(start, end int) { + for i := start; i < end; i++ { + eqs[flatW][i].Mul(&eqs[flatW-1][i], &stride) + } + }, 512).Wait() + flatW++ + alpha.Mul(&alpha, &stride) } } - return challenges -} -func getFirstChallengeNames(logNbInstances int, prefix string) []string { - res := make([]string, logNbInstances) - firstChallengePrefix := prefix + "fC." - for i := 0; i < logNbInstances; i++ { - res[i] = firstChallengePrefix + strconv.Itoa(i) + claims := &zeroCheckClaims{ + level: level, + levelI: levelI, + resources: r, + input: input, + inputIndices: inputIndices, + eqs: eqs, + gateEvaluatorPools: pools, + } + return sumcheckProve(claims, &r.transcript) +} + +// verifyLevel runs the sumcheck verification for a SumcheckLevel: derives the fold challenge, +// computes the claimed sum, and calls sumcheckVerify. +func (r *resources) verifyLevel(levelI int, proof Proof) error { + level := r.schedule[levelI].(constraint.GkrSumcheckLevel) + initialChallengeI := len(r.schedule) + + nbClaims := gkrcore.NbClaims(level) + var foldingCoeff small_rational.SmallRational + if nbClaims >= 2 { + foldingCoeff = r.transcript.getChallenge() + } + + var ys []small_rational.SmallRational + for _, group := range level { + for _, wI := range group.Wires { + evalI := 0 + for _, src := range group.ClaimSources { + var y small_rational.SmallRational + if src == initialChallengeI { + y = r.assignment[wI].Evaluate(r.levelPoints[src], &r.memPool) + } else { + y = proof[src].finalEvalProof[r.evalPositions[wI][evalI]] + evalI++ + } + ys = append(ys, y) + } + } } - return res -} -func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]small_rational.SmallRational, error) { - res := make([]small_rational.SmallRational, len(names)) - for i, name := range names { - if bytes, err := transcript.ComputeChallenge(name); err != nil { - return nil, err - } else if err = res[i].SetBytesCanonical(bytes); err != nil { - return nil, err - } + ysPoly := polynomial.Polynomial(ys) + claimedSum := ysPoly.Eval(&foldingCoeff) + + lazyClaims := &zeroCheckLazyClaims{ + foldingCoeff: foldingCoeff, + resources: r, + levelI: levelI, } - return res, nil + return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, gkrcore.Degree(level, r.circuit), &r.transcript) } // Prove consistency of the claimed assignment -func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { - o, err := setup(c, assignment, transcriptSettings, options...) +func Prove(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (Proof, error) { + r, err := newResources(c, schedule, assignment, hasher) if err != nil { return nil, err } - defer o.workers.Stop() + defer r.workers.Stop() - claims := newClaimsManager(c, assignment, o) + proof := make(Proof, len(schedule)) - proof := make(Proof, len(c)) - // firstChallenge called rho in the paper - var firstChallenge []small_rational.SmallRational - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return nil, err + // Derive initial challenge point + firstChallenge := make([]small_rational.SmallRational, r.nbVars) + for j := range r.nbVars { + firstChallenge[j] = r.transcript.getChallenge() } + r.levelPoints[len(schedule)] = firstChallenge - wirePrefix := o.transcriptPrefix + "w" - var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - - wire := c[i] - - if wire.IsOutput() { - claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) - } - - claim := claims.getClaim(i) - if wire.NoProof() { // input wires with one claim only - proof[i] = sumcheckProof{ - partialSumPolys: []polynomial.Polynomial{}, - finalEvalProof: []small_rational.SmallRational{}, - } - } else { - if proof[i], err = sumcheckProve( - claim, fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err != nil { - return proof, err - } + for levelI := len(schedule) - 1; levelI >= 0; levelI-- { + switch s := schedule[levelI].(type) { + case constraint.GkrSkipLevel: + r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - baseChallenge = make([][]byte, len(proof[i].finalEvalProof)) - for j := range proof[i].finalEvalProof { - baseChallenge[j] = proof[i].finalEvalProof[j].Marshal() - } + case constraint.GkrSumcheckLevel: + proof[levelI] = r.proveLevel(levelI) + // Bind finalEvalProof for next level's challenge derivation + r.transcript.bind(proof[levelI].finalEvalProof...) } - // the verifier checks a single claim about input wires itself - claims.deleteClaim(i) } return proof, nil } -// Verify the consistency of the claimed output with the claimed input -// Unlike in Prove, the assignment argument need not be complete -func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { - o, err := setup(c, assignment, transcriptSettings, options...) +// Verify the consistency of the claimed output with the claimed input. +// Unlike in Prove, the assignment argument need not be complete. +func Verify(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, proof Proof, hasher hash.Hash) error { + r, err := newResources(c, schedule, assignment, hasher) if err != nil { return err } - defer o.workers.Stop() - - claims := newClaimsManager(c, assignment, o) + defer r.workers.Stop() - var firstChallenge []small_rational.SmallRational - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return err + // Derive initial challenge point + firstChallenge := make([]small_rational.SmallRational, r.nbVars) + for j := range r.nbVars { + firstChallenge[j] = r.transcript.getChallenge() } + r.levelPoints[len(schedule)] = firstChallenge - wirePrefix := o.transcriptPrefix + "w" - var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - wire := c[i] - - if wire.IsOutput() { - claims.add(i, firstChallenge, assignment[i].Evaluate(firstChallenge, claims.memPool)) - } - - proofW := proof[i] - claim := claims.getLazyClaim(i) - if wire.NoProof() { // input wires with one claim only - // make sure the proof is empty - if len(proofW.finalEvalProof) != 0 || len(proofW.partialSumPolys) != 0 { - return errors.New("no proof allowed for input wire with a single claim") - } + for levelI := len(schedule) - 1; levelI >= 0; levelI-- { + switch s := schedule[levelI].(type) { + case constraint.GkrSkipLevel: + r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - if wire.NbClaims() == 1 { // input wire - // simply evaluate and see if it matches - if len(claim.evaluationPoints) == 0 || len(claim.claimedEvaluations) == 0 { - return errors.New("missing input wire claim") - } - evaluation := assignment[i].Evaluate(claim.evaluationPoints[0], claims.memPool) - if !claim.claimedEvaluations[0].Equal(&evaluation) { - return errors.New("incorrect input wire claim") - } + case constraint.GkrSumcheckLevel: + if err = r.verifyLevel(levelI, proof); err != nil { + return fmt.Errorf("sumcheck proof rejected: %v", err) } - } else if err = sumcheckVerify( - claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { // incorporate prover claims about w's input into the transcript - baseChallenge = make([][]byte, len(proofW.finalEvalProof)) - for j := range baseChallenge { - baseChallenge[j] = proofW.finalEvalProof[j].Marshal() - } - } else { - return fmt.Errorf("sumcheck proof rejected: %v", err) //TODO: Any polynomials to dump? + // Bind finalEvalProof for next level's challenge derivation + r.transcript.bind(proof[levelI].finalEvalProof...) } - claims.deleteClaim(i) } return nil } diff --git a/internal/gkr/small_rational/sumcheck.go b/internal/gkr/small_rational/sumcheck.go index 19d2c86c77..281d6a4348 100644 --- a/internal/gkr/small_rational/sumcheck.go +++ b/internal/gkr/small_rational/sumcheck.go @@ -7,33 +7,62 @@ package gkr import ( "errors" - "strconv" + "hash" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" ) -// This does not make use of parallelism and represents polynomials as lists of coefficients -// It is currently geared towards arithmetic hashes. Once we have a more unified hash function interface, this can be generified. +// This does not make use of parallelism and represents polynomials as lists of coefficients. + +// transcript is a Fiat-Shamir transcript backed by a running hash. +// Field elements are written via bind; challenges are derived via getChallenge. +// The hash is never reset — all previous data is implicitly part of future challenges. +type transcript struct { + h hash.Hash + bound bool // whether bind was called since the last getChallenge +} + +// bind writes field elements to the transcript as bindings for the next challenge. +func (t *transcript) bind(elements ...small_rational.SmallRational) { + if len(elements) == 0 { + return + } + for i := range elements { + bytes := elements[i].Bytes() + t.h.Write(bytes[:]) + } + t.bound = true +} + +// getChallenge binds optional elements, then squeezes a challenge from the current hash state. +// If no bindings were added since the last squeeze, a separator byte is written first +// to advance the state and prevent repeated values. +func (t *transcript) getChallenge(bindings ...small_rational.SmallRational) small_rational.SmallRational { + t.bind(bindings...) + if !t.bound { + t.h.Write([]byte{0}) + } + t.bound = false + var res small_rational.SmallRational + res.SetBytes(t.h.Sum(nil)) + return res +} // sumcheckClaims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. // Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) type sumcheckClaims interface { - fold(a small_rational.SmallRational) polynomial.Polynomial // fold into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. - next(small_rational.SmallRational) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ + roundPolynomial() polynomial.Polynomial // compute gⱼ polynomial for current round + roundFold(r small_rational.SmallRational) // fold inputs and eq at challenge r varsNum() int // number of variables - claimsNum() int // number of claims proveFinalEval(r []small_rational.SmallRational) []small_rational.SmallRational // in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof } // sumcheckLazyClaims is the sumcheckClaims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. type sumcheckLazyClaims interface { - claimsNum() int // claimsNum = m - varsNum() int // varsNum = n - foldedSum(a small_rational.SmallRational) small_rational.SmallRational // foldedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - degree(i int) int // degree of the total claim in the i'th variable - verifyFinalEval(r []small_rational.SmallRational, foldingCoeff small_rational.SmallRational, purportedValue small_rational.SmallRational, proof []small_rational.SmallRational) error + varsNum() int // varsNum = n + degree(i int) int // degree of the total claim in the i'th variable + verifyFinalEval(r []small_rational.SmallRational, purportedValue small_rational.SmallRational, proof []small_rational.SmallRational) error } // sumcheckProof of a multi-statement. @@ -42,130 +71,46 @@ type sumcheckProof struct { finalEvalProof []small_rational.SmallRational //in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof } -func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) (challengeNames []string, err error) { - numChallenges := varsNum - if claimsNum >= 2 { - numChallenges++ - } - challengeNames = make([]string, numChallenges) - if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "fold" - } - prefix := settings.Prefix + "pSP." - for i := 0; i < varsNum; i++ { - challengeNames[i+numChallenges-varsNum] = prefix + strconv.Itoa(i) - } - if settings.Transcript == nil { - transcript := fiatshamir.NewTranscript(settings.Hash, challengeNames...) - settings.Transcript = transcript - } - - for i := range settings.BaseChallenges { - if err = settings.Transcript.Bind(challengeNames[0], settings.BaseChallenges[i]); err != nil { - return - } - } - return -} - -func next(transcript *fiatshamir.Transcript, bindings []small_rational.SmallRational, remainingChallengeNames *[]string) (small_rational.SmallRational, error) { - challengeName := (*remainingChallengeNames)[0] - for i := range bindings { - bytes := bindings[i].Bytes() - if err := transcript.Bind(challengeName, bytes[:]); err != nil { - return small_rational.SmallRational{}, err - } - } - var res small_rational.SmallRational - bytes, err := transcript.ComputeChallenge(challengeName) - res.SetBytes(bytes) - - *remainingChallengeNames = (*remainingChallengeNames)[1:] - - return res, err -} - -// sumcheckProve create a non-interactive proof -func sumcheckProve(claims sumcheckClaims, transcriptSettings fiatshamir.Settings) (sumcheckProof, error) { - - var proof sumcheckProof - remainingChallengeNames, err := setupTranscript(claims.claimsNum(), claims.varsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return proof, err - } - - var foldingCoeff small_rational.SmallRational - if claims.claimsNum() >= 2 { - if foldingCoeff, err = next(transcript, []small_rational.SmallRational{}, &remainingChallengeNames); err != nil { - return proof, err - } - } - +// sumcheckProve creates a non-interactive sumcheck proof. +// The fold challenge is derived by the caller (proveLevel). +// Pattern: roundPolynomial, [roundFold, roundPolynomial]*, proveFinalEval. +func sumcheckProve(claims sumcheckClaims, t *transcript) sumcheckProof { varsNum := claims.varsNum() - proof.partialSumPolys = make([]polynomial.Polynomial, varsNum) - proof.partialSumPolys[0] = claims.fold(foldingCoeff) + proof := sumcheckProof{partialSumPolys: make([]polynomial.Polynomial, varsNum)} + proof.partialSumPolys[0] = claims.roundPolynomial() challenges := make([]small_rational.SmallRational, varsNum) - for j := 0; j+1 < varsNum; j++ { - if challenges[j], err = next(transcript, proof.partialSumPolys[j], &remainingChallengeNames); err != nil { - return proof, err - } - proof.partialSumPolys[j+1] = claims.next(challenges[j]) - } - - if challenges[varsNum-1], err = next(transcript, proof.partialSumPolys[varsNum-1], &remainingChallengeNames); err != nil { - return proof, err + for j := range varsNum - 1 { + challenges[j] = t.getChallenge(proof.partialSumPolys[j]...) + claims.roundFold(challenges[j]) + proof.partialSumPolys[j+1] = claims.roundPolynomial() } + challenges[varsNum-1] = t.getChallenge(proof.partialSumPolys[varsNum-1]...) proof.finalEvalProof = claims.proveFinalEval(challenges) - - return proof, nil + return proof } -func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, transcriptSettings fiatshamir.Settings) error { - remainingChallengeNames, err := setupTranscript(claims.claimsNum(), claims.varsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return err - } - - var foldingCoeff small_rational.SmallRational - - if claims.claimsNum() >= 2 { - if foldingCoeff, err = next(transcript, []small_rational.SmallRational{}, &remainingChallengeNames); err != nil { - return err - } - } - +// sumcheckVerify verifies a non-interactive sumcheck proof. +// The fold challenge is derived by the caller (verifyLevel). +// claimedSum is the expected sum; degree is the polynomial's degree in each variable. +func sumcheckVerify(claims sumcheckLazyClaims, proof sumcheckProof, claimedSum small_rational.SmallRational, degree int, t *transcript) error { r := make([]small_rational.SmallRational, claims.varsNum()) - // Just so that there is enough room for gJ to be reused - maxDegree := claims.degree(0) - for j := 1; j < claims.varsNum(); j++ { - if d := claims.degree(j); d > maxDegree { - maxDegree = d - } - } - gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() - gJR := claims.foldedSum(foldingCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) + gJ := make(polynomial.Polynomial, degree+1) + gJR := claimedSum for j := range claims.varsNum() { - if len(proof.partialSumPolys[j]) != claims.degree(j) { + if len(proof.partialSumPolys[j]) != degree { return errors.New("malformed proof") } copy(gJ[1:], proof.partialSumPolys[j]) - gJ[0].Sub(&gJR, &proof.partialSumPolys[j][0]) // Requirement that gⱼ(0) + gⱼ(1) = gⱼ₋₁(r) - // gJ is ready + gJ[0].Sub(&gJR, &proof.partialSumPolys[j][0]) - //Prepare for the next iteration - if r[j], err = next(transcript, proof.partialSumPolys[j], &remainingChallengeNames); err != nil { - return err - } - // This is an extremely inefficient way of interpolating. TODO: Interpolate without symbolically computing a polynomial - gJCoeffs := polynomial.InterpolateOnRange(gJ[:(claims.degree(j) + 1)]) + r[j] = t.getChallenge(proof.partialSumPolys[j]...) + gJCoeffs := polynomial.InterpolateOnRange(gJ[:(degree + 1)]) gJR = gJCoeffs.Eval(&r[j]) } - return claims.verifyFinalEval(r, foldingCoeff, gJR, proof.finalEvalProof) + return claims.verifyFinalEval(r, gJR, proof.finalEvalProof) } diff --git a/internal/gkr/small_rational/sumcheck_test.go b/internal/gkr/small_rational/sumcheck_test.go index 9cb6eabd96..b364291f7b 100644 --- a/internal/gkr/small_rational/sumcheck_test.go +++ b/internal/gkr/small_rational/sumcheck_test.go @@ -9,7 +9,6 @@ import ( "fmt" "hash" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark/internal/small_rational/polynomial" "github.com/stretchr/testify/assert" @@ -24,11 +23,9 @@ func testSumcheckSingleClaimMultilin(polyInt []uint64, hashGenerator func() hash } claim := singleMultilinClaim{g: poly.Clone()} + t := transcript{h: hashGenerator()} - proof, err := sumcheckProve(&claim, fiatshamir.WithHash(hashGenerator())) - if err != nil { - return err - } + proof := sumcheckProve(&claim, &t) var sb strings.Builder for _, p := range proof.partialSumPolys { @@ -44,13 +41,15 @@ func testSumcheckSingleClaimMultilin(polyInt []uint64, hashGenerator func() hash } lazyClaim := singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if err = sumcheckVerify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())); err != nil { + t = transcript{h: hashGenerator()} + if err := sumcheckVerify(lazyClaim, proof, lazyClaim.claimedSum, 1, &t); err != nil { return err } proof.partialSumPolys[0][0].Add(&proof.partialSumPolys[0][0], toElement(1)) lazyClaim = singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if sumcheckVerify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())) == nil { + t = transcript{h: hashGenerator()} + if sumcheckVerify(lazyClaim, proof, lazyClaim.claimedSum, 1, &t) == nil { return fmt.Errorf("bad proof accepted") } return nil diff --git a/internal/gkr/small_rational/sumcheck_test_vector_gen.go b/internal/gkr/small_rational/sumcheck_test_vector_gen.go index ea969d171e..89ad304949 100644 --- a/internal/gkr/small_rational/sumcheck_test_vector_gen.go +++ b/internal/gkr/small_rational/sumcheck_test_vector_gen.go @@ -14,7 +14,6 @@ import ( "path/filepath" "runtime/pprof" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" @@ -38,11 +37,9 @@ func runMultilin(testCaseInfo *sumcheckTestCaseInfo) error { return err } - proof, err := sumcheckProve( - &singleMultilinClaim{poly}, fiatshamir.WithHash(hsh)) - if err != nil { - return err - } + claim := singleMultilinClaim{poly} + t := transcript{h: hsh} + proof := sumcheckProve(&claim, &t) testCaseInfo.Proof = sumcheckToPrintableProof(proof) // Verification @@ -56,12 +53,20 @@ func runMultilin(testCaseInfo *sumcheckTestCaseInfo) error { return err } - if err = sumcheckVerify(singleMultilinLazyClaim{g: poly, claimedSum: claimedSum}, proof, fiatshamir.WithHash(hsh)); err != nil { + if hsh, err = hashFromDescription(testCaseInfo.Hash); err != nil { + return err + } + t = transcript{h: hsh} + if err = sumcheckVerify(singleMultilinLazyClaim{g: poly, claimedSum: claimedSum}, proof, claimedSum, 1, &t); err != nil { return fmt.Errorf("proof rejected: %v", err) } proof.partialSumPolys[0][0].Add(&proof.partialSumPolys[0][0], toElement(1)) - if err = sumcheckVerify(singleMultilinLazyClaim{g: poly, claimedSum: claimedSum}, proof, fiatshamir.WithHash(hsh)); err == nil { + if hsh, err = hashFromDescription(testCaseInfo.Hash); err != nil { + return err + } + t = transcript{h: hsh} + if err = sumcheckVerify(singleMultilinLazyClaim{g: poly, claimedSum: claimedSum}, proof, claimedSum, 1, &t); err == nil { return fmt.Errorf("bad proof accepted") } @@ -150,18 +155,14 @@ type singleMultilinClaim struct { g polynomial.MultiLin } -func (c singleMultilinClaim) proveFinalEval(r []small_rational.SmallRational) []small_rational.SmallRational { +func (c *singleMultilinClaim) proveFinalEval(r []small_rational.SmallRational) []small_rational.SmallRational { return nil // verifier can compute the final eval itself } -func (c singleMultilinClaim) varsNum() int { +func (c *singleMultilinClaim) varsNum() int { return bits.TrailingZeros(uint(len(c.g))) } -func (c singleMultilinClaim) claimsNum() int { - return 1 -} - func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { sum := g[len(g)/2] for i := len(g)/2 + 1; i < len(g); i++ { @@ -170,13 +171,12 @@ func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { return []small_rational.SmallRational{sum} } -func (c singleMultilinClaim) fold(small_rational.SmallRational) polynomial.Polynomial { +func (c *singleMultilinClaim) roundPolynomial() polynomial.Polynomial { return sumForX1One(c.g) } -func (c *singleMultilinClaim) next(r small_rational.SmallRational) polynomial.Polynomial { +func (c *singleMultilinClaim) roundFold(r small_rational.SmallRational) { c.g.Fold(r) - return sumForX1One(c.g) } type singleMultilinLazyClaim struct { @@ -184,7 +184,7 @@ type singleMultilinLazyClaim struct { claimedSum small_rational.SmallRational } -func (c singleMultilinLazyClaim) verifyFinalEval(r []small_rational.SmallRational, _ small_rational.SmallRational, purportedValue small_rational.SmallRational, proof []small_rational.SmallRational) error { +func (c singleMultilinLazyClaim) verifyFinalEval(r []small_rational.SmallRational, purportedValue small_rational.SmallRational, proof []small_rational.SmallRational) error { val := c.g.Evaluate(r, nil) if val.Equal(&purportedValue) { return nil @@ -192,15 +192,7 @@ func (c singleMultilinLazyClaim) verifyFinalEval(r []small_rational.SmallRationa return fmt.Errorf("mismatch") } -func (c singleMultilinLazyClaim) foldedSum(_ small_rational.SmallRational) small_rational.SmallRational { - return c.claimedSum -} - -func (c singleMultilinLazyClaim) degree(i int) int { - return 1 -} - -func (c singleMultilinLazyClaim) claimsNum() int { +func (c singleMultilinLazyClaim) degree(int) int { return 1 } diff --git a/internal/gkr/small_rational/test_vector_gen.go b/internal/gkr/small_rational/test_vector_gen.go index 4736f0f9a0..327a18ee98 100644 --- a/internal/gkr/small_rational/test_vector_gen.go +++ b/internal/gkr/small_rational/test_vector_gen.go @@ -15,9 +15,9 @@ import ( "github.com/consensys/bavard" "github.com/consensys/gnark-crypto/ecc" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - "github.com/consensys/gnark/internal/gkr/gkrtesting" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/internal/gkr/gkrcore" + "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/small_rational" "github.com/consensys/gnark/internal/small_rational/polynomial" ) @@ -64,10 +64,8 @@ func run(absPath string) error { return err } - transcriptSetting := fiatshamir.WithHash(testCase.Hash) - var proof Proof - proof, err = Prove(testCase.Circuit, testCase.FullAssignment, transcriptSetting) + proof, err = Prove(testCase.Circuit, testCase.Schedule, testCase.FullAssignment, testCase.Hash) if err != nil { return err } @@ -89,7 +87,7 @@ func run(absPath string) error { return err } - err = Verify(testCase.Circuit, testCase.InOutAssignment, proof, transcriptSetting) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, proof, testCase.Hash) if err != nil { return err } @@ -99,7 +97,7 @@ func run(absPath string) error { return err } - err = Verify(testCase.Circuit, testCase.InOutAssignment, proof, fiatshamir.WithHash(newMessageCounter(2, 0))) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, proof, newMessageCounter(2, 0)) if err == nil { return fmt.Errorf("bad proof accepted") } @@ -196,6 +194,7 @@ type TestCase struct { Proof Proof FullAssignment WireAssignment InOutAssignment WireAssignment + Schedule constraint.GkrProvingSchedule Info gkrtesting.TestCaseInfo // we are generating the test vectors, so we need to keep the circuit instance info to ADD the proof to it and resave it } @@ -227,6 +226,20 @@ func newTestCase(path string) (*TestCase, error) { if proof, err = unmarshalProof(info.Proof); err != nil { return nil, err } + var schedule constraint.GkrProvingSchedule + if schedule, err = info.Schedule.ToProvingSchedule(); err != nil { + return nil, err + } + if schedule == nil { + if schedule, err = gkrcore.DefaultProvingSchedule(circuit); err != nil { + return nil, err + } + } + + outputSet := make(map[int]bool, len(circuit)) + for _, o := range circuit.Outputs() { + outputSet[o] = true + } fullAssignment := make(WireAssignment, len(circuit)) inOutAssignment := make(WireAssignment, len(circuit)) @@ -240,7 +253,7 @@ func newTestCase(path string) (*TestCase, error) { } assignmentRaw = info.Input[inI] inI++ - } else if circuit[i].IsOutput() { + } else if outputSet[i] { if outI == len(info.Output) { return nil, fmt.Errorf("fewer output in vector than in circuit") } @@ -261,7 +274,7 @@ func newTestCase(path string) (*TestCase, error) { fullAssignment.Complete(circuit) for i := range circuit { - if circuit[i].IsOutput() { + if outputSet[i] { if err = sliceEquals(inOutAssignment[i], fullAssignment[i]); err != nil { return nil, fmt.Errorf("assignment mismatch: %v", err) } @@ -274,6 +287,7 @@ func newTestCase(path string) (*TestCase, error) { Proof: proof, Hash: _hash, Circuit: circuit, + Schedule: schedule, Info: info, } From db27299aac51da3152fe968f2b03c6f8b6dd64d4 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Tue, 10 Mar 2026 23:31:16 -0500 Subject: [PATCH 228/251] mostly fix gadget package --- internal/gkr/gkr.go | 374 ++++++++++++++++++--------------------- internal/gkr/gkr_test.go | 46 +++-- internal/gkr/sumcheck.go | 57 ++---- std/gkrapi/compile.go | 26 ++- 4 files changed, 235 insertions(+), 268 deletions(-) diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index 1b30915736..af4b14c44a 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -5,6 +5,7 @@ import ( "fmt" "strconv" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrcore" fiatshamir "github.com/consensys/gnark/std/fiat-shamir" @@ -41,134 +42,71 @@ func (a WireAssignment) NbVars() int { // A SNARK gadget capable of verifying a GKR proof // The goal is to prove/verify evaluations of many instances of the same circuit. -type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) +type Proof []sumcheckProof // for each schedule level, a sumcheck proof // zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). -// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) w(-) sums up to the expected multilinear -// extension of the values of w across all instances. -// Its purpose is to batch the checking of multiple evaluations of the same wire. +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, +// where the sum runs over all (wire v, claim source s) pairs in the level. type zeroCheckLazyClaims struct { - wireI int - evaluationPoints [][]frontend.Variable - claimedEvaluations []frontend.Variable - manager *claimsManager // WARNING: Circular references -} - -func (e *zeroCheckLazyClaims) getWire() Wire { - return e.manager.circuit[e.wireI] -} - -// verifyFinalEval finalizes the verification of w. -// The prover's claims w(xᵢ) = yᵢ have already been reduced to verifying -// ∑ cⁱ eq(xᵢ, r) w(r) = purportedValue. ( c is foldingCoeff ) -// Both purportedValue and the vector r have been randomized during the sumcheck protocol. -// By taking the w term out of the sum we get the equivalent claim that -// for E := ∑ eq(xᵢ, r), it must be that E w(r) = purportedValue. -// If w is an input wire, the verifier can directly check its evaluation at r. -// Otherwise, the prover makes claims about the evaluation of w's input wires, -// wᵢ, at r, to be verified later. -// The claims are communicated through the proof parameter. -// The verifier checks here if the claimed evaluations of wᵢ(r) are consistent with -// the main claim, by checking E w(wᵢ(r)...) = purportedValue. -func (e *zeroCheckLazyClaims) verifyFinalEval(api frontend.API, r []frontend.Variable, foldingCoeff, purportedValue frontend.Variable, uniqueInputEvaluations []frontend.Variable) error { - // the eq terms ( E ) - numClaims := len(e.evaluationPoints) - evaluation := polynomial.EvalEq(api, e.evaluationPoints[numClaims-1], r) - for i := numClaims - 2; i >= 0; i-- { - evaluation = api.Mul(evaluation, foldingCoeff) - eq := polynomial.EvalEq(api, e.evaluationPoints[i], r) - evaluation = api.Add(evaluation, eq) - } - - wire := e.getWire() - - // the g(...) term - var gateEvaluation frontend.Variable - if wire.IsInput() { - gateEvaluation = e.manager.assignment[e.wireI].Evaluate(api, r) - } else { - - injection, injectionLeftInv := - e.manager.circuit.ClaimPropagationInfo(e.wireI) - - if len(injection) != len(uniqueInputEvaluations) { - return fmt.Errorf("%d input wire evaluations given, %d expected", len(uniqueInputEvaluations), len(injection)) - } - - for uniqueI, i := range injection { // map from unique to all - e.manager.add(wire.Inputs[i], r, uniqueInputEvaluations[uniqueI]) - } - - inputEvaluations := make([]frontend.Variable, len(wire.Inputs)) - for i, uniqueI := range injectionLeftInv { // map from all to unique - inputEvaluations[i] = uniqueInputEvaluations[uniqueI] - } - - gateEvaluation = wire.Gate.Evaluate(FrontendAPIWrapper{api}, inputEvaluations...) - } - evaluation = api.Mul(evaluation, gateEvaluation) - - api.AssertIsEqual(evaluation, purportedValue) - return nil -} - -func (e *zeroCheckLazyClaims) claimsNum() int { - return len(e.evaluationPoints) + foldingCoeff frontend.Variable + levelI int + schedule constraint.GkrProvingSchedule + circuit Circuit + assignment WireAssignment + levelPoints [][]frontend.Variable // [stepI] → eval point r + evalPositions [][]int // [wireI][evalI] → position in source level's finalEvalProof + proof Proof + nbVars int } func (e *zeroCheckLazyClaims) varsNum() int { - return len(e.evaluationPoints[0]) -} - -func (e *zeroCheckLazyClaims) foldedSum(api frontend.API, a frontend.Variable) frontend.Variable { - evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) - return evalsAsPoly.Eval(api, a) + return e.nbVars } func (e *zeroCheckLazyClaims) degree(int) int { - return 1 + e.getWire().Gate.Degree + return gkrcore.Degree(e.schedule[e.levelI].(constraint.GkrSumcheckLevel), e.circuit) } -type claimsManager struct { - claims []*zeroCheckLazyClaims - assignment WireAssignment - circuit Circuit -} - -func newClaimsManager(circuit Circuit, assignment WireAssignment) (claims claimsManager) { - claims.assignment = assignment - claims.claims = make([]*zeroCheckLazyClaims, len(circuit)) - claims.circuit = circuit +// verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. +// The sumcheck protocol has already reduced the per-wire claims to verifying +// ∑ᵢ cⁱ eq(xᵢ, r) · wᵢ(r) = purportedValue, where the sum runs over all +// claims on each wire and c is foldingCoeff. +// Both purportedValue and the vector r have been randomized during sumcheck. +// +// For input wires, w(r) is computed directly from the assignment. +// For non-input wires, the prover claims evaluations of the input wires at r, +// communicated through uniqueInputEvaluations; those claims are verified later. +func (e *zeroCheckLazyClaims) verifyFinalEval(api frontend.API, r []frontend.Variable, purportedValue frontend.Variable, uniqueInputEvaluations []frontend.Variable) error { + e.levelPoints[e.levelI] = r + level := e.schedule[e.levelI].(constraint.GkrSumcheckLevel) + perWireInputEvals := gkrcore.ReduplicateInputs(level, e.circuit, uniqueInputEvaluations) + + var terms []frontend.Variable + flatW := 0 + for _, group := range level { + for _, wI := range group.Wires { + wire := e.circuit[wI] + + var gateEval frontend.Variable + if wire.IsInput() { + gateEval = e.assignment[wI].Evaluate(api, r) + } else { + gateEval = wire.Gate.Evaluate(FrontendAPIWrapper{api}, perWireInputEvals[flatW]...) + } - for i := range circuit { - if circuit[i].IsInput() { - circuit[i].Gate.Degree = 1 - circuit[i].Gate.Evaluate = gkrcore.Identity - } - claims.claims[i] = &zeroCheckLazyClaims{ - wireI: i, - evaluationPoints: make([][]frontend.Variable, 0, circuit[i].NbClaims()), - claimedEvaluations: make(polynomial.Polynomial, circuit[i].NbClaims()), - manager: &claims, + for _, src := range group.ClaimSources { + eq := polynomial.EvalEq(api, e.levelPoints[src], r) + term := api.Mul(eq, gateEval) + terms = append(terms, term) + } + flatW++ } } - return -} - -func (m *claimsManager) add(wire int, evaluationPoint []frontend.Variable, evaluation frontend.Variable) { - claim := m.claims[wire] - i := len(claim.evaluationPoints) - claim.claimedEvaluations[i] = evaluation - claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) -} - -func (m *claimsManager) getLazyClaim(wire int) *zeroCheckLazyClaims { - return m.claims[wire] -} -func (m *claimsManager) deleteClaim(wire int) { - m.claims[wire].manager = nil - m.claims[wire] = nil + ys := polynomial.Polynomial(terms) + total := ys.Eval(api, e.foldingCoeff) + api.AssertIsEqual(total, purportedValue) + return nil } type settings struct { @@ -179,7 +117,7 @@ type settings struct { type Option func(*settings) -func setup(api frontend.API, c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { +func setup(api frontend.API, c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { var o settings var err error for _, option := range options { @@ -193,7 +131,7 @@ func setup(api frontend.API, c Circuit, assignment WireAssignment, transcriptSet } if transcriptSettings.Transcript == nil { - challengeNames := ChallengeNames(c, o.nbVars, transcriptSettings.Prefix) + challengeNames := ChallengeNames(c, schedule, o.nbVars, transcriptSettings.Prefix) o.transcript = fiatshamir.NewTranscript(api, transcriptSettings.Hash, challengeNames) if err = o.transcript.Bind(challengeNames[0], transcriptSettings.BaseChallenges); err != nil { return o, err @@ -205,51 +143,33 @@ func setup(api frontend.API, c Circuit, assignment WireAssignment, transcriptSet return o, err } -func ChallengeNames(c Circuit, logNbInstances int, prefix string) []string { - - // Pre-compute the size TODO: Consider not doing this and just grow the list by appending - size := logNbInstances // first challenge - - for i := range c { - if c[i].NoProof() { // no proof, no challenge - continue - } - if c[i].NbClaims() > 1 { //fold the claims - size++ - } - size += logNbInstances // full run of sumcheck on logNbInstances variables - } - - nums := make([]string, max(len(c), logNbInstances)) - for i := range nums { - nums[i] = strconv.Itoa(i) - } - - challenges := make([]string, size) +func ChallengeNames(c Circuit, schedule constraint.GkrProvingSchedule, logNbInstances int, prefix string) []string { + var challenges []string - // output wire claims + // First challenge: fC.0 ... fC.(n-1) firstChallengePrefix := prefix + "fC." for j := 0; j < logNbInstances; j++ { - challenges[j] = firstChallengePrefix + nums[j] + challenges = append(challenges, firstChallengePrefix+strconv.Itoa(j)) } - j := logNbInstances - for i := len(c) - 1; i >= 0; i-- { - if c[i].NoProof() { + + // Per sumcheck level (in reverse order to match Verify iteration) + for levelI := len(schedule) - 1; levelI >= 0; levelI-- { + s, ok := schedule[levelI].(constraint.GkrSumcheckLevel) + if !ok { continue } - wirePrefix := prefix + "w" + nums[i] + "." + levelPrefix := prefix + "l" + strconv.Itoa(levelI) + "." - if c[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "fold" - j++ + if gkrcore.NbClaims(s) >= 2 { + challenges = append(challenges, levelPrefix+"fold") } - partialSumPrefix := wirePrefix + "pSP." + pSPPrefix := levelPrefix + "pSP." for k := 0; k < logNbInstances; k++ { - challenges[j] = partialSumPrefix + nums[k] - j++ + challenges = append(challenges, pSPPrefix+strconv.Itoa(k)) } } + return challenges } @@ -272,52 +192,95 @@ func getChallenges(transcript *fiatshamir.Transcript, names []string) (challenge return } -// Verify the consistency of the claimed output with the claimed input -// Unlike in Prove, the assignment argument need not be complete -func Verify(api frontend.API, c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { - o, err := setup(api, c, assignment, transcriptSettings, options...) +// Verify the consistency of the claimed output with the claimed input. +// Unlike in Prove, the assignment argument need not be complete. +func Verify(api frontend.API, c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { + o, err := setup(api, c, schedule, assignment, transcriptSettings, options...) if err != nil { return err } - claims := newClaimsManager(c, assignment) + evalPositions := gkrcore.BuildFinalEvalPositions(schedule, c) + levelPoints := make([][]frontend.Variable, len(schedule)+1) + initialChallengeI := len(schedule) var firstChallenge []frontend.Variable firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) if err != nil { return err } + levelPoints[initialChallengeI] = firstChallenge - wirePrefix := o.transcriptPrefix + "w" var baseChallenge []frontend.Variable - for i := len(c) - 1; i >= 0; i-- { - wire := c[i] + for levelI := len(schedule) - 1; levelI >= 0; levelI-- { + switch s := schedule[levelI].(type) { + case constraint.GkrSkipLevel: + levelPoints[levelI] = levelPoints[s.ClaimSources[0]] + + case constraint.GkrSumcheckLevel: + nbClaims := gkrcore.NbClaims(s) + levelPrefix := o.transcriptPrefix + "l" + strconv.Itoa(levelI) + "." + + foldingCoeff := frontend.Variable(0) + if nbClaims >= 2 { + foldName := levelPrefix + "fold" + if err = o.transcript.Bind(foldName, baseChallenge); err != nil { + return err + } + if foldingCoeff, err = o.transcript.ComputeChallenge(foldName); err != nil { + return err + } + baseChallenge = nil + } - if wire.IsOutput() { - claims.add(i, firstChallenge, assignment[i].Evaluate(api, firstChallenge)) - } + // Collect y-values + var ys []frontend.Variable + for _, group := range s { + for _, wI := range group.Wires { + evalI := 0 + for _, src := range group.ClaimSources { + var y frontend.Variable + if src == initialChallengeI { + y = assignment[wI].Evaluate(api, levelPoints[src]) + } else { + y = proof[src].FinalEvalProof[evalPositions[wI][evalI]] + evalI++ + } + ys = append(ys, y) + } + } + } + + ysPoly := polynomial.Polynomial(ys) + claimedSum := ysPoly.Eval(api, foldingCoeff) + + lazyClaims := &zeroCheckLazyClaims{ + foldingCoeff: foldingCoeff, + levelI: levelI, + schedule: schedule, + circuit: c, + assignment: assignment, + levelPoints: levelPoints, + evalPositions: evalPositions, + proof: proof, + nbVars: o.nbVars, + } - proofW := proof[i] - claim := claims.getLazyClaim(i) - if wire.NoProof() { // input wires with one claim only - // make sure the proof is empty - if len(proofW.FinalEvalProof) != 0 || len(proofW.PartialSumPolys) != 0 { - return errors.New("no proof allowed for input wire with a single claim") + var scBaseChallenges []frontend.Variable + if nbClaims < 2 { + scBaseChallenges = baseChallenge } - if wire.NbClaims() == 1 { // input wire - // simply evaluate and see if it matches - evaluation := assignment[i].Evaluate(api, claim.evaluationPoints[0]) - api.AssertIsEqual(claim.claimedEvaluations[0], evaluation) + if err = verifySumcheck( + api, lazyClaims, proof[levelI], claimedSum, + gkrcore.Degree(s, c), + fiatshamir.WithTranscript(o.transcript, levelPrefix, scBaseChallenges...), + ); err != nil { + return fmt.Errorf("sumcheck proof rejected at level %d: %v", levelI, err) } - } else if err = verifySumcheck( - api, claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { - baseChallenge = proofW.FinalEvalProof - } else { - return err + + baseChallenge = proof[levelI].FinalEvalProof } - claims.deleteClaim(i) } return nil } @@ -339,27 +302,35 @@ func (p Proof) Serialize() []frontend.Variable { res = append(res, p[i].FinalEvalProof...) } if len(res) != size { - panic("bug") // TODO: Remove + panic("bug") } return res } // ComputeLogNbInstances derives n such that the number of instances is 2ⁿ -// from the size of the proof and the circuit structure. -// The number actually computed is that of rounds in each ZeroCheck instance, which is equal -// to the desired result. -// It is used in proof deserialization. -func ComputeLogNbInstances(circuit Circuit, serializedProofLen int) int { - partialEvalElemsPerVar := 0 - for _, w := range circuit { - partialEvalElemsPerVar += w.ZeroCheckDegree() - serializedProofLen -= w.NbUniqueOutputs +// from the size of the proof and the circuit/schedule structure. +func ComputeLogNbInstances(circuit Circuit, schedule constraint.GkrProvingSchedule, serializedProofLen int) int { + perVar := 0 + for _, step := range schedule { + s, ok := step.(constraint.GkrSumcheckLevel) + if !ok { + continue + } + perVar += gkrcore.Degree(s, circuit) + serializedProofLen -= len(gkrcore.UniqueGateInputs(s, circuit)) } - res := serializedProofLen / partialEvalElemsPerVar - if res*partialEvalElemsPerVar != serializedProofLen { - panic("cannot compute logNbInstances") + if perVar == 0 { + if serializedProofLen == 0 { + return -1 + } + } else { + res := serializedProofLen / perVar + if res*perVar == serializedProofLen { + return res + } } - return res + + panic("cannot compute logNbInstances") } type variablesReader []frontend.Variable @@ -374,19 +345,22 @@ func (r *variablesReader) hasNextN(n int) bool { return len(*r) >= n } -func DeserializeProof(circuit Circuit, serializedProof []frontend.Variable) (Proof, error) { - proof := make(Proof, len(circuit)) - logNbInstances := ComputeLogNbInstances(circuit, len(serializedProof)) +func DeserializeProof(circuit Circuit, schedule constraint.GkrProvingSchedule, serializedProof []frontend.Variable) (Proof, error) { + proof := make(Proof, len(schedule)) + logNbInstances := ComputeLogNbInstances(circuit, schedule, len(serializedProof)) reader := variablesReader(serializedProof) - for i, wI := range circuit { - if !wI.NoProof() { - proof[i].PartialSumPolys = make([]polynomial.Polynomial, logNbInstances) - for j := range proof[i].PartialSumPolys { - proof[i].PartialSumPolys[j] = reader.nextN(wI.ZeroCheckDegree()) - } + for levelI, step := range schedule { + s, ok := step.(constraint.GkrSumcheckLevel) + if !ok { + continue + } + degree := gkrcore.Degree(s, circuit) + proof[levelI].PartialSumPolys = make([]polynomial.Polynomial, logNbInstances) + for j := range proof[levelI].PartialSumPolys { + proof[levelI].PartialSumPolys[j] = reader.nextN(degree) } - proof[i].FinalEvalProof = reader.nextN(wI.NbUniqueInputs()) + proof[levelI].FinalEvalProof = reader.nextN(len(gkrcore.UniqueGateInputs(s, circuit))) } if reader.hasNextN(1) { return nil, fmt.Errorf("proof too long: expected %d encountered %d", len(serializedProof)-len(reader), len(serializedProof)) diff --git a/internal/gkr/gkr_test.go b/internal/gkr/gkr_test.go index 6b0ecd1b67..916764ffe9 100644 --- a/internal/gkr/gkr_test.go +++ b/internal/gkr/gkr_test.go @@ -10,7 +10,9 @@ import ( "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/internal/gkr/gkrcore" "github.com/consensys/gnark/internal/gkr/gkrtesting" fiatshamir "github.com/consensys/gnark/std/fiat-shamir" "github.com/consensys/gnark/std/hash" @@ -114,7 +116,7 @@ func (c *GkrVerifierCircuit) Define(api frontend.API) error { return err } - if proof, err = DeserializeProof(testCase.Circuit, c.SerializedProof); err != nil { + if proof, err = DeserializeProof(testCase.Circuit, testCase.Schedule, c.SerializedProof); err != nil { return err } assignment := makeInOutAssignment(testCase.Circuit, c.Input, c.Output) @@ -128,20 +130,18 @@ func (c *GkrVerifierCircuit) Define(api frontend.API) error { } } - return Verify(api, testCase.Circuit, assignment, proof, fiatshamir.WithHash(hsh)) + return Verify(api, testCase.Circuit, testCase.Schedule, assignment, proof, fiatshamir.WithHash(hsh)) } func makeInOutAssignment(c Circuit, inputValues [][]frontend.Variable, outputValues [][]frontend.Variable) WireAssignment { res := make(WireAssignment, len(c)) - inI, outI := 0, 0 - for i := range c { - if c[i].IsInput() { - res[i] = inputValues[inI] - inI++ - } else if c[i].IsOutput() { - res[i] = outputValues[outI] - outI++ - } + inputs := c.Inputs() + outputs := c.Outputs() + for i, wI := range inputs { + res[wI] = inputValues[i] + } + for i, wI := range outputs { + res[wI] = outputValues[i] } return res } @@ -153,12 +153,13 @@ func fillWithBlanks(slice [][]frontend.Variable, size int) { } type TestCase struct { - Circuit Circuit - Hash HashDescription - Proof Proof - Input [][]frontend.Variable - Output [][]frontend.Variable - Name string + Circuit Circuit + Schedule constraint.GkrProvingSchedule + Hash HashDescription + Proof Proof + Input [][]frontend.Variable + Output [][]frontend.Variable + Name string } type TestCaseInfo struct { Hash HashDescription `json:"hash"` @@ -188,7 +189,14 @@ func getTestCase(path string) (*TestCase, error) { return nil, err } - _, cse.Circuit = cache.GetCircuit(filepath.Join(dir, info.Circuit)) + serializableCircuit, gadgetCircuit := cache.GetCircuit(filepath.Join(dir, info.Circuit)) + cse.Circuit = gadgetCircuit + + schedule, schedErr := gkrcore.DefaultProvingSchedule(serializableCircuit) + if schedErr != nil { + return nil, schedErr + } + cse.Schedule = schedule cse.Proof = unmarshalProof(info.Proof) @@ -241,7 +249,7 @@ func TestLogNbInstances(t *testing.T) { testCase, err := getTestCase(path) assert.NoError(t, err) serializedProof := testCase.Proof.Serialize() - logNbInstances := ComputeLogNbInstances(testCase.Circuit, len(serializedProof)) + logNbInstances := ComputeLogNbInstances(testCase.Circuit, testCase.Schedule, len(serializedProof)) assert.Equal(t, 1, logNbInstances) } } diff --git a/internal/gkr/sumcheck.go b/internal/gkr/sumcheck.go index e963509686..b0c46356e3 100644 --- a/internal/gkr/sumcheck.go +++ b/internal/gkr/sumcheck.go @@ -13,11 +13,9 @@ import ( // sumcheckLazyClaims is the Claims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. type sumcheckLazyClaims interface { - claimsNum() int // claimsNum = m - varsNum() int // varsNum = n - foldedSum(api frontend.API, a frontend.Variable) frontend.Variable // foldedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - degree(i int) int // degree of the total claim in the i'th variable - verifyFinalEval(api frontend.API, r []frontend.Variable, foldingCoeff, purportedValue frontend.Variable, proof []frontend.Variable) error + varsNum() int + degree(i int) int + verifyFinalEval(api frontend.API, r []frontend.Variable, purportedValue frontend.Variable, proof []frontend.Variable) error } // sumcheckProof of a multi-sumcheck statement. @@ -26,18 +24,11 @@ type sumcheckProof struct { FinalEvalProof []frontend.Variable } -func setupTranscript(api frontend.API, claimsNum int, varsNum int, settings *fiatshamir.Settings) ([]string, error) { - numChallenges := varsNum - if claimsNum >= 2 { - numChallenges++ - } - challengeNames := make([]string, numChallenges) - if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "fold" - } +func setupTranscript(api frontend.API, varsNum int, settings *fiatshamir.Settings) ([]string, error) { + challengeNames := make([]string, varsNum) prefix := settings.Prefix + "pSP." for i := 0; i < varsNum; i++ { - challengeNames[i+numChallenges-varsNum] = prefix + strconv.Itoa(i) + challengeNames[i] = prefix + strconv.Itoa(i) } if settings.Transcript == nil { settings.Transcript = fiatshamir.NewTranscript(api, settings.Hash, challengeNames) @@ -57,52 +48,34 @@ func next(transcript *fiatshamir.Transcript, bindings []frontend.Variable, remai return res, err } -func verifySumcheck(api frontend.API, claims sumcheckLazyClaims, proof sumcheckProof, transcriptSettings fiatshamir.Settings) error { +func verifySumcheck(api frontend.API, claims sumcheckLazyClaims, proof sumcheckProof, claimedSum frontend.Variable, degree int, transcriptSettings fiatshamir.Settings) error { - remainingChallengeNames, err := setupTranscript(api, claims.claimsNum(), claims.varsNum(), &transcriptSettings) + remainingChallengeNames, err := setupTranscript(api, claims.varsNum(), &transcriptSettings) transcript := transcriptSettings.Transcript if err != nil { return err } - var foldingCoeff frontend.Variable - - if claims.claimsNum() >= 2 { - if foldingCoeff, err = next(transcript, []frontend.Variable{}, &remainingChallengeNames); err != nil { - return err - } - } - r := make([]frontend.Variable, claims.varsNum()) - // Just so that there is enough room for gJ to be reused - maxDegree := claims.degree(0) - for j := 1; j < claims.varsNum(); j++ { - if d := claims.degree(j); d > maxDegree { - maxDegree = d - } - } - - gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.varsNum() - gJR := claims.foldedSum(api, foldingCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) + gJ := make(polynomial.Polynomial, degree+1) + gJR := claimedSum for j := 0; j < claims.varsNum(); j++ { - partialSumPoly := proof.PartialSumPolys[j] //proof.PartialSumPolys(j) - if len(partialSumPoly) != claims.degree(j) { - return errors.New("malformed proof") //Malformed proof + partialSumPoly := proof.PartialSumPolys[j] + if len(partialSumPoly) != degree { + return errors.New("malformed proof") } copy(gJ[1:], partialSumPoly) gJ[0] = api.Sub(gJR, partialSumPoly[0]) // Requirement that gⱼ(0) + gⱼ(1) = gⱼ₋₁(r) - // gJ is ready - //Prepare for the next iteration if r[j], err = next(transcript, proof.PartialSumPolys[j], &remainingChallengeNames); err != nil { return err } - gJR = polynomial.InterpolateLDE(api, r[j], gJ[:(claims.degree(j)+1)]) + gJR = polynomial.InterpolateLDE(api, r[j], gJ[:(degree+1)]) } - return claims.verifyFinalEval(api, r, foldingCoeff, gJR, proof.FinalEvalProof) + return claims.verifyFinalEval(api, r, gJR, proof.FinalEvalProof) } diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 9c5f6f8f7b..a3ed22b540 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" gadget "github.com/consensys/gnark/internal/gkr" gkrbls12377 "github.com/consensys/gnark/internal/gkr/bls12-377" @@ -26,6 +27,7 @@ type InitialChallengeGetter func() []frontend.Variable // Circuit represents a GKR circuit. type Circuit struct { circuit gkrcore.GadgetCircuit + schedule constraint.GkrProvingSchedule gates []gkrcore.GateBytecode assignments gadget.WireAssignment getInitialChallenges InitialChallengeGetter // optional getter for the initial Fiat-Shamir challenge @@ -80,8 +82,14 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C return nil, err } + schedule, err := gkrcore.DefaultProvingSchedule(serializableCircuit) + if err != nil { + return nil, fmt.Errorf("failed to compute proving schedule: %w", err) + } + res := Circuit{ circuit: gadgetCircuit, + schedule: schedule, assignments: make(gadget.WireAssignment, len(api.circuit)), api: api.parentApi, hashName: fiatshamirHashName, @@ -89,13 +97,13 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C switch curveID { case ecc.BN254: - res.blueprints = gkrbn254.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) + res.blueprints = gkrbn254.NewBlueprints(serializableCircuit, schedule, fiatshamirHashName, compiler) case ecc.BLS12_377: - res.blueprints = gkrbls12377.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) + res.blueprints = gkrbls12377.NewBlueprints(serializableCircuit, schedule, fiatshamirHashName, compiler) case ecc.BLS12_381: - res.blueprints = gkrbls12381.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) + res.blueprints = gkrbls12381.NewBlueprints(serializableCircuit, schedule, fiatshamirHashName, compiler) case ecc.BW6_761: - res.blueprints = gkrbw6761.NewBlueprints(serializableCircuit, fiatshamirHashName, compiler) + res.blueprints = gkrbw6761.NewBlueprints(serializableCircuit, schedule, fiatshamirHashName, compiler) default: return nil, fmt.Errorf("unsupported curve: %s", curveID) } @@ -204,6 +212,10 @@ func (c *Circuit) finalize(api frontend.API) error { // if the circuit consists of only one instance, directly solve the circuit if len(c.assignments[c.ins[0]]) == 1 { + outputSet := make(map[int]bool, len(c.outs)) + for _, wI := range c.outs { + outputSet[int(wI)] = true + } gateIn := make([]frontend.Variable, c.circuit.MaxGateNbIn()) for wI, w := range c.circuit { if w.IsInput() { @@ -213,7 +225,7 @@ func (c *Circuit) finalize(api frontend.API) error { gateIn[inI] = c.assignments[inWI][0] // take the first (only) instance } res := w.Gate.Evaluate(gadget.FrontendAPIWrapper{API: api}, gateIn[:len(w.Inputs)]...) - if w.IsOutput() { + if outputSet[wI] { api.AssertIsEqual(res, c.assignments[wI][0]) } else { c.assignments[wI] = append(c.assignments[wI], res) @@ -273,7 +285,7 @@ func (c *Circuit) verify(api frontend.API, circuit gkrcore.GadgetCircuit, initia err error ) - if proof, err = gadget.DeserializeProof(circuit, proofSerialized); err != nil { + if proof, err = gadget.DeserializeProof(circuit, c.schedule, proofSerialized); err != nil { return err } @@ -282,7 +294,7 @@ func (c *Circuit) verify(api frontend.API, circuit gkrcore.GadgetCircuit, initia return err } - return gadget.Verify(api, circuit, c.assignments, proof, fiatshamir.WithHash(hsh, initialChallenges...)) + return gadget.Verify(api, circuit, c.schedule, c.assignments, proof, fiatshamir.WithHash(hsh, initialChallenges...)) } // GetValue is a debugging utility returning the value of variable v at instance i. From 2766c39d1b330cc66a31fe85daef51d88d77b54c Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Mar 2026 18:41:26 -0500 Subject: [PATCH 229/251] feat: claimSource with extra claim index field --- constraint/gkr.go | 50 +++++-- internal/gkr/gkrcore/schedule.go | 202 +++++++++++--------------- internal/gkr/gkrcore/schedule_test.go | 156 ++++++++++---------- internal/gkr/gkrtesting/gkrtesting.go | 20 ++- 4 files changed, 219 insertions(+), 209 deletions(-) diff --git a/constraint/gkr.go b/constraint/gkr.go index 650d0efe04..e528cb8d2f 100644 --- a/constraint/gkr.go +++ b/constraint/gkr.go @@ -1,22 +1,29 @@ package constraint type ( - // GkrClaimGroup represents a set of wires with their claim sources. - // It is agnostic of the protocol - it only describes which wires have claims - // from which sources, not what to do with them. - // - // ClaimSources contains step indices that produced evaluation claims for these wires. - // The special value len(schedule) is a virtual step index representing the verifier's - // initial challenge (rho). It is never an actual index into the schedule slice. + // GkrClaimSource identifies an incoming evaluation claim for a wire. + // Level is the level that produced the claim. + // OutgoingClaimIndex selects which of that level's outgoing evaluation points is referenced; + // always 0 for SumcheckLevels, 0..M-1 for SkipLevels with M inherited evaluation points. + // The initial verifier challenge is represented as {Level: len(schedule), OutgoingClaimIndex: 0}. + GkrClaimSource struct { + Level int `json:"level"` + OutgoingClaimIndex int `json:"outgoingClaimIndex"` + } + + // GkrClaimGroup represents a set of wires sharing identical claim sources. + // finalEvalProof index = pos(wire, srcLevel) * NbOutgoingEvalPoints(srcLevel) + ClaimSources[claimI].OutgoingClaimIndex, + // where pos(wire, srcLevel) is the wire's position in srcLevel's UniqueGateInputs list. GkrClaimGroup struct { - Wires []int `json:"wires"` - ClaimSources []int `json:"claimSourcesCache"` // step indices; len(schedule) = initial challenge + Wires []int `json:"wires"` + ClaimSources []GkrClaimSource `json:"claimSources"` } - // GkrProvingLevel is a sealed interface for a single level in the proving schedule. - // A level is either a GkrSkipLevel or a GkrSumcheckLevel. + // GkrProvingLevel is a single level in the proving schedule. GkrProvingLevel interface { - gkrProvingLevel() // marker method restricting implementations to GkrSkipLevel and GkrSumcheckLevel + NbOutgoingEvalPoints() int + NbClaims() int + ClaimGroups() []GkrClaimGroup } // GkrSkipLevel represents a level where zerocheck is skipped. @@ -33,5 +40,20 @@ type ( GkrProvingSchedule []GkrProvingLevel ) -func (GkrSumcheckLevel) gkrProvingLevel() {} -func (GkrSkipLevel) gkrProvingLevel() {} +func (g GkrClaimGroup) NbClaims() int { return len(g.Wires) * len(g.ClaimSources) } + +func (l GkrSumcheckLevel) NbOutgoingEvalPoints() int { return 1 } +func (l GkrSumcheckLevel) NbClaims() int { + n := 0 + for _, g := range l { + n += len(g.Wires) * len(g.ClaimSources) + } + return n +} +func (l GkrSumcheckLevel) ClaimGroups() []GkrClaimGroup { return l } + +func (l GkrSkipLevel) NbOutgoingEvalPoints() int { return len(l.ClaimSources) } +func (l GkrSkipLevel) NbClaims() int { + return GkrClaimGroup(l).NbClaims() +} +func (l GkrSkipLevel) ClaimGroups() []GkrClaimGroup { return []GkrClaimGroup{GkrClaimGroup(l)} } diff --git a/internal/gkr/gkrcore/schedule.go b/internal/gkr/gkrcore/schedule.go index d9d413692f..929d813325 100644 --- a/internal/gkr/gkrcore/schedule.go +++ b/internal/gkr/gkrcore/schedule.go @@ -7,28 +7,13 @@ import ( "github.com/consensys/gnark/constraint" ) -// UniqueGateInputs returns the unique gate input wire indices for all wires in the level, -// deduplicated in batch-then-wire-then-input order (first occurrence wins). -// For input wires (no gate inputs), the wire itself is returned. -func UniqueGateInputs[G any](level constraint.GkrProvingLevel, c Circuit[G]) []int { - uniqueInputs, _ := InputMapping(level, c) - return uniqueInputs -} - -// InputMapping computes the unique gate inputs and the per-wire index mapping in a single pass. -// inputIndices[wireInLevel][gateInputJ] → position in uniqueInputs. -// For input wires: inputIndices[i] = []int{position of wI in uniqueInputs}. -func InputMapping[G any](level constraint.GkrProvingLevel, c Circuit[G]) (uniqueInputs []int, inputIndices [][]int) { - var groups []constraint.GkrClaimGroup - switch l := level.(type) { - case constraint.GkrSumcheckLevel: - groups = l - case constraint.GkrSkipLevel: - groups = []constraint.GkrClaimGroup{constraint.GkrClaimGroup(l)} - } - +// InputMapping returns as uniqueInputs the deduplicated list of inputs to the level, +// and as inputIndices for every wire in the level the list of positions for each of its +// inputs in the uniqueInputs list. +// Input wires of the circuit are considered self-input, as a convenience for the sumcheck protocol. +func (c Circuit[G]) InputMapping(level constraint.GkrProvingLevel) (uniqueInputs []int, inputIndices [][]int) { seen := make(map[int]int) // wire index → position in uniqueInputs - for _, group := range groups { + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := c[wI] inputs := wire.Inputs @@ -52,24 +37,17 @@ func InputMapping[G any](level constraint.GkrProvingLevel, c Circuit[G]) (unique return } -// ReduplicateInputs expands unique evaluations to per-wire gate input evaluation lists. -func ReduplicateInputs[G, F any](level constraint.GkrProvingLevel, c Circuit[G], uniqueEvals []F) [][]F { - _, inputIndices := InputMapping(level, c) - result := make([][]F, len(inputIndices)) - for wireInLevel := range inputIndices { - wireInputs := make([]F, len(inputIndices[wireInLevel])) - for gateInputJ, uniqueI := range inputIndices[wireInLevel] { - wireInputs[gateInputJ] = uniqueEvals[uniqueI] - } - result[wireInLevel] = wireInputs - } - return result +// UniqueGateInputs returns the unique gate input wire indices for all wires in the level, +// deduplicated in batch-then-wire-then-input order (first occurrence wins). +// For circuit input wires (no gate inputs), the wire itself is returned. +func (c Circuit[G]) UniqueGateInputs(level constraint.GkrProvingLevel) []int { + uniqueInputs, _ := c.InputMapping(level) + return uniqueInputs } -// Degree returns max(Gate.Degree across all wires in all groups) + 1. -func Degree[G any](level constraint.GkrSumcheckLevel, c Circuit[G]) int { +func (c Circuit[G]) ZeroCheckDegree(level constraint.GkrSumcheckLevel) int { maxDeg := 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { w := &c[wI] curr := 1 @@ -82,53 +60,60 @@ func Degree[G any](level constraint.GkrSumcheckLevel, c Circuit[G]) int { return maxDeg + 1 } -// NbClaims returns the total number of claims in a sumcheck level. -func NbClaims(level constraint.GkrSumcheckLevel) int { - n := 0 - for _, g := range level { - n += len(g.Wires) * len(g.ClaimSources) - } - return n -} - // ProofSize returns the total number of field elements in a GKR proof. -func ProofSize[G any](schedule constraint.GkrProvingSchedule, c Circuit[G], logNbInstances int) int { +func (c Circuit[G]) ProofSize(schedule constraint.GkrProvingSchedule, logNbInstances int) int { size := 0 - for _, step := range schedule { - s, ok := step.(constraint.GkrSumcheckLevel) - if !ok { - continue + for _, level := range schedule { + // For every outgoing claim and unique input wire, there will be + // an outgoing evaluation claim included in finalEvalProof. + size += len(c.UniqueGateInputs(level)) * level.NbOutgoingEvalPoints() + if sc, ok := level.(constraint.GkrSumcheckLevel); ok { + // ZeroCheckDegree is the degree of each sumcheck polynomial. + // logNbInstances is the number of rounds in each sumcheck. + size += c.ZeroCheckDegree(sc) * logNbInstances } - size += logNbInstances * Degree(s, c) // partialSumPolys - size += len(UniqueGateInputs(s, c)) // finalEvalProof } return size } +// ReduplicateInputs expands unique evaluations to per-wire gate input evaluation lists. +func ReduplicateInputs[F any, G any](level constraint.GkrProvingLevel, c Circuit[G], uniqueEvals []F) [][]F { + _, inputIndices := c.InputMapping(level) + result := make([][]F, len(inputIndices)) + for wireInLevel := range inputIndices { + wireInputs := make([]F, len(inputIndices[wireInLevel])) + for gateInputJ, uniqueI := range inputIndices[wireInLevel] { + wireInputs[gateInputJ] = uniqueEvals[uniqueI] + } + result[wireInLevel] = wireInputs + } + return result +} + // scheduleBuilder accumulates topology and per-wire claim sources while a schedule is being built. // Steps are appended in out-to-in (topological) order and reversed by finalize. -// Claim source indices are stored as their index in the pre-reversal levels slice, with -1 as the -// sentinel for the initial challenge. finalize maps each src to its final absolute index via -// n-1-src, where n = len(levels), so -1 -> n (initial challenge) and i -> n-1-i (real levels). -type scheduleBuilder struct { - circuit SerializableCircuit +// Claim source level values are stored as their index in the levels slice, with -1 as the +// sentinel for the initial challenge. finalize will map each src.Level to its final absolute index via +// n-1-src.level, where n = len(levels), so -1 → n (initial challenge) and i → n-1-i (real levels). +type scheduleBuilder[G any] struct { + circuit Circuit[G] wireOutputs [][]int // wireOutputs[i] indices of wires that wire i feeds into, in increasing order and deduplicated. wireLevels []int // wireLevels[i] which level wire i has been put in wireProcessed []bool - claimSourcesCache [][]int // claimSourcesCache[i] is the result of claimSourcesCache(i), or nil if not yet computed. + claimSourcesCache [][]constraint.GkrClaimSource // claimSourcesCache[i] is the result of claimSources(i), or nil if not yet computed. firstUnprocessedWire int levels constraint.GkrProvingSchedule } // newScheduleBuilder initialises a builder for the given circuit. // It computes the outputs inverse-adjacency list. -func newScheduleBuilder(c SerializableCircuit) scheduleBuilder { - b := scheduleBuilder{ +func newScheduleBuilder[G any](c Circuit[G]) scheduleBuilder[G] { + b := scheduleBuilder[G]{ circuit: c, wireOutputs: make([][]int, len(c)), wireLevels: make([]int, len(c)), wireProcessed: make([]bool, len(c)), - claimSourcesCache: make([][]int, len(c)), + claimSourcesCache: make([][]constraint.GkrClaimSource, len(c)), firstUnprocessedWire: len(c) - 1, } seen := make(map[int]bool, len(c)) @@ -147,7 +132,7 @@ func newScheduleBuilder(c SerializableCircuit) scheduleBuilder { // addSumcheckLevel appends a GkrSumcheckLevel to the schedule. Each batch is a set of wire indices // to be proven together in a single zerocheck; all wires in a batch must share the same claim sources. // All wires across all batches must be ready. -func (b *scheduleBuilder) addSumcheckLevel(batches ...[]int) error { +func (b *scheduleBuilder[G]) addSumcheckLevel(batches ...[]int) error { claimGroups, err := b.buildClaimGroups(batches) if err != nil { return err @@ -158,7 +143,7 @@ func (b *scheduleBuilder) addSumcheckLevel(batches ...[]int) error { // addSkipLevel appends a GkrSkipLevel to the schedule for a single set of wire indices. // All wires in the batch must share the same claim sources and must be ready. -func (b *scheduleBuilder) addSkipLevel(wireIndices []int) error { +func (b *scheduleBuilder[G]) addSkipLevel(wireIndices []int) error { claimGroups, err := b.buildClaimGroups([][]int{wireIndices}) if err != nil { return err @@ -170,11 +155,11 @@ func (b *scheduleBuilder) addSkipLevel(wireIndices []int) error { // buildClaimGroups processes a set of batches, validates claim source consistency within each // batch, updates wireLevels and wireProcessed, and returns the resulting GkrClaimGroups. // Every ClaimSources slice is sorted. The user may reorder it to optimize eq handling. -func (b *scheduleBuilder) buildClaimGroups(batches [][]int) ([]constraint.GkrClaimGroup, error) { +func (b *scheduleBuilder[G]) buildClaimGroups(batches [][]int) ([]constraint.GkrClaimGroup, error) { levelIdx := len(b.levels) claimGroups := make([]constraint.GkrClaimGroup, len(batches)) for i, wireIndices := range batches { - var claimSources []int + var claimSources []constraint.GkrClaimSource for j, wI := range wireIndices { wireClaims, ok := b.claimSources(wI) if !ok { @@ -200,7 +185,7 @@ func (b *scheduleBuilder) buildClaimGroups(batches [][]int) ([]constraint.GkrCla // nextReady returns the highest wire index in the contiguous ready suffix starting at // firstUnprocessedWire, along with each wire's claim sources in wire-index order. // Returns firstUnprocessedWire, nil if no wires are ready. -func (b *scheduleBuilder) nextReady() (highestWireI int, sources [][]int) { +func (b *scheduleBuilder[G]) nextReady() (highestWireI int, sources [][]constraint.GkrClaimSource) { for lowestWireI := b.firstUnprocessedWire; lowestWireI >= 0; lowestWireI-- { if b.wireProcessed[lowestWireI] { break @@ -216,36 +201,49 @@ func (b *scheduleBuilder) nextReady() (highestWireI int, sources [][]int) { } // claimSources checks whether all consumers of wire wI have already been processed. -// If so, it returns the deduplicated sorted claim sources for wI and true. +// If so, it returns the deduplicated claim sources for wI and true. // If not, it returns nil and false. Results are cached. -func (b *scheduleBuilder) claimSources(wI int) ([]int, bool) { +// SkipLevels are proper claim targets: a wire feeding into a SkipLevel L with M inherited +// evaluation points gets M claim sources {L, 0}, {L, 1}, ..., {L, M-1}. +func (b *scheduleBuilder[G]) claimSources(wI int) ([]constraint.GkrClaimSource, bool) { if b.claimSourcesCache[wI] != nil { return b.claimSourcesCache[wI], true } - var wireClaims []int + var wireClaims []constraint.GkrClaimSource if b.circuit[wI].Exported || len(b.wireOutputs[wI]) == 0 { - wireClaims = append(wireClaims, -1) + wireClaims = append(wireClaims, constraint.GkrClaimSource{Level: -1, OutgoingClaimIndex: 0}) } for _, consumerWI := range b.wireOutputs[wI] { if !b.wireProcessed[consumerWI] { return nil, false } consumerLevel := b.wireLevels[consumerWI] - if skip, ok := b.levels[consumerLevel].(constraint.GkrSkipLevel); ok { - wireClaims = append(wireClaims, skip.ClaimSources...) + if _, isSkip := b.levels[consumerLevel].(constraint.GkrSkipLevel); isSkip { + // SkipLevel inherits M evaluation points from its own claim sources. + M := b.levels[consumerLevel].NbOutgoingEvalPoints() + for k := range M { + wireClaims = append(wireClaims, constraint.GkrClaimSource{Level: consumerLevel, OutgoingClaimIndex: k}) + } } else { - wireClaims = append(wireClaims, consumerLevel) + wireClaims = append(wireClaims, constraint.GkrClaimSource{Level: consumerLevel, OutgoingClaimIndex: 0}) + } + } + // Deduplicate while preserving order. + seen := make(map[constraint.GkrClaimSource]bool, len(wireClaims)) + out := wireClaims[:0] + for _, cs := range wireClaims { + if !seen[cs] { + seen[cs] = true + out = append(out, cs) } } - slices.Sort(wireClaims) - wireClaims = slices.Compact(wireClaims) - b.claimSourcesCache[wI] = wireClaims - return wireClaims, true + b.claimSourcesCache[wI] = out + return out, true } -// finalize reverses the schedule into in-to-out order and returns the completed schedule. -// It errors if any wire has not been processed, meaning the caller did not schedule a layer for every wire. -func (b *scheduleBuilder) finalize() (constraint.GkrProvingSchedule, error) { +// finalize reverses the schedule into in-to-out order and fixes up Level indices in all +// ClaimSources. It errors if any wire has not been processed. +func (b *scheduleBuilder[G]) finalize() (constraint.GkrProvingSchedule, error) { for i, processed := range b.wireProcessed { if !processed { return nil, fmt.Errorf("wire %d has not been processed", i) @@ -254,54 +252,30 @@ func (b *scheduleBuilder) finalize() (constraint.GkrProvingSchedule, error) { n := len(b.levels) slices.Reverse(b.levels) - // Fix up ClaimSources in every GkrClaimGroup: pre-reversal index src maps to n-1-src, + // Fix up ClaimSources: pre-reversal Level index src maps to n-1-src, // and the initial-challenge sentinel -1 maps to n. - for _, layer := range b.levels { - switch l := layer.(type) { - case constraint.GkrSkipLevel: - mirror(l.ClaimSources, n) - case constraint.GkrSumcheckLevel: - for _, cg := range l { - mirror(cg.ClaimSources, n) - } + for _, level := range b.levels { + for _, group := range level.ClaimGroups() { + mirrorClaimSources(group.ClaimSources, n) } } + return b.levels, nil } -// mirror maps each pre-reversal index src in-place to its post-reversal absolute index n-1-src. -// The initial-challenge sentinel -1 maps to n. -func mirror(s []int, n int) { +// mirrorClaimSources maps each pre-reversal Level index src.Level in-place to its post-reversal +// absolute index n-1-src.Level. The initial-challenge sentinel -1 maps to n. +func mirrorClaimSources(s []constraint.GkrClaimSource, n int) { n-- for j := range s { - s[j] = n - s[j] - } -} - -// BuildFinalEvalPositions returns, for each wire, its position in each source level's -// UniqueGateInputs. Indexed as [wireI][evalI] → position in finalEvalProof. -// Claim sources referencing the initial challenge (index len(schedule)) have no entry; -// they are always first in descending ClaimSources and handled separately by the verifier. -// NB! This function assumes all ClaimSources slices are in decreasing order. -func BuildFinalEvalPositions[G any](schedule constraint.GkrProvingSchedule, c Circuit[G]) [][]int { - positions := make([][]int, len(c)) - for i := len(schedule) - 1; i >= 0; i-- { - sc, ok := schedule[i].(constraint.GkrSumcheckLevel) - if !ok { - continue - } - uniqueInputs := UniqueGateInputs(sc, c) - for j, wI := range uniqueInputs { - positions[wI] = append(positions[wI], j) - } + s[j].Level = n - s[j].Level } - return positions } // DefaultProvingSchedule generates a schedule that greedily batches input wires with the same // single claim source into the same GkrSkipLevel. Non-input wires, and input wires with multiple // claim sources, each get their own GkrSumcheckLevel. -func DefaultProvingSchedule(c SerializableCircuit) (constraint.GkrProvingSchedule, error) { +func DefaultProvingSchedule[G any](c Circuit[G]) (constraint.GkrProvingSchedule, error) { b := newScheduleBuilder(c) for b.firstUnprocessedWire >= 0 { diff --git a/internal/gkr/gkrcore/schedule_test.go b/internal/gkr/gkrcore/schedule_test.go index 3481726a1a..a99f55a032 100644 --- a/internal/gkr/gkrcore/schedule_test.go +++ b/internal/gkr/gkrcore/schedule_test.go @@ -12,88 +12,98 @@ import ( var scheduleTestCache = gkrtesting.NewCache(ecc.BN254.ScalarField()) +func TestDefaultProvingSchedule(t *testing.T) { + _, c := scheduleTestCache.Compile(t, gkrtesting.SingleMulGateCircuit()) + schedule, err := gkrcore.DefaultProvingSchedule(c) + require.NoError(t, err) + + // SingleMulGateCircuit: wires 0, 1 (inputs), 2 (mul gate with inputs 0, 1). + // UniqueGateInputs for level 2 (wire 2) = [0, 1]. + // 3 = len(schedule) = initial challenge sentinel. + require.Equal(t, constraint.GkrProvingSchedule{ + // Level 0: input wire 0, claimed by mul gate at level 2 (its position in UniqueGateInputs = 0) + constraint.GkrSkipLevel{Wires: []int{0}, ClaimSources: []constraint.GkrClaimSource{{Level: 2}}}, + // Level 1: input wire 1, claimed by mul gate at level 2 (position 1 in UniqueGateInputs) + constraint.GkrSkipLevel{Wires: []int{1}, ClaimSources: []constraint.GkrClaimSource{{Level: 2}}}, + // Level 2: mul gate output, claimed by initial challenge + constraint.GkrSumcheckLevel{{Wires: []int{2}, ClaimSources: []constraint.GkrClaimSource{{Level: 3}}}}, + }, schedule) +} + func TestDefaultProvingSchedulePoseidon2(t *testing.T) { _, c := scheduleTestCache.Compile(t, gkrtesting.Poseidon2Circuit(4, 2)) schedule, err := gkrcore.DefaultProvingSchedule(c) require.NoError(t, err) // Wire layout for Poseidon2Circuit(4, 2) — 25 wires total: - // 0, 1 inputs - // 2–5 full-round 0 (lin0, lin1, sBox0, sBox1) - // 6–9 full-round 1 (lin0, lin1, sBox0, sBox1) - // 10–12 partial-round 0 (lin0, lin1, sBox0) - // 13–15 partial-round 1 (lin0, lin1, sBox0) - // 16–19 full-round 2 (lin0, lin1, sBox0, sBox1) - // 20–23 full-round 3 (lin0, lin1, sBox0, sBox1) - // 24 feed-forward output + // 0, 1 inputs + // 2–5 full-round 0 (lin0=2, lin1=3, sBox0=4, sBox1=5) + // 6–9 full-round 1 (lin0=6, lin1=7, sBox0=8, sBox1=9) + // 10–12 partial-round 0 (lin0=10, lin1=11, sBox0=12) + // 13–15 partial-round 1 (lin0=13, lin1=14, sBox0=15) + // 16–19 full-round 2 (lin0=16, lin1=17, sBox0=18, sBox1=19) + // 20–23 full-round 3 (lin0=20, lin1=21, sBox0=22, sBox1=23) + // 24 feed-forward output (inputs: 22, 23, 1) // - // Claim source indices refer to levels (post-reversal); 17 = len(schedule) = initial challenge. + // SkipSumcheck levels (degree-1 gates or input wires with a single claim source) + // produce M outgoing eval points, one per inherited claim. + // SumcheckLevels always produce exactly 1 outgoing eval point (OCI=0). + // 17 = len(schedule) = initial challenge sentinel. // - // Linear wires (degree 1, single claim source) become SkipLevels. - // S-box wires (degree 2) become SumcheckLevels. - // The two s-boxes of a full round share identical claim sources and are batched together. - // Full-round 1 s-boxes (wires 8, 9) feed three downstream sumchecks: partial-round 0 s-box - // (level 8), partial-round 1 s-box (level 11), and full-round 2 s-boxes (level 13). - expected := constraint.GkrProvingSchedule{ - - // Level 0: input[0] — single claim source: full-round 0 s-boxes at level 3. - constraint.GkrSkipLevel{Wires: []int{0}, ClaimSources: []int{3}}, - // Level 1: input[1] — feeds full-round 0 s-boxes (level 3) and the feed-forward (level 17 = initial challenge). - constraint.GkrSkipLevel{Wires: []int{1}, ClaimSources: []int{17, 3}}, - - // Level 2: full-round 0 lin1+lin0 (degree 1, batched) — claim source: full-round 0 s-boxes at level 3. - constraint.GkrSkipLevel{Wires: []int{3, 2}, ClaimSources: []int{3}}, - // Level 3: full-round 0 sBox1+sBox0 (degree 2, batched) — claim source: full-round 1 s-boxes at level 5. - constraint.GkrSumcheckLevel{{Wires: []int{5, 4}, ClaimSources: []int{5}}}, - - // Level 4: full-round 1 lin1+lin0 (degree 1, batched) — claim source: full-round 1 s-boxes at level 5. - constraint.GkrSkipLevel{Wires: []int{7, 6}, ClaimSources: []int{5}}, - // Level 5: full-round 1 sBox1+sBox0 (degree 2, batched) — three downstream sumchecks. - constraint.GkrSumcheckLevel{{Wires: []int{9, 8}, ClaimSources: []int{13, 11, 8}}}, - - // Level 6: partial-round 0 lin0 (degree 1) — claim source: partial-round 0 s-box at level 8. - constraint.GkrSkipLevel{Wires: []int{10}, ClaimSources: []int{8}}, - // Level 7: partial-round 0 lin1 (degree 1) — feeds partial-round 1 s-box (level 11) and full-round 2 s-boxes (level 13). - constraint.GkrSkipLevel{Wires: []int{11}, ClaimSources: []int{13, 11}}, - // Level 8: partial-round 0 sBox0 (degree 2) — feeds partial-round 1 lin0+lin1. - constraint.GkrSumcheckLevel{{Wires: []int{12}, ClaimSources: []int{13, 11}}}, - - // Level 9: partial-round 1 lin0 (degree 1) — claim source: partial-round 1 s-box at level 11. - constraint.GkrSkipLevel{Wires: []int{13}, ClaimSources: []int{11}}, - // Level 10: partial-round 1 lin1 (degree 1) — claim source: full-round 2 s-boxes at level 13. - constraint.GkrSkipLevel{Wires: []int{14}, ClaimSources: []int{13}}, - // Level 11: partial-round 1 sBox0 (degree 2) — claim source: full-round 2 s-boxes at level 13. - constraint.GkrSumcheckLevel{{Wires: []int{15}, ClaimSources: []int{13}}}, - - // Level 12: full-round 2 lin1+lin0 (degree 1, batched) — claim source: full-round 2 s-boxes at level 13. - constraint.GkrSkipLevel{Wires: []int{17, 16}, ClaimSources: []int{13}}, - // Level 13: full-round 2 sBox1+sBox0 (degree 2, batched) — claim source: full-round 3 s-boxes at level 15. - constraint.GkrSumcheckLevel{{Wires: []int{19, 18}, ClaimSources: []int{15}}}, - - // Level 14: full-round 3 lin1+lin0 (degree 1, batched) — claim source: full-round 3 s-boxes at level 15. - constraint.GkrSkipLevel{Wires: []int{21, 20}, ClaimSources: []int{15}}, - // Level 15: full-round 3 sBox1+sBox0 (degree 2, batched) — claim source: initial challenge (17). - constraint.GkrSumcheckLevel{{Wires: []int{23, 22}, ClaimSources: []int{17}}}, - - // Level 16: feed-forward output (degree 1) — claim source: initial challenge (17). - constraint.GkrSkipLevel{Wires: []int{24}, ClaimSources: []int{17}}, - } - require.Equal(t, expected, schedule) -} + // Level 7 (wire 11, partial-round 0 lin1) has two claim sources (from levels 9 and 10), + // so it produces M=2 outgoing eval points. Wires feeding into level 7 therefore + // inherit two claim sources from it: {7,0} and {7,1}. + require.Equal(t, constraint.GkrProvingSchedule{ + // Level 0: input wire 0 — claimed by level 2 (full-round 0 lin1+lin0 skip). + constraint.GkrSkipLevel{Wires: []int{0}, ClaimSources: []constraint.GkrClaimSource{{Level: 2}}}, -func TestDefaultProvingSchedule(t *testing.T) { - _, c := scheduleTestCache.Compile(t, gkrtesting.SingleMulGateCircuit()) - schedule, err := gkrcore.DefaultProvingSchedule(c) - require.NoError(t, err) + // Level 1: input wire 1 — claimed by level 2 and level 16 (feed-forward skip). + constraint.GkrSkipLevel{Wires: []int{1}, ClaimSources: []constraint.GkrClaimSource{{Level: 2}, {Level: 16}}}, - // SingleMulGateCircuit has wires 0, 1 (inputs) and 2 (mul gate). - // Claim source indices refer to levels; 3 = len(schedule) = initial challenge. - require.Equal(t, constraint.GkrProvingSchedule{ - // Level 0: input[0] — claim source: mul gate at level 2. - constraint.GkrSkipLevel{Wires: []int{0}, ClaimSources: []int{2}}, - // Level 1: input[1] — claim source: mul gate at level 2. - constraint.GkrSkipLevel{Wires: []int{1}, ClaimSources: []int{2}}, - // Level 2: mul gate output (degree 2) — claim source: initial challenge (3). - constraint.GkrSumcheckLevel{{Wires: []int{2}, ClaimSources: []int{3}}}, + // Level 2: full-round 0 lin1+lin0 (skip, inputs from wires 0 and 1). + constraint.GkrSkipLevel{Wires: []int{3, 2}, ClaimSources: []constraint.GkrClaimSource{{Level: 3}}}, + + // Level 3: full-round 0 sBox1+sBox0 (sumcheck). + constraint.GkrSumcheckLevel{{Wires: []int{5, 4}, ClaimSources: []constraint.GkrClaimSource{{Level: 4}}}}, + + // Level 4: full-round 1 lin1+lin0 (skip, inputs [4, 5]). + constraint.GkrSkipLevel{Wires: []int{7, 6}, ClaimSources: []constraint.GkrClaimSource{{Level: 5}}}, + + // Level 5: full-round 1 sBox1+sBox0 (sumcheck, inputs lin1=7 and lin0=6). + // Feeds into level 6 (partial-round 0 lin0, M=1) and level 7 (partial-round 0 lin1, M=2). + constraint.GkrSumcheckLevel{{Wires: []int{9, 8}, ClaimSources: []constraint.GkrClaimSource{{Level: 6}, {Level: 7}, {Level: 7, OutgoingClaimIndex: 1}}}}, + + // Level 6: partial-round 0 lin0 (skip, inputs [8, 9]). + constraint.GkrSkipLevel{Wires: []int{10}, ClaimSources: []constraint.GkrClaimSource{{Level: 8}}}, + + // Level 7: partial-round 0 lin1 (skip, inputs [8, 9]). M=2 (two claim sources). + constraint.GkrSkipLevel{Wires: []int{11}, ClaimSources: []constraint.GkrClaimSource{{Level: 9}, {Level: 10}}}, + + // Level 8: partial-round 0 sBox0 (sumcheck, input lin0=10). + constraint.GkrSumcheckLevel{{Wires: []int{12}, ClaimSources: []constraint.GkrClaimSource{{Level: 9}, {Level: 10}}}}, + + // Level 9: partial-round 1 lin0 (skip, inputs [12, 11]). + constraint.GkrSkipLevel{Wires: []int{13}, ClaimSources: []constraint.GkrClaimSource{{Level: 11}}}, + + // Level 10: partial-round 1 lin1 (skip, inputs [12, 11]). + constraint.GkrSkipLevel{Wires: []int{14}, ClaimSources: []constraint.GkrClaimSource{{Level: 12}}}, + + // Level 11: partial-round 1 sBox0 (sumcheck, input lin0=13). + constraint.GkrSumcheckLevel{{Wires: []int{15}, ClaimSources: []constraint.GkrClaimSource{{Level: 12}}}}, + + // Level 12: full-round 2 lin1+lin0 (skip, inputs [15, 14]). + constraint.GkrSkipLevel{Wires: []int{17, 16}, ClaimSources: []constraint.GkrClaimSource{{Level: 13}}}, + + // Level 13: full-round 2 sBox1+sBox0 (sumcheck, inputs lin1=17 and lin0=16). + constraint.GkrSumcheckLevel{{Wires: []int{19, 18}, ClaimSources: []constraint.GkrClaimSource{{Level: 14}}}}, + + // Level 14: full-round 3 lin1+lin0 (skip, inputs [18, 19]). + constraint.GkrSkipLevel{Wires: []int{21, 20}, ClaimSources: []constraint.GkrClaimSource{{Level: 15}}}, + + // Level 15: full-round 3 sBox1+sBox0 (sumcheck, inputs lin1=21 and lin0=20). + constraint.GkrSumcheckLevel{{Wires: []int{23, 22}, ClaimSources: []constraint.GkrClaimSource{{Level: 16}}}}, + + // Level 16: feed-forward output (skip, inputs [22, 23, 1]). Claimed by initial challenge (17). + constraint.GkrSkipLevel{Wires: []int{24}, ClaimSources: []constraint.GkrClaimSource{{Level: 17}}}, }, schedule) } diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 47702ba2f6..1a629141d7 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -3,6 +3,7 @@ package gkrtesting import ( "encoding/json" "errors" + "fmt" "math/big" "os" "path/filepath" @@ -164,18 +165,18 @@ type TestCaseInfo struct { Schedule ScheduleInfo `json:"schedule,omitempty"` } -// ScheduleStepInfo is the JSON representation of a ProvingLevel with a type discriminator. +// ScheduleStepInfo is the JSON representation of a single proving level. +// Type is "sumcheck" or "skip". type ScheduleStepInfo struct { - Type string `json:"type"` - Groups []constraint.GkrClaimGroup `json:"groups,omitempty"` // for SumcheckLevel - Wires []int `json:"wires,omitempty"` // for SkipLevel - Claims []int `json:"claimSources,omitempty"` // for SkipLevel + Type string `json:"type"` + ClaimGroups []constraint.GkrClaimGroup `json:"claimGroups,omitempty"` // for "sumcheck" + ClaimGroup *constraint.GkrClaimGroup `json:"claimGroup,omitempty"` // for "skip" } // ScheduleInfo is the JSON-serializable form of a ProvingSchedule. type ScheduleInfo []ScheduleStepInfo -// ToProvingSchedule converts a ScheduleInfo to a gkrcore.ProvingSchedule. +// ToProvingSchedule converts a ScheduleInfo to a constraint.GkrProvingSchedule. // A nil ScheduleInfo returns nil, which callers should interpret as DefaultProvingSchedule. func (p ScheduleInfo) ToProvingSchedule() (constraint.GkrProvingSchedule, error) { if p == nil { @@ -185,13 +186,16 @@ func (p ScheduleInfo) ToProvingSchedule() (constraint.GkrProvingSchedule, error) for i, step := range p { switch step.Type { case "sumcheck": - groups := step.Groups + groups := step.ClaimGroups if groups == nil { groups = []constraint.GkrClaimGroup{} } s[i] = constraint.GkrSumcheckLevel(groups) case "skip": - s[i] = constraint.GkrSkipLevel{Wires: step.Wires, ClaimSources: step.Claims} + if step.ClaimGroup == nil { + return nil, fmt.Errorf("level %d: type=skip but claimGroup is absent", i) + } + s[i] = constraint.GkrSkipLevel(*step.ClaimGroup) default: return nil, errors.New("unknown ProvingLevel type: " + step.Type) } From 3bda3bc461365ae2d64c64d1bf42aade9dd59df1 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Fri, 13 Mar 2026 18:42:07 -0500 Subject: [PATCH 230/251] proof size as method --- internal/gkr/bn254/blueprint.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 5fd64ac5ed..a9110c5b30 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -304,7 +304,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return gkrcore.ProofSize(b.Schedule, b.SolveBlueprint.Circuit, logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(b.Schedule, logNbInstances) } // NbOutputs implements Blueprint From 63c141cfa95cd3de10b084e58b7039f0b7721d01 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sun, 15 Mar 2026 16:52:06 -0500 Subject: [PATCH 231/251] refactor: serialization --- internal/gkr/gkrcore/serialize.go | 110 ++++++++++++------------------ 1 file changed, 44 insertions(+), 66 deletions(-) diff --git a/internal/gkr/gkrcore/serialize.go b/internal/gkr/gkrcore/serialize.go index 8fc56d0160..bf90301c33 100644 --- a/internal/gkr/gkrcore/serialize.go +++ b/internal/gkr/gkrcore/serialize.go @@ -19,7 +19,7 @@ func writeUint8(w io.Writer, x int) error { return err } -func writeUint16(w io.Writer, x int) error { +func writeUint16[T int | uint16](w io.Writer, x T) error { var buf [2]byte if x >= 65536 || x < 0 { return fmt.Errorf("%d out of range", x) @@ -38,6 +38,18 @@ func writeBool(w io.Writer, b bool) error { return err } +func writeUint16Slice[T int | uint16](w io.Writer, s []T) error { + if err := writeUint16(w, len(s)); err != nil { + return err + } + for _, v := range s { + if err := writeUint16(w, v); err != nil { + return err + } + } + return nil +} + func writeBigInt(w io.Writer, x *big.Int) error { bytes := x.Bytes() if err := writeUint8(w, len(bytes)); err != nil { @@ -57,7 +69,7 @@ func writeBigInt(w io.Writer, x *big.Int) error { // Format: // // Circuit: [wire_count:u16] [wire...] -// Wire: [input_count:u16] [input_indices:u16...] [exported:bool] [gate?] +// Wire: [input_count:u16] [input_indices:u16...] [exported:bool] [gate] // Gate (non-input only): [const_count:u16] [constants...] [inst_count:u16] [instructions...] // Constant: [byte_len:u8] [bytes...] // Instruction: [op:u8] [input_count:u16] [input_indices:u16...] @@ -66,7 +78,7 @@ func SerializeCircuit(w io.Writer, c SerializableCircuit) error { return fmt.Errorf("circuit length too large: %d", len(c)) } - // Write number of wires + // Write the number of wires if err := writeUint16(w, len(c)); err != nil { return err } @@ -75,19 +87,11 @@ func SerializeCircuit(w io.Writer, c SerializableCircuit) error { for i := range c { wire := &c[i] - // Write number of inputs - if err := writeUint16(w, len(wire.Inputs)); err != nil { + if err := writeUint16Slice(w, wire.Inputs); err != nil { return err } - // Write each input index - for _, input := range wire.Inputs { - if err := writeUint16(w, input); err != nil { - return err - } - } - - // Write exported flag + // Write the exported flag if err := writeBool(w, wire.Exported); err != nil { return err } @@ -96,10 +100,10 @@ func SerializeCircuit(w io.Writer, c SerializableCircuit) error { if !wire.IsInput() { gate := &wire.Gate - // Write bytecode + // Write the bytecode bytecode := &gate.Evaluate - // Write constants + // Write the constants if err := writeUint16(w, len(bytecode.Constants)); err != nil { return err } @@ -109,27 +113,20 @@ func SerializeCircuit(w io.Writer, c SerializableCircuit) error { } } - // Write instructions + // Write the instructions if err := writeUint16(w, len(bytecode.Instructions)); err != nil { return err } for _, inst := range bytecode.Instructions { - // Write operation + // Write the operation if _, err := w.Write([]byte{byte(inst.Op)}); err != nil { return err } - // Write number of instruction inputs - if err := writeUint16(w, len(inst.Inputs)); err != nil { + // Write the instruction inputs + if err := writeUint16Slice(w, inst.Inputs); err != nil { return err } - - // Write each instruction input - for _, input := range inst.Inputs { - if err := writeUint16(w, int(input)); err != nil { - return err - } - } } } } @@ -145,35 +142,32 @@ func SerializeCircuit(w io.Writer, c SerializableCircuit) error { // Format: // // Schedule: [level_count:u16] [level...] -// Level: [type:u8] [skip_group | sumcheck_groups] -// SkipLevel: [claim_group] -// SumcheckLevel: [group_count:u16] [claim_group...] -// ClaimGroup: [wire_count:u16] [wire_indices:u16...] [source_count:u16] [source_indices:u16...] +// Level: [skip_sumcheck:bool] [group_count:u16] [claim_group...] +// GkrClaimGroup: [wire_count:u16] [wire_indices:u16...] [source_count:u16] [claim_source...] +// GkrClaimSource: [level:u16] [outgoing_claim_index:u16] func SerializeSchedule(w io.Writer, s constraint.GkrProvingSchedule) error { if len(s) >= 65536 { return fmt.Errorf("schedule length too large: %d", len(s)) } writeClaimGroup := func(cg constraint.GkrClaimGroup) error { - // Write wire count and indices - if err := writeUint16(w, len(cg.Wires)); err != nil { + if err := writeUint16Slice(w, cg.Wires); err != nil { return err } - for _, wireIdx := range cg.Wires { - if err := writeUint16(w, wireIdx); err != nil { - return err - } - } - // Write claim source count and indices + // Write claim source count; each source is two uint16s: Level and OutgoingClaimIndex if err := writeUint16(w, len(cg.ClaimSources)); err != nil { return err } - for _, srcIdx := range cg.ClaimSources { - if err := writeUint16(w, srcIdx); err != nil { + for _, src := range cg.ClaimSources { + if err := writeUint16(w, src.Level); err != nil { + return err + } + if err := writeUint16(w, src.OutgoingClaimIndex); err != nil { return err } } + return nil } @@ -184,34 +178,18 @@ func SerializeSchedule(w io.Writer, s constraint.GkrProvingSchedule) error { // Write each level for _, level := range s { - switch l := level.(type) { - case constraint.GkrSkipLevel: - // Type: 0 for skip - if _, err := w.Write([]byte{0}); err != nil { - return err - } - if err := writeClaimGroup(constraint.GkrClaimGroup(l)); err != nil { - return err - } - - case constraint.GkrSumcheckLevel: - // Type: 1 for sumcheck - if _, err := w.Write([]byte{1}); err != nil { - return err - } - // Write number of groups - if err := writeUint16(w, len(l)); err != nil { + _, isSkip := level.(constraint.GkrSkipLevel) + if err := writeBool(w, isSkip); err != nil { + return err + } + groups := level.ClaimGroups() + if err := writeUint16(w, len(groups)); err != nil { + return err + } + for _, cg := range groups { + if err := writeClaimGroup(cg); err != nil { return err } - // Write each group - for _, cg := range l { - if err := writeClaimGroup(cg); err != nil { - return err - } - } - - default: - return fmt.Errorf("unknown level type: %T", level) } } From f65cc965d67ea26c8ca70048e3ba30d339dd4ab5 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sun, 15 Mar 2026 17:03:16 -0500 Subject: [PATCH 232/251] refactor: extra dimension for outgoingEvalPoints --- internal/gkr/bn254/gkr.go | 62 +++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 0b056132e4..d9578f7742 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -27,7 +27,7 @@ type ( // The goal is to prove/verify evaluations of many instances of the same circuit -// WireAssignment is assignment of values to the same wire across many instances of the circuit +// WireAssignment is the assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) @@ -47,7 +47,7 @@ func (e *zeroCheckLazyClaims) varsNum() int { } func (e *zeroCheckLazyClaims) degree(int) int { - return gkrcore.Degree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel), e.resources.circuit) + return e.resources.circuit.ZeroCheckDegree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel)) } // verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. @@ -62,13 +62,13 @@ func (e *zeroCheckLazyClaims) degree(int) int { // The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming // that the full sum matches purportedValue. func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { - e.resources.levelPoints[e.levelI] = r - level := e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel) + e.resources.outgoingEvalPoints[e.levelI] = [][]fr.Element{r} + level := e.resources.schedule[e.levelI] perWireInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) var terms []fr.Element flatW := 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := e.resources.circuit[wI] @@ -84,7 +84,7 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. } for _, src := range group.ClaimSources { - eq := polynomial.EvalEq(e.resources.levelPoints[src], r) + eq := polynomial.EvalEq(e.resources.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], r) var term fr.Element term.Mul(&eq, &gateEval) terms = append(terms, term) @@ -93,8 +93,8 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. } } - ys := polynomial.Polynomial(terms) - if total := ys.Eval(&e.foldingCoeff); total.Equal(&purportedValue) { + claimedEvals := polynomial.Polynomial(terms) + if total := claimedEvals.Eval(&e.foldingCoeff); total.Equal(&purportedValue) { return nil } return errors.New("incompatible evaluations") @@ -105,7 +105,6 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. // where the sum runs over all (wire v, claim source s) pairs in the level. // Each wire has its own eq table with the batching coefficients baked in. type zeroCheckClaims struct { - level constraint.GkrSumcheckLevel levelI int resources *resources input []polynomial.MultiLin // UniqueGateInputs order @@ -115,7 +114,7 @@ type zeroCheckClaims struct { } func (c *zeroCheckClaims) varsNum() int { - return len(c.resources.levelPoints[c.level[0].ClaimSources[0]]) + return c.resources.nbVars } // roundPolynomial computes gⱼ = ∑ₕ ∑ᵥ eqs[v](Xⱼ, h...) · gateᵥ(inputs(Xⱼ, h...)). @@ -123,7 +122,8 @@ func (c *zeroCheckClaims) varsNum() int { // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). // By convention, g₀ is a constant polynomial equal to the claimed sum. func (c *zeroCheckClaims) roundPolynomial() polynomial.Polynomial { - degree := gkrcore.Degree(c.level, c.resources.circuit) + level := c.resources.schedule[c.levelI].(constraint.GkrSumcheckLevel) + degree := c.resources.circuit.ZeroCheckDegree(level) nbUniqueInputs := len(c.input) nbWires := len(c.eqs) @@ -287,18 +287,17 @@ func (r *resources) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { } type resources struct { - // levelPoints[i] is the sumcheck evaluation point r produced at schedule level i. - // levelPoints[len(schedule)] holds the initial challenge (firstChallenge / rho). - // SkipLevels produce no point; their slot remains nil. - levelPoints [][]fr.Element - evalPositions [][]int // [wireI][evalI] → position in source level's finalEvalProof - nbVars int - assignment WireAssignment - memPool polynomial.Pool - workers *utils.WorkerPool - circuit Circuit - schedule constraint.GkrProvingSchedule - transcript transcript + // outgoingEvalPoints[i][k] is the k-th outgoing evaluation point (evaluation challenge) produced at schedule level i. + // outgoingEvalPoints[len(schedule)][0] holds the initial challenge (firstChallenge / rho). + // SumcheckLevels produce one point (k=0). SkipLevels pass on all their evaluation points. + outgoingEvalPoints [][][]fr.Element + nbVars int + assignment WireAssignment + memPool polynomial.Pool + workers *utils.WorkerPool + circuit Circuit + schedule constraint.GkrProvingSchedule + transcript transcript } func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (resources, error) { @@ -309,15 +308,14 @@ func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment } return resources{ - levelPoints: make([][]fr.Element, len(schedule)+1), - evalPositions: gkrcore.BuildFinalEvalPositions(schedule, c), - nbVars: nbVars, - assignment: assignment, - memPool: polynomial.NewPool(c.MemoryRequirements(nbInstances)...), - workers: utils.NewWorkerPool(), - circuit: c, - schedule: schedule, - transcript: transcript{h: hasher}, + outgoingEvalPoints: make([][][]fr.Element, len(schedule)+1), + nbVars: nbVars, + assignment: assignment, + memPool: polynomial.NewPool(c.MemoryRequirements(nbInstances)...), + workers: utils.NewWorkerPool(), + circuit: c, + schedule: schedule, + transcript: transcript{h: hasher}, }, nil } From 63af77fa44c8b027b0428de17ed5f23998d81ca5 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sun, 15 Mar 2026 20:36:18 -0500 Subject: [PATCH 233/251] feat: UniqueInputIndices func --- constraint/gkr.go | 10 +++++++- internal/gkr/gkrcore/schedule.go | 34 +++++++++++++++++++++++++++ internal/gkr/gkrcore/schedule_test.go | 33 +++++++++++++------------- internal/gkr/gkrcore/serialize.go | 2 +- 4 files changed, 60 insertions(+), 19 deletions(-) diff --git a/constraint/gkr.go b/constraint/gkr.go index e528cb8d2f..1350f3bb9e 100644 --- a/constraint/gkr.go +++ b/constraint/gkr.go @@ -22,8 +22,12 @@ type ( // GkrProvingLevel is a single level in the proving schedule. GkrProvingLevel interface { NbOutgoingEvalPoints() int + // NbClaims returns the total number of claims at this level. NbClaims() int ClaimGroups() []GkrClaimGroup + // FinalEvalProofIndex returns where to find the evaluationPointI'th evaluation claim for the wireI'th input wire to the layer, + // in the layer's final evaluation proof. + FinalEvalProofIndex(wireI, evaluationPointI int) int } // GkrSkipLevel represents a level where zerocheck is skipped. @@ -50,10 +54,14 @@ func (l GkrSumcheckLevel) NbClaims() int { } return n } -func (l GkrSumcheckLevel) ClaimGroups() []GkrClaimGroup { return l } +func (l GkrSumcheckLevel) ClaimGroups() []GkrClaimGroup { return l } +func (l GkrSumcheckLevel) FinalEvalProofIndex(wireI, _ int) int { return wireI } func (l GkrSkipLevel) NbOutgoingEvalPoints() int { return len(l.ClaimSources) } func (l GkrSkipLevel) NbClaims() int { return GkrClaimGroup(l).NbClaims() } func (l GkrSkipLevel) ClaimGroups() []GkrClaimGroup { return []GkrClaimGroup{GkrClaimGroup(l)} } +func (l GkrSkipLevel) FinalEvalProofIndex(wireI, evaluationPointI int) int { + return wireI*l.NbOutgoingEvalPoints() + evaluationPointI +} diff --git a/internal/gkr/gkrcore/schedule.go b/internal/gkr/gkrcore/schedule.go index 929d813325..9ef3be7a99 100644 --- a/internal/gkr/gkrcore/schedule.go +++ b/internal/gkr/gkrcore/schedule.go @@ -310,3 +310,37 @@ func DefaultProvingSchedule[G any](c Circuit[G]) (constraint.GkrProvingSchedule, } return b.finalize() } + +// UniqueInputIndices returns uniqueInputIndices[wI][claimI], the position of wire wI +// in the UniqueGateInputs list of the source level for its claimI-th claim source. +// The sentinel initial-challenge claim maps to 0 (unused at call sites). +func (c Circuit[G]) UniqueInputIndices(schedule constraint.GkrProvingSchedule) [][]int { + cache := make([]map[int]int, len(schedule)) // cache[levelI][wireI] is the unique input index of wireI in levelI. + res := make([][]int, len(c)) + + // This loop weaves the level's treatment both as a claim source and as the collection of input wires + for levelI := len(schedule) - 1; levelI >= 0; levelI-- { + level := schedule[levelI] + cache[levelI] = make(map[int]int) + + for _, group := range level.ClaimGroups() { + for _, wI := range group.Wires { + + for _, inputWI := range c[wI].Inputs { + if _, ok := cache[levelI][inputWI]; !ok { + cache[levelI][inputWI] = len(cache[levelI]) + } + } + + for _, claimSource := range group.ClaimSources { + if claimSource.Level == len(schedule) { // output + res[wI] = append(res[wI], 0) // zero by convention + } else { + res[wI] = append(res[wI], cache[claimSource.Level][wI]) + } + } + } + } + } + return res +} diff --git a/internal/gkr/gkrcore/schedule_test.go b/internal/gkr/gkrcore/schedule_test.go index a99f55a032..55241be576 100644 --- a/internal/gkr/gkrcore/schedule_test.go +++ b/internal/gkr/gkrcore/schedule_test.go @@ -21,11 +21,11 @@ func TestDefaultProvingSchedule(t *testing.T) { // UniqueGateInputs for level 2 (wire 2) = [0, 1]. // 3 = len(schedule) = initial challenge sentinel. require.Equal(t, constraint.GkrProvingSchedule{ - // Level 0: input wire 0, claimed by mul gate at level 2 (its position in UniqueGateInputs = 0) + // Level 0: input wire 0, position 0 in mul gate's UniqueGateInputs [0, 1] constraint.GkrSkipLevel{Wires: []int{0}, ClaimSources: []constraint.GkrClaimSource{{Level: 2}}}, - // Level 1: input wire 1, claimed by mul gate at level 2 (position 1 in UniqueGateInputs) + // Level 1: input wire 1, position 1 in mul gate's UniqueGateInputs [0, 1] constraint.GkrSkipLevel{Wires: []int{1}, ClaimSources: []constraint.GkrClaimSource{{Level: 2}}}, - // Level 2: mul gate output, claimed by initial challenge + // Level 2: mul gate output, claimed by initial challenge (sentinel) constraint.GkrSumcheckLevel{{Wires: []int{2}, ClaimSources: []constraint.GkrClaimSource{{Level: 3}}}}, }, schedule) } @@ -37,22 +37,21 @@ func TestDefaultProvingSchedulePoseidon2(t *testing.T) { // Wire layout for Poseidon2Circuit(4, 2) — 25 wires total: // 0, 1 inputs - // 2–5 full-round 0 (lin0=2, lin1=3, sBox0=4, sBox1=5) - // 6–9 full-round 1 (lin0=6, lin1=7, sBox0=8, sBox1=9) - // 10–12 partial-round 0 (lin0=10, lin1=11, sBox0=12) - // 13–15 partial-round 1 (lin0=13, lin1=14, sBox0=15) - // 16–19 full-round 2 (lin0=16, lin1=17, sBox0=18, sBox1=19) - // 20–23 full-round 3 (lin0=20, lin1=21, sBox0=22, sBox1=23) - // 24 feed-forward output (inputs: 22, 23, 1) + // 2–3 full-round 0 lin (lin0=2, lin1=3) + // 4–5 full-round 0 sBox (sBox0=4, sBox1=5) + // 6–7 full-round 1 lin (lin0=6, lin1=7) + // 8–9 full-round 1 sBox (sBox0=8, sBox1=9) + // 10–11 partial-round 0 lin (lin0=10, lin1=11) + // 12 partial-round 0 sBox0 + // 13–14 partial-round 1 lin (lin0=13, lin1=14) + // 15 partial-round 1 sBox0 + // 16–17 full-round 2 lin (lin0=16, lin1=17) + // 18–19 full-round 2 sBox (sBox0=18, sBox1=19) + // 20–21 full-round 3 lin (lin0=20, lin1=21) + // 22–23 full-round 3 sBox (sBox0=22, sBox1=23) + // 24 feed-forward output // - // SkipSumcheck levels (degree-1 gates or input wires with a single claim source) - // produce M outgoing eval points, one per inherited claim. - // SumcheckLevels always produce exactly 1 outgoing eval point (OCI=0). // 17 = len(schedule) = initial challenge sentinel. - // - // Level 7 (wire 11, partial-round 0 lin1) has two claim sources (from levels 9 and 10), - // so it produces M=2 outgoing eval points. Wires feeding into level 7 therefore - // inherit two claim sources from it: {7,0} and {7,1}. require.Equal(t, constraint.GkrProvingSchedule{ // Level 0: input wire 0 — claimed by level 2 (full-round 0 lin1+lin0 skip). constraint.GkrSkipLevel{Wires: []int{0}, ClaimSources: []constraint.GkrClaimSource{{Level: 2}}}, diff --git a/internal/gkr/gkrcore/serialize.go b/internal/gkr/gkrcore/serialize.go index bf90301c33..d62bbeaae0 100644 --- a/internal/gkr/gkrcore/serialize.go +++ b/internal/gkr/gkrcore/serialize.go @@ -21,7 +21,7 @@ func writeUint8(w io.Writer, x int) error { func writeUint16[T int | uint16](w io.Writer, x T) error { var buf [2]byte - if x >= 65536 || x < 0 { + if int(x) >= 65536 || x < 0 { return fmt.Errorf("%d out of range", x) } binary.LittleEndian.PutUint16(buf[:], uint16(x)) From 955b157194aec47c3c41a3ffb21baa377286d5ed Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sun, 15 Mar 2026 20:40:54 -0500 Subject: [PATCH 234/251] feat: use uniqueInputIndices in the resources struct --- internal/gkr/bn254/gkr.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index d9578f7742..3049ab21a4 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -230,7 +230,7 @@ func (c *zeroCheckClaims) roundFold(r fr.Element) { // proveFinalEval provides the unique input wire values wᵢ(r₁, ..., rₙ). func (c *zeroCheckClaims) proveFinalEval(r []fr.Element) []fr.Element { - c.resources.levelPoints[c.levelI] = r + c.resources.outgoingEvalPoints[c.levelI] = [][]fr.Element{r} evaluations := make([]fr.Element, len(c.input)) for i := range c.input { c.input[i].Fold(r[len(r)-1]) @@ -295,9 +295,10 @@ type resources struct { assignment WireAssignment memPool polynomial.Pool workers *utils.WorkerPool - circuit Circuit - schedule constraint.GkrProvingSchedule - transcript transcript + circuit Circuit + schedule constraint.GkrProvingSchedule + transcript transcript + uniqueInputIndices [][]int // uniqueInputIndices[wI][claimI]: w's unique-input index in the layer its claimI-th evaluation is coming from } func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (resources, error) { @@ -316,6 +317,7 @@ func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment circuit: c, schedule: schedule, transcript: transcript{h: hasher}, + uniqueInputIndices: c.UniqueInputIndices(schedule), }, nil } From b0fc1ec704785f23a4370a815ba02c3664038e2d Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sun, 15 Mar 2026 20:51:40 -0500 Subject: [PATCH 235/251] feat: proveSkipLevel --- internal/gkr/bn254/gkr.go | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 3049ab21a4..c7183b41a0 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -321,8 +321,25 @@ func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment }, nil } -// proveLevel runs the sumcheck for a SumcheckLevel: derives the fold challenge, -// builds per-wire eq tables, constructs zeroCheckClaims, and calls sumcheckProve. +// proveSkipLevel evaluates each unique gate input at each inherited evaluation point and records +// the outgoing evaluation points on the resources. +func (r *resources) proveSkipLevel(levelI int) sumcheckProof { + level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := make([][]fr.Element, level.NbOutgoingEvalPoints()) + for k, src := range level.ClaimSources { + outPoints[k] = r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] + } + r.outgoingEvalPoints[levelI] = outPoints + + uniqueInputs := r.circuit.UniqueGateInputs(level) + evals := make([]fr.Element, len(uniqueInputs)*len(outPoints)) + for uiI, inW := range uniqueInputs { + for k, point := range outPoints { + evals[level.FinalEvalProofIndex(uiI, k)] = r.assignment[inW].Evaluate(point, &r.memPool) + } + } + return sumcheckProof{finalEvalProof: evals} +} func (r *resources) proveLevel(levelI int) sumcheckProof { level := r.schedule[levelI].(constraint.GkrSumcheckLevel) From 715176d77bce08a1652fc630f10a7ea4db45a17b Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sun, 15 Mar 2026 21:41:43 -0500 Subject: [PATCH 236/251] feat: verifySkipLevel --- internal/gkr/bn254/gkr.go | 84 +++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 20 deletions(-) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index c7183b41a0..4da9c2bad7 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -64,10 +64,10 @@ func (e *zeroCheckLazyClaims) degree(int) int { func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { e.resources.outgoingEvalPoints[e.levelI] = [][]fr.Element{r} level := e.resources.schedule[e.levelI] - perWireInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) + gateInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) - var terms []fr.Element - flatW := 0 + var claimedEvals polynomial.Polynomial + levelWireI := 0 for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := e.resources.circuit[wI] @@ -77,7 +77,7 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) } else { evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) - for _, v := range perWireInputEvals[flatW] { + for _, v := range gateInputEvals[levelWireI] { evaluator.pushInput(v) } gateEval.Set(evaluator.evaluate()) @@ -87,17 +87,16 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. eq := polynomial.EvalEq(e.resources.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], r) var term fr.Element term.Mul(&eq, &gateEval) - terms = append(terms, term) + claimedEvals = append(claimedEvals, term) } - flatW++ + levelWireI++ } } - claimedEvals := polynomial.Polynomial(terms) - if total := claimedEvals.Eval(&e.foldingCoeff); total.Equal(&purportedValue) { - return nil + if total := claimedEvals.Eval(&e.foldingCoeff); !total.Equal(&purportedValue) { + return errors.New("incompatible evaluations") } - return errors.New("incompatible evaluations") + return nil } // zeroCheckClaims is a claim for sumcheck (prover side). @@ -295,10 +294,10 @@ type resources struct { assignment WireAssignment memPool polynomial.Pool workers *utils.WorkerPool - circuit Circuit - schedule constraint.GkrProvingSchedule - transcript transcript - uniqueInputIndices [][]int // uniqueInputIndices[wI][claimI]: w's unique-input index in the layer its claimI-th evaluation is coming from + circuit Circuit + schedule constraint.GkrProvingSchedule + transcript transcript + uniqueInputIndices [][]int // uniqueInputIndices[wI][claimI]: w's unique-input index in the layer its claimI-th evaluation is coming from } func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (resources, error) { @@ -321,15 +320,22 @@ func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment }, nil } -// proveSkipLevel evaluates each unique gate input at each inherited evaluation point and records -// the outgoing evaluation points on the resources. -func (r *resources) proveSkipLevel(levelI int) sumcheckProof { +// collectOutgoingEvalPoints sets the outgoing evaluation points of a skip level, equal to its incoming ones. +func (r *resources) collectOutgoingEvalPoints(levelI int) [][]fr.Element { level := r.schedule[levelI].(constraint.GkrSkipLevel) outPoints := make([][]fr.Element, level.NbOutgoingEvalPoints()) for k, src := range level.ClaimSources { outPoints[k] = r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] } r.outgoingEvalPoints[levelI] = outPoints + return outPoints +} + +// proveSkipLevel evaluates each unique gate input at each inherited evaluation point and records +// the outgoing evaluation points on the resources. +func (r *resources) proveSkipLevel(levelI int) sumcheckProof { + outPoints := r.collectOutgoingEvalPoints(levelI) + level := r.schedule[levelI].(constraint.GkrSkipLevel) uniqueInputs := r.circuit.UniqueGateInputs(level) evals := make([]fr.Element, len(uniqueInputs)*len(outPoints)) @@ -340,10 +346,48 @@ func (r *resources) proveSkipLevel(levelI int) sumcheckProof { } return sumcheckProof{finalEvalProof: evals} } -func (r *resources) proveLevel(levelI int) sumcheckProof { - level := r.schedule[levelI].(constraint.GkrSumcheckLevel) - nbClaims := gkrcore.NbClaims(level) +// verifySkipLevel verifies a SkipSumcheck level: checks that the finalEvalProof +// is consistent with the assignment and gate evaluations, and records outgoing eval points. +func (r *resources) verifySkipLevel(levelI int, proof Proof) error { + outPoints := r.collectOutgoingEvalPoints(levelI) + level := r.schedule[levelI].(constraint.GkrSkipLevel) + + finalEval := proof[levelI].finalEvalProof + _, inputIndices := r.circuit.InputMapping(level) + group := constraint.GkrClaimGroup(level) + initialChallengeI := len(r.schedule) + + for levelWireI, wI := range group.Wires { + wire := r.circuit[wI] + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) + for claimI, src := range group.ClaimSources { + point := outPoints[claimI] + var gateEval fr.Element + if wire.IsInput() { + gateEval = r.assignment[wI].Evaluate(point, &r.memPool) + if claimed := finalEval[level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI)]; !claimed.Equal(&gateEval) { // an input wire has a unique self-input + return fmt.Errorf("level %d wire %d: finalEvalProof[%d] = %v, want %v", levelI, wI, level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI), &claimed, &gateEval) + } + } else { + for _, inI := range inputIndices[levelWireI] { + evaluator.pushInput(finalEval[level.FinalEvalProofIndex(inI, claimI)]) + } + gateEval.Set(evaluator.evaluate()) + } + var claimedEval fr.Element + if src.Level == initialChallengeI { + claimedEval = r.assignment[wI].Evaluate(point, &r.memPool) + } else { + claimedEval = proof[src.Level].finalEvalProof[r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex)] + } + if !claimedEval.Equal(&gateEval) { + return fmt.Errorf("level %d wire %d claim %d: claimed eval %v disagrees with gate eval %v", levelI, wI, claimI, &claimedEval, &gateEval) + } + } + } + return nil +} var foldingCoeff fr.Element if nbClaims >= 2 { foldingCoeff = r.transcript.getChallenge() From 3f46589a226c48f307bb45885b29d4ff44632989 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sun, 15 Mar 2026 21:42:44 -0500 Subject: [PATCH 237/251] feat: proveSumcheckLevel --- internal/gkr/bn254/gkr.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 4da9c2bad7..dcd5bb26e9 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -388,25 +388,29 @@ func (r *resources) verifySkipLevel(levelI int, proof Proof) error { } return nil } + +func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { + level := r.schedule[levelI] + nbClaims := level.NbClaims() var foldingCoeff fr.Element if nbClaims >= 2 { foldingCoeff = r.transcript.getChallenge() } - uniqueInputs, inputIndices := gkrcore.InputMapping(level, r.circuit) + uniqueInputs, inputIndices := r.circuit.InputMapping(level) input := make([]polynomial.MultiLin, len(uniqueInputs)) for i, inW := range uniqueInputs { input[i] = r.memPool.Clone(r.assignment[inW]) } nbWires := 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { nbWires += len(group.Wires) } pools := make([]*gateEvaluatorPool, nbWires) flatW := 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := r.circuit[wI] gate := wire.Gate.Evaluate @@ -423,12 +427,12 @@ func (r *resources) verifySkipLevel(levelI int, proof Proof) error { var alpha fr.Element alpha.SetOne() flatW = 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { nbSources := len(group.ClaimSources) groupEq := polynomial.MultiLin(r.memPool.Make(eqLength)) groupEq[0].Set(&alpha) - groupEq.Eq(r.levelPoints[group.ClaimSources[0]]) + groupEq.Eq(r.outgoingEvalPoints[group.ClaimSources[0].Level][group.ClaimSources[0].OutgoingClaimIndex]) if nbSources > 1 { newEq := polynomial.MultiLin(r.memPool.Make(eqLength)) @@ -436,7 +440,7 @@ func (r *resources) verifySkipLevel(levelI int, proof Proof) error { for k := 1; k < nbSources; k++ { aI.Mul(&aI, &foldingCoeff) newEq[0].Set(&aI) - r.eqAcc(groupEq, newEq, r.levelPoints[group.ClaimSources[k]]) + r.eqAcc(groupEq, newEq, r.outgoingEvalPoints[group.ClaimSources[k].Level][group.ClaimSources[k].OutgoingClaimIndex]) } r.memPool.Dump(newEq) } @@ -452,7 +456,7 @@ func (r *resources) verifySkipLevel(levelI int, proof Proof) error { alpha.Mul(&alpha, &stride) for w := 1; w < len(group.Wires); w++ { - eqs[flatW] = polynomial.MultiLin(r.memPool.Make(eqLength)) + eqs[flatW] = r.memPool.Make(eqLength) r.workers.Submit(eqLength, func(start, end int) { for i := start; i < end; i++ { eqs[flatW][i].Mul(&eqs[flatW-1][i], &stride) @@ -464,7 +468,6 @@ func (r *resources) verifySkipLevel(levelI int, proof Proof) error { } claims := &zeroCheckClaims{ - level: level, levelI: levelI, resources: r, input: input, From 55105502c08745ed8ff788ddf552a4773d363910 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sun, 15 Mar 2026 22:07:08 -0500 Subject: [PATCH 238/251] feat: verifySumcheckLevel --- internal/gkr/bn254/gkr.go | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index dcd5bb26e9..d080cc16c1 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -478,44 +478,37 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { return sumcheckProve(claims, &r.transcript) } -// verifyLevel runs the sumcheck verification for a SumcheckLevel: derives the fold challenge, -// computes the claimed sum, and calls sumcheckVerify. -func (r *resources) verifyLevel(levelI int, proof Proof) error { - level := r.schedule[levelI].(constraint.GkrSumcheckLevel) - initialChallengeI := len(r.schedule) - - nbClaims := gkrcore.NbClaims(level) +func (r *resources) verifySumcheckLevel(levelI int, proof Proof) error { + level := r.schedule[levelI] + nbClaims := level.NbClaims() var foldingCoeff fr.Element if nbClaims >= 2 { foldingCoeff = r.transcript.getChallenge() } - var ys []fr.Element - for _, group := range level { + initialChallengeI := len(r.schedule) + claimedEvals := make(polynomial.Polynomial, 0, level.NbClaims()) + + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { - evalI := 0 - for _, src := range group.ClaimSources { - var y fr.Element - if src == initialChallengeI { - y = r.assignment[wI].Evaluate(r.levelPoints[src], &r.memPool) + for claimI, src := range group.ClaimSources { + if src.Level == initialChallengeI { + claimedEvals = append(claimedEvals, r.assignment[wI].Evaluate(r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], &r.memPool)) } else { - y = proof[src].finalEvalProof[r.evalPositions[wI][evalI]] - evalI++ + claimedEvals = append(claimedEvals, proof[src.Level].finalEvalProof[r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex)]) } - ys = append(ys, y) } } } - ysPoly := polynomial.Polynomial(ys) - claimedSum := ysPoly.Eval(&foldingCoeff) + claimedSum := claimedEvals.Eval(&foldingCoeff) lazyClaims := &zeroCheckLazyClaims{ foldingCoeff: foldingCoeff, resources: r, levelI: levelI, } - return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, gkrcore.Degree(level, r.circuit), &r.transcript) + return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, r.circuit.ZeroCheckDegree(level.(constraint.GkrSumcheckLevel)), &r.transcript) } // Prove consistency of the claimed assignment From ad463dca0273d73224b597a2589b486b8a46118f Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sun, 15 Mar 2026 22:10:48 -0500 Subject: [PATCH 239/251] feat: prove/verify --- internal/gkr/bn254/gkr.go | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index d080cc16c1..56f6fe2f2c 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -521,23 +521,20 @@ func Prove(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAss proof := make(Proof, len(schedule)) - // Derive initial challenge point + // Derive the initial challenge point firstChallenge := make([]fr.Element, r.nbVars) for j := range r.nbVars { firstChallenge[j] = r.transcript.getChallenge() } - r.levelPoints[len(schedule)] = firstChallenge + r.outgoingEvalPoints[len(schedule)] = [][]fr.Element{firstChallenge} for levelI := len(schedule) - 1; levelI >= 0; levelI-- { - switch s := schedule[levelI].(type) { - case constraint.GkrSkipLevel: - r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - - case constraint.GkrSumcheckLevel: - proof[levelI] = r.proveLevel(levelI) - // Bind finalEvalProof for next level's challenge derivation - r.transcript.bind(proof[levelI].finalEvalProof...) + if _, isSkip := r.schedule[levelI].(constraint.GkrSkipLevel); isSkip { + proof[levelI] = r.proveSkipLevel(levelI) + } else { + proof[levelI] = r.proveSumcheckLevel(levelI) } + r.transcript.bind(proof[levelI].finalEvalProof...) } return proof, nil @@ -552,25 +549,23 @@ func Verify(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAs } defer r.workers.Stop() - // Derive initial challenge point + // Derive the initial challenge point firstChallenge := make([]fr.Element, r.nbVars) for j := range r.nbVars { firstChallenge[j] = r.transcript.getChallenge() } - r.levelPoints[len(schedule)] = firstChallenge + r.outgoingEvalPoints[len(schedule)] = [][]fr.Element{firstChallenge} for levelI := len(schedule) - 1; levelI >= 0; levelI-- { - switch s := schedule[levelI].(type) { - case constraint.GkrSkipLevel: - r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - - case constraint.GkrSumcheckLevel: - if err = r.verifyLevel(levelI, proof); err != nil { - return fmt.Errorf("sumcheck proof rejected: %v", err) - } - // Bind finalEvalProof for next level's challenge derivation - r.transcript.bind(proof[levelI].finalEvalProof...) + if _, isSkip := r.schedule[levelI].(constraint.GkrSkipLevel); isSkip { + err = r.verifySkipLevel(levelI, proof) + } else { + err = r.verifySumcheckLevel(levelI, proof) + } + if err != nil { + return fmt.Errorf("level %d: %v", levelI, err) } + r.transcript.bind(proof[levelI].finalEvalProof...) } return nil } From 90afedeac1a6f214af7332392a19e149da33c290 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sun, 15 Mar 2026 22:17:12 -0500 Subject: [PATCH 240/251] gkr tests --- internal/gkr/bn254/gkr_test.go | 151 ++++++++++++++++++++++++--------- 1 file changed, 110 insertions(+), 41 deletions(-) diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index b12ad775b9..11b0872de4 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -68,8 +68,8 @@ func TestMimc(t *testing.T) { test(t, gkrtesting.MiMCCircuit(93)) } -// testLevel exercises proveLevel/verifyLevel for a single sumcheck level. -func testLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrSumcheckLevel) { +// testSumcheckLevel exercises proveLevel/verifyLevel for a single sumcheck level. +func testSumcheckLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrProvingLevel) { t.Helper() _, sCircuit := cache.Compile(t, circuit) @@ -90,19 +90,19 @@ func testLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrSum assert.NoError(t, err) defer proveR.workers.Stop() - proveR.levelPoints[1] = challenge - proof := Proof{proveR.proveLevel(0)} + proveR.outgoingEvalPoints[1] = [][]fr.Element{challenge} + proof := Proof{proveR.proveSumcheckLevel(0)} // Verify verifyR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) defer verifyR.workers.Stop() - verifyR.levelPoints[1] = challenge - assert.NoError(t, verifyR.verifyLevel(0, proof)) + verifyR.outgoingEvalPoints[1] = [][]fr.Element{challenge} + assert.NoError(t, verifyR.verifySumcheckLevel(0, proof)) } -func TestLevel(t *testing.T) { +func TestSumcheckLevel(t *testing.T) { // Wires 0,1 = inputs; wires 2,3,4 = mul(0,1). All gates are independent outputs. circuit := gkrcore.RawCircuit{ {}, @@ -111,41 +111,114 @@ func TestLevel(t *testing.T) { {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, } - // All levels have initial challenge at index 1 (len(schedule) = 1). + // Each level has an initial challenge at index 1 (len(schedule) = 1). + // GkrClaimSource{Level:1} is the initial-challenge sentinel. tests := []struct { name string - level constraint.GkrSumcheckLevel + level constraint.GkrProvingLevel }{ { name: "single wire", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []int{1}}, + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, { name: "two groups", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []int{1}}, - {Wires: []int{3}, ClaimSources: []int{1}}, + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + {Wires: []int{3}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, { name: "one group with two wires", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4, 3}, ClaimSources: []int{1}}, + {Wires: []int{4, 3}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, { name: "mixed: single + multi-wire group", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []int{1}}, - {Wires: []int{3, 2}, ClaimSources: []int{1}}, + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + {Wires: []int{3, 2}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - testLevel(t, circuit, tc.level) + testSumcheckLevel(t, circuit, tc.level) + }) + } +} + +// testSkipLevel exercises proveSkipLevel/verifySkipLevel for a single skip level. +func testSkipLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrProvingLevel) { + t.Helper() + _, sCircuit := cache.Compile(t, circuit) + + ins := sCircuit.Inputs() + assignment := make(WireAssignment, len(sCircuit)) + for _, i := range ins { + assignment[i] = make([]fr.Element, 2) + fr.Vector(assignment[i]).MustSetRandom() + } + + assignment.Complete(sCircuit) + + schedule := constraint.GkrProvingSchedule{level} + challenge := []fr.Element{five} + + // Prove + proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) + assert.NoError(t, err) + defer proveR.workers.Stop() + + proveR.outgoingEvalPoints[1] = [][]fr.Element{challenge} + proof := Proof{proveR.proveSkipLevel(0)} + + // Verify + verifyR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) + assert.NoError(t, err) + defer verifyR.workers.Stop() + + verifyR.outgoingEvalPoints[1] = [][]fr.Element{challenge} + assert.NoError(t, verifyR.verifySkipLevel(0, proof)) +} + +func TestSkipLevel(t *testing.T) { + // Wires 0,1 = inputs; wires 2,3 = identity(0); wire 4 = add(0,1). All degree-1 outputs. + circuit := gkrcore.RawCircuit{ + {}, + {}, + {Gate: gkrcore.Identity, Inputs: []int{0}}, + {Gate: gkrcore.Identity, Inputs: []int{0}}, + {Gate: gkrcore.Add2, Inputs: []int{0, 1}}, + } + // Each level has an initial challenge at index 1 (len(schedule) = 1). + tests := []struct { + name string + level constraint.GkrProvingLevel + }{ + { + name: "single input wire", + level: constraint.GkrSkipLevel{Wires: []int{0}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + { + name: "single identity gate", + level: constraint.GkrSkipLevel{Wires: []int{2}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + { + name: "add gate", + level: constraint.GkrSkipLevel{Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + { + name: "two identity gates one group", + level: constraint.GkrSkipLevel{Wires: []int{2, 3}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + testSkipLevel(t, circuit, tc.level) }) } } @@ -219,29 +292,11 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constra err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(1, 1)) assert.NoError(t, err, "proof rejected") - if proof.isEmpty() { // special case for TestNoGate: - continue // there's no way to make a trivial proof fail - } - err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(0, 1)) assert.NotNil(t, err, "bad proof accepted") } } -func (p Proof) isEmpty() bool { - for i := range p { - if len(p[i].finalEvalProof) != 0 { - return false - } - for j := range p[i].partialSumPolys { - if len(p[i].partialSumPolys[j]) != 0 { - return false - } - } - } - return true -} - func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { _, c := cache.Compile(t, gkrtesting.NoGateCircuit()) @@ -275,10 +330,23 @@ func generateTestVerifier(path string) func(t *testing.T) { assert.NoError(t, err) err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) assert.NoError(t, err, "proof rejected") + testCase, err = newTestCase(path) assert.NoError(t, err) err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, newMessageCounter(2, 0)) assert.NotNil(t, err, "bad proof accepted") + + testCase, err = newTestCase(path) + assert.NoError(t, err) + testCase.InOutAssignment[len(testCase.InOutAssignment)-1][0].Add(&testCase.InOutAssignment[len(testCase.InOutAssignment)-1][0], &one) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) + assert.NotNil(t, err, "tampered output accepted") + + testCase, err = newTestCase(path) + assert.NoError(t, err) + testCase.InOutAssignment[0][0].Add(&testCase.InOutAssignment[0][0], &one) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) + assert.NotNil(t, err, "tampered input accepted") } } @@ -358,11 +426,11 @@ func TestSingleMulGateExplicitSchedule(t *testing.T) { _, sCircuit := cache.Compile(t, circuit) // Wire 2 is the mul gate output (inputs: 0, 1). - // Explicit schedule: one SumcheckLevel for wire 2 only. - // ClaimSources: [1] = initial challenge (index len(schedule) = 1). + // Explicit schedule: one GkrProvingLevel for wire 2. + // GkrClaimSource{Level:1} is the initial-challenge sentinel (len(schedule)=1). schedule := constraint.GkrProvingSchedule{ constraint.GkrSumcheckLevel{ - {Wires: []int{2}, ClaimSources: []int{1}}, + {Wires: []int{2}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, } testWithSchedule(t, circuit, schedule) @@ -444,10 +512,11 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } var schedule constraint.GkrProvingSchedule - if schedule, err = info.Schedule.ToProvingSchedule(); err != nil { - return nil, err - } - if schedule == nil { + if info.Schedule != nil { + if schedule, err = info.Schedule.ToProvingSchedule(); err != nil { + return nil, err + } + } else { if schedule, err = gkrcore.DefaultProvingSchedule(circuit); err != nil { return nil, err } From 4e03a9863f2024d3cd4e2b004f47fa49671bd1b2 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Sun, 15 Mar 2026 22:50:12 -0500 Subject: [PATCH 241/251] test: more rigorous --- internal/gkr/bn254/gkr_test.go | 105 +++++++++++++++++++++++++++++---- 1 file changed, 94 insertions(+), 11 deletions(-) diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 11b0872de4..b5daa239b6 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -68,6 +68,35 @@ func TestMimc(t *testing.T) { test(t, gkrtesting.MiMCCircuit(93)) } +func TestPoseidon2(t *testing.T) { + test(t, gkrtesting.Poseidon2Circuit(4, 2)) +} + +// sentinelEvalPoints builds the evaluation-point slice to place at the +// initial-challenge sentinel level (outgoingEvalPoints[sentinelIdx]). +// M is derived as max(OutgoingClaimIndex)+1 across all claim sources that +// reference sentinelIdx; each point is a single-element slice {k+1}. +func sentinelEvalPoints(level constraint.GkrProvingLevel, sentinelIdx int) [][]fr.Element { + M := 0 + for _, cg := range level.ClaimGroups() { + for _, cs := range cg.ClaimSources { + if cs.Level == sentinelIdx && cs.OutgoingClaimIndex+1 > M { + M = cs.OutgoingClaimIndex + 1 + } + } + } + if M == 0 { + M = 1 + } + pts := make([][]fr.Element, M) + for k := range pts { + var e fr.Element + e.SetInt64(int64(k + 1)) + pts[k] = []fr.Element{e} + } + return pts +} + // testSumcheckLevel exercises proveLevel/verifyLevel for a single sumcheck level. func testSumcheckLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrProvingLevel) { t.Helper() @@ -83,14 +112,14 @@ func testSumcheckLevel(t *testing.T, circuit gkrcore.RawCircuit, level constrain assignment.Complete(sCircuit) schedule := constraint.GkrProvingSchedule{level} - challenge := []fr.Element{five} + initEvalPoints := sentinelEvalPoints(level, len(schedule)) // Prove proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) defer proveR.workers.Stop() - proveR.outgoingEvalPoints[1] = [][]fr.Element{challenge} + proveR.outgoingEvalPoints[len(schedule)] = initEvalPoints proof := Proof{proveR.proveSumcheckLevel(0)} // Verify @@ -98,7 +127,7 @@ func testSumcheckLevel(t *testing.T, circuit gkrcore.RawCircuit, level constrain assert.NoError(t, err) defer verifyR.workers.Stop() - verifyR.outgoingEvalPoints[1] = [][]fr.Element{challenge} + verifyR.outgoingEvalPoints[len(schedule)] = initEvalPoints assert.NoError(t, verifyR.verifySumcheckLevel(0, proof)) } @@ -149,6 +178,34 @@ func TestSumcheckLevel(t *testing.T) { testSumcheckLevel(t, circuit, tc.level) }) } + + // Two-claim cases: sentinel level provides two evaluation points, + // exercising OutgoingClaimIndex = 1 in a sumcheck level. + twoClaim := []struct { + name string + level constraint.GkrProvingLevel + }{ + { + // Single group whose result feeds eval point 1 of the sentinel. + name: "single wire claim index 1", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1, OutgoingClaimIndex: 1}}}, + }, + }, + { + // Two groups consuming different sentinel eval points. + name: "two groups different claim indices", + level: constraint.GkrSumcheckLevel{ + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1, OutgoingClaimIndex: 0}}}, + {Wires: []int{3}, ClaimSources: []constraint.GkrClaimSource{{Level: 1, OutgoingClaimIndex: 1}}}, + }, + }, + } + for _, tc := range twoClaim { + t.Run(tc.name, func(t *testing.T) { + testSumcheckLevel(t, circuit, tc.level) + }) + } } // testSkipLevel exercises proveSkipLevel/verifySkipLevel for a single skip level. @@ -166,14 +223,14 @@ func testSkipLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.Gk assignment.Complete(sCircuit) schedule := constraint.GkrProvingSchedule{level} - challenge := []fr.Element{five} + initEvalPoints := sentinelEvalPoints(level, len(schedule)) // Prove proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) defer proveR.workers.Stop() - proveR.outgoingEvalPoints[1] = [][]fr.Element{challenge} + proveR.outgoingEvalPoints[len(schedule)] = initEvalPoints proof := Proof{proveR.proveSkipLevel(0)} // Verify @@ -181,7 +238,7 @@ func testSkipLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.Gk assert.NoError(t, err) defer verifyR.workers.Stop() - verifyR.outgoingEvalPoints[1] = [][]fr.Element{challenge} + verifyR.outgoingEvalPoints[len(schedule)] = initEvalPoints assert.NoError(t, verifyR.verifySkipLevel(0, proof)) } @@ -194,8 +251,9 @@ func TestSkipLevel(t *testing.T) { {Gate: gkrcore.Identity, Inputs: []int{0}}, {Gate: gkrcore.Add2, Inputs: []int{0, 1}}, } - // Each level has an initial challenge at index 1 (len(schedule) = 1). - tests := []struct { + + // Single-claim cases: one inherited evaluation point (OutgoingClaimIndex always 0). + singleClaim := []struct { name string level constraint.GkrProvingLevel }{ @@ -216,7 +274,34 @@ func TestSkipLevel(t *testing.T) { level: constraint.GkrSkipLevel{Wires: []int{2, 3}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, } - for _, tc := range tests { + for _, tc := range singleClaim { + t.Run(tc.name, func(t *testing.T) { + testSkipLevel(t, circuit, tc.level) + }) + } + + // Two-claim cases: two inherited evaluation points, exercising OutgoingClaimIndex = 0 and 1. + // ClaimSources references the same sentinel level at both outgoing indices. + twoClaimSources := []constraint.GkrClaimSource{{Level: 1, OutgoingClaimIndex: 0}, {Level: 1, OutgoingClaimIndex: 1}} + twoClaim := []struct { + name string + level constraint.GkrProvingLevel + }{ + { + name: "input wire two claim points", + level: constraint.GkrSkipLevel{Wires: []int{0}, ClaimSources: twoClaimSources}, + }, + { + name: "identity gate two claim points", + level: constraint.GkrSkipLevel{Wires: []int{2}, ClaimSources: twoClaimSources}, + }, + { + // Two unique inputs [0,1]; finalEvalProof = [w0(p0), w0(p1), w1(p0), w1(p1)]. + name: "add gate two claim points", + level: constraint.GkrSkipLevel{Wires: []int{4}, ClaimSources: twoClaimSources}, + }, + } + for _, tc := range twoClaim { t.Run(tc.name, func(t *testing.T) { testSkipLevel(t, circuit, tc.level) }) @@ -282,8 +367,6 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constra fullAssignment.Complete(sCircuit) - t.Log("Selected inputs for test") - proof, err := Prove(sCircuit, schedule, fullAssignment, newMessageCounter(1, 1)) assert.NoError(t, err) From 23cf32e5ab7b25f0686cdaef7d57532b75fb0fda Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 16 Mar 2026 00:09:42 -0500 Subject: [PATCH 242/251] generify for gadget, remove impossible sentinel logic --- internal/gkr/bn254/blueprint.go | 2 +- internal/gkr/bn254/gkr.go | 2 +- internal/gkr/bn254/gkr_test.go | 115 +---------- internal/gkr/gkr.go | 264 ++++++++++---------------- internal/gkr/gkr_test.go | 3 +- internal/gkr/gkrcore/serialize.go | 197 ------------------- internal/gkr/gkrtesting/gkrtesting.go | 22 +++ internal/gkr/sumcheck.go | 40 +--- std/gkrapi/api_test.go | 94 +++++++++ std/gkrapi/compile.go | 12 +- 10 files changed, 230 insertions(+), 521 deletions(-) delete mode 100644 internal/gkr/gkrcore/serialize.go diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index a9110c5b30..4a9e445f2b 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -447,7 +447,7 @@ func NewBlueprints(circuit gkrcore.SerializableCircuit, schedule constraint.GkrP } proveID := compiler.AddBlueprint(prove) - // Create and register GetAssignment blueprint + // Create and register the GetAssignment blueprint getAssignment := &BlueprintGetAssignment{ SolveBlueprintID: solveID, } diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 56f6fe2f2c..0afe241f4c 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -776,7 +776,7 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() - // Return evaluator to pool (it keeps its vars slice from polynomial pool) + // Return evaluator to pool (it keeps its vars slice from the polynomial pool) gep.available[e] = struct{}{} } diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index b5daa239b6..7df3933588 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -11,7 +11,6 @@ import ( "os" "path/filepath" "reflect" - "strconv" "testing" "time" @@ -72,31 +71,6 @@ func TestPoseidon2(t *testing.T) { test(t, gkrtesting.Poseidon2Circuit(4, 2)) } -// sentinelEvalPoints builds the evaluation-point slice to place at the -// initial-challenge sentinel level (outgoingEvalPoints[sentinelIdx]). -// M is derived as max(OutgoingClaimIndex)+1 across all claim sources that -// reference sentinelIdx; each point is a single-element slice {k+1}. -func sentinelEvalPoints(level constraint.GkrProvingLevel, sentinelIdx int) [][]fr.Element { - M := 0 - for _, cg := range level.ClaimGroups() { - for _, cs := range cg.ClaimSources { - if cs.Level == sentinelIdx && cs.OutgoingClaimIndex+1 > M { - M = cs.OutgoingClaimIndex + 1 - } - } - } - if M == 0 { - M = 1 - } - pts := make([][]fr.Element, M) - for k := range pts { - var e fr.Element - e.SetInt64(int64(k + 1)) - pts[k] = []fr.Element{e} - } - return pts -} - // testSumcheckLevel exercises proveLevel/verifyLevel for a single sumcheck level. func testSumcheckLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrProvingLevel) { t.Helper() @@ -112,14 +86,14 @@ func testSumcheckLevel(t *testing.T, circuit gkrcore.RawCircuit, level constrain assignment.Complete(sCircuit) schedule := constraint.GkrProvingSchedule{level} - initEvalPoints := sentinelEvalPoints(level, len(schedule)) + initEvalPoint := [][]fr.Element{{one}} // Prove proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) defer proveR.workers.Stop() - proveR.outgoingEvalPoints[len(schedule)] = initEvalPoints + proveR.outgoingEvalPoints[len(schedule)] = initEvalPoint proof := Proof{proveR.proveSumcheckLevel(0)} // Verify @@ -127,7 +101,7 @@ func testSumcheckLevel(t *testing.T, circuit gkrcore.RawCircuit, level constrain assert.NoError(t, err) defer verifyR.workers.Stop() - verifyR.outgoingEvalPoints[len(schedule)] = initEvalPoints + verifyR.outgoingEvalPoints[len(schedule)] = initEvalPoint assert.NoError(t, verifyR.verifySumcheckLevel(0, proof)) } @@ -178,34 +152,6 @@ func TestSumcheckLevel(t *testing.T) { testSumcheckLevel(t, circuit, tc.level) }) } - - // Two-claim cases: sentinel level provides two evaluation points, - // exercising OutgoingClaimIndex = 1 in a sumcheck level. - twoClaim := []struct { - name string - level constraint.GkrProvingLevel - }{ - { - // Single group whose result feeds eval point 1 of the sentinel. - name: "single wire claim index 1", - level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1, OutgoingClaimIndex: 1}}}, - }, - }, - { - // Two groups consuming different sentinel eval points. - name: "two groups different claim indices", - level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1, OutgoingClaimIndex: 0}}}, - {Wires: []int{3}, ClaimSources: []constraint.GkrClaimSource{{Level: 1, OutgoingClaimIndex: 1}}}, - }, - }, - } - for _, tc := range twoClaim { - t.Run(tc.name, func(t *testing.T) { - testSumcheckLevel(t, circuit, tc.level) - }) - } } // testSkipLevel exercises proveSkipLevel/verifySkipLevel for a single skip level. @@ -223,14 +169,14 @@ func testSkipLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.Gk assignment.Complete(sCircuit) schedule := constraint.GkrProvingSchedule{level} - initEvalPoints := sentinelEvalPoints(level, len(schedule)) + initEvalPoint := [][]fr.Element{{one}} // Prove proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) defer proveR.workers.Stop() - proveR.outgoingEvalPoints[len(schedule)] = initEvalPoints + proveR.outgoingEvalPoints[len(schedule)] = initEvalPoint proof := Proof{proveR.proveSkipLevel(0)} // Verify @@ -238,7 +184,7 @@ func testSkipLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.Gk assert.NoError(t, err) defer verifyR.workers.Stop() - verifyR.outgoingEvalPoints[len(schedule)] = initEvalPoints + verifyR.outgoingEvalPoints[len(schedule)] = initEvalPoint assert.NoError(t, verifyR.verifySkipLevel(0, proof)) } @@ -279,33 +225,6 @@ func TestSkipLevel(t *testing.T) { testSkipLevel(t, circuit, tc.level) }) } - - // Two-claim cases: two inherited evaluation points, exercising OutgoingClaimIndex = 0 and 1. - // ClaimSources references the same sentinel level at both outgoing indices. - twoClaimSources := []constraint.GkrClaimSource{{Level: 1, OutgoingClaimIndex: 0}, {Level: 1, OutgoingClaimIndex: 1}} - twoClaim := []struct { - name string - level constraint.GkrProvingLevel - }{ - { - name: "input wire two claim points", - level: constraint.GkrSkipLevel{Wires: []int{0}, ClaimSources: twoClaimSources}, - }, - { - name: "identity gate two claim points", - level: constraint.GkrSkipLevel{Wires: []int{2}, ClaimSources: twoClaimSources}, - }, - { - // Two unique inputs [0,1]; finalEvalProof = [w0(p0), w0(p1), w1(p0), w1(p1)]. - name: "add gate two claim points", - level: constraint.GkrSkipLevel{Wires: []int{4}, ClaimSources: twoClaimSources}, - }, - } - for _, tc := range twoClaim { - t.Run(tc.name, func(t *testing.T) { - testSkipLevel(t, circuit, tc.level) - }) - } } var one, two, three, four, five, six fr.Element @@ -319,26 +238,6 @@ func init() { six.Double(&three) } -var testManyInstancesLogMaxInstances = -1 - -func getLogMaxInstances(t *testing.T) int { - if testManyInstancesLogMaxInstances == -1 { - - s := os.Getenv("GKR_LOG_INSTANCES") - if s == "" { - testManyInstancesLogMaxInstances = 5 - } else { - var err error - testManyInstancesLogMaxInstances, err = strconv.Atoi(s) - if err != nil { - t.Error(err) - } - } - - } - return testManyInstancesLogMaxInstances -} - func test(t *testing.T, circuit gkrcore.RawCircuit) { testWithSchedule(t, circuit, nil) } @@ -352,7 +251,7 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constra } ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) - maxSize := 1 << getLogMaxInstances(t) + maxSize := 1 << gkrtesting.GetLogMaxInstances(t) for i := range ins { insAssignment[i] = make([]fr.Element, maxSize) diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index af4b14c44a..85c7396402 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -3,12 +3,11 @@ package gkr import ( "errors" "fmt" - "strconv" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrcore" - fiatshamir "github.com/consensys/gnark/std/fiat-shamir" + "github.com/consensys/gnark/std/hash" "github.com/consensys/gnark/std/polynomial" ) @@ -48,15 +47,14 @@ type Proof []sumcheckProof // for each schedule level, a sumcheck proof // It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, // where the sum runs over all (wire v, claim source s) pairs in the level. type zeroCheckLazyClaims struct { - foldingCoeff frontend.Variable - levelI int - schedule constraint.GkrProvingSchedule - circuit Circuit - assignment WireAssignment - levelPoints [][]frontend.Variable // [stepI] → eval point r - evalPositions [][]int // [wireI][evalI] → position in source level's finalEvalProof - proof Proof - nbVars int + foldingCoeff frontend.Variable + levelI int + schedule constraint.GkrProvingSchedule + circuit Circuit + assignment WireAssignment + outgoingEvalPoints [][][]frontend.Variable // [levelI][outgoingClaimI] → eval point + proof Proof + nbVars int } func (e *zeroCheckLazyClaims) varsNum() int { @@ -64,7 +62,7 @@ func (e *zeroCheckLazyClaims) varsNum() int { } func (e *zeroCheckLazyClaims) degree(int) int { - return gkrcore.Degree(e.schedule[e.levelI].(constraint.GkrSumcheckLevel), e.circuit) + return e.circuit.ZeroCheckDegree(e.schedule[e.levelI].(constraint.GkrSumcheckLevel)) } // verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. @@ -77,13 +75,13 @@ func (e *zeroCheckLazyClaims) degree(int) int { // For non-input wires, the prover claims evaluations of the input wires at r, // communicated through uniqueInputEvaluations; those claims are verified later. func (e *zeroCheckLazyClaims) verifyFinalEval(api frontend.API, r []frontend.Variable, purportedValue frontend.Variable, uniqueInputEvaluations []frontend.Variable) error { - e.levelPoints[e.levelI] = r - level := e.schedule[e.levelI].(constraint.GkrSumcheckLevel) + e.outgoingEvalPoints[e.levelI] = [][]frontend.Variable{r} + level := e.schedule[e.levelI] perWireInputEvals := gkrcore.ReduplicateInputs(level, e.circuit, uniqueInputEvaluations) var terms []frontend.Variable flatW := 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := e.circuit[wI] @@ -95,7 +93,7 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(api frontend.API, r []frontend.Var } for _, src := range group.ClaimSources { - eq := polynomial.EvalEq(api, e.levelPoints[src], r) + eq := polynomial.EvalEq(api, e.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], r) term := api.Mul(eq, gateEval) terms = append(terms, term) } @@ -103,184 +101,112 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(api frontend.API, r []frontend.Var } } - ys := polynomial.Polynomial(terms) - total := ys.Eval(api, e.foldingCoeff) + claimedEvals := polynomial.Polynomial(terms) + total := claimedEvals.Eval(api, e.foldingCoeff) api.AssertIsEqual(total, purportedValue) return nil } -type settings struct { - transcript *fiatshamir.Transcript - transcriptPrefix string - nbVars int +// transcript is a Fiat-Shamir transcript backed by a running hash. +// Field elements are written via bind; challenges are derived via getChallenge. +// The hash is never reset — all previous data is implicitly part of future challenges. +type transcript struct { + h hash.FieldHasher + bound bool } -type Option func(*settings) - -func setup(api frontend.API, c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { - var o settings - var err error - for _, option := range options { - option(&o) - } - - o.nbVars = assignment.NbVars() - nbInstances := assignment.NbInstances() - if 1<= 0; levelI-- { - s, ok := schedule[levelI].(constraint.GkrSumcheckLevel) - if !ok { - continue - } - levelPrefix := prefix + "l" + strconv.Itoa(levelI) + "." - - if gkrcore.NbClaims(s) >= 2 { - challenges = append(challenges, levelPrefix+"fold") - } - - pSPPrefix := levelPrefix + "pSP." - for k := 0; k < logNbInstances; k++ { - challenges = append(challenges, pSPPrefix+strconv.Itoa(k)) - } - } - - return challenges -} - -func getFirstChallengeNames(logNbInstances int, prefix string) []string { - res := make([]string, logNbInstances) - firstChallengePrefix := prefix + "fC." - for i := 0; i < logNbInstances; i++ { - res[i] = firstChallengePrefix + strconv.Itoa(i) - } - return res -} - -func getChallenges(transcript *fiatshamir.Transcript, names []string) (challenges []frontend.Variable, err error) { - challenges = make([]frontend.Variable, len(names)) - for i, name := range names { - if challenges[i], err = transcript.ComputeChallenge(name); err != nil { - return - } - } - return + t.bound = false + return t.h.Sum() } // Verify the consistency of the claimed output with the claimed input. // Unlike in Prove, the assignment argument need not be complete. -func Verify(api frontend.API, c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { - o, err := setup(api, c, schedule, assignment, transcriptSettings, options...) - if err != nil { - return err +func Verify(api frontend.API, c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, proof Proof, h hash.FieldHasher) error { + nbVars := assignment.NbVars() + if 1<= 0; levelI-- { - switch s := schedule[levelI].(type) { - case constraint.GkrSkipLevel: - levelPoints[levelI] = levelPoints[s.ClaimSources[0]] + level := schedule[levelI] - case constraint.GkrSumcheckLevel: - nbClaims := gkrcore.NbClaims(s) - levelPrefix := o.transcriptPrefix + "l" + strconv.Itoa(levelI) + "." + if skipLevel, isSkip := level.(constraint.GkrSkipLevel); isSkip { + outPoints := make([][]frontend.Variable, level.NbOutgoingEvalPoints()) + for k, src := range skipLevel.ClaimSources { + outPoints[k] = outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] + } + outgoingEvalPoints[levelI] = outPoints + } else { + nbClaims := level.NbClaims() foldingCoeff := frontend.Variable(0) if nbClaims >= 2 { - foldName := levelPrefix + "fold" - if err = o.transcript.Bind(foldName, baseChallenge); err != nil { - return err - } - if foldingCoeff, err = o.transcript.ComputeChallenge(foldName); err != nil { - return err - } - baseChallenge = nil + foldingCoeff = t.getChallenge() } - // Collect y-values - var ys []frontend.Variable - for _, group := range s { + var claimedEvals []frontend.Variable + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { - evalI := 0 - for _, src := range group.ClaimSources { - var y frontend.Variable - if src == initialChallengeI { - y = assignment[wI].Evaluate(api, levelPoints[src]) + for claimI, src := range group.ClaimSources { + var claimedEval frontend.Variable + if src.Level == initialChallengeI { + claimedEval = assignment[wI].Evaluate(api, outgoingEvalPoints[src.Level][src.OutgoingClaimIndex]) } else { - y = proof[src].FinalEvalProof[evalPositions[wI][evalI]] - evalI++ + idx := schedule[src.Level].FinalEvalProofIndex(uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex) + claimedEval = proof[src.Level].FinalEvalProof[idx] } - ys = append(ys, y) + claimedEvals = append(claimedEvals, claimedEval) } } } - - ysPoly := polynomial.Polynomial(ys) - claimedSum := ysPoly.Eval(api, foldingCoeff) + claimedSum := polynomial.Polynomial(claimedEvals).Eval(api, foldingCoeff) lazyClaims := &zeroCheckLazyClaims{ - foldingCoeff: foldingCoeff, - levelI: levelI, - schedule: schedule, - circuit: c, - assignment: assignment, - levelPoints: levelPoints, - evalPositions: evalPositions, - proof: proof, - nbVars: o.nbVars, - } - - var scBaseChallenges []frontend.Variable - if nbClaims < 2 { - scBaseChallenges = baseChallenge + foldingCoeff: foldingCoeff, + levelI: levelI, + schedule: schedule, + circuit: c, + assignment: assignment, + outgoingEvalPoints: outgoingEvalPoints, + proof: proof, + nbVars: nbVars, } - if err = verifySumcheck( + if err := verifySumcheck( api, lazyClaims, proof[levelI], claimedSum, - gkrcore.Degree(s, c), - fiatshamir.WithTranscript(o.transcript, levelPrefix, scBaseChallenges...), + c.ZeroCheckDegree(level.(constraint.GkrSumcheckLevel)), + t, ); err != nil { return fmt.Errorf("sumcheck proof rejected at level %d: %v", levelI, err) } - - baseChallenge = proof[levelI].FinalEvalProof } + + t.bind(proof[levelI].FinalEvalProof...) } return nil } @@ -311,13 +237,14 @@ func (p Proof) Serialize() []frontend.Variable { // from the size of the proof and the circuit/schedule structure. func ComputeLogNbInstances(circuit Circuit, schedule constraint.GkrProvingSchedule, serializedProofLen int) int { perVar := 0 - for _, step := range schedule { - s, ok := step.(constraint.GkrSumcheckLevel) - if !ok { - continue + for _, level := range schedule { + nbUniqueInputs := len(circuit.UniqueGateInputs(level)) + if _, isSkip := level.(constraint.GkrSkipLevel); isSkip { + serializedProofLen -= nbUniqueInputs * level.NbOutgoingEvalPoints() + } else { + perVar += circuit.ZeroCheckDegree(level.(constraint.GkrSumcheckLevel)) + serializedProofLen -= nbUniqueInputs } - perVar += gkrcore.Degree(s, circuit) - serializedProofLen -= len(gkrcore.UniqueGateInputs(s, circuit)) } if perVar == 0 { if serializedProofLen == 0 { @@ -350,17 +277,18 @@ func DeserializeProof(circuit Circuit, schedule constraint.GkrProvingSchedule, s logNbInstances := ComputeLogNbInstances(circuit, schedule, len(serializedProof)) reader := variablesReader(serializedProof) - for levelI, step := range schedule { - s, ok := step.(constraint.GkrSumcheckLevel) - if !ok { - continue - } - degree := gkrcore.Degree(s, circuit) - proof[levelI].PartialSumPolys = make([]polynomial.Polynomial, logNbInstances) - for j := range proof[levelI].PartialSumPolys { - proof[levelI].PartialSumPolys[j] = reader.nextN(degree) + for levelI, level := range schedule { + nbUniqueInputs := len(circuit.UniqueGateInputs(level)) + if _, isSkip := level.(constraint.GkrSkipLevel); isSkip { + proof[levelI].FinalEvalProof = reader.nextN(nbUniqueInputs * level.NbOutgoingEvalPoints()) + } else { + degree := circuit.ZeroCheckDegree(level.(constraint.GkrSumcheckLevel)) + proof[levelI].PartialSumPolys = make([]polynomial.Polynomial, logNbInstances) + for j := range proof[levelI].PartialSumPolys { + proof[levelI].PartialSumPolys[j] = reader.nextN(degree) + } + proof[levelI].FinalEvalProof = reader.nextN(nbUniqueInputs) } - proof[levelI].FinalEvalProof = reader.nextN(len(gkrcore.UniqueGateInputs(s, circuit))) } if reader.hasNextN(1) { return nil, fmt.Errorf("proof too long: expected %d encountered %d", len(serializedProof)-len(reader), len(serializedProof)) diff --git a/internal/gkr/gkr_test.go b/internal/gkr/gkr_test.go index 916764ffe9..93fb91697c 100644 --- a/internal/gkr/gkr_test.go +++ b/internal/gkr/gkr_test.go @@ -14,7 +14,6 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/consensys/gnark/internal/gkr/gkrtesting" - fiatshamir "github.com/consensys/gnark/std/fiat-shamir" "github.com/consensys/gnark/std/hash" "github.com/consensys/gnark/std/polynomial" "github.com/consensys/gnark/test" @@ -130,7 +129,7 @@ func (c *GkrVerifierCircuit) Define(api frontend.API) error { } } - return Verify(api, testCase.Circuit, testCase.Schedule, assignment, proof, fiatshamir.WithHash(hsh)) + return Verify(api, testCase.Circuit, testCase.Schedule, assignment, proof, hsh) } func makeInOutAssignment(c Circuit, inputValues [][]frontend.Variable, outputValues [][]frontend.Variable) WireAssignment { diff --git a/internal/gkr/gkrcore/serialize.go b/internal/gkr/gkrcore/serialize.go deleted file mode 100644 index d62bbeaae0..0000000000 --- a/internal/gkr/gkrcore/serialize.go +++ /dev/null @@ -1,197 +0,0 @@ -package gkrcore - -import ( - "encoding/binary" - "fmt" - "io" - "math/big" - - "github.com/consensys/gnark/constraint" -) - -// Common serialization utilities - -func writeUint8(w io.Writer, x int) error { - if x >= 256 || x < 0 { - return fmt.Errorf("%d out of range", x) - } - _, err := w.Write([]byte{byte(x)}) - return err -} - -func writeUint16[T int | uint16](w io.Writer, x T) error { - var buf [2]byte - if int(x) >= 65536 || x < 0 { - return fmt.Errorf("%d out of range", x) - } - binary.LittleEndian.PutUint16(buf[:], uint16(x)) - _, err := w.Write(buf[:]) - return err -} - -func writeBool(w io.Writer, b bool) error { - var v byte - if b { - v = 1 - } - _, err := w.Write([]byte{v}) - return err -} - -func writeUint16Slice[T int | uint16](w io.Writer, s []T) error { - if err := writeUint16(w, len(s)); err != nil { - return err - } - for _, v := range s { - if err := writeUint16(w, v); err != nil { - return err - } - } - return nil -} - -func writeBigInt(w io.Writer, x *big.Int) error { - bytes := x.Bytes() - if err := writeUint8(w, len(bytes)); err != nil { - return err - } - _, err := w.Write(bytes) - return err -} - -// SerializeCircuit writes a SerializableCircuit to w in deterministic binary format, -// primarily for hashing circuits to create unique identifiers. -// -// The encoding is compact (uint16 for counts/indices, uint8 for bigint byte lengths) and -// uses little-endian throughout. Gate metadata (NbIn, Degree, SolvableVar) is omitted -// since it can be recomputed from bytecode on deserialization. -// -// Format: -// -// Circuit: [wire_count:u16] [wire...] -// Wire: [input_count:u16] [input_indices:u16...] [exported:bool] [gate] -// Gate (non-input only): [const_count:u16] [constants...] [inst_count:u16] [instructions...] -// Constant: [byte_len:u8] [bytes...] -// Instruction: [op:u8] [input_count:u16] [input_indices:u16...] -func SerializeCircuit(w io.Writer, c SerializableCircuit) error { - if len(c) >= 1<<32 { - return fmt.Errorf("circuit length too large: %d", len(c)) - } - - // Write the number of wires - if err := writeUint16(w, len(c)); err != nil { - return err - } - - // Write each wire - for i := range c { - wire := &c[i] - - if err := writeUint16Slice(w, wire.Inputs); err != nil { - return err - } - - // Write the exported flag - if err := writeBool(w, wire.Exported); err != nil { - return err - } - - // If not an input wire, write gate information - if !wire.IsInput() { - gate := &wire.Gate - - // Write the bytecode - bytecode := &gate.Evaluate - - // Write the constants - if err := writeUint16(w, len(bytecode.Constants)); err != nil { - return err - } - for _, constant := range bytecode.Constants { - if err := writeBigInt(w, constant); err != nil { - return err - } - } - - // Write the instructions - if err := writeUint16(w, len(bytecode.Instructions)); err != nil { - return err - } - for _, inst := range bytecode.Instructions { - // Write the operation - if _, err := w.Write([]byte{byte(inst.Op)}); err != nil { - return err - } - - // Write the instruction inputs - if err := writeUint16Slice(w, inst.Inputs); err != nil { - return err - } - } - } - } - - return nil -} - -// SerializeSchedule writes a GkrProvingSchedule to w in deterministic binary format, -// primarily for hashing schedules to create unique identifiers. -// -// The encoding uses uint16 for counts/indices and little-endian throughout. -// -// Format: -// -// Schedule: [level_count:u16] [level...] -// Level: [skip_sumcheck:bool] [group_count:u16] [claim_group...] -// GkrClaimGroup: [wire_count:u16] [wire_indices:u16...] [source_count:u16] [claim_source...] -// GkrClaimSource: [level:u16] [outgoing_claim_index:u16] -func SerializeSchedule(w io.Writer, s constraint.GkrProvingSchedule) error { - if len(s) >= 65536 { - return fmt.Errorf("schedule length too large: %d", len(s)) - } - - writeClaimGroup := func(cg constraint.GkrClaimGroup) error { - if err := writeUint16Slice(w, cg.Wires); err != nil { - return err - } - - // Write claim source count; each source is two uint16s: Level and OutgoingClaimIndex - if err := writeUint16(w, len(cg.ClaimSources)); err != nil { - return err - } - for _, src := range cg.ClaimSources { - if err := writeUint16(w, src.Level); err != nil { - return err - } - if err := writeUint16(w, src.OutgoingClaimIndex); err != nil { - return err - } - } - - return nil - } - - // Write number of levels - if err := writeUint16(w, len(s)); err != nil { - return err - } - - // Write each level - for _, level := range s { - _, isSkip := level.(constraint.GkrSkipLevel) - if err := writeBool(w, isSkip); err != nil { - return err - } - groups := level.ClaimGroups() - if err := writeUint16(w, len(groups)); err != nil { - return err - } - for _, cg := range groups { - if err := writeClaimGroup(cg); err != nil { - return err - } - } - } - - return nil -} diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index 1a629141d7..a645a5c5c0 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -7,7 +7,9 @@ import ( "math/big" "os" "path/filepath" + "strconv" "sync" + "testing" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" @@ -355,3 +357,23 @@ func Poseidon2Circuit(nbFullRounds, nbPartialRounds int) gkrcore.RawCircuit { return c } + +var testManyInstancesLogMaxInstances = -1 + +func GetLogMaxInstances(t *testing.T) int { + if testManyInstancesLogMaxInstances == -1 { + + s := os.Getenv("GKR_LOG_INSTANCES") + if s == "" { + testManyInstancesLogMaxInstances = 5 + } else { + var err error + testManyInstancesLogMaxInstances, err = strconv.Atoi(s) + if err != nil { + t.Error(err) + } + } + + } + return testManyInstancesLogMaxInstances +} \ No newline at end of file diff --git a/internal/gkr/sumcheck.go b/internal/gkr/sumcheck.go index b0c46356e3..50ee4abc5c 100644 --- a/internal/gkr/sumcheck.go +++ b/internal/gkr/sumcheck.go @@ -2,10 +2,8 @@ package gkr import ( "errors" - "strconv" "github.com/consensys/gnark/frontend" - fiatshamir "github.com/consensys/gnark/std/fiat-shamir" "github.com/consensys/gnark/std/polynomial" ) @@ -24,38 +22,7 @@ type sumcheckProof struct { FinalEvalProof []frontend.Variable } -func setupTranscript(api frontend.API, varsNum int, settings *fiatshamir.Settings) ([]string, error) { - challengeNames := make([]string, varsNum) - prefix := settings.Prefix + "pSP." - for i := 0; i < varsNum; i++ { - challengeNames[i] = prefix + strconv.Itoa(i) - } - if settings.Transcript == nil { - settings.Transcript = fiatshamir.NewTranscript(api, settings.Hash, challengeNames) - } - - return challengeNames, settings.Transcript.Bind(challengeNames[0], settings.BaseChallenges) -} - -func next(transcript *fiatshamir.Transcript, bindings []frontend.Variable, remainingChallengeNames *[]string) (frontend.Variable, error) { - challengeName := (*remainingChallengeNames)[0] - if err := transcript.Bind(challengeName, bindings); err != nil { - return nil, err - } - - res, err := transcript.ComputeChallenge(challengeName) - *remainingChallengeNames = (*remainingChallengeNames)[1:] - return res, err -} - -func verifySumcheck(api frontend.API, claims sumcheckLazyClaims, proof sumcheckProof, claimedSum frontend.Variable, degree int, transcriptSettings fiatshamir.Settings) error { - - remainingChallengeNames, err := setupTranscript(api, claims.varsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return err - } - +func verifySumcheck(api frontend.API, claims sumcheckLazyClaims, proof sumcheckProof, claimedSum frontend.Variable, degree int, t *transcript) error { r := make([]frontend.Variable, claims.varsNum()) gJ := make(polynomial.Polynomial, degree+1) @@ -69,13 +36,10 @@ func verifySumcheck(api frontend.API, claims sumcheckLazyClaims, proof sumcheckP copy(gJ[1:], partialSumPoly) gJ[0] = api.Sub(gJR, partialSumPoly[0]) // Requirement that gⱼ(0) + gⱼ(1) = gⱼ₋₁(r) - if r[j], err = next(transcript, proof.PartialSumPolys[j], &remainingChallengeNames); err != nil { - return err - } + r[j] = t.getChallenge(proof.PartialSumPolys[j]...) gJR = polynomial.InterpolateLDE(api, r[j], gJ[:(degree+1)]) } return claims.verifyFinalEval(api, r, gJR, proof.FinalEvalProof) - } diff --git a/std/gkrapi/api_test.go b/std/gkrapi/api_test.go index 5f839f6f6a..0b83fd81e0 100644 --- a/std/gkrapi/api_test.go +++ b/std/gkrapi/api_test.go @@ -778,3 +778,97 @@ func TestMulti(t *testing.T) { test.NewAssert(t).CheckCircuit(new(testMultiCircuit), test.WithValidAssignment(&assignment)) } + +// Poseidon2 gate functions — match gkrtesting.Poseidon2Circuit(4, 2) exactly. + +func p2ExtLinear0(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { + return api.Add(x[0], x[0], x[1]) // 2*x[0] + x[1] +} + +func p2ExtLinear1(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { + return api.Add(x[0], x[1], x[1]) // x[0] + 2*x[1] +} + +func p2IntLinear1(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { + return api.Add(x[0], x[1], x[1], x[1]) // x[0] + 3*x[1] +} + +func p2SBox(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { + return api.Mul(x[0], x[0]) // x^2 +} + +func p2FeedForward(api gkr.GateAPI, x ...frontend.Variable) frontend.Variable { + return api.Add(x[0], x[0], x[1], x[2]) // 2*x[0] + x[1] + x[2] +} + +type poseidon2GadgetCircuit struct { + In0 []frontend.Variable + In1 []frontend.Variable + hashName string +} + +func (c *poseidon2GadgetCircuit) Define(api frontend.API) error { + gkrApi, err := New(api) + if err != nil { + return err + } + in0 := gkrApi.NewInput() + in1 := gkrApi.NewInput() + s0, s1 := in0, in1 + + appendFullRound := func() { + lin0 := gkrApi.Gate(p2ExtLinear0, s0, s1) + lin1 := gkrApi.Gate(p2ExtLinear1, s0, s1) + s0 = gkrApi.Gate(p2SBox, lin0) + s1 = gkrApi.Gate(p2SBox, lin1) + } + appendPartialRound := func() { + lin0 := gkrApi.Gate(p2ExtLinear0, s0, s1) + lin1 := gkrApi.Gate(p2IntLinear1, s0, s1) + s0 = gkrApi.Gate(p2SBox, lin0) + s1 = lin1 + } + + // 4 full rounds, 2 partial rounds: 2 + 2 structure + appendFullRound() + appendFullRound() + appendPartialRound() + appendPartialRound() + appendFullRound() + appendFullRound() + + _ = gkrApi.Gate(p2FeedForward, s0, s1, in1) + + gkrCircuit, err := gkrApi.Compile(c.hashName) + if err != nil { + return err + } + + instanceIn := make(map[gkr.Variable]frontend.Variable) + for i := range c.In0 { + instanceIn[in0] = c.In0[i] + instanceIn[in1] = c.In1[i] + if _, err := gkrCircuit.AddInstance(instanceIn); err != nil { + return fmt.Errorf("failed to add instance: %w", err) + } + } + return nil +} + +func TestPoseidon2Gadget(t *testing.T) { + assert := test.NewAssert(t) + nbInstances := 2 + In0 := make([]frontend.Variable, nbInstances) + In1 := make([]frontend.Variable, nbInstances) + for i := range In0 { + In0[i] = i + 1 + In1[i] = 2 * (i + 1) + } + assignment := &poseidon2GadgetCircuit{In0: In0, In1: In1} + circuit := &poseidon2GadgetCircuit{ + In0: make([]frontend.Variable, nbInstances), + In1: make([]frontend.Variable, nbInstances), + hashName: "-1", + } + assert.CheckCircuit(circuit, test.WithValidAssignment(assignment), test.WithCurves(ecc.BN254)) +} diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index a3ed22b540..9acd761e96 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -14,7 +14,6 @@ import ( gkrbw6761 "github.com/consensys/gnark/internal/gkr/bw6-761" "github.com/consensys/gnark/internal/gkr/gkrcore" "github.com/consensys/gnark/internal/utils" - fiatshamir "github.com/consensys/gnark/std/fiat-shamir" "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/consensys/gnark/std/hash" "github.com/consensys/gnark/std/multicommit" @@ -72,7 +71,7 @@ func WithInitialChallenge(getInitialChallenge InitialChallengeGetter) CompileOpt // From this point on, the circuit cannot be modified, // but instances can be added to it. func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*Circuit, error) { - // Dispatch to curve-specific factory + // Dispatch to a curve-specific factory compiler := api.parentApi.Compiler() field := compiler.Field() curveID := utils.FieldToCurve(field) @@ -294,23 +293,24 @@ func (c *Circuit) verify(api frontend.API, circuit gkrcore.GadgetCircuit, initia return err } - return gadget.Verify(api, circuit, c.schedule, c.assignments, proof, fiatshamir.WithHash(hsh, initialChallenges...)) + hsh.Write(initialChallenges...) + return gadget.Verify(api, circuit, c.schedule, c.assignments, proof, hsh) } // GetValue is a debugging utility returning the value of variable v at instance i. // While v can be an input or output variable, GetValue is most useful for querying intermediate values in the circuit. func (c *Circuit) GetValue(v gkr.Variable, i int) frontend.Variable { - // Create an instruction that will retrieve the assignment at solve time + // Create an instruction that will retrieve the assignment at solving time compiler := c.api.Compiler() // Build calldata: [0]=totalSize, [1]=wireI, [2]=instanceI, [3...]=dependency_wire_as_linear_expression - // The dependency ensures this instruction runs after the solve instruction for instance i + // The dependency ensures this instruction runs after the solving instruction for instance i calldata := make([]uint32, 3, 6) // pre-allocate: size + wireI + instanceI + dependency linear expression (typically 3) calldata[1] = uint32(v) calldata[2] = uint32(i) // Use the first output variable from instance i as a dependency - // This ensures the solve instruction for this instance has completed + // This ensures the solving instruction for this instance has completed if len(c.outs) == 0 || i >= len(c.assignments[c.outs[0]]) { panic("GetValue called with invalid instance or before instance was added") } From e634ee14953eac2231c51022f624350a5e2df63d Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 16 Mar 2026 01:42:52 -0500 Subject: [PATCH 243/251] generification --- constraint/gkr.go | 13 + .../backend/template/gkr/blueprint.go.tmpl | 2 +- .../backend/template/gkr/gkr.go.tmpl | 246 +++++++++++------- .../backend/template/gkr/gkr.test.go.tmpl | 174 ++++++++----- .../backend/template/gkr/sumcheck.go.tmpl | 10 +- internal/gkr/bls12-377/blueprint.go | 2 +- internal/gkr/bls12-377/gkr.go | 244 ++++++++++------- internal/gkr/bls12-377/gkr_test.go | 174 ++++++++----- internal/gkr/bls12-377/sumcheck.go | 10 +- internal/gkr/bls12-381/blueprint.go | 2 +- internal/gkr/bls12-381/gkr.go | 244 ++++++++++------- internal/gkr/bls12-381/gkr_test.go | 174 ++++++++----- internal/gkr/bls12-381/sumcheck.go | 10 +- internal/gkr/bn254/blueprint.go | 2 +- internal/gkr/bn254/gkr.go | 24 +- internal/gkr/bn254/gkr_test.go | 11 +- internal/gkr/bn254/sumcheck.go | 10 +- internal/gkr/bw6-761/blueprint.go | 2 +- internal/gkr/bw6-761/gkr.go | 244 ++++++++++------- internal/gkr/bw6-761/gkr_test.go | 174 ++++++++----- internal/gkr/bw6-761/sumcheck.go | 10 +- internal/gkr/gkr.go | 179 ++++++------- internal/gkr/gkrcore/types.go | 4 + internal/gkr/gkrtesting/gkrtesting.go | 2 +- internal/gkr/small_rational/gkr.go | 244 ++++++++++------- internal/gkr/small_rational/sumcheck.go | 10 +- internal/gkr/sumcheck.go | 26 ++ .../single_identity_gate_two_instances.json | 11 +- ...nput_two_identity_gates_two_instances.json | 23 +- .../single_input_two_outs_two_instances.json | 11 +- .../single_mimc_gate_four_instances.json | 8 +- .../single_mimc_gate_two_instances.json | 8 +- .../single_mul_gate_two_instances.json | 8 +- .../testdata/gkr_circuit_bn254.scs | Bin 69707 -> 33711 bytes ...s_composed_single_input_two_instances.json | 18 +- ...uts_select-input-3_gate_two_instances.json | 15 +- 36 files changed, 1410 insertions(+), 939 deletions(-) diff --git a/constraint/gkr.go b/constraint/gkr.go index 1350f3bb9e..f295f82de4 100644 --- a/constraint/gkr.go +++ b/constraint/gkr.go @@ -65,3 +65,16 @@ func (l GkrSkipLevel) ClaimGroups() []GkrClaimGroup { return []GkrClaimGroup{Gkr func (l GkrSkipLevel) FinalEvalProofIndex(wireI, evaluationPointI int) int { return wireI*l.NbOutgoingEvalPoints() + evaluationPointI } + +// BindGkrFinalEvalProof binds the non-input-wire entries of finalEvalProof into the transcript. +// Input-wire evaluations are fully determined by the public assignment (and by evaluation points +// already committed to the transcript), so hashing them contributes nothing to Fiat-Shamir security. +// uniqueGateInputs is the deduplicated list of gate-input wire indices for the level in the same +// order as the finalEvalProof entries (i.e. the order returned by UniqueGateInputs). +func BindGkrFinalEvalProof[F any](transcript interface{ Bind(...F) }, finalEvalProof []F, uniqueGateInputs []int, isInput func(wireI int) bool, level GkrProvingLevel) { + for i, inputWireI := range uniqueGateInputs { + if !isInput(inputWireI) { + transcript.Bind(finalEvalProof[level.FinalEvalProofIndex(i, 0):level.FinalEvalProofIndex(i+1, 0)]...) + } + } +} diff --git a/internal/generator/backend/template/gkr/blueprint.go.tmpl b/internal/generator/backend/template/gkr/blueprint.go.tmpl index 3558aba064..7c5492d172 100644 --- a/internal/generator/backend/template/gkr/blueprint.go.tmpl +++ b/internal/generator/backend/template/gkr/blueprint.go.tmpl @@ -297,7 +297,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return gkrcore.ProofSize(b.Schedule, b.SolveBlueprint.Circuit, logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(b.Schedule, logNbInstances) } // NbOutputs implements Blueprint diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 85891ce98b..48a904f414 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -20,7 +20,7 @@ type ( // The goal is to prove/verify evaluations of many instances of the same circuit -// WireAssignment is assignment of values to the same wire across many instances of the circuit +// WireAssignment is the assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) @@ -40,7 +40,7 @@ func (e *zeroCheckLazyClaims) varsNum() int { } func (e *zeroCheckLazyClaims) degree(int) int { - return gkrcore.Degree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel), e.resources.circuit) + return e.resources.circuit.ZeroCheckDegree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel)) } // verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. @@ -55,13 +55,13 @@ func (e *zeroCheckLazyClaims) degree(int) int { // The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming // that the full sum matches purportedValue. func (e *zeroCheckLazyClaims) verifyFinalEval(r []{{ .ElementType }}, purportedValue {{ .ElementType }}, uniqueInputEvaluations []{{ .ElementType }}) error { - e.resources.levelPoints[e.levelI] = r - level := e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel) - perWireInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) + e.resources.outgoingEvalPoints[e.levelI] = [][]{{ .ElementType }}{r} + level := e.resources.schedule[e.levelI] + gateInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) - var terms []{{ .ElementType }} - flatW := 0 - for _, group := range level { + var claimedEvals polynomial.Polynomial + levelWireI := 0 + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := e.resources.circuit[wI] @@ -70,27 +70,26 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []{{ .ElementType }}, purportedV gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) } else { evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) - for _, v := range perWireInputEvals[flatW] { + for _, v := range gateInputEvals[levelWireI] { evaluator.pushInput(v) } gateEval.Set(evaluator.evaluate()) } for _, src := range group.ClaimSources { - eq := polynomial.EvalEq(e.resources.levelPoints[src], r) + eq := polynomial.EvalEq(e.resources.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], r) var term {{ .ElementType }} term.Mul(&eq, &gateEval) - terms = append(terms, term) + claimedEvals = append(claimedEvals, term) } - flatW++ + levelWireI++ } } - ys := polynomial.Polynomial(terms) - if total := ys.Eval(&e.foldingCoeff); total.Equal(&purportedValue) { - return nil + if total := claimedEvals.Eval(&e.foldingCoeff); !total.Equal(&purportedValue) { + return errors.New("incompatible evaluations") } - return errors.New("incompatible evaluations") + return nil } // zeroCheckClaims is a claim for sumcheck (prover side). @@ -98,7 +97,6 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []{{ .ElementType }}, purportedV // where the sum runs over all (wire v, claim source s) pairs in the level. // Each wire has its own eq table with the batching coefficients baked in. type zeroCheckClaims struct { - level constraint.GkrSumcheckLevel levelI int resources *resources input []polynomial.MultiLin // UniqueGateInputs order @@ -108,7 +106,7 @@ type zeroCheckClaims struct { } func (c *zeroCheckClaims) varsNum() int { - return len(c.resources.levelPoints[c.level[0].ClaimSources[0]]) + return c.resources.nbVars } // roundPolynomial computes gⱼ = ∑ₕ ∑ᵥ eqs[v](Xⱼ, h...) · gateᵥ(inputs(Xⱼ, h...)). @@ -116,7 +114,8 @@ func (c *zeroCheckClaims) varsNum() int { // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). // By convention, g₀ is a constant polynomial equal to the claimed sum. func (c *zeroCheckClaims) roundPolynomial() polynomial.Polynomial { - degree := gkrcore.Degree(c.level, c.resources.circuit) + level := c.resources.schedule[c.levelI].(constraint.GkrSumcheckLevel) + degree := c.resources.circuit.ZeroCheckDegree(level) nbUniqueInputs := len(c.input) nbWires := len(c.eqs) @@ -223,7 +222,7 @@ func (c *zeroCheckClaims) roundFold(r {{ .ElementType }}) { // proveFinalEval provides the unique input wire values wᵢ(r₁, ..., rₙ). func (c *zeroCheckClaims) proveFinalEval(r []{{ .ElementType }}) []{{ .ElementType }} { - c.resources.levelPoints[c.levelI] = r + c.resources.outgoingEvalPoints[c.levelI] = [][]{{ .ElementType }}{r} evaluations := make([]{{ .ElementType }}, len(c.input)) for i := range c.input { c.input[i].Fold(r[len(r)-1]) @@ -280,18 +279,18 @@ func (r *resources) eqAcc(e, m polynomial.MultiLin, q []{{ .ElementType }}) { } type resources struct { - // levelPoints[i] is the sumcheck evaluation point r produced at schedule level i. - // levelPoints[len(schedule)] holds the initial challenge (firstChallenge / rho). - // SkipLevels produce no point; their slot remains nil. - levelPoints [][]{{ .ElementType }} - evalPositions [][]int // [wireI][evalI] → position in source level's finalEvalProof - nbVars int - assignment WireAssignment - memPool polynomial.Pool - workers *utils.WorkerPool - circuit Circuit - schedule constraint.GkrProvingSchedule - transcript transcript + // outgoingEvalPoints[i][k] is the k-th outgoing evaluation point (evaluation challenge) produced at schedule level i. + // outgoingEvalPoints[len(schedule)][0] holds the initial challenge (firstChallenge / rho). + // SumcheckLevels produce one point (k=0). SkipLevels pass on all their evaluation points. + outgoingEvalPoints [][][]{{ .ElementType }} + nbVars int + assignment WireAssignment + memPool polynomial.Pool + workers *utils.WorkerPool + circuit Circuit + schedule constraint.GkrProvingSchedule + transcript transcript + uniqueInputIndices [][]int // uniqueInputIndices[wI][claimI]: w's unique-input index in the layer its claimI-th evaluation is coming from } func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (resources, error) { @@ -302,43 +301,109 @@ func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment } return resources{ - levelPoints: make([][]{{ .ElementType }}, len(schedule)+1), - evalPositions: gkrcore.BuildFinalEvalPositions(schedule, c), - nbVars: nbVars, - assignment: assignment, - memPool: polynomial.NewPool(c.MemoryRequirements(nbInstances)...), - workers: utils.NewWorkerPool(), - circuit: c, - schedule: schedule, - transcript: transcript{h: hasher}, + outgoingEvalPoints: make([][][]{{ .ElementType }}, len(schedule)+1), + nbVars: nbVars, + assignment: assignment, + memPool: polynomial.NewPool(c.MemoryRequirements(nbInstances)...), + workers: utils.NewWorkerPool(), + circuit: c, + schedule: schedule, + transcript: transcript{h: hasher}, + uniqueInputIndices: c.UniqueInputIndices(schedule), }, nil } -// proveLevel runs the sumcheck for a SumcheckLevel: derives the fold challenge, -// builds per-wire eq tables, constructs zeroCheckClaims, and calls sumcheckProve. -func (r *resources) proveLevel(levelI int) sumcheckProof { - level := r.schedule[levelI].(constraint.GkrSumcheckLevel) +// collectOutgoingEvalPoints sets the outgoing evaluation points of a skip level, equal to its incoming ones. +func (r *resources) collectOutgoingEvalPoints(levelI int) [][]{{ .ElementType }} { + level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := make([][]{{ .ElementType }}, level.NbOutgoingEvalPoints()) + for k, src := range level.ClaimSources { + outPoints[k] = r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] + } + r.outgoingEvalPoints[levelI] = outPoints + return outPoints +} + +// proveSkipLevel evaluates each unique gate input at each inherited evaluation point and records +// the outgoing evaluation points on the resources. +func (r *resources) proveSkipLevel(levelI int) sumcheckProof { + outPoints := r.collectOutgoingEvalPoints(levelI) + level := r.schedule[levelI].(constraint.GkrSkipLevel) + + uniqueInputs := r.circuit.UniqueGateInputs(level) + evals := make([]{{ .ElementType }}, len(uniqueInputs)*len(outPoints)) + for uiI, inW := range uniqueInputs { + for k, point := range outPoints { + evals[level.FinalEvalProofIndex(uiI, k)] = r.assignment[inW].Evaluate(point, &r.memPool) + } + } + return sumcheckProof{finalEvalProof: evals} +} + +// verifySkipLevel verifies a SkipSumcheck level: checks that the finalEvalProof +// is consistent with the assignment and gate evaluations, and records outgoing eval points. +func (r *resources) verifySkipLevel(levelI int, proof Proof) error { + outPoints := r.collectOutgoingEvalPoints(levelI) + level := r.schedule[levelI].(constraint.GkrSkipLevel) - nbClaims := gkrcore.NbClaims(level) + finalEval := proof[levelI].finalEvalProof + _, inputIndices := r.circuit.InputMapping(level) + group := constraint.GkrClaimGroup(level) + initialChallengeI := len(r.schedule) + + for levelWireI, wI := range group.Wires { + wire := r.circuit[wI] + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) + for claimI, src := range group.ClaimSources { + point := outPoints[claimI] + var gateEval {{ .ElementType }} + if wire.IsInput() { + gateEval = r.assignment[wI].Evaluate(point, &r.memPool) + if claimed := finalEval[level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI)]; !claimed.Equal(&gateEval) { + return fmt.Errorf("level %d wire %d: finalEvalProof[%d] = %v, want %v", levelI, wI, level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI), &claimed, &gateEval) + } + } else { + for _, inI := range inputIndices[levelWireI] { + evaluator.pushInput(finalEval[level.FinalEvalProofIndex(inI, claimI)]) + } + gateEval.Set(evaluator.evaluate()) + } + var claimedEval {{ .ElementType }} + if src.Level == initialChallengeI { + claimedEval = r.assignment[wI].Evaluate(point, &r.memPool) + } else { + claimedEval = proof[src.Level].finalEvalProof[r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex)] + } + if !claimedEval.Equal(&gateEval) { + return fmt.Errorf("level %d wire %d claim %d: claimed eval %v disagrees with gate eval %v", levelI, wI, claimI, &claimedEval, &gateEval) + } + } + } + return nil +} + +func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { + level := r.schedule[levelI] + nbClaims := level.NbClaims() var foldingCoeff {{ .ElementType }} if nbClaims >= 2 { foldingCoeff = r.transcript.getChallenge() } - uniqueInputs, inputIndices := gkrcore.InputMapping(level, r.circuit) + uniqueInputs, inputIndices := r.circuit.InputMapping(level) input := make([]polynomial.MultiLin, len(uniqueInputs)) for i, inW := range uniqueInputs { input[i] = r.memPool.Clone(r.assignment[inW]) } nbWires := 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { nbWires += len(group.Wires) } pools := make([]*gateEvaluatorPool, nbWires) flatW := 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := r.circuit[wI] gate := wire.Gate.Evaluate @@ -355,12 +420,12 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { var alpha {{ .ElementType }} alpha.SetOne() flatW = 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { nbSources := len(group.ClaimSources) groupEq := polynomial.MultiLin(r.memPool.Make(eqLength)) groupEq[0].Set(&alpha) - groupEq.Eq(r.levelPoints[group.ClaimSources[0]]) + groupEq.Eq(r.outgoingEvalPoints[group.ClaimSources[0].Level][group.ClaimSources[0].OutgoingClaimIndex]) if nbSources > 1 { newEq := polynomial.MultiLin(r.memPool.Make(eqLength)) @@ -368,7 +433,7 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { for k := 1; k < nbSources; k++ { aI.Mul(&aI, &foldingCoeff) newEq[0].Set(&aI) - r.eqAcc(groupEq, newEq, r.levelPoints[group.ClaimSources[k]]) + r.eqAcc(groupEq, newEq, r.outgoingEvalPoints[group.ClaimSources[k].Level][group.ClaimSources[k].OutgoingClaimIndex]) } r.memPool.Dump(newEq) } @@ -396,7 +461,6 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { } claims := &zeroCheckClaims{ - level: level, levelI: levelI, resources: r, input: input, @@ -407,44 +471,37 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { return sumcheckProve(claims, &r.transcript) } -// verifyLevel runs the sumcheck verification for a SumcheckLevel: derives the fold challenge, -// computes the claimed sum, and calls sumcheckVerify. -func (r *resources) verifyLevel(levelI int, proof Proof) error { - level := r.schedule[levelI].(constraint.GkrSumcheckLevel) - initialChallengeI := len(r.schedule) - - nbClaims := gkrcore.NbClaims(level) +func (r *resources) verifySumcheckLevel(levelI int, proof Proof) error { + level := r.schedule[levelI] + nbClaims := level.NbClaims() var foldingCoeff {{ .ElementType }} if nbClaims >= 2 { foldingCoeff = r.transcript.getChallenge() } - var ys []{{ .ElementType }} - for _, group := range level { + initialChallengeI := len(r.schedule) + claimedEvals := make(polynomial.Polynomial, 0, level.NbClaims()) + + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { - evalI := 0 - for _, src := range group.ClaimSources { - var y {{ .ElementType }} - if src == initialChallengeI { - y = r.assignment[wI].Evaluate(r.levelPoints[src], &r.memPool) + for claimI, src := range group.ClaimSources { + if src.Level == initialChallengeI { + claimedEvals = append(claimedEvals, r.assignment[wI].Evaluate(r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], &r.memPool)) } else { - y = proof[src].finalEvalProof[r.evalPositions[wI][evalI]] - evalI++ + claimedEvals = append(claimedEvals, proof[src.Level].finalEvalProof[r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex)]) } - ys = append(ys, y) } } } - ysPoly := polynomial.Polynomial(ys) - claimedSum := ysPoly.Eval(&foldingCoeff) + claimedSum := claimedEvals.Eval(&foldingCoeff) lazyClaims := &zeroCheckLazyClaims{ foldingCoeff: foldingCoeff, resources: r, levelI: levelI, } - return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, gkrcore.Degree(level, r.circuit), &r.transcript) + return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, r.circuit.ZeroCheckDegree(level.(constraint.GkrSumcheckLevel)), &r.transcript) } // Prove consistency of the claimed assignment @@ -457,23 +514,20 @@ func Prove(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAss proof := make(Proof, len(schedule)) - // Derive initial challenge point + // Derive the initial challenge point firstChallenge := make([]{{ .ElementType }}, r.nbVars) for j := range r.nbVars { firstChallenge[j] = r.transcript.getChallenge() } - r.levelPoints[len(schedule)] = firstChallenge + r.outgoingEvalPoints[len(schedule)] = [][]{{ .ElementType }}{firstChallenge} for levelI := len(schedule) - 1; levelI >= 0; levelI-- { - switch s := schedule[levelI].(type) { - case constraint.GkrSkipLevel: - r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - - case constraint.GkrSumcheckLevel: - proof[levelI] = r.proveLevel(levelI) - // Bind finalEvalProof for next level's challenge derivation - r.transcript.bind(proof[levelI].finalEvalProof...) + if _, isSkip := r.schedule[levelI].(constraint.GkrSkipLevel); isSkip { + proof[levelI] = r.proveSkipLevel(levelI) + } else { + proof[levelI] = r.proveSumcheckLevel(levelI) } + constraint.BindGkrFinalEvalProof(&r.transcript, proof[levelI].finalEvalProof, c.UniqueGateInputs(r.schedule[levelI]), c.IsInput, r.schedule[levelI]) } return proof, nil @@ -488,25 +542,23 @@ func Verify(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAs } defer r.workers.Stop() - // Derive initial challenge point + // Derive the initial challenge point firstChallenge := make([]{{ .ElementType }}, r.nbVars) for j := range r.nbVars { firstChallenge[j] = r.transcript.getChallenge() } - r.levelPoints[len(schedule)] = firstChallenge + r.outgoingEvalPoints[len(schedule)] = [][]{{ .ElementType }}{firstChallenge} for levelI := len(schedule) - 1; levelI >= 0; levelI-- { - switch s := schedule[levelI].(type) { - case constraint.GkrSkipLevel: - r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - - case constraint.GkrSumcheckLevel: - if err = r.verifyLevel(levelI, proof); err != nil { - return fmt.Errorf("sumcheck proof rejected: %v", err) - } - // Bind finalEvalProof for next level's challenge derivation - r.transcript.bind(proof[levelI].finalEvalProof...) + if _, isSkip := r.schedule[levelI].(constraint.GkrSkipLevel); isSkip { + err = r.verifySkipLevel(levelI, proof) + } else { + err = r.verifySumcheckLevel(levelI, proof) + } + if err != nil { + return fmt.Errorf("level %d: %v", levelI, err) } + constraint.BindGkrFinalEvalProof(&r.transcript, proof[levelI].finalEvalProof, c.UniqueGateInputs(r.schedule[levelI]), c.IsInput, r.schedule[levelI]) } return nil } @@ -599,7 +651,7 @@ func newGateEvaluator(gate gkrcore.GateBytecode, nbIn int, elementPool ...*polyn if len(elementPool) > 0 { e.vars = elementPool[0].Make(gate.NbConstants() + nbIn + len(gate.Instructions)) } else { - e.vars = make([]{{.ElementType}}, gate.NbConstants()+nbIn+len(gate.Instructions)) + e.vars = make([]{{ .ElementType }}, gate.NbConstants()+nbIn+len(gate.Instructions)) } e.vars = e.vars[:gate.NbConstants()] for i, constVal := range gate.Constants { @@ -717,7 +769,7 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() - // Return evaluator to pool (it keeps its vars slice from polynomial pool) + // Return evaluator to pool (it keeps its vars slice from the polynomial pool) gep.available[e] = struct{}{} } diff --git a/internal/generator/backend/template/gkr/gkr.test.go.tmpl b/internal/generator/backend/template/gkr/gkr.test.go.tmpl index 7da946057a..5977893dbf 100644 --- a/internal/generator/backend/template/gkr/gkr.test.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.test.go.tmpl @@ -5,7 +5,6 @@ import ( "os" "path/filepath" "reflect" - "strconv" "testing" "time" @@ -62,8 +61,12 @@ func TestMimc(t *testing.T) { test(t, gkrtesting.MiMCCircuit(93)) } -// testLevel exercises proveLevel/verifyLevel for a single sumcheck level. -func testLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrSumcheckLevel) { +func TestPoseidon2(t *testing.T) { + test(t, gkrtesting.Poseidon2Circuit(4, 2)) +} + +// testSumcheckLevel exercises proveSumcheckLevel/verifySumcheckLevel for a single sumcheck level. +func testSumcheckLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrProvingLevel) { t.Helper() _, sCircuit := cache.Compile(t, circuit) @@ -77,26 +80,26 @@ func testLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrSum assignment.Complete(sCircuit) schedule := constraint.GkrProvingSchedule{level} - challenge := []{{ .ElementType }}{five} + initEvalPoint := [][]{{ .ElementType }}{ {one} } // Prove proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) defer proveR.workers.Stop() - proveR.levelPoints[1] = challenge - proof := Proof{proveR.proveLevel(0)} + proveR.outgoingEvalPoints[len(schedule)] = initEvalPoint + proof := Proof{proveR.proveSumcheckLevel(0)} // Verify verifyR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) defer verifyR.workers.Stop() - verifyR.levelPoints[1] = challenge - assert.NoError(t, verifyR.verifyLevel(0, proof)) + verifyR.outgoingEvalPoints[len(schedule)] = initEvalPoint + assert.NoError(t, verifyR.verifySumcheckLevel(0, proof)) } -func TestLevel(t *testing.T) { +func TestSumcheckLevel(t *testing.T) { // Wires 0,1 = inputs; wires 2,3,4 = mul(0,1). All gates are independent outputs. circuit := gkrcore.RawCircuit{ {}, @@ -105,41 +108,115 @@ func TestLevel(t *testing.T) { {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, } - // All levels have initial challenge at index 1 (len(schedule) = 1). + // Each level has an initial challenge at index 1 (len(schedule) = 1). + // GkrClaimSource{Level:1} is the initial-challenge sentinel. tests := []struct { name string - level constraint.GkrSumcheckLevel + level constraint.GkrProvingLevel }{ { name: "single wire", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []int{1}}, + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{ {Level: 1} }}, }, }, { name: "two groups", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []int{1}}, - {Wires: []int{3}, ClaimSources: []int{1}}, + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{ {Level: 1} }}, + {Wires: []int{3}, ClaimSources: []constraint.GkrClaimSource{ {Level: 1} }}, }, }, { name: "one group with two wires", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4, 3}, ClaimSources: []int{1}}, + {Wires: []int{4, 3}, ClaimSources: []constraint.GkrClaimSource{ {Level: 1} }}, }, }, { name: "mixed: single + multi-wire group", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []int{1}}, - {Wires: []int{3, 2}, ClaimSources: []int{1}}, + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{ {Level: 1} }}, + {Wires: []int{3, 2}, ClaimSources: []constraint.GkrClaimSource{ {Level: 1} }}, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - testLevel(t, circuit, tc.level) + testSumcheckLevel(t, circuit, tc.level) + }) + } +} + +// testSkipLevel exercises proveSkipLevel/verifySkipLevel for a single skip level. +func testSkipLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrProvingLevel) { + t.Helper() + _, sCircuit := cache.Compile(t, circuit) + + ins := sCircuit.Inputs() + assignment := make(WireAssignment, len(sCircuit)) + for _, i := range ins { + assignment[i] = make([]{{ .ElementType }}, 2) + {{ .FieldPackageName }}.Vector(assignment[i]).MustSetRandom() + } + + assignment.Complete(sCircuit) + + schedule := constraint.GkrProvingSchedule{level} + initEvalPoint := [][]{{ .ElementType }}{ {one} } + + // Prove + proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) + assert.NoError(t, err) + defer proveR.workers.Stop() + + proveR.outgoingEvalPoints[len(schedule)] = initEvalPoint + proof := Proof{proveR.proveSkipLevel(0)} + + // Verify + verifyR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) + assert.NoError(t, err) + defer verifyR.workers.Stop() + + verifyR.outgoingEvalPoints[len(schedule)] = initEvalPoint + assert.NoError(t, verifyR.verifySkipLevel(0, proof)) +} + +func TestSkipLevel(t *testing.T) { + // Wires 0,1 = inputs; wires 2,3 = identity(0); wire 4 = add(0,1). All degree-1 outputs. + circuit := gkrcore.RawCircuit{ + {}, + {}, + {Gate: gkrcore.Identity, Inputs: []int{0}}, + {Gate: gkrcore.Identity, Inputs: []int{0}}, + {Gate: gkrcore.Add2, Inputs: []int{0, 1}}, + } + + // Single-claim cases: one inherited evaluation point (OutgoingClaimIndex always 0). + singleClaim := []struct { + name string + level constraint.GkrProvingLevel + }{ + { + name: "single input wire", + level: constraint.GkrSkipLevel{Wires: []int{0}, ClaimSources: []constraint.GkrClaimSource{ {Level: 1} }}, + }, + { + name: "single identity gate", + level: constraint.GkrSkipLevel{Wires: []int{2}, ClaimSources: []constraint.GkrClaimSource{ {Level: 1} }}, + }, + { + name: "add gate", + level: constraint.GkrSkipLevel{Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{ {Level: 1} }}, + }, + { + name: "two identity gates one group", + level: constraint.GkrSkipLevel{Wires: []int{2, 3}, ClaimSources: []constraint.GkrClaimSource{ {Level: 1} }}, + }, + } + for _, tc := range singleClaim { + t.Run(tc.name, func(t *testing.T) { + testSkipLevel(t, circuit, tc.level) }) } } @@ -155,26 +232,6 @@ func init() { six.Double(&three) } -var testManyInstancesLogMaxInstances = -1 - -func getLogMaxInstances(t *testing.T) int { - if testManyInstancesLogMaxInstances == -1 { - - s := os.Getenv("GKR_LOG_INSTANCES") - if s == "" { - testManyInstancesLogMaxInstances = 5 - } else { - var err error - testManyInstancesLogMaxInstances, err = strconv.Atoi(s) - if err != nil { - t.Error(err) - } - } - - } - return testManyInstancesLogMaxInstances -} - func test(t *testing.T, circuit gkrcore.RawCircuit) { testWithSchedule(t, circuit, nil) } @@ -188,7 +245,7 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constra } ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) - maxSize := 1 << getLogMaxInstances(t) + maxSize := 1 << gkrtesting.GetLogMaxInstances(t) for i := range ins { insAssignment[i] = make([]{{ .ElementType }}, maxSize) @@ -203,8 +260,6 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constra fullAssignment.Complete(sCircuit) - t.Log("Selected inputs for test") - proof, err := Prove(sCircuit, schedule, fullAssignment, newMessageCounter(1, 1)) assert.NoError(t, err) @@ -213,29 +268,11 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constra err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(1, 1)) assert.NoError(t, err, "proof rejected") - if proof.isEmpty() { // special case for TestNoGate: - continue // there's no way to make a trivial proof fail - } - err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(0, 1)) assert.NotNil(t, err, "bad proof accepted") } } -func (p Proof) isEmpty() bool { - for i := range p { - if len(p[i].finalEvalProof) != 0 { - return false - } - for j := range p[i].partialSumPolys { - if len(p[i].partialSumPolys[j]) != 0 { - return false - } - } - } - return true -} - func testNoGate(t *testing.T, inputAssignments ...[]{{ .ElementType }}) { _, c := cache.Compile(t, gkrtesting.NoGateCircuit()) @@ -269,10 +306,23 @@ func generateTestVerifier(path string) func(t *testing.T) { assert.NoError(t, err) err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) assert.NoError(t, err, "proof rejected") + testCase, err = newTestCase(path) assert.NoError(t, err) err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, newMessageCounter(2, 0)) assert.NotNil(t, err, "bad proof accepted") + + testCase, err = newTestCase(path) + assert.NoError(t, err) + testCase.InOutAssignment[len(testCase.InOutAssignment)-1][0].Add(&testCase.InOutAssignment[len(testCase.InOutAssignment)-1][0], &one) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) + assert.NotNil(t, err, "tampered output accepted") + + testCase, err = newTestCase(path) + assert.NoError(t, err) + testCase.InOutAssignment[0][0].Add(&testCase.InOutAssignment[0][0], &one) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) + assert.NotNil(t, err, "tampered input accepted") } } @@ -352,11 +402,11 @@ func TestSingleMulGateExplicitSchedule(t *testing.T) { _, sCircuit := cache.Compile(t, circuit) // Wire 2 is the mul gate output (inputs: 0, 1). - // Explicit schedule: one SumcheckLevel for wire 2 only. - // ClaimSources: [1] = initial challenge (index len(schedule) = 1). + // Explicit schedule: one GkrProvingLevel for wire 2. + // GkrClaimSource{Level:1} is the initial-challenge sentinel (len(schedule)=1). schedule := constraint.GkrProvingSchedule{ constraint.GkrSumcheckLevel{ - {Wires: []int{2}, ClaimSources: []int{1}}, + {Wires: []int{2}, ClaimSources: []constraint.GkrClaimSource{ {Level: 1} }}, }, } testWithSchedule(t, circuit, schedule) diff --git a/internal/generator/backend/template/gkr/sumcheck.go.tmpl b/internal/generator/backend/template/gkr/sumcheck.go.tmpl index 60b3c75664..bd5c2c2c28 100644 --- a/internal/generator/backend/template/gkr/sumcheck.go.tmpl +++ b/internal/generator/backend/template/gkr/sumcheck.go.tmpl @@ -9,15 +9,15 @@ import ( // This does not make use of parallelism and represents polynomials as lists of coefficients. // transcript is a Fiat-Shamir transcript backed by a running hash. -// Field elements are written via bind; challenges are derived via getChallenge. +// Field elements are written via Bind; challenges are derived via getChallenge. // The hash is never reset — all previous data is implicitly part of future challenges. type transcript struct { h hash.Hash - bound bool // whether bind was called since the last getChallenge + bound bool // whether Bind was called since the last getChallenge } -// bind writes field elements to the transcript as bindings for the next challenge. -func (t *transcript) bind(elements ...{{ .ElementType }}) { +// Bind writes field elements to the transcript as bindings for the next challenge. +func (t *transcript) Bind(elements ...{{ .ElementType }}) { if len(elements) == 0 { return } @@ -32,7 +32,7 @@ func (t *transcript) bind(elements ...{{ .ElementType }}) { // If no bindings were added since the last squeeze, a separator byte is written first // to advance the state and prevent repeated values. func (t *transcript) getChallenge(bindings ...{{ .ElementType }}) {{ .ElementType }} { - t.bind(bindings...) + t.Bind(bindings...) if !t.bound { t.h.Write([]byte{0}) } diff --git a/internal/gkr/bls12-377/blueprint.go b/internal/gkr/bls12-377/blueprint.go index 9461400e99..f5d004f0a1 100644 --- a/internal/gkr/bls12-377/blueprint.go +++ b/internal/gkr/bls12-377/blueprint.go @@ -304,7 +304,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return gkrcore.ProofSize(b.Schedule, b.SolveBlueprint.Circuit, logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(b.Schedule, logNbInstances) } // NbOutputs implements Blueprint diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 3ff62f2c0a..4db8d3dd6a 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -27,7 +27,7 @@ type ( // The goal is to prove/verify evaluations of many instances of the same circuit -// WireAssignment is assignment of values to the same wire across many instances of the circuit +// WireAssignment is the assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) @@ -47,7 +47,7 @@ func (e *zeroCheckLazyClaims) varsNum() int { } func (e *zeroCheckLazyClaims) degree(int) int { - return gkrcore.Degree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel), e.resources.circuit) + return e.resources.circuit.ZeroCheckDegree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel)) } // verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. @@ -62,13 +62,13 @@ func (e *zeroCheckLazyClaims) degree(int) int { // The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming // that the full sum matches purportedValue. func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { - e.resources.levelPoints[e.levelI] = r - level := e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel) - perWireInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) + e.resources.outgoingEvalPoints[e.levelI] = [][]fr.Element{r} + level := e.resources.schedule[e.levelI] + gateInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) - var terms []fr.Element - flatW := 0 - for _, group := range level { + var claimedEvals polynomial.Polynomial + levelWireI := 0 + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := e.resources.circuit[wI] @@ -77,27 +77,26 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) } else { evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) - for _, v := range perWireInputEvals[flatW] { + for _, v := range gateInputEvals[levelWireI] { evaluator.pushInput(v) } gateEval.Set(evaluator.evaluate()) } for _, src := range group.ClaimSources { - eq := polynomial.EvalEq(e.resources.levelPoints[src], r) + eq := polynomial.EvalEq(e.resources.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], r) var term fr.Element term.Mul(&eq, &gateEval) - terms = append(terms, term) + claimedEvals = append(claimedEvals, term) } - flatW++ + levelWireI++ } } - ys := polynomial.Polynomial(terms) - if total := ys.Eval(&e.foldingCoeff); total.Equal(&purportedValue) { - return nil + if total := claimedEvals.Eval(&e.foldingCoeff); !total.Equal(&purportedValue) { + return errors.New("incompatible evaluations") } - return errors.New("incompatible evaluations") + return nil } // zeroCheckClaims is a claim for sumcheck (prover side). @@ -105,7 +104,6 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. // where the sum runs over all (wire v, claim source s) pairs in the level. // Each wire has its own eq table with the batching coefficients baked in. type zeroCheckClaims struct { - level constraint.GkrSumcheckLevel levelI int resources *resources input []polynomial.MultiLin // UniqueGateInputs order @@ -115,7 +113,7 @@ type zeroCheckClaims struct { } func (c *zeroCheckClaims) varsNum() int { - return len(c.resources.levelPoints[c.level[0].ClaimSources[0]]) + return c.resources.nbVars } // roundPolynomial computes gⱼ = ∑ₕ ∑ᵥ eqs[v](Xⱼ, h...) · gateᵥ(inputs(Xⱼ, h...)). @@ -123,7 +121,8 @@ func (c *zeroCheckClaims) varsNum() int { // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). // By convention, g₀ is a constant polynomial equal to the claimed sum. func (c *zeroCheckClaims) roundPolynomial() polynomial.Polynomial { - degree := gkrcore.Degree(c.level, c.resources.circuit) + level := c.resources.schedule[c.levelI].(constraint.GkrSumcheckLevel) + degree := c.resources.circuit.ZeroCheckDegree(level) nbUniqueInputs := len(c.input) nbWires := len(c.eqs) @@ -230,7 +229,7 @@ func (c *zeroCheckClaims) roundFold(r fr.Element) { // proveFinalEval provides the unique input wire values wᵢ(r₁, ..., rₙ). func (c *zeroCheckClaims) proveFinalEval(r []fr.Element) []fr.Element { - c.resources.levelPoints[c.levelI] = r + c.resources.outgoingEvalPoints[c.levelI] = [][]fr.Element{r} evaluations := make([]fr.Element, len(c.input)) for i := range c.input { c.input[i].Fold(r[len(r)-1]) @@ -287,18 +286,18 @@ func (r *resources) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { } type resources struct { - // levelPoints[i] is the sumcheck evaluation point r produced at schedule level i. - // levelPoints[len(schedule)] holds the initial challenge (firstChallenge / rho). - // SkipLevels produce no point; their slot remains nil. - levelPoints [][]fr.Element - evalPositions [][]int // [wireI][evalI] → position in source level's finalEvalProof - nbVars int - assignment WireAssignment - memPool polynomial.Pool - workers *utils.WorkerPool - circuit Circuit - schedule constraint.GkrProvingSchedule - transcript transcript + // outgoingEvalPoints[i][k] is the k-th outgoing evaluation point (evaluation challenge) produced at schedule level i. + // outgoingEvalPoints[len(schedule)][0] holds the initial challenge (firstChallenge / rho). + // SumcheckLevels produce one point (k=0). SkipLevels pass on all their evaluation points. + outgoingEvalPoints [][][]fr.Element + nbVars int + assignment WireAssignment + memPool polynomial.Pool + workers *utils.WorkerPool + circuit Circuit + schedule constraint.GkrProvingSchedule + transcript transcript + uniqueInputIndices [][]int // uniqueInputIndices[wI][claimI]: w's unique-input index in the layer its claimI-th evaluation is coming from } func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (resources, error) { @@ -309,43 +308,109 @@ func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment } return resources{ - levelPoints: make([][]fr.Element, len(schedule)+1), - evalPositions: gkrcore.BuildFinalEvalPositions(schedule, c), - nbVars: nbVars, - assignment: assignment, - memPool: polynomial.NewPool(c.MemoryRequirements(nbInstances)...), - workers: utils.NewWorkerPool(), - circuit: c, - schedule: schedule, - transcript: transcript{h: hasher}, + outgoingEvalPoints: make([][][]fr.Element, len(schedule)+1), + nbVars: nbVars, + assignment: assignment, + memPool: polynomial.NewPool(c.MemoryRequirements(nbInstances)...), + workers: utils.NewWorkerPool(), + circuit: c, + schedule: schedule, + transcript: transcript{h: hasher}, + uniqueInputIndices: c.UniqueInputIndices(schedule), }, nil } -// proveLevel runs the sumcheck for a SumcheckLevel: derives the fold challenge, -// builds per-wire eq tables, constructs zeroCheckClaims, and calls sumcheckProve. -func (r *resources) proveLevel(levelI int) sumcheckProof { - level := r.schedule[levelI].(constraint.GkrSumcheckLevel) +// collectOutgoingEvalPoints sets the outgoing evaluation points of a skip level, equal to its incoming ones. +func (r *resources) collectOutgoingEvalPoints(levelI int) [][]fr.Element { + level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := make([][]fr.Element, level.NbOutgoingEvalPoints()) + for k, src := range level.ClaimSources { + outPoints[k] = r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] + } + r.outgoingEvalPoints[levelI] = outPoints + return outPoints +} + +// proveSkipLevel evaluates each unique gate input at each inherited evaluation point and records +// the outgoing evaluation points on the resources. +func (r *resources) proveSkipLevel(levelI int) sumcheckProof { + outPoints := r.collectOutgoingEvalPoints(levelI) + level := r.schedule[levelI].(constraint.GkrSkipLevel) + + uniqueInputs := r.circuit.UniqueGateInputs(level) + evals := make([]fr.Element, len(uniqueInputs)*len(outPoints)) + for uiI, inW := range uniqueInputs { + for k, point := range outPoints { + evals[level.FinalEvalProofIndex(uiI, k)] = r.assignment[inW].Evaluate(point, &r.memPool) + } + } + return sumcheckProof{finalEvalProof: evals} +} + +// verifySkipLevel verifies a SkipSumcheck level: checks that the finalEvalProof +// is consistent with the assignment and gate evaluations, and records outgoing eval points. +func (r *resources) verifySkipLevel(levelI int, proof Proof) error { + outPoints := r.collectOutgoingEvalPoints(levelI) + level := r.schedule[levelI].(constraint.GkrSkipLevel) - nbClaims := gkrcore.NbClaims(level) + finalEval := proof[levelI].finalEvalProof + _, inputIndices := r.circuit.InputMapping(level) + group := constraint.GkrClaimGroup(level) + initialChallengeI := len(r.schedule) + + for levelWireI, wI := range group.Wires { + wire := r.circuit[wI] + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) + for claimI, src := range group.ClaimSources { + point := outPoints[claimI] + var gateEval fr.Element + if wire.IsInput() { + gateEval = r.assignment[wI].Evaluate(point, &r.memPool) + if claimed := finalEval[level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI)]; !claimed.Equal(&gateEval) { + return fmt.Errorf("level %d wire %d: finalEvalProof[%d] = %v, want %v", levelI, wI, level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI), &claimed, &gateEval) + } + } else { + for _, inI := range inputIndices[levelWireI] { + evaluator.pushInput(finalEval[level.FinalEvalProofIndex(inI, claimI)]) + } + gateEval.Set(evaluator.evaluate()) + } + var claimedEval fr.Element + if src.Level == initialChallengeI { + claimedEval = r.assignment[wI].Evaluate(point, &r.memPool) + } else { + claimedEval = proof[src.Level].finalEvalProof[r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex)] + } + if !claimedEval.Equal(&gateEval) { + return fmt.Errorf("level %d wire %d claim %d: claimed eval %v disagrees with gate eval %v", levelI, wI, claimI, &claimedEval, &gateEval) + } + } + } + return nil +} + +func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { + level := r.schedule[levelI] + nbClaims := level.NbClaims() var foldingCoeff fr.Element if nbClaims >= 2 { foldingCoeff = r.transcript.getChallenge() } - uniqueInputs, inputIndices := gkrcore.InputMapping(level, r.circuit) + uniqueInputs, inputIndices := r.circuit.InputMapping(level) input := make([]polynomial.MultiLin, len(uniqueInputs)) for i, inW := range uniqueInputs { input[i] = r.memPool.Clone(r.assignment[inW]) } nbWires := 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { nbWires += len(group.Wires) } pools := make([]*gateEvaluatorPool, nbWires) flatW := 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := r.circuit[wI] gate := wire.Gate.Evaluate @@ -362,12 +427,12 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { var alpha fr.Element alpha.SetOne() flatW = 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { nbSources := len(group.ClaimSources) groupEq := polynomial.MultiLin(r.memPool.Make(eqLength)) groupEq[0].Set(&alpha) - groupEq.Eq(r.levelPoints[group.ClaimSources[0]]) + groupEq.Eq(r.outgoingEvalPoints[group.ClaimSources[0].Level][group.ClaimSources[0].OutgoingClaimIndex]) if nbSources > 1 { newEq := polynomial.MultiLin(r.memPool.Make(eqLength)) @@ -375,7 +440,7 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { for k := 1; k < nbSources; k++ { aI.Mul(&aI, &foldingCoeff) newEq[0].Set(&aI) - r.eqAcc(groupEq, newEq, r.levelPoints[group.ClaimSources[k]]) + r.eqAcc(groupEq, newEq, r.outgoingEvalPoints[group.ClaimSources[k].Level][group.ClaimSources[k].OutgoingClaimIndex]) } r.memPool.Dump(newEq) } @@ -403,7 +468,6 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { } claims := &zeroCheckClaims{ - level: level, levelI: levelI, resources: r, input: input, @@ -414,44 +478,37 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { return sumcheckProve(claims, &r.transcript) } -// verifyLevel runs the sumcheck verification for a SumcheckLevel: derives the fold challenge, -// computes the claimed sum, and calls sumcheckVerify. -func (r *resources) verifyLevel(levelI int, proof Proof) error { - level := r.schedule[levelI].(constraint.GkrSumcheckLevel) - initialChallengeI := len(r.schedule) - - nbClaims := gkrcore.NbClaims(level) +func (r *resources) verifySumcheckLevel(levelI int, proof Proof) error { + level := r.schedule[levelI] + nbClaims := level.NbClaims() var foldingCoeff fr.Element if nbClaims >= 2 { foldingCoeff = r.transcript.getChallenge() } - var ys []fr.Element - for _, group := range level { + initialChallengeI := len(r.schedule) + claimedEvals := make(polynomial.Polynomial, 0, level.NbClaims()) + + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { - evalI := 0 - for _, src := range group.ClaimSources { - var y fr.Element - if src == initialChallengeI { - y = r.assignment[wI].Evaluate(r.levelPoints[src], &r.memPool) + for claimI, src := range group.ClaimSources { + if src.Level == initialChallengeI { + claimedEvals = append(claimedEvals, r.assignment[wI].Evaluate(r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], &r.memPool)) } else { - y = proof[src].finalEvalProof[r.evalPositions[wI][evalI]] - evalI++ + claimedEvals = append(claimedEvals, proof[src.Level].finalEvalProof[r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex)]) } - ys = append(ys, y) } } } - ysPoly := polynomial.Polynomial(ys) - claimedSum := ysPoly.Eval(&foldingCoeff) + claimedSum := claimedEvals.Eval(&foldingCoeff) lazyClaims := &zeroCheckLazyClaims{ foldingCoeff: foldingCoeff, resources: r, levelI: levelI, } - return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, gkrcore.Degree(level, r.circuit), &r.transcript) + return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, r.circuit.ZeroCheckDegree(level.(constraint.GkrSumcheckLevel)), &r.transcript) } // Prove consistency of the claimed assignment @@ -464,23 +521,20 @@ func Prove(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAss proof := make(Proof, len(schedule)) - // Derive initial challenge point + // Derive the initial challenge point firstChallenge := make([]fr.Element, r.nbVars) for j := range r.nbVars { firstChallenge[j] = r.transcript.getChallenge() } - r.levelPoints[len(schedule)] = firstChallenge + r.outgoingEvalPoints[len(schedule)] = [][]fr.Element{firstChallenge} for levelI := len(schedule) - 1; levelI >= 0; levelI-- { - switch s := schedule[levelI].(type) { - case constraint.GkrSkipLevel: - r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - - case constraint.GkrSumcheckLevel: - proof[levelI] = r.proveLevel(levelI) - // Bind finalEvalProof for next level's challenge derivation - r.transcript.bind(proof[levelI].finalEvalProof...) + if _, isSkip := r.schedule[levelI].(constraint.GkrSkipLevel); isSkip { + proof[levelI] = r.proveSkipLevel(levelI) + } else { + proof[levelI] = r.proveSumcheckLevel(levelI) } + constraint.BindGkrFinalEvalProof(&r.transcript, proof[levelI].finalEvalProof, c.UniqueGateInputs(r.schedule[levelI]), c.IsInput, r.schedule[levelI]) } return proof, nil @@ -495,25 +549,23 @@ func Verify(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAs } defer r.workers.Stop() - // Derive initial challenge point + // Derive the initial challenge point firstChallenge := make([]fr.Element, r.nbVars) for j := range r.nbVars { firstChallenge[j] = r.transcript.getChallenge() } - r.levelPoints[len(schedule)] = firstChallenge + r.outgoingEvalPoints[len(schedule)] = [][]fr.Element{firstChallenge} for levelI := len(schedule) - 1; levelI >= 0; levelI-- { - switch s := schedule[levelI].(type) { - case constraint.GkrSkipLevel: - r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - - case constraint.GkrSumcheckLevel: - if err = r.verifyLevel(levelI, proof); err != nil { - return fmt.Errorf("sumcheck proof rejected: %v", err) - } - // Bind finalEvalProof for next level's challenge derivation - r.transcript.bind(proof[levelI].finalEvalProof...) + if _, isSkip := r.schedule[levelI].(constraint.GkrSkipLevel); isSkip { + err = r.verifySkipLevel(levelI, proof) + } else { + err = r.verifySumcheckLevel(levelI, proof) + } + if err != nil { + return fmt.Errorf("level %d: %v", levelI, err) } + constraint.BindGkrFinalEvalProof(&r.transcript, proof[levelI].finalEvalProof, c.UniqueGateInputs(r.schedule[levelI]), c.IsInput, r.schedule[levelI]) } return nil } @@ -724,7 +776,7 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() - // Return evaluator to pool (it keeps its vars slice from polynomial pool) + // Return evaluator to pool (it keeps its vars slice from the polynomial pool) gep.available[e] = struct{}{} } diff --git a/internal/gkr/bls12-377/gkr_test.go b/internal/gkr/bls12-377/gkr_test.go index 0c42aff870..57f5c1e664 100644 --- a/internal/gkr/bls12-377/gkr_test.go +++ b/internal/gkr/bls12-377/gkr_test.go @@ -11,7 +11,6 @@ import ( "os" "path/filepath" "reflect" - "strconv" "testing" "time" @@ -68,8 +67,12 @@ func TestMimc(t *testing.T) { test(t, gkrtesting.MiMCCircuit(93)) } -// testLevel exercises proveLevel/verifyLevel for a single sumcheck level. -func testLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrSumcheckLevel) { +func TestPoseidon2(t *testing.T) { + test(t, gkrtesting.Poseidon2Circuit(4, 2)) +} + +// testSumcheckLevel exercises proveSumcheckLevel/verifySumcheckLevel for a single sumcheck level. +func testSumcheckLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrProvingLevel) { t.Helper() _, sCircuit := cache.Compile(t, circuit) @@ -83,26 +86,26 @@ func testLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrSum assignment.Complete(sCircuit) schedule := constraint.GkrProvingSchedule{level} - challenge := []fr.Element{five} + initEvalPoint := [][]fr.Element{{one}} // Prove proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) defer proveR.workers.Stop() - proveR.levelPoints[1] = challenge - proof := Proof{proveR.proveLevel(0)} + proveR.outgoingEvalPoints[len(schedule)] = initEvalPoint + proof := Proof{proveR.proveSumcheckLevel(0)} // Verify verifyR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) defer verifyR.workers.Stop() - verifyR.levelPoints[1] = challenge - assert.NoError(t, verifyR.verifyLevel(0, proof)) + verifyR.outgoingEvalPoints[len(schedule)] = initEvalPoint + assert.NoError(t, verifyR.verifySumcheckLevel(0, proof)) } -func TestLevel(t *testing.T) { +func TestSumcheckLevel(t *testing.T) { // Wires 0,1 = inputs; wires 2,3,4 = mul(0,1). All gates are independent outputs. circuit := gkrcore.RawCircuit{ {}, @@ -111,41 +114,115 @@ func TestLevel(t *testing.T) { {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, } - // All levels have initial challenge at index 1 (len(schedule) = 1). + // Each level has an initial challenge at index 1 (len(schedule) = 1). + // GkrClaimSource{Level:1} is the initial-challenge sentinel. tests := []struct { name string - level constraint.GkrSumcheckLevel + level constraint.GkrProvingLevel }{ { name: "single wire", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []int{1}}, + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, { name: "two groups", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []int{1}}, - {Wires: []int{3}, ClaimSources: []int{1}}, + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + {Wires: []int{3}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, { name: "one group with two wires", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4, 3}, ClaimSources: []int{1}}, + {Wires: []int{4, 3}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, { name: "mixed: single + multi-wire group", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []int{1}}, - {Wires: []int{3, 2}, ClaimSources: []int{1}}, + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + {Wires: []int{3, 2}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - testLevel(t, circuit, tc.level) + testSumcheckLevel(t, circuit, tc.level) + }) + } +} + +// testSkipLevel exercises proveSkipLevel/verifySkipLevel for a single skip level. +func testSkipLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrProvingLevel) { + t.Helper() + _, sCircuit := cache.Compile(t, circuit) + + ins := sCircuit.Inputs() + assignment := make(WireAssignment, len(sCircuit)) + for _, i := range ins { + assignment[i] = make([]fr.Element, 2) + fr.Vector(assignment[i]).MustSetRandom() + } + + assignment.Complete(sCircuit) + + schedule := constraint.GkrProvingSchedule{level} + initEvalPoint := [][]fr.Element{{one}} + + // Prove + proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) + assert.NoError(t, err) + defer proveR.workers.Stop() + + proveR.outgoingEvalPoints[len(schedule)] = initEvalPoint + proof := Proof{proveR.proveSkipLevel(0)} + + // Verify + verifyR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) + assert.NoError(t, err) + defer verifyR.workers.Stop() + + verifyR.outgoingEvalPoints[len(schedule)] = initEvalPoint + assert.NoError(t, verifyR.verifySkipLevel(0, proof)) +} + +func TestSkipLevel(t *testing.T) { + // Wires 0,1 = inputs; wires 2,3 = identity(0); wire 4 = add(0,1). All degree-1 outputs. + circuit := gkrcore.RawCircuit{ + {}, + {}, + {Gate: gkrcore.Identity, Inputs: []int{0}}, + {Gate: gkrcore.Identity, Inputs: []int{0}}, + {Gate: gkrcore.Add2, Inputs: []int{0, 1}}, + } + + // Single-claim cases: one inherited evaluation point (OutgoingClaimIndex always 0). + singleClaim := []struct { + name string + level constraint.GkrProvingLevel + }{ + { + name: "single input wire", + level: constraint.GkrSkipLevel{Wires: []int{0}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + { + name: "single identity gate", + level: constraint.GkrSkipLevel{Wires: []int{2}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + { + name: "add gate", + level: constraint.GkrSkipLevel{Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + { + name: "two identity gates one group", + level: constraint.GkrSkipLevel{Wires: []int{2, 3}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + } + for _, tc := range singleClaim { + t.Run(tc.name, func(t *testing.T) { + testSkipLevel(t, circuit, tc.level) }) } } @@ -161,26 +238,6 @@ func init() { six.Double(&three) } -var testManyInstancesLogMaxInstances = -1 - -func getLogMaxInstances(t *testing.T) int { - if testManyInstancesLogMaxInstances == -1 { - - s := os.Getenv("GKR_LOG_INSTANCES") - if s == "" { - testManyInstancesLogMaxInstances = 5 - } else { - var err error - testManyInstancesLogMaxInstances, err = strconv.Atoi(s) - if err != nil { - t.Error(err) - } - } - - } - return testManyInstancesLogMaxInstances -} - func test(t *testing.T, circuit gkrcore.RawCircuit) { testWithSchedule(t, circuit, nil) } @@ -194,7 +251,7 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constra } ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) - maxSize := 1 << getLogMaxInstances(t) + maxSize := 1 << gkrtesting.GetLogMaxInstances(t) for i := range ins { insAssignment[i] = make([]fr.Element, maxSize) @@ -209,8 +266,6 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constra fullAssignment.Complete(sCircuit) - t.Log("Selected inputs for test") - proof, err := Prove(sCircuit, schedule, fullAssignment, newMessageCounter(1, 1)) assert.NoError(t, err) @@ -219,29 +274,11 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constra err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(1, 1)) assert.NoError(t, err, "proof rejected") - if proof.isEmpty() { // special case for TestNoGate: - continue // there's no way to make a trivial proof fail - } - err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(0, 1)) assert.NotNil(t, err, "bad proof accepted") } } -func (p Proof) isEmpty() bool { - for i := range p { - if len(p[i].finalEvalProof) != 0 { - return false - } - for j := range p[i].partialSumPolys { - if len(p[i].partialSumPolys[j]) != 0 { - return false - } - } - } - return true -} - func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { _, c := cache.Compile(t, gkrtesting.NoGateCircuit()) @@ -275,10 +312,23 @@ func generateTestVerifier(path string) func(t *testing.T) { assert.NoError(t, err) err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) assert.NoError(t, err, "proof rejected") + testCase, err = newTestCase(path) assert.NoError(t, err) err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, newMessageCounter(2, 0)) assert.NotNil(t, err, "bad proof accepted") + + testCase, err = newTestCase(path) + assert.NoError(t, err) + testCase.InOutAssignment[len(testCase.InOutAssignment)-1][0].Add(&testCase.InOutAssignment[len(testCase.InOutAssignment)-1][0], &one) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) + assert.NotNil(t, err, "tampered output accepted") + + testCase, err = newTestCase(path) + assert.NoError(t, err) + testCase.InOutAssignment[0][0].Add(&testCase.InOutAssignment[0][0], &one) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) + assert.NotNil(t, err, "tampered input accepted") } } @@ -358,11 +408,11 @@ func TestSingleMulGateExplicitSchedule(t *testing.T) { _, sCircuit := cache.Compile(t, circuit) // Wire 2 is the mul gate output (inputs: 0, 1). - // Explicit schedule: one SumcheckLevel for wire 2 only. - // ClaimSources: [1] = initial challenge (index len(schedule) = 1). + // Explicit schedule: one GkrProvingLevel for wire 2. + // GkrClaimSource{Level:1} is the initial-challenge sentinel (len(schedule)=1). schedule := constraint.GkrProvingSchedule{ constraint.GkrSumcheckLevel{ - {Wires: []int{2}, ClaimSources: []int{1}}, + {Wires: []int{2}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, } testWithSchedule(t, circuit, schedule) diff --git a/internal/gkr/bls12-377/sumcheck.go b/internal/gkr/bls12-377/sumcheck.go index f8b77a197c..c1700e3db3 100644 --- a/internal/gkr/bls12-377/sumcheck.go +++ b/internal/gkr/bls12-377/sumcheck.go @@ -16,15 +16,15 @@ import ( // This does not make use of parallelism and represents polynomials as lists of coefficients. // transcript is a Fiat-Shamir transcript backed by a running hash. -// Field elements are written via bind; challenges are derived via getChallenge. +// Field elements are written via Bind; challenges are derived via getChallenge. // The hash is never reset — all previous data is implicitly part of future challenges. type transcript struct { h hash.Hash - bound bool // whether bind was called since the last getChallenge + bound bool // whether Bind was called since the last getChallenge } -// bind writes field elements to the transcript as bindings for the next challenge. -func (t *transcript) bind(elements ...fr.Element) { +// Bind writes field elements to the transcript as bindings for the next challenge. +func (t *transcript) Bind(elements ...fr.Element) { if len(elements) == 0 { return } @@ -39,7 +39,7 @@ func (t *transcript) bind(elements ...fr.Element) { // If no bindings were added since the last squeeze, a separator byte is written first // to advance the state and prevent repeated values. func (t *transcript) getChallenge(bindings ...fr.Element) fr.Element { - t.bind(bindings...) + t.Bind(bindings...) if !t.bound { t.h.Write([]byte{0}) } diff --git a/internal/gkr/bls12-381/blueprint.go b/internal/gkr/bls12-381/blueprint.go index 7c150c6f52..2257585bf5 100644 --- a/internal/gkr/bls12-381/blueprint.go +++ b/internal/gkr/bls12-381/blueprint.go @@ -304,7 +304,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return gkrcore.ProofSize(b.Schedule, b.SolveBlueprint.Circuit, logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(b.Schedule, logNbInstances) } // NbOutputs implements Blueprint diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index e5bb7c7676..cd0ccc12b7 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -27,7 +27,7 @@ type ( // The goal is to prove/verify evaluations of many instances of the same circuit -// WireAssignment is assignment of values to the same wire across many instances of the circuit +// WireAssignment is the assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) @@ -47,7 +47,7 @@ func (e *zeroCheckLazyClaims) varsNum() int { } func (e *zeroCheckLazyClaims) degree(int) int { - return gkrcore.Degree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel), e.resources.circuit) + return e.resources.circuit.ZeroCheckDegree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel)) } // verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. @@ -62,13 +62,13 @@ func (e *zeroCheckLazyClaims) degree(int) int { // The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming // that the full sum matches purportedValue. func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { - e.resources.levelPoints[e.levelI] = r - level := e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel) - perWireInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) + e.resources.outgoingEvalPoints[e.levelI] = [][]fr.Element{r} + level := e.resources.schedule[e.levelI] + gateInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) - var terms []fr.Element - flatW := 0 - for _, group := range level { + var claimedEvals polynomial.Polynomial + levelWireI := 0 + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := e.resources.circuit[wI] @@ -77,27 +77,26 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) } else { evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) - for _, v := range perWireInputEvals[flatW] { + for _, v := range gateInputEvals[levelWireI] { evaluator.pushInput(v) } gateEval.Set(evaluator.evaluate()) } for _, src := range group.ClaimSources { - eq := polynomial.EvalEq(e.resources.levelPoints[src], r) + eq := polynomial.EvalEq(e.resources.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], r) var term fr.Element term.Mul(&eq, &gateEval) - terms = append(terms, term) + claimedEvals = append(claimedEvals, term) } - flatW++ + levelWireI++ } } - ys := polynomial.Polynomial(terms) - if total := ys.Eval(&e.foldingCoeff); total.Equal(&purportedValue) { - return nil + if total := claimedEvals.Eval(&e.foldingCoeff); !total.Equal(&purportedValue) { + return errors.New("incompatible evaluations") } - return errors.New("incompatible evaluations") + return nil } // zeroCheckClaims is a claim for sumcheck (prover side). @@ -105,7 +104,6 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. // where the sum runs over all (wire v, claim source s) pairs in the level. // Each wire has its own eq table with the batching coefficients baked in. type zeroCheckClaims struct { - level constraint.GkrSumcheckLevel levelI int resources *resources input []polynomial.MultiLin // UniqueGateInputs order @@ -115,7 +113,7 @@ type zeroCheckClaims struct { } func (c *zeroCheckClaims) varsNum() int { - return len(c.resources.levelPoints[c.level[0].ClaimSources[0]]) + return c.resources.nbVars } // roundPolynomial computes gⱼ = ∑ₕ ∑ᵥ eqs[v](Xⱼ, h...) · gateᵥ(inputs(Xⱼ, h...)). @@ -123,7 +121,8 @@ func (c *zeroCheckClaims) varsNum() int { // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). // By convention, g₀ is a constant polynomial equal to the claimed sum. func (c *zeroCheckClaims) roundPolynomial() polynomial.Polynomial { - degree := gkrcore.Degree(c.level, c.resources.circuit) + level := c.resources.schedule[c.levelI].(constraint.GkrSumcheckLevel) + degree := c.resources.circuit.ZeroCheckDegree(level) nbUniqueInputs := len(c.input) nbWires := len(c.eqs) @@ -230,7 +229,7 @@ func (c *zeroCheckClaims) roundFold(r fr.Element) { // proveFinalEval provides the unique input wire values wᵢ(r₁, ..., rₙ). func (c *zeroCheckClaims) proveFinalEval(r []fr.Element) []fr.Element { - c.resources.levelPoints[c.levelI] = r + c.resources.outgoingEvalPoints[c.levelI] = [][]fr.Element{r} evaluations := make([]fr.Element, len(c.input)) for i := range c.input { c.input[i].Fold(r[len(r)-1]) @@ -287,18 +286,18 @@ func (r *resources) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { } type resources struct { - // levelPoints[i] is the sumcheck evaluation point r produced at schedule level i. - // levelPoints[len(schedule)] holds the initial challenge (firstChallenge / rho). - // SkipLevels produce no point; their slot remains nil. - levelPoints [][]fr.Element - evalPositions [][]int // [wireI][evalI] → position in source level's finalEvalProof - nbVars int - assignment WireAssignment - memPool polynomial.Pool - workers *utils.WorkerPool - circuit Circuit - schedule constraint.GkrProvingSchedule - transcript transcript + // outgoingEvalPoints[i][k] is the k-th outgoing evaluation point (evaluation challenge) produced at schedule level i. + // outgoingEvalPoints[len(schedule)][0] holds the initial challenge (firstChallenge / rho). + // SumcheckLevels produce one point (k=0). SkipLevels pass on all their evaluation points. + outgoingEvalPoints [][][]fr.Element + nbVars int + assignment WireAssignment + memPool polynomial.Pool + workers *utils.WorkerPool + circuit Circuit + schedule constraint.GkrProvingSchedule + transcript transcript + uniqueInputIndices [][]int // uniqueInputIndices[wI][claimI]: w's unique-input index in the layer its claimI-th evaluation is coming from } func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (resources, error) { @@ -309,43 +308,109 @@ func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment } return resources{ - levelPoints: make([][]fr.Element, len(schedule)+1), - evalPositions: gkrcore.BuildFinalEvalPositions(schedule, c), - nbVars: nbVars, - assignment: assignment, - memPool: polynomial.NewPool(c.MemoryRequirements(nbInstances)...), - workers: utils.NewWorkerPool(), - circuit: c, - schedule: schedule, - transcript: transcript{h: hasher}, + outgoingEvalPoints: make([][][]fr.Element, len(schedule)+1), + nbVars: nbVars, + assignment: assignment, + memPool: polynomial.NewPool(c.MemoryRequirements(nbInstances)...), + workers: utils.NewWorkerPool(), + circuit: c, + schedule: schedule, + transcript: transcript{h: hasher}, + uniqueInputIndices: c.UniqueInputIndices(schedule), }, nil } -// proveLevel runs the sumcheck for a SumcheckLevel: derives the fold challenge, -// builds per-wire eq tables, constructs zeroCheckClaims, and calls sumcheckProve. -func (r *resources) proveLevel(levelI int) sumcheckProof { - level := r.schedule[levelI].(constraint.GkrSumcheckLevel) +// collectOutgoingEvalPoints sets the outgoing evaluation points of a skip level, equal to its incoming ones. +func (r *resources) collectOutgoingEvalPoints(levelI int) [][]fr.Element { + level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := make([][]fr.Element, level.NbOutgoingEvalPoints()) + for k, src := range level.ClaimSources { + outPoints[k] = r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] + } + r.outgoingEvalPoints[levelI] = outPoints + return outPoints +} + +// proveSkipLevel evaluates each unique gate input at each inherited evaluation point and records +// the outgoing evaluation points on the resources. +func (r *resources) proveSkipLevel(levelI int) sumcheckProof { + outPoints := r.collectOutgoingEvalPoints(levelI) + level := r.schedule[levelI].(constraint.GkrSkipLevel) + + uniqueInputs := r.circuit.UniqueGateInputs(level) + evals := make([]fr.Element, len(uniqueInputs)*len(outPoints)) + for uiI, inW := range uniqueInputs { + for k, point := range outPoints { + evals[level.FinalEvalProofIndex(uiI, k)] = r.assignment[inW].Evaluate(point, &r.memPool) + } + } + return sumcheckProof{finalEvalProof: evals} +} + +// verifySkipLevel verifies a SkipSumcheck level: checks that the finalEvalProof +// is consistent with the assignment and gate evaluations, and records outgoing eval points. +func (r *resources) verifySkipLevel(levelI int, proof Proof) error { + outPoints := r.collectOutgoingEvalPoints(levelI) + level := r.schedule[levelI].(constraint.GkrSkipLevel) - nbClaims := gkrcore.NbClaims(level) + finalEval := proof[levelI].finalEvalProof + _, inputIndices := r.circuit.InputMapping(level) + group := constraint.GkrClaimGroup(level) + initialChallengeI := len(r.schedule) + + for levelWireI, wI := range group.Wires { + wire := r.circuit[wI] + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) + for claimI, src := range group.ClaimSources { + point := outPoints[claimI] + var gateEval fr.Element + if wire.IsInput() { + gateEval = r.assignment[wI].Evaluate(point, &r.memPool) + if claimed := finalEval[level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI)]; !claimed.Equal(&gateEval) { + return fmt.Errorf("level %d wire %d: finalEvalProof[%d] = %v, want %v", levelI, wI, level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI), &claimed, &gateEval) + } + } else { + for _, inI := range inputIndices[levelWireI] { + evaluator.pushInput(finalEval[level.FinalEvalProofIndex(inI, claimI)]) + } + gateEval.Set(evaluator.evaluate()) + } + var claimedEval fr.Element + if src.Level == initialChallengeI { + claimedEval = r.assignment[wI].Evaluate(point, &r.memPool) + } else { + claimedEval = proof[src.Level].finalEvalProof[r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex)] + } + if !claimedEval.Equal(&gateEval) { + return fmt.Errorf("level %d wire %d claim %d: claimed eval %v disagrees with gate eval %v", levelI, wI, claimI, &claimedEval, &gateEval) + } + } + } + return nil +} + +func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { + level := r.schedule[levelI] + nbClaims := level.NbClaims() var foldingCoeff fr.Element if nbClaims >= 2 { foldingCoeff = r.transcript.getChallenge() } - uniqueInputs, inputIndices := gkrcore.InputMapping(level, r.circuit) + uniqueInputs, inputIndices := r.circuit.InputMapping(level) input := make([]polynomial.MultiLin, len(uniqueInputs)) for i, inW := range uniqueInputs { input[i] = r.memPool.Clone(r.assignment[inW]) } nbWires := 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { nbWires += len(group.Wires) } pools := make([]*gateEvaluatorPool, nbWires) flatW := 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := r.circuit[wI] gate := wire.Gate.Evaluate @@ -362,12 +427,12 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { var alpha fr.Element alpha.SetOne() flatW = 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { nbSources := len(group.ClaimSources) groupEq := polynomial.MultiLin(r.memPool.Make(eqLength)) groupEq[0].Set(&alpha) - groupEq.Eq(r.levelPoints[group.ClaimSources[0]]) + groupEq.Eq(r.outgoingEvalPoints[group.ClaimSources[0].Level][group.ClaimSources[0].OutgoingClaimIndex]) if nbSources > 1 { newEq := polynomial.MultiLin(r.memPool.Make(eqLength)) @@ -375,7 +440,7 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { for k := 1; k < nbSources; k++ { aI.Mul(&aI, &foldingCoeff) newEq[0].Set(&aI) - r.eqAcc(groupEq, newEq, r.levelPoints[group.ClaimSources[k]]) + r.eqAcc(groupEq, newEq, r.outgoingEvalPoints[group.ClaimSources[k].Level][group.ClaimSources[k].OutgoingClaimIndex]) } r.memPool.Dump(newEq) } @@ -403,7 +468,6 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { } claims := &zeroCheckClaims{ - level: level, levelI: levelI, resources: r, input: input, @@ -414,44 +478,37 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { return sumcheckProve(claims, &r.transcript) } -// verifyLevel runs the sumcheck verification for a SumcheckLevel: derives the fold challenge, -// computes the claimed sum, and calls sumcheckVerify. -func (r *resources) verifyLevel(levelI int, proof Proof) error { - level := r.schedule[levelI].(constraint.GkrSumcheckLevel) - initialChallengeI := len(r.schedule) - - nbClaims := gkrcore.NbClaims(level) +func (r *resources) verifySumcheckLevel(levelI int, proof Proof) error { + level := r.schedule[levelI] + nbClaims := level.NbClaims() var foldingCoeff fr.Element if nbClaims >= 2 { foldingCoeff = r.transcript.getChallenge() } - var ys []fr.Element - for _, group := range level { + initialChallengeI := len(r.schedule) + claimedEvals := make(polynomial.Polynomial, 0, level.NbClaims()) + + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { - evalI := 0 - for _, src := range group.ClaimSources { - var y fr.Element - if src == initialChallengeI { - y = r.assignment[wI].Evaluate(r.levelPoints[src], &r.memPool) + for claimI, src := range group.ClaimSources { + if src.Level == initialChallengeI { + claimedEvals = append(claimedEvals, r.assignment[wI].Evaluate(r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], &r.memPool)) } else { - y = proof[src].finalEvalProof[r.evalPositions[wI][evalI]] - evalI++ + claimedEvals = append(claimedEvals, proof[src.Level].finalEvalProof[r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex)]) } - ys = append(ys, y) } } } - ysPoly := polynomial.Polynomial(ys) - claimedSum := ysPoly.Eval(&foldingCoeff) + claimedSum := claimedEvals.Eval(&foldingCoeff) lazyClaims := &zeroCheckLazyClaims{ foldingCoeff: foldingCoeff, resources: r, levelI: levelI, } - return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, gkrcore.Degree(level, r.circuit), &r.transcript) + return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, r.circuit.ZeroCheckDegree(level.(constraint.GkrSumcheckLevel)), &r.transcript) } // Prove consistency of the claimed assignment @@ -464,23 +521,20 @@ func Prove(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAss proof := make(Proof, len(schedule)) - // Derive initial challenge point + // Derive the initial challenge point firstChallenge := make([]fr.Element, r.nbVars) for j := range r.nbVars { firstChallenge[j] = r.transcript.getChallenge() } - r.levelPoints[len(schedule)] = firstChallenge + r.outgoingEvalPoints[len(schedule)] = [][]fr.Element{firstChallenge} for levelI := len(schedule) - 1; levelI >= 0; levelI-- { - switch s := schedule[levelI].(type) { - case constraint.GkrSkipLevel: - r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - - case constraint.GkrSumcheckLevel: - proof[levelI] = r.proveLevel(levelI) - // Bind finalEvalProof for next level's challenge derivation - r.transcript.bind(proof[levelI].finalEvalProof...) + if _, isSkip := r.schedule[levelI].(constraint.GkrSkipLevel); isSkip { + proof[levelI] = r.proveSkipLevel(levelI) + } else { + proof[levelI] = r.proveSumcheckLevel(levelI) } + constraint.BindGkrFinalEvalProof(&r.transcript, proof[levelI].finalEvalProof, c.UniqueGateInputs(r.schedule[levelI]), c.IsInput, r.schedule[levelI]) } return proof, nil @@ -495,25 +549,23 @@ func Verify(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAs } defer r.workers.Stop() - // Derive initial challenge point + // Derive the initial challenge point firstChallenge := make([]fr.Element, r.nbVars) for j := range r.nbVars { firstChallenge[j] = r.transcript.getChallenge() } - r.levelPoints[len(schedule)] = firstChallenge + r.outgoingEvalPoints[len(schedule)] = [][]fr.Element{firstChallenge} for levelI := len(schedule) - 1; levelI >= 0; levelI-- { - switch s := schedule[levelI].(type) { - case constraint.GkrSkipLevel: - r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - - case constraint.GkrSumcheckLevel: - if err = r.verifyLevel(levelI, proof); err != nil { - return fmt.Errorf("sumcheck proof rejected: %v", err) - } - // Bind finalEvalProof for next level's challenge derivation - r.transcript.bind(proof[levelI].finalEvalProof...) + if _, isSkip := r.schedule[levelI].(constraint.GkrSkipLevel); isSkip { + err = r.verifySkipLevel(levelI, proof) + } else { + err = r.verifySumcheckLevel(levelI, proof) + } + if err != nil { + return fmt.Errorf("level %d: %v", levelI, err) } + constraint.BindGkrFinalEvalProof(&r.transcript, proof[levelI].finalEvalProof, c.UniqueGateInputs(r.schedule[levelI]), c.IsInput, r.schedule[levelI]) } return nil } @@ -724,7 +776,7 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() - // Return evaluator to pool (it keeps its vars slice from polynomial pool) + // Return evaluator to pool (it keeps its vars slice from the polynomial pool) gep.available[e] = struct{}{} } diff --git a/internal/gkr/bls12-381/gkr_test.go b/internal/gkr/bls12-381/gkr_test.go index 02ab912447..5f9fa833f8 100644 --- a/internal/gkr/bls12-381/gkr_test.go +++ b/internal/gkr/bls12-381/gkr_test.go @@ -11,7 +11,6 @@ import ( "os" "path/filepath" "reflect" - "strconv" "testing" "time" @@ -68,8 +67,12 @@ func TestMimc(t *testing.T) { test(t, gkrtesting.MiMCCircuit(93)) } -// testLevel exercises proveLevel/verifyLevel for a single sumcheck level. -func testLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrSumcheckLevel) { +func TestPoseidon2(t *testing.T) { + test(t, gkrtesting.Poseidon2Circuit(4, 2)) +} + +// testSumcheckLevel exercises proveSumcheckLevel/verifySumcheckLevel for a single sumcheck level. +func testSumcheckLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrProvingLevel) { t.Helper() _, sCircuit := cache.Compile(t, circuit) @@ -83,26 +86,26 @@ func testLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrSum assignment.Complete(sCircuit) schedule := constraint.GkrProvingSchedule{level} - challenge := []fr.Element{five} + initEvalPoint := [][]fr.Element{{one}} // Prove proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) defer proveR.workers.Stop() - proveR.levelPoints[1] = challenge - proof := Proof{proveR.proveLevel(0)} + proveR.outgoingEvalPoints[len(schedule)] = initEvalPoint + proof := Proof{proveR.proveSumcheckLevel(0)} // Verify verifyR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) defer verifyR.workers.Stop() - verifyR.levelPoints[1] = challenge - assert.NoError(t, verifyR.verifyLevel(0, proof)) + verifyR.outgoingEvalPoints[len(schedule)] = initEvalPoint + assert.NoError(t, verifyR.verifySumcheckLevel(0, proof)) } -func TestLevel(t *testing.T) { +func TestSumcheckLevel(t *testing.T) { // Wires 0,1 = inputs; wires 2,3,4 = mul(0,1). All gates are independent outputs. circuit := gkrcore.RawCircuit{ {}, @@ -111,41 +114,115 @@ func TestLevel(t *testing.T) { {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, } - // All levels have initial challenge at index 1 (len(schedule) = 1). + // Each level has an initial challenge at index 1 (len(schedule) = 1). + // GkrClaimSource{Level:1} is the initial-challenge sentinel. tests := []struct { name string - level constraint.GkrSumcheckLevel + level constraint.GkrProvingLevel }{ { name: "single wire", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []int{1}}, + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, { name: "two groups", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []int{1}}, - {Wires: []int{3}, ClaimSources: []int{1}}, + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + {Wires: []int{3}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, { name: "one group with two wires", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4, 3}, ClaimSources: []int{1}}, + {Wires: []int{4, 3}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, { name: "mixed: single + multi-wire group", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []int{1}}, - {Wires: []int{3, 2}, ClaimSources: []int{1}}, + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + {Wires: []int{3, 2}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - testLevel(t, circuit, tc.level) + testSumcheckLevel(t, circuit, tc.level) + }) + } +} + +// testSkipLevel exercises proveSkipLevel/verifySkipLevel for a single skip level. +func testSkipLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrProvingLevel) { + t.Helper() + _, sCircuit := cache.Compile(t, circuit) + + ins := sCircuit.Inputs() + assignment := make(WireAssignment, len(sCircuit)) + for _, i := range ins { + assignment[i] = make([]fr.Element, 2) + fr.Vector(assignment[i]).MustSetRandom() + } + + assignment.Complete(sCircuit) + + schedule := constraint.GkrProvingSchedule{level} + initEvalPoint := [][]fr.Element{{one}} + + // Prove + proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) + assert.NoError(t, err) + defer proveR.workers.Stop() + + proveR.outgoingEvalPoints[len(schedule)] = initEvalPoint + proof := Proof{proveR.proveSkipLevel(0)} + + // Verify + verifyR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) + assert.NoError(t, err) + defer verifyR.workers.Stop() + + verifyR.outgoingEvalPoints[len(schedule)] = initEvalPoint + assert.NoError(t, verifyR.verifySkipLevel(0, proof)) +} + +func TestSkipLevel(t *testing.T) { + // Wires 0,1 = inputs; wires 2,3 = identity(0); wire 4 = add(0,1). All degree-1 outputs. + circuit := gkrcore.RawCircuit{ + {}, + {}, + {Gate: gkrcore.Identity, Inputs: []int{0}}, + {Gate: gkrcore.Identity, Inputs: []int{0}}, + {Gate: gkrcore.Add2, Inputs: []int{0, 1}}, + } + + // Single-claim cases: one inherited evaluation point (OutgoingClaimIndex always 0). + singleClaim := []struct { + name string + level constraint.GkrProvingLevel + }{ + { + name: "single input wire", + level: constraint.GkrSkipLevel{Wires: []int{0}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + { + name: "single identity gate", + level: constraint.GkrSkipLevel{Wires: []int{2}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + { + name: "add gate", + level: constraint.GkrSkipLevel{Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + { + name: "two identity gates one group", + level: constraint.GkrSkipLevel{Wires: []int{2, 3}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + } + for _, tc := range singleClaim { + t.Run(tc.name, func(t *testing.T) { + testSkipLevel(t, circuit, tc.level) }) } } @@ -161,26 +238,6 @@ func init() { six.Double(&three) } -var testManyInstancesLogMaxInstances = -1 - -func getLogMaxInstances(t *testing.T) int { - if testManyInstancesLogMaxInstances == -1 { - - s := os.Getenv("GKR_LOG_INSTANCES") - if s == "" { - testManyInstancesLogMaxInstances = 5 - } else { - var err error - testManyInstancesLogMaxInstances, err = strconv.Atoi(s) - if err != nil { - t.Error(err) - } - } - - } - return testManyInstancesLogMaxInstances -} - func test(t *testing.T, circuit gkrcore.RawCircuit) { testWithSchedule(t, circuit, nil) } @@ -194,7 +251,7 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constra } ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) - maxSize := 1 << getLogMaxInstances(t) + maxSize := 1 << gkrtesting.GetLogMaxInstances(t) for i := range ins { insAssignment[i] = make([]fr.Element, maxSize) @@ -209,8 +266,6 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constra fullAssignment.Complete(sCircuit) - t.Log("Selected inputs for test") - proof, err := Prove(sCircuit, schedule, fullAssignment, newMessageCounter(1, 1)) assert.NoError(t, err) @@ -219,29 +274,11 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constra err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(1, 1)) assert.NoError(t, err, "proof rejected") - if proof.isEmpty() { // special case for TestNoGate: - continue // there's no way to make a trivial proof fail - } - err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(0, 1)) assert.NotNil(t, err, "bad proof accepted") } } -func (p Proof) isEmpty() bool { - for i := range p { - if len(p[i].finalEvalProof) != 0 { - return false - } - for j := range p[i].partialSumPolys { - if len(p[i].partialSumPolys[j]) != 0 { - return false - } - } - } - return true -} - func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { _, c := cache.Compile(t, gkrtesting.NoGateCircuit()) @@ -275,10 +312,23 @@ func generateTestVerifier(path string) func(t *testing.T) { assert.NoError(t, err) err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) assert.NoError(t, err, "proof rejected") + testCase, err = newTestCase(path) assert.NoError(t, err) err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, newMessageCounter(2, 0)) assert.NotNil(t, err, "bad proof accepted") + + testCase, err = newTestCase(path) + assert.NoError(t, err) + testCase.InOutAssignment[len(testCase.InOutAssignment)-1][0].Add(&testCase.InOutAssignment[len(testCase.InOutAssignment)-1][0], &one) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) + assert.NotNil(t, err, "tampered output accepted") + + testCase, err = newTestCase(path) + assert.NoError(t, err) + testCase.InOutAssignment[0][0].Add(&testCase.InOutAssignment[0][0], &one) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) + assert.NotNil(t, err, "tampered input accepted") } } @@ -358,11 +408,11 @@ func TestSingleMulGateExplicitSchedule(t *testing.T) { _, sCircuit := cache.Compile(t, circuit) // Wire 2 is the mul gate output (inputs: 0, 1). - // Explicit schedule: one SumcheckLevel for wire 2 only. - // ClaimSources: [1] = initial challenge (index len(schedule) = 1). + // Explicit schedule: one GkrProvingLevel for wire 2. + // GkrClaimSource{Level:1} is the initial-challenge sentinel (len(schedule)=1). schedule := constraint.GkrProvingSchedule{ constraint.GkrSumcheckLevel{ - {Wires: []int{2}, ClaimSources: []int{1}}, + {Wires: []int{2}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, } testWithSchedule(t, circuit, schedule) diff --git a/internal/gkr/bls12-381/sumcheck.go b/internal/gkr/bls12-381/sumcheck.go index c00902fe0e..0b6196a393 100644 --- a/internal/gkr/bls12-381/sumcheck.go +++ b/internal/gkr/bls12-381/sumcheck.go @@ -16,15 +16,15 @@ import ( // This does not make use of parallelism and represents polynomials as lists of coefficients. // transcript is a Fiat-Shamir transcript backed by a running hash. -// Field elements are written via bind; challenges are derived via getChallenge. +// Field elements are written via Bind; challenges are derived via getChallenge. // The hash is never reset — all previous data is implicitly part of future challenges. type transcript struct { h hash.Hash - bound bool // whether bind was called since the last getChallenge + bound bool // whether Bind was called since the last getChallenge } -// bind writes field elements to the transcript as bindings for the next challenge. -func (t *transcript) bind(elements ...fr.Element) { +// Bind writes field elements to the transcript as bindings for the next challenge. +func (t *transcript) Bind(elements ...fr.Element) { if len(elements) == 0 { return } @@ -39,7 +39,7 @@ func (t *transcript) bind(elements ...fr.Element) { // If no bindings were added since the last squeeze, a separator byte is written first // to advance the state and prevent repeated values. func (t *transcript) getChallenge(bindings ...fr.Element) fr.Element { - t.bind(bindings...) + t.Bind(bindings...) if !t.bound { t.h.Write([]byte{0}) } diff --git a/internal/gkr/bn254/blueprint.go b/internal/gkr/bn254/blueprint.go index 4a9e445f2b..a9110c5b30 100644 --- a/internal/gkr/bn254/blueprint.go +++ b/internal/gkr/bn254/blueprint.go @@ -447,7 +447,7 @@ func NewBlueprints(circuit gkrcore.SerializableCircuit, schedule constraint.GkrP } proveID := compiler.AddBlueprint(prove) - // Create and register the GetAssignment blueprint + // Create and register GetAssignment blueprint getAssignment := &BlueprintGetAssignment{ SolveBlueprintID: solveID, } diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 0afe241f4c..5cab1854d9 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -366,7 +366,7 @@ func (r *resources) verifySkipLevel(levelI int, proof Proof) error { var gateEval fr.Element if wire.IsInput() { gateEval = r.assignment[wI].Evaluate(point, &r.memPool) - if claimed := finalEval[level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI)]; !claimed.Equal(&gateEval) { // an input wire has a unique self-input + if claimed := finalEval[level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI)]; !claimed.Equal(&gateEval) { return fmt.Errorf("level %d wire %d: finalEvalProof[%d] = %v, want %v", levelI, wI, level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI), &claimed, &gateEval) } } else { @@ -409,7 +409,7 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { } pools := make([]*gateEvaluatorPool, nbWires) - flatW := 0 + levelWireI := 0 for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := r.circuit[wI] @@ -417,8 +417,8 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { if wire.IsInput() { gate = gkrcore.IdentityBytecode() } - pools[flatW] = newGateEvaluatorPool(gate, len(inputIndices[flatW]), &r.memPool) - flatW++ + pools[levelWireI] = newGateEvaluatorPool(gate, len(inputIndices[levelWireI]), &r.memPool) + levelWireI++ } } @@ -426,7 +426,7 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { eqs := make([]polynomial.MultiLin, nbWires) var alpha fr.Element alpha.SetOne() - flatW = 0 + levelWireI = 0 for _, group := range level.ClaimGroups() { nbSources := len(group.ClaimSources) @@ -451,18 +451,18 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { stride.Mul(&stride, &foldingCoeff) } - eqs[flatW] = groupEq - flatW++ + eqs[levelWireI] = groupEq + levelWireI++ alpha.Mul(&alpha, &stride) for w := 1; w < len(group.Wires); w++ { - eqs[flatW] = r.memPool.Make(eqLength) + eqs[levelWireI] = polynomial.MultiLin(r.memPool.Make(eqLength)) r.workers.Submit(eqLength, func(start, end int) { for i := start; i < end; i++ { - eqs[flatW][i].Mul(&eqs[flatW-1][i], &stride) + eqs[levelWireI][i].Mul(&eqs[levelWireI-1][i], &stride) } }, 512).Wait() - flatW++ + levelWireI++ alpha.Mul(&alpha, &stride) } } @@ -534,7 +534,7 @@ func Prove(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAss } else { proof[levelI] = r.proveSumcheckLevel(levelI) } - r.transcript.bind(proof[levelI].finalEvalProof...) + constraint.BindGkrFinalEvalProof(&r.transcript, proof[levelI].finalEvalProof, c.UniqueGateInputs(r.schedule[levelI]), c.IsInput, r.schedule[levelI]) } return proof, nil @@ -565,7 +565,7 @@ func Verify(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAs if err != nil { return fmt.Errorf("level %d: %v", levelI, err) } - r.transcript.bind(proof[levelI].finalEvalProof...) + constraint.BindGkrFinalEvalProof(&r.transcript, proof[levelI].finalEvalProof, c.UniqueGateInputs(r.schedule[levelI]), c.IsInput, r.schedule[levelI]) } return nil } diff --git a/internal/gkr/bn254/gkr_test.go b/internal/gkr/bn254/gkr_test.go index 7df3933588..3d00b93430 100644 --- a/internal/gkr/bn254/gkr_test.go +++ b/internal/gkr/bn254/gkr_test.go @@ -71,7 +71,7 @@ func TestPoseidon2(t *testing.T) { test(t, gkrtesting.Poseidon2Circuit(4, 2)) } -// testSumcheckLevel exercises proveLevel/verifyLevel for a single sumcheck level. +// testSumcheckLevel exercises proveSumcheckLevel/verifySumcheckLevel for a single sumcheck level. func testSumcheckLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrProvingLevel) { t.Helper() _, sCircuit := cache.Compile(t, circuit) @@ -494,11 +494,10 @@ func newTestCase(path string) (*TestCase, error) { return nil, err } var schedule constraint.GkrProvingSchedule - if info.Schedule != nil { - if schedule, err = info.Schedule.ToProvingSchedule(); err != nil { - return nil, err - } - } else { + if schedule, err = info.Schedule.ToProvingSchedule(); err != nil { + return nil, err + } + if schedule == nil { if schedule, err = gkrcore.DefaultProvingSchedule(circuit); err != nil { return nil, err } diff --git a/internal/gkr/bn254/sumcheck.go b/internal/gkr/bn254/sumcheck.go index cdf37feb95..be4d3bf5c4 100644 --- a/internal/gkr/bn254/sumcheck.go +++ b/internal/gkr/bn254/sumcheck.go @@ -16,15 +16,15 @@ import ( // This does not make use of parallelism and represents polynomials as lists of coefficients. // transcript is a Fiat-Shamir transcript backed by a running hash. -// Field elements are written via bind; challenges are derived via getChallenge. +// Field elements are written via Bind; challenges are derived via getChallenge. // The hash is never reset — all previous data is implicitly part of future challenges. type transcript struct { h hash.Hash - bound bool // whether bind was called since the last getChallenge + bound bool // whether Bind was called since the last getChallenge } -// bind writes field elements to the transcript as bindings for the next challenge. -func (t *transcript) bind(elements ...fr.Element) { +// Bind writes field elements to the transcript as bindings for the next challenge. +func (t *transcript) Bind(elements ...fr.Element) { if len(elements) == 0 { return } @@ -39,7 +39,7 @@ func (t *transcript) bind(elements ...fr.Element) { // If no bindings were added since the last squeeze, a separator byte is written first // to advance the state and prevent repeated values. func (t *transcript) getChallenge(bindings ...fr.Element) fr.Element { - t.bind(bindings...) + t.Bind(bindings...) if !t.bound { t.h.Write([]byte{0}) } diff --git a/internal/gkr/bw6-761/blueprint.go b/internal/gkr/bw6-761/blueprint.go index 8430fb44db..f90c542d19 100644 --- a/internal/gkr/bw6-761/blueprint.go +++ b/internal/gkr/bw6-761/blueprint.go @@ -304,7 +304,7 @@ func (b *BlueprintProve) proofSize() int { } nbPaddedInstances := ecc.NextPowerOfTwo(uint64(b.SolveBlueprint.NbInstances)) logNbInstances := bits.TrailingZeros64(nbPaddedInstances) - return gkrcore.ProofSize(b.Schedule, b.SolveBlueprint.Circuit, logNbInstances) + return b.SolveBlueprint.Circuit.ProofSize(b.Schedule, logNbInstances) } // NbOutputs implements Blueprint diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index f24adababa..e72ccb326f 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -27,7 +27,7 @@ type ( // The goal is to prove/verify evaluations of many instances of the same circuit -// WireAssignment is assignment of values to the same wire across many instances of the circuit +// WireAssignment is the assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) @@ -47,7 +47,7 @@ func (e *zeroCheckLazyClaims) varsNum() int { } func (e *zeroCheckLazyClaims) degree(int) int { - return gkrcore.Degree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel), e.resources.circuit) + return e.resources.circuit.ZeroCheckDegree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel)) } // verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. @@ -62,13 +62,13 @@ func (e *zeroCheckLazyClaims) degree(int) int { // The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming // that the full sum matches purportedValue. func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { - e.resources.levelPoints[e.levelI] = r - level := e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel) - perWireInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) + e.resources.outgoingEvalPoints[e.levelI] = [][]fr.Element{r} + level := e.resources.schedule[e.levelI] + gateInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) - var terms []fr.Element - flatW := 0 - for _, group := range level { + var claimedEvals polynomial.Polynomial + levelWireI := 0 + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := e.resources.circuit[wI] @@ -77,27 +77,26 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) } else { evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) - for _, v := range perWireInputEvals[flatW] { + for _, v := range gateInputEvals[levelWireI] { evaluator.pushInput(v) } gateEval.Set(evaluator.evaluate()) } for _, src := range group.ClaimSources { - eq := polynomial.EvalEq(e.resources.levelPoints[src], r) + eq := polynomial.EvalEq(e.resources.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], r) var term fr.Element term.Mul(&eq, &gateEval) - terms = append(terms, term) + claimedEvals = append(claimedEvals, term) } - flatW++ + levelWireI++ } } - ys := polynomial.Polynomial(terms) - if total := ys.Eval(&e.foldingCoeff); total.Equal(&purportedValue) { - return nil + if total := claimedEvals.Eval(&e.foldingCoeff); !total.Equal(&purportedValue) { + return errors.New("incompatible evaluations") } - return errors.New("incompatible evaluations") + return nil } // zeroCheckClaims is a claim for sumcheck (prover side). @@ -105,7 +104,6 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. // where the sum runs over all (wire v, claim source s) pairs in the level. // Each wire has its own eq table with the batching coefficients baked in. type zeroCheckClaims struct { - level constraint.GkrSumcheckLevel levelI int resources *resources input []polynomial.MultiLin // UniqueGateInputs order @@ -115,7 +113,7 @@ type zeroCheckClaims struct { } func (c *zeroCheckClaims) varsNum() int { - return len(c.resources.levelPoints[c.level[0].ClaimSources[0]]) + return c.resources.nbVars } // roundPolynomial computes gⱼ = ∑ₕ ∑ᵥ eqs[v](Xⱼ, h...) · gateᵥ(inputs(Xⱼ, h...)). @@ -123,7 +121,8 @@ func (c *zeroCheckClaims) varsNum() int { // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). // By convention, g₀ is a constant polynomial equal to the claimed sum. func (c *zeroCheckClaims) roundPolynomial() polynomial.Polynomial { - degree := gkrcore.Degree(c.level, c.resources.circuit) + level := c.resources.schedule[c.levelI].(constraint.GkrSumcheckLevel) + degree := c.resources.circuit.ZeroCheckDegree(level) nbUniqueInputs := len(c.input) nbWires := len(c.eqs) @@ -230,7 +229,7 @@ func (c *zeroCheckClaims) roundFold(r fr.Element) { // proveFinalEval provides the unique input wire values wᵢ(r₁, ..., rₙ). func (c *zeroCheckClaims) proveFinalEval(r []fr.Element) []fr.Element { - c.resources.levelPoints[c.levelI] = r + c.resources.outgoingEvalPoints[c.levelI] = [][]fr.Element{r} evaluations := make([]fr.Element, len(c.input)) for i := range c.input { c.input[i].Fold(r[len(r)-1]) @@ -287,18 +286,18 @@ func (r *resources) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { } type resources struct { - // levelPoints[i] is the sumcheck evaluation point r produced at schedule level i. - // levelPoints[len(schedule)] holds the initial challenge (firstChallenge / rho). - // SkipLevels produce no point; their slot remains nil. - levelPoints [][]fr.Element - evalPositions [][]int // [wireI][evalI] → position in source level's finalEvalProof - nbVars int - assignment WireAssignment - memPool polynomial.Pool - workers *utils.WorkerPool - circuit Circuit - schedule constraint.GkrProvingSchedule - transcript transcript + // outgoingEvalPoints[i][k] is the k-th outgoing evaluation point (evaluation challenge) produced at schedule level i. + // outgoingEvalPoints[len(schedule)][0] holds the initial challenge (firstChallenge / rho). + // SumcheckLevels produce one point (k=0). SkipLevels pass on all their evaluation points. + outgoingEvalPoints [][][]fr.Element + nbVars int + assignment WireAssignment + memPool polynomial.Pool + workers *utils.WorkerPool + circuit Circuit + schedule constraint.GkrProvingSchedule + transcript transcript + uniqueInputIndices [][]int // uniqueInputIndices[wI][claimI]: w's unique-input index in the layer its claimI-th evaluation is coming from } func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (resources, error) { @@ -309,43 +308,109 @@ func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment } return resources{ - levelPoints: make([][]fr.Element, len(schedule)+1), - evalPositions: gkrcore.BuildFinalEvalPositions(schedule, c), - nbVars: nbVars, - assignment: assignment, - memPool: polynomial.NewPool(c.MemoryRequirements(nbInstances)...), - workers: utils.NewWorkerPool(), - circuit: c, - schedule: schedule, - transcript: transcript{h: hasher}, + outgoingEvalPoints: make([][][]fr.Element, len(schedule)+1), + nbVars: nbVars, + assignment: assignment, + memPool: polynomial.NewPool(c.MemoryRequirements(nbInstances)...), + workers: utils.NewWorkerPool(), + circuit: c, + schedule: schedule, + transcript: transcript{h: hasher}, + uniqueInputIndices: c.UniqueInputIndices(schedule), }, nil } -// proveLevel runs the sumcheck for a SumcheckLevel: derives the fold challenge, -// builds per-wire eq tables, constructs zeroCheckClaims, and calls sumcheckProve. -func (r *resources) proveLevel(levelI int) sumcheckProof { - level := r.schedule[levelI].(constraint.GkrSumcheckLevel) +// collectOutgoingEvalPoints sets the outgoing evaluation points of a skip level, equal to its incoming ones. +func (r *resources) collectOutgoingEvalPoints(levelI int) [][]fr.Element { + level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := make([][]fr.Element, level.NbOutgoingEvalPoints()) + for k, src := range level.ClaimSources { + outPoints[k] = r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] + } + r.outgoingEvalPoints[levelI] = outPoints + return outPoints +} + +// proveSkipLevel evaluates each unique gate input at each inherited evaluation point and records +// the outgoing evaluation points on the resources. +func (r *resources) proveSkipLevel(levelI int) sumcheckProof { + outPoints := r.collectOutgoingEvalPoints(levelI) + level := r.schedule[levelI].(constraint.GkrSkipLevel) + + uniqueInputs := r.circuit.UniqueGateInputs(level) + evals := make([]fr.Element, len(uniqueInputs)*len(outPoints)) + for uiI, inW := range uniqueInputs { + for k, point := range outPoints { + evals[level.FinalEvalProofIndex(uiI, k)] = r.assignment[inW].Evaluate(point, &r.memPool) + } + } + return sumcheckProof{finalEvalProof: evals} +} + +// verifySkipLevel verifies a SkipSumcheck level: checks that the finalEvalProof +// is consistent with the assignment and gate evaluations, and records outgoing eval points. +func (r *resources) verifySkipLevel(levelI int, proof Proof) error { + outPoints := r.collectOutgoingEvalPoints(levelI) + level := r.schedule[levelI].(constraint.GkrSkipLevel) - nbClaims := gkrcore.NbClaims(level) + finalEval := proof[levelI].finalEvalProof + _, inputIndices := r.circuit.InputMapping(level) + group := constraint.GkrClaimGroup(level) + initialChallengeI := len(r.schedule) + + for levelWireI, wI := range group.Wires { + wire := r.circuit[wI] + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) + for claimI, src := range group.ClaimSources { + point := outPoints[claimI] + var gateEval fr.Element + if wire.IsInput() { + gateEval = r.assignment[wI].Evaluate(point, &r.memPool) + if claimed := finalEval[level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI)]; !claimed.Equal(&gateEval) { + return fmt.Errorf("level %d wire %d: finalEvalProof[%d] = %v, want %v", levelI, wI, level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI), &claimed, &gateEval) + } + } else { + for _, inI := range inputIndices[levelWireI] { + evaluator.pushInput(finalEval[level.FinalEvalProofIndex(inI, claimI)]) + } + gateEval.Set(evaluator.evaluate()) + } + var claimedEval fr.Element + if src.Level == initialChallengeI { + claimedEval = r.assignment[wI].Evaluate(point, &r.memPool) + } else { + claimedEval = proof[src.Level].finalEvalProof[r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex)] + } + if !claimedEval.Equal(&gateEval) { + return fmt.Errorf("level %d wire %d claim %d: claimed eval %v disagrees with gate eval %v", levelI, wI, claimI, &claimedEval, &gateEval) + } + } + } + return nil +} + +func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { + level := r.schedule[levelI] + nbClaims := level.NbClaims() var foldingCoeff fr.Element if nbClaims >= 2 { foldingCoeff = r.transcript.getChallenge() } - uniqueInputs, inputIndices := gkrcore.InputMapping(level, r.circuit) + uniqueInputs, inputIndices := r.circuit.InputMapping(level) input := make([]polynomial.MultiLin, len(uniqueInputs)) for i, inW := range uniqueInputs { input[i] = r.memPool.Clone(r.assignment[inW]) } nbWires := 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { nbWires += len(group.Wires) } pools := make([]*gateEvaluatorPool, nbWires) flatW := 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := r.circuit[wI] gate := wire.Gate.Evaluate @@ -362,12 +427,12 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { var alpha fr.Element alpha.SetOne() flatW = 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { nbSources := len(group.ClaimSources) groupEq := polynomial.MultiLin(r.memPool.Make(eqLength)) groupEq[0].Set(&alpha) - groupEq.Eq(r.levelPoints[group.ClaimSources[0]]) + groupEq.Eq(r.outgoingEvalPoints[group.ClaimSources[0].Level][group.ClaimSources[0].OutgoingClaimIndex]) if nbSources > 1 { newEq := polynomial.MultiLin(r.memPool.Make(eqLength)) @@ -375,7 +440,7 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { for k := 1; k < nbSources; k++ { aI.Mul(&aI, &foldingCoeff) newEq[0].Set(&aI) - r.eqAcc(groupEq, newEq, r.levelPoints[group.ClaimSources[k]]) + r.eqAcc(groupEq, newEq, r.outgoingEvalPoints[group.ClaimSources[k].Level][group.ClaimSources[k].OutgoingClaimIndex]) } r.memPool.Dump(newEq) } @@ -403,7 +468,6 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { } claims := &zeroCheckClaims{ - level: level, levelI: levelI, resources: r, input: input, @@ -414,44 +478,37 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { return sumcheckProve(claims, &r.transcript) } -// verifyLevel runs the sumcheck verification for a SumcheckLevel: derives the fold challenge, -// computes the claimed sum, and calls sumcheckVerify. -func (r *resources) verifyLevel(levelI int, proof Proof) error { - level := r.schedule[levelI].(constraint.GkrSumcheckLevel) - initialChallengeI := len(r.schedule) - - nbClaims := gkrcore.NbClaims(level) +func (r *resources) verifySumcheckLevel(levelI int, proof Proof) error { + level := r.schedule[levelI] + nbClaims := level.NbClaims() var foldingCoeff fr.Element if nbClaims >= 2 { foldingCoeff = r.transcript.getChallenge() } - var ys []fr.Element - for _, group := range level { + initialChallengeI := len(r.schedule) + claimedEvals := make(polynomial.Polynomial, 0, level.NbClaims()) + + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { - evalI := 0 - for _, src := range group.ClaimSources { - var y fr.Element - if src == initialChallengeI { - y = r.assignment[wI].Evaluate(r.levelPoints[src], &r.memPool) + for claimI, src := range group.ClaimSources { + if src.Level == initialChallengeI { + claimedEvals = append(claimedEvals, r.assignment[wI].Evaluate(r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], &r.memPool)) } else { - y = proof[src].finalEvalProof[r.evalPositions[wI][evalI]] - evalI++ + claimedEvals = append(claimedEvals, proof[src.Level].finalEvalProof[r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex)]) } - ys = append(ys, y) } } } - ysPoly := polynomial.Polynomial(ys) - claimedSum := ysPoly.Eval(&foldingCoeff) + claimedSum := claimedEvals.Eval(&foldingCoeff) lazyClaims := &zeroCheckLazyClaims{ foldingCoeff: foldingCoeff, resources: r, levelI: levelI, } - return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, gkrcore.Degree(level, r.circuit), &r.transcript) + return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, r.circuit.ZeroCheckDegree(level.(constraint.GkrSumcheckLevel)), &r.transcript) } // Prove consistency of the claimed assignment @@ -464,23 +521,20 @@ func Prove(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAss proof := make(Proof, len(schedule)) - // Derive initial challenge point + // Derive the initial challenge point firstChallenge := make([]fr.Element, r.nbVars) for j := range r.nbVars { firstChallenge[j] = r.transcript.getChallenge() } - r.levelPoints[len(schedule)] = firstChallenge + r.outgoingEvalPoints[len(schedule)] = [][]fr.Element{firstChallenge} for levelI := len(schedule) - 1; levelI >= 0; levelI-- { - switch s := schedule[levelI].(type) { - case constraint.GkrSkipLevel: - r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - - case constraint.GkrSumcheckLevel: - proof[levelI] = r.proveLevel(levelI) - // Bind finalEvalProof for next level's challenge derivation - r.transcript.bind(proof[levelI].finalEvalProof...) + if _, isSkip := r.schedule[levelI].(constraint.GkrSkipLevel); isSkip { + proof[levelI] = r.proveSkipLevel(levelI) + } else { + proof[levelI] = r.proveSumcheckLevel(levelI) } + constraint.BindGkrFinalEvalProof(&r.transcript, proof[levelI].finalEvalProof, c.UniqueGateInputs(r.schedule[levelI]), c.IsInput, r.schedule[levelI]) } return proof, nil @@ -495,25 +549,23 @@ func Verify(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAs } defer r.workers.Stop() - // Derive initial challenge point + // Derive the initial challenge point firstChallenge := make([]fr.Element, r.nbVars) for j := range r.nbVars { firstChallenge[j] = r.transcript.getChallenge() } - r.levelPoints[len(schedule)] = firstChallenge + r.outgoingEvalPoints[len(schedule)] = [][]fr.Element{firstChallenge} for levelI := len(schedule) - 1; levelI >= 0; levelI-- { - switch s := schedule[levelI].(type) { - case constraint.GkrSkipLevel: - r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - - case constraint.GkrSumcheckLevel: - if err = r.verifyLevel(levelI, proof); err != nil { - return fmt.Errorf("sumcheck proof rejected: %v", err) - } - // Bind finalEvalProof for next level's challenge derivation - r.transcript.bind(proof[levelI].finalEvalProof...) + if _, isSkip := r.schedule[levelI].(constraint.GkrSkipLevel); isSkip { + err = r.verifySkipLevel(levelI, proof) + } else { + err = r.verifySumcheckLevel(levelI, proof) + } + if err != nil { + return fmt.Errorf("level %d: %v", levelI, err) } + constraint.BindGkrFinalEvalProof(&r.transcript, proof[levelI].finalEvalProof, c.UniqueGateInputs(r.schedule[levelI]), c.IsInput, r.schedule[levelI]) } return nil } @@ -724,7 +776,7 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() - // Return evaluator to pool (it keeps its vars slice from polynomial pool) + // Return evaluator to pool (it keeps its vars slice from the polynomial pool) gep.available[e] = struct{}{} } diff --git a/internal/gkr/bw6-761/gkr_test.go b/internal/gkr/bw6-761/gkr_test.go index 90bc302571..630dfe6fd7 100644 --- a/internal/gkr/bw6-761/gkr_test.go +++ b/internal/gkr/bw6-761/gkr_test.go @@ -11,7 +11,6 @@ import ( "os" "path/filepath" "reflect" - "strconv" "testing" "time" @@ -68,8 +67,12 @@ func TestMimc(t *testing.T) { test(t, gkrtesting.MiMCCircuit(93)) } -// testLevel exercises proveLevel/verifyLevel for a single sumcheck level. -func testLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrSumcheckLevel) { +func TestPoseidon2(t *testing.T) { + test(t, gkrtesting.Poseidon2Circuit(4, 2)) +} + +// testSumcheckLevel exercises proveSumcheckLevel/verifySumcheckLevel for a single sumcheck level. +func testSumcheckLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrProvingLevel) { t.Helper() _, sCircuit := cache.Compile(t, circuit) @@ -83,26 +86,26 @@ func testLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrSum assignment.Complete(sCircuit) schedule := constraint.GkrProvingSchedule{level} - challenge := []fr.Element{five} + initEvalPoint := [][]fr.Element{{one}} // Prove proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) defer proveR.workers.Stop() - proveR.levelPoints[1] = challenge - proof := Proof{proveR.proveLevel(0)} + proveR.outgoingEvalPoints[len(schedule)] = initEvalPoint + proof := Proof{proveR.proveSumcheckLevel(0)} // Verify verifyR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) assert.NoError(t, err) defer verifyR.workers.Stop() - verifyR.levelPoints[1] = challenge - assert.NoError(t, verifyR.verifyLevel(0, proof)) + verifyR.outgoingEvalPoints[len(schedule)] = initEvalPoint + assert.NoError(t, verifyR.verifySumcheckLevel(0, proof)) } -func TestLevel(t *testing.T) { +func TestSumcheckLevel(t *testing.T) { // Wires 0,1 = inputs; wires 2,3,4 = mul(0,1). All gates are independent outputs. circuit := gkrcore.RawCircuit{ {}, @@ -111,41 +114,115 @@ func TestLevel(t *testing.T) { {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, {Gate: gkrcore.Mul2, Inputs: []int{0, 1}}, } - // All levels have initial challenge at index 1 (len(schedule) = 1). + // Each level has an initial challenge at index 1 (len(schedule) = 1). + // GkrClaimSource{Level:1} is the initial-challenge sentinel. tests := []struct { name string - level constraint.GkrSumcheckLevel + level constraint.GkrProvingLevel }{ { name: "single wire", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []int{1}}, + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, { name: "two groups", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []int{1}}, - {Wires: []int{3}, ClaimSources: []int{1}}, + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + {Wires: []int{3}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, { name: "one group with two wires", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4, 3}, ClaimSources: []int{1}}, + {Wires: []int{4, 3}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, { name: "mixed: single + multi-wire group", level: constraint.GkrSumcheckLevel{ - {Wires: []int{4}, ClaimSources: []int{1}}, - {Wires: []int{3, 2}, ClaimSources: []int{1}}, + {Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + {Wires: []int{3, 2}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - testLevel(t, circuit, tc.level) + testSumcheckLevel(t, circuit, tc.level) + }) + } +} + +// testSkipLevel exercises proveSkipLevel/verifySkipLevel for a single skip level. +func testSkipLevel(t *testing.T, circuit gkrcore.RawCircuit, level constraint.GkrProvingLevel) { + t.Helper() + _, sCircuit := cache.Compile(t, circuit) + + ins := sCircuit.Inputs() + assignment := make(WireAssignment, len(sCircuit)) + for _, i := range ins { + assignment[i] = make([]fr.Element, 2) + fr.Vector(assignment[i]).MustSetRandom() + } + + assignment.Complete(sCircuit) + + schedule := constraint.GkrProvingSchedule{level} + initEvalPoint := [][]fr.Element{{one}} + + // Prove + proveR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) + assert.NoError(t, err) + defer proveR.workers.Stop() + + proveR.outgoingEvalPoints[len(schedule)] = initEvalPoint + proof := Proof{proveR.proveSkipLevel(0)} + + // Verify + verifyR, err := newResources(sCircuit, schedule, assignment, newMessageCounter(1, 1)) + assert.NoError(t, err) + defer verifyR.workers.Stop() + + verifyR.outgoingEvalPoints[len(schedule)] = initEvalPoint + assert.NoError(t, verifyR.verifySkipLevel(0, proof)) +} + +func TestSkipLevel(t *testing.T) { + // Wires 0,1 = inputs; wires 2,3 = identity(0); wire 4 = add(0,1). All degree-1 outputs. + circuit := gkrcore.RawCircuit{ + {}, + {}, + {Gate: gkrcore.Identity, Inputs: []int{0}}, + {Gate: gkrcore.Identity, Inputs: []int{0}}, + {Gate: gkrcore.Add2, Inputs: []int{0, 1}}, + } + + // Single-claim cases: one inherited evaluation point (OutgoingClaimIndex always 0). + singleClaim := []struct { + name string + level constraint.GkrProvingLevel + }{ + { + name: "single input wire", + level: constraint.GkrSkipLevel{Wires: []int{0}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + { + name: "single identity gate", + level: constraint.GkrSkipLevel{Wires: []int{2}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + { + name: "add gate", + level: constraint.GkrSkipLevel{Wires: []int{4}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + { + name: "two identity gates one group", + level: constraint.GkrSkipLevel{Wires: []int{2, 3}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, + }, + } + for _, tc := range singleClaim { + t.Run(tc.name, func(t *testing.T) { + testSkipLevel(t, circuit, tc.level) }) } } @@ -161,26 +238,6 @@ func init() { six.Double(&three) } -var testManyInstancesLogMaxInstances = -1 - -func getLogMaxInstances(t *testing.T) int { - if testManyInstancesLogMaxInstances == -1 { - - s := os.Getenv("GKR_LOG_INSTANCES") - if s == "" { - testManyInstancesLogMaxInstances = 5 - } else { - var err error - testManyInstancesLogMaxInstances, err = strconv.Atoi(s) - if err != nil { - t.Error(err) - } - } - - } - return testManyInstancesLogMaxInstances -} - func test(t *testing.T, circuit gkrcore.RawCircuit) { testWithSchedule(t, circuit, nil) } @@ -194,7 +251,7 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constra } ins := gCircuit.Inputs() insAssignment := make(WireAssignment, len(ins)) - maxSize := 1 << getLogMaxInstances(t) + maxSize := 1 << gkrtesting.GetLogMaxInstances(t) for i := range ins { insAssignment[i] = make([]fr.Element, maxSize) @@ -209,8 +266,6 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constra fullAssignment.Complete(sCircuit) - t.Log("Selected inputs for test") - proof, err := Prove(sCircuit, schedule, fullAssignment, newMessageCounter(1, 1)) assert.NoError(t, err) @@ -219,29 +274,11 @@ func testWithSchedule(t *testing.T, circuit gkrcore.RawCircuit, schedule constra err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(1, 1)) assert.NoError(t, err, "proof rejected") - if proof.isEmpty() { // special case for TestNoGate: - continue // there's no way to make a trivial proof fail - } - err = Verify(sCircuit, schedule, fullAssignment, proof, newMessageCounter(0, 1)) assert.NotNil(t, err, "bad proof accepted") } } -func (p Proof) isEmpty() bool { - for i := range p { - if len(p[i].finalEvalProof) != 0 { - return false - } - for j := range p[i].partialSumPolys { - if len(p[i].partialSumPolys[j]) != 0 { - return false - } - } - } - return true -} - func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { _, c := cache.Compile(t, gkrtesting.NoGateCircuit()) @@ -275,10 +312,23 @@ func generateTestVerifier(path string) func(t *testing.T) { assert.NoError(t, err) err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) assert.NoError(t, err, "proof rejected") + testCase, err = newTestCase(path) assert.NoError(t, err) err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, newMessageCounter(2, 0)) assert.NotNil(t, err, "bad proof accepted") + + testCase, err = newTestCase(path) + assert.NoError(t, err) + testCase.InOutAssignment[len(testCase.InOutAssignment)-1][0].Add(&testCase.InOutAssignment[len(testCase.InOutAssignment)-1][0], &one) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) + assert.NotNil(t, err, "tampered output accepted") + + testCase, err = newTestCase(path) + assert.NoError(t, err) + testCase.InOutAssignment[0][0].Add(&testCase.InOutAssignment[0][0], &one) + err = Verify(testCase.Circuit, testCase.Schedule, testCase.InOutAssignment, testCase.Proof, testCase.Hash) + assert.NotNil(t, err, "tampered input accepted") } } @@ -358,11 +408,11 @@ func TestSingleMulGateExplicitSchedule(t *testing.T) { _, sCircuit := cache.Compile(t, circuit) // Wire 2 is the mul gate output (inputs: 0, 1). - // Explicit schedule: one SumcheckLevel for wire 2 only. - // ClaimSources: [1] = initial challenge (index len(schedule) = 1). + // Explicit schedule: one GkrProvingLevel for wire 2. + // GkrClaimSource{Level:1} is the initial-challenge sentinel (len(schedule)=1). schedule := constraint.GkrProvingSchedule{ constraint.GkrSumcheckLevel{ - {Wires: []int{2}, ClaimSources: []int{1}}, + {Wires: []int{2}, ClaimSources: []constraint.GkrClaimSource{{Level: 1}}}, }, } testWithSchedule(t, circuit, schedule) diff --git a/internal/gkr/bw6-761/sumcheck.go b/internal/gkr/bw6-761/sumcheck.go index d6c227ad68..87e6d425ae 100644 --- a/internal/gkr/bw6-761/sumcheck.go +++ b/internal/gkr/bw6-761/sumcheck.go @@ -16,15 +16,15 @@ import ( // This does not make use of parallelism and represents polynomials as lists of coefficients. // transcript is a Fiat-Shamir transcript backed by a running hash. -// Field elements are written via bind; challenges are derived via getChallenge. +// Field elements are written via Bind; challenges are derived via getChallenge. // The hash is never reset — all previous data is implicitly part of future challenges. type transcript struct { h hash.Hash - bound bool // whether bind was called since the last getChallenge + bound bool // whether Bind was called since the last getChallenge } -// bind writes field elements to the transcript as bindings for the next challenge. -func (t *transcript) bind(elements ...fr.Element) { +// Bind writes field elements to the transcript as bindings for the next challenge. +func (t *transcript) Bind(elements ...fr.Element) { if len(elements) == 0 { return } @@ -39,7 +39,7 @@ func (t *transcript) bind(elements ...fr.Element) { // If no bindings were added since the last squeeze, a separator byte is written first // to advance the state and prevent repeated values. func (t *transcript) getChallenge(bindings ...fr.Element) fr.Element { - t.bind(bindings...) + t.Bind(bindings...) if !t.bound { t.h.Write([]byte{0}) } diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index 85c7396402..b0f02f727f 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -43,26 +43,33 @@ func (a WireAssignment) NbVars() int { type Proof []sumcheckProof // for each schedule level, a sumcheck proof -// zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). -// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, -// where the sum runs over all (wire v, claim source s) pairs in the level. -type zeroCheckLazyClaims struct { - foldingCoeff frontend.Variable - levelI int - schedule constraint.GkrProvingSchedule +// resources holds all shared state for gadget GKR verification. +type resources struct { + api frontend.API + t *transcript circuit Circuit + schedule constraint.GkrProvingSchedule assignment WireAssignment outgoingEvalPoints [][][]frontend.Variable // [levelI][outgoingClaimI] → eval point - proof Proof nbVars int + uniqueInputIndices [][]int // [wI][claimI]: w's unique-input index in the layer its claimI-th evaluation is coming from +} + +// zeroCheckLazyClaims is a lazy claim for sumcheck (verifier side). +// It checks that the polynomial ∑ᵢ cⁱ eq(-, xᵢ) wᵢ(-) sums to the expected value, +// where the sum runs over all (wire v, claim source s) pairs in the level. +type zeroCheckLazyClaims struct { + foldingCoeff frontend.Variable + r *resources + levelI int } func (e *zeroCheckLazyClaims) varsNum() int { - return e.nbVars + return e.r.nbVars } func (e *zeroCheckLazyClaims) degree(int) int { - return e.circuit.ZeroCheckDegree(e.schedule[e.levelI].(constraint.GkrSumcheckLevel)) + return e.r.circuit.ZeroCheckDegree(e.r.schedule[e.levelI].(constraint.GkrSumcheckLevel)) } // verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. @@ -75,29 +82,29 @@ func (e *zeroCheckLazyClaims) degree(int) int { // For non-input wires, the prover claims evaluations of the input wires at r, // communicated through uniqueInputEvaluations; those claims are verified later. func (e *zeroCheckLazyClaims) verifyFinalEval(api frontend.API, r []frontend.Variable, purportedValue frontend.Variable, uniqueInputEvaluations []frontend.Variable) error { - e.outgoingEvalPoints[e.levelI] = [][]frontend.Variable{r} - level := e.schedule[e.levelI] - perWireInputEvals := gkrcore.ReduplicateInputs(level, e.circuit, uniqueInputEvaluations) + e.r.outgoingEvalPoints[e.levelI] = [][]frontend.Variable{r} + level := e.r.schedule[e.levelI] + perWireInputEvals := gkrcore.ReduplicateInputs(level, e.r.circuit, uniqueInputEvaluations) var terms []frontend.Variable - flatW := 0 + levelWireI := 0 for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { - wire := e.circuit[wI] + wire := e.r.circuit[wI] var gateEval frontend.Variable if wire.IsInput() { - gateEval = e.assignment[wI].Evaluate(api, r) + gateEval = e.r.assignment[wI].Evaluate(api, r) } else { - gateEval = wire.Gate.Evaluate(FrontendAPIWrapper{api}, perWireInputEvals[flatW]...) + gateEval = wire.Gate.Evaluate(FrontendAPIWrapper{api}, perWireInputEvals[levelWireI]...) } for _, src := range group.ClaimSources { - eq := polynomial.EvalEq(api, e.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], r) + eq := polynomial.EvalEq(api, e.r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], r) term := api.Mul(eq, gateEval) terms = append(terms, term) } - flatW++ + levelWireI++ } } @@ -107,29 +114,52 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(api frontend.API, r []frontend.Var return nil } -// transcript is a Fiat-Shamir transcript backed by a running hash. -// Field elements are written via bind; challenges are derived via getChallenge. -// The hash is never reset — all previous data is implicitly part of future challenges. -type transcript struct { - h hash.FieldHasher - bound bool +func (r *resources) verifySkipLevel(levelI int) { + level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := make([][]frontend.Variable, level.NbOutgoingEvalPoints()) + for k, src := range level.ClaimSources { + outPoints[k] = r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] + } + r.outgoingEvalPoints[levelI] = outPoints } -func (t *transcript) bind(elements ...frontend.Variable) { - if len(elements) == 0 { - return +func (r *resources) verifySumcheckLevel(levelI int, proof Proof) error { + level := r.schedule[levelI] + nbClaims := level.NbClaims() + initialChallengeI := len(r.schedule) + + foldingCoeff := frontend.Variable(0) + if nbClaims >= 2 { + foldingCoeff = r.t.getChallenge() } - t.h.Write(elements...) - t.bound = true -} -func (t *transcript) getChallenge(bindings ...frontend.Variable) frontend.Variable { - t.bind(bindings...) - if !t.bound { - t.h.Write(0) // separator to prevent repeated values + var claimedEvals []frontend.Variable + for _, group := range level.ClaimGroups() { + for _, wI := range group.Wires { + for claimI, src := range group.ClaimSources { + var claimedEval frontend.Variable + if src.Level == initialChallengeI { + claimedEval = r.assignment[wI].Evaluate(r.api, r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex]) + } else { + idx := r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex) + claimedEval = proof[src.Level].FinalEvalProof[idx] + } + claimedEvals = append(claimedEvals, claimedEval) + } + } + } + claimedSum := polynomial.Polynomial(claimedEvals).Eval(r.api, foldingCoeff) + + lazyClaims := &zeroCheckLazyClaims{ + foldingCoeff: foldingCoeff, + r: r, + levelI: levelI, + } + if err := verifySumcheck(r.api, lazyClaims, proof[levelI], claimedSum, + r.circuit.ZeroCheckDegree(level.(constraint.GkrSumcheckLevel)), r.t); err != nil { + return fmt.Errorf("sumcheck proof rejected at level %d: %v", levelI, err) } - t.bound = false - return t.h.Sum() + return nil } // Verify the consistency of the claimed output with the claimed input. @@ -140,73 +170,34 @@ func Verify(api frontend.API, c Circuit, schedule constraint.GkrProvingSchedule, return errors.New("number of instances must be a power of 2") } - t := &transcript{h: h} + r := &resources{ + api: api, + t: &transcript{h: h}, + circuit: c, + schedule: schedule, + assignment: assignment, + outgoingEvalPoints: make([][][]frontend.Variable, len(schedule)+1), + nbVars: nbVars, + uniqueInputIndices: c.UniqueInputIndices(schedule), + } - uniqueInputIndices := c.UniqueInputIndices(schedule) - outgoingEvalPoints := make([][][]frontend.Variable, len(schedule)+1) initialChallengeI := len(schedule) - firstChallenge := make([]frontend.Variable, nbVars) for j := range nbVars { - firstChallenge[j] = t.getChallenge() + firstChallenge[j] = r.t.getChallenge() } - outgoingEvalPoints[initialChallengeI] = [][]frontend.Variable{firstChallenge} + r.outgoingEvalPoints[initialChallengeI] = [][]frontend.Variable{firstChallenge} for levelI := len(schedule) - 1; levelI >= 0; levelI-- { - level := schedule[levelI] - - if skipLevel, isSkip := level.(constraint.GkrSkipLevel); isSkip { - outPoints := make([][]frontend.Variable, level.NbOutgoingEvalPoints()) - for k, src := range skipLevel.ClaimSources { - outPoints[k] = outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] - } - outgoingEvalPoints[levelI] = outPoints + if _, isSkip := schedule[levelI].(constraint.GkrSkipLevel); isSkip { + r.verifySkipLevel(levelI) } else { - nbClaims := level.NbClaims() - - foldingCoeff := frontend.Variable(0) - if nbClaims >= 2 { - foldingCoeff = t.getChallenge() - } - - var claimedEvals []frontend.Variable - for _, group := range level.ClaimGroups() { - for _, wI := range group.Wires { - for claimI, src := range group.ClaimSources { - var claimedEval frontend.Variable - if src.Level == initialChallengeI { - claimedEval = assignment[wI].Evaluate(api, outgoingEvalPoints[src.Level][src.OutgoingClaimIndex]) - } else { - idx := schedule[src.Level].FinalEvalProofIndex(uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex) - claimedEval = proof[src.Level].FinalEvalProof[idx] - } - claimedEvals = append(claimedEvals, claimedEval) - } - } - } - claimedSum := polynomial.Polynomial(claimedEvals).Eval(api, foldingCoeff) - - lazyClaims := &zeroCheckLazyClaims{ - foldingCoeff: foldingCoeff, - levelI: levelI, - schedule: schedule, - circuit: c, - assignment: assignment, - outgoingEvalPoints: outgoingEvalPoints, - proof: proof, - nbVars: nbVars, - } - - if err := verifySumcheck( - api, lazyClaims, proof[levelI], claimedSum, - c.ZeroCheckDegree(level.(constraint.GkrSumcheckLevel)), - t, - ); err != nil { - return fmt.Errorf("sumcheck proof rejected at level %d: %v", levelI, err) + if err := r.verifySumcheckLevel(levelI, proof); err != nil { + return err } } - - t.bind(proof[levelI].FinalEvalProof...) + constraint.BindGkrFinalEvalProof(r.t, proof[levelI].FinalEvalProof, + c.UniqueGateInputs(schedule[levelI]), c.IsInput, schedule[levelI]) } return nil } diff --git a/internal/gkr/gkrcore/types.go b/internal/gkr/gkrcore/types.go index c0b62d4637..c552a44b84 100644 --- a/internal/gkr/gkrcore/types.go +++ b/internal/gkr/gkrcore/types.go @@ -63,6 +63,10 @@ func (w Wire[GateExecutable]) IsInput() bool { return len(w.Inputs) == 0 } +func (c Circuit[GateExecutable]) IsInput(wireIndex int) bool { + return c[wireIndex].IsInput() +} + // ClaimPropagationInfo returns sets of indices describing the pruning of claim propagation. // At the end of sumcheck for wire #wireIndex, we end up with sequences "uniqueEvaluations" and "evaluations", // the former a subsequence of the latter. diff --git a/internal/gkr/gkrtesting/gkrtesting.go b/internal/gkr/gkrtesting/gkrtesting.go index a645a5c5c0..329fb3475b 100644 --- a/internal/gkr/gkrtesting/gkrtesting.go +++ b/internal/gkr/gkrtesting/gkrtesting.go @@ -376,4 +376,4 @@ func GetLogMaxInstances(t *testing.T) int { } return testManyInstancesLogMaxInstances -} \ No newline at end of file +} diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 828fb0122a..baf61c2fad 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -27,7 +27,7 @@ type ( // The goal is to prove/verify evaluations of many instances of the same circuit -// WireAssignment is assignment of values to the same wire across many instances of the circuit +// WireAssignment is the assignment of values to the same wire across many instances of the circuit type WireAssignment []polynomial.MultiLin type Proof []sumcheckProof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) @@ -47,7 +47,7 @@ func (e *zeroCheckLazyClaims) varsNum() int { } func (e *zeroCheckLazyClaims) degree(int) int { - return gkrcore.Degree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel), e.resources.circuit) + return e.resources.circuit.ZeroCheckDegree(e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel)) } // verifyFinalEval finalizes the verification of a level at the sumcheck evaluation point r. @@ -62,13 +62,13 @@ func (e *zeroCheckLazyClaims) degree(int) int { // The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming // that the full sum matches purportedValue. func (e *zeroCheckLazyClaims) verifyFinalEval(r []small_rational.SmallRational, purportedValue small_rational.SmallRational, uniqueInputEvaluations []small_rational.SmallRational) error { - e.resources.levelPoints[e.levelI] = r - level := e.resources.schedule[e.levelI].(constraint.GkrSumcheckLevel) - perWireInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) + e.resources.outgoingEvalPoints[e.levelI] = [][]small_rational.SmallRational{r} + level := e.resources.schedule[e.levelI] + gateInputEvals := gkrcore.ReduplicateInputs(level, e.resources.circuit, uniqueInputEvaluations) - var terms []small_rational.SmallRational - flatW := 0 - for _, group := range level { + var claimedEvals polynomial.Polynomial + levelWireI := 0 + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := e.resources.circuit[wI] @@ -77,27 +77,26 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []small_rational.SmallRational, gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) } else { evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) - for _, v := range perWireInputEvals[flatW] { + for _, v := range gateInputEvals[levelWireI] { evaluator.pushInput(v) } gateEval.Set(evaluator.evaluate()) } for _, src := range group.ClaimSources { - eq := polynomial.EvalEq(e.resources.levelPoints[src], r) + eq := polynomial.EvalEq(e.resources.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], r) var term small_rational.SmallRational term.Mul(&eq, &gateEval) - terms = append(terms, term) + claimedEvals = append(claimedEvals, term) } - flatW++ + levelWireI++ } } - ys := polynomial.Polynomial(terms) - if total := ys.Eval(&e.foldingCoeff); total.Equal(&purportedValue) { - return nil + if total := claimedEvals.Eval(&e.foldingCoeff); !total.Equal(&purportedValue) { + return errors.New("incompatible evaluations") } - return errors.New("incompatible evaluations") + return nil } // zeroCheckClaims is a claim for sumcheck (prover side). @@ -105,7 +104,6 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []small_rational.SmallRational, // where the sum runs over all (wire v, claim source s) pairs in the level. // Each wire has its own eq table with the batching coefficients baked in. type zeroCheckClaims struct { - level constraint.GkrSumcheckLevel levelI int resources *resources input []polynomial.MultiLin // UniqueGateInputs order @@ -115,7 +113,7 @@ type zeroCheckClaims struct { } func (c *zeroCheckClaims) varsNum() int { - return len(c.resources.levelPoints[c.level[0].ClaimSources[0]]) + return c.resources.nbVars } // roundPolynomial computes gⱼ = ∑ₕ ∑ᵥ eqs[v](Xⱼ, h...) · gateᵥ(inputs(Xⱼ, h...)). @@ -123,7 +121,8 @@ func (c *zeroCheckClaims) varsNum() int { // The value gⱼ(0) is inferred from the equation gⱼ(0) + gⱼ(1) = gⱼ₋₁(rⱼ₋₁). // By convention, g₀ is a constant polynomial equal to the claimed sum. func (c *zeroCheckClaims) roundPolynomial() polynomial.Polynomial { - degree := gkrcore.Degree(c.level, c.resources.circuit) + level := c.resources.schedule[c.levelI].(constraint.GkrSumcheckLevel) + degree := c.resources.circuit.ZeroCheckDegree(level) nbUniqueInputs := len(c.input) nbWires := len(c.eqs) @@ -230,7 +229,7 @@ func (c *zeroCheckClaims) roundFold(r small_rational.SmallRational) { // proveFinalEval provides the unique input wire values wᵢ(r₁, ..., rₙ). func (c *zeroCheckClaims) proveFinalEval(r []small_rational.SmallRational) []small_rational.SmallRational { - c.resources.levelPoints[c.levelI] = r + c.resources.outgoingEvalPoints[c.levelI] = [][]small_rational.SmallRational{r} evaluations := make([]small_rational.SmallRational, len(c.input)) for i := range c.input { c.input[i].Fold(r[len(r)-1]) @@ -287,18 +286,18 @@ func (r *resources) eqAcc(e, m polynomial.MultiLin, q []small_rational.SmallRati } type resources struct { - // levelPoints[i] is the sumcheck evaluation point r produced at schedule level i. - // levelPoints[len(schedule)] holds the initial challenge (firstChallenge / rho). - // SkipLevels produce no point; their slot remains nil. - levelPoints [][]small_rational.SmallRational - evalPositions [][]int // [wireI][evalI] → position in source level's finalEvalProof - nbVars int - assignment WireAssignment - memPool polynomial.Pool - workers *utils.WorkerPool - circuit Circuit - schedule constraint.GkrProvingSchedule - transcript transcript + // outgoingEvalPoints[i][k] is the k-th outgoing evaluation point (evaluation challenge) produced at schedule level i. + // outgoingEvalPoints[len(schedule)][0] holds the initial challenge (firstChallenge / rho). + // SumcheckLevels produce one point (k=0). SkipLevels pass on all their evaluation points. + outgoingEvalPoints [][][]small_rational.SmallRational + nbVars int + assignment WireAssignment + memPool polynomial.Pool + workers *utils.WorkerPool + circuit Circuit + schedule constraint.GkrProvingSchedule + transcript transcript + uniqueInputIndices [][]int // uniqueInputIndices[wI][claimI]: w's unique-input index in the layer its claimI-th evaluation is coming from } func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, hasher hash.Hash) (resources, error) { @@ -309,43 +308,109 @@ func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment } return resources{ - levelPoints: make([][]small_rational.SmallRational, len(schedule)+1), - evalPositions: gkrcore.BuildFinalEvalPositions(schedule, c), - nbVars: nbVars, - assignment: assignment, - memPool: polynomial.NewPool(c.MemoryRequirements(nbInstances)...), - workers: utils.NewWorkerPool(), - circuit: c, - schedule: schedule, - transcript: transcript{h: hasher}, + outgoingEvalPoints: make([][][]small_rational.SmallRational, len(schedule)+1), + nbVars: nbVars, + assignment: assignment, + memPool: polynomial.NewPool(c.MemoryRequirements(nbInstances)...), + workers: utils.NewWorkerPool(), + circuit: c, + schedule: schedule, + transcript: transcript{h: hasher}, + uniqueInputIndices: c.UniqueInputIndices(schedule), }, nil } -// proveLevel runs the sumcheck for a SumcheckLevel: derives the fold challenge, -// builds per-wire eq tables, constructs zeroCheckClaims, and calls sumcheckProve. -func (r *resources) proveLevel(levelI int) sumcheckProof { - level := r.schedule[levelI].(constraint.GkrSumcheckLevel) +// collectOutgoingEvalPoints sets the outgoing evaluation points of a skip level, equal to its incoming ones. +func (r *resources) collectOutgoingEvalPoints(levelI int) [][]small_rational.SmallRational { + level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := make([][]small_rational.SmallRational, level.NbOutgoingEvalPoints()) + for k, src := range level.ClaimSources { + outPoints[k] = r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] + } + r.outgoingEvalPoints[levelI] = outPoints + return outPoints +} + +// proveSkipLevel evaluates each unique gate input at each inherited evaluation point and records +// the outgoing evaluation points on the resources. +func (r *resources) proveSkipLevel(levelI int) sumcheckProof { + outPoints := r.collectOutgoingEvalPoints(levelI) + level := r.schedule[levelI].(constraint.GkrSkipLevel) + + uniqueInputs := r.circuit.UniqueGateInputs(level) + evals := make([]small_rational.SmallRational, len(uniqueInputs)*len(outPoints)) + for uiI, inW := range uniqueInputs { + for k, point := range outPoints { + evals[level.FinalEvalProofIndex(uiI, k)] = r.assignment[inW].Evaluate(point, &r.memPool) + } + } + return sumcheckProof{finalEvalProof: evals} +} + +// verifySkipLevel verifies a SkipSumcheck level: checks that the finalEvalProof +// is consistent with the assignment and gate evaluations, and records outgoing eval points. +func (r *resources) verifySkipLevel(levelI int, proof Proof) error { + outPoints := r.collectOutgoingEvalPoints(levelI) + level := r.schedule[levelI].(constraint.GkrSkipLevel) - nbClaims := gkrcore.NbClaims(level) + finalEval := proof[levelI].finalEvalProof + _, inputIndices := r.circuit.InputMapping(level) + group := constraint.GkrClaimGroup(level) + initialChallengeI := len(r.schedule) + + for levelWireI, wI := range group.Wires { + wire := r.circuit[wI] + evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) + for claimI, src := range group.ClaimSources { + point := outPoints[claimI] + var gateEval small_rational.SmallRational + if wire.IsInput() { + gateEval = r.assignment[wI].Evaluate(point, &r.memPool) + if claimed := finalEval[level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI)]; !claimed.Equal(&gateEval) { + return fmt.Errorf("level %d wire %d: finalEvalProof[%d] = %v, want %v", levelI, wI, level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI), &claimed, &gateEval) + } + } else { + for _, inI := range inputIndices[levelWireI] { + evaluator.pushInput(finalEval[level.FinalEvalProofIndex(inI, claimI)]) + } + gateEval.Set(evaluator.evaluate()) + } + var claimedEval small_rational.SmallRational + if src.Level == initialChallengeI { + claimedEval = r.assignment[wI].Evaluate(point, &r.memPool) + } else { + claimedEval = proof[src.Level].finalEvalProof[r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex)] + } + if !claimedEval.Equal(&gateEval) { + return fmt.Errorf("level %d wire %d claim %d: claimed eval %v disagrees with gate eval %v", levelI, wI, claimI, &claimedEval, &gateEval) + } + } + } + return nil +} + +func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { + level := r.schedule[levelI] + nbClaims := level.NbClaims() var foldingCoeff small_rational.SmallRational if nbClaims >= 2 { foldingCoeff = r.transcript.getChallenge() } - uniqueInputs, inputIndices := gkrcore.InputMapping(level, r.circuit) + uniqueInputs, inputIndices := r.circuit.InputMapping(level) input := make([]polynomial.MultiLin, len(uniqueInputs)) for i, inW := range uniqueInputs { input[i] = r.memPool.Clone(r.assignment[inW]) } nbWires := 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { nbWires += len(group.Wires) } pools := make([]*gateEvaluatorPool, nbWires) flatW := 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := r.circuit[wI] gate := wire.Gate.Evaluate @@ -362,12 +427,12 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { var alpha small_rational.SmallRational alpha.SetOne() flatW = 0 - for _, group := range level { + for _, group := range level.ClaimGroups() { nbSources := len(group.ClaimSources) groupEq := polynomial.MultiLin(r.memPool.Make(eqLength)) groupEq[0].Set(&alpha) - groupEq.Eq(r.levelPoints[group.ClaimSources[0]]) + groupEq.Eq(r.outgoingEvalPoints[group.ClaimSources[0].Level][group.ClaimSources[0].OutgoingClaimIndex]) if nbSources > 1 { newEq := polynomial.MultiLin(r.memPool.Make(eqLength)) @@ -375,7 +440,7 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { for k := 1; k < nbSources; k++ { aI.Mul(&aI, &foldingCoeff) newEq[0].Set(&aI) - r.eqAcc(groupEq, newEq, r.levelPoints[group.ClaimSources[k]]) + r.eqAcc(groupEq, newEq, r.outgoingEvalPoints[group.ClaimSources[k].Level][group.ClaimSources[k].OutgoingClaimIndex]) } r.memPool.Dump(newEq) } @@ -403,7 +468,6 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { } claims := &zeroCheckClaims{ - level: level, levelI: levelI, resources: r, input: input, @@ -414,44 +478,37 @@ func (r *resources) proveLevel(levelI int) sumcheckProof { return sumcheckProve(claims, &r.transcript) } -// verifyLevel runs the sumcheck verification for a SumcheckLevel: derives the fold challenge, -// computes the claimed sum, and calls sumcheckVerify. -func (r *resources) verifyLevel(levelI int, proof Proof) error { - level := r.schedule[levelI].(constraint.GkrSumcheckLevel) - initialChallengeI := len(r.schedule) - - nbClaims := gkrcore.NbClaims(level) +func (r *resources) verifySumcheckLevel(levelI int, proof Proof) error { + level := r.schedule[levelI] + nbClaims := level.NbClaims() var foldingCoeff small_rational.SmallRational if nbClaims >= 2 { foldingCoeff = r.transcript.getChallenge() } - var ys []small_rational.SmallRational - for _, group := range level { + initialChallengeI := len(r.schedule) + claimedEvals := make(polynomial.Polynomial, 0, level.NbClaims()) + + for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { - evalI := 0 - for _, src := range group.ClaimSources { - var y small_rational.SmallRational - if src == initialChallengeI { - y = r.assignment[wI].Evaluate(r.levelPoints[src], &r.memPool) + for claimI, src := range group.ClaimSources { + if src.Level == initialChallengeI { + claimedEvals = append(claimedEvals, r.assignment[wI].Evaluate(r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex], &r.memPool)) } else { - y = proof[src].finalEvalProof[r.evalPositions[wI][evalI]] - evalI++ + claimedEvals = append(claimedEvals, proof[src.Level].finalEvalProof[r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex)]) } - ys = append(ys, y) } } } - ysPoly := polynomial.Polynomial(ys) - claimedSum := ysPoly.Eval(&foldingCoeff) + claimedSum := claimedEvals.Eval(&foldingCoeff) lazyClaims := &zeroCheckLazyClaims{ foldingCoeff: foldingCoeff, resources: r, levelI: levelI, } - return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, gkrcore.Degree(level, r.circuit), &r.transcript) + return sumcheckVerify(lazyClaims, proof[levelI], claimedSum, r.circuit.ZeroCheckDegree(level.(constraint.GkrSumcheckLevel)), &r.transcript) } // Prove consistency of the claimed assignment @@ -464,23 +521,20 @@ func Prove(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAss proof := make(Proof, len(schedule)) - // Derive initial challenge point + // Derive the initial challenge point firstChallenge := make([]small_rational.SmallRational, r.nbVars) for j := range r.nbVars { firstChallenge[j] = r.transcript.getChallenge() } - r.levelPoints[len(schedule)] = firstChallenge + r.outgoingEvalPoints[len(schedule)] = [][]small_rational.SmallRational{firstChallenge} for levelI := len(schedule) - 1; levelI >= 0; levelI-- { - switch s := schedule[levelI].(type) { - case constraint.GkrSkipLevel: - r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - - case constraint.GkrSumcheckLevel: - proof[levelI] = r.proveLevel(levelI) - // Bind finalEvalProof for next level's challenge derivation - r.transcript.bind(proof[levelI].finalEvalProof...) + if _, isSkip := r.schedule[levelI].(constraint.GkrSkipLevel); isSkip { + proof[levelI] = r.proveSkipLevel(levelI) + } else { + proof[levelI] = r.proveSumcheckLevel(levelI) } + constraint.BindGkrFinalEvalProof(&r.transcript, proof[levelI].finalEvalProof, c.UniqueGateInputs(r.schedule[levelI]), c.IsInput, r.schedule[levelI]) } return proof, nil @@ -495,25 +549,23 @@ func Verify(c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAs } defer r.workers.Stop() - // Derive initial challenge point + // Derive the initial challenge point firstChallenge := make([]small_rational.SmallRational, r.nbVars) for j := range r.nbVars { firstChallenge[j] = r.transcript.getChallenge() } - r.levelPoints[len(schedule)] = firstChallenge + r.outgoingEvalPoints[len(schedule)] = [][]small_rational.SmallRational{firstChallenge} for levelI := len(schedule) - 1; levelI >= 0; levelI-- { - switch s := schedule[levelI].(type) { - case constraint.GkrSkipLevel: - r.levelPoints[levelI] = r.levelPoints[s.ClaimSources[0]] - - case constraint.GkrSumcheckLevel: - if err = r.verifyLevel(levelI, proof); err != nil { - return fmt.Errorf("sumcheck proof rejected: %v", err) - } - // Bind finalEvalProof for next level's challenge derivation - r.transcript.bind(proof[levelI].finalEvalProof...) + if _, isSkip := r.schedule[levelI].(constraint.GkrSkipLevel); isSkip { + err = r.verifySkipLevel(levelI, proof) + } else { + err = r.verifySumcheckLevel(levelI, proof) + } + if err != nil { + return fmt.Errorf("level %d: %v", levelI, err) } + constraint.BindGkrFinalEvalProof(&r.transcript, proof[levelI].finalEvalProof, c.UniqueGateInputs(r.schedule[levelI]), c.IsInput, r.schedule[levelI]) } return nil } @@ -724,7 +776,7 @@ func (gep *gateEvaluatorPool) put(e *gateEvaluator) { gep.lock.Lock() defer gep.lock.Unlock() - // Return evaluator to pool (it keeps its vars slice from polynomial pool) + // Return evaluator to pool (it keeps its vars slice from the polynomial pool) gep.available[e] = struct{}{} } diff --git a/internal/gkr/small_rational/sumcheck.go b/internal/gkr/small_rational/sumcheck.go index 281d6a4348..375c402639 100644 --- a/internal/gkr/small_rational/sumcheck.go +++ b/internal/gkr/small_rational/sumcheck.go @@ -16,15 +16,15 @@ import ( // This does not make use of parallelism and represents polynomials as lists of coefficients. // transcript is a Fiat-Shamir transcript backed by a running hash. -// Field elements are written via bind; challenges are derived via getChallenge. +// Field elements are written via Bind; challenges are derived via getChallenge. // The hash is never reset — all previous data is implicitly part of future challenges. type transcript struct { h hash.Hash - bound bool // whether bind was called since the last getChallenge + bound bool // whether Bind was called since the last getChallenge } -// bind writes field elements to the transcript as bindings for the next challenge. -func (t *transcript) bind(elements ...small_rational.SmallRational) { +// Bind writes field elements to the transcript as bindings for the next challenge. +func (t *transcript) Bind(elements ...small_rational.SmallRational) { if len(elements) == 0 { return } @@ -39,7 +39,7 @@ func (t *transcript) bind(elements ...small_rational.SmallRational) { // If no bindings were added since the last squeeze, a separator byte is written first // to advance the state and prevent repeated values. func (t *transcript) getChallenge(bindings ...small_rational.SmallRational) small_rational.SmallRational { - t.bind(bindings...) + t.Bind(bindings...) if !t.bound { t.h.Write([]byte{0}) } diff --git a/internal/gkr/sumcheck.go b/internal/gkr/sumcheck.go index 50ee4abc5c..94cc29349f 100644 --- a/internal/gkr/sumcheck.go +++ b/internal/gkr/sumcheck.go @@ -4,6 +4,7 @@ import ( "errors" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/hash" "github.com/consensys/gnark/std/polynomial" ) @@ -22,6 +23,31 @@ type sumcheckProof struct { FinalEvalProof []frontend.Variable } +// transcript is a Fiat-Shamir transcript backed by a running hash. +// Field elements are written via Bind; challenges are derived via getChallenge. +// The hash is never reset — all previous data is implicitly part of future challenges. +type transcript struct { + h hash.FieldHasher + bound bool +} + +func (t *transcript) Bind(elements ...frontend.Variable) { + if len(elements) == 0 { + return + } + t.h.Write(elements...) + t.bound = true +} + +func (t *transcript) getChallenge(bindings ...frontend.Variable) frontend.Variable { + t.Bind(bindings...) + if !t.bound { + t.h.Write(0) // separator to prevent repeated values + } + t.bound = false + return t.h.Sum() +} + func verifySumcheck(api frontend.API, claims sumcheckLazyClaims, proof sumcheckProof, claimedSum frontend.Variable, degree int, t *transcript) error { r := make([]frontend.Variable, claims.varsNum()) diff --git a/internal/gkr/test_vectors/single_identity_gate_two_instances.json b/internal/gkr/test_vectors/single_identity_gate_two_instances.json index ba28e35961..97d6fdd62c 100644 --- a/internal/gkr/test_vectors/single_identity_gate_two_instances.json +++ b/internal/gkr/test_vectors/single_identity_gate_two_instances.json @@ -18,19 +18,16 @@ ], "proof": [ { - "finalEvalProof": [], + "finalEvalProof": [ + 5 + ], "partialSumPolys": [] }, { "finalEvalProof": [ 5 ], - "partialSumPolys": [ - [ - -3, - -8 - ] - ] + "partialSumPolys": [] } ] } \ No newline at end of file diff --git a/internal/gkr/test_vectors/single_input_two_identity_gates_two_instances.json b/internal/gkr/test_vectors/single_input_two_identity_gates_two_instances.json index 1451b332c2..052e08d08f 100644 --- a/internal/gkr/test_vectors/single_input_two_identity_gates_two_instances.json +++ b/internal/gkr/test_vectors/single_input_two_identity_gates_two_instances.json @@ -21,36 +21,17 @@ ] ], "proof": [ - { - "finalEvalProof": [], - "partialSumPolys": [ - [ - 0, - 0 - ] - ] - }, { "finalEvalProof": [ 1 ], - "partialSumPolys": [ - [ - -3, - -16 - ] - ] + "partialSumPolys": [] }, { "finalEvalProof": [ 1 ], - "partialSumPolys": [ - [ - -3, - -16 - ] - ] + "partialSumPolys": [] } ] } \ No newline at end of file diff --git a/internal/gkr/test_vectors/single_input_two_outs_two_instances.json b/internal/gkr/test_vectors/single_input_two_outs_two_instances.json index 897aea7ee5..9f39dc5cdd 100644 --- a/internal/gkr/test_vectors/single_input_two_outs_two_instances.json +++ b/internal/gkr/test_vectors/single_input_two_outs_two_instances.json @@ -22,7 +22,9 @@ ], "proof": [ { - "finalEvalProof": [], + "finalEvalProof": [ + 0 + ], "partialSumPolys": [ [ 0, @@ -46,12 +48,7 @@ "finalEvalProof": [ 0 ], - "partialSumPolys": [ - [ - -2, - -12 - ] - ] + "partialSumPolys": [] } ] } \ No newline at end of file diff --git a/internal/gkr/test_vectors/single_mimc_gate_four_instances.json b/internal/gkr/test_vectors/single_mimc_gate_four_instances.json index a724ba5a7b..3907e92c92 100644 --- a/internal/gkr/test_vectors/single_mimc_gate_four_instances.json +++ b/internal/gkr/test_vectors/single_mimc_gate_four_instances.json @@ -28,11 +28,15 @@ ], "proof": [ { - "finalEvalProof": [], + "finalEvalProof": [ + -1 + ], "partialSumPolys": [] }, { - "finalEvalProof": [], + "finalEvalProof": [ + -3 + ], "partialSumPolys": [] }, { diff --git a/internal/gkr/test_vectors/single_mimc_gate_two_instances.json b/internal/gkr/test_vectors/single_mimc_gate_two_instances.json index 901db48692..80a585dced 100644 --- a/internal/gkr/test_vectors/single_mimc_gate_two_instances.json +++ b/internal/gkr/test_vectors/single_mimc_gate_two_instances.json @@ -22,11 +22,15 @@ ], "proof": [ { - "finalEvalProof": [], + "finalEvalProof": [ + 1 + ], "partialSumPolys": [] }, { - "finalEvalProof": [], + "finalEvalProof": [ + 0 + ], "partialSumPolys": [] }, { diff --git a/internal/gkr/test_vectors/single_mul_gate_two_instances.json b/internal/gkr/test_vectors/single_mul_gate_two_instances.json index b85a6df42c..390f3ef9ba 100644 --- a/internal/gkr/test_vectors/single_mul_gate_two_instances.json +++ b/internal/gkr/test_vectors/single_mul_gate_two_instances.json @@ -22,11 +22,15 @@ ], "proof": [ { - "finalEvalProof": [], + "finalEvalProof": [ + 5 + ], "partialSumPolys": [] }, { - "finalEvalProof": [], + "finalEvalProof": [ + 1 + ], "partialSumPolys": [] }, { diff --git a/internal/gkr/test_vectors/testdata/gkr_circuit_bn254.scs b/internal/gkr/test_vectors/testdata/gkr_circuit_bn254.scs index 503d00e477b3fd4fac56c2d744d6668021e4ab09..ad8109f98438ac75dcefb44406edcc1d156f299c 100644 GIT binary patch delta 7637 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 Pyt5)ay%YcS<--30dA@K* literal 69707 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 diff --git a/internal/gkr/test_vectors/two_identity_gates_composed_single_input_two_instances.json b/internal/gkr/test_vectors/two_identity_gates_composed_single_input_two_instances.json index 69a2038a75..d4c277e246 100644 --- a/internal/gkr/test_vectors/two_identity_gates_composed_single_input_two_instances.json +++ b/internal/gkr/test_vectors/two_identity_gates_composed_single_input_two_instances.json @@ -18,30 +18,22 @@ ], "proof": [ { - "finalEvalProof": [], + "finalEvalProof": [ + 3 + ], "partialSumPolys": [] }, { "finalEvalProof": [ 3 ], - "partialSumPolys": [ - [ - -1, - 0 - ] - ] + "partialSumPolys": [] }, { "finalEvalProof": [ 3 ], - "partialSumPolys": [ - [ - -1, - 0 - ] - ] + "partialSumPolys": [] } ] } \ No newline at end of file diff --git a/internal/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json b/internal/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json index 2dca0746a2..731b3c6f35 100644 --- a/internal/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json +++ b/internal/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json @@ -22,11 +22,15 @@ ], "proof": [ { - "finalEvalProof": [], + "finalEvalProof": [ + -1 + ], "partialSumPolys": [] }, { - "finalEvalProof": [], + "finalEvalProof": [ + 1 + ], "partialSumPolys": [] }, { @@ -34,12 +38,7 @@ -1, 1 ], - "partialSumPolys": [ - [ - -3, - -16 - ] - ] + "partialSumPolys": [] } ] } \ No newline at end of file From 6a6e88d468167f780fca75f938748d84bfc56e68 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 16 Mar 2026 02:15:32 -0500 Subject: [PATCH 244/251] fix: verifySkipLevel gadget --- .../backend/template/gkr/gkr.go.tmpl | 3 ++ internal/gkr/bls12-377/gkr.go | 3 ++ internal/gkr/bls12-381/gkr.go | 3 ++ internal/gkr/bn254/gkr.go | 25 ++++----- internal/gkr/bw6-761/gkr.go | 3 ++ internal/gkr/gkr.go | 52 ++++++++++++++----- internal/gkr/gkrcore/schedule.go | 10 ++++ internal/gkr/small_rational/gkr.go | 3 ++ internal/gkr/sumcheck.go | 2 +- 9 files changed, 75 insertions(+), 29 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 48a904f414..58e866a234 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -68,6 +68,9 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []{{ .ElementType }}, purportedV var gateEval {{ .ElementType }} if wire.IsInput() { gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) + if !gateInputEvals[levelWireI][0].Equal(&gateEval) { + return errors.New("incompatible evaluations") + } } else { evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, v := range gateInputEvals[levelWireI] { diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index 4db8d3dd6a..bc37e4339a 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -75,6 +75,9 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. var gateEval fr.Element if wire.IsInput() { gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) + if !gateInputEvals[levelWireI][0].Equal(&gateEval) { + return errors.New("incompatible evaluations") + } } else { evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, v := range gateInputEvals[levelWireI] { diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index cd0ccc12b7..eb9339f377 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -75,6 +75,9 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. var gateEval fr.Element if wire.IsInput() { gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) + if !gateInputEvals[levelWireI][0].Equal(&gateEval) { + return errors.New("incompatible evaluations") + } } else { evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, v := range gateInputEvals[levelWireI] { diff --git a/internal/gkr/bn254/gkr.go b/internal/gkr/bn254/gkr.go index 5cab1854d9..617b104f8e 100644 --- a/internal/gkr/bn254/gkr.go +++ b/internal/gkr/bn254/gkr.go @@ -56,9 +56,10 @@ func (e *zeroCheckLazyClaims) degree(int) int { // claims on each wire and c is foldingCoeff. // Both purportedValue and the vector r have been randomized during sumcheck. // -// For input wires, w(r) is computed directly from the assignment. -// For non-input wires, the prover claims evaluations of the input wires at r, -// communicated through uniqueInputEvaluations; those claims are verified later. +// For input wires, w(r) is computed directly from the assignment and the claimed +// evaluation in uniqueInputEvaluations is checked equal to it. +// For non-input wires, the prover claims evaluations of their gate inputs at r via +// uniqueInputEvaluations; those claims are verified by lower levels' sumchecks. // The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming // that the full sum matches purportedValue. func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { @@ -75,6 +76,9 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. var gateEval fr.Element if wire.IsInput() { gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) + if !gateInputEvals[levelWireI][0].Equal(&gateEval) { + return errors.New("incompatible evaluations") + } } else { evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, v := range gateInputEvals[levelWireI] { @@ -320,22 +324,11 @@ func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment }, nil } -// collectOutgoingEvalPoints sets the outgoing evaluation points of a skip level, equal to its incoming ones. -func (r *resources) collectOutgoingEvalPoints(levelI int) [][]fr.Element { - level := r.schedule[levelI].(constraint.GkrSkipLevel) - outPoints := make([][]fr.Element, level.NbOutgoingEvalPoints()) - for k, src := range level.ClaimSources { - outPoints[k] = r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] - } - r.outgoingEvalPoints[levelI] = outPoints - return outPoints -} - // proveSkipLevel evaluates each unique gate input at each inherited evaluation point and records // the outgoing evaluation points on the resources. func (r *resources) proveSkipLevel(levelI int) sumcheckProof { - outPoints := r.collectOutgoingEvalPoints(levelI) level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := gkrcore.CollectOutgoingEvalPoints(level, levelI, r.outgoingEvalPoints) uniqueInputs := r.circuit.UniqueGateInputs(level) evals := make([]fr.Element, len(uniqueInputs)*len(outPoints)) @@ -350,8 +343,8 @@ func (r *resources) proveSkipLevel(levelI int) sumcheckProof { // verifySkipLevel verifies a SkipSumcheck level: checks that the finalEvalProof // is consistent with the assignment and gate evaluations, and records outgoing eval points. func (r *resources) verifySkipLevel(levelI int, proof Proof) error { - outPoints := r.collectOutgoingEvalPoints(levelI) level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := gkrcore.CollectOutgoingEvalPoints(level, levelI, r.outgoingEvalPoints) finalEval := proof[levelI].finalEvalProof _, inputIndices := r.circuit.InputMapping(level) diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index e72ccb326f..5f811d55bc 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -75,6 +75,9 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr. var gateEval fr.Element if wire.IsInput() { gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) + if !gateInputEvals[levelWireI][0].Equal(&gateEval) { + return errors.New("incompatible evaluations") + } } else { evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, v := range gateInputEvals[levelWireI] { diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index b0f02f727f..2691b13c29 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -78,9 +78,10 @@ func (e *zeroCheckLazyClaims) degree(int) int { // claims on each wire and c is foldingCoeff. // Both purportedValue and the vector r have been randomized during sumcheck. // -// For input wires, w(r) is computed directly from the assignment. -// For non-input wires, the prover claims evaluations of the input wires at r, -// communicated through uniqueInputEvaluations; those claims are verified later. +// For input wires, w(r) is computed directly from the assignment and the claimed +// evaluation in uniqueInputEvaluations is asserted equal to it. +// For non-input wires, the prover claims evaluations of their gate inputs at r via +// uniqueInputEvaluations; those claims are verified by lower levels' sumchecks. func (e *zeroCheckLazyClaims) verifyFinalEval(api frontend.API, r []frontend.Variable, purportedValue frontend.Variable, uniqueInputEvaluations []frontend.Variable) error { e.r.outgoingEvalPoints[e.levelI] = [][]frontend.Variable{r} level := e.r.schedule[e.levelI] @@ -95,6 +96,7 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(api frontend.API, r []frontend.Var var gateEval frontend.Variable if wire.IsInput() { gateEval = e.r.assignment[wI].Evaluate(api, r) + api.AssertIsEqual(perWireInputEvals[levelWireI][0], gateEval) } else { gateEval = wire.Gate.Evaluate(FrontendAPIWrapper{api}, perWireInputEvals[levelWireI]...) } @@ -114,13 +116,40 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(api frontend.API, r []frontend.Var return nil } -func (r *resources) verifySkipLevel(levelI int) { +func (r *resources) verifySkipLevel(levelI int, proof Proof) { level := r.schedule[levelI].(constraint.GkrSkipLevel) - outPoints := make([][]frontend.Variable, level.NbOutgoingEvalPoints()) - for k, src := range level.ClaimSources { - outPoints[k] = r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] + outPoints := gkrcore.CollectOutgoingEvalPoints(level, levelI, r.outgoingEvalPoints) + + finalEval := proof[levelI].FinalEvalProof + _, inputIndices := r.circuit.InputMapping(level) + group := constraint.GkrClaimGroup(level) + initialChallengeI := len(r.schedule) + + for levelWireI, wI := range group.Wires { + wire := r.circuit[wI] + gateIns := make([]frontend.Variable, len(wire.Inputs)) + for claimI, src := range group.ClaimSources { + point := outPoints[claimI] + var gateEval frontend.Variable + if wire.IsInput() { + gateEval = r.assignment[wI].Evaluate(r.api, point) + claimed := finalEval[level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI)] + r.api.AssertIsEqual(claimed, gateEval) + } else { + for _, inI := range inputIndices[levelWireI] { + gateIns[inI] = finalEval[level.FinalEvalProofIndex(inI, claimI)] + } + gateEval = wire.Gate.Evaluate(FrontendAPIWrapper{r.api}, gateIns...) + } + var claimedEval frontend.Variable + if src.Level == initialChallengeI { + claimedEval = r.assignment[wI].Evaluate(r.api, point) + } else { + claimedEval = proof[src.Level].FinalEvalProof[r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex)] + } + r.api.AssertIsEqual(claimedEval, gateEval) + } } - r.outgoingEvalPoints[levelI] = outPoints } func (r *resources) verifySumcheckLevel(levelI int, proof Proof) error { @@ -141,8 +170,8 @@ func (r *resources) verifySumcheckLevel(levelI int, proof Proof) error { if src.Level == initialChallengeI { claimedEval = r.assignment[wI].Evaluate(r.api, r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex]) } else { - idx := r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex) - claimedEval = proof[src.Level].FinalEvalProof[idx] + i := r.schedule[src.Level].FinalEvalProofIndex(r.uniqueInputIndices[wI][claimI], src.OutgoingClaimIndex) + claimedEval = proof[src.Level].FinalEvalProof[i] } claimedEvals = append(claimedEvals, claimedEval) } @@ -163,7 +192,6 @@ func (r *resources) verifySumcheckLevel(levelI int, proof Proof) error { } // Verify the consistency of the claimed output with the claimed input. -// Unlike in Prove, the assignment argument need not be complete. func Verify(api frontend.API, c Circuit, schedule constraint.GkrProvingSchedule, assignment WireAssignment, proof Proof, h hash.FieldHasher) error { nbVars := assignment.NbVars() if 1<= 0; levelI-- { if _, isSkip := schedule[levelI].(constraint.GkrSkipLevel); isSkip { - r.verifySkipLevel(levelI) + r.verifySkipLevel(levelI, proof) } else { if err := r.verifySumcheckLevel(levelI, proof); err != nil { return err diff --git a/internal/gkr/gkrcore/schedule.go b/internal/gkr/gkrcore/schedule.go index 9ef3be7a99..7fecf28aab 100644 --- a/internal/gkr/gkrcore/schedule.go +++ b/internal/gkr/gkrcore/schedule.go @@ -344,3 +344,13 @@ func (c Circuit[G]) UniqueInputIndices(schedule constraint.GkrProvingSchedule) [ } return res } + +// CollectOutgoingEvalPoints sets the outgoing evaluation points of a skip level, equal to its incoming ones. +func CollectOutgoingEvalPoints[F any](level constraint.GkrSkipLevel, levelI int, outgoingEvalPoints [][][]F) [][]F { + outPoints := make([][]F, level.NbOutgoingEvalPoints()) + for k, src := range level.ClaimSources { + outPoints[k] = outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] + } + outgoingEvalPoints[levelI] = outPoints + return outPoints +} diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index baf61c2fad..31eeaa0673 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -75,6 +75,9 @@ func (e *zeroCheckLazyClaims) verifyFinalEval(r []small_rational.SmallRational, var gateEval small_rational.SmallRational if wire.IsInput() { gateEval = e.resources.assignment[wI].Evaluate(r, &e.resources.memPool) + if !gateInputEvals[levelWireI][0].Equal(&gateEval) { + return errors.New("incompatible evaluations") + } } else { evaluator := newGateEvaluator(wire.Gate.Evaluate, len(wire.Inputs)) for _, v := range gateInputEvals[levelWireI] { diff --git a/internal/gkr/sumcheck.go b/internal/gkr/sumcheck.go index 94cc29349f..a3deee14f7 100644 --- a/internal/gkr/sumcheck.go +++ b/internal/gkr/sumcheck.go @@ -54,7 +54,7 @@ func verifySumcheck(api frontend.API, claims sumcheckLazyClaims, proof sumcheckP gJ := make(polynomial.Polynomial, degree+1) gJR := claimedSum - for j := 0; j < claims.varsNum(); j++ { + for j := range claims.varsNum() { partialSumPoly := proof.PartialSumPolys[j] if len(partialSumPoly) != degree { return errors.New("malformed proof") From 7962e4140479f2618d512ea9baa7c571676a1e1c Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 16 Mar 2026 02:20:58 -0500 Subject: [PATCH 245/251] build generify --- .../backend/template/gkr/gkr.go.tmpl | 40 +++++++------------ internal/gkr/bls12-377/gkr.go | 40 +++++++------------ internal/gkr/bls12-381/gkr.go | 40 +++++++------------ internal/gkr/bw6-761/gkr.go | 40 +++++++------------ internal/gkr/small_rational/gkr.go | 40 +++++++------------ 5 files changed, 75 insertions(+), 125 deletions(-) diff --git a/internal/generator/backend/template/gkr/gkr.go.tmpl b/internal/generator/backend/template/gkr/gkr.go.tmpl index 58e866a234..9dfd6ce8cb 100644 --- a/internal/generator/backend/template/gkr/gkr.go.tmpl +++ b/internal/generator/backend/template/gkr/gkr.go.tmpl @@ -49,9 +49,10 @@ func (e *zeroCheckLazyClaims) degree(int) int { // claims on each wire and c is foldingCoeff. // Both purportedValue and the vector r have been randomized during sumcheck. // -// For input wires, w(r) is computed directly from the assignment. -// For non-input wires, the prover claims evaluations of the input wires at r, -// communicated through uniqueInputEvaluations; those claims are verified later. +// For input wires, w(r) is computed directly from the assignment and the claimed +// evaluation in uniqueInputEvaluations is checked equal to it. +// For non-input wires, the prover claims evaluations of their gate inputs at r via +// uniqueInputEvaluations; those claims are verified by lower levels' sumchecks. // The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming // that the full sum matches purportedValue. func (e *zeroCheckLazyClaims) verifyFinalEval(r []{{ .ElementType }}, purportedValue {{ .ElementType }}, uniqueInputEvaluations []{{ .ElementType }}) error { @@ -316,22 +317,11 @@ func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment }, nil } -// collectOutgoingEvalPoints sets the outgoing evaluation points of a skip level, equal to its incoming ones. -func (r *resources) collectOutgoingEvalPoints(levelI int) [][]{{ .ElementType }} { - level := r.schedule[levelI].(constraint.GkrSkipLevel) - outPoints := make([][]{{ .ElementType }}, level.NbOutgoingEvalPoints()) - for k, src := range level.ClaimSources { - outPoints[k] = r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] - } - r.outgoingEvalPoints[levelI] = outPoints - return outPoints -} - // proveSkipLevel evaluates each unique gate input at each inherited evaluation point and records // the outgoing evaluation points on the resources. func (r *resources) proveSkipLevel(levelI int) sumcheckProof { - outPoints := r.collectOutgoingEvalPoints(levelI) level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := gkrcore.CollectOutgoingEvalPoints(level, levelI, r.outgoingEvalPoints) uniqueInputs := r.circuit.UniqueGateInputs(level) evals := make([]{{ .ElementType }}, len(uniqueInputs)*len(outPoints)) @@ -346,8 +336,8 @@ func (r *resources) proveSkipLevel(levelI int) sumcheckProof { // verifySkipLevel verifies a SkipSumcheck level: checks that the finalEvalProof // is consistent with the assignment and gate evaluations, and records outgoing eval points. func (r *resources) verifySkipLevel(levelI int, proof Proof) error { - outPoints := r.collectOutgoingEvalPoints(levelI) level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := gkrcore.CollectOutgoingEvalPoints(level, levelI, r.outgoingEvalPoints) finalEval := proof[levelI].finalEvalProof _, inputIndices := r.circuit.InputMapping(level) @@ -405,7 +395,7 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { } pools := make([]*gateEvaluatorPool, nbWires) - flatW := 0 + levelWireI := 0 for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := r.circuit[wI] @@ -413,8 +403,8 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { if wire.IsInput() { gate = gkrcore.IdentityBytecode() } - pools[flatW] = newGateEvaluatorPool(gate, len(inputIndices[flatW]), &r.memPool) - flatW++ + pools[levelWireI] = newGateEvaluatorPool(gate, len(inputIndices[levelWireI]), &r.memPool) + levelWireI++ } } @@ -422,7 +412,7 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { eqs := make([]polynomial.MultiLin, nbWires) var alpha {{ .ElementType }} alpha.SetOne() - flatW = 0 + levelWireI = 0 for _, group := range level.ClaimGroups() { nbSources := len(group.ClaimSources) @@ -447,18 +437,18 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { stride.Mul(&stride, &foldingCoeff) } - eqs[flatW] = groupEq - flatW++ + eqs[levelWireI] = groupEq + levelWireI++ alpha.Mul(&alpha, &stride) for w := 1; w < len(group.Wires); w++ { - eqs[flatW] = polynomial.MultiLin(r.memPool.Make(eqLength)) + eqs[levelWireI] = polynomial.MultiLin(r.memPool.Make(eqLength)) r.workers.Submit(eqLength, func(start, end int) { for i := start; i < end; i++ { - eqs[flatW][i].Mul(&eqs[flatW-1][i], &stride) + eqs[levelWireI][i].Mul(&eqs[levelWireI-1][i], &stride) } }, 512).Wait() - flatW++ + levelWireI++ alpha.Mul(&alpha, &stride) } } diff --git a/internal/gkr/bls12-377/gkr.go b/internal/gkr/bls12-377/gkr.go index bc37e4339a..07dd53baca 100644 --- a/internal/gkr/bls12-377/gkr.go +++ b/internal/gkr/bls12-377/gkr.go @@ -56,9 +56,10 @@ func (e *zeroCheckLazyClaims) degree(int) int { // claims on each wire and c is foldingCoeff. // Both purportedValue and the vector r have been randomized during sumcheck. // -// For input wires, w(r) is computed directly from the assignment. -// For non-input wires, the prover claims evaluations of the input wires at r, -// communicated through uniqueInputEvaluations; those claims are verified later. +// For input wires, w(r) is computed directly from the assignment and the claimed +// evaluation in uniqueInputEvaluations is checked equal to it. +// For non-input wires, the prover claims evaluations of their gate inputs at r via +// uniqueInputEvaluations; those claims are verified by lower levels' sumchecks. // The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming // that the full sum matches purportedValue. func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { @@ -323,22 +324,11 @@ func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment }, nil } -// collectOutgoingEvalPoints sets the outgoing evaluation points of a skip level, equal to its incoming ones. -func (r *resources) collectOutgoingEvalPoints(levelI int) [][]fr.Element { - level := r.schedule[levelI].(constraint.GkrSkipLevel) - outPoints := make([][]fr.Element, level.NbOutgoingEvalPoints()) - for k, src := range level.ClaimSources { - outPoints[k] = r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] - } - r.outgoingEvalPoints[levelI] = outPoints - return outPoints -} - // proveSkipLevel evaluates each unique gate input at each inherited evaluation point and records // the outgoing evaluation points on the resources. func (r *resources) proveSkipLevel(levelI int) sumcheckProof { - outPoints := r.collectOutgoingEvalPoints(levelI) level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := gkrcore.CollectOutgoingEvalPoints(level, levelI, r.outgoingEvalPoints) uniqueInputs := r.circuit.UniqueGateInputs(level) evals := make([]fr.Element, len(uniqueInputs)*len(outPoints)) @@ -353,8 +343,8 @@ func (r *resources) proveSkipLevel(levelI int) sumcheckProof { // verifySkipLevel verifies a SkipSumcheck level: checks that the finalEvalProof // is consistent with the assignment and gate evaluations, and records outgoing eval points. func (r *resources) verifySkipLevel(levelI int, proof Proof) error { - outPoints := r.collectOutgoingEvalPoints(levelI) level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := gkrcore.CollectOutgoingEvalPoints(level, levelI, r.outgoingEvalPoints) finalEval := proof[levelI].finalEvalProof _, inputIndices := r.circuit.InputMapping(level) @@ -412,7 +402,7 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { } pools := make([]*gateEvaluatorPool, nbWires) - flatW := 0 + levelWireI := 0 for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := r.circuit[wI] @@ -420,8 +410,8 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { if wire.IsInput() { gate = gkrcore.IdentityBytecode() } - pools[flatW] = newGateEvaluatorPool(gate, len(inputIndices[flatW]), &r.memPool) - flatW++ + pools[levelWireI] = newGateEvaluatorPool(gate, len(inputIndices[levelWireI]), &r.memPool) + levelWireI++ } } @@ -429,7 +419,7 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { eqs := make([]polynomial.MultiLin, nbWires) var alpha fr.Element alpha.SetOne() - flatW = 0 + levelWireI = 0 for _, group := range level.ClaimGroups() { nbSources := len(group.ClaimSources) @@ -454,18 +444,18 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { stride.Mul(&stride, &foldingCoeff) } - eqs[flatW] = groupEq - flatW++ + eqs[levelWireI] = groupEq + levelWireI++ alpha.Mul(&alpha, &stride) for w := 1; w < len(group.Wires); w++ { - eqs[flatW] = polynomial.MultiLin(r.memPool.Make(eqLength)) + eqs[levelWireI] = polynomial.MultiLin(r.memPool.Make(eqLength)) r.workers.Submit(eqLength, func(start, end int) { for i := start; i < end; i++ { - eqs[flatW][i].Mul(&eqs[flatW-1][i], &stride) + eqs[levelWireI][i].Mul(&eqs[levelWireI-1][i], &stride) } }, 512).Wait() - flatW++ + levelWireI++ alpha.Mul(&alpha, &stride) } } diff --git a/internal/gkr/bls12-381/gkr.go b/internal/gkr/bls12-381/gkr.go index eb9339f377..bb240ddab0 100644 --- a/internal/gkr/bls12-381/gkr.go +++ b/internal/gkr/bls12-381/gkr.go @@ -56,9 +56,10 @@ func (e *zeroCheckLazyClaims) degree(int) int { // claims on each wire and c is foldingCoeff. // Both purportedValue and the vector r have been randomized during sumcheck. // -// For input wires, w(r) is computed directly from the assignment. -// For non-input wires, the prover claims evaluations of the input wires at r, -// communicated through uniqueInputEvaluations; those claims are verified later. +// For input wires, w(r) is computed directly from the assignment and the claimed +// evaluation in uniqueInputEvaluations is checked equal to it. +// For non-input wires, the prover claims evaluations of their gate inputs at r via +// uniqueInputEvaluations; those claims are verified by lower levels' sumchecks. // The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming // that the full sum matches purportedValue. func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { @@ -323,22 +324,11 @@ func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment }, nil } -// collectOutgoingEvalPoints sets the outgoing evaluation points of a skip level, equal to its incoming ones. -func (r *resources) collectOutgoingEvalPoints(levelI int) [][]fr.Element { - level := r.schedule[levelI].(constraint.GkrSkipLevel) - outPoints := make([][]fr.Element, level.NbOutgoingEvalPoints()) - for k, src := range level.ClaimSources { - outPoints[k] = r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] - } - r.outgoingEvalPoints[levelI] = outPoints - return outPoints -} - // proveSkipLevel evaluates each unique gate input at each inherited evaluation point and records // the outgoing evaluation points on the resources. func (r *resources) proveSkipLevel(levelI int) sumcheckProof { - outPoints := r.collectOutgoingEvalPoints(levelI) level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := gkrcore.CollectOutgoingEvalPoints(level, levelI, r.outgoingEvalPoints) uniqueInputs := r.circuit.UniqueGateInputs(level) evals := make([]fr.Element, len(uniqueInputs)*len(outPoints)) @@ -353,8 +343,8 @@ func (r *resources) proveSkipLevel(levelI int) sumcheckProof { // verifySkipLevel verifies a SkipSumcheck level: checks that the finalEvalProof // is consistent with the assignment and gate evaluations, and records outgoing eval points. func (r *resources) verifySkipLevel(levelI int, proof Proof) error { - outPoints := r.collectOutgoingEvalPoints(levelI) level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := gkrcore.CollectOutgoingEvalPoints(level, levelI, r.outgoingEvalPoints) finalEval := proof[levelI].finalEvalProof _, inputIndices := r.circuit.InputMapping(level) @@ -412,7 +402,7 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { } pools := make([]*gateEvaluatorPool, nbWires) - flatW := 0 + levelWireI := 0 for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := r.circuit[wI] @@ -420,8 +410,8 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { if wire.IsInput() { gate = gkrcore.IdentityBytecode() } - pools[flatW] = newGateEvaluatorPool(gate, len(inputIndices[flatW]), &r.memPool) - flatW++ + pools[levelWireI] = newGateEvaluatorPool(gate, len(inputIndices[levelWireI]), &r.memPool) + levelWireI++ } } @@ -429,7 +419,7 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { eqs := make([]polynomial.MultiLin, nbWires) var alpha fr.Element alpha.SetOne() - flatW = 0 + levelWireI = 0 for _, group := range level.ClaimGroups() { nbSources := len(group.ClaimSources) @@ -454,18 +444,18 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { stride.Mul(&stride, &foldingCoeff) } - eqs[flatW] = groupEq - flatW++ + eqs[levelWireI] = groupEq + levelWireI++ alpha.Mul(&alpha, &stride) for w := 1; w < len(group.Wires); w++ { - eqs[flatW] = polynomial.MultiLin(r.memPool.Make(eqLength)) + eqs[levelWireI] = polynomial.MultiLin(r.memPool.Make(eqLength)) r.workers.Submit(eqLength, func(start, end int) { for i := start; i < end; i++ { - eqs[flatW][i].Mul(&eqs[flatW-1][i], &stride) + eqs[levelWireI][i].Mul(&eqs[levelWireI-1][i], &stride) } }, 512).Wait() - flatW++ + levelWireI++ alpha.Mul(&alpha, &stride) } } diff --git a/internal/gkr/bw6-761/gkr.go b/internal/gkr/bw6-761/gkr.go index 5f811d55bc..8d07fe0310 100644 --- a/internal/gkr/bw6-761/gkr.go +++ b/internal/gkr/bw6-761/gkr.go @@ -56,9 +56,10 @@ func (e *zeroCheckLazyClaims) degree(int) int { // claims on each wire and c is foldingCoeff. // Both purportedValue and the vector r have been randomized during sumcheck. // -// For input wires, w(r) is computed directly from the assignment. -// For non-input wires, the prover claims evaluations of the input wires at r, -// communicated through uniqueInputEvaluations; those claims are verified later. +// For input wires, w(r) is computed directly from the assignment and the claimed +// evaluation in uniqueInputEvaluations is checked equal to it. +// For non-input wires, the prover claims evaluations of their gate inputs at r via +// uniqueInputEvaluations; those claims are verified by lower levels' sumchecks. // The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming // that the full sum matches purportedValue. func (e *zeroCheckLazyClaims) verifyFinalEval(r []fr.Element, purportedValue fr.Element, uniqueInputEvaluations []fr.Element) error { @@ -323,22 +324,11 @@ func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment }, nil } -// collectOutgoingEvalPoints sets the outgoing evaluation points of a skip level, equal to its incoming ones. -func (r *resources) collectOutgoingEvalPoints(levelI int) [][]fr.Element { - level := r.schedule[levelI].(constraint.GkrSkipLevel) - outPoints := make([][]fr.Element, level.NbOutgoingEvalPoints()) - for k, src := range level.ClaimSources { - outPoints[k] = r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] - } - r.outgoingEvalPoints[levelI] = outPoints - return outPoints -} - // proveSkipLevel evaluates each unique gate input at each inherited evaluation point and records // the outgoing evaluation points on the resources. func (r *resources) proveSkipLevel(levelI int) sumcheckProof { - outPoints := r.collectOutgoingEvalPoints(levelI) level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := gkrcore.CollectOutgoingEvalPoints(level, levelI, r.outgoingEvalPoints) uniqueInputs := r.circuit.UniqueGateInputs(level) evals := make([]fr.Element, len(uniqueInputs)*len(outPoints)) @@ -353,8 +343,8 @@ func (r *resources) proveSkipLevel(levelI int) sumcheckProof { // verifySkipLevel verifies a SkipSumcheck level: checks that the finalEvalProof // is consistent with the assignment and gate evaluations, and records outgoing eval points. func (r *resources) verifySkipLevel(levelI int, proof Proof) error { - outPoints := r.collectOutgoingEvalPoints(levelI) level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := gkrcore.CollectOutgoingEvalPoints(level, levelI, r.outgoingEvalPoints) finalEval := proof[levelI].finalEvalProof _, inputIndices := r.circuit.InputMapping(level) @@ -412,7 +402,7 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { } pools := make([]*gateEvaluatorPool, nbWires) - flatW := 0 + levelWireI := 0 for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := r.circuit[wI] @@ -420,8 +410,8 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { if wire.IsInput() { gate = gkrcore.IdentityBytecode() } - pools[flatW] = newGateEvaluatorPool(gate, len(inputIndices[flatW]), &r.memPool) - flatW++ + pools[levelWireI] = newGateEvaluatorPool(gate, len(inputIndices[levelWireI]), &r.memPool) + levelWireI++ } } @@ -429,7 +419,7 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { eqs := make([]polynomial.MultiLin, nbWires) var alpha fr.Element alpha.SetOne() - flatW = 0 + levelWireI = 0 for _, group := range level.ClaimGroups() { nbSources := len(group.ClaimSources) @@ -454,18 +444,18 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { stride.Mul(&stride, &foldingCoeff) } - eqs[flatW] = groupEq - flatW++ + eqs[levelWireI] = groupEq + levelWireI++ alpha.Mul(&alpha, &stride) for w := 1; w < len(group.Wires); w++ { - eqs[flatW] = polynomial.MultiLin(r.memPool.Make(eqLength)) + eqs[levelWireI] = polynomial.MultiLin(r.memPool.Make(eqLength)) r.workers.Submit(eqLength, func(start, end int) { for i := start; i < end; i++ { - eqs[flatW][i].Mul(&eqs[flatW-1][i], &stride) + eqs[levelWireI][i].Mul(&eqs[levelWireI-1][i], &stride) } }, 512).Wait() - flatW++ + levelWireI++ alpha.Mul(&alpha, &stride) } } diff --git a/internal/gkr/small_rational/gkr.go b/internal/gkr/small_rational/gkr.go index 31eeaa0673..1e4882d272 100644 --- a/internal/gkr/small_rational/gkr.go +++ b/internal/gkr/small_rational/gkr.go @@ -56,9 +56,10 @@ func (e *zeroCheckLazyClaims) degree(int) int { // claims on each wire and c is foldingCoeff. // Both purportedValue and the vector r have been randomized during sumcheck. // -// For input wires, w(r) is computed directly from the assignment. -// For non-input wires, the prover claims evaluations of the input wires at r, -// communicated through uniqueInputEvaluations; those claims are verified later. +// For input wires, w(r) is computed directly from the assignment and the claimed +// evaluation in uniqueInputEvaluations is checked equal to it. +// For non-input wires, the prover claims evaluations of their gate inputs at r via +// uniqueInputEvaluations; those claims are verified by lower levels' sumchecks. // The verifier checks consistency by evaluating gateᵥ(inputEvals...) and confirming // that the full sum matches purportedValue. func (e *zeroCheckLazyClaims) verifyFinalEval(r []small_rational.SmallRational, purportedValue small_rational.SmallRational, uniqueInputEvaluations []small_rational.SmallRational) error { @@ -323,22 +324,11 @@ func newResources(c Circuit, schedule constraint.GkrProvingSchedule, assignment }, nil } -// collectOutgoingEvalPoints sets the outgoing evaluation points of a skip level, equal to its incoming ones. -func (r *resources) collectOutgoingEvalPoints(levelI int) [][]small_rational.SmallRational { - level := r.schedule[levelI].(constraint.GkrSkipLevel) - outPoints := make([][]small_rational.SmallRational, level.NbOutgoingEvalPoints()) - for k, src := range level.ClaimSources { - outPoints[k] = r.outgoingEvalPoints[src.Level][src.OutgoingClaimIndex] - } - r.outgoingEvalPoints[levelI] = outPoints - return outPoints -} - // proveSkipLevel evaluates each unique gate input at each inherited evaluation point and records // the outgoing evaluation points on the resources. func (r *resources) proveSkipLevel(levelI int) sumcheckProof { - outPoints := r.collectOutgoingEvalPoints(levelI) level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := gkrcore.CollectOutgoingEvalPoints(level, levelI, r.outgoingEvalPoints) uniqueInputs := r.circuit.UniqueGateInputs(level) evals := make([]small_rational.SmallRational, len(uniqueInputs)*len(outPoints)) @@ -353,8 +343,8 @@ func (r *resources) proveSkipLevel(levelI int) sumcheckProof { // verifySkipLevel verifies a SkipSumcheck level: checks that the finalEvalProof // is consistent with the assignment and gate evaluations, and records outgoing eval points. func (r *resources) verifySkipLevel(levelI int, proof Proof) error { - outPoints := r.collectOutgoingEvalPoints(levelI) level := r.schedule[levelI].(constraint.GkrSkipLevel) + outPoints := gkrcore.CollectOutgoingEvalPoints(level, levelI, r.outgoingEvalPoints) finalEval := proof[levelI].finalEvalProof _, inputIndices := r.circuit.InputMapping(level) @@ -412,7 +402,7 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { } pools := make([]*gateEvaluatorPool, nbWires) - flatW := 0 + levelWireI := 0 for _, group := range level.ClaimGroups() { for _, wI := range group.Wires { wire := r.circuit[wI] @@ -420,8 +410,8 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { if wire.IsInput() { gate = gkrcore.IdentityBytecode() } - pools[flatW] = newGateEvaluatorPool(gate, len(inputIndices[flatW]), &r.memPool) - flatW++ + pools[levelWireI] = newGateEvaluatorPool(gate, len(inputIndices[levelWireI]), &r.memPool) + levelWireI++ } } @@ -429,7 +419,7 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { eqs := make([]polynomial.MultiLin, nbWires) var alpha small_rational.SmallRational alpha.SetOne() - flatW = 0 + levelWireI = 0 for _, group := range level.ClaimGroups() { nbSources := len(group.ClaimSources) @@ -454,18 +444,18 @@ func (r *resources) proveSumcheckLevel(levelI int) sumcheckProof { stride.Mul(&stride, &foldingCoeff) } - eqs[flatW] = groupEq - flatW++ + eqs[levelWireI] = groupEq + levelWireI++ alpha.Mul(&alpha, &stride) for w := 1; w < len(group.Wires); w++ { - eqs[flatW] = polynomial.MultiLin(r.memPool.Make(eqLength)) + eqs[levelWireI] = polynomial.MultiLin(r.memPool.Make(eqLength)) r.workers.Submit(eqLength, func(start, end int) { for i := start; i < end; i++ { - eqs[flatW][i].Mul(&eqs[flatW-1][i], &stride) + eqs[levelWireI][i].Mul(&eqs[levelWireI-1][i], &stride) } }, 512).Wait() - flatW++ + levelWireI++ alpha.Mul(&alpha, &stride) } } From f29b7f9f0ed8c4561294e680eaf7f58710ac22b6 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 16 Mar 2026 02:29:00 -0500 Subject: [PATCH 246/251] fix: index bug --- internal/gkr/gkr.go | 4 ++-- internal/gkr/test_vectors/generate.go | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index 2691b13c29..9009ca71e5 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -136,8 +136,8 @@ func (r *resources) verifySkipLevel(levelI int, proof Proof) { claimed := finalEval[level.FinalEvalProofIndex(inputIndices[levelWireI][0], claimI)] r.api.AssertIsEqual(claimed, gateEval) } else { - for _, inI := range inputIndices[levelWireI] { - gateIns[inI] = finalEval[level.FinalEvalProofIndex(inI, claimI)] + for i, inI := range inputIndices[levelWireI] { + gateIns[i] = finalEval[level.FinalEvalProofIndex(inI, claimI)] } gateEval = wire.Gate.Evaluate(FrontendAPIWrapper{r.api}, gateIns...) } diff --git a/internal/gkr/test_vectors/generate.go b/internal/gkr/test_vectors/generate.go index 5aeabe5d53..b2f1d57ee9 100644 --- a/internal/gkr/test_vectors/generate.go +++ b/internal/gkr/test_vectors/generate.go @@ -16,13 +16,15 @@ import ( ) func main() { - var wg sync.WaitGroup - wg.Add(3) - for _, f := range []func() error{ + tasks := []func() error{ gkr.GenerateSumcheckVectors, gkr.GenerateVectors, generateSerializationTestData, - } { + } + + var wg sync.WaitGroup + wg.Add(len(tasks)) + for _, f := range tasks { go func() { assertNoError(f()) wg.Done() From 1bb1f996fa004d453a343f35cb63b461746f63d4 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 16 Mar 2026 02:29:36 -0500 Subject: [PATCH 247/251] serialization changes --- .../testdata/gkr_circuit_bn254.scs | Bin 33711 -> 33473 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/internal/gkr/test_vectors/testdata/gkr_circuit_bn254.scs b/internal/gkr/test_vectors/testdata/gkr_circuit_bn254.scs index ad8109f98438ac75dcefb44406edcc1d156f299c..20ee02101452e38f1bb9c70c7a99c66cf7caffc6 100644 GIT binary patch delta 499 zcmZWkJxc>Y5Z&3^n~)}v5FuCyBE&-RCy0oRpkj>YN2QInB6=VM%&ACPVT&|2{sNH$ zdrNfg4XVQI6 zm`Eke^1GiPrSMbnGbWX>gYP@>@pUDAUd5j$T}^V?*^|CFwT*Dn9)!jCI9x1!12H8} Ap#T5? delta 808 zcmX@u%Cx?nslLCN0R`}(@E90af*}G5Y!G^?1cdfuhR~%@ngzmT0MQ^JMkXdO%?cF< z(I7cSW+ujlh6Y9u8;CbH9}VKIg{lE*u%0|O_<4ODP{a|4|Ns9F6ypa{{~75>*VEfV zH2cK)|2H%+FoHtB1u1M4CtnSz2ZxOkP?V98^$#QcffU%v1|SJ?ge#KslqPqEHX}Rl zFa2G|$cN%Q+s&q7@rvtGnxiOt-F)0h~mCNC=9 V##B)}Ik7}xazP3E=Ef2SJ^ Date: Mon, 16 Mar 2026 02:33:52 -0500 Subject: [PATCH 248/251] fix: Outputs --- internal/gkr/gkrcore/types.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/gkr/gkrcore/types.go b/internal/gkr/gkrcore/types.go index c552a44b84..83bf399059 100644 --- a/internal/gkr/gkrcore/types.go +++ b/internal/gkr/gkrcore/types.go @@ -127,15 +127,15 @@ func (c Circuit[GateExecutable]) Inputs() []int { // Outputs returns the list of output wire indices. func (c Circuit[GateExecutable]) Outputs() []int { - isOutput := make([]bool, len(c)) + isOutputTo := make([]bool, len(c)) for i := range c { for _, in := range c[i].Inputs { - isOutput[in] = true + isOutputTo[in] = true } } res := make([]int, 0, len(c)) for i := range c { - if !isOutput[i] { + if !isOutputTo[i] || c[i].Exported { res = append(res, i) } } From 875d0d990ecdb6febab646d50be9c3f2f954b48c Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 16 Mar 2026 02:45:27 -0500 Subject: [PATCH 249/251] import hashes in gkr itself --- std/gkrapi/compile.go | 1 + std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go | 1 - std/permutation/gkr-mimc/gkr-mimc.go | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 9acd761e96..75bb9125ca 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -16,6 +16,7 @@ import ( "github.com/consensys/gnark/internal/utils" "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/consensys/gnark/std/hash" + _ "github.com/consensys/gnark/std/hash/all" "github.com/consensys/gnark/std/multicommit" ) diff --git a/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go b/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go index bbbef1f87c..f21222d92b 100644 --- a/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go +++ b/std/hash/poseidon2/gkr-poseidon2/gkr-poseidon2.go @@ -5,7 +5,6 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/hash" - _ "github.com/consensys/gnark/std/hash/all" gkr_poseidon2 "github.com/consensys/gnark/std/permutation/poseidon2/gkr-poseidon2" ) diff --git a/std/permutation/gkr-mimc/gkr-mimc.go b/std/permutation/gkr-mimc/gkr-mimc.go index 14270b8135..679d618ad7 100644 --- a/std/permutation/gkr-mimc/gkr-mimc.go +++ b/std/permutation/gkr-mimc/gkr-mimc.go @@ -15,7 +15,6 @@ import ( "github.com/consensys/gnark/std/gkrapi" "github.com/consensys/gnark/std/gkrapi/gkr" "github.com/consensys/gnark/std/hash" - _ "github.com/consensys/gnark/std/hash/all" ) // compressor implements a compression function by applying From b730f0d16d3953af882e9ed9a72250875ccdd545 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 16 Mar 2026 16:31:34 -0500 Subject: [PATCH 250/251] build: regenerate serialization circuit --- .../testdata/gkr_circuit_bn254.scs | Bin 33473 -> 33479 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/internal/gkr/test_vectors/testdata/gkr_circuit_bn254.scs b/internal/gkr/test_vectors/testdata/gkr_circuit_bn254.scs index 20ee02101452e38f1bb9c70c7a99c66cf7caffc6..f1cb52784c3f41907a53affe133d45adee84cce5 100644 GIT binary patch delta 79 zcmX@u%5=PyiE;TvMsvoHjh5-bd~6I13{6cbTNlZ%VYf#m8U7e=<}42n^k*99+-W$c*jUnsY^xR8~R w(R6ZUkvWjuSmYwhdK;*g5r~Br76j+#l%+c5l%^IGW#*N5y0C5*D&EQu0Gv=B1poj5 From c82730dc2c1af150fb1d334cc83ac614f7bc4d9d Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Mon, 16 Mar 2026 16:32:19 -0500 Subject: [PATCH 251/251] feat: mandatorily add circuit and schedule hash to initial challenges --- internal/gkr/gkrcore/serialize.go | 197 ++++++++++++++++++++++++++++++ std/gkrapi/compile.go | 56 +++++---- 2 files changed, 232 insertions(+), 21 deletions(-) create mode 100644 internal/gkr/gkrcore/serialize.go diff --git a/internal/gkr/gkrcore/serialize.go b/internal/gkr/gkrcore/serialize.go new file mode 100644 index 0000000000..d62bbeaae0 --- /dev/null +++ b/internal/gkr/gkrcore/serialize.go @@ -0,0 +1,197 @@ +package gkrcore + +import ( + "encoding/binary" + "fmt" + "io" + "math/big" + + "github.com/consensys/gnark/constraint" +) + +// Common serialization utilities + +func writeUint8(w io.Writer, x int) error { + if x >= 256 || x < 0 { + return fmt.Errorf("%d out of range", x) + } + _, err := w.Write([]byte{byte(x)}) + return err +} + +func writeUint16[T int | uint16](w io.Writer, x T) error { + var buf [2]byte + if int(x) >= 65536 || x < 0 { + return fmt.Errorf("%d out of range", x) + } + binary.LittleEndian.PutUint16(buf[:], uint16(x)) + _, err := w.Write(buf[:]) + return err +} + +func writeBool(w io.Writer, b bool) error { + var v byte + if b { + v = 1 + } + _, err := w.Write([]byte{v}) + return err +} + +func writeUint16Slice[T int | uint16](w io.Writer, s []T) error { + if err := writeUint16(w, len(s)); err != nil { + return err + } + for _, v := range s { + if err := writeUint16(w, v); err != nil { + return err + } + } + return nil +} + +func writeBigInt(w io.Writer, x *big.Int) error { + bytes := x.Bytes() + if err := writeUint8(w, len(bytes)); err != nil { + return err + } + _, err := w.Write(bytes) + return err +} + +// SerializeCircuit writes a SerializableCircuit to w in deterministic binary format, +// primarily for hashing circuits to create unique identifiers. +// +// The encoding is compact (uint16 for counts/indices, uint8 for bigint byte lengths) and +// uses little-endian throughout. Gate metadata (NbIn, Degree, SolvableVar) is omitted +// since it can be recomputed from bytecode on deserialization. +// +// Format: +// +// Circuit: [wire_count:u16] [wire...] +// Wire: [input_count:u16] [input_indices:u16...] [exported:bool] [gate] +// Gate (non-input only): [const_count:u16] [constants...] [inst_count:u16] [instructions...] +// Constant: [byte_len:u8] [bytes...] +// Instruction: [op:u8] [input_count:u16] [input_indices:u16...] +func SerializeCircuit(w io.Writer, c SerializableCircuit) error { + if len(c) >= 1<<32 { + return fmt.Errorf("circuit length too large: %d", len(c)) + } + + // Write the number of wires + if err := writeUint16(w, len(c)); err != nil { + return err + } + + // Write each wire + for i := range c { + wire := &c[i] + + if err := writeUint16Slice(w, wire.Inputs); err != nil { + return err + } + + // Write the exported flag + if err := writeBool(w, wire.Exported); err != nil { + return err + } + + // If not an input wire, write gate information + if !wire.IsInput() { + gate := &wire.Gate + + // Write the bytecode + bytecode := &gate.Evaluate + + // Write the constants + if err := writeUint16(w, len(bytecode.Constants)); err != nil { + return err + } + for _, constant := range bytecode.Constants { + if err := writeBigInt(w, constant); err != nil { + return err + } + } + + // Write the instructions + if err := writeUint16(w, len(bytecode.Instructions)); err != nil { + return err + } + for _, inst := range bytecode.Instructions { + // Write the operation + if _, err := w.Write([]byte{byte(inst.Op)}); err != nil { + return err + } + + // Write the instruction inputs + if err := writeUint16Slice(w, inst.Inputs); err != nil { + return err + } + } + } + } + + return nil +} + +// SerializeSchedule writes a GkrProvingSchedule to w in deterministic binary format, +// primarily for hashing schedules to create unique identifiers. +// +// The encoding uses uint16 for counts/indices and little-endian throughout. +// +// Format: +// +// Schedule: [level_count:u16] [level...] +// Level: [skip_sumcheck:bool] [group_count:u16] [claim_group...] +// GkrClaimGroup: [wire_count:u16] [wire_indices:u16...] [source_count:u16] [claim_source...] +// GkrClaimSource: [level:u16] [outgoing_claim_index:u16] +func SerializeSchedule(w io.Writer, s constraint.GkrProvingSchedule) error { + if len(s) >= 65536 { + return fmt.Errorf("schedule length too large: %d", len(s)) + } + + writeClaimGroup := func(cg constraint.GkrClaimGroup) error { + if err := writeUint16Slice(w, cg.Wires); err != nil { + return err + } + + // Write claim source count; each source is two uint16s: Level and OutgoingClaimIndex + if err := writeUint16(w, len(cg.ClaimSources)); err != nil { + return err + } + for _, src := range cg.ClaimSources { + if err := writeUint16(w, src.Level); err != nil { + return err + } + if err := writeUint16(w, src.OutgoingClaimIndex); err != nil { + return err + } + } + + return nil + } + + // Write number of levels + if err := writeUint16(w, len(s)); err != nil { + return err + } + + // Write each level + for _, level := range s { + _, isSkip := level.(constraint.GkrSkipLevel) + if err := writeBool(w, isSkip); err != nil { + return err + } + groups := level.ClaimGroups() + if err := writeUint16(w, len(groups)); err != nil { + return err + } + for _, cg := range groups { + if err := writeClaimGroup(cg); err != nil { + return err + } + } + } + + return nil +} diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 75bb9125ca..9a43e35a61 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -1,6 +1,7 @@ package gkrapi import ( + "crypto/sha256" "errors" "fmt" @@ -26,14 +27,17 @@ type InitialChallengeGetter func() []frontend.Variable // Circuit represents a GKR circuit. type Circuit struct { - circuit gkrcore.GadgetCircuit - schedule constraint.GkrProvingSchedule - gates []gkrcore.GateBytecode - assignments gadget.WireAssignment - getInitialChallenges InitialChallengeGetter // optional getter for the initial Fiat-Shamir challenge - ins []gkr.Variable - outs []gkr.Variable - api frontend.API // the parent API + circuit gkrcore.GadgetCircuit + schedule constraint.GkrProvingSchedule + gates []gkrcore.GateBytecode + assignments gadget.WireAssignment + ins []gkr.Variable + outs []gkr.Variable + api frontend.API // the parent API + + // Fiat-Shamir bootstrapping + getInitialChallenges InitialChallengeGetter // optional getter for the I/O related portion of the initial Fiat-Shamir challenge + statementHash []byte // hash of the circuit and schedule // Blueprint-based fields blueprints gkrcore.Blueprints @@ -60,8 +64,8 @@ func (api *API) NewInput() gkr.Variable { type CompileOption func(*Circuit) -// WithInitialChallenge provides a getter for the initial Fiat-Shamir challenge. -// If not provided, the initial challenge will be a commitment to all the input and output values of the circuit. +// WithInitialChallenge provides a getter for the I/O portion of the initial Fiat-Shamir challenge. +// If not provided, the I/O initial challenge will be a commitment to all the input and output values of the circuit. func WithInitialChallenge(getInitialChallenge InitialChallengeGetter) CompileOption { return func(c *Circuit) { c.getInitialChallenges = getInitialChallenge @@ -87,12 +91,21 @@ func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*C return nil, fmt.Errorf("failed to compute proving schedule: %w", err) } + hsh := sha256.New() + if err = gkrcore.SerializeCircuit(hsh, serializableCircuit); err != nil { + return nil, fmt.Errorf("failed to serialize circuit: %w", err) + } + if err = gkrcore.SerializeSchedule(hsh, schedule); err != nil { + return nil, fmt.Errorf("failed to serialize schedule: %w", err) + } + res := Circuit{ - circuit: gadgetCircuit, - schedule: schedule, - assignments: make(gadget.WireAssignment, len(api.circuit)), - api: api.parentApi, - hashName: fiatshamirHashName, + circuit: gadgetCircuit, + schedule: schedule, + assignments: make(gadget.WireAssignment, len(api.circuit)), + api: api.parentApi, + hashName: fiatshamirHashName, + statementHash: hsh.Sum(nil), } switch curveID { @@ -235,21 +248,22 @@ func (c *Circuit) finalize(api frontend.API) error { } if c.getInitialChallenges != nil { - return c.verify(api, c.circuit, c.getInitialChallenges()) + return c.verify(api, c.circuit, append([]frontend.Variable{c.statementHash}, c.getInitialChallenges()...)) } - // default initial challenge is a commitment to all input and output values - insOuts := make([]frontend.Variable, 0, (len(c.ins)+len(c.outs))*len(c.assignments[c.ins[0]])) + // The default initial challenge is a commitment to the circuit, solving schedule, and all input and output values. + challenges := make([]frontend.Variable, 1, (len(c.ins)+len(c.outs))*len(c.assignments[c.ins[0]])+1) + challenges[0] = c.statementHash for _, in := range c.ins { - insOuts = append(insOuts, c.assignments[in]...) + challenges = append(challenges, c.assignments[in]...) } for _, out := range c.outs { - insOuts = append(insOuts, c.assignments[out]...) + challenges = append(challenges, c.assignments[out]...) } multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { return c.verify(api, c.circuit, []frontend.Variable{commitment}) - }, insOuts...) + }, challenges...) return nil }

    YiRJ;eBsDMN7F6K|>k9ON z+d>eJ;CJoRuxyLTNU4X{c$w6S(YNAqPN7c1J^pv{QFjU81V6x*WrK%!rsUn?ugNzr z2%|Tv;@&Ux^&T|ZM12fpYtofKg}6|V7w0RWcEiYLpSs0fX8zeFi!NR^qxQqf8H%^1 z!q<5?NqXA&Fj&=WeriBd$jSKLflJ1Ef%Y4Un7?AU38W(i2qxqK6r>g$Z`<%JE?)Eg&k?YaxlH32Pkj%|1~2_MYI^hk zul)LS5fkp0jl(HxYg=r>C43!u7Z(3{OmR)L<(h`2r>2_-wYF~71#J|JE&dulbIl>uBLQ-9_WBH`WKIUp=n7nE z_j8O%7^c2E^!E=SBdQ5%M&NeWU9k^Ln7LNM4lDm}a4`QjIFlzNOBg2b5KGqs7Q8J$ zOeB7Iq|d32FptovStrF~3OT`psakbgz0{Q&uf^yN2KmRUkPwU@8acjkW?wZ2o2@Z|RHL7IvF_R33^@!T z2#DHtm0IZda`+GC1|l_h4oAN*SbRGE{r!~8{+DQxD^5DE7jbCyxH&=7s?ks4Ux^vz z5Ylg<%B^wuw+26{)zs+Asdp3rLcpCfYkL_b`RdLw{uDSn8p*$%C$H}beJ-xAFmKp{ zuWvpS$C!OyItVj=_U>(*w691@+|!FKdMPK)@?{{goDgr12xu42z8`=)0zhYil?Z;t zZj^b<5;f*4%OW<7Yfi__NPf=8q1puPADJv(!}gANWq`JH{2-vV7)lV}%&)4;IMNlB zJz?-&^6YNlgYIPE8Ubp0Xa`(LySNgg2CJ4(KGUUGB)?>o!hNeJrWDzqWmpZFF*wF9 zkk>%=49Tg3_N&2 zE3>gJ0c?IwNxMQ_qCIV$6GM}vozycRNP^D@PNEg#5B-uV$cq$glu9K%2L^r@&O%;a)2#(Aq@s)==dD_k;& zK!bguC6r7K` zqln1zMeEJ_(|xZIC;4Dy)lYtEAQA%UWYG^S(bgds%Xi_l-OUOCxW>;#GaPJIZH1vk zh-_}V@EUReWAueagY}QvcP2Dwo+)4p_t#n^R{#sO^4W-!8-D1Ma7@KDm0`2-y%It~ zo-*iLpSsteJ9@t$0!wq`fJuDKk)@x_D$^rEag4sJ#(_-a7 zZZm1p`>rSM{fyR~mNG86k9O4iO-U_}RFUdl3f(N1ht#Dh0B3b#4IgxnDT&JzQkg>X zH|K50aHQ~+F4g%@L&MLn(Y7pW6k$#qh+m>!gDu?XeR`1@W!M+b0nSs#y0=(0ipkqq~R+>{YPrv#z=>GCEcbEcb_xzAXO%)8tGI{^FdfT6; zZ3hMXVCe#F$mu(enAw7$iOAO@8;5+)*Ez8x>7CxYtH;QU5S+kMU%Qu1}=r zh;vJKZ4EdTBp4CzO?BL?oIxW$vU>vt##2(4Lpbj91kqILa(?7i+zG7I%{rOYvvJR& zmCqr0+KCJ|*kv@J-85eGd0mio2&1KO6hENqaQBWAE10#b$cseXioT=|N8cGUQ(7E#ZBWy4|#*NYzK92>|y3OFqD7wRNl<3N>j&Yn=?~I~C)xifU zF2~4uPmM9gt5yX@ES1OCIk3jRok(V6P0T7MaygcRs4kVrOIdh=H-Y zRU2TN_I@n}%ne-633w@~U1gA3N>bc8sczVWJ!tkcQS_{ z8Aqz|%)W9DKOBwzNNuAwzH!K7a|^r@Qv}7bRk>s2#scDPi=<6A<<7l|;O z2P>Zjs%HEF*;M{*>9KUwar@f*wo#~lu1#}j2|H66SDkcQDrE70; z%#z^d0|#Om)86xpk^UP~s}=WJ8r zwKW1q$V+hZbu_WF1R~&5d%GYJ6f{4481xvs-@<~VMEo1@%E{*F1)K*+g;9YpquJAa zVIWxGUj93s<`4i04~b&ax(vRE+B$3bVi?T87;Q#f%#CZDt21@}^(5qY2P{+DuouP$ zwz(8JnSh6suizNpgWt{#sBOQJw7Lzrk}gTtM7 z@Fe+OtSh2MGdl7+V*D4{%VG}AMRv9A@@bQhedTdoA-f+?6G7*N&Ik@=rA16N2ho!| zbTaS2cQ}e^kEOr#1To_oNVzFIqE|cLs?oLJ$ql z5+rn}%t-%?SvbjF?`E&Ri=dSHI<%n=m0}4sV}G%h9wbHoLvOnTFfFC*3wgZ}!SRQT(n=s%j%_er`Z^9t>n+L~|go7zG|YmK93sasG}RzH;c7pc5IafLHe`@uu{d z@W!wz@+nWnA1$DIk>;za&?k}lcM*KgFDe}$ccWvUkTC;I_xDT~^u9j7Y(p;+sVdEn zXI~z~qG3G2*GKSRUTvN-chtZrul`qbI3;Uqw(tqmgy@hr?YsRC5qOAqn?TRwfwm?@ zpST&v0lSQ}M8+t}0gu;#UBB@HA2*0qut~qr6{l%{LqgKFU3w1{3f#s=h9QaeJa(!j z!6jIqIqH1K^Wt8Le3>G_57~h|$swnS=Aw1KurR7F~1$z!6=gFmyAVaQUzKc91GWqE=ae zlkG-QPKwplAF6Nk?-NilQ+Pod5c2yi_lO^CSx^VcCHb8FZ_gZ1CKY$|3EZ48YL!Ic zCmfLfKB6!R3l=D@9Kv_a)u;D-f;|g4jr4$dtr1}jSo|F31XiT}NW~+g1seeCJe}DcQ^phnb_7sp& z90f+9^IGfZwK+7mZI@C0aB9}>R}a1xmvjFXD5v0I3ojnSPiQTq)(I>E4y7!^w8fm| zs1KqEQH&eE3t<^!Nst#ZaFbZv{ib(CrE7ohQ3c;(%*Cz4MAtp%MT&jo9-CgF9$*C= z#6gNuLW%*zb2@E|G6zs=EYj(tOs~m%Bg|i3WDzbDbD)A= z`GhE%^OJTF{^ya0Yvf5sC*s_o8C}zrxpbB`6%vp4HW5APKuvBMHeJ4esE3(UHb-BG$A;DCx<2K z+x{rWKi4YtM&gLq`gkeco?Wr)33HLVg#C?`BAywkq9EM@vaAjGetnGG9ZwXjTf5E!RMA= z?!vV4Ou_2SQ&c$4STy^?cCfKDNv=1~wLcZ&3Hml@a@Jxk%%qY8)5-)!m%{f-NEJ>k zp-*6sCK^dKuYutilEZz~oEjPo6|SKhE2VK5od67NB0bB8PNQ8H;XQZIR~nq3DU;&zEPosqex#$1rCZxS;6e4)Ie0E zQQ8t2lE>7-Ml>)$d(2C*=2Smnw@v<^Y#cHf_785ZCI7vR!4(eLW1K6y51O@rsMj0a z7is%gnL-D;a%{@yoS(J=+U<+SO}~qaO^9A)Q`EUs#UzkKq8Bb*#kAR!^=s8;9(dCO zigTE{ko~R+=NRTX+^K=p`YY-^37nJ|PmyLtfL`gUXk=9NnxV~w`5LmZFq#8>Yr8?0 zJZ{qj$Yp;esN;jF{VhE z*brGQqgd7r#hy&AA8{4imz3fFsET5m=5GHi4&}U13BW1lC5P6Z2lTj0YSlulncV{EZx4iDHMMm~!y_ ze&nBZBaO^;%f=ztx|hbnz;853L@IpT60c;pFP#&?97r#+0@gw6X2z<8Q2#b}b_(@=gL+5K(H?rXtNxFX%xU6R;>dHj z;jDivM@L=z8;g`-N~A&BxQtpdiU48;g`Cgn^bqjGmk;cq#;V$oXw}5yE4np85EJ-%)T`&TJW6)&1HNNhC)^?V9PvL6 zx_5N6^<=5bT0dZ4>EmV1B!Q~Jxh15j*!HYsjV6U!L296mslD!V`owbsP|SAYntb{9 zSA;SpFYY@TyF?EPoI9HRX(gCrmw$F+cbZrZJ~Y7fc-=%N^{6G*Y1pT38&M=ikh>(vQ*%1NBF8E8)e=~3NfDHr(xl@N_|w6{BZsB z`K!u1j>Ko|w4n2P_dg92Cw$L0QPyic%$N4*yW+8J&pgl7`rDF7&*AA*W3Hi~xJnIl z5gzSDc)ViwZlXucmbx$#g2JZRo%2_6av1TNmee5CaCX|B`S0-tTx-+PpPN8N=K#p`EsVlRg3 zb#v+gMdF7IDM{D(0uyq^T#lAICqY=MHYBQXs-rn2CY1e1aTB^(IR4176TBtl-T*BS`l3v+b584Hry{)KY@;S(j2I55sNJIor2+wSldc|Js zjS?!H_+u_j3BNM*xJeEZP+tLp`SGrY@@Q|YjYi||LL140GqWw~+@H+92suE|i;aMp zrrvk+h82?}f*WhVd0WWWCI&x`TjGn+S6V^L;yER92s|+G{O+y`sk*6g?J)TMe9f-l zw|Cb!RpdfdWSXcMxqBSh9H+)R$+OP(!|Fh{XHe^>kFU7rgg*>O~w0Sua`^I%y+ zWE%D;Y+2wF*ynr)PfwE~_d!be4_|ek+FzFD=iNH26MYk}lJBR34oy8LNf?}PPZk;v zSVq<3iqJl?pN3WNr?AFy<+n2(7&Y)%v{L@wFoGMmMozKA(l)J_CfE~_-v3aWsIm5D zY=$=fgx}5HMdnL>4SNLEVkLr&H6E0m8f(7|E8b>@U>QTBz@o+ z8heDutcimpa!XK?CAe)g8MNnB!Y5N!>A$7$XE8xV;DSm0jC%t!w)8-Mg58`j7Ylk`(e#arDkMn`Ti znZIz=?QwOBZ$j7{ec=h%D6lc$2-sBPJV~I26*6834UF_gPpsLlF6g&GrRf-O`+*kV z3dhZzEwua)2zuuPQUd({Xd_R9^VjF6i+0RvsPW!VrD2AMV$W0`=~l~ zT{k5J@k9YekXv~$xNgyi7HU7V%7T@#4HUF&>C(2&LO&3km*3}&gKf?Z+cswj zYcQa?O1Zd5TE`J2`z745D#O#BGKuh+joC%VYx-F72w}^6nKbRt1tvikqBQw7hYR$r}r;j8u0G4JaSu0WfIf{Zh zVg?C9Lh*Ra7Pj62-=O;~MR|kyy7{%~B(Ms2wTT_6It+EGTa%1eIf6nOi*(LZ>*5_E ziB639WO?@|KUd5{n~)mHGjVDgSzeHF*8ZV2?TpQDBJ0)7tLouuXJ?=M|$Aoh{LZ)crb+Vb;E))SvlP;nywd z#(hhpGyT1=q)TR;tTqTnkA2I!K%{l4Y-7hl{m0Wdc`JMdLj29cvT*Jt=v{a@`m;}5 z8?YypvwG5wbI<<9Vw@017O}Y2`6_L(=%rQrb-xw4*Xx+QubpHr40M!ccs^CGWMx%9 zLcGi1oyI3{tmpemLgW9o`);~rRu|EjPr2^)t?(?JZZ7o(N&zo?lSLt7Lzy4kH9qt5 z3TVFQvMw?SC$YF7t%N0xFs#mwKA-oDB7do{8&)rE?QYzxeJwn}H{lUN%NOG{)-Lo> zv``8rqo3$lPvU~~VC_yQ%(VcPm4#W3AFZJb4R7@Fl4Rt|lZ;wp zGOykpiyD#);7<+!JCU!pM1KW#jprA;2HgJhIFZgUn7Ev%@ERw4P3yw`&-9IJr4;#3wXq{$mdNX}E*B<^8g#|I;^5;^Xf# zsqgE0niM}#ZT)~F`;6-5&iT1zxUBAoWX=2o&lybdu;Ms}m<`a{{5&Yp)t3GjcF}>o z)?3VjqV|H(uX2yM^2Qa@`taAEyQlo8#zwcK;p(rQMeF`;W-8I1Pbrffo!}Spe;zyk zLn&&Q4SbyW-q$r@N!#GWF$U#jr@xxjRjfIhv31yD_Pv5xD8o><)BT~1xRV&Kh z`#p~8>zf?2HBHzBHYq{xe2feMStG3v5gZ;_^t`0BVjN1^qK)lD+60xba zxHUxDEbPmzUN-}})8Rq>zK7YvxJ>PscNrb5hsQS;q#)~FJzfA-Ah1?A36|>fSRWf9 zp(UHu`=r}q#7x^o#>s@f@ujN5O&TzgPS&rVKmlBjjMO^bG7%W4X!pDT7Q2wZF!P*GPg1Df}0i4s#EzijI(X~ zOJMuM23(MpVv*R8kt@QBgsw=o8drBE;@+&QaaN<0u6yA(7+|xQhcPru*i_|*c2@bI zWPFhf+WZc9^{8^ppA3$I!_6lpLccenA{Ew!B{CU*i{o%JW&5b!9%-JDgf~ zC?6z{6J0~CX#OO*n|?Mom4otN0%2k_AVtj6E!uWpIEC$43r!eSh+pFr>bH?{OeU#hMFJrUS=>|)C}d}PZ4 z>_WRUR_Y+$eal;K62+nL6m{EM0W=n@Ds!er+A?#<1lwE#YlpK<)yx)5os!|^8{oT) zGOV8tsvM;$&&q6#p33b<3AB`@gnr8Jt9>9>;-|bhukGu6dUo9{|1hv4xb!K_BSXA@ zDoo7YsROOB+Iu*>o}%kWSLE+`U~g016I;g<&ODPlzI12saGTk5OcXXzT)x;quUAjium5Oz-O{~OR@SO$iM(#_-*PkV> zXi%F+MB4hDrqwad4;V%25P?An@KcXc8Lvj$vyhl--#BHOr{AwIx77uldJd75bnMm~Hg<@P`niKW{enuFEU9j)W`5xxF{X$WB7OP3dH8h&iB&hLnZp_j@Ni>q( z|EP!GdeociMA`C!=4I;IB2}uiWfI!_&ZNsCXC<=N#~(NnKb2`i!t9x+KN`i6|v z3x&6q5sdU|Cu^sOFSxsGAO$Pz!pesr-f8828q@nGR|*fyB4>qIWD60X8O5lzkpyqT zY8vhSU>?FVGv<15r=o+hLF!ue;sRm96JZJ11P6n@V69vrM@ECG*-95xKg3;&%x6ziPI@J9Iz;_>SkfT8x%vt=o_Zg zGLl`QUPWXz>yINDcd@o5W@HRcs6dE268M7J=&NOtOJXweL^JaVyvJh5%Ln=rZl~^d zC1#dl2CtCh^`|{g&2PmvobuQA3OMC}RIW^ACQ39!gU?|_ilY0oPm%#tI0bNDSyF9! z>QHFa)5mOgEeFIA^$dzBf#40CiXJXZ9MyCjrGf#hBeHhUPX6uQhYY#sica~}OhOLX z21Grd5H@Tx0AD|MuO)pVfZS3!w7hw}5M!xuuI;>f6m4Gm4rafLZ|?c$Xz5$l@q(oL*Z4S`iOKV0+@i8!;=Kw;0sS`#NY2R8J&=?eUtw7wa~h~r~l}$7GHOKs_0_e{5~osx6ezI z3F115JjzfjpLE0OH#Tr0t*R=mU`tp8oau;=Hc|I@>-j8$wAp{x&%jGKU~SF2rZ2*K z&!HUc37jMrhc=N}h9NXu&64K)^qkC4oZv;|H%5OP9jy)Ft!$${K^&9kj4Xf~`}JPN z{H046<^ow1GvDF$&J~XD^3Qrupt{SSXh-!Lr){e?tbcAEj;px4Iz(Qkj+u4 z)R&GVve2H!D;TKrax%4*$%Ph)yh%o{GMg_{+@=g}uMQr{9|3R0VSu7M<<`pX@L9Rr zIDE0mLOG;el1~4=P{&Y0$CfTI7!(Ym7JvQQkF`&lLl%2O)Lg*PEl%6sq7F z#ZVsE=er%xJFM0zQ=oU7H$jo8mUiyoj3FGWE^jO&?h1>!!v#F3qOcwp3FFjsLLV_xE`GSzxv=r!HJL(LFb z4Gw5lk;XJLq_djM;&4-{bB{Hbsl4l8sZ-9`zl5il#ay$tB2s*z6Gnuv!!l_(D50xV zc|vDkO>Ux_KVbb!2~&Y$Fc#hno8Fa&^HPAy113LXx~)*i84XI;y=H~GH3+LeR>L1* zwTzWS^{1USL~riH*B|QC$bN8SRKbcgd;-l`n@O(Kj{e9<-;4En0XHGd5?D*y=48f6GItg^GJvghd`HgeH4vg* zy{Bb)=8n!dh^o(MMQ|~OsviTqpp9b2F@~#{1G5VIiefK3EFRWmyNzWk4xTqRzZvLN z`fCnL$bMLsZdFb2VpX~D)wH(n71mO|fmNe~e|T@6rP&iI6fog+<78QPB*Ks;lKpQF z+2TgCm7NPdQ*t%J9t+BEeX1!2HKFm9RR zRS-%h8)SW7IpAr!l@i-rH1pcB^ZgO?akZS+8+^W!?3cf1WkwfpxA^mc&@QYDuHVYp zPTw6a#{ZffQNisWPL=51c09E<6>4_R;m=hBm}mKDUBE#3Z4tI>;6>JrTwl;?L^NGj z*n;OC)oV4S#)xyn#_(qV)Ap{VBza*gmKbrAwk;KjhVF8;DUN-7miB?Q{ix9Pn`_XK z7jd4dvbvCTS^_v@O26aKk$h13r`Yu)n?Dl<$>h@%!PFO}=c*gGtzClEr9?<(O~8{( zK1~i81XoYWLj*bF@JnPKoFfw4m``DUZm$PMQ0w#epZC*n1A6uE)Ao4ed6VWNL6R<^ zE8an3S}SZ^(hcw#lei;U-8+s>XRRYg%)c$+S=)Pt^oR#|Bfc}zuUs$h{OO?En|WE$-)XAQ(12Fi?u2@aQo#7)W4)f*vp?}C4<2numFk%sG!eP zGEgia{Zf)i2AqCcUWyUTXlBlS{-e397e+&wblvW^_C*!5%0dk@+r9cn>7UREVBFN)d>lu3m&rUA+Qy#5|v)KA6m>k%1n1$8H1Dy|ol ztr+Z8S>QWUqCs-I@6JtK4#0f+!`?Kp+5aLi==!hQaIlcfBd)kD#JR6eP^;Bg=y!A8 zpen?J>6uubsOKn6^*d?pL}d#aLp8uB4an0{r2f01<$bT3>rbQi14l*dg3G!ymXm@g zbiQz>y4SrS#nnn9Bg?1%7+q^+Hd%&0s!QwqmP^i-9D2j7J6e56F^|UG3a1A*QFsmB zbwDxNL}WEOD9IlbWk!+$BS{;PDrdjckfHRcrkFn5yihQqy$(50TfH;Zk1XUwnjuAq zTUl&O+EnpO-{S9{67LcSpY?f(4=7n0-20jB1TrE}R!KL0EDOurPxL46-S z1}VFuFsv(+PD9*(-|8iPZ4~~#x!wBmRc2|p#bS5LJWm+~?z*DpRL z7ad#WeKX5G^T=es#i{Q&%=wi`TAAxOIdspaPxU@6s0N}*VO#J0m|}K29=h|2?ta8u zkCHQVA{77zn-%&An!5SV4n-4AIrPY#@9ro+TGv*{;3`H)-_;MaMYDuE|N;XbgYSFBkzaurj#pB>p8tJ-SO;2f?f;d}k(-YCM5+ zl78i`G&%4P56&S!JOVCPAFE|4-+pt>gE00lqFHLR4pxu-j%PECWzs-UTT_{o4aOTm zyJn|~3>|M4!3Z#mT-4wndDJv1+x3Ly4E6_T*HKhHmq6aJ9e~J!L1C zGK6BCLE$rTo;Z{TA<8b&4C~EhxU^qwAs+Nn;YaAq*(1_-f2&$_JK&eNZIGl(GxZ!q zJm2FVT0=Y0^{+2%g~WVyQOgR2@}3+c5w8@@&lA#AeN-GrZ9 zdujaLftv9ImZ|f(skY74Jk~tkY6)nU-cRsaYt?0ewM|hEK*T}eaxswXe1W}%6w_Ov zel>&fu(a^U`RiNcc_1r5U5wxoh>P;qtqhAs3Sq1@h^Ng4cy3eFI!l#yR?6cu7}J-R z^5ZBb^*=SQVGu1-=pVw?5{BoMFv@x^OTr?h-gLRdKGQ=6B#+5%Zx$cRA*;I2)FJh} zt-+Q26a_(&lJcwVYpQ|+6Nkwa+{wbl6+L~jeWresvKVSkD)c5i_WSB6-;C^$=0DBR zgNg*AH$xB8RIIFZ{=&O3kwAmVF?@F94>8y9dr(Nvg#rmwU){8#_BIFhta z70dpN&u>=65HbS4g7}#F)?)IMzaNl{!p8wfg>tysjy_kIC+k?aK9T<`Y^FmO+N>nF zIk#3kj9fCF^W~vFH@`k^YUiGqoh$sMAl{WMX|qI@GLVO3TyQ&?AQ-=JG(n>c9oW;# z$%y{K|C|W#a64TS;W2Uib5RThAhq1|cVSD3AHw%%z`~#{cN!_o9-xU(db6*0hYH_D(k~y9g2*dS+nHX_QU43( zK*Y$o%m4RGG0Ys+;&{~2?>YMR3UaFe!wT}DujaCd{|mR~6Q{6) z3iVRL%%oW`e`N;uLUh}2Ym*7h;a(}lf9YcKszkOXTZl$XU?lFWW$UhgPF0%5p*VNP zO1a4BFXWXTPBbHzQd^sKbVsIewse{R3|(6nI;tTm#Lk@Tqt_!0OBlnKKbR9x(>{*2 zM;@WH_Ln;NC2^mLPq_eHncg`_+HI8m{iQsgA6^uyMo!k!nuy z0oJDfg`-BXF0VG`Uwitemu)TO5UPgZ@Ug*q@3^M0giN_O#c!_OUT~RLhJz+8oYZY_ ztcQO^s`uieAC5GxW$SiB+X(HIFTzBOLY>p`)kH|!4Q6jsaN&0KGz zQ=IrG%)P}I4|Z+3jhk-kP5k4om-I_eEXp^vwv3%rOt+o3P1%Mx?(8s++1wk0J~D>M zvXKgOhbDy(r4u2io0|8fvcDhgNzRXr%UB)S2q}+MkH%$CY{oj8rM}9i-Pz~WH|1D9Lg@+3DqP@@I`DYcmuj7tp*s`ZU5;5 zIm&4=gGfhRK{gM4W>FOZ0r2PQ>^vj42JNJ!D$7+Wn9N)DSk&QJc0zRjVYm^>-MaFx zz2h@RuwzrHUQaLHPALKkX)L;%Mgmnh`&A-_E+09f>C1c52IFqv{I1PrUp%{XF+0b( z)@rAIR+G>uf8?!d3)bCKtMV^X;a`k}Q09(tALIIsQHVWbW59g%7e)5Khnm_;#)RTk z`Mo7I3}iL9qO2qu^uk%8dAYmfp1~E~{I}(n@^?r0&tK9E;U?JM*D@#; zlFN5GKVF3E%qFPuq>pXhg^Mr$d_af#aatEFhmU=dgr7y#8kKUoH)?idHsI81mom7z zD?lvbgm_#U#@G|ttAjkA%??dWzTZJYfD_JUrR$8nSa<__kU!^5;%aT0-+zks<_4H3 zk;j2!r2zH}fT^3?)AHyC?B3c$07Hx`7TjkvyAZd1nv zyCm;FES!|Wsgt^11UCK2z!!bbdcGCdj4izgal|oYb-@u!0O>O@U>KW63=%-*s6_OB z?7`ZonJ1xpJoD8It|steyC?ExCjpVExU%WAx z;rFvJ!DP~ae-^Sf@I&{g9wezPldmJM@0eUiaJ&V0HP?RMtvG<}-25~qP8tZ8vAh~7 z4nOvXCit3R0=oQROJ^IOu&f);$sb9*+u#S}MP1n1d;hZititbt?Z>TNhs;bA`3+q0 z8nI#}?0E3c@uatO|CT>jZz2lp+y6kAg#ohiaC`U>YQs&qmn>?-U=+uMSp7J)b$|1^ z^KZX}yUvMOMD${CPTpv()GYvJKW3Ij9cV0~BA51TF?yA8L+v;A$p=+6{t73pn#0bn zWY|iT4)7_sji*H_zvN53C%0Av3aN;Lz0IMB?3&?R#j^f=@KwIj>LO0R!sD{=2@;kd zW7LM{vSe;35sO4xW>>Z7v#(r1Y&sB)&|vo%v_yXD*aAB(OJvFQg_Cm;fU0&qX$a{J z4>jM7o1`2n5EiRhz(DVI0(>3Pj8u1Ia|-B$IjNnut-z_mnT!P|!G%GC|H6AU6?PGm z%t=km@$rJ+vP-C9kdQznt$lWb)zKu4; z-=-8+vnwQw5j2=s7>67o)R94n+hjNZ3!Aj6E_UI359H8e-xIQT$b`9p7pr%+B?PPS zmRuc_(2TG#(XTw%1CFj!t-)@0zK6_^jC?N{G%k9 zQuK^>*AespTFfQBHkiOzSVdq>8k;?5DX}4iqQ8BA#%yq0ZXrD zUZ+&Z;2~@kUA98t&6#|uyayqz$a#8Ac=^N+cQ5$aPgo!gnG*4jkT*E`nI?}1+k97N z90>Qz34+4bp)9%?MY>V0fVp=*-xZEH+PMo{NXut_G#nJeUZiPzW2{S?>v6vEQzjqw z4V1qM0U+|`2J^8o;4`lVs6TaOk4FVzAbY?t|IYA25eNKl6-7-MG}RnQ6Xl8g`we3j zXioWt;5YH!M{WWhBM21$HsceEnw%Qal`Sr0QetO{i{@b| zZy*1_e-rFBiKagVsl1`r|76W(UhJ#=^afgeEf7}&Fg2Xitb@rNY+f9|2hC4bA#<>L zEEdq7*2 zXD$6>XAPN$#3u6U&=!J9r~6}mFxGgFZ*ZJV;5G%!;tP)u*q)lJqm8xtEX z!%d(|!M%vkaM0@ZEb=uwMA|v{UI0zig0*?wE^zJ>t~yT>pt(r52_?^)SRi7*Lt%a@ zinu{0DaIZ#sMNxc1k&m}+3T7I=GCTPLyQqC?#Wp*v02@sKF9TR^k3>Mt=7nQ^X?m1 zRj|3cpTH9`^rxLI?**t+l!Nisy~b1 zB1=&cM`U-I0@n1))M;GA(wnpCaJ>nIK0=7jSn)v~lI+x1SCY?wlT?q5W{A;nSqNTPF zpl+h-*h#@YL4i!B zx}xK`Y$FX%)E}33br|FrFKFdaNg@uXt8Q_epTaZOM*`d632L_mTH2$)%M=95*mJJM zs(9FNg{7!xg>Sx1D&O z!dKZbhGeFksT0CTtowD1^KWM*UP@|`Vsc|_|G^Wlg5B4DHMQr;U3XqdOEVP$7> z%`0|qVL2d8{St}7FxJ0~$$2EJrwMOuF-zHMmPJSWqJoAeL4<}p9> z!`tzNPgTc<-WG)@RDZwAOWc$3t|y2gszxQ|vePmGK7MCliF#XPx;US)1qH?ch=2Yr zpIHJCgq)p2CTS*zVRbsB4M_nq_5J&<>0L{F!%RMZp3%9&mO5KxIowZYXKt|;;gb9O{9LMs)vee zwvQ3X6{pvR@(|^tkDaB3n2gOXnJI(vPnsmtX6B0(N+KY#$uJ|!19p7 zr;&f1;^`hC3;XUWSmlAfN&E?FHRe{s9@L|DS0R-5WUuJmqlWJ5fLa;ODZyCVSadQY zmbsuRf8; z^JP0Tdq;^$Wl%7ju|y5PirJR8?AgKb`Q$MXQhKO>|4xifF5`>bOW)Qm`0k(FI`x=f z*X633W&(Vc@MFaGbKUa?h^fyj|GK!oZh-}MoFL^x_7q|XK2$xEI+xk zA!Z?d%dH-zlN1}FqX#@#e1Zth}# zkU~gt4N0kY>jC9v=;1O3PHuF*ko15!Ftu5DO2yB4t$liv^MK+kYbgM2MVU5*$hso_0VafS_$F<8y)D(jwAS?> zW%$Z#O>LM;B+3_sEH9wEr%yO5LH(bagYtHP)3OgwD-yUQ{j;`V3^1!)Jym>(Mfz@u zC7V%()wV+Hy3B>`WYg1eA7du4{0P>IDy+ZPV^8}1lh=UII?E*_%$>U#E>2z5Fnm2J zGG*~_W7p$P=_pEWONzl+SxNWkwub5qt-o%od<9!2DwmWV2MY0e5g&xu4@J^h`h0MR z!&f3=e+osF5l2o9F*|D|BzP4M*2LvJ1nvQ^vZrC@J(O+Z=(_IFCRmQuqL$^ z5xGx2#(8KFIx&Ty4|hk>+LJIIj3Ai^YWo&{sYKb}H9*BtpVz{>Q7nl&E5am?3Lv}) z_e6%%M&1Zp{89rx`NVoaPjzljLZI<|LGTpys_*ojC)uKjPFj8PDCLBu+ret;^x?VR)zE00 zXs4oy+pD6 zqoHFEn1;nG&L6)$S1kOdu0J=decT8}Fp*ZB&oiK}?7IF^1!JCPwEAaJYO{7+U7P_Z z_0WC-Y`S$Q-hOpeuLq2Zfu-2_0$?tAW6@=MI zQq-=!Oat~RoicK!)PtNuK@$B5wc&thBM4lhH(7| zgE3MtYAEZT<&9AXB5J@MU@m2tPue~`(i z>}MP1ac*~Ch|ZO~@jv%=2&`ppUb6Q0Z*4mK@Nok^iq*m8WhK{<|JkxOC>sLv@j1>b zfZWR=EXsx%P<$?Zy?Ec@rY60cFxKyT#d>?o&Mli(XPvZ#(tz)3n`yC*rerTWQ016Y z`+-Z?*ZSLtOpa??OVE%?S-^1C7jQt-u*3pY-}kYRk+M@yTbA&4QrAh^8@cFHGu>2V zFb7$LEe8DYn-fJkb{>Q#VM$i8?2dae*l7A7R%@8C_s7*F|DaC}@Q#v$UkZmk+BF)S zGA6#N?jK%3M$$j1`}3$rM4Kjt<-sezlQ>MLnTpNKFE{xaOq8B;K?)lWJnkf46gF7s zHeNLZGXKos#je}QcaSUdZ?>ntsT_W%7cz_vMQ=nG9v96qFC@=1%dti_Fxe4w9v)Y# zqjR4dh#7 z-q7nUN~y#al8np)y&133NW;{tP9)nqg}KA>u3r>_j^CEnIZAy?8>#2Ph}+n*K>gBn zl5-LCU(tV7l8}wNFynSi98lc;);%3xV9m%tAhzj<_EMYlhaKi><+PnsZmsQRd~lWZ zB@^jObuP})waYE!Zb)MInP*-cm#k3D^x{gM)^qURxOZ%(RuHP*GQz#$fDtJgbuc*- zf4nh2=QN*kJ#0Ee_>YOr2{8JLawV42R`<%1+*bJ`$1Y7&21Zr;rCt-z$VlNNcMWr^UMh_W}ffy2WK!XI*=0O>wG6|GsIL~iG zHa0E6)|iACvS62<;uw(B^$9In&xbNeJQX~mXhI|&OFSYL;2N~x9H6NJP@WXt-4)(F zx)LjH7c!b@6@bWjiLTx~e$Kv0J$*{ zTJBR-y2>E2geKnRW8ElE13KjE=i=!=Ph*zTgJ7vYf^3g;tuENcP(+;P40R4As7Fk@ z0M@DKxmb|mDJYoubW*XDaO)B1LkFG@DLS*~lg*nnxfSd0W1enpG40d3Or2&tn7XQ< z)8(NkPiAxw{&6f7w}>0{yQ6w|<_bIzsgl#bE0LCm=K>fj|nc@`mU0N;ljJnQ+Nq^efb>^|zl{nLe zi7pf&#^K;EjdPOwpbUAot36)yB-On3MBsiSL1~r*n8grT# z9KF8WlmqvQM}jbJo(6G}dB@dIa7;$Pl~{zhAJoXdfL7;9n+*yqdQ5)3YVBalu9=YT zL2o;Is_6RbP^^7i>(Hx3Gu`y1Eds7dPDm-aWu)O!XBr0yBAht;Bhn=g_fnR?9&Y97 zhSc@2Joh}D#rodRt9!0M9?V#UPy3N!nOxcvl2hd6@f>=ak%zDExtJF95DpBS> zeLlxL_u+1l-?34GeFA?Uelh`i6_jvCT`A=fPz}d3!1nIlF$2JFo;X##Lg0$vVF*~@t^k>u}{P+@w*^;>TpZ}@A(@b&K_Bl+_a?>QzP zsy08bwKfAqu%cx{JKIoe;?`krs|Rl-a5!JiJ9AQ7GxctK(`G<4zhcBHt%D&Xl=X?> zKeLOb2z>Q$2G=(Cu(z})58LHV;gxxR%|e}pI!neC5o5Gy!>5tivhv^Ks!M`aizp9v zEoQsVI+^V zUFGs(L1vkK_Gwebf1b+>Z;#>JLkQ0rUK=f9Ri-i&HtX<9UEVl2o^#>vZApQf_I}M~*o}VkdzU)S^!$>N z511=8IL>Sn{Cz)dQN#4yX+Lbkp;2GoA*USJ(DbKY6oB1z@vyfu^{R^xlPgcfq8b#4 zySGgeZyfeEdzcnE52kqT@58gUKgH}N+ekYbS*3^%Oq@~3b=`%{5usA;`|6OE{pgc( zx18l3%`<4k99`lXHYhr|CEM1OX|HNw!btKA`R zUp?u~U(AyRuTK8=gmti~bFPBV6pD-JwCF_Uj~#yq-Sh~d@YN1$gm)xdAmp8xWa(T% zz#(A@MpX|wJKD3wl39g^Mp9MgB#>D_ba{bCgq4B631(! zS1-~2N-Pt=(rHhk!|;%F#%E&wHpeNkSY^ur=q9h}k7;#yu4^xn+!?&vKkV48;GyiUFT?<^1^@lz3m<^uH0{7U;!^C0f1b+=ELXTg0k5$!Pdsd)G zQg=VHRlrF3qvrYw{qf%N$%+X~CkN=I+D?bB<3DRBQL$b_By)#kN$;^ zab1TKF3VpdIBa#d!?tJj-S|&{70~9fdtx5e|gE*#eN;}LpOOwwSJvDbqt*fj+6r^DXu^&IClkemC@-?O(Qep}Es zo5eda;mo^2<4SJ(xE}qme^`^KdfJb~W<5zfAYw*DU&mgi3_JpLVHDhCkK%e8?y#>% zy%Z}|Tj0mIK==06$)7|gn>g!Q8!`bkU%zHV6y;SVodAyv>>tK{7P`Hn`v5UY`>QHA zaylA)>{h`@b&mwgV1o`Qan7<+C+xU7nc4BnQknz{hZXwjfvb#?6`> zJ9pRhF2S6w!oal+C0!?<3#={*UoV!+O2$x~blhZY9$aeunN!v)DBW~PwA?W&la*%x z_UVwEfls0r8;fJZ{yGnrW=UehNFzOS)~gv^0w9eIUpLEjZGv&Q8Z6v@{-^ZNa}IgC{3R*K+Lsdn2SO+DbW1&CFh}wqcG6jii~E6euCvn z{MU8a9fP@2CAz;aSuF0(T_ZDM}i4XuNLh zEz9lmmrF$Ef&&&J$t0GAPbJY#LFlh=bT%*RO`fIJBO@Xb;VaOAz7*^`N2G~&+JCz4 z_6h`&@XqVWe~5NOthDO-`8s(eJs1>pwWT4Xomzd;LY)YsNCCmyZ z6}v&zPK?aLwP&8j5jc%iO)au>xYQLfpqg>}zHTfbB^o5Ps`2)Yc)omBgds>{lt8oH z9z$DX3_~@=PPog+_6NBcGEQ4w=De{C{=i>`@S+uO4#cmH-Rf%-wmlDnuT^jHvUm>6 zq`yy`g>iL}+rIk{y1P!Eg@Vs&ayqqJCEXhW(N!38xx|8L!K+`jHJ8N&cXl6mawi!J zxl(`vn{b+Nu}kaD=tT0J&g(RQ=w?M#u^xK*ghPcfX)YELVFdTW?nW<@e2=Nx%jd+H z^gZ1DVlpmqglKv%T7dLu%tycIE#TheMO;q2b_nr)>UdLU@wA`2hc1-R3NIcb>|Z9%g>nRU7X9;l+Y@;AOx^HjxY%;`CS@RKCQ3O9h8JG%r^$Ldg)%-BAyWgp^_* zpa9J5og46V3p1oW;pKl6=XIe1b<{j;8hIK^0#FX6E!g)4dZvZkh~|4mctHKE zM`xqWR;#$N!Z4Yn1I$h{Z~Z~fr9eM?&fe@;qBh8G_fLEWjQ}EabGcGXS_*1WLHtq$ zl@|<*cKmh@mdP&`J58CvmCUh#QPBKn2czNUXncdEc9p}ICM_F!2tK- z3tsN*WQ!q~Lmk)9iXFl03Oy4%e> zZLPMv9p$D28A*dq<^UdKIy1n0yb1C0XvI?O= zAriIbP>|z4%1O>?xIL;dXH`dVc%wRNT#4fndUn2%eql+qt@nHg!^}L{uf?pp^u}_( z1e$BW+&;xi4h?3yq{wt@cEY!C%7|UT9tEDW zzY*1;F#&L|_ZKQhc2hw5FuY=_4XGgy0-5+!W=)EajTL{weW8QcfSYjHmVTmV`#a~( z8Z-HTdPZAY0QX!ATK}W2d`4UbnBYs$9JN~1%D_p$9M&pJ#j^Z7uuh$v9x?PM$iGf>*qurpW13k?xp8dpaKbpS#!Y2BgKf;&H91s<~4p z-ce%7FORqd9ttM255kMW_Zsk{jj@6E!ZQ+5rE{mG#(B*8KRrt)&TNs7K|s%FLh-AV ze|_cZ_{XDmNCS3Wo5gL?m4XDChdVp8sQ>cRbtR zS8hwYwEiU-1ONghXGh3)ohvTtk6-4!umPq^@B5n?jy?oOcNL9di;R^k3H4z3jejj#+Oo zp{G-jHKA!B91?K*enfE;yyR>je0zuNyUDt~^BM<=qb~Neq-@`fu@$H@EA3(OY z8C((ugzt!VXmhPS181?wj^8e2bNUxPqp1}WP_YxjN5uhW7ws#+3>WgA!!zvT7U>|+ zPK{;O9QZiVKU(Z2=9cX_?NV|k+Ha<*j}8;`+PaHvKI9aUb4KE%SLcbdbI-|h&3>GG ze1kJy`sL!Yh)o!xfj52@zEYM7&rJpAn?siwt~O2Y=-GTR5g?e$1=j?QmmkT#hk0tW z(rt)FfNtgyBkiD@U-reVl6hi$Q!swh`6`=1xCFBEEZMRrTR*cGtrU`Wu>s*iLhY0q zUBx(i`&9?fgjKZd*X{>1;^ks@2GAFfj>kdixY*-BK?4ee`4h?{?`8Q;t=zYd2bSzK z&Q51(7k}t=>#}I*2>wg6^<_xOijR0K%44#)Q{7PkaM27h%Znx_#%FZ*@@3hNEV|Wx)C*VN zOsu#MCrz2*-@qiikx4E87~@w8DPrA8K`TD5#h(B4nI?BuWRh~nLF6hLsydGk${8J* z{;meSFg4bcWV;iqo|M)Ug{Fm^NB!_5HDhUR^<~4iL6@IN^&aj7+jrKa(W~vJ)i0To z+~oAv4=~dggTR&V@P6m^uZ#2*j7ed`K!|~_Aj~YyT_4kcX0{{tQ|XKmET0?5 z3f9(V2iAWU{nV9EZJXd8$F>P z@B%|c_aD)6&u=+vi_9PG^azt@w?<8*DJzALHFkb@ef%~~jT77}j&3c$_FAppQ`Rh; z!gYNgLsge2u9-&GXa_*5!$RK9<8?PyVg^qWXRY#G9`JwfT5uCI0sNIAV@cU_gRd+Z zN9`85HP-Y(E>XB4!;?*mQI5!tiVw+Wzf!8`OCidw63O(_7Bs!=Yn(jk*(Pq6L=gcFLs=mZP!3YoM`U;T|Jh_CF2w3C3XbZe@!6H z5C00X*Z4BRv3T3BiBMeLqFr$kXRzu8w|{0G-wRZ;aL!$0Ap*}3w(!&f=}IrPSed7nC6d; z+X_8pMp88m+dj#dm;y=<46Nu!1ZwF6Q`inuN3H}cc-{JGDQ22F|#$9-LxZ~8n&I%1sc6aXl~xC zLWN86T_O%aREW?zdLA--5*8CV{qVoU5R=Pc8Jn|XZQGhT1voX2HjPwIhq_b22{ zTR-2|vnp5-a?ymdu*Yfh&MN%+E15)V$F!leaf3LA@wQw#)&`AWPC?sOU!DzrS1_?h zI`)!wyqX{1hCtqIs-uH-Qp?{j1!K6?OS#);i5zx1IGHc7ZQF(@cY*a0VHlC^xTJ)} zvJ!R9V2IfFt3)6W%-;SN%&;l)8kA2+|6K>!hYC6{nMX29aFbYWyvfH+;5Qh0Yupmu z9~!o0!}sb+iZ=LKP#wBRs?~UMphZ! z&Lknk2NiwAbF+NQjMAF=MhRc+IBTAdo?6h z_Gh|?w-A12+Lo#>i5Jnw)RK30(eyLdn|TpcfXDT?BB9@QT@-a-ir1HMvJ1sy@C$1Z z4Ht6#&L8(XX&42p?@;$b8K}z<+b6rj1^r{5A0UIX({Jt;ys+GvNh2v63;;OI1F0_5 ziL>bFvsw&QZ|p3uE5|jQ>35z)+^m|Q-Yz2Fk@8D4whwFQ@8!mU#DV!gL}mH9GTx1JCP0|V;SpdO%>Ei9YKld8?TCNa?N{8Sc@qJ+i2_=d)rwULRS%h zh(&P6(^8#1yC(XswDZ((5)Y1DHfNU=>P$ki%abH%!xiWdk4vd<=S~P8KNvlLwbo6r z)W~g3Abp>w?HxU^yw6GPQL`@3?)_cs9X_lH3U}${`u!?#F|`0@`?(=* zHOtFSePQ8EE4o&i%n|oz(RCt@uX|UCM?je_pOfHT{q45s!h(Sa_D^%>WH{ZyfCaN% zT~D(lyVm+wgH%VR1&&j9y>v6I_pHQ?;dL9@?8Fc@ZIY_?vZ?uA`P(V&oCm)&?ehXtp>D|Sh{k9oYafMVa=MifYqBl5Vg^XI zx!3xdTeYodP;9|)gY$>_CtRiCrnD}RnIq?&zq`JOsOiwn*J{Rgb->S2y?eO1V67I? zv_{<1KFR+DlDI5%V8w{=X2W-${uWhKrPX>J#2W4%$9y7g^_86@SLEo=$=f4bLtTyo zz>TG#B`V=L5&u#XD=ZMb@Npo`>B|@6Ne19`xz<9hShXi!fFNPm;&*E{d^cRWxa5YK z5Y?ps4RFQObAhAZ!Og#auk6}9Tq2V9!$8^XZ)WXs3EJ6dVfB)QoK1*qh9~GWJwPVw zZCI5@n!+f=rdbhJNi=eWn@&0*IbmGlDI;`9QS5OvzIW68`5R5S&cvBVV%OECp(WMe z$1=k9M%N&fU>YEWr0K%oqOOlu5enD~b$<04N6NVwhHlpE>t^AnET3dBXrPOZl;xt& zoQBp}kx_|$C&X8nbMJprsP3xzY_ho1YcSfl@99?sz zJ^+|P>N0NhW9unBDVE1vQWruHNI|zEHe85th!0+!L=OIrq;|hvnPJLWk0WRP?z3OW zj8YjdLt`(KS3Au11*?Ok_;bd%BWaRwN9)nGxY-}Fgrvye-FG#u+5Jm7-EY95;H?_ zlI)c`?`b{jF{tTsvxVAcvoQogrn9Abi7?p|*3eILNuBn-z#`SWM)PaYus`piCk<4o zV2oJpiB5x8^0zgGZ!^zqh$anO)89}@0vu3Q z5_@;W=d1|7@p8(5a?zFk_akA#6K8~oUD3-{Nj?AGj~#RVM|Kdmet)xq^m{kQtA~wr zv%Dgryh4tH>*V!RtJC(HeYvI8i+y~KCZK$qN$aE`xgh=a9wUMGJqOpd;x8}5 z+X_PYz8T$)BYkQ;b0#*m)mb|&GJIuC=>yTiP4n4SRyzcPF&guxA|p^t)2Jeb_>^` zv`%k}IzN{BE82JOd8?iWG~asCIa3=q)M^;N>Y98Vm>nkK?}#w*boltCdiEOMJxeks zUG={V*3Y}P1y<$|{<)!ZMS<~0r;0>y;UN*Dt_+=DQ`$>$uI^#v$=0NSJc<4iwg&j* ztaCWxN#5X7HvN;ri4WGC`6>O^aE|T~;F9@P6^Yl%ibQ!+k_)rr+*W5<27=Mr`(n&K z1fW1P1YO|~Ongow-Uyf@8I>B4+W~Y?DQ3k>de5J_GdJ)4U56s?rY`^#P4Z~cDlPc# z*zQgF<$pC08B+0dxQTdNs3%^gcwA$PT_n*^lf#Mlc6e=WYMNDlto^&s=bluM#5fh+X__VM@@4fg`*wH|OSuaZvW(D3>?>YStfZaFXOD8G z9ER7o2Hm!R6gi`tJ-z;p)^}pkTqz7MFuSQm|6JxM)3P{r?v)dD(^|0PRG6%Lcx^wU z`;dO7R{3u9EfvDe^HV)r52?J&$7|#46C-J27@h72bkijz+V=KAw~j_C?g5VfUs}J; zc~~sTbkE>d=n)>>qV=$Qc45%7_0LC4OL5{Tk>5Xwsh>CaSD5N@uyn@Q2m0UC1bws5 zV*cjYH4hjVewXjJSRuJqmY+yCUE|d0f727l%svzNOCHtwy$Z{Oe6rHuDEcq~(C6Wx zmDhwTtTW*+7Fp|W_~Gbmb6tdD^!WK4Nx$6J1C*daysWr%aa^c2mNtC#t37^Fk%Y? ziEHNR`+`h1Le{VaRAS)IvDbanTA$wdx?|O!r`bi$`pC5x1_K*F7wKdfRs8 z(nn0cR+9~EHlW`BvSH`@+8*X>@7F)rb7kDR4KCJ(h~hd5keXo6agKv3YL4A#VW^sy zPtD_KTAqRKjZNgm(x0(P>gz8F0)FROHEKpx27Fxffq&v%62A;x5Saa)0=M0Vv?=k@ zD@Wwk5W}2)s?R(8Nh^jebRNsJgzO(#^Gsrx`D4%XUM+-xzy!-2C6rfVOtS%D3ey5Q z@FrjwM5W;TbORk)!4sTNJp`@#M^0jXYUIhQKpTCWN}gD64Qt119*}&;wtoqk-NNaER1ufuB|0X?5E!+uq&ec zuVzVllfPamK4ta?+`*|!WCHr?K40vba)wt{3G<`Yde1OC(%PB9vqX7ZHgg(nZ7e^{ zJD#uFGz>p1zVo`|?N@`_`kPmH70B?Tm@YD@0{##O3^*pL~a|e1os=LT)K=wdVQSWJf*EqP?i8^ z;;6!UuX7jXEXlU=D~(M$zUGfkJ}k1HB<7c|TI6}W&;XEcrH8>?-e}Lg4F0*}jy!+w zK*Yu>-P|k7hJT2V=i0V0%D~#6UVa2qo)O9A)}~=NU~$~!)B~r$K~zl*m`%{ck1uDWGg=Ee!1yjn8=em;tPBn*;Kr9y)=G=Kz+OCx z)uZWdteyx+(7czgb%5=uNFM9tcbRQdIy3#9!T6MpsP`b0R(ZD*&83bnG`Rh(J4H^? zouXoPzOZ0D#^=ik*yN~2hp5X4nlG`nFSwJt5vQHPeq!z;Toxa9%al(wO0@hgf>p9O z5d++b<0i^DPd*J)UZaclu6}_m$R4wOta@%%5yo@r5Y{ohWvjP7;>Pz6D5mJL_d*AKUW}6}i6p<|beXev#g3&pgR7&P= zh_2rpo~BZ7!jvw!Ouc)|;Lfy+HF?3@OdAjc$aHQq)Nc2b0pgmNj#_b{)-XM=Hv*e~ zNX;1&Kp?AS$LDSY@TcbvH^j9VhTrs=4Ow_&`-QqHNg}E?ZHW2G?YuH!fdb)|_-0=+ z<6~Vv)1C+rb-gi?e7sHRMRct}?Ym5wDf1L#Z4y1;@PBVEj#WlM=x-9vl;2i?Vd)*? z-1ZY^uAMGc=6QSv2A(gT_7^Cv2W90yS0F2<`OCT8(=PoS8;j-BOz7|a_Ua;e;AQ04 zQDVtO{?6$qe?G?9Tm6@2o&x*nHi-pIBtQ;T8pu6Br6 zT^jLK%+5S@@=pz!ZP~aFly+EtqzRg3K50#vtv)D_ir6`PDsc{ha20;F8ZVkHI*#|@ zDqC{b`}U7z42xl)nN&GO;?hQ}%$2fx*x1B=Wn}Gfs4nMFgi}F2e}TEyz(iDu#hx_l zO=3Uo^amChesqYYB?mE^g%rzh{{;DHiT1*dyakor|2|z<8*_&~?XoF0S_CJxgHPir z|L=bK6s)N)Jm5evR=s-Z$UN{hWNFY4oJ=9ba=>xJWQPcmbYu|v0s=3a{BFk*2S$*p zsoo76FNGw_;}=1IM$e24pDdQ>rBQoTlq>sH6(1m-ZDM7l7ois0n#R3e=lB#s=CHk= zR~fGgQ}&c!7snvV|&Z?b{L)F zqKP0t!f(hXiKgG3;NUT>X5lWjYH}a{*3)k7`%m3r*EnC6Z~Im0L_97RyilkvbM`73 z@>R=H7-~V{yx08jGE(YAYPUsMDqT85J6k((mJaW_czKU&ihYPv0~9mD*KCIf%DnGS z9GFTSqOX(S{oF#(=5=l-+n=A5VN&I>Zx)mBL_gRqxi&HB%2e zUE!FnSzQUv(=k%vd2c_th#pmXRcE!;ANMTEY z0t9hFyhx@wb>I(jfbkDvIr4QqmHqqwwfq(gRW+~vtbQ!j_UfWrlC<1GL#~mJCY%I~ zQ>zNvCb>Hd$dQNv&sQovBurKiJZCH zdHFj2<{`oaQsPAdt`$0$PyF3{sQ>&DQZlZc4!q90a_=ybl7Q znv@Y_QYTk?zW;z}4fcKxanpWxXEow(*V~H-?=sg0qBt5 zs_EVd0-L?T6thR+ZUIt!uz(VPfA!~!G!~B&{t1x=xL0T(6$~3dM(t+ZIRD^!eAXk- zga)CJ!QF5sEkZHQ2VVQOVb9)#l{0T+<^)IhOljoF(>H&}KThJZr-=+J4sI{C6enQQ z`zCYJa?J1Z<&prGF7m~yy!&u4>px%3X1%J$>fSH|&Hmym;E5y>5iUR;>UBztm?eU0 zA`y~R#fC}9kt;yChCOD?9Sv~Bk9W%OZUMS2ls|Qd^%&h{W59d`8lb)}`*FLc2}+;A zwW!_umtrV(yWVUM49NwWgxa!A1#3A=%UCHeW|%^qOE#TQ1d#M7#rT7Q$9-e`1JllA zhaUEOxY$J21H9MSh{$Hqrr#N`Cyaxqp-~!@1I2T&d+>TM1+>SWiHlgfWgVut&U@vH z%W99-o#}#|%0sw3t-jz5h%vrZ{TR~F`>|Jh%y`c4O107no$ z>LVSUQiOlXOBmvaLJk1Se@!=N*Mv)GYYzUfu~ zIMIT{HGQKrlP=IpOd>t?ENsEaJib7HEGYuim9X00seZ?V;qUr%?j2_ag5oQl(vW?v zPwoZ5`|%ymAHI$qgzLE%6Q)D3YWHmS4@UT!ftX{)Bz$Y(ss-30?m2^aN5PvgYjXG8 zz2{6AW0pJk>dm|-K6}e!8cXCTFf@`p-nr?_PB<7uj#AgBr@jX$%wwkdeFItmz5yB^Hso;!QMHB!` z5HCC(*!DO9bT-UQ+BHaCW?_v?;F*yII?H@0jmg3ot^OC#XTn8k#+l!m1y4lwr)j~J zQH7K4Pq%F-vdD$5MbKi*XrL4+##aCJ%K)GsU^pY5Zq+=iF_lq`Qz4t;1>bvOADeV{ z?kqx1=&~Y!z3`tx{cp!}E7cY_H4lRGRstAr-D$nZo^z1A5ddk4_YDh)esBQtAw&0b zFtL_(qNR3j6oq}*>KC9qQAT%%Cl6p(Nc8H|8~csnhL}(41=QuB#YldIQ!xU~=am4> zf2od7y4aGdxSf)V3V$=L-m3_&C~W;B@c38%8JR}F2t-EYNzz<1WS_dL!#3s6%B-pf zeoh~<$#TE>kIOk>*^VPnxzRI+ITBuR0Clt!@ze;Q)fSrG8v024KXJ)C5P4{J2;%kg z*5UWW1+*4mPy)fNewy;Y0J=F!H$J?f!F~4qmK2KFFS5IDxL0(_-E|fq+*JRqJhvB3$V6 zy9YI9;Y)_LK_kmS54xwr*Z!}j(|8v$7Q71PmsZ)jRK;O>hBDLSP^+=jFC{2;V~h(P zk#&DnrBZyp4(*Ie>v)-6gQlPCXy>2Tp>#Y`jrhIp}<`w!ggk8whv23hXZuqD z+`v;v$NOmgE9uLpfdx}HYx9_vLUJP~hEU-Dd7iuuSVVm!($rhnx8@4@Hs(X})C9b% zLc4!iD`n;57~jORR#yBCF#WRUkYnQhYLR`vQcGY4O^|0MT?OGgd$1)1-p9;4_pu*C zD7@x2^VIkMjtX9KkLp&M-~X-bUP;5yOS8)?Sn%aeXH4fKJZ+eYr4w7Q9kmpi?)t36 zJ@<4O-voA<)f{w^D41DGws!YHnWLx4@=yBO$3*t><#^I><04VD{vou8Im5^s@myr0)pLzp;)wedL9UFhGb%5U1_-}_ zYxyD#hcp~cPJv%&HafrPKIZG1{hUbJOhs`=KA04MBpTPXl;(;kY5sK z!H45L1*Fp=1zMX3YvOVEGr?^{9IJ$C6xA zzLg@=66u&+UFjIhbup{lf|5C~W_(1n7|Cymn+KwAEI{ZO)T24-kBoHc;aY|5piQGg3GuTYvl4uoAy`yE1 z<8>@fL)JvEL((Fg9xg?7Hi@iDY3^K?WQA$!%?RZvl&~+#!EUfDgf2M(qgxH;O(`~w zw;E{9RcULPjbAT5Y4GY+Q0%WFHi1w)+|ophgcj)hsX~Ov?=oIw5BP5yUv&=T37wZ3cqNS zT@xKK@6rsb1RXHcA5)5)ipwp<_cL>&L#|q?FS0>JiiHOJ*R&CRfuk*8w` zf!ja+?u9g=BCocnZWPyGmvgV)fWi(sK6lFZW+e1BW#;Q7lO`Q&D&{v7v=;mHLz{w-FbS4@y)zp7}&Zho*qa*OZpt zC$0R_pwWQ1MYoa_g9&~s@YCR6LLZa+~wt!Ro#*Wl5hJ>&BloIwCVe*yY+SXWReR% zd~)Ev(){r=^`Vqbmufm78I^srDeRCf^9Q=1jd0Hbn0-!sIn@%fPuXS4liT{d5cOMV z>r&Z=v;EQ;?Ong8PPK;2i8CVEeT}R?L+d-#r0!1_ZJ0y5c~PI7{!FHO+ENT9f(IC5 zSDqXL{hAhc2ws0O>eADo6Gll8mSLok+EEGUs?hOALLDLIf6Di=&Se*B{Xs^)MW=`2 z;fHn}Yd0oU(~R`%YXPDBR3SvG=5o)eaY~EWg@qy8jq5~9=!sTQ+pHI{`I%&l^OwKVGQ*+>MI5~n z{mG3q+!pg-b@2vaZ269$Wu#(-r`(yT6D9L|!fL3J&qH=e3IhEJSB0s)FG7ibu^D2C zBXnED0u}6(juDE$8~LUmzbzvKOggtB?txxH*+`a@WKi?k>dt1mg&bLc zy1m{1QT3K#aWqZXC@#U>-6go|V!>f?cbAah1a}P*ELdRiWdj5!c(5SBoxnnZyGsZG z&hWhFJMv?%t9PcSyQizC>aM$LrtP&M4Hd7l%`!SvO_nnIr6jN&ZDm3leDD7x3PcXt zQtRist-9aVQ+THi-~27Jiej8iOdqCMBs;vO%vOdhNF7D%V8h(sT!m3r4}kc5wC5|o z%_U;K2g#x((hb@*zBuo?YE}NBs@N+*tOIkN-+K6>L;qdwe2*vJ$A|Z!&G1%(>#g|Y z$*zXoejCab3VSGZs-{Hy$4B|q%sg-D1k=qKLA%r5#`{S6rGQbg8Jbj*B$Y9ly8mpa zt&uid%G~??l1OUs@KNPSVnh;cbro@PvmLvONL5aPq@e;{h)gL2P~QUcfQGvvRkbYH z1C#O%-~pM ztTanOl$4C;D5s7k5Fn=CkmqOUM`Yw7Cc2;p4bU{8Le+$2KdsuKTf0F#n3%%Qa?=H* zKlPU0=V2L}cHpsmSjQ+Fwdajgp>~_PLzObsV^ELt_?ZrvhPmXj|MPJrnTA{{4qZIRJxB4GP2f2$4B)s5vUaKm?K>vl)qgUL%S!Ovyf+;EaaLk zDvY(>k-0_5UN~t-xf{#K!71g-M{8zf?LhosmNN22zG6!Vmt?%AD!sf}l8Chj=d0J- zL7!IHGrjNdd(@6w7GAF(ik=f{jSaS}mga{>wE~LGAmhBoZ6V}uv*0m+*Sh8nKAToBQ zmD)fHe}wimB#6}SSgvkuj(rA$ankqDx{^Lyy5Bm)h=*n?)-+aL)HUF>AVb8ctuGM= zKDfM;(hhUUOlNR5pD*fIzbVpY9QBHbs?GVwAn^xHzlxcm5|Pm`?wx%-LY|EAx9@6} z#_uN+O!~*_SVNNoUs_Y0@YRgJ6Uk=Kzt}>dj_taq#WHShs*rENN|Dewyf18+#wr%h z!UQa}gZD>7HivJjEWd7J!$W%SnU{alCpzF}XgHxkacBQNR=U@z7}iQgL3t0WPsWFo z+A1=SKJJ+CR8*VOE|b&_{LYAu=R(Bc4$=nI$BWwGz{*P`2t znw?=IEs)M~LM|&gGmSe=&0yY%30{ntz$S7OS?j!mvF2gShKT*H1o4kl$erXFhgDQjEh3Nf=E#7P%57?JH#Bc~+I^me=l0WU!=e%glqh z0_!Sth5RZ92fxAJt|G&Zeh;M)aipX#t%m1uG)?d!Utl>pvFsOU zaF^pauWPRBg*T8V*E%8?-uOn3p~WvP67;2w9v7q66(Sg%G@r=Li*Cm4xwf*?MBTuv zXA{cb;*Q=IPbQSR%j|71s~vtY9?gaX4IN8cR1umziOVGUPPzRUp4%VF<6L(5>Sv@k z;@?-P`#O*^I=4aJUtthKt(jtv7<{6))E!QJPb`wVC~d^(k0RGAMEKoO{myj`744L8 z4lbmKzvHHvd?Z|T+n?B9;l3ssPR*DMtcPg{)aYC;ht6K>;J;Zr(MJz$p^;wh%e3Lt zc0U|RZ2OiACw_3L4%|3eT52)2SP^fzzaasByW3gJwn1cG)XsHn{sv<^>cA`UEBEi- zu_P?Xgj26AGrx?-i>UB#xeqPm<^Sc5i{?3a{AVc`fxoLxns`fZKSroU`SL=Iu-@Xu zh1g)DMI1GB$stv;T%&tlTK{)(aswkca{tnraB;^Oz>7jJn`+s z&gV$7mPVaCuWN@4V#_7_<(Y;KbOWp~y5_F83Lul^Eo&xIPreA+-ki7Z!yPY~VvkC7 zH(Qdd-311lhEDF#=RV|na2km?FdMC=Gd%XVD)79=&0i&+W)Tq&aIqOpARqIg{FtHb z`eU_!En_9GLH!<@`;FndhIPintFUG5T0T&iZ$_qffad~lEMVN!d!~|_)Dz5>1mc@3 zG(?#7@%e%R0U!`V98y|;X|SJJT16L}yE z6~Hr^?hpphDwVZ30CUqF{6wi&!~;gh2IQ6r>Bkhr-*j&WcY;DxvL?c=qrTbv?2Y{` zbB1$6J@=YxD~Kv|EYrNKvSFhtzwmD)n`dQp!Z)GT3VFcDU~*jKvU!Tz8U*C&OC_so zZ#3*sON~dF>Y1SY@ByrhL)wayWz;1Jc-!|U1vDqmOLLEe{sdFq;J%8eRv+CwuiT@iTBrVLi>+%!fc^oro+t%bV{?MOQFC9D&WH^E; zNw&)J-Ypk^8B-a`tg1a4hrkI(yM@<;(-(-CrH z1JV>FXf`M-ChD#mXxV*VVO_F@Yo@w7C*gORBT3#T2E^j|$|ARXEQa0i9m~pcD9Rzm=`||>3(?f7H>H2<14^Xt zR1mA@ZtBJ3Z)60(oMMwlz;I#kRNDfhZ@<*OB#CoG5q9A<0r2;l_x?qjlU9-$$@q=+ zO-e`J8D9b|3xU%6r&>?hu@y?yEaI8l2S{d>Mc?f&-phCFM$H|?d58u)d)?6oIM54S zR>}U9^*S`{HSEH{=6e#T3V>BSK;>f_68ia&Q8#mMumgU5Tixbvxas8@!YdzY{VG%% z57f^;d6v|{ET4#=0`Y&2MC?+dml*A|5`{sv6RW-|t)MTiNkmP1>f&~8eFXg`fMZdA zlCu!EMPZa{CyMdv&gQO#zDfdiN(0rM3$0rATIe=aY@TGoho7vA6=tz2ECXdSXvbe_T zq{BHhrn2_4^<-!>nTsOrZr~mBwU8DzjAIPgK&8W-xB+(joh? z`;t@o>G35IkiWKsAwh&890}}{ph$79L^ZJQU8~Gc#?=LTxg9Kf2Gc-&|LV(yLEAy@ zj6_pgCVO7YXvj!K!3jEtNz4vRUCp0Q->5nBL_Qx&Wtvvs1f+IT>=}3E!b;p3$tYMH z%|xe>SUPFaFW0>*qhFHR9TX?>%*B1p^nVjD{b+ic4uvI(41OPADH)wGNlzwq z>)K2&^Aud$B5|-WmV&nrIAk3*914EvWrvnoGN=-D6jlt0gPqCG5=U{Yq-` zuk|$VSlL3SvL7j@!`DCvEQ+&;=%=Dz2V`_GH`Oi4_=ls$6&<<(r$st#DBhM;oE89e+dijzX{Mlt?n%4nN5 z)nDOIEQ6WD7*`bi{aZ$Tb;!xF`Gu3Xi6_5lxChNuip%^6;E)(iLs!gSm}DIPYqs1< zDIB>4RrqKHa&i^6?^lsZWsQi0SzxeF(3RaLZgd$1eqmo5WH~FjUG-GuSNr%o?rAk< z*0!_2hX2H%f8)=1@gw$bHg-IDKMu#8E_5wuPz=}bDik|_jkCI7l>rU@yUL1NKYNoKf*iv*h~=pe%FJ z;y_H;mKVZ21ys0*x8fcRhRx{p+nTP_$Df}fu{K^3MT<%uiVN~K<5K!ktQJ-x=;>se zNi9dDb(e@a&@-)F%?8oPJZ_`cp7EDmlHnO%xnr^1C%5nksnqU$km6pl0kjBdy}1W?r%%0sldBxuN20+RHwS+!mmg;E8jk`+v|5mFCO;XTNE5C zzARZ%OTmHOJged6^{u!g%Hd4}jtmRDl2MVR5b!Oo;F1E^A&wtyo(SxqE2!{YU&}CM zy2o5%(Dj*qD1EXDz;#D%!zxtEjZS(E?in`bbuTohzIw7P1m z4KU=S_oz!X;heYA9{Dq`kH3Q#Fu@MLmP(@#5=OrFh+`+_{3Q$bM=sU)DF@x#A&T;0 zoI-#S?vdzU_6+)~MqDX0Bf#cd(Jc=Edjap3qc`Pkxotq5=V4C;{F^9h?KP5Ca@Z|m zsrU+B=;tr;`E2KZi+LhACo%Qr5uyo}WUwxnLt+|o7>=F78v*oGae`6n{0jaHc8btX z>M1>S8`;hh6)Xtcim!rl(6*Z3xIe<{k4Tq>&dg|($j=O5hDK)QXm&}|n{mYYjrCa61+TmJUmx%Ju=yxkWlca% zpWsI=L-irlTaR*Swmr`4SOvh7B{c7d>nq)}>uTJR8ntS*$QfH6LiY9qUFF-%ReK%$ z8?g5(fY@2NO9_JX1Qqv}*?kC)k-_dCM^ozddM%0~36keM7~hK4@mK+4ULIe`o!)*t z?fN;skwegmT+r}9*g!+S(f0?3zN@Yf(V#obADQ=|t~QEO`LF1i0Q#W;_&8l8b|+8k z%QuNjMxN_+didk|F1j<~6;I3c1}IrUyPzR4Z~fH5KyIat(E~xuVQdJJhtTYp?oPyb z3-;qz%^Up2Ug+{@JInTid08f!dSyL;P-o^Ez1EaCYW5OM%2E~Q z{uXfI3CuajthA@x{08%GM|KGDQ?JY!`R*%;ljQ{Cmrzm(I#J$?c2!}4vXK>~Ld{|h zFrf`uBEZ+fH%yMGP;BM=bhNaP`Jq#90MyUm_6RA$`w7t%MZtzQN@I&Vnn)+GFB9h3 zswsX4wOb`3spk>RLmB{+ivM$)Z}1(z%y`FB+NtdQ0xlV%lV*%N4P%YZN0pqDteDsx z^|!M;QPO+pR(~|I=G)O>3}83b5n6>ZfGlcfg#zMQ%(XyTdx*;g=N=&{{`9n!^(i|s zWx8|Ot4_$b_O2b+9zx^ediHn5a})Tj5DJX91$f5Y!lFZxMr!4@PEJ3_#%WLx0pSdl zT=&b=ce|!p<8poX_Ygm<8#dFf@H?mF`-HOb*T^Z!HQCG>jej3B!dbI?Z7}?irhN zoiRAwVD`$9?1gCeco*wGgg)FzQ@u~104+#<%RC=sYg35vJ75cs6D^hs(%%SX*B1pu2q3z=FGc^x*SS7jc+;u z4CNTqsPt$CtknJUkBaQ{%k!t_j7BsUZfP986_qXl2VT79aoVDKrg1GRJ>Lk~_LcSE zo@BzQD~!-wmn2B+{wVug9g|VxD(E&v4xXP?j5T=B(BG)Sb~_=ggahs7?=*z}6jW8@ z&)|S^IHrgX4N~v9P();UF2}eAui#Y*V74RdBX;eEru2`SzCbGu7~)vdUJWDWNA@>b zb4wH#vFSugOzLs(4j210cp{V<`I4RSspZG>qcL!1pJzKZA-gTPf_LC|V$B9R%`r>} z#t|qNkp^dhvRYrML61nG!dC1%g6Jew!gOiUk!s@olu@q8`fXDDIPf+dVQk*Pvd3u&br7^OEnAoy@#16`UISIEK-9j! ze;{<>i0goZJ$u0wGX7j$lA)&0(e*#GmRo);gNPL-bYou1YQ)zH^A4-21r>M#*K7pk zKA{F?*n-$}NsyD3IP3jyU9Bvk;KcoUuE*rhYJ@u9gjN}nSAO3*=310e${*dINU)Ew z+gVyvH2;;7{_3U0VG*(Ir0F*Fz&JN(ln!k^<4%AsA<#eUyc3tMD~p#VX&H2V*^3?m zkivMuF_w()D}ehi-y}BjxgnhJf^@a`?&O4h(nQ{oId}S};kV0SohsKnqS=U%L+7_$ z=gHY?&5cCFpLT*di!WjCOWt!et6 zd_L15)Dc797Ohaby^=HaPy7gZ)X+NaPSO&0k$m7hlBGI=-$-RB>c5R7rfK>tCrf?t zqu4!Td|rG}#4yT#j|$1{bE z^*F`Csl&7&pP?qTtz8{{H`)&l1bY61_PNZ;hN>+@8?h%e*$9pww- zjWjrMc;JNCtm`sRVduR_k`|Lg++P6lEzF~VeY9UK4JN`<>bMO5);@iW{Ij2n7C6A% zWQKA?|14R5>C--Z8AO9Me;5!nhOMe}yf(>s;iTPhLa?|lgNnN%7d~K56}G1B`MPxl z%R(0F_2nF!wiJQjmJ&xF4oUn-3nd3f@PW_>B9^jV2mG**%-1MUP~nyP;u>|u`^U?C zp43T#>Gijd0MWrBHYM{-xpCZ@Rm%nA9pI>=uV5^{NK>{i<^Cg@;gp)mdp1 z9!ue{<)H;q%{<{KlU(-+ja4GZ@{6%Q)dDM@(>kz9GcL}xVOB_vni@96 ztGH4#hvS+Cbc-%W!b`~5{eBAXa4acUQw202VufT+F_%oZblo!(n(5qvP%Z4xATG$S zCS->Jq*4F|uU2@RGCtPg-GInG{E7=Hx1js9{K6G@z^!#)Z~b2-aviYBABS^t9lX$w z=EURR_x#>v6Gqo_mBscPUBb%}aH0jxqZnk5c|slKvPSNZICX(_b=(&XrWSmu3xvYo z`~r^zP{2MyMiaj;?IJQdiYv=x^ax>mLK7&by7Hl>x3!~r z{2Zz{BnM}@*~$mJP)b}m%0DR)h{%n>{wyygj~DDlvopL*Oc{C|9^$YEB` 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 z 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%0D07qR(dD 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∈qP~v#GBVn7~Dz?z0RR zFxGKhXIh92lyr)@5HD0o?+Nmd)eQT~@n?#U@S#en$f)eG4A#MK1IrFvp@Gxuqnpgk8 z+kp=Nz0wD;g)I`(q!LX2p4oyCI=+TF|3aK9x}QxZc6c0;N3vys)7g*Hp6G5h$G^%+ zWT}~~GQJYJhYLo>F)tj1u5rt^1a~+iGF$7Hi=k(8Xf~Gx4A$NJ=n+=$Vohp>HKpDo2z! zC=AS#BtY!aU+Q7kOt)Na2-2S^l=W zb&J|jv2wEeOk~Ny@hXY}{Cb_W2wq*;3Ze%c^pkQd>uw||;;4m^7kVg{D~IF$XYXie?@50s z`uoqcxuBXTL9>nD4x8HD&-qOKBYs!gF(O~DY<7+Q`Yk|rW8%Ib53xMmM_{_i^of9# zv;GF;6RN2Zjdl>|jPgMqXO>|PzhBRPIJmzgtOL~%w)?$(ueLi0zuk*#h5U^=`uHc- zeAr4zq5NUwg2>g?f?YiY;noB6teutXnqM;HCN@~Cd#icip||>TWINE?yuMaJ{0|TQ zFGd&NDspgmiut`(3KHLY|01t?Bl{Bul0p+FII(Vr6K2l)_CKp-P$sd^h7$@DGIkZN zeES;3)0hL6bFn3BFezA|u-I64EV8Qyn==R4wtQMePS^L9#U*bX!S;h`S4%amdL9{g zDnb$p)=Pb%a^t)n>5yc5PcD(fe zS-b$TaoCvkL?;DgERIq+98S!TS`q3PW~_Us#uhvGVhueCJ9en_9)yqvLAU1*B6(!f zOr%bXz)q-|Ip-}&1&G%J2gq^I?WR!-lTJyB(QJ9G5-1kx7W!_(miRpD_1`bs1qAJ0 zRY-ncguTD6GdYH-K9prvjBlx>_BJB@iJ7l%z|f^;oM;ect<2%M)WO{p=!~5bEBlNP zJ3kH)9EG(5(yi9wU7P-HVo!F+`0#%C<)H#*RvOja+vTn!AOg`{DRwi4ABx=^{v?gJ zw;ICy?@9wL%-ifZME{@qRLGES3)aw0ZizZ?nnAxUO|PZ^g(^fICMJDsO9{oVM_<$- z+^*~(%v+UQM>rA%aKDgld-H-4q}nAe6=Av9f%$u-8x|d_O*@+0w^0|*sh>)L5;p_` zQefIAI_idIKr71AKG4w;Pzw*{DGA<(c@BI)^ps86#PNX(a(IFXLh`!@AA_P#VqB-mP7fcy(PPp~i+ zw!fz_gxl>;xE=qICx*#r_cpdGQKTj&COjwrL!Ux^#X~rX(n7p4l+uI?Tjj0~%44;7 zEWSCURSK59@Y+>)*hjPlX);zYiL=E#ZE*c&&g9G7jn8taaBpDhjdz$8K+Q@X{^)aB ztud**Z%K;0i5}ZdsNB+~9SV1hfY(CoZ*TSa1ADZwzTYqK!0Y2mS4xLI{W`nfzqgN3 zHOM4XLr8G~myf7cPYj|1hdGZx;mvr61UfkYSc2Arj4*Z%-L?w08JCJ^w65SKC}~K|;d2Sv{F{ z%STB5HEd!8tnuV+-TL~ILAZ>=q79zZZ^g5<_W%muW#Pu{`0pprsQ`k$Wta%)f0OunduOWr3O-^NQ` z?=#k8VkG3|@O56$BdFw_vrW6egT7u{zHQ*H&eG>Yf8 zS7pMt`1SB6{Ou{vZ=Tz*`z4g(vcEYON8HtejdSn&x9LG@;$7P(@4z+8m!hY-L86r} zb$LDJkH1LHHuH33XwgW{0)#(GMO_fde%>zSE(Ez3_D(-1 zagtOCz-JR0FGf~kA{eB%p+oG5#N}pZI;@GrGqX)%$mcHNl}IMZv)#OGK%d{ZAMSObwBt7 z&J2_lVLpx9Euxf|Y{2)Ld<%RSfAjXPn+u#h>>C~m*cnHC3JKbydU@?8tj`CQ(oLue zD2Dy2&BN!lk!+R?x%(t`J7sSbw4xKCNngkOF^aD&kT4XqWIQ1gPDm;LZ$o7<3!F5f zgCj=euX2%$1GwzxIln7; z?*uPX!7-icv|rW0^f6+NymrD|{(u954I?7Uo8PvbN1^j@rwriIE-(L^6&`>BoxM#9 zD+@x0PV&@qQ-$7+jb?-yxLoK`LnjeT3* z6AWU{KTcqk0ZFhpYLEc%Uo+}CM5Z^p@qRT9CVk|gm&O|buRRn;?$52^$GJ5F!_d^UiqzYgu|*t8pRNO#7Ov?V6e&fvZD`A>JZYx`9?b1kv(4b1fgV@BhG6`=K3(Dzq3H;>S%S3s3KMqkp z#|%A0Y$az8&-F3MT>dd#=fu1Pq~2}I0>HusfG0;og+^r%yZ+Rn8e1(8<{at$Yw+d; z0Hlz&;y2s)aB=;l3%RZF<-NH%_@MuY>-GJu0>&J4y7g>07Ey1 zaO+YCH!}fz>fyNj#6VlctF7qRHhnT;r_Xr`0Y=PJtOYK&t=80;%+9_LfsW8wu_8Ekchq@ zp~kH}1}XD&akR>ryT1H(YU_IrZ*Qh^urNtaK+f6*vDvDaIX|sgfPQtVw{n>L$ieqq z@yy#g3K8h#FySocZZYg(Sp7bMC6VLf_SDpnqXyuHqm&}XdmHbNq&01WW|8#orsD%B zKNPD3g>K{u<9>Lpc-q*s_1UA{>JQb;+R7ZBgW_aThgfrisG}1yYm81e{-MQMlRe~Q z_eWH^D@)scbqEOsMQOuDlCp*Xs-m+U#fvs%1<)f7gfKDo9-Oc?YEIYNEEP&*U5i|R zR?_p#7Up+gqeDOf)g31Fvr_+t!Ki9)M)CR?Y&zuxDBroaFno()wFx*ko~K#*qv3~( z4<0+zuJ7Yvr*SbGmRFx#tGN3x`OPEWt!kK&r|dA>fZrkp(K7=JUCJRw zo2l5SVz6*djpzsX-@VU8@e#gI;Mb-!axBZ!eX44;w`ETtca;QVCcHoxdUVB<24Dya za8Zz4CW1eG@6IFeUL;QWJIzEVv>aQl3yI*T{n<4@TnJBV`%M%MCEgk5h-k_@oWd55 z5W`avZyD8yP@lxCkOIRq1_fbF4R*O#YTbtGOac7OBCwGO(o3q%q_N$l5>84|04EcN zqc&q}c(dwrK7E05)}@ADi&33yCHMu<{+Fzb#GS+6mGo)!@-pkk-?5S$vfL&;)162s z9xAmLzL`%ni6w0`MSS@Y%PQJ9qwB2As=(0cVvW*kOm=rxe1Yz{&LEhK&fc60mK-gk zH`iU_v3U<4;X3;wG?6%y4U>Aw5Z?*|8Xh9?cnG?~(OW70!p`{L^j{(qgEq!oJqlYZ zo#_;-{5*?Tf4_Y073T6*#bg8*IR*gvlK%mD7k_{n2#RGMRO3Dub$KAx0vx-aT~6O+ zv_dv8ElL%=S2CYkd$MrBQ9SutYWMJc+N;ajtE<4n{i_0|bWh+Bvz>gr`( zwZE~YzePaywM7<|bc$L4r0gs#+{+1Q#TYpvNGTF0qy}6h)OHumjhfH-Mr!5RD~F+f z0Y7T#fy~tPHuvr(>dVam}x@yR!esaOlC{6hDC9Mw0#fO|)sREOX&X5buP- zdf1aGDBDukcQAiJm+7N__Pi!RnBz)#ctj(SR%9~s3c;a`od+Gj|?nO6(iv& zn@EhNMD;8@*%VC&4SQT)#{Q~9eUaP51eq>%FMaK+6(Wjlz>L&JJufiqYK2b>L=QTi zENrSTPXPhC^|wDXJMaK?js^V>y=Xbnv19%~?>mej{dh;^hbJ0B{iil!69@~YBuyB= zeNDdCWc@`jL?S|1et<(^mijWJ*yqZbc=~L3?saWF)fo*W3K+$?zlDN#EcH zKAqZ!&UY4@Fv(QRZpjeW`3lU+*K_H}9zx|}8L^}I@=b*NnP|wrG1uZGgPLTkZw?OC zUBFX4u;ah#IM((-@QMb6){LKgkSX(O5(H;WoOWSUA9lf63#=8No{e~EgOxZwxWe2K z`Ss_q_8?_0pq=`)>u4cE<$>xM*(acAvvO7ne6@SpP}?F_R4-6 zS+;1U^SI#x@dBWBz?ORivX^qE2`t;^A@68pkI;SJu&y*t&-Z2(6(+9-vvyqtakds1 zYvqPwxLM%kR6HNGasx33Mj#uG1RK)a`>r)C)5`Z$wQ-oZG&y8+ZdLu_FtOR6Y4)Pa z#%d{E>9O_8Lk<&6jG1ts9N3`9w5^-12!6wdf0#Mpnyld%dk(>Ig5FTSz)jdm^8E^OYcPN@H}*DAY{icT z2eefU>`%U?)B0ZQ_C84ORucm>n{OkZ3{&@@N^rhA=it^=ue_-b1ZqLrzKZcwLS`bx zc4}||92ReI7#xIROkJmx3F@f1so;H8x(_UDH~aUN2qgxL)5VW&rsF~{@qGLO^O2w~ z9PH`NKSZ1?41*28EHzGKgO29@`Ka1nUcy0|WKh8M@%yj~MqS*@IjM`9Ndu|BOg&?o z@Sw!gtR|U;Z8Dvv@LYLu9bOQ2+2VSKNT;cFmOFlB4?aiMGDHE0G4qt{lY;N_u9Hhb zSr4;u2Q>~%)N_2ip3wBlg2n$0CM1{rKo^=XH4(`)=#?gYO6ZZBx6M~i=_Oykcxd&3 z&ax68$@QzhYslX-PdM`cW1;%%+(w19^!h{e(*OBW+Psxm-^=ArNMQ|#zl^_tJEsv4 zV@_6yNcSmL*$7?C9W#}xPxrx>R(i8fA6SkyOB#9!U=TdfU1-2`?S9(OI&o}~Hx;jR zRy11Xr6$ff^zI8Sce@>&=V#5gDc;!yAd**cW)-l+c|I;LyYyJc139}R8}|WgQu-|A z4`EXFf_}Umc+H!1v$_}eMbvcH*KGEmzu7k7+{SAw^yIg4M1OgIE;9($n0W&cXWeOF zxq^Nj09e%mz{-WnOIonZ2y@vK>m}9m2^!|2c>; zx`0lmWVV=h0h#G}qONN%rFF z-X>4sM|~h-+YOK2vlpjaqHJ;tV<9PJ7NS(D>L)06^GiC^xMM7|9A^gNdK@l)7_q`!c6D=*WR?m79HnDaM z{G1WIde)M=W>Eh{{Jwbp2Ir|$#bnJ5(?!4br@YyZ@a4s!)gWYL#;Q(?_xsmP0HtOF zYVNtIu(r8h8`4djFiDP1EZ23ogGekI$9BCvHsC zTe+0xv|5}n{#zsnw{zS?A4COVB3njPzKzO3AfD31!o&+1#?J#8#Lo-4-yTM*1hIy$ zX$z$~uc4uJ@_t}Ve{&z}i_nOFsE~wECzd7G@%#JV_w0mp-1Tg(sacGlUxB0zSSwPU zoF~Y2S~XH|xLv?j98#tmmLDYN<{~|oYDu;raz zUd6gM;82C%dW8cPV|5NxHpN(=^@SL01|JZ;w|c@*4GIf1c!QirLI3xEKB(3!oVgU{SZh49nj&PLZPVty5_3 zMIAe8$z0uZhS?_>6#yLZ|-bI#X&Uclp8Fd`3hgM)(fRTCkd8`M& zr#rm;Q^md!aNJzAp9WAk*L1EIKhR)jKFIUBD%v<&?^9R~ih!$S`x;WdQ`yjT0W_@5Ow*XGo!6`;IAm+2Eo{>#cKKRbxdDl6jc;<#Xj(_No6qoDcGJOf3E?*`JyP9mRUn<{a1x zmtlNqFESi5lZaXtnZI9H2(y)q^*H8KdWmXW+$lCsRNHflv=z_Ys=Jx>_!RFufWw~- zo1XV#cL)4El*ewOk#DA8Y3&rudj3JvN2oXm7fwlju`qM~VQGk8azmX}mthde?XhBT zNN|`nut76ssF9I9hv=V?IA@J(a|zT=L>GMdYft$V;PbS~>HG(Os@P+?c-K!=HYzH0QT*SftbD z7xM4r5+<9dJ7=k7U?zD>TTP!kvXGVQBvtdz#+S`s&TrCfCKM2GL3>Dq)8PV~UUzB< zwJ`M1f-=FvCqKKZu2b>mf!9So3b>ZF2hVxAOI8RG+=FSeOFXL4B!WQUdUR=kh_lM> zPs{jljt`V1m>gz*C-<|#&XKLT{BjpdzcBW7`f(#084k*-PZbV|z!et`>VQdNM;fnI zEa|JY@X_OM(hBtyd}GTW(n(`fF)R&rm5rlRS_BBDeL=q@%*t3A5KJkM*&AHNA)q1s zDT9I`AO~dSpFbAu%4aHYa6Dx>v`=slD@4kX7(&%&&z%B`LXt<(Q@3^;{{_m5!51~_tYmaNC%RyETO zHqwM26&tvYWcFL6$HsFyi;g2Ktr|-4RGUa~0u()4!y}3zmUW(1uSEbjVucqOU*T!S z62Se&Qa^#egXQqM7IB@2 z;=$lSKu|JOJdq#cpQ%+4aseFafwN<#ll|-r_&wihQ>z3z?p+oMkz0?YEvrU$xmJ{yW76~*f^uV(`Zjqjfmh`$Zxh-v_ z&JG)V3z|^Y1M-nftMsn}(ni69yH?2Fq|#@)w8;tgN2RgxNsI%aaA5+&<^wL;qKkCp z)&eK8U_)XcT-(XkcHnN(ry<`tb*G{d zER1at)Aq*R_bajm0iZbl8r>FqxFAlBT|*2*xqtz+T)?!aIzV@@Ry3npN-EZ&nwku8 zT70ZMh(h==T}qBT^=_AB5k2NBLeKnt`AuIOY!Vrt19hvOa({B~?EXND?s~)Cb(jL* zyYL-;3!7XEH4NnqGRW}-+Va)J*MZ6jgk7-q4k18GcMIQ_@?R6|IDaAZg+wlrZpZ9W zLkFix0X`{13~YO7UA#FwNFNG1oPXc!phvprg4HwIZHOc#Pu=~Zl}Q5d98w>HD#Xj( z*QGwp$@9(BMqa;1_}x>=)%}2i_mZUpAM{;DEnxHC|2-cofeT={mozC1KpUIy^Rpt# zum&0~;EYA|2ts@9TMB4#`vgrEeV&E-+%*~^y4*j{itMW%@TVNlRo$|cfnl(9B82N} zCkumQp++@1p_AN*oYgcpuX5SPMd%A=ZFli@8| zKI9!fHMJi+226w%VS%q9L8=fPEwe`&gF@L0eezIvVy!HWomc|PeVV1!G?rDA=bBud zqF~}sBwE^d4(+)3@wGr_Vd5ne6SueQora3lp{}H#_zQ`er4?S3y4lX|cuxz*_oURP zD&-DvPiRQ)HbXPPKS)yD)TdYm^O!IGb>T*@qQZxtfTyj zNgJsj(->GNr2{W38s1?C9!BxjxgULh82577h1M5?zK<6f6kROXIJEZK+Fu<7e03Gh zN`oKic7}X)voLvs2}@om*k-wwol-9>0FBOSLlpVlZgyiJhxJ4|??V@a7DNXfmzLpR zG+JpQMH(v@z$v@PNEZ%{h5M6OeOT4q)<-^lye^qMZUw^=~tpT33GgZHwiON)xUcA1(8ZLQQ31A41-{4q0r@aWHR@9x}J69$Hg5<3^KA znm#VxAO5D=`NDB5x`f|&<5y9E2!&36FB*q2aa>p}ZMCiy`b;QJGko1`v&=+clQiJF^e zaxE+tkt2$oZb3QB;0F<$V#0dLNMbq_5f1!a)xR#yn#WvH4*PS@Yp4#pOzY%;mzA}T z+Aljf5(k#AMGBnR^?9>g*KQdI+_YUm(Gzv9zBV%)S!R?EjC1ZOhfI@htIcXx5&47f zPI1yRBDBYlZ&Pp^=-Q@=ELt|kanS!AEL#!G-(&xRWUW)~B1e3zQ&RW$sw(Y1msBsj zN60+jQyw~{_Ro?2&7E>pz_W9C-SW~4y&siPX9EGU9zsryyk7^=fBgI__J?Yc3tIFK zAmRQF`c*sSgR#d=G6f3C7>u+^RwiblK6Vd0lu#{;)Rt*S-omwKJ4Qp)zo_#f5(M{O zZeNi*LrTo0f4ljnf%*&N)-1uy<;egsj~Y+tM@nibe-YOqXs2Y0M$I~oG^FbBY$Edb zY**@6@BtZgoIP~bkkfP0kpEacanB7^+jVGL`O&Sjn9Q=__rqqaM@Zu2WghFv`zf<_ zJE`d9y>pWro$u3%mD?u0ns_a)-YoLR+}YnWSR+hgLKsi^6eEQL-9M_~cUSbvAHw0-wPUH;pBCh_2U1YypQF)9jY->c*j zSOpci@WusFe3*dX++v3wlD2X!mF2!HK%b>mYkuQOw*b||{F}IHFM%_y@;|^FawEEM z?!7zS~*G> zsAmEbflqzZM0QJgXJIv6{NBhu*XOXu zReW`hoJBArdk&kvN=LkPFQ{zhc}9A9qN;J}na}7gXqd9jSB{Ys6W*HP) z@jL+ytsE7{1e=AoWZVLI5_KYEZ->RJX&ujiyRN`9L|2$SrXiAb*oa^J#$kv;t`^9M}s3tdp)b^QoQ!g&>NXtvBHY-;R6N9iYl(|lOn^D zPOPTKuxq>;oMbCu%dwWj7a}qOlgv!{15sML-Uo?S^DxX_>Z&@^qZ+@3oqpF6`C6^j z^$YrncYCxzA$SeFUPMwEVT1+0dmX2jode0_9Xa0DiCMxQqMP%9+;W4L5iV#J*al* zJx}#slDs<^d}__h_GLMlhbDdvtpA+i<$zdNRrNuKveo|~KKzg$IXOk0l= zDr!PJZ2b5rFW)LNvwDDTsjo=D0ue)Hi0RFUW2vXOWffpZs7-@>cDH_eUQx07gJnab zx0Kd(KhP%-r=`ay&GAg(1bH6oXA&f9%xMleWKGO%Cxc(l??qGe_u5vLL*aS2(_Ur~ z*xsarqy{M)K2XZ#`x8_&sZmRFKYYb6%_a{B3AOu#?&Vpy^A5ITW^NT6QZVf|*(YyW z<^^hB@oAQxoM=VpkM(ucOtQH$!}s`QktNBJ0fK^yQCC&KdI&|@|J5XGffN~W`aW{? zK+cc)*~b+{+2_j2hq;2tRyw6yc$b|tc=l5OeXx!v^QG73XLHrfol7qYPLfa>$nc|hZl;YsX?roJZF`5QRwieagB$TpS87V)Ay@pLG|u9mD&9f7%zU>iv7#(~A`ap2=6bbL=`A@5ce|VUJ89`r^vJk0Vm! z;}2nlL;TO^8RO+q*3|jpYMT>F`PhI&Djp3n#wOPCRhV@e^BT|nwrtQwvHA)AcAqn^ zav~%?CXNXv3u8$e!TqzMt~Nx8RFgE23lK&%-Ky4ik!)WQfLKz%KVlq08DGf6&8PkZ zH&2rt$G?Op!-iX9+ax7~Lt*K+1Z}BXA{`!7r_Z;#qQG&liID+E!^3eAjU%RN)Wpq* zsF;jRM4%!SP5rksNY)uXAWRRW*IgJoln8d(>2w6nU^Vde~c0q`@xa^rl7; z-xwp_pr%7+z_X9d)oJrE$x^(_I(1BNFg z$72@G<$O*Ls(E$RwfS!Nu<`A?VAqexrnCCP%3f_n+SNp!r1EjbuiC2AZtB~x+H5(M z_Cz5`;vT$+WVTvvCB1HB;WTCB@9H;EsW`tR`kDWxbi6b=+7y)ZB&+b@>N z*u7qTpc`dF7?=wtg6fL|nqVtDe(V=26}sTdUFx!M7x-pHd$ppnP*py4%Pc)GZ9MFyW^K;WaMu|vD(A|c6E6(00(u&T5*_*sQsU?DnJiGWp} zPG6lubdWaYVcKZ_gS(1eMN~v?!H6)!`CAKcqw!ltf=CU>_GsXIm@D+KqZ%{Ruulr2 z++;*$5~_?;za^3&j37>^Ao1p7*6DHNYBF-Yr6D(Rv!~dy3i1%rsVg;c=1o72YqP;c zG3qham~m|63{{zrDP1(3EXHVzz$~kq!2{w zB|r!?;(|q+O>oYnBG`+e5%zTIaeQg2VrT0Y>Vij3iYHb;v{zSRLf=jeHW=)d#q`)sWWp#5x#55Dlw z+<{Mr=uEP;`iS9*&nkO;`}?#E120jGz;8JEia~oS8x)vV8UXMcuQ0*|)!A8;kFVTm zM2{tD9CXj8_pY_sJXLI8+XQ@pZd*O$B~trtx<0`Z;U$fM7Ps~%>T8qZSb@xOt(m4q zMal0LzJP=Tb)rx+Anh5xzcFWNWf^qMlfaox6y_{;@F|)TN4px!Qgk%~wqm|TAWY;S zk-@1R*ia|g^}c|052Rb4zf4wRFi*mqPqeB^R{L`?Z4@XQ2Py|Aj31EpOqSWqoi6e9 zcaf4>$&+5>8tV1)E``i_%i6!GznRD&$b}V8L|yzRvXC7X8+q>Y2|@L@C~HFdqhc$sQEhg)S@S~PP$q4zS4+Xp)xxN~N^AK6R@AXO{VT`1 z35>`>GnW?;IrGQI5U4S>l<6;wO$pmp6Nw*%Qd&ZF%QY1tXhnU$G>=N#VgtNTlUc9( zrh-i{>d!hD)UV;+$8uvUSnh1q3?F?)i2pU377sZ5#sUrlmBL^w^W28ths9Z&-C_g!yu@F7bCBK!)_PCm zGRfPJd}f^r$bna|tRgRD`uP#r8S5I6I0#IMv|!SB9Y4CnFqvK{8e9v%L_kXamd42O!0Pp7-Zs-0v{Xgb#18y2` zChbpx_H8Fo4NWzR9n!*jNR)e-7T?#Qg|ASt*)9Q^%(G&mzu_bD`r4ZOeEFjFX za?dmTKEh~-xb@#IPqh@0ie>y_Em5NP^o3%ez+^`ewEqfFhS43DkidnmwWfAisEIh~ zDOf`M*NGj0P7L)A zbB*!Gm;p?Z9EjnXTy%jJF7hl8hrjla!b13?@!xg~ovJ2jINJsPSVQl^5Vm0@7fC4vIBW5 z7k=+U`cfrtrr4FmW_dgrX3hS4R%T0UDgdsEKdg>z2<%mkAd+N)K&dLQrh@ZYtyS9RO+1clXZqc(>H`KsF888 z1st^@t&Ars+D#E4F3HS9ZTh{C;J>zy+&e02y@O#yL87sl%fD7IM8h!8{P@C3KAL5; z<`eHOjgWPc7?A`QsdzP^LX=bgBi8O3A_s^>e zpi;Dx2~E+>4g>i?E4ny|lO*Kp`6B^Ke##p4VQ4^ob%cg`z=XecSbkTnGA_kz@519O~G)oZOxE!(Q~; zavUAq`qsgrwRt`Z3*5w~7VVPJh)^QEJZje{P*yVY>1C78 zi!|EvGzGyRpnp}6z`2TBC{a(hQn}#|r-1{(R2gjP8B<@hUKctOw7CV5f}||mk5|39 zuhm#(!1dgnm?1WznSmZGBi|_bvC1->h_#h(C*wi%tJ5-8XU&)p@k@c3wv<2~q_~kK z>7GCtg|fl22Uz;o9e$dFMZ;_}dB00VrAym<##EfUa5axh3U_4$yr_aVg9JC?l-q6I zXNrTKf6kI8N_s-Kp+L%71KnAAGcu+@AF^Yg(R4~zsljGAqx*wqMpd zpsOfGNEB1vJQK;@ZyH-9Ub6N*5k}R_wyq`@0fjo6=H}CL`kH5glfgGZmlld!$Tq6A zWytnM zi(55)P1QA_^DU>e6g53e{w_roUX+Zt)Z;%K%$M8;v<2G99L@L>KSVYzr!dGx#*93q zu=Xje9ogHrAN@2=FQu}9{v~8&9K5UeDkcqyAl$9rU$d$oV~nYA?-<_dg-}e!Ij+NR zAEYWUZ+!#(rLX>S_(L-4wZd2Q7iW%T&~Gm^6(t;1497X^qA*OQnOHJ#^4b9i^E}#8 zYkQ*I?i1Z3lVMW^$nT`3ulmr_BdDFf09J&YUBT_gG~0-yvBPBuSfji$Ya59CPbU`e zg3#h$`I``@SSq9&YjN*gbOQ6rYffZY)~6I@@8AX;Caqn(747tgG~vAt3(sn`EGtlS zj3MTAbjZ96pDTM=Pr=orbjbMVhIJ@I{k;e$&Es#bTNMc>+HVMaFG{nXU8zs=!AEpr zZ}ru^ji49cvjmo(KIZZd0O9HNZ^IN8yS2|VmgC5M=8T@@IJD}v-PR(=?8h)VDuh#D z*|EyEJa&+YF!ME#S}KZq&|i`wnqT&PljXkrQvWCA0ur#~3>dW?{`q=J|4EkrBzzQL z_M!hL@uFD99COkopRH+(t%`jTi#s-knPsL`yD887L8SQbn3=DbI0sBIeb@Ht0_pAD&!%H-X zpUotiyO5n=NVs8JSC;e;gq<<`$({ojNduD`}(<$kTXCyi}FV zIFZ45ZJdXGe_r!|%TqMTww2$ovziVefouJKx!^b^cHsm{DwaRi$tN$5w!DU?wMivs zq2vN^DXcW?7zs8$`UW}6>bdeYwn-eYYh8UD2LiPKtM$E5Wwq>N#nN@I)L68Z#ZH8B z_0OwnZmAZh+{aOD!U;+N-zzyba7)~1q{XMHB(xKGzjmQtOE%sN7KjGMeT&Nn0%mGkE1X&g#@X+;EC2ok64GgQ$k~G{_}Hu#YtL_ zX&$gZ;G5~d`$yXU0vo_IWI*|9zG-MaA->yUPZ}bx+wSJ`V~CS`ZS!;EWLklAk!^Y; zIaSv9K^-?z0a9U!;AMaKD-H;-+VOWaX(o&_wb|Nn87OntoF-0aIz~0!iS(=t;6J65!m2GIk*OQi7!VQx$=g_x-Mdx^dL|@oIxK!rO*gyzD zT7nF#XbN~HJ^sl@FqMm^-Tw#$7YuX-ku3|hi@d_g{+2rn3dOe-1V#9?u{jMud)u2z z^DRaJJae-d(-)hZE5SZyooUxIq z0fRaRd`t5J%_OvNKEk=R0#8)5XFR9@j1IGMYa*}owvV0?kFr$3d;d7d`6%gJP_Pva zZ~|LOj4alNTmNRYkEKqS(0=-cE*9S0`HS0_Vzykk zwHG~k)Y!`bS+YY2l|pfQHQJCd@m>FJU(W}&VVi7lqK)-y?G@M>waKzanmL1<4EWAe z($U4z?am<$kCiXWuTT)o{-IL^?Pgr@bz==)#*e9~WDW#1UL;lfOO9Rn&Oa}+IZvQ^ zH@2%?K|9O&*mWd|x^-|s1RC?;{3F!swLojg#~d|~S-%1H=Y~2YG`lg-xX#DVSFbo$ ziQtLb9@>=WCy?cv$@r~)s5}?#R)%Q+Y(^9*1OroBwN%)mAW@^)mi2nQptD~@+Fu8> zM7ET=c06XOh>hoH<2l#y@{MZEKup!t>KLP`=S=!I!o;mz0uQ)IgO~NCaU6U_fh5nU zty>31pK@3ak`*IC@ejEYi+NjyU;U`q@&MjZvSQ4_`|QwJDb61VR*V@)pQKuk55bLn z5av*oJO`LW_?b4Z>7~8fm}NAzX5zzubL=;d&h&Sd=-ecwQ|-FO7;Pe#%0t=VFWhF& zQA&R47^sDZxL}LqD|{l&}$5RivEu^p8Ha9QlEWFc~2>uQnP{-`H{f;E`{ zYi#$`onSVX5Jgl)(WVoBw3Pk5gvT<}9Dn^FapyzA8#XjNMg=i!)AEHkW99t$Yy@-M zT%3*YOG^X@!n6=t&-SWGUR&f~qcQ;8x0PiXG4uk>i;=?CM)t@pOckLqJ?0>WXW-E@ zFkUty_h5QBaGf5nkE@Fd?Qn_8ybw{zbrJ8W6lQ4nb-I(Lrw#YCEr_=|@w|lCkJDGH z;IFIq8MFYdYyl8s(WuS5`}#7!&m;%iv`?`#SAQ7Lu}psy_S_4jZT|Y&64xy6#Wuy( z6BAb9@}Q0TTXT_XO*Iu~R{=m>oF}BO^kWpWAU@}5J1M1{=94WEy#Uq+mQkx<>>2@A z#JIH~g!7#B;x;FtEdGwLaYKB)<#(z8)9(ay2LPH#C>_hZvpd8mj%41Ytfao6wPb59 zHgLm^uQ0N1VNTX(QjnIiWbH!G*&fZ4Rp$VdVaNOH``b29c_?bl6Gj%ZNBq>-g(Tj# z!1^!E>h%@o<@{zO-XjKrl`CavKJt-!DL2az?SB8{(eYV_S0s>Btl7=&7LbEho3c?^D8LQ^#nuL?<4s! zKWl&$yaK2~!jL0ZyVhU*7Bq;&FoA7&9YENHVF-c=^XnK zB}Mh;cK6~98Y5s+paC`C1giH9^TJZc>u18HGhAUu4++|RIUN6qU3Sy2T_>@DpFUOC zHR+bews^7Wrt=?P3<^hNdF?fJWqD^&qmu3X7-5|n6agerey*8IAgOX#C*cFPmHce5 z8j}%k--iBGItP)g=i`vp!l6-l1qefUgEg`3Hhr`)yNmHY zQS@}M2=k@`W^o2swFE$1|B4zaa@Zz-YgPJi%!~%C8)@)>oZMiCt0g}4m}0$`ROVnf z32lBo6L8fRLNO6VV=@(Ng?m&Mq<@$KyLb7dd(DmhNBByLVVrsvDln44AvIr?-t7}U z;A11eWxL2*2K|fD`F@`2o%D}G$l>2TgApD4lAZ`Cmj*11TtpimcIlEuQ#FtbjDweD z1h{BX=A5WU2%ZFc(e-K&Y-q@qGvR0SFYu)WxzlWNe(!bgAvlS2rY-ca>M3?2QDehG zC2cef)aqCmhfyz9IB|3qTq+_PF?5D4^r3V)P@wGb#5fL>;(1K=tD1krV!Nac({PF@ zM-el)oLZm^b;7Hh`L0SFjFuC^ttuidZ1nb{MvzB@+}ym@O9Tm&nh|H9!qo8O~WS8LJ|X}6!lxAI&S2T=4cFV{i3L6{Q=UCXG>K&Skr8jtq`&M#f~mpZN2PHF^nHuN>}Lh zT9^$C>YU$m*o70%tq$9A2_!J;1$bfAS?G~OFQfoD;tl&MODHV7SmQSMX38`A5(WsA zc;1KdkY72^D+#^)kr*j)tEG_XdtrtFY0jFcot=uQAj&t(7|=9CB5^h4T2&vehy`^Z zaQY?CkUo`0G77e-$#{I9?{#xmHo7j_dT8kG!qTiLOycJ|Kz_a|3D=uRB;b8}c&6vx zJIu%^wf&^X_|1afP-6{KA64#@ZXjJq>{`giwlB3+(MjyI9`~>RENfq(xHY}`@n7w= z#bS^~9?=<3g!MJfUrMgttItTh2RM{$#MejlphnHtkwWj3*z?BKUl}Rh`IkEpmgvgw z=H`P-%K3o%nFoY(>y8XCEZZOj3=7Egb|tZl+J}puxz93$a-i zv9i5QUs?Uwrc?=jJVO}D&=E$Qwa{{WCE!UrL5Y~xbtbuoigk1$BPf|l92!#iZCJW- zK^yAoM0wlZ4Iv4ziC|EWW9XN~{jK@DLdHNyPT{Y|t%X#NO9VryapsL;JuW6!vO^_Z z=prQo+E@B$$^;~r@h6k(bN;!a0U4>zDw3t6El1{5@b^LF|tzt2p%u z@S6C-_yB<^(t-ps1C)yVh(Tk*iY>LDvu9HWazX!`HVzfbLPYE&UMz#Uk=`MZDrOZ(~`Cu35Tsjeuj*>db|TbT ztv;WzrCJI9I^5&0PHgG0ZR<9hUmYb|^8ja=^CJfXdrGBXU84zM#3o0VnQ0&9J>`d{p!uK^^iCZ+tt?8}1z9PzJxK;<^ zbibEP-%W+ateHihp(^>dx;^Uf2CtL}>i)Y4Na~b&q zP3J`nBfeD%icICR(a9HF@qi;ge*A$1*A`ZTj^61TW0Mk9lEbR+DE*IC@<~2981Gnv z!7i8KKfkch>`L^qq;4Qa9^V_@fPRXN`Fs&4^h+M%vW0W30KHt|MpSaicLDHXc-I;X zYH!)$sVVt%JRCLG`;IBN%iMbGeFU4DgT${Km0j%VJltylxB5uYI@1g=lJ!kIf*e{u zqu9+icg>$4L*by@`xF_kcSEGp#dy!*1U@9_ESkR>2D%Bsfz9n{#9fP^<4OEOM8DS7 z-YV6(+lBNsegWgWHO!x9iE=nPX<{T1;)XoZ7TQ$NtUH69hx*>e#ZCb@3$@JPmi!$4 zXT9(yCnM>c6V2R4a;{Gb%a2GWmn=8^?4tMzxxV0HW=V6R4wCkt2u&heF?93QAJc2i z^v|Jmj|l-DUrw}l967X|FR<96Biv?Y2?D$r9Kx|?kv=M$>Jgh}3UG!)=H&Z% zB|*X>KxrEoTkMcDu@Q5E3d2eSo z-h*HZcHGG+g^;Pp2Ok{{F9J*)%OKwG@}dHNXEEe~EyGwjO;T|TCBru~T8~I(JqKtd zv}1`~bSGhHQ)ZFKES;v$JXEl{~piuRMf}D*BngIbhILCd-j6$OOiC z?GKR%C*Bp?oGqsmW~$tV=WnBvGyZ_}%-!$I-^TpW8IQ*EJe+3pw6@W2U49dWZ+5*lN5<6V?xGPt1O;P!eMx{#{?X7ViN`0XT6e!Lh#_&J z5QT+>twn@@5QPO!Q&UkX)Xnd=L6s8BUcyl{r%&P$MTU}db+7rV$FV!{bGCtKc=s7Kw&BIjQ z6{_4$Os$B>Y(K*~>km|#Knj^+PO(}4(Dy_NrSo#u9DRkN5pkgb<__s2RGP_pxbZ;$ z(JTrxs;+*r4P#`)oRFqFr5FmOOoI*AlMd^`P*#VNB5nVn$l zwMJ-XCa55J_qNx3z|u{P)_GiYG?cm~vA`K#ZBN|9aX^fNsZ;A1P6X=g6Yk*O|5vVr zy`2GIPIhRavJ-aA=gC#Q{3vD~GI*Z9pTri1@e-V6!fouGH&FxG+YXp_PN=6w72&;E zrw+?yiGGaZQ}%(<$QnMUeX_{PSw%S*Kp#NvqMPn!Y)@I}1$0-=3H@2jtwWSqcW8?^ z24$@Q=MPLWD7KjG7B(eGoEjfC;L-j4ZMKcT*>{qG6=i@R7js%fEOO@ni$=CUa@V0R zzKIY#ZIyOck$$SOe9w&VMYM6oE$*I$#Vm6|U0WbNx!jfKvud~A$u7DJhOe4Snp7t+ zGJtZ~?!L+bW8Wu4S4K4ih3`=v!Wf`sQ;9J{GOSbWW?Qx{pvsWeRT z0wDlLRM zCBJ9j>649e9OU{qC?z5^|Anuj}u1-U>*F?0!T>BL5B7 zf@ZgZ`lY1h&CI5=Nt1C7g*z)cWP)Ta>y6wdRhJA!YC3wW|Kat4f52U`CXxQr4s9*@ z?W?m|7n&@2p8{=7g1_`R1Izo!7I@J!wQnahKD6gA9^F|=YFQ&BWL)87>Y1S}xJ_Tz559O*_wW3jDveeqQ$MUlSwd5s>~aGVX2pIkHs}G6Ll_SQ9iq8Voa{(Jyw}QTx8)*atbuuK1kvMA0$s| zoXIMpoakR${UuPJzougd?QPUXEHmt@yMP#qpZ2wli{2{vu{g3ZTO`dSu~^h38$?3tLKc{}aAsDZT+_NLw4oE2n-I^vUAt8MM$?|e zt&4suwY?L~uZv!*&^}z5Q-S&HVAJmJ+&XaHA!uoe_Tp-bRCFe$i)PI{shK(ZhF?)nZX~wZz*3vr2bNN z!N`6X0?*kH)nvSN+jho|m^jZM4-?MIe&cZs*)Jv`7g)ijLy+^ta3|_E>PN~J27S2S zWu39Q_4=G=mA4B^>=m~w?(_{b(|>1EBSCdtHB&CXY@CKG6?*^`|4vp=8+HBb$3at% zWXZp2#Le_f^n;pHN6aKr?`O<1l6#X{!fy}doU`CgGmNPz)qw4YcsUz{osq#>ntm(N zvwjMDD0$~l990p1@RS#R+d_N%P<#r*ypWa^J)Z8=5rJ+~69ZIvH)W1?rVysa3jUJhgjCVPB46 z;Xi~Z^y)EVT4rri_Bube0<*5=O+mn?E5Zsc!&A|qmA%_M{L`;VRfnYW$I1_pN`pMc z=L{6B4>rygUSD36t{5GpWne^@G`mE;O%+8l<*HTFVSs?o%PrC%h-; zpXEp+pl7dWX-Amb{I~+UV7>cW96l!iGx55UDLP+z z#|M^k{Jk{9CJAN3$1`4A}Xc+I|=KQr}aIAoX4ZgA-pX`V)Hs>xD1sz>`GC$?$XrLQRyzd zx8SWmKOI^qbHf2>DN>Gwe=Yt}*nOIM&~Zi)oPj#Bg+<^LE9lBtq(m|5wEk1XD&cFH z5W!qGs;Toyspk{bFA0McF?G=ylvegR6c`7M#Znm|I&SA0pKEsRXajYmxI+ueF^%a2*s(mXd?y_Z!3T;L_IxsJ(n>23 z+lTh@-J737j*F;)J|DfTg#y2Cqq@cj)<~!X5tJ)A!K!11(;krY~Q(QTqyA0d&U_}v; z+R}v6fWP@%{2aP?TVDSX5A!wAL2V04%0i1jt7xhJ7BO|XQhHQ4HZIsiX<*&7){O=6 z$;N6vo~5wXt^Ojp)7J|DAt<<|R4p&Vfy&0Bo!&L!&s);QV2NtjEu_y2K28f9gjHI| zK<8CXqQn+oZql^>C~@xCmPpl3uD`i0jgTI5^*sz=Uw@BqIU0KNFGNA-@vDH1`8u*B z_%%|x=A^N>*;^m5k2@gI7&3ujNosvCIR_3YXGhpUDajKT7oIn=q0zK4z-l*9Shv7w z#2gOkznP+KYj98@;6gPct9IjJj*OfeqG_xCnVXgw6J1#MGv zt@w;x(i0aI4PH33@$#QO0R_BDlza4-8uaWN7#$|Z6opzpATH*n|22M2A@d4d)A_@4 z299avvJ56SsOlNWzVMW?D_{%adQT~v7Mfl!=DlD>FcNpEFYIe<7JpI4U|Sh;Lg-G6 zGYo@=Paev2D!fQ~BaTmzvvcKMZlZ*n+t_2BRiRJn3dG>w z6D-f6IClE--^DhQ8cETlx>9~C-yoIk5-N8X_r^!Ynyw}~g_l#R?ljeWOotj^N3=de zhiqKyj9=A8ym}RfMr{M^AiLOaUgg@C{WtaS-D0<`j7J-Sy)<(sH7 z{Sq4g^d;W4<lkOR`JS%$g(v~GOF;eg*gR5%K zly82%u02F&Ix6(Wc0{;M%^eh^M~_*tpEAQfFQs2(jD^k@*c_T&*kANAf2GqNL1I-r#V(2Y2 zL`CLdU~~Hf(I!h5zjqYY_0XJBmuBiMZj1{XN-M(Cf#NsNr*7pVlT@UOgjLUhTe~x^ zu|-Z$DJ?KM;#N>L=G(Px@DO?TV2#`W4-v-fMk4-^f&&4#(5bvc(;YgJDiMC0XE*Ex z|0H!0scC;qQ4ZXM&ArL9;Kghh#!yGxgv=xvk)VGk6tzGpU0EYd!ORa2I`r{}+jkwt z`|iOPz)erXb?_NyP-~(};V(oj57CxNPDi5rnrP`nelX~0NUry;&YJF$z9FJ0NAy7$ z{6-#guy4!`si%$oa~$mvS&$LK4{`*;ItKAXPGu9GOAi-(C5(DQ75g>noFH+KvKaxl=62;}UeF7&oUcRg8d*9ompjVw7VD<#5 zu+M5P@Xxk)zp}F47w@Ch!cF&o9e@!z$_YtO8oK@ipCH9*gQ}rBJ+WlcEHoB;`fXj~ zYfv%$nrVB$cfb9uw}WDCCO_6GS#h=~wA-<5JyI!mW*Ixp*WpD<=u0|QTSloV@jJ#G zmM{lkG~y8>lPRXlBata~H@0TuXd+BGg9+S)XSH=+_#2{^KrMm^)PYoRK1_mCu>>kj z)`}+Orh&#E;}QNBTH9X4@!u0p^52$j6}^=Iy22co?2BLcgH1GAG@=8LAz8 zsfiLs)v+RurBEuA1Naoax~9~$wW#rXI52k@nQ?O7D}*vU5?44G=M=;70+<9abTgrY zZ*FJ(ZK6_>bkAvC^pm|lE)~x>=7$;|$nL+qD*jXP2=hYxPenURd2Ns>Ve0iCA1fe% zL26R}rE+58LRH)F8E)KzYz1|4R3bWfX@FEalp7jq$W$bcJh?}A)FC5?UfSb0E&aR97yZbdBx%3 z1F4-y93P!9s-5IiSRc(@O2FToIs%OHDNKT`_^LebYSs4_g=lazfo^&&+A*`VWz+bit};|8AgMd zCk9&+!ZXx1xYOijMo5KKUZRO=#U)rkyqXv2KT-Ch;+VL2KtuB~yK=05pzNmPYaw(N z69IE2A$aYFK#FZg6A%c>pQy_D9oRM8k4XlDQ4m#i8#Q=R0F{OVdBc}NN#|dQUgiMV zdi9GsZH`4w2m1=jAA`^)^LGy{?UvNR?{|0k|{$+VB%n4knH$2X<6D`cHWG zZB_%Q8!6LF?Hboz>!B0vuAhG(jd3;nkPQm4M+^;^rZk2mqGO z#i1Zb(Uj=;2euu{FJnwlOpnxYmEnIQbh(3#>K6W;$71Oon*dn*CzdmP0S{p;8{SYhHd&~;8U^moqf9YDwNL(6AX)Z(uBxT@-zbF? z8FK7UabDz90<0q<=SbR+)OADeaCdGypMqP@F?5uYIGAuOeRG!!qQ?CNUxbP(b{nW0 zFVEs#R5pLThg-K6e63g^Yjt675>p1)H)gAjz0#yN|FW;DvjEFsZsWpkvLM#W+Vp{k zq?fA57T6CHG+E%{I8Yk#CgP^a8f#*w!jILPU<^WKyn{Nllf?WjfJ2Qu)#O0Clz|%| zZr}y&_ZyfPM_O)K*z!`9V}DwWP+joqqoBKk4T5Adr9xL%4Z=12YHIK~shKn)$fn@W zz(+@Ric=GD{mw0?+Gi#~<=_oH;&YCO^B>C4n=&vEKV!lxRck~IEulmziOs%WfBA*9 z3ata0{9(l{^FfW+H^rID??)^Cfno5?KK{u;OqbOf`NNJ_|vG+WK%?C{^1qDbb=i{6@!{?(dnhaOE?`_vM48JEUq)SAY*r zX5)>RC=Z4)56pwA8lR`teRhq=!c1l+DD2~F3AXlml3a@6f({IV)Ml}4%`$BQ?*v0m z8c;&q#vKNCbf>3eV7JOG^h$kbhUn`ErkqXLr(mrjFNAru;1hLSTl2u1s6(fnAlhNLTu30E*N8zy24Tsyr0(xj6R zdy=N>s*7Y>pmm=KAVn%Cq+|$Hza)-F&u<8bh)-9SrkM}ArpF(3u4wg2i_370y|9@5 zWsmf(_rx_}fNvZIE4+^$G+_`53+}AozgP5{F)?@rn8+@6B22I0(L#B*8{Ykl!hi>q zhMQ0x@vp{~8!;3xXxtPBGVm+pz7O{hE`B(Bn|l$ZA$DODY%yN*+eRI}GCHFPNVW|D z(6Rz)ss&LXle?C#>E^rgV7~z-0+IXtw>ZkFg&+KaV zk3eK>^ooY~yp>vw z^zH*sJid21OgJHvW-+W!4w1A^Hbx%$qhjju-LV znV7@nSM4+5qoUrl@Err=d%_ufk{8Qm%!SyOsA}O)%Qpr&0|u)t@dL{x16!4gSuMak zcUYY7;v@OCDIK}F-Z(NLR>0x)Lc`05RO1OL!$Vf%W2uxJQ0vw$szSJPSSqwn9ee$t z;yj4n*+kwrK~hmkS6ECx_yEl>Eut>WQ0p4Vs1zVEbo8OkGZ$R$|H5NdxkM@g`?0s<9q0F)q!G5H_|Tr4z)pb9u5fdqq*v& zY~28;2Gug5&M)Hc-_Pl(u32+*eLc}1G{=;Qy*$1oHH|fg#54}5sF=~){!$hlm2p~G zQ%kK{bfU|1LGZDkT}@TJk6?KYYOeTs)Qisw%B@8CE0@0@OYaFU^*Dw`!IkXnWjKc* z!}wqW+cXzKIlrFBjbJPXL(I-`oZ20#iF=y(vG5wfj^ZB$=!GFpZ$Z4Mott=El&P~f zf^mS%mSJnR#8w7BknTH7ca;0P&Vo5~-sI3>7Xg&Be3cnw+u8zqK~1HGs~}g+m!I8z zcb{q+hZTk$=ZWn&pd2@h{r!xeum$-gWjvnVxS#N`mTlf6~ zx+kjOHnnd}Io|~=Ne-q*OmtcGI>bnp!83FPZc{vdO8Lf&l(f9&2g$`SAG1o+k4H|c zjN3GejF0_o1!ZVU(pixYY-cRLe)Stl$QQ_}BTlp@d#z2QQiGMj4I9vUHFTjcQVPJ1 zC7!-|Ir^T3b*yfD0X8pikktLt_+C`w9hFFbY`8wg$zgK}_auvbr7*m1E7|8e$Zb;3 zQ0)sChgLAGws3w(`)>}fO=iu!h9SADzoS<^ZUb5>0n;cXGO3e_e72o#%}F0Oh_8xx zpjCq0kEa2N$N{8o?%qUy@Fkm7b@|0LfVF?T*q16317}n<6J3~gPNmQCmGh!;wj14w zfZ9(p*+7_XHRT!6fu~}6yG^osvQ&LBB8+TIKcI(+FBjGN3=#e5BTJ`C# z%aU9S+r!JU%!cL`c+M-_1MaUaT+;_QCg^J)(_tCDnYGFeq$6EnH!6}O>Yf#Uc7cF^ ze0~;EUZ-nFO9}x$KtK?{x}8DKH!Q1Nb#<3)mHzv%*5?tRBcP)vpaEW0RP=<<(D-bm-1s)wRB??X7sh(d!{7JavcF`TJq{B3 zuxZPKi01A(s;J$X;|8c#@^wZ!+&sH5Y>QS?x8Gbhbu?Rf6#4t;FXap_4(Yb`K!FoI zx1^bJbvvHRyNHj+UgArn-l;_Q#LUuMfwuF8TN4ZWOi?3FPWPz`+-<4xQ(S{4^Yvhr zg;C&myI4!j12=9ho{Bsi~cVDzSkc6H6P{Q@_=8^|U zWUpZ`n|l#1ZK6!>I;+xtBL80QW_P>>edX+CmbHzHd34$vVhX?>*8>ex8~EM`;Fk2= zZw$5(n(KR@yu=)ggn=~?_~(1b;+#FyleQt;(w^R$KXLx5^|Pqd1j<tg^{Vl&tT;I8kYOkq7KTM?j0>p`YDM=84ZLR8vBeN^Le1UF#~a8fV_ zaMArjZ^QZ^;!}Y)vlY8heYQhn; zwqc!|yHQ;|BQ&cp{jdhH3a?SJYy#odcR<1AIXnr%)cPRVYa$+(`#|~fj{c9TcaLYX z|KrCiA?G6J(_QXzOG28n>Fn+nlH8QD-I23#JKHA54p^lSvf?h1jL6yM)K)pHau~yg z#bjm}n{BrJE}zHm`#b!_b?tp!*Zci?zuvFc^Z9(ecMUS#8VB@VA@Mu`-_8baLe!#6 z@2aoN9RSR1IQ^1FolZ{Q&NT$OXJ<+z=Nvg1s1D47d3^mtvDAKpaue2iBs)yW^mdDC=!B0znyh0w>Z}khy9R{u;*l%%PgHfZjhrFF@C{bKeIXjKFXS zF>GftCYAC^2Tjz%H!t1BexN=M`a(Bi^Bjv91I>+p5Q-RF&f^buh;bU8m+FZkWLupA zY90j;@ahxh?Z-J$ud3=fz#HO{XkafWChZySkSqAMEn zt^4Vi)wOoC*-tCoq%op0aKB6VjJifW|QT4AA<}g7;eEH+R`42 zH_=|OJ3sU#F<;ZZe3T&+733yFz%G%v>iF^;RRcc_KZ4Dc?MOKhoU@RBB)P+n4J#F+ z>Oep9k0KNC^iyt~?Dl(?X%;ki`zH9uM{Z)0&nMraFK(wQajO>EM6$;R;@cbsEoCBF&!iu(`$-EcSIf8LA) zkLz}ukSm5-oZIfkdcPd9$1^@dG*SawT+17=y?qTkrj_sqQgxhx<4 z2_O`Lz^&umb~+7Q@j8SIt+m^XhjB{HaYc~VHNO$vJ-t$!}S|ub(8~mAP!H@=_nMR><}7U zg{VNVGe*RV+lWg}xJK@sm}%6d+E+6audlwV9=a0ikEh;?|Gl zYp(>}qdKa&-N9E%ezhyB&_;D?Ayd`P+4#!+p#aT}-}xHd`880Pk;uNtRd2b2Z&AcI zo^Jk@pOErZQue+9h($KjkPPaicKKSac7CwWX6%l%zpkX(VryIm(y~_; zUv+XAdY7YxvQF$ZYqkV`gyy8I)cYiSCqVy5)6;@HrMk+hw)R`%QTy5TNPSzX62H%k z+@O1AJy$2+-ieJ=$(}&;wSLkLRa{XuxLRokpMsmchTmr=M)F;s2=tNc?MS{ml4t7t zjos+Q)|*00JdLoIu18J4%__qASIPz5?1xsjrxa?!nawk(Kc?b+At}ua-k+1rbd{;b z2%>Lu@KjyO(})iF2D_UsPd`M^q?)6qhKWxjY+QEl1pY64s;!7I)yx8(KI|1vQ<%6gwb~Uf zIwA{sD45lWuy4K1ZxPI%og`4b4}#*SJN^~Yf;+ykuv%NqGff6z6)P^Lqk^j(!x>~a z_R0tB1RR2NY^F?5A2BT0tq^{m-SF-y!`)N&c25QH^IjNF!6BRhyB7WrZau@nV#U5$ zH}8)r_~F~42Eo$(4-AJZY~3m3O6ml=@&q5raEQl*@xSZw3m6UoY}F}bHekCStf@VG zlk7$lei0)$p8T5s zeAPp5*iHWq|5-Uoev%Sy-^g;bL6v>L{(@Jjf6!$%6eSqNC$i{1R_W>|D?}Ep3kJ6S zYZmRvPBe>-602nRLqmO2000H-P*P}tZGVkk6YL7q`E5#{Z^dF7JyBs9)9XA>&C z%`*sFiEESPG=`u@LQa(F|9i;>d5_hKx{y~Me51{N1^&a6uSaJ^`zqqY1Pt5b-P{Ku zG?0cJ`mKq*7ZOJ*QpaYCb&mTHO25_;cpWPYXJg;`y3B@xA(x|&6*K>(n@&{|PB zvd?jc`se2r1}CJV5J zsdGI$$A@nn2}MfU2F@Z|+2>9MsM9Ad<_*95+FG<2K;1l*%lXXsHnnw8V0N4oc|2)6 zm_GJ2gxIe#Y;sv78=Bw5LgaWUcK;UCkO9ZFT^f2V%0Tg{B+)q~uCs@dGUO@f(z0XV z^AB&s@u7m-9Y4j$m?wWk-zDU7MhwzWeWz`qfXZr#(h2 zkuCv*A$(3OKRQ>bldm6HW*(X<`aHJZB;O!>{xE7i_by_Emt&fJb1*=MKG?VTTKz|4 zri&$hV{~hYde9>Aq5@qi@UUAk?gXjxjAUU7mSD6FIT&S<|6ccB#%jYunZcb$UcUX( z@+5NCCg?HqfFNb-q?f3P4W^fdrft|bHwTp2lW6dv)GU$ zrbSiCM$II)c=iIsDNoWClE$}Bzj778b@!PGpql_+^qRn>*j6l(;x~gL^gCpx23|A% z`@;T!h%tkYG2ZkD?(pxv`IKyJ+2_%PY8eCq8uv9hKWrM_p?ikd%xW2rk6``C6t%s( zKUk)hiZkQECyW#z`Ozge?3cqU7O(O(Nt8f|#^OF(`RIE96jAj- z+-1W?g&LX`c!ojcjPp<-6xPIo?5|Yov&1ZIKW_7)9!5d)vjEQq7`R4gc;_F2p`#1Z z@~)M1qedOCs24F}pm9Xc_TuvoNO;KTHez}#)8ziKTw$hH`MII;c!$C7b-Bv5^JbCP zAOe|_h)JOhFH0rzry8yy*WFtfMvTl!vGosP6c#n4yN^z6KWq81ZB7BPI?Vl?1XVsm z+U5-aS*J37d~^wv9+|-J1<&t?iF&sb0@dGn_L;7laE*PihgBLf6RO+d`77*;#eRn& z+vDvpqeu@;O6`=_oakRhJY}Q~FEGc8pMqwET@@H6C|K0r2jWK10_oZ0sj%v#NiJq- z=k%c7!%HXUNbwZ-Fq9qdwSIAuUr^_87@rHF zhAK8WKCt?FES&miJ=}XpjFCKlZ$oLd9-nAlxA;csfmL#iTs%-IX7wjx8D# z)#n-E8lfOg6aAjpS3U254B$eiG{MPxM>@|94U{`9Q89}@5-;fx3@6Uzl~*k#<={vJ z^{6Zz9U_Q0oN{Iy+?xnJS#g=5w*n;!qe{Y%EM99}+oH$4ac@j&Pb^pg(0&Mq=! z#S%j6Ar4Q0ibyF|UB=ZkxeJG(Z%65b2mjP!QY z1$2v$LH}I=z#0$$>{h6HK(LJ6)h-fyXR)y?IjYg+Zz5J?k_Mm`#Cp%~MJ@?A0HKd9 z#@lF7r3jwncA=fDFx^l!I-|r$gtQaJ2t-X>`AvmSG9_m-nc*jdN(7wgrfoUs)1{cOUvlt;IF#O|3@QG%&8WC^@a#x0dWHT$2al zfIL8vT5K$wwUhv27gV2FVojaoNj|0AqQ5(hGytRy`}XHR+&v;oUA1qE4<6mQPC=uS zZRV%TWfvRLl*0p5Q(Akp$uWqFrrFS|vx*G(Xy^MG1T0eIMHjZR5#N6b8{ZksYLj;|%(^2t=(0|LvhgTDZJ|=}NGv|dUrxkzIl*F@?j&}Bu(L~Cm5hp-RPYtgH zp53FnE=5?PrjPHf^;Zs?=rc_&PS_)Jr^PGwJ@)vf{3@FqC>@rV6EV73?MGEXR$IBE z*zH@3_4lF1IT|Bi<8Tg??ZUxxzXuse*Mzp*J2$6`+*QuvSQPgbh!WM z?Yzh$<>nU0sne_joDICP~IN{AWB*DE=O z&r?259uirihv!aDdpB|wtoov)jb!(CH?`4LZLhYSWB(%jAj99BMxf?PMbtGzNW2BvE04cgyz zYMOfAHttt22OomS@we|OjSjx$U|Tv~oa?;gl*zL;2fAsxEED+b!rsj1(5cevWk9sj@KBL3m)%&CDims?sd*z@lm zzXFpU@jREOZGk&!!LY0dfmQQGwp7K0>gTDgVOR%(L+b}!&_J~prWcfKj~feH9<^AL z-vv9Elvgkw@MRmb;!l#J-l2Y&Z)83+GF6uc^v019W!w~%ny!hsZ<7nJc<17;3GyK! zO)hmfG7?U@;AXb1RswaqP(KU9hBpnSaM zsaGcH%Z9sS=I7Tfo?CGpKS}vCmU$C#IJSE7J9w=w^2$NKCPX&-=E*=gdgA3$7hZ4b zt){8Uk1oDqvqLPKwi2s4=))5I<@c2I4_hM>mMJ80%H+q^b9onhj@y{lu5hIC@wkDE67wU}d4yisZYVFZWPPcyIxBz?M-r#)GEz#b??eM@; zkl`|MzVJg3ZmR-zq~-U_fYI=`l>318v6-62{&)R4Qz_#qC}i0z@NtaPmNhM5)M8!7 zX5zxo6W+7dH=R9Vq3OCyp{G6jt9;$wV2+L(gtd>_ z-?RG)m@vpu;5_XHp7nA*e9YJRH$V^H4`}j0zEAbJnF-@KlJ#FlgB z^%f3+UoHMAxMJ(Zj_%sr6Pi9+Ws(Q*ZF!bw_tC<70J;D!H_>eqUJ-Owa4q#e+g|-q zpkAU>^c2)$3vHx#mbxxO095^)Cz;wW7y3hYKJ;{8VT`=v|9J|~srjcHN~f+-1=l>N zN`j|ii5Vqc%m3d&c;Y310>dtJTzMIERdBKFI=ojrv~U#NZ*<#ftr=UKt6TY&MYz7dgayf`-5Zu>k{p$YU?>5UKA_|q9c9<5}7Xb(^uWbpT0%k zbN>ng2Jrvh`ft&p$os0WB|N|*G6On$ymI{y|KAoaIlZL}aEffF(WdIChdy6Rf|rBm zOEx4piQQ(&;2(LwHQBBORhZvY0DzHQa%#)W8}eI)kq;0w`fa{C0V4qHq~$ig>l+SV zOJ*I~N*<}bNzE`F{rT4T-lea{YG1u^hUrRL_B&|JsR0EN{ZyPfAcr{uoYSIu9p3bA z{XYEbUI3vO0b$?y=12v`i0roNuL~lKqP`L0h;G1~e^9s?3BIl~r}TfUWU?H3ejcz^ z2`{(!by=Tc=>OwKxH=>@fUA}vUjq;#< zL!AK!%M0`>DYrvPjCBXMS-4?<9CC@%u5%0!a_j`YP1ujD)0hzYdpFAYye=d>TA@fs zfoUoG+TA3jcEsi3^>^+73e>p=ZY7 z{o9bHq&pIwXxl!ZXqRyg%l`NNHB`3}nZ$2=Vou$vN17`Dh$KYE6QL^!z)uw>mRyPP z3uE$0{HELFoX!hQ`Nk<8L^1IZ*5^u7*RKX(Hhprs21>Sq-=asoMqf3?G> z#EOZpGdUb>)VrO1jfvkAidEZ6S^R}vgTMLM$bq_T8qVbl>_2_Yl7eDfUT@!1O>E z$(wYlQsfdaiM2CjY#H0xlK-`N5yA}M*K>M2Hd}ykocar^Mv4!ax}R6s*8Czgyd$rE zhP~$A0?EtMgn08)&y{b8k&;2TRde|zr9B3-p>>(a)mI_ z=$lN52px?SUEz<;Glq)|=TZ>lln`(yy7OEL$|{`M_tz$@<{+9t21eDQ(J>N4&|-+C z9U*hRRCL)NrU{f&M6~i41<6i7f2PA`(u^Qi%2$B^**oCbW6p-&(<^(~B_D-uoMMbm z@TEbE-!HsCe*1@J+XfBFrQc{c*sU0+BK^(o}r@`B*=3rAUoa* zQ=WR=C0P)R)OCm7GteB|b>S-PP3buePU2ZnaN7c)r))O*bm2%jcZvXk%D`lJPfjAo ziuB_XsVIb#HtuKkPnlkMboqGUsd4z#4((eAr|noDC&>;^uB`|9t(z#5u)W6JZEg`_ zahZ-z3MxB`Gm@H@2XwJ>_Qs4_Y>-gA?)bpgTx=pgtIn4?0N`~p434DDJwef*R+nonMC%WqcosFsSJSvgv00-xKy!{}=o zc(p|gFRjDXWjz0BU37DF8u|irEHZiwYLt*zPp1o@9!$Q{Fpw^^lsvn4tKT_5)x|b2 z%~*KRBwy-er4Iq^l$7u2O;sTzvh-&de+H(}>6fmtDq4cS?tqM~u}Z9w{l&p*z&>fG zdr1=_gG*KbloUBIf;oWt_GQ&#=%c6uH^68Xr96ny02MXMFoKD&O;f+p797ckRM)w? zaEQ$|c_>k-z|ZR`Lsau6RvQP-kKV5G8IW=oee8}%M{v2i zfzeG6D0Tl98d;5e0R3vv#JKLZ0IvxU>+%+j=9tz(UyPWCt}apf&B;Lw^oF~NUF6W3;rRbo_3=c|#nQW~EUTQ6vO#1^jCebkNCrX0yP9oR~cndlcI zj%@k1!dHuhN=FV*n=s%=}@z_Rmv9i*ZjIg@G$B4fN{)g%NoChmT=D>as4R_KUB6!;KyGbdi~|#7RXqa1&+@ zC;;1KbP7{U(_IGxlmQVof1w9UE6fD&Uu`8~g)~!vBgNgJe3Ej8X6OUPRURMtBnf~O zorW#W%mKSm&yGC<8O7bd7uIK+ufCq3MV{#_+bkq#Zp1vwsd}N~{Ln6i{+T9NVWqIS zom!}j0f?r}9Td8e&}g^b)}&5eOZsqtM4MVkNR1z5uIHAhv%CT_;>|{DLxNF|)bHF& z>IAsQ=kdx00+b{unPo4!LO6{u_y}pT^9&3eY=JW%F-Pv?pKKuz#J4-kV5eLP0+J35 zcwQP3+d^963_{~aQQyH)G+JzUkzz-*Xp-j65G5^&=m9nWnWJ_55Poi5y#E0QR&5Wm z<$M2nO+epTNr@}bgcqIY7B%EFYu`yfiMU|18a3nv*2mF$_9Z@KTbW{EzE_^S#-v?3 zxz_f()AI%3Rb6~-q{Nof$}1Q+>}pavfaG)-EcZE8Z){!ES4(ruo1s=V-$3lE0XS+D zzgK0u%zV13EZX2e@hduoR*xfDwKgl5kEJg$SerC&!|1EQkeM>15O64N~-XqV5eFOT6 zEdH7J^gLfsd2&HzxVUJ@EjFTyINo^M4v2Y7v6+&iYM1z3m5K{0LEEXn4pt8G4>98J z%$Mc-UY}#rMB#s6i8~eHVR>9P_2zn~7sOrTF zkAMG2_zp)K^;5BSV3I9JkC*q!U_pO+UYpf%hAc<_NVr*KFh#3nB+SZ>s;Uzf--e8= z`ZiIJjf51Jo4#$Q%U_5tTEQovzdKsx#BRo>O8J+pN03XT1vPXZH zAT`|g$k%vqAZygqzQbqS_!7cKo@7~4WH$^*H14$4+a%~!2(~Z&S8GMmBsCY8Z*w!> z=8bmF8MckTcD^Hc62T&GdRZMPwjSeGDOsA z4Gl}&QWgP!OC(42UPc6#{YLfHAizaO16gyiJ#t!=?;Q$e6VzhvV5bhwa}NETibmv= zs4z&0NVbi1!VV@)Fx>Mv=Gl7rxADqfz;6$owLvmt$q^L4)_*ZCsJ5MH$422k%aV7n z1!D`bBaqPvX6V*ylmPR0Dcn3i$N_6YX}tFermJ^u5-#};2k5pqIO^S5`w=+$-PJA_ zmb&^iF_x6FEyTa3jD4VdfAB3bQykZl6n4F7;2tldV~1I*H?ZJz*SK} zZ(Eob&isK#=LTafMyahKB8Fm-u&(;L)(Wf=H)_lLRvmq zQt+Px0RG$ucCO#K68UpP-=<4-ob!Pi8g6GaPQ)F`2ZTF^MG#Qhj?M=*O{D7%>&~eG z8(Ykr;qSc~Il$ZW!q)}&B}dP2F`vZ%J=SabRSxr2xan}K!AXc5C}NRc(c%bcF&t(% z`is9yM3xkFy(qDx7y@-w7#pV8iIyj`NBVu z8)n1N8IBmqn1NPPV0R!$s#MjtLtCT6$A-r=-MLw}Aou1P>qCoM<6?Y7e>W}Eu_$=i zq80y**0xaMi5u%*&lSf6U+-50cI3r{&ncBh<2i1&su;EzYIm8~GNc3V$EZ)K zbplfi`HQbVnlu?7DDo(+!ZklMpUMDO!y7#NG#-SxZS$n5)ee^0uN>NY6Q~{jA-|S( z2&1cv*9xtrJc7T;t5j#fYL1cDx@xS!h`02Hi$f*xn$v;znLbX?`>wcOa|a{rO293# zxdgR(?z~axYBAw;QfPR&;bIu66O@Eux_2A!Fw-BksQW<|mUW?-q^JupQ|9GAAWxZL zw_O`w`#oZdk3vt3QcUvm-bsBwm66m0jVL>@b#Ax%aFaA?IgWPdLIMQn_#kJBJwfA# zkGm8EC&_f&5u8~^{|mA@8K?Xdx0sg& zNs-*44+s;LsJzOz-y_qSeYJye8}ii2){-NPm3U9A*_IJ+%CcmJ*jgf{W6J(mhdBpu zv4Wb`l5a#Sv8EPy369uNGHqcAMCUV?3MmkU_o&6T#ZYr#KAACFyIi3RvZU!V5M1^u z=)B-C>Kb3W8VxVde3kzt3c7GKG=H<>HB3CNOk&FvsM~@Lk(BFm&Fk_20orJ{7e^W4 zT6E5zuzR~B=2m7K0-XN&f{)hAt&#t*V`ES2A1G8$0s^j(!^ z9VXw$Heji0X2WLXRq(H6F}41K$+kL3Jas+se}A}?(JsVO9>a%hb$R2TQCHXR`OqiV zE@{g(sJTL4kgN<{YdswW`1%xlJ->L2mR@i%Tc+%{}pIpt#- zpsaVRrn>s~o<9{UF1}SCy5s6buI(G4fDngkLbuNl{oyV3 z7iXPt7u3>E%oHtF{CrZsZZrNZ^AP(N|L?6Ya<*Y z4JWenvA0>rCek$3co863UKql^Ylx}Gd#-jHCdv_Q6*Q}@`#jR! zm+WL>$dxCkoHDe{Kk&cEDL0P5yLtPw10)s$4&^VgeBD)0i;>Tn1st8Rl`-?iG{?jZ zjZ)?#j*k1hL&T!i@c?m&?VKT`|3fg--Th14+$w%>&aObWW&LorZx|Ia@gIfgy0zrf zr!I(X&B8>!0GiE=C>`hQ>8jd&+M%7Bt=VL^qfEOREun<>d8Wbct(8P-ytuXUlSxch zr_bEDO@8XIKt9&*sEHGbQ+w+j2;e9JU)S9x;^_1 zaYU>0+>7JPEkb~3dW-PAFg2e`t7BdO^CAHmgI;M)X zCL`$UWy>q-HojS)iEO(Knd;@ascs`#Y?^OOMD>Xn-Kl!9;I+96@AWqVEaXlRpaU=3gS7w5cKZ#utu+mlsb`t(W|nazj2)tKJ|V5z#I}9q^4=MW{C4TJzlpB z<0q2_=NkKAm^3I4rv1!b-)JYHUcsH8Dz*c$yc=vAOf!*ks~1YegCnYwUC;?b*8Fi6 zORYgvifk6-y*z@Em<9x9ENDj*A{9-Aw6AuR?ow4q+3NGNnA{QtrBYW;g)}G)c%5uP)#G=8=`~ z4PlQ1OwFRq-7H`sJbDmV;81ASABI8ED3U8?Y-R^z8(t>Le}rJ#_i!+$UM*iyfC>2* z5Ak;R!Q{#q6hfi?&$_)usS3lug#up@?B#D1ic_~Vh58;CWUoGDV1fU!M&EAJeNH?4 z5eu;J-`hl!&%C4!a^+tvyVkHJk@I=fdLfgrI@=@j{>Xl%R2PO~4bZ)oj6=Ni|4rLI z;nDE3A|Ewiy)4YLCg3^tZoT8=N;9T4KF9&m?GeZto@G%r=k%q*i zM>*h8KT^?j4Re3p$fQb2&mM}4b+#0SNyQGJlYd;G$X{NP`JV@G@*#2?j_f(R8YzK8 zDoX!=QoUlsMdfjSB{p~FSxPm~*iV_qT(S6^g!~FKj0scwM_>yqh+|wqq2Us?k$igv z8&yu_pBJVq7UdGSzW<3%wS7(5Nf@`-EPupdMkFIcuZpyebIGpiJPz;$Gy$j8A{Q{f z=+>5}Gw8o4iuDOUPBt@4f{VGW0tBa;C#ZkSl8P7kHc8dXr~Rn>Gup)qDPZx|%$k&q zy)h2o__&|!g$$QpsCx2Y`OnB#Oiq5>RJ|nUi^#43wjA>t!Tz2~iX7te{pDc zoj|IJci$|5)VZ9`H63&Y-c{IAFCH8%)?%_HQ|Q>IaaC9`6dWMmJg4S7D-AyDj)bx2 z4lD~aCfE)uaTU9WbFy0q%Dr`Nd}Y66|kKtFOhH3NNyGTvjehb&0j^B%c1dofaF=Y~*>FUF`J*iU}*y8gDp+fyFu zOTi}e(WNOol5XDrvKx|A7+N|jXR0lKe6^Xw^vh?onMBc2!5(35I&hn zc9w#;iVZ&Ir_e@Vbxj*PS^f}s5wU($19#uaz9ZA!jjy@G0U9c=mdk{k&E)5H^aWu$ z@g2*$BikuahUqUUKX-&*8>6FO!v4DhqdbvgQ=LrO@~A~9SgeI`8j}?mv^4r!`7H{n zQKicnG3+iZ^GL+J5Kh07M+~hJKxKWE&xV5i_r}jjU#0|M)NC_uRzLVID)%*R3+~$V zTT6cC_mS?J<*}w=K4-MjYt;GEw$NmUKJJkjk*MJA(jm-^Q;il!#oIS>zQJ|KeRO{8 z_^eelFSc%j*7=#m*rfT!vEw`JX(nPa_4^0vr4#BoQ-!+c+!g(#-^__`#y!Y2{6>@_ zdm%KMJ@G_$Z9ySdvp!p-QFD4zk`Akv$@%%_8mQL-2<4d+f+)Y?D@{_16Svv9Wy|&^^(?b&Xnwj*r*I4qMy7!$t@BSo-=ql9PK<@sP5sfMDCSe z>D{Z>Xx> zWB94m{WTSYiS-GY+?$CqlFTqxY_&8NrEQ<(>Iwe9%~3GXfxUEn1_A3fQR6J{b}2M) z4Q?DeAHTD5Ay0fF`!%E#n@#xfa3yj56Yi93c7&EKXw`p%8m@(S+i;-BRnx&YNbCIA zD~|QA7d=+q-6=fXm!W>n9Irr1SaPSwSkiq4Rt7c$rdgz2m{_#sykKI=e91y{o9u%|p$rYQz59 z{fh-A;_ktN9n5s7e9d`Nx?L6*8%hODcb5|CxnNsfnEBs6RAOqu^pQ3gcVghZg^XA9 z0R{Un`8obN^0Vir^L2Y=iZd=)rc0v2g%HndVzhJ^DiCAp-HWT5Ox2YTX=>sF#C1~J zhpPM~f&w&kUOYEGVC;X$w^y;Y*ZM}*1kH5kAt|a{umpF{bE#)V^w><~oIp4$*`l4s zL;6l_(k4)i(u5Z$cJ4RB6?k!*zU~@+my03ZX~eY#FyhpdEz)Nq1Hh#)!bh+^L6IZ&>w1+mSksl-O&GfehJ&1z)Z6H{+n=6r7&qWQ~^dq!!K>l!z*ej)p$ zyIiJFGb5CR9Lo6GnoPL8!`!9Y;3+x$jkc5Y-lMg>vy>0iBZ>P)OvD=N zrD!JX79&)eLE_$b(XuYs(H+x7sfSjj*bJ6&%euxXY%lH=`b?0&g0D)rP=nn45dDFx zveFCo?qPY97)zPeZ;qMkp3tDK$wtm{i4*Hmw$!9++%36S!BM}UwnMbHF%$!#EYSh& z@u>XZrtdRO|I$=~{qRkFDVWbQ&JJR$b0>+}LZO(g2Jgm`oknuh7zABZ1uMVd!cXS2 zeC^%Kh@}{zb7I%qMZa(YN0HnoMz(hsVv^S^jA&>*1Zs%YzQMVPpx6jH7CnCPrymik zu+HugWYvydF!hLcAD*`f+j_+5(5p^fi5rE8Fm+!kMHZcy7F&vN`s0@rLwT{O1Ys)b1n~N6Ku>LB#s*yh;P+1KXXXVKwuOfC(}w^MrM-`Gh^@w% zQykX$#iGq-(t3U|2Tq8lug)g;o~~=GLlu3sxA?0QZ6k7o*^PzxMlIHd@T+K%5=9Q5}=4WcV0rV(5LFeI_8Foy!yKezhD9ch-W~g7(rY3 zHU1-;n&GGHH(DcSiIrzWPhKs8?NoipY;J5HitZM!sFSQ9>d_8IU)LfUio+2qPl zSmTz3uzX!bM*!1&H~id1S~J((Z85kvdclQhZ0fh8yOOA0_wvNHeTnh!gJyrfe~vhK zT&PcXReopgr^r@+7eomZT6yo0c$rda z#6&mE%3a#;R&hXC2JmW7zb9K?*QAr3pK%@H!8bkAd@M8ds+Y}}7%8gNjoU&~dXH9k zFHaPRpSl+Jkh%Fa)jhS`EpOc=(+mdyc`g1E(YyDh>FN{db`v=@@~lLE#x0d-DUY>nNk*b<4SPWzLy5VPD1Kox5goeaQ|Q$vXDf;<#iprO z|7FU=Xi<;e0Kq4cOV=cVxB&}Z6L)JqBRz$OFVAU}V@Ub>FKf0P<2xnLvj?5YL3rk? z6KmEs@O^zIk`4eoRZbL3Qp>io++``_(;G0$G_MIg;Qs|pBOn1s8v{#aZQDYPj}(R^xKvxYse@ zg5ww<>!f%d2mYP*ig=+v~kE%u*QM6+?Hbo>|~kyi4-A{H6^+|(1|QB zPd%Dl&G&~Vu>h~-1(u?B$*17hVpxw&qJksvJa^%r1AcuHWqtd0q3qE@z#fS*vAZAa zs0{$zzpPsp_~4R@5h~MQA}s`14%YCu3lJ0ecWY$;v{rE)h?-R?#||zk0oTNBdJL-G zt*YM-0S-WUH{rV-Qi>2h1zJj={=ul1^+iuFx_nMS*08@UcHC`=7QXR+cerMW^7Q&kgZX~FK;&BasBL`SJ&$Yc-QC@CvrZQM@&uQ$=-6}SWH;Em z_o2sgkwpHiR?TTS768sw=JwS2(@>=di2+IgRimtDHi48X?t5-bjwPlRdTUiyLYS3T zX|iD)QI-P%s4~D7V3=;~2eMn@UN;MKggA3fz9yY=Tc~Ke9jU4CYI!+jB~jbA&#U#W zz7Z=+n76)jpO%sY3psYT7x^G@=Phu=uXGK(~I~ zptct*+rmgZTw^U7&QAzX$e;b9KwAIZmOjzCuV`&=c#QV+lUf`q>QL2Z{V7HFh))xdC@*=PIQN=rPZT;!w50415CPC z;`bJ+vM2A5W`h6Hn0fwL_jK;bm0qhd3~s|g45IFP&V3U=7_gexAk+?Ez^{$Zwde7dPK{$tj$-gB#IXb3-WAC&NM>-WM&_M2VA>g zO(lPJHh2)5#l4>g#1zA89|^>6Y<3UoBe@S{c052*{^fE^s8I1_-|>~cV<&ggxdZk4 z-tD&PN9;TtFS;vcc~=$t4<0=yp)(u~R-muV$C>-x4cp%1{+?JW0tnU~35q{nsa~#X z?PhWTS8VNIdADNc-8_p@#6D_N15~kQ!!HD{Bu*{BDp4XK&x9|MhAGdJs!1COqZn-I z4Joi62PXfnJypoyYu#AjdXqS9uW!XMtXKHn*FZ^>84jlQmauw@nc{Z?IGp5Q{<|d% z<|T=~an#+5i3=EFLk(M=j#YG4w|5kc?bsw@8`f27j$?!IRnZcS*eI5fWzJ*!daiX@ zPxttun~9w6>@QssbnMBN`Px%V6~lgF#C`|jWl?W-dDdXCXAm{9{w@LVe)k#uCK|r>3+S3yt{elW@jF&)M8X2t=Tr!f7uastR}J`p71CexLn3w zO;opHc&~V8PE8}t3nLx2CXYikP&KRDlkaN#bN~iE&#acnSQK|?lJW~ zJNAoTYA@$lFE69(j|Wz+$F+~#v%u7!xDzlF?s)>`-umI12fF_Vih8|pT?&m< zv+B^f4y9qWmM=-j(EO+)wmg3?P9d<$)3?vB0q8(p0^HBj*i1ryjSY!Wt(UO)mVyR^ z!ZBr`&L&K&0VNHDo~vN*ee^ma>fbl=Q=O(Q?tJwMu7Jc0Kun| z|0LJ=W$4gO*V@lx&AzsQgJ<2<{4N#;r2Yk{B{x&8=0MclkQ6!_)~jimJyy>UNlW}< z`97l^|5>$`ma3i{l?Et^;%oRXZjiUA@yz9`ec^KLN8 zpROP92zW|T-Cr1q4ijf2M`BkPMNUg!{*ppqFEraTUEhDvE_isB*`^Pq9HpId7X4Zf zd{?7WMFsLZvb@x`ZM;79WLYaja()YM&E+7r6tej=u1a!ACZ8(A3&KV@^j%3q=kt&7?D4XS!)nrG>=4{(l zl|XeD*H)C9owdo`Ue%!{OpFcAr+PWHa4R9p5!Vu815T?^#(Z4cZZsnP1|&VD)POm0 zsv%(8ud@SyFqm!Dnx+iMcnfV3!sA*!2BYL-tfEc6b&V;Qx$bRpn`r2=8`}wHetUF! zv+k zckM`1HOc|26BOI_*k(#?pCzd5`r4R!o8uCHt$YvQ*UvtGoP8AM5G}77&3BK-kl(de z21|4WpBL^5X^TJQ*A-qe2U6Y)8t-0r2xB&pL#ErAEd~RtWm(Hliv5z4jPz?Ug^}N# zD8Od;EOo(W1Xh~6Gi{#4T&FFaiQ>F>1l;Bw+Jd6d{FnJ^w+=v9w>1atl_fCW7m=7T zwX7Pw)FS~8*7sU5U43Q;q|xe8m`~%oqB+GVyhlAZwP2w#+KE!&`4Y2@9*{d`9~@6aG&Qs_nz}U?{l8>?pV2o zLwG0W&@s^9$Zssaly!kqdo|Qti8(+jHwV-*Sl{peGmXqCl8V3ztN3kkGvu~O+fO2< zF*w=teU9nOjZbW~{Gv2GfA?2cxgsUJiIb6bS7R5x+~NOSL@JYJJRtXjOW6bqGXI{_ z%w%&;J*8uUoY3e5z2-U%cEPSl6AYSZO(4eau61$#uz%esROsJ?F0Bk5fL*GzXh@ zlr=zwQVr0;#wm@4Gq%+O0JuxKz^C&uzCp4Bp=ABgY9Qj4wtFRdNSGW{%BvQ4B&-9> z51;{L#rX*R(&LZpKbu?no3@*oo!nvusLYB?-cgsRA`QaasR3yH_$Y<12CXh29+Hj8 zlZHf%m9auBBTo5QQ8_2prxoxwf&WW*FPYKinE~cm9Dh^A#ZKDwppJHYEUmyls6BOi zk;CF`A!Kk9>lktg_U57nLbWabz~6LoVh>nk$3x%rfS}?FYd9?(yyGjlKVgca|NC)S zxB40@siMnd*yE*xiG9?Tp~n@>H!H6bI_iZs^UG^0W|je@k5)HM|Di$M^3)mkt5G2t z!SKIm+-m*!$h(>~aD)!>N^1`^=?!U?XkplQYJk-!e!Lf-z@7Wfm8b8xw+(o13laDC zCt%M8@i9H*u{$yi{=M&(qTJoKm90t3nZu*C9ZRxVcFa1uXlGxW(|ThN8Ie5y64VK5%)lmjK?|Jqxpze*_!y z{PDfF;JWNy3_2(Kn>W*6>9Jsgu)|ejMOqjc&b{PEyja?b4%h*14~NA)tv}cu9Q0uA zQGKF^B8)2b=BG3n+a|2$O=_Rm!}?QZ-9;Jc@EX4EXpYMUl$CyAM#bHoIP>%)_HXBByX*<(XdOGdC9s@3R0#hEY%Vrq3}+DUu!!6XD|-vlK_(xp^*wV4 zQC7rO&fADB>OCS3`kk=(ELHw?N$8LA@bWM*X!GgVjx>72*8nZ!5*{7FQAlcw$C3%k zmx*0moNkbJ{C>oS6oJv>T`Rk1=xvs1ss5(nh16BC%T*;GS!KpZ_$9d_f0y9-5MGx- zjb(qqJ3WhpXp*0kMq#b#xzqiCV=Sw*yuR3}CQk3J)-Q12D^kLez5OgNP8#PAT)4m<`fFoGK2C|CW~SLwiZm{v3WxIi4`0qr@WcO#_82n0yBoU87QLKGl8|aPw~@5+CZ=Q0N=eN z4eFLD=5=7FTuQ-eP*Eyf@g&H%55=1y@stehZy!Q|58(>H78E75kbKlud?^@byUfb3 zb$`?KJ;Rx7cr~M2ix?MIlsN`}Bbe+dpX(K_ssmFlLh@kX{73&56164wGq)68r|`D3 zw<{@mb6OEF7aXd9%>-U9H{nCQCcnD-Tg=LPsU+D9tL{1&*+UBI$rIxP_yZj&_!20we}Cnd&a&&rJG#wA z1&|Y;CmKzw%o!TIoy&_bHyM2!1a8~W3HNkP^=b_bVJeFZwOtootrfb}oe7FYnu3G2 zJ6{X_QWd4av?sWnJ}J`x0L1K@zEeoa9R-mwsYyXa*#5^elJ( zDc)vza&9e={QMJBzj-U#H_}}0=A7gA4YMWXz_ox|Ir&&BM-}yg$*K6V;>uUlM|=DR z=6kUdR}Q2Uk@~=c?pax_@t`Fehpui9xeBh`Sk>Ykidb8bhxn70U2XU++IvsU4Iz=A zyP;~VsTrmr8Xg&%LXz-eBdk!vt zdSms($^}3UIGs4I4i$DkUSuEHdxioHI~24M-IZz_=%5{Ro8W{$Q5XX~HYU(NVJf(v zk(z@6-fu9Gl+ay&9Gtev@|(8Ve-{**nVMSenQ*mhKRSRuUoHH4V6jaXEaZwqetP+{ z0;UEF&)o@a{Xu3619Sd*N9t1I8y?7EHfVSxhXzBa8p%Dn?gQh|avA`+{>-ohz&ICLU63o1Z{Dx?v7B9JwqxOtaXH8Uil`0>|Pdkh!TS zLuYZ?sR^3J3uU}#rN<6yViio<+lyb$q#)k#CpKOfF99PGv+rcLYNcIaj%mjCTB%Dg zYA(}+GJ94W>ob^vnxkAnERuX9309$5o7jf%x>%%kD7cxH}w}BUZCg?B03S6RE@|G2Spj4{j>`HMt-Z^vGU9jfY$ZO)e z{!d+%jgBA&ByLw-G+e9X9Su?^&7&_y6@o23s7e#sM3{T>*8u8dFW-bZgns64D${b0 zxB>&TQUe(z-VT(UC&lOBbL$!LLr`uJGzEpYfB%eo1MWneCV45PRwgw6;8H07l3BCrJZlDZ4c=3Lp^ia&bN z>Z*+z!KdolY&2oa{gq3h_8LeBzIJq(a0(peg*eB(Klu#}A>vfl&=#I+(96G6(58$t zUE+__xW(pspip%uqHStgyWQ zUCSPsrDm}gETq|haGo+Ulgc^`m7gpfZeb#lb zukMlk8hVsYiu$#+&7u2>hs*y%;RY+Im27UsCGtz<2~b6)DSz6k(l2VY)C7BgkhXk7 zjs<5~!EQ_s2h=x~Vx+RrOx{xJFvWtunW=$dR`>_vwD2G?zE*rOQrs9fp5^Is)zVxI z5yD!2>FlVWp-+M(djDD7+J06ncOXie*|y$r{6YNM7=MsFAQS$@fLC198Ju#zT928O4^1J21lvQ$Sl*9Hi*GSMQ#8gtWMZ9YUyU z#MkD9=EN2)L%%p`S#>w1IXLJ?MwYfqhAr^$d%*cFgu~KzXZ}t2fe`Eq3SD5EbqbX;MM^N2lyTEI(@?R zSi9kcw#^sXxvv+5n=5{xM$tU2Og!oTwo%WhM&m6@sqx1S4bG;{BSopX3oJ*;<6X)} zDNqgaV@p$@tHC!E)euZiB+rhtrPqZbR-ygwQ1&-niAICsCkf=!qwD+7E70F_u*Iu^ zo7v^NI?P_a((rY3c}iHxkG1YAmSF zz`A5sjA9;9byE5JBZX&>Izd;9U)W^O9JGSU@TXNChEUd!{vl0(wK*~%ofwS%aTkt5xceWv8dq^faTu(!Au^)dX1TNOH7;9XEEbzKB6HYmI#tQs0 zE;MhV`{~M)p1ysD$P603+U&jbFZEAucpJzq?W*(~ERul`!D+nhZgi-v7T|#GBWJhq z58hY&yj;Aeqhcj1lr?RTo9a+sM!l-meKWC1_rHZs6IYK7kfLGvYI88NhJgRCIBoxu zSYQ3F-Y~Y>*pff>J|ECj1eu3bTYEvs;@ZrKeL3x81Sf=EMUUWqq|s1RIcfR@cCU+{b7$}5UUpRZwH6oC1{Qic#kF@X|?NWUnTqg z#8bh!#fP#`q+Xp(H0W1Xan&y=aHb%^h^L_w3`sMGK$XOemY>2jel|MoVdu|qO{!1i zJ@(c$&1=>*$OvzJVB)(xn-2td&(Nc_(KhkIEPF?YXVAjal&1Ag%tQ2l(aU-XYn&6L zzj?@mFBBK@^AkDKDQT;MZu`ArC+3mB27L)i5ZA^>a&NELUF3FO7x=xAjg(mg``=e> zawoEp>(RbJ?T9QRmkhS|QlDR)_-w*5;{x=0#gCLtUG3NScM|m@KyIRyHe`U$T;PBr z)*|JZ#~Om-+t>1LugG>^Q0kela1R)J20;+3PevUNiAggWt2QX(*NBhq9P`sYFgXY^ zY1Sf;x5S%CCM|Sb=vY|~XUy$^0o$_!$$#&^zZHHx7HL23z-;?YY0EeCcoQMWzF=c2 zN!B5KIb634K*ZjOx!Y+gK5@+%)gN3`(pyDEHo3sashOn|frV#CV<~ve9x+#{r z)jdNGuul`UAL&?fBFIb{lb-*LdzwG(0!uTs}!&!5#TdF2b z_1SBC!Evz-(xh=h*fj0Q)tuC|gh|>I5u4Z&A{GpID zX*%3qaz7=xQ{(LB#%VLG*IXu`D}%5ro-QD*)q++mc_ZSvYod&2CRc4fIum%m(7t`2 zA-0{gC`~>ja>IV!E5w@b4E3|BNF(X2_$~;9E}9_$aE@4re$0rQ;mYg4SQ2&8kp~q2H z$hpVM0q?TbF((_u7Wdy5q@#wf#2gQW>nDLNS8?Rx8upha&NX1|pmkeG?|@6+%rb_g zWi2@CmI6xQHw_qfY0%2~TT1*1fJoC1pIZh~M{$c$h2W5jAC5;p3Ny>pV6CHztMIp# z1ruArr*!^%AC(^lS-d!(2FCn8I3wy3&e}1jS*k&2;h&NWG(K7rw4A@)&vl9M=*FcX zl0ang`uj4e(_sSDw<+}y0Q-3#WXVg4fzvom4ELG$TCwjNO*c|3uFmn4Z*JS=@hKxRxps znG0*o>jw1Uy|B$a6Ivb`0?(M`)O2-Ey`}^}*P}c40i5HuoLsD7mGxa=gjF!rTRu}C z4CwYFhkl(^peyv$02WnwxZ1qwYO77H%*yUy&~=&dd@C@a`^H;KOT;8~#y)Lt{%U64 z{IYo_?o$Vd|3Mn_QG&rX_?ZSbtCr%V3$!3Lek}eL_m8XF>aWDWFLF>i8_BN|b%~cg zD=FnaV1=|utsN!1gxhB5`j$D_>XE#Hk~v)jC|7c|YZ_fDaPTHbD>i?*wwT3CItVU& z>∋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%0A$gVTw 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

  • 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'E(Vo0%gx}5kzrHgBw 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

    al_etJlnvt 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

    RvFI~njy_+}-2 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