Skip to content

feat: GKR Add Instance #1504

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 39 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
609d47d
refactor: addInstance instead of series etc
Tabaie Jun 1, 2025
79d4cbf
feat: check for duplicate gates, allow limiting curves for gate
Tabaie Jun 1, 2025
82e33e5
refactor: gkr api tests
Tabaie Jun 1, 2025
ba06e5d
refactor gkr example
Tabaie Jun 1, 2025
700c152
refactor: solve hint called per instance
Tabaie Jun 2, 2025
30f5633
revert: newPrint back in std/gkr to avoid import cycle
Tabaie Jun 2, 2025
44d6b4c
refactor: remove circuit/instance rearranging
Tabaie Jun 2, 2025
f5f726c
chore: generify
Tabaie Jun 2, 2025
644fd6a
fix: registry duplicate detection
Tabaie Jun 2, 2025
2001e83
fix: solver hint id mismatch
Tabaie Jun 2, 2025
d3ca3c1
remove redundant make
Tabaie Jun 2, 2025
6474482
fix: works on plonk
Tabaie Jun 2, 2025
0c0b6b0
refactor: solve hint for test engine
Tabaie Jun 3, 2025
478d53d
fix prove hint
Tabaie Jun 3, 2025
6c83740
fix package tests
Tabaie Jun 3, 2025
d6e382f
refactor: remove println
Tabaie Jun 3, 2025
a8f30a4
chore: generify print removal
Tabaie Jun 3, 2025
8ac5f9f
feat: GetValue
Tabaie Jun 3, 2025
5bb879d
fix all gkrapi tests pass
Tabaie Jun 3, 2025
7f9a0f1
fix gkr-poseidon2
Tabaie Jun 3, 2025
355277c
Merge branch 'master' into feat/gkr/add-instance
Tabaie Jun 3, 2025
cfdb9d3
fix: reduce in test engine
Tabaie Jun 3, 2025
12788f3
fix: rename GkrCompressions -> GkrPermutations
Tabaie Jun 3, 2025
9a0bf0e
bench: gkrposeidon2
Tabaie Jun 3, 2025
618beff
fix pad for bls12377
Tabaie Jun 3, 2025
22b77f2
some more padding fixes
Tabaie Jun 3, 2025
250a35b
fix: padding issue in bn254
Tabaie Jun 4, 2025
1586760
chore generify fix
Tabaie Jun 4, 2025
e593d90
Let Uint64 panic
Tabaie Jun 4, 2025
9df619a
Update constraint/solver/gkrgates/registry.go
Tabaie Jun 4, 2025
8c88c52
refactor: use assert.EqualError
Tabaie Jun 4, 2025
f4d7aa8
Merge branch 'master' into feat/gkr/add-instance
Tabaie Jun 5, 2025
499ff52
refactor: import hash/all in test
Tabaie Jun 5, 2025
4ae9324
Merge branch 'master' into feat/gkr/add-instance
Tabaie Jun 6, 2025
4765d61
revert incorrect renaming
Tabaie Jun 8, 2025
b3d1af8
fix: use multicommitter
Tabaie Jun 10, 2025
31a0a59
fix: single instance and no instance edge cases
Tabaie Jun 11, 2025
db56157
fix: single-instance, circuit with depth
Tabaie Jun 11, 2025
b4544d6
Merge branch 'master' into feat/gkr/add-instance
Tabaie Jun 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions constraint/bls12-377/solver.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions constraint/bls12-381/solver.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions constraint/bls24-315/solver.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions constraint/bls24-317/solver.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions constraint/bn254/solver.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions constraint/bw6-633/solver.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions constraint/bw6-761/solver.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

62 changes: 50 additions & 12 deletions constraint/solver/gkrgates/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -99,31 +100,62 @@ 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 {
s := registerSettings{degree: -1, solvableVar: -1, name: GetDefaultGateName(f), curves: []ecc.ID{ecc.BN254}}
//
// 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)
}

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()
}

gatesLock.Lock()
defer gatesLock.Unlock()

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)
}

for _, curve := range curvesForTesting {
gateVer, err := NewGateVerifier(curve)
if err != nil {
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, g.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
if s.noDegreeVerification {
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)
}
}
}
Expand All @@ -135,16 +167,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)
return nil
gates[s.name] = gkrtypes.NewGate(f, nbIn, s.degree, s.solvableVar, allowedCurves)
return true, nil
}

func Get(name gkr.GateName) *gkrtypes.Gate {
Expand All @@ -160,6 +190,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) {
Expand All @@ -172,30 +203,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)
}
Expand Down
46 changes: 36 additions & 10 deletions constraint/solver/gkrgates/registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
})
}

Expand All @@ -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).Error()
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.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.EqualError(t, err, expectedError, "error message must match expected error")
assert.False(t, registered, "registration must fail for zero polynomial with degree")
})
}
11 changes: 11 additions & 0 deletions internal/generator/backend/template/gkr/gate_testing.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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)³)
Expand Down
2 changes: 1 addition & 1 deletion internal/generator/backend/template/gkr/gkr.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading