Skip to content

Commit

Permalink
inverse function to U8ToVar (VarToU8) and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasmenendez committed Jan 15, 2025
1 parent 0ab4d0b commit cf8bbfb
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 8 deletions.
46 changes: 38 additions & 8 deletions utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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
}
28 changes: 28 additions & 0 deletions utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())))
}

0 comments on commit cf8bbfb

Please sign in to comment.