github.com/consensys/gnark-crypto@v0.14.0/internal/generator/ecc/template/tests/point.go.tmpl (about)

     1  
     2  {{ $TAffine := print (toUpper .PointName) "Affine" }}
     3  {{ $TJacobian := print (toUpper .PointName) "Jac" }}
     4  {{ $TJacobianExtended := print (toLower .PointName) "JacExtended" }}
     5  
     6  {{$fuzzer := "GenFp()"}}
     7  {{if eq .CoordType "fptower.E2" }}
     8  	{{$fuzzer = "GenE2()"}}
     9  {{- else if eq .CoordType "fptower.E4" }}
    10  	{{$fuzzer = "GenE4()"}}
    11  {{- end}}
    12  
    13  {{$c := 16}}
    14  {{if eq .Name "secp256k1"}}
    15      {{$c = 15}}
    16  {{end}}
    17  
    18  import (
    19  	"fmt"
    20  	"math/big"
    21  	"testing"
    22  	"math/rand/v2"
    23  
    24  	{{if eq .Name "secp256k1"}}
    25  		crand "crypto/rand"
    26  	{{end}}
    27  
    28  	{{if or (eq .CoordType "fptower.E2") (eq .CoordType "fptower.E4")}}
    29  	"github.com/consensys/gnark-crypto/ecc/{{.Name}}/internal/fptower"
    30  	{{else}}
    31  	"github.com/consensys/gnark-crypto/ecc/{{.Name}}/fp"
    32  	{{end}}
    33  	"github.com/consensys/gnark-crypto/ecc/{{.Name}}/fr"
    34  	"github.com/leanovate/gopter"
    35  	"github.com/leanovate/gopter/prop"
    36  )
    37  
    38  {{if .GLV}}
    39      func Test{{ $TAffine }}Endomorphism(t *testing.T) {
    40  		t.Parallel()
    41          parameters := gopter.DefaultTestParameters()
    42          if testing.Short() {
    43  			parameters.MinSuccessfulTests = nbFuzzShort
    44  		} else {
    45  			parameters.MinSuccessfulTests = nbFuzz
    46  		}
    47  
    48          properties := gopter.NewProperties(parameters)
    49  
    50          properties.Property("[{{ toUpper .Name }}] check that phi(P) = lambdaGLV * P", prop.ForAll(
    51              func(a {{ .CoordType}}) bool {
    52                  var p, res1, res2 {{ $TJacobian }}
    53                  g := MapTo{{ toUpper .PointName}}(a)
    54                  p.FromAffine(&g)
    55                  res1.phi(&p)
    56                  res2.mulWindowed(&p, &lambdaGLV)
    57  
    58                  return p.IsInSubGroup() && res1.Equal(&res2)
    59              },
    60              {{$fuzzer}},
    61          ))
    62  
    63          properties.Property("[{{ toUpper .Name }}] check that phi^2(P) + phi(P) + P = 0", prop.ForAll(
    64                  func(a {{ .CoordType}}) bool {
    65                  var p, res, tmp {{ $TJacobian }}
    66                  g := MapTo{{ toUpper .PointName}}(a)
    67                  p.FromAffine(&g)
    68                  tmp.phi(&p)
    69                  res.phi(&tmp).
    70                      AddAssign(&tmp).
    71                      AddAssign(&p)
    72  
    73                  return res.Z.IsZero()
    74              },
    75              {{$fuzzer}},
    76          ))
    77  
    78          {{if eq .PointName "g2" }}
    79          {{- if and (eq .PointName "g2") (ne .Name "bw6-761") (ne .Name "bw6-633") (ne .Name "bw6-756") }}
    80              properties.Property("[{{ toUpper .Name }}] check that psi^2(P) = -phi(P)", prop.ForAll(
    81                  func(a {{ .CoordType}}) bool {
    82                      var p, res1, res2 {{ $TJacobian }}
    83                      g := MapTo{{ toUpper .PointName}}(a)
    84                      p.FromAffine(&g)
    85                      {{- if or (eq .Name "bls24-315") (eq .Name "bls24-317")}}
    86                          res1.psi(&p).psi(&res1).psi(&res1).psi(&res1).Neg(&res1)
    87                      {{- else}}
    88                          res1.psi(&p).psi(&res1).Neg(&res1)
    89                      {{- end}}
    90                      {{- if eq .Name "bn254"}}
    91                          res2.phi(&p)
    92                      {{- else}}
    93                          res2.Set(&p)
    94                          res2.X.MulByElement(&res2.X, &thirdRootOneG1)
    95                      {{- end}}
    96  
    97                      return p.IsInSubGroup() && res1.Equal(&res2)
    98                  },
    99                  {{$fuzzer}},
   100              ))
   101          {{end}}
   102          {{end}}
   103          properties.TestingRun(t, gopter.ConsoleReporter(false))
   104      }
   105  {{end}}
   106  
   107  func Test{{ $TAffine }}IsOnCurve(t *testing.T) {
   108  	t.Parallel()
   109  	parameters := gopter.DefaultTestParameters()
   110  	if testing.Short() {
   111  		parameters.MinSuccessfulTests = nbFuzzShort
   112  	} else {
   113  		parameters.MinSuccessfulTests = nbFuzz
   114  	}
   115  
   116  	properties := gopter.NewProperties(parameters)
   117  
   118  	properties.Property("[{{ toUpper .Name }}] {{.PointName}}Gen (affine) should be on the curve", prop.ForAll(
   119  		func(a {{ .CoordType}}) bool {
   120  			var op1, op2 {{ $TAffine }}
   121  			op1.FromJacobian(&{{.PointName}}Gen)
   122  			op2.Set(&op1)
   123  			op2.Y.Mul(&op2.Y, &a)
   124  			return op1.IsOnCurve() && !op2.IsOnCurve()
   125  		},
   126  		{{$fuzzer}},
   127  	))
   128  
   129  	properties.Property("[{{ toUpper .Name }}] {{.PointName}}Gen (Jacobian) should be on the curve", prop.ForAll(
   130  		func(a {{ .CoordType}}) bool {
   131  			var op1, op2, op3 {{ $TJacobian }}
   132  			op1.Set(&{{.PointName}}Gen)
   133  			op3.Set(&{{.PointName}}Gen)
   134  
   135  			op2 = fuzz{{ $TJacobian }}(&{{.PointName}}Gen, a)
   136  			op3.Y.Mul(&op3.Y, &a)
   137  			return op1.IsOnCurve() && op2.IsOnCurve() && !op3.IsOnCurve()
   138  		},
   139  		{{$fuzzer}},
   140  	))
   141  
   142  	properties.Property("[{{ toUpper .Name }}] IsInSubGroup and MulBy subgroup order should be the same", prop.ForAll(
   143  		func(a {{ .CoordType}}) bool {
   144              var op1, op2 {{ $TJacobian }}
   145  			op1 = fuzz{{ $TJacobian }}(&{{.PointName}}Gen, a)
   146              _r := fr.Modulus()
   147              op2.ScalarMultiplication(&op1, _r)
   148  			return op1.IsInSubGroup() && op2.Z.IsZero()
   149  		},
   150  		{{$fuzzer}},
   151  	))
   152  
   153  	properties.TestingRun(t, gopter.ConsoleReporter(false))
   154  }
   155  
   156  
   157  func Test{{ $TAffine }}Conversions(t *testing.T) {
   158  	t.Parallel()
   159  	parameters := gopter.DefaultTestParameters()
   160  	if testing.Short() {
   161  		parameters.MinSuccessfulTests = nbFuzzShort
   162  	} else {
   163  		parameters.MinSuccessfulTests = nbFuzz
   164  	}
   165  
   166  	properties := gopter.NewProperties(parameters)
   167  
   168  
   169  	properties.Property("[{{ toUpper .Name }}] Affine representation should be independent of the Jacobian representative", prop.ForAll(
   170  		func(a {{ .CoordType}}) bool {
   171  			g := fuzz{{ $TJacobian }}(&{{ toLower .PointName }}Gen, a)
   172  			var op1 {{ $TAffine }}
   173  			op1.FromJacobian(&g)
   174  			return op1.X.Equal(&{{ toLower .PointName }}Gen.X) && op1.Y.Equal(&{{ toLower .PointName }}Gen.Y)
   175  		},
   176  		{{$fuzzer}},
   177  	))
   178  
   179  
   180  	properties.Property("[{{ toUpper .Name }}] Affine representation should be independent of a Extended Jacobian representative", prop.ForAll(
   181  		func(a {{ .CoordType}}) bool {
   182  			var g {{ $TJacobianExtended }}
   183  			g.X.Set(&{{ toLower .PointName }}Gen.X)
   184  			g.Y.Set(&{{ toLower .PointName }}Gen.Y)
   185  			g.ZZ.Set(&{{ toLower .PointName }}Gen.Z)
   186  			g.ZZZ.Set(&{{ toLower .PointName }}Gen.Z)
   187  			gfuzz := fuzz{{ $TJacobianExtended }}(&g, a)
   188  
   189  			var op1 {{ $TAffine }}
   190  			op1.fromJacExtended(&gfuzz)
   191  			return op1.X.Equal(&{{ toLower .PointName }}Gen.X) && op1.Y.Equal(&{{ toLower .PointName }}Gen.Y)
   192  		},
   193  		{{$fuzzer}},
   194  	))
   195  
   196  	properties.Property("[{{ toUpper .Name }}] Jacobian representation should be the same as the affine representative", prop.ForAll(
   197  		func(a {{ .CoordType}}) bool {
   198  			var g {{ $TJacobian }}
   199  			var op1 {{ $TAffine }}
   200  			op1.X.Set(&{{ toLower .PointName }}Gen.X)
   201  			op1.Y.Set(&{{ toLower .PointName }}Gen.Y)
   202  
   203  			var one {{ .CoordType}}
   204  			one.SetOne()
   205  
   206  			g.FromAffine(&op1)
   207  
   208  			return g.X.Equal(&{{ toLower .PointName }}Gen.X) && g.Y.Equal(&{{ toLower .PointName }}Gen.Y) && g.Z.Equal(&one)
   209  		},
   210  		{{$fuzzer}},
   211  	))
   212  
   213  	properties.Property("[{{ toUpper .Name }}] Converting affine symbol for infinity to Jacobian should output correct infinity in Jacobian", prop.ForAll(
   214  		func() bool {
   215  			var g {{ $TAffine }}
   216  			g.X.SetZero()
   217  			g.Y.SetZero()
   218  			var op1 {{ $TJacobian }}
   219  			op1.FromAffine(&g)
   220  			var one, zero {{ .CoordType}}
   221  			one.SetOne()
   222  			return op1.X.Equal(&one) && op1.Y.Equal(&one) && op1.Z.Equal(&zero)
   223  		},
   224  	))
   225  
   226  	properties.Property("[{{ toUpper .Name }}] Converting infinity in extended Jacobian to affine should output infinity symbol in Affine", prop.ForAll(
   227  		func() bool {
   228  			var g {{ $TAffine }}
   229  			var op1 {{ $TJacobianExtended }}
   230  			var zero {{ .CoordType}}
   231  			op1.X.Set(&{{ toLower .PointName }}Gen.X)
   232  			op1.Y.Set(&{{ toLower .PointName }}Gen.Y)
   233  			g.fromJacExtended(&op1)
   234  			return g.X.Equal(&zero) && g.Y.Equal(&zero)
   235  		},
   236  	))
   237  
   238  	properties.Property("[{{ toUpper .Name }}] Converting infinity in extended Jacobian to Jacobian should output infinity in Jacobian", prop.ForAll(
   239  		func() bool {
   240  			var g {{ $TJacobian }}
   241  			var op1 {{ $TJacobianExtended }}
   242  			var zero, one {{ .CoordType}}
   243  			one.SetOne()
   244  			op1.X.Set(&{{ toLower .PointName }}Gen.X)
   245  			op1.Y.Set(&{{ toLower .PointName }}Gen.Y)
   246  			g.fromJacExtended(&op1)
   247  			return g.X.Equal(&one) && g.Y.Equal(&one) && g.Z.Equal(&zero)
   248  		},
   249  	))
   250  
   251  	properties.Property("[{{ toUpper .Name }}] [Jacobian] Two representatives of the same class should be equal", prop.ForAll(
   252  		func(a, b {{ .CoordType}}) bool {
   253  			op1 := fuzz{{ $TJacobian }}(&{{ toLower .PointName }}Gen, a)
   254  			op2 := fuzz{{ $TJacobian }}(&{{ toLower .PointName }}Gen, b)
   255  			return op1.Equal(&op2)
   256  		},
   257  		{{$fuzzer}},
   258  		{{$fuzzer}},
   259  	))
   260  
   261      {{- if eq .PointName "g1" }}
   262  	properties.Property("[{{ toUpper .Name }}] BatchJacobianToAffineG1 and FromJacobian should output the same result", prop.ForAll(
   263  		func(a, b {{ .CoordType}}) bool {
   264  			g1 := fuzz{{ $TJacobian }}(&{{ toLower .PointName }}Gen, a)
   265  			g2 := fuzz{{ $TJacobian }}(&{{ toLower .PointName }}Gen, b)
   266  			var op1, op2 {{ $TAffine }}
   267  			op1.FromJacobian(&g1)
   268  			op2.FromJacobian(&g2)
   269  			baseTableAff := BatchJacobianToAffineG1([]G1Jac{g1, g2})
   270  			return op1.Equal(&baseTableAff[0]) && op2.Equal(&baseTableAff[1])
   271  		},
   272  		GenFp(),
   273  		GenFp(),
   274  	))
   275      {{- end }}
   276  
   277  	properties.TestingRun(t, gopter.ConsoleReporter(false))
   278  }
   279  
   280  func Test{{ $TAffine }}Ops(t *testing.T) {
   281  	t.Parallel()
   282  	parameters := gopter.DefaultTestParameters()
   283  	parameters.MinSuccessfulTests = 10
   284  
   285  	properties := gopter.NewProperties(parameters)
   286  
   287  	genScalar := GenFr()
   288  
   289      properties.Property("[{{ toUpper .Name }}] Add(P,-P) should return the point at infinity", prop.ForAll(
   290              func(s fr.Element) bool {
   291                  var op1, op2 {{ toUpper .PointName }}Affine
   292                  var sInt big.Int
   293                  g := {{ toLower .PointName }}GenAff
   294                  s.BigInt(&sInt)
   295                  op1.ScalarMultiplication(&g, &sInt)
   296                  op2.Neg(&op1)
   297  
   298                  op1.Add(&op1, &op2)
   299                  return op1.IsInfinity()
   300  
   301           },
   302          GenFr(),
   303       ))
   304  
   305      properties.Property("[{{ toUpper .Name }}] Add(P,0) and Add(0,P) should return P", prop.ForAll(
   306               func(s fr.Element) bool {
   307                      var op1, op2 {{ toUpper .PointName }}Affine
   308                      var sInt big.Int
   309                      g := {{ toLower .PointName }}GenAff
   310                      s.BigInt(&sInt)
   311                      op1.ScalarMultiplication(&g, &sInt)
   312                      op2.setInfinity()
   313  
   314                      op1.Add(&op1, &op2)
   315                      op2.Add(&op2, &op1)
   316                      return op1.Equal(&op2)
   317  
   318            },
   319           GenFr(),
   320      ))
   321  
   322      properties.Property("[{{ toUpper .Name }}] Add should call double when adding the same point", prop.ForAll(
   323          func(s fr.Element) bool {
   324              var op1, op2 {{ toUpper .PointName }}Affine
   325              var sInt big.Int
   326              g := {{ toLower .PointName }}GenAff
   327              s.BigInt(&sInt)
   328              op1.ScalarMultiplication(&g, &sInt)
   329  
   330              op2.Double(&op1)
   331              op1.Add(&op1, &op1)
   332              return op1.Equal(&op2)
   333  
   334         },
   335         GenFr(),
   336     ))
   337  
   338      properties.Property("[{{ toUpper .Name }}] [2]G = double(G) + G - G", prop.ForAll(
   339         func(s fr.Element) bool {
   340              var sInt big.Int
   341              g := {{ toLower .PointName }}GenAff
   342              s.BigInt(&sInt)
   343              g.ScalarMultiplication(&g, &sInt)
   344              var op1, op2 {{ toUpper .PointName }}Affine
   345              op1.ScalarMultiplication(&g, big.NewInt(2))
   346              op2.Double(&g)
   347              op2.Add(&op2, &g)
   348              op2.Sub(&op2, &g)
   349              return op1.Equal(&op2)
   350          },
   351          GenFr(),
   352       ))
   353  
   354  	properties.Property("[{{ toUpper .Name }}] [-s]G = -[s]G", prop.ForAll(
   355  		func(s fr.Element) bool {
   356  			g := {{ toLower .PointName }}GenAff
   357  			var gj {{ toUpper .PointName }}Jac
   358  			var nbs, bs big.Int
   359  			s.BigInt(&bs)
   360  			nbs.Neg(&bs)
   361  
   362  			var res = true
   363  
   364  			// mulGLV
   365  			{
   366  				var op1, op2 {{ toUpper .PointName }}Affine
   367  				op1.ScalarMultiplication(&g, &bs).Neg(&op1)
   368  				op2.ScalarMultiplication(&g, &nbs)
   369  				res = res && op1.Equal(&op2)
   370  			}
   371  
   372  			// mulWindowed
   373  			{
   374  				var op1, op2 {{ toUpper .PointName }}Jac
   375  				op1.mulWindowed(&gj, &bs).Neg(&op1)
   376  				op2.mulWindowed(&gj, &nbs)
   377  				res = res && op1.Equal(&op2)
   378  			}
   379  
   380  			return res
   381  		},
   382  		GenFr(),
   383  	))
   384  
   385  	properties.Property("[{{ toUpper .Name }}] [Jacobian] Add should call double when adding the same point", prop.ForAll(
   386  		func(a, b {{ .CoordType}}) bool {
   387  			fop1 := fuzz{{ $TJacobian }}(&{{ toLower .PointName }}Gen, a)
   388  			fop2 := fuzz{{ $TJacobian }}(&{{ toLower .PointName }}Gen, b)
   389  			var op1, op2 {{ $TJacobian }}
   390  			op1.Set(&fop1).AddAssign(&fop2)
   391  			op2.Double(&fop2)
   392  			return op1.Equal(&op2)
   393  		},
   394  		{{$fuzzer}},
   395  		{{$fuzzer}},
   396  	))
   397  
   398  	properties.Property("[{{ toUpper .Name }}] [Jacobian] Adding the opposite of a point to itself should output inf", prop.ForAll(
   399  		func(a, b {{ .CoordType}}) bool {
   400  			fop1 := fuzz{{ $TJacobian }}(&{{ toLower .PointName }}Gen, a)
   401  			fop2 := fuzz{{ $TJacobian }}(&{{ toLower .PointName }}Gen, b)
   402  			fop2.Neg(&fop2)
   403  			fop1.AddAssign(&fop2)
   404  			return fop1.Equal(&{{ toLower .PointName }}Infinity)
   405  		},
   406  		{{$fuzzer}},
   407  		{{$fuzzer}},
   408  	))
   409  
   410  	properties.Property("[{{ toUpper .Name }}] [Jacobian] Adding the inf to a point should not modify the point", prop.ForAll(
   411  		func(a {{ .CoordType}}) bool {
   412  			fop1 := fuzz{{ $TJacobian }}(&{{ toLower .PointName }}Gen, a)
   413  			fop1.AddAssign(&{{ toLower .PointName }}Infinity)
   414  			var op2 {{ $TJacobian }}
   415  			op2.Set(&{{ toLower .PointName }}Infinity)
   416  			op2.AddAssign(&{{ toLower .PointName }}Gen)
   417  			return fop1.Equal(&{{ toLower .PointName }}Gen) && op2.Equal(&{{ toLower .PointName }}Gen)
   418  		},
   419  		{{$fuzzer}},
   420  	))
   421  
   422  	properties.Property("[{{ toUpper .Name }}] [Jacobian Extended] addMixed (-G) should equal subMixed(G)", prop.ForAll(
   423  		func(a {{ .CoordType}}) bool {
   424  			fop1 := fuzz{{ $TJacobian }}(&{{ toLower .PointName }}Gen, a)
   425  			var p1,p1Neg {{ $TAffine }}
   426  			p1.FromJacobian(&fop1)
   427  			p1Neg = p1
   428  			p1Neg.Y.Neg(&p1Neg.Y)
   429  			var o1, o2 {{ $TJacobianExtended }}
   430  			o1.addMixed(&p1Neg)
   431  			o2.subMixed(&p1)
   432  
   433  			return 	o1.X.Equal(&o2.X) &&
   434  					o1.Y.Equal(&o2.Y) &&
   435  					o1.ZZ.Equal(&o2.ZZ) &&
   436  					o1.ZZZ.Equal(&o2.ZZZ)
   437  		},
   438  		{{$fuzzer}},
   439  	))
   440  
   441  	properties.Property("[{{ toUpper .Name }}] [Jacobian Extended] doubleMixed (-G) should equal doubleNegMixed(G)", prop.ForAll(
   442  		func(a {{ .CoordType}}) bool {
   443  			fop1 := fuzz{{ $TJacobian }}(&{{ toLower .PointName }}Gen, a)
   444  			var p1,p1Neg {{ $TAffine }}
   445  			p1.FromJacobian(&fop1)
   446  			p1Neg = p1
   447  			p1Neg.Y.Neg(&p1Neg.Y)
   448  			var o1, o2 {{ $TJacobianExtended }}
   449  			o1.doubleMixed(&p1Neg)
   450  			o2.doubleNegMixed(&p1)
   451  
   452  			return 	o1.X.Equal(&o2.X) &&
   453  					o1.Y.Equal(&o2.Y) &&
   454  					o1.ZZ.Equal(&o2.ZZ) &&
   455  					o1.ZZZ.Equal(&o2.ZZZ)
   456  		},
   457  		{{$fuzzer}},
   458  	))
   459  
   460  	properties.Property("[{{ toUpper .Name }}] [Jacobian] Addmix the negation to itself should output 0", prop.ForAll(
   461  		func(a {{ .CoordType}}) bool {
   462  			fop1 := fuzz{{ $TJacobian }}(&{{ toLower .PointName }}Gen, a)
   463  			fop1.Neg(&fop1)
   464  			var op2 {{ $TAffine }}
   465  			op2.FromJacobian(&{{ toLower .PointName }}Gen)
   466  			fop1.AddMixed(&op2)
   467  			return fop1.Equal(&{{ toLower .PointName }}Infinity)
   468  		},
   469  		{{$fuzzer}},
   470  	))
   471  
   472  	properties.Property("[{{ toUpper .Name }}] scalar multiplication (double and add) should depend only on the scalar mod r", prop.ForAll(
   473  		func(s fr.Element) bool {
   474  
   475  			r := fr.Modulus()
   476  			var g {{ $TJacobian }}
   477  			g.ScalarMultiplication(&{{.PointName}}Gen, r)
   478  
   479  			var scalar, blindedScalar, rminusone big.Int
   480  			var op1, op2, op3, gneg {{ $TJacobian }}
   481  			rminusone.SetUint64(1).Sub(r, &rminusone)
   482  			op3.mulWindowed(&{{.PointName}}Gen, &rminusone)
   483  			gneg.Neg(&{{.PointName}}Gen)
   484  			s.BigInt(&scalar)
   485  			blindedScalar.Mul(&scalar, r).Add(&blindedScalar, &scalar)
   486  			op1.mulWindowed(&{{.PointName}}Gen, &scalar)
   487  			op2.mulWindowed(&{{.PointName}}Gen, &blindedScalar)
   488  
   489  			return op1.Equal(&op2) && g.Equal(&{{.PointName}}Infinity) && !op1.Equal(&{{.PointName}}Infinity) && gneg.Equal(&op3)
   490  
   491  		},
   492  		genScalar,
   493  	))
   494  
   495  	{{ if or (eq .CoordType "fptower.E2") (eq .CoordType "fptower.E4")}}
   496  		properties.Property("[{{ toUpper .Name }}] psi should map points from E' to itself", prop.ForAll(
   497  			func() bool {
   498  				var a {{ $TJacobian }}
   499  				a.psi(&{{ toLower .PointName }}Gen)
   500  				return a.IsOnCurve() && !a.Equal(&g2Gen)
   501  			},
   502  		))
   503  	{{ end }}
   504  
   505      {{if .GLV}}
   506  		properties.Property("[{{ toUpper .Name }}] scalar multiplication (GLV) should depend only on the scalar mod r", prop.ForAll(
   507  			func(s fr.Element) bool {
   508  
   509  				r := fr.Modulus()
   510  				var g {{ $TJacobian }}
   511  				g.mulGLV(&{{.PointName}}Gen, r)
   512  
   513  				var scalar, blindedScalar, rminusone big.Int
   514  				var op1, op2, op3, gneg {{ $TJacobian }}
   515  				rminusone.SetUint64(1).Sub(r, &rminusone)
   516  				op3.ScalarMultiplication(&{{.PointName}}Gen, &rminusone)
   517  				gneg.Neg(&{{.PointName}}Gen)
   518  				s.BigInt(&scalar)
   519  				blindedScalar.Mul(&scalar, r).Add(&blindedScalar, &scalar)
   520  				op1.ScalarMultiplication(&{{.PointName}}Gen, &scalar)
   521  				op2.ScalarMultiplication(&{{.PointName}}Gen, &blindedScalar)
   522  
   523  				return op1.Equal(&op2) && g.Equal(&{{.PointName}}Infinity) && !op1.Equal(&{{.PointName}}Infinity) && gneg.Equal(&op3)
   524  
   525  			},
   526  			genScalar,
   527  		))
   528  
   529          properties.Property("[{{ toUpper .Name }}] GLV and Double and Add should output the same result", prop.ForAll(
   530              func(s fr.Element) bool {
   531  
   532                  var r big.Int
   533                  var op1, op2 {{ $TJacobian }}
   534                  s.BigInt(&r)
   535                  op1.mulWindowed(&{{.PointName}}Gen, &r)
   536                  op2.mulGLV(&{{.PointName}}Gen, &r)
   537                  return op1.Equal(&op2) && !op1.Equal(&{{.PointName}}Infinity)
   538  
   539              },
   540              genScalar,
   541          ))
   542  
   543  
   544      {{end}}
   545  
   546      {{- if eq .PointName "g1" }}
   547  	properties.Property("[{{ toUpper .Name }}] JointScalarMultiplicationBase and ScalarMultiplication should output the same results", prop.ForAll(
   548  		func(s1, s2 fr.Element) bool {
   549  
   550  			var op1, op2, temp {{ $TJacobian }}
   551  
   552  			op1.JointScalarMultiplicationBase(&g1GenAff, s1.BigInt(new(big.Int)), s2.BigInt(new(big.Int)))
   553  			temp.ScalarMultiplication(&g1Gen, s2.BigInt(new(big.Int)))
   554  			op2.ScalarMultiplication(&g1Gen, s1.BigInt(new(big.Int))).
   555  				AddAssign(&temp)
   556  
   557  			return op1.Equal(&op2)
   558  
   559  		},
   560  		genScalar,
   561  		genScalar,
   562  	))
   563  
   564  
   565      {{- end }}
   566  
   567  	properties.TestingRun(t, gopter.ConsoleReporter(false))
   568  }
   569  
   570  
   571  {{if .CofactorCleaning }}
   572  func Test{{ $TAffine }}CofactorCleaning(t *testing.T) {
   573  	t.Parallel()
   574  	parameters := gopter.DefaultTestParameters()
   575  	if testing.Short() {
   576  		parameters.MinSuccessfulTests = nbFuzzShort
   577  	} else {
   578  		parameters.MinSuccessfulTests = nbFuzz
   579  	}
   580  
   581  	properties := gopter.NewProperties(parameters)
   582  
   583  	properties.Property("[{{ toUpper .Name }}] Clearing the cofactor of a random point should set it in the r-torsion", prop.ForAll(
   584  		func() bool {
   585  			var a, x, b {{ .CoordType }}
   586  			a.SetRandom()
   587  			{{if eq .CoordType "fp.Element" }}
   588  				{{if eq .PointName "g2" }}
   589  					x.Square(&a).Mul(&x, &a).Add(&x, &bTwistCurveCoeff)
   590  				{{else}}
   591  					x.Square(&a).Mul(&x, &a).Add(&x, &bCurveCoeff)
   592  				{{end}}
   593  				for x.Legendre() != 1 {
   594  					a.SetRandom()
   595  					{{if eq .PointName "g2" }}
   596  						x.Square(&a).Mul(&x, &a).Add(&x, &bTwistCurveCoeff)
   597  					{{else}}
   598  						x.Square(&a).Mul(&x, &a).Add(&x, &bCurveCoeff)
   599  					{{end}}
   600  				}
   601  			{{else}}
   602  			{{/* eq .CoordType "fptower.E2" */}}
   603  				x.Square(&a).Mul(&x, &a).Add(&x, &bTwistCurveCoeff)
   604  				for x.Legendre() != 1 {
   605  					a.SetRandom()
   606  					x.Square(&a).Mul(&x, &a).Add(&x, &bTwistCurveCoeff)
   607  				}
   608  			{{end}}
   609  			b.Sqrt(&x)
   610  			var point, pointCleared, infinity {{ $TJacobian }}
   611  			point.X.Set(&a)
   612  			point.Y.Set(&b)
   613  			point.Z.SetOne()
   614  			pointCleared.ClearCofactor(&point)
   615  			infinity.Set(&{{.PointName}}Infinity)
   616  			return point.IsOnCurve() && pointCleared.IsInSubGroup() && !pointCleared.Equal(&infinity)
   617  		},
   618  	))
   619  	properties.TestingRun(t, gopter.ConsoleReporter(false))
   620  
   621  }
   622  {{end}}
   623  
   624  func Test{{ $TAffine }}BatchScalarMultiplication(t *testing.T) {
   625  
   626  	parameters := gopter.DefaultTestParameters()
   627  	if testing.Short() {
   628  		parameters.MinSuccessfulTests = nbFuzzShort
   629  	} else {
   630  		parameters.MinSuccessfulTests = nbFuzzShort
   631  	}
   632  
   633  	properties := gopter.NewProperties(parameters)
   634  
   635  	genScalar := GenFr()
   636  
   637  	// size of the multiExps
   638  	const nbSamples = 10
   639  
   640  	properties.Property("[{{ toUpper .Name }}] BatchScalarMultiplication should be consistent with individual scalar multiplications", prop.ForAll(
   641  		func(mixer fr.Element) bool {
   642  			// mixer ensures that all the words of a fpElement are set
   643  			var sampleScalars [nbSamples]fr.Element
   644  
   645  			for i := 1; i <= nbSamples; i++ {
   646  				sampleScalars[i-1].SetUint64(uint64(i)).
   647  					Mul(&sampleScalars[i-1], &mixer)
   648  			}
   649  
   650  			result := BatchScalarMultiplication{{ toUpper .PointName }}(&{{.PointName}}GenAff, sampleScalars[:])
   651  
   652  			if len(result) != len(sampleScalars) {
   653  				return false
   654  			}
   655  
   656  			for i := 0; i < len(result); i++ {
   657  				var expectedJac {{ $TJacobian }}
   658  				var expected {{ $TAffine }}
   659  				var b big.Int
   660  				expectedJac.ScalarMultiplication(&{{.PointName}}Gen, sampleScalars[i].BigInt(&b))
   661  				expected.FromJacobian(&expectedJac)
   662  				if !result[i].Equal(&expected) {
   663  					return false
   664  				}
   665  			}
   666  			return true
   667  		},
   668  		genScalar,
   669  	))
   670  
   671  	properties.TestingRun(t, gopter.ConsoleReporter(false))
   672  }
   673  
   674  // ------------------------------------------------------------
   675  // benches
   676  
   677  func Benchmark{{ $TJacobian }}IsInSubGroup(b *testing.B) {
   678  	var a {{ $TJacobian }}
   679  	a.Set(&{{.PointName}}Gen)
   680  	b.ResetTimer()
   681  	for i := 0; i < b.N; i++ {
   682  		a.IsInSubGroup()
   683  	}
   684  
   685  }
   686  
   687  func Benchmark{{ $TJacobian }}Equal(b *testing.B) {
   688  	var scalar {{ .CoordType}}
   689  	if _, err := scalar.SetRandom(); err != nil {
   690  		b.Fatalf("failed to set scalar: %s", err)
   691  	}
   692  
   693  	var a {{ $TJacobian }}
   694  	a.ScalarMultiplication(&{{.PointName}}Gen, big.NewInt(42))
   695  
   696  	b.Run("equal", func(b *testing.B) {
   697  		var scalarSquared {{ .CoordType}}
   698  		scalarSquared.Square(&scalar)
   699  
   700  		aZScaled := a
   701  		aZScaled.X.Mul(&aZScaled.X, &scalarSquared)
   702  		aZScaled.Y.Mul(&aZScaled.Y, &scalarSquared).Mul(&aZScaled.Y, &scalar)
   703  		aZScaled.Z.Mul(&aZScaled.Z, &scalar)
   704  
   705  		// Check the setup.
   706  		if !a.Equal(&aZScaled) {
   707  			b.Fatalf("invalid test setup")
   708  		}
   709  
   710  		b.ResetTimer()
   711  		for i := 0; i < b.N; i++ {
   712  			a.Equal(&aZScaled)
   713  		}
   714  	})
   715  
   716  	b.Run("not equal", func(b *testing.B) {
   717  		var aPlus1 {{ $TJacobian }}
   718  		aPlus1.AddAssign(&{{.PointName}}Gen)
   719  
   720  		// Check the setup.
   721  		if a.Equal(&aPlus1) {
   722  			b.Fatalf("invalid test setup")
   723  		}
   724  
   725  		b.ResetTimer()
   726  		for i := 0; i < b.N; i++ {
   727  			a.Equal(&aPlus1)
   728  		}
   729  	})
   730  }
   731  
   732  func BenchmarkBatchAdd{{ $TAffine }}(b *testing.B) {
   733  
   734  	var P, R p{{$TAffine}}C{{$c}}
   735  	var RR pp{{$TAffine}}C{{$c}}
   736  	ridx := make([]int, len(P))
   737  
   738  	// TODO P == R may produce skewed benches
   739  	fillBenchBases{{ toUpper $.PointName }}(P[:])
   740  	fillBenchBases{{ toUpper $.PointName }}(R[:])
   741  
   742  	for i:=0; i < len(ridx);i++ {
   743  		ridx[i] = i
   744  	}
   745  
   746  	// random permute
   747  	rand.Shuffle(len(ridx), func(i, j int) { ridx[i], ridx[j] = ridx[j], ridx[i] })
   748  
   749  	for i, ri := range ridx {
   750  		RR[i] = &R[ri]
   751  	}
   752  
   753  	b.ResetTimer()
   754  	for i := 0; i < b.N; i++ {
   755  		batchAdd{{ $TAffine }}[p{{$TAffine}}C{{$c}}, pp{{$TAffine}}C{{$c}}, c{{$TAffine}}C{{$c}}](&RR, &P, len(P))
   756  	}
   757  }
   758  
   759  func Benchmark{{ $TAffine }}BatchScalarMultiplication(b *testing.B) {
   760  	// ensure every words of the scalars are filled
   761  	var mixer fr.Element
   762  	mixer.SetString("7716837800905789770901243404444209691916730933998574719964609384059111546487")
   763  
   764  	const pow = 15
   765  	const nbSamples = 1 << pow
   766  
   767  	var sampleScalars [nbSamples]fr.Element
   768  
   769  	for i := 1; i <= nbSamples; i++ {
   770  		sampleScalars[i-1].SetUint64(uint64(i)).
   771  			Mul(&sampleScalars[i-1], &mixer)
   772  	}
   773  
   774  	for i := 5; i <= pow; i++ {
   775  		using := 1 << i
   776  
   777  		b.Run(fmt.Sprintf("%d points", using), func(b *testing.B) {
   778  			b.ResetTimer()
   779  			for j := 0; j < b.N; j++ {
   780  				_ = BatchScalarMultiplication{{ toUpper .PointName }}(&{{.PointName}}GenAff, sampleScalars[:using])
   781  			}
   782  		})
   783  	}
   784  }
   785  
   786  func Benchmark{{ $TJacobian }}ScalarMultiplication(b *testing.B) {
   787  
   788  	var scalar big.Int
   789  	r := fr.Modulus()
   790  	scalar.SetString("5243587517512619047944770508185965837690552500527637822603658699938581184513", 10)
   791  	scalar.Add(&scalar, r)
   792  
   793  	var doubleAndAdd {{ $TJacobian }}
   794  
   795  	b.Run("double and add", func(b *testing.B) {
   796  		b.ResetTimer()
   797  		for j := 0; j < b.N; j++ {
   798  			doubleAndAdd.mulWindowed(&{{.PointName}}Gen, &scalar)
   799  		}
   800  	})
   801  
   802      {{if .GLV}}
   803  	var glv {{ $TJacobian }}
   804  	b.Run("GLV", func(b *testing.B) {
   805  		b.ResetTimer()
   806  		for j := 0; j < b.N; j++ {
   807  			glv.mulGLV(&{{.PointName}}Gen, &scalar)
   808  		}
   809  	})
   810      {{end}}
   811  
   812  }
   813  
   814  
   815  {{if .CofactorCleaning}}
   816  func Benchmark{{ $TAffine }}CofactorClearing(b *testing.B) {
   817  	var a {{ $TJacobian }}
   818  	a.Set(&{{ toLower .PointName }}Gen)
   819  	for i := 0; i < b.N; i++ {
   820  		a.ClearCofactor(&a)
   821  	}
   822  }
   823  {{end}}
   824  
   825  func Benchmark{{ $TJacobian }}Add(b *testing.B) {
   826  	var a {{ $TJacobian }}
   827  	a.Double(&{{.PointName}}Gen)
   828  	b.ResetTimer()
   829  	for i := 0; i < b.N; i++ {
   830  		a.AddAssign(&{{.PointName}}Gen)
   831  	}
   832  }
   833  
   834  func Benchmark{{ $TJacobian }}AddMixed(b *testing.B) {
   835  	var a {{ $TJacobian }}
   836  	a.Double(&{{.PointName}}Gen)
   837  
   838  	var c {{ $TAffine }}
   839  	c.FromJacobian(&{{.PointName}}Gen)
   840  	b.ResetTimer()
   841  	for i := 0; i < b.N; i++ {
   842  		a.AddMixed(&c)
   843  	}
   844  
   845  }
   846  
   847  func Benchmark{{ $TJacobian }}Double(b *testing.B) {
   848  	var a {{ $TJacobian }}
   849  	a.Set(&{{.PointName}}Gen)
   850  	b.ResetTimer()
   851  	for i := 0; i < b.N; i++ {
   852  		a.DoubleAssign()
   853  	}
   854  
   855  }
   856  
   857  func Benchmark{{ toUpper .PointName}}JacExtAddMixed(b *testing.B) {
   858  	var a {{ $TJacobianExtended }}
   859  	a.doubleMixed(&{{.PointName}}GenAff)
   860  
   861  	var c {{ $TAffine }}
   862  	c.FromJacobian(&{{.PointName}}Gen)
   863  	b.ResetTimer()
   864  	for i := 0; i < b.N; i++ {
   865  		a.addMixed(&c)
   866  	}
   867  }
   868  
   869  func Benchmark{{ toUpper .PointName}}JacExtSubMixed(b *testing.B) {
   870  	var a {{ $TJacobianExtended }}
   871  	a.doubleMixed(&{{.PointName}}GenAff)
   872  
   873  	var c {{ $TAffine }}
   874  	c.FromJacobian(&{{.PointName}}Gen)
   875  	b.ResetTimer()
   876  	for i := 0; i < b.N; i++ {
   877  		a.subMixed(&c)
   878  	}
   879  }
   880  
   881  func Benchmark{{ toUpper .PointName}}JacExtDoubleMixed(b *testing.B) {
   882  	var a {{ $TJacobianExtended }}
   883  	a.doubleMixed(&{{.PointName}}GenAff)
   884  
   885  	var c {{ $TAffine }}
   886  	c.FromJacobian(&{{.PointName}}Gen)
   887  	b.ResetTimer()
   888  	for i := 0; i < b.N; i++ {
   889  		a.doubleMixed(&c)
   890  	}
   891  }
   892  
   893  func Benchmark{{ toUpper .PointName}}JacExtDoubleNegMixed(b *testing.B) {
   894  	var a {{ $TJacobianExtended }}
   895  	a.doubleMixed(&{{.PointName}}GenAff)
   896  
   897  	var c {{ $TAffine }}
   898  	c.FromJacobian(&{{.PointName}}Gen)
   899  	b.ResetTimer()
   900  	for i := 0; i < b.N; i++ {
   901  		a.doubleNegMixed(&c)
   902  	}
   903  }
   904  
   905  func Benchmark{{ toUpper .PointName}}JacExtAdd(b *testing.B) {
   906  	var a, c {{ $TJacobianExtended }}
   907  	a.doubleMixed(&{{.PointName}}GenAff)
   908      c.double(&a)
   909  
   910  	b.ResetTimer()
   911  	for i := 0; i < b.N; i++ {
   912  		a.add(&c)
   913  	}
   914  }
   915  
   916  func Benchmark{{ toUpper .PointName}}JacExtDouble(b *testing.B) {
   917  	var a {{ $TJacobianExtended }}
   918  	a.doubleMixed(&{{.PointName}}GenAff)
   919  
   920  	b.ResetTimer()
   921  	for i := 0; i < b.N; i++ {
   922  		a.double(&a)
   923  	}
   924  }
   925  
   926  func Benchmark{{ toUpper .PointName}}AffineAdd(b *testing.B) {
   927  	var a {{ $TAffine }}
   928  	a.Double(&{{.PointName}}GenAff)
   929  	b.ResetTimer()
   930  	for i := 0; i < b.N; i++ {
   931  		a.Add(&a, &{{.PointName}}GenAff)
   932  	}
   933  }
   934  
   935  func Benchmark{{ toUpper .PointName}}AffineDouble(b *testing.B) {
   936  	var a {{ $TAffine }}
   937  	a.Double(&{{.PointName}}GenAff)
   938  	b.ResetTimer()
   939  	for i := 0; i < b.N; i++ {
   940  		a.Double(&a)
   941  	}
   942  }
   943  
   944  func fuzz{{ $TJacobian }}(p *{{ $TJacobian }}, f {{ .CoordType}}) {{ $TJacobian }} {
   945  	var res {{ $TJacobian }}
   946  	res.X.Mul(&p.X, &f).Mul(&res.X, &f)
   947  	res.Y.Mul(&p.Y, &f).Mul(&res.Y, &f).Mul(&res.Y, &f)
   948  	res.Z.Mul(&p.Z, &f)
   949  	return res
   950  }
   951  
   952  func fuzz{{ $TJacobianExtended }}(p *{{ $TJacobianExtended }}, f {{ .CoordType}}) {{ $TJacobianExtended }} {
   953  	var res {{ $TJacobianExtended }}
   954  	var ff, fff {{ .CoordType}}
   955  	ff.Square(&f)
   956  	fff.Mul(&ff, &f)
   957  	res.X.Mul(&p.X, &ff)
   958  	res.Y.Mul(&p.Y, &fff)
   959  	res.ZZ.Mul(&p.ZZ, &ff)
   960  	res.ZZZ.Mul(&p.ZZZ, &fff)
   961  	return res
   962  }
   963  
   964  {{- if eq .Name "secp256k1"}}
   965  const (
   966         nbFuzzShort = 10
   967         nbFuzz      = 100
   968  
   969        )
   970  
   971  // define Gopters generators
   972  
   973  // GenFr generates an Fr element
   974  func GenFr() gopter.Gen {
   975  	return func(genParams *gopter.GenParameters) *gopter.GenResult {
   976  		var elmt fr.Element
   977  
   978  		if _, err := elmt.SetRandom(); err != nil {
   979  			panic(err)
   980  		}
   981  
   982  		return gopter.NewGenResult(elmt, gopter.NoShrinker)
   983  	}
   984  }
   985  
   986  // GenFp generates an Fp element
   987  func GenFp() gopter.Gen {
   988  	return func(genParams *gopter.GenParameters) *gopter.GenResult {
   989  		var elmt fp.Element
   990  
   991  		if _, err := elmt.SetRandom(); err != nil {
   992  			panic(err)
   993  		}
   994  
   995  		return gopter.NewGenResult(elmt, gopter.NoShrinker)
   996  	}
   997  }
   998  
   999  // GenBigInt generates a big.Int
  1000  func GenBigInt() gopter.Gen {
  1001        return func(genParams *gopter.GenParameters) *gopter.GenResult {
  1002             var s big.Int
  1003             var b [fp.Bytes]byte
  1004             _, err := crand.Read(b[:]) //#nosec G404 weak rng is fine here
  1005             if err != nil {
  1006                 panic(err)
  1007             }
  1008             s.SetBytes(b[:])
  1009             genResult := gopter.NewGenResult(s, gopter.NoShrinker)
  1010             return genResult
  1011      }
  1012  }
  1013  {{- end}}