diff --git a/curves/bls12377/conversions.go b/curves/bls12377/conversions.go new file mode 100644 index 0000000..2e80f36 --- /dev/null +++ b/curves/bls12377/conversions.go @@ -0,0 +1,79 @@ +package bls12377 + +import ( + bls12_377 "github.com/consensys/gnark-crypto/ecc/bls12-377" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fp" + icicle_bls12_377 "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12377" +) + +func StripZ(p *icicle_bls12_377.Projective) *icicle_bls12_377.Affine { + return &icicle_bls12_377.Affine{ + X: p.X, + Y: p.Y, + } +} + +func BatchConvertFromG1Affine(elements []bls12_377.G1Affine) []icicle_bls12_377.Affine { + var newElements []icicle_bls12_377.Affine + for _, e := range elements { + var newElement icicle_bls12_377.Projective + FromG1AffineGnark(&e, &newElement) + + newElements = append(newElements, *StripZ(&newElement)) + } + return newElements +} + +func ProjectiveToGnarkAffine(p *icicle_bls12_377.Projective) *bls12_377.G1Affine { + px := BaseFieldToGnarkFp(&p.X) + py := BaseFieldToGnarkFp(&p.Y) + pz := BaseFieldToGnarkFp(&p.Z) + + zInv := new(fp.Element) + x := new(fp.Element) + y := new(fp.Element) + + zInv.Inverse(pz) + + x.Mul(px, zInv) + y.Mul(py, zInv) + + return &bls12_377.G1Affine{X: *x, Y: *y} +} + +func G1ProjectivePointToGnarkJac(p *icicle_bls12_377.Projective) *bls12_377.G1Jac { + var p1 bls12_377.G1Jac + p1.FromAffine(ProjectiveToGnarkAffine(p)) + + return &p1 +} + +func FromG1AffineGnark(gnark *bls12_377.G1Affine, p *icicle_bls12_377.Projective) *icicle_bls12_377.Projective { + var z icicle_bls12_377.BaseField + z.One() + + p.X = *NewFieldFromFpGnark(gnark.X) + p.Y = *NewFieldFromFpGnark(gnark.Y) + p.Z = z + + return p +} + +func G1ProjectivePointFromJacGnark(p *icicle_bls12_377.Projective, gnark *bls12_377.G1Jac) *icicle_bls12_377.Projective { + var pointAffine bls12_377.G1Affine + pointAffine.FromJacobian(gnark) + + var z icicle_bls12_377.BaseField + z.One() + + p.X = *NewFieldFromFpGnark(pointAffine.X) + p.Y = *NewFieldFromFpGnark(pointAffine.Y) + p.Z = z + + return p +} + +func AffineToGnarkAffine(p *icicle_bls12_377.Affine) *bls12_377.G1Affine { + pointProjective := p.ToProjective() + return ProjectiveToGnarkAffine(&pointProjective) +} diff --git a/curves/bls12377/g1_conversions.go b/curves/bls12377/g1_conversions.go deleted file mode 100644 index b2d13bc..0000000 --- a/curves/bls12377/g1_conversions.go +++ /dev/null @@ -1,71 +0,0 @@ -package bls12377 - -import ( - "github.com/consensys/gnark-crypto/ecc/bls12-377" - "github.com/consensys/gnark-crypto/ecc/bls12-377/fp" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bls12377" -) - -func BatchConvertFromG1Affine(elements []bls12377.G1Affine) []icicle.G1PointAffine { - var newElements []icicle.G1PointAffine - for _, e := range elements { - var newElement icicle.G1ProjectivePoint - FromG1AffineGnark(&e, &newElement) - - newElements = append(newElements, *newElement.StripZ()) - } - return newElements -} - -func ProjectiveToGnarkAffine(p *icicle.G1ProjectivePoint) *bls12377.G1Affine { - px := BaseFieldToGnarkFp(&p.X) - py := BaseFieldToGnarkFp(&p.Y) - pz := BaseFieldToGnarkFp(&p.Z) - - zInv := new(fp.Element) - x := new(fp.Element) - y := new(fp.Element) - - zInv.Inverse(pz) - - x.Mul(px, zInv) - y.Mul(py, zInv) - - return &bls12377.G1Affine{X: *x, Y: *y} -} - -func G1ProjectivePointToGnarkJac(p *icicle.G1ProjectivePoint) *bls12377.G1Jac { - var p1 bls12377.G1Jac - p1.FromAffine(ProjectiveToGnarkAffine(p)) - - return &p1 -} - -func FromG1AffineGnark(gnark *bls12377.G1Affine, p *icicle.G1ProjectivePoint) *icicle.G1ProjectivePoint { - var z icicle.G1BaseField - z.SetOne() - - p.X = *NewFieldFromFpGnark(gnark.X) - p.Y = *NewFieldFromFpGnark(gnark.Y) - p.Z = z - - return p -} - -func G1ProjectivePointFromJacGnark(p *icicle.G1ProjectivePoint, gnark *bls12377.G1Jac) *icicle.G1ProjectivePoint { - var pointAffine bls12377.G1Affine - pointAffine.FromJacobian(gnark) - - var z icicle.G1BaseField - z.SetOne() - - p.X = *NewFieldFromFpGnark(pointAffine.X) - p.Y = *NewFieldFromFpGnark(pointAffine.Y) - p.Z = z - - return p -} - -func AffineToGnarkAffine(p *icicle.G1PointAffine) *bls12377.G1Affine { - return ProjectiveToGnarkAffine(p.ToProjective()) -} \ No newline at end of file diff --git a/curves/bls12377/g1_test.go b/curves/bls12377/g1_test.go index fa3eb94..65464ec 100644 --- a/curves/bls12377/g1_test.go +++ b/curves/bls12377/g1_test.go @@ -12,26 +12,188 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Code generated by Ingonyama DO NOT EDIT + package bls12377 import ( + "bufio" "fmt" + "math" + "math/big" + "os" "testing" + "time" - bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377" + "github.com/consensys/gnark-crypto/ecc" + bls12_377 "github.com/consensys/gnark-crypto/ecc/bls12-377" "github.com/consensys/gnark-crypto/ecc/bls12-377/fp" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bls12377" + core "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + icicle_bls12_377 "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12377" "github.com/stretchr/testify/assert" ) -func TestFieldBN254FromGnark(t *testing.T) { +func randG1Jac() (bls12_377.G1Jac, error) { + var point bls12_377.G1Jac + var scalar fr.Element + + _, err := scalar.SetRandom() + if err != nil { + return point, err + } + + genG1Jac, _, _, _ := bls12_377.Generators() + + //randomBigInt, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 63)) + //randomBigInt, err := rand.Int(rand.Reader, big.NewInt(100)) + randomBigInt := big.NewInt(100) + + point.ScalarMultiplication(&genG1Jac, scalar.BigInt(randomBigInt)) + return point, nil +} + +func GeneratePoints(count int) ([]icicle_bls12_377.Affine, []bls12_377.G1Affine) { + // Declare a slice of integers + var points []icicle_bls12_377.Affine + var pointsAffine []bls12_377.G1Affine + + // populate the slice + for i := 0; i < 10; i++ { + gnarkP, _ := randG1Jac() + var pointAffine bls12_377.G1Affine + pointAffine.FromJacobian(&gnarkP) + + var p icicle_bls12_377.Projective + G1ProjectivePointFromJacGnark(&p, &gnarkP) + + pointsAffine = append(pointsAffine, pointAffine) + points = append(points, *StripZ(&p)) + } + + log2_10 := math.Log2(10) + log2Count := math.Log2(float64(count)) + log2Size := int(math.Ceil(log2Count - log2_10)) + + for i := 0; i < log2Size; i++ { + pointsAffine = append(pointsAffine, pointsAffine...) + points = append(points, points...) + } + + return points[:count], pointsAffine[:count] +} + +func ReadGnarkPointsFromFile(filePath string, size int) (points []icicle_bls12_377.Affine, gnarkPoints []bls12_377.G1Affine) { + points = make([]icicle_bls12_377.Affine, size) + gnarkPoints = make([]bls12_377.G1Affine, size) + file, _ := os.Open(filePath) + scanner := bufio.NewScanner(file) + + for i := 0; scanner.Scan(); i++ { + gnarkPoints[i].X.SetString(scanner.Text()) + scanner.Scan() + gnarkPoints[i].Y.SetString(scanner.Text()) + + var p icicle_bls12_377.Projective + FromG1AffineGnark(&gnarkPoints[i], &p) + + points[i] = *StripZ(&p) + + } + return +} + +func GeneratePointsProj(count int) ([]icicle_bls12_377.Projective, []bls12_377.G1Jac) { + // Declare a slice of integers + var points []icicle_bls12_377.Projective + var pointsAffine []bls12_377.G1Jac + + // Use a loop to populate the slice + for i := 0; i < count; i++ { + gnarkP, _ := randG1Jac() + + var p icicle_bls12_377.Projective + G1ProjectivePointFromJacGnark(&p, &gnarkP) + + pointsAffine = append(pointsAffine, gnarkP) + points = append(points, p) + } + + return points, pointsAffine +} + +func GenerateScalars(count int, skewed bool) ([]icicle_bls12_377.ScalarField, []fr.Element) { + // Declare a slice of integers + var scalars []icicle_bls12_377.ScalarField + var scalars_fr []fr.Element + + var rand fr.Element + var zero fr.Element + zero.SetZero() + var one fr.Element + one.SetOne() + var randLarge fr.Element + randLarge.SetRandom() + + if skewed && count > 1_200_000 { + for i := 0; i < count-1_200_000; i++ { + rand.SetRandom() + s := NewFieldFromFrGnark(rand) + + scalars_fr = append(scalars_fr, rand) + scalars = append(scalars, *s) + } + + for i := 0; i < 600_000; i++ { + s := NewFieldFromFrGnark(randLarge) + + scalars_fr = append(scalars_fr, randLarge) + scalars = append(scalars, *s) + } + for i := 0; i < 400_000; i++ { + s := NewFieldFromFrGnark(zero) + + scalars_fr = append(scalars_fr, zero) + scalars = append(scalars, *s) + } + for i := 0; i < 200_000; i++ { + s := NewFieldFromFrGnark(one) + + scalars_fr = append(scalars_fr, one) + scalars = append(scalars, *s) + } + } else { + for i := 0; i < count; i++ { + rand.SetRandom() + s := NewFieldFromFrGnark(rand) + + scalars_fr = append(scalars_fr, rand) + scalars = append(scalars, *s) + } + } + + return scalars[:count], scalars_fr[:count] +} + +func ReadGnarkScalarsFromFile(filePath string, size int) (scalars []icicle_bls12_377.ScalarField, gnarkScalars []fr.Element) { + scalars = make([]icicle_bls12_377.ScalarField, size) + gnarkScalars = make([]fr.Element, size) + file, _ := os.Open(filePath) + scanner := bufio.NewScanner(file) + for i := 0; scanner.Scan(); i++ { + gnarkScalars[i].SetString(scanner.Text()) + scalars[i] = *NewFieldFromFrGnark(gnarkScalars[i]) + } + return +} + +func TestFieldFromGnark(t *testing.T) { var rand fr.Element rand.SetRandom() f := NewFieldFromFrGnark(rand) - - assert.Equal(t, f.S, icicle.ConvertUint64ArrToUint32Arr4(rand.Bits())) + element_bits := rand.Bits() + assert.Equal(t, f.GetLimbs(), core.ConvertUint64ArrToUint32Arr(element_bits[:])) } func BenchmarkBatchConvertFromFrGnarkThreaded(b *testing.B) { @@ -57,12 +219,12 @@ func BenchmarkBatchConvertFromFrGnark(b *testing.B) { }) } -func TestPointBN254FromGnark(t *testing.T) { +func TestPointFromGnark(t *testing.T) { gnarkP, _ := randG1Jac() - var f icicle.G1BaseField - f.SetOne() - var p icicle.G1ProjectivePoint + var f icicle_bls12_377.BaseField + f.One() + var p icicle_bls12_377.Projective G1ProjectivePointFromJacGnark(&p, &gnarkP) z_inv := new(fp.Element) @@ -83,13 +245,13 @@ func TestPointBN254FromGnark(t *testing.T) { assert.Equal(t, p.Z, f) } -func TestPointAffineNoInfinityBN254ToProjective(t *testing.T) { +func TestPointAffineNoInfinityToProjective(t *testing.T) { gnarkP, _ := randG1Jac() - var f icicle.G1BaseField - var p icicle.G1ProjectivePoint + var f icicle_bls12_377.BaseField + var p icicle_bls12_377.Projective - f.SetOne() - affine := G1ProjectivePointFromJacGnark(&p, &gnarkP).StripZ() + f.One() + affine := StripZ(G1ProjectivePointFromJacGnark(&p, &gnarkP)) proj := affine.ToProjective() assert.Equal(t, proj.X, affine.X) @@ -99,12 +261,37 @@ func TestPointAffineNoInfinityBN254ToProjective(t *testing.T) { func TestToGnarkAffine(t *testing.T) { gJac, _ := randG1Jac() - var proj icicle.G1ProjectivePoint + var proj icicle_bls12_377.Projective G1ProjectivePointFromJacGnark(&proj, &gJac) - var gAffine bls12377.G1Affine + var gAffine bls12_377.G1Affine gAffine.FromJacobian(&gJac) affine := ProjectiveToGnarkAffine(&proj) - assert.Equal(t, affine, gAffine) + assert.Equal(t, *affine, gAffine) +} + +func TestMSM(t *testing.T) { + for _, v := range []int{24} { + count := 1 << v + + _, gnarkPoints := GeneratePoints(count) + fmt.Print("Finished generating points\n") + _, gnarkScalars := GenerateScalars(count, true) + fmt.Print("Finished generating scalars\n") + + startTime := time.Now() + res, e := MsmOnDevice(gnarkPoints, gnarkScalars) // non mont + fmt.Printf("icicle MSM took: %d ms\n", time.Since(startTime).Milliseconds()) + + assert.Equal(t, e, nil, "error should be nil") + fmt.Print("Finished icicle MSM\n") + + var AffineLib bls12_377.G1Affine + + gResult, _ := AffineLib.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{}) + fmt.Print("Finished Gnark MSM\n") + + assert.True(t, gResult.Equal(res)) + } } diff --git a/curves/bls12377/g2_conversions.go b/curves/bls12377/g2_conversions.go index 232072d..a07d260 100644 --- a/curves/bls12377/g2_conversions.go +++ b/curves/bls12377/g2_conversions.go @@ -1,47 +1,66 @@ +//go:build g2 + package bls12377 import ( - "github.com/consensys/gnark-crypto/ecc/bls12-377" - "github.com/consensys/gnark-crypto/ecc/bls12-377/fp" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bls12377" "fmt" + + bls12_377 "github.com/consensys/gnark-crypto/ecc/bls12-377" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fp" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12377/g2" ) -func ToGnarkFp(f *icicle.G2Element) *fp.Element { - fb := f.ToBytesLe() +func ToGnarkFp(f *g2.G2BaseField) *fp.Element { + fb := f.ToBytesLittleEndian() var b48 [48]byte copy(b48[:], fb[:48]) v, e := fp.LittleEndian.Element(&b48) if e != nil { - panic(fmt.Sprintf("unable to create convert point %v got error %v", f, e)) + panic(fmt.Sprintf("unable to convert point %v; got error %v", f, e)) } return &v } -func ToGnarkE2(f *icicle.ExtentionField) bls12377.E2 { - return bls12377.E2{ - A0: *ToGnarkFp(&f.A0), - A1: *ToGnarkFp(&f.A1), +func ToGnarkE2(f *g2.G2BaseField) bls12_377.E2 { + bytes := f.ToBytesLittleEndian() + a0, _ := fp.LittleEndian.Element((*[fp.Bytes]byte)(bytes[:len(bytes)/2])) + a1, _ := fp.LittleEndian.Element((*[fp.Bytes]byte)(bytes[len(bytes)/2:])) + return bls12_377.E2{ + A0: a0, + A1: a1, } } -func G2PointToGnarkJac(p *icicle.G2Point) *bls12377.G2Jac { +func GnarkE2Bits(f *bls12_377.E2) []uint64 { + a0 := f.A0.Bits() + a1 := f.A1.Bits() + return append(a0[:], a1[:]...) +} + +func FromGnarkE2(f *bls12_377.E2) g2.G2BaseField { + var field g2.G2BaseField + field.FromLimbs(core.ConvertUint64ArrToUint32Arr(GnarkE2Bits(f))) + return field +} + +func G2PointToGnarkJac(p *g2.G2Projective) *bls12_377.G2Jac { x := ToGnarkE2(&p.X) y := ToGnarkE2(&p.Y) z := ToGnarkE2(&p.Z) - var zSquared bls12377.E2 + var zSquared bls12_377.E2 zSquared.Mul(&z, &z) - var X bls12377.E2 + var X bls12_377.E2 X.Mul(&x, &z) - var Y bls12377.E2 + var Y bls12_377.E2 Y.Mul(&y, &zSquared) - after := bls12377.G2Jac{ + after := bls12_377.G2Jac{ X: X, Y: Y, Z: z, @@ -50,31 +69,29 @@ func G2PointToGnarkJac(p *icicle.G2Point) *bls12377.G2Jac { return &after } -func G2AffineFromGnarkAffine(gnark *bls12377.G2Affine, g *icicle.G2PointAffine) *icicle.G2PointAffine { - g.X.A0 = gnark.X.A0.Bits() - g.X.A1 = gnark.X.A1.Bits() - g.Y.A0 = gnark.Y.A0.Bits() - g.Y.A1 = gnark.Y.A1.Bits() +func G2PointToGnarkAffine(p *g2.G2Projective) *bls12_377.G2Affine { + var affine bls12_377.G2Affine + affine.FromJacobian(G2PointToGnarkJac(p)) + return &affine +} +func G2AffineFromGnarkAffine(gnark *bls12_377.G2Affine, g *g2.G2Affine) *g2.G2Affine { + g.X = FromGnarkE2(&gnark.X) + g.Y = FromGnarkE2(&gnark.Y) return g } -func G2PointAffineFromGnarkJac(gnark *bls12377.G2Jac, g *icicle.G2PointAffine) *icicle.G2PointAffine { - var pointAffine bls12377.G2Affine +func G2PointAffineFromGnarkJac(gnark *bls12_377.G2Jac, g *g2.G2Affine) *g2.G2Affine { + var pointAffine bls12_377.G2Affine pointAffine.FromJacobian(gnark) - g.X.A0 = pointAffine.X.A0.Bits() - g.X.A1 = pointAffine.X.A1.Bits() - g.Y.A0 = pointAffine.Y.A0.Bits() - g.Y.A1 = pointAffine.Y.A1.Bits() - - return g + return G2AffineFromGnarkAffine(&pointAffine, g) } -func BatchConvertFromG2Affine(elements []bls12377.G2Affine) []icicle.G2PointAffine { - var newElements []icicle.G2PointAffine +func BatchConvertFromG2Affine(elements []bls12_377.G2Affine) []g2.G2Affine { + var newElements []g2.G2Affine for _, gg2Affine := range elements { - var newElement icicle.G2PointAffine + var newElement g2.G2Affine G2AffineFromGnarkAffine(&gg2Affine, &newElement) newElements = append(newElements, newElement) @@ -82,19 +99,19 @@ func BatchConvertFromG2Affine(elements []bls12377.G2Affine) []icicle.G2PointAffi return newElements } -func BatchConvertFromG2AffineThreads(elements []bls12377.G2Affine, routines int) []icicle.G2PointAffine { - var newElements []icicle.G2PointAffine +func BatchConvertFromG2AffineThreaded(elements []bls12_377.G2Affine, routines int) []g2.G2Affine { + var newElements []g2.G2Affine if routines > 1 && routines <= len(elements) { - channels := make([]chan []icicle.G2PointAffine, routines) + channels := make([]chan []g2.G2Affine, routines) for i := 0; i < routines; i++ { - channels[i] = make(chan []icicle.G2PointAffine, 1) + channels[i] = make(chan []g2.G2Affine, 1) } - convert := func(elements []bls12377.G2Affine, chanIndex int) { - var convertedElements []icicle.G2PointAffine + convert := func(elements []bls12_377.G2Affine, chanIndex int) { + var convertedElements []g2.G2Affine for _, e := range elements { - var converted icicle.G2PointAffine + var converted g2.G2Affine G2AffineFromGnarkAffine(&e, &converted) convertedElements = append(convertedElements, converted) } @@ -118,7 +135,7 @@ func BatchConvertFromG2AffineThreads(elements []bls12377.G2Affine, routines int) } } else { for _, e := range elements { - var converted icicle.G2PointAffine + var converted g2.G2Affine G2AffineFromGnarkAffine(&e, &converted) newElements = append(newElements, converted) } diff --git a/curves/bls12377/g2_icicle.go b/curves/bls12377/g2_icicle.go new file mode 100644 index 0000000..89b02fa --- /dev/null +++ b/curves/bls12377/g2_icicle.go @@ -0,0 +1,34 @@ +//go:build g2 + +package bls12377 + +import ( + "errors" + + bls12_377 "github.com/consensys/gnark-crypto/ecc/bls12-377" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + cr "github.com/ingonyama-zk/icicle/v2/wrappers/golang/cuda_runtime" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12377/g2" +) + +func G2MsmOnDevice(gnarkPoints []bls12_377.G2Affine, gnarkScalars []fr.Element) (*bls12_377.G2Affine, error) { + iciclePoints := core.HostSliceFromElements(BatchConvertFromG2Affine(gnarkPoints)) + icicleScalars := core.HostSliceFromElements(BatchConvertFromFrGnark(gnarkScalars)) + + cfg := core.GetDefaultMSMConfig() + var p g2.G2Projective + var out core.DeviceSlice + _, e := out.Malloc(p.Size(), p.Size()) + if e != cr.CudaSuccess { + return nil, errors.New("Cannot allocate") + } + e = g2.G2Msm(icicleScalars, iciclePoints, &cfg, out) + if e != cr.CudaSuccess { + return nil, errors.New("Msm failed") + } + outHost := make(core.HostSlice[g2.G2Projective], 1) + outHost.CopyFromDevice(&out) + out.Free() + return G2PointToGnarkAffine(&outHost[0]), nil +} diff --git a/curves/bls12377/g2_test.go b/curves/bls12377/g2_test.go index 2f7957e..ffaa9a7 100644 --- a/curves/bls12377/g2_test.go +++ b/curves/bls12377/g2_test.go @@ -1,4 +1,4 @@ -// Copyright 2023 Ingonyama +// Copyright 2024 Ingonyama // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,27 +12,102 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Code generated by Ingonyama DO NOT EDIT + +//go:build g2 + package bls12377 import ( "fmt" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12377/g2" + "math" + "math/big" "testing" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bls12377" + "github.com/consensys/gnark-crypto/ecc" + bls12_377 "github.com/consensys/gnark-crypto/ecc/bls12-377" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/stretchr/testify/assert" ) +func randG2Jac() (bls12_377.G2Jac, error) { + var point bls12_377.G2Jac + var scalar fr.Element + + _, err := scalar.SetRandom() + if err != nil { + return point, err + } + + _, genG2Jac, _, _ := bls12_377.Generators() + + randomBigInt := big.NewInt(1000) + + point.ScalarMultiplication(&genG2Jac, scalar.BigInt(randomBigInt)) + return point, nil +} + +func GenerateG2Points(count int) ([]g2.G2Affine, []bls12_377.G2Affine) { + // Declare a slice of integers + var points []g2.G2Affine + var pointsAffine []bls12_377.G2Affine + + // populate the slice + for i := 0; i < 10; i++ { + gnarkP, _ := randG2Jac() + + var p g2.G2Affine + G2PointAffineFromGnarkJac(&gnarkP, &p) + + var gp bls12_377.G2Affine + gp.FromJacobian(&gnarkP) + pointsAffine = append(pointsAffine, gp) + points = append(points, p) + } + + log2_10 := math.Log2(10) + log2Count := math.Log2(float64(count)) + log2Size := int(math.Ceil(log2Count - log2_10)) + + for i := 0; i < log2Size; i++ { + pointsAffine = append(pointsAffine, pointsAffine...) + points = append(points, points...) + } + + return points[:count], pointsAffine[:count] +} + func TestToGnarkJacG2(t *testing.T) { gnark, _ := randG2Jac() - var pointAffine icicle.G2PointAffine + var pointAffine g2.G2Affine G2PointAffineFromGnarkJac(&gnark, &pointAffine) - var pointProjective icicle.G2Point - pointProjective.FromAffine(&pointAffine) + var pointProjective g2.G2Projective + pointProjective.FromAffine(pointAffine) fmt.Printf("%+v\n", pointProjective) backToGnark := G2PointToGnarkJac(&pointProjective) assert.True(t, gnark.Equal(backToGnark)) } + +func TestMsmG2(t *testing.T) { + for _, v := range []int{24} { + count := 1 << v + _, gnarkPoints := GenerateG2Points(count) + fmt.Print("Finished generating points\n") + _, gnarkScalars := GenerateScalars(count, false) + fmt.Print("Finished generating scalars\n") + + res, e := G2MsmOnDevice(gnarkPoints, gnarkScalars) + assert.Equal(t, e, nil, "error should be nil") + + var AffineLib bls12_377.G2Affine + + gResult, _ := AffineLib.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{}) + + assert.True(t, gResult.Equal(res)) + } +} diff --git a/curves/bls12377/g2_utils.go b/curves/bls12377/g2_utils.go new file mode 100644 index 0000000..ee681c8 --- /dev/null +++ b/curves/bls12377/g2_utils.go @@ -0,0 +1,25 @@ +//go:build g2 + +package bls12377 + +import ( + bls12_377 "github.com/consensys/gnark-crypto/ecc/bls12-377" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12377/g2" +) + +func CopyG2PointsToDevice(points []bls12_377.G2Affine, pointsBytes int, copyDone chan core.DeviceSlice) { + var devicePonts core.DeviceSlice + if pointsBytes == 0 { + copyDone <- devicePonts + } else { + iciclePoints := core.HostSliceFromElements(BatchConvertFromG2Affine(points)) + iciclePoints.CopyToDevice(&devicePonts, true) + + copyDone <- devicePonts + } +} + +func HostSliceFromG2Points(gnarkPoints []bls12_377.G2Affine) core.HostSlice[g2.G2Affine] { + return core.HostSliceFromElements(BatchConvertFromG2Affine(gnarkPoints)) +} diff --git a/curves/bls12377/icicle.go b/curves/bls12377/icicle.go index 72b7d35..52dc2a5 100644 --- a/curves/bls12377/icicle.go +++ b/curves/bls12377/icicle.go @@ -1,105 +1,137 @@ package bls12377 import ( - "fmt" - "math" - "unsafe" - - "github.com/consensys/gnark-crypto/ecc/bls12-377" - "github.com/consensys/gnark-crypto/ecc/bls12-377/fp" - goicicle "github.com/ingonyama-zk/icicle/goicicle" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bls12377" + "errors" + + bls12_377 "github.com/consensys/gnark-crypto/ecc/bls12-377" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + cr "github.com/ingonyama-zk/icicle/v2/wrappers/golang/cuda_runtime" + icicle_bls12_377 "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12377" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12377/msm" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12377/ntt" ) -type OnDeviceData struct { - P unsafe.Pointer - Size int +func MsmOnDevice(gnarkPoints []bls12_377.G1Affine, gnarkScalars []fr.Element) (*bls12_377.G1Affine, error) { + iciclePoints := HostSliceFromPoints(gnarkPoints) + icicleScalars := HostSliceFromScalars(gnarkScalars) + + cfg := core.GetDefaultMSMConfig() + var p icicle_bls12_377.Projective + var out core.DeviceSlice + _, e := out.Malloc(p.Size(), p.Size()) + if e != cr.CudaSuccess { + return nil, errors.New("cannot allocate") + } + e = msm.Msm(icicleScalars, iciclePoints, &cfg, out) + if e != cr.CudaSuccess { + return nil, errors.New("msm failed") + } + outHost := make(core.HostSlice[icicle_bls12_377.Projective], 1) + outHost.CopyFromDevice(&out) + out.Free() + return ProjectiveToGnarkAffine(&outHost[0]), nil } -func INttOnDevice(scalars_d, twiddles_d, cosetPowers_d unsafe.Pointer, size, sizeBytes int, isCoset bool) unsafe.Pointer { - ReverseScalars(scalars_d, size) +func Ntt[T any](gnarkScalars fr.Vector, dir core.NTTDir, cfg *core.NTTConfig[T]) (fr.Vector, error) { + icicleScalars := core.HostSliceFromElements(BatchConvertFromFrGnark(gnarkScalars)) + output := make(core.HostSlice[icicle_bls12_377.ScalarField], len(gnarkScalars)) + res := ntt.Ntt(icicleScalars, dir, cfg, output) + if res.IcicleErrorCode != core.IcicleErrorCode(0) { + return nil, errors.New("ntt failed") + } + // TODO Reverse order processing + // if cfg.Ordering == core.KNN || cfg.Ordering == core.KRR { - scalarsInterp := icicle.Interpolate(scalars_d, twiddles_d, cosetPowers_d, size, isCoset) + // } + return BatchConvertScalarFieldToFrGnark(output), nil +} - return scalarsInterp +func NttOnDevice(gnarkScalars fr.Vector) (fr.Vector, error) { + cfg := ntt.GetDefaultNttConfig() + return Ntt(gnarkScalars, core.KForward, &cfg) } -func NttOnDevice(scalars_out, scalars_d, twiddles_d, coset_powers_d unsafe.Pointer, size, twid_size, size_bytes int, isCoset bool) { - res := icicle.Evaluate(scalars_out, scalars_d, twiddles_d, coset_powers_d, size, twid_size, isCoset) +func INttOnDevice(gnarkScalars fr.Vector) (fr.Vector, error) { + cfg := ntt.GetDefaultNttConfig() + return Ntt(gnarkScalars, core.KInverse, &cfg) +} - if res != 0 { - fmt.Print("Issue evaluating") - } +// func INttOnDevice(scalars_d, twiddles_d, cosetPowers_d unsafe.Pointer, size, sizeBytes int, isCoset bool) unsafe.Pointer { +// ReverseScalars(scalars_d, size) - ReverseScalars(scalars_out, size) -} +// scalarsInterp := icicle_bls12_377.Interpolate(scalars_d, twiddles_d, cosetPowers_d, size, isCoset) -func MsmOnDevice(scalars_d, points_d unsafe.Pointer, count int, convert bool) (bls12377.G1Jac, unsafe.Pointer, error) { - pointBytes := fp.Bytes * 3 // 3 Elements because of 3 coordinates - out_d, _ := goicicle.CudaMalloc(pointBytes) +// return scalarsInterp +// } - icicle.Commit(out_d, scalars_d, points_d, count, 10) +// func NttOnDevice(scalars_out, scalars_d, twiddles_d, coset_powers_d unsafe.Pointer, size, twid_size, size_bytes int, isCoset bool) { +// res := icicle_bls12_377.Ntt(scalars_out, scalars_d, twiddles_d, coset_powers_d, size, twid_size, isCoset) - if convert { - outHost := make([]icicle.G1ProjectivePoint, 1) - goicicle.CudaMemCpyDtoH[icicle.G1ProjectivePoint](outHost, out_d, pointBytes) +// if res.IcicleErrorCode != core.IcicleErrorCode(0) { +// fmt.Print("Issue evaluating") +// } - return *G1ProjectivePointToGnarkJac(&outHost[0]), nil, nil - } +// ReverseScalars(scalars_out, size) +// } - return bls12377.G1Jac{}, out_d, nil -} +// func MsmOnDevice(scalars_d, points_d unsafe.Pointer, count int, convert bool) (bls12_377.G1Jac, unsafe.Pointer, error) { +// var p icicle_bls12_377.Projective +// var out_d core.DeviceSlice +// _, e := out_d.Malloc(p.Size(), p.Size()) +// if e != cr.CudaSuccess { +// return bls12_377.G1Jac{}, nil, errors.New("Allocation error") +// } -func MsmG2OnDevice(scalars_d, points_d unsafe.Pointer, count int, convert bool) (bls12377.G2Jac, unsafe.Pointer, error) { - pointBytes := fp.Bytes * 6 // 6 Elements because of 3 coordinates each with real and imaginary elements - out_d, _ := goicicle.CudaMalloc(pointBytes) +// icicle_bls12_377.Msm((s)) - icicle.CommitG2(out_d, scalars_d, points_d, count, 10) +// icicle_bls12_377.Msm(out_d, scalars_d, points_d, count, 10) - if convert { - outHost := make([]icicle.G2Point, 1) - goicicle.CudaMemCpyDtoH[icicle.G2Point](outHost, out_d, pointBytes) - return *G2PointToGnarkJac(&outHost[0]), nil, nil - } +// if convert { +// outHost := make([]icicle_bls12_377.Projective, 1) +// cr.CopyFromDevice(outHost, out_d, uint(pointBytes)) - return bls12377.G2Jac{}, out_d, nil -} +// return *G1ProjectivePointToGnarkJac(&outHost[0]), nil, nil +// } -func GenerateTwiddleFactors(size int, inverse bool) (unsafe.Pointer, error) { - om_selector := int(math.Log(float64(size)) / math.Log(2)) - return icicle.GenerateTwiddles(size, om_selector, inverse) -} +// return bls12_377.G1Jac{}, out_d, nil +// } -func ReverseScalars(ptr unsafe.Pointer, size int) error { - if success, err := icicle.ReverseScalars(ptr, size); success != 0 { - return err - } - - return nil -} +// func GenerateTwiddleFactors(size int, inverse bool) (unsafe.Pointer, error) { +// om_selector := int(math.Log(float64(size)) / math.Log(2)) +// return icicle_bls12_377.GenerateTwiddles(size, om_selector, inverse) +// } -func PolyOps(a_d, b_d, c_d, den_d unsafe.Pointer, size int) { - ret := icicle.VecScalarMulMod(a_d, b_d, size) +// func ReverseScalars(ptr unsafe.Pointer, size int) error { +// if success, err := icicle_bls12_377.ReverseScalars(ptr, size); success != 0 { +// return err +// } - if ret != 0 { - fmt.Print("Vector mult a*b issue") - } - ret = icicle.VecScalarSub(a_d, c_d, size) +// return nil +// } - if ret != 0 { - fmt.Print("Vector sub issue") - } - ret = icicle.VecScalarMulMod(a_d, den_d, size) +// func PolyOps(a_d, b_d, c_d, den_d unsafe.Pointer, size int) { +// ret := icicle_bls12_377.VecScalarMulMod(a_d, b_d, size) - if ret != 0 { - fmt.Print("Vector mult a*den issue") - } -} +// if ret != 0 { +// fmt.Print("Vector mult a*b issue") +// } +// ret = icicle_bls12_377.VecScalarSub(a_d, c_d, size) -func MontConvOnDevice(scalars_d unsafe.Pointer, size int, is_into bool) { - if is_into { - icicle.ToMontgomery(scalars_d, size) - } else { - icicle.FromMontgomery(scalars_d, size) - } -} +// if ret != 0 { +// fmt.Print("Vector sub issue") +// } +// ret = icicle_bls12_377.VecScalarMulMod(a_d, den_d, size) + +// if ret != 0 { +// fmt.Print("Vector mult a*den issue") +// } +// } + +// func MontConvOnDevice(scalars_d unsafe.Pointer, size int, is_into bool) { +// if is_into { +// icicle_bls12_377.ToMontgomery(scalars_d, size) +// } else { +// icicle_bls12_377.FromMontgomery(scalars_d, size) +// } +// } diff --git a/curves/bls12377/msm_test.go b/curves/bls12377/msm_test.go deleted file mode 100644 index 267dd6a..0000000 --- a/curves/bls12377/msm_test.go +++ /dev/null @@ -1,520 +0,0 @@ -// Copyright 2023 Ingonyama -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bls12377 - -import ( - "bufio" - "fmt" - "math" - "math/big" - "os" - "strings" - "testing" - "time" - "unsafe" - - "github.com/consensys/gnark-crypto/ecc" - bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377" - "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" - "github.com/ingonyama-zk/icicle/goicicle" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bls12377" - "github.com/stretchr/testify/assert" -) - -func randG1Jac() (bls12377.G1Jac, error) { - var point bls12377.G1Jac - var scalar fr.Element - - _, err := scalar.SetRandom() - if err != nil { - return point, err - } - - genG1Jac, _, _, _ := bls12377.Generators() - - //randomBigInt, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 63)) - //randomBigInt, err := rand.Int(rand.Reader, big.NewInt(100)) - randomBigInt := big.NewInt(100) - - point.ScalarMultiplication(&genG1Jac, scalar.BigInt(randomBigInt)) - return point, nil -} - -func GeneratePoints(count int) ([]icicle.G1PointAffine, []bls12377.G1Affine) { - // Declare a slice of integers - var points []icicle.G1PointAffine - var pointsAffine []bls12377.G1Affine - - // populate the slice - for i := 0; i < 10; i++ { - gnarkP, _ := randG1Jac() - var pointAffine bls12377.G1Affine - pointAffine.FromJacobian(&gnarkP) - - var p icicle.G1ProjectivePoint - G1ProjectivePointFromJacGnark(&p, &gnarkP) - - pointsAffine = append(pointsAffine, pointAffine) - points = append(points, *p.StripZ()) - } - - log2_10 := math.Log2(10) - log2Count := math.Log2(float64(count)) - log2Size := int(math.Ceil(log2Count - log2_10)) - - for i := 0; i < log2Size; i++ { - pointsAffine = append(pointsAffine, pointsAffine...) - points = append(points, points...) - } - - return points[:count], pointsAffine[:count] -} - -func ReadGnarkPointsFromFile(filePath string, size int) (points []icicle.G1PointAffine, gnarkPoints []bls12377.G1Affine) { - points = make([]icicle.G1PointAffine, size) - gnarkPoints = make([]bls12377.G1Affine, size) - file, _ := os.Open(filePath) - scanner := bufio.NewScanner(file) - - for i := 0; scanner.Scan(); i++ { - gnarkPoints[i].X.SetString(scanner.Text()) - scanner.Scan() - gnarkPoints[i].Y.SetString(scanner.Text()) - - var p icicle.G1ProjectivePoint - FromG1AffineGnark(&gnarkPoints[i], &p) - - points[i] = *p.StripZ() - - } - return -} - -func GeneratePointsProj(count int) ([]icicle.G1ProjectivePoint, []bls12377.G1Jac) { - // Declare a slice of integers - var points []icicle.G1ProjectivePoint - var pointsAffine []bls12377.G1Jac - - // Use a loop to populate the slice - for i := 0; i < count; i++ { - gnarkP, _ := randG1Jac() - - var p icicle.G1ProjectivePoint - G1ProjectivePointFromJacGnark(&p, &gnarkP) - - pointsAffine = append(pointsAffine, gnarkP) - points = append(points, p) - } - - return points, pointsAffine -} - -func GenerateScalars(count int, skewed bool) ([]icicle.G1ScalarField, []fr.Element) { - // Declare a slice of integers - var scalars []icicle.G1ScalarField - var scalars_fr []fr.Element - - var rand fr.Element - var zero fr.Element - zero.SetZero() - var one fr.Element - one.SetOne() - var randLarge fr.Element - randLarge.SetRandom() - - if skewed && count > 1_200_000 { - for i := 0; i < count-1_200_000; i++ { - rand.SetRandom() - s := NewFieldFromFrGnark(rand) - - scalars_fr = append(scalars_fr, rand) - scalars = append(scalars, *s) - } - - for i := 0; i < 600_000; i++ { - s := NewFieldFromFrGnark(randLarge) - - scalars_fr = append(scalars_fr, randLarge) - scalars = append(scalars, *s) - } - for i := 0; i < 400_000; i++ { - s := NewFieldFromFrGnark(zero) - - scalars_fr = append(scalars_fr, zero) - scalars = append(scalars, *s) - } - for i := 0; i < 200_000; i++ { - s := NewFieldFromFrGnark(one) - - scalars_fr = append(scalars_fr, one) - scalars = append(scalars, *s) - } - } else { - for i := 0; i < count; i++ { - rand.SetRandom() - s := NewFieldFromFrGnark(rand) - - scalars_fr = append(scalars_fr, rand) - scalars = append(scalars, *s) - } - } - - return scalars[:count], scalars_fr[:count] -} - -func ReadGnarkScalarsFromFile(filePath string, size int) (scalars []icicle.G1ScalarField, gnarkScalars []fr.Element) { - scalars = make([]icicle.G1ScalarField, size) - gnarkScalars = make([]fr.Element, size) - file, _ := os.Open(filePath) - scanner := bufio.NewScanner(file) - for i := 0; scanner.Scan(); i++ { - gnarkScalars[i].SetString(scanner.Text()) - scalars[i] = *NewFieldFromFrGnark(gnarkScalars[i]) - } - return -} - -func TestMSM(t *testing.T) { - for _, v := range []int{24} { - count := 1 << v - - points, gnarkPoints := GeneratePoints(count) - fmt.Print("Finished generating points\n") - scalars, gnarkScalars := GenerateScalars(count, true) - fmt.Print("Finished generating scalars\n") - - out := new(icicle.G1ProjectivePoint) - startTime := time.Now() - _, e := icicle.Msm(out, points, scalars, 0) // non mont - fmt.Printf("icicle MSM took: %d ms\n", time.Since(startTime).Milliseconds()) - - assert.Equal(t, e, nil, "error should be nil") - fmt.Print("Finished icicle MSM\n") - - var bls12377AffineLib bls12377.G1Affine - - gResult, _ := bls12377AffineLib.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{}) - fmt.Print("Finished Gnark MSM\n") - - assert.True(t, gResult.Equal(ProjectiveToGnarkAffine(out))) - } -} - -func TestCommitMSM(t *testing.T) { - for _, v := range []int{24} { - count := 1< 1 && routines <= len(elements) { - channels := make([]chan []icicle.G1ScalarField, routines) + channels := make([]chan []icicle_bls12_377.ScalarField, routines) for i := 0; i < routines; i++ { - channels[i] = make(chan []icicle.G1ScalarField, 1) + channels[i] = make(chan []icicle_bls12_377.ScalarField, 1) } convert := func(elements []fr.Element, chanIndex int) { - var convertedElements []icicle.G1ScalarField + var convertedElements []icicle_bls12_377.ScalarField for _, e := range elements { converted := NewFieldFromFrGnark(e) convertedElements = append(convertedElements, *converted) @@ -128,7 +125,7 @@ func BatchConvertFromFrGnarkThreaded(elements []fr.Element, routines int) []icic return newElements } -func BatchConvertG1BaseFieldToFrGnark(elements []icicle.G1BaseField) []fr.Element { +func BatchConvertBaseFieldToFrGnark(elements []icicle_bls12_377.BaseField) []fr.Element { var newElements []fr.Element for _, e := range elements { converted := BaseFieldToGnarkFr(&e) @@ -138,7 +135,7 @@ func BatchConvertG1BaseFieldToFrGnark(elements []icicle.G1BaseField) []fr.Elemen return newElements } -func BatchConvertG1ScalarFieldToFrGnark(elements []icicle.G1ScalarField) []fr.Element { +func BatchConvertScalarFieldToFrGnark(elements []icicle_bls12_377.ScalarField) []fr.Element { var newElements []fr.Element for _, e := range elements { converted := ScalarToGnarkFr(&e) @@ -148,7 +145,7 @@ func BatchConvertG1ScalarFieldToFrGnark(elements []icicle.G1ScalarField) []fr.El return newElements } -func BatchConvertG1BaseFieldToFrGnarkThreaded(elements []icicle.G1BaseField, routines int) []fr.Element { +func BatchConvertBaseFieldToFrGnarkThreaded(elements []icicle_bls12_377.BaseField, routines int) []fr.Element { var newElements []fr.Element if routines > 1 { @@ -157,7 +154,7 @@ func BatchConvertG1BaseFieldToFrGnarkThreaded(elements []icicle.G1BaseField, rou channels[i] = make(chan []fr.Element, 1) } - convert := func(elements []icicle.G1BaseField, chanIndex int) { + convert := func(elements []icicle_bls12_377.BaseField, chanIndex int) { var convertedElements []fr.Element for _, e := range elements { converted := BaseFieldToGnarkFr(&e) @@ -186,7 +183,7 @@ func BatchConvertG1BaseFieldToFrGnarkThreaded(elements []icicle.G1BaseField, rou return newElements } -func BatchConvertG1ScalarFieldToFrGnarkThreaded(elements []icicle.G1ScalarField, routines int) []fr.Element { +func BatchConvertScalarFieldToFrGnarkThreaded(elements []icicle_bls12_377.ScalarField, routines int) []fr.Element { var newElements []fr.Element if routines > 1 { @@ -195,7 +192,7 @@ func BatchConvertG1ScalarFieldToFrGnarkThreaded(elements []icicle.G1ScalarField, channels[i] = make(chan []fr.Element, 1) } - convert := func(elements []icicle.G1ScalarField, chanIndex int) { + convert := func(elements []icicle_bls12_377.ScalarField, chanIndex int) { var convertedElements []fr.Element for _, e := range elements { converted := ScalarToGnarkFr(&e) @@ -224,42 +221,30 @@ func BatchConvertG1ScalarFieldToFrGnarkThreaded(elements []icicle.G1ScalarField, return newElements } -func NewFieldFromFrGnark(element fr.Element) *icicle.G1ScalarField { - S := icicle.ConvertUint64ArrToUint32Arr4(element.Bits()) // get non-montgomry +func NewFieldFromFrGnark(element fr.Element) *icicle_bls12_377.ScalarField { + element_bits := element.Bits() + s := core.ConvertUint64ArrToUint32Arr(element_bits[:]) // get non-montgomry - return &icicle.G1ScalarField{S: S} + var field icicle_bls12_377.ScalarField + field.FromLimbs(s) + return &field } -func NewFieldFromFpGnark(element fp.Element) *icicle.G1BaseField { - S := icicle.ConvertUint64ArrToUint32Arr6(element.Bits()) // get non-montgomry +func NewFieldFromFpGnark(element fp.Element) *icicle_bls12_377.BaseField { + element_bits := element.Bits() + s := core.ConvertUint64ArrToUint32Arr(element_bits[:]) // get non-montgomry - return &icicle.G1BaseField{S: S} + var field icicle_bls12_377.BaseField + field.FromLimbs(s) + return &field } -func BaseFieldToGnarkFr(f *icicle.G1BaseField) *fr.Element { - fb := f.ToBytesLe() - var b32 [32]byte - copy(b32[:], fb[:32]) - - v, e := fr.LittleEndian.Element(&b32) - - if e != nil { - panic(fmt.Sprintf("unable to convert point %v got error %v", f, e)) - } - +func BaseFieldToGnarkFr(f *icicle_bls12_377.BaseField) *fr.Element { + v, _ := fr.LittleEndian.Element((*[fr.Bytes]byte)(f.ToBytesLittleEndian())) return &v } -func BaseFieldToGnarkFp(f *icicle.G1BaseField) *fp.Element { - fb := f.ToBytesLe() - var b32 [48]byte - copy(b32[:], fb[:48]) - - v, e := fp.LittleEndian.Element(&b32) - - if e != nil { - panic(fmt.Sprintf("unable to convert point %v got error %v", f, e)) - } - +func BaseFieldToGnarkFp(f *icicle_bls12_377.BaseField) *fp.Element { + v, _ := fp.LittleEndian.Element((*[fp.Bytes]byte)(f.ToBytesLittleEndian())) return &v } diff --git a/curves/bls12381/conversions.go b/curves/bls12381/conversions.go new file mode 100644 index 0000000..e6c2df9 --- /dev/null +++ b/curves/bls12381/conversions.go @@ -0,0 +1,79 @@ +package bls12381 + +import ( + bls12_381 "github.com/consensys/gnark-crypto/ecc/bls12-381" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fp" + icicle_bls12_381 "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12381" +) + +func StripZ(p *icicle_bls12_381.Projective) *icicle_bls12_381.Affine { + return &icicle_bls12_381.Affine{ + X: p.X, + Y: p.Y, + } +} + +func BatchConvertFromG1Affine(elements []bls12_381.G1Affine) []icicle_bls12_381.Affine { + var newElements []icicle_bls12_381.Affine + for _, e := range elements { + var newElement icicle_bls12_381.Projective + FromG1AffineGnark(&e, &newElement) + + newElements = append(newElements, *StripZ(&newElement)) + } + return newElements +} + +func ProjectiveToGnarkAffine(p *icicle_bls12_381.Projective) *bls12_381.G1Affine { + px := BaseFieldToGnarkFp(&p.X) + py := BaseFieldToGnarkFp(&p.Y) + pz := BaseFieldToGnarkFp(&p.Z) + + zInv := new(fp.Element) + x := new(fp.Element) + y := new(fp.Element) + + zInv.Inverse(pz) + + x.Mul(px, zInv) + y.Mul(py, zInv) + + return &bls12_381.G1Affine{X: *x, Y: *y} +} + +func G1ProjectivePointToGnarkJac(p *icicle_bls12_381.Projective) *bls12_381.G1Jac { + var p1 bls12_381.G1Jac + p1.FromAffine(ProjectiveToGnarkAffine(p)) + + return &p1 +} + +func FromG1AffineGnark(gnark *bls12_381.G1Affine, p *icicle_bls12_381.Projective) *icicle_bls12_381.Projective { + var z icicle_bls12_381.BaseField + z.One() + + p.X = *NewFieldFromFpGnark(gnark.X) + p.Y = *NewFieldFromFpGnark(gnark.Y) + p.Z = z + + return p +} + +func G1ProjectivePointFromJacGnark(p *icicle_bls12_381.Projective, gnark *bls12_381.G1Jac) *icicle_bls12_381.Projective { + var pointAffine bls12_381.G1Affine + pointAffine.FromJacobian(gnark) + + var z icicle_bls12_381.BaseField + z.One() + + p.X = *NewFieldFromFpGnark(pointAffine.X) + p.Y = *NewFieldFromFpGnark(pointAffine.Y) + p.Z = z + + return p +} + +func AffineToGnarkAffine(p *icicle_bls12_381.Affine) *bls12_381.G1Affine { + pointProjective := p.ToProjective() + return ProjectiveToGnarkAffine(&pointProjective) +} diff --git a/curves/bls12381/g1_test.go b/curves/bls12381/g1_test.go new file mode 100644 index 0000000..34d45bd --- /dev/null +++ b/curves/bls12381/g1_test.go @@ -0,0 +1,297 @@ +// Copyright 2023 Ingonyama +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by Ingonyama DO NOT EDIT + +package bls12381 + +import ( + "bufio" + "fmt" + "math" + "math/big" + "os" + "testing" + "time" + + "github.com/consensys/gnark-crypto/ecc" + bls12_381 "github.com/consensys/gnark-crypto/ecc/bls12-381" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fp" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + core "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + icicle_bls12_381 "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12381" + "github.com/stretchr/testify/assert" +) + +func randG1Jac() (bls12_381.G1Jac, error) { + var point bls12_381.G1Jac + var scalar fr.Element + + _, err := scalar.SetRandom() + if err != nil { + return point, err + } + + genG1Jac, _, _, _ := bls12_381.Generators() + + //randomBigInt, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 63)) + //randomBigInt, err := rand.Int(rand.Reader, big.NewInt(100)) + randomBigInt := big.NewInt(100) + + point.ScalarMultiplication(&genG1Jac, scalar.BigInt(randomBigInt)) + return point, nil +} + +func GeneratePoints(count int) ([]icicle_bls12_381.Affine, []bls12_381.G1Affine) { + // Declare a slice of integers + var points []icicle_bls12_381.Affine + var pointsAffine []bls12_381.G1Affine + + // populate the slice + for i := 0; i < 10; i++ { + gnarkP, _ := randG1Jac() + var pointAffine bls12_381.G1Affine + pointAffine.FromJacobian(&gnarkP) + + var p icicle_bls12_381.Projective + G1ProjectivePointFromJacGnark(&p, &gnarkP) + + pointsAffine = append(pointsAffine, pointAffine) + points = append(points, *StripZ(&p)) + } + + log2_10 := math.Log2(10) + log2Count := math.Log2(float64(count)) + log2Size := int(math.Ceil(log2Count - log2_10)) + + for i := 0; i < log2Size; i++ { + pointsAffine = append(pointsAffine, pointsAffine...) + points = append(points, points...) + } + + return points[:count], pointsAffine[:count] +} + +func ReadGnarkPointsFromFile(filePath string, size int) (points []icicle_bls12_381.Affine, gnarkPoints []bls12_381.G1Affine) { + points = make([]icicle_bls12_381.Affine, size) + gnarkPoints = make([]bls12_381.G1Affine, size) + file, _ := os.Open(filePath) + scanner := bufio.NewScanner(file) + + for i := 0; scanner.Scan(); i++ { + gnarkPoints[i].X.SetString(scanner.Text()) + scanner.Scan() + gnarkPoints[i].Y.SetString(scanner.Text()) + + var p icicle_bls12_381.Projective + FromG1AffineGnark(&gnarkPoints[i], &p) + + points[i] = *StripZ(&p) + + } + return +} + +func GeneratePointsProj(count int) ([]icicle_bls12_381.Projective, []bls12_381.G1Jac) { + // Declare a slice of integers + var points []icicle_bls12_381.Projective + var pointsAffine []bls12_381.G1Jac + + // Use a loop to populate the slice + for i := 0; i < count; i++ { + gnarkP, _ := randG1Jac() + + var p icicle_bls12_381.Projective + G1ProjectivePointFromJacGnark(&p, &gnarkP) + + pointsAffine = append(pointsAffine, gnarkP) + points = append(points, p) + } + + return points, pointsAffine +} + +func GenerateScalars(count int, skewed bool) ([]icicle_bls12_381.ScalarField, []fr.Element) { + // Declare a slice of integers + var scalars []icicle_bls12_381.ScalarField + var scalars_fr []fr.Element + + var rand fr.Element + var zero fr.Element + zero.SetZero() + var one fr.Element + one.SetOne() + var randLarge fr.Element + randLarge.SetRandom() + + if skewed && count > 1_200_000 { + for i := 0; i < count-1_200_000; i++ { + rand.SetRandom() + s := NewFieldFromFrGnark(rand) + + scalars_fr = append(scalars_fr, rand) + scalars = append(scalars, *s) + } + + for i := 0; i < 600_000; i++ { + s := NewFieldFromFrGnark(randLarge) + + scalars_fr = append(scalars_fr, randLarge) + scalars = append(scalars, *s) + } + for i := 0; i < 400_000; i++ { + s := NewFieldFromFrGnark(zero) + + scalars_fr = append(scalars_fr, zero) + scalars = append(scalars, *s) + } + for i := 0; i < 200_000; i++ { + s := NewFieldFromFrGnark(one) + + scalars_fr = append(scalars_fr, one) + scalars = append(scalars, *s) + } + } else { + for i := 0; i < count; i++ { + rand.SetRandom() + s := NewFieldFromFrGnark(rand) + + scalars_fr = append(scalars_fr, rand) + scalars = append(scalars, *s) + } + } + + return scalars[:count], scalars_fr[:count] +} + +func ReadGnarkScalarsFromFile(filePath string, size int) (scalars []icicle_bls12_381.ScalarField, gnarkScalars []fr.Element) { + scalars = make([]icicle_bls12_381.ScalarField, size) + gnarkScalars = make([]fr.Element, size) + file, _ := os.Open(filePath) + scanner := bufio.NewScanner(file) + for i := 0; scanner.Scan(); i++ { + gnarkScalars[i].SetString(scanner.Text()) + scalars[i] = *NewFieldFromFrGnark(gnarkScalars[i]) + } + return +} + +func TestFieldFromGnark(t *testing.T) { + var rand fr.Element + rand.SetRandom() + + f := NewFieldFromFrGnark(rand) + element_bits := rand.Bits() + assert.Equal(t, f.GetLimbs(), core.ConvertUint64ArrToUint32Arr(element_bits[:])) +} + +func BenchmarkBatchConvertFromFrGnarkThreaded(b *testing.B) { + // ROUTINES := []int{4,5,6,7,8} + + // for _, routineAmount := range ROUTINES { + routineAmount := 7 + _, scalars_fr := GenerateScalars(1<<24, false) + b.Run(fmt.Sprintf("Convert %d", routineAmount), func(b *testing.B) { + for n := 0; n < b.N; n++ { + _ = BatchConvertFromFrGnarkThreaded(scalars_fr, routineAmount) + } + }) + // } +} + +func BenchmarkBatchConvertFromFrGnark(b *testing.B) { + _, scalars_fr := GenerateScalars(1<<24, false) + b.Run("BatchConvert 2^24", func(b *testing.B) { + for n := 0; n < b.N; n++ { + _ = BatchConvertFromFrGnark(scalars_fr) + } + }) +} + +func TestPointFromGnark(t *testing.T) { + gnarkP, _ := randG1Jac() + + var f icicle_bls12_381.BaseField + f.One() + var p icicle_bls12_381.Projective + G1ProjectivePointFromJacGnark(&p, &gnarkP) + + z_inv := new(fp.Element) + z_invsq := new(fp.Element) + z_invq3 := new(fp.Element) + x := new(fp.Element) + y := new(fp.Element) + + z_inv.Inverse(&gnarkP.Z) + z_invsq.Mul(z_inv, z_inv) + z_invq3.Mul(z_invsq, z_inv) + + x.Mul(&gnarkP.X, z_invsq) + y.Mul(&gnarkP.Y, z_invq3) + + assert.Equal(t, p.X, *NewFieldFromFpGnark(*x)) + assert.Equal(t, p.Y, *NewFieldFromFpGnark(*y)) + assert.Equal(t, p.Z, f) +} + +func TestPointAffineNoInfinityToProjective(t *testing.T) { + gnarkP, _ := randG1Jac() + var f icicle_bls12_381.BaseField + var p icicle_bls12_381.Projective + + f.One() + affine := StripZ(G1ProjectivePointFromJacGnark(&p, &gnarkP)) + proj := affine.ToProjective() + + assert.Equal(t, proj.X, affine.X) + assert.Equal(t, proj.X, affine.X) + assert.Equal(t, proj.Z, f) +} + +func TestToGnarkAffine(t *testing.T) { + gJac, _ := randG1Jac() + var proj icicle_bls12_381.Projective + G1ProjectivePointFromJacGnark(&proj, &gJac) + + var gAffine bls12_381.G1Affine + gAffine.FromJacobian(&gJac) + + affine := ProjectiveToGnarkAffine(&proj) + assert.Equal(t, *affine, gAffine) +} + +func TestMSM(t *testing.T) { + for _, v := range []int{24} { + count := 1 << v + + _, gnarkPoints := GeneratePoints(count) + fmt.Print("Finished generating points\n") + _, gnarkScalars := GenerateScalars(count, true) + fmt.Print("Finished generating scalars\n") + + startTime := time.Now() + res, e := MsmOnDevice(gnarkPoints, gnarkScalars) // non mont + fmt.Printf("icicle MSM took: %d ms\n", time.Since(startTime).Milliseconds()) + + assert.Equal(t, e, nil, "error should be nil") + fmt.Print("Finished icicle MSM\n") + + var AffineLib bls12_381.G1Affine + + gResult, _ := AffineLib.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{}) + fmt.Print("Finished Gnark MSM\n") + + assert.True(t, gResult.Equal(res)) + } +} diff --git a/curves/bls12381/g2_conversions.go b/curves/bls12381/g2_conversions.go new file mode 100644 index 0000000..a895db7 --- /dev/null +++ b/curves/bls12381/g2_conversions.go @@ -0,0 +1,145 @@ +//go:build g2 + +package bls12381 + +import ( + "fmt" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12381/g2" + + bls12_381 "github.com/consensys/gnark-crypto/ecc/bls12-381" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fp" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" +) + +func ToGnarkFp(f *g2.G2BaseField) *fp.Element { + fb := f.ToBytesLittleEndian() + var b48 [48]byte + copy(b48[:], fb[:48]) + + v, e := fp.LittleEndian.Element(&b48) + + if e != nil { + panic(fmt.Sprintf("unable to convert point %v; got error %v", f, e)) + } + + return &v +} + +func ToGnarkE2(f *g2.G2BaseField) bls12_381.E2 { + bytes := f.ToBytesLittleEndian() + a0, _ := fp.LittleEndian.Element((*[fp.Bytes]byte)(bytes[:len(bytes)/2])) + a1, _ := fp.LittleEndian.Element((*[fp.Bytes]byte)(bytes[len(bytes)/2:])) + return bls12_381.E2{ + A0: a0, + A1: a1, + } +} + +func GnarkE2Bits(f *bls12_381.E2) []uint64 { + a0 := f.A0.Bits() + a1 := f.A1.Bits() + return append(a0[:], a1[:]...) +} + +func FromGnarkE2(f *bls12_381.E2) g2.G2BaseField { + var field g2.G2BaseField + field.FromLimbs(core.ConvertUint64ArrToUint32Arr(GnarkE2Bits(f))) + return field +} + +func G2PointToGnarkJac(p *g2.G2Projective) *bls12_381.G2Jac { + x := ToGnarkE2(&p.X) + y := ToGnarkE2(&p.Y) + z := ToGnarkE2(&p.Z) + var zSquared bls12_381.E2 + zSquared.Mul(&z, &z) + + var X bls12_381.E2 + X.Mul(&x, &z) + + var Y bls12_381.E2 + Y.Mul(&y, &zSquared) + + after := bls12_381.G2Jac{ + X: X, + Y: Y, + Z: z, + } + + return &after +} + +func G2PointToGnarkAffine(p *g2.G2Projective) *bls12_381.G2Affine { + var affine bls12_381.G2Affine + affine.FromJacobian(G2PointToGnarkJac(p)) + return &affine +} + +func G2AffineFromGnarkAffine(gnark *bls12_381.G2Affine, g *g2.G2Affine) *g2.G2Affine { + g.X = FromGnarkE2(&gnark.X) + g.Y = FromGnarkE2(&gnark.Y) + return g +} + +func G2PointAffineFromGnarkJac(gnark *bls12_381.G2Jac, g *g2.G2Affine) *g2.G2Affine { + var pointAffine bls12_381.G2Affine + pointAffine.FromJacobian(gnark) + + return G2AffineFromGnarkAffine(&pointAffine, g) +} + +func BatchConvertFromG2Affine(elements []bls12_381.G2Affine) []g2.G2Affine { + var newElements []g2.G2Affine + for _, gg2Affine := range elements { + var newElement g2.G2Affine + G2AffineFromGnarkAffine(&gg2Affine, &newElement) + + newElements = append(newElements, newElement) + } + return newElements +} + +func BatchConvertFromG2AffineThreaded(elements []bls12_381.G2Affine, routines int) []g2.G2Affine { + var newElements []g2.G2Affine + + if routines > 1 && routines <= len(elements) { + channels := make([]chan []g2.G2Affine, routines) + for i := 0; i < routines; i++ { + channels[i] = make(chan []g2.G2Affine, 1) + } + + convert := func(elements []bls12_381.G2Affine, chanIndex int) { + var convertedElements []g2.G2Affine + for _, e := range elements { + var converted g2.G2Affine + G2AffineFromGnarkAffine(&e, &converted) + convertedElements = append(convertedElements, converted) + } + + channels[chanIndex] <- convertedElements + } + + batchLen := len(elements) / routines + for i := 0; i < routines; i++ { + start := batchLen * i + end := batchLen * (i + 1) + elemsToConv := elements[start:end] + if i == routines-1 { + elemsToConv = elements[start:] + } + go convert(elemsToConv, i) + } + + for i := 0; i < routines; i++ { + newElements = append(newElements, <-channels[i]...) + } + } else { + for _, e := range elements { + var converted g2.G2Affine + G2AffineFromGnarkAffine(&e, &converted) + newElements = append(newElements, converted) + } + } + + return newElements +} diff --git a/curves/bls12381/g2_icicle.go b/curves/bls12381/g2_icicle.go new file mode 100644 index 0000000..2955d2d --- /dev/null +++ b/curves/bls12381/g2_icicle.go @@ -0,0 +1,34 @@ +//go:build g2 + +package bls12381 + +import ( + "errors" + + bls12_381 "github.com/consensys/gnark-crypto/ecc/bls12-381" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + cr "github.com/ingonyama-zk/icicle/v2/wrappers/golang/cuda_runtime" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12381/g2" +) + +func G2MsmOnDevice(gnarkPoints []bls12_381.G2Affine, gnarkScalars []fr.Element) (*bls12_381.G2Affine, error) { + iciclePoints := core.HostSliceFromElements(BatchConvertFromG2Affine(gnarkPoints)) + icicleScalars := core.HostSliceFromElements(BatchConvertFromFrGnark(gnarkScalars)) + + cfg := core.GetDefaultMSMConfig() + var p g2.G2Projective + var out core.DeviceSlice + _, e := out.Malloc(p.Size(), p.Size()) + if e != cr.CudaSuccess { + return nil, errors.New("Cannot allocate") + } + e = g2.G2Msm(icicleScalars, iciclePoints, &cfg, out) + if e != cr.CudaSuccess { + return nil, errors.New("Msm failed") + } + outHost := make(core.HostSlice[g2.G2Projective], 1) + outHost.CopyFromDevice(&out) + out.Free() + return G2PointToGnarkAffine(&outHost[0]), nil +} diff --git a/curves/bls12381/g2_test.go b/curves/bls12381/g2_test.go new file mode 100644 index 0000000..4b5bead --- /dev/null +++ b/curves/bls12381/g2_test.go @@ -0,0 +1,113 @@ +// Copyright 2024 Ingonyama +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by Ingonyama DO NOT EDIT + +//go:build g2 + +package bls12381 + +import ( + "fmt" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12381/g2" + "math" + "math/big" + "testing" + + "github.com/consensys/gnark-crypto/ecc" + bls12_381 "github.com/consensys/gnark-crypto/ecc/bls12-381" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + "github.com/stretchr/testify/assert" +) + +func randG2Jac() (bls12_381.G2Jac, error) { + var point bls12_381.G2Jac + var scalar fr.Element + + _, err := scalar.SetRandom() + if err != nil { + return point, err + } + + _, genG2Jac, _, _ := bls12_381.Generators() + + randomBigInt := big.NewInt(1000) + + point.ScalarMultiplication(&genG2Jac, scalar.BigInt(randomBigInt)) + return point, nil +} + +func GenerateG2Points(count int) ([]g2.G2Affine, []bls12_381.G2Affine) { + // Declare a slice of integers + var points []g2.G2Affine + var pointsAffine []bls12_381.G2Affine + + // populate the slice + for i := 0; i < 10; i++ { + gnarkP, _ := randG2Jac() + + var p g2.G2Affine + G2PointAffineFromGnarkJac(&gnarkP, &p) + + var gp bls12_381.G2Affine + gp.FromJacobian(&gnarkP) + pointsAffine = append(pointsAffine, gp) + points = append(points, p) + } + + log2_10 := math.Log2(10) + log2Count := math.Log2(float64(count)) + log2Size := int(math.Ceil(log2Count - log2_10)) + + for i := 0; i < log2Size; i++ { + pointsAffine = append(pointsAffine, pointsAffine...) + points = append(points, points...) + } + + return points[:count], pointsAffine[:count] +} + +func TestToGnarkJacG2(t *testing.T) { + gnark, _ := randG2Jac() + + var pointAffine g2.G2Affine + G2PointAffineFromGnarkJac(&gnark, &pointAffine) + + var pointProjective g2.G2Projective + pointProjective.FromAffine(pointAffine) + + fmt.Printf("%+v\n", pointProjective) + backToGnark := G2PointToGnarkJac(&pointProjective) + + assert.True(t, gnark.Equal(backToGnark)) +} + +func TestMsmG2(t *testing.T) { + for _, v := range []int{24} { + count := 1 << v + _, gnarkPoints := GenerateG2Points(count) + fmt.Print("Finished generating points\n") + _, gnarkScalars := GenerateScalars(count, false) + fmt.Print("Finished generating scalars\n") + + res, e := G2MsmOnDevice(gnarkPoints, gnarkScalars) + assert.Equal(t, e, nil, "error should be nil") + + var AffineLib bls12_381.G2Affine + + gResult, _ := AffineLib.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{}) + + assert.True(t, gResult.Equal(res)) + } +} diff --git a/curves/bls12381/g2_utils.go b/curves/bls12381/g2_utils.go new file mode 100644 index 0000000..bb450e6 --- /dev/null +++ b/curves/bls12381/g2_utils.go @@ -0,0 +1,25 @@ +//go:build g2 + +package bls12381 + +import ( + bls12_381 "github.com/consensys/gnark-crypto/ecc/bls12-381" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12381/g2" +) + +func CopyG2PointsToDevice(points []bls12_381.G2Affine, pointsBytes int, copyDone chan core.DeviceSlice) { + var devicePonts core.DeviceSlice + if pointsBytes == 0 { + copyDone <- devicePonts + } else { + iciclePoints := core.HostSliceFromElements(BatchConvertFromG2Affine(points)) + iciclePoints.CopyToDevice(&devicePonts, true) + + copyDone <- devicePonts + } +} + +func HostSliceFromG2Points(gnarkPoints []bls12_381.G2Affine) core.HostSlice[g2.G2Affine] { + return core.HostSliceFromElements(BatchConvertFromG2Affine(gnarkPoints)) +} diff --git a/curves/bls12381/icicle.go b/curves/bls12381/icicle.go new file mode 100644 index 0000000..166230e --- /dev/null +++ b/curves/bls12381/icicle.go @@ -0,0 +1,137 @@ +package bls12381 + +import ( + "errors" + + bls12_381 "github.com/consensys/gnark-crypto/ecc/bls12-381" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + cr "github.com/ingonyama-zk/icicle/v2/wrappers/golang/cuda_runtime" + icicle_bls12_381 "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12381" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12381/msm" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12381/ntt" +) + +func MsmOnDevice(gnarkPoints []bls12_381.G1Affine, gnarkScalars []fr.Element) (*bls12_381.G1Affine, error) { + iciclePoints := HostSliceFromPoints(gnarkPoints) + icicleScalars := HostSliceFromScalars(gnarkScalars) + + cfg := core.GetDefaultMSMConfig() + var p icicle_bls12_381.Projective + var out core.DeviceSlice + _, e := out.Malloc(p.Size(), p.Size()) + if e != cr.CudaSuccess { + return nil, errors.New("cannot allocate") + } + e = msm.Msm(icicleScalars, iciclePoints, &cfg, out) + if e != cr.CudaSuccess { + return nil, errors.New("msm failed") + } + outHost := make(core.HostSlice[icicle_bls12_381.Projective], 1) + outHost.CopyFromDevice(&out) + out.Free() + return ProjectiveToGnarkAffine(&outHost[0]), nil +} + +func Ntt[T any](gnarkScalars fr.Vector, dir core.NTTDir, cfg *core.NTTConfig[T]) (fr.Vector, error) { + icicleScalars := core.HostSliceFromElements(BatchConvertFromFrGnark(gnarkScalars)) + output := make(core.HostSlice[icicle_bls12_381.ScalarField], len(gnarkScalars)) + res := ntt.Ntt(icicleScalars, dir, cfg, output) + if res.IcicleErrorCode != core.IcicleErrorCode(0) { + return nil, errors.New("ntt failed") + } + // TODO Reverse order processing + // if cfg.Ordering == core.KNN || cfg.Ordering == core.KRR { + + // } + return BatchConvertScalarFieldToFrGnark(output), nil +} + +func NttOnDevice(gnarkScalars fr.Vector) (fr.Vector, error) { + cfg := ntt.GetDefaultNttConfig() + return Ntt(gnarkScalars, core.KForward, &cfg) +} + +func INttOnDevice(gnarkScalars fr.Vector) (fr.Vector, error) { + cfg := ntt.GetDefaultNttConfig() + return Ntt(gnarkScalars, core.KInverse, &cfg) +} + +// func INttOnDevice(scalars_d, twiddles_d, cosetPowers_d unsafe.Pointer, size, sizeBytes int, isCoset bool) unsafe.Pointer { +// ReverseScalars(scalars_d, size) + +// scalarsInterp := icicle_bls12_381.Interpolate(scalars_d, twiddles_d, cosetPowers_d, size, isCoset) + +// return scalarsInterp +// } + +// func NttOnDevice(scalars_out, scalars_d, twiddles_d, coset_powers_d unsafe.Pointer, size, twid_size, size_bytes int, isCoset bool) { +// res := icicle_bls12_381.Ntt(scalars_out, scalars_d, twiddles_d, coset_powers_d, size, twid_size, isCoset) + +// if res.IcicleErrorCode != core.IcicleErrorCode(0) { +// fmt.Print("Issue evaluating") +// } + +// ReverseScalars(scalars_out, size) +// } + +// func MsmOnDevice(scalars_d, points_d unsafe.Pointer, count int, convert bool) (bls12_381.G1Jac, unsafe.Pointer, error) { +// var p icicle_bls12_381.Projective +// var out_d core.DeviceSlice +// _, e := out_d.Malloc(p.Size(), p.Size()) +// if e != cr.CudaSuccess { +// return bls12_381.G1Jac{}, nil, errors.New("Allocation error") +// } + +// icicle_bls12_381.Msm((s)) + +// icicle_bls12_381.Msm(out_d, scalars_d, points_d, count, 10) + +// if convert { +// outHost := make([]icicle_bls12_381.Projective, 1) +// cr.CopyFromDevice(outHost, out_d, uint(pointBytes)) + +// return *G1ProjectivePointToGnarkJac(&outHost[0]), nil, nil +// } + +// return bls12_381.G1Jac{}, out_d, nil +// } + +// func GenerateTwiddleFactors(size int, inverse bool) (unsafe.Pointer, error) { +// om_selector := int(math.Log(float64(size)) / math.Log(2)) +// return icicle_bls12_381.GenerateTwiddles(size, om_selector, inverse) +// } + +// func ReverseScalars(ptr unsafe.Pointer, size int) error { +// if success, err := icicle_bls12_381.ReverseScalars(ptr, size); success != 0 { +// return err +// } + +// return nil +// } + +// func PolyOps(a_d, b_d, c_d, den_d unsafe.Pointer, size int) { +// ret := icicle_bls12_381.VecScalarMulMod(a_d, b_d, size) + +// if ret != 0 { +// fmt.Print("Vector mult a*b issue") +// } +// ret = icicle_bls12_381.VecScalarSub(a_d, c_d, size) + +// if ret != 0 { +// fmt.Print("Vector sub issue") +// } +// ret = icicle_bls12_381.VecScalarMulMod(a_d, den_d, size) + +// if ret != 0 { +// fmt.Print("Vector mult a*den issue") +// } +// } + +// func MontConvOnDevice(scalars_d unsafe.Pointer, size int, is_into bool) { +// if is_into { +// icicle_bls12_381.ToMontgomery(scalars_d, size) +// } else { +// icicle_bls12_381.FromMontgomery(scalars_d, size) +// } +// } diff --git a/curves/bls12381/utils.go b/curves/bls12381/utils.go new file mode 100644 index 0000000..8650ac2 --- /dev/null +++ b/curves/bls12381/utils.go @@ -0,0 +1,250 @@ +package bls12381 + +import ( + "fmt" + + bls12_381 "github.com/consensys/gnark-crypto/ecc/bls12-381" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fp" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + icicle_bls12_381 "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bls12381" +) + +func CopyScalarsToDevice(scalars []fr.Element, copyDone chan core.DeviceSlice) { + icicleScalars := core.HostSliceFromElements(BatchConvertFromFrGnark(scalars)) + + var deviceScalars core.DeviceSlice + icicleScalars.CopyToDevice(&deviceScalars, true) + icicle_bls12_381.AffineFromMontgomery(&deviceScalars) + + copyDone <- deviceScalars +} + +func CopyPointsToDevice(points []bls12_381.G1Affine, copyDone chan core.DeviceSlice) { + var devicePonts core.DeviceSlice + if len(points) == 0 { + copyDone <- devicePonts + } else { + iciclePoints := core.HostSliceFromElements(BatchConvertFromG1Affine(points)) + iciclePoints.CopyToDevice(&devicePonts, true) + + copyDone <- devicePonts + } +} + +func HostSliceFromScalars(gnarkScalars []fr.Element) core.HostSlice[icicle_bls12_381.ScalarField] { + return core.HostSliceFromElements(BatchConvertFromFrGnark(gnarkScalars)) +} + +func HostSliceFromPoints(gnarkPoints []bls12_381.G1Affine) core.HostSlice[icicle_bls12_381.Affine] { + return core.HostSliceFromElements(BatchConvertFromG1Affine(gnarkPoints)) +} + +func FreeDeviceSlice(deviceSlice core.DeviceSlice) { + deviceSlice.Free() +} + +func ScalarToGnarkFr(f *icicle_bls12_381.ScalarField) *fr.Element { + fb := f.ToBytesLittleEndian() + var b32 [32]byte + copy(b32[:], fb[:32]) + + v, e := fr.LittleEndian.Element(&b32) + + if e != nil { + panic(fmt.Sprintf("unable to create convert point %v got error %v", f, e)) + } + + return &v +} + +func ScalarToGnarkFp(f *icicle_bls12_381.ScalarField) *fp.Element { + fb := f.ToBytesLittleEndian() + var b48 [48]byte + copy(b48[:], fb[:48]) + + v, e := fp.LittleEndian.Element(&b48) + + if e != nil { + panic(fmt.Sprintf("unable to create convert point %v got error %v", f, e)) + } + + return &v +} + +func BatchConvertFromFrGnark(elements []fr.Element) []icicle_bls12_381.ScalarField { + var newElements []icicle_bls12_381.ScalarField + for _, e := range elements { + converted := NewFieldFromFrGnark(e) + newElements = append(newElements, *converted) + } + + return newElements +} + +func BatchConvertFromFrGnarkThreaded(elements []fr.Element, routines int) []icicle_bls12_381.ScalarField { + var newElements []icicle_bls12_381.ScalarField + + if routines > 1 && routines <= len(elements) { + channels := make([]chan []icicle_bls12_381.ScalarField, routines) + for i := 0; i < routines; i++ { + channels[i] = make(chan []icicle_bls12_381.ScalarField, 1) + } + + convert := func(elements []fr.Element, chanIndex int) { + var convertedElements []icicle_bls12_381.ScalarField + for _, e := range elements { + converted := NewFieldFromFrGnark(e) + convertedElements = append(convertedElements, *converted) + } + + channels[chanIndex] <- convertedElements + } + + batchLen := len(elements) / routines + for i := 0; i < routines; i++ { + start := batchLen * i + end := batchLen * (i + 1) + elemsToConv := elements[start:end] + if i == routines-1 { + elemsToConv = elements[start:] + } + go convert(elemsToConv, i) + } + + for i := 0; i < routines; i++ { + newElements = append(newElements, <-channels[i]...) + } + } else { + for _, e := range elements { + converted := NewFieldFromFrGnark(e) + newElements = append(newElements, *converted) + } + } + + return newElements +} + +func BatchConvertBaseFieldToFrGnark(elements []icicle_bls12_381.BaseField) []fr.Element { + var newElements []fr.Element + for _, e := range elements { + converted := BaseFieldToGnarkFr(&e) + newElements = append(newElements, *converted) + } + + return newElements +} + +func BatchConvertScalarFieldToFrGnark(elements []icicle_bls12_381.ScalarField) []fr.Element { + var newElements []fr.Element + for _, e := range elements { + converted := ScalarToGnarkFr(&e) + newElements = append(newElements, *converted) + } + + return newElements +} + +func BatchConvertBaseFieldToFrGnarkThreaded(elements []icicle_bls12_381.BaseField, routines int) []fr.Element { + var newElements []fr.Element + + if routines > 1 { + channels := make([]chan []fr.Element, routines) + for i := 0; i < routines; i++ { + channels[i] = make(chan []fr.Element, 1) + } + + convert := func(elements []icicle_bls12_381.BaseField, chanIndex int) { + var convertedElements []fr.Element + for _, e := range elements { + converted := BaseFieldToGnarkFr(&e) + convertedElements = append(convertedElements, *converted) + } + + channels[chanIndex] <- convertedElements + } + + batchLen := len(elements) / routines + for i := 0; i < routines; i++ { + elemsToConv := elements[batchLen*i : batchLen*(i+1)] + go convert(elemsToConv, i) + } + + for i := 0; i < routines; i++ { + newElements = append(newElements, <-channels[i]...) + } + } else { + for _, e := range elements { + converted := BaseFieldToGnarkFr(&e) + newElements = append(newElements, *converted) + } + } + + return newElements +} + +func BatchConvertScalarFieldToFrGnarkThreaded(elements []icicle_bls12_381.ScalarField, routines int) []fr.Element { + var newElements []fr.Element + + if routines > 1 { + channels := make([]chan []fr.Element, routines) + for i := 0; i < routines; i++ { + channels[i] = make(chan []fr.Element, 1) + } + + convert := func(elements []icicle_bls12_381.ScalarField, chanIndex int) { + var convertedElements []fr.Element + for _, e := range elements { + converted := ScalarToGnarkFr(&e) + convertedElements = append(convertedElements, *converted) + } + + channels[chanIndex] <- convertedElements + } + + batchLen := len(elements) / routines + for i := 0; i < routines; i++ { + elemsToConv := elements[batchLen*i : batchLen*(i+1)] + go convert(elemsToConv, i) + } + + for i := 0; i < routines; i++ { + newElements = append(newElements, <-channels[i]...) + } + } else { + for _, e := range elements { + converted := ScalarToGnarkFr(&e) + newElements = append(newElements, *converted) + } + } + + return newElements +} + +func NewFieldFromFrGnark(element fr.Element) *icicle_bls12_381.ScalarField { + element_bits := element.Bits() + s := core.ConvertUint64ArrToUint32Arr(element_bits[:]) // get non-montgomry + + var field icicle_bls12_381.ScalarField + field.FromLimbs(s) + return &field +} + +func NewFieldFromFpGnark(element fp.Element) *icicle_bls12_381.BaseField { + element_bits := element.Bits() + s := core.ConvertUint64ArrToUint32Arr(element_bits[:]) // get non-montgomry + + var field icicle_bls12_381.BaseField + field.FromLimbs(s) + return &field +} + +func BaseFieldToGnarkFr(f *icicle_bls12_381.BaseField) *fr.Element { + v, _ := fr.LittleEndian.Element((*[fr.Bytes]byte)(f.ToBytesLittleEndian())) + return &v +} + +func BaseFieldToGnarkFp(f *icicle_bls12_381.BaseField) *fp.Element { + v, _ := fp.LittleEndian.Element((*[fp.Bytes]byte)(f.ToBytesLittleEndian())) + return &v +} diff --git a/curves/bn254/conversions.go b/curves/bn254/conversions.go new file mode 100644 index 0000000..2a8fe9c --- /dev/null +++ b/curves/bn254/conversions.go @@ -0,0 +1,79 @@ +package bn254 + +import ( + bn254 "github.com/consensys/gnark-crypto/ecc/bn254" + "github.com/consensys/gnark-crypto/ecc/bn254/fp" + icicle_bn254 "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bn254" +) + +func StripZ(p *icicle_bn254.Projective) *icicle_bn254.Affine { + return &icicle_bn254.Affine{ + X: p.X, + Y: p.Y, + } +} + +func BatchConvertFromG1Affine(elements []bn254.G1Affine) []icicle_bn254.Affine { + var newElements []icicle_bn254.Affine + for _, e := range elements { + var newElement icicle_bn254.Projective + FromG1AffineGnark(&e, &newElement) + + newElements = append(newElements, *StripZ(&newElement)) + } + return newElements +} + +func ProjectiveToGnarkAffine(p *icicle_bn254.Projective) *bn254.G1Affine { + px := BaseFieldToGnarkFp(&p.X) + py := BaseFieldToGnarkFp(&p.Y) + pz := BaseFieldToGnarkFp(&p.Z) + + zInv := new(fp.Element) + x := new(fp.Element) + y := new(fp.Element) + + zInv.Inverse(pz) + + x.Mul(px, zInv) + y.Mul(py, zInv) + + return &bn254.G1Affine{X: *x, Y: *y} +} + +func G1ProjectivePointToGnarkJac(p *icicle_bn254.Projective) *bn254.G1Jac { + var p1 bn254.G1Jac + p1.FromAffine(ProjectiveToGnarkAffine(p)) + + return &p1 +} + +func FromG1AffineGnark(gnark *bn254.G1Affine, p *icicle_bn254.Projective) *icicle_bn254.Projective { + var z icicle_bn254.BaseField + z.One() + + p.X = *NewFieldFromFpGnark(gnark.X) + p.Y = *NewFieldFromFpGnark(gnark.Y) + p.Z = z + + return p +} + +func G1ProjectivePointFromJacGnark(p *icicle_bn254.Projective, gnark *bn254.G1Jac) *icicle_bn254.Projective { + var pointAffine bn254.G1Affine + pointAffine.FromJacobian(gnark) + + var z icicle_bn254.BaseField + z.One() + + p.X = *NewFieldFromFpGnark(pointAffine.X) + p.Y = *NewFieldFromFpGnark(pointAffine.Y) + p.Z = z + + return p +} + +func AffineToGnarkAffine(p *icicle_bn254.Affine) *bn254.G1Affine { + pointProjective := p.ToProjective() + return ProjectiveToGnarkAffine(&pointProjective) +} diff --git a/curves/bn254/g1_conversions.go b/curves/bn254/g1_conversions.go deleted file mode 100644 index 0d50d34..0000000 --- a/curves/bn254/g1_conversions.go +++ /dev/null @@ -1,71 +0,0 @@ -package bn254 - -import ( - "github.com/consensys/gnark-crypto/ecc/bn254" - "github.com/consensys/gnark-crypto/ecc/bn254/fp" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bn254" -) - -func BatchConvertFromG1Affine(elements []bn254.G1Affine) []icicle.G1PointAffine { - var newElements []icicle.G1PointAffine - for _, e := range elements { - var newElement icicle.G1ProjectivePoint - FromG1AffineGnark(&e, &newElement) - - newElements = append(newElements, *newElement.StripZ()) - } - return newElements -} - -func ProjectiveToGnarkAffine(p *icicle.G1ProjectivePoint) *bn254.G1Affine { - px := BaseFieldToGnarkFp(&p.X) - py := BaseFieldToGnarkFp(&p.Y) - pz := BaseFieldToGnarkFp(&p.Z) - - zInv := new(fp.Element) - x := new(fp.Element) - y := new(fp.Element) - - zInv.Inverse(pz) - - x.Mul(px, zInv) - y.Mul(py, zInv) - - return &bn254.G1Affine{X: *x, Y: *y} -} - -func G1ProjectivePointToGnarkJac(p *icicle.G1ProjectivePoint) *bn254.G1Jac { - var p1 bn254.G1Jac - p1.FromAffine(ProjectiveToGnarkAffine(p)) - - return &p1 -} - -func FromG1AffineGnark(gnark *bn254.G1Affine, p *icicle.G1ProjectivePoint) *icicle.G1ProjectivePoint { - var z icicle.G1BaseField - z.SetOne() - - p.X = *NewFieldFromFpGnark[icicle.G1BaseField](gnark.X) - p.Y = *NewFieldFromFpGnark[icicle.G1BaseField](gnark.Y) - p.Z = z - - return p -} - -func G1ProjectivePointFromJacGnark(p *icicle.G1ProjectivePoint, gnark *bn254.G1Jac) *icicle.G1ProjectivePoint { - var pointAffine bn254.G1Affine - pointAffine.FromJacobian(gnark) - - var z icicle.G1BaseField - z.SetOne() - - p.X = *NewFieldFromFpGnark[icicle.G1BaseField](pointAffine.X) - p.Y = *NewFieldFromFpGnark[icicle.G1BaseField](pointAffine.Y) - p.Z = z - - return p -} - -func AffineToGnarkAffine(p *icicle.G1PointAffine) *bn254.G1Affine { - return ProjectiveToGnarkAffine(p.ToProjective()) -} \ No newline at end of file diff --git a/curves/bn254/g1_test.go b/curves/bn254/g1_test.go index cac90c9..4b21d2f 100644 --- a/curves/bn254/g1_test.go +++ b/curves/bn254/g1_test.go @@ -11,28 +11,189 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - + // Code generated by Ingonyama DO NOT EDIT package bn254 import ( + "bufio" "fmt" + "math" + "math/big" + "os" "testing" - "github.com/consensys/gnark-crypto/ecc/bn254" + "time" + + "github.com/consensys/gnark-crypto/ecc" + bn254 "github.com/consensys/gnark-crypto/ecc/bn254" "github.com/consensys/gnark-crypto/ecc/bn254/fp" "github.com/consensys/gnark-crypto/ecc/bn254/fr" + core "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + icicle_bn254 "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bn254" "github.com/stretchr/testify/assert" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bn254" ) -func TestFieldBN254FromGnark(t *testing.T) { +func randG1Jac() (bn254.G1Jac, error) { + var point bn254.G1Jac + var scalar fr.Element + + _, err := scalar.SetRandom() + if err != nil { + return point, err + } + + genG1Jac, _, _, _ := bn254.Generators() + + //randomBigInt, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 63)) + //randomBigInt, err := rand.Int(rand.Reader, big.NewInt(100)) + randomBigInt := big.NewInt(100) + + point.ScalarMultiplication(&genG1Jac, scalar.BigInt(randomBigInt)) + return point, nil +} + +func GeneratePoints(count int) ([]icicle_bn254.Affine, []bn254.G1Affine) { + // Declare a slice of integers + var points []icicle_bn254.Affine + var pointsAffine []bn254.G1Affine + + // populate the slice + for i := 0; i < 10; i++ { + gnarkP, _ := randG1Jac() + var pointAffine bn254.G1Affine + pointAffine.FromJacobian(&gnarkP) + + var p icicle_bn254.Projective + G1ProjectivePointFromJacGnark(&p, &gnarkP) + + pointsAffine = append(pointsAffine, pointAffine) + points = append(points, *StripZ(&p)) + } + + log2_10 := math.Log2(10) + log2Count := math.Log2(float64(count)) + log2Size := int(math.Ceil(log2Count - log2_10)) + + for i := 0; i < log2Size; i++ { + pointsAffine = append(pointsAffine, pointsAffine...) + points = append(points, points...) + } + + return points[:count], pointsAffine[:count] +} + +func ReadGnarkPointsFromFile(filePath string, size int) (points []icicle_bn254.Affine, gnarkPoints []bn254.G1Affine) { + points = make([]icicle_bn254.Affine, size) + gnarkPoints = make([]bn254.G1Affine, size) + file, _ := os.Open(filePath) + scanner := bufio.NewScanner(file) + + for i := 0; scanner.Scan(); i++ { + gnarkPoints[i].X.SetString(scanner.Text()) + scanner.Scan() + gnarkPoints[i].Y.SetString(scanner.Text()) + + var p icicle_bn254.Projective + FromG1AffineGnark(&gnarkPoints[i], &p) + + points[i] = *StripZ(&p) + + } + return +} + +func GeneratePointsProj(count int) ([]icicle_bn254.Projective, []bn254.G1Jac) { + // Declare a slice of integers + var points []icicle_bn254.Projective + var pointsAffine []bn254.G1Jac + + // Use a loop to populate the slice + for i := 0; i < count; i++ { + gnarkP, _ := randG1Jac() + + var p icicle_bn254.Projective + G1ProjectivePointFromJacGnark(&p, &gnarkP) + + pointsAffine = append(pointsAffine, gnarkP) + points = append(points, p) + } + + return points, pointsAffine +} + +func GenerateScalars(count int, skewed bool) ([]icicle_bn254.ScalarField, []fr.Element) { + // Declare a slice of integers + var scalars []icicle_bn254.ScalarField + var scalars_fr []fr.Element + var rand fr.Element - rand.SetRandom() + var zero fr.Element + zero.SetZero() + var one fr.Element + one.SetOne() + var randLarge fr.Element + randLarge.SetRandom() + + if skewed && count > 1_200_000 { + for i := 0; i < count-1_200_000; i++ { + rand.SetRandom() + s := NewFieldFromFrGnark(rand) + + scalars_fr = append(scalars_fr, rand) + scalars = append(scalars, *s) + } + + for i := 0; i < 600_000; i++ { + s := NewFieldFromFrGnark(randLarge) + + scalars_fr = append(scalars_fr, randLarge) + scalars = append(scalars, *s) + } + for i := 0; i < 400_000; i++ { + s := NewFieldFromFrGnark(zero) + + scalars_fr = append(scalars_fr, zero) + scalars = append(scalars, *s) + } + for i := 0; i < 200_000; i++ { + s := NewFieldFromFrGnark(one) + + scalars_fr = append(scalars_fr, one) + scalars = append(scalars, *s) + } + } else { + for i := 0; i < count; i++ { + rand.SetRandom() + s := NewFieldFromFrGnark(rand) + + scalars_fr = append(scalars_fr, rand) + scalars = append(scalars, *s) + } + } + + return scalars[:count], scalars_fr[:count] +} - f := NewFieldFromFrGnark[icicle.G1ScalarField](rand) +func ReadGnarkScalarsFromFile(filePath string, size int) (scalars []icicle_bn254.ScalarField, gnarkScalars []fr.Element) { + scalars = make([]icicle_bn254.ScalarField, size) + gnarkScalars = make([]fr.Element, size) + file, _ := os.Open(filePath) + scanner := bufio.NewScanner(file) + for i := 0; scanner.Scan(); i++ { + gnarkScalars[i].SetString(scanner.Text()) + scalars[i] = *NewFieldFromFrGnark(gnarkScalars[i]) + } + return +} - assert.Equal(t, f.S, icicle.ConvertUint64ArrToUint32Arr(rand.Bits())) +func TestFieldFromGnark(t *testing.T) { + var rand fr.Element + rand.SetRandom() + + f := NewFieldFromFrGnark(rand) + element_bits := rand.Bits() + assert.Equal(t, f.GetLimbs(), core.ConvertUint64ArrToUint32Arr(element_bits[:])) } func BenchmarkBatchConvertFromFrGnarkThreaded(b *testing.B) { @@ -40,31 +201,31 @@ func BenchmarkBatchConvertFromFrGnarkThreaded(b *testing.B) { // for _, routineAmount := range ROUTINES { routineAmount := 7 - _, scalars_fr := GenerateScalars(1 << 24, false) + _, scalars_fr := GenerateScalars(1<<24, false) b.Run(fmt.Sprintf("Convert %d", routineAmount), func(b *testing.B) { for n := 0; n < b.N; n++ { - _ = BatchConvertFromFrGnarkThreaded[icicle.G1ScalarField](scalars_fr, routineAmount) + _ = BatchConvertFromFrGnarkThreaded(scalars_fr, routineAmount) } }) // } } func BenchmarkBatchConvertFromFrGnark(b *testing.B) { - _, scalars_fr := GenerateScalars(1 << 24, false) + _, scalars_fr := GenerateScalars(1<<24, false) b.Run("BatchConvert 2^24", func(b *testing.B) { for n := 0; n < b.N; n++ { - _ = BatchConvertFromFrGnark[icicle.G1ScalarField](scalars_fr) + _ = BatchConvertFromFrGnark(scalars_fr) } }) } -func TestPointBN254FromGnark(t *testing.T) { +func TestPointFromGnark(t *testing.T) { gnarkP, _ := randG1Jac() - var f icicle.G1BaseField - f.SetOne() - var p icicle.G1ProjectivePoint - G1ProjectivePointFromJacGnark(&p,&gnarkP) + var f icicle_bn254.BaseField + f.One() + var p icicle_bn254.Projective + G1ProjectivePointFromJacGnark(&p, &gnarkP) z_inv := new(fp.Element) z_invsq := new(fp.Element) @@ -79,18 +240,18 @@ func TestPointBN254FromGnark(t *testing.T) { x.Mul(&gnarkP.X, z_invsq) y.Mul(&gnarkP.Y, z_invq3) - assert.Equal(t, p.X, *NewFieldFromFpGnark[icicle.G1BaseField](*x)) - assert.Equal(t, p.Y, *NewFieldFromFpGnark[icicle.G1BaseField](*y)) + assert.Equal(t, p.X, *NewFieldFromFpGnark(*x)) + assert.Equal(t, p.Y, *NewFieldFromFpGnark(*y)) assert.Equal(t, p.Z, f) } -func TestPointAffineNoInfinityBN254ToProjective(t *testing.T) { +func TestPointAffineNoInfinityToProjective(t *testing.T) { gnarkP, _ := randG1Jac() - var f icicle.G1BaseField - var p icicle.G1ProjectivePoint - - f.SetOne() - affine := G1ProjectivePointFromJacGnark(&p,&gnarkP).StripZ() + var f icicle_bn254.BaseField + var p icicle_bn254.Projective + + f.One() + affine := StripZ(G1ProjectivePointFromJacGnark(&p, &gnarkP)) proj := affine.ToProjective() assert.Equal(t, proj.X, affine.X) @@ -100,12 +261,37 @@ func TestPointAffineNoInfinityBN254ToProjective(t *testing.T) { func TestToGnarkAffine(t *testing.T) { gJac, _ := randG1Jac() - var proj icicle.G1ProjectivePoint + var proj icicle_bn254.Projective G1ProjectivePointFromJacGnark(&proj, &gJac) var gAffine bn254.G1Affine gAffine.FromJacobian(&gJac) affine := ProjectiveToGnarkAffine(&proj) - assert.Equal(t, affine, gAffine) + assert.Equal(t, *affine, gAffine) +} + +func TestMSM(t *testing.T) { + for _, v := range []int{24} { + count := 1 << v + + _, gnarkPoints := GeneratePoints(count) + fmt.Print("Finished generating points\n") + _, gnarkScalars := GenerateScalars(count, true) + fmt.Print("Finished generating scalars\n") + + startTime := time.Now() + res, e := MsmOnDevice(gnarkPoints, gnarkScalars) // non mont + fmt.Printf("icicle MSM took: %d ms\n", time.Since(startTime).Milliseconds()) + + assert.Equal(t, e, nil, "error should be nil") + fmt.Print("Finished icicle MSM\n") + + var AffineLib bn254.G1Affine + + gResult, _ := AffineLib.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{}) + fmt.Print("Finished Gnark MSM\n") + + assert.True(t, gResult.Equal(res)) + } } diff --git a/curves/bn254/g2_conversions.go b/curves/bn254/g2_conversions.go index 7d68657..07bb27e 100644 --- a/curves/bn254/g2_conversions.go +++ b/curves/bn254/g2_conversions.go @@ -1,14 +1,18 @@ +//go:build g2 + package bn254 import ( - "github.com/consensys/gnark-crypto/ecc/bn254" - "github.com/consensys/gnark-crypto/ecc/bn254/fp" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bn254" "fmt" + + bn254 "github.com/consensys/gnark-crypto/ecc/bn254" + "github.com/consensys/gnark-crypto/ecc/bn254/fp" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bn254/g2" ) -func ToGnarkFp(f *icicle.G2Element) *fp.Element { - fb := f.ToBytesLe() +func ToGnarkFp(f *g2.G2BaseField) *fp.Element { + fb := f.ToBytesLittleEndian() var b32 [32]byte copy(b32[:], fb[:32]) @@ -21,14 +25,29 @@ func ToGnarkFp(f *icicle.G2Element) *fp.Element { return &v } -func ToGnarkE2(f *icicle.ExtentionField) bn254.E2 { +func ToGnarkE2(f *g2.G2BaseField) bn254.E2 { + bytes := f.ToBytesLittleEndian() + a0, _ := fp.LittleEndian.Element((*[fp.Bytes]byte)(bytes[:len(bytes)/2])) + a1, _ := fp.LittleEndian.Element((*[fp.Bytes]byte)(bytes[len(bytes)/2:])) return bn254.E2{ - A0: *ToGnarkFp(&f.A0), - A1: *ToGnarkFp(&f.A1), + A0: a0, + A1: a1, } } -func G2PointToGnarkJac(p *icicle.G2Point) *bn254.G2Jac { +func GnarkE2Bits(f *bn254.E2) []uint64 { + a0 := f.A0.Bits() + a1 := f.A1.Bits() + return append(a0[:], a1[:]...) +} + +func FromGnarkE2(f *bn254.E2) g2.G2BaseField { + var field g2.G2BaseField + field.FromLimbs(core.ConvertUint64ArrToUint32Arr(GnarkE2Bits(f))) + return field +} + +func G2PointToGnarkJac(p *g2.G2Projective) *bn254.G2Jac { x := ToGnarkE2(&p.X) y := ToGnarkE2(&p.Y) z := ToGnarkE2(&p.Z) @@ -50,31 +69,29 @@ func G2PointToGnarkJac(p *icicle.G2Point) *bn254.G2Jac { return &after } -func G2AffineFromGnarkAffine(gnark *bn254.G2Affine, g *icicle.G2PointAffine) *icicle.G2PointAffine { - g.X.A0 = gnark.X.A0.Bits() - g.X.A1 = gnark.X.A1.Bits() - g.Y.A0 = gnark.Y.A0.Bits() - g.Y.A1 = gnark.Y.A1.Bits() +func G2PointToGnarkAffine(p *g2.G2Projective) *bn254.G2Affine { + var affine bn254.G2Affine + affine.FromJacobian(G2PointToGnarkJac(p)) + return &affine +} +func G2AffineFromGnarkAffine(gnark *bn254.G2Affine, g *g2.G2Affine) *g2.G2Affine { + g.X = FromGnarkE2(&gnark.X) + g.Y = FromGnarkE2(&gnark.Y) return g } -func G2PointAffineFromGnarkJac(gnark *bn254.G2Jac, g *icicle.G2PointAffine) *icicle.G2PointAffine { +func G2PointAffineFromGnarkJac(gnark *bn254.G2Jac, g *g2.G2Affine) *g2.G2Affine { var pointAffine bn254.G2Affine pointAffine.FromJacobian(gnark) - g.X.A0 = pointAffine.X.A0.Bits() - g.X.A1 = pointAffine.X.A1.Bits() - g.Y.A0 = pointAffine.Y.A0.Bits() - g.Y.A1 = pointAffine.Y.A1.Bits() - - return g + return G2AffineFromGnarkAffine(&pointAffine, g) } -func BatchConvertFromG2Affine(elements []bn254.G2Affine) []icicle.G2PointAffine { - var newElements []icicle.G2PointAffine +func BatchConvertFromG2Affine(elements []bn254.G2Affine) []g2.G2Affine { + var newElements []g2.G2Affine for _, gg2Affine := range elements { - var newElement icicle.G2PointAffine + var newElement g2.G2Affine G2AffineFromGnarkAffine(&gg2Affine, &newElement) newElements = append(newElements, newElement) @@ -82,19 +99,19 @@ func BatchConvertFromG2Affine(elements []bn254.G2Affine) []icicle.G2PointAffine return newElements } -func BatchConvertFromG2AffineThreaded(elements []bn254.G2Affine, routines int) []icicle.G2PointAffine { - var newElements []icicle.G2PointAffine +func BatchConvertFromG2AffineThreaded(elements []bn254.G2Affine, routines int) []g2.G2Affine { + var newElements []g2.G2Affine if routines > 1 && routines <= len(elements) { - channels := make([]chan []icicle.G2PointAffine, routines) + channels := make([]chan []g2.G2Affine, routines) for i := 0; i < routines; i++ { - channels[i] = make(chan []icicle.G2PointAffine, 1) + channels[i] = make(chan []g2.G2Affine, 1) } convert := func(elements []bn254.G2Affine, chanIndex int) { - var convertedElements []icicle.G2PointAffine + var convertedElements []g2.G2Affine for _, e := range elements { - var converted icicle.G2PointAffine + var converted g2.G2Affine G2AffineFromGnarkAffine(&e, &converted) convertedElements = append(convertedElements, converted) } @@ -118,7 +135,7 @@ func BatchConvertFromG2AffineThreaded(elements []bn254.G2Affine, routines int) [ } } else { for _, e := range elements { - var converted icicle.G2PointAffine + var converted g2.G2Affine G2AffineFromGnarkAffine(&e, &converted) newElements = append(newElements, converted) } diff --git a/curves/bn254/g2_icicle.go b/curves/bn254/g2_icicle.go new file mode 100644 index 0000000..0b3a102 --- /dev/null +++ b/curves/bn254/g2_icicle.go @@ -0,0 +1,34 @@ +//go:build g2 + +package bn254 + +import ( + "errors" + + bn254 "github.com/consensys/gnark-crypto/ecc/bn254" + "github.com/consensys/gnark-crypto/ecc/bn254/fr" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + cr "github.com/ingonyama-zk/icicle/v2/wrappers/golang/cuda_runtime" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bn254/g2" +) + +func G2MsmOnDevice(gnarkPoints []bn254.G2Affine, gnarkScalars []fr.Element) (*bn254.G2Affine, error) { + iciclePoints := core.HostSliceFromElements(BatchConvertFromG2Affine(gnarkPoints)) + icicleScalars := core.HostSliceFromElements(BatchConvertFromFrGnark(gnarkScalars)) + + cfg := core.GetDefaultMSMConfig() + var p g2.G2Projective + var out core.DeviceSlice + _, e := out.Malloc(p.Size(), p.Size()) + if e != cr.CudaSuccess { + return nil, errors.New("Cannot allocate") + } + e = g2.G2Msm(icicleScalars, iciclePoints, &cfg, out) + if e != cr.CudaSuccess { + return nil, errors.New("Msm failed") + } + outHost := make(core.HostSlice[g2.G2Projective], 1) + outHost.CopyFromDevice(&out) + out.Free() + return G2PointToGnarkAffine(&outHost[0]), nil +} diff --git a/curves/bn254/g2_test.go b/curves/bn254/g2_test.go index a95c134..434b938 100644 --- a/curves/bn254/g2_test.go +++ b/curves/bn254/g2_test.go @@ -1,4 +1,4 @@ -// Copyright 2023 Ingonyama +// Copyright 2024 Ingonyama // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,27 +14,100 @@ // Code generated by Ingonyama DO NOT EDIT +//go:build g2 + package bn254 import ( "fmt" + "math" + "math/big" "testing" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bn254" + "github.com/consensys/gnark-crypto/ecc" + bn254 "github.com/consensys/gnark-crypto/ecc/bn254" + "github.com/consensys/gnark-crypto/ecc/bn254/fr" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bn254/g2" "github.com/stretchr/testify/assert" ) +func randG2Jac() (bn254.G2Jac, error) { + var point bn254.G2Jac + var scalar fr.Element + + _, err := scalar.SetRandom() + if err != nil { + return point, err + } + + _, genG2Jac, _, _ := bn254.Generators() + + randomBigInt := big.NewInt(1000) + + point.ScalarMultiplication(&genG2Jac, scalar.BigInt(randomBigInt)) + return point, nil +} + +func GenerateG2Points(count int) ([]g2.G2Affine, []bn254.G2Affine) { + // Declare a slice of integers + var points []g2.G2Affine + var pointsAffine []bn254.G2Affine + + // populate the slice + for i := 0; i < 10; i++ { + gnarkP, _ := randG2Jac() + + var p g2.G2Affine + G2PointAffineFromGnarkJac(&gnarkP, &p) + + var gp bn254.G2Affine + gp.FromJacobian(&gnarkP) + pointsAffine = append(pointsAffine, gp) + points = append(points, p) + } + + log2_10 := math.Log2(10) + log2Count := math.Log2(float64(count)) + log2Size := int(math.Ceil(log2Count - log2_10)) + + for i := 0; i < log2Size; i++ { + pointsAffine = append(pointsAffine, pointsAffine...) + points = append(points, points...) + } + + return points[:count], pointsAffine[:count] +} + func TestToGnarkJacG2(t *testing.T) { gnark, _ := randG2Jac() - var pointAffine icicle.G2PointAffine + var pointAffine g2.G2Affine G2PointAffineFromGnarkJac(&gnark, &pointAffine) - var pointProjective icicle.G2Point - pointProjective.FromAffine(&pointAffine) + var pointProjective g2.G2Projective + pointProjective.FromAffine(pointAffine) fmt.Printf("%+v\n", pointProjective) backToGnark := G2PointToGnarkJac(&pointProjective) assert.True(t, gnark.Equal(backToGnark)) } + +func TestMsmG2(t *testing.T) { + for _, v := range []int{24} { + count := 1 << v + _, gnarkPoints := GenerateG2Points(count) + fmt.Print("Finished generating points\n") + _, gnarkScalars := GenerateScalars(count, false) + fmt.Print("Finished generating scalars\n") + + res, e := G2MsmOnDevice(gnarkPoints, gnarkScalars) + assert.Equal(t, e, nil, "error should be nil") + + var AffineLib bn254.G2Affine + + gResult, _ := AffineLib.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{}) + + assert.True(t, gResult.Equal(res)) + } +} diff --git a/curves/bn254/g2_utils.go b/curves/bn254/g2_utils.go new file mode 100644 index 0000000..d645e26 --- /dev/null +++ b/curves/bn254/g2_utils.go @@ -0,0 +1,25 @@ +//go:build g2 + +package bn254 + +import ( + bn254 "github.com/consensys/gnark-crypto/ecc/bn254" + core "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bn254/g2" +) + +func CopyG2PointsToDevice(points []bn254.G2Affine, pointsBytes int, copyDone chan core.DeviceSlice) { + var devicePonts core.DeviceSlice + if pointsBytes == 0 { + copyDone <- devicePonts + } else { + iciclePoints := core.HostSliceFromElements(BatchConvertFromG2Affine(points)) + iciclePoints.CopyToDevice(&devicePonts, true) + + copyDone <- devicePonts + } +} + +func HostSliceFromG2Points(gnarkPoints []bn254.G2Affine) core.HostSlice[g2.G2Affine] { + return core.HostSliceFromElements(BatchConvertFromG2Affine(gnarkPoints)) +} diff --git a/curves/bn254/icicle.go b/curves/bn254/icicle.go index e4e927c..2334b96 100644 --- a/curves/bn254/icicle.go +++ b/curves/bn254/icicle.go @@ -1,105 +1,137 @@ package bn254 import ( - "fmt" - "math" - "unsafe" - - "github.com/consensys/gnark-crypto/ecc/bn254" - "github.com/consensys/gnark-crypto/ecc/bn254/fp" - goicicle "github.com/ingonyama-zk/icicle/goicicle" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bn254" + "errors" + + bn254 "github.com/consensys/gnark-crypto/ecc/bn254" + "github.com/consensys/gnark-crypto/ecc/bn254/fr" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + cr "github.com/ingonyama-zk/icicle/v2/wrappers/golang/cuda_runtime" + icicle_bn254 "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bn254" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bn254/msm" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bn254/ntt" ) -type OnDeviceData struct { - P unsafe.Pointer - Size int +func MsmOnDevice(gnarkPoints []bn254.G1Affine, gnarkScalars []fr.Element) (*bn254.G1Affine, error) { + iciclePoints := HostSliceFromPoints(gnarkPoints) + icicleScalars := HostSliceFromScalars(gnarkScalars) + + cfg := core.GetDefaultMSMConfig() + var p icicle_bn254.Projective + var out core.DeviceSlice + _, e := out.Malloc(p.Size(), p.Size()) + if e != cr.CudaSuccess { + return nil, errors.New("cannot allocate") + } + e = msm.Msm(icicleScalars, iciclePoints, &cfg, out) + if e != cr.CudaSuccess { + return nil, errors.New("msm failed") + } + outHost := make(core.HostSlice[icicle_bn254.Projective], 1) + outHost.CopyFromDevice(&out) + out.Free() + return ProjectiveToGnarkAffine(&outHost[0]), nil } -func INttOnDevice(scalars_d, twiddles_d, cosetPowers_d unsafe.Pointer, size, sizeBytes int, isCoset bool) unsafe.Pointer { - ReverseScalars(scalars_d, size) +func Ntt[T any](gnarkScalars fr.Vector, dir core.NTTDir, cfg *core.NTTConfig[T]) (fr.Vector, error) { + icicleScalars := core.HostSliceFromElements(BatchConvertFromFrGnark(gnarkScalars)) + output := make(core.HostSlice[icicle_bn254.ScalarField], len(gnarkScalars)) + res := ntt.Ntt(icicleScalars, dir, cfg, output) + if res.IcicleErrorCode != core.IcicleErrorCode(0) { + return nil, errors.New("ntt failed") + } + // TODO Reverse order processing + // if cfg.Ordering == core.KNN || cfg.Ordering == core.KRR { - scalarsInterp := icicle.Interpolate(scalars_d, twiddles_d, cosetPowers_d, size, isCoset) + // } + return BatchConvertScalarFieldToFrGnark(output), nil +} - return scalarsInterp +func NttOnDevice(gnarkScalars fr.Vector) (fr.Vector, error) { + cfg := ntt.GetDefaultNttConfig() + return Ntt(gnarkScalars, core.KForward, &cfg) } -func NttOnDevice(scalars_out, scalars_d, twiddles_d, coset_powers_d unsafe.Pointer, size, twid_size, size_bytes int, isCoset bool) { - res := icicle.Evaluate(scalars_out, scalars_d, twiddles_d, coset_powers_d, size, twid_size, isCoset) +func INttOnDevice(gnarkScalars fr.Vector) (fr.Vector, error) { + cfg := ntt.GetDefaultNttConfig() + return Ntt(gnarkScalars, core.KInverse, &cfg) +} - if res != 0 { - fmt.Print("Issue evaluating") - } +// func INttOnDevice(scalars_d, twiddles_d, cosetPowers_d unsafe.Pointer, size, sizeBytes int, isCoset bool) unsafe.Pointer { +// ReverseScalars(scalars_d, size) - ReverseScalars(scalars_out, size) -} +// scalarsInterp := icicle_bn254.Interpolate(scalars_d, twiddles_d, cosetPowers_d, size, isCoset) -func MsmOnDevice(scalars_d, points_d unsafe.Pointer, count int, convert bool) (bn254.G1Jac, unsafe.Pointer, error) { - pointBytes := fp.Bytes * 3 // 3 Elements because of 3 coordinates - out_d, _ := goicicle.CudaMalloc(pointBytes) +// return scalarsInterp +// } - icicle.Commit(out_d, scalars_d, points_d, count, 10) +// func NttOnDevice(scalars_out, scalars_d, twiddles_d, coset_powers_d unsafe.Pointer, size, twid_size, size_bytes int, isCoset bool) { +// res := icicle_bn254.Ntt(scalars_out, scalars_d, twiddles_d, coset_powers_d, size, twid_size, isCoset) - if convert { - outHost := make([]icicle.G1ProjectivePoint, 1) - goicicle.CudaMemCpyDtoH[icicle.G1ProjectivePoint](outHost, out_d, pointBytes) +// if res.IcicleErrorCode != core.IcicleErrorCode(0) { +// fmt.Print("Issue evaluating") +// } - return *G1ProjectivePointToGnarkJac(&outHost[0]), nil, nil - } +// ReverseScalars(scalars_out, size) +// } - return bn254.G1Jac{}, out_d, nil -} +// func MsmOnDevice(scalars_d, points_d unsafe.Pointer, count int, convert bool) (bn254.G1Jac, unsafe.Pointer, error) { +// var p icicle_bn254.Projective +// var out_d core.DeviceSlice +// _, e := out_d.Malloc(p.Size(), p.Size()) +// if e != cr.CudaSuccess { +// return bn254.G1Jac{}, nil, errors.New("Allocation error") +// } -func MsmG2OnDevice(scalars_d, points_d unsafe.Pointer, count int, convert bool) (bn254.G2Jac, unsafe.Pointer, error) { - pointBytes := fp.Bytes * 6 // 6 Elements because of 3 coordinates each with real and imaginary elements - out_d, _ := goicicle.CudaMalloc(pointBytes) +// icicle_bn254.Msm((s)) - icicle.CommitG2(out_d, scalars_d, points_d, count, 10) +// icicle_bn254.Msm(out_d, scalars_d, points_d, count, 10) - if convert { - outHost := make([]icicle.G2Point, 1) - goicicle.CudaMemCpyDtoH[icicle.G2Point](outHost, out_d, pointBytes) - return *G2PointToGnarkJac(&outHost[0]), nil, nil - } +// if convert { +// outHost := make([]icicle_bn254.Projective, 1) +// cr.CopyFromDevice(outHost, out_d, uint(pointBytes)) - return bn254.G2Jac{}, out_d, nil -} +// return *G1ProjectivePointToGnarkJac(&outHost[0]), nil, nil +// } -func GenerateTwiddleFactors(size int, inverse bool) (unsafe.Pointer, error) { - om_selector := int(math.Log(float64(size)) / math.Log(2)) - return icicle.GenerateTwiddles(size, om_selector, inverse) -} +// return bn254.G1Jac{}, out_d, nil +// } -func ReverseScalars(ptr unsafe.Pointer, size int) error { - if success, err := icicle.ReverseScalars(ptr, size); success != 0 { - return err - } - - return nil -} +// func GenerateTwiddleFactors(size int, inverse bool) (unsafe.Pointer, error) { +// om_selector := int(math.Log(float64(size)) / math.Log(2)) +// return icicle_bn254.GenerateTwiddles(size, om_selector, inverse) +// } -func PolyOps(a_d, b_d, c_d, den_d unsafe.Pointer, size int) { - ret := icicle.VecScalarMulMod(a_d, b_d, size) +// func ReverseScalars(ptr unsafe.Pointer, size int) error { +// if success, err := icicle_bn254.ReverseScalars(ptr, size); success != 0 { +// return err +// } - if ret != 0 { - fmt.Print("Vector mult a*b issue") - } - ret = icicle.VecScalarSub(a_d, c_d, size) +// return nil +// } - if ret != 0 { - fmt.Print("Vector sub issue") - } - ret = icicle.VecScalarMulMod(a_d, den_d, size) +// func PolyOps(a_d, b_d, c_d, den_d unsafe.Pointer, size int) { +// ret := icicle_bn254.VecScalarMulMod(a_d, b_d, size) - if ret != 0 { - fmt.Print("Vector mult a*den issue") - } -} +// if ret != 0 { +// fmt.Print("Vector mult a*b issue") +// } +// ret = icicle_bn254.VecScalarSub(a_d, c_d, size) -func MontConvOnDevice(scalars_d unsafe.Pointer, size int, is_into bool) { - if is_into { - icicle.ToMontgomery(scalars_d, size) - } else { - icicle.FromMontgomery(scalars_d, size) - } -} +// if ret != 0 { +// fmt.Print("Vector sub issue") +// } +// ret = icicle_bn254.VecScalarMulMod(a_d, den_d, size) + +// if ret != 0 { +// fmt.Print("Vector mult a*den issue") +// } +// } + +// func MontConvOnDevice(scalars_d unsafe.Pointer, size int, is_into bool) { +// if is_into { +// icicle_bn254.ToMontgomery(scalars_d, size) +// } else { +// icicle_bn254.FromMontgomery(scalars_d, size) +// } +// } diff --git a/curves/bn254/msm_test.go b/curves/bn254/msm_test.go deleted file mode 100644 index 5568392..0000000 --- a/curves/bn254/msm_test.go +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright 2023 Ingonyama -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by Ingonyama DO NOT EDIT - -package bn254 - -import ( - "bufio" - "fmt" - "math" - "math/big" - "os" - "strings" - "testing" - "time" - "unsafe" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bn254" - "github.com/consensys/gnark-crypto/ecc/bn254/fr" - "github.com/ingonyama-zk/icicle/goicicle" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bn254" - "github.com/stretchr/testify/assert" -) - -func randG1Jac() (bn254.G1Jac, error) { - var point bn254.G1Jac - var scalar fr.Element - - _, err := scalar.SetRandom() - if err != nil { - return point, err - } - - genG1Jac, _, _, _ := bn254.Generators() - - //randomBigInt, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 63)) - //randomBigInt, err := rand.Int(rand.Reader, big.NewInt(100)) - randomBigInt := big.NewInt(100) - - point.ScalarMultiplication(&genG1Jac, scalar.BigInt(randomBigInt)) - return point, nil -} - -func GeneratePoints(count int) ([]icicle.G1PointAffine, []bn254.G1Affine) { - // Declare a slice of integers - var points []icicle.G1PointAffine - var pointsAffine []bn254.G1Affine - - // populate the slice - for i := 0; i < 10; i++ { - gnarkP, _ := randG1Jac() - var pointAffine bn254.G1Affine - pointAffine.FromJacobian(&gnarkP) - - var p icicle.G1ProjectivePoint - G1ProjectivePointFromJacGnark(&p, &gnarkP) - - pointsAffine = append(pointsAffine, pointAffine) - points = append(points, *p.StripZ()) - } - - log2_10 := math.Log2(10) - log2Count := math.Log2(float64(count)) - log2Size := int(math.Ceil(log2Count - log2_10)) - - for i := 0; i < log2Size; i++ { - pointsAffine = append(pointsAffine, pointsAffine...) - points = append(points, points...) - } - - return points[:count], pointsAffine[:count] -} - -func ReadGnarkPointsFromFile(filePath string, size int) (points []icicle.G1PointAffine, gnarkPoints []bn254.G1Affine) { - points = make([]icicle.G1PointAffine, size) - gnarkPoints = make([]bn254.G1Affine, size) - file, _ := os.Open(filePath) - scanner := bufio.NewScanner(file) - - for i := 0; scanner.Scan(); i++ { - gnarkPoints[i].X.SetString(scanner.Text()) - scanner.Scan() - gnarkPoints[i].Y.SetString(scanner.Text()) - - var p icicle.G1ProjectivePoint - FromG1AffineGnark(&gnarkPoints[i], &p) - - points[i] = *p.StripZ() - - } - return -} - -func GeneratePointsProj(count int) ([]icicle.G1ProjectivePoint, []bn254.G1Jac) { - // Declare a slice of integers - var points []icicle.G1ProjectivePoint - var pointsAffine []bn254.G1Jac - - // Use a loop to populate the slice - for i := 0; i < count; i++ { - gnarkP, _ := randG1Jac() - - var p icicle.G1ProjectivePoint - G1ProjectivePointFromJacGnark(&p, &gnarkP) - - pointsAffine = append(pointsAffine, gnarkP) - points = append(points, p) - } - - return points, pointsAffine -} - -func GenerateScalars(count int, skewed bool) ([]icicle.G1ScalarField, []fr.Element) { - // Declare a slice of integers - var scalars []icicle.G1ScalarField - var scalars_fr []fr.Element - - var rand fr.Element - var zero fr.Element - zero.SetZero() - var one fr.Element - one.SetOne() - var randLarge fr.Element - randLarge.SetRandom() - - if skewed && count > 1_200_000 { - for i := 0; i < count-1_200_000; i++ { - rand.SetRandom() - s := NewFieldFromFrGnark[icicle.G1ScalarField](rand) - - scalars_fr = append(scalars_fr, rand) - scalars = append(scalars, *s) - } - - for i := 0; i < 600_000; i++ { - s := NewFieldFromFrGnark[icicle.G1ScalarField](randLarge) - - scalars_fr = append(scalars_fr, randLarge) - scalars = append(scalars, *s) - } - for i := 0; i < 400_000; i++ { - s := NewFieldFromFrGnark[icicle.G1ScalarField](zero) - - scalars_fr = append(scalars_fr, zero) - scalars = append(scalars, *s) - } - for i := 0; i < 200_000; i++ { - s := NewFieldFromFrGnark[icicle.G1ScalarField](one) - - scalars_fr = append(scalars_fr, one) - scalars = append(scalars, *s) - } - } else { - for i := 0; i < count; i++ { - rand.SetRandom() - s := NewFieldFromFrGnark[icicle.G1ScalarField](rand) - - scalars_fr = append(scalars_fr, rand) - scalars = append(scalars, *s) - } - } - - return scalars[:count], scalars_fr[:count] -} - -func ReadGnarkScalarsFromFile(filePath string, size int) (scalars []icicle.G1ScalarField, gnarkScalars []fr.Element) { - scalars = make([]icicle.G1ScalarField, size) - gnarkScalars = make([]fr.Element, size) - file, _ := os.Open(filePath) - scanner := bufio.NewScanner(file) - for i := 0; scanner.Scan(); i++ { - gnarkScalars[i].SetString(scanner.Text()) - scalars[i] = *NewFieldFromFrGnark[icicle.G1ScalarField](gnarkScalars[i]) - } - return -} - -func TestMSM(t *testing.T) { - for _, v := range []int{24} { - count := 1 << v - - points, gnarkPoints := GeneratePoints(count) - fmt.Print("Finished generating points\n") - scalars, gnarkScalars := GenerateScalars(count, true) - fmt.Print("Finished generating scalars\n") - - out := new(icicle.G1ProjectivePoint) - startTime := time.Now() - _, e := icicle.Msm(out, points, scalars, 0) // non mont - fmt.Printf("icicle MSM took: %d ms\n", time.Since(startTime).Milliseconds()) - - assert.Equal(t, e, nil, "error should be nil") - fmt.Print("Finished icicle MSM\n") - - var bn254AffineLib bn254.G1Affine - - gResult, _ := bn254AffineLib.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{}) - fmt.Print("Finished Gnark MSM\n") - - assert.True(t, gResult.Equal(ProjectiveToGnarkAffine(out))) - } -} - -func TestCommitMSM(t *testing.T) { - for _, v := range []int{24} { - count := 1< 1 && routines <= len(elements) { - channels := make([]chan []T, routines) + channels := make([]chan []icicle_bn254.ScalarField, routines) for i := 0; i < routines; i++ { - channels[i] = make(chan []T, 1) + channels[i] = make(chan []icicle_bn254.ScalarField, 1) } convert := func(elements []fr.Element, chanIndex int) { - var convertedElements []T + var convertedElements []icicle_bn254.ScalarField for _, e := range elements { - converted := NewFieldFromFrGnark[T](e) + converted := NewFieldFromFrGnark(e) convertedElements = append(convertedElements, *converted) } @@ -120,7 +117,7 @@ func BatchConvertFromFrGnarkThreaded[T icicle.G1BaseField | icicle.G1ScalarField } } else { for _, e := range elements { - converted := NewFieldFromFrGnark[T](e) + converted := NewFieldFromFrGnark(e) newElements = append(newElements, *converted) } } @@ -128,7 +125,7 @@ func BatchConvertFromFrGnarkThreaded[T icicle.G1BaseField | icicle.G1ScalarField return newElements } -func BatchConvertG1BaseFieldToFrGnark(elements []icicle.G1BaseField) []fr.Element { +func BatchConvertBaseFieldToFrGnark(elements []icicle_bn254.BaseField) []fr.Element { var newElements []fr.Element for _, e := range elements { converted := BaseFieldToGnarkFr(&e) @@ -138,7 +135,7 @@ func BatchConvertG1BaseFieldToFrGnark(elements []icicle.G1BaseField) []fr.Elemen return newElements } -func BatchConvertG1ScalarFieldToFrGnark(elements []icicle.G1ScalarField) []fr.Element { +func BatchConvertScalarFieldToFrGnark(elements []icicle_bn254.ScalarField) []fr.Element { var newElements []fr.Element for _, e := range elements { converted := ScalarToGnarkFr(&e) @@ -148,7 +145,7 @@ func BatchConvertG1ScalarFieldToFrGnark(elements []icicle.G1ScalarField) []fr.El return newElements } -func BatchConvertG1BaseFieldToFrGnarkThreaded(elements []icicle.G1BaseField, routines int) []fr.Element { +func BatchConvertBaseFieldToFrGnarkThreaded(elements []icicle_bn254.BaseField, routines int) []fr.Element { var newElements []fr.Element if routines > 1 { @@ -157,7 +154,7 @@ func BatchConvertG1BaseFieldToFrGnarkThreaded(elements []icicle.G1BaseField, rou channels[i] = make(chan []fr.Element, 1) } - convert := func(elements []icicle.G1BaseField, chanIndex int) { + convert := func(elements []icicle_bn254.BaseField, chanIndex int) { var convertedElements []fr.Element for _, e := range elements { converted := BaseFieldToGnarkFr(&e) @@ -186,7 +183,7 @@ func BatchConvertG1BaseFieldToFrGnarkThreaded(elements []icicle.G1BaseField, rou return newElements } -func BatchConvertG1ScalarFieldToFrGnarkThreaded(elements []icicle.G1ScalarField, routines int) []fr.Element { +func BatchConvertScalarFieldToFrGnarkThreaded(elements []icicle_bn254.ScalarField, routines int) []fr.Element { var newElements []fr.Element if routines > 1 { @@ -195,7 +192,7 @@ func BatchConvertG1ScalarFieldToFrGnarkThreaded(elements []icicle.G1ScalarField, channels[i] = make(chan []fr.Element, 1) } - convert := func(elements []icicle.G1ScalarField, chanIndex int) { + convert := func(elements []icicle_bn254.ScalarField, chanIndex int) { var convertedElements []fr.Element for _, e := range elements { converted := ScalarToGnarkFr(&e) @@ -224,42 +221,30 @@ func BatchConvertG1ScalarFieldToFrGnarkThreaded(elements []icicle.G1ScalarField, return newElements } -func NewFieldFromFrGnark[T icicle.G1BaseField | icicle.G1ScalarField](element fr.Element) *T { - s := icicle.ConvertUint64ArrToUint32Arr(element.Bits()) // get non-montgomry +func NewFieldFromFrGnark(element fr.Element) *icicle_bn254.ScalarField { + element_bits := element.Bits() + s := core.ConvertUint64ArrToUint32Arr(element_bits[:]) // get non-montgomry - return &T{S: s} + var field icicle_bn254.ScalarField + field.FromLimbs(s) + return &field } -func NewFieldFromFpGnark[T icicle.G1BaseField | icicle.G1ScalarField](element fp.Element) *T { - s := icicle.ConvertUint64ArrToUint32Arr(element.Bits()) // get non-montgomry +func NewFieldFromFpGnark(element fp.Element) *icicle_bn254.BaseField { + element_bits := element.Bits() + s := core.ConvertUint64ArrToUint32Arr(element_bits[:]) // get non-montgomry - return &T{S: s} + var field icicle_bn254.BaseField + field.FromLimbs(s) + return &field } -func BaseFieldToGnarkFr(f *icicle.G1BaseField) *fr.Element { - fb := f.ToBytesLe() - var b32 [32]byte - copy(b32[:], fb[:32]) - - v, e := fr.LittleEndian.Element(&b32) - - if e != nil { - panic(fmt.Sprintf("unable to convert point %v got error %v", f, e)) - } - +func BaseFieldToGnarkFr(f *icicle_bn254.BaseField) *fr.Element { + v, _ := fr.LittleEndian.Element((*[fr.Bytes]byte)(f.ToBytesLittleEndian())) return &v } -func BaseFieldToGnarkFp(f *icicle.G1BaseField) *fp.Element { - fb := f.ToBytesLe() - var b32 [32]byte - copy(b32[:], fb[:32]) - - v, e := fp.LittleEndian.Element(&b32) - - if e != nil { - panic(fmt.Sprintf("unable to convert point %v got error %v", f, e)) - } - +func BaseFieldToGnarkFp(f *icicle_bn254.BaseField) *fp.Element { + v, _ := fp.LittleEndian.Element((*[fp.Bytes]byte)(f.ToBytesLittleEndian())) return &v } diff --git a/curves/bw6761/conversions.go b/curves/bw6761/conversions.go new file mode 100644 index 0000000..1cfeb61 --- /dev/null +++ b/curves/bw6761/conversions.go @@ -0,0 +1,79 @@ +package bw6761 + +import ( + bw6_761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark-crypto/ecc/bw6-761/fp" + icicle_bw6_761 "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bw6761" +) + +func StripZ(p *icicle_bw6_761.Projective) *icicle_bw6_761.Affine { + return &icicle_bw6_761.Affine{ + X: p.X, + Y: p.Y, + } +} + +func BatchConvertFromG1Affine(elements []bw6_761.G1Affine) []icicle_bw6_761.Affine { + var newElements []icicle_bw6_761.Affine + for _, e := range elements { + var newElement icicle_bw6_761.Projective + FromG1AffineGnark(&e, &newElement) + + newElements = append(newElements, *StripZ(&newElement)) + } + return newElements +} + +func ProjectiveToGnarkAffine(p *icicle_bw6_761.Projective) *bw6_761.G1Affine { + px := BaseFieldToGnarkFp(&p.X) + py := BaseFieldToGnarkFp(&p.Y) + pz := BaseFieldToGnarkFp(&p.Z) + + zInv := new(fp.Element) + x := new(fp.Element) + y := new(fp.Element) + + zInv.Inverse(pz) + + x.Mul(px, zInv) + y.Mul(py, zInv) + + return &bw6_761.G1Affine{X: *x, Y: *y} +} + +func G1ProjectivePointToGnarkJac(p *icicle_bw6_761.Projective) *bw6_761.G1Jac { + var p1 bw6_761.G1Jac + p1.FromAffine(ProjectiveToGnarkAffine(p)) + + return &p1 +} + +func FromG1AffineGnark(gnark *bw6_761.G1Affine, p *icicle_bw6_761.Projective) *icicle_bw6_761.Projective { + var z icicle_bw6_761.BaseField + z.One() + + p.X = *NewFieldFromFpGnark(gnark.X) + p.Y = *NewFieldFromFpGnark(gnark.Y) + p.Z = z + + return p +} + +func G1ProjectivePointFromJacGnark(p *icicle_bw6_761.Projective, gnark *bw6_761.G1Jac) *icicle_bw6_761.Projective { + var pointAffine bw6_761.G1Affine + pointAffine.FromJacobian(gnark) + + var z icicle_bw6_761.BaseField + z.One() + + p.X = *NewFieldFromFpGnark(pointAffine.X) + p.Y = *NewFieldFromFpGnark(pointAffine.Y) + p.Z = z + + return p +} + +func AffineToGnarkAffine(p *icicle_bw6_761.Affine) *bw6_761.G1Affine { + pointProjective := p.ToProjective() + return ProjectiveToGnarkAffine(&pointProjective) +} diff --git a/curves/bw6761/g1_conversions.go b/curves/bw6761/g1_conversions.go deleted file mode 100644 index bb6fcbb..0000000 --- a/curves/bw6761/g1_conversions.go +++ /dev/null @@ -1,98 +0,0 @@ -package bw6761 - -import ( - "encoding/binary" - "github.com/consensys/gnark-crypto/ecc/bw6-761" - "github.com/consensys/gnark-crypto/ecc/bw6-761/fp" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bw6761" -) - -func BatchConvertFromG1Affine(elements []bw6761.G1Affine) []icicle.G1PointAffine { - var newElements []icicle.G1PointAffine - for _, e := range elements { - var newElement icicle.G1ProjectivePoint - FromG1AffineGnark(&e, &newElement) - - newElements = append(newElements, *newElement.StripZ()) - } - return newElements -} - -func ProjectiveToGnarkAffine(p *icicle.G1ProjectivePoint) *bw6761.G1Affine { - px := BaseFieldToGnarkFp(&p.X) - py := BaseFieldToGnarkFp(&p.Y) - pz := BaseFieldToGnarkFp(&p.Z) - - zInv := new(fp.Element) - x := new(fp.Element) - y := new(fp.Element) - - zInv.Inverse(pz) - - x.Mul(px, zInv) - y.Mul(py, zInv) - - return &bw6761.G1Affine{X: *x, Y: *y} -} - -func G1ProjectivePointToGnarkJac(p *icicle.G1ProjectivePoint) *bw6761.G1Jac { - var p1 bw6761.G1Jac - p1.FromAffine(ProjectiveToGnarkAffine(p)) - - return &p1 -} - -func FromG1AffineGnark(gnark *bw6761.G1Affine, p *icicle.G1ProjectivePoint) *icicle.G1ProjectivePoint { - var z icicle.G1BaseField - z.SetOne() - - p.X = *NewFieldFromFpGnark(gnark.X) - p.Y = *NewFieldFromFpGnark(gnark.Y) - p.Z = z - - return p -} - -func G1ProjectivePointFromJacGnark(p *icicle.G1ProjectivePoint, gnark *bw6761.G1Jac) *icicle.G1ProjectivePoint { - var pointAffine bw6761.G1Affine - pointAffine.FromJacobian(gnark) - - var z icicle.G1BaseField - z.SetOne() - - p.X = *NewFieldFromFpGnark(pointAffine.X) - p.Y = *NewFieldFromFpGnark(pointAffine.Y) - p.Z = z - - return p -} - -func AffineToGnarkAffine(p *icicle.G1PointAffine) *bw6761.G1Affine { - return ProjectiveToGnarkAffine(p.ToProjective()) -} - -func ConvertUint64ArrToUint32Arr6(arr64 [6]uint64) [12]uint32 { - var arr32 [12]uint32 - for i, v := range arr64 { - b := make([]byte, 8) - binary.LittleEndian.PutUint64(b, v) - - arr32[i*2] = binary.LittleEndian.Uint32(b[0:4]) - arr32[i*2+1] = binary.LittleEndian.Uint32(b[4:8]) - } - - return arr32 -} - -func ConvertUint64ArrToUint32Arr12(arr64 [12]uint64) [24]uint32 { - var arr32 [24]uint32 - for i, v := range arr64 { - b := make([]byte, 8) - binary.LittleEndian.PutUint64(b, v) - - arr32[i*2] = binary.LittleEndian.Uint32(b[0:4]) - arr32[i*2+1] = binary.LittleEndian.Uint32(b[4:8]) - } - - return arr32 -} diff --git a/curves/bw6761/g1_test.go b/curves/bw6761/g1_test.go new file mode 100644 index 0000000..9d79a95 --- /dev/null +++ b/curves/bw6761/g1_test.go @@ -0,0 +1,297 @@ +// Copyright 2023 Ingonyama +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by Ingonyama DO NOT EDIT + +package bw6761 + +import ( + "bufio" + "fmt" + "math" + "math/big" + "os" + "testing" + "time" + + "github.com/consensys/gnark-crypto/ecc" + bw6_761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark-crypto/ecc/bw6-761/fp" + "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" + core "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + icicle_bw6_761 "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bw6761" + "github.com/stretchr/testify/assert" +) + +func randG1Jac() (bw6_761.G1Jac, error) { + var point bw6_761.G1Jac + var scalar fr.Element + + _, err := scalar.SetRandom() + if err != nil { + return point, err + } + + genG1Jac, _, _, _ := bw6_761.Generators() + + //randomBigInt, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 63)) + //randomBigInt, err := rand.Int(rand.Reader, big.NewInt(100)) + randomBigInt := big.NewInt(100) + + point.ScalarMultiplication(&genG1Jac, scalar.BigInt(randomBigInt)) + return point, nil +} + +func GeneratePoints(count int) ([]icicle_bw6_761.Affine, []bw6_761.G1Affine) { + // Declare a slice of integers + var points []icicle_bw6_761.Affine + var pointsAffine []bw6_761.G1Affine + + // populate the slice + for i := 0; i < 10; i++ { + gnarkP, _ := randG1Jac() + var pointAffine bw6_761.G1Affine + pointAffine.FromJacobian(&gnarkP) + + var p icicle_bw6_761.Projective + G1ProjectivePointFromJacGnark(&p, &gnarkP) + + pointsAffine = append(pointsAffine, pointAffine) + points = append(points, *StripZ(&p)) + } + + log2_10 := math.Log2(10) + log2Count := math.Log2(float64(count)) + log2Size := int(math.Ceil(log2Count - log2_10)) + + for i := 0; i < log2Size; i++ { + pointsAffine = append(pointsAffine, pointsAffine...) + points = append(points, points...) + } + + return points[:count], pointsAffine[:count] +} + +func ReadGnarkPointsFromFile(filePath string, size int) (points []icicle_bw6_761.Affine, gnarkPoints []bw6_761.G1Affine) { + points = make([]icicle_bw6_761.Affine, size) + gnarkPoints = make([]bw6_761.G1Affine, size) + file, _ := os.Open(filePath) + scanner := bufio.NewScanner(file) + + for i := 0; scanner.Scan(); i++ { + gnarkPoints[i].X.SetString(scanner.Text()) + scanner.Scan() + gnarkPoints[i].Y.SetString(scanner.Text()) + + var p icicle_bw6_761.Projective + FromG1AffineGnark(&gnarkPoints[i], &p) + + points[i] = *StripZ(&p) + + } + return +} + +func GeneratePointsProj(count int) ([]icicle_bw6_761.Projective, []bw6_761.G1Jac) { + // Declare a slice of integers + var points []icicle_bw6_761.Projective + var pointsAffine []bw6_761.G1Jac + + // Use a loop to populate the slice + for i := 0; i < count; i++ { + gnarkP, _ := randG1Jac() + + var p icicle_bw6_761.Projective + G1ProjectivePointFromJacGnark(&p, &gnarkP) + + pointsAffine = append(pointsAffine, gnarkP) + points = append(points, p) + } + + return points, pointsAffine +} + +func GenerateScalars(count int, skewed bool) ([]icicle_bw6_761.ScalarField, []fr.Element) { + // Declare a slice of integers + var scalars []icicle_bw6_761.ScalarField + var scalars_fr []fr.Element + + var rand fr.Element + var zero fr.Element + zero.SetZero() + var one fr.Element + one.SetOne() + var randLarge fr.Element + randLarge.SetRandom() + + if skewed && count > 1_200_000 { + for i := 0; i < count-1_200_000; i++ { + rand.SetRandom() + s := NewFieldFromFrGnark(rand) + + scalars_fr = append(scalars_fr, rand) + scalars = append(scalars, *s) + } + + for i := 0; i < 600_000; i++ { + s := NewFieldFromFrGnark(randLarge) + + scalars_fr = append(scalars_fr, randLarge) + scalars = append(scalars, *s) + } + for i := 0; i < 400_000; i++ { + s := NewFieldFromFrGnark(zero) + + scalars_fr = append(scalars_fr, zero) + scalars = append(scalars, *s) + } + for i := 0; i < 200_000; i++ { + s := NewFieldFromFrGnark(one) + + scalars_fr = append(scalars_fr, one) + scalars = append(scalars, *s) + } + } else { + for i := 0; i < count; i++ { + rand.SetRandom() + s := NewFieldFromFrGnark(rand) + + scalars_fr = append(scalars_fr, rand) + scalars = append(scalars, *s) + } + } + + return scalars[:count], scalars_fr[:count] +} + +func ReadGnarkScalarsFromFile(filePath string, size int) (scalars []icicle_bw6_761.ScalarField, gnarkScalars []fr.Element) { + scalars = make([]icicle_bw6_761.ScalarField, size) + gnarkScalars = make([]fr.Element, size) + file, _ := os.Open(filePath) + scanner := bufio.NewScanner(file) + for i := 0; scanner.Scan(); i++ { + gnarkScalars[i].SetString(scanner.Text()) + scalars[i] = *NewFieldFromFrGnark(gnarkScalars[i]) + } + return +} + +func TestFieldFromGnark(t *testing.T) { + var rand fr.Element + rand.SetRandom() + + f := NewFieldFromFrGnark(rand) + element_bits := rand.Bits() + assert.Equal(t, f.GetLimbs(), core.ConvertUint64ArrToUint32Arr(element_bits[:])) +} + +func BenchmarkBatchConvertFromFrGnarkThreaded(b *testing.B) { + // ROUTINES := []int{4,5,6,7,8} + + // for _, routineAmount := range ROUTINES { + routineAmount := 7 + _, scalars_fr := GenerateScalars(1<<24, false) + b.Run(fmt.Sprintf("Convert %d", routineAmount), func(b *testing.B) { + for n := 0; n < b.N; n++ { + _ = BatchConvertFromFrGnarkThreaded(scalars_fr, routineAmount) + } + }) + // } +} + +func BenchmarkBatchConvertFromFrGnark(b *testing.B) { + _, scalars_fr := GenerateScalars(1<<24, false) + b.Run("BatchConvert 2^24", func(b *testing.B) { + for n := 0; n < b.N; n++ { + _ = BatchConvertFromFrGnark(scalars_fr) + } + }) +} + +func TestPointFromGnark(t *testing.T) { + gnarkP, _ := randG1Jac() + + var f icicle_bw6_761.BaseField + f.One() + var p icicle_bw6_761.Projective + G1ProjectivePointFromJacGnark(&p, &gnarkP) + + z_inv := new(fp.Element) + z_invsq := new(fp.Element) + z_invq3 := new(fp.Element) + x := new(fp.Element) + y := new(fp.Element) + + z_inv.Inverse(&gnarkP.Z) + z_invsq.Mul(z_inv, z_inv) + z_invq3.Mul(z_invsq, z_inv) + + x.Mul(&gnarkP.X, z_invsq) + y.Mul(&gnarkP.Y, z_invq3) + + assert.Equal(t, p.X, *NewFieldFromFpGnark(*x)) + assert.Equal(t, p.Y, *NewFieldFromFpGnark(*y)) + assert.Equal(t, p.Z, f) +} + +func TestPointAffineNoInfinityToProjective(t *testing.T) { + gnarkP, _ := randG1Jac() + var f icicle_bw6_761.BaseField + var p icicle_bw6_761.Projective + + f.One() + affine := StripZ(G1ProjectivePointFromJacGnark(&p, &gnarkP)) + proj := affine.ToProjective() + + assert.Equal(t, proj.X, affine.X) + assert.Equal(t, proj.X, affine.X) + assert.Equal(t, proj.Z, f) +} + +func TestToGnarkAffine(t *testing.T) { + gJac, _ := randG1Jac() + var proj icicle_bw6_761.Projective + G1ProjectivePointFromJacGnark(&proj, &gJac) + + var gAffine bw6_761.G1Affine + gAffine.FromJacobian(&gJac) + + affine := ProjectiveToGnarkAffine(&proj) + assert.Equal(t, *affine, gAffine) +} + +func TestMSM(t *testing.T) { + for _, v := range []int{24} { + count := 1 << v + + _, gnarkPoints := GeneratePoints(count) + fmt.Print("Finished generating points\n") + _, gnarkScalars := GenerateScalars(count, true) + fmt.Print("Finished generating scalars\n") + + startTime := time.Now() + res, e := MsmOnDevice(gnarkPoints, gnarkScalars) // non mont + fmt.Printf("icicle MSM took: %d ms\n", time.Since(startTime).Milliseconds()) + + assert.Equal(t, e, nil, "error should be nil") + fmt.Print("Finished icicle MSM\n") + + var AffineLib bw6_761.G1Affine + + gResult, _ := AffineLib.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{}) + fmt.Print("Finished Gnark MSM\n") + + assert.True(t, gResult.Equal(res)) + } +} diff --git a/curves/bw6761/g2_conversions.go b/curves/bw6761/g2_conversions.go index f8a9c23..a70c860 100644 --- a/curves/bw6761/g2_conversions.go +++ b/curves/bw6761/g2_conversions.go @@ -1,27 +1,31 @@ +//go:build g2 + package bw6761 import ( "fmt" - "github.com/consensys/gnark-crypto/ecc/bw6-761" + + bw6_761 "github.com/consensys/gnark-crypto/ecc/bw6-761" "github.com/consensys/gnark-crypto/ecc/bw6-761/fp" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bw6761" + core "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bw6761/g2" ) -func ToGnarkFp(f *icicle.G2Element) *fp.Element { - fb := f.ToBytesLe() +func ToGnarkFp(f *g2.G2BaseField) *fp.Element { + fb := f.ToBytesLittleEndian() var b96 [96]byte copy(b96[:], fb[:96]) v, e := fp.LittleEndian.Element(&b96) if e != nil { - panic(fmt.Sprintf("unable to convert point %v got error %v", f, e)) + panic(fmt.Sprintf("unable to convert point %v; got error %v", f, e)) } return &v } -func G2PointToGnarkJac(p *icicle.G2Point) *bw6761.G2Jac { +func G2PointToGnarkJac(p *g2.G2Projective) *bw6_761.G2Jac { x := ToGnarkFp(&p.X) y := ToGnarkFp(&p.Y) z := ToGnarkFp(&p.Z) @@ -34,7 +38,7 @@ func G2PointToGnarkJac(p *icicle.G2Point) *bw6761.G2Jac { var Y fp.Element Y.Mul(y, &zSquared) - after := bw6761.G2Jac{ + after := bw6_761.G2Jac{ X: X, Y: Y, Z: *z, @@ -43,31 +47,79 @@ func G2PointToGnarkJac(p *icicle.G2Point) *bw6761.G2Jac { return &after } -func G2AffineFromGnarkAffine(gnark *bw6761.G2Affine, g *icicle.G2PointAffine) *icicle.G2PointAffine { - g.X = gnark.X.Bits() - g.Y = gnark.Y.Bits() +func G2PointToGnarkAffine(p *g2.G2Projective) *bw6_761.G2Affine { + var affine bw6_761.G2Affine + affine.FromJacobian(G2PointToGnarkJac(p)) + return &affine +} + +func G2AffineFromGnarkAffine(gnark *bw6_761.G2Affine, g *g2.G2Affine) *g2.G2Affine { + xBits := gnark.X.Bits() + yBits := gnark.Y.Bits() + g.X.FromLimbs(core.ConvertUint64ArrToUint32Arr(xBits[:])) + g.Y.FromLimbs(core.ConvertUint64ArrToUint32Arr(yBits[:])) return g } -func G2PointAffineFromGnarkJac(gnark *bw6761.G2Jac, g *icicle.G2PointAffine) *icicle.G2PointAffine { - var pointAffine bw6761.G2Affine +func G2PointAffineFromGnarkJac(gnark *bw6_761.G2Jac, g *g2.G2Affine) *g2.G2Affine { + var pointAffine bw6_761.G2Affine pointAffine.FromJacobian(gnark) - g.X = pointAffine.X.Bits() - g.X = pointAffine.X.Bits() - g.Y = pointAffine.Y.Bits() - g.Y = pointAffine.Y.Bits() - - return g + return G2AffineFromGnarkAffine(&pointAffine, g) } -func BatchConvertFromG2Affine(elements []bw6761.G2Affine) []icicle.G2PointAffine { - var newElements []icicle.G2PointAffine +func BatchConvertFromG2Affine(elements []bw6_761.G2Affine) []g2.G2Affine { + var newElements []g2.G2Affine for _, gg2Affine := range elements { - var newElement icicle.G2PointAffine + var newElement g2.G2Affine G2AffineFromGnarkAffine(&gg2Affine, &newElement) newElements = append(newElements, newElement) } return newElements } + +func BatchConvertFromG2AffineThreaded(elements []bw6_761.G2Affine, routines int) []g2.G2Affine { + var newElements []g2.G2Affine + + if routines > 1 && routines <= len(elements) { + channels := make([]chan []g2.G2Affine, routines) + for i := 0; i < routines; i++ { + channels[i] = make(chan []g2.G2Affine, 1) + } + + convert := func(elements []bw6_761.G2Affine, chanIndex int) { + var convertedElements []g2.G2Affine + for _, e := range elements { + var converted g2.G2Affine + G2AffineFromGnarkAffine(&e, &converted) + convertedElements = append(convertedElements, converted) + } + + channels[chanIndex] <- convertedElements + } + + batchLen := len(elements) / routines + for i := 0; i < routines; i++ { + start := batchLen * i + end := batchLen * (i + 1) + elemsToConv := elements[start:end] + if i == routines-1 { + elemsToConv = elements[start:] + } + go convert(elemsToConv, i) + } + + for i := 0; i < routines; i++ { + newElements = append(newElements, <-channels[i]...) + } + } else { + for _, e := range elements { + var converted g2.G2Affine + G2AffineFromGnarkAffine(&e, &converted) + newElements = append(newElements, converted) + } + } + + return newElements +} diff --git a/curves/bw6761/g2_icicle.go b/curves/bw6761/g2_icicle.go new file mode 100644 index 0000000..0bbfa54 --- /dev/null +++ b/curves/bw6761/g2_icicle.go @@ -0,0 +1,34 @@ +//go:build g2 + +package bw6761 + +import ( + "errors" + + bw6_761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + cr "github.com/ingonyama-zk/icicle/v2/wrappers/golang/cuda_runtime" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bw6761/g2" +) + +func G2MsmOnDevice(gnarkPoints []bw6_761.G2Affine, gnarkScalars []fr.Element) (*bw6_761.G2Affine, error) { + iciclePoints := core.HostSliceFromElements(BatchConvertFromG2Affine(gnarkPoints)) + icicleScalars := core.HostSliceFromElements(BatchConvertFromFrGnark(gnarkScalars)) + + cfg := core.GetDefaultMSMConfig() + var p g2.G2Projective + var out core.DeviceSlice + _, e := out.Malloc(p.Size(), p.Size()) + if e != cr.CudaSuccess { + return nil, errors.New("Cannot allocate") + } + e = g2.G2Msm(icicleScalars, iciclePoints, &cfg, out) + if e != cr.CudaSuccess { + return nil, errors.New("Msm failed") + } + outHost := make(core.HostSlice[g2.G2Projective], 1) + outHost.CopyFromDevice(&out) + out.Free() + return G2PointToGnarkAffine(&outHost[0]), nil +} diff --git a/curves/bw6761/g2_test.go b/curves/bw6761/g2_test.go new file mode 100644 index 0000000..fe2dd89 --- /dev/null +++ b/curves/bw6761/g2_test.go @@ -0,0 +1,113 @@ +// Copyright 2024 Ingonyama +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by Ingonyama DO NOT EDIT + +//go:build g2 + +package bw6761 + +import ( + "fmt" + "math" + "math/big" + "testing" + + "github.com/consensys/gnark-crypto/ecc" + bw6_761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bw6761/g2" + "github.com/stretchr/testify/assert" +) + +func randG2Jac() (bw6_761.G2Jac, error) { + var point bw6_761.G2Jac + var scalar fr.Element + + _, err := scalar.SetRandom() + if err != nil { + return point, err + } + + _, genG2Jac, _, _ := bw6_761.Generators() + + randomBigInt := big.NewInt(1000) + + point.ScalarMultiplication(&genG2Jac, scalar.BigInt(randomBigInt)) + return point, nil +} + +func GenerateG2Points(count int) ([]g2.G2Affine, []bw6_761.G2Affine) { + // Declare a slice of integers + var points []g2.G2Affine + var pointsAffine []bw6_761.G2Affine + + // populate the slice + for i := 0; i < 10; i++ { + gnarkP, _ := randG2Jac() + + var p g2.G2Affine + G2PointAffineFromGnarkJac(&gnarkP, &p) + + var gp bw6_761.G2Affine + gp.FromJacobian(&gnarkP) + pointsAffine = append(pointsAffine, gp) + points = append(points, p) + } + + log2_10 := math.Log2(10) + log2Count := math.Log2(float64(count)) + log2Size := int(math.Ceil(log2Count - log2_10)) + + for i := 0; i < log2Size; i++ { + pointsAffine = append(pointsAffine, pointsAffine...) + points = append(points, points...) + } + + return points[:count], pointsAffine[:count] +} + +func TestToGnarkJacG2(t *testing.T) { + gnark, _ := randG2Jac() + + var pointAffine g2.G2Affine + G2PointAffineFromGnarkJac(&gnark, &pointAffine) + + var pointProjective g2.G2Projective + pointProjective.FromAffine(pointAffine) + + fmt.Printf("%+v\n", pointProjective) + backToGnark := G2PointToGnarkJac(&pointProjective) + + assert.True(t, gnark.Equal(backToGnark)) +} + +func TestMsmG2(t *testing.T) { + for _, v := range []int{24} { + count := 1 << v + _, gnarkPoints := GenerateG2Points(count) + fmt.Print("Finished generating points\n") + _, gnarkScalars := GenerateScalars(count, false) + fmt.Print("Finished generating scalars\n") + + res, e := G2MsmOnDevice(gnarkPoints, gnarkScalars) + assert.Equal(t, e, nil, "error should be nil") + + var AffineLib bw6_761.G2Affine + + gResult, _ := AffineLib.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{}) + + assert.True(t, gResult.Equal(res)) + } +} diff --git a/curves/bw6761/g2_utils.go b/curves/bw6761/g2_utils.go new file mode 100644 index 0000000..00faeef --- /dev/null +++ b/curves/bw6761/g2_utils.go @@ -0,0 +1,25 @@ +//go:build g2 + +package bw6761 + +import ( + bw6_761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + core "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bw6761/g2" +) + +func CopyG2PointsToDevice(points []bw6_761.G2Affine, pointsBytes int, copyDone chan core.DeviceSlice) { + var devicePonts core.DeviceSlice + if pointsBytes == 0 { + copyDone <- devicePonts + } else { + iciclePoints := core.HostSliceFromElements(BatchConvertFromG2Affine(points)) + iciclePoints.CopyToDevice(&devicePonts, true) + + copyDone <- devicePonts + } +} + +func HostSliceFromG2Points(gnarkPoints []bw6_761.G2Affine) core.HostSlice[g2.G2Affine] { + return core.HostSliceFromElements(BatchConvertFromG2Affine(gnarkPoints)) +} diff --git a/curves/bw6761/icicle.go b/curves/bw6761/icicle.go index c2e256b..3e6d747 100644 --- a/curves/bw6761/icicle.go +++ b/curves/bw6761/icicle.go @@ -1,105 +1,137 @@ package bw6761 import ( - "fmt" - "math" - "unsafe" - - "github.com/consensys/gnark-crypto/ecc/bw6-761" - "github.com/consensys/gnark-crypto/ecc/bw6-761/fp" - "github.com/ingonyama-zk/icicle/goicicle" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bw6761" + "errors" + + bw6_761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + cr "github.com/ingonyama-zk/icicle/v2/wrappers/golang/cuda_runtime" + icicle_bw6_761 "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bw6761" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bw6761/msm" + "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bw6761/ntt" ) -type OnDeviceData struct { - P unsafe.Pointer - Size int +func MsmOnDevice(gnarkPoints []bw6_761.G1Affine, gnarkScalars []fr.Element) (*bw6_761.G1Affine, error) { + iciclePoints := HostSliceFromPoints(gnarkPoints) + icicleScalars := HostSliceFromScalars(gnarkScalars) + + cfg := core.GetDefaultMSMConfig() + var p icicle_bw6_761.Projective + var out core.DeviceSlice + _, e := out.Malloc(p.Size(), p.Size()) + if e != cr.CudaSuccess { + return nil, errors.New("cannot allocate") + } + e = msm.Msm(icicleScalars, iciclePoints, &cfg, out) + if e != cr.CudaSuccess { + return nil, errors.New("msm failed") + } + outHost := make(core.HostSlice[icicle_bw6_761.Projective], 1) + outHost.CopyFromDevice(&out) + out.Free() + return ProjectiveToGnarkAffine(&outHost[0]), nil } -func INttOnDevice(scalars_d, twiddles_d, cosetPowers_d unsafe.Pointer, size, sizeBytes int, isCoset bool) unsafe.Pointer { - ReverseScalars(scalars_d, size) +func Ntt[T any](gnarkScalars fr.Vector, dir core.NTTDir, cfg *core.NTTConfig[T]) (fr.Vector, error) { + icicleScalars := core.HostSliceFromElements(BatchConvertFromFrGnark(gnarkScalars)) + output := make(core.HostSlice[icicle_bw6_761.ScalarField], len(gnarkScalars)) + res := ntt.Ntt(icicleScalars, dir, cfg, output) + if res.IcicleErrorCode != core.IcicleErrorCode(0) { + return nil, errors.New("ntt failed") + } + // TODO Reverse order processing + // if cfg.Ordering == core.KNN || cfg.Ordering == core.KRR { - scalarsInterp := icicle.Interpolate(scalars_d, twiddles_d, cosetPowers_d, size, isCoset) + // } + return BatchConvertScalarFieldToFrGnark(output), nil +} - return scalarsInterp +func NttOnDevice(gnarkScalars fr.Vector) (fr.Vector, error) { + cfg := ntt.GetDefaultNttConfig() + return Ntt(gnarkScalars, core.KForward, &cfg) } -func NttOnDevice(scalars_out, scalars_d, twiddles_d, coset_powers_d unsafe.Pointer, size, twid_size, size_bytes int, isCoset bool) { - res := icicle.Evaluate(scalars_out, scalars_d, twiddles_d, coset_powers_d, size, twid_size, isCoset) +func INttOnDevice(gnarkScalars fr.Vector) (fr.Vector, error) { + cfg := ntt.GetDefaultNttConfig() + return Ntt(gnarkScalars, core.KInverse, &cfg) +} - if res != 0 { - fmt.Print("Issue evaluating") - } +// func INttOnDevice(scalars_d, twiddles_d, cosetPowers_d unsafe.Pointer, size, sizeBytes int, isCoset bool) unsafe.Pointer { +// ReverseScalars(scalars_d, size) - ReverseScalars(scalars_out, size) -} +// scalarsInterp := icicle_bw6_761.Interpolate(scalars_d, twiddles_d, cosetPowers_d, size, isCoset) -func MsmOnDevice(scalars_d, points_d unsafe.Pointer, count int, convert bool) (bw6761.G1Jac, unsafe.Pointer, error) { - pointBytes := fp.Bytes * 3 // 3 Elements because of 3 coordinates - out_d, _ := goicicle.CudaMalloc(pointBytes) +// return scalarsInterp +// } - icicle.Commit(out_d, scalars_d, points_d, count, 10) +// func NttOnDevice(scalars_out, scalars_d, twiddles_d, coset_powers_d unsafe.Pointer, size, twid_size, size_bytes int, isCoset bool) { +// res := icicle_bw6_761.Ntt(scalars_out, scalars_d, twiddles_d, coset_powers_d, size, twid_size, isCoset) - if convert { - outHost := make([]icicle.G1ProjectivePoint, 1) - goicicle.CudaMemCpyDtoH[icicle.G1ProjectivePoint](outHost, out_d, pointBytes) +// if res.IcicleErrorCode != core.IcicleErrorCode(0) { +// fmt.Print("Issue evaluating") +// } - return *G1ProjectivePointToGnarkJac(&outHost[0]), nil, nil - } +// ReverseScalars(scalars_out, size) +// } - return bw6761.G1Jac{}, out_d, nil -} +// func MsmOnDevice(scalars_d, points_d unsafe.Pointer, count int, convert bool) (bw6_761.G1Jac, unsafe.Pointer, error) { +// var p icicle_bw6_761.Projective +// var out_d core.DeviceSlice +// _, e := out_d.Malloc(p.Size(), p.Size()) +// if e != cr.CudaSuccess { +// return bw6_761.G1Jac{}, nil, errors.New("Allocation error") +// } -func MsmG2OnDevice(scalars_d, points_d unsafe.Pointer, count int, convert bool) (bw6761.G2Jac, unsafe.Pointer, error) { - pointBytes := fp.Bytes * 6 // 6 Elements because of 3 coordinates each with real and imaginary elements - out_d, _ := goicicle.CudaMalloc(pointBytes) +// icicle_bw6_761.Msm((s)) - icicle.CommitG2(out_d, scalars_d, points_d, count, 10) +// icicle_bw6_761.Msm(out_d, scalars_d, points_d, count, 10) - if convert { - outHost := make([]icicle.G2Point, 1) - goicicle.CudaMemCpyDtoH[icicle.G2Point](outHost, out_d, pointBytes) - return *G2PointToGnarkJac(&outHost[0]), nil, nil - } +// if convert { +// outHost := make([]icicle_bw6_761.Projective, 1) +// cr.CopyFromDevice(outHost, out_d, uint(pointBytes)) - return bw6761.G2Jac{}, out_d, nil -} +// return *G1ProjectivePointToGnarkJac(&outHost[0]), nil, nil +// } -func GenerateTwiddleFactors(size int, inverse bool) (unsafe.Pointer, error) { - om_selector := int(math.Log(float64(size)) / math.Log(2)) - return icicle.GenerateTwiddles(size, om_selector, inverse) -} +// return bw6_761.G1Jac{}, out_d, nil +// } -func ReverseScalars(ptr unsafe.Pointer, size int) error { - if success, err := icicle.ReverseScalars(ptr, size); success != 0 { - return err - } +// func GenerateTwiddleFactors(size int, inverse bool) (unsafe.Pointer, error) { +// om_selector := int(math.Log(float64(size)) / math.Log(2)) +// return icicle_bw6_761.GenerateTwiddles(size, om_selector, inverse) +// } - return nil -} +// func ReverseScalars(ptr unsafe.Pointer, size int) error { +// if success, err := icicle_bw6_761.ReverseScalars(ptr, size); success != 0 { +// return err +// } -func PolyOps(a_d, b_d, c_d, den_d unsafe.Pointer, size int) { - ret := icicle.VecScalarMulMod(a_d, b_d, size) +// return nil +// } - if ret != 0 { - fmt.Print("Vector mult a*b issue") - } - ret = icicle.VecScalarSub(a_d, c_d, size) +// func PolyOps(a_d, b_d, c_d, den_d unsafe.Pointer, size int) { +// ret := icicle_bw6_761.VecScalarMulMod(a_d, b_d, size) - if ret != 0 { - fmt.Print("Vector sub issue") - } - ret = icicle.VecScalarMulMod(a_d, den_d, size) +// if ret != 0 { +// fmt.Print("Vector mult a*b issue") +// } +// ret = icicle_bw6_761.VecScalarSub(a_d, c_d, size) - if ret != 0 { - fmt.Print("Vector mult a*den issue") - } -} +// if ret != 0 { +// fmt.Print("Vector sub issue") +// } +// ret = icicle_bw6_761.VecScalarMulMod(a_d, den_d, size) -func MontConvOnDevice(scalars_d unsafe.Pointer, size int, is_into bool) { - if is_into { - icicle.ToMontgomery(scalars_d, size) - } else { - icicle.FromMontgomery(scalars_d, size) - } -} +// if ret != 0 { +// fmt.Print("Vector mult a*den issue") +// } +// } + +// func MontConvOnDevice(scalars_d unsafe.Pointer, size int, is_into bool) { +// if is_into { +// icicle_bw6_761.ToMontgomery(scalars_d, size) +// } else { +// icicle_bw6_761.FromMontgomery(scalars_d, size) +// } +// } diff --git a/curves/bw6761/utils.go b/curves/bw6761/utils.go index 699335d..06d62fe 100644 --- a/curves/bw6761/utils.go +++ b/curves/bw6761/utils.go @@ -2,53 +2,50 @@ package bw6761 import ( "fmt" - "unsafe" - "github.com/consensys/gnark-crypto/ecc/bw6-761" + bw6_761 "github.com/consensys/gnark-crypto/ecc/bw6-761" "github.com/consensys/gnark-crypto/ecc/bw6-761/fp" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" - "github.com/ingonyama-zk/icicle/goicicle" - icicle "github.com/ingonyama-zk/icicle/goicicle/curves/bw6761" + core "github.com/ingonyama-zk/icicle/v2/wrappers/golang/core" + icicle_bw6_761 "github.com/ingonyama-zk/icicle/v2/wrappers/golang/curves/bw6761" ) -func CopyToDevice(scalars []fr.Element, bytes int, copyDone chan unsafe.Pointer) { - devicePtr, _ := goicicle.CudaMalloc(bytes) - goicicle.CudaMemCpyHtoD[fr.Element](devicePtr, scalars, bytes) - MontConvOnDevice(devicePtr, len(scalars), false) +func CopyScalarsToDevice(scalars []fr.Element, copyDone chan core.DeviceSlice) { + icicleScalars := core.HostSliceFromElements(BatchConvertFromFrGnark(scalars)) - copyDone <- devicePtr + var deviceScalars core.DeviceSlice + icicleScalars.CopyToDevice(&deviceScalars, true) + icicle_bw6_761.AffineFromMontgomery(&deviceScalars) + + copyDone <- deviceScalars } -func CopyPointsToDevice(points []bw6761.G1Affine, pointsBytes int, copyDone chan unsafe.Pointer) { - if pointsBytes == 0 { - copyDone <- nil +func CopyPointsToDevice(points []bw6_761.G1Affine, copyDone chan core.DeviceSlice) { + var devicePonts core.DeviceSlice + if len(points) == 0 { + copyDone <- devicePonts } else { - devicePtr, _ := goicicle.CudaMalloc(pointsBytes) - iciclePoints := BatchConvertFromG1Affine(points) - goicicle.CudaMemCpyHtoD[icicle.G1PointAffine](devicePtr, iciclePoints, pointsBytes) + iciclePoints := core.HostSliceFromElements(BatchConvertFromG1Affine(points)) + iciclePoints.CopyToDevice(&devicePonts, true) - copyDone <- devicePtr + copyDone <- devicePonts } } -func CopyG2PointsToDevice(points []bw6761.G2Affine, pointsBytes int, copyDone chan unsafe.Pointer) { - if pointsBytes == 0 { - copyDone <- nil - } else { - devicePtr, _ := goicicle.CudaMalloc(pointsBytes) - iciclePoints := BatchConvertFromG2Affine(points) - goicicle.CudaMemCpyHtoD[icicle.G2PointAffine](devicePtr, iciclePoints, pointsBytes) +func HostSliceFromScalars(gnarkScalars []fr.Element) core.HostSlice[icicle_bw6_761.ScalarField] { + return core.HostSliceFromElements(BatchConvertFromFrGnark(gnarkScalars)) +} - copyDone <- devicePtr - } +func HostSliceFromPoints(gnarkPoints []bw6_761.G1Affine) core.HostSlice[icicle_bw6_761.Affine] { + return core.HostSliceFromElements(BatchConvertFromG1Affine(gnarkPoints)) } -func FreeDevicePointer(ptr unsafe.Pointer) { - goicicle.CudaFree(ptr) +func FreeDeviceSlice(deviceSlice core.DeviceSlice) { + deviceSlice.Free() } -func ScalarToGnarkFr(f *icicle.G1ScalarField) *fr.Element { - fb := f.ToBytesLe() +func ScalarToGnarkFr(f *icicle_bw6_761.ScalarField) *fr.Element { + fb := f.ToBytesLittleEndian() var b48 [48]byte copy(b48[:], fb[:48]) @@ -61,8 +58,8 @@ func ScalarToGnarkFr(f *icicle.G1ScalarField) *fr.Element { return &v } -func ScalarToGnarkFp(f *icicle.G1ScalarField) *fp.Element { - fb := f.ToBytesLe() +func ScalarToGnarkFp(f *icicle_bw6_761.ScalarField) *fp.Element { + fb := f.ToBytesLittleEndian() var b96 [96]byte copy(b96[:], fb[:96]) @@ -75,7 +72,60 @@ func ScalarToGnarkFp(f *icicle.G1ScalarField) *fp.Element { return &v } -func BatchConvertG1BaseFieldToFrGnark(elements []icicle.G1BaseField) []fr.Element { +func BatchConvertFromFrGnark(elements []fr.Element) []icicle_bw6_761.ScalarField { + var newElements []icicle_bw6_761.ScalarField + for _, e := range elements { + converted := NewFieldFromFrGnark(e) + newElements = append(newElements, *converted) + } + + return newElements +} + +func BatchConvertFromFrGnarkThreaded(elements []fr.Element, routines int) []icicle_bw6_761.ScalarField { + var newElements []icicle_bw6_761.ScalarField + + if routines > 1 && routines <= len(elements) { + channels := make([]chan []icicle_bw6_761.ScalarField, routines) + for i := 0; i < routines; i++ { + channels[i] = make(chan []icicle_bw6_761.ScalarField, 1) + } + + convert := func(elements []fr.Element, chanIndex int) { + var convertedElements []icicle_bw6_761.ScalarField + for _, e := range elements { + converted := NewFieldFromFrGnark(e) + convertedElements = append(convertedElements, *converted) + } + + channels[chanIndex] <- convertedElements + } + + batchLen := len(elements) / routines + for i := 0; i < routines; i++ { + start := batchLen * i + end := batchLen * (i + 1) + elemsToConv := elements[start:end] + if i == routines-1 { + elemsToConv = elements[start:] + } + go convert(elemsToConv, i) + } + + for i := 0; i < routines; i++ { + newElements = append(newElements, <-channels[i]...) + } + } else { + for _, e := range elements { + converted := NewFieldFromFrGnark(e) + newElements = append(newElements, *converted) + } + } + + return newElements +} + +func BatchConvertBaseFieldToFrGnark(elements []icicle_bw6_761.BaseField) []fr.Element { var newElements []fr.Element for _, e := range elements { converted := BaseFieldToGnarkFr(&e) @@ -85,7 +135,7 @@ func BatchConvertG1BaseFieldToFrGnark(elements []icicle.G1BaseField) []fr.Elemen return newElements } -func BatchConvertG1ScalarFieldToFrGnark(elements []icicle.G1ScalarField) []fr.Element { +func BatchConvertScalarFieldToFrGnark(elements []icicle_bw6_761.ScalarField) []fr.Element { var newElements []fr.Element for _, e := range elements { converted := ScalarToGnarkFr(&e) @@ -95,7 +145,7 @@ func BatchConvertG1ScalarFieldToFrGnark(elements []icicle.G1ScalarField) []fr.El return newElements } -func BatchConvertG1BaseFieldToFrGnarkThreaded(elements []icicle.G1BaseField, routines int) []fr.Element { +func BatchConvertBaseFieldToFrGnarkThreaded(elements []icicle_bw6_761.BaseField, routines int) []fr.Element { var newElements []fr.Element if routines > 1 { @@ -104,7 +154,7 @@ func BatchConvertG1BaseFieldToFrGnarkThreaded(elements []icicle.G1BaseField, rou channels[i] = make(chan []fr.Element, 1) } - convert := func(elements []icicle.G1BaseField, chanIndex int) { + convert := func(elements []icicle_bw6_761.BaseField, chanIndex int) { var convertedElements []fr.Element for _, e := range elements { converted := BaseFieldToGnarkFr(&e) @@ -133,7 +183,7 @@ func BatchConvertG1BaseFieldToFrGnarkThreaded(elements []icicle.G1BaseField, rou return newElements } -func BatchConvertG1ScalarFieldToFrGnarkThreaded(elements []icicle.G1ScalarField, routines int) []fr.Element { +func BatchConvertScalarFieldToFrGnarkThreaded(elements []icicle_bw6_761.ScalarField, routines int) []fr.Element { var newElements []fr.Element if routines > 1 { @@ -142,7 +192,7 @@ func BatchConvertG1ScalarFieldToFrGnarkThreaded(elements []icicle.G1ScalarField, channels[i] = make(chan []fr.Element, 1) } - convert := func(elements []icicle.G1ScalarField, chanIndex int) { + convert := func(elements []icicle_bw6_761.ScalarField, chanIndex int) { var convertedElements []fr.Element for _, e := range elements { converted := ScalarToGnarkFr(&e) @@ -171,42 +221,30 @@ func BatchConvertG1ScalarFieldToFrGnarkThreaded(elements []icicle.G1ScalarField, return newElements } -func NewFieldFromFrGnark(element fr.Element) *icicle.G1ScalarField { - S := ConvertUint64ArrToUint32Arr6(element.Bits()) // get non-montgomry +func NewFieldFromFrGnark(element fr.Element) *icicle_bw6_761.ScalarField { + element_bits := element.Bits() + s := core.ConvertUint64ArrToUint32Arr(element_bits[:]) // get non-montgomry - return &icicle.G1ScalarField{S} + var field icicle_bw6_761.ScalarField + field.FromLimbs(s) + return &field } -func NewFieldFromFpGnark(element fp.Element) *icicle.G1BaseField { - S := ConvertUint64ArrToUint32Arr12(element.Bits()) // get non-montgomry +func NewFieldFromFpGnark(element fp.Element) *icicle_bw6_761.BaseField { + element_bits := element.Bits() + s := core.ConvertUint64ArrToUint32Arr(element_bits[:]) // get non-montgomry - return &icicle.G1BaseField{S} + var field icicle_bw6_761.BaseField + field.FromLimbs(s) + return &field } -func BaseFieldToGnarkFr(f *icicle.G1BaseField) *fr.Element { - fb := f.ToBytesLe() - var b48 [48]byte - copy(b48[:], fb[:48]) - - v, e := fr.LittleEndian.Element(&b48) - - if e != nil { - panic(fmt.Sprintf("unable to convert point %v got error %v", f, e)) - } - +func BaseFieldToGnarkFr(f *icicle_bw6_761.BaseField) *fr.Element { + v, _ := fr.LittleEndian.Element((*[fr.Bytes]byte)(f.ToBytesLittleEndian())) return &v } -func BaseFieldToGnarkFp(f *icicle.G1BaseField) *fp.Element { - fb := f.ToBytesLe() - var b96 [96]byte - copy(b96[:], fb[:96]) - - v, e := fp.LittleEndian.Element(&b96) - - if e != nil { - panic(fmt.Sprintf("unable to convert point %v got error %v", f, e)) - } - +func BaseFieldToGnarkFp(f *icicle_bw6_761.BaseField) *fp.Element { + v, _ := fp.LittleEndian.Element((*[fp.Bytes]byte)(f.ToBytesLittleEndian())) return &v } diff --git a/go.mod b/go.mod index 73132aa..7f6d188 100644 --- a/go.mod +++ b/go.mod @@ -4,19 +4,17 @@ go 1.20 require ( github.com/consensys/gnark-crypto v0.12.2-0.20231208203441-d4eab6ddd2af - github.com/ingonyama-zk/icicle v0.1.0 - github.com/stretchr/testify v1.8.3 + github.com/ingonyama-zk/icicle/v2 v2.0.3 + github.com/stretchr/testify v1.9.0 ) require ( - github.com/bits-and-blooms/bitset v1.7.0 // indirect + github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/kr/text v0.2.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect - golang.org/x/sys v0.9.0 // indirect + golang.org/x/sys v0.18.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index 7ce127f..bd617a4 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,16 @@ -github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= -github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= +github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.12.2-0.20231208203441-d4eab6ddd2af h1:QbTpU3l/2wEFLtF4DQgApTXCDEtd9Jb8olP84VxvP4E= github.com/consensys/gnark-crypto v0.12.2-0.20231208203441-d4eab6ddd2af/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/ingonyama-zk/icicle v0.1.0 h1:9zbHaYv8/4g3HWRabBCpeH+64U8GJ99K1qeqE2jO6LM= -github.com/ingonyama-zk/icicle v0.1.0/go.mod h1:kAK8/EoN7fUEmakzgZIYdWy1a2rBnpCaZLqSHwZWxEk= +github.com/ingonyama-zk/icicle/v2 v2.0.3 h1:qNFXWQqUuOdJXh+25lIdCRJLqLrUwPkAfcK4wJXBap0= +github.com/ingonyama-zk/icicle/v2 v2.0.3/go.mod h1:rr3B+xKQKW1U40A+vEzA4hI2ilTrPSJBtxedfnaUYHw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= @@ -20,13 +18,13 @@ github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFV github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=