From d3d287e2b744a50ff707c449889f3f49241f4584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=C2=B7Gou?= Date: Sat, 29 Mar 2025 11:40:41 +0800 Subject: [PATCH 1/5] add boundedComparatorCircuit --- std/math/cmp/bounded_test.go | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/std/math/cmp/bounded_test.go b/std/math/cmp/bounded_test.go index fcdfc0677d..ceccb7ce25 100644 --- a/std/math/cmp/bounded_test.go +++ b/std/math/cmp/bounded_test.go @@ -1,11 +1,12 @@ package cmp_test import ( + "math/big" + "testing" + "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/math/cmp" "github.com/consensys/gnark/test" - "math/big" - "testing" ) func TestAssertIsLessEq(t *testing.T) { @@ -143,3 +144,26 @@ func (c *minCircuit) Define(api frontend.API) error { return nil } + +type boundedComparatorCircuit struct { + A frontend.Variable + + WantIsLess int + WantIsLessEq int + Bound int +} + +func (c *boundedComparatorCircuit) Define(api frontend.API) error { + comparator := cmp.NewBoundedComparator(api, big.NewInt(int64(c.Bound)), false) + if c.WantIsLess == 1 { + comparator.AssertIsLess(c.A, c.Bound) + } + if c.WantIsLessEq == 1 { + comparator.AssertIsLessEq(c.A, c.Bound) + } + + api.AssertIsEqual(c.WantIsLess, comparator.IsLess(c.A, c.Bound)) + api.AssertIsEqual(c.WantIsLessEq, comparator.IsLessEq(c.A, c.Bound)) + + return nil +} From 655fd73b1252580f7eb4e441753f9b6899a0578a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=C2=B7Gou?= Date: Sat, 29 Mar 2025 11:40:55 +0800 Subject: [PATCH 2/5] add TestBoundedComparator --- std/math/cmp/bounded_test.go | 48 ++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/std/math/cmp/bounded_test.go b/std/math/cmp/bounded_test.go index ceccb7ce25..a1688490b6 100644 --- a/std/math/cmp/bounded_test.go +++ b/std/math/cmp/bounded_test.go @@ -1,9 +1,11 @@ package cmp_test import ( + "fmt" "math/big" "testing" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/math/cmp" "github.com/consensys/gnark/test" @@ -167,3 +169,49 @@ func (c *boundedComparatorCircuit) Define(api frontend.API) error { return nil } + +type boundedComparatorTestCase struct { + A int + + WantIsLess int + WantIsLessEq int + Bound int + + expectedSuccess bool +} + +func TestBoundedComparator(t *testing.T) { + assert := test.NewAssert(t) + + var testCases []boundedComparatorTestCase + for bound := 2; bound <= 15; bound++ { + c := 1 << (big.NewInt(int64(bound)).BitLen()) + for i := 0; i <= bound+5; i++ { + testCase := boundedComparatorTestCase{ + A: i, Bound: bound, WantIsLess: 1, WantIsLessEq: 1, expectedSuccess: true} + if i >= bound { + testCase.WantIsLess = 0 + if i > bound { + testCase.WantIsLessEq = 0 + } + } + if i-bound >= c { + testCase.expectedSuccess = false + } + testCases = append(testCases, testCase) + } + } + + for _, tc := range testCases { + assert.Run(func(assert *test.Assert) { + circuit := &boundedComparatorCircuit{Bound: tc.Bound, WantIsLess: tc.WantIsLess, WantIsLessEq: tc.WantIsLessEq} + assignment := &boundedComparatorCircuit{A: tc.A} + err := test.IsSolved(circuit, assignment, ecc.BN254.ScalarField()) + if tc.expectedSuccess { + assert.NoError(err) + } else { + assert.Error(err) + } + }, fmt.Sprintf("bound=%d a=%d", tc.Bound, tc.A)) + } +} From 93dc07320ac971422d014168d51baf1c0b586ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=C2=B7Gou?= Date: Sat, 29 Mar 2025 11:51:38 +0800 Subject: [PATCH 3/5] add comment for IsLess and IsLessEq of BoundedComparator --- std/math/cmp/bounded.go | 7 ++++++- std/math/cmp/bounded_test.go | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/std/math/cmp/bounded.go b/std/math/cmp/bounded.go index 14d5a433a5..d34972b9ed 100644 --- a/std/math/cmp/bounded.go +++ b/std/math/cmp/bounded.go @@ -2,10 +2,11 @@ package cmp import ( "fmt" + "math/big" + "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/math/bits" - "math/big" ) func init() { @@ -151,6 +152,8 @@ func (bc BoundedComparator) AssertIsLess(a, b frontend.Variable) { } // IsLess returns 1 if a < b, and returns 0 if a >= b. +// When |a - b| >= 2^absDiffUpp.BitLen(), a panic is occurred, +// then the method has no return value, and a proof can not be generated func (bc BoundedComparator) IsLess(a, b frontend.Variable) frontend.Variable { res, err := bc.api.Compiler().NewHint(isLessOutputHint, 1, a, b) if err != nil { @@ -164,6 +167,8 @@ func (bc BoundedComparator) IsLess(a, b frontend.Variable) frontend.Variable { } // IsLessEq returns 1 if a <= b, and returns 0 if a > b. +// When |a - b| > 2^absDiffUpp.BitLen(), a panic is occurred, +// then the method has no return value, and a proof can not be generated func (bc BoundedComparator) IsLessEq(a, b frontend.Variable) frontend.Variable { // a <= b <==> a < b + 1 return bc.IsLess(a, bc.api.Add(b, 1)) diff --git a/std/math/cmp/bounded_test.go b/std/math/cmp/bounded_test.go index a1688490b6..12cee7bd53 100644 --- a/std/math/cmp/bounded_test.go +++ b/std/math/cmp/bounded_test.go @@ -156,7 +156,7 @@ type boundedComparatorCircuit struct { } func (c *boundedComparatorCircuit) Define(api frontend.API) error { - comparator := cmp.NewBoundedComparator(api, big.NewInt(int64(c.Bound)), false) + comparator := cmp.NewBoundedComparator(api, big.NewInt(int64(c.Bound)), true) if c.WantIsLess == 1 { comparator.AssertIsLess(c.A, c.Bound) } From 6521945f211f9c07ff6631cc0b9df99aeef42d42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=C2=B7Gou?= Date: Sat, 29 Mar 2025 12:07:48 +0800 Subject: [PATCH 4/5] Update bounded.go --- std/math/cmp/bounded.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/std/math/cmp/bounded.go b/std/math/cmp/bounded.go index d34972b9ed..7b60220f8e 100644 --- a/std/math/cmp/bounded.go +++ b/std/math/cmp/bounded.go @@ -153,7 +153,9 @@ func (bc BoundedComparator) AssertIsLess(a, b frontend.Variable) { // IsLess returns 1 if a < b, and returns 0 if a >= b. // When |a - b| >= 2^absDiffUpp.BitLen(), a panic is occurred, -// then the method has no return value, and a proof can not be generated +// then the method has no return value, and a proof can not be generated. +// It is recommended to use the IsLess method to get a valid return value +// in https://github.com/Consensys/gnark/blob/master/std/math/cmp/generic.go func (bc BoundedComparator) IsLess(a, b frontend.Variable) frontend.Variable { res, err := bc.api.Compiler().NewHint(isLessOutputHint, 1, a, b) if err != nil { @@ -168,7 +170,9 @@ func (bc BoundedComparator) IsLess(a, b frontend.Variable) frontend.Variable { // IsLessEq returns 1 if a <= b, and returns 0 if a > b. // When |a - b| > 2^absDiffUpp.BitLen(), a panic is occurred, -// then the method has no return value, and a proof can not be generated +// then the method has no return value, and a proof can not be generated. +// It is recommended to use the IsLessOrEqual method to get a valid return value +// in https://github.com/Consensys/gnark/blob/master/std/math/cmp/generic.go func (bc BoundedComparator) IsLessEq(a, b frontend.Variable) frontend.Variable { // a <= b <==> a < b + 1 return bc.IsLess(a, bc.api.Add(b, 1)) From 6c937522203cf9d5d7dc89eefa0c6f3657756a71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=C2=B7Gou?= Date: Sat, 29 Mar 2025 12:21:30 +0800 Subject: [PATCH 5/5] Update api.go --- frontend/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/api.go b/frontend/api.go index 40a6fdfaba..57696f1290 100644 --- a/frontend/api.go +++ b/frontend/api.go @@ -99,7 +99,7 @@ type API interface { // // If the absolute difference between the variables i1 and i2 is known, then // it is more efficient to use the bounded methods in package - // [github.com/consensys/gnark/std/math/bits]. + // [https://github.com/Consensys/gnark/blob/master/std/math/cmp]. Cmp(i1, i2 Variable) Variable // --------------------------------------------------------------------------------------------- @@ -121,7 +121,7 @@ type API interface { // // If the absolute difference between the variables b and bound is known, then // it is more efficient to use the bounded methods in package - // [github.com/consensys/gnark/std/math/bits]. + // [https://github.com/Consensys/gnark/blob/master/std/math/cmp]. AssertIsLessOrEqual(v Variable, bound Variable) // Println behaves like fmt.Println but accepts cd.Variable as parameter