From cf8bbfbd057a747d4aeafc30b1fd9717031500c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Men=C3=A9ndez?= Date: Wed, 15 Jan 2025 09:08:41 +0100 Subject: [PATCH] inverse function to U8ToVar (VarToU8) and tests --- utils/utils.go | 46 +++++++++++++++++++++++++++++++++++++-------- utils/utils_test.go | 28 +++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/utils/utils.go b/utils/utils.go index 300a58a..72249f9 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -41,14 +41,7 @@ func UnpackVarToScalar[S emulated.FieldParams](api frontend.API, v frontend.Vari var fr S nBits := int(fr.BitsPerLimb()) nLimbs := int(fr.NbLimbs()) - limbs := make([]frontend.Variable, nLimbs) - // get binary representation of the variable - vBin := bits.ToBinary(api, v, bits.WithNbDigits(nBits*nLimbs)) - // group bits into limbs of nbBits until fill all limbs or all bits - for i := 0; i < nLimbs; i++ { - g := vBin[i*nBits : (i+1)*nBits] - limbs[i] = bits.FromBinary(api, g) - } + limbs := varToLimbsOfBits(api, v, nLimbs, nBits) // convert limbs to emulated element of the field field, err := emulated.NewField[S](api) if err != nil { @@ -89,6 +82,27 @@ func U8ToVar(api frontend.API, u8 []uints.U8) (frontend.Variable, error) { return res, nil } +// VarToU8 converts a variable to a slice of uint8. First, the variable is +// converted to a slice of uint64, then each uint64 is converted to a slice +// of uint8 and concatenated. Finally, the endianness is swapped to match the +// expected endianness of the slice of uint8. +func VarToU8(api frontend.API, v frontend.Variable) ([]uints.U8, error) { + limbs := varToLimbsOfBits(api, v, 4, 64) + // convert each limb to []uint8 + bf, err := uints.New[uints.U64](api) + if err != nil { + return nil, err + } + var u8 []uints.U8 + for _, limb := range limbs { + bLimb := bf.ValueOf(limb) + for _, b := range bLimb { + u8 = append(u8, b) + } + } + return SwapEndianness(u8), nil +} + // SwapEndianness swaps the endianness of a slice of uint8 by reversing it. func SwapEndianness(u8 []uints.U8) []uints.U8 { var swap []uints.U8 @@ -105,3 +119,19 @@ func SwapEndianness(u8 []uints.U8) []uints.U8 { func StrictCmp(api frontend.API, a, b frontend.Variable) frontend.Variable { return api.Select(api.IsZero(api.Sub(a, b)), 0, 1) } + +// varToLimbsOfBits function converts a variable to a slice of variables, each +// representing a limb of nbBits. The variable is first converted to a binary +// representation, then the bits are grouped into limbs of nbBits until all +// limbs are filled or all bits are used. +func varToLimbsOfBits(api frontend.API, v frontend.Variable, nLimbs, nBits int) []frontend.Variable { + limbs := make([]frontend.Variable, nLimbs) + // get binary representation of the variable + vBin := bits.ToBinary(api, v, bits.WithNbDigits(nBits*nLimbs)) + // group bits into limbs of nbBits until fill all limbs or all bits + for i := 0; i < nLimbs; i++ { + g := vBin[i*nBits : (i+1)*nBits] + limbs[i] = bits.FromBinary(api, g) + } + return limbs +} diff --git a/utils/utils_test.go b/utils/utils_test.go index b2042fe..4ea20e7 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -45,3 +45,31 @@ func TestUnpackPackVar(t *testing.T) { test.WithCurves(ecc.BW6_761), test.WithBackends(backend.GROTH16), test.WithProverOpts(groth16.GetNativeProverOptions(ecc.BN254.ScalarField(), ecc.BW6_761.ScalarField()))) } + +type testVarToU8Circuit struct { + Input frontend.Variable +} + +func (c *testVarToU8Circuit) Define(api frontend.API) error { + u8s, err := VarToU8(api, c.Input) + if err != nil { + return err + } + v, err := U8ToVar(api, u8s) + if err != nil { + return err + } + api.AssertIsEqual(c.Input, v) + return nil +} + +func TestVarToU8(t *testing.T) { + assert := test.NewAssert(t) + // generate a random big number + r, err := rand.Int(rand.Reader, ecc.BLS12_377.ScalarField()) + assert.NoError(err) + + assert.SolvingSucceeded(&testVarToU8Circuit{}, &testVarToU8Circuit{Input: r}, + test.WithCurves(ecc.BW6_761), test.WithBackends(backend.GROTH16), + test.WithProverOpts(groth16.GetNativeProverOptions(ecc.BN254.ScalarField(), ecc.BW6_761.ScalarField()))) +}