Skip to content
Open
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
17 changes: 17 additions & 0 deletions std/math/uints/bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/internal/kvstore"
"github.com/consensys/gnark/std/internal/logderivprecomp"
stdbits "github.com/consensys/gnark/std/math/bits"
"github.com/consensys/gnark/std/rangecheck"
)

Expand Down Expand Up @@ -103,6 +104,22 @@ func (bf *Bytes) packInternal(val frontend.Variable) U8 {
return U8{Val: val, internal: true}
}

// ToBits decomposes a byte into little-endian bits and constrains the byte to
// equal the recomposition of these bits.
func (bf *Bytes) ToBits(a U8) []frontend.Variable {
return bf.api.ToBinary(a.Val, 8)
}

// FromBits packs eight little-endian bits into a byte. The input bits are
// constrained to be boolean unless they are already marked as boolean by the
// compiler.
func (bf *Bytes) FromBits(bits ...frontend.Variable) U8 {
if len(bits) != 8 {
panic("expected exactly 8 bits")
}
return bf.packInternal(stdbits.FromBinary(bf.api, bits))
}

// ValueOf returns a constrainted [U8] variable. For a constant value, use
// [NewU8] instead.
func (bf *Bytes) ValueOf(a frontend.Variable) U8 {
Expand Down
28 changes: 28 additions & 0 deletions std/math/uints/uint8.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ func (bf *BinaryField[T]) zero() T {
return res
}

// API returns the underlying frontend API.
func (bf *BinaryField[T]) API() frontend.API {
return bf.Bytes.api
}

// ByteValueOf converts a frontend.Variable into a single byte. If the input
// doesn't fit into a byte then solver fails.
func (bf *BinaryField[T]) ByteValueOf(a frontend.Variable) U8 {
Expand Down Expand Up @@ -196,6 +201,29 @@ func (bf *BinaryField[T]) PackLSB(a ...U8) T {
return ret
}

// ToBits decomposes a long integer into little-endian bits and constrains each
// byte to equal the recomposition of its bits.
func (bf *BinaryField[T]) ToBits(a T) []frontend.Variable {
ret := make([]frontend.Variable, 0, bf.lenBts()*8)
for i := 0; i < bf.lenBts(); i++ {
ret = append(ret, bf.Bytes.ToBits(a[i])...)
}
return ret
}

// FromBits packs little-endian bits into a long integer. The number of bits
// must match the width of T.
func (bf *BinaryField[T]) FromBits(bits ...frontend.Variable) T {
if len(bits) != bf.lenBts()*8 {
panic("incorrect number of bits")
}
var ret T
for i := 0; i < bf.lenBts(); i++ {
ret[i] = bf.Bytes.FromBits(bits[8*i : 8*(i+1)]...)
}
return ret
}

// UnpackMSB unpacks a long integer T into bytes assuming most significant
// byte first order.
// For example, UnpackMSB(0x12345678) = (0x12, 0x34, 0x56, 0x78)
Expand Down
20 changes: 20 additions & 0 deletions std/math/uints/uint8_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,26 @@ func TestValueOf(t *testing.T) {
assert.CheckCircuit(&valueOfCircuit[U32]{}, test.WithInvalidAssignment(&valueOfCircuit[U32]{In: 0x1234567812345678, Expected: [4]U8{NewU8(0x78), NewU8(0x56), NewU8(0x34), NewU8(0x12)}}))
}

type bitsRoundTripCircuit struct {
In, Expected U64
}

func (c *bitsRoundTripCircuit) Define(api frontend.API) error {
uapi, err := New[U64](api)
if err != nil {
return err
}
bits := uapi.ToBits(c.In)
res := uapi.FromBits(bits...)
uapi.AssertEq(res, c.Expected)
return nil
}

func TestBitsRoundTrip(t *testing.T) {
assert := test.NewAssert(t)
assert.CheckCircuit(&bitsRoundTripCircuit{}, test.WithValidAssignment(&bitsRoundTripCircuit{In: NewU64(0x0123456789abcdef), Expected: NewU64(0x0123456789abcdef)}))
}

type addCircuit struct {
In []U32
Expected U32
Expand Down
29 changes: 29 additions & 0 deletions std/permutation/keccakf/keccak_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"github.com/consensys/gnark-crypto/ecc"
"github.com/consensys/gnark/backend"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/frontend/cs/r1cs"
"github.com/consensys/gnark/frontend/cs/scs"
"github.com/consensys/gnark/std/permutation/keccakf"
"github.com/consensys/gnark/test"
)
Expand Down Expand Up @@ -35,6 +37,33 @@ func (c *keccakfCircuit) Define(api frontend.API) error {
return nil
}

type keccakfCountCircuit struct {
In [25]uints.U64
Expected [25]uints.U64 `gnark:",public"`
}

func (c *keccakfCountCircuit) Define(api frontend.API) error {
uapi, err := uints.New[uints.U64](api)
if err != nil {
return err
}
res := keccakf.Permute(uapi, c.In)
for i := range res {
uapi.AssertEq(res[i], c.Expected[i])
}
return nil
}

func TestKeccakfCount(t *testing.T) {
assert := test.NewAssert(t)
ccs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &keccakfCountCircuit{})
assert.NoError(err)
t.Log("KeccakF-1600 r1cs constraints =", ccs.GetNbConstraints(), "instructions =", ccs.GetNbInstructions())
ccs, err = frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, &keccakfCountCircuit{})
assert.NoError(err)
t.Log("KeccakF-1600 scs constraints =", ccs.GetNbConstraints(), "instructions =", ccs.GetNbInstructions())
}

func TestKeccakf(t *testing.T) {
var nativeIn [25]uint64
var res [25]uint64
Expand Down
Loading