Skip to content

Commit

Permalink
new inverse method of PackScalarToVar (UnpackVarToScalar) and test
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasmenendez committed Jan 13, 2025
1 parent 8d5b0ca commit 0ab4d0b
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 0 deletions.
28 changes: 28 additions & 0 deletions utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"math/big"

"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/math/bits"
"github.com/consensys/gnark/std/math/emulated"
"github.com/consensys/gnark/std/math/uints"
)
Expand All @@ -29,6 +30,33 @@ func PackScalarToVar[S emulated.FieldParams](api frontend.API, s *emulated.Eleme
return res, nil
}

// UnpackVarToScalar function converts a frontend.Variable to an emulated
// element of the S field. It is the inverse of PackScalarToVar. The variable
// is transformed into a binary representation and then grouped into limbs of
// nbBits until all limbs are filled or all bits are used. Then the limbs are
// converted to an emulated element of the field. If something goes wrong, an
// error is returned.
func UnpackVarToScalar[S emulated.FieldParams](api frontend.API, v frontend.Variable) (*emulated.Element[S], error) {
// get field parameters
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)
}
// convert limbs to emulated element of the field
field, err := emulated.NewField[S](api)
if err != nil {
return nil, err
}
return field.NewElement(limbs), nil
}

// ElemToU8 converts a field element to a slice of uint8 by converting each
// limb to a slice of uint8 and concatenating them.
func ElemToU8[T emulated.FieldParams](api frontend.API, elem emulated.Element[T]) ([]uints.U8, error) {
Expand Down
47 changes: 47 additions & 0 deletions utils/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package utils

import (
"crypto/rand"
"testing"

"github.com/consensys/gnark-crypto/ecc"
"github.com/consensys/gnark/backend"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/algebra/native/sw_bls12377"
"github.com/consensys/gnark/std/math/emulated"
"github.com/consensys/gnark/std/recursion/groth16"
"github.com/consensys/gnark/test"
)

type testPackUnpackCircuit struct {
Input emulated.Element[sw_bls12377.ScalarField]
}

func (c *testPackUnpackCircuit) Define(api frontend.API) error {
// pack the emulated element to a variable
packed, err := PackScalarToVar[sw_bls12377.ScalarField](api, &c.Input)
if err != nil {
return err
}
// unpack the variable to an emulated element
unpacked, err := UnpackVarToScalar[sw_bls12377.ScalarField](api, packed)
if err != nil {
return err
}
// compare the limbs of the input and the unpacked element
for i, limb := range c.Input.Limbs {
api.AssertIsEqual(limb, unpacked.Limbs[i])
}
return nil
}

func TestUnpackPackVar(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(&testPackUnpackCircuit{}, &testPackUnpackCircuit{Input: emulated.ValueOf[sw_bls12377.ScalarField](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 0ab4d0b

Please sign in to comment.