Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions std/math/bitslice/opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@ func parseOpts(opts ...Option) (*opt, error) {
return o, nil
}

// Option allows to customize the behavior of functions in this package. See
// [WithNbDigits] and [WithUnconstrainedOutputs] for examples.
type Option func(*opt) error

// WithNbDigits sets the bound on the number of digits the input can have. If
// this is not set, then we use standard binary decomposition of the input. If
// it is set and it is less than the width of the native field, then we use
// lookup table based method for bounding the inputs which is more efficient.
func WithNbDigits(nbDigits int) Option {
return func(o *opt) error {
if nbDigits < 1 {
Expand All @@ -29,6 +35,8 @@ func WithNbDigits(nbDigits int) Option {
}
}

// WithUnconstrainedOutputs allows to skip the output decomposition and outputs
// width checks. Can be used when these are performed by the caller.
func WithUnconstrainedOutputs() Option {
return func(o *opt) error {
o.nocheck = true
Expand Down
17 changes: 16 additions & 1 deletion std/math/bitslice/partition.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Package bitslice allows partitioning variables.
package bitslice

import (
"math/big"

"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/math/bits"
"github.com/consensys/gnark/std/rangecheck"
)

Expand Down Expand Up @@ -40,6 +42,19 @@ func Partition(api frontend.API, v frontend.Variable, split uint, opts ...Option
}
return 0, v
}
// when nbDigits is not set, then we assume the bound is the field size.
// However, in that case the decomposition check is more involved as we need
// to avoid the recomposed value to overflow the field. We do not have good
// methods for avoiding it when using range checker gadget, so we defer it
// to `bits.ToBinary`.
if opt.digits == 0 || opt.digits >= api.Compiler().FieldBitLen() {
bts := bits.ToBinary(api, v)
lowerBts := bts[:split]
upperBts := bts[split:]
lower = bits.FromBinary(api, lowerBts)
upper = bits.FromBinary(api, upperBts)
return lower, upper
}
ret, err := api.Compiler().NewHint(partitionHint, 2, split, v)
if err != nil {
panic(err)
Expand All @@ -58,7 +73,7 @@ func Partition(api frontend.API, v frontend.Variable, split uint, opts ...Option
if opt.digits > 0 {
upperBound = opt.digits
}
rh.Check(upper, upperBound)
rh.Check(upper, upperBound-int(split))
rh.Check(lower, int(split))

m := big.NewInt(1)
Expand Down
15 changes: 14 additions & 1 deletion std/math/bitslice/partition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@ import (
"github.com/consensys/gnark/test"
)

// TODO: add option for choosing nbDigits

type partitionCircuit struct {
Split uint
In, ExpLower, ExpUpper frontend.Variable

nbDigitsOpt int
}

func (c *partitionCircuit) Define(api frontend.API) error {
lower, upper := Partition(api, c.In, c.Split)
var opts []Option
if c.nbDigitsOpt > 0 {
opts = append(opts, WithNbDigits(c.nbDigitsOpt))
}
lower, upper := Partition(api, c.In, c.Split, opts...)
api.AssertIsEqual(lower, c.ExpLower)
api.AssertIsEqual(upper, c.ExpUpper)
return nil
Expand All @@ -26,3 +34,8 @@ func TestPartition(t *testing.T) {
assert.CheckCircuit(&partitionCircuit{Split: 16}, test.WithValidAssignment(&partitionCircuit{Split: 16, ExpUpper: 0xffff, ExpLower: 0x1234, In: 0xffff1234}))
assert.CheckCircuit(&partitionCircuit{Split: 32}, test.WithValidAssignment(&partitionCircuit{Split: 32, ExpUpper: 0, ExpLower: 0xffff1234, In: 0xffff1234}))
}

func TestIssue1153(t *testing.T) {
assert := test.NewAssert(t)
assert.CheckCircuit(&partitionCircuit{Split: 8, nbDigitsOpt: 16}, test.WithInvalidAssignment(&partitionCircuit{ExpUpper: 0xff1, ExpLower: 0x21, In: 0xff121}))
}