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}}