From 609d47dea177c8bc7c60593600548f0eacc4477f Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 1 Jun 2025 11:36:14 -0500 Subject: [PATCH 01/50] 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 02/50] 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 03/50] 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 04/50] 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 05/50] 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 06/50] 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 07/50] 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 08/50] 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 09/50] 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 10/50] 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 11/50] 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 12/50] 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 13/50] 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 14/50] 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 15/50] 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 16/50] 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 17/50] 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 18/50] 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 19/50] 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 20/50] 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 21/50] 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 22/50] 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 23/50] 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 24/50] 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 25/50] 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 26/50] 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 27/50] 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 28/50] 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 29/50] 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 30/50] 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 31/50] 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 4765d61a173989114a1ca98daa9d19d18c0f7798 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Sun, 8 Jun 2025 14:19:38 -0500 Subject: [PATCH 32/50] 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 b3d1af83bba4f91aa2f9ac024ed7e76fde65c55d Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 10 Jun 2025 17:38:55 -0500 Subject: [PATCH 33/50] 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 31a0a598cfe7b562d3bd789c793f10b6b7cdb5dd Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 11 Jun 2025 13:41:31 -0500 Subject: [PATCH 34/50] 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 35/50] 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 36/50] 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 37/50] 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 b7107e40f540e0a699b01e8b48dace9eda2ed368 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Mon, 29 Sep 2025 00:14:53 -0500 Subject: [PATCH 38/50] style: public options --- constraint/solver/gkrgates/registry.go | 74 ++++++++++++++++++++------ 1 file changed, 57 insertions(+), 17 deletions(-) diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index 23b821cf63..aa58a0cfe6 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -2,6 +2,7 @@ package gkrgates import ( + "errors" "fmt" "reflect" "runtime" @@ -36,53 +37,86 @@ type registerSettings struct { curves []ecc.ID } -type registerOption func(*registerSettings) +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) { +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) { +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) { +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) { +func WithUnverifiedDegree(degree int) RegisterOption { + return func(settings *registerSettings) error { + if settings.degree != -1 { + return fmt.Errorf("gate degree already set to %d", settings.solvableVar) + } 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) { +func WithDegree(degree int) RegisterOption { + return func(settings *registerSettings) error { + if settings.degree != -1 { + return fmt.Errorf("gate degree already set to %d", settings.solvableVar) + } 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) { +func WithName(name gkr.GateName) RegisterOption { + return func(settings *registerSettings) error { + if settings.name != "" { + return fmt.Errorf("gate name already set to \"%s\"", settings.name) + } settings.name = name + return nil } } @@ -90,9 +124,13 @@ func WithName(name gkr.GateName) registerOption { // The default is to validate 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) { +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 } } @@ -102,10 +140,12 @@ func WithCurves(curves ...ecc.ID) registerOption { // - 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) (registered bool, err 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) + if err = option(&s); err != nil { + return + } } curvesForTesting := s.curves From 67257dc0f00aaba11bcaf6d036d912f8fee604f6 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 30 Sep 2025 17:17:26 -0500 Subject: [PATCH 39/50] revert: Register to return an error only --- constraint/solver/gkrgates/registry.go | 24 ++++----- constraint/solver/gkrgates/registry_test.go | 53 +++++++++---------- std/gkrapi/api.go | 2 +- std/gkrapi/example_test.go | 15 ++---- .../poseidon2/gkr-poseidon2/gkr.go | 16 +++--- 5 files changed, 51 insertions(+), 59 deletions(-) diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index aa58a0cfe6..9938464fff 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -140,11 +140,11 @@ func WithCurves(curves ...ecc.ID) RegisterOption { // - 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) (registered bool, err error) { +func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { s := registerSettings{degree: -1, solvableVar: -1, name: GetDefaultGateName(f)} for _, option := range options { - if err = option(&s); err != nil { - return + if err := option(&s); err != nil { + return err } } @@ -162,26 +162,26 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) (register if g, ok := gates[s.name]; ok { // gate already registered 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) + 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 false, err + return 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, g.Degree(), curve) + return fmt.Errorf("mismatch with already registered gate \"%s\" (degree %d) over curve %s", s.name, g.Degree(), curve) } } - return false, nil // gate already registered + return nil // gate already registered } for _, curve := range curvesForTesting { gateVer, err := NewGateVerifier(curve) if err != nil { - return false, err + return err } if s.degree == -1 { // find a degree @@ -190,12 +190,12 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) (register } const maxAutoDegreeBound = 32 if s.degree, err = gateVer.findDegree(f, maxAutoDegreeBound, nbIn); err != nil { - return false, 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 false, fmt.Errorf("for gate \"%s\": %v", s.name, err) + return fmt.Errorf("for gate \"%s\": %v", s.name, err) } } } @@ -207,14 +207,14 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) (register } else { // solvable variable given if !s.noSolvableVarVerification && !gateVer.isVarSolvable(f, s.solvableVar, nbIn) { - return false, 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) } } } gates[s.name] = gkrtypes.NewGate(f, nbIn, s.degree, s.solvableVar, allowedCurves) - return true, nil + return nil } func Get(name gkr.GateName) *gkrtypes.Gate { diff --git a/constraint/solver/gkrgates/registry_test.go b/constraint/solver/gkrgates/registry_test.go index 7fe739b152..fbca20ffba 100644 --- a/constraint/solver/gkrgates/registry_test.go +++ b/constraint/solver/gkrgates/registry_test.go @@ -1,7 +1,6 @@ package gkrgates import ( - "fmt" "testing" "github.com/consensys/gnark/frontend" @@ -16,33 +15,32 @@ func TestRegister(t *testing.T) { t.Run(string(name), func(t *testing.T) { name = name + "-register-gate-test" - 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.NoError(t, + Register(f, nbIn, WithDegree(degree), WithName(name+"_given")), + "given degree must be accepted", + ) - 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+"_lower")), + "error must be returned for lower degree", + ) - 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.Error(t, + Register(f, nbIn, WithDegree(degree+1), WithName(name+"_higher")), + "error must be returned for higher degree", + ) - 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.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") - 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 { + 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") }) } @@ -67,21 +65,20 @@ 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).Error() 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 - registered, err := Register(zeroGate, 1, WithName(gateName)) - assert.Error(t, err, "error must be returned for zero polynomial") - assert.EqualError(t, err, expectedError, "error message must match expected error") - assert.False(t, registered, "registration must fail for zero polynomial") + 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 - registered, err = Register(zeroGate, 1, WithName(gateName), WithDegree(2)) - assert.Error(t, err, "error must be returned for zero polynomial with degree") - assert.EqualError(t, err, expectedError, "error message must match expected error") - assert.False(t, registered, "registration must fail for zero polynomial with degree") + assert.Error(t, + Register(zeroGate, 1, WithName(gateName), WithDegree(2)), + "error must be returned for zero polynomial with degree", + ) }) } diff --git a/std/gkrapi/api.go b/std/gkrapi/api.go index ae3c2b7954..5afc886e5f 100644 --- a/std/gkrapi/api.go +++ b/std/gkrapi/api.go @@ -27,7 +27,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...) diff --git a/std/gkrapi/example_test.go b/std/gkrapi/example_test.go index 705f288a73..04422a93cc 100644 --- a/std/gkrapi/example_test.go +++ b/std/gkrapi/example_test.go @@ -25,16 +25,11 @@ func Example() { // SNARK circuit being compiled. // But in production applications it would be necessary. - _, 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) + 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 diff --git a/std/permutation/poseidon2/gkr-poseidon2/gkr.go b/std/permutation/poseidon2/gkr-poseidon2/gkr.go index 800e79ed05..5b34261c8a 100644 --- a/std/permutation/poseidon2/gkr-poseidon2/gkr.go +++ b/std/permutation/poseidon2/gkr-poseidon2/gkr.go @@ -275,30 +275,30 @@ func registerGatesBls12377() error { 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 { + 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 { + 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 { + 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 { + 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 { + 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)) + 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 { - _, 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(ecc.BLS12_377)) return err } @@ -343,7 +343,7 @@ 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)) + 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 } From b1d61f152789d2b01fca840b2c21f38eae1949eb Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 30 Sep 2025 17:20:35 -0500 Subject: [PATCH 40/50] fix: gate name conflict --- constraint/solver/gkrgates/registry.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index 9938464fff..dfaa2f3f16 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -112,6 +112,9 @@ func WithDegree(degree int) RegisterOption { // 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) } @@ -141,12 +144,15 @@ func WithCurves(curves ...ecc.ID) RegisterOption { // // 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, name: GetDefaultGateName(f)} + 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 From 99cf4a86f29a413277e70fbdeae74de84194e6ea Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 30 Sep 2025 17:28:01 -0500 Subject: [PATCH 41/50] refactor: make `newSolvingDataOption` public --- .../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 04873fdcb8..476f71aeee 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -24,15 +24,15 @@ type newSolvingDataSettings struct { assignment gkrtypes.WireAssignment } -type newSolvingDataOption func(*newSolvingDataSettings) +type NewSolvingDataOption func(*newSolvingDataSettings) -func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { +func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } -func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { +func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { opt(&s) diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index c977d4997a..9aa53ca04b 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -31,15 +31,15 @@ type newSolvingDataSettings struct { assignment gkrtypes.WireAssignment } -type newSolvingDataOption func(*newSolvingDataSettings) +type NewSolvingDataOption func(*newSolvingDataSettings) -func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { +func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } -func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { +func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { opt(&s) diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index 81572a4ac4..6a37a2ffc0 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -31,15 +31,15 @@ type newSolvingDataSettings struct { assignment gkrtypes.WireAssignment } -type newSolvingDataOption func(*newSolvingDataSettings) +type NewSolvingDataOption func(*newSolvingDataSettings) -func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { +func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } -func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { +func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { opt(&s) diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index 783cc964c8..6a7baa53b1 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -31,15 +31,15 @@ type newSolvingDataSettings struct { assignment gkrtypes.WireAssignment } -type newSolvingDataOption func(*newSolvingDataSettings) +type NewSolvingDataOption func(*newSolvingDataSettings) -func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { +func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } -func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { +func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { opt(&s) diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index 234a327324..8b1eff19fe 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -31,15 +31,15 @@ type newSolvingDataSettings struct { assignment gkrtypes.WireAssignment } -type newSolvingDataOption func(*newSolvingDataSettings) +type NewSolvingDataOption func(*newSolvingDataSettings) -func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { +func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } -func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { +func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { opt(&s) diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index f855222636..f98b1d65a5 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -31,15 +31,15 @@ type newSolvingDataSettings struct { assignment gkrtypes.WireAssignment } -type newSolvingDataOption func(*newSolvingDataSettings) +type NewSolvingDataOption func(*newSolvingDataSettings) -func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { +func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } -func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { +func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { opt(&s) diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index 19d347d099..6e0269c942 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -31,15 +31,15 @@ type newSolvingDataSettings struct { assignment gkrtypes.WireAssignment } -type newSolvingDataOption func(*newSolvingDataSettings) +type NewSolvingDataOption func(*newSolvingDataSettings) -func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { +func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } -func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { +func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { opt(&s) diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index 09e9c13f0f..1b9389ae58 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -31,15 +31,15 @@ type newSolvingDataSettings struct { assignment gkrtypes.WireAssignment } -type newSolvingDataOption func(*newSolvingDataSettings) +type NewSolvingDataOption func(*newSolvingDataSettings) -func WithAssignment(assignment gkrtypes.WireAssignment) newSolvingDataOption { +func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } -func NewSolvingData(info gkrtypes.SolvingInfo, options ...newSolvingDataOption) *SolvingData { +func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { opt(&s) From 52422799a9709dae74373d16ebc11bafe2039c59 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Tue, 30 Sep 2025 18:15:26 -0500 Subject: [PATCH 42/50] docs: GKR Solver Hints --- .../backend/template/gkr/solver_hints.go.tmpl | 22 ++++++++++++++----- internal/gkr/bls12-377/solver_hints.go | 21 +++++++++++++----- internal/gkr/bls12-381/solver_hints.go | 21 +++++++++++++----- internal/gkr/bls24-315/solver_hints.go | 21 +++++++++++++----- internal/gkr/bls24-317/solver_hints.go | 21 +++++++++++++----- internal/gkr/bn254/solver_hints.go | 21 +++++++++++++----- internal/gkr/bw6-633/solver_hints.go | 21 +++++++++++++----- internal/gkr/bw6-761/solver_hints.go | 21 +++++++++++++----- std/gkrapi/compile.go | 2 +- 9 files changed, 129 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 476f71aeee..d017f5bd30 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -26,12 +26,17 @@ type newSolvingDataSettings struct { type NewSolvingDataOption func(*newSolvingDataSettings) +// WithAssignment re-use already computed wire assignments. func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } +// NewSolvingData converts gkrtypes.SolvingInfo into a concrete SolvingData object: +// - 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. func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { @@ -64,7 +69,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 + // 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 } @@ -76,7 +81,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies - +// GetAssignmentHint generates a hint that returns the value of a particular wire at a particular 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) != 3 { @@ -91,6 +97,8 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } } +// 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 { instanceI := ins[0].Uint64() @@ -122,11 +130,13 @@ func SolveHint(data *SolvingData) hint.Hint { } } +// 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(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - data.assignment.RepeatUntilEnd(data.nbInstances) + 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 @@ -149,9 +159,9 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// 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) { +// 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] diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index 9aa53ca04b..e82910ea74 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -33,12 +33,17 @@ type newSolvingDataSettings struct { type NewSolvingDataOption func(*newSolvingDataSettings) +// WithAssignment re-use already computed wire assignments. func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } +// NewSolvingData converts gkrtypes.SolvingInfo into a concrete SolvingData object: +// - 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. func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { @@ -71,7 +76,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 + // 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 } @@ -83,6 +88,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies +// GetAssignmentHint generates a hint that returns the value of a particular wire at a particular 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) != 3 { @@ -97,6 +104,8 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } } +// 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 { instanceI := ins[0].Uint64() @@ -128,11 +137,13 @@ func SolveHint(data *SolvingData) hint.Hint { } } +// 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(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - data.assignment.RepeatUntilEnd(data.nbInstances) + 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 @@ -155,9 +166,9 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// 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) { +// 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/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index 6a37a2ffc0..0a91fe6e0a 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -33,12 +33,17 @@ type newSolvingDataSettings struct { type NewSolvingDataOption func(*newSolvingDataSettings) +// WithAssignment re-use already computed wire assignments. func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } +// NewSolvingData converts gkrtypes.SolvingInfo into a concrete SolvingData object: +// - 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. func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { @@ -71,7 +76,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 + // 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 } @@ -83,6 +88,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies +// GetAssignmentHint generates a hint that returns the value of a particular wire at a particular 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) != 3 { @@ -97,6 +104,8 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } } +// 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 { instanceI := ins[0].Uint64() @@ -128,11 +137,13 @@ func SolveHint(data *SolvingData) hint.Hint { } } +// 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(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - data.assignment.RepeatUntilEnd(data.nbInstances) + 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 @@ -155,9 +166,9 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// 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) { +// 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/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index 6a7baa53b1..ae2380f441 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -33,12 +33,17 @@ type newSolvingDataSettings struct { type NewSolvingDataOption func(*newSolvingDataSettings) +// WithAssignment re-use already computed wire assignments. func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } +// NewSolvingData converts gkrtypes.SolvingInfo into a concrete SolvingData object: +// - 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. func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { @@ -71,7 +76,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 + // 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 } @@ -83,6 +88,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies +// GetAssignmentHint generates a hint that returns the value of a particular wire at a particular 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) != 3 { @@ -97,6 +104,8 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } } +// 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 { instanceI := ins[0].Uint64() @@ -128,11 +137,13 @@ func SolveHint(data *SolvingData) hint.Hint { } } +// 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(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - data.assignment.RepeatUntilEnd(data.nbInstances) + 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 @@ -155,9 +166,9 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// 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) { +// 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/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index 8b1eff19fe..1145c87225 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -33,12 +33,17 @@ type newSolvingDataSettings struct { type NewSolvingDataOption func(*newSolvingDataSettings) +// WithAssignment re-use already computed wire assignments. func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } +// NewSolvingData converts gkrtypes.SolvingInfo into a concrete SolvingData object: +// - 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. func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { @@ -71,7 +76,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 + // 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 } @@ -83,6 +88,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies +// GetAssignmentHint generates a hint that returns the value of a particular wire at a particular 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) != 3 { @@ -97,6 +104,8 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } } +// 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 { instanceI := ins[0].Uint64() @@ -128,11 +137,13 @@ func SolveHint(data *SolvingData) hint.Hint { } } +// 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(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - data.assignment.RepeatUntilEnd(data.nbInstances) + 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 @@ -155,9 +166,9 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// 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) { +// 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/solver_hints.go b/internal/gkr/bn254/solver_hints.go index f98b1d65a5..4f9610397b 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -33,12 +33,17 @@ type newSolvingDataSettings struct { type NewSolvingDataOption func(*newSolvingDataSettings) +// WithAssignment re-use already computed wire assignments. func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } +// NewSolvingData converts gkrtypes.SolvingInfo into a concrete SolvingData object: +// - 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. func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { @@ -71,7 +76,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 + // 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 } @@ -83,6 +88,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies +// GetAssignmentHint generates a hint that returns the value of a particular wire at a particular 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) != 3 { @@ -97,6 +104,8 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } } +// 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 { instanceI := ins[0].Uint64() @@ -128,11 +137,13 @@ func SolveHint(data *SolvingData) hint.Hint { } } +// 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(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - data.assignment.RepeatUntilEnd(data.nbInstances) + 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 @@ -155,9 +166,9 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// 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) { +// 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/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index 6e0269c942..1312803ba6 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -33,12 +33,17 @@ type newSolvingDataSettings struct { type NewSolvingDataOption func(*newSolvingDataSettings) +// WithAssignment re-use already computed wire assignments. func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } +// NewSolvingData converts gkrtypes.SolvingInfo into a concrete SolvingData object: +// - 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. func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { @@ -71,7 +76,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 + // 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 } @@ -83,6 +88,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies +// GetAssignmentHint generates a hint that returns the value of a particular wire at a particular 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) != 3 { @@ -97,6 +104,8 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } } +// 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 { instanceI := ins[0].Uint64() @@ -128,11 +137,13 @@ func SolveHint(data *SolvingData) hint.Hint { } } +// 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(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - data.assignment.RepeatUntilEnd(data.nbInstances) + 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 @@ -155,9 +166,9 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// 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) { +// 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/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index 1b9389ae58..cc4212a165 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -33,12 +33,17 @@ type newSolvingDataSettings struct { type NewSolvingDataOption func(*newSolvingDataSettings) +// WithAssignment re-use already computed wire assignments. func WithAssignment(assignment gkrtypes.WireAssignment) NewSolvingDataOption { return func(s *newSolvingDataSettings) { s.assignment = assignment } } +// NewSolvingData converts gkrtypes.SolvingInfo into a concrete SolvingData object: +// - 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. func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) *SolvingData { var s newSolvingDataSettings for _, opt := range options { @@ -71,7 +76,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 + // 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 } @@ -83,6 +88,8 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) // this module assumes that wire and instance indexes respect dependencies +// GetAssignmentHint generates a hint that returns the value of a particular wire at a particular 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) != 3 { @@ -97,6 +104,8 @@ func GetAssignmentHint(data *SolvingData) hint.Hint { } } +// 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 { instanceI := ins[0].Uint64() @@ -128,11 +137,13 @@ func SolveHint(data *SolvingData) hint.Hint { } } +// 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(hashName string, data *SolvingData) hint.Hint { return func(_ *big.Int, ins, outs []*big.Int) error { - data.assignment.RepeatUntilEnd(data.nbInstances) + 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 @@ -155,9 +166,9 @@ func ProveHint(hashName string, data *SolvingData) hint.Hint { } } -// 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) { +// 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/std/gkrapi/compile.go b/std/gkrapi/compile.go index 64205b80b8..66bc477938 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -141,7 +141,7 @@ func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr return res, nil } -// finalize encodes the verification circuitry for the GKR circuit +// finalize encodes the verification circuitry for the GKR circuit. func (c *Circuit) finalize(api frontend.API) error { if api != c.api { panic("api mismatch") From c523fd2e3ec945c4a43bfa61f5f85f8f6fc5aaae Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 1 Oct 2025 10:34:53 -0500 Subject: [PATCH 43/50] refactor: effectuate PR feedback --- constraint/solver/gkrgates/registry.go | 53 +++-- internal/generator/backend/main.go | 8 +- .../backend/template/gkr/solver_hints.go.tmpl | 6 +- internal/gkr/engine_hints.go | 1 - internal/gkr/gkrtypes/types.go | 8 +- internal/gkr/small_rational/gate_testing.go | 209 ------------------ std/gkrapi/compile.go | 4 +- std/gkrapi/example_test.go | 4 +- 8 files changed, 44 insertions(+), 249 deletions(-) delete mode 100644 internal/gkr/small_rational/gate_testing.go diff --git a/constraint/solver/gkrgates/registry.go b/constraint/solver/gkrgates/registry.go index dfaa2f3f16..e5d13bbfe9 100644 --- a/constraint/solver/gkrgates/registry.go +++ b/constraint/solver/gkrgates/registry.go @@ -90,7 +90,7 @@ func WithNoSolvableVar() RegisterOption { func WithUnverifiedDegree(degree int) RegisterOption { return func(settings *registerSettings) error { if settings.degree != -1 { - return fmt.Errorf("gate degree already set to %d", settings.solvableVar) + return fmt.Errorf("gate degree already set to %d", settings.degree) } settings.noDegreeVerification = true settings.degree = degree @@ -102,7 +102,7 @@ func WithUnverifiedDegree(degree int) RegisterOption { func WithDegree(degree int) RegisterOption { return func(settings *registerSettings) error { if settings.degree != -1 { - return fmt.Errorf("gate degree already set to %d", settings.solvableVar) + return fmt.Errorf("gate degree already set to %d", settings.degree) } settings.degree = degree return nil @@ -172,7 +172,7 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { } for _, curve := range curvesForTesting { - gateVer, err := NewGateVerifier(curve) + gateVer, err := newGateVerifier(curve) if err != nil { return err } @@ -185,7 +185,7 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { } for _, curve := range curvesForTesting { - gateVer, err := NewGateVerifier(curve) + gateVer, err := newGateVerifier(curve) if err != nil { return err } @@ -200,7 +200,7 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { } } else { if !s.noDegreeVerification { // check that the given degree is correct - if err = gateVer.verifyDegree(f, s.degree, nbIn); err != nil { + if err = gateVer.verifyGateFunctionDegree(f, s.degree, nbIn); err != nil { return fmt.Errorf("for gate \"%s\": %v", s.name, err) } } @@ -223,6 +223,9 @@ func Register(f gkr.GateFunction, nbIn int, options ...RegisterOption) error { 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.Gate { gatesLock.Lock() defer gatesLock.Unlock() @@ -232,14 +235,16 @@ 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) - verifyDegree func(f gkr.GateFunction, claimedDegree, nbIn int) error - equal func(f1, f2 gkr.GateFunction, nbIn int) bool + 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 } -func NewGateVerifier(curve ecc.ID) (*gateVerifier, error) { +func newGateVerifier(curve ecc.ID) (*gateVerifier, error) { var ( o gateVerifier err error @@ -248,37 +253,37 @@ func NewGateVerifier(curve ecc.ID) (*gateVerifier, error) { case ecc.BLS12_377: o.isAdditive = bls12377.IsGateFunctionAdditive o.findDegree = bls12377.FindGateFunctionDegree - o.verifyDegree = bls12377.VerifyGateFunctionDegree + o.verifyGateFunctionDegree = bls12377.VerifyGateFunctionDegree o.equal = bls12377.EqualGateFunction case ecc.BLS12_381: o.isAdditive = bls12381.IsGateFunctionAdditive o.findDegree = bls12381.FindGateFunctionDegree - o.verifyDegree = bls12381.VerifyGateFunctionDegree + o.verifyGateFunctionDegree = bls12381.VerifyGateFunctionDegree o.equal = bls12381.EqualGateFunction case ecc.BLS24_315: o.isAdditive = bls24315.IsGateFunctionAdditive o.findDegree = bls24315.FindGateFunctionDegree - o.verifyDegree = bls24315.VerifyGateFunctionDegree + o.verifyGateFunctionDegree = bls24315.VerifyGateFunctionDegree o.equal = bls24315.EqualGateFunction case ecc.BLS24_317: o.isAdditive = bls24317.IsGateFunctionAdditive o.findDegree = bls24317.FindGateFunctionDegree - o.verifyDegree = bls24317.VerifyGateFunctionDegree + o.verifyGateFunctionDegree = bls24317.VerifyGateFunctionDegree o.equal = bls24317.EqualGateFunction case ecc.BN254: o.isAdditive = bn254.IsGateFunctionAdditive o.findDegree = bn254.FindGateFunctionDegree - o.verifyDegree = bn254.VerifyGateFunctionDegree + o.verifyGateFunctionDegree = bn254.VerifyGateFunctionDegree o.equal = bn254.EqualGateFunction case ecc.BW6_633: o.isAdditive = bw6633.IsGateFunctionAdditive o.findDegree = bw6633.FindGateFunctionDegree - o.verifyDegree = bw6633.VerifyGateFunctionDegree + o.verifyGateFunctionDegree = bw6633.VerifyGateFunctionDegree o.equal = bw6633.EqualGateFunction case ecc.BW6_761: o.isAdditive = bw6761.IsGateFunctionAdditive o.findDegree = bw6761.FindGateFunctionDegree - o.verifyDegree = bw6761.VerifyGateFunctionDegree + o.verifyGateFunctionDegree = bw6761.VerifyGateFunctionDegree o.equal = bw6761.EqualGateFunction default: err = fmt.Errorf("unsupported curve %s", curve) @@ -293,7 +298,7 @@ func GetDefaultGateName(fn gkr.GateFunction) gkr.GateName { 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. +// 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 { @@ -305,15 +310,17 @@ func (v *gateVerifier) findSolvableVar(f gkr.GateFunction, nbIn int) int { 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. +// 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 (v *gateVerifier) VerifyDegree(g *gkrtypes.Gate) error { - if err := v.verifyDegree(g.Evaluate, g.Degree(), g.NbIn()); err != nil { +// verifyDegree checks that the declared total degree of the gate polynomial +// is correct. +func (v *gateVerifier) verifyDegree(g *gkrtypes.Gate) error { + if err := v.verifyGateFunctionDegree(g.Evaluate, g.Degree(), g.NbIn()); err != nil { deg, errFind := v.findDegree(g.Evaluate, g.Degree(), g.NbIn()) if errFind != nil { return fmt.Errorf("could not find gate degree: %w\n\tdegree verification error: %w", errFind, errFind) @@ -323,7 +330,9 @@ func (v *gateVerifier) VerifyDegree(g *gkrtypes.Gate) error { return nil } -func (v *gateVerifier) VerifySolvability(g *gkrtypes.Gate) error { +// verifySolvability checks that the variable declared as "solvable" +// in fact occurs with degree exactly 1. +func (v *gateVerifier) verifySolvability(g *gkrtypes.Gate) error { if g.SolvableVar() == -1 { return nil } diff --git a/internal/generator/backend/main.go b/internal/generator/backend/main.go index d07f64a827..7305d7e00d 100644 --- a/internal/generator/backend/main.go +++ b/internal/generator/backend/main.go @@ -299,7 +299,6 @@ func generateGkrBackend(cfg gkrConfig) error { // gkr backend entries := []bavard.Entry{ {File: filepath.Join(packageDir, "gkr.go"), Templates: []string{"gkr.go.tmpl"}}, - {File: filepath.Join(packageDir, "gate_testing.go"), Templates: []string{"gate_testing.go.tmpl"}}, {File: filepath.Join(packageDir, "sumcheck.go"), Templates: []string{"sumcheck.go.tmpl"}}, {File: filepath.Join(packageDir, "sumcheck_test.go"), Templates: []string{"sumcheck.test.go.tmpl", "sumcheck.test.defs.go.tmpl"}}, {File: filepath.Join(packageDir, testVectorUtilsFileName), Templates: []string{"test_vector_utils.go.tmpl"}}, @@ -317,9 +316,10 @@ func generateGkrBackend(cfg gkrConfig) error { {File: filepath.Join(packageDir, "sumcheck_test_vector_gen.go"), Templates: []string{"sumcheck.test.vectors.gen.go.tmpl", "sumcheck.test.defs.go.tmpl"}}, }...) } else { - entries = append(entries, bavard.Entry{ - File: filepath.Join(packageDir, "solver_hints.go"), Templates: []string{"solver_hints.go.tmpl"}, - }) + entries = append(entries, []bavard.Entry{ + {File: filepath.Join(packageDir, "solver_hints.go"), Templates: []string{"solver_hints.go.tmpl"}}, + {File: filepath.Join(packageDir, "gate_testing.go"), Templates: []string{"gate_testing.go.tmpl"}}, + }...) } if err := bgen.Generate(cfg, "gkr", "./template/gkr/", entries...); err != nil { diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index d017f5bd30..c25ca5ab73 100644 --- a/internal/generator/backend/template/gkr/solver_hints.go.tmpl +++ b/internal/generator/backend/template/gkr/solver_hints.go.tmpl @@ -58,15 +58,15 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) 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))) + panic(fmt.Sprintf("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)) + panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } 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)) + panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) } } // inline equivalent of repeatUntilEnd diff --git a/internal/gkr/engine_hints.go b/internal/gkr/engine_hints.go index 74b15c77ba..5dbc22c811 100644 --- a/internal/gkr/engine_hints.go +++ b/internal/gkr/engine_hints.go @@ -85,7 +85,6 @@ func (h *TestEngineHints) Prove(mod *big.Int, ins, outs []*big.Int) error { 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) diff --git a/internal/gkr/gkrtypes/types.go b/internal/gkr/gkrtypes/types.go index d313a7bc59..a76059ac73 100644 --- a/internal/gkr/gkrtypes/types.go +++ b/internal/gkr/gkrtypes/types.go @@ -3,6 +3,7 @@ package gkrtypes import ( "errors" "fmt" + "slices" "github.com/consensys/gnark" "github.com/consensys/gnark-crypto/ecc" @@ -34,12 +35,7 @@ func NewGate(f gkr.GateFunction, nbIn int, degree int, solvableVar int, curves [ } func (g *Gate) SupportsCurve(curve ecc.ID) bool { - for _, c := range g.curves { - if c == curve { - return true - } - } - return false + return slices.Contains(g.curves, curve) } func (g *Gate) Evaluate(api gkr.GateAPI, in ...frontend.Variable) frontend.Variable { diff --git a/internal/gkr/small_rational/gate_testing.go b/internal/gkr/small_rational/gate_testing.go deleted file mode 100644 index 93c4ca4191..0000000000 --- a/internal/gkr/small_rational/gate_testing.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2020-2025 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" - - "errors" - "slices" - - "github.com/consensys/gnark/internal/small_rational" - "github.com/consensys/gnark/internal/small_rational/polynomial" - "github.com/consensys/gnark/std/gkrapi/gkr" -) - -// 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 { - fWrapped := api.convertFunc(f) - - // 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(small_rational.Vector, nbIn) - x.MustSetRandom() - x0 := x[i] - x[i].SetZero() - in := slices.Clone(x) - y0 := fWrapped(in...) - - x[i] = x0 - copy(in, x) - y1 := fWrapped(in...) - - x[i].Double(&x[i]) - copy(in, x) - y2 := fWrapped(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 = fWrapped(in...) - - x[i] = x0 - copy(in, x) - y1 = fWrapped(in...) - - y1.Sub(y1, y0) - - return y1.Equal(y2) -} - -// fitPoly tries to fit a polynomial of degree less than degreeBound to f. -// 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 { - // turn f univariate by defining p(x) as f(x, rx, ..., sx) - // where r, s, ... are random constants - fIn := make([]small_rational.SmallRational, nbIn) - consts := make(small_rational.Vector, nbIn-1) - consts.MustSetRandom() - - p := make(polynomial.Polynomial, degreeBound) - x := make(small_rational.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(f(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 := f(fIn...) - if !pAt.Equal(fAt) { - return nil - } - - // trim p - lastNonZero := len(p) - 1 - for lastNonZero >= 0 && p[lastNonZero].IsZero() { - lastNonZero-- - } - return p[:lastNonZero+1] -} - -// 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) { - fFr := api.convertFunc(f) - bound := uint64(max) + 1 - for degreeBound := uint64(4); degreeBound <= bound; degreeBound *= 8 { - if p := fFr.fitPoly(nbIn, 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 VerifyGateFunctionDegree(f gkr.GateFunction, claimedDegree, nbIn int) error { - 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) - } 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 -} - -// 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) { - 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([][]small_rational.SmallRational, len(X)) // the last column is the Y values - for i := range augmentedRows { - augmentedRows[i] = make([]small_rational.SmallRational, 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 small_rational.SmallRational - 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 small_rational.SmallRational - c.Mul(&augmentedRows[j][i], &negInv) - // augmentedRows[j][i].SetZero() omitted - for k := i + 1; k < len(augmentedRows[i]); k++ { - var t small_rational.SmallRational - 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 small_rational.SmallRational - t.Mul(&res[j], &augmentedRows[i][j]) - res[i].Sub(&res[i], &t) - } - res[i].Div(&res[i], &augmentedRows[i][i]) - } - - return res, nil -} diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 66bc477938..95f05a64b0 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -108,8 +108,8 @@ func (api *API) Compile(parentApi frontend.API, fiatshamirHashName string, optio 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 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 !c.toStore.Circuit[k].IsInput() { return nil, fmt.Errorf("value provided for non-input variable %d", k) diff --git a/std/gkrapi/example_test.go b/std/gkrapi/example_test.go index 04422a93cc..9b9c56c8e4 100644 --- a/std/gkrapi/example_test.go +++ b/std/gkrapi/example_test.go @@ -16,8 +16,8 @@ import ( func Example() { // This example computes the double of multiple BLS12-377 G1 points, which can be computed natively over BW6-761. - // 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. + // The two curves form a "cycle", meaning the scalar field of one is the base field of the other. + // 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 From 36784d66e9c79c5ff96f126a64cec1d6728824e9 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 1 Oct 2025 10:40:27 -0500 Subject: [PATCH 44/50] build: go generate --- .../generator/backend/template/gkr/solver_hints.go.tmpl | 2 +- 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, 22 insertions(+), 22 deletions(-) diff --git a/internal/generator/backend/template/gkr/solver_hints.go.tmpl b/internal/generator/backend/template/gkr/solver_hints.go.tmpl index c25ca5ab73..80a908c720 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 NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) } for j := range s.assignment[i] { if _, err := d.assignment[i][j].SetInterface(s.assignment[i][j]); err != nil { - panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %w", i, j, err)) + panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) } } // inline equivalent of repeatUntilEnd diff --git a/internal/gkr/bls12-377/solver_hints.go b/internal/gkr/bls12-377/solver_hints.go index e82910ea74..7b3d086213 100644 --- a/internal/gkr/bls12-377/solver_hints.go +++ b/internal/gkr/bls12-377/solver_hints.go @@ -65,15 +65,15 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) 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))) + panic(fmt.Sprintf("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)) + panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } 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)) + panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) } } // inline equivalent of repeatUntilEnd diff --git a/internal/gkr/bls12-381/solver_hints.go b/internal/gkr/bls12-381/solver_hints.go index 0a91fe6e0a..d6c1dae79b 100644 --- a/internal/gkr/bls12-381/solver_hints.go +++ b/internal/gkr/bls12-381/solver_hints.go @@ -65,15 +65,15 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) 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))) + panic(fmt.Sprintf("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)) + panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } 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)) + panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) } } // inline equivalent of repeatUntilEnd diff --git a/internal/gkr/bls24-315/solver_hints.go b/internal/gkr/bls24-315/solver_hints.go index ae2380f441..1c2b9b235e 100644 --- a/internal/gkr/bls24-315/solver_hints.go +++ b/internal/gkr/bls24-315/solver_hints.go @@ -65,15 +65,15 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) 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))) + panic(fmt.Sprintf("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)) + panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } 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)) + panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) } } // inline equivalent of repeatUntilEnd diff --git a/internal/gkr/bls24-317/solver_hints.go b/internal/gkr/bls24-317/solver_hints.go index 1145c87225..c844ba6452 100644 --- a/internal/gkr/bls24-317/solver_hints.go +++ b/internal/gkr/bls24-317/solver_hints.go @@ -65,15 +65,15 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) 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))) + panic(fmt.Sprintf("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)) + panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } 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)) + panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) } } // inline equivalent of repeatUntilEnd diff --git a/internal/gkr/bn254/solver_hints.go b/internal/gkr/bn254/solver_hints.go index 4f9610397b..61d5bc7ed3 100644 --- a/internal/gkr/bn254/solver_hints.go +++ b/internal/gkr/bn254/solver_hints.go @@ -65,15 +65,15 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) 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))) + panic(fmt.Sprintf("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)) + panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } 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)) + panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) } } // inline equivalent of repeatUntilEnd diff --git a/internal/gkr/bw6-633/solver_hints.go b/internal/gkr/bw6-633/solver_hints.go index 1312803ba6..2f0254c237 100644 --- a/internal/gkr/bw6-633/solver_hints.go +++ b/internal/gkr/bw6-633/solver_hints.go @@ -65,15 +65,15 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) 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))) + panic(fmt.Sprintf("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)) + panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } 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)) + panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) } } // inline equivalent of repeatUntilEnd diff --git a/internal/gkr/bw6-761/solver_hints.go b/internal/gkr/bw6-761/solver_hints.go index cc4212a165..1f47c9a578 100644 --- a/internal/gkr/bw6-761/solver_hints.go +++ b/internal/gkr/bw6-761/solver_hints.go @@ -65,15 +65,15 @@ func NewSolvingData(info gkrtypes.SolvingInfo, options ...NewSolvingDataOption) 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))) + panic(fmt.Sprintf("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)) + panic(fmt.Sprintf("provided assignment for wire %d has %d instances, expected %d", i, len(s.assignment[i]), info.NbInstances)) } 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)) + panic(fmt.Sprintf("provided assignment for wire %d instance %d is not a valid field element: %v", i, j, err)) } } // inline equivalent of repeatUntilEnd From 0ae0d21e907f6f9182ec7838160277e88f9581ae Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 1 Oct 2025 11:29:23 -0500 Subject: [PATCH 45/50] refactor: don't use stored hint IDs --- constraint/bls12-377/solver.go | 8 +++++--- constraint/bls12-381/solver.go | 8 +++++--- constraint/bls24-315/solver.go | 8 +++++--- constraint/bls24-317/solver.go | 8 +++++--- constraint/bn254/solver.go | 8 +++++--- constraint/bw6-633/solver.go | 8 +++++--- constraint/bw6-761/solver.go | 8 +++++--- .../template/representations/solver.go.tmpl | 10 +++++++--- internal/gkr/gkr.go | 7 +++++-- internal/gkr/gkr_test.go | 17 +++++++++-------- internal/gkr/utils_test.go | 2 +- 11 files changed, 57 insertions(+), 35 deletions(-) diff --git a/constraint/bls12-377/solver.go b/constraint/bls12-377/solver.go index b542c5ef81..c1af0b40cf 100644 --- a/constraint/bls12-377/solver.go +++ b/constraint/bls12-377/solver.go @@ -20,6 +20,7 @@ import ( "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" + gkrhints "github.com/consensys/gnark/internal/gkr" gkr "github.com/consensys/gnark/internal/gkr/bls12-377" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/rs/zerolog" @@ -56,10 +57,11 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) + var gkrHints gkrhints.TestEngineHints 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))) + 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(cs.GkrInfo.HashName, gkrData))) } // parse options opt, err := csolver.NewConfig(opts...) diff --git a/constraint/bls12-381/solver.go b/constraint/bls12-381/solver.go index 545c2819e3..e00ca8c79d 100644 --- a/constraint/bls12-381/solver.go +++ b/constraint/bls12-381/solver.go @@ -20,6 +20,7 @@ import ( "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" + gkrhints "github.com/consensys/gnark/internal/gkr" gkr "github.com/consensys/gnark/internal/gkr/bls12-381" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/rs/zerolog" @@ -56,10 +57,11 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) + var gkrHints gkrhints.TestEngineHints 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))) + 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(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 d7f0d1ad72..badfeeeb7a 100644 --- a/constraint/bls24-315/solver.go +++ b/constraint/bls24-315/solver.go @@ -20,6 +20,7 @@ import ( "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" + gkrhints "github.com/consensys/gnark/internal/gkr" gkr "github.com/consensys/gnark/internal/gkr/bls24-315" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/rs/zerolog" @@ -56,10 +57,11 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) + var gkrHints gkrhints.TestEngineHints 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))) + 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(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 bc3e8df876..da4bc49d82 100644 --- a/constraint/bls24-317/solver.go +++ b/constraint/bls24-317/solver.go @@ -20,6 +20,7 @@ import ( "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" + gkrhints "github.com/consensys/gnark/internal/gkr" gkr "github.com/consensys/gnark/internal/gkr/bls24-317" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/rs/zerolog" @@ -56,10 +57,11 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) + var gkrHints gkrhints.TestEngineHints 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))) + 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(cs.GkrInfo.HashName, gkrData))) } // parse options opt, err := csolver.NewConfig(opts...) diff --git a/constraint/bn254/solver.go b/constraint/bn254/solver.go index a35e25b704..0b1dc6719c 100644 --- a/constraint/bn254/solver.go +++ b/constraint/bn254/solver.go @@ -20,6 +20,7 @@ import ( "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" + gkrhints "github.com/consensys/gnark/internal/gkr" gkr "github.com/consensys/gnark/internal/gkr/bn254" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/rs/zerolog" @@ -56,10 +57,11 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) + var gkrHints gkrhints.TestEngineHints 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))) + 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(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 f3f8b92774..ccd5b425e4 100644 --- a/constraint/bw6-633/solver.go +++ b/constraint/bw6-633/solver.go @@ -20,6 +20,7 @@ import ( "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" + gkrhints "github.com/consensys/gnark/internal/gkr" gkr "github.com/consensys/gnark/internal/gkr/bw6-633" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/rs/zerolog" @@ -56,10 +57,11 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) + var gkrHints gkrhints.TestEngineHints 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))) + 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(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 6734482a28..92c0362f03 100644 --- a/constraint/bw6-761/solver.go +++ b/constraint/bw6-761/solver.go @@ -20,6 +20,7 @@ import ( "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" + gkrhints "github.com/consensys/gnark/internal/gkr" gkr "github.com/consensys/gnark/internal/gkr/bw6-761" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/rs/zerolog" @@ -56,10 +57,11 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) + var gkrHints gkrhints.TestEngineHints 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))) + 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(cs.GkrInfo.HashName, gkrData))) } // parse options opt, err := csolver.NewConfig(opts...) diff --git a/internal/generator/backend/template/representations/solver.go.tmpl b/internal/generator/backend/template/representations/solver.go.tmpl index bac5e6c4ee..0f7b056781 100644 --- a/internal/generator/backend/template/representations/solver.go.tmpl +++ b/internal/generator/backend/template/representations/solver.go.tmpl @@ -12,8 +12,11 @@ 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 }}" + gkrhints "github.com/consensys/gnark/internal/gkr" + {{- end }} "github.com/consensys/gnark/internal/gkr/gkrtypes" {{ template "import_fr" . }} ) @@ -48,10 +51,11 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) + var gkrHints gkrhints.TestEngineHints 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))) + 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(cs.GkrInfo.HashName, gkrData))) } {{ end -}} diff --git a/internal/gkr/gkr.go b/internal/gkr/gkr.go index 955ad8a354..7cfe315a32 100644 --- a/internal/gkr/gkr.go +++ b/internal/gkr/gkr.go @@ -341,7 +341,10 @@ func (p Proof) Serialize() []frontend.Variable { return res } -func computeLogNbInstances(wires []*gkrtypes.Wire, serializedProofLen int) int { +// 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 { partialEvalElemsPerVar := 0 for _, w := range wires { if !w.NoProof() { @@ -366,7 +369,7 @@ func (r *variablesReader) hasNextN(n int) bool { func DeserializeProof(sorted []*gkrtypes.Wire, serializedProof []frontend.Variable) (Proof, error) { proof := make(Proof, len(sorted)) - logNbInstances := computeLogNbInstances(sorted, len(serializedProof)) + logNbInstances := ComputeLogNbInstances(sorted, len(serializedProof)) reader := variablesReader(serializedProof) for i, wI := range sorted { diff --git a/internal/gkr/gkr_test.go b/internal/gkr/gkr_test.go index faf8eadc95..29b95c6c4c 100644 --- a/internal/gkr/gkr_test.go +++ b/internal/gkr/gkr_test.go @@ -1,4 +1,4 @@ -package gkr +package gkr_test import ( "encoding/json" @@ -10,6 +10,7 @@ import ( "github.com/consensys/gnark/backend" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" fiatshamir "github.com/consensys/gnark/std/fiat-shamir" @@ -108,7 +109,7 @@ type GkrVerifierCircuit struct { func (c *GkrVerifierCircuit) Define(api frontend.API) error { var testCase *TestCase - var proof Proof + var proof gkr.Proof var err error //var proofRef Proof if testCase, err = getTestCase(c.TestCaseName); err != nil { @@ -116,7 +117,7 @@ func (c *GkrVerifierCircuit) Define(api frontend.API) error { } sorted := testCase.Circuit.TopologicalSort() - if proof, err = DeserializeProof(sorted, c.SerializedProof); err != nil { + if proof, err = gkr.DeserializeProof(sorted, c.SerializedProof); err != nil { return err } assignment := makeInOutAssignment(testCase.Circuit, c.Input, c.Output) @@ -130,7 +131,7 @@ func (c *GkrVerifierCircuit) Define(api frontend.API) error { } } - return Verify(api, testCase.Circuit, assignment, proof, fiatshamir.WithHash(hsh)) + return gkr.Verify(api, testCase.Circuit, assignment, proof, fiatshamir.WithHash(hsh)) } func makeInOutAssignment(c gkrtypes.Circuit, inputValues [][]frontend.Variable, outputValues [][]frontend.Variable) gkrtypes.WireAssignment { @@ -158,7 +159,7 @@ func fillWithBlanks(slice [][]frontend.Variable, size int) { type TestCase struct { Circuit gkrtypes.Circuit Hash HashDescription - Proof Proof + Proof gkr.Proof Input [][]frontend.Variable Output [][]frontend.Variable Name string @@ -215,8 +216,8 @@ type PrintableSumcheckProof struct { PartialSumPolys [][]interface{} `json:"partialSumPolys"` } -func unmarshalProof(printable PrintableProof) (proof Proof) { - proof = make(Proof, len(printable)) +func unmarshalProof(printable PrintableProof) (proof gkr.Proof) { + proof = make(gkr.Proof, len(printable)) for i := range printable { if printable[i].FinalEvalProof != nil { @@ -245,7 +246,7 @@ func TestLogNbInstances(t *testing.T) { assert.NoError(t, err) wires := testCase.Circuit.TopologicalSort() serializedProof := testCase.Proof.Serialize() - logNbInstances := computeLogNbInstances(wires, len(serializedProof)) + logNbInstances := gkr.ComputeLogNbInstances(wires, len(serializedProof)) assert.Equal(t, 1, logNbInstances) } } diff --git a/internal/gkr/utils_test.go b/internal/gkr/utils_test.go index d0c489a698..68d1e822d3 100644 --- a/internal/gkr/utils_test.go +++ b/internal/gkr/utils_test.go @@ -1,4 +1,4 @@ -package gkr +package gkr_test import ( "fmt" From 82f2c9eebe7ae29bdc8f99e047125671b12750b1 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 1 Oct 2025 11:46:13 -0500 Subject: [PATCH 46/50] resolve some import cycles cause by hint reference --- std/fiat-shamir/transcript_test.go | 9 +- std/internal/mimc/mimc_test.go | 11 +- std/math/bitslice/partition_test.go | 9 +- .../fieldextension/fieldextension_test.go | 11 +- std/math/uints/uint8_test.go | 105 +++++++++--------- std/multicommit/nativecommit_test.go | 17 +-- 6 files changed, 84 insertions(+), 78 deletions(-) diff --git a/std/fiat-shamir/transcript_test.go b/std/fiat-shamir/transcript_test.go index 7fd67bdba6..ce0e4efb8b 100644 --- a/std/fiat-shamir/transcript_test.go +++ b/std/fiat-shamir/transcript_test.go @@ -1,7 +1,7 @@ // Copyright 2020-2025 Consensys Software Inc. // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. -package fiatshamir +package fiatshamir_test import ( "crypto/rand" @@ -9,12 +9,13 @@ import ( "testing" "github.com/consensys/gnark-crypto/ecc" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + gcfiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/internal/utils" + fiatshamir "github.com/consensys/gnark/std/fiat-shamir" "github.com/consensys/gnark/std/hash/mimc" "github.com/consensys/gnark/test" ) @@ -33,7 +34,7 @@ func (circuit *FiatShamirCircuit) Define(api frontend.API) error { } // New transcript with 3 challenges to be derived - tsSnark := NewTranscript(api, &hSnark, []string{"alpha", "beta", "gamma"}) + tsSnark := fiatshamir.NewTranscript(api, &hSnark, []string{"alpha", "beta", "gamma"}) // Bind challenges if err := tsSnark.Bind("alpha", circuit.Bindings[0][:]); err != nil { @@ -88,7 +89,7 @@ func TestFiatShamir(t *testing.T) { for curveID, h := range testData { // instantiate the hash and the transcript in plain go - ts := fiatshamir.NewTranscript(h.New(), "alpha", "beta", "gamma") + ts := gcfiatshamir.NewTranscript(h.New(), "alpha", "beta", "gamma") var bindings [3][4]*big.Int for i := 0; i < 3; i++ { diff --git a/std/internal/mimc/mimc_test.go b/std/internal/mimc/mimc_test.go index 2fbc48a756..5478f0a6cf 100644 --- a/std/internal/mimc/mimc_test.go +++ b/std/internal/mimc/mimc_test.go @@ -1,7 +1,7 @@ // Copyright 2020-2025 Consensys Software Inc. // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. -package mimc +package mimc_test import ( "crypto/rand" @@ -13,6 +13,7 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/internal/mimc" "github.com/consensys/gnark/test" ) @@ -22,7 +23,7 @@ type mimcCircuit struct { } func (circuit *mimcCircuit) Define(api frontend.API) error { - mimc, err := NewMiMC(api) + mimc, err := mimc.NewMiMC(api) if err != nil { return err } @@ -93,8 +94,8 @@ type stateStoreTestCircuit struct { func (s *stateStoreTestCircuit) Define(api frontend.API) error { - hsh1, err1 := NewMiMC(api) - hsh2, err2 := NewMiMC(api) + hsh1, err1 := mimc.NewMiMC(api) + hsh2, err2 := mimc.NewMiMC(api) if err1 != nil || err2 != nil { return fmt.Errorf("could not instantiate the MIMC hasher: %w", errors.Join(err1, err2)) @@ -161,7 +162,7 @@ type recoveredStateTestCircuit struct { } func (c *recoveredStateTestCircuit) Define(api frontend.API) error { - h, err := NewMiMC(api) + h, err := mimc.NewMiMC(api) if err != nil { return fmt.Errorf("initialize hash: %w", err) } diff --git a/std/math/bitslice/partition_test.go b/std/math/bitslice/partition_test.go index a098e824b7..8095b89070 100644 --- a/std/math/bitslice/partition_test.go +++ b/std/math/bitslice/partition_test.go @@ -1,9 +1,10 @@ -package bitslice +package bitslice_test import ( "testing" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/math/bitslice" "github.com/consensys/gnark/test" ) @@ -17,11 +18,11 @@ type partitionCircuit struct { } func (c *partitionCircuit) Define(api frontend.API) error { - var opts []Option + var opts []bitslice.Option if c.nbDigitsOpt > 0 { - opts = append(opts, WithNbDigits(c.nbDigitsOpt)) + opts = append(opts, bitslice.WithNbDigits(c.nbDigitsOpt)) } - lower, upper := Partition(api, c.In, c.Split, opts...) + lower, upper := bitslice.Partition(api, c.In, c.Split, opts...) api.AssertIsEqual(lower, c.ExpLower) api.AssertIsEqual(upper, c.ExpUpper) return nil diff --git a/std/math/fieldextension/fieldextension_test.go b/std/math/fieldextension/fieldextension_test.go index dae14ed989..f72d6066e8 100644 --- a/std/math/fieldextension/fieldextension_test.go +++ b/std/math/fieldextension/fieldextension_test.go @@ -1,10 +1,11 @@ -package fieldextension +package fieldextension_test import ( "testing" "github.com/consensys/gnark-crypto/field/babybear" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/math/fieldextension" "github.com/consensys/gnark/test" ) @@ -14,7 +15,7 @@ type ReduceCircut struct { } func (c *ReduceCircut) Define(api frontend.API) error { - e, err := NewExtension(api) + e, err := fieldextension.NewExtension(api) if err != nil { return err } @@ -75,7 +76,7 @@ type AddCircuit struct { } func (c *AddCircuit) Define(api frontend.API) error { - e, err := NewExtension(api) + e, err := fieldextension.NewExtension(api) if err != nil { return err } @@ -122,7 +123,7 @@ type SubCircuit struct { } func (c *SubCircuit) Define(api frontend.API) error { - e, err := NewExtension(api) + e, err := fieldextension.NewExtension(api) if err != nil { return err } @@ -169,7 +170,7 @@ type MulCircuit struct { } func (c *MulCircuit) Define(api frontend.API) error { - e, err := NewExtension(api) + e, err := fieldextension.NewExtension(api) if err != nil { return err } diff --git a/std/math/uints/uint8_test.go b/std/math/uints/uint8_test.go index 1ec6ce4c1b..67331ab182 100644 --- a/std/math/uints/uint8_test.go +++ b/std/math/uints/uint8_test.go @@ -1,4 +1,4 @@ -package uints +package uints_test import ( "fmt" @@ -6,17 +6,18 @@ import ( "testing" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/math/uints" "github.com/consensys/gnark/test" ) type lrotCirc struct { - In U32 - Out U32 + In uints.U32 + Out uints.U32 Shift int } func (c *lrotCirc) Define(api frontend.API) error { - uapi, err := New[U32](api) + uapi, err := uints.New[uints.U32](api) if err != nil { return err } @@ -27,27 +28,27 @@ func (c *lrotCirc) Define(api frontend.API) error { func TestLeftRotation(t *testing.T) { assert := test.NewAssert(t) - assert.CheckCircuit(&lrotCirc{Shift: 4}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: 4, Out: NewU32(bits.RotateLeft32(0x12345678, 4))})) - assert.CheckCircuit(&lrotCirc{Shift: 14}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: 14, Out: NewU32(bits.RotateLeft32(0x12345678, 14))})) - assert.CheckCircuit(&lrotCirc{Shift: 3}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: 3, Out: NewU32(bits.RotateLeft32(0x12345678, 3))})) - assert.CheckCircuit(&lrotCirc{Shift: 11}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: 11, Out: NewU32(bits.RotateLeft32(0x12345678, 11))})) + assert.CheckCircuit(&lrotCirc{Shift: 4}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: 4, Out: uints.NewU32(bits.RotateLeft32(0x12345678, 4))})) + assert.CheckCircuit(&lrotCirc{Shift: 14}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: 14, Out: uints.NewU32(bits.RotateLeft32(0x12345678, 14))})) + assert.CheckCircuit(&lrotCirc{Shift: 3}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: 3, Out: uints.NewU32(bits.RotateLeft32(0x12345678, 3))})) + assert.CheckCircuit(&lrotCirc{Shift: 11}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: 11, Out: uints.NewU32(bits.RotateLeft32(0x12345678, 11))})) // full block - assert.CheckCircuit(&lrotCirc{Shift: 16}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: 16, Out: NewU32(bits.RotateLeft32(0x12345678, 16))})) + assert.CheckCircuit(&lrotCirc{Shift: 16}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: 16, Out: uints.NewU32(bits.RotateLeft32(0x12345678, 16))})) // negative rotations - assert.CheckCircuit(&lrotCirc{Shift: -4}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: -4, Out: NewU32(bits.RotateLeft32(0x12345678, -4))})) - assert.CheckCircuit(&lrotCirc{Shift: -14}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: -14, Out: NewU32(bits.RotateLeft32(0x12345678, -14))})) - assert.CheckCircuit(&lrotCirc{Shift: -3}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: -3, Out: NewU32(bits.RotateLeft32(0x12345678, -3))})) - assert.CheckCircuit(&lrotCirc{Shift: -11}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: -11, Out: NewU32(bits.RotateLeft32(0x12345678, -11))})) - assert.CheckCircuit(&lrotCirc{Shift: -16}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: -16, Out: NewU32(bits.RotateLeft32(0x12345678, -16))})) + assert.CheckCircuit(&lrotCirc{Shift: -4}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: -4, Out: uints.NewU32(bits.RotateLeft32(0x12345678, -4))})) + assert.CheckCircuit(&lrotCirc{Shift: -14}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: -14, Out: uints.NewU32(bits.RotateLeft32(0x12345678, -14))})) + assert.CheckCircuit(&lrotCirc{Shift: -3}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: -3, Out: uints.NewU32(bits.RotateLeft32(0x12345678, -3))})) + assert.CheckCircuit(&lrotCirc{Shift: -11}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: -11, Out: uints.NewU32(bits.RotateLeft32(0x12345678, -11))})) + assert.CheckCircuit(&lrotCirc{Shift: -16}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: -16, Out: uints.NewU32(bits.RotateLeft32(0x12345678, -16))})) } type rshiftCircuit struct { - In, Expected U32 + In, Expected uints.U32 Shift int } func (c *rshiftCircuit) Define(api frontend.API) error { - uapi, err := New[U32](api) + uapi, err := uints.New[uints.U32](api) if err != nil { return err } @@ -58,19 +59,19 @@ func (c *rshiftCircuit) Define(api frontend.API) error { func TestRshift(t *testing.T) { assert := test.NewAssert(t) - assert.CheckCircuit(&rshiftCircuit{Shift: 4}, test.WithValidAssignment(&rshiftCircuit{Shift: 4, In: NewU32(0x12345678), Expected: NewU32(0x12345678 >> 4)})) - assert.CheckCircuit(&rshiftCircuit{Shift: 12}, test.WithValidAssignment(&rshiftCircuit{Shift: 12, In: NewU32(0x12345678), Expected: NewU32(0x12345678 >> 12)})) - assert.CheckCircuit(&rshiftCircuit{Shift: 3}, test.WithValidAssignment(&rshiftCircuit{Shift: 3, In: NewU32(0x12345678), Expected: NewU32(0x12345678 >> 3)})) - assert.CheckCircuit(&rshiftCircuit{Shift: 11}, test.WithValidAssignment(&rshiftCircuit{Shift: 11, In: NewU32(0x12345678), Expected: NewU32(0x12345678 >> 11)})) + assert.CheckCircuit(&rshiftCircuit{Shift: 4}, test.WithValidAssignment(&rshiftCircuit{Shift: 4, In: uints.NewU32(0x12345678), Expected: uints.NewU32(0x12345678 >> 4)})) + assert.CheckCircuit(&rshiftCircuit{Shift: 12}, test.WithValidAssignment(&rshiftCircuit{Shift: 12, In: uints.NewU32(0x12345678), Expected: uints.NewU32(0x12345678 >> 12)})) + assert.CheckCircuit(&rshiftCircuit{Shift: 3}, test.WithValidAssignment(&rshiftCircuit{Shift: 3, In: uints.NewU32(0x12345678), Expected: uints.NewU32(0x12345678 >> 3)})) + assert.CheckCircuit(&rshiftCircuit{Shift: 11}, test.WithValidAssignment(&rshiftCircuit{Shift: 11, In: uints.NewU32(0x12345678), Expected: uints.NewU32(0x12345678 >> 11)})) } -type valueOfCircuit[T Long] struct { +type valueOfCircuit[T uints.Long] struct { In frontend.Variable Expected T } func (c *valueOfCircuit[T]) Define(api frontend.API) error { - uapi, err := New[T](api) + uapi, err := uints.New[T](api) if err != nil { return err } @@ -81,18 +82,18 @@ func (c *valueOfCircuit[T]) Define(api frontend.API) error { func TestValueOf(t *testing.T) { assert := test.NewAssert(t) - assert.CheckCircuit(&valueOfCircuit[U64]{}, test.WithValidAssignment(&valueOfCircuit[U64]{In: 0x12345678, Expected: [8]U8{NewU8(0x78), NewU8(0x56), NewU8(0x34), NewU8(0x12), NewU8(0), NewU8(0), NewU8(0), NewU8(0)}})) - assert.CheckCircuit(&valueOfCircuit[U32]{}, test.WithValidAssignment(&valueOfCircuit[U32]{In: 0x12345678, Expected: [4]U8{NewU8(0x78), NewU8(0x56), NewU8(0x34), NewU8(0x12)}})) - assert.CheckCircuit(&valueOfCircuit[U32]{}, test.WithInvalidAssignment(&valueOfCircuit[U32]{In: 0x1234567812345678, Expected: [4]U8{NewU8(0x78), NewU8(0x56), NewU8(0x34), NewU8(0x12)}})) + assert.CheckCircuit(&valueOfCircuit[uints.U64]{}, test.WithValidAssignment(&valueOfCircuit[uints.U64]{In: 0x12345678, Expected: [8]uints.U8{uints.NewU8(0x78), uints.NewU8(0x56), uints.NewU8(0x34), uints.NewU8(0x12), uints.NewU8(0), uints.NewU8(0), uints.NewU8(0), uints.NewU8(0)}})) + assert.CheckCircuit(&valueOfCircuit[uints.U32]{}, test.WithValidAssignment(&valueOfCircuit[uints.U32]{In: 0x12345678, Expected: [4]uints.U8{uints.NewU8(0x78), uints.NewU8(0x56), uints.NewU8(0x34), uints.NewU8(0x12)}})) + assert.CheckCircuit(&valueOfCircuit[uints.U32]{}, test.WithInvalidAssignment(&valueOfCircuit[uints.U32]{In: 0x1234567812345678, Expected: [4]uints.U8{uints.NewU8(0x78), uints.NewU8(0x56), uints.NewU8(0x34), uints.NewU8(0x12)}})) } type addCircuit struct { - In [2]U32 - Expected U32 + In [2]uints.U32 + Expected uints.U32 } func (c *addCircuit) Define(api frontend.API) error { - uapi, err := New[U32](api) + uapi, err := uints.New[uints.U32](api) if err != nil { return err } @@ -103,19 +104,19 @@ func (c *addCircuit) Define(api frontend.API) error { func TestAdd(t *testing.T) { assert := test.NewAssert(t) - assert.CheckCircuit(&addCircuit{}, test.WithValidAssignment(&addCircuit{In: [2]U32{NewU32(^uint32(0)), NewU32(2)}, Expected: NewU32(1)})) + assert.CheckCircuit(&addCircuit{}, test.WithValidAssignment(&addCircuit{In: [2]uints.U32{uints.NewU32(^uint32(0)), uints.NewU32(2)}, Expected: uints.NewU32(1)})) } -// Add tests where we try to initialize unconstrained U8 +// Add tests where we try to initialize unconstrained uints.U8 type ConstrainedCheckCircuit struct { - A, B, C U8 + A, B, C uints.U8 mode int } func (c *ConstrainedCheckCircuit) Define(api frontend.API) error { - uapi, err := NewBytes(api) + uapi, err := uints.NewBytes(api) if err != nil { - return fmt.Errorf("NewBytes: %w", err) + return fmt.Errorf("uints.NewBytes: %w", err) } switch c.mode { case 0: @@ -135,29 +136,29 @@ func TestConstrainedCircuit(t *testing.T) { assert := test.NewAssert(t) assert.Run(func(assert *test.Assert) { - assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 0}, test.WithValidAssignment(&ConstrainedCheckCircuit{A: NewU8(0x0f), B: NewU8(0xf0), C: NewU8(0x00)})) - assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 0}, test.WithInvalidAssignment(&ConstrainedCheckCircuit{A: U8{Val: 0x00ff}, B: U8{Val: 0xf0f}, C: U8{Val: 0x00f}})) + assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 0}, test.WithValidAssignment(&ConstrainedCheckCircuit{A: uints.NewU8(0x0f), B: uints.NewU8(0xf0), C: uints.NewU8(0x00)})) + assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 0}, test.WithInvalidAssignment(&ConstrainedCheckCircuit{A: uints.U8{Val: 0x00ff}, B: uints.U8{Val: 0xf0f}, C: uints.U8{Val: 0x00f}})) }, "and") assert.Run(func(assert *test.Assert) { - assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 1}, test.WithValidAssignment(&ConstrainedCheckCircuit{A: NewU8(0x0f), B: NewU8(0xf0), C: NewU8(0xff)})) - assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 1}, test.WithInvalidAssignment(&ConstrainedCheckCircuit{A: U8{Val: 0x0f00}, B: U8{Val: 0x0f0}, C: U8{Val: 0xff0}})) + assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 1}, test.WithValidAssignment(&ConstrainedCheckCircuit{A: uints.NewU8(0x0f), B: uints.NewU8(0xf0), C: uints.NewU8(0xff)})) + assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 1}, test.WithInvalidAssignment(&ConstrainedCheckCircuit{A: uints.U8{Val: 0x0f00}, B: uints.U8{Val: 0x0f0}, C: uints.U8{Val: 0xff0}})) }, "or") assert.Run(func(assert *test.Assert) { - assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 2}, test.WithValidAssignment(&ConstrainedCheckCircuit{A: NewU8(0x0f), B: NewU8(0xf0), C: NewU8(0xff)})) - assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 2}, test.WithInvalidAssignment(&ConstrainedCheckCircuit{A: U8{Val: 0x0f0f}, B: U8{Val: 0x0ff}, C: U8{Val: 0xff0}})) + assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 2}, test.WithValidAssignment(&ConstrainedCheckCircuit{A: uints.NewU8(0x0f), B: uints.NewU8(0xf0), C: uints.NewU8(0xff)})) + assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 2}, test.WithInvalidAssignment(&ConstrainedCheckCircuit{A: uints.U8{Val: 0x0f0f}, B: uints.U8{Val: 0x0ff}, C: uints.U8{Val: 0xff0}})) }, "xor") } type ToValueCircuit struct { - In U32 + In uints.U32 withCheck bool Expected frontend.Variable } func (c *ToValueCircuit) Define(api frontend.API) error { - uapi, err := New[U32](api) + uapi, err := uints.New[uints.U32](api) if err != nil { - return fmt.Errorf("New: %w", err) + return fmt.Errorf("uints.New: %w", err) } res := uapi.ToValue(c.In) if c.withCheck { @@ -168,19 +169,19 @@ func (c *ToValueCircuit) Define(api frontend.API) error { func TestToValue(t *testing.T) { assert := test.NewAssert(t) - assert.CheckCircuit(&ToValueCircuit{withCheck: true}, test.WithValidAssignment(&ToValueCircuit{In: NewU32(0x12345678), Expected: 0x12345678})) - assert.CheckCircuit(&ToValueCircuit{withCheck: false}, test.WithInvalidAssignment(&ToValueCircuit{In: [4]U8{{Val: 0x780}, {Val: 0x56}, {Val: 0x34}, {Val: 0x12}}, Expected: 0x12345678})) + assert.CheckCircuit(&ToValueCircuit{withCheck: true}, test.WithValidAssignment(&ToValueCircuit{In: uints.NewU32(0x12345678), Expected: 0x12345678})) + assert.CheckCircuit(&ToValueCircuit{withCheck: false}, test.WithInvalidAssignment(&ToValueCircuit{In: [4]uints.U8{{Val: 0x780}, {Val: 0x56}, {Val: 0x34}, {Val: 0x12}}, Expected: 0x12345678})) } type ValueWitnessCircuit struct { - In U8 + In uints.U8 Expected frontend.Variable } func (c *ValueWitnessCircuit) Define(api frontend.API) error { - uapi, err := NewBytes(api) + uapi, err := uints.NewBytes(api) if err != nil { - return fmt.Errorf("NewBytes: %w", err) + return fmt.Errorf("uints.NewBytes: %w", err) } res := uapi.Value(c.In) api.AssertIsEqual(res, c.Expected) @@ -189,8 +190,8 @@ func (c *ValueWitnessCircuit) Define(api frontend.API) error { func TestValueWitness(t *testing.T) { assert := test.NewAssert(t) - assert.CheckCircuit(&ValueWitnessCircuit{}, test.WithValidAssignment(&ValueWitnessCircuit{In: NewU8(0x12), Expected: 0x12})) - assert.CheckCircuit(&ValueWitnessCircuit{}, test.WithInvalidAssignment(&ValueWitnessCircuit{In: U8{Val: 0x1234}, Expected: 0x1234})) + assert.CheckCircuit(&ValueWitnessCircuit{}, test.WithValidAssignment(&ValueWitnessCircuit{In: uints.NewU8(0x12), Expected: 0x12})) + assert.CheckCircuit(&ValueWitnessCircuit{}, test.WithInvalidAssignment(&ValueWitnessCircuit{In: uints.U8{Val: 0x1234}, Expected: 0x1234})) } type ValueInCircuitCircuit struct { @@ -199,11 +200,11 @@ type ValueInCircuitCircuit struct { } func (c *ValueInCircuitCircuit) Define(api frontend.API) error { - uapi, err := NewBytes(api) + uapi, err := uints.NewBytes(api) if err != nil { - return fmt.Errorf("NewBytes: %w", err) + return fmt.Errorf("uints.NewBytes: %w", err) } - in := U8{Val: c.In} + in := uints.U8{Val: c.In} res := uapi.Value(in) api.AssertIsEqual(res, c.Expected) return nil diff --git a/std/multicommit/nativecommit_test.go b/std/multicommit/nativecommit_test.go index 84de07ee72..ce0ef619ab 100644 --- a/std/multicommit/nativecommit_test.go +++ b/std/multicommit/nativecommit_test.go @@ -1,4 +1,4 @@ -package multicommit +package multicommit_test import ( "testing" @@ -10,6 +10,7 @@ import ( "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/internal/widecommitter" "github.com/consensys/gnark/std/math/fieldextension" + "github.com/consensys/gnark/std/multicommit" "github.com/consensys/gnark/test" ) @@ -18,8 +19,8 @@ type noRecursionCircuit struct { } func (c *noRecursionCircuit) Define(api frontend.API) error { - WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { - WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { return nil }, commitment) + multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { + multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { return nil }, commitment) return nil }, c.X) return nil @@ -39,12 +40,12 @@ type multipleCommitmentCircuit struct { func (c *multipleCommitmentCircuit) Define(api frontend.API) error { var stored frontend.Variable // first callback receives first unique commitment derived from the root commitment - WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { + multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { api.AssertIsDifferent(c.X, commitment) stored = commitment return nil }, c.X) - WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { + multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { api.AssertIsDifferent(stored, commitment) return nil }, c.X) @@ -63,7 +64,7 @@ type noCommitVariable struct { } func (c *noCommitVariable) Define(api frontend.API) error { - WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { return nil }) + multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { return nil }) return nil } @@ -83,12 +84,12 @@ type wideCommitment struct { func (c *wideCommitment) Define(api frontend.API) error { if c.withCommitment { - WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { + multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { api.AssertIsDifferent(commitment, 0) return nil }, c.X) } - WithWideCommitment(api, func(api frontend.API, commitment []frontend.Variable) error { + multicommit.WithWideCommitment(api, func(api frontend.API, commitment []frontend.Variable) error { fe, err := fieldextension.NewExtension(api, fieldextension.WithDegree(8)) if err != nil { return err From 26bbef6d54838be1dc70ad40c24ece8e01cf4eac Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 1 Oct 2025 11:51:04 -0500 Subject: [PATCH 47/50] feat: gkrhints package to avoid import cycle --- constraint/bls12-377/solver.go | 2 +- constraint/bls12-381/solver.go | 2 +- constraint/bls24-315/solver.go | 2 +- constraint/bls24-317/solver.go | 2 +- constraint/bn254/solver.go | 2 +- constraint/bw6-633/solver.go | 2 +- constraint/bw6-761/solver.go | 2 +- .../template/representations/solver.go.tmpl | 2 +- internal/gkr/gkr_test.go | 17 ++++++++--------- internal/gkr/{ => gkrhints}/engine_hints.go | 2 +- internal/gkr/utils_test.go | 2 +- std/gkrapi/compile.go | 7 ++++--- 12 files changed, 22 insertions(+), 22 deletions(-) rename internal/gkr/{ => gkrhints}/engine_hints.go (99%) diff --git a/constraint/bls12-377/solver.go b/constraint/bls12-377/solver.go index c1af0b40cf..bfc2929074 100644 --- a/constraint/bls12-377/solver.go +++ b/constraint/bls12-377/solver.go @@ -20,8 +20,8 @@ import ( "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" - gkrhints "github.com/consensys/gnark/internal/gkr" 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" diff --git a/constraint/bls12-381/solver.go b/constraint/bls12-381/solver.go index e00ca8c79d..1ef8fd17ea 100644 --- a/constraint/bls12-381/solver.go +++ b/constraint/bls12-381/solver.go @@ -20,8 +20,8 @@ import ( "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" - gkrhints "github.com/consensys/gnark/internal/gkr" 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" diff --git a/constraint/bls24-315/solver.go b/constraint/bls24-315/solver.go index badfeeeb7a..4dc7877987 100644 --- a/constraint/bls24-315/solver.go +++ b/constraint/bls24-315/solver.go @@ -20,8 +20,8 @@ import ( "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" - gkrhints "github.com/consensys/gnark/internal/gkr" 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" diff --git a/constraint/bls24-317/solver.go b/constraint/bls24-317/solver.go index da4bc49d82..6000c2bc70 100644 --- a/constraint/bls24-317/solver.go +++ b/constraint/bls24-317/solver.go @@ -20,8 +20,8 @@ import ( "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" - gkrhints "github.com/consensys/gnark/internal/gkr" 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" diff --git a/constraint/bn254/solver.go b/constraint/bn254/solver.go index 0b1dc6719c..34bccc08e6 100644 --- a/constraint/bn254/solver.go +++ b/constraint/bn254/solver.go @@ -20,8 +20,8 @@ import ( "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" - gkrhints "github.com/consensys/gnark/internal/gkr" 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" diff --git a/constraint/bw6-633/solver.go b/constraint/bw6-633/solver.go index ccd5b425e4..ebb3fbd274 100644 --- a/constraint/bw6-633/solver.go +++ b/constraint/bw6-633/solver.go @@ -20,8 +20,8 @@ import ( "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" - gkrhints "github.com/consensys/gnark/internal/gkr" 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" diff --git a/constraint/bw6-761/solver.go b/constraint/bw6-761/solver.go index 92c0362f03..1526c65c20 100644 --- a/constraint/bw6-761/solver.go +++ b/constraint/bw6-761/solver.go @@ -20,8 +20,8 @@ import ( "github.com/consensys/gnark/constraint" csolver "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/constraint/solver/gkrgates" - gkrhints "github.com/consensys/gnark/internal/gkr" 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" diff --git a/internal/generator/backend/template/representations/solver.go.tmpl b/internal/generator/backend/template/representations/solver.go.tmpl index 0f7b056781..0ad544fd9c 100644 --- a/internal/generator/backend/template/representations/solver.go.tmpl +++ b/internal/generator/backend/template/representations/solver.go.tmpl @@ -15,7 +15,7 @@ import ( {{- if not .NoGKR }} "github.com/consensys/gnark/constraint/solver/gkrgates" gkr "github.com/consensys/gnark/internal/gkr/{{ toLower .Curve }}" - gkrhints "github.com/consensys/gnark/internal/gkr" + "github.com/consensys/gnark/internal/gkr/gkrhints" {{- end }} "github.com/consensys/gnark/internal/gkr/gkrtypes" {{ template "import_fr" . }} diff --git a/internal/gkr/gkr_test.go b/internal/gkr/gkr_test.go index 29b95c6c4c..02c6d6cac2 100644 --- a/internal/gkr/gkr_test.go +++ b/internal/gkr/gkr_test.go @@ -1,4 +1,4 @@ -package gkr_test +package gkr import ( "encoding/json" @@ -10,7 +10,6 @@ import ( "github.com/consensys/gnark/backend" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/gkr" "github.com/consensys/gnark/internal/gkr/gkrtesting" "github.com/consensys/gnark/internal/gkr/gkrtypes" fiatshamir "github.com/consensys/gnark/std/fiat-shamir" @@ -109,7 +108,7 @@ type GkrVerifierCircuit struct { func (c *GkrVerifierCircuit) Define(api frontend.API) error { var testCase *TestCase - var proof gkr.Proof + var proof Proof var err error //var proofRef Proof if testCase, err = getTestCase(c.TestCaseName); err != nil { @@ -117,7 +116,7 @@ func (c *GkrVerifierCircuit) Define(api frontend.API) error { } sorted := testCase.Circuit.TopologicalSort() - if proof, err = gkr.DeserializeProof(sorted, c.SerializedProof); err != nil { + if proof, err = DeserializeProof(sorted, c.SerializedProof); err != nil { return err } assignment := makeInOutAssignment(testCase.Circuit, c.Input, c.Output) @@ -131,7 +130,7 @@ func (c *GkrVerifierCircuit) Define(api frontend.API) error { } } - return gkr.Verify(api, testCase.Circuit, assignment, proof, fiatshamir.WithHash(hsh)) + return Verify(api, testCase.Circuit, assignment, proof, fiatshamir.WithHash(hsh)) } func makeInOutAssignment(c gkrtypes.Circuit, inputValues [][]frontend.Variable, outputValues [][]frontend.Variable) gkrtypes.WireAssignment { @@ -159,7 +158,7 @@ func fillWithBlanks(slice [][]frontend.Variable, size int) { type TestCase struct { Circuit gkrtypes.Circuit Hash HashDescription - Proof gkr.Proof + Proof Proof Input [][]frontend.Variable Output [][]frontend.Variable Name string @@ -216,8 +215,8 @@ type PrintableSumcheckProof struct { PartialSumPolys [][]interface{} `json:"partialSumPolys"` } -func unmarshalProof(printable PrintableProof) (proof gkr.Proof) { - proof = make(gkr.Proof, len(printable)) +func unmarshalProof(printable PrintableProof) (proof Proof) { + proof = make(Proof, len(printable)) for i := range printable { if printable[i].FinalEvalProof != nil { @@ -246,7 +245,7 @@ func TestLogNbInstances(t *testing.T) { assert.NoError(t, err) wires := testCase.Circuit.TopologicalSort() serializedProof := testCase.Proof.Serialize() - logNbInstances := gkr.ComputeLogNbInstances(wires, len(serializedProof)) + logNbInstances := ComputeLogNbInstances(wires, len(serializedProof)) assert.Equal(t, 1, logNbInstances) } } diff --git a/internal/gkr/engine_hints.go b/internal/gkr/gkrhints/engine_hints.go similarity index 99% rename from internal/gkr/engine_hints.go rename to internal/gkr/gkrhints/engine_hints.go index 5dbc22c811..3b74cdf4a0 100644 --- a/internal/gkr/engine_hints.go +++ b/internal/gkr/gkrhints/engine_hints.go @@ -1,4 +1,4 @@ -package gkr +package gkrhints import ( "errors" diff --git a/internal/gkr/utils_test.go b/internal/gkr/utils_test.go index 68d1e822d3..d0c489a698 100644 --- a/internal/gkr/utils_test.go +++ b/internal/gkr/utils_test.go @@ -1,4 +1,4 @@ -package gkr_test +package gkr import ( "fmt" diff --git a/std/gkrapi/compile.go b/std/gkrapi/compile.go index 95f05a64b0..a255a4bd97 100644 --- a/std/gkrapi/compile.go +++ b/std/gkrapi/compile.go @@ -9,6 +9,7 @@ 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/gkrhints" "github.com/consensys/gnark/internal/gkr/gkrinfo" "github.com/consensys/gnark/internal/gkr/gkrtypes" "github.com/consensys/gnark/internal/utils" @@ -32,8 +33,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 - hints *gadget.TestEngineHints // hints for the GKR circuit, used for testing purposes + api frontend.API // the parent API used for hints + hints *gkrhints.TestEngineHints // hints for the GKR circuit, used for testing purposes } // New creates a new GKR API @@ -70,7 +71,7 @@ func (api *API) Compile(parentApi frontend.API, fiatshamirHashName string, optio res.toStore.HashName = fiatshamirHashName var err error - res.hints, err = gadget.NewTestEngineHints(&res.toStore) + res.hints, err = gkrhints.NewTestEngineHints(&res.toStore) if err != nil { panic(fmt.Errorf("failed to call GKR hints: %w", err)) } From 7e1c10f485222a184d5395c9879d79466697591c Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 1 Oct 2025 11:51:11 -0500 Subject: [PATCH 48/50] Revert "resolve some import cycles cause by hint reference" This reverts commit 82f2c9eebe7ae29bdc8f99e047125671b12750b1. --- std/fiat-shamir/transcript_test.go | 9 +- std/internal/mimc/mimc_test.go | 11 +- std/math/bitslice/partition_test.go | 9 +- .../fieldextension/fieldextension_test.go | 11 +- std/math/uints/uint8_test.go | 105 +++++++++--------- std/multicommit/nativecommit_test.go | 17 ++- 6 files changed, 78 insertions(+), 84 deletions(-) diff --git a/std/fiat-shamir/transcript_test.go b/std/fiat-shamir/transcript_test.go index ce0e4efb8b..7fd67bdba6 100644 --- a/std/fiat-shamir/transcript_test.go +++ b/std/fiat-shamir/transcript_test.go @@ -1,7 +1,7 @@ // Copyright 2020-2025 Consensys Software Inc. // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. -package fiatshamir_test +package fiatshamir import ( "crypto/rand" @@ -9,13 +9,12 @@ import ( "testing" "github.com/consensys/gnark-crypto/ecc" - gcfiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/internal/utils" - fiatshamir "github.com/consensys/gnark/std/fiat-shamir" "github.com/consensys/gnark/std/hash/mimc" "github.com/consensys/gnark/test" ) @@ -34,7 +33,7 @@ func (circuit *FiatShamirCircuit) Define(api frontend.API) error { } // New transcript with 3 challenges to be derived - tsSnark := fiatshamir.NewTranscript(api, &hSnark, []string{"alpha", "beta", "gamma"}) + tsSnark := NewTranscript(api, &hSnark, []string{"alpha", "beta", "gamma"}) // Bind challenges if err := tsSnark.Bind("alpha", circuit.Bindings[0][:]); err != nil { @@ -89,7 +88,7 @@ func TestFiatShamir(t *testing.T) { for curveID, h := range testData { // instantiate the hash and the transcript in plain go - ts := gcfiatshamir.NewTranscript(h.New(), "alpha", "beta", "gamma") + ts := fiatshamir.NewTranscript(h.New(), "alpha", "beta", "gamma") var bindings [3][4]*big.Int for i := 0; i < 3; i++ { diff --git a/std/internal/mimc/mimc_test.go b/std/internal/mimc/mimc_test.go index 5478f0a6cf..2fbc48a756 100644 --- a/std/internal/mimc/mimc_test.go +++ b/std/internal/mimc/mimc_test.go @@ -1,7 +1,7 @@ // Copyright 2020-2025 Consensys Software Inc. // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. -package mimc_test +package mimc import ( "crypto/rand" @@ -13,7 +13,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/std/internal/mimc" "github.com/consensys/gnark/test" ) @@ -23,7 +22,7 @@ type mimcCircuit struct { } func (circuit *mimcCircuit) Define(api frontend.API) error { - mimc, err := mimc.NewMiMC(api) + mimc, err := NewMiMC(api) if err != nil { return err } @@ -94,8 +93,8 @@ type stateStoreTestCircuit struct { func (s *stateStoreTestCircuit) Define(api frontend.API) error { - hsh1, err1 := mimc.NewMiMC(api) - hsh2, err2 := mimc.NewMiMC(api) + hsh1, err1 := NewMiMC(api) + hsh2, err2 := NewMiMC(api) if err1 != nil || err2 != nil { return fmt.Errorf("could not instantiate the MIMC hasher: %w", errors.Join(err1, err2)) @@ -162,7 +161,7 @@ type recoveredStateTestCircuit struct { } func (c *recoveredStateTestCircuit) Define(api frontend.API) error { - h, err := mimc.NewMiMC(api) + h, err := NewMiMC(api) if err != nil { return fmt.Errorf("initialize hash: %w", err) } diff --git a/std/math/bitslice/partition_test.go b/std/math/bitslice/partition_test.go index 8095b89070..a098e824b7 100644 --- a/std/math/bitslice/partition_test.go +++ b/std/math/bitslice/partition_test.go @@ -1,10 +1,9 @@ -package bitslice_test +package bitslice import ( "testing" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/std/math/bitslice" "github.com/consensys/gnark/test" ) @@ -18,11 +17,11 @@ type partitionCircuit struct { } func (c *partitionCircuit) Define(api frontend.API) error { - var opts []bitslice.Option + var opts []Option if c.nbDigitsOpt > 0 { - opts = append(opts, bitslice.WithNbDigits(c.nbDigitsOpt)) + opts = append(opts, WithNbDigits(c.nbDigitsOpt)) } - lower, upper := bitslice.Partition(api, c.In, c.Split, opts...) + lower, upper := Partition(api, c.In, c.Split, opts...) api.AssertIsEqual(lower, c.ExpLower) api.AssertIsEqual(upper, c.ExpUpper) return nil diff --git a/std/math/fieldextension/fieldextension_test.go b/std/math/fieldextension/fieldextension_test.go index f72d6066e8..dae14ed989 100644 --- a/std/math/fieldextension/fieldextension_test.go +++ b/std/math/fieldextension/fieldextension_test.go @@ -1,11 +1,10 @@ -package fieldextension_test +package fieldextension import ( "testing" "github.com/consensys/gnark-crypto/field/babybear" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/std/math/fieldextension" "github.com/consensys/gnark/test" ) @@ -15,7 +14,7 @@ type ReduceCircut struct { } func (c *ReduceCircut) Define(api frontend.API) error { - e, err := fieldextension.NewExtension(api) + e, err := NewExtension(api) if err != nil { return err } @@ -76,7 +75,7 @@ type AddCircuit struct { } func (c *AddCircuit) Define(api frontend.API) error { - e, err := fieldextension.NewExtension(api) + e, err := NewExtension(api) if err != nil { return err } @@ -123,7 +122,7 @@ type SubCircuit struct { } func (c *SubCircuit) Define(api frontend.API) error { - e, err := fieldextension.NewExtension(api) + e, err := NewExtension(api) if err != nil { return err } @@ -170,7 +169,7 @@ type MulCircuit struct { } func (c *MulCircuit) Define(api frontend.API) error { - e, err := fieldextension.NewExtension(api) + e, err := NewExtension(api) if err != nil { return err } diff --git a/std/math/uints/uint8_test.go b/std/math/uints/uint8_test.go index 67331ab182..1ec6ce4c1b 100644 --- a/std/math/uints/uint8_test.go +++ b/std/math/uints/uint8_test.go @@ -1,4 +1,4 @@ -package uints_test +package uints import ( "fmt" @@ -6,18 +6,17 @@ import ( "testing" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/std/math/uints" "github.com/consensys/gnark/test" ) type lrotCirc struct { - In uints.U32 - Out uints.U32 + In U32 + Out U32 Shift int } func (c *lrotCirc) Define(api frontend.API) error { - uapi, err := uints.New[uints.U32](api) + uapi, err := New[U32](api) if err != nil { return err } @@ -28,27 +27,27 @@ func (c *lrotCirc) Define(api frontend.API) error { func TestLeftRotation(t *testing.T) { assert := test.NewAssert(t) - assert.CheckCircuit(&lrotCirc{Shift: 4}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: 4, Out: uints.NewU32(bits.RotateLeft32(0x12345678, 4))})) - assert.CheckCircuit(&lrotCirc{Shift: 14}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: 14, Out: uints.NewU32(bits.RotateLeft32(0x12345678, 14))})) - assert.CheckCircuit(&lrotCirc{Shift: 3}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: 3, Out: uints.NewU32(bits.RotateLeft32(0x12345678, 3))})) - assert.CheckCircuit(&lrotCirc{Shift: 11}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: 11, Out: uints.NewU32(bits.RotateLeft32(0x12345678, 11))})) + assert.CheckCircuit(&lrotCirc{Shift: 4}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: 4, Out: NewU32(bits.RotateLeft32(0x12345678, 4))})) + assert.CheckCircuit(&lrotCirc{Shift: 14}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: 14, Out: NewU32(bits.RotateLeft32(0x12345678, 14))})) + assert.CheckCircuit(&lrotCirc{Shift: 3}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: 3, Out: NewU32(bits.RotateLeft32(0x12345678, 3))})) + assert.CheckCircuit(&lrotCirc{Shift: 11}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: 11, Out: NewU32(bits.RotateLeft32(0x12345678, 11))})) // full block - assert.CheckCircuit(&lrotCirc{Shift: 16}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: 16, Out: uints.NewU32(bits.RotateLeft32(0x12345678, 16))})) + assert.CheckCircuit(&lrotCirc{Shift: 16}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: 16, Out: NewU32(bits.RotateLeft32(0x12345678, 16))})) // negative rotations - assert.CheckCircuit(&lrotCirc{Shift: -4}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: -4, Out: uints.NewU32(bits.RotateLeft32(0x12345678, -4))})) - assert.CheckCircuit(&lrotCirc{Shift: -14}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: -14, Out: uints.NewU32(bits.RotateLeft32(0x12345678, -14))})) - assert.CheckCircuit(&lrotCirc{Shift: -3}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: -3, Out: uints.NewU32(bits.RotateLeft32(0x12345678, -3))})) - assert.CheckCircuit(&lrotCirc{Shift: -11}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: -11, Out: uints.NewU32(bits.RotateLeft32(0x12345678, -11))})) - assert.CheckCircuit(&lrotCirc{Shift: -16}, test.WithValidAssignment(&lrotCirc{In: uints.NewU32(0x12345678), Shift: -16, Out: uints.NewU32(bits.RotateLeft32(0x12345678, -16))})) + assert.CheckCircuit(&lrotCirc{Shift: -4}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: -4, Out: NewU32(bits.RotateLeft32(0x12345678, -4))})) + assert.CheckCircuit(&lrotCirc{Shift: -14}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: -14, Out: NewU32(bits.RotateLeft32(0x12345678, -14))})) + assert.CheckCircuit(&lrotCirc{Shift: -3}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: -3, Out: NewU32(bits.RotateLeft32(0x12345678, -3))})) + assert.CheckCircuit(&lrotCirc{Shift: -11}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: -11, Out: NewU32(bits.RotateLeft32(0x12345678, -11))})) + assert.CheckCircuit(&lrotCirc{Shift: -16}, test.WithValidAssignment(&lrotCirc{In: NewU32(0x12345678), Shift: -16, Out: NewU32(bits.RotateLeft32(0x12345678, -16))})) } type rshiftCircuit struct { - In, Expected uints.U32 + In, Expected U32 Shift int } func (c *rshiftCircuit) Define(api frontend.API) error { - uapi, err := uints.New[uints.U32](api) + uapi, err := New[U32](api) if err != nil { return err } @@ -59,19 +58,19 @@ func (c *rshiftCircuit) Define(api frontend.API) error { func TestRshift(t *testing.T) { assert := test.NewAssert(t) - assert.CheckCircuit(&rshiftCircuit{Shift: 4}, test.WithValidAssignment(&rshiftCircuit{Shift: 4, In: uints.NewU32(0x12345678), Expected: uints.NewU32(0x12345678 >> 4)})) - assert.CheckCircuit(&rshiftCircuit{Shift: 12}, test.WithValidAssignment(&rshiftCircuit{Shift: 12, In: uints.NewU32(0x12345678), Expected: uints.NewU32(0x12345678 >> 12)})) - assert.CheckCircuit(&rshiftCircuit{Shift: 3}, test.WithValidAssignment(&rshiftCircuit{Shift: 3, In: uints.NewU32(0x12345678), Expected: uints.NewU32(0x12345678 >> 3)})) - assert.CheckCircuit(&rshiftCircuit{Shift: 11}, test.WithValidAssignment(&rshiftCircuit{Shift: 11, In: uints.NewU32(0x12345678), Expected: uints.NewU32(0x12345678 >> 11)})) + assert.CheckCircuit(&rshiftCircuit{Shift: 4}, test.WithValidAssignment(&rshiftCircuit{Shift: 4, In: NewU32(0x12345678), Expected: NewU32(0x12345678 >> 4)})) + assert.CheckCircuit(&rshiftCircuit{Shift: 12}, test.WithValidAssignment(&rshiftCircuit{Shift: 12, In: NewU32(0x12345678), Expected: NewU32(0x12345678 >> 12)})) + assert.CheckCircuit(&rshiftCircuit{Shift: 3}, test.WithValidAssignment(&rshiftCircuit{Shift: 3, In: NewU32(0x12345678), Expected: NewU32(0x12345678 >> 3)})) + assert.CheckCircuit(&rshiftCircuit{Shift: 11}, test.WithValidAssignment(&rshiftCircuit{Shift: 11, In: NewU32(0x12345678), Expected: NewU32(0x12345678 >> 11)})) } -type valueOfCircuit[T uints.Long] struct { +type valueOfCircuit[T Long] struct { In frontend.Variable Expected T } func (c *valueOfCircuit[T]) Define(api frontend.API) error { - uapi, err := uints.New[T](api) + uapi, err := New[T](api) if err != nil { return err } @@ -82,18 +81,18 @@ func (c *valueOfCircuit[T]) Define(api frontend.API) error { func TestValueOf(t *testing.T) { assert := test.NewAssert(t) - assert.CheckCircuit(&valueOfCircuit[uints.U64]{}, test.WithValidAssignment(&valueOfCircuit[uints.U64]{In: 0x12345678, Expected: [8]uints.U8{uints.NewU8(0x78), uints.NewU8(0x56), uints.NewU8(0x34), uints.NewU8(0x12), uints.NewU8(0), uints.NewU8(0), uints.NewU8(0), uints.NewU8(0)}})) - assert.CheckCircuit(&valueOfCircuit[uints.U32]{}, test.WithValidAssignment(&valueOfCircuit[uints.U32]{In: 0x12345678, Expected: [4]uints.U8{uints.NewU8(0x78), uints.NewU8(0x56), uints.NewU8(0x34), uints.NewU8(0x12)}})) - assert.CheckCircuit(&valueOfCircuit[uints.U32]{}, test.WithInvalidAssignment(&valueOfCircuit[uints.U32]{In: 0x1234567812345678, Expected: [4]uints.U8{uints.NewU8(0x78), uints.NewU8(0x56), uints.NewU8(0x34), uints.NewU8(0x12)}})) + assert.CheckCircuit(&valueOfCircuit[U64]{}, test.WithValidAssignment(&valueOfCircuit[U64]{In: 0x12345678, Expected: [8]U8{NewU8(0x78), NewU8(0x56), NewU8(0x34), NewU8(0x12), NewU8(0), NewU8(0), NewU8(0), NewU8(0)}})) + assert.CheckCircuit(&valueOfCircuit[U32]{}, test.WithValidAssignment(&valueOfCircuit[U32]{In: 0x12345678, Expected: [4]U8{NewU8(0x78), NewU8(0x56), NewU8(0x34), NewU8(0x12)}})) + assert.CheckCircuit(&valueOfCircuit[U32]{}, test.WithInvalidAssignment(&valueOfCircuit[U32]{In: 0x1234567812345678, Expected: [4]U8{NewU8(0x78), NewU8(0x56), NewU8(0x34), NewU8(0x12)}})) } type addCircuit struct { - In [2]uints.U32 - Expected uints.U32 + In [2]U32 + Expected U32 } func (c *addCircuit) Define(api frontend.API) error { - uapi, err := uints.New[uints.U32](api) + uapi, err := New[U32](api) if err != nil { return err } @@ -104,19 +103,19 @@ func (c *addCircuit) Define(api frontend.API) error { func TestAdd(t *testing.T) { assert := test.NewAssert(t) - assert.CheckCircuit(&addCircuit{}, test.WithValidAssignment(&addCircuit{In: [2]uints.U32{uints.NewU32(^uint32(0)), uints.NewU32(2)}, Expected: uints.NewU32(1)})) + assert.CheckCircuit(&addCircuit{}, test.WithValidAssignment(&addCircuit{In: [2]U32{NewU32(^uint32(0)), NewU32(2)}, Expected: NewU32(1)})) } -// Add tests where we try to initialize unconstrained uints.U8 +// Add tests where we try to initialize unconstrained U8 type ConstrainedCheckCircuit struct { - A, B, C uints.U8 + A, B, C U8 mode int } func (c *ConstrainedCheckCircuit) Define(api frontend.API) error { - uapi, err := uints.NewBytes(api) + uapi, err := NewBytes(api) if err != nil { - return fmt.Errorf("uints.NewBytes: %w", err) + return fmt.Errorf("NewBytes: %w", err) } switch c.mode { case 0: @@ -136,29 +135,29 @@ func TestConstrainedCircuit(t *testing.T) { assert := test.NewAssert(t) assert.Run(func(assert *test.Assert) { - assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 0}, test.WithValidAssignment(&ConstrainedCheckCircuit{A: uints.NewU8(0x0f), B: uints.NewU8(0xf0), C: uints.NewU8(0x00)})) - assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 0}, test.WithInvalidAssignment(&ConstrainedCheckCircuit{A: uints.U8{Val: 0x00ff}, B: uints.U8{Val: 0xf0f}, C: uints.U8{Val: 0x00f}})) + assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 0}, test.WithValidAssignment(&ConstrainedCheckCircuit{A: NewU8(0x0f), B: NewU8(0xf0), C: NewU8(0x00)})) + assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 0}, test.WithInvalidAssignment(&ConstrainedCheckCircuit{A: U8{Val: 0x00ff}, B: U8{Val: 0xf0f}, C: U8{Val: 0x00f}})) }, "and") assert.Run(func(assert *test.Assert) { - assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 1}, test.WithValidAssignment(&ConstrainedCheckCircuit{A: uints.NewU8(0x0f), B: uints.NewU8(0xf0), C: uints.NewU8(0xff)})) - assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 1}, test.WithInvalidAssignment(&ConstrainedCheckCircuit{A: uints.U8{Val: 0x0f00}, B: uints.U8{Val: 0x0f0}, C: uints.U8{Val: 0xff0}})) + assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 1}, test.WithValidAssignment(&ConstrainedCheckCircuit{A: NewU8(0x0f), B: NewU8(0xf0), C: NewU8(0xff)})) + assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 1}, test.WithInvalidAssignment(&ConstrainedCheckCircuit{A: U8{Val: 0x0f00}, B: U8{Val: 0x0f0}, C: U8{Val: 0xff0}})) }, "or") assert.Run(func(assert *test.Assert) { - assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 2}, test.WithValidAssignment(&ConstrainedCheckCircuit{A: uints.NewU8(0x0f), B: uints.NewU8(0xf0), C: uints.NewU8(0xff)})) - assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 2}, test.WithInvalidAssignment(&ConstrainedCheckCircuit{A: uints.U8{Val: 0x0f0f}, B: uints.U8{Val: 0x0ff}, C: uints.U8{Val: 0xff0}})) + assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 2}, test.WithValidAssignment(&ConstrainedCheckCircuit{A: NewU8(0x0f), B: NewU8(0xf0), C: NewU8(0xff)})) + assert.CheckCircuit(&ConstrainedCheckCircuit{mode: 2}, test.WithInvalidAssignment(&ConstrainedCheckCircuit{A: U8{Val: 0x0f0f}, B: U8{Val: 0x0ff}, C: U8{Val: 0xff0}})) }, "xor") } type ToValueCircuit struct { - In uints.U32 + In U32 withCheck bool Expected frontend.Variable } func (c *ToValueCircuit) Define(api frontend.API) error { - uapi, err := uints.New[uints.U32](api) + uapi, err := New[U32](api) if err != nil { - return fmt.Errorf("uints.New: %w", err) + return fmt.Errorf("New: %w", err) } res := uapi.ToValue(c.In) if c.withCheck { @@ -169,19 +168,19 @@ func (c *ToValueCircuit) Define(api frontend.API) error { func TestToValue(t *testing.T) { assert := test.NewAssert(t) - assert.CheckCircuit(&ToValueCircuit{withCheck: true}, test.WithValidAssignment(&ToValueCircuit{In: uints.NewU32(0x12345678), Expected: 0x12345678})) - assert.CheckCircuit(&ToValueCircuit{withCheck: false}, test.WithInvalidAssignment(&ToValueCircuit{In: [4]uints.U8{{Val: 0x780}, {Val: 0x56}, {Val: 0x34}, {Val: 0x12}}, Expected: 0x12345678})) + assert.CheckCircuit(&ToValueCircuit{withCheck: true}, test.WithValidAssignment(&ToValueCircuit{In: NewU32(0x12345678), Expected: 0x12345678})) + assert.CheckCircuit(&ToValueCircuit{withCheck: false}, test.WithInvalidAssignment(&ToValueCircuit{In: [4]U8{{Val: 0x780}, {Val: 0x56}, {Val: 0x34}, {Val: 0x12}}, Expected: 0x12345678})) } type ValueWitnessCircuit struct { - In uints.U8 + In U8 Expected frontend.Variable } func (c *ValueWitnessCircuit) Define(api frontend.API) error { - uapi, err := uints.NewBytes(api) + uapi, err := NewBytes(api) if err != nil { - return fmt.Errorf("uints.NewBytes: %w", err) + return fmt.Errorf("NewBytes: %w", err) } res := uapi.Value(c.In) api.AssertIsEqual(res, c.Expected) @@ -190,8 +189,8 @@ func (c *ValueWitnessCircuit) Define(api frontend.API) error { func TestValueWitness(t *testing.T) { assert := test.NewAssert(t) - assert.CheckCircuit(&ValueWitnessCircuit{}, test.WithValidAssignment(&ValueWitnessCircuit{In: uints.NewU8(0x12), Expected: 0x12})) - assert.CheckCircuit(&ValueWitnessCircuit{}, test.WithInvalidAssignment(&ValueWitnessCircuit{In: uints.U8{Val: 0x1234}, Expected: 0x1234})) + assert.CheckCircuit(&ValueWitnessCircuit{}, test.WithValidAssignment(&ValueWitnessCircuit{In: NewU8(0x12), Expected: 0x12})) + assert.CheckCircuit(&ValueWitnessCircuit{}, test.WithInvalidAssignment(&ValueWitnessCircuit{In: U8{Val: 0x1234}, Expected: 0x1234})) } type ValueInCircuitCircuit struct { @@ -200,11 +199,11 @@ type ValueInCircuitCircuit struct { } func (c *ValueInCircuitCircuit) Define(api frontend.API) error { - uapi, err := uints.NewBytes(api) + uapi, err := NewBytes(api) if err != nil { - return fmt.Errorf("uints.NewBytes: %w", err) + return fmt.Errorf("NewBytes: %w", err) } - in := uints.U8{Val: c.In} + in := U8{Val: c.In} res := uapi.Value(in) api.AssertIsEqual(res, c.Expected) return nil diff --git a/std/multicommit/nativecommit_test.go b/std/multicommit/nativecommit_test.go index ce0ef619ab..84de07ee72 100644 --- a/std/multicommit/nativecommit_test.go +++ b/std/multicommit/nativecommit_test.go @@ -1,4 +1,4 @@ -package multicommit_test +package multicommit import ( "testing" @@ -10,7 +10,6 @@ import ( "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/internal/widecommitter" "github.com/consensys/gnark/std/math/fieldextension" - "github.com/consensys/gnark/std/multicommit" "github.com/consensys/gnark/test" ) @@ -19,8 +18,8 @@ type noRecursionCircuit struct { } func (c *noRecursionCircuit) Define(api frontend.API) error { - multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { - multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { return nil }, commitment) + WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { + WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { return nil }, commitment) return nil }, c.X) return nil @@ -40,12 +39,12 @@ type multipleCommitmentCircuit struct { func (c *multipleCommitmentCircuit) Define(api frontend.API) error { var stored frontend.Variable // first callback receives first unique commitment derived from the root commitment - multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { + WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { api.AssertIsDifferent(c.X, commitment) stored = commitment return nil }, c.X) - multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { + WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { api.AssertIsDifferent(stored, commitment) return nil }, c.X) @@ -64,7 +63,7 @@ type noCommitVariable struct { } func (c *noCommitVariable) Define(api frontend.API) error { - multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { return nil }) + WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { return nil }) return nil } @@ -84,12 +83,12 @@ type wideCommitment struct { func (c *wideCommitment) Define(api frontend.API) error { if c.withCommitment { - multicommit.WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { + WithCommitment(api, func(api frontend.API, commitment frontend.Variable) error { api.AssertIsDifferent(commitment, 0) return nil }, c.X) } - multicommit.WithWideCommitment(api, func(api frontend.API, commitment []frontend.Variable) error { + WithWideCommitment(api, func(api frontend.API, commitment []frontend.Variable) error { fe, err := fieldextension.NewExtension(api, fieldextension.WithDegree(8)) if err != nil { return err From a3d316ec7460e6cfba9ca31ed2e627b8730d4f79 Mon Sep 17 00:00:00 2001 From: Tabaie Date: Wed, 1 Oct 2025 12:23:59 -0500 Subject: [PATCH 49/50] refactor: don't record engine hint IDs --- internal/gkr/gkrinfo/info.go | 13 ++----- std/gkrapi/api_test.go | 35 +++++++++++++++---- std/gkrapi/compile.go | 22 ++++++------ std/gkrapi/example_test.go | 5 ++- .../poseidon2/gkr-poseidon2/gkr.go | 10 ++++-- 5 files changed, 52 insertions(+), 33 deletions(-) diff --git a/internal/gkr/gkrinfo/info.go b/internal/gkr/gkrinfo/info.go index 6d14e37dfb..9d4d72e175 100644 --- a/internal/gkr/gkrinfo/info.go +++ b/internal/gkr/gkrinfo/info.go @@ -1,10 +1,6 @@ // Package gkrinfo contains serializable information capable of being saved in a SNARK circuit CS object. package gkrinfo -import ( - "github.com/consensys/gnark/constraint/solver" -) - type ( InputDependency struct { OutputWire int @@ -20,12 +16,9 @@ type ( Circuit []Wire StoringInfo struct { - Circuit Circuit - NbInstances int - HashName string - GetAssignmentHintID solver.HintID - SolveHintID solver.HintID - ProveHintID solver.HintID + Circuit Circuit + NbInstances int + HashName string } Permutations struct { diff --git a/std/gkrapi/api_test.go b/std/gkrapi/api_test.go index 1f3d187dae..5895c66597 100644 --- a/std/gkrapi/api_test.go +++ b/std/gkrapi/api_test.go @@ -40,7 +40,10 @@ func (c *doubleNoDependencyCircuit) Define(api frontend.API) error { x := gkrApi.NewInput() z := gkrApi.Add(x, x) - gkrCircuit := gkrApi.Compile(api, c.hashName) + gkrCircuit, err := gkrApi.Compile(api, c.hashName) + if err != nil { + return err + } instanceIn := make(map[gkr.Variable]frontend.Variable) for i := range c.X { @@ -86,7 +89,10 @@ func (c *sqNoDependencyCircuit) Define(api frontend.API) error { x := gkrApi.NewInput() z := gkrApi.Mul(x, x) - gkrCircuit := gkrApi.Compile(api, c.hashName) + gkrCircuit, err := gkrApi.Compile(api, c.hashName) + if err != nil { + return err + } instanceIn := make(map[gkr.Variable]frontend.Variable) for i := range c.X { @@ -132,7 +138,10 @@ func (c *mulNoDependencyCircuit) Define(api frontend.API) error { y := gkrApi.NewInput() z := gkrApi.Mul(x, y) - gkrCircuit := gkrApi.Compile(api, c.hashName) + gkrCircuit, err := gkrApi.Compile(api, c.hashName) + if err != nil { + return err + } instanceIn := make(map[gkr.Variable]frontend.Variable) for i := range c.X { @@ -190,7 +199,10 @@ func (c *mulWithDependencyCircuit) Define(api frontend.API) error { y := gkrApi.NewInput() z := gkrApi.Mul(x, y) - gkrCircuit := gkrApi.Compile(api, c.hashName) + gkrCircuit, err := gkrApi.Compile(api, c.hashName) + if err != nil { + return err + } state := c.XFirst instanceIn := make(map[gkr.Variable]frontend.Variable) @@ -292,7 +304,10 @@ func (c *benchMiMCMerkleTreeCircuit) Define(api frontend.API) error { y := gkrApi.NewInput() z := gkrApi.Gate(mimcGate, x, y) - gkrCircuit := gkrApi.Compile(api, "-20") + gkrCircuit, err := gkrApi.Compile(api, "-20") + if err != nil { + return err + } // prepare input curLayer := make([]frontend.Variable, 1< Date: Wed, 1 Oct 2025 12:43:19 -0500 Subject: [PATCH 50/50] use nil pointer for function reference --- constraint/bls12-377/solver.go | 2 +- constraint/bls12-381/solver.go | 2 +- constraint/bls24-315/solver.go | 2 +- constraint/bls24-317/solver.go | 2 +- constraint/bn254/solver.go | 2 +- constraint/bw6-633/solver.go | 2 +- constraint/bw6-761/solver.go | 2 +- .../generator/backend/template/representations/solver.go.tmpl | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/constraint/bls12-377/solver.go b/constraint/bls12-377/solver.go index bfc2929074..ac7bd11252 100644 --- a/constraint/bls12-377/solver.go +++ b/constraint/bls12-377/solver.go @@ -57,7 +57,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) - var gkrHints gkrhints.TestEngineHints + var gkrHints *gkrhints.TestEngineHints opts = append(opts, csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)), diff --git a/constraint/bls12-381/solver.go b/constraint/bls12-381/solver.go index 1ef8fd17ea..0aa3655def 100644 --- a/constraint/bls12-381/solver.go +++ b/constraint/bls12-381/solver.go @@ -57,7 +57,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) - var gkrHints gkrhints.TestEngineHints + var gkrHints *gkrhints.TestEngineHints opts = append(opts, csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)), diff --git a/constraint/bls24-315/solver.go b/constraint/bls24-315/solver.go index 4dc7877987..063bef050a 100644 --- a/constraint/bls24-315/solver.go +++ b/constraint/bls24-315/solver.go @@ -57,7 +57,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) - var gkrHints gkrhints.TestEngineHints + var gkrHints *gkrhints.TestEngineHints opts = append(opts, csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)), diff --git a/constraint/bls24-317/solver.go b/constraint/bls24-317/solver.go index 6000c2bc70..865aefd962 100644 --- a/constraint/bls24-317/solver.go +++ b/constraint/bls24-317/solver.go @@ -57,7 +57,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) - var gkrHints gkrhints.TestEngineHints + var gkrHints *gkrhints.TestEngineHints opts = append(opts, csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)), diff --git a/constraint/bn254/solver.go b/constraint/bn254/solver.go index 34bccc08e6..9380f8e17e 100644 --- a/constraint/bn254/solver.go +++ b/constraint/bn254/solver.go @@ -57,7 +57,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) - var gkrHints gkrhints.TestEngineHints + var gkrHints *gkrhints.TestEngineHints opts = append(opts, csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)), diff --git a/constraint/bw6-633/solver.go b/constraint/bw6-633/solver.go index ebb3fbd274..bc23cd2356 100644 --- a/constraint/bw6-633/solver.go +++ b/constraint/bw6-633/solver.go @@ -57,7 +57,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) - var gkrHints gkrhints.TestEngineHints + var gkrHints *gkrhints.TestEngineHints opts = append(opts, csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)), diff --git a/constraint/bw6-761/solver.go b/constraint/bw6-761/solver.go index 1526c65c20..6bf6d30f30 100644 --- a/constraint/bw6-761/solver.go +++ b/constraint/bw6-761/solver.go @@ -57,7 +57,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) - var gkrHints gkrhints.TestEngineHints + var gkrHints *gkrhints.TestEngineHints opts = append(opts, csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)), diff --git a/internal/generator/backend/template/representations/solver.go.tmpl b/internal/generator/backend/template/representations/solver.go.tmpl index 0ad544fd9c..5f6fa68c70 100644 --- a/internal/generator/backend/template/representations/solver.go.tmpl +++ b/internal/generator/backend/template/representations/solver.go.tmpl @@ -51,7 +51,7 @@ func newSolver(cs *system, witness fr.Vector, opts ...csolver.Option) (*solver, return nil, err } gkrData := gkr.NewSolvingData(solvingInfo) - var gkrHints gkrhints.TestEngineHints + var gkrHints *gkrhints.TestEngineHints opts = append(opts, csolver.OverrideHint(csolver.GetHintID(gkrHints.GetAssignment), gkr.GetAssignmentHint(gkrData)), csolver.OverrideHint(csolver.GetHintID(gkrHints.Solve), gkr.SolveHint(gkrData)),