From 647bcf53ae9bf75d0f96ba720222373fd638d79d Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 5 Sep 2024 15:08:24 -0400 Subject: [PATCH 1/2] refactor: kill bls12-378 and bw6-756 curves --- ecc/bls12-378/bls12-378.go | 153 - ecc/bls12-378/ecdsa/doc.go | 28 - ecc/bls12-378/ecdsa/ecdsa.go | 305 -- ecc/bls12-378/ecdsa/ecdsa_test.go | 166 - ecc/bls12-378/ecdsa/marshal.go | 136 - ecc/bls12-378/ecdsa/marshal_test.go | 64 - ecc/bls12-378/fp/arith.go | 73 - ecc/bls12-378/fp/asm.go | 27 - ecc/bls12-378/fp/asm_noadx.go | 28 - ecc/bls12-378/fp/doc.go | 53 - ecc/bls12-378/fp/element.go | 1848 ---------- ecc/bls12-378/fp/element_exp.go | 1040 ------ ecc/bls12-378/fp/element_mul_amd64.s | 857 ----- ecc/bls12-378/fp/element_ops_amd64.go | 107 - ecc/bls12-378/fp/element_ops_amd64.s | 306 -- ecc/bls12-378/fp/element_ops_purego.go | 745 ----- ecc/bls12-378/fp/element_test.go | 2909 ---------------- ecc/bls12-378/fp/hash_to_field/doc.go | 32 - .../fp/hash_to_field/hash_to_field.go | 66 - .../fp/hash_to_field/hash_to_field_test.go | 41 - ecc/bls12-378/fp/vector.go | 255 -- ecc/bls12-378/fp/vector_test.go | 90 - ecc/bls12-378/fr/arith.go | 73 - ecc/bls12-378/fr/asm.go | 27 - ecc/bls12-378/fr/asm_noadx.go | 28 - ecc/bls12-378/fr/doc.go | 53 - ecc/bls12-378/fr/element.go | 1620 --------- ecc/bls12-378/fr/element_exp.go | 642 ---- ecc/bls12-378/fr/element_mul_amd64.s | 487 --- ecc/bls12-378/fr/element_ops_amd64.go | 107 - ecc/bls12-378/fr/element_ops_amd64.s | 230 -- ecc/bls12-378/fr/element_ops_purego.go | 443 --- ecc/bls12-378/fr/element_test.go | 2889 ---------------- ecc/bls12-378/fr/fft/bitreverse.go | 574 ---- ecc/bls12-378/fr/fft/bitreverse_test.go | 113 - ecc/bls12-378/fr/fft/doc.go | 19 - ecc/bls12-378/fr/fft/domain.go | 292 -- ecc/bls12-378/fr/fft/domain_test.go | 47 - ecc/bls12-378/fr/fft/fft.go | 421 --- ecc/bls12-378/fr/fft/fft_test.go | 329 -- ecc/bls12-378/fr/fft/options.go | 103 - ecc/bls12-378/fr/fri/doc.go | 18 - ecc/bls12-378/fr/fri/fri.go | 698 ---- ecc/bls12-378/fr/fri/fri_test.go | 240 -- ecc/bls12-378/fr/generator.go | 47 - ecc/bls12-378/fr/gkr/gkr.go | 944 ------ ecc/bls12-378/fr/gkr/gkr_test.go | 750 ----- ecc/bls12-378/fr/hash_to_field/doc.go | 32 - .../fr/hash_to_field/hash_to_field.go | 66 - .../fr/hash_to_field/hash_to_field_test.go | 41 - ecc/bls12-378/fr/iop/doc.go | 19 - ecc/bls12-378/fr/iop/expressions.go | 85 - ecc/bls12-378/fr/iop/expressions_test.go | 82 - ecc/bls12-378/fr/iop/polynomial.go | 462 --- ecc/bls12-378/fr/iop/polynomial_test.go | 729 ---- ecc/bls12-378/fr/iop/quotient.go | 91 - ecc/bls12-378/fr/iop/quotient_test.go | 143 - ecc/bls12-378/fr/iop/ratios.go | 372 --- ecc/bls12-378/fr/iop/ratios_test.go | 326 -- ecc/bls12-378/fr/iop/utils.go | 79 - ecc/bls12-378/fr/mimc/doc.go | 60 - ecc/bls12-378/fr/mimc/mimc.go | 204 -- ecc/bls12-378/fr/mimc/options.go | 50 - ecc/bls12-378/fr/pedersen/doc.go | 31 - ecc/bls12-378/fr/pedersen/example_test.go | 221 -- ecc/bls12-378/fr/pedersen/pedersen.go | 361 -- ecc/bls12-378/fr/pedersen/pedersen_test.go | 238 -- ecc/bls12-378/fr/permutation/doc.go | 18 - ecc/bls12-378/fr/permutation/permutation.go | 379 --- .../fr/permutation/permutation_test.go | 94 - ecc/bls12-378/fr/plookup/doc.go | 18 - ecc/bls12-378/fr/plookup/plookup_test.go | 140 - ecc/bls12-378/fr/plookup/table.go | 252 -- ecc/bls12-378/fr/plookup/vector.go | 717 ---- ecc/bls12-378/fr/polynomial/doc.go | 18 - ecc/bls12-378/fr/polynomial/multilin.go | 291 -- ecc/bls12-378/fr/polynomial/multilin_test.go | 139 - ecc/bls12-378/fr/polynomial/polynomial.go | 220 -- .../fr/polynomial/polynomial_test.go | 218 -- ecc/bls12-378/fr/polynomial/pool.go | 201 -- ecc/bls12-378/fr/sumcheck/sumcheck.go | 181 - ecc/bls12-378/fr/sumcheck/sumcheck_test.go | 161 - .../fr/test_vector_utils/test_vector_utils.go | 227 -- ecc/bls12-378/fr/vector.go | 253 -- ecc/bls12-378/fr/vector_test.go | 90 - ecc/bls12-378/g1.go | 1207 ------- ecc/bls12-378/g1_test.go | 885 ----- ecc/bls12-378/g2.go | 1150 ------- ecc/bls12-378/g2_test.go | 874 ----- ecc/bls12-378/hash_to_g1.go | 331 -- ecc/bls12-378/hash_to_g1_test.go | 306 -- ecc/bls12-378/hash_to_g2.go | 140 - ecc/bls12-378/internal/fptower/asm.go | 28 - ecc/bls12-378/internal/fptower/asm_noadx.go | 25 - ecc/bls12-378/internal/fptower/e12.go | 866 ----- ecc/bls12-378/internal/fptower/e12_pairing.go | 187 -- ecc/bls12-378/internal/fptower/e12_test.go | 569 ---- ecc/bls12-378/internal/fptower/e2.go | 298 -- ecc/bls12-378/internal/fptower/e2_amd64.go | 29 - ecc/bls12-378/internal/fptower/e2_amd64.s | 320 -- ecc/bls12-378/internal/fptower/e2_bls378.go | 104 - ecc/bls12-378/internal/fptower/e2_fallback.go | 40 - ecc/bls12-378/internal/fptower/e2_test.go | 531 --- ecc/bls12-378/internal/fptower/e6.go | 343 -- ecc/bls12-378/internal/fptower/e6_test.go | 363 -- ecc/bls12-378/internal/fptower/frobenius.go | 217 -- .../internal/fptower/generators_test.go | 50 - ecc/bls12-378/internal/fptower/parameters.go | 33 - ecc/bls12-378/kzg/doc.go | 18 - ecc/bls12-378/kzg/kzg.go | 593 ---- ecc/bls12-378/kzg/kzg_test.go | 758 ----- ecc/bls12-378/kzg/marshal.go | 291 -- ecc/bls12-378/kzg/utils.go | 182 - ecc/bls12-378/marshal.go | 1297 ------- ecc/bls12-378/marshal_test.go | 530 --- ecc/bls12-378/multiexp.go | 867 ----- ecc/bls12-378/multiexp_affine.go | 710 ---- ecc/bls12-378/multiexp_jacobian.go | 199 -- ecc/bls12-378/multiexp_test.go | 863 ----- ecc/bls12-378/pairing.go | 535 --- ecc/bls12-378/pairing_test.go | 504 --- ecc/bls12-378/twistededwards/curve.go | 67 - ecc/bls12-378/twistededwards/doc.go | 18 - ecc/bls12-378/twistededwards/eddsa/doc.go | 22 - ecc/bls12-378/twistededwards/eddsa/eddsa.go | 257 -- .../twistededwards/eddsa/eddsa_test.go | 291 -- ecc/bls12-378/twistededwards/eddsa/marshal.go | 177 - ecc/bls12-378/twistededwards/point.go | 673 ---- ecc/bls12-378/twistededwards/point_test.go | 969 ------ ecc/bw6-633/g1.go | 1 - ecc/bw6-633/g2.go | 1 - ecc/bw6-756/bw6-756.go | 134 - ecc/bw6-756/ecdsa/doc.go | 28 - ecc/bw6-756/ecdsa/ecdsa.go | 305 -- ecc/bw6-756/ecdsa/ecdsa_test.go | 166 - ecc/bw6-756/ecdsa/marshal.go | 136 - ecc/bw6-756/ecdsa/marshal_test.go | 64 - ecc/bw6-756/fp/arith.go | 73 - ecc/bw6-756/fp/asm.go | 27 - ecc/bw6-756/fp/asm_noadx.go | 28 - ecc/bw6-756/fp/bw6_utils.go | 27 - ecc/bw6-756/fp/doc.go | 53 - ecc/bw6-756/fp/element.go | 2724 --------------- ecc/bw6-756/fp/element_exp.go | 1993 ----------- ecc/bw6-756/fp/element_mul_amd64.s | 2758 --------------- ecc/bw6-756/fp/element_ops_amd64.go | 107 - ecc/bw6-756/fp/element_ops_amd64.s | 502 --- ecc/bw6-756/fp/element_ops_purego.go | 2227 ------------- ecc/bw6-756/fp/element_test.go | 2969 ----------------- ecc/bw6-756/fp/hash_to_field/doc.go | 32 - ecc/bw6-756/fp/hash_to_field/hash_to_field.go | 66 - .../fp/hash_to_field/hash_to_field_test.go | 41 - ecc/bw6-756/fp/vector.go | 261 -- ecc/bw6-756/fp/vector_test.go | 90 - ecc/bw6-756/fr/arith.go | 73 - ecc/bw6-756/fr/asm.go | 27 - ecc/bw6-756/fr/asm_noadx.go | 28 - ecc/bw6-756/fr/doc.go | 53 - ecc/bw6-756/fr/element.go | 1848 ---------- ecc/bw6-756/fr/element_exp.go | 1040 ------ ecc/bw6-756/fr/element_mul_amd64.s | 857 ----- ecc/bw6-756/fr/element_ops_amd64.go | 107 - ecc/bw6-756/fr/element_ops_amd64.s | 306 -- ecc/bw6-756/fr/element_ops_purego.go | 745 ----- ecc/bw6-756/fr/element_test.go | 2909 ---------------- ecc/bw6-756/fr/fft/bitreverse.go | 574 ---- ecc/bw6-756/fr/fft/bitreverse_test.go | 113 - ecc/bw6-756/fr/fft/doc.go | 19 - ecc/bw6-756/fr/fft/domain.go | 292 -- ecc/bw6-756/fr/fft/domain_test.go | 47 - ecc/bw6-756/fr/fft/fft.go | 421 --- ecc/bw6-756/fr/fft/fft_test.go | 329 -- ecc/bw6-756/fr/fft/options.go | 103 - ecc/bw6-756/fr/fri/doc.go | 18 - ecc/bw6-756/fr/fri/fri.go | 698 ---- ecc/bw6-756/fr/fri/fri_test.go | 240 -- ecc/bw6-756/fr/generator.go | 47 - ecc/bw6-756/fr/gkr/gkr.go | 944 ------ ecc/bw6-756/fr/gkr/gkr_test.go | 750 ----- ecc/bw6-756/fr/hash_to_field/doc.go | 32 - ecc/bw6-756/fr/hash_to_field/hash_to_field.go | 66 - .../fr/hash_to_field/hash_to_field_test.go | 41 - ecc/bw6-756/fr/iop/doc.go | 19 - ecc/bw6-756/fr/iop/expressions.go | 85 - ecc/bw6-756/fr/iop/expressions_test.go | 82 - ecc/bw6-756/fr/iop/polynomial.go | 462 --- ecc/bw6-756/fr/iop/polynomial_test.go | 729 ---- ecc/bw6-756/fr/iop/quotient.go | 91 - ecc/bw6-756/fr/iop/quotient_test.go | 143 - ecc/bw6-756/fr/iop/ratios.go | 372 --- ecc/bw6-756/fr/iop/ratios_test.go | 326 -- ecc/bw6-756/fr/iop/utils.go | 79 - ecc/bw6-756/fr/mimc/doc.go | 60 - ecc/bw6-756/fr/mimc/mimc.go | 204 -- ecc/bw6-756/fr/mimc/options.go | 50 - ecc/bw6-756/fr/pedersen/doc.go | 31 - ecc/bw6-756/fr/pedersen/example_test.go | 221 -- ecc/bw6-756/fr/pedersen/pedersen.go | 361 -- ecc/bw6-756/fr/pedersen/pedersen_test.go | 238 -- ecc/bw6-756/fr/permutation/doc.go | 18 - ecc/bw6-756/fr/permutation/permutation.go | 379 --- .../fr/permutation/permutation_test.go | 94 - ecc/bw6-756/fr/plookup/doc.go | 18 - ecc/bw6-756/fr/plookup/plookup_test.go | 140 - ecc/bw6-756/fr/plookup/table.go | 252 -- ecc/bw6-756/fr/plookup/vector.go | 717 ---- ecc/bw6-756/fr/polynomial/doc.go | 18 - ecc/bw6-756/fr/polynomial/multilin.go | 291 -- ecc/bw6-756/fr/polynomial/multilin_test.go | 139 - ecc/bw6-756/fr/polynomial/polynomial.go | 220 -- ecc/bw6-756/fr/polynomial/polynomial_test.go | 218 -- ecc/bw6-756/fr/polynomial/pool.go | 201 -- ecc/bw6-756/fr/sumcheck/sumcheck.go | 181 - ecc/bw6-756/fr/sumcheck/sumcheck_test.go | 161 - .../fr/test_vector_utils/test_vector_utils.go | 227 -- ecc/bw6-756/fr/vector.go | 255 -- ecc/bw6-756/fr/vector_test.go | 90 - ecc/bw6-756/g1.go | 1232 ------- ecc/bw6-756/g1_test.go | 885 ----- ecc/bw6-756/g2.go | 1150 ------- ecc/bw6-756/g2_test.go | 855 ----- ecc/bw6-756/hash_to_g1.go | 331 -- ecc/bw6-756/hash_to_g1_test.go | 306 -- ecc/bw6-756/hash_to_g2.go | 403 --- ecc/bw6-756/hash_to_g2_test.go | 275 -- ecc/bw6-756/internal/fptower/e3.go | 348 -- ecc/bw6-756/internal/fptower/e3_test.go | 313 -- ecc/bw6-756/internal/fptower/e6.go | 773 ----- ecc/bw6-756/internal/fptower/e6_pairing.go | 242 -- ecc/bw6-756/internal/fptower/e6_test.go | 436 --- ecc/bw6-756/internal/fptower/frobenius.go | 102 - .../internal/fptower/generators_test.go | 42 - ecc/bw6-756/internal/fptower/parameters.go | 33 - ecc/bw6-756/kzg/doc.go | 18 - ecc/bw6-756/kzg/kzg.go | 593 ---- ecc/bw6-756/kzg/kzg_test.go | 758 ----- ecc/bw6-756/kzg/marshal.go | 291 -- ecc/bw6-756/kzg/utils.go | 182 - ecc/bw6-756/marshal.go | 1272 ------- ecc/bw6-756/marshal_test.go | 520 --- ecc/bw6-756/multiexp.go | 761 ----- ecc/bw6-756/multiexp_affine.go | 571 ---- ecc/bw6-756/multiexp_jacobian.go | 163 - ecc/bw6-756/multiexp_test.go | 863 ----- ecc/bw6-756/pairing.go | 583 ---- ecc/bw6-756/pairing_test.go | 505 --- ecc/bw6-756/twistededwards/curve.go | 67 - ecc/bw6-756/twistededwards/doc.go | 18 - ecc/bw6-756/twistededwards/eddsa/doc.go | 22 - ecc/bw6-756/twistededwards/eddsa/eddsa.go | 266 -- .../twistededwards/eddsa/eddsa_test.go | 291 -- ecc/bw6-756/twistededwards/eddsa/marshal.go | 177 - ecc/bw6-756/twistededwards/point.go | 673 ---- ecc/bw6-756/twistededwards/point_test.go | 969 ------ internal/generator/config/bls12-378.go | 74 - internal/generator/config/bw6-756.go | 177 - .../crypto/hash/mimc/template/mimc.go.tmpl | 4 +- internal/generator/ecc/template/point.go.tmpl | 76 +- .../ecc/template/tests/marshal.go.tmpl | 2 +- .../ecc/template/tests/point.go.tmpl | 2 +- .../edwards/eddsa/template/eddsa.go.tmpl | 6 +- .../generator/edwards/template/point.go.tmpl | 8 - .../generator/fft/template/domain.go.tmpl | 12 +- .../fft/template/fr.generator.go.tmpl | 10 +- .../generator/fft/template/imports.go.tmpl | 12 +- .../generator/kzg/template/marshal.go.tmpl | 14 +- .../pairing/template/tests/pairing.go.tmpl | 4 +- internal/generator/tower/generate.go | 2 +- 268 files changed, 29 insertions(+), 98924 deletions(-) delete mode 100644 ecc/bls12-378/bls12-378.go delete mode 100644 ecc/bls12-378/ecdsa/doc.go delete mode 100644 ecc/bls12-378/ecdsa/ecdsa.go delete mode 100644 ecc/bls12-378/ecdsa/ecdsa_test.go delete mode 100644 ecc/bls12-378/ecdsa/marshal.go delete mode 100644 ecc/bls12-378/ecdsa/marshal_test.go delete mode 100644 ecc/bls12-378/fp/arith.go delete mode 100644 ecc/bls12-378/fp/asm.go delete mode 100644 ecc/bls12-378/fp/asm_noadx.go delete mode 100644 ecc/bls12-378/fp/doc.go delete mode 100644 ecc/bls12-378/fp/element.go delete mode 100644 ecc/bls12-378/fp/element_exp.go delete mode 100644 ecc/bls12-378/fp/element_mul_amd64.s delete mode 100644 ecc/bls12-378/fp/element_ops_amd64.go delete mode 100644 ecc/bls12-378/fp/element_ops_amd64.s delete mode 100644 ecc/bls12-378/fp/element_ops_purego.go delete mode 100644 ecc/bls12-378/fp/element_test.go delete mode 100644 ecc/bls12-378/fp/hash_to_field/doc.go delete mode 100644 ecc/bls12-378/fp/hash_to_field/hash_to_field.go delete mode 100644 ecc/bls12-378/fp/hash_to_field/hash_to_field_test.go delete mode 100644 ecc/bls12-378/fp/vector.go delete mode 100644 ecc/bls12-378/fp/vector_test.go delete mode 100644 ecc/bls12-378/fr/arith.go delete mode 100644 ecc/bls12-378/fr/asm.go delete mode 100644 ecc/bls12-378/fr/asm_noadx.go delete mode 100644 ecc/bls12-378/fr/doc.go delete mode 100644 ecc/bls12-378/fr/element.go delete mode 100644 ecc/bls12-378/fr/element_exp.go delete mode 100644 ecc/bls12-378/fr/element_mul_amd64.s delete mode 100644 ecc/bls12-378/fr/element_ops_amd64.go delete mode 100644 ecc/bls12-378/fr/element_ops_amd64.s delete mode 100644 ecc/bls12-378/fr/element_ops_purego.go delete mode 100644 ecc/bls12-378/fr/element_test.go delete mode 100644 ecc/bls12-378/fr/fft/bitreverse.go delete mode 100644 ecc/bls12-378/fr/fft/bitreverse_test.go delete mode 100644 ecc/bls12-378/fr/fft/doc.go delete mode 100644 ecc/bls12-378/fr/fft/domain.go delete mode 100644 ecc/bls12-378/fr/fft/domain_test.go delete mode 100644 ecc/bls12-378/fr/fft/fft.go delete mode 100644 ecc/bls12-378/fr/fft/fft_test.go delete mode 100644 ecc/bls12-378/fr/fft/options.go delete mode 100644 ecc/bls12-378/fr/fri/doc.go delete mode 100644 ecc/bls12-378/fr/fri/fri.go delete mode 100644 ecc/bls12-378/fr/fri/fri_test.go delete mode 100644 ecc/bls12-378/fr/generator.go delete mode 100644 ecc/bls12-378/fr/gkr/gkr.go delete mode 100644 ecc/bls12-378/fr/gkr/gkr_test.go delete mode 100644 ecc/bls12-378/fr/hash_to_field/doc.go delete mode 100644 ecc/bls12-378/fr/hash_to_field/hash_to_field.go delete mode 100644 ecc/bls12-378/fr/hash_to_field/hash_to_field_test.go delete mode 100644 ecc/bls12-378/fr/iop/doc.go delete mode 100644 ecc/bls12-378/fr/iop/expressions.go delete mode 100644 ecc/bls12-378/fr/iop/expressions_test.go delete mode 100644 ecc/bls12-378/fr/iop/polynomial.go delete mode 100644 ecc/bls12-378/fr/iop/polynomial_test.go delete mode 100644 ecc/bls12-378/fr/iop/quotient.go delete mode 100644 ecc/bls12-378/fr/iop/quotient_test.go delete mode 100644 ecc/bls12-378/fr/iop/ratios.go delete mode 100644 ecc/bls12-378/fr/iop/ratios_test.go delete mode 100644 ecc/bls12-378/fr/iop/utils.go delete mode 100644 ecc/bls12-378/fr/mimc/doc.go delete mode 100644 ecc/bls12-378/fr/mimc/mimc.go delete mode 100644 ecc/bls12-378/fr/mimc/options.go delete mode 100644 ecc/bls12-378/fr/pedersen/doc.go delete mode 100644 ecc/bls12-378/fr/pedersen/example_test.go delete mode 100644 ecc/bls12-378/fr/pedersen/pedersen.go delete mode 100644 ecc/bls12-378/fr/pedersen/pedersen_test.go delete mode 100644 ecc/bls12-378/fr/permutation/doc.go delete mode 100644 ecc/bls12-378/fr/permutation/permutation.go delete mode 100644 ecc/bls12-378/fr/permutation/permutation_test.go delete mode 100644 ecc/bls12-378/fr/plookup/doc.go delete mode 100644 ecc/bls12-378/fr/plookup/plookup_test.go delete mode 100644 ecc/bls12-378/fr/plookup/table.go delete mode 100644 ecc/bls12-378/fr/plookup/vector.go delete mode 100644 ecc/bls12-378/fr/polynomial/doc.go delete mode 100644 ecc/bls12-378/fr/polynomial/multilin.go delete mode 100644 ecc/bls12-378/fr/polynomial/multilin_test.go delete mode 100644 ecc/bls12-378/fr/polynomial/polynomial.go delete mode 100644 ecc/bls12-378/fr/polynomial/polynomial_test.go delete mode 100644 ecc/bls12-378/fr/polynomial/pool.go delete mode 100644 ecc/bls12-378/fr/sumcheck/sumcheck.go delete mode 100644 ecc/bls12-378/fr/sumcheck/sumcheck_test.go delete mode 100644 ecc/bls12-378/fr/test_vector_utils/test_vector_utils.go delete mode 100644 ecc/bls12-378/fr/vector.go delete mode 100644 ecc/bls12-378/fr/vector_test.go delete mode 100644 ecc/bls12-378/g1.go delete mode 100644 ecc/bls12-378/g1_test.go delete mode 100644 ecc/bls12-378/g2.go delete mode 100644 ecc/bls12-378/g2_test.go delete mode 100644 ecc/bls12-378/hash_to_g1.go delete mode 100644 ecc/bls12-378/hash_to_g1_test.go delete mode 100644 ecc/bls12-378/hash_to_g2.go delete mode 100644 ecc/bls12-378/internal/fptower/asm.go delete mode 100644 ecc/bls12-378/internal/fptower/asm_noadx.go delete mode 100644 ecc/bls12-378/internal/fptower/e12.go delete mode 100644 ecc/bls12-378/internal/fptower/e12_pairing.go delete mode 100644 ecc/bls12-378/internal/fptower/e12_test.go delete mode 100644 ecc/bls12-378/internal/fptower/e2.go delete mode 100644 ecc/bls12-378/internal/fptower/e2_amd64.go delete mode 100644 ecc/bls12-378/internal/fptower/e2_amd64.s delete mode 100644 ecc/bls12-378/internal/fptower/e2_bls378.go delete mode 100644 ecc/bls12-378/internal/fptower/e2_fallback.go delete mode 100644 ecc/bls12-378/internal/fptower/e2_test.go delete mode 100644 ecc/bls12-378/internal/fptower/e6.go delete mode 100644 ecc/bls12-378/internal/fptower/e6_test.go delete mode 100644 ecc/bls12-378/internal/fptower/frobenius.go delete mode 100644 ecc/bls12-378/internal/fptower/generators_test.go delete mode 100644 ecc/bls12-378/internal/fptower/parameters.go delete mode 100644 ecc/bls12-378/kzg/doc.go delete mode 100644 ecc/bls12-378/kzg/kzg.go delete mode 100644 ecc/bls12-378/kzg/kzg_test.go delete mode 100644 ecc/bls12-378/kzg/marshal.go delete mode 100644 ecc/bls12-378/kzg/utils.go delete mode 100644 ecc/bls12-378/marshal.go delete mode 100644 ecc/bls12-378/marshal_test.go delete mode 100644 ecc/bls12-378/multiexp.go delete mode 100644 ecc/bls12-378/multiexp_affine.go delete mode 100644 ecc/bls12-378/multiexp_jacobian.go delete mode 100644 ecc/bls12-378/multiexp_test.go delete mode 100644 ecc/bls12-378/pairing.go delete mode 100644 ecc/bls12-378/pairing_test.go delete mode 100644 ecc/bls12-378/twistededwards/curve.go delete mode 100644 ecc/bls12-378/twistededwards/doc.go delete mode 100644 ecc/bls12-378/twistededwards/eddsa/doc.go delete mode 100644 ecc/bls12-378/twistededwards/eddsa/eddsa.go delete mode 100644 ecc/bls12-378/twistededwards/eddsa/eddsa_test.go delete mode 100644 ecc/bls12-378/twistededwards/eddsa/marshal.go delete mode 100644 ecc/bls12-378/twistededwards/point.go delete mode 100644 ecc/bls12-378/twistededwards/point_test.go delete mode 100644 ecc/bw6-756/bw6-756.go delete mode 100644 ecc/bw6-756/ecdsa/doc.go delete mode 100644 ecc/bw6-756/ecdsa/ecdsa.go delete mode 100644 ecc/bw6-756/ecdsa/ecdsa_test.go delete mode 100644 ecc/bw6-756/ecdsa/marshal.go delete mode 100644 ecc/bw6-756/ecdsa/marshal_test.go delete mode 100644 ecc/bw6-756/fp/arith.go delete mode 100644 ecc/bw6-756/fp/asm.go delete mode 100644 ecc/bw6-756/fp/asm_noadx.go delete mode 100644 ecc/bw6-756/fp/bw6_utils.go delete mode 100644 ecc/bw6-756/fp/doc.go delete mode 100644 ecc/bw6-756/fp/element.go delete mode 100644 ecc/bw6-756/fp/element_exp.go delete mode 100644 ecc/bw6-756/fp/element_mul_amd64.s delete mode 100644 ecc/bw6-756/fp/element_ops_amd64.go delete mode 100644 ecc/bw6-756/fp/element_ops_amd64.s delete mode 100644 ecc/bw6-756/fp/element_ops_purego.go delete mode 100644 ecc/bw6-756/fp/element_test.go delete mode 100644 ecc/bw6-756/fp/hash_to_field/doc.go delete mode 100644 ecc/bw6-756/fp/hash_to_field/hash_to_field.go delete mode 100644 ecc/bw6-756/fp/hash_to_field/hash_to_field_test.go delete mode 100644 ecc/bw6-756/fp/vector.go delete mode 100644 ecc/bw6-756/fp/vector_test.go delete mode 100644 ecc/bw6-756/fr/arith.go delete mode 100644 ecc/bw6-756/fr/asm.go delete mode 100644 ecc/bw6-756/fr/asm_noadx.go delete mode 100644 ecc/bw6-756/fr/doc.go delete mode 100644 ecc/bw6-756/fr/element.go delete mode 100644 ecc/bw6-756/fr/element_exp.go delete mode 100644 ecc/bw6-756/fr/element_mul_amd64.s delete mode 100644 ecc/bw6-756/fr/element_ops_amd64.go delete mode 100644 ecc/bw6-756/fr/element_ops_amd64.s delete mode 100644 ecc/bw6-756/fr/element_ops_purego.go delete mode 100644 ecc/bw6-756/fr/element_test.go delete mode 100644 ecc/bw6-756/fr/fft/bitreverse.go delete mode 100644 ecc/bw6-756/fr/fft/bitreverse_test.go delete mode 100644 ecc/bw6-756/fr/fft/doc.go delete mode 100644 ecc/bw6-756/fr/fft/domain.go delete mode 100644 ecc/bw6-756/fr/fft/domain_test.go delete mode 100644 ecc/bw6-756/fr/fft/fft.go delete mode 100644 ecc/bw6-756/fr/fft/fft_test.go delete mode 100644 ecc/bw6-756/fr/fft/options.go delete mode 100644 ecc/bw6-756/fr/fri/doc.go delete mode 100644 ecc/bw6-756/fr/fri/fri.go delete mode 100644 ecc/bw6-756/fr/fri/fri_test.go delete mode 100644 ecc/bw6-756/fr/generator.go delete mode 100644 ecc/bw6-756/fr/gkr/gkr.go delete mode 100644 ecc/bw6-756/fr/gkr/gkr_test.go delete mode 100644 ecc/bw6-756/fr/hash_to_field/doc.go delete mode 100644 ecc/bw6-756/fr/hash_to_field/hash_to_field.go delete mode 100644 ecc/bw6-756/fr/hash_to_field/hash_to_field_test.go delete mode 100644 ecc/bw6-756/fr/iop/doc.go delete mode 100644 ecc/bw6-756/fr/iop/expressions.go delete mode 100644 ecc/bw6-756/fr/iop/expressions_test.go delete mode 100644 ecc/bw6-756/fr/iop/polynomial.go delete mode 100644 ecc/bw6-756/fr/iop/polynomial_test.go delete mode 100644 ecc/bw6-756/fr/iop/quotient.go delete mode 100644 ecc/bw6-756/fr/iop/quotient_test.go delete mode 100644 ecc/bw6-756/fr/iop/ratios.go delete mode 100644 ecc/bw6-756/fr/iop/ratios_test.go delete mode 100644 ecc/bw6-756/fr/iop/utils.go delete mode 100644 ecc/bw6-756/fr/mimc/doc.go delete mode 100644 ecc/bw6-756/fr/mimc/mimc.go delete mode 100644 ecc/bw6-756/fr/mimc/options.go delete mode 100644 ecc/bw6-756/fr/pedersen/doc.go delete mode 100644 ecc/bw6-756/fr/pedersen/example_test.go delete mode 100644 ecc/bw6-756/fr/pedersen/pedersen.go delete mode 100644 ecc/bw6-756/fr/pedersen/pedersen_test.go delete mode 100644 ecc/bw6-756/fr/permutation/doc.go delete mode 100644 ecc/bw6-756/fr/permutation/permutation.go delete mode 100644 ecc/bw6-756/fr/permutation/permutation_test.go delete mode 100644 ecc/bw6-756/fr/plookup/doc.go delete mode 100644 ecc/bw6-756/fr/plookup/plookup_test.go delete mode 100644 ecc/bw6-756/fr/plookup/table.go delete mode 100644 ecc/bw6-756/fr/plookup/vector.go delete mode 100644 ecc/bw6-756/fr/polynomial/doc.go delete mode 100644 ecc/bw6-756/fr/polynomial/multilin.go delete mode 100644 ecc/bw6-756/fr/polynomial/multilin_test.go delete mode 100644 ecc/bw6-756/fr/polynomial/polynomial.go delete mode 100644 ecc/bw6-756/fr/polynomial/polynomial_test.go delete mode 100644 ecc/bw6-756/fr/polynomial/pool.go delete mode 100644 ecc/bw6-756/fr/sumcheck/sumcheck.go delete mode 100644 ecc/bw6-756/fr/sumcheck/sumcheck_test.go delete mode 100644 ecc/bw6-756/fr/test_vector_utils/test_vector_utils.go delete mode 100644 ecc/bw6-756/fr/vector.go delete mode 100644 ecc/bw6-756/fr/vector_test.go delete mode 100644 ecc/bw6-756/g1.go delete mode 100644 ecc/bw6-756/g1_test.go delete mode 100644 ecc/bw6-756/g2.go delete mode 100644 ecc/bw6-756/g2_test.go delete mode 100644 ecc/bw6-756/hash_to_g1.go delete mode 100644 ecc/bw6-756/hash_to_g1_test.go delete mode 100644 ecc/bw6-756/hash_to_g2.go delete mode 100644 ecc/bw6-756/hash_to_g2_test.go delete mode 100644 ecc/bw6-756/internal/fptower/e3.go delete mode 100644 ecc/bw6-756/internal/fptower/e3_test.go delete mode 100644 ecc/bw6-756/internal/fptower/e6.go delete mode 100644 ecc/bw6-756/internal/fptower/e6_pairing.go delete mode 100644 ecc/bw6-756/internal/fptower/e6_test.go delete mode 100644 ecc/bw6-756/internal/fptower/frobenius.go delete mode 100644 ecc/bw6-756/internal/fptower/generators_test.go delete mode 100644 ecc/bw6-756/internal/fptower/parameters.go delete mode 100644 ecc/bw6-756/kzg/doc.go delete mode 100644 ecc/bw6-756/kzg/kzg.go delete mode 100644 ecc/bw6-756/kzg/kzg_test.go delete mode 100644 ecc/bw6-756/kzg/marshal.go delete mode 100644 ecc/bw6-756/kzg/utils.go delete mode 100644 ecc/bw6-756/marshal.go delete mode 100644 ecc/bw6-756/marshal_test.go delete mode 100644 ecc/bw6-756/multiexp.go delete mode 100644 ecc/bw6-756/multiexp_affine.go delete mode 100644 ecc/bw6-756/multiexp_jacobian.go delete mode 100644 ecc/bw6-756/multiexp_test.go delete mode 100644 ecc/bw6-756/pairing.go delete mode 100644 ecc/bw6-756/pairing_test.go delete mode 100644 ecc/bw6-756/twistededwards/curve.go delete mode 100644 ecc/bw6-756/twistededwards/doc.go delete mode 100644 ecc/bw6-756/twistededwards/eddsa/doc.go delete mode 100644 ecc/bw6-756/twistededwards/eddsa/eddsa.go delete mode 100644 ecc/bw6-756/twistededwards/eddsa/eddsa_test.go delete mode 100644 ecc/bw6-756/twistededwards/eddsa/marshal.go delete mode 100644 ecc/bw6-756/twistededwards/point.go delete mode 100644 ecc/bw6-756/twistededwards/point_test.go delete mode 100644 internal/generator/config/bls12-378.go delete mode 100644 internal/generator/config/bw6-756.go diff --git a/ecc/bls12-378/bls12-378.go b/ecc/bls12-378/bls12-378.go deleted file mode 100644 index 3d979d67e7..0000000000 --- a/ecc/bls12-378/bls12-378.go +++ /dev/null @@ -1,153 +0,0 @@ -// Package bls12378 efficient elliptic curve, pairing and hash to curve implementation for bls12-378. -// -// bls12-378: A Barreto--Lynn--Scott curve -// -// embedding degree k=12 -// seed x₀=11045256207009841153 -// 𝔽r: r=14883435066912132899950318861128167269793560281114003360875131245101026639873 (x₀⁴-x₀²+1) -// 𝔽p: p=605248206075306171733248481581800960739847691770924913753520744034740935903401304776283802348837311170974282940417 ((x₀-1)² ⋅ r(x₀)/3+x₀) -// (E/𝔽p): Y²=X³+1 -// (Eₜ/𝔽p²): Y² = X³+u (M-type twist) -// r ∣ #E(Fp) and r ∣ #Eₜ(𝔽p²) -// -// Extension fields tower: -// -// 𝔽p²[u] = 𝔽p/u²+5 -// 𝔽p⁶[v] = 𝔽p²/v³-u -// 𝔽p¹²[w] = 𝔽p⁶/w²-v -// -// optimal Ate loop size: -// -// x₀ -// -// Security: estimated 126-bit level following [https://eprint.iacr.org/2019/885.pdf] -// (r is 254 bits and p¹² is 4536 bits) -// -// # Warning -// -// This code has not been audited and is provided as-is. In particular, there is no security guarantees such as constant time implementation or side-channel attack resistance. -package bls12378 - -import ( - "math/big" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/internal/fptower" -) - -// ID bls378 ID -const ID = ecc.BLS12_378 - -// aCurveCoeff is the a coefficients of the curve Y²=X³+ax+b -var aCurveCoeff fp.Element -var bCurveCoeff fp.Element - -// bTwistCurveCoeff b coeff of the twist (defined over 𝔽p²) curve -var bTwistCurveCoeff fptower.E2 - -// generators of the r-torsion group, resp. in ker(pi-id), ker(Tr) -var g1Gen G1Jac -var g2Gen G2Jac - -var g1GenAff G1Affine -var g2GenAff G2Affine - -// point at infinity -var g1Infinity G1Jac -var g2Infinity G2Jac - -// optimal Ate loop counter -var LoopCounter [64]int8 - -// Parameters useful for the GLV scalar multiplication. The third roots define the -// endomorphisms ϕ₁ and ϕ₂ for and . lambda is such that lies above -// in the ring Z[ϕ]. More concretely it's the associated eigenvalue -// of ϕ₁ (resp ϕ₂) restricted to (resp ) -// see https://www.cosic.esat.kuleuven.be/nessie/reports/phase2/GLV.pdf -var thirdRootOneG1 fp.Element -var thirdRootOneG2 fp.Element -var lambdaGLV big.Int - -// glvBasis stores R-linearly independent vectors (a,b), (c,d) -// in ker((u,v) → u+vλ[r]), and their determinant -var glvBasis ecc.Lattice - -// ψ o π o ψ⁻¹, where ψ:E → E' is the degree 6 iso defined over 𝔽p¹² -var endo struct { - u fptower.E2 - v fptower.E2 -} - -// seed x₀ of the curve -var xGen big.Int - -// expose the tower -- github.com/consensys/gnark uses it in a gnark circuit - -// 𝔽p² -type E2 = fptower.E2 - -// 𝔽p⁶ -type E6 = fptower.E6 - -// 𝔽p¹² -type E12 = fptower.E12 - -func init() { - aCurveCoeff.SetUint64(0) - bCurveCoeff.SetUint64(1) - bTwistCurveCoeff.A1.SetUint64(1) // M-twist - - // E(3,y) * cofactor - g1Gen.X.SetString("302027100877540500544138164010696035562809807233645104772290911818386302983750063098216015456036850656714568735197") - g1Gen.Y.SetString("232851047397483214541821965369374725182070455016459237170823497053622811786333462699984177726412751508198874482530") - g1Gen.Z.SetOne() - - // E_t(1,y) * cofactor' - g2Gen.X.SetString("470810816643554779222760025249941413452299198622737082648784137654933833261310635469274149014014206108405592809732", - "317092959336227428400228502739777439718827088477410533227996105067347670094088101088421556743730925535231685964487") - g2Gen.Y.SetString("248853758964950314624408411876149087897475217517523838449839260719963153199419627931373025216041741725848318074460", - "389162134924826972299508957175841717907876177152103852864177212390074067430801162403069988146334006672491106545644") - g2Gen.Z.SetString("1", - "0") - - g1GenAff.FromJacobian(&g1Gen) - g2GenAff.FromJacobian(&g2Gen) - - // (X,Y,Z) = (1,1,0) - g1Infinity.X.SetOne() - g1Infinity.Y.SetOne() - g2Infinity.X.SetOne() - g2Infinity.Y.SetOne() - - thirdRootOneG1.SetString("164391353554439166353793911729193406645071739502673898176639736370075683438438023898983435337729") - thirdRootOneG2.Square(&thirdRootOneG1) - lambdaGLV.SetString("121997684678489422961514670190292369408", 10) //(x₀²-1) - _r := fr.Modulus() - ecc.PrecomputeLattice(_r, &lambdaGLV, &glvBasis) - - endo.u.A0.SetString("164391353554439166353793911729193406645071739502673898176639736370075683438438023898983435337730") - endo.v.A0.SetString("595603361117066405543541008735167904222384847192046901135681663787023479658010166685728902742824780272831835669219") - - // binary decomposition of x₀ little endian - LoopCounter = [64]int8{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1} - - // x₀ - xGen.SetString("11045256207009841153", 10) - -} - -// Generators return the generators of the r-torsion group, resp. in ker(pi-id), ker(Tr) -func Generators() (g1Jac G1Jac, g2Jac G2Jac, g1Aff G1Affine, g2Aff G2Affine) { - g1Aff = g1GenAff - g2Aff = g2GenAff - g1Jac = g1Gen - g2Jac = g2Gen - return -} - -// CurveCoefficients returns the a, b coefficients of the curve equation. -func CurveCoefficients() (a, b fp.Element) { - return aCurveCoeff, bCurveCoeff -} diff --git a/ecc/bls12-378/ecdsa/doc.go b/ecc/bls12-378/ecdsa/doc.go deleted file mode 100644 index d47fad5523..0000000000 --- a/ecc/bls12-378/ecdsa/doc.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package ecdsa provides ECDSA signature scheme on the bls12-378 curve. -// -// The implementation is adapted from https://pkg.go.dev/crypto/ecdsa. -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// -// Documentation: -// - Wikipedia: https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm -// - FIPS 186-4: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf -// - SEC 1, v-2: https://www.secg.org/sec1-v2.pdf -package ecdsa diff --git a/ecc/bls12-378/ecdsa/ecdsa.go b/ecc/bls12-378/ecdsa/ecdsa.go deleted file mode 100644 index 74f52145a1..0000000000 --- a/ecc/bls12-378/ecdsa/ecdsa.go +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package ecdsa - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "crypto/sha512" - "crypto/subtle" - "hash" - "io" - "math/big" - - "github.com/consensys/gnark-crypto/ecc/bls12-378" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/signature" -) - -const ( - sizeFr = fr.Bytes - sizeFrBits = fr.Bits - sizeFp = fp.Bytes - sizePublicKey = sizeFp - sizePrivateKey = sizeFr + sizePublicKey - sizeSignature = 2 * sizeFr -) - -var order = fr.Modulus() - -// PublicKey represents an ECDSA public key -type PublicKey struct { - A bls12378.G1Affine -} - -// PrivateKey represents an ECDSA private key -type PrivateKey struct { - PublicKey PublicKey - scalar [sizeFr]byte // secret scalar, in big Endian -} - -// Signature represents an ECDSA signature -type Signature struct { - R, S [sizeFr]byte -} - -var one = new(big.Int).SetInt64(1) - -// randFieldElement returns a random element of the order of the given -// curve using the procedure given in FIPS 186-4, Appendix B.5.1. -func randFieldElement(rand io.Reader) (k *big.Int, err error) { - b := make([]byte, fr.Bits/8+8) - _, err = io.ReadFull(rand, b) - if err != nil { - return - } - - k = new(big.Int).SetBytes(b) - n := new(big.Int).Sub(order, one) - k.Mod(k, n) - k.Add(k, one) - return -} - -// GenerateKey generates a public and private key pair. -func GenerateKey(rand io.Reader) (*PrivateKey, error) { - - k, err := randFieldElement(rand) - if err != nil { - return nil, err - - } - _, _, g, _ := bls12378.Generators() - - privateKey := new(PrivateKey) - k.FillBytes(privateKey.scalar[:sizeFr]) - privateKey.PublicKey.A.ScalarMultiplication(&g, k) - return privateKey, nil -} - -// HashToInt converts a hash value to an integer. Per FIPS 186-4, Section 6.4, -// we use the left-most bits of the hash to match the bit-length of the order of -// the curve. This also performs Step 5 of SEC 1, Version 2.0, Section 4.1.3. -func HashToInt(hash []byte) *big.Int { - if len(hash) > sizeFr { - hash = hash[:sizeFr] - } - ret := new(big.Int).SetBytes(hash) - excess := ret.BitLen() - sizeFrBits - if excess > 0 { - ret.Rsh(ret, uint(excess)) - } - return ret -} - -type zr struct{} - -// Read replaces the contents of dst with zeros. It is safe for concurrent use. -func (zr) Read(dst []byte) (n int, err error) { - for i := range dst { - dst[i] = 0 - } - return len(dst), nil -} - -var zeroReader = zr{} - -const ( - aesIV = "gnark-crypto IV." // must be 16 chars (equal block size) -) - -func nonce(privateKey *PrivateKey, hash []byte) (csprng *cipher.StreamReader, err error) { - // This implementation derives the nonce from an AES-CTR CSPRNG keyed by: - // - // SHA2-512(privateKey.scalar ∥ entropy ∥ hash)[:32] - // - // The CSPRNG key is indifferentiable from a random oracle as shown in - // [Coron], the AES-CTR stream is indifferentiable from a random oracle - // under standard cryptographic assumptions (see [Larsson] for examples). - // - // [Coron]: https://cs.nyu.edu/~dodis/ps/merkle.pdf - // [Larsson]: https://web.archive.org/web/20040719170906/https://www.nada.kth.se/kurser/kth/2D1441/semteo03/lecturenotes/assump.pdf - - // Get 256 bits of entropy from rand. - entropy := make([]byte, 32) - _, err = io.ReadFull(rand.Reader, entropy) - if err != nil { - return - - } - - // Initialize an SHA-512 hash context; digest... - md := sha512.New() - md.Write(privateKey.scalar[:sizeFr]) // the private key, - md.Write(entropy) // the entropy, - md.Write(hash) // and the input hash; - key := md.Sum(nil)[:32] // and compute ChopMD-256(SHA-512), - // which is an indifferentiable MAC. - - // Create an AES-CTR instance to use as a CSPRNG. - block, _ := aes.NewCipher(key) - - // Create a CSPRNG that xors a stream of zeros with - // the output of the AES-CTR instance. - csprng = &cipher.StreamReader{ - R: zeroReader, - S: cipher.NewCTR(block, []byte(aesIV)), - } - - return csprng, err -} - -// Equal compares 2 public keys -func (pub *PublicKey) Equal(x signature.PublicKey) bool { - xx, ok := x.(*PublicKey) - if !ok { - return false - } - bpk := pub.Bytes() - bxx := xx.Bytes() - return subtle.ConstantTimeCompare(bpk, bxx) == 1 -} - -// Public returns the public key associated to the private key. -func (privKey *PrivateKey) Public() signature.PublicKey { - var pub PublicKey - pub.A.Set(&privKey.PublicKey.A) - return &pub -} - -// Sign performs the ECDSA signature -// -// k ← 𝔽r (random) -// P = k ⋅ g1Gen -// r = x_P (mod order) -// s = k⁻¹ . (m + sk ⋅ r) -// signature = {r, s} -// -// SEC 1, Version 2.0, Section 4.1.3 -func (privKey *PrivateKey) Sign(message []byte, hFunc hash.Hash) ([]byte, error) { - scalar, r, s, kInv := new(big.Int), new(big.Int), new(big.Int), new(big.Int) - scalar.SetBytes(privKey.scalar[:sizeFr]) - for { - for { - csprng, err := nonce(privKey, message) - if err != nil { - return nil, err - } - k, err := randFieldElement(csprng) - if err != nil { - return nil, err - } - - var P bls12378.G1Affine - P.ScalarMultiplicationBase(k) - kInv.ModInverse(k, order) - - P.X.BigInt(r) - - r.Mod(r, order) - if r.Sign() != 0 { - break - } - } - s.Mul(r, scalar) - - var m *big.Int - if hFunc != nil { - // compute the hash of the message as an integer - dataToHash := make([]byte, len(message)) - copy(dataToHash[:], message[:]) - hFunc.Reset() - _, err := hFunc.Write(dataToHash[:]) - if err != nil { - return nil, err - } - hramBin := hFunc.Sum(nil) - m = HashToInt(hramBin) - } else { - m = HashToInt(message) - } - - s.Add(m, s). - Mul(kInv, s). - Mod(s, order) // order != 0 - if s.Sign() != 0 { - break - } - } - - var sig Signature - r.FillBytes(sig.R[:sizeFr]) - s.FillBytes(sig.S[:sizeFr]) - - return sig.Bytes(), nil -} - -// Verify validates the ECDSA signature -// -// R ?= (s⁻¹ ⋅ m ⋅ Base + s⁻¹ ⋅ R ⋅ publiKey)_x -// -// SEC 1, Version 2.0, Section 4.1.4 -func (publicKey *PublicKey) Verify(sigBin, message []byte, hFunc hash.Hash) (bool, error) { - - // Deserialize the signature - var sig Signature - if _, err := sig.SetBytes(sigBin); err != nil { - return false, err - } - - r, s := new(big.Int), new(big.Int) - r.SetBytes(sig.R[:sizeFr]) - s.SetBytes(sig.S[:sizeFr]) - - sInv := new(big.Int).ModInverse(s, order) - - var m *big.Int - if hFunc != nil { - // compute the hash of the message as an integer - dataToHash := make([]byte, len(message)) - copy(dataToHash[:], message[:]) - hFunc.Reset() - _, err := hFunc.Write(dataToHash[:]) - if err != nil { - return false, err - } - hramBin := hFunc.Sum(nil) - m = HashToInt(hramBin) - } else { - m = HashToInt(message) - } - - u1 := new(big.Int).Mul(m, sInv) - u1.Mod(u1, order) - u2 := new(big.Int).Mul(r, sInv) - u2.Mod(u2, order) - var U bls12378.G1Jac - U.JointScalarMultiplicationBase(&publicKey.A, u1, u2) - - var z big.Int - U.Z.Square(&U.Z). - Inverse(&U.Z). - Mul(&U.Z, &U.X). - BigInt(&z) - - z.Mod(&z, order) - - return z.Cmp(r) == 0, nil - -} diff --git a/ecc/bls12-378/ecdsa/ecdsa_test.go b/ecc/bls12-378/ecdsa/ecdsa_test.go deleted file mode 100644 index 218c7f17e9..0000000000 --- a/ecc/bls12-378/ecdsa/ecdsa_test.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package ecdsa - -import ( - "crypto/rand" - "crypto/sha256" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "math/big" - "testing" - - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -func TestECDSA(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - properties := gopter.NewProperties(parameters) - - properties.Property("[BLS12-378] test the signing and verification", prop.ForAll( - func() bool { - - privKey, _ := GenerateKey(rand.Reader) - publicKey := privKey.PublicKey - - msg := []byte("testing ECDSA") - hFunc := sha256.New() - sig, _ := privKey.Sign(msg, hFunc) - flag, _ := publicKey.Verify(sig, msg, hFunc) - - return flag - }, - )) - - properties.Property("[BLS12-378] test the signing and verification (pre-hashed)", prop.ForAll( - func() bool { - - privKey, _ := GenerateKey(rand.Reader) - publicKey := privKey.PublicKey - - msg := []byte("testing ECDSA") - sig, _ := privKey.Sign(msg, nil) - flag, _ := publicKey.Verify(sig, msg, nil) - - return flag - }, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestNonMalleability(t *testing.T) { - - // buffer too big - t.Run("buffer_overflow", func(t *testing.T) { - bsig := make([]byte, 2*sizeFr+1) - var sig Signature - _, err := sig.SetBytes(bsig) - if err != errWrongSize { - t.Fatal("should raise wrong size error") - } - }) - - // R overflows p_mod - t.Run("R_overflow", func(t *testing.T) { - bsig := make([]byte, 2*sizeFr) - r := big.NewInt(1) - frMod := fr.Modulus() - r.Add(r, frMod) - buf := r.Bytes() - copy(bsig, buf[:]) - - var sig Signature - _, err := sig.SetBytes(bsig) - if err != errRBiggerThanRMod { - t.Fatal("should raise error r >= r_mod") - } - }) - - // S overflows p_mod - t.Run("S_overflow", func(t *testing.T) { - bsig := make([]byte, 2*sizeFr) - r := big.NewInt(1) - frMod := fr.Modulus() - r.Add(r, frMod) - buf := r.Bytes() - copy(bsig[sizeFr:], buf[:]) - big.NewInt(1).FillBytes(bsig[:sizeFr]) - - var sig Signature - _, err := sig.SetBytes(bsig) - if err != errSBiggerThanRMod { - t.Fatal("should raise error s >= r_mod") - } - }) - -} - -func TestNoZeros(t *testing.T) { - t.Run("R=0", func(t *testing.T) { - // R is 0 - var sig Signature - big.NewInt(0).FillBytes(sig.R[:]) - big.NewInt(1).FillBytes(sig.S[:]) - bts := sig.Bytes() - var newSig Signature - _, err := newSig.SetBytes(bts) - if err != errZero { - t.Fatal("expected error for zero R") - } - }) - t.Run("S=0", func(t *testing.T) { - // S is 0 - var sig Signature - big.NewInt(1).FillBytes(sig.R[:]) - big.NewInt(0).FillBytes(sig.S[:]) - bts := sig.Bytes() - var newSig Signature - _, err := newSig.SetBytes(bts) - if err != errZero { - t.Fatal("expected error for zero S") - } - }) -} - -// ------------------------------------------------------------ -// benches - -func BenchmarkSignECDSA(b *testing.B) { - - privKey, _ := GenerateKey(rand.Reader) - - msg := []byte("benchmarking ECDSA sign()") - b.ResetTimer() - for i := 0; i < b.N; i++ { - privKey.Sign(msg, nil) - } -} - -func BenchmarkVerifyECDSA(b *testing.B) { - - privKey, _ := GenerateKey(rand.Reader) - msg := []byte("benchmarking ECDSA sign()") - sig, _ := privKey.Sign(msg, nil) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - privKey.PublicKey.Verify(sig, msg, nil) - } -} diff --git a/ecc/bls12-378/ecdsa/marshal.go b/ecc/bls12-378/ecdsa/marshal.go deleted file mode 100644 index 07c63a2e5c..0000000000 --- a/ecc/bls12-378/ecdsa/marshal.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package ecdsa - -import ( - "crypto/subtle" - "errors" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "io" - "math/big" -) - -var errWrongSize = errors.New("wrong size buffer") -var errRBiggerThanRMod = errors.New("r >= r_mod") -var errSBiggerThanRMod = errors.New("s >= r_mod") -var errZero = errors.New("zero value") - -// Bytes returns the binary representation of the public key -// follows https://tools.ietf.org/html/rfc8032#section-3.1 -// and returns a compressed representation of the point (x,y) -// -// x, y are the coordinates of the point -// on the curve as big endian integers. -// compressed representation store x with a parity bit to recompute y -func (pk *PublicKey) Bytes() []byte { - var res [sizePublicKey]byte - pkBin := pk.A.Bytes() - subtle.ConstantTimeCopy(1, res[:sizePublicKey], pkBin[:]) - return res[:] -} - -// SetBytes sets p from binary representation in buf. -// buf represents a public key as x||y where x, y are -// interpreted as big endian binary numbers corresponding -// to the coordinates of a point on the curve. -// It returns the number of bytes read from the buffer. -func (pk *PublicKey) SetBytes(buf []byte) (int, error) { - n := 0 - if len(buf) < sizePublicKey { - return n, io.ErrShortBuffer - } - if _, err := pk.A.SetBytes(buf[:sizePublicKey]); err != nil { - return 0, err - } - n += sizeFp - return n, nil -} - -// Bytes returns the binary representation of pk, -// as byte array publicKey||scalar -// where publicKey is as publicKey.Bytes(), and -// scalar is in big endian, of size sizeFr. -func (privKey *PrivateKey) Bytes() []byte { - var res [sizePrivateKey]byte - pubkBin := privKey.PublicKey.A.Bytes() - subtle.ConstantTimeCopy(1, res[:sizePublicKey], pubkBin[:]) - subtle.ConstantTimeCopy(1, res[sizePublicKey:sizePrivateKey], privKey.scalar[:]) - return res[:] -} - -// SetBytes sets pk from buf, where buf is interpreted -// as publicKey||scalar -// where publicKey is as publicKey.Bytes(), and -// scalar is in big endian, of size sizeFr. -// It returns the number byte read. -func (privKey *PrivateKey) SetBytes(buf []byte) (int, error) { - n := 0 - if len(buf) < sizePrivateKey { - return n, io.ErrShortBuffer - } - if _, err := privKey.PublicKey.A.SetBytes(buf[:sizePublicKey]); err != nil { - return 0, err - } - n += sizePublicKey - subtle.ConstantTimeCopy(1, privKey.scalar[:], buf[sizePublicKey:sizePrivateKey]) - n += sizeFr - return n, nil -} - -// Bytes returns the binary representation of sig -// as a byte array of size 2*sizeFr r||s -func (sig *Signature) Bytes() []byte { - var res [sizeSignature]byte - subtle.ConstantTimeCopy(1, res[:sizeFr], sig.R[:]) - subtle.ConstantTimeCopy(1, res[sizeFr:], sig.S[:]) - return res[:] -} - -// SetBytes sets sig from a buffer in binary. -// buf is read interpreted as r||s -// It returns the number of bytes read from buf. -func (sig *Signature) SetBytes(buf []byte) (int, error) { - n := 0 - if len(buf) != sizeSignature { - return n, errWrongSize - } - - // S, R < R_mod (to avoid malleability) - frMod := fr.Modulus() - zero := big.NewInt(0) - bufBigInt := new(big.Int) - bufBigInt.SetBytes(buf[:sizeFr]) - if bufBigInt.Cmp(zero) == 0 { - return 0, errZero - } - if bufBigInt.Cmp(frMod) != -1 { - return 0, errRBiggerThanRMod - } - bufBigInt.SetBytes(buf[sizeFr : 2*sizeFr]) - if bufBigInt.Cmp(zero) == 0 { - return 0, errZero - } - if bufBigInt.Cmp(frMod) != -1 { - return 0, errSBiggerThanRMod - } - - subtle.ConstantTimeCopy(1, sig.R[:], buf[:sizeFr]) - n += sizeFr - subtle.ConstantTimeCopy(1, sig.S[:], buf[sizeFr:2*sizeFr]) - n += sizeFr - return n, nil -} diff --git a/ecc/bls12-378/ecdsa/marshal_test.go b/ecc/bls12-378/ecdsa/marshal_test.go deleted file mode 100644 index af81dc44e3..0000000000 --- a/ecc/bls12-378/ecdsa/marshal_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package ecdsa - -import ( - "crypto/rand" - "crypto/subtle" - "testing" - - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -const ( - nbFuzzShort = 10 - nbFuzz = 100 -) - -func TestSerialization(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BLS12-378] ECDSA serialization: SetBytes(Bytes()) should stay the same", prop.ForAll( - func() bool { - privKey, _ := GenerateKey(rand.Reader) - - var end PrivateKey - buf := privKey.Bytes() - n, err := end.SetBytes(buf[:]) - if err != nil { - return false - } - if n != sizePrivateKey { - return false - } - - return end.PublicKey.Equal(&privKey.PublicKey) && subtle.ConstantTimeCompare(end.scalar[:], privKey.scalar[:]) == 1 - - }, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} diff --git a/ecc/bls12-378/fp/arith.go b/ecc/bls12-378/fp/arith.go deleted file mode 100644 index 6f281563b3..0000000000 --- a/ecc/bls12-378/fp/arith.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -import ( - "math/bits" -) - -// madd0 hi = a*b + c (discards lo bits) -func madd0(a, b, c uint64) (hi uint64) { - var carry, lo uint64 - hi, lo = bits.Mul64(a, b) - _, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - return -} - -// madd1 hi, lo = a*b + c -func madd1(a, b, c uint64) (hi uint64, lo uint64) { - var carry uint64 - hi, lo = bits.Mul64(a, b) - lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - return -} - -// madd2 hi, lo = a*b + c + d -func madd2(a, b, c, d uint64) (hi uint64, lo uint64) { - var carry uint64 - hi, lo = bits.Mul64(a, b) - c, carry = bits.Add64(c, d, 0) - hi, _ = bits.Add64(hi, 0, carry) - lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - return -} - -func madd3(a, b, c, d, e uint64) (hi uint64, lo uint64) { - var carry uint64 - hi, lo = bits.Mul64(a, b) - c, carry = bits.Add64(c, d, 0) - hi, _ = bits.Add64(hi, 0, carry) - lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, e, carry) - return -} -func max(a int, b int) int { - if a > b { - return a - } - return b -} - -func min(a int, b int) int { - if a < b { - return a - } - return b -} diff --git a/ecc/bls12-378/fp/asm.go b/ecc/bls12-378/fp/asm.go deleted file mode 100644 index 0481989ec6..0000000000 --- a/ecc/bls12-378/fp/asm.go +++ /dev/null @@ -1,27 +0,0 @@ -//go:build !noadx -// +build !noadx - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -import "golang.org/x/sys/cpu" - -var ( - supportAdx = cpu.X86.HasADX && cpu.X86.HasBMI2 - _ = supportAdx -) diff --git a/ecc/bls12-378/fp/asm_noadx.go b/ecc/bls12-378/fp/asm_noadx.go deleted file mode 100644 index 92f8cc0f42..0000000000 --- a/ecc/bls12-378/fp/asm_noadx.go +++ /dev/null @@ -1,28 +0,0 @@ -//go:build noadx -// +build noadx - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -// note: this is needed for test purposes, as dynamically changing supportAdx doesn't flag -// certain errors (like fatal error: missing stackmap) -// this ensures we test all asm path. -var ( - supportAdx = false - _ = supportAdx -) diff --git a/ecc/bls12-378/fp/doc.go b/ecc/bls12-378/fp/doc.go deleted file mode 100644 index 3068596eda..0000000000 --- a/ecc/bls12-378/fp/doc.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package fp contains field arithmetic operations for modulus = 0x3eeb04...000001. -// -// The API is similar to math/big (big.Int), but the operations are significantly faster (up to 20x for the modular multiplication on amd64, see also https://hackmd.io/@gnark/modular_multiplication) -// -// The modulus is hardcoded in all the operations. -// -// Field elements are represented as an array, and assumed to be in Montgomery form in all methods: -// -// type Element [6]uint64 -// -// # Usage -// -// Example API signature: -// -// // Mul z = x * y (mod q) -// func (z *Element) Mul(x, y *Element) *Element -// -// and can be used like so: -// -// var a, b Element -// a.SetUint64(2) -// b.SetString("984896738") -// a.Mul(a, b) -// a.Sub(a, a) -// .Add(a, b) -// .Inv(a) -// b.Exp(b, new(big.Int).SetUint64(42)) -// -// Modulus q = -// -// q[base10] = 605248206075306171733248481581800960739847691770924913753520744034740935903401304776283802348837311170974282940417 -// q[base16] = 0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce97f76a822c00009948a20000000001 -// -// # Warning -// -// This code has not been audited and is provided as-is. In particular, there is no security guarantees such as constant time implementation or side-channel attack resistance. -package fp diff --git a/ecc/bls12-378/fp/element.go b/ecc/bls12-378/fp/element.go deleted file mode 100644 index 2c6ca88863..0000000000 --- a/ecc/bls12-378/fp/element.go +++ /dev/null @@ -1,1848 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -import ( - "crypto/rand" - "encoding/binary" - "errors" - "io" - "math/big" - "math/bits" - "reflect" - "strconv" - "strings" - - "github.com/bits-and-blooms/bitset" - "github.com/consensys/gnark-crypto/field/hash" - "github.com/consensys/gnark-crypto/field/pool" -) - -// Element represents a field element stored on 6 words (uint64) -// -// Element are assumed to be in Montgomery form in all methods. -// -// Modulus q = -// -// q[base10] = 605248206075306171733248481581800960739847691770924913753520744034740935903401304776283802348837311170974282940417 -// q[base16] = 0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce97f76a822c00009948a20000000001 -// -// # Warning -// -// This code has not been audited and is provided as-is. In particular, there is no security guarantees such as constant time implementation or side-channel attack resistance. -type Element [6]uint64 - -const ( - Limbs = 6 // number of 64 bits words needed to represent a Element - Bits = 378 // number of bits needed to represent a Element - Bytes = 48 // number of bytes needed to represent a Element -) - -// Field modulus q -const ( - q0 uint64 = 11045256207009841153 - q1 uint64 = 14886639130118979584 - q2 uint64 = 10956628289047010687 - q3 uint64 = 9513184293603517222 - q4 uint64 = 6038022134869067682 - q5 uint64 = 283357621510263184 -) - -var qElement = Element{ - q0, - q1, - q2, - q3, - q4, - q5, -} - -var _modulus big.Int // q stored as big.Int - -// Modulus returns q as a big.Int -// -// q[base10] = 605248206075306171733248481581800960739847691770924913753520744034740935903401304776283802348837311170974282940417 -// q[base16] = 0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce97f76a822c00009948a20000000001 -func Modulus() *big.Int { - return new(big.Int).Set(&_modulus) -} - -// q + r'.r = 1, i.e., qInvNeg = - q⁻¹ mod r -// used for Montgomery reduction -const qInvNeg uint64 = 11045256207009841151 - -func init() { - _modulus.SetString("3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce97f76a822c00009948a20000000001", 16) -} - -// NewElement returns a new Element from a uint64 value -// -// it is equivalent to -// -// var v Element -// v.SetUint64(...) -func NewElement(v uint64) Element { - z := Element{v} - z.Mul(&z, &rSquare) - return z -} - -// SetUint64 sets z to v and returns z -func (z *Element) SetUint64(v uint64) *Element { - // sets z LSB to v (non-Montgomery form) and convert z to Montgomery form - *z = Element{v} - return z.Mul(z, &rSquare) // z.toMont() -} - -// SetInt64 sets z to v and returns z -func (z *Element) SetInt64(v int64) *Element { - - // absolute value of v - m := v >> 63 - z.SetUint64(uint64((v ^ m) - m)) - - if m != 0 { - // v is negative - z.Neg(z) - } - - return z -} - -// Set z = x and returns z -func (z *Element) Set(x *Element) *Element { - z[0] = x[0] - z[1] = x[1] - z[2] = x[2] - z[3] = x[3] - z[4] = x[4] - z[5] = x[5] - return z -} - -// SetInterface converts provided interface into Element -// returns an error if provided type is not supported -// supported types: -// -// Element -// *Element -// uint64 -// int -// string (see SetString for valid formats) -// *big.Int -// big.Int -// []byte -func (z *Element) SetInterface(i1 interface{}) (*Element, error) { - if i1 == nil { - return nil, errors.New("can't set fp.Element with ") - } - - switch c1 := i1.(type) { - case Element: - return z.Set(&c1), nil - case *Element: - if c1 == nil { - return nil, errors.New("can't set fp.Element with ") - } - return z.Set(c1), nil - case uint8: - return z.SetUint64(uint64(c1)), nil - case uint16: - return z.SetUint64(uint64(c1)), nil - case uint32: - return z.SetUint64(uint64(c1)), nil - case uint: - return z.SetUint64(uint64(c1)), nil - case uint64: - return z.SetUint64(c1), nil - case int8: - return z.SetInt64(int64(c1)), nil - case int16: - return z.SetInt64(int64(c1)), nil - case int32: - return z.SetInt64(int64(c1)), nil - case int64: - return z.SetInt64(c1), nil - case int: - return z.SetInt64(int64(c1)), nil - case string: - return z.SetString(c1) - case *big.Int: - if c1 == nil { - return nil, errors.New("can't set fp.Element with ") - } - return z.SetBigInt(c1), nil - case big.Int: - return z.SetBigInt(&c1), nil - case []byte: - return z.SetBytes(c1), nil - default: - return nil, errors.New("can't set fp.Element from type " + reflect.TypeOf(i1).String()) - } -} - -// SetZero z = 0 -func (z *Element) SetZero() *Element { - z[0] = 0 - z[1] = 0 - z[2] = 0 - z[3] = 0 - z[4] = 0 - z[5] = 0 - return z -} - -// SetOne z = 1 (in Montgomery form) -func (z *Element) SetOne() *Element { - z[0] = 1481365419032838079 - z[1] = 10045892448872562649 - z[2] = 7242180086616818316 - z[3] = 8832319421896135475 - z[4] = 13356930855120736188 - z[5] = 28498675542444634 - return z -} - -// Div z = x*y⁻¹ (mod q) -func (z *Element) Div(x, y *Element) *Element { - var yInv Element - yInv.Inverse(y) - z.Mul(x, &yInv) - return z -} - -// Equal returns z == x; constant-time -func (z *Element) Equal(x *Element) bool { - return z.NotEqual(x) == 0 -} - -// NotEqual returns 0 if and only if z == x; constant-time -func (z *Element) NotEqual(x *Element) uint64 { - return (z[5] ^ x[5]) | (z[4] ^ x[4]) | (z[3] ^ x[3]) | (z[2] ^ x[2]) | (z[1] ^ x[1]) | (z[0] ^ x[0]) -} - -// IsZero returns z == 0 -func (z *Element) IsZero() bool { - return (z[5] | z[4] | z[3] | z[2] | z[1] | z[0]) == 0 -} - -// IsOne returns z == 1 -func (z *Element) IsOne() bool { - return ((z[5] ^ 28498675542444634) | (z[4] ^ 13356930855120736188) | (z[3] ^ 8832319421896135475) | (z[2] ^ 7242180086616818316) | (z[1] ^ 10045892448872562649) | (z[0] ^ 1481365419032838079)) == 0 -} - -// IsUint64 reports whether z can be represented as an uint64. -func (z *Element) IsUint64() bool { - zz := *z - zz.fromMont() - return zz.FitsOnOneWord() -} - -// Uint64 returns the uint64 representation of x. If x cannot be represented in a uint64, the result is undefined. -func (z *Element) Uint64() uint64 { - return z.Bits()[0] -} - -// FitsOnOneWord reports whether z words (except the least significant word) are 0 -// -// It is the responsibility of the caller to convert from Montgomery to Regular form if needed. -func (z *Element) FitsOnOneWord() bool { - return (z[5] | z[4] | z[3] | z[2] | z[1]) == 0 -} - -// Cmp compares (lexicographic order) z and x and returns: -// -// -1 if z < x -// 0 if z == x -// +1 if z > x -func (z *Element) Cmp(x *Element) int { - _z := z.Bits() - _x := x.Bits() - if _z[5] > _x[5] { - return 1 - } else if _z[5] < _x[5] { - return -1 - } - if _z[4] > _x[4] { - return 1 - } else if _z[4] < _x[4] { - return -1 - } - if _z[3] > _x[3] { - return 1 - } else if _z[3] < _x[3] { - return -1 - } - if _z[2] > _x[2] { - return 1 - } else if _z[2] < _x[2] { - return -1 - } - if _z[1] > _x[1] { - return 1 - } else if _z[1] < _x[1] { - return -1 - } - if _z[0] > _x[0] { - return 1 - } else if _z[0] < _x[0] { - return -1 - } - return 0 -} - -// LexicographicallyLargest returns true if this element is strictly lexicographically -// larger than its negation, false otherwise -func (z *Element) LexicographicallyLargest() bool { - // adapted from github.com/zkcrypto/bls12_381 - // we check if the element is larger than (q-1) / 2 - // if z - (((q -1) / 2) + 1) have no underflow, then z > (q-1) / 2 - - _z := z.Bits() - - var b uint64 - _, b = bits.Sub64(_z[0], 5522628103504920577, 0) - _, b = bits.Sub64(_z[1], 16666691601914265600, b) - _, b = bits.Sub64(_z[2], 5478314144523505343, b) - _, b = bits.Sub64(_z[3], 4756592146801758611, b) - _, b = bits.Sub64(_z[4], 3019011067434533841, b) - _, b = bits.Sub64(_z[5], 141678810755131592, b) - - return b == 0 -} - -// SetRandom sets z to a uniform random value in [0, q). -// -// This might error only if reading from crypto/rand.Reader errors, -// in which case, value of z is undefined. -func (z *Element) SetRandom() (*Element, error) { - // this code is generated for all modulus - // and derived from go/src/crypto/rand/util.go - - // l is number of limbs * 8; the number of bytes needed to reconstruct 6 uint64 - const l = 48 - - // bitLen is the maximum bit length needed to encode a value < q. - const bitLen = 378 - - // k is the maximum byte length needed to encode a value < q. - const k = (bitLen + 7) / 8 - - // b is the number of bits in the most significant byte of q-1. - b := uint(bitLen % 8) - if b == 0 { - b = 8 - } - - var bytes [l]byte - - for { - // note that bytes[k:l] is always 0 - if _, err := io.ReadFull(rand.Reader, bytes[:k]); err != nil { - return nil, err - } - - // Clear unused bits in in the most significant byte to increase probability - // that the candidate is < q. - bytes[k-1] &= uint8(int(1<> 1 - z[0] = z[0]>>1 | z[1]<<63 - z[1] = z[1]>>1 | z[2]<<63 - z[2] = z[2]>>1 | z[3]<<63 - z[3] = z[3]>>1 | z[4]<<63 - z[4] = z[4]>>1 | z[5]<<63 - z[5] >>= 1 - -} - -// fromMont converts z in place (i.e. mutates) from Montgomery to regular representation -// sets and returns z = z * 1 -func (z *Element) fromMont() *Element { - fromMont(z) - return z -} - -// Add z = x + y (mod q) -func (z *Element) Add(x, y *Element) *Element { - - var carry uint64 - z[0], carry = bits.Add64(x[0], y[0], 0) - z[1], carry = bits.Add64(x[1], y[1], carry) - z[2], carry = bits.Add64(x[2], y[2], carry) - z[3], carry = bits.Add64(x[3], y[3], carry) - z[4], carry = bits.Add64(x[4], y[4], carry) - z[5], _ = bits.Add64(x[5], y[5], carry) - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } - return z -} - -// Double z = x + x (mod q), aka Lsh 1 -func (z *Element) Double(x *Element) *Element { - - var carry uint64 - z[0], carry = bits.Add64(x[0], x[0], 0) - z[1], carry = bits.Add64(x[1], x[1], carry) - z[2], carry = bits.Add64(x[2], x[2], carry) - z[3], carry = bits.Add64(x[3], x[3], carry) - z[4], carry = bits.Add64(x[4], x[4], carry) - z[5], _ = bits.Add64(x[5], x[5], carry) - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } - return z -} - -// Sub z = x - y (mod q) -func (z *Element) Sub(x, y *Element) *Element { - var b uint64 - z[0], b = bits.Sub64(x[0], y[0], 0) - z[1], b = bits.Sub64(x[1], y[1], b) - z[2], b = bits.Sub64(x[2], y[2], b) - z[3], b = bits.Sub64(x[3], y[3], b) - z[4], b = bits.Sub64(x[4], y[4], b) - z[5], b = bits.Sub64(x[5], y[5], b) - if b != 0 { - var c uint64 - z[0], c = bits.Add64(z[0], q0, 0) - z[1], c = bits.Add64(z[1], q1, c) - z[2], c = bits.Add64(z[2], q2, c) - z[3], c = bits.Add64(z[3], q3, c) - z[4], c = bits.Add64(z[4], q4, c) - z[5], _ = bits.Add64(z[5], q5, c) - } - return z -} - -// Neg z = q - x -func (z *Element) Neg(x *Element) *Element { - if x.IsZero() { - z.SetZero() - return z - } - var borrow uint64 - z[0], borrow = bits.Sub64(q0, x[0], 0) - z[1], borrow = bits.Sub64(q1, x[1], borrow) - z[2], borrow = bits.Sub64(q2, x[2], borrow) - z[3], borrow = bits.Sub64(q3, x[3], borrow) - z[4], borrow = bits.Sub64(q4, x[4], borrow) - z[5], _ = bits.Sub64(q5, x[5], borrow) - return z -} - -// Select is a constant-time conditional move. -// If c=0, z = x0. Else z = x1 -func (z *Element) Select(c int, x0 *Element, x1 *Element) *Element { - cC := uint64((int64(c) | -int64(c)) >> 63) // "canonicized" into: 0 if c=0, -1 otherwise - z[0] = x0[0] ^ cC&(x0[0]^x1[0]) - z[1] = x0[1] ^ cC&(x0[1]^x1[1]) - z[2] = x0[2] ^ cC&(x0[2]^x1[2]) - z[3] = x0[3] ^ cC&(x0[3]^x1[3]) - z[4] = x0[4] ^ cC&(x0[4]^x1[4]) - z[5] = x0[5] ^ cC&(x0[5]^x1[5]) - return z -} - -// _mulGeneric is unoptimized textbook CIOS -// it is a fallback solution on x86 when ADX instruction set is not available -// and is used for testing purposes. -func _mulGeneric(z, x, y *Element) { - - // Implements CIOS multiplication -- section 2.3.2 of Tolga Acar's thesis - // https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf - // - // The algorithm: - // - // for i=0 to N-1 - // C := 0 - // for j=0 to N-1 - // (C,t[j]) := t[j] + x[j]*y[i] + C - // (t[N+1],t[N]) := t[N] + C - // - // C := 0 - // m := t[0]*q'[0] mod D - // (C,_) := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // (C,t[N-1]) := t[N] + C - // t[N] := t[N+1] + C - // - // → N is the number of machine words needed to store the modulus q - // → D is the word size. For example, on a 64-bit architecture D is 2 64 - // → x[i], y[i], q[i] is the ith word of the numbers x,y,q - // → q'[0] is the lowest word of the number -q⁻¹ mod r. This quantity is pre-computed, as it does not depend on the inputs. - // → t is a temporary array of size N+2 - // → C, S are machine words. A pair (C,S) refers to (hi-bits, lo-bits) of a two-word number - - var t [7]uint64 - var D uint64 - var m, C uint64 - // ----------------------------------- - // First loop - - C, t[0] = bits.Mul64(y[0], x[0]) - C, t[1] = madd1(y[0], x[1], C) - C, t[2] = madd1(y[0], x[2], C) - C, t[3] = madd1(y[0], x[3], C) - C, t[4] = madd1(y[0], x[4], C) - C, t[5] = madd1(y[0], x[5], C) - - t[6], D = bits.Add64(t[6], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - - t[5], C = bits.Add64(t[6], C, 0) - t[6], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[1], x[0], t[0]) - C, t[1] = madd2(y[1], x[1], t[1], C) - C, t[2] = madd2(y[1], x[2], t[2], C) - C, t[3] = madd2(y[1], x[3], t[3], C) - C, t[4] = madd2(y[1], x[4], t[4], C) - C, t[5] = madd2(y[1], x[5], t[5], C) - - t[6], D = bits.Add64(t[6], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - - t[5], C = bits.Add64(t[6], C, 0) - t[6], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[2], x[0], t[0]) - C, t[1] = madd2(y[2], x[1], t[1], C) - C, t[2] = madd2(y[2], x[2], t[2], C) - C, t[3] = madd2(y[2], x[3], t[3], C) - C, t[4] = madd2(y[2], x[4], t[4], C) - C, t[5] = madd2(y[2], x[5], t[5], C) - - t[6], D = bits.Add64(t[6], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - - t[5], C = bits.Add64(t[6], C, 0) - t[6], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[3], x[0], t[0]) - C, t[1] = madd2(y[3], x[1], t[1], C) - C, t[2] = madd2(y[3], x[2], t[2], C) - C, t[3] = madd2(y[3], x[3], t[3], C) - C, t[4] = madd2(y[3], x[4], t[4], C) - C, t[5] = madd2(y[3], x[5], t[5], C) - - t[6], D = bits.Add64(t[6], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - - t[5], C = bits.Add64(t[6], C, 0) - t[6], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[4], x[0], t[0]) - C, t[1] = madd2(y[4], x[1], t[1], C) - C, t[2] = madd2(y[4], x[2], t[2], C) - C, t[3] = madd2(y[4], x[3], t[3], C) - C, t[4] = madd2(y[4], x[4], t[4], C) - C, t[5] = madd2(y[4], x[5], t[5], C) - - t[6], D = bits.Add64(t[6], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - - t[5], C = bits.Add64(t[6], C, 0) - t[6], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[5], x[0], t[0]) - C, t[1] = madd2(y[5], x[1], t[1], C) - C, t[2] = madd2(y[5], x[2], t[2], C) - C, t[3] = madd2(y[5], x[3], t[3], C) - C, t[4] = madd2(y[5], x[4], t[4], C) - C, t[5] = madd2(y[5], x[5], t[5], C) - - t[6], D = bits.Add64(t[6], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - - t[5], C = bits.Add64(t[6], C, 0) - t[6], _ = bits.Add64(0, D, C) - - if t[6] != 0 { - // we need to reduce, we have a result on 7 words - var b uint64 - z[0], b = bits.Sub64(t[0], q0, 0) - z[1], b = bits.Sub64(t[1], q1, b) - z[2], b = bits.Sub64(t[2], q2, b) - z[3], b = bits.Sub64(t[3], q3, b) - z[4], b = bits.Sub64(t[4], q4, b) - z[5], _ = bits.Sub64(t[5], q5, b) - return - } - - // copy t into z - z[0] = t[0] - z[1] = t[1] - z[2] = t[2] - z[3] = t[3] - z[4] = t[4] - z[5] = t[5] - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } -} - -func _fromMontGeneric(z *Element) { - // the following lines implement z = z * 1 - // with a modified CIOS montgomery multiplication - // see Mul for algorithm documentation - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - z[5] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - z[5] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - z[5] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - z[5] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - z[5] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - z[5] = C - } - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } -} - -func _reduceGeneric(z *Element) { - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } -} - -// BatchInvert returns a new slice with every element inverted. -// Uses Montgomery batch inversion trick -func BatchInvert(a []Element) []Element { - res := make([]Element, len(a)) - if len(a) == 0 { - return res - } - - zeroes := bitset.New(uint(len(a))) - accumulator := One() - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - zeroes.Set(uint(i)) - continue - } - res[i] = accumulator - accumulator.Mul(&accumulator, &a[i]) - } - - accumulator.Inverse(&accumulator) - - for i := len(a) - 1; i >= 0; i-- { - if zeroes.Test(uint(i)) { - continue - } - res[i].Mul(&res[i], &accumulator) - accumulator.Mul(&accumulator, &a[i]) - } - - return res -} - -func _butterflyGeneric(a, b *Element) { - t := *a - a.Add(a, b) - b.Sub(&t, b) -} - -// BitLen returns the minimum number of bits needed to represent z -// returns 0 if z == 0 -func (z *Element) BitLen() int { - if z[5] != 0 { - return 320 + bits.Len64(z[5]) - } - if z[4] != 0 { - return 256 + bits.Len64(z[4]) - } - if z[3] != 0 { - return 192 + bits.Len64(z[3]) - } - if z[2] != 0 { - return 128 + bits.Len64(z[2]) - } - if z[1] != 0 { - return 64 + bits.Len64(z[1]) - } - return bits.Len64(z[0]) -} - -// Hash msg to count prime field elements. -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-5.2 -func Hash(msg, dst []byte, count int) ([]Element, error) { - // 128 bits of security - // L = ceil((ceil(log2(p)) + k) / 8), where k is the security parameter = 128 - const Bytes = 1 + (Bits-1)/8 - const L = 16 + Bytes - - lenInBytes := count * L - pseudoRandomBytes, err := hash.ExpandMsgXmd(msg, dst, lenInBytes) - if err != nil { - return nil, err - } - - // get temporary big int from the pool - vv := pool.BigInt.Get() - - res := make([]Element, count) - for i := 0; i < count; i++ { - vv.SetBytes(pseudoRandomBytes[i*L : (i+1)*L]) - res[i].SetBigInt(vv) - } - - // release object into pool - pool.BigInt.Put(vv) - - return res, nil -} - -// Exp z = xᵏ (mod q) -func (z *Element) Exp(x Element, k *big.Int) *Element { - if k.IsUint64() && k.Uint64() == 0 { - return z.SetOne() - } - - e := k - if k.Sign() == -1 { - // negative k, we invert - // if k < 0: xᵏ (mod q) == (x⁻¹)ᵏ (mod q) - x.Inverse(&x) - - // we negate k in a temp big.Int since - // Int.Bit(_) of k and -k is different - e = pool.BigInt.Get() - defer pool.BigInt.Put(e) - e.Neg(k) - } - - z.Set(&x) - - for i := e.BitLen() - 2; i >= 0; i-- { - z.Square(z) - if e.Bit(i) == 1 { - z.Mul(z, &x) - } - } - - return z -} - -// rSquare where r is the Montgommery constant -// see section 2.3.2 of Tolga Acar's thesis -// https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf -var rSquare = Element{ - 13541478318970833666, - 5510290684934426267, - 8467587974331926354, - 13931463632695577534, - 3531303697457869800, - 51529254522778566, -} - -// toMont converts z to Montgomery form -// sets and returns z = z * r² -func (z *Element) toMont() *Element { - return z.Mul(z, &rSquare) -} - -// String returns the decimal representation of z as generated by -// z.Text(10). -func (z *Element) String() string { - return z.Text(10) -} - -// toBigInt returns z as a big.Int in Montgomery form -func (z *Element) toBigInt(res *big.Int) *big.Int { - var b [Bytes]byte - binary.BigEndian.PutUint64(b[40:48], z[0]) - binary.BigEndian.PutUint64(b[32:40], z[1]) - binary.BigEndian.PutUint64(b[24:32], z[2]) - binary.BigEndian.PutUint64(b[16:24], z[3]) - binary.BigEndian.PutUint64(b[8:16], z[4]) - binary.BigEndian.PutUint64(b[0:8], z[5]) - - return res.SetBytes(b[:]) -} - -// Text returns the string representation of z in the given base. -// Base must be between 2 and 36, inclusive. The result uses the -// lower-case letters 'a' to 'z' for digit values 10 to 35. -// No prefix (such as "0x") is added to the string. If z is a nil -// pointer it returns "". -// If base == 10 and -z fits in a uint16 prefix "-" is added to the string. -func (z *Element) Text(base int) string { - if base < 2 || base > 36 { - panic("invalid base") - } - if z == nil { - return "" - } - - const maxUint16 = 65535 - if base == 10 { - var zzNeg Element - zzNeg.Neg(z) - zzNeg.fromMont() - if zzNeg.FitsOnOneWord() && zzNeg[0] <= maxUint16 && zzNeg[0] != 0 { - return "-" + strconv.FormatUint(zzNeg[0], base) - } - } - zz := *z - zz.fromMont() - if zz.FitsOnOneWord() { - return strconv.FormatUint(zz[0], base) - } - vv := pool.BigInt.Get() - r := zz.toBigInt(vv).Text(base) - pool.BigInt.Put(vv) - return r -} - -// BigInt sets and return z as a *big.Int -func (z *Element) BigInt(res *big.Int) *big.Int { - _z := *z - _z.fromMont() - return _z.toBigInt(res) -} - -// ToBigIntRegular returns z as a big.Int in regular form -// -// Deprecated: use BigInt(*big.Int) instead -func (z Element) ToBigIntRegular(res *big.Int) *big.Int { - z.fromMont() - return z.toBigInt(res) -} - -// Bits provides access to z by returning its value as a little-endian [6]uint64 array. -// Bits is intended to support implementation of missing low-level Element -// functionality outside this package; it should be avoided otherwise. -func (z *Element) Bits() [6]uint64 { - _z := *z - fromMont(&_z) - return _z -} - -// Bytes returns the value of z as a big-endian byte array -func (z *Element) Bytes() (res [Bytes]byte) { - BigEndian.PutElement(&res, *z) - return -} - -// Marshal returns the value of z as a big-endian byte slice -func (z *Element) Marshal() []byte { - b := z.Bytes() - return b[:] -} - -// Unmarshal is an alias for SetBytes, it sets z to the value of e. -func (z *Element) Unmarshal(e []byte) { - z.SetBytes(e) -} - -// SetBytes interprets e as the bytes of a big-endian unsigned integer, -// sets z to that value, and returns z. -func (z *Element) SetBytes(e []byte) *Element { - if len(e) == Bytes { - // fast path - v, err := BigEndian.Element((*[Bytes]byte)(e)) - if err == nil { - *z = v - return z - } - } - - // slow path. - // get a big int from our pool - vv := pool.BigInt.Get() - vv.SetBytes(e) - - // set big int - z.SetBigInt(vv) - - // put temporary object back in pool - pool.BigInt.Put(vv) - - return z -} - -// SetBytesCanonical interprets e as the bytes of a big-endian 48-byte integer. -// If e is not a 48-byte slice or encodes a value higher than q, -// SetBytesCanonical returns an error. -func (z *Element) SetBytesCanonical(e []byte) error { - if len(e) != Bytes { - return errors.New("invalid fp.Element encoding") - } - v, err := BigEndian.Element((*[Bytes]byte)(e)) - if err != nil { - return err - } - *z = v - return nil -} - -// SetBigInt sets z to v and returns z -func (z *Element) SetBigInt(v *big.Int) *Element { - z.SetZero() - - var zero big.Int - - // fast path - c := v.Cmp(&_modulus) - if c == 0 { - // v == 0 - return z - } else if c != 1 && v.Cmp(&zero) != -1 { - // 0 < v < q - return z.setBigInt(v) - } - - // get temporary big int from the pool - vv := pool.BigInt.Get() - - // copy input + modular reduction - vv.Mod(v, &_modulus) - - // set big int byte value - z.setBigInt(vv) - - // release object into pool - pool.BigInt.Put(vv) - return z -} - -// setBigInt assumes 0 ⩽ v < q -func (z *Element) setBigInt(v *big.Int) *Element { - vBits := v.Bits() - - if bits.UintSize == 64 { - for i := 0; i < len(vBits); i++ { - z[i] = uint64(vBits[i]) - } - } else { - for i := 0; i < len(vBits); i++ { - if i%2 == 0 { - z[i/2] = uint64(vBits[i]) - } else { - z[i/2] |= uint64(vBits[i]) << 32 - } - } - } - - return z.toMont() -} - -// SetString creates a big.Int with number and calls SetBigInt on z -// -// The number prefix determines the actual base: A prefix of -// ”0b” or ”0B” selects base 2, ”0”, ”0o” or ”0O” selects base 8, -// and ”0x” or ”0X” selects base 16. Otherwise, the selected base is 10 -// and no prefix is accepted. -// -// For base 16, lower and upper case letters are considered the same: -// The letters 'a' to 'f' and 'A' to 'F' represent digit values 10 to 15. -// -// An underscore character ”_” may appear between a base -// prefix and an adjacent digit, and between successive digits; such -// underscores do not change the value of the number. -// Incorrect placement of underscores is reported as a panic if there -// are no other errors. -// -// If the number is invalid this method leaves z unchanged and returns nil, error. -func (z *Element) SetString(number string) (*Element, error) { - // get temporary big int from the pool - vv := pool.BigInt.Get() - - if _, ok := vv.SetString(number, 0); !ok { - return nil, errors.New("Element.SetString failed -> can't parse number into a big.Int " + number) - } - - z.SetBigInt(vv) - - // release object into pool - pool.BigInt.Put(vv) - - return z, nil -} - -// MarshalJSON returns json encoding of z (z.Text(10)) -// If z == nil, returns null -func (z *Element) MarshalJSON() ([]byte, error) { - if z == nil { - return []byte("null"), nil - } - const maxSafeBound = 15 // we encode it as number if it's small - s := z.Text(10) - if len(s) <= maxSafeBound { - return []byte(s), nil - } - var sbb strings.Builder - sbb.WriteByte('"') - sbb.WriteString(s) - sbb.WriteByte('"') - return []byte(sbb.String()), nil -} - -// UnmarshalJSON accepts numbers and strings as input -// See Element.SetString for valid prefixes (0x, 0b, ...) -func (z *Element) UnmarshalJSON(data []byte) error { - s := string(data) - if len(s) > Bits*3 { - return errors.New("value too large (max = Element.Bits * 3)") - } - - // we accept numbers and strings, remove leading and trailing quotes if any - if len(s) > 0 && s[0] == '"' { - s = s[1:] - } - if len(s) > 0 && s[len(s)-1] == '"' { - s = s[:len(s)-1] - } - - // get temporary big int from the pool - vv := pool.BigInt.Get() - - if _, ok := vv.SetString(s, 0); !ok { - return errors.New("can't parse into a big.Int: " + s) - } - - z.SetBigInt(vv) - - // release object into pool - pool.BigInt.Put(vv) - return nil -} - -// A ByteOrder specifies how to convert byte slices into a Element -type ByteOrder interface { - Element(*[Bytes]byte) (Element, error) - PutElement(*[Bytes]byte, Element) - String() string -} - -// BigEndian is the big-endian implementation of ByteOrder and AppendByteOrder. -var BigEndian bigEndian - -type bigEndian struct{} - -// Element interpret b is a big-endian 48-byte slice. -// If b encodes a value higher than q, Element returns error. -func (bigEndian) Element(b *[Bytes]byte) (Element, error) { - var z Element - z[0] = binary.BigEndian.Uint64((*b)[40:48]) - z[1] = binary.BigEndian.Uint64((*b)[32:40]) - z[2] = binary.BigEndian.Uint64((*b)[24:32]) - z[3] = binary.BigEndian.Uint64((*b)[16:24]) - z[4] = binary.BigEndian.Uint64((*b)[8:16]) - z[5] = binary.BigEndian.Uint64((*b)[0:8]) - - if !z.smallerThanModulus() { - return Element{}, errors.New("invalid fp.Element encoding") - } - - z.toMont() - return z, nil -} - -func (bigEndian) PutElement(b *[Bytes]byte, e Element) { - e.fromMont() - binary.BigEndian.PutUint64((*b)[40:48], e[0]) - binary.BigEndian.PutUint64((*b)[32:40], e[1]) - binary.BigEndian.PutUint64((*b)[24:32], e[2]) - binary.BigEndian.PutUint64((*b)[16:24], e[3]) - binary.BigEndian.PutUint64((*b)[8:16], e[4]) - binary.BigEndian.PutUint64((*b)[0:8], e[5]) -} - -func (bigEndian) String() string { return "BigEndian" } - -// LittleEndian is the little-endian implementation of ByteOrder and AppendByteOrder. -var LittleEndian littleEndian - -type littleEndian struct{} - -func (littleEndian) Element(b *[Bytes]byte) (Element, error) { - var z Element - z[0] = binary.LittleEndian.Uint64((*b)[0:8]) - z[1] = binary.LittleEndian.Uint64((*b)[8:16]) - z[2] = binary.LittleEndian.Uint64((*b)[16:24]) - z[3] = binary.LittleEndian.Uint64((*b)[24:32]) - z[4] = binary.LittleEndian.Uint64((*b)[32:40]) - z[5] = binary.LittleEndian.Uint64((*b)[40:48]) - - if !z.smallerThanModulus() { - return Element{}, errors.New("invalid fp.Element encoding") - } - - z.toMont() - return z, nil -} - -func (littleEndian) PutElement(b *[Bytes]byte, e Element) { - e.fromMont() - binary.LittleEndian.PutUint64((*b)[0:8], e[0]) - binary.LittleEndian.PutUint64((*b)[8:16], e[1]) - binary.LittleEndian.PutUint64((*b)[16:24], e[2]) - binary.LittleEndian.PutUint64((*b)[24:32], e[3]) - binary.LittleEndian.PutUint64((*b)[32:40], e[4]) - binary.LittleEndian.PutUint64((*b)[40:48], e[5]) -} - -func (littleEndian) String() string { return "LittleEndian" } - -// Legendre returns the Legendre symbol of z (either +1, -1, or 0.) -func (z *Element) Legendre() int { - var l Element - // z^((q-1)/2) - l.expByLegendreExp(*z) - - if l.IsZero() { - return 0 - } - - // if l == 1 - if l.IsOne() { - return 1 - } - return -1 -} - -// Sqrt z = √x (mod q) -// if the square root doesn't exist (x is not a square mod q) -// Sqrt leaves z unchanged and returns nil -func (z *Element) Sqrt(x *Element) *Element { - // q ≡ 1 (mod 4) - // see modSqrtTonelliShanks in math/big/int.go - // using https://www.maa.org/sites/default/files/pdf/upload_library/22/Polya/07468342.di020786.02p0470a.pdf - - var y, b, t, w Element - // w = x^((s-1)/2)) - w.expBySqrtExp(*x) - - // y = x^((s+1)/2)) = w * x - y.Mul(x, &w) - - // b = xˢ = w * w * x = y * x - b.Mul(&w, &y) - - // g = nonResidue ^ s - var g = Element{ - 15655215628902554004, - 15894127656167592378, - 9702012166408397168, - 12335982559306940759, - 1313802173610541430, - 81629743607937133, - } - r := uint64(41) - - // compute legendre symbol - // t = x^((q-1)/2) = r-1 squaring of xˢ - t = b - for i := uint64(0); i < r-1; i++ { - t.Square(&t) - } - if t.IsZero() { - return z.SetZero() - } - if !t.IsOne() { - // t != 1, we don't have a square root - return nil - } - for { - var m uint64 - t = b - - // for t != 1 - for !t.IsOne() { - t.Square(&t) - m++ - } - - if m == 0 { - return z.Set(&y) - } - // t = g^(2^(r-m-1)) (mod q) - ge := int(r - m - 1) - t = g - for ge > 0 { - t.Square(&t) - ge-- - } - - g.Square(&t) - y.Mul(&y, &t) - b.Mul(&b, &g) - r = m - } -} - -const ( - k = 32 // word size / 2 - signBitSelector = uint64(1) << 63 - approxLowBitsN = k - 1 - approxHighBitsN = k + 1 -) - -const ( - inversionCorrectionFactorWord0 = 851295657643717122 - inversionCorrectionFactorWord1 = 10857859049187504913 - inversionCorrectionFactorWord2 = 7148604188520083019 - inversionCorrectionFactorWord3 = 1138623559447261654 - inversionCorrectionFactorWord4 = 1203095380280779597 - inversionCorrectionFactorWord5 = 148579538565968037 - invIterationsN = 26 -) - -// Inverse z = x⁻¹ (mod q) -// -// if x == 0, sets and returns z = x -func (z *Element) Inverse(x *Element) *Element { - // Implements "Optimized Binary GCD for Modular Inversion" - // https://github.com/pornin/bingcd/blob/main/doc/bingcd.pdf - - a := *x - b := Element{ - q0, - q1, - q2, - q3, - q4, - q5, - } // b := q - - u := Element{1} - - // Update factors: we get [u; v] ← [f₀ g₀; f₁ g₁] [u; v] - // cᵢ = fᵢ + 2³¹ - 1 + 2³² * (gᵢ + 2³¹ - 1) - var c0, c1 int64 - - // Saved update factors to reduce the number of field multiplications - var pf0, pf1, pg0, pg1 int64 - - var i uint - - var v, s Element - - // Since u,v are updated every other iteration, we must make sure we terminate after evenly many iterations - // This also lets us get away with half as many updates to u,v - // To make this constant-time-ish, replace the condition with i < invIterationsN - for i = 0; i&1 == 1 || !a.IsZero(); i++ { - n := max(a.BitLen(), b.BitLen()) - aApprox, bApprox := approximate(&a, n), approximate(&b, n) - - // f₀, g₀, f₁, g₁ = 1, 0, 0, 1 - c0, c1 = updateFactorIdentityMatrixRow0, updateFactorIdentityMatrixRow1 - - for j := 0; j < approxLowBitsN; j++ { - - // -2ʲ < f₀, f₁ ≤ 2ʲ - // |f₀| + |f₁| < 2ʲ⁺¹ - - if aApprox&1 == 0 { - aApprox /= 2 - } else { - s, borrow := bits.Sub64(aApprox, bApprox, 0) - if borrow == 1 { - s = bApprox - aApprox - bApprox = aApprox - c0, c1 = c1, c0 - // invariants unchanged - } - - aApprox = s / 2 - c0 = c0 - c1 - - // Now |f₀| < 2ʲ⁺¹ ≤ 2ʲ⁺¹ (only the weaker inequality is needed, strictly speaking) - // Started with f₀ > -2ʲ and f₁ ≤ 2ʲ, so f₀ - f₁ > -2ʲ⁺¹ - // Invariants unchanged for f₁ - } - - c1 *= 2 - // -2ʲ⁺¹ < f₁ ≤ 2ʲ⁺¹ - // So now |f₀| + |f₁| < 2ʲ⁺² - } - - s = a - - var g0 int64 - // from this point on c0 aliases for f0 - c0, g0 = updateFactorsDecompose(c0) - aHi := a.linearCombNonModular(&s, c0, &b, g0) - if aHi&signBitSelector != 0 { - // if aHi < 0 - c0, g0 = -c0, -g0 - aHi = negL(&a, aHi) - } - // right-shift a by k-1 bits - a[0] = (a[0] >> approxLowBitsN) | ((a[1]) << approxHighBitsN) - a[1] = (a[1] >> approxLowBitsN) | ((a[2]) << approxHighBitsN) - a[2] = (a[2] >> approxLowBitsN) | ((a[3]) << approxHighBitsN) - a[3] = (a[3] >> approxLowBitsN) | ((a[4]) << approxHighBitsN) - a[4] = (a[4] >> approxLowBitsN) | ((a[5]) << approxHighBitsN) - a[5] = (a[5] >> approxLowBitsN) | (aHi << approxHighBitsN) - - var f1 int64 - // from this point on c1 aliases for g0 - f1, c1 = updateFactorsDecompose(c1) - bHi := b.linearCombNonModular(&s, f1, &b, c1) - if bHi&signBitSelector != 0 { - // if bHi < 0 - f1, c1 = -f1, -c1 - bHi = negL(&b, bHi) - } - // right-shift b by k-1 bits - b[0] = (b[0] >> approxLowBitsN) | ((b[1]) << approxHighBitsN) - b[1] = (b[1] >> approxLowBitsN) | ((b[2]) << approxHighBitsN) - b[2] = (b[2] >> approxLowBitsN) | ((b[3]) << approxHighBitsN) - b[3] = (b[3] >> approxLowBitsN) | ((b[4]) << approxHighBitsN) - b[4] = (b[4] >> approxLowBitsN) | ((b[5]) << approxHighBitsN) - b[5] = (b[5] >> approxLowBitsN) | (bHi << approxHighBitsN) - - if i&1 == 1 { - // Combine current update factors with previously stored ones - // [F₀, G₀; F₁, G₁] ← [f₀, g₀; f₁, g₁] [pf₀, pg₀; pf₁, pg₁], with capital letters denoting new combined values - // We get |F₀| = | f₀pf₀ + g₀pf₁ | ≤ |f₀pf₀| + |g₀pf₁| = |f₀| |pf₀| + |g₀| |pf₁| ≤ 2ᵏ⁻¹|pf₀| + 2ᵏ⁻¹|pf₁| - // = 2ᵏ⁻¹ (|pf₀| + |pf₁|) < 2ᵏ⁻¹ 2ᵏ = 2²ᵏ⁻¹ - // So |F₀| < 2²ᵏ⁻¹ meaning it fits in a 2k-bit signed register - - // c₀ aliases f₀, c₁ aliases g₁ - c0, g0, f1, c1 = c0*pf0+g0*pf1, - c0*pg0+g0*pg1, - f1*pf0+c1*pf1, - f1*pg0+c1*pg1 - - s = u - - // 0 ≤ u, v < 2²⁵⁵ - // |F₀|, |G₀| < 2⁶³ - u.linearComb(&u, c0, &v, g0) - // |F₁|, |G₁| < 2⁶³ - v.linearComb(&s, f1, &v, c1) - - } else { - // Save update factors - pf0, pg0, pf1, pg1 = c0, g0, f1, c1 - } - } - - // For every iteration that we miss, v is not being multiplied by 2ᵏ⁻² - const pSq uint64 = 1 << (2 * (k - 1)) - a = Element{pSq} - // If the function is constant-time ish, this loop will not run (no need to take it out explicitly) - for ; i < invIterationsN; i += 2 { - // could optimize further with mul by word routine or by pre-computing a table since with k=26, - // we would multiply by pSq up to 13times; - // on x86, the assembly routine outperforms generic code for mul by word - // on arm64, we may loose up to ~5% for 6 limbs - v.Mul(&v, &a) - } - - u.Set(x) // for correctness check - - z.Mul(&v, &Element{ - inversionCorrectionFactorWord0, - inversionCorrectionFactorWord1, - inversionCorrectionFactorWord2, - inversionCorrectionFactorWord3, - inversionCorrectionFactorWord4, - inversionCorrectionFactorWord5, - }) - - // correctness check - v.Mul(&u, z) - if !v.IsOne() && !u.IsZero() { - return z.inverseExp(u) - } - - return z -} - -// inverseExp computes z = x⁻¹ (mod q) = x**(q-2) (mod q) -func (z *Element) inverseExp(x Element) *Element { - // e == q-2 - e := Modulus() - e.Sub(e, big.NewInt(2)) - - z.Set(&x) - - for i := e.BitLen() - 2; i >= 0; i-- { - z.Square(z) - if e.Bit(i) == 1 { - z.Mul(z, &x) - } - } - - return z -} - -// approximate a big number x into a single 64 bit word using its uppermost and lowermost bits -// if x fits in a word as is, no approximation necessary -func approximate(x *Element, nBits int) uint64 { - - if nBits <= 64 { - return x[0] - } - - const mask = (uint64(1) << (k - 1)) - 1 // k-1 ones - lo := mask & x[0] - - hiWordIndex := (nBits - 1) / 64 - - hiWordBitsAvailable := nBits - hiWordIndex*64 - hiWordBitsUsed := min(hiWordBitsAvailable, approxHighBitsN) - - mask_ := uint64(^((1 << (hiWordBitsAvailable - hiWordBitsUsed)) - 1)) - hi := (x[hiWordIndex] & mask_) << (64 - hiWordBitsAvailable) - - mask_ = ^(1<<(approxLowBitsN+hiWordBitsUsed) - 1) - mid := (mask_ & x[hiWordIndex-1]) >> hiWordBitsUsed - - return lo | mid | hi -} - -// linearComb z = xC * x + yC * y; -// 0 ≤ x, y < 2³⁷⁸ -// |xC|, |yC| < 2⁶³ -func (z *Element) linearComb(x *Element, xC int64, y *Element, yC int64) { - // | (hi, z) | < 2 * 2⁶³ * 2³⁷⁸ = 2⁴⁴² - // therefore | hi | < 2⁵⁸ ≤ 2⁶³ - hi := z.linearCombNonModular(x, xC, y, yC) - z.montReduceSigned(z, hi) -} - -// montReduceSigned z = (xHi * r + x) * r⁻¹ using the SOS algorithm -// Requires |xHi| < 2⁶³. Most significant bit of xHi is the sign bit. -func (z *Element) montReduceSigned(x *Element, xHi uint64) { - const signBitRemover = ^signBitSelector - mustNeg := xHi&signBitSelector != 0 - // the SOS implementation requires that most significant bit is 0 - // Let X be xHi*r + x - // If X is negative we would have initially stored it as 2⁶⁴ r + X (à la 2's complement) - xHi &= signBitRemover - // with this a negative X is now represented as 2⁶³ r + X - - var t [2*Limbs - 1]uint64 - var C uint64 - - m := x[0] * qInvNeg - - C = madd0(m, q0, x[0]) - C, t[1] = madd2(m, q1, x[1], C) - C, t[2] = madd2(m, q2, x[2], C) - C, t[3] = madd2(m, q3, x[3], C) - C, t[4] = madd2(m, q4, x[4], C) - C, t[5] = madd2(m, q5, x[5], C) - - // m * qElement[5] ≤ (2⁶⁴ - 1) * (2⁶³ - 1) = 2¹²⁷ - 2⁶⁴ - 2⁶³ + 1 - // x[5] + C ≤ 2*(2⁶⁴ - 1) = 2⁶⁵ - 2 - // On LHS, (C, t[5]) ≤ 2¹²⁷ - 2⁶⁴ - 2⁶³ + 1 + 2⁶⁵ - 2 = 2¹²⁷ + 2⁶³ - 1 - // So on LHS, C ≤ 2⁶³ - t[6] = xHi + C - // xHi + C < 2⁶³ + 2⁶³ = 2⁶⁴ - - // - { - const i = 1 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - - t[i+Limbs] += C - } - { - const i = 2 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - - t[i+Limbs] += C - } - { - const i = 3 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - - t[i+Limbs] += C - } - { - const i = 4 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - - t[i+Limbs] += C - } - { - const i = 5 - m := t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, z[0] = madd2(m, q1, t[i+1], C) - C, z[1] = madd2(m, q2, t[i+2], C) - C, z[2] = madd2(m, q3, t[i+3], C) - C, z[3] = madd2(m, q4, t[i+4], C) - z[5], z[4] = madd2(m, q5, t[i+5], C) - } - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } - // - - if mustNeg { - // We have computed ( 2⁶³ r + X ) r⁻¹ = 2⁶³ + X r⁻¹ instead - var b uint64 - z[0], b = bits.Sub64(z[0], signBitSelector, 0) - z[1], b = bits.Sub64(z[1], 0, b) - z[2], b = bits.Sub64(z[2], 0, b) - z[3], b = bits.Sub64(z[3], 0, b) - z[4], b = bits.Sub64(z[4], 0, b) - z[5], b = bits.Sub64(z[5], 0, b) - - // Occurs iff x == 0 && xHi < 0, i.e. X = rX' for -2⁶³ ≤ X' < 0 - - if b != 0 { - // z[5] = -1 - // negative: add q - const neg1 = 0xFFFFFFFFFFFFFFFF - - var carry uint64 - - z[0], carry = bits.Add64(z[0], q0, 0) - z[1], carry = bits.Add64(z[1], q1, carry) - z[2], carry = bits.Add64(z[2], q2, carry) - z[3], carry = bits.Add64(z[3], q3, carry) - z[4], carry = bits.Add64(z[4], q4, carry) - z[5], _ = bits.Add64(neg1, q5, carry) - } - } -} - -const ( - updateFactorsConversionBias int64 = 0x7fffffff7fffffff // (2³¹ - 1)(2³² + 1) - updateFactorIdentityMatrixRow0 = 1 - updateFactorIdentityMatrixRow1 = 1 << 32 -) - -func updateFactorsDecompose(c int64) (int64, int64) { - c += updateFactorsConversionBias - const low32BitsFilter int64 = 0xFFFFFFFF - f := c&low32BitsFilter - 0x7FFFFFFF - g := c>>32&low32BitsFilter - 0x7FFFFFFF - return f, g -} - -// negL negates in place [x | xHi] and return the new most significant word xHi -func negL(x *Element, xHi uint64) uint64 { - var b uint64 - - x[0], b = bits.Sub64(0, x[0], 0) - x[1], b = bits.Sub64(0, x[1], b) - x[2], b = bits.Sub64(0, x[2], b) - x[3], b = bits.Sub64(0, x[3], b) - x[4], b = bits.Sub64(0, x[4], b) - x[5], b = bits.Sub64(0, x[5], b) - xHi, _ = bits.Sub64(0, xHi, b) - - return xHi -} - -// mulWNonModular multiplies by one word in non-montgomery, without reducing -func (z *Element) mulWNonModular(x *Element, y int64) uint64 { - - // w := abs(y) - m := y >> 63 - w := uint64((y ^ m) - m) - - var c uint64 - c, z[0] = bits.Mul64(x[0], w) - c, z[1] = madd1(x[1], w, c) - c, z[2] = madd1(x[2], w, c) - c, z[3] = madd1(x[3], w, c) - c, z[4] = madd1(x[4], w, c) - c, z[5] = madd1(x[5], w, c) - - if y < 0 { - c = negL(z, c) - } - - return c -} - -// linearCombNonModular computes a linear combination without modular reduction -func (z *Element) linearCombNonModular(x *Element, xC int64, y *Element, yC int64) uint64 { - var yTimes Element - - yHi := yTimes.mulWNonModular(y, yC) - xHi := z.mulWNonModular(x, xC) - - var carry uint64 - z[0], carry = bits.Add64(z[0], yTimes[0], 0) - z[1], carry = bits.Add64(z[1], yTimes[1], carry) - z[2], carry = bits.Add64(z[2], yTimes[2], carry) - z[3], carry = bits.Add64(z[3], yTimes[3], carry) - z[4], carry = bits.Add64(z[4], yTimes[4], carry) - z[5], carry = bits.Add64(z[5], yTimes[5], carry) - - yHi, _ = bits.Add64(xHi, yHi, carry) - - return yHi -} diff --git a/ecc/bls12-378/fp/element_exp.go b/ecc/bls12-378/fp/element_exp.go deleted file mode 100644 index 68439a4d79..0000000000 --- a/ecc/bls12-378/fp/element_exp.go +++ /dev/null @@ -1,1040 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -// expBySqrtExp is equivalent to z.Exp(x, fbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff3a5fddaa08b0000265228) -// -// uses github.com/mmcloughlin/addchain v0.4.0 to generate a shorter addition chain -func (z *Element) expBySqrtExp(x Element) *Element { - // addition chain: - // - // _10 = 2*1 - // _11 = 1 + _10 - // _101 = _10 + _11 - // _110 = 1 + _101 - // _1001 = _11 + _110 - // _1011 = _10 + _1001 - // _1100 = 1 + _1011 - // _1101 = 1 + _1100 - // _10001 = _101 + _1100 - // _10011 = _10 + _10001 - // _10101 = _10 + _10011 - // _11011 = _110 + _10101 - // _11101 = _10 + _11011 - // _100011 = _110 + _11101 - // _100111 = _1100 + _11011 - // _101001 = _10 + _100111 - // _110101 = _1100 + _101001 - // _110111 = _10 + _110101 - // _111001 = _10 + _110111 - // _111011 = _10 + _111001 - // _111101 = _10 + _111011 - // _111111 = _10 + _111101 - // _1111100 = _111101 + _111111 - // _1111111 = _11 + _1111100 - // i39 = ((_1111100 << 4 + _11101) << 3 + _11) << 6 - // i57 = ((1 + i39) << 9 + _1011) << 6 + _1101 - // i78 = ((i57 << 9 + _10011) << 7 + _100011) << 3 - // i98 = ((1 + i78) << 11 + _101001) << 6 + _111001 - // i117 = ((i98 << 7 + _110101) << 4 + _1101) << 6 - // i138 = ((_1001 + i117) << 12 + _111011) << 6 + _10001 - // i162 = ((i138 << 11 + _111101) << 6 + _101) << 5 - // i184 = ((1 + i162) << 11 + _1011) << 8 + _111101 - // i205 = ((i184 << 6 + _11011) << 8 + _100011) << 5 - // i227 = ((_10001 + i205) << 12 + _100011) << 7 + _10011 - // i257 = ((i227 << 6 + _10011) << 13 + _110111) << 9 - // i279 = ((_11011 + i257) << 9 + _1101) << 10 + _101001 - // i299 = ((i279 << 8 + _100111) << 2 + 1) << 8 - // i311 = ((_1111111 + i299) << 2 + _11) << 7 + _11101 - // i331 = ((i311 << 3 + 1) << 8 + _1111111) << 7 - // i350 = ((_111011 + i331) << 6 + _10101) << 10 + _10001 - // i386 = ((i350 << 3 + _11) << 23 + _10011) << 8 - // return ((_101001 + i386) << 6 + _101) << 3 - // - // Operations: 330 squares 67 multiplies - - // Allocate Temporaries. - var ( - t0 = new(Element) - t1 = new(Element) - t2 = new(Element) - t3 = new(Element) - t4 = new(Element) - t5 = new(Element) - t6 = new(Element) - t7 = new(Element) - t8 = new(Element) - t9 = new(Element) - t10 = new(Element) - t11 = new(Element) - t12 = new(Element) - t13 = new(Element) - t14 = new(Element) - t15 = new(Element) - t16 = new(Element) - t17 = new(Element) - t18 = new(Element) - ) - - // var t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18 Element - // Step 1: t6 = x^0x2 - t6.Square(&x) - - // Step 2: t2 = x^0x3 - t2.Mul(&x, t6) - - // Step 3: z = x^0x5 - z.Mul(t6, t2) - - // Step 4: t0 = x^0x6 - t0.Mul(&x, z) - - // Step 5: t15 = x^0x9 - t15.Mul(t2, t0) - - // Step 6: t14 = x^0xb - t14.Mul(t6, t15) - - // Step 7: t5 = x^0xc - t5.Mul(&x, t14) - - // Step 8: t9 = x^0xd - t9.Mul(&x, t5) - - // Step 9: t3 = x^0x11 - t3.Mul(z, t5) - - // Step 10: t1 = x^0x13 - t1.Mul(t6, t3) - - // Step 11: t4 = x^0x15 - t4.Mul(t6, t1) - - // Step 12: t10 = x^0x1b - t10.Mul(t0, t4) - - // Step 13: t7 = x^0x1d - t7.Mul(t6, t10) - - // Step 14: t12 = x^0x23 - t12.Mul(t0, t7) - - // Step 15: t8 = x^0x27 - t8.Mul(t5, t10) - - // Step 16: t0 = x^0x29 - t0.Mul(t6, t8) - - // Step 17: t16 = x^0x35 - t16.Mul(t5, t0) - - // Step 18: t11 = x^0x37 - t11.Mul(t6, t16) - - // Step 19: t17 = x^0x39 - t17.Mul(t6, t11) - - // Step 20: t5 = x^0x3b - t5.Mul(t6, t17) - - // Step 21: t13 = x^0x3d - t13.Mul(t6, t5) - - // Step 22: t6 = x^0x3f - t6.Mul(t6, t13) - - // Step 23: t18 = x^0x7c - t18.Mul(t13, t6) - - // Step 24: t6 = x^0x7f - t6.Mul(t2, t18) - - // Step 28: t18 = x^0x7c0 - for s := 0; s < 4; s++ { - t18.Square(t18) - } - - // Step 29: t18 = x^0x7dd - t18.Mul(t7, t18) - - // Step 32: t18 = x^0x3ee8 - for s := 0; s < 3; s++ { - t18.Square(t18) - } - - // Step 33: t18 = x^0x3eeb - t18.Mul(t2, t18) - - // Step 39: t18 = x^0xfbac0 - for s := 0; s < 6; s++ { - t18.Square(t18) - } - - // Step 40: t18 = x^0xfbac1 - t18.Mul(&x, t18) - - // Step 49: t18 = x^0x1f758200 - for s := 0; s < 9; s++ { - t18.Square(t18) - } - - // Step 50: t18 = x^0x1f75820b - t18.Mul(t14, t18) - - // Step 56: t18 = x^0x7dd6082c0 - for s := 0; s < 6; s++ { - t18.Square(t18) - } - - // Step 57: t18 = x^0x7dd6082cd - t18.Mul(t9, t18) - - // Step 66: t18 = x^0xfbac1059a00 - for s := 0; s < 9; s++ { - t18.Square(t18) - } - - // Step 67: t18 = x^0xfbac1059a13 - t18.Mul(t1, t18) - - // Step 74: t18 = x^0x7dd6082cd0980 - for s := 0; s < 7; s++ { - t18.Square(t18) - } - - // Step 75: t18 = x^0x7dd6082cd09a3 - t18.Mul(t12, t18) - - // Step 78: t18 = x^0x3eeb0416684d18 - for s := 0; s < 3; s++ { - t18.Square(t18) - } - - // Step 79: t18 = x^0x3eeb0416684d19 - t18.Mul(&x, t18) - - // Step 90: t18 = x^0x1f75820b34268c800 - for s := 0; s < 11; s++ { - t18.Square(t18) - } - - // Step 91: t18 = x^0x1f75820b34268c829 - t18.Mul(t0, t18) - - // Step 97: t18 = x^0x7dd6082cd09a320a40 - for s := 0; s < 6; s++ { - t18.Square(t18) - } - - // Step 98: t17 = x^0x7dd6082cd09a320a79 - t17.Mul(t17, t18) - - // Step 105: t17 = x^0x3eeb0416684d19053c80 - for s := 0; s < 7; s++ { - t17.Square(t17) - } - - // Step 106: t16 = x^0x3eeb0416684d19053cb5 - t16.Mul(t16, t17) - - // Step 110: t16 = x^0x3eeb0416684d19053cb50 - for s := 0; s < 4; s++ { - t16.Square(t16) - } - - // Step 111: t16 = x^0x3eeb0416684d19053cb5d - t16.Mul(t9, t16) - - // Step 117: t16 = x^0xfbac1059a1346414f2d740 - for s := 0; s < 6; s++ { - t16.Square(t16) - } - - // Step 118: t15 = x^0xfbac1059a1346414f2d749 - t15.Mul(t15, t16) - - // Step 130: t15 = x^0xfbac1059a1346414f2d749000 - for s := 0; s < 12; s++ { - t15.Square(t15) - } - - // Step 131: t15 = x^0xfbac1059a1346414f2d74903b - t15.Mul(t5, t15) - - // Step 137: t15 = x^0x3eeb0416684d19053cb5d240ec0 - for s := 0; s < 6; s++ { - t15.Square(t15) - } - - // Step 138: t15 = x^0x3eeb0416684d19053cb5d240ed1 - t15.Mul(t3, t15) - - // Step 149: t15 = x^0x1f75820b34268c829e5ae920768800 - for s := 0; s < 11; s++ { - t15.Square(t15) - } - - // Step 150: t15 = x^0x1f75820b34268c829e5ae92076883d - t15.Mul(t13, t15) - - // Step 156: t15 = x^0x7dd6082cd09a320a796ba481da20f40 - for s := 0; s < 6; s++ { - t15.Square(t15) - } - - // Step 157: t15 = x^0x7dd6082cd09a320a796ba481da20f45 - t15.Mul(z, t15) - - // Step 162: t15 = x^0xfbac1059a1346414f2d74903b441e8a0 - for s := 0; s < 5; s++ { - t15.Square(t15) - } - - // Step 163: t15 = x^0xfbac1059a1346414f2d74903b441e8a1 - t15.Mul(&x, t15) - - // Step 174: t15 = x^0x7dd6082cd09a320a796ba481da20f450800 - for s := 0; s < 11; s++ { - t15.Square(t15) - } - - // Step 175: t14 = x^0x7dd6082cd09a320a796ba481da20f45080b - t14.Mul(t14, t15) - - // Step 183: t14 = x^0x7dd6082cd09a320a796ba481da20f45080b00 - for s := 0; s < 8; s++ { - t14.Square(t14) - } - - // Step 184: t13 = x^0x7dd6082cd09a320a796ba481da20f45080b3d - t13.Mul(t13, t14) - - // Step 190: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf40 - for s := 0; s < 6; s++ { - t13.Square(t13) - } - - // Step 191: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf5b - t13.Mul(t10, t13) - - // Step 199: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf5b00 - for s := 0; s < 8; s++ { - t13.Square(t13) - } - - // Step 200: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf5b23 - t13.Mul(t12, t13) - - // Step 205: t13 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6460 - for s := 0; s < 5; s++ { - t13.Square(t13) - } - - // Step 206: t13 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6471 - t13.Mul(t3, t13) - - // Step 218: t13 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6471000 - for s := 0; s < 12; s++ { - t13.Square(t13) - } - - // Step 219: t12 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6471023 - t12.Mul(t12, t13) - - // Step 226: t12 = x^0x1f75820b34268c829e5ae92076883d14202cf5b23881180 - for s := 0; s < 7; s++ { - t12.Square(t12) - } - - // Step 227: t12 = x^0x1f75820b34268c829e5ae92076883d14202cf5b23881193 - t12.Mul(t1, t12) - - // Step 233: t12 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464c0 - for s := 0; s < 6; s++ { - t12.Square(t12) - } - - // Step 234: t12 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d3 - t12.Mul(t1, t12) - - // Step 247: t12 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a6000 - for s := 0; s < 13; s++ { - t12.Square(t12) - } - - // Step 248: t11 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a6037 - t11.Mul(t11, t12) - - // Step 257: t11 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e00 - for s := 0; s < 9; s++ { - t11.Square(t11) - } - - // Step 258: t10 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b - t10.Mul(t10, t11) - - // Step 267: t10 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc3600 - for s := 0; s < 9; s++ { - t10.Square(t10) - } - - // Step 268: t9 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d - t9.Mul(t9, t10) - - // Step 278: t9 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83400 - for s := 0; s < 10; s++ { - t9.Square(t9) - } - - // Step 279: t9 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429 - t9.Mul(t0, t9) - - // Step 287: t9 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d8342900 - for s := 0; s < 8; s++ { - t9.Square(t9) - } - - // Step 288: t8 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d8342927 - t8.Mul(t8, t9) - - // Step 290: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49c - for s := 0; s < 2; s++ { - t8.Square(t8) - } - - // Step 291: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d - t8.Mul(&x, t8) - - // Step 299: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d00 - for s := 0; s < 8; s++ { - t8.Square(t8) - } - - // Step 300: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7f - t8.Mul(t6, t8) - - // Step 302: t8 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275fc - for s := 0; s < 2; s++ { - t8.Square(t8) - } - - // Step 303: t8 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff - t8.Mul(t2, t8) - - // Step 310: t8 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff80 - for s := 0; s < 7; s++ { - t8.Square(t8) - } - - // Step 311: t7 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d - t7.Mul(t7, t8) - - // Step 314: t7 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce8 - for s := 0; s < 3; s++ { - t7.Square(t7) - } - - // Step 315: t7 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce9 - t7.Mul(&x, t7) - - // Step 323: t7 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce900 - for s := 0; s < 8; s++ { - t7.Square(t7) - } - - // Step 324: t6 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce97f - t6.Mul(t6, t7) - - // Step 331: t6 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bf80 - for s := 0; s < 7; s++ { - t6.Square(t6) - } - - // Step 332: t5 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb - t5.Mul(t5, t6) - - // Step 338: t5 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feec0 - for s := 0; s < 6; s++ { - t5.Square(t5) - } - - // Step 339: t4 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed5 - t4.Mul(t4, t5) - - // Step 349: t4 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5400 - for s := 0; s < 10; s++ { - t4.Square(t4) - } - - // Step 350: t3 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411 - t3.Mul(t3, t4) - - // Step 353: t3 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff3a5fddaa088 - for s := 0; s < 3; s++ { - t3.Square(t3) - } - - // Step 354: t2 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff3a5fddaa08b - t2.Mul(t2, t3) - - // Step 377: t2 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed5045800000 - for s := 0; s < 23; s++ { - t2.Square(t2) - } - - // Step 378: t1 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed5045800013 - t1.Mul(t1, t2) - - // Step 386: t1 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed504580001300 - for s := 0; s < 8; s++ { - t1.Square(t1) - } - - // Step 387: t0 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed504580001329 - t0.Mul(t0, t1) - - // Step 393: t0 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca40 - for s := 0; s < 6; s++ { - t0.Square(t0) - } - - // Step 394: z = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca45 - z.Mul(z, t0) - - // Step 397: z = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff3a5fddaa08b0000265228 - for s := 0; s < 3; s++ { - z.Square(z) - } - - return z -} - -// expByLegendreExp is equivalent to z.Exp(x, 1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca4510000000000) -// -// uses github.com/mmcloughlin/addchain v0.4.0 to generate a shorter addition chain -func (z *Element) expByLegendreExp(x Element) *Element { - // addition chain: - // - // _10 = 2*1 - // _11 = 1 + _10 - // _101 = _10 + _11 - // _110 = 1 + _101 - // _1001 = _11 + _110 - // _1011 = _10 + _1001 - // _1100 = 1 + _1011 - // _1101 = 1 + _1100 - // _10001 = _101 + _1100 - // _10011 = _10 + _10001 - // _10101 = _10 + _10011 - // _11011 = _110 + _10101 - // _11101 = _10 + _11011 - // _100011 = _110 + _11101 - // _100111 = _1100 + _11011 - // _101001 = _10 + _100111 - // _110101 = _1100 + _101001 - // _110111 = _10 + _110101 - // _111001 = _10 + _110111 - // _111011 = _10 + _111001 - // _111101 = _10 + _111011 - // _111111 = _10 + _111101 - // _1111100 = _111101 + _111111 - // _1111111 = _11 + _1111100 - // i39 = ((_1111100 << 4 + _11101) << 3 + _11) << 6 - // i57 = ((1 + i39) << 9 + _1011) << 6 + _1101 - // i78 = ((i57 << 9 + _10011) << 7 + _100011) << 3 - // i98 = ((1 + i78) << 11 + _101001) << 6 + _111001 - // i117 = ((i98 << 7 + _110101) << 4 + _1101) << 6 - // i138 = ((_1001 + i117) << 12 + _111011) << 6 + _10001 - // i162 = ((i138 << 11 + _111101) << 6 + _101) << 5 - // i184 = ((1 + i162) << 11 + _1011) << 8 + _111101 - // i205 = ((i184 << 6 + _11011) << 8 + _100011) << 5 - // i227 = ((_10001 + i205) << 12 + _100011) << 7 + _10011 - // i257 = ((i227 << 6 + _10011) << 13 + _110111) << 9 - // i279 = ((_11011 + i257) << 9 + _1101) << 10 + _101001 - // i299 = ((i279 << 8 + _100111) << 2 + 1) << 8 - // i311 = ((_1111111 + i299) << 2 + _11) << 7 + _11101 - // i331 = ((i311 << 3 + 1) << 8 + _1111111) << 7 - // i350 = ((_111011 + i331) << 6 + _10101) << 10 + _10001 - // i386 = ((i350 << 3 + _11) << 23 + _10011) << 8 - // i399 = ((_101001 + i386) << 6 + _101) << 4 + 1 - // return i399 << 40 - // - // Operations: 371 squares 68 multiplies - - // Allocate Temporaries. - var ( - t0 = new(Element) - t1 = new(Element) - t2 = new(Element) - t3 = new(Element) - t4 = new(Element) - t5 = new(Element) - t6 = new(Element) - t7 = new(Element) - t8 = new(Element) - t9 = new(Element) - t10 = new(Element) - t11 = new(Element) - t12 = new(Element) - t13 = new(Element) - t14 = new(Element) - t15 = new(Element) - t16 = new(Element) - t17 = new(Element) - t18 = new(Element) - ) - - // var t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18 Element - // Step 1: t6 = x^0x2 - t6.Square(&x) - - // Step 2: t2 = x^0x3 - t2.Mul(&x, t6) - - // Step 3: z = x^0x5 - z.Mul(t6, t2) - - // Step 4: t0 = x^0x6 - t0.Mul(&x, z) - - // Step 5: t15 = x^0x9 - t15.Mul(t2, t0) - - // Step 6: t14 = x^0xb - t14.Mul(t6, t15) - - // Step 7: t5 = x^0xc - t5.Mul(&x, t14) - - // Step 8: t9 = x^0xd - t9.Mul(&x, t5) - - // Step 9: t3 = x^0x11 - t3.Mul(z, t5) - - // Step 10: t1 = x^0x13 - t1.Mul(t6, t3) - - // Step 11: t4 = x^0x15 - t4.Mul(t6, t1) - - // Step 12: t10 = x^0x1b - t10.Mul(t0, t4) - - // Step 13: t7 = x^0x1d - t7.Mul(t6, t10) - - // Step 14: t12 = x^0x23 - t12.Mul(t0, t7) - - // Step 15: t8 = x^0x27 - t8.Mul(t5, t10) - - // Step 16: t0 = x^0x29 - t0.Mul(t6, t8) - - // Step 17: t16 = x^0x35 - t16.Mul(t5, t0) - - // Step 18: t11 = x^0x37 - t11.Mul(t6, t16) - - // Step 19: t17 = x^0x39 - t17.Mul(t6, t11) - - // Step 20: t5 = x^0x3b - t5.Mul(t6, t17) - - // Step 21: t13 = x^0x3d - t13.Mul(t6, t5) - - // Step 22: t6 = x^0x3f - t6.Mul(t6, t13) - - // Step 23: t18 = x^0x7c - t18.Mul(t13, t6) - - // Step 24: t6 = x^0x7f - t6.Mul(t2, t18) - - // Step 28: t18 = x^0x7c0 - for s := 0; s < 4; s++ { - t18.Square(t18) - } - - // Step 29: t18 = x^0x7dd - t18.Mul(t7, t18) - - // Step 32: t18 = x^0x3ee8 - for s := 0; s < 3; s++ { - t18.Square(t18) - } - - // Step 33: t18 = x^0x3eeb - t18.Mul(t2, t18) - - // Step 39: t18 = x^0xfbac0 - for s := 0; s < 6; s++ { - t18.Square(t18) - } - - // Step 40: t18 = x^0xfbac1 - t18.Mul(&x, t18) - - // Step 49: t18 = x^0x1f758200 - for s := 0; s < 9; s++ { - t18.Square(t18) - } - - // Step 50: t18 = x^0x1f75820b - t18.Mul(t14, t18) - - // Step 56: t18 = x^0x7dd6082c0 - for s := 0; s < 6; s++ { - t18.Square(t18) - } - - // Step 57: t18 = x^0x7dd6082cd - t18.Mul(t9, t18) - - // Step 66: t18 = x^0xfbac1059a00 - for s := 0; s < 9; s++ { - t18.Square(t18) - } - - // Step 67: t18 = x^0xfbac1059a13 - t18.Mul(t1, t18) - - // Step 74: t18 = x^0x7dd6082cd0980 - for s := 0; s < 7; s++ { - t18.Square(t18) - } - - // Step 75: t18 = x^0x7dd6082cd09a3 - t18.Mul(t12, t18) - - // Step 78: t18 = x^0x3eeb0416684d18 - for s := 0; s < 3; s++ { - t18.Square(t18) - } - - // Step 79: t18 = x^0x3eeb0416684d19 - t18.Mul(&x, t18) - - // Step 90: t18 = x^0x1f75820b34268c800 - for s := 0; s < 11; s++ { - t18.Square(t18) - } - - // Step 91: t18 = x^0x1f75820b34268c829 - t18.Mul(t0, t18) - - // Step 97: t18 = x^0x7dd6082cd09a320a40 - for s := 0; s < 6; s++ { - t18.Square(t18) - } - - // Step 98: t17 = x^0x7dd6082cd09a320a79 - t17.Mul(t17, t18) - - // Step 105: t17 = x^0x3eeb0416684d19053c80 - for s := 0; s < 7; s++ { - t17.Square(t17) - } - - // Step 106: t16 = x^0x3eeb0416684d19053cb5 - t16.Mul(t16, t17) - - // Step 110: t16 = x^0x3eeb0416684d19053cb50 - for s := 0; s < 4; s++ { - t16.Square(t16) - } - - // Step 111: t16 = x^0x3eeb0416684d19053cb5d - t16.Mul(t9, t16) - - // Step 117: t16 = x^0xfbac1059a1346414f2d740 - for s := 0; s < 6; s++ { - t16.Square(t16) - } - - // Step 118: t15 = x^0xfbac1059a1346414f2d749 - t15.Mul(t15, t16) - - // Step 130: t15 = x^0xfbac1059a1346414f2d749000 - for s := 0; s < 12; s++ { - t15.Square(t15) - } - - // Step 131: t15 = x^0xfbac1059a1346414f2d74903b - t15.Mul(t5, t15) - - // Step 137: t15 = x^0x3eeb0416684d19053cb5d240ec0 - for s := 0; s < 6; s++ { - t15.Square(t15) - } - - // Step 138: t15 = x^0x3eeb0416684d19053cb5d240ed1 - t15.Mul(t3, t15) - - // Step 149: t15 = x^0x1f75820b34268c829e5ae920768800 - for s := 0; s < 11; s++ { - t15.Square(t15) - } - - // Step 150: t15 = x^0x1f75820b34268c829e5ae92076883d - t15.Mul(t13, t15) - - // Step 156: t15 = x^0x7dd6082cd09a320a796ba481da20f40 - for s := 0; s < 6; s++ { - t15.Square(t15) - } - - // Step 157: t15 = x^0x7dd6082cd09a320a796ba481da20f45 - t15.Mul(z, t15) - - // Step 162: t15 = x^0xfbac1059a1346414f2d74903b441e8a0 - for s := 0; s < 5; s++ { - t15.Square(t15) - } - - // Step 163: t15 = x^0xfbac1059a1346414f2d74903b441e8a1 - t15.Mul(&x, t15) - - // Step 174: t15 = x^0x7dd6082cd09a320a796ba481da20f450800 - for s := 0; s < 11; s++ { - t15.Square(t15) - } - - // Step 175: t14 = x^0x7dd6082cd09a320a796ba481da20f45080b - t14.Mul(t14, t15) - - // Step 183: t14 = x^0x7dd6082cd09a320a796ba481da20f45080b00 - for s := 0; s < 8; s++ { - t14.Square(t14) - } - - // Step 184: t13 = x^0x7dd6082cd09a320a796ba481da20f45080b3d - t13.Mul(t13, t14) - - // Step 190: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf40 - for s := 0; s < 6; s++ { - t13.Square(t13) - } - - // Step 191: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf5b - t13.Mul(t10, t13) - - // Step 199: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf5b00 - for s := 0; s < 8; s++ { - t13.Square(t13) - } - - // Step 200: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf5b23 - t13.Mul(t12, t13) - - // Step 205: t13 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6460 - for s := 0; s < 5; s++ { - t13.Square(t13) - } - - // Step 206: t13 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6471 - t13.Mul(t3, t13) - - // Step 218: t13 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6471000 - for s := 0; s < 12; s++ { - t13.Square(t13) - } - - // Step 219: t12 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6471023 - t12.Mul(t12, t13) - - // Step 226: t12 = x^0x1f75820b34268c829e5ae92076883d14202cf5b23881180 - for s := 0; s < 7; s++ { - t12.Square(t12) - } - - // Step 227: t12 = x^0x1f75820b34268c829e5ae92076883d14202cf5b23881193 - t12.Mul(t1, t12) - - // Step 233: t12 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464c0 - for s := 0; s < 6; s++ { - t12.Square(t12) - } - - // Step 234: t12 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d3 - t12.Mul(t1, t12) - - // Step 247: t12 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a6000 - for s := 0; s < 13; s++ { - t12.Square(t12) - } - - // Step 248: t11 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a6037 - t11.Mul(t11, t12) - - // Step 257: t11 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e00 - for s := 0; s < 9; s++ { - t11.Square(t11) - } - - // Step 258: t10 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b - t10.Mul(t10, t11) - - // Step 267: t10 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc3600 - for s := 0; s < 9; s++ { - t10.Square(t10) - } - - // Step 268: t9 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d - t9.Mul(t9, t10) - - // Step 278: t9 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83400 - for s := 0; s < 10; s++ { - t9.Square(t9) - } - - // Step 279: t9 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429 - t9.Mul(t0, t9) - - // Step 287: t9 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d8342900 - for s := 0; s < 8; s++ { - t9.Square(t9) - } - - // Step 288: t8 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d8342927 - t8.Mul(t8, t9) - - // Step 290: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49c - for s := 0; s < 2; s++ { - t8.Square(t8) - } - - // Step 291: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d - t8.Mul(&x, t8) - - // Step 299: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d00 - for s := 0; s < 8; s++ { - t8.Square(t8) - } - - // Step 300: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7f - t8.Mul(t6, t8) - - // Step 302: t8 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275fc - for s := 0; s < 2; s++ { - t8.Square(t8) - } - - // Step 303: t8 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff - t8.Mul(t2, t8) - - // Step 310: t8 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff80 - for s := 0; s < 7; s++ { - t8.Square(t8) - } - - // Step 311: t7 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d - t7.Mul(t7, t8) - - // Step 314: t7 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce8 - for s := 0; s < 3; s++ { - t7.Square(t7) - } - - // Step 315: t7 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce9 - t7.Mul(&x, t7) - - // Step 323: t7 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce900 - for s := 0; s < 8; s++ { - t7.Square(t7) - } - - // Step 324: t6 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce97f - t6.Mul(t6, t7) - - // Step 331: t6 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bf80 - for s := 0; s < 7; s++ { - t6.Square(t6) - } - - // Step 332: t5 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb - t5.Mul(t5, t6) - - // Step 338: t5 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feec0 - for s := 0; s < 6; s++ { - t5.Square(t5) - } - - // Step 339: t4 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed5 - t4.Mul(t4, t5) - - // Step 349: t4 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5400 - for s := 0; s < 10; s++ { - t4.Square(t4) - } - - // Step 350: t3 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411 - t3.Mul(t3, t4) - - // Step 353: t3 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff3a5fddaa088 - for s := 0; s < 3; s++ { - t3.Square(t3) - } - - // Step 354: t2 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff3a5fddaa08b - t2.Mul(t2, t3) - - // Step 377: t2 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed5045800000 - for s := 0; s < 23; s++ { - t2.Square(t2) - } - - // Step 378: t1 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed5045800013 - t1.Mul(t1, t2) - - // Step 386: t1 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed504580001300 - for s := 0; s < 8; s++ { - t1.Square(t1) - } - - // Step 387: t0 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed504580001329 - t0.Mul(t0, t1) - - // Step 393: t0 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca40 - for s := 0; s < 6; s++ { - t0.Square(t0) - } - - // Step 394: z = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca45 - z.Mul(z, t0) - - // Step 398: z = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca450 - for s := 0; s < 4; s++ { - z.Square(z) - } - - // Step 399: z = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca451 - z.Mul(&x, z) - - // Step 439: z = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca4510000000000 - for s := 0; s < 40; s++ { - z.Square(z) - } - - return z -} diff --git a/ecc/bls12-378/fp/element_mul_amd64.s b/ecc/bls12-378/fp/element_mul_amd64.s deleted file mode 100644 index 39ededda7e..0000000000 --- a/ecc/bls12-378/fp/element_mul_amd64.s +++ /dev/null @@ -1,857 +0,0 @@ -// +build !purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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. - -#include "textflag.h" -#include "funcdata.h" - -// modulus q -DATA q<>+0(SB)/8, $0x9948a20000000001 -DATA q<>+8(SB)/8, $0xce97f76a822c0000 -DATA q<>+16(SB)/8, $0x980dc360d0a49d7f -DATA q<>+24(SB)/8, $0x84059eb647102326 -DATA q<>+32(SB)/8, $0x53cb5d240ed107a2 -DATA q<>+40(SB)/8, $0x03eeb0416684d190 -GLOBL q<>(SB), (RODATA+NOPTR), $48 - -// qInv0 q'[0] -DATA qInv0<>(SB)/8, $0x9948a1ffffffffff -GLOBL qInv0<>(SB), (RODATA+NOPTR), $8 - -#define REDUCE(ra0, ra1, ra2, ra3, ra4, ra5, rb0, rb1, rb2, rb3, rb4, rb5) \ - MOVQ ra0, rb0; \ - SUBQ q<>(SB), ra0; \ - MOVQ ra1, rb1; \ - SBBQ q<>+8(SB), ra1; \ - MOVQ ra2, rb2; \ - SBBQ q<>+16(SB), ra2; \ - MOVQ ra3, rb3; \ - SBBQ q<>+24(SB), ra3; \ - MOVQ ra4, rb4; \ - SBBQ q<>+32(SB), ra4; \ - MOVQ ra5, rb5; \ - SBBQ q<>+40(SB), ra5; \ - CMOVQCS rb0, ra0; \ - CMOVQCS rb1, ra1; \ - CMOVQCS rb2, ra2; \ - CMOVQCS rb3, ra3; \ - CMOVQCS rb4, ra4; \ - CMOVQCS rb5, ra5; \ - -// mul(res, x, y *Element) -TEXT ·mul(SB), $24-24 - - // the algorithm is described in the Element.Mul declaration (.go) - // however, to benefit from the ADCX and ADOX carry chains - // we split the inner loops in 2: - // for i=0 to N-1 - // for j=0 to N-1 - // (A,t[j]) := t[j] + x[j]*y[i] + A - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // t[N-1] = C + A - - NO_LOCAL_POINTERS - CMPB ·supportAdx(SB), $1 - JNE l1 - MOVQ x+8(FP), R8 - - // x[0] -> R10 - // x[1] -> R11 - // x[2] -> R12 - MOVQ 0(R8), R10 - MOVQ 8(R8), R11 - MOVQ 16(R8), R12 - MOVQ y+16(FP), R13 - - // A -> BP - // t[0] -> R14 - // t[1] -> R15 - // t[2] -> CX - // t[3] -> BX - // t[4] -> SI - // t[5] -> DI - // clear the flags - XORQ AX, AX - MOVQ 0(R13), DX - - // (A,t[0]) := x[0]*y[0] + A - MULXQ R10, R14, R15 - - // (A,t[1]) := x[1]*y[0] + A - MULXQ R11, AX, CX - ADOXQ AX, R15 - - // (A,t[2]) := x[2]*y[0] + A - MULXQ R12, AX, BX - ADOXQ AX, CX - - // (A,t[3]) := x[3]*y[0] + A - MULXQ 24(R8), AX, SI - ADOXQ AX, BX - - // (A,t[4]) := x[4]*y[0] + A - MULXQ 32(R8), AX, DI - ADOXQ AX, SI - - // (A,t[5]) := x[5]*y[0] + A - MULXQ 40(R8), AX, BP - ADOXQ AX, DI - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R9 - ADCXQ R14, AX - MOVQ R9, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // t[5] = C + A - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ BP, DI - - // clear the flags - XORQ AX, AX - MOVQ 8(R13), DX - - // (A,t[0]) := t[0] + x[0]*y[1] + A - MULXQ R10, AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[1] + A - ADCXQ BP, R15 - MULXQ R11, AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[1] + A - ADCXQ BP, CX - MULXQ R12, AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[1] + A - ADCXQ BP, BX - MULXQ 24(R8), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[1] + A - ADCXQ BP, SI - MULXQ 32(R8), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[1] + A - ADCXQ BP, DI - MULXQ 40(R8), AX, BP - ADOXQ AX, DI - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R9 - ADCXQ R14, AX - MOVQ R9, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // t[5] = C + A - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ BP, DI - - // clear the flags - XORQ AX, AX - MOVQ 16(R13), DX - - // (A,t[0]) := t[0] + x[0]*y[2] + A - MULXQ R10, AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[2] + A - ADCXQ BP, R15 - MULXQ R11, AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[2] + A - ADCXQ BP, CX - MULXQ R12, AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[2] + A - ADCXQ BP, BX - MULXQ 24(R8), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[2] + A - ADCXQ BP, SI - MULXQ 32(R8), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[2] + A - ADCXQ BP, DI - MULXQ 40(R8), AX, BP - ADOXQ AX, DI - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R9 - ADCXQ R14, AX - MOVQ R9, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // t[5] = C + A - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ BP, DI - - // clear the flags - XORQ AX, AX - MOVQ 24(R13), DX - - // (A,t[0]) := t[0] + x[0]*y[3] + A - MULXQ R10, AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[3] + A - ADCXQ BP, R15 - MULXQ R11, AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[3] + A - ADCXQ BP, CX - MULXQ R12, AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[3] + A - ADCXQ BP, BX - MULXQ 24(R8), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[3] + A - ADCXQ BP, SI - MULXQ 32(R8), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[3] + A - ADCXQ BP, DI - MULXQ 40(R8), AX, BP - ADOXQ AX, DI - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R9 - ADCXQ R14, AX - MOVQ R9, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // t[5] = C + A - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ BP, DI - - // clear the flags - XORQ AX, AX - MOVQ 32(R13), DX - - // (A,t[0]) := t[0] + x[0]*y[4] + A - MULXQ R10, AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[4] + A - ADCXQ BP, R15 - MULXQ R11, AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[4] + A - ADCXQ BP, CX - MULXQ R12, AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[4] + A - ADCXQ BP, BX - MULXQ 24(R8), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[4] + A - ADCXQ BP, SI - MULXQ 32(R8), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[4] + A - ADCXQ BP, DI - MULXQ 40(R8), AX, BP - ADOXQ AX, DI - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R9 - ADCXQ R14, AX - MOVQ R9, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // t[5] = C + A - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ BP, DI - - // clear the flags - XORQ AX, AX - MOVQ 40(R13), DX - - // (A,t[0]) := t[0] + x[0]*y[5] + A - MULXQ R10, AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[5] + A - ADCXQ BP, R15 - MULXQ R11, AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[5] + A - ADCXQ BP, CX - MULXQ R12, AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[5] + A - ADCXQ BP, BX - MULXQ 24(R8), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[5] + A - ADCXQ BP, SI - MULXQ 32(R8), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[5] + A - ADCXQ BP, DI - MULXQ 40(R8), AX, BP - ADOXQ AX, DI - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R9 - ADCXQ R14, AX - MOVQ R9, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // t[5] = C + A - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ BP, DI - - // reduce element(R14,R15,CX,BX,SI,DI) using temp registers (R9,R8,R13,R10,R11,R12) - REDUCE(R14,R15,CX,BX,SI,DI,R9,R8,R13,R10,R11,R12) - - MOVQ res+0(FP), AX - MOVQ R14, 0(AX) - MOVQ R15, 8(AX) - MOVQ CX, 16(AX) - MOVQ BX, 24(AX) - MOVQ SI, 32(AX) - MOVQ DI, 40(AX) - RET - -l1: - MOVQ res+0(FP), AX - MOVQ AX, (SP) - MOVQ x+8(FP), AX - MOVQ AX, 8(SP) - MOVQ y+16(FP), AX - MOVQ AX, 16(SP) - CALL ·_mulGeneric(SB) - RET - -TEXT ·fromMont(SB), $8-8 - NO_LOCAL_POINTERS - - // the algorithm is described here - // https://hackmd.io/@gnark/modular_multiplication - // when y = 1 we have: - // for i=0 to N-1 - // t[i] = x[i] - // for i=0 to N-1 - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // t[N-1] = C - CMPB ·supportAdx(SB), $1 - JNE l2 - MOVQ res+0(FP), DX - MOVQ 0(DX), R14 - MOVQ 8(DX), R15 - MOVQ 16(DX), CX - MOVQ 24(DX), BX - MOVQ 32(DX), SI - MOVQ 40(DX), DI - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ AX, DI - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ AX, DI - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ AX, DI - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ AX, DI - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ AX, DI - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ AX, DI - - // reduce element(R14,R15,CX,BX,SI,DI) using temp registers (R8,R9,R10,R11,R12,R13) - REDUCE(R14,R15,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13) - - MOVQ res+0(FP), AX - MOVQ R14, 0(AX) - MOVQ R15, 8(AX) - MOVQ CX, 16(AX) - MOVQ BX, 24(AX) - MOVQ SI, 32(AX) - MOVQ DI, 40(AX) - RET - -l2: - MOVQ res+0(FP), AX - MOVQ AX, (SP) - CALL ·_fromMontGeneric(SB) - RET diff --git a/ecc/bls12-378/fp/element_ops_amd64.go b/ecc/bls12-378/fp/element_ops_amd64.go deleted file mode 100644 index 83bba45aed..0000000000 --- a/ecc/bls12-378/fp/element_ops_amd64.go +++ /dev/null @@ -1,107 +0,0 @@ -//go:build !purego -// +build !purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -//go:noescape -func MulBy3(x *Element) - -//go:noescape -func MulBy5(x *Element) - -//go:noescape -func MulBy13(x *Element) - -//go:noescape -func mul(res, x, y *Element) - -//go:noescape -func fromMont(res *Element) - -//go:noescape -func reduce(res *Element) - -// Butterfly sets -// -// a = a + b (mod q) -// b = a - b (mod q) -// -//go:noescape -func Butterfly(a, b *Element) - -// Mul z = x * y (mod q) -// -// x and y must be less than q -func (z *Element) Mul(x, y *Element) *Element { - - // Implements CIOS multiplication -- section 2.3.2 of Tolga Acar's thesis - // https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf - // - // The algorithm: - // - // for i=0 to N-1 - // C := 0 - // for j=0 to N-1 - // (C,t[j]) := t[j] + x[j]*y[i] + C - // (t[N+1],t[N]) := t[N] + C - // - // C := 0 - // m := t[0]*q'[0] mod D - // (C,_) := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // (C,t[N-1]) := t[N] + C - // t[N] := t[N+1] + C - // - // → N is the number of machine words needed to store the modulus q - // → D is the word size. For example, on a 64-bit architecture D is 2 64 - // → x[i], y[i], q[i] is the ith word of the numbers x,y,q - // → q'[0] is the lowest word of the number -q⁻¹ mod r. This quantity is pre-computed, as it does not depend on the inputs. - // → t is a temporary array of size N+2 - // → C, S are machine words. A pair (C,S) refers to (hi-bits, lo-bits) of a two-word number - // - // As described here https://hackmd.io/@gnark/modular_multiplication we can get rid of one carry chain and simplify: - // (also described in https://eprint.iacr.org/2022/1400.pdf annex) - // - // for i=0 to N-1 - // (A,t[0]) := t[0] + x[0]*y[i] - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (A,t[j]) := t[j] + x[j]*y[i] + A - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // t[N-1] = C + A - // - // This optimization saves 5N + 2 additions in the algorithm, and can be used whenever the highest bit - // of the modulus is zero (and not all of the remaining bits are set). - - mul(z, x, y) - return z -} - -// Square z = x * x (mod q) -// -// x must be less than q -func (z *Element) Square(x *Element) *Element { - // see Mul for doc. - mul(z, x, x) - return z -} diff --git a/ecc/bls12-378/fp/element_ops_amd64.s b/ecc/bls12-378/fp/element_ops_amd64.s deleted file mode 100644 index 9440e0ccbc..0000000000 --- a/ecc/bls12-378/fp/element_ops_amd64.s +++ /dev/null @@ -1,306 +0,0 @@ -// +build !purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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. - -#include "textflag.h" -#include "funcdata.h" - -// modulus q -DATA q<>+0(SB)/8, $0x9948a20000000001 -DATA q<>+8(SB)/8, $0xce97f76a822c0000 -DATA q<>+16(SB)/8, $0x980dc360d0a49d7f -DATA q<>+24(SB)/8, $0x84059eb647102326 -DATA q<>+32(SB)/8, $0x53cb5d240ed107a2 -DATA q<>+40(SB)/8, $0x03eeb0416684d190 -GLOBL q<>(SB), (RODATA+NOPTR), $48 - -// qInv0 q'[0] -DATA qInv0<>(SB)/8, $0x9948a1ffffffffff -GLOBL qInv0<>(SB), (RODATA+NOPTR), $8 - -#define REDUCE(ra0, ra1, ra2, ra3, ra4, ra5, rb0, rb1, rb2, rb3, rb4, rb5) \ - MOVQ ra0, rb0; \ - SUBQ q<>(SB), ra0; \ - MOVQ ra1, rb1; \ - SBBQ q<>+8(SB), ra1; \ - MOVQ ra2, rb2; \ - SBBQ q<>+16(SB), ra2; \ - MOVQ ra3, rb3; \ - SBBQ q<>+24(SB), ra3; \ - MOVQ ra4, rb4; \ - SBBQ q<>+32(SB), ra4; \ - MOVQ ra5, rb5; \ - SBBQ q<>+40(SB), ra5; \ - CMOVQCS rb0, ra0; \ - CMOVQCS rb1, ra1; \ - CMOVQCS rb2, ra2; \ - CMOVQCS rb3, ra3; \ - CMOVQCS rb4, ra4; \ - CMOVQCS rb5, ra5; \ - -TEXT ·reduce(SB), NOSPLIT, $0-8 - MOVQ res+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - MOVQ 32(AX), DI - MOVQ 40(AX), R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R9,R10,R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - MOVQ DI, 32(AX) - MOVQ R8, 40(AX) - RET - -// MulBy3(x *Element) -TEXT ·MulBy3(SB), NOSPLIT, $0-8 - MOVQ x+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - MOVQ 32(AX), DI - MOVQ 40(AX), R8 - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R9,R10,R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) - - ADDQ 0(AX), DX - ADCQ 8(AX), CX - ADCQ 16(AX), BX - ADCQ 24(AX), SI - ADCQ 32(AX), DI - ADCQ 40(AX), R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R15,R9,R10,R11,R12,R13) - REDUCE(DX,CX,BX,SI,DI,R8,R15,R9,R10,R11,R12,R13) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - MOVQ DI, 32(AX) - MOVQ R8, 40(AX) - RET - -// MulBy5(x *Element) -TEXT ·MulBy5(SB), NOSPLIT, $0-8 - MOVQ x+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - MOVQ 32(AX), DI - MOVQ 40(AX), R8 - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R9,R10,R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) - - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R15,R9,R10,R11,R12,R13) - REDUCE(DX,CX,BX,SI,DI,R8,R15,R9,R10,R11,R12,R13) - - ADDQ 0(AX), DX - ADCQ 8(AX), CX - ADCQ 16(AX), BX - ADCQ 24(AX), SI - ADCQ 32(AX), DI - ADCQ 40(AX), R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R14,R15,R9,R10,R11,R12) - REDUCE(DX,CX,BX,SI,DI,R8,R14,R15,R9,R10,R11,R12) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - MOVQ DI, 32(AX) - MOVQ R8, 40(AX) - RET - -// MulBy13(x *Element) -TEXT ·MulBy13(SB), $40-8 - MOVQ x+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - MOVQ 32(AX), DI - MOVQ 40(AX), R8 - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R9,R10,R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) - - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP)) - REDUCE(DX,CX,BX,SI,DI,R8,R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP)) - - MOVQ DX, R15 - MOVQ CX, s0-8(SP) - MOVQ BX, s1-16(SP) - MOVQ SI, s2-24(SP) - MOVQ DI, s3-32(SP) - MOVQ R8, s4-40(SP) - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R9,R10,R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) - - ADDQ R15, DX - ADCQ s0-8(SP), CX - ADCQ s1-16(SP), BX - ADCQ s2-24(SP), SI - ADCQ s3-32(SP), DI - ADCQ s4-40(SP), R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R9,R10,R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) - - ADDQ 0(AX), DX - ADCQ 8(AX), CX - ADCQ 16(AX), BX - ADCQ 24(AX), SI - ADCQ 32(AX), DI - ADCQ 40(AX), R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R9,R10,R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - MOVQ DI, 32(AX) - MOVQ R8, 40(AX) - RET - -// Butterfly(a, b *Element) sets a = a + b; b = a - b -TEXT ·Butterfly(SB), $48-16 - MOVQ a+0(FP), AX - MOVQ 0(AX), CX - MOVQ 8(AX), BX - MOVQ 16(AX), SI - MOVQ 24(AX), DI - MOVQ 32(AX), R8 - MOVQ 40(AX), R9 - MOVQ CX, R10 - MOVQ BX, R11 - MOVQ SI, R12 - MOVQ DI, R13 - MOVQ R8, R14 - MOVQ R9, R15 - XORQ AX, AX - MOVQ b+8(FP), DX - ADDQ 0(DX), CX - ADCQ 8(DX), BX - ADCQ 16(DX), SI - ADCQ 24(DX), DI - ADCQ 32(DX), R8 - ADCQ 40(DX), R9 - SUBQ 0(DX), R10 - SBBQ 8(DX), R11 - SBBQ 16(DX), R12 - SBBQ 24(DX), R13 - SBBQ 32(DX), R14 - SBBQ 40(DX), R15 - MOVQ CX, s0-8(SP) - MOVQ BX, s1-16(SP) - MOVQ SI, s2-24(SP) - MOVQ DI, s3-32(SP) - MOVQ R8, s4-40(SP) - MOVQ R9, s5-48(SP) - MOVQ $0x9948a20000000001, CX - MOVQ $0xce97f76a822c0000, BX - MOVQ $0x980dc360d0a49d7f, SI - MOVQ $0x84059eb647102326, DI - MOVQ $0x53cb5d240ed107a2, R8 - MOVQ $0x03eeb0416684d190, R9 - CMOVQCC AX, CX - CMOVQCC AX, BX - CMOVQCC AX, SI - CMOVQCC AX, DI - CMOVQCC AX, R8 - CMOVQCC AX, R9 - ADDQ CX, R10 - ADCQ BX, R11 - ADCQ SI, R12 - ADCQ DI, R13 - ADCQ R8, R14 - ADCQ R9, R15 - MOVQ s0-8(SP), CX - MOVQ s1-16(SP), BX - MOVQ s2-24(SP), SI - MOVQ s3-32(SP), DI - MOVQ s4-40(SP), R8 - MOVQ s5-48(SP), R9 - MOVQ R10, 0(DX) - MOVQ R11, 8(DX) - MOVQ R12, 16(DX) - MOVQ R13, 24(DX) - MOVQ R14, 32(DX) - MOVQ R15, 40(DX) - - // reduce element(CX,BX,SI,DI,R8,R9) using temp registers (R10,R11,R12,R13,R14,R15) - REDUCE(CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15) - - MOVQ a+0(FP), AX - MOVQ CX, 0(AX) - MOVQ BX, 8(AX) - MOVQ SI, 16(AX) - MOVQ DI, 24(AX) - MOVQ R8, 32(AX) - MOVQ R9, 40(AX) - RET diff --git a/ecc/bls12-378/fp/element_ops_purego.go b/ecc/bls12-378/fp/element_ops_purego.go deleted file mode 100644 index ac0e487372..0000000000 --- a/ecc/bls12-378/fp/element_ops_purego.go +++ /dev/null @@ -1,745 +0,0 @@ -//go:build !amd64 || purego -// +build !amd64 purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -import "math/bits" - -// MulBy3 x *= 3 (mod q) -func MulBy3(x *Element) { - _x := *x - x.Double(x).Add(x, &_x) -} - -// MulBy5 x *= 5 (mod q) -func MulBy5(x *Element) { - _x := *x - x.Double(x).Double(x).Add(x, &_x) -} - -// MulBy13 x *= 13 (mod q) -func MulBy13(x *Element) { - var y = Element{ - 8212494240417053874, - 5029498262967025157, - 9404736542133420963, - 13073247822498485877, - 1581382318314538223, - 87125160541517067, - } - x.Mul(x, &y) -} - -// Butterfly sets -// -// a = a + b (mod q) -// b = a - b (mod q) -func Butterfly(a, b *Element) { - _butterflyGeneric(a, b) -} - -func fromMont(z *Element) { - _fromMontGeneric(z) -} - -func reduce(z *Element) { - _reduceGeneric(z) -} - -// Mul z = x * y (mod q) -// -// x and y must be less than q -func (z *Element) Mul(x, y *Element) *Element { - - // Implements CIOS multiplication -- section 2.3.2 of Tolga Acar's thesis - // https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf - // - // The algorithm: - // - // for i=0 to N-1 - // C := 0 - // for j=0 to N-1 - // (C,t[j]) := t[j] + x[j]*y[i] + C - // (t[N+1],t[N]) := t[N] + C - // - // C := 0 - // m := t[0]*q'[0] mod D - // (C,_) := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // (C,t[N-1]) := t[N] + C - // t[N] := t[N+1] + C - // - // → N is the number of machine words needed to store the modulus q - // → D is the word size. For example, on a 64-bit architecture D is 2 64 - // → x[i], y[i], q[i] is the ith word of the numbers x,y,q - // → q'[0] is the lowest word of the number -q⁻¹ mod r. This quantity is pre-computed, as it does not depend on the inputs. - // → t is a temporary array of size N+2 - // → C, S are machine words. A pair (C,S) refers to (hi-bits, lo-bits) of a two-word number - // - // As described here https://hackmd.io/@gnark/modular_multiplication we can get rid of one carry chain and simplify: - // (also described in https://eprint.iacr.org/2022/1400.pdf annex) - // - // for i=0 to N-1 - // (A,t[0]) := t[0] + x[0]*y[i] - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (A,t[j]) := t[j] + x[j]*y[i] + A - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // t[N-1] = C + A - // - // This optimization saves 5N + 2 additions in the algorithm, and can be used whenever the highest bit - // of the modulus is zero (and not all of the remaining bits are set). - - var t0, t1, t2, t3, t4, t5 uint64 - var u0, u1, u2, u3, u4, u5 uint64 - { - var c0, c1, c2 uint64 - v := x[0] - u0, t0 = bits.Mul64(v, y[0]) - u1, t1 = bits.Mul64(v, y[1]) - u2, t2 = bits.Mul64(v, y[2]) - u3, t3 = bits.Mul64(v, y[3]) - u4, t4 = bits.Mul64(v, y[4]) - u5, t5 = bits.Mul64(v, y[5]) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, 0, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[1] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[2] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[3] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[4] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[5] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - z[0] = t0 - z[1] = t1 - z[2] = t2 - z[3] = t3 - z[4] = t4 - z[5] = t5 - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } - return z -} - -// Square z = x * x (mod q) -// -// x must be less than q -func (z *Element) Square(x *Element) *Element { - // see Mul for algorithm documentation - - var t0, t1, t2, t3, t4, t5 uint64 - var u0, u1, u2, u3, u4, u5 uint64 - { - var c0, c1, c2 uint64 - v := x[0] - u0, t0 = bits.Mul64(v, x[0]) - u1, t1 = bits.Mul64(v, x[1]) - u2, t2 = bits.Mul64(v, x[2]) - u3, t3 = bits.Mul64(v, x[3]) - u4, t4 = bits.Mul64(v, x[4]) - u5, t5 = bits.Mul64(v, x[5]) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, 0, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[1] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[2] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[3] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[4] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[5] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - z[0] = t0 - z[1] = t1 - z[2] = t2 - z[3] = t3 - z[4] = t4 - z[5] = t5 - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } - return z -} diff --git a/ecc/bls12-378/fp/element_test.go b/ecc/bls12-378/fp/element_test.go deleted file mode 100644 index 0a785a7fe9..0000000000 --- a/ecc/bls12-378/fp/element_test.go +++ /dev/null @@ -1,2909 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -import ( - "crypto/rand" - "encoding/json" - "fmt" - "math/big" - "math/bits" - - mrand "math/rand" - - "testing" - - "github.com/leanovate/gopter" - ggen "github.com/leanovate/gopter/gen" - "github.com/leanovate/gopter/prop" - - "github.com/stretchr/testify/require" -) - -// ------------------------------------------------------------------------------------------------- -// benchmarks -// most benchmarks are rudimentary and should sample a large number of random inputs -// or be run multiple times to ensure it didn't measure the fastest path of the function - -var benchResElement Element - -func BenchmarkElementSelect(b *testing.B) { - var x, y Element - x.SetRandom() - y.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Select(i%3, &x, &y) - } -} - -func BenchmarkElementSetRandom(b *testing.B) { - var x Element - x.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = x.SetRandom() - } -} - -func BenchmarkElementSetBytes(b *testing.B) { - var x Element - x.SetRandom() - bb := x.Bytes() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - benchResElement.SetBytes(bb[:]) - } - -} - -func BenchmarkElementMulByConstants(b *testing.B) { - b.Run("mulBy3", func(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - MulBy3(&benchResElement) - } - }) - b.Run("mulBy5", func(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - MulBy5(&benchResElement) - } - }) - b.Run("mulBy13", func(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - MulBy13(&benchResElement) - } - }) -} - -func BenchmarkElementInverse(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - benchResElement.Inverse(&x) - } - -} - -func BenchmarkElementButterfly(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - Butterfly(&x, &benchResElement) - } -} - -func BenchmarkElementExp(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b1, _ := rand.Int(rand.Reader, Modulus()) - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Exp(x, b1) - } -} - -func BenchmarkElementDouble(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Double(&benchResElement) - } -} - -func BenchmarkElementAdd(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Add(&x, &benchResElement) - } -} - -func BenchmarkElementSub(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Sub(&x, &benchResElement) - } -} - -func BenchmarkElementNeg(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Neg(&benchResElement) - } -} - -func BenchmarkElementDiv(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Div(&x, &benchResElement) - } -} - -func BenchmarkElementFromMont(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.fromMont() - } -} - -func BenchmarkElementSquare(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Square(&benchResElement) - } -} - -func BenchmarkElementSqrt(b *testing.B) { - var a Element - a.SetUint64(4) - a.Neg(&a) - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Sqrt(&a) - } -} - -func BenchmarkElementMul(b *testing.B) { - x := Element{ - 13541478318970833666, - 5510290684934426267, - 8467587974331926354, - 13931463632695577534, - 3531303697457869800, - 51529254522778566, - } - benchResElement.SetOne() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Mul(&benchResElement, &x) - } -} - -func BenchmarkElementCmp(b *testing.B) { - x := Element{ - 13541478318970833666, - 5510290684934426267, - 8467587974331926354, - 13931463632695577534, - 3531303697457869800, - 51529254522778566, - } - benchResElement = x - benchResElement[0] = 0 - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Cmp(&x) - } -} - -func TestElementCmp(t *testing.T) { - var x, y Element - - if x.Cmp(&y) != 0 { - t.Fatal("x == y") - } - - one := One() - y.Sub(&y, &one) - - if x.Cmp(&y) != -1 { - t.Fatal("x < y") - } - if y.Cmp(&x) != 1 { - t.Fatal("x < y") - } - - x = y - if x.Cmp(&y) != 0 { - t.Fatal("x == y") - } - - x.Sub(&x, &one) - if x.Cmp(&y) != -1 { - t.Fatal("x < y") - } - if y.Cmp(&x) != 1 { - t.Fatal("x < y") - } -} -func TestElementIsRandom(t *testing.T) { - for i := 0; i < 50; i++ { - var x, y Element - x.SetRandom() - y.SetRandom() - if x.Equal(&y) { - t.Fatal("2 random numbers are unlikely to be equal") - } - } -} - -func TestElementIsUint64(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("reduce should output a result smaller than modulus", prop.ForAll( - func(v uint64) bool { - var e Element - e.SetUint64(v) - - if !e.IsUint64() { - return false - } - - return e.Uint64() == v - }, - ggen.UInt64(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementNegZero(t *testing.T) { - var a, b Element - b.SetZero() - for a.IsZero() { - a.SetRandom() - } - a.Neg(&b) - if !a.IsZero() { - t.Fatal("neg(0) != 0") - } -} - -// ------------------------------------------------------------------------------------------------- -// Gopter tests -// most of them are generated with a template - -const ( - nbFuzzShort = 200 - nbFuzz = 1000 -) - -// special values to be used in tests -var staticTestValues []Element - -func init() { - staticTestValues = append(staticTestValues, Element{}) // zero - staticTestValues = append(staticTestValues, One()) // one - staticTestValues = append(staticTestValues, rSquare) // r² - var e, one Element - one.SetOne() - e.Sub(&qElement, &one) - staticTestValues = append(staticTestValues, e) // q - 1 - e.Double(&one) - staticTestValues = append(staticTestValues, e) // 2 - - { - a := qElement - a[0]-- - staticTestValues = append(staticTestValues, a) - } - staticTestValues = append(staticTestValues, Element{0}) - staticTestValues = append(staticTestValues, Element{0, 0}) - staticTestValues = append(staticTestValues, Element{1}) - staticTestValues = append(staticTestValues, Element{0, 1}) - staticTestValues = append(staticTestValues, Element{2}) - staticTestValues = append(staticTestValues, Element{0, 2}) - - { - a := qElement - a[5]-- - staticTestValues = append(staticTestValues, a) - } - { - a := qElement - a[5]-- - a[0]++ - staticTestValues = append(staticTestValues, a) - } - - { - a := qElement - a[5] = 0 - staticTestValues = append(staticTestValues, a) - } - -} - -func TestElementReduce(t *testing.T) { - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - s := testValues[i] - expected := s - reduce(&s) - _reduceGeneric(&expected) - if !s.Equal(&expected) { - t.Fatal("reduce failed: asm and generic impl don't match") - } - } - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := genFull() - - properties.Property("reduce should output a result smaller than modulus", prop.ForAll( - func(a Element) bool { - b := a - reduce(&a) - _reduceGeneric(&b) - return a.smallerThanModulus() && a.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementEqual(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("x.Equal(&y) iff x == y; likely false for random pairs", prop.ForAll( - func(a testPairElement, b testPairElement) bool { - return a.element.Equal(&b.element) == (a.element == b.element) - }, - genA, - genB, - )) - - properties.Property("x.Equal(&y) if x == y", prop.ForAll( - func(a testPairElement) bool { - b := a.element - return a.element.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementBytes(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("SetBytes(Bytes()) should stay constant", prop.ForAll( - func(a testPairElement) bool { - var b Element - bytes := a.element.Bytes() - b.SetBytes(bytes[:]) - return a.element.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementInverseExp(t *testing.T) { - // inverse must be equal to exp^-2 - exp := Modulus() - exp.Sub(exp, new(big.Int).SetUint64(2)) - - invMatchExp := func(a testPairElement) bool { - var b Element - b.Set(&a.element) - a.element.Inverse(&a.element) - b.Exp(b, exp) - - return a.element.Equal(&b) - } - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - properties := gopter.NewProperties(parameters) - genA := gen() - properties.Property("inv == exp^-2", prop.ForAll(invMatchExp, genA)) - properties.TestingRun(t, gopter.ConsoleReporter(false)) - - parameters.MinSuccessfulTests = 1 - properties = gopter.NewProperties(parameters) - properties.Property("inv(0) == 0", prop.ForAll(invMatchExp, ggen.OneConstOf(testPairElement{}))) - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func mulByConstant(z *Element, c uint8) { - var y Element - y.SetUint64(uint64(c)) - z.Mul(z, &y) -} - -func TestElementMulByConstants(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - implemented := []uint8{0, 1, 2, 3, 5, 13} - properties.Property("mulByConstant", prop.ForAll( - func(a testPairElement) bool { - for _, c := range implemented { - var constant Element - constant.SetUint64(uint64(c)) - - b := a.element - b.Mul(&b, &constant) - - aa := a.element - mulByConstant(&aa, c) - - if !aa.Equal(&b) { - return false - } - } - - return true - }, - genA, - )) - - properties.Property("MulBy3(x) == Mul(x, 3)", prop.ForAll( - func(a testPairElement) bool { - var constant Element - constant.SetUint64(3) - - b := a.element - b.Mul(&b, &constant) - - MulBy3(&a.element) - - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("MulBy5(x) == Mul(x, 5)", prop.ForAll( - func(a testPairElement) bool { - var constant Element - constant.SetUint64(5) - - b := a.element - b.Mul(&b, &constant) - - MulBy5(&a.element) - - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("MulBy13(x) == Mul(x, 13)", prop.ForAll( - func(a testPairElement) bool { - var constant Element - constant.SetUint64(13) - - b := a.element - b.Mul(&b, &constant) - - MulBy13(&a.element) - - return a.element.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementLegendre(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("legendre should output same result than big.Int.Jacobi", prop.ForAll( - func(a testPairElement) bool { - return a.element.Legendre() == big.Jacobi(&a.bigint, Modulus()) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementBitLen(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("BitLen should output same result than big.Int.BitLen", prop.ForAll( - func(a testPairElement) bool { - return a.element.fromMont().BitLen() == a.bigint.BitLen() - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementButterflies(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("butterfly0 == a -b; a +b", prop.ForAll( - func(a, b testPairElement) bool { - a0, b0 := a.element, b.element - - _butterflyGeneric(&a.element, &b.element) - Butterfly(&a0, &b0) - - return a.element.Equal(&a0) && b.element.Equal(&b0) - }, - genA, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementLexicographicallyLargest(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("element.Cmp should match LexicographicallyLargest output", prop.ForAll( - func(a testPairElement) bool { - var negA Element - negA.Neg(&a.element) - - cmpResult := a.element.Cmp(&negA) - lResult := a.element.LexicographicallyLargest() - - if lResult && cmpResult == 1 { - return true - } - if !lResult && cmpResult != 1 { - return true - } - return false - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementAdd(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Add: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Add(&a.element, &b.element) - a.element.Add(&a.element, &b.element) - b.element.Add(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Add: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Add(&a.element, &b.element) - - var d, e big.Int - d.Add(&a.bigint, &b.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Add(&a.element, &r) - d.Add(&a.bigint, &rb).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Add: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Add(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Add(&a, &b) - d.Add(&aBig, &bBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Add failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementSub(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Sub: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Sub(&a.element, &b.element) - a.element.Sub(&a.element, &b.element) - b.element.Sub(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Sub: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Sub(&a.element, &b.element) - - var d, e big.Int - d.Sub(&a.bigint, &b.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Sub(&a.element, &r) - d.Sub(&a.bigint, &rb).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Sub: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Sub(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Sub(&a, &b) - d.Sub(&aBig, &bBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Sub failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementMul(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Mul: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Mul(&a.element, &b.element) - a.element.Mul(&a.element, &b.element) - b.element.Mul(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Mul: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Mul(&a.element, &b.element) - - var d, e big.Int - d.Mul(&a.bigint, &b.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Mul(&a.element, &r) - d.Mul(&a.bigint, &rb).Mod(&d, Modulus()) - - // checking generic impl against asm path - var cGeneric Element - _mulGeneric(&cGeneric, &a.element, &r) - if !cGeneric.Equal(&c) { - // need to give context to failing error. - return false - } - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Mul: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Mul(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - properties.Property("Mul: assembly implementation must be consistent with generic one", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - c.Mul(&a.element, &b.element) - _mulGeneric(&d, &a.element, &b.element) - return c.Equal(&d) - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Mul(&a, &b) - d.Mul(&aBig, &bBig).Mod(&d, Modulus()) - - // checking asm against generic impl - var cGeneric Element - _mulGeneric(&cGeneric, &a, &b) - if !cGeneric.Equal(&c) { - t.Fatal("Mul failed special test values: asm and generic impl don't match") - } - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Mul failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementDiv(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Div: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Div(&a.element, &b.element) - a.element.Div(&a.element, &b.element) - b.element.Div(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Div: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Div(&a.element, &b.element) - - var d, e big.Int - d.ModInverse(&b.bigint, Modulus()) - d.Mul(&d, &a.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Div(&a.element, &r) - d.ModInverse(&rb, Modulus()) - d.Mul(&d, &a.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Div: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Div(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Div(&a, &b) - d.ModInverse(&bBig, Modulus()) - d.Mul(&d, &aBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Div failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementExp(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Exp: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Exp(a.element, &b.bigint) - a.element.Exp(a.element, &b.bigint) - b.element.Exp(d, &b.bigint) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Exp: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Exp(a.element, &b.bigint) - - var d, e big.Int - d.Exp(&a.bigint, &b.bigint, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Exp(a.element, &rb) - d.Exp(&a.bigint, &rb, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Exp: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Exp(a.element, &b.bigint) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Exp(a, &bBig) - d.Exp(&aBig, &bBig, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Exp failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementSquare(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Square: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Square(&a.element) - a.element.Square(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Square: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Square(&a.element) - - var d, e big.Int - d.Mul(&a.bigint, &a.bigint).Mod(&d, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Square: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Square(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Square(&a) - - var d, e big.Int - d.Mul(&aBig, &aBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Square failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementInverse(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Inverse: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Inverse(&a.element) - a.element.Inverse(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Inverse: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Inverse(&a.element) - - var d, e big.Int - d.ModInverse(&a.bigint, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Inverse: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Inverse(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Inverse(&a) - - var d, e big.Int - d.ModInverse(&aBig, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Inverse failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementSqrt(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Sqrt: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - b := a.element - - b.Sqrt(&a.element) - a.element.Sqrt(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Sqrt: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Sqrt(&a.element) - - var d, e big.Int - d.ModSqrt(&a.bigint, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Sqrt: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Sqrt(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Sqrt(&a) - - var d, e big.Int - d.ModSqrt(&aBig, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Sqrt failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementDouble(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Double: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Double(&a.element) - a.element.Double(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Double: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Double(&a.element) - - var d, e big.Int - d.Lsh(&a.bigint, 1).Mod(&d, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Double: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Double(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Double(&a) - - var d, e big.Int - d.Lsh(&aBig, 1).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Double failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementNeg(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Neg: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Neg(&a.element) - a.element.Neg(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Neg: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Neg(&a.element) - - var d, e big.Int - d.Neg(&a.bigint).Mod(&d, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Neg: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Neg(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Neg(&a) - - var d, e big.Int - d.Neg(&aBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Neg failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementFixedExp(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - var ( - _bLegendreExponentElement *big.Int - _bSqrtExponentElement *big.Int - ) - - _bLegendreExponentElement, _ = new(big.Int).SetString("1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca4510000000000", 16) - const sqrtExponentElement = "fbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff3a5fddaa08b0000265228" - _bSqrtExponentElement, _ = new(big.Int).SetString(sqrtExponentElement, 16) - - genA := gen() - - properties.Property(fmt.Sprintf("expBySqrtExp must match Exp(%s)", sqrtExponentElement), prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.expBySqrtExp(c) - d.Exp(d, _bSqrtExponentElement) - return c.Equal(&d) - }, - genA, - )) - - properties.Property("expByLegendreExp must match Exp(1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca4510000000000)", prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.expByLegendreExp(c) - d.Exp(d, _bLegendreExponentElement) - return c.Equal(&d) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementHalve(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - var twoInv Element - twoInv.SetUint64(2) - twoInv.Inverse(&twoInv) - - properties.Property("z.Halve must match z / 2", prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.Halve() - d.Mul(&d, &twoInv) - return c.Equal(&d) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func combineSelectionArguments(c int64, z int8) int { - if z%3 == 0 { - return 0 - } - return int(c) -} - -func TestElementSelect(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := genFull() - genB := genFull() - genC := ggen.Int64() //the condition - genZ := ggen.Int8() //to make zeros artificially more likely - - properties.Property("Select: must select correctly", prop.ForAll( - func(a, b Element, cond int64, z int8) bool { - condC := combineSelectionArguments(cond, z) - - var c Element - c.Select(condC, &a, &b) - - if condC == 0 { - return c.Equal(&a) - } - return c.Equal(&b) - }, - genA, - genB, - genC, - genZ, - )) - - properties.Property("Select: having the receiver as operand should output the same result", prop.ForAll( - func(a, b Element, cond int64, z int8) bool { - condC := combineSelectionArguments(cond, z) - - var c, d Element - d.Set(&a) - c.Select(condC, &a, &b) - a.Select(condC, &a, &b) - b.Select(condC, &d, &b) - return a.Equal(&b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - genC, - genZ, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementSetInt64(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("z.SetInt64 must match z.SetString", prop.ForAll( - func(a testPairElement, v int64) bool { - c := a.element - d := a.element - - c.SetInt64(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, ggen.Int64(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementSetInterface(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genInt := ggen.Int - genInt8 := ggen.Int8 - genInt16 := ggen.Int16 - genInt32 := ggen.Int32 - genInt64 := ggen.Int64 - - genUint := ggen.UInt - genUint8 := ggen.UInt8 - genUint16 := ggen.UInt16 - genUint32 := ggen.UInt32 - genUint64 := ggen.UInt64 - - properties.Property("z.SetInterface must match z.SetString with int8", prop.ForAll( - func(a testPairElement, v int8) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt8(), - )) - - properties.Property("z.SetInterface must match z.SetString with int16", prop.ForAll( - func(a testPairElement, v int16) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt16(), - )) - - properties.Property("z.SetInterface must match z.SetString with int32", prop.ForAll( - func(a testPairElement, v int32) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt32(), - )) - - properties.Property("z.SetInterface must match z.SetString with int64", prop.ForAll( - func(a testPairElement, v int64) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt64(), - )) - - properties.Property("z.SetInterface must match z.SetString with int", prop.ForAll( - func(a testPairElement, v int) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint8", prop.ForAll( - func(a testPairElement, v uint8) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint8(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint16", prop.ForAll( - func(a testPairElement, v uint16) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint16(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint32", prop.ForAll( - func(a testPairElement, v uint32) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint32(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint64", prop.ForAll( - func(a testPairElement, v uint64) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint64(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint", prop.ForAll( - func(a testPairElement, v uint) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - - { - assert := require.New(t) - var e Element - r, err := e.SetInterface(nil) - assert.Nil(r) - assert.Error(err) - - var ptE *Element - var ptB *big.Int - - r, err = e.SetInterface(ptE) - assert.Nil(r) - assert.Error(err) - ptE = new(Element).SetOne() - r, err = e.SetInterface(ptE) - assert.NoError(err) - assert.True(r.IsOne()) - - r, err = e.SetInterface(ptB) - assert.Nil(r) - assert.Error(err) - - } -} - -func TestElementNegativeExp(t *testing.T) { - t.Parallel() - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("x⁻ᵏ == 1/xᵏ", prop.ForAll( - func(a, b testPairElement) bool { - - var nb, d, e big.Int - nb.Neg(&b.bigint) - - var c Element - c.Exp(a.element, &nb) - - d.Exp(&a.bigint, &nb, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementNewElement(t *testing.T) { - assert := require.New(t) - - t.Parallel() - - e := NewElement(1) - assert.True(e.IsOne()) - - e = NewElement(0) - assert.True(e.IsZero()) -} - -func TestElementBatchInvert(t *testing.T) { - assert := require.New(t) - - t.Parallel() - - // ensure batchInvert([x]) == invert(x) - for i := int64(-1); i <= 2; i++ { - var e, eInv Element - e.SetInt64(i) - eInv.Inverse(&e) - - a := []Element{e} - aInv := BatchInvert(a) - - assert.True(aInv[0].Equal(&eInv), "batchInvert != invert") - - } - - // test x * x⁻¹ == 1 - tData := [][]int64{ - {-1, 1, 2, 3}, - {0, -1, 1, 2, 3, 0}, - {0, -1, 1, 0, 2, 3, 0}, - {-1, 1, 0, 2, 3}, - {0, 0, 1}, - {1, 0, 0}, - {0, 0, 0}, - } - - for _, t := range tData { - a := make([]Element, len(t)) - for i := 0; i < len(a); i++ { - a[i].SetInt64(t[i]) - } - - aInv := BatchInvert(a) - - assert.True(len(aInv) == len(a)) - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - assert.True(aInv[i].IsZero(), "0⁻¹ != 0") - } else { - assert.True(a[i].Mul(&a[i], &aInv[i]).IsOne(), "x * x⁻¹ != 1") - } - } - } - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("batchInvert --> x * x⁻¹ == 1", prop.ForAll( - func(tp testPairElement, r uint8) bool { - - a := make([]Element, r) - if r != 0 { - a[0] = tp.element - - } - one := One() - for i := 1; i < len(a); i++ { - a[i].Add(&a[i-1], &one) - } - - aInv := BatchInvert(a) - - assert.True(len(aInv) == len(a)) - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - if !aInv[i].IsZero() { - return false - } - } else { - if !a[i].Mul(&a[i], &aInv[i]).IsOne() { - return false - } - } - } - return true - }, - genA, ggen.UInt8(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementFromMont(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Assembly implementation must be consistent with generic one", prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.fromMont() - _fromMontGeneric(&d) - return c.Equal(&d) - }, - genA, - )) - - properties.Property("x.fromMont().toMont() == x", prop.ForAll( - func(a testPairElement) bool { - c := a.element - c.fromMont().toMont() - return c.Equal(&a.element) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementJSON(t *testing.T) { - assert := require.New(t) - - type S struct { - A Element - B [3]Element - C *Element - D *Element - } - - // encode to JSON - var s S - s.A.SetString("-1") - s.B[2].SetUint64(42) - s.D = new(Element).SetUint64(8000) - - encoded, err := json.Marshal(&s) - assert.NoError(err) - // we may need to adjust "42" and "8000" values for some moduli; see Text() method for more details. - formatValue := func(v int64) string { - var a big.Int - a.SetInt64(v) - a.Mod(&a, Modulus()) - const maxUint16 = 65535 - var aNeg big.Int - aNeg.Neg(&a).Mod(&aNeg, Modulus()) - if aNeg.Uint64() != 0 && aNeg.Uint64() <= maxUint16 { - return "-" + aNeg.Text(10) - } - return a.Text(10) - } - expected := fmt.Sprintf("{\"A\":%s,\"B\":[0,0,%s],\"C\":null,\"D\":%s}", formatValue(-1), formatValue(42), formatValue(8000)) - assert.Equal(expected, string(encoded)) - - // decode valid - var decoded S - err = json.Unmarshal([]byte(expected), &decoded) - assert.NoError(err) - - assert.Equal(s, decoded, "element -> json -> element round trip failed") - - // decode hex and string values - withHexValues := "{\"A\":\"-1\",\"B\":[0,\"0x00000\",\"0x2A\"],\"C\":null,\"D\":\"8000\"}" - - var decodedS S - err = json.Unmarshal([]byte(withHexValues), &decodedS) - assert.NoError(err) - - assert.Equal(s, decodedS, " json with strings -> element failed") - -} - -type testPairElement struct { - element Element - bigint big.Int -} - -func gen() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var g testPairElement - - g.element = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - if qElement[5] != ^uint64(0) { - g.element[5] %= (qElement[5] + 1) - } - - for !g.element.smallerThanModulus() { - g.element = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - if qElement[5] != ^uint64(0) { - g.element[5] %= (qElement[5] + 1) - } - } - - g.element.BigInt(&g.bigint) - genResult := gopter.NewGenResult(g, gopter.NoShrinker) - return genResult - } -} - -func genFull() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - - genRandomFq := func() Element { - var g Element - - g = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - - if qElement[5] != ^uint64(0) { - g[5] %= (qElement[5] + 1) - } - - for !g.smallerThanModulus() { - g = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - if qElement[5] != ^uint64(0) { - g[5] %= (qElement[5] + 1) - } - } - - return g - } - a := genRandomFq() - - var carry uint64 - a[0], carry = bits.Add64(a[0], qElement[0], carry) - a[1], carry = bits.Add64(a[1], qElement[1], carry) - a[2], carry = bits.Add64(a[2], qElement[2], carry) - a[3], carry = bits.Add64(a[3], qElement[3], carry) - a[4], carry = bits.Add64(a[4], qElement[4], carry) - a[5], _ = bits.Add64(a[5], qElement[5], carry) - - genResult := gopter.NewGenResult(a, gopter.NoShrinker) - return genResult - } -} - -func (z *Element) matchVeryBigInt(aHi uint64, aInt *big.Int) error { - var modulus big.Int - var aIntMod big.Int - modulus.SetInt64(1) - modulus.Lsh(&modulus, (Limbs+1)*64) - aIntMod.Mod(aInt, &modulus) - - slice := append(z[:], aHi) - - return bigIntMatchUint64Slice(&aIntMod, slice) -} - -// TODO: Phase out in favor of property based testing -func (z *Element) assertMatchVeryBigInt(t *testing.T, aHi uint64, aInt *big.Int) { - - if err := z.matchVeryBigInt(aHi, aInt); err != nil { - t.Error(err) - } -} - -// bigIntMatchUint64Slice is a test helper to match big.Int words against a uint64 slice -func bigIntMatchUint64Slice(aInt *big.Int, a []uint64) error { - - words := aInt.Bits() - - const steps = 64 / bits.UintSize - const filter uint64 = 0xFFFFFFFFFFFFFFFF >> (64 - bits.UintSize) - for i := 0; i < len(a)*steps; i++ { - - var wI big.Word - - if i < len(words) { - wI = words[i] - } - - aI := a[i/steps] >> ((i * bits.UintSize) % 64) - aI &= filter - - if uint64(wI) != aI { - return fmt.Errorf("bignum mismatch: disagreement on word %d: %x ≠ %x; %d ≠ %d", i, uint64(wI), aI, uint64(wI), aI) - } - } - - return nil -} - -func TestElementInversionApproximation(t *testing.T) { - var x Element - for i := 0; i < 1000; i++ { - x.SetRandom() - - // Normally small elements are unlikely. Here we give them a higher chance - xZeros := mrand.Int() % Limbs //#nosec G404 weak rng is fine here - for j := 1; j < xZeros; j++ { - x[Limbs-j] = 0 - } - - a := approximate(&x, x.BitLen()) - aRef := approximateRef(&x) - - if a != aRef { - t.Error("Approximation mismatch") - } - } -} - -func TestElementInversionCorrectionFactorFormula(t *testing.T) { - const kLimbs = k * Limbs - const power = kLimbs*6 + invIterationsN*(kLimbs-k+1) - factorInt := big.NewInt(1) - factorInt.Lsh(factorInt, power) - factorInt.Mod(factorInt, Modulus()) - - var refFactorInt big.Int - inversionCorrectionFactor := Element{ - inversionCorrectionFactorWord0, - inversionCorrectionFactorWord1, - inversionCorrectionFactorWord2, - inversionCorrectionFactorWord3, - inversionCorrectionFactorWord4, - inversionCorrectionFactorWord5, - } - inversionCorrectionFactor.toBigInt(&refFactorInt) - - if refFactorInt.Cmp(factorInt) != 0 { - t.Error("mismatch") - } -} - -func TestElementLinearComb(t *testing.T) { - var x Element - var y Element - - for i := 0; i < 1000; i++ { - x.SetRandom() - y.SetRandom() - testLinearComb(t, &x, mrand.Int63(), &y, mrand.Int63()) //#nosec G404 weak rng is fine here - } -} - -// Probably unnecessary post-dev. In case the output of inv is wrong, this checks whether it's only off by a constant factor. -func TestElementInversionCorrectionFactor(t *testing.T) { - - // (1/x)/inv(x) = (1/1)/inv(1) ⇔ inv(1) = x inv(x) - - var one Element - var oneInv Element - one.SetOne() - oneInv.Inverse(&one) - - for i := 0; i < 100; i++ { - var x Element - var xInv Element - x.SetRandom() - xInv.Inverse(&x) - - x.Mul(&x, &xInv) - if !x.Equal(&oneInv) { - t.Error("Correction factor is inconsistent") - } - } - - if !oneInv.Equal(&one) { - var i big.Int - oneInv.BigInt(&i) // no montgomery - i.ModInverse(&i, Modulus()) - var fac Element - fac.setBigInt(&i) // back to montgomery - - var facTimesFac Element - facTimesFac.Mul(&fac, &Element{ - inversionCorrectionFactorWord0, - inversionCorrectionFactorWord1, - inversionCorrectionFactorWord2, - inversionCorrectionFactorWord3, - inversionCorrectionFactorWord4, - inversionCorrectionFactorWord5, - }) - - t.Error("Correction factor is consistently off by", fac, "Should be", facTimesFac) - } -} - -func TestElementBigNumNeg(t *testing.T) { - var a Element - aHi := negL(&a, 0) - if !a.IsZero() || aHi != 0 { - t.Error("-0 != 0") - } -} - -func TestElementBigNumWMul(t *testing.T) { - var x Element - - for i := 0; i < 1000; i++ { - x.SetRandom() - w := mrand.Int63() //#nosec G404 weak rng is fine here - testBigNumWMul(t, &x, w) - } -} - -func TestElementVeryBigIntConversion(t *testing.T) { - xHi := mrand.Uint64() //#nosec G404 weak rng is fine here - var x Element - x.SetRandom() - var xInt big.Int - x.toVeryBigIntSigned(&xInt, xHi) - x.assertMatchVeryBigInt(t, xHi, &xInt) -} - -type veryBigInt struct { - asInt big.Int - low Element - hi uint64 -} - -// genVeryBigIntSigned if sign == 0, no sign is forced -func genVeryBigIntSigned(sign int) gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var g veryBigInt - - g.low = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - - g.hi = genParams.NextUint64() - - if sign < 0 { - g.hi |= signBitSelector - } else if sign > 0 { - g.hi &= ^signBitSelector - } - - g.low.toVeryBigIntSigned(&g.asInt, g.hi) - - genResult := gopter.NewGenResult(g, gopter.NoShrinker) - return genResult - } -} - -func TestElementMontReduce(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - gen := genVeryBigIntSigned(0) - - properties.Property("Montgomery reduction is correct", prop.ForAll( - func(g veryBigInt) bool { - var res Element - var resInt big.Int - - montReduce(&resInt, &g.asInt) - res.montReduceSigned(&g.low, g.hi) - - return res.matchVeryBigInt(0, &resInt) == nil - }, - gen, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementMontReduceMultipleOfR(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - gen := ggen.UInt64() - - properties.Property("Montgomery reduction is correct", prop.ForAll( - func(hi uint64) bool { - var zero, res Element - var asInt, resInt big.Int - - zero.toVeryBigIntSigned(&asInt, hi) - - montReduce(&resInt, &asInt) - res.montReduceSigned(&zero, hi) - - return res.matchVeryBigInt(0, &resInt) == nil - }, - gen, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElement0Inverse(t *testing.T) { - var x Element - x.Inverse(&x) - if !x.IsZero() { - t.Fail() - } -} - -// TODO: Tests like this (update factor related) are common to all fields. Move them to somewhere non-autogen -func TestUpdateFactorSubtraction(t *testing.T) { - for i := 0; i < 1000; i++ { - - f0, g0 := randomizeUpdateFactors() - f1, g1 := randomizeUpdateFactors() - - for f0-f1 > 1<<31 || f0-f1 <= -1<<31 { - f1 /= 2 - } - - for g0-g1 > 1<<31 || g0-g1 <= -1<<31 { - g1 /= 2 - } - - c0 := updateFactorsCompose(f0, g0) - c1 := updateFactorsCompose(f1, g1) - - cRes := c0 - c1 - fRes, gRes := updateFactorsDecompose(cRes) - - if fRes != f0-f1 || gRes != g0-g1 { - t.Error(i) - } - } -} - -func TestUpdateFactorsDouble(t *testing.T) { - for i := 0; i < 1000; i++ { - f, g := randomizeUpdateFactors() - - if f > 1<<30 || f < (-1<<31+1)/2 { - f /= 2 - if g <= 1<<29 && g >= (-1<<31+1)/4 { - g *= 2 //g was kept small on f's account. Now that we're halving f, we can double g - } - } - - if g > 1<<30 || g < (-1<<31+1)/2 { - g /= 2 - - if f <= 1<<29 && f >= (-1<<31+1)/4 { - f *= 2 //f was kept small on g's account. Now that we're halving g, we can double f - } - } - - c := updateFactorsCompose(f, g) - cD := c * 2 - fD, gD := updateFactorsDecompose(cD) - - if fD != 2*f || gD != 2*g { - t.Error(i) - } - } -} - -func TestUpdateFactorsNeg(t *testing.T) { - var fMistake bool - for i := 0; i < 1000; i++ { - f, g := randomizeUpdateFactors() - - if f == 0x80000000 || g == 0x80000000 { - // Update factors this large can only have been obtained after 31 iterations and will therefore never be negated - // We don't have capacity to store -2³¹ - // Repeat this iteration - i-- - continue - } - - c := updateFactorsCompose(f, g) - nc := -c - nf, ng := updateFactorsDecompose(nc) - fMistake = fMistake || nf != -f - if nf != -f || ng != -g { - t.Errorf("Mismatch iteration #%d:\n%d, %d ->\n %d -> %d ->\n %d, %d\n Inputs in hex: %X, %X", - i, f, g, c, nc, nf, ng, f, g) - } - } - if fMistake { - t.Error("Mistake with f detected") - } else { - t.Log("All good with f") - } -} - -func TestUpdateFactorsNeg0(t *testing.T) { - c := updateFactorsCompose(0, 0) - t.Logf("c(0,0) = %X", c) - cn := -c - - if c != cn { - t.Error("Negation of zero update factors should yield the same result.") - } -} - -func TestUpdateFactorDecomposition(t *testing.T) { - var negSeen bool - - for i := 0; i < 1000; i++ { - - f, g := randomizeUpdateFactors() - - if f <= -(1<<31) || f > 1<<31 { - t.Fatal("f out of range") - } - - negSeen = negSeen || f < 0 - - c := updateFactorsCompose(f, g) - - fBack, gBack := updateFactorsDecompose(c) - - if f != fBack || g != gBack { - t.Errorf("(%d, %d) -> %d -> (%d, %d)\n", f, g, c, fBack, gBack) - } - } - - if !negSeen { - t.Fatal("No negative f factors") - } -} - -func TestUpdateFactorInitialValues(t *testing.T) { - - f0, g0 := updateFactorsDecompose(updateFactorIdentityMatrixRow0) - f1, g1 := updateFactorsDecompose(updateFactorIdentityMatrixRow1) - - if f0 != 1 || g0 != 0 || f1 != 0 || g1 != 1 { - t.Error("Update factor initial value constants are incorrect") - } -} - -func TestUpdateFactorsRandomization(t *testing.T) { - var maxLen int - - //t.Log("|f| + |g| is not to exceed", 1 << 31) - for i := 0; i < 1000; i++ { - f, g := randomizeUpdateFactors() - lf, lg := abs64T32(f), abs64T32(g) - absSum := lf + lg - if absSum >= 1<<31 { - - if absSum == 1<<31 { - maxLen++ - } else { - t.Error(i, "Sum of absolute values too large, f =", f, ",g =", g, ",|f| + |g| =", absSum) - } - } - } - - if maxLen == 0 { - t.Error("max len not observed") - } else { - t.Log(maxLen, "maxLens observed") - } -} - -func randomizeUpdateFactor(absLimit uint32) int64 { - const maxSizeLikelihood = 10 - maxSize := mrand.Intn(maxSizeLikelihood) //#nosec G404 weak rng is fine here - - absLimit64 := int64(absLimit) - var f int64 - switch maxSize { - case 0: - f = absLimit64 - case 1: - f = -absLimit64 - default: - f = int64(mrand.Uint64()%(2*uint64(absLimit64)+1)) - absLimit64 //#nosec G404 weak rng is fine here - } - - if f > 1<<31 { - return 1 << 31 - } else if f < -1<<31+1 { - return -1<<31 + 1 - } - - return f -} - -func abs64T32(f int64) uint32 { - if f >= 1<<32 || f < -1<<32 { - panic("f out of range") - } - - if f < 0 { - return uint32(-f) - } - return uint32(f) -} - -func randomizeUpdateFactors() (int64, int64) { - var f [2]int64 - b := mrand.Int() % 2 //#nosec G404 weak rng is fine here - - f[b] = randomizeUpdateFactor(1 << 31) - - //As per the paper, |f| + |g| \le 2³¹. - f[1-b] = randomizeUpdateFactor(1<<31 - abs64T32(f[b])) - - //Patching another edge case - if f[0]+f[1] == -1<<31 { - b = mrand.Int() % 2 //#nosec G404 weak rng is fine here - f[b]++ - } - - return f[0], f[1] -} - -func testLinearComb(t *testing.T, x *Element, xC int64, y *Element, yC int64) { - - var p1 big.Int - x.toBigInt(&p1) - p1.Mul(&p1, big.NewInt(xC)) - - var p2 big.Int - y.toBigInt(&p2) - p2.Mul(&p2, big.NewInt(yC)) - - p1.Add(&p1, &p2) - p1.Mod(&p1, Modulus()) - montReduce(&p1, &p1) - - var z Element - z.linearComb(x, xC, y, yC) - z.assertMatchVeryBigInt(t, 0, &p1) -} - -func testBigNumWMul(t *testing.T, a *Element, c int64) { - var aHi uint64 - var aTimes Element - aHi = aTimes.mulWNonModular(a, c) - - assertMulProduct(t, a, c, &aTimes, aHi) -} - -func updateFactorsCompose(f int64, g int64) int64 { - return f + g<<32 -} - -var rInv big.Int - -func montReduce(res *big.Int, x *big.Int) { - if rInv.BitLen() == 0 { // initialization - rInv.SetUint64(1) - rInv.Lsh(&rInv, Limbs*64) - rInv.ModInverse(&rInv, Modulus()) - } - res.Mul(x, &rInv) - res.Mod(res, Modulus()) -} - -func (z *Element) toVeryBigIntUnsigned(i *big.Int, xHi uint64) { - z.toBigInt(i) - var upperWord big.Int - upperWord.SetUint64(xHi) - upperWord.Lsh(&upperWord, Limbs*64) - i.Add(&upperWord, i) -} - -func (z *Element) toVeryBigIntSigned(i *big.Int, xHi uint64) { - z.toVeryBigIntUnsigned(i, xHi) - if signBitSelector&xHi != 0 { - twosCompModulus := big.NewInt(1) - twosCompModulus.Lsh(twosCompModulus, (Limbs+1)*64) - i.Sub(i, twosCompModulus) - } -} - -func assertMulProduct(t *testing.T, x *Element, c int64, result *Element, resultHi uint64) big.Int { - var xInt big.Int - x.toBigInt(&xInt) - - xInt.Mul(&xInt, big.NewInt(c)) - - result.assertMatchVeryBigInt(t, resultHi, &xInt) - return xInt -} - -func approximateRef(x *Element) uint64 { - - var asInt big.Int - x.toBigInt(&asInt) - n := x.BitLen() - - if n <= 64 { - return asInt.Uint64() - } - - modulus := big.NewInt(1 << 31) - var lo big.Int - lo.Mod(&asInt, modulus) - - modulus.Lsh(modulus, uint(n-64)) - var hi big.Int - hi.Div(&asInt, modulus) - hi.Lsh(&hi, 31) - - hi.Add(&hi, &lo) - return hi.Uint64() -} diff --git a/ecc/bls12-378/fp/hash_to_field/doc.go b/ecc/bls12-378/fp/hash_to_field/doc.go deleted file mode 100644 index f29458a500..0000000000 --- a/ecc/bls12-378/fp/hash_to_field/doc.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package htf provides hasher based on RFC 9380 Section 5. -// -// The [RFC 9380] defines a method for hashing bytes to elliptic curves. Section -// 5 of the RFC describes a method for uniformly hashing bytes into a field -// using a domain separation. The hashing is implemented in [fp], but this -// package provides a wrapper for the method which implements [hash.Hash] for -// using the method recursively. -// -// [RFC 9380]: https://datatracker.ietf.org/doc/html/rfc9380 -package hash_to_field - -import ( - _ "hash" - - _ "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" -) diff --git a/ecc/bls12-378/fp/hash_to_field/hash_to_field.go b/ecc/bls12-378/fp/hash_to_field/hash_to_field.go deleted file mode 100644 index 7a1e30c0a7..0000000000 --- a/ecc/bls12-378/fp/hash_to_field/hash_to_field.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package hash_to_field - -import ( - "fmt" - "hash" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" -) - -type wrappedHashToField struct { - domain []byte - toHash []byte -} - -// New returns a new hasher instance which uses [fp.Hash] to hash all the -// written bytes to a field element, returning the byte representation of the -// field element. The domain separator is passed as-is to hashing method. -func New(domainSeparator []byte) hash.Hash { - return &wrappedHashToField{ - domain: append([]byte{}, domainSeparator...), // copy in case the argument is modified - } -} - -func (w *wrappedHashToField) Write(p []byte) (n int, err error) { - w.toHash = append(w.toHash, p...) - return len(p), nil -} - -func (w *wrappedHashToField) Sum(b []byte) []byte { - res, err := fp.Hash(w.toHash, w.domain, 1) - if err != nil { - // we want to follow the interface, cannot return error and have to panic - // but by default the method shouldn't return an error internally - panic(fmt.Sprintf("native field to hash: %v", err)) - } - bts := res[0].Bytes() - return append(b, bts[:]...) -} - -func (w *wrappedHashToField) Reset() { - w.toHash = nil -} - -func (w *wrappedHashToField) Size() int { - return fp.Bytes -} - -func (w *wrappedHashToField) BlockSize() int { - return fp.Bytes -} diff --git a/ecc/bls12-378/fp/hash_to_field/hash_to_field_test.go b/ecc/bls12-378/fp/hash_to_field/hash_to_field_test.go deleted file mode 100644 index 6384d7ae87..0000000000 --- a/ecc/bls12-378/fp/hash_to_field/hash_to_field_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package hash_to_field - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" -) - -func TestHashInterface(t *testing.T) { - msg := []byte("test") - sep := []byte("separator") - res, err := fp.Hash(msg, sep, 1) - if err != nil { - t.Fatal("hash to field", err) - } - - htfFn := New(sep) - htfFn.Write(msg) - bts := htfFn.Sum(nil) - var res2 fp.Element - res2.SetBytes(bts[:fp.Bytes]) - if !res[0].Equal(&res2) { - t.Error("not equal") - } -} diff --git a/ecc/bls12-378/fp/vector.go b/ecc/bls12-378/fp/vector.go deleted file mode 100644 index 3fe1137102..0000000000 --- a/ecc/bls12-378/fp/vector.go +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" - "runtime" - "strings" - "sync" - "sync/atomic" - "unsafe" -) - -// Vector represents a slice of Element. -// -// It implements the following interfaces: -// - Stringer -// - io.WriterTo -// - io.ReaderFrom -// - encoding.BinaryMarshaler -// - encoding.BinaryUnmarshaler -// - sort.Interface -type Vector []Element - -// MarshalBinary implements encoding.BinaryMarshaler -func (vector *Vector) MarshalBinary() (data []byte, err error) { - var buf bytes.Buffer - - if _, err = vector.WriteTo(&buf); err != nil { - return - } - return buf.Bytes(), nil -} - -// UnmarshalBinary implements encoding.BinaryUnmarshaler -func (vector *Vector) UnmarshalBinary(data []byte) error { - r := bytes.NewReader(data) - _, err := vector.ReadFrom(r) - return err -} - -// WriteTo implements io.WriterTo and writes a vector of big endian encoded Element. -// Length of the vector is encoded as a uint32 on the first 4 bytes. -func (vector *Vector) WriteTo(w io.Writer) (int64, error) { - // encode slice length - if err := binary.Write(w, binary.BigEndian, uint32(len(*vector))); err != nil { - return 0, err - } - - n := int64(4) - - var buf [Bytes]byte - for i := 0; i < len(*vector); i++ { - BigEndian.PutElement(&buf, (*vector)[i]) - m, err := w.Write(buf[:]) - n += int64(m) - if err != nil { - return n, err - } - } - return n, nil -} - -// AsyncReadFrom reads a vector of big endian encoded Element. -// Length of the vector must be encoded as a uint32 on the first 4 bytes. -// It consumes the needed bytes from the reader and returns the number of bytes read and an error if any. -// It also returns a channel that will be closed when the validation is done. -// The validation consist of checking that the elements are smaller than the modulus, and -// converting them to montgomery form. -func (vector *Vector) AsyncReadFrom(r io.Reader) (int64, error, chan error) { - chErr := make(chan error, 1) - var buf [Bytes]byte - if read, err := io.ReadFull(r, buf[:4]); err != nil { - close(chErr) - return int64(read), err, chErr - } - sliceLen := binary.BigEndian.Uint32(buf[:4]) - - n := int64(4) - (*vector) = make(Vector, sliceLen) - if sliceLen == 0 { - close(chErr) - return n, nil, chErr - } - - bSlice := unsafe.Slice((*byte)(unsafe.Pointer(&(*vector)[0])), sliceLen*Bytes) - read, err := io.ReadFull(r, bSlice) - n += int64(read) - if err != nil { - close(chErr) - return n, err, chErr - } - - go func() { - var cptErrors uint64 - // process the elements in parallel - execute(int(sliceLen), func(start, end int) { - - var z Element - for i := start; i < end; i++ { - // we have to set vector[i] - bstart := i * Bytes - bend := bstart + Bytes - b := bSlice[bstart:bend] - z[0] = binary.BigEndian.Uint64(b[40:48]) - z[1] = binary.BigEndian.Uint64(b[32:40]) - z[2] = binary.BigEndian.Uint64(b[24:32]) - z[3] = binary.BigEndian.Uint64(b[16:24]) - z[4] = binary.BigEndian.Uint64(b[8:16]) - z[5] = binary.BigEndian.Uint64(b[0:8]) - - if !z.smallerThanModulus() { - atomic.AddUint64(&cptErrors, 1) - return - } - z.toMont() - (*vector)[i] = z - } - }) - - if cptErrors > 0 { - chErr <- fmt.Errorf("async read: %d elements failed validation", cptErrors) - } - close(chErr) - }() - return n, nil, chErr -} - -// ReadFrom implements io.ReaderFrom and reads a vector of big endian encoded Element. -// Length of the vector must be encoded as a uint32 on the first 4 bytes. -func (vector *Vector) ReadFrom(r io.Reader) (int64, error) { - - var buf [Bytes]byte - if read, err := io.ReadFull(r, buf[:4]); err != nil { - return int64(read), err - } - sliceLen := binary.BigEndian.Uint32(buf[:4]) - - n := int64(4) - (*vector) = make(Vector, sliceLen) - - for i := 0; i < int(sliceLen); i++ { - read, err := io.ReadFull(r, buf[:]) - n += int64(read) - if err != nil { - return n, err - } - (*vector)[i], err = BigEndian.Element(&buf) - if err != nil { - return n, err - } - } - - return n, nil -} - -// String implements fmt.Stringer interface -func (vector Vector) String() string { - var sbb strings.Builder - sbb.WriteByte('[') - for i := 0; i < len(vector); i++ { - sbb.WriteString(vector[i].String()) - if i != len(vector)-1 { - sbb.WriteByte(',') - } - } - sbb.WriteByte(']') - return sbb.String() -} - -// Len is the number of elements in the collection. -func (vector Vector) Len() int { - return len(vector) -} - -// Less reports whether the element with -// index i should sort before the element with index j. -func (vector Vector) Less(i, j int) bool { - return vector[i].Cmp(&vector[j]) == -1 -} - -// Swap swaps the elements with indexes i and j. -func (vector Vector) Swap(i, j int) { - vector[i], vector[j] = vector[j], vector[i] -} - -// TODO @gbotrel make a public package out of that. -// execute executes the work function in parallel. -// this is copy paste from internal/parallel/parallel.go -// as we don't want to generate code importing internal/ -func execute(nbIterations int, work func(int, int), maxCpus ...int) { - - nbTasks := runtime.NumCPU() - if len(maxCpus) == 1 { - nbTasks = maxCpus[0] - if nbTasks < 1 { - nbTasks = 1 - } else if nbTasks > 512 { - nbTasks = 512 - } - } - - if nbTasks == 1 { - // no go routines - work(0, nbIterations) - return - } - - nbIterationsPerCpus := nbIterations / nbTasks - - // more CPUs than tasks: a CPU will work on exactly one iteration - if nbIterationsPerCpus < 1 { - nbIterationsPerCpus = 1 - nbTasks = nbIterations - } - - var wg sync.WaitGroup - - extraTasks := nbIterations - (nbTasks * nbIterationsPerCpus) - extraTasksOffset := 0 - - for i := 0; i < nbTasks; i++ { - wg.Add(1) - _start := i*nbIterationsPerCpus + extraTasksOffset - _end := _start + nbIterationsPerCpus - if extraTasks > 0 { - _end++ - extraTasks-- - extraTasksOffset++ - } - go func() { - work(_start, _end) - wg.Done() - }() - } - - wg.Wait() -} diff --git a/ecc/bls12-378/fp/vector_test.go b/ecc/bls12-378/fp/vector_test.go deleted file mode 100644 index 5d88af91c0..0000000000 --- a/ecc/bls12-378/fp/vector_test.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -import ( - "bytes" - "github.com/stretchr/testify/require" - "reflect" - "sort" - "testing" -) - -func TestVectorSort(t *testing.T) { - assert := require.New(t) - - v := make(Vector, 3) - v[0].SetUint64(2) - v[1].SetUint64(3) - v[2].SetUint64(1) - - sort.Sort(v) - - assert.Equal("[1,2,3]", v.String()) -} - -func TestVectorRoundTrip(t *testing.T) { - assert := require.New(t) - - v1 := make(Vector, 3) - v1[0].SetUint64(2) - v1[1].SetUint64(3) - v1[2].SetUint64(1) - - b, err := v1.MarshalBinary() - assert.NoError(err) - - var v2, v3 Vector - - err = v2.UnmarshalBinary(b) - assert.NoError(err) - - err = v3.unmarshalBinaryAsync(b) - assert.NoError(err) - - assert.True(reflect.DeepEqual(v1, v2)) - assert.True(reflect.DeepEqual(v3, v2)) -} - -func TestVectorEmptyRoundTrip(t *testing.T) { - assert := require.New(t) - - v1 := make(Vector, 0) - - b, err := v1.MarshalBinary() - assert.NoError(err) - - var v2, v3 Vector - - err = v2.UnmarshalBinary(b) - assert.NoError(err) - - err = v3.unmarshalBinaryAsync(b) - assert.NoError(err) - - assert.True(reflect.DeepEqual(v1, v2)) - assert.True(reflect.DeepEqual(v3, v2)) -} - -func (vector *Vector) unmarshalBinaryAsync(data []byte) error { - r := bytes.NewReader(data) - _, err, chErr := vector.AsyncReadFrom(r) - if err != nil { - return err - } - return <-chErr -} diff --git a/ecc/bls12-378/fr/arith.go b/ecc/bls12-378/fr/arith.go deleted file mode 100644 index 7cfd55da19..0000000000 --- a/ecc/bls12-378/fr/arith.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import ( - "math/bits" -) - -// madd0 hi = a*b + c (discards lo bits) -func madd0(a, b, c uint64) (hi uint64) { - var carry, lo uint64 - hi, lo = bits.Mul64(a, b) - _, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - return -} - -// madd1 hi, lo = a*b + c -func madd1(a, b, c uint64) (hi uint64, lo uint64) { - var carry uint64 - hi, lo = bits.Mul64(a, b) - lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - return -} - -// madd2 hi, lo = a*b + c + d -func madd2(a, b, c, d uint64) (hi uint64, lo uint64) { - var carry uint64 - hi, lo = bits.Mul64(a, b) - c, carry = bits.Add64(c, d, 0) - hi, _ = bits.Add64(hi, 0, carry) - lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - return -} - -func madd3(a, b, c, d, e uint64) (hi uint64, lo uint64) { - var carry uint64 - hi, lo = bits.Mul64(a, b) - c, carry = bits.Add64(c, d, 0) - hi, _ = bits.Add64(hi, 0, carry) - lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, e, carry) - return -} -func max(a int, b int) int { - if a > b { - return a - } - return b -} - -func min(a int, b int) int { - if a < b { - return a - } - return b -} diff --git a/ecc/bls12-378/fr/asm.go b/ecc/bls12-378/fr/asm.go deleted file mode 100644 index da061913ba..0000000000 --- a/ecc/bls12-378/fr/asm.go +++ /dev/null @@ -1,27 +0,0 @@ -//go:build !noadx -// +build !noadx - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import "golang.org/x/sys/cpu" - -var ( - supportAdx = cpu.X86.HasADX && cpu.X86.HasBMI2 - _ = supportAdx -) diff --git a/ecc/bls12-378/fr/asm_noadx.go b/ecc/bls12-378/fr/asm_noadx.go deleted file mode 100644 index 7f52ffa197..0000000000 --- a/ecc/bls12-378/fr/asm_noadx.go +++ /dev/null @@ -1,28 +0,0 @@ -//go:build noadx -// +build noadx - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -// note: this is needed for test purposes, as dynamically changing supportAdx doesn't flag -// certain errors (like fatal error: missing stackmap) -// this ensures we test all asm path. -var ( - supportAdx = false - _ = supportAdx -) diff --git a/ecc/bls12-378/fr/doc.go b/ecc/bls12-378/fr/doc.go deleted file mode 100644 index 5c0faae5cb..0000000000 --- a/ecc/bls12-378/fr/doc.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package fr contains field arithmetic operations for modulus = 0x20e7b9...000001. -// -// The API is similar to math/big (big.Int), but the operations are significantly faster (up to 20x for the modular multiplication on amd64, see also https://hackmd.io/@gnark/modular_multiplication) -// -// The modulus is hardcoded in all the operations. -// -// Field elements are represented as an array, and assumed to be in Montgomery form in all methods: -// -// type Element [4]uint64 -// -// # Usage -// -// Example API signature: -// -// // Mul z = x * y (mod q) -// func (z *Element) Mul(x, y *Element) *Element -// -// and can be used like so: -// -// var a, b Element -// a.SetUint64(2) -// b.SetString("984896738") -// a.Mul(a, b) -// a.Sub(a, a) -// .Add(a, b) -// .Inv(a) -// b.Exp(b, new(big.Int).SetUint64(42)) -// -// Modulus q = -// -// q[base10] = 14883435066912132899950318861128167269793560281114003360875131245101026639873 -// q[base16] = 0x20e7b9c8ef7b2eb187787fb4e3dbb0ffeae77f3da09400013291440000000001 -// -// # Warning -// -// This code has not been audited and is provided as-is. In particular, there is no security guarantees such as constant time implementation or side-channel attack resistance. -package fr diff --git a/ecc/bls12-378/fr/element.go b/ecc/bls12-378/fr/element.go deleted file mode 100644 index 6f0cca28e3..0000000000 --- a/ecc/bls12-378/fr/element.go +++ /dev/null @@ -1,1620 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import ( - "crypto/rand" - "encoding/binary" - "errors" - "io" - "math/big" - "math/bits" - "reflect" - "strconv" - "strings" - - "github.com/bits-and-blooms/bitset" - "github.com/consensys/gnark-crypto/field/hash" - "github.com/consensys/gnark-crypto/field/pool" -) - -// Element represents a field element stored on 4 words (uint64) -// -// Element are assumed to be in Montgomery form in all methods. -// -// Modulus q = -// -// q[base10] = 14883435066912132899950318861128167269793560281114003360875131245101026639873 -// q[base16] = 0x20e7b9c8ef7b2eb187787fb4e3dbb0ffeae77f3da09400013291440000000001 -// -// # Warning -// -// This code has not been audited and is provided as-is. In particular, there is no security guarantees such as constant time implementation or side-channel attack resistance. -type Element [4]uint64 - -const ( - Limbs = 4 // number of 64 bits words needed to represent a Element - Bits = 254 // number of bits needed to represent a Element - Bytes = 32 // number of bytes needed to represent a Element -) - -// Field modulus q -const ( - q0 uint64 = 3643768340310130689 - q1 uint64 = 16926637627159085057 - q2 uint64 = 9761692607219216639 - q3 uint64 = 2371068001496280753 -) - -var qElement = Element{ - q0, - q1, - q2, - q3, -} - -var _modulus big.Int // q stored as big.Int - -// Modulus returns q as a big.Int -// -// q[base10] = 14883435066912132899950318861128167269793560281114003360875131245101026639873 -// q[base16] = 0x20e7b9c8ef7b2eb187787fb4e3dbb0ffeae77f3da09400013291440000000001 -func Modulus() *big.Int { - return new(big.Int).Set(&_modulus) -} - -// q + r'.r = 1, i.e., qInvNeg = - q⁻¹ mod r -// used for Montgomery reduction -const qInvNeg uint64 = 3643768340310130687 - -func init() { - _modulus.SetString("20e7b9c8ef7b2eb187787fb4e3dbb0ffeae77f3da09400013291440000000001", 16) -} - -// NewElement returns a new Element from a uint64 value -// -// it is equivalent to -// -// var v Element -// v.SetUint64(...) -func NewElement(v uint64) Element { - z := Element{v} - z.Mul(&z, &rSquare) - return z -} - -// SetUint64 sets z to v and returns z -func (z *Element) SetUint64(v uint64) *Element { - // sets z LSB to v (non-Montgomery form) and convert z to Montgomery form - *z = Element{v} - return z.Mul(z, &rSquare) // z.toMont() -} - -// SetInt64 sets z to v and returns z -func (z *Element) SetInt64(v int64) *Element { - - // absolute value of v - m := v >> 63 - z.SetUint64(uint64((v ^ m) - m)) - - if m != 0 { - // v is negative - z.Neg(z) - } - - return z -} - -// Set z = x and returns z -func (z *Element) Set(x *Element) *Element { - z[0] = x[0] - z[1] = x[1] - z[2] = x[2] - z[3] = x[3] - return z -} - -// SetInterface converts provided interface into Element -// returns an error if provided type is not supported -// supported types: -// -// Element -// *Element -// uint64 -// int -// string (see SetString for valid formats) -// *big.Int -// big.Int -// []byte -func (z *Element) SetInterface(i1 interface{}) (*Element, error) { - if i1 == nil { - return nil, errors.New("can't set fr.Element with ") - } - - switch c1 := i1.(type) { - case Element: - return z.Set(&c1), nil - case *Element: - if c1 == nil { - return nil, errors.New("can't set fr.Element with ") - } - return z.Set(c1), nil - case uint8: - return z.SetUint64(uint64(c1)), nil - case uint16: - return z.SetUint64(uint64(c1)), nil - case uint32: - return z.SetUint64(uint64(c1)), nil - case uint: - return z.SetUint64(uint64(c1)), nil - case uint64: - return z.SetUint64(c1), nil - case int8: - return z.SetInt64(int64(c1)), nil - case int16: - return z.SetInt64(int64(c1)), nil - case int32: - return z.SetInt64(int64(c1)), nil - case int64: - return z.SetInt64(c1), nil - case int: - return z.SetInt64(int64(c1)), nil - case string: - return z.SetString(c1) - case *big.Int: - if c1 == nil { - return nil, errors.New("can't set fr.Element with ") - } - return z.SetBigInt(c1), nil - case big.Int: - return z.SetBigInt(&c1), nil - case []byte: - return z.SetBytes(c1), nil - default: - return nil, errors.New("can't set fr.Element from type " + reflect.TypeOf(i1).String()) - } -} - -// SetZero z = 0 -func (z *Element) SetZero() *Element { - z[0] = 0 - z[1] = 0 - z[2] = 0 - z[3] = 0 - return z -} - -// SetOne z = 1 (in Montgomery form) -func (z *Element) SetOne() *Element { - z[0] = 11387109765248188409 - z[1] = 10640745125853265911 - z[2] = 5455128044303689984 - z[3] = 1849268063235586341 - return z -} - -// Div z = x*y⁻¹ (mod q) -func (z *Element) Div(x, y *Element) *Element { - var yInv Element - yInv.Inverse(y) - z.Mul(x, &yInv) - return z -} - -// Equal returns z == x; constant-time -func (z *Element) Equal(x *Element) bool { - return z.NotEqual(x) == 0 -} - -// NotEqual returns 0 if and only if z == x; constant-time -func (z *Element) NotEqual(x *Element) uint64 { - return (z[3] ^ x[3]) | (z[2] ^ x[2]) | (z[1] ^ x[1]) | (z[0] ^ x[0]) -} - -// IsZero returns z == 0 -func (z *Element) IsZero() bool { - return (z[3] | z[2] | z[1] | z[0]) == 0 -} - -// IsOne returns z == 1 -func (z *Element) IsOne() bool { - return ((z[3] ^ 1849268063235586341) | (z[2] ^ 5455128044303689984) | (z[1] ^ 10640745125853265911) | (z[0] ^ 11387109765248188409)) == 0 -} - -// IsUint64 reports whether z can be represented as an uint64. -func (z *Element) IsUint64() bool { - zz := *z - zz.fromMont() - return zz.FitsOnOneWord() -} - -// Uint64 returns the uint64 representation of x. If x cannot be represented in a uint64, the result is undefined. -func (z *Element) Uint64() uint64 { - return z.Bits()[0] -} - -// FitsOnOneWord reports whether z words (except the least significant word) are 0 -// -// It is the responsibility of the caller to convert from Montgomery to Regular form if needed. -func (z *Element) FitsOnOneWord() bool { - return (z[3] | z[2] | z[1]) == 0 -} - -// Cmp compares (lexicographic order) z and x and returns: -// -// -1 if z < x -// 0 if z == x -// +1 if z > x -func (z *Element) Cmp(x *Element) int { - _z := z.Bits() - _x := x.Bits() - if _z[3] > _x[3] { - return 1 - } else if _z[3] < _x[3] { - return -1 - } - if _z[2] > _x[2] { - return 1 - } else if _z[2] < _x[2] { - return -1 - } - if _z[1] > _x[1] { - return 1 - } else if _z[1] < _x[1] { - return -1 - } - if _z[0] > _x[0] { - return 1 - } else if _z[0] < _x[0] { - return -1 - } - return 0 -} - -// LexicographicallyLargest returns true if this element is strictly lexicographically -// larger than its negation, false otherwise -func (z *Element) LexicographicallyLargest() bool { - // adapted from github.com/zkcrypto/bls12_381 - // we check if the element is larger than (q-1) / 2 - // if z - (((q -1) / 2) + 1) have no underflow, then z > (q-1) / 2 - - _z := z.Bits() - - var b uint64 - _, b = bits.Sub64(_z[0], 11045256207009841153, 0) - _, b = bits.Sub64(_z[1], 17686690850434318336, b) - _, b = bits.Sub64(_z[2], 14104218340464384127, b) - _, b = bits.Sub64(_z[3], 1185534000748140376, b) - - return b == 0 -} - -// SetRandom sets z to a uniform random value in [0, q). -// -// This might error only if reading from crypto/rand.Reader errors, -// in which case, value of z is undefined. -func (z *Element) SetRandom() (*Element, error) { - // this code is generated for all modulus - // and derived from go/src/crypto/rand/util.go - - // l is number of limbs * 8; the number of bytes needed to reconstruct 4 uint64 - const l = 32 - - // bitLen is the maximum bit length needed to encode a value < q. - const bitLen = 254 - - // k is the maximum byte length needed to encode a value < q. - const k = (bitLen + 7) / 8 - - // b is the number of bits in the most significant byte of q-1. - b := uint(bitLen % 8) - if b == 0 { - b = 8 - } - - var bytes [l]byte - - for { - // note that bytes[k:l] is always 0 - if _, err := io.ReadFull(rand.Reader, bytes[:k]); err != nil { - return nil, err - } - - // Clear unused bits in in the most significant byte to increase probability - // that the candidate is < q. - bytes[k-1] &= uint8(int(1<> 1 - z[0] = z[0]>>1 | z[1]<<63 - z[1] = z[1]>>1 | z[2]<<63 - z[2] = z[2]>>1 | z[3]<<63 - z[3] >>= 1 - -} - -// fromMont converts z in place (i.e. mutates) from Montgomery to regular representation -// sets and returns z = z * 1 -func (z *Element) fromMont() *Element { - fromMont(z) - return z -} - -// Add z = x + y (mod q) -func (z *Element) Add(x, y *Element) *Element { - - var carry uint64 - z[0], carry = bits.Add64(x[0], y[0], 0) - z[1], carry = bits.Add64(x[1], y[1], carry) - z[2], carry = bits.Add64(x[2], y[2], carry) - z[3], _ = bits.Add64(x[3], y[3], carry) - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], _ = bits.Sub64(z[3], q3, b) - } - return z -} - -// Double z = x + x (mod q), aka Lsh 1 -func (z *Element) Double(x *Element) *Element { - - var carry uint64 - z[0], carry = bits.Add64(x[0], x[0], 0) - z[1], carry = bits.Add64(x[1], x[1], carry) - z[2], carry = bits.Add64(x[2], x[2], carry) - z[3], _ = bits.Add64(x[3], x[3], carry) - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], _ = bits.Sub64(z[3], q3, b) - } - return z -} - -// Sub z = x - y (mod q) -func (z *Element) Sub(x, y *Element) *Element { - var b uint64 - z[0], b = bits.Sub64(x[0], y[0], 0) - z[1], b = bits.Sub64(x[1], y[1], b) - z[2], b = bits.Sub64(x[2], y[2], b) - z[3], b = bits.Sub64(x[3], y[3], b) - if b != 0 { - var c uint64 - z[0], c = bits.Add64(z[0], q0, 0) - z[1], c = bits.Add64(z[1], q1, c) - z[2], c = bits.Add64(z[2], q2, c) - z[3], _ = bits.Add64(z[3], q3, c) - } - return z -} - -// Neg z = q - x -func (z *Element) Neg(x *Element) *Element { - if x.IsZero() { - z.SetZero() - return z - } - var borrow uint64 - z[0], borrow = bits.Sub64(q0, x[0], 0) - z[1], borrow = bits.Sub64(q1, x[1], borrow) - z[2], borrow = bits.Sub64(q2, x[2], borrow) - z[3], _ = bits.Sub64(q3, x[3], borrow) - return z -} - -// Select is a constant-time conditional move. -// If c=0, z = x0. Else z = x1 -func (z *Element) Select(c int, x0 *Element, x1 *Element) *Element { - cC := uint64((int64(c) | -int64(c)) >> 63) // "canonicized" into: 0 if c=0, -1 otherwise - z[0] = x0[0] ^ cC&(x0[0]^x1[0]) - z[1] = x0[1] ^ cC&(x0[1]^x1[1]) - z[2] = x0[2] ^ cC&(x0[2]^x1[2]) - z[3] = x0[3] ^ cC&(x0[3]^x1[3]) - return z -} - -// _mulGeneric is unoptimized textbook CIOS -// it is a fallback solution on x86 when ADX instruction set is not available -// and is used for testing purposes. -func _mulGeneric(z, x, y *Element) { - - // Implements CIOS multiplication -- section 2.3.2 of Tolga Acar's thesis - // https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf - // - // The algorithm: - // - // for i=0 to N-1 - // C := 0 - // for j=0 to N-1 - // (C,t[j]) := t[j] + x[j]*y[i] + C - // (t[N+1],t[N]) := t[N] + C - // - // C := 0 - // m := t[0]*q'[0] mod D - // (C,_) := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // (C,t[N-1]) := t[N] + C - // t[N] := t[N+1] + C - // - // → N is the number of machine words needed to store the modulus q - // → D is the word size. For example, on a 64-bit architecture D is 2 64 - // → x[i], y[i], q[i] is the ith word of the numbers x,y,q - // → q'[0] is the lowest word of the number -q⁻¹ mod r. This quantity is pre-computed, as it does not depend on the inputs. - // → t is a temporary array of size N+2 - // → C, S are machine words. A pair (C,S) refers to (hi-bits, lo-bits) of a two-word number - - var t [5]uint64 - var D uint64 - var m, C uint64 - // ----------------------------------- - // First loop - - C, t[0] = bits.Mul64(y[0], x[0]) - C, t[1] = madd1(y[0], x[1], C) - C, t[2] = madd1(y[0], x[2], C) - C, t[3] = madd1(y[0], x[3], C) - - t[4], D = bits.Add64(t[4], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - - t[3], C = bits.Add64(t[4], C, 0) - t[4], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[1], x[0], t[0]) - C, t[1] = madd2(y[1], x[1], t[1], C) - C, t[2] = madd2(y[1], x[2], t[2], C) - C, t[3] = madd2(y[1], x[3], t[3], C) - - t[4], D = bits.Add64(t[4], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - - t[3], C = bits.Add64(t[4], C, 0) - t[4], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[2], x[0], t[0]) - C, t[1] = madd2(y[2], x[1], t[1], C) - C, t[2] = madd2(y[2], x[2], t[2], C) - C, t[3] = madd2(y[2], x[3], t[3], C) - - t[4], D = bits.Add64(t[4], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - - t[3], C = bits.Add64(t[4], C, 0) - t[4], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[3], x[0], t[0]) - C, t[1] = madd2(y[3], x[1], t[1], C) - C, t[2] = madd2(y[3], x[2], t[2], C) - C, t[3] = madd2(y[3], x[3], t[3], C) - - t[4], D = bits.Add64(t[4], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - - t[3], C = bits.Add64(t[4], C, 0) - t[4], _ = bits.Add64(0, D, C) - - if t[4] != 0 { - // we need to reduce, we have a result on 5 words - var b uint64 - z[0], b = bits.Sub64(t[0], q0, 0) - z[1], b = bits.Sub64(t[1], q1, b) - z[2], b = bits.Sub64(t[2], q2, b) - z[3], _ = bits.Sub64(t[3], q3, b) - return - } - - // copy t into z - z[0] = t[0] - z[1] = t[1] - z[2] = t[2] - z[3] = t[3] - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], _ = bits.Sub64(z[3], q3, b) - } -} - -func _fromMontGeneric(z *Element) { - // the following lines implement z = z * 1 - // with a modified CIOS montgomery multiplication - // see Mul for algorithm documentation - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - z[3] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - z[3] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - z[3] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - z[3] = C - } - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], _ = bits.Sub64(z[3], q3, b) - } -} - -func _reduceGeneric(z *Element) { - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], _ = bits.Sub64(z[3], q3, b) - } -} - -// BatchInvert returns a new slice with every element inverted. -// Uses Montgomery batch inversion trick -func BatchInvert(a []Element) []Element { - res := make([]Element, len(a)) - if len(a) == 0 { - return res - } - - zeroes := bitset.New(uint(len(a))) - accumulator := One() - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - zeroes.Set(uint(i)) - continue - } - res[i] = accumulator - accumulator.Mul(&accumulator, &a[i]) - } - - accumulator.Inverse(&accumulator) - - for i := len(a) - 1; i >= 0; i-- { - if zeroes.Test(uint(i)) { - continue - } - res[i].Mul(&res[i], &accumulator) - accumulator.Mul(&accumulator, &a[i]) - } - - return res -} - -func _butterflyGeneric(a, b *Element) { - t := *a - a.Add(a, b) - b.Sub(&t, b) -} - -// BitLen returns the minimum number of bits needed to represent z -// returns 0 if z == 0 -func (z *Element) BitLen() int { - if z[3] != 0 { - return 192 + bits.Len64(z[3]) - } - if z[2] != 0 { - return 128 + bits.Len64(z[2]) - } - if z[1] != 0 { - return 64 + bits.Len64(z[1]) - } - return bits.Len64(z[0]) -} - -// Hash msg to count prime field elements. -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-5.2 -func Hash(msg, dst []byte, count int) ([]Element, error) { - // 128 bits of security - // L = ceil((ceil(log2(p)) + k) / 8), where k is the security parameter = 128 - const Bytes = 1 + (Bits-1)/8 - const L = 16 + Bytes - - lenInBytes := count * L - pseudoRandomBytes, err := hash.ExpandMsgXmd(msg, dst, lenInBytes) - if err != nil { - return nil, err - } - - // get temporary big int from the pool - vv := pool.BigInt.Get() - - res := make([]Element, count) - for i := 0; i < count; i++ { - vv.SetBytes(pseudoRandomBytes[i*L : (i+1)*L]) - res[i].SetBigInt(vv) - } - - // release object into pool - pool.BigInt.Put(vv) - - return res, nil -} - -// Exp z = xᵏ (mod q) -func (z *Element) Exp(x Element, k *big.Int) *Element { - if k.IsUint64() && k.Uint64() == 0 { - return z.SetOne() - } - - e := k - if k.Sign() == -1 { - // negative k, we invert - // if k < 0: xᵏ (mod q) == (x⁻¹)ᵏ (mod q) - x.Inverse(&x) - - // we negate k in a temp big.Int since - // Int.Bit(_) of k and -k is different - e = pool.BigInt.Get() - defer pool.BigInt.Put(e) - e.Neg(k) - } - - z.Set(&x) - - for i := e.BitLen() - 2; i >= 0; i-- { - z.Square(z) - if e.Bit(i) == 1 { - z.Mul(z, &x) - } - } - - return z -} - -// rSquare where r is the Montgommery constant -// see section 2.3.2 of Tolga Acar's thesis -// https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf -var rSquare = Element{ - 1260465344847950704, - 15627634503313390135, - 1085346480195626314, - 405261321576397495, -} - -// toMont converts z to Montgomery form -// sets and returns z = z * r² -func (z *Element) toMont() *Element { - return z.Mul(z, &rSquare) -} - -// String returns the decimal representation of z as generated by -// z.Text(10). -func (z *Element) String() string { - return z.Text(10) -} - -// toBigInt returns z as a big.Int in Montgomery form -func (z *Element) toBigInt(res *big.Int) *big.Int { - var b [Bytes]byte - binary.BigEndian.PutUint64(b[24:32], z[0]) - binary.BigEndian.PutUint64(b[16:24], z[1]) - binary.BigEndian.PutUint64(b[8:16], z[2]) - binary.BigEndian.PutUint64(b[0:8], z[3]) - - return res.SetBytes(b[:]) -} - -// Text returns the string representation of z in the given base. -// Base must be between 2 and 36, inclusive. The result uses the -// lower-case letters 'a' to 'z' for digit values 10 to 35. -// No prefix (such as "0x") is added to the string. If z is a nil -// pointer it returns "". -// If base == 10 and -z fits in a uint16 prefix "-" is added to the string. -func (z *Element) Text(base int) string { - if base < 2 || base > 36 { - panic("invalid base") - } - if z == nil { - return "" - } - - const maxUint16 = 65535 - if base == 10 { - var zzNeg Element - zzNeg.Neg(z) - zzNeg.fromMont() - if zzNeg.FitsOnOneWord() && zzNeg[0] <= maxUint16 && zzNeg[0] != 0 { - return "-" + strconv.FormatUint(zzNeg[0], base) - } - } - zz := *z - zz.fromMont() - if zz.FitsOnOneWord() { - return strconv.FormatUint(zz[0], base) - } - vv := pool.BigInt.Get() - r := zz.toBigInt(vv).Text(base) - pool.BigInt.Put(vv) - return r -} - -// BigInt sets and return z as a *big.Int -func (z *Element) BigInt(res *big.Int) *big.Int { - _z := *z - _z.fromMont() - return _z.toBigInt(res) -} - -// ToBigIntRegular returns z as a big.Int in regular form -// -// Deprecated: use BigInt(*big.Int) instead -func (z Element) ToBigIntRegular(res *big.Int) *big.Int { - z.fromMont() - return z.toBigInt(res) -} - -// Bits provides access to z by returning its value as a little-endian [4]uint64 array. -// Bits is intended to support implementation of missing low-level Element -// functionality outside this package; it should be avoided otherwise. -func (z *Element) Bits() [4]uint64 { - _z := *z - fromMont(&_z) - return _z -} - -// Bytes returns the value of z as a big-endian byte array -func (z *Element) Bytes() (res [Bytes]byte) { - BigEndian.PutElement(&res, *z) - return -} - -// Marshal returns the value of z as a big-endian byte slice -func (z *Element) Marshal() []byte { - b := z.Bytes() - return b[:] -} - -// Unmarshal is an alias for SetBytes, it sets z to the value of e. -func (z *Element) Unmarshal(e []byte) { - z.SetBytes(e) -} - -// SetBytes interprets e as the bytes of a big-endian unsigned integer, -// sets z to that value, and returns z. -func (z *Element) SetBytes(e []byte) *Element { - if len(e) == Bytes { - // fast path - v, err := BigEndian.Element((*[Bytes]byte)(e)) - if err == nil { - *z = v - return z - } - } - - // slow path. - // get a big int from our pool - vv := pool.BigInt.Get() - vv.SetBytes(e) - - // set big int - z.SetBigInt(vv) - - // put temporary object back in pool - pool.BigInt.Put(vv) - - return z -} - -// SetBytesCanonical interprets e as the bytes of a big-endian 32-byte integer. -// If e is not a 32-byte slice or encodes a value higher than q, -// SetBytesCanonical returns an error. -func (z *Element) SetBytesCanonical(e []byte) error { - if len(e) != Bytes { - return errors.New("invalid fr.Element encoding") - } - v, err := BigEndian.Element((*[Bytes]byte)(e)) - if err != nil { - return err - } - *z = v - return nil -} - -// SetBigInt sets z to v and returns z -func (z *Element) SetBigInt(v *big.Int) *Element { - z.SetZero() - - var zero big.Int - - // fast path - c := v.Cmp(&_modulus) - if c == 0 { - // v == 0 - return z - } else if c != 1 && v.Cmp(&zero) != -1 { - // 0 < v < q - return z.setBigInt(v) - } - - // get temporary big int from the pool - vv := pool.BigInt.Get() - - // copy input + modular reduction - vv.Mod(v, &_modulus) - - // set big int byte value - z.setBigInt(vv) - - // release object into pool - pool.BigInt.Put(vv) - return z -} - -// setBigInt assumes 0 ⩽ v < q -func (z *Element) setBigInt(v *big.Int) *Element { - vBits := v.Bits() - - if bits.UintSize == 64 { - for i := 0; i < len(vBits); i++ { - z[i] = uint64(vBits[i]) - } - } else { - for i := 0; i < len(vBits); i++ { - if i%2 == 0 { - z[i/2] = uint64(vBits[i]) - } else { - z[i/2] |= uint64(vBits[i]) << 32 - } - } - } - - return z.toMont() -} - -// SetString creates a big.Int with number and calls SetBigInt on z -// -// The number prefix determines the actual base: A prefix of -// ”0b” or ”0B” selects base 2, ”0”, ”0o” or ”0O” selects base 8, -// and ”0x” or ”0X” selects base 16. Otherwise, the selected base is 10 -// and no prefix is accepted. -// -// For base 16, lower and upper case letters are considered the same: -// The letters 'a' to 'f' and 'A' to 'F' represent digit values 10 to 15. -// -// An underscore character ”_” may appear between a base -// prefix and an adjacent digit, and between successive digits; such -// underscores do not change the value of the number. -// Incorrect placement of underscores is reported as a panic if there -// are no other errors. -// -// If the number is invalid this method leaves z unchanged and returns nil, error. -func (z *Element) SetString(number string) (*Element, error) { - // get temporary big int from the pool - vv := pool.BigInt.Get() - - if _, ok := vv.SetString(number, 0); !ok { - return nil, errors.New("Element.SetString failed -> can't parse number into a big.Int " + number) - } - - z.SetBigInt(vv) - - // release object into pool - pool.BigInt.Put(vv) - - return z, nil -} - -// MarshalJSON returns json encoding of z (z.Text(10)) -// If z == nil, returns null -func (z *Element) MarshalJSON() ([]byte, error) { - if z == nil { - return []byte("null"), nil - } - const maxSafeBound = 15 // we encode it as number if it's small - s := z.Text(10) - if len(s) <= maxSafeBound { - return []byte(s), nil - } - var sbb strings.Builder - sbb.WriteByte('"') - sbb.WriteString(s) - sbb.WriteByte('"') - return []byte(sbb.String()), nil -} - -// UnmarshalJSON accepts numbers and strings as input -// See Element.SetString for valid prefixes (0x, 0b, ...) -func (z *Element) UnmarshalJSON(data []byte) error { - s := string(data) - if len(s) > Bits*3 { - return errors.New("value too large (max = Element.Bits * 3)") - } - - // we accept numbers and strings, remove leading and trailing quotes if any - if len(s) > 0 && s[0] == '"' { - s = s[1:] - } - if len(s) > 0 && s[len(s)-1] == '"' { - s = s[:len(s)-1] - } - - // get temporary big int from the pool - vv := pool.BigInt.Get() - - if _, ok := vv.SetString(s, 0); !ok { - return errors.New("can't parse into a big.Int: " + s) - } - - z.SetBigInt(vv) - - // release object into pool - pool.BigInt.Put(vv) - return nil -} - -// A ByteOrder specifies how to convert byte slices into a Element -type ByteOrder interface { - Element(*[Bytes]byte) (Element, error) - PutElement(*[Bytes]byte, Element) - String() string -} - -// BigEndian is the big-endian implementation of ByteOrder and AppendByteOrder. -var BigEndian bigEndian - -type bigEndian struct{} - -// Element interpret b is a big-endian 32-byte slice. -// If b encodes a value higher than q, Element returns error. -func (bigEndian) Element(b *[Bytes]byte) (Element, error) { - var z Element - z[0] = binary.BigEndian.Uint64((*b)[24:32]) - z[1] = binary.BigEndian.Uint64((*b)[16:24]) - z[2] = binary.BigEndian.Uint64((*b)[8:16]) - z[3] = binary.BigEndian.Uint64((*b)[0:8]) - - if !z.smallerThanModulus() { - return Element{}, errors.New("invalid fr.Element encoding") - } - - z.toMont() - return z, nil -} - -func (bigEndian) PutElement(b *[Bytes]byte, e Element) { - e.fromMont() - binary.BigEndian.PutUint64((*b)[24:32], e[0]) - binary.BigEndian.PutUint64((*b)[16:24], e[1]) - binary.BigEndian.PutUint64((*b)[8:16], e[2]) - binary.BigEndian.PutUint64((*b)[0:8], e[3]) -} - -func (bigEndian) String() string { return "BigEndian" } - -// LittleEndian is the little-endian implementation of ByteOrder and AppendByteOrder. -var LittleEndian littleEndian - -type littleEndian struct{} - -func (littleEndian) Element(b *[Bytes]byte) (Element, error) { - var z Element - z[0] = binary.LittleEndian.Uint64((*b)[0:8]) - z[1] = binary.LittleEndian.Uint64((*b)[8:16]) - z[2] = binary.LittleEndian.Uint64((*b)[16:24]) - z[3] = binary.LittleEndian.Uint64((*b)[24:32]) - - if !z.smallerThanModulus() { - return Element{}, errors.New("invalid fr.Element encoding") - } - - z.toMont() - return z, nil -} - -func (littleEndian) PutElement(b *[Bytes]byte, e Element) { - e.fromMont() - binary.LittleEndian.PutUint64((*b)[0:8], e[0]) - binary.LittleEndian.PutUint64((*b)[8:16], e[1]) - binary.LittleEndian.PutUint64((*b)[16:24], e[2]) - binary.LittleEndian.PutUint64((*b)[24:32], e[3]) -} - -func (littleEndian) String() string { return "LittleEndian" } - -// Legendre returns the Legendre symbol of z (either +1, -1, or 0.) -func (z *Element) Legendre() int { - var l Element - // z^((q-1)/2) - l.expByLegendreExp(*z) - - if l.IsZero() { - return 0 - } - - // if l == 1 - if l.IsOne() { - return 1 - } - return -1 -} - -// Sqrt z = √x (mod q) -// if the square root doesn't exist (x is not a square mod q) -// Sqrt leaves z unchanged and returns nil -func (z *Element) Sqrt(x *Element) *Element { - // q ≡ 1 (mod 4) - // see modSqrtTonelliShanks in math/big/int.go - // using https://www.maa.org/sites/default/files/pdf/upload_library/22/Polya/07468342.di020786.02p0470a.pdf - - var y, b, t, w Element - // w = x^((s-1)/2)) - w.expBySqrtExp(*x) - - // y = x^((s+1)/2)) = w * x - y.Mul(x, &w) - - // b = xˢ = w * w * x = y * x - b.Mul(&w, &y) - - // g = nonResidue ^ s - var g = Element{ - 4558548184074722573, - 11721321436470045759, - 14707307855974552649, - 1565820507177503731, - } - r := uint64(42) - - // compute legendre symbol - // t = x^((q-1)/2) = r-1 squaring of xˢ - t = b - for i := uint64(0); i < r-1; i++ { - t.Square(&t) - } - if t.IsZero() { - return z.SetZero() - } - if !t.IsOne() { - // t != 1, we don't have a square root - return nil - } - for { - var m uint64 - t = b - - // for t != 1 - for !t.IsOne() { - t.Square(&t) - m++ - } - - if m == 0 { - return z.Set(&y) - } - // t = g^(2^(r-m-1)) (mod q) - ge := int(r - m - 1) - t = g - for ge > 0 { - t.Square(&t) - ge-- - } - - g.Square(&t) - y.Mul(&y, &t) - b.Mul(&b, &g) - r = m - } -} - -const ( - k = 32 // word size / 2 - signBitSelector = uint64(1) << 63 - approxLowBitsN = k - 1 - approxHighBitsN = k + 1 -) - -const ( - inversionCorrectionFactorWord0 = 11496758646349758257 - inversionCorrectionFactorWord1 = 14106295395927053233 - inversionCorrectionFactorWord2 = 9675338311035607220 - inversionCorrectionFactorWord3 = 300574624876614870 - invIterationsN = 18 -) - -// Inverse z = x⁻¹ (mod q) -// -// if x == 0, sets and returns z = x -func (z *Element) Inverse(x *Element) *Element { - // Implements "Optimized Binary GCD for Modular Inversion" - // https://github.com/pornin/bingcd/blob/main/doc/bingcd.pdf - - a := *x - b := Element{ - q0, - q1, - q2, - q3, - } // b := q - - u := Element{1} - - // Update factors: we get [u; v] ← [f₀ g₀; f₁ g₁] [u; v] - // cᵢ = fᵢ + 2³¹ - 1 + 2³² * (gᵢ + 2³¹ - 1) - var c0, c1 int64 - - // Saved update factors to reduce the number of field multiplications - var pf0, pf1, pg0, pg1 int64 - - var i uint - - var v, s Element - - // Since u,v are updated every other iteration, we must make sure we terminate after evenly many iterations - // This also lets us get away with half as many updates to u,v - // To make this constant-time-ish, replace the condition with i < invIterationsN - for i = 0; i&1 == 1 || !a.IsZero(); i++ { - n := max(a.BitLen(), b.BitLen()) - aApprox, bApprox := approximate(&a, n), approximate(&b, n) - - // f₀, g₀, f₁, g₁ = 1, 0, 0, 1 - c0, c1 = updateFactorIdentityMatrixRow0, updateFactorIdentityMatrixRow1 - - for j := 0; j < approxLowBitsN; j++ { - - // -2ʲ < f₀, f₁ ≤ 2ʲ - // |f₀| + |f₁| < 2ʲ⁺¹ - - if aApprox&1 == 0 { - aApprox /= 2 - } else { - s, borrow := bits.Sub64(aApprox, bApprox, 0) - if borrow == 1 { - s = bApprox - aApprox - bApprox = aApprox - c0, c1 = c1, c0 - // invariants unchanged - } - - aApprox = s / 2 - c0 = c0 - c1 - - // Now |f₀| < 2ʲ⁺¹ ≤ 2ʲ⁺¹ (only the weaker inequality is needed, strictly speaking) - // Started with f₀ > -2ʲ and f₁ ≤ 2ʲ, so f₀ - f₁ > -2ʲ⁺¹ - // Invariants unchanged for f₁ - } - - c1 *= 2 - // -2ʲ⁺¹ < f₁ ≤ 2ʲ⁺¹ - // So now |f₀| + |f₁| < 2ʲ⁺² - } - - s = a - - var g0 int64 - // from this point on c0 aliases for f0 - c0, g0 = updateFactorsDecompose(c0) - aHi := a.linearCombNonModular(&s, c0, &b, g0) - if aHi&signBitSelector != 0 { - // if aHi < 0 - c0, g0 = -c0, -g0 - aHi = negL(&a, aHi) - } - // right-shift a by k-1 bits - a[0] = (a[0] >> approxLowBitsN) | ((a[1]) << approxHighBitsN) - a[1] = (a[1] >> approxLowBitsN) | ((a[2]) << approxHighBitsN) - a[2] = (a[2] >> approxLowBitsN) | ((a[3]) << approxHighBitsN) - a[3] = (a[3] >> approxLowBitsN) | (aHi << approxHighBitsN) - - var f1 int64 - // from this point on c1 aliases for g0 - f1, c1 = updateFactorsDecompose(c1) - bHi := b.linearCombNonModular(&s, f1, &b, c1) - if bHi&signBitSelector != 0 { - // if bHi < 0 - f1, c1 = -f1, -c1 - bHi = negL(&b, bHi) - } - // right-shift b by k-1 bits - b[0] = (b[0] >> approxLowBitsN) | ((b[1]) << approxHighBitsN) - b[1] = (b[1] >> approxLowBitsN) | ((b[2]) << approxHighBitsN) - b[2] = (b[2] >> approxLowBitsN) | ((b[3]) << approxHighBitsN) - b[3] = (b[3] >> approxLowBitsN) | (bHi << approxHighBitsN) - - if i&1 == 1 { - // Combine current update factors with previously stored ones - // [F₀, G₀; F₁, G₁] ← [f₀, g₀; f₁, g₁] [pf₀, pg₀; pf₁, pg₁], with capital letters denoting new combined values - // We get |F₀| = | f₀pf₀ + g₀pf₁ | ≤ |f₀pf₀| + |g₀pf₁| = |f₀| |pf₀| + |g₀| |pf₁| ≤ 2ᵏ⁻¹|pf₀| + 2ᵏ⁻¹|pf₁| - // = 2ᵏ⁻¹ (|pf₀| + |pf₁|) < 2ᵏ⁻¹ 2ᵏ = 2²ᵏ⁻¹ - // So |F₀| < 2²ᵏ⁻¹ meaning it fits in a 2k-bit signed register - - // c₀ aliases f₀, c₁ aliases g₁ - c0, g0, f1, c1 = c0*pf0+g0*pf1, - c0*pg0+g0*pg1, - f1*pf0+c1*pf1, - f1*pg0+c1*pg1 - - s = u - - // 0 ≤ u, v < 2²⁵⁵ - // |F₀|, |G₀| < 2⁶³ - u.linearComb(&u, c0, &v, g0) - // |F₁|, |G₁| < 2⁶³ - v.linearComb(&s, f1, &v, c1) - - } else { - // Save update factors - pf0, pg0, pf1, pg1 = c0, g0, f1, c1 - } - } - - // For every iteration that we miss, v is not being multiplied by 2ᵏ⁻² - const pSq uint64 = 1 << (2 * (k - 1)) - a = Element{pSq} - // If the function is constant-time ish, this loop will not run (no need to take it out explicitly) - for ; i < invIterationsN; i += 2 { - // could optimize further with mul by word routine or by pre-computing a table since with k=26, - // we would multiply by pSq up to 13times; - // on x86, the assembly routine outperforms generic code for mul by word - // on arm64, we may loose up to ~5% for 6 limbs - v.Mul(&v, &a) - } - - u.Set(x) // for correctness check - - z.Mul(&v, &Element{ - inversionCorrectionFactorWord0, - inversionCorrectionFactorWord1, - inversionCorrectionFactorWord2, - inversionCorrectionFactorWord3, - }) - - // correctness check - v.Mul(&u, z) - if !v.IsOne() && !u.IsZero() { - return z.inverseExp(u) - } - - return z -} - -// inverseExp computes z = x⁻¹ (mod q) = x**(q-2) (mod q) -func (z *Element) inverseExp(x Element) *Element { - // e == q-2 - e := Modulus() - e.Sub(e, big.NewInt(2)) - - z.Set(&x) - - for i := e.BitLen() - 2; i >= 0; i-- { - z.Square(z) - if e.Bit(i) == 1 { - z.Mul(z, &x) - } - } - - return z -} - -// approximate a big number x into a single 64 bit word using its uppermost and lowermost bits -// if x fits in a word as is, no approximation necessary -func approximate(x *Element, nBits int) uint64 { - - if nBits <= 64 { - return x[0] - } - - const mask = (uint64(1) << (k - 1)) - 1 // k-1 ones - lo := mask & x[0] - - hiWordIndex := (nBits - 1) / 64 - - hiWordBitsAvailable := nBits - hiWordIndex*64 - hiWordBitsUsed := min(hiWordBitsAvailable, approxHighBitsN) - - mask_ := uint64(^((1 << (hiWordBitsAvailable - hiWordBitsUsed)) - 1)) - hi := (x[hiWordIndex] & mask_) << (64 - hiWordBitsAvailable) - - mask_ = ^(1<<(approxLowBitsN+hiWordBitsUsed) - 1) - mid := (mask_ & x[hiWordIndex-1]) >> hiWordBitsUsed - - return lo | mid | hi -} - -// linearComb z = xC * x + yC * y; -// 0 ≤ x, y < 2²⁵⁴ -// |xC|, |yC| < 2⁶³ -func (z *Element) linearComb(x *Element, xC int64, y *Element, yC int64) { - // | (hi, z) | < 2 * 2⁶³ * 2²⁵⁴ = 2³¹⁸ - // therefore | hi | < 2⁶² ≤ 2⁶³ - hi := z.linearCombNonModular(x, xC, y, yC) - z.montReduceSigned(z, hi) -} - -// montReduceSigned z = (xHi * r + x) * r⁻¹ using the SOS algorithm -// Requires |xHi| < 2⁶³. Most significant bit of xHi is the sign bit. -func (z *Element) montReduceSigned(x *Element, xHi uint64) { - const signBitRemover = ^signBitSelector - mustNeg := xHi&signBitSelector != 0 - // the SOS implementation requires that most significant bit is 0 - // Let X be xHi*r + x - // If X is negative we would have initially stored it as 2⁶⁴ r + X (à la 2's complement) - xHi &= signBitRemover - // with this a negative X is now represented as 2⁶³ r + X - - var t [2*Limbs - 1]uint64 - var C uint64 - - m := x[0] * qInvNeg - - C = madd0(m, q0, x[0]) - C, t[1] = madd2(m, q1, x[1], C) - C, t[2] = madd2(m, q2, x[2], C) - C, t[3] = madd2(m, q3, x[3], C) - - // m * qElement[3] ≤ (2⁶⁴ - 1) * (2⁶³ - 1) = 2¹²⁷ - 2⁶⁴ - 2⁶³ + 1 - // x[3] + C ≤ 2*(2⁶⁴ - 1) = 2⁶⁵ - 2 - // On LHS, (C, t[3]) ≤ 2¹²⁷ - 2⁶⁴ - 2⁶³ + 1 + 2⁶⁵ - 2 = 2¹²⁷ + 2⁶³ - 1 - // So on LHS, C ≤ 2⁶³ - t[4] = xHi + C - // xHi + C < 2⁶³ + 2⁶³ = 2⁶⁴ - - // - { - const i = 1 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - - t[i+Limbs] += C - } - { - const i = 2 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - - t[i+Limbs] += C - } - { - const i = 3 - m := t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, z[0] = madd2(m, q1, t[i+1], C) - C, z[1] = madd2(m, q2, t[i+2], C) - z[3], z[2] = madd2(m, q3, t[i+3], C) - } - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], _ = bits.Sub64(z[3], q3, b) - } - // - - if mustNeg { - // We have computed ( 2⁶³ r + X ) r⁻¹ = 2⁶³ + X r⁻¹ instead - var b uint64 - z[0], b = bits.Sub64(z[0], signBitSelector, 0) - z[1], b = bits.Sub64(z[1], 0, b) - z[2], b = bits.Sub64(z[2], 0, b) - z[3], b = bits.Sub64(z[3], 0, b) - - // Occurs iff x == 0 && xHi < 0, i.e. X = rX' for -2⁶³ ≤ X' < 0 - - if b != 0 { - // z[3] = -1 - // negative: add q - const neg1 = 0xFFFFFFFFFFFFFFFF - - var carry uint64 - - z[0], carry = bits.Add64(z[0], q0, 0) - z[1], carry = bits.Add64(z[1], q1, carry) - z[2], carry = bits.Add64(z[2], q2, carry) - z[3], _ = bits.Add64(neg1, q3, carry) - } - } -} - -const ( - updateFactorsConversionBias int64 = 0x7fffffff7fffffff // (2³¹ - 1)(2³² + 1) - updateFactorIdentityMatrixRow0 = 1 - updateFactorIdentityMatrixRow1 = 1 << 32 -) - -func updateFactorsDecompose(c int64) (int64, int64) { - c += updateFactorsConversionBias - const low32BitsFilter int64 = 0xFFFFFFFF - f := c&low32BitsFilter - 0x7FFFFFFF - g := c>>32&low32BitsFilter - 0x7FFFFFFF - return f, g -} - -// negL negates in place [x | xHi] and return the new most significant word xHi -func negL(x *Element, xHi uint64) uint64 { - var b uint64 - - x[0], b = bits.Sub64(0, x[0], 0) - x[1], b = bits.Sub64(0, x[1], b) - x[2], b = bits.Sub64(0, x[2], b) - x[3], b = bits.Sub64(0, x[3], b) - xHi, _ = bits.Sub64(0, xHi, b) - - return xHi -} - -// mulWNonModular multiplies by one word in non-montgomery, without reducing -func (z *Element) mulWNonModular(x *Element, y int64) uint64 { - - // w := abs(y) - m := y >> 63 - w := uint64((y ^ m) - m) - - var c uint64 - c, z[0] = bits.Mul64(x[0], w) - c, z[1] = madd1(x[1], w, c) - c, z[2] = madd1(x[2], w, c) - c, z[3] = madd1(x[3], w, c) - - if y < 0 { - c = negL(z, c) - } - - return c -} - -// linearCombNonModular computes a linear combination without modular reduction -func (z *Element) linearCombNonModular(x *Element, xC int64, y *Element, yC int64) uint64 { - var yTimes Element - - yHi := yTimes.mulWNonModular(y, yC) - xHi := z.mulWNonModular(x, xC) - - var carry uint64 - z[0], carry = bits.Add64(z[0], yTimes[0], 0) - z[1], carry = bits.Add64(z[1], yTimes[1], carry) - z[2], carry = bits.Add64(z[2], yTimes[2], carry) - z[3], carry = bits.Add64(z[3], yTimes[3], carry) - - yHi, _ = bits.Add64(xHi, yHi, carry) - - return yHi -} diff --git a/ecc/bls12-378/fr/element_exp.go b/ecc/bls12-378/fr/element_exp.go deleted file mode 100644 index 372af00511..0000000000 --- a/ecc/bls12-378/fr/element_exp.go +++ /dev/null @@ -1,642 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -// expBySqrtExp is equivalent to z.Exp(x, 41cf7391def65d630ef0ff69c7b761ffd5cefe7b4128000265228) -// -// uses github.com/mmcloughlin/addchain v0.4.0 to generate a shorter addition chain -func (z *Element) expBySqrtExp(x Element) *Element { - // addition chain: - // - // _10 = 2*1 - // _100 = 2*_10 - // _101 = 1 + _100 - // _1010 = 2*_101 - // _1111 = _101 + _1010 - // _10011 = _100 + _1111 - // _10100 = 1 + _10011 - // _11101 = _1010 + _10011 - // _101100 = _1111 + _11101 - // _1001001 = _11101 + _101100 - // _1001101 = _100 + _1001001 - // _1001111 = _10 + _1001101 - // _1010011 = _100 + _1001111 - // _1011100 = _1111 + _1001101 - // _10101011 = _1001111 + _1011100 - // _10111110 = _10011 + _10101011 - // _11001000 = _1010 + _10111110 - // i18 = 2*_11001000 - // i19 = _10101011 + i18 - // i20 = _1001001 + i19 - // i21 = i18 + i20 - // i22 = _1001101 + i21 - // i23 = _1010011 + i22 - // i24 = _1001001 + i23 - // i25 = i20 + i24 - // i26 = _1111 + i25 - // i27 = i19 + i26 - // i28 = i22 + i27 - // i29 = i24 + i28 - // i30 = _10111110 + i29 - // i31 = _101100 + i30 - // i32 = i25 + i31 - // i33 = i30 + i32 - // i34 = i28 + i33 - // i35 = _10100 + i34 - // i36 = i21 + i35 - // i37 = i32 + i36 - // i38 = i27 + i37 - // i39 = i31 + i38 - // i40 = i23 + i39 - // i41 = 2*i36 - // i42 = i38 + i40 - // i43 = _1011100 + i42 - // i92 = ((i41 << 16 + i42) << 14 + i33) << 17 - // i129 = ((i37 + i92) << 20 + i26 + i43) << 14 - // i168 = ((i34 + i129) << 17 + i35) << 19 + i40 - // i209 = ((i168 << 17 + i43) << 17 + i39) << 5 - // i248 = ((_101 + i209) << 30 + i29) << 6 + _101 - // return i248 << 3 - // - // Operations: 200 squares 51 multiplies - - // Allocate Temporaries. - var ( - t0 = new(Element) - t1 = new(Element) - t2 = new(Element) - t3 = new(Element) - t4 = new(Element) - t5 = new(Element) - t6 = new(Element) - t7 = new(Element) - t8 = new(Element) - t9 = new(Element) - t10 = new(Element) - ) - - // var t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10 Element - // Step 1: t3 = x^0x2 - t3.Square(&x) - - // Step 2: t2 = x^0x4 - t2.Square(t3) - - // Step 3: z = x^0x5 - z.Mul(&x, t2) - - // Step 4: t9 = x^0xa - t9.Square(z) - - // Step 5: t6 = x^0xf - t6.Mul(z, t9) - - // Step 6: t8 = x^0x13 - t8.Mul(t2, t6) - - // Step 7: t4 = x^0x14 - t4.Mul(&x, t8) - - // Step 8: t0 = x^0x1d - t0.Mul(t9, t8) - - // Step 9: t1 = x^0x2c - t1.Mul(t6, t0) - - // Step 10: t0 = x^0x49 - t0.Mul(t0, t1) - - // Step 11: t5 = x^0x4d - t5.Mul(t2, t0) - - // Step 12: t7 = x^0x4f - t7.Mul(t3, t5) - - // Step 13: t3 = x^0x53 - t3.Mul(t2, t7) - - // Step 14: t2 = x^0x5c - t2.Mul(t6, t5) - - // Step 15: t7 = x^0xab - t7.Mul(t7, t2) - - // Step 16: t8 = x^0xbe - t8.Mul(t8, t7) - - // Step 17: t9 = x^0xc8 - t9.Mul(t9, t8) - - // Step 18: t10 = x^0x190 - t10.Square(t9) - - // Step 19: t9 = x^0x23b - t9.Mul(t7, t10) - - // Step 20: t7 = x^0x284 - t7.Mul(t0, t9) - - // Step 21: t10 = x^0x414 - t10.Mul(t10, t7) - - // Step 22: t5 = x^0x461 - t5.Mul(t5, t10) - - // Step 23: t3 = x^0x4b4 - t3.Mul(t3, t5) - - // Step 24: t0 = x^0x4fd - t0.Mul(t0, t3) - - // Step 25: t7 = x^0x781 - t7.Mul(t7, t0) - - // Step 26: t6 = x^0x790 - t6.Mul(t6, t7) - - // Step 27: t9 = x^0x9cb - t9.Mul(t9, t6) - - // Step 28: t5 = x^0xe2c - t5.Mul(t5, t9) - - // Step 29: t0 = x^0x1329 - t0.Mul(t0, t5) - - // Step 30: t8 = x^0x13e7 - t8.Mul(t8, t0) - - // Step 31: t1 = x^0x1413 - t1.Mul(t1, t8) - - // Step 32: t7 = x^0x1b94 - t7.Mul(t7, t1) - - // Step 33: t8 = x^0x2f7b - t8.Mul(t8, t7) - - // Step 34: t5 = x^0x3da7 - t5.Mul(t5, t8) - - // Step 35: t4 = x^0x3dbb - t4.Mul(t4, t5) - - // Step 36: t10 = x^0x41cf - t10.Mul(t10, t4) - - // Step 37: t7 = x^0x5d63 - t7.Mul(t7, t10) - - // Step 38: t9 = x^0x672e - t9.Mul(t9, t7) - - // Step 39: t1 = x^0x7b41 - t1.Mul(t1, t9) - - // Step 40: t3 = x^0x7ff5 - t3.Mul(t3, t1) - - // Step 41: t10 = x^0x839e - t10.Square(t10) - - // Step 42: t9 = x^0xe723 - t9.Mul(t9, t3) - - // Step 43: t2 = x^0xe77f - t2.Mul(t2, t9) - - // Step 59: t10 = x^0x839e0000 - for s := 0; s < 16; s++ { - t10.Square(t10) - } - - // Step 60: t9 = x^0x839ee723 - t9.Mul(t9, t10) - - // Step 74: t9 = x^0x20e7b9c8c000 - for s := 0; s < 14; s++ { - t9.Square(t9) - } - - // Step 75: t8 = x^0x20e7b9c8ef7b - t8.Mul(t8, t9) - - // Step 92: t8 = x^0x41cf7391def60000 - for s := 0; s < 17; s++ { - t8.Square(t8) - } - - // Step 93: t7 = x^0x41cf7391def65d63 - t7.Mul(t7, t8) - - // Step 113: t7 = x^0x41cf7391def65d6300000 - for s := 0; s < 20; s++ { - t7.Square(t7) - } - - // Step 114: t6 = x^0x41cf7391def65d6300790 - t6.Mul(t6, t7) - - // Step 115: t6 = x^0x41cf7391def65d630ef0f - t6.Mul(t2, t6) - - // Step 129: t6 = x^0x1073dce477bd9758c3bc3c000 - for s := 0; s < 14; s++ { - t6.Square(t6) - } - - // Step 130: t5 = x^0x1073dce477bd9758c3bc3fda7 - t5.Mul(t5, t6) - - // Step 147: t5 = x^0x20e7b9c8ef7b2eb187787fb4e0000 - for s := 0; s < 17; s++ { - t5.Square(t5) - } - - // Step 148: t4 = x^0x20e7b9c8ef7b2eb187787fb4e3dbb - t4.Mul(t4, t5) - - // Step 167: t4 = x^0x1073dce477bd9758c3bc3fda71edd80000 - for s := 0; s < 19; s++ { - t4.Square(t4) - } - - // Step 168: t3 = x^0x1073dce477bd9758c3bc3fda71edd87ff5 - t3.Mul(t3, t4) - - // Step 185: t3 = x^0x20e7b9c8ef7b2eb187787fb4e3dbb0ffea0000 - for s := 0; s < 17; s++ { - t3.Square(t3) - } - - // Step 186: t2 = x^0x20e7b9c8ef7b2eb187787fb4e3dbb0ffeae77f - t2.Mul(t2, t3) - - // Step 203: t2 = x^0x41cf7391def65d630ef0ff69c7b761ffd5cefe0000 - for s := 0; s < 17; s++ { - t2.Square(t2) - } - - // Step 204: t1 = x^0x41cf7391def65d630ef0ff69c7b761ffd5cefe7b41 - t1.Mul(t1, t2) - - // Step 209: t1 = x^0x839ee723bdecbac61de1fed38f6ec3ffab9dfcf6820 - for s := 0; s < 5; s++ { - t1.Square(t1) - } - - // Step 210: t1 = x^0x839ee723bdecbac61de1fed38f6ec3ffab9dfcf6825 - t1.Mul(z, t1) - - // Step 240: t1 = x^0x20e7b9c8ef7b2eb187787fb4e3dbb0ffeae77f3da0940000000 - for s := 0; s < 30; s++ { - t1.Square(t1) - } - - // Step 241: t0 = x^0x20e7b9c8ef7b2eb187787fb4e3dbb0ffeae77f3da0940001329 - t0.Mul(t0, t1) - - // Step 247: t0 = x^0x839ee723bdecbac61de1fed38f6ec3ffab9dfcf682500004ca40 - for s := 0; s < 6; s++ { - t0.Square(t0) - } - - // Step 248: z = x^0x839ee723bdecbac61de1fed38f6ec3ffab9dfcf682500004ca45 - z.Mul(z, t0) - - // Step 251: z = x^0x41cf7391def65d630ef0ff69c7b761ffd5cefe7b4128000265228 - for s := 0; s < 3; s++ { - z.Square(z) - } - - return z -} - -// expByLegendreExp is equivalent to z.Exp(x, 1073dce477bd9758c3bc3fda71edd87ff573bf9ed04a00009948a20000000000) -// -// uses github.com/mmcloughlin/addchain v0.4.0 to generate a shorter addition chain -func (z *Element) expByLegendreExp(x Element) *Element { - // addition chain: - // - // _10 = 2*1 - // _100 = 2*_10 - // _101 = 1 + _100 - // _1010 = 2*_101 - // _1111 = _101 + _1010 - // _10011 = _100 + _1111 - // _10100 = 1 + _10011 - // _11101 = _1010 + _10011 - // _101100 = _1111 + _11101 - // _1001001 = _11101 + _101100 - // _1001101 = _100 + _1001001 - // _1001111 = _10 + _1001101 - // _1010001 = _10 + _1001111 - // _1010011 = _10 + _1010001 - // _1011100 = _1111 + _1001101 - // _10101011 = _1001111 + _1011100 - // _10111110 = _10011 + _10101011 - // _11001000 = _1010 + _10111110 - // i19 = 2*_11001000 - // i20 = _10101011 + i19 - // i21 = _1001001 + i20 - // i22 = i19 + i21 - // i23 = _1001101 + i22 - // i24 = _1010011 + i23 - // i25 = _1001001 + i24 - // i26 = i21 + i25 - // i27 = _1111 + i26 - // i28 = i20 + i27 - // i29 = i23 + i28 - // i30 = i25 + i29 - // i31 = _10111110 + i30 - // i32 = _101100 + i31 - // i33 = i26 + i32 - // i34 = i31 + i33 - // i35 = i29 + i34 - // i36 = _10100 + i35 - // i37 = i22 + i36 - // i38 = i33 + i37 - // i39 = i28 + i38 - // i40 = i32 + i39 - // i41 = i24 + i40 - // i42 = 2*i37 - // i43 = i39 + i41 - // i44 = _1011100 + i43 - // i93 = ((i42 << 16 + i43) << 14 + i34) << 17 - // i130 = ((i38 + i93) << 20 + i27 + i44) << 14 - // i169 = ((i35 + i130) << 17 + i36) << 19 + i41 - // i210 = ((i169 << 17 + i44) << 17 + i40) << 5 - // i253 = ((_101 + i210) << 30 + i30) << 10 + _1010001 - // return i253 << 41 - // - // Operations: 242 squares 52 multiplies - - // Allocate Temporaries. - var ( - t0 = new(Element) - t1 = new(Element) - t2 = new(Element) - t3 = new(Element) - t4 = new(Element) - t5 = new(Element) - t6 = new(Element) - t7 = new(Element) - t8 = new(Element) - t9 = new(Element) - t10 = new(Element) - t11 = new(Element) - ) - - // var t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11 Element - // Step 1: t3 = x^0x2 - t3.Square(&x) - - // Step 2: z = x^0x4 - z.Square(t3) - - // Step 3: t1 = x^0x5 - t1.Mul(&x, z) - - // Step 4: t10 = x^0xa - t10.Square(t1) - - // Step 5: t7 = x^0xf - t7.Mul(t1, t10) - - // Step 6: t9 = x^0x13 - t9.Mul(z, t7) - - // Step 7: t5 = x^0x14 - t5.Mul(&x, t9) - - // Step 8: t0 = x^0x1d - t0.Mul(t10, t9) - - // Step 9: t2 = x^0x2c - t2.Mul(t7, t0) - - // Step 10: t0 = x^0x49 - t0.Mul(t0, t2) - - // Step 11: t6 = x^0x4d - t6.Mul(z, t0) - - // Step 12: t8 = x^0x4f - t8.Mul(t3, t6) - - // Step 13: z = x^0x51 - z.Mul(t3, t8) - - // Step 14: t4 = x^0x53 - t4.Mul(t3, z) - - // Step 15: t3 = x^0x5c - t3.Mul(t7, t6) - - // Step 16: t8 = x^0xab - t8.Mul(t8, t3) - - // Step 17: t9 = x^0xbe - t9.Mul(t9, t8) - - // Step 18: t10 = x^0xc8 - t10.Mul(t10, t9) - - // Step 19: t11 = x^0x190 - t11.Square(t10) - - // Step 20: t10 = x^0x23b - t10.Mul(t8, t11) - - // Step 21: t8 = x^0x284 - t8.Mul(t0, t10) - - // Step 22: t11 = x^0x414 - t11.Mul(t11, t8) - - // Step 23: t6 = x^0x461 - t6.Mul(t6, t11) - - // Step 24: t4 = x^0x4b4 - t4.Mul(t4, t6) - - // Step 25: t0 = x^0x4fd - t0.Mul(t0, t4) - - // Step 26: t8 = x^0x781 - t8.Mul(t8, t0) - - // Step 27: t7 = x^0x790 - t7.Mul(t7, t8) - - // Step 28: t10 = x^0x9cb - t10.Mul(t10, t7) - - // Step 29: t6 = x^0xe2c - t6.Mul(t6, t10) - - // Step 30: t0 = x^0x1329 - t0.Mul(t0, t6) - - // Step 31: t9 = x^0x13e7 - t9.Mul(t9, t0) - - // Step 32: t2 = x^0x1413 - t2.Mul(t2, t9) - - // Step 33: t8 = x^0x1b94 - t8.Mul(t8, t2) - - // Step 34: t9 = x^0x2f7b - t9.Mul(t9, t8) - - // Step 35: t6 = x^0x3da7 - t6.Mul(t6, t9) - - // Step 36: t5 = x^0x3dbb - t5.Mul(t5, t6) - - // Step 37: t11 = x^0x41cf - t11.Mul(t11, t5) - - // Step 38: t8 = x^0x5d63 - t8.Mul(t8, t11) - - // Step 39: t10 = x^0x672e - t10.Mul(t10, t8) - - // Step 40: t2 = x^0x7b41 - t2.Mul(t2, t10) - - // Step 41: t4 = x^0x7ff5 - t4.Mul(t4, t2) - - // Step 42: t11 = x^0x839e - t11.Square(t11) - - // Step 43: t10 = x^0xe723 - t10.Mul(t10, t4) - - // Step 44: t3 = x^0xe77f - t3.Mul(t3, t10) - - // Step 60: t11 = x^0x839e0000 - for s := 0; s < 16; s++ { - t11.Square(t11) - } - - // Step 61: t10 = x^0x839ee723 - t10.Mul(t10, t11) - - // Step 75: t10 = x^0x20e7b9c8c000 - for s := 0; s < 14; s++ { - t10.Square(t10) - } - - // Step 76: t9 = x^0x20e7b9c8ef7b - t9.Mul(t9, t10) - - // Step 93: t9 = x^0x41cf7391def60000 - for s := 0; s < 17; s++ { - t9.Square(t9) - } - - // Step 94: t8 = x^0x41cf7391def65d63 - t8.Mul(t8, t9) - - // Step 114: t8 = x^0x41cf7391def65d6300000 - for s := 0; s < 20; s++ { - t8.Square(t8) - } - - // Step 115: t7 = x^0x41cf7391def65d6300790 - t7.Mul(t7, t8) - - // Step 116: t7 = x^0x41cf7391def65d630ef0f - t7.Mul(t3, t7) - - // Step 130: t7 = x^0x1073dce477bd9758c3bc3c000 - for s := 0; s < 14; s++ { - t7.Square(t7) - } - - // Step 131: t6 = x^0x1073dce477bd9758c3bc3fda7 - t6.Mul(t6, t7) - - // Step 148: t6 = x^0x20e7b9c8ef7b2eb187787fb4e0000 - for s := 0; s < 17; s++ { - t6.Square(t6) - } - - // Step 149: t5 = x^0x20e7b9c8ef7b2eb187787fb4e3dbb - t5.Mul(t5, t6) - - // Step 168: t5 = x^0x1073dce477bd9758c3bc3fda71edd80000 - for s := 0; s < 19; s++ { - t5.Square(t5) - } - - // Step 169: t4 = x^0x1073dce477bd9758c3bc3fda71edd87ff5 - t4.Mul(t4, t5) - - // Step 186: t4 = x^0x20e7b9c8ef7b2eb187787fb4e3dbb0ffea0000 - for s := 0; s < 17; s++ { - t4.Square(t4) - } - - // Step 187: t3 = x^0x20e7b9c8ef7b2eb187787fb4e3dbb0ffeae77f - t3.Mul(t3, t4) - - // Step 204: t3 = x^0x41cf7391def65d630ef0ff69c7b761ffd5cefe0000 - for s := 0; s < 17; s++ { - t3.Square(t3) - } - - // Step 205: t2 = x^0x41cf7391def65d630ef0ff69c7b761ffd5cefe7b41 - t2.Mul(t2, t3) - - // Step 210: t2 = x^0x839ee723bdecbac61de1fed38f6ec3ffab9dfcf6820 - for s := 0; s < 5; s++ { - t2.Square(t2) - } - - // Step 211: t1 = x^0x839ee723bdecbac61de1fed38f6ec3ffab9dfcf6825 - t1.Mul(t1, t2) - - // Step 241: t1 = x^0x20e7b9c8ef7b2eb187787fb4e3dbb0ffeae77f3da0940000000 - for s := 0; s < 30; s++ { - t1.Square(t1) - } - - // Step 242: t0 = x^0x20e7b9c8ef7b2eb187787fb4e3dbb0ffeae77f3da0940001329 - t0.Mul(t0, t1) - - // Step 252: t0 = x^0x839ee723bdecbac61de1fed38f6ec3ffab9dfcf682500004ca400 - for s := 0; s < 10; s++ { - t0.Square(t0) - } - - // Step 253: z = x^0x839ee723bdecbac61de1fed38f6ec3ffab9dfcf682500004ca451 - z.Mul(z, t0) - - // Step 294: z = x^0x1073dce477bd9758c3bc3fda71edd87ff573bf9ed04a00009948a20000000000 - for s := 0; s < 41; s++ { - z.Square(z) - } - - return z -} diff --git a/ecc/bls12-378/fr/element_mul_amd64.s b/ecc/bls12-378/fr/element_mul_amd64.s deleted file mode 100644 index 75908e04dd..0000000000 --- a/ecc/bls12-378/fr/element_mul_amd64.s +++ /dev/null @@ -1,487 +0,0 @@ -// +build !purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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. - -#include "textflag.h" -#include "funcdata.h" - -// modulus q -DATA q<>+0(SB)/8, $0x3291440000000001 -DATA q<>+8(SB)/8, $0xeae77f3da0940001 -DATA q<>+16(SB)/8, $0x87787fb4e3dbb0ff -DATA q<>+24(SB)/8, $0x20e7b9c8ef7b2eb1 -GLOBL q<>(SB), (RODATA+NOPTR), $32 - -// qInv0 q'[0] -DATA qInv0<>(SB)/8, $0x329143ffffffffff -GLOBL qInv0<>(SB), (RODATA+NOPTR), $8 - -#define REDUCE(ra0, ra1, ra2, ra3, rb0, rb1, rb2, rb3) \ - MOVQ ra0, rb0; \ - SUBQ q<>(SB), ra0; \ - MOVQ ra1, rb1; \ - SBBQ q<>+8(SB), ra1; \ - MOVQ ra2, rb2; \ - SBBQ q<>+16(SB), ra2; \ - MOVQ ra3, rb3; \ - SBBQ q<>+24(SB), ra3; \ - CMOVQCS rb0, ra0; \ - CMOVQCS rb1, ra1; \ - CMOVQCS rb2, ra2; \ - CMOVQCS rb3, ra3; \ - -// mul(res, x, y *Element) -TEXT ·mul(SB), $24-24 - - // the algorithm is described in the Element.Mul declaration (.go) - // however, to benefit from the ADCX and ADOX carry chains - // we split the inner loops in 2: - // for i=0 to N-1 - // for j=0 to N-1 - // (A,t[j]) := t[j] + x[j]*y[i] + A - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // t[N-1] = C + A - - NO_LOCAL_POINTERS - CMPB ·supportAdx(SB), $1 - JNE l1 - MOVQ x+8(FP), SI - - // x[0] -> DI - // x[1] -> R8 - // x[2] -> R9 - // x[3] -> R10 - MOVQ 0(SI), DI - MOVQ 8(SI), R8 - MOVQ 16(SI), R9 - MOVQ 24(SI), R10 - MOVQ y+16(FP), R11 - - // A -> BP - // t[0] -> R14 - // t[1] -> R13 - // t[2] -> CX - // t[3] -> BX - // clear the flags - XORQ AX, AX - MOVQ 0(R11), DX - - // (A,t[0]) := x[0]*y[0] + A - MULXQ DI, R14, R13 - - // (A,t[1]) := x[1]*y[0] + A - MULXQ R8, AX, CX - ADOXQ AX, R13 - - // (A,t[2]) := x[2]*y[0] + A - MULXQ R9, AX, BX - ADOXQ AX, CX - - // (A,t[3]) := x[3]*y[0] + A - MULXQ R10, AX, BP - ADOXQ AX, BX - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R12 - ADCXQ R14, AX - MOVQ R12, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R13, R14 - MULXQ q<>+8(SB), AX, R13 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R13 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R13 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // t[3] = C + A - MOVQ $0, AX - ADCXQ AX, BX - ADOXQ BP, BX - - // clear the flags - XORQ AX, AX - MOVQ 8(R11), DX - - // (A,t[0]) := t[0] + x[0]*y[1] + A - MULXQ DI, AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[1] + A - ADCXQ BP, R13 - MULXQ R8, AX, BP - ADOXQ AX, R13 - - // (A,t[2]) := t[2] + x[2]*y[1] + A - ADCXQ BP, CX - MULXQ R9, AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[1] + A - ADCXQ BP, BX - MULXQ R10, AX, BP - ADOXQ AX, BX - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R12 - ADCXQ R14, AX - MOVQ R12, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R13, R14 - MULXQ q<>+8(SB), AX, R13 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R13 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R13 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // t[3] = C + A - MOVQ $0, AX - ADCXQ AX, BX - ADOXQ BP, BX - - // clear the flags - XORQ AX, AX - MOVQ 16(R11), DX - - // (A,t[0]) := t[0] + x[0]*y[2] + A - MULXQ DI, AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[2] + A - ADCXQ BP, R13 - MULXQ R8, AX, BP - ADOXQ AX, R13 - - // (A,t[2]) := t[2] + x[2]*y[2] + A - ADCXQ BP, CX - MULXQ R9, AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[2] + A - ADCXQ BP, BX - MULXQ R10, AX, BP - ADOXQ AX, BX - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R12 - ADCXQ R14, AX - MOVQ R12, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R13, R14 - MULXQ q<>+8(SB), AX, R13 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R13 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R13 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // t[3] = C + A - MOVQ $0, AX - ADCXQ AX, BX - ADOXQ BP, BX - - // clear the flags - XORQ AX, AX - MOVQ 24(R11), DX - - // (A,t[0]) := t[0] + x[0]*y[3] + A - MULXQ DI, AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[3] + A - ADCXQ BP, R13 - MULXQ R8, AX, BP - ADOXQ AX, R13 - - // (A,t[2]) := t[2] + x[2]*y[3] + A - ADCXQ BP, CX - MULXQ R9, AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[3] + A - ADCXQ BP, BX - MULXQ R10, AX, BP - ADOXQ AX, BX - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R12 - ADCXQ R14, AX - MOVQ R12, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R13, R14 - MULXQ q<>+8(SB), AX, R13 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R13 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R13 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // t[3] = C + A - MOVQ $0, AX - ADCXQ AX, BX - ADOXQ BP, BX - - // reduce element(R14,R13,CX,BX) using temp registers (SI,R12,R11,DI) - REDUCE(R14,R13,CX,BX,SI,R12,R11,DI) - - MOVQ res+0(FP), AX - MOVQ R14, 0(AX) - MOVQ R13, 8(AX) - MOVQ CX, 16(AX) - MOVQ BX, 24(AX) - RET - -l1: - MOVQ res+0(FP), AX - MOVQ AX, (SP) - MOVQ x+8(FP), AX - MOVQ AX, 8(SP) - MOVQ y+16(FP), AX - MOVQ AX, 16(SP) - CALL ·_mulGeneric(SB) - RET - -TEXT ·fromMont(SB), $8-8 - NO_LOCAL_POINTERS - - // the algorithm is described here - // https://hackmd.io/@gnark/modular_multiplication - // when y = 1 we have: - // for i=0 to N-1 - // t[i] = x[i] - // for i=0 to N-1 - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // t[N-1] = C - CMPB ·supportAdx(SB), $1 - JNE l2 - MOVQ res+0(FP), DX - MOVQ 0(DX), R14 - MOVQ 8(DX), R13 - MOVQ 16(DX), CX - MOVQ 24(DX), BX - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R13, R14 - MULXQ q<>+8(SB), AX, R13 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R13 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R13 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - MOVQ $0, AX - ADCXQ AX, BX - ADOXQ AX, BX - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R13, R14 - MULXQ q<>+8(SB), AX, R13 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R13 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R13 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - MOVQ $0, AX - ADCXQ AX, BX - ADOXQ AX, BX - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R13, R14 - MULXQ q<>+8(SB), AX, R13 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R13 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R13 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - MOVQ $0, AX - ADCXQ AX, BX - ADOXQ AX, BX - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R13, R14 - MULXQ q<>+8(SB), AX, R13 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R13 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R13 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - MOVQ $0, AX - ADCXQ AX, BX - ADOXQ AX, BX - - // reduce element(R14,R13,CX,BX) using temp registers (SI,DI,R8,R9) - REDUCE(R14,R13,CX,BX,SI,DI,R8,R9) - - MOVQ res+0(FP), AX - MOVQ R14, 0(AX) - MOVQ R13, 8(AX) - MOVQ CX, 16(AX) - MOVQ BX, 24(AX) - RET - -l2: - MOVQ res+0(FP), AX - MOVQ AX, (SP) - CALL ·_fromMontGeneric(SB) - RET diff --git a/ecc/bls12-378/fr/element_ops_amd64.go b/ecc/bls12-378/fr/element_ops_amd64.go deleted file mode 100644 index e40a9caed5..0000000000 --- a/ecc/bls12-378/fr/element_ops_amd64.go +++ /dev/null @@ -1,107 +0,0 @@ -//go:build !purego -// +build !purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -//go:noescape -func MulBy3(x *Element) - -//go:noescape -func MulBy5(x *Element) - -//go:noescape -func MulBy13(x *Element) - -//go:noescape -func mul(res, x, y *Element) - -//go:noescape -func fromMont(res *Element) - -//go:noescape -func reduce(res *Element) - -// Butterfly sets -// -// a = a + b (mod q) -// b = a - b (mod q) -// -//go:noescape -func Butterfly(a, b *Element) - -// Mul z = x * y (mod q) -// -// x and y must be less than q -func (z *Element) Mul(x, y *Element) *Element { - - // Implements CIOS multiplication -- section 2.3.2 of Tolga Acar's thesis - // https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf - // - // The algorithm: - // - // for i=0 to N-1 - // C := 0 - // for j=0 to N-1 - // (C,t[j]) := t[j] + x[j]*y[i] + C - // (t[N+1],t[N]) := t[N] + C - // - // C := 0 - // m := t[0]*q'[0] mod D - // (C,_) := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // (C,t[N-1]) := t[N] + C - // t[N] := t[N+1] + C - // - // → N is the number of machine words needed to store the modulus q - // → D is the word size. For example, on a 64-bit architecture D is 2 64 - // → x[i], y[i], q[i] is the ith word of the numbers x,y,q - // → q'[0] is the lowest word of the number -q⁻¹ mod r. This quantity is pre-computed, as it does not depend on the inputs. - // → t is a temporary array of size N+2 - // → C, S are machine words. A pair (C,S) refers to (hi-bits, lo-bits) of a two-word number - // - // As described here https://hackmd.io/@gnark/modular_multiplication we can get rid of one carry chain and simplify: - // (also described in https://eprint.iacr.org/2022/1400.pdf annex) - // - // for i=0 to N-1 - // (A,t[0]) := t[0] + x[0]*y[i] - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (A,t[j]) := t[j] + x[j]*y[i] + A - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // t[N-1] = C + A - // - // This optimization saves 5N + 2 additions in the algorithm, and can be used whenever the highest bit - // of the modulus is zero (and not all of the remaining bits are set). - - mul(z, x, y) - return z -} - -// Square z = x * x (mod q) -// -// x must be less than q -func (z *Element) Square(x *Element) *Element { - // see Mul for doc. - mul(z, x, x) - return z -} diff --git a/ecc/bls12-378/fr/element_ops_amd64.s b/ecc/bls12-378/fr/element_ops_amd64.s deleted file mode 100644 index a863389b1f..0000000000 --- a/ecc/bls12-378/fr/element_ops_amd64.s +++ /dev/null @@ -1,230 +0,0 @@ -// +build !purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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. - -#include "textflag.h" -#include "funcdata.h" - -// modulus q -DATA q<>+0(SB)/8, $0x3291440000000001 -DATA q<>+8(SB)/8, $0xeae77f3da0940001 -DATA q<>+16(SB)/8, $0x87787fb4e3dbb0ff -DATA q<>+24(SB)/8, $0x20e7b9c8ef7b2eb1 -GLOBL q<>(SB), (RODATA+NOPTR), $32 - -// qInv0 q'[0] -DATA qInv0<>(SB)/8, $0x329143ffffffffff -GLOBL qInv0<>(SB), (RODATA+NOPTR), $8 - -#define REDUCE(ra0, ra1, ra2, ra3, rb0, rb1, rb2, rb3) \ - MOVQ ra0, rb0; \ - SUBQ q<>(SB), ra0; \ - MOVQ ra1, rb1; \ - SBBQ q<>+8(SB), ra1; \ - MOVQ ra2, rb2; \ - SBBQ q<>+16(SB), ra2; \ - MOVQ ra3, rb3; \ - SBBQ q<>+24(SB), ra3; \ - CMOVQCS rb0, ra0; \ - CMOVQCS rb1, ra1; \ - CMOVQCS rb2, ra2; \ - CMOVQCS rb3, ra3; \ - -TEXT ·reduce(SB), NOSPLIT, $0-8 - MOVQ res+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - - // reduce element(DX,CX,BX,SI) using temp registers (DI,R8,R9,R10) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - RET - -// MulBy3(x *Element) -TEXT ·MulBy3(SB), NOSPLIT, $0-8 - MOVQ x+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - - // reduce element(DX,CX,BX,SI) using temp registers (DI,R8,R9,R10) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10) - - ADDQ 0(AX), DX - ADCQ 8(AX), CX - ADCQ 16(AX), BX - ADCQ 24(AX), SI - - // reduce element(DX,CX,BX,SI) using temp registers (R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,R11,R12,R13,R14) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - RET - -// MulBy5(x *Element) -TEXT ·MulBy5(SB), NOSPLIT, $0-8 - MOVQ x+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - - // reduce element(DX,CX,BX,SI) using temp registers (DI,R8,R9,R10) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10) - - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - - // reduce element(DX,CX,BX,SI) using temp registers (R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,R11,R12,R13,R14) - - ADDQ 0(AX), DX - ADCQ 8(AX), CX - ADCQ 16(AX), BX - ADCQ 24(AX), SI - - // reduce element(DX,CX,BX,SI) using temp registers (R15,DI,R8,R9) - REDUCE(DX,CX,BX,SI,R15,DI,R8,R9) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - RET - -// MulBy13(x *Element) -TEXT ·MulBy13(SB), NOSPLIT, $0-8 - MOVQ x+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - - // reduce element(DX,CX,BX,SI) using temp registers (DI,R8,R9,R10) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10) - - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - - // reduce element(DX,CX,BX,SI) using temp registers (R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,R11,R12,R13,R14) - - MOVQ DX, R11 - MOVQ CX, R12 - MOVQ BX, R13 - MOVQ SI, R14 - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - - // reduce element(DX,CX,BX,SI) using temp registers (DI,R8,R9,R10) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10) - - ADDQ R11, DX - ADCQ R12, CX - ADCQ R13, BX - ADCQ R14, SI - - // reduce element(DX,CX,BX,SI) using temp registers (DI,R8,R9,R10) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10) - - ADDQ 0(AX), DX - ADCQ 8(AX), CX - ADCQ 16(AX), BX - ADCQ 24(AX), SI - - // reduce element(DX,CX,BX,SI) using temp registers (DI,R8,R9,R10) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - RET - -// Butterfly(a, b *Element) sets a = a + b; b = a - b -TEXT ·Butterfly(SB), NOSPLIT, $0-16 - MOVQ a+0(FP), AX - MOVQ 0(AX), CX - MOVQ 8(AX), BX - MOVQ 16(AX), SI - MOVQ 24(AX), DI - MOVQ CX, R8 - MOVQ BX, R9 - MOVQ SI, R10 - MOVQ DI, R11 - XORQ AX, AX - MOVQ b+8(FP), DX - ADDQ 0(DX), CX - ADCQ 8(DX), BX - ADCQ 16(DX), SI - ADCQ 24(DX), DI - SUBQ 0(DX), R8 - SBBQ 8(DX), R9 - SBBQ 16(DX), R10 - SBBQ 24(DX), R11 - MOVQ $0x3291440000000001, R12 - MOVQ $0xeae77f3da0940001, R13 - MOVQ $0x87787fb4e3dbb0ff, R14 - MOVQ $0x20e7b9c8ef7b2eb1, R15 - CMOVQCC AX, R12 - CMOVQCC AX, R13 - CMOVQCC AX, R14 - CMOVQCC AX, R15 - ADDQ R12, R8 - ADCQ R13, R9 - ADCQ R14, R10 - ADCQ R15, R11 - MOVQ R8, 0(DX) - MOVQ R9, 8(DX) - MOVQ R10, 16(DX) - MOVQ R11, 24(DX) - - // reduce element(CX,BX,SI,DI) using temp registers (R8,R9,R10,R11) - REDUCE(CX,BX,SI,DI,R8,R9,R10,R11) - - MOVQ a+0(FP), AX - MOVQ CX, 0(AX) - MOVQ BX, 8(AX) - MOVQ SI, 16(AX) - MOVQ DI, 24(AX) - RET diff --git a/ecc/bls12-378/fr/element_ops_purego.go b/ecc/bls12-378/fr/element_ops_purego.go deleted file mode 100644 index 14036d3bd7..0000000000 --- a/ecc/bls12-378/fr/element_ops_purego.go +++ /dev/null @@ -1,443 +0,0 @@ -//go:build !amd64 || purego -// +build !amd64 purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import "math/bits" - -// MulBy3 x *= 3 (mod q) -func MulBy3(x *Element) { - _x := *x - x.Double(x).Add(x, &_x) -} - -// MulBy5 x *= 5 (mod q) -func MulBy5(x *Element) { - _x := *x - x.Double(x).Double(x).Add(x, &_x) -} - -// MulBy13 x *= 13 (mod q) -func MulBy13(x *Element) { - var y = Element{ - 914279102867832731, - 5956798511920709511, - 10193226651174906632, - 329804807099814901, - } - x.Mul(x, &y) -} - -// Butterfly sets -// -// a = a + b (mod q) -// b = a - b (mod q) -func Butterfly(a, b *Element) { - _butterflyGeneric(a, b) -} - -func fromMont(z *Element) { - _fromMontGeneric(z) -} - -func reduce(z *Element) { - _reduceGeneric(z) -} - -// Mul z = x * y (mod q) -// -// x and y must be less than q -func (z *Element) Mul(x, y *Element) *Element { - - // Implements CIOS multiplication -- section 2.3.2 of Tolga Acar's thesis - // https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf - // - // The algorithm: - // - // for i=0 to N-1 - // C := 0 - // for j=0 to N-1 - // (C,t[j]) := t[j] + x[j]*y[i] + C - // (t[N+1],t[N]) := t[N] + C - // - // C := 0 - // m := t[0]*q'[0] mod D - // (C,_) := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // (C,t[N-1]) := t[N] + C - // t[N] := t[N+1] + C - // - // → N is the number of machine words needed to store the modulus q - // → D is the word size. For example, on a 64-bit architecture D is 2 64 - // → x[i], y[i], q[i] is the ith word of the numbers x,y,q - // → q'[0] is the lowest word of the number -q⁻¹ mod r. This quantity is pre-computed, as it does not depend on the inputs. - // → t is a temporary array of size N+2 - // → C, S are machine words. A pair (C,S) refers to (hi-bits, lo-bits) of a two-word number - // - // As described here https://hackmd.io/@gnark/modular_multiplication we can get rid of one carry chain and simplify: - // (also described in https://eprint.iacr.org/2022/1400.pdf annex) - // - // for i=0 to N-1 - // (A,t[0]) := t[0] + x[0]*y[i] - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (A,t[j]) := t[j] + x[j]*y[i] + A - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // t[N-1] = C + A - // - // This optimization saves 5N + 2 additions in the algorithm, and can be used whenever the highest bit - // of the modulus is zero (and not all of the remaining bits are set). - - var t0, t1, t2, t3 uint64 - var u0, u1, u2, u3 uint64 - { - var c0, c1, c2 uint64 - v := x[0] - u0, t0 = bits.Mul64(v, y[0]) - u1, t1 = bits.Mul64(v, y[1]) - u2, t2 = bits.Mul64(v, y[2]) - u3, t3 = bits.Mul64(v, y[3]) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - c2, _ = bits.Add64(u3, 0, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - - t2, c0 = bits.Add64(0, c1, c0) - u3, _ = bits.Add64(u3, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - c2, _ = bits.Add64(c2, 0, c0) - t2, c0 = bits.Add64(t3, t2, 0) - t3, _ = bits.Add64(u3, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[1] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - c2, _ = bits.Add64(u3, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - - t2, c0 = bits.Add64(0, c1, c0) - u3, _ = bits.Add64(u3, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - c2, _ = bits.Add64(c2, 0, c0) - t2, c0 = bits.Add64(t3, t2, 0) - t3, _ = bits.Add64(u3, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[2] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - c2, _ = bits.Add64(u3, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - - t2, c0 = bits.Add64(0, c1, c0) - u3, _ = bits.Add64(u3, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - c2, _ = bits.Add64(c2, 0, c0) - t2, c0 = bits.Add64(t3, t2, 0) - t3, _ = bits.Add64(u3, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[3] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - c2, _ = bits.Add64(u3, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - - t2, c0 = bits.Add64(0, c1, c0) - u3, _ = bits.Add64(u3, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - c2, _ = bits.Add64(c2, 0, c0) - t2, c0 = bits.Add64(t3, t2, 0) - t3, _ = bits.Add64(u3, c2, c0) - - } - z[0] = t0 - z[1] = t1 - z[2] = t2 - z[3] = t3 - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], _ = bits.Sub64(z[3], q3, b) - } - return z -} - -// Square z = x * x (mod q) -// -// x must be less than q -func (z *Element) Square(x *Element) *Element { - // see Mul for algorithm documentation - - var t0, t1, t2, t3 uint64 - var u0, u1, u2, u3 uint64 - { - var c0, c1, c2 uint64 - v := x[0] - u0, t0 = bits.Mul64(v, x[0]) - u1, t1 = bits.Mul64(v, x[1]) - u2, t2 = bits.Mul64(v, x[2]) - u3, t3 = bits.Mul64(v, x[3]) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - c2, _ = bits.Add64(u3, 0, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - - t2, c0 = bits.Add64(0, c1, c0) - u3, _ = bits.Add64(u3, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - c2, _ = bits.Add64(c2, 0, c0) - t2, c0 = bits.Add64(t3, t2, 0) - t3, _ = bits.Add64(u3, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[1] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - c2, _ = bits.Add64(u3, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - - t2, c0 = bits.Add64(0, c1, c0) - u3, _ = bits.Add64(u3, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - c2, _ = bits.Add64(c2, 0, c0) - t2, c0 = bits.Add64(t3, t2, 0) - t3, _ = bits.Add64(u3, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[2] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - c2, _ = bits.Add64(u3, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - - t2, c0 = bits.Add64(0, c1, c0) - u3, _ = bits.Add64(u3, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - c2, _ = bits.Add64(c2, 0, c0) - t2, c0 = bits.Add64(t3, t2, 0) - t3, _ = bits.Add64(u3, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[3] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - c2, _ = bits.Add64(u3, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - - t2, c0 = bits.Add64(0, c1, c0) - u3, _ = bits.Add64(u3, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - c2, _ = bits.Add64(c2, 0, c0) - t2, c0 = bits.Add64(t3, t2, 0) - t3, _ = bits.Add64(u3, c2, c0) - - } - z[0] = t0 - z[1] = t1 - z[2] = t2 - z[3] = t3 - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], _ = bits.Sub64(z[3], q3, b) - } - return z -} diff --git a/ecc/bls12-378/fr/element_test.go b/ecc/bls12-378/fr/element_test.go deleted file mode 100644 index bff7017c81..0000000000 --- a/ecc/bls12-378/fr/element_test.go +++ /dev/null @@ -1,2889 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import ( - "crypto/rand" - "encoding/json" - "fmt" - "math/big" - "math/bits" - - mrand "math/rand" - - "testing" - - "github.com/leanovate/gopter" - ggen "github.com/leanovate/gopter/gen" - "github.com/leanovate/gopter/prop" - - "github.com/stretchr/testify/require" -) - -// ------------------------------------------------------------------------------------------------- -// benchmarks -// most benchmarks are rudimentary and should sample a large number of random inputs -// or be run multiple times to ensure it didn't measure the fastest path of the function - -var benchResElement Element - -func BenchmarkElementSelect(b *testing.B) { - var x, y Element - x.SetRandom() - y.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Select(i%3, &x, &y) - } -} - -func BenchmarkElementSetRandom(b *testing.B) { - var x Element - x.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = x.SetRandom() - } -} - -func BenchmarkElementSetBytes(b *testing.B) { - var x Element - x.SetRandom() - bb := x.Bytes() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - benchResElement.SetBytes(bb[:]) - } - -} - -func BenchmarkElementMulByConstants(b *testing.B) { - b.Run("mulBy3", func(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - MulBy3(&benchResElement) - } - }) - b.Run("mulBy5", func(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - MulBy5(&benchResElement) - } - }) - b.Run("mulBy13", func(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - MulBy13(&benchResElement) - } - }) -} - -func BenchmarkElementInverse(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - benchResElement.Inverse(&x) - } - -} - -func BenchmarkElementButterfly(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - Butterfly(&x, &benchResElement) - } -} - -func BenchmarkElementExp(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b1, _ := rand.Int(rand.Reader, Modulus()) - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Exp(x, b1) - } -} - -func BenchmarkElementDouble(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Double(&benchResElement) - } -} - -func BenchmarkElementAdd(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Add(&x, &benchResElement) - } -} - -func BenchmarkElementSub(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Sub(&x, &benchResElement) - } -} - -func BenchmarkElementNeg(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Neg(&benchResElement) - } -} - -func BenchmarkElementDiv(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Div(&x, &benchResElement) - } -} - -func BenchmarkElementFromMont(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.fromMont() - } -} - -func BenchmarkElementSquare(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Square(&benchResElement) - } -} - -func BenchmarkElementSqrt(b *testing.B) { - var a Element - a.SetUint64(4) - a.Neg(&a) - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Sqrt(&a) - } -} - -func BenchmarkElementMul(b *testing.B) { - x := Element{ - 1260465344847950704, - 15627634503313390135, - 1085346480195626314, - 405261321576397495, - } - benchResElement.SetOne() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Mul(&benchResElement, &x) - } -} - -func BenchmarkElementCmp(b *testing.B) { - x := Element{ - 1260465344847950704, - 15627634503313390135, - 1085346480195626314, - 405261321576397495, - } - benchResElement = x - benchResElement[0] = 0 - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Cmp(&x) - } -} - -func TestElementCmp(t *testing.T) { - var x, y Element - - if x.Cmp(&y) != 0 { - t.Fatal("x == y") - } - - one := One() - y.Sub(&y, &one) - - if x.Cmp(&y) != -1 { - t.Fatal("x < y") - } - if y.Cmp(&x) != 1 { - t.Fatal("x < y") - } - - x = y - if x.Cmp(&y) != 0 { - t.Fatal("x == y") - } - - x.Sub(&x, &one) - if x.Cmp(&y) != -1 { - t.Fatal("x < y") - } - if y.Cmp(&x) != 1 { - t.Fatal("x < y") - } -} -func TestElementIsRandom(t *testing.T) { - for i := 0; i < 50; i++ { - var x, y Element - x.SetRandom() - y.SetRandom() - if x.Equal(&y) { - t.Fatal("2 random numbers are unlikely to be equal") - } - } -} - -func TestElementIsUint64(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("reduce should output a result smaller than modulus", prop.ForAll( - func(v uint64) bool { - var e Element - e.SetUint64(v) - - if !e.IsUint64() { - return false - } - - return e.Uint64() == v - }, - ggen.UInt64(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementNegZero(t *testing.T) { - var a, b Element - b.SetZero() - for a.IsZero() { - a.SetRandom() - } - a.Neg(&b) - if !a.IsZero() { - t.Fatal("neg(0) != 0") - } -} - -// ------------------------------------------------------------------------------------------------- -// Gopter tests -// most of them are generated with a template - -const ( - nbFuzzShort = 200 - nbFuzz = 1000 -) - -// special values to be used in tests -var staticTestValues []Element - -func init() { - staticTestValues = append(staticTestValues, Element{}) // zero - staticTestValues = append(staticTestValues, One()) // one - staticTestValues = append(staticTestValues, rSquare) // r² - var e, one Element - one.SetOne() - e.Sub(&qElement, &one) - staticTestValues = append(staticTestValues, e) // q - 1 - e.Double(&one) - staticTestValues = append(staticTestValues, e) // 2 - - { - a := qElement - a[0]-- - staticTestValues = append(staticTestValues, a) - } - staticTestValues = append(staticTestValues, Element{0}) - staticTestValues = append(staticTestValues, Element{0, 0}) - staticTestValues = append(staticTestValues, Element{1}) - staticTestValues = append(staticTestValues, Element{0, 1}) - staticTestValues = append(staticTestValues, Element{2}) - staticTestValues = append(staticTestValues, Element{0, 2}) - - { - a := qElement - a[3]-- - staticTestValues = append(staticTestValues, a) - } - { - a := qElement - a[3]-- - a[0]++ - staticTestValues = append(staticTestValues, a) - } - - { - a := qElement - a[3] = 0 - staticTestValues = append(staticTestValues, a) - } - -} - -func TestElementReduce(t *testing.T) { - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - s := testValues[i] - expected := s - reduce(&s) - _reduceGeneric(&expected) - if !s.Equal(&expected) { - t.Fatal("reduce failed: asm and generic impl don't match") - } - } - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := genFull() - - properties.Property("reduce should output a result smaller than modulus", prop.ForAll( - func(a Element) bool { - b := a - reduce(&a) - _reduceGeneric(&b) - return a.smallerThanModulus() && a.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementEqual(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("x.Equal(&y) iff x == y; likely false for random pairs", prop.ForAll( - func(a testPairElement, b testPairElement) bool { - return a.element.Equal(&b.element) == (a.element == b.element) - }, - genA, - genB, - )) - - properties.Property("x.Equal(&y) if x == y", prop.ForAll( - func(a testPairElement) bool { - b := a.element - return a.element.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementBytes(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("SetBytes(Bytes()) should stay constant", prop.ForAll( - func(a testPairElement) bool { - var b Element - bytes := a.element.Bytes() - b.SetBytes(bytes[:]) - return a.element.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementInverseExp(t *testing.T) { - // inverse must be equal to exp^-2 - exp := Modulus() - exp.Sub(exp, new(big.Int).SetUint64(2)) - - invMatchExp := func(a testPairElement) bool { - var b Element - b.Set(&a.element) - a.element.Inverse(&a.element) - b.Exp(b, exp) - - return a.element.Equal(&b) - } - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - properties := gopter.NewProperties(parameters) - genA := gen() - properties.Property("inv == exp^-2", prop.ForAll(invMatchExp, genA)) - properties.TestingRun(t, gopter.ConsoleReporter(false)) - - parameters.MinSuccessfulTests = 1 - properties = gopter.NewProperties(parameters) - properties.Property("inv(0) == 0", prop.ForAll(invMatchExp, ggen.OneConstOf(testPairElement{}))) - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func mulByConstant(z *Element, c uint8) { - var y Element - y.SetUint64(uint64(c)) - z.Mul(z, &y) -} - -func TestElementMulByConstants(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - implemented := []uint8{0, 1, 2, 3, 5, 13} - properties.Property("mulByConstant", prop.ForAll( - func(a testPairElement) bool { - for _, c := range implemented { - var constant Element - constant.SetUint64(uint64(c)) - - b := a.element - b.Mul(&b, &constant) - - aa := a.element - mulByConstant(&aa, c) - - if !aa.Equal(&b) { - return false - } - } - - return true - }, - genA, - )) - - properties.Property("MulBy3(x) == Mul(x, 3)", prop.ForAll( - func(a testPairElement) bool { - var constant Element - constant.SetUint64(3) - - b := a.element - b.Mul(&b, &constant) - - MulBy3(&a.element) - - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("MulBy5(x) == Mul(x, 5)", prop.ForAll( - func(a testPairElement) bool { - var constant Element - constant.SetUint64(5) - - b := a.element - b.Mul(&b, &constant) - - MulBy5(&a.element) - - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("MulBy13(x) == Mul(x, 13)", prop.ForAll( - func(a testPairElement) bool { - var constant Element - constant.SetUint64(13) - - b := a.element - b.Mul(&b, &constant) - - MulBy13(&a.element) - - return a.element.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementLegendre(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("legendre should output same result than big.Int.Jacobi", prop.ForAll( - func(a testPairElement) bool { - return a.element.Legendre() == big.Jacobi(&a.bigint, Modulus()) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementBitLen(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("BitLen should output same result than big.Int.BitLen", prop.ForAll( - func(a testPairElement) bool { - return a.element.fromMont().BitLen() == a.bigint.BitLen() - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementButterflies(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("butterfly0 == a -b; a +b", prop.ForAll( - func(a, b testPairElement) bool { - a0, b0 := a.element, b.element - - _butterflyGeneric(&a.element, &b.element) - Butterfly(&a0, &b0) - - return a.element.Equal(&a0) && b.element.Equal(&b0) - }, - genA, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementLexicographicallyLargest(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("element.Cmp should match LexicographicallyLargest output", prop.ForAll( - func(a testPairElement) bool { - var negA Element - negA.Neg(&a.element) - - cmpResult := a.element.Cmp(&negA) - lResult := a.element.LexicographicallyLargest() - - if lResult && cmpResult == 1 { - return true - } - if !lResult && cmpResult != 1 { - return true - } - return false - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementAdd(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Add: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Add(&a.element, &b.element) - a.element.Add(&a.element, &b.element) - b.element.Add(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Add: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Add(&a.element, &b.element) - - var d, e big.Int - d.Add(&a.bigint, &b.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Add(&a.element, &r) - d.Add(&a.bigint, &rb).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Add: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Add(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Add(&a, &b) - d.Add(&aBig, &bBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Add failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementSub(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Sub: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Sub(&a.element, &b.element) - a.element.Sub(&a.element, &b.element) - b.element.Sub(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Sub: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Sub(&a.element, &b.element) - - var d, e big.Int - d.Sub(&a.bigint, &b.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Sub(&a.element, &r) - d.Sub(&a.bigint, &rb).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Sub: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Sub(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Sub(&a, &b) - d.Sub(&aBig, &bBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Sub failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementMul(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Mul: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Mul(&a.element, &b.element) - a.element.Mul(&a.element, &b.element) - b.element.Mul(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Mul: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Mul(&a.element, &b.element) - - var d, e big.Int - d.Mul(&a.bigint, &b.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Mul(&a.element, &r) - d.Mul(&a.bigint, &rb).Mod(&d, Modulus()) - - // checking generic impl against asm path - var cGeneric Element - _mulGeneric(&cGeneric, &a.element, &r) - if !cGeneric.Equal(&c) { - // need to give context to failing error. - return false - } - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Mul: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Mul(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - properties.Property("Mul: assembly implementation must be consistent with generic one", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - c.Mul(&a.element, &b.element) - _mulGeneric(&d, &a.element, &b.element) - return c.Equal(&d) - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Mul(&a, &b) - d.Mul(&aBig, &bBig).Mod(&d, Modulus()) - - // checking asm against generic impl - var cGeneric Element - _mulGeneric(&cGeneric, &a, &b) - if !cGeneric.Equal(&c) { - t.Fatal("Mul failed special test values: asm and generic impl don't match") - } - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Mul failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementDiv(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Div: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Div(&a.element, &b.element) - a.element.Div(&a.element, &b.element) - b.element.Div(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Div: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Div(&a.element, &b.element) - - var d, e big.Int - d.ModInverse(&b.bigint, Modulus()) - d.Mul(&d, &a.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Div(&a.element, &r) - d.ModInverse(&rb, Modulus()) - d.Mul(&d, &a.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Div: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Div(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Div(&a, &b) - d.ModInverse(&bBig, Modulus()) - d.Mul(&d, &aBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Div failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementExp(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Exp: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Exp(a.element, &b.bigint) - a.element.Exp(a.element, &b.bigint) - b.element.Exp(d, &b.bigint) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Exp: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Exp(a.element, &b.bigint) - - var d, e big.Int - d.Exp(&a.bigint, &b.bigint, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Exp(a.element, &rb) - d.Exp(&a.bigint, &rb, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Exp: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Exp(a.element, &b.bigint) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Exp(a, &bBig) - d.Exp(&aBig, &bBig, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Exp failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementSquare(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Square: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Square(&a.element) - a.element.Square(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Square: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Square(&a.element) - - var d, e big.Int - d.Mul(&a.bigint, &a.bigint).Mod(&d, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Square: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Square(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Square(&a) - - var d, e big.Int - d.Mul(&aBig, &aBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Square failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementInverse(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Inverse: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Inverse(&a.element) - a.element.Inverse(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Inverse: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Inverse(&a.element) - - var d, e big.Int - d.ModInverse(&a.bigint, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Inverse: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Inverse(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Inverse(&a) - - var d, e big.Int - d.ModInverse(&aBig, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Inverse failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementSqrt(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Sqrt: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - b := a.element - - b.Sqrt(&a.element) - a.element.Sqrt(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Sqrt: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Sqrt(&a.element) - - var d, e big.Int - d.ModSqrt(&a.bigint, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Sqrt: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Sqrt(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Sqrt(&a) - - var d, e big.Int - d.ModSqrt(&aBig, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Sqrt failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementDouble(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Double: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Double(&a.element) - a.element.Double(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Double: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Double(&a.element) - - var d, e big.Int - d.Lsh(&a.bigint, 1).Mod(&d, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Double: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Double(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Double(&a) - - var d, e big.Int - d.Lsh(&aBig, 1).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Double failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementNeg(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Neg: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Neg(&a.element) - a.element.Neg(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Neg: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Neg(&a.element) - - var d, e big.Int - d.Neg(&a.bigint).Mod(&d, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Neg: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Neg(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Neg(&a) - - var d, e big.Int - d.Neg(&aBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Neg failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementFixedExp(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - var ( - _bLegendreExponentElement *big.Int - _bSqrtExponentElement *big.Int - ) - - _bLegendreExponentElement, _ = new(big.Int).SetString("1073dce477bd9758c3bc3fda71edd87ff573bf9ed04a00009948a20000000000", 16) - const sqrtExponentElement = "41cf7391def65d630ef0ff69c7b761ffd5cefe7b4128000265228" - _bSqrtExponentElement, _ = new(big.Int).SetString(sqrtExponentElement, 16) - - genA := gen() - - properties.Property(fmt.Sprintf("expBySqrtExp must match Exp(%s)", sqrtExponentElement), prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.expBySqrtExp(c) - d.Exp(d, _bSqrtExponentElement) - return c.Equal(&d) - }, - genA, - )) - - properties.Property("expByLegendreExp must match Exp(1073dce477bd9758c3bc3fda71edd87ff573bf9ed04a00009948a20000000000)", prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.expByLegendreExp(c) - d.Exp(d, _bLegendreExponentElement) - return c.Equal(&d) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementHalve(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - var twoInv Element - twoInv.SetUint64(2) - twoInv.Inverse(&twoInv) - - properties.Property("z.Halve must match z / 2", prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.Halve() - d.Mul(&d, &twoInv) - return c.Equal(&d) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func combineSelectionArguments(c int64, z int8) int { - if z%3 == 0 { - return 0 - } - return int(c) -} - -func TestElementSelect(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := genFull() - genB := genFull() - genC := ggen.Int64() //the condition - genZ := ggen.Int8() //to make zeros artificially more likely - - properties.Property("Select: must select correctly", prop.ForAll( - func(a, b Element, cond int64, z int8) bool { - condC := combineSelectionArguments(cond, z) - - var c Element - c.Select(condC, &a, &b) - - if condC == 0 { - return c.Equal(&a) - } - return c.Equal(&b) - }, - genA, - genB, - genC, - genZ, - )) - - properties.Property("Select: having the receiver as operand should output the same result", prop.ForAll( - func(a, b Element, cond int64, z int8) bool { - condC := combineSelectionArguments(cond, z) - - var c, d Element - d.Set(&a) - c.Select(condC, &a, &b) - a.Select(condC, &a, &b) - b.Select(condC, &d, &b) - return a.Equal(&b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - genC, - genZ, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementSetInt64(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("z.SetInt64 must match z.SetString", prop.ForAll( - func(a testPairElement, v int64) bool { - c := a.element - d := a.element - - c.SetInt64(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, ggen.Int64(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementSetInterface(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genInt := ggen.Int - genInt8 := ggen.Int8 - genInt16 := ggen.Int16 - genInt32 := ggen.Int32 - genInt64 := ggen.Int64 - - genUint := ggen.UInt - genUint8 := ggen.UInt8 - genUint16 := ggen.UInt16 - genUint32 := ggen.UInt32 - genUint64 := ggen.UInt64 - - properties.Property("z.SetInterface must match z.SetString with int8", prop.ForAll( - func(a testPairElement, v int8) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt8(), - )) - - properties.Property("z.SetInterface must match z.SetString with int16", prop.ForAll( - func(a testPairElement, v int16) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt16(), - )) - - properties.Property("z.SetInterface must match z.SetString with int32", prop.ForAll( - func(a testPairElement, v int32) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt32(), - )) - - properties.Property("z.SetInterface must match z.SetString with int64", prop.ForAll( - func(a testPairElement, v int64) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt64(), - )) - - properties.Property("z.SetInterface must match z.SetString with int", prop.ForAll( - func(a testPairElement, v int) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint8", prop.ForAll( - func(a testPairElement, v uint8) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint8(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint16", prop.ForAll( - func(a testPairElement, v uint16) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint16(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint32", prop.ForAll( - func(a testPairElement, v uint32) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint32(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint64", prop.ForAll( - func(a testPairElement, v uint64) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint64(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint", prop.ForAll( - func(a testPairElement, v uint) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - - { - assert := require.New(t) - var e Element - r, err := e.SetInterface(nil) - assert.Nil(r) - assert.Error(err) - - var ptE *Element - var ptB *big.Int - - r, err = e.SetInterface(ptE) - assert.Nil(r) - assert.Error(err) - ptE = new(Element).SetOne() - r, err = e.SetInterface(ptE) - assert.NoError(err) - assert.True(r.IsOne()) - - r, err = e.SetInterface(ptB) - assert.Nil(r) - assert.Error(err) - - } -} - -func TestElementNegativeExp(t *testing.T) { - t.Parallel() - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("x⁻ᵏ == 1/xᵏ", prop.ForAll( - func(a, b testPairElement) bool { - - var nb, d, e big.Int - nb.Neg(&b.bigint) - - var c Element - c.Exp(a.element, &nb) - - d.Exp(&a.bigint, &nb, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementNewElement(t *testing.T) { - assert := require.New(t) - - t.Parallel() - - e := NewElement(1) - assert.True(e.IsOne()) - - e = NewElement(0) - assert.True(e.IsZero()) -} - -func TestElementBatchInvert(t *testing.T) { - assert := require.New(t) - - t.Parallel() - - // ensure batchInvert([x]) == invert(x) - for i := int64(-1); i <= 2; i++ { - var e, eInv Element - e.SetInt64(i) - eInv.Inverse(&e) - - a := []Element{e} - aInv := BatchInvert(a) - - assert.True(aInv[0].Equal(&eInv), "batchInvert != invert") - - } - - // test x * x⁻¹ == 1 - tData := [][]int64{ - {-1, 1, 2, 3}, - {0, -1, 1, 2, 3, 0}, - {0, -1, 1, 0, 2, 3, 0}, - {-1, 1, 0, 2, 3}, - {0, 0, 1}, - {1, 0, 0}, - {0, 0, 0}, - } - - for _, t := range tData { - a := make([]Element, len(t)) - for i := 0; i < len(a); i++ { - a[i].SetInt64(t[i]) - } - - aInv := BatchInvert(a) - - assert.True(len(aInv) == len(a)) - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - assert.True(aInv[i].IsZero(), "0⁻¹ != 0") - } else { - assert.True(a[i].Mul(&a[i], &aInv[i]).IsOne(), "x * x⁻¹ != 1") - } - } - } - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("batchInvert --> x * x⁻¹ == 1", prop.ForAll( - func(tp testPairElement, r uint8) bool { - - a := make([]Element, r) - if r != 0 { - a[0] = tp.element - - } - one := One() - for i := 1; i < len(a); i++ { - a[i].Add(&a[i-1], &one) - } - - aInv := BatchInvert(a) - - assert.True(len(aInv) == len(a)) - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - if !aInv[i].IsZero() { - return false - } - } else { - if !a[i].Mul(&a[i], &aInv[i]).IsOne() { - return false - } - } - } - return true - }, - genA, ggen.UInt8(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementFromMont(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Assembly implementation must be consistent with generic one", prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.fromMont() - _fromMontGeneric(&d) - return c.Equal(&d) - }, - genA, - )) - - properties.Property("x.fromMont().toMont() == x", prop.ForAll( - func(a testPairElement) bool { - c := a.element - c.fromMont().toMont() - return c.Equal(&a.element) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementJSON(t *testing.T) { - assert := require.New(t) - - type S struct { - A Element - B [3]Element - C *Element - D *Element - } - - // encode to JSON - var s S - s.A.SetString("-1") - s.B[2].SetUint64(42) - s.D = new(Element).SetUint64(8000) - - encoded, err := json.Marshal(&s) - assert.NoError(err) - // we may need to adjust "42" and "8000" values for some moduli; see Text() method for more details. - formatValue := func(v int64) string { - var a big.Int - a.SetInt64(v) - a.Mod(&a, Modulus()) - const maxUint16 = 65535 - var aNeg big.Int - aNeg.Neg(&a).Mod(&aNeg, Modulus()) - if aNeg.Uint64() != 0 && aNeg.Uint64() <= maxUint16 { - return "-" + aNeg.Text(10) - } - return a.Text(10) - } - expected := fmt.Sprintf("{\"A\":%s,\"B\":[0,0,%s],\"C\":null,\"D\":%s}", formatValue(-1), formatValue(42), formatValue(8000)) - assert.Equal(expected, string(encoded)) - - // decode valid - var decoded S - err = json.Unmarshal([]byte(expected), &decoded) - assert.NoError(err) - - assert.Equal(s, decoded, "element -> json -> element round trip failed") - - // decode hex and string values - withHexValues := "{\"A\":\"-1\",\"B\":[0,\"0x00000\",\"0x2A\"],\"C\":null,\"D\":\"8000\"}" - - var decodedS S - err = json.Unmarshal([]byte(withHexValues), &decodedS) - assert.NoError(err) - - assert.Equal(s, decodedS, " json with strings -> element failed") - -} - -type testPairElement struct { - element Element - bigint big.Int -} - -func gen() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var g testPairElement - - g.element = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - if qElement[3] != ^uint64(0) { - g.element[3] %= (qElement[3] + 1) - } - - for !g.element.smallerThanModulus() { - g.element = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - if qElement[3] != ^uint64(0) { - g.element[3] %= (qElement[3] + 1) - } - } - - g.element.BigInt(&g.bigint) - genResult := gopter.NewGenResult(g, gopter.NoShrinker) - return genResult - } -} - -func genFull() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - - genRandomFq := func() Element { - var g Element - - g = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - - if qElement[3] != ^uint64(0) { - g[3] %= (qElement[3] + 1) - } - - for !g.smallerThanModulus() { - g = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - if qElement[3] != ^uint64(0) { - g[3] %= (qElement[3] + 1) - } - } - - return g - } - a := genRandomFq() - - var carry uint64 - a[0], carry = bits.Add64(a[0], qElement[0], carry) - a[1], carry = bits.Add64(a[1], qElement[1], carry) - a[2], carry = bits.Add64(a[2], qElement[2], carry) - a[3], _ = bits.Add64(a[3], qElement[3], carry) - - genResult := gopter.NewGenResult(a, gopter.NoShrinker) - return genResult - } -} - -func (z *Element) matchVeryBigInt(aHi uint64, aInt *big.Int) error { - var modulus big.Int - var aIntMod big.Int - modulus.SetInt64(1) - modulus.Lsh(&modulus, (Limbs+1)*64) - aIntMod.Mod(aInt, &modulus) - - slice := append(z[:], aHi) - - return bigIntMatchUint64Slice(&aIntMod, slice) -} - -// TODO: Phase out in favor of property based testing -func (z *Element) assertMatchVeryBigInt(t *testing.T, aHi uint64, aInt *big.Int) { - - if err := z.matchVeryBigInt(aHi, aInt); err != nil { - t.Error(err) - } -} - -// bigIntMatchUint64Slice is a test helper to match big.Int words against a uint64 slice -func bigIntMatchUint64Slice(aInt *big.Int, a []uint64) error { - - words := aInt.Bits() - - const steps = 64 / bits.UintSize - const filter uint64 = 0xFFFFFFFFFFFFFFFF >> (64 - bits.UintSize) - for i := 0; i < len(a)*steps; i++ { - - var wI big.Word - - if i < len(words) { - wI = words[i] - } - - aI := a[i/steps] >> ((i * bits.UintSize) % 64) - aI &= filter - - if uint64(wI) != aI { - return fmt.Errorf("bignum mismatch: disagreement on word %d: %x ≠ %x; %d ≠ %d", i, uint64(wI), aI, uint64(wI), aI) - } - } - - return nil -} - -func TestElementInversionApproximation(t *testing.T) { - var x Element - for i := 0; i < 1000; i++ { - x.SetRandom() - - // Normally small elements are unlikely. Here we give them a higher chance - xZeros := mrand.Int() % Limbs //#nosec G404 weak rng is fine here - for j := 1; j < xZeros; j++ { - x[Limbs-j] = 0 - } - - a := approximate(&x, x.BitLen()) - aRef := approximateRef(&x) - - if a != aRef { - t.Error("Approximation mismatch") - } - } -} - -func TestElementInversionCorrectionFactorFormula(t *testing.T) { - const kLimbs = k * Limbs - const power = kLimbs*6 + invIterationsN*(kLimbs-k+1) - factorInt := big.NewInt(1) - factorInt.Lsh(factorInt, power) - factorInt.Mod(factorInt, Modulus()) - - var refFactorInt big.Int - inversionCorrectionFactor := Element{ - inversionCorrectionFactorWord0, - inversionCorrectionFactorWord1, - inversionCorrectionFactorWord2, - inversionCorrectionFactorWord3, - } - inversionCorrectionFactor.toBigInt(&refFactorInt) - - if refFactorInt.Cmp(factorInt) != 0 { - t.Error("mismatch") - } -} - -func TestElementLinearComb(t *testing.T) { - var x Element - var y Element - - for i := 0; i < 1000; i++ { - x.SetRandom() - y.SetRandom() - testLinearComb(t, &x, mrand.Int63(), &y, mrand.Int63()) //#nosec G404 weak rng is fine here - } -} - -// Probably unnecessary post-dev. In case the output of inv is wrong, this checks whether it's only off by a constant factor. -func TestElementInversionCorrectionFactor(t *testing.T) { - - // (1/x)/inv(x) = (1/1)/inv(1) ⇔ inv(1) = x inv(x) - - var one Element - var oneInv Element - one.SetOne() - oneInv.Inverse(&one) - - for i := 0; i < 100; i++ { - var x Element - var xInv Element - x.SetRandom() - xInv.Inverse(&x) - - x.Mul(&x, &xInv) - if !x.Equal(&oneInv) { - t.Error("Correction factor is inconsistent") - } - } - - if !oneInv.Equal(&one) { - var i big.Int - oneInv.BigInt(&i) // no montgomery - i.ModInverse(&i, Modulus()) - var fac Element - fac.setBigInt(&i) // back to montgomery - - var facTimesFac Element - facTimesFac.Mul(&fac, &Element{ - inversionCorrectionFactorWord0, - inversionCorrectionFactorWord1, - inversionCorrectionFactorWord2, - inversionCorrectionFactorWord3, - }) - - t.Error("Correction factor is consistently off by", fac, "Should be", facTimesFac) - } -} - -func TestElementBigNumNeg(t *testing.T) { - var a Element - aHi := negL(&a, 0) - if !a.IsZero() || aHi != 0 { - t.Error("-0 != 0") - } -} - -func TestElementBigNumWMul(t *testing.T) { - var x Element - - for i := 0; i < 1000; i++ { - x.SetRandom() - w := mrand.Int63() //#nosec G404 weak rng is fine here - testBigNumWMul(t, &x, w) - } -} - -func TestElementVeryBigIntConversion(t *testing.T) { - xHi := mrand.Uint64() //#nosec G404 weak rng is fine here - var x Element - x.SetRandom() - var xInt big.Int - x.toVeryBigIntSigned(&xInt, xHi) - x.assertMatchVeryBigInt(t, xHi, &xInt) -} - -type veryBigInt struct { - asInt big.Int - low Element - hi uint64 -} - -// genVeryBigIntSigned if sign == 0, no sign is forced -func genVeryBigIntSigned(sign int) gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var g veryBigInt - - g.low = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - - g.hi = genParams.NextUint64() - - if sign < 0 { - g.hi |= signBitSelector - } else if sign > 0 { - g.hi &= ^signBitSelector - } - - g.low.toVeryBigIntSigned(&g.asInt, g.hi) - - genResult := gopter.NewGenResult(g, gopter.NoShrinker) - return genResult - } -} - -func TestElementMontReduce(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - gen := genVeryBigIntSigned(0) - - properties.Property("Montgomery reduction is correct", prop.ForAll( - func(g veryBigInt) bool { - var res Element - var resInt big.Int - - montReduce(&resInt, &g.asInt) - res.montReduceSigned(&g.low, g.hi) - - return res.matchVeryBigInt(0, &resInt) == nil - }, - gen, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementMontReduceMultipleOfR(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - gen := ggen.UInt64() - - properties.Property("Montgomery reduction is correct", prop.ForAll( - func(hi uint64) bool { - var zero, res Element - var asInt, resInt big.Int - - zero.toVeryBigIntSigned(&asInt, hi) - - montReduce(&resInt, &asInt) - res.montReduceSigned(&zero, hi) - - return res.matchVeryBigInt(0, &resInt) == nil - }, - gen, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElement0Inverse(t *testing.T) { - var x Element - x.Inverse(&x) - if !x.IsZero() { - t.Fail() - } -} - -// TODO: Tests like this (update factor related) are common to all fields. Move them to somewhere non-autogen -func TestUpdateFactorSubtraction(t *testing.T) { - for i := 0; i < 1000; i++ { - - f0, g0 := randomizeUpdateFactors() - f1, g1 := randomizeUpdateFactors() - - for f0-f1 > 1<<31 || f0-f1 <= -1<<31 { - f1 /= 2 - } - - for g0-g1 > 1<<31 || g0-g1 <= -1<<31 { - g1 /= 2 - } - - c0 := updateFactorsCompose(f0, g0) - c1 := updateFactorsCompose(f1, g1) - - cRes := c0 - c1 - fRes, gRes := updateFactorsDecompose(cRes) - - if fRes != f0-f1 || gRes != g0-g1 { - t.Error(i) - } - } -} - -func TestUpdateFactorsDouble(t *testing.T) { - for i := 0; i < 1000; i++ { - f, g := randomizeUpdateFactors() - - if f > 1<<30 || f < (-1<<31+1)/2 { - f /= 2 - if g <= 1<<29 && g >= (-1<<31+1)/4 { - g *= 2 //g was kept small on f's account. Now that we're halving f, we can double g - } - } - - if g > 1<<30 || g < (-1<<31+1)/2 { - g /= 2 - - if f <= 1<<29 && f >= (-1<<31+1)/4 { - f *= 2 //f was kept small on g's account. Now that we're halving g, we can double f - } - } - - c := updateFactorsCompose(f, g) - cD := c * 2 - fD, gD := updateFactorsDecompose(cD) - - if fD != 2*f || gD != 2*g { - t.Error(i) - } - } -} - -func TestUpdateFactorsNeg(t *testing.T) { - var fMistake bool - for i := 0; i < 1000; i++ { - f, g := randomizeUpdateFactors() - - if f == 0x80000000 || g == 0x80000000 { - // Update factors this large can only have been obtained after 31 iterations and will therefore never be negated - // We don't have capacity to store -2³¹ - // Repeat this iteration - i-- - continue - } - - c := updateFactorsCompose(f, g) - nc := -c - nf, ng := updateFactorsDecompose(nc) - fMistake = fMistake || nf != -f - if nf != -f || ng != -g { - t.Errorf("Mismatch iteration #%d:\n%d, %d ->\n %d -> %d ->\n %d, %d\n Inputs in hex: %X, %X", - i, f, g, c, nc, nf, ng, f, g) - } - } - if fMistake { - t.Error("Mistake with f detected") - } else { - t.Log("All good with f") - } -} - -func TestUpdateFactorsNeg0(t *testing.T) { - c := updateFactorsCompose(0, 0) - t.Logf("c(0,0) = %X", c) - cn := -c - - if c != cn { - t.Error("Negation of zero update factors should yield the same result.") - } -} - -func TestUpdateFactorDecomposition(t *testing.T) { - var negSeen bool - - for i := 0; i < 1000; i++ { - - f, g := randomizeUpdateFactors() - - if f <= -(1<<31) || f > 1<<31 { - t.Fatal("f out of range") - } - - negSeen = negSeen || f < 0 - - c := updateFactorsCompose(f, g) - - fBack, gBack := updateFactorsDecompose(c) - - if f != fBack || g != gBack { - t.Errorf("(%d, %d) -> %d -> (%d, %d)\n", f, g, c, fBack, gBack) - } - } - - if !negSeen { - t.Fatal("No negative f factors") - } -} - -func TestUpdateFactorInitialValues(t *testing.T) { - - f0, g0 := updateFactorsDecompose(updateFactorIdentityMatrixRow0) - f1, g1 := updateFactorsDecompose(updateFactorIdentityMatrixRow1) - - if f0 != 1 || g0 != 0 || f1 != 0 || g1 != 1 { - t.Error("Update factor initial value constants are incorrect") - } -} - -func TestUpdateFactorsRandomization(t *testing.T) { - var maxLen int - - //t.Log("|f| + |g| is not to exceed", 1 << 31) - for i := 0; i < 1000; i++ { - f, g := randomizeUpdateFactors() - lf, lg := abs64T32(f), abs64T32(g) - absSum := lf + lg - if absSum >= 1<<31 { - - if absSum == 1<<31 { - maxLen++ - } else { - t.Error(i, "Sum of absolute values too large, f =", f, ",g =", g, ",|f| + |g| =", absSum) - } - } - } - - if maxLen == 0 { - t.Error("max len not observed") - } else { - t.Log(maxLen, "maxLens observed") - } -} - -func randomizeUpdateFactor(absLimit uint32) int64 { - const maxSizeLikelihood = 10 - maxSize := mrand.Intn(maxSizeLikelihood) //#nosec G404 weak rng is fine here - - absLimit64 := int64(absLimit) - var f int64 - switch maxSize { - case 0: - f = absLimit64 - case 1: - f = -absLimit64 - default: - f = int64(mrand.Uint64()%(2*uint64(absLimit64)+1)) - absLimit64 //#nosec G404 weak rng is fine here - } - - if f > 1<<31 { - return 1 << 31 - } else if f < -1<<31+1 { - return -1<<31 + 1 - } - - return f -} - -func abs64T32(f int64) uint32 { - if f >= 1<<32 || f < -1<<32 { - panic("f out of range") - } - - if f < 0 { - return uint32(-f) - } - return uint32(f) -} - -func randomizeUpdateFactors() (int64, int64) { - var f [2]int64 - b := mrand.Int() % 2 //#nosec G404 weak rng is fine here - - f[b] = randomizeUpdateFactor(1 << 31) - - //As per the paper, |f| + |g| \le 2³¹. - f[1-b] = randomizeUpdateFactor(1<<31 - abs64T32(f[b])) - - //Patching another edge case - if f[0]+f[1] == -1<<31 { - b = mrand.Int() % 2 //#nosec G404 weak rng is fine here - f[b]++ - } - - return f[0], f[1] -} - -func testLinearComb(t *testing.T, x *Element, xC int64, y *Element, yC int64) { - - var p1 big.Int - x.toBigInt(&p1) - p1.Mul(&p1, big.NewInt(xC)) - - var p2 big.Int - y.toBigInt(&p2) - p2.Mul(&p2, big.NewInt(yC)) - - p1.Add(&p1, &p2) - p1.Mod(&p1, Modulus()) - montReduce(&p1, &p1) - - var z Element - z.linearComb(x, xC, y, yC) - z.assertMatchVeryBigInt(t, 0, &p1) -} - -func testBigNumWMul(t *testing.T, a *Element, c int64) { - var aHi uint64 - var aTimes Element - aHi = aTimes.mulWNonModular(a, c) - - assertMulProduct(t, a, c, &aTimes, aHi) -} - -func updateFactorsCompose(f int64, g int64) int64 { - return f + g<<32 -} - -var rInv big.Int - -func montReduce(res *big.Int, x *big.Int) { - if rInv.BitLen() == 0 { // initialization - rInv.SetUint64(1) - rInv.Lsh(&rInv, Limbs*64) - rInv.ModInverse(&rInv, Modulus()) - } - res.Mul(x, &rInv) - res.Mod(res, Modulus()) -} - -func (z *Element) toVeryBigIntUnsigned(i *big.Int, xHi uint64) { - z.toBigInt(i) - var upperWord big.Int - upperWord.SetUint64(xHi) - upperWord.Lsh(&upperWord, Limbs*64) - i.Add(&upperWord, i) -} - -func (z *Element) toVeryBigIntSigned(i *big.Int, xHi uint64) { - z.toVeryBigIntUnsigned(i, xHi) - if signBitSelector&xHi != 0 { - twosCompModulus := big.NewInt(1) - twosCompModulus.Lsh(twosCompModulus, (Limbs+1)*64) - i.Sub(i, twosCompModulus) - } -} - -func assertMulProduct(t *testing.T, x *Element, c int64, result *Element, resultHi uint64) big.Int { - var xInt big.Int - x.toBigInt(&xInt) - - xInt.Mul(&xInt, big.NewInt(c)) - - result.assertMatchVeryBigInt(t, resultHi, &xInt) - return xInt -} - -func approximateRef(x *Element) uint64 { - - var asInt big.Int - x.toBigInt(&asInt) - n := x.BitLen() - - if n <= 64 { - return asInt.Uint64() - } - - modulus := big.NewInt(1 << 31) - var lo big.Int - lo.Mod(&asInt, modulus) - - modulus.Lsh(modulus, uint(n-64)) - var hi big.Int - hi.Div(&asInt, modulus) - hi.Lsh(&hi, 31) - - hi.Add(&hi, &lo) - return hi.Uint64() -} diff --git a/ecc/bls12-378/fr/fft/bitreverse.go b/ecc/bls12-378/fr/fft/bitreverse.go deleted file mode 100644 index af7878efba..0000000000 --- a/ecc/bls12-378/fr/fft/bitreverse.go +++ /dev/null @@ -1,574 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fft - -import ( - "math/bits" - "runtime" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" -) - -// BitReverse applies the bit-reversal permutation to v. -// len(v) must be a power of 2 -func BitReverse(v []fr.Element) { - n := uint64(len(v)) - if bits.OnesCount64(n) != 1 { - panic("len(a) must be a power of 2") - } - - if runtime.GOARCH == "arm64" { - bitReverseNaive(v) - } else { - bitReverseCobra(v) - } -} - -// bitReverseNaive applies the bit-reversal permutation to v. -// len(v) must be a power of 2 -func bitReverseNaive(v []fr.Element) { - n := uint64(len(v)) - nn := uint64(64 - bits.TrailingZeros64(n)) - - for i := uint64(0); i < n; i++ { - iRev := bits.Reverse64(i) >> nn - if iRev > i { - v[i], v[iRev] = v[iRev], v[i] - } - } -} - -// bitReverseCobraInPlace applies the bit-reversal permutation to v. -// len(v) must be a power of 2 -// This is derived from: -// -// - Towards an Optimal Bit-Reversal Permutation Program -// Larry Carter and Kang Su Gatlin, 1998 -// https://csaws.cs.technion.ac.il/~itai/Courses/Cache/bit.pdf -// -// - Practically efficient methods for performing bit-reversed -// permutation in C++11 on the x86-64 architecture -// Knauth, Adas, Whitfield, Wang, Ickler, Conrad, Serang, 2017 -// https://arxiv.org/pdf/1708.01873.pdf -// -// - and more specifically, constantine implementation: -// https://github.com/mratsim/constantine/blob/d51699248db04e29c7b1ad97e0bafa1499db00b5/constantine/math/polynomials/fft.nim#L205 -// by Mamy Ratsimbazafy (@mratsim). -func bitReverseCobraInPlace(v []fr.Element) { - logN := uint64(bits.Len64(uint64(len(v))) - 1) - logTileSize := deriveLogTileSize(logN) - logBLen := logN - 2*logTileSize - bLen := uint64(1) << logBLen - bShift := logBLen + logTileSize - tileSize := uint64(1) << logTileSize - - // rough idea; - // bit reversal permutation naive implementation may have some cache associativity issues, - // since we are accessing elements by strides of powers of 2. - // on large inputs, this is noticeable and can be improved by using a t buffer. - // idea is for t buffer to be small enough to fit in cache. - // in the first inner loop, we copy the elements of v into t in a bit-reversed order. - // in the subsequent inner loops, accesses have much better cache locality than the naive implementation. - // hence even if we apparently do more work (swaps / copies), we are faster. - // - // on arm64 (and particularly on M1 macs), this is not noticeable, and the naive implementation is faster, - // in most cases. - // on x86 (and particularly on aws hpc6a) this is noticeable, and the t buffer implementation is faster (up to 3x). - // - // optimal choice for the tile size is cache dependent; in theory, we want the t buffer to fit in the L1 cache; - // in practice, a common size for L1 is 64kb, a field element is 32bytes or more. - // hence we can fit 2k elements in the L1 cache, which corresponds to a tile size of 2**5 with some margin for cache conflicts. - // - // for most sizes of interest, this tile size choice doesn't yield good results; - // we find that a tile size of 2**9 gives best results for input sizes from 2**21 up to 2**27+. - t := make([]fr.Element, tileSize*tileSize) - - // see https://csaws.cs.technion.ac.il/~itai/Courses/Cache/bit.pdf - // for a detailed explanation of the algorithm. - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> (64 - logTileSize)) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> (64 - logTileSize)) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> (64 - logTileSize) - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> (64 - logTileSize) - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> (64 - logTileSize)) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } -} - -func bitReverseCobra(v []fr.Element) { - switch len(v) { - case 1 << 21: - bitReverseCobraInPlace_9_21(v) - case 1 << 22: - bitReverseCobraInPlace_9_22(v) - case 1 << 23: - bitReverseCobraInPlace_9_23(v) - case 1 << 24: - bitReverseCobraInPlace_9_24(v) - case 1 << 25: - bitReverseCobraInPlace_9_25(v) - case 1 << 26: - bitReverseCobraInPlace_9_26(v) - case 1 << 27: - bitReverseCobraInPlace_9_27(v) - default: - if len(v) > 1<<27 { - bitReverseCobraInPlace(v) - } else { - bitReverseNaive(v) - } - } -} - -func deriveLogTileSize(logN uint64) uint64 { - q := uint64(9) // see bitReverseCobraInPlace for more details - - for int(logN)-int(2*q) <= 0 { - q-- - } - - return q -} - -// bitReverseCobraInPlace_9_21 applies the bit-reversal permutation to v. -// len(v) must be 1 << 21. -// see bitReverseCobraInPlace for more details; this function is specialized for 9, -// as it declares the t buffer and various constants statically for performance. -func bitReverseCobraInPlace_9_21(v []fr.Element) { - const ( - logTileSize = uint64(9) - tileSize = uint64(1) << logTileSize - logN = 21 - logBLen = logN - 2*logTileSize - bShift = logBLen + logTileSize - bLen = uint64(1) << logBLen - ) - - var t [tileSize * tileSize]fr.Element - - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> 55) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> 55) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> 55 - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> 55 - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> 55) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } - -} - -// bitReverseCobraInPlace_9_22 applies the bit-reversal permutation to v. -// len(v) must be 1 << 22. -// see bitReverseCobraInPlace for more details; this function is specialized for 9, -// as it declares the t buffer and various constants statically for performance. -func bitReverseCobraInPlace_9_22(v []fr.Element) { - const ( - logTileSize = uint64(9) - tileSize = uint64(1) << logTileSize - logN = 22 - logBLen = logN - 2*logTileSize - bShift = logBLen + logTileSize - bLen = uint64(1) << logBLen - ) - - var t [tileSize * tileSize]fr.Element - - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> 55) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> 55) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> 55 - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> 55 - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> 55) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } - -} - -// bitReverseCobraInPlace_9_23 applies the bit-reversal permutation to v. -// len(v) must be 1 << 23. -// see bitReverseCobraInPlace for more details; this function is specialized for 9, -// as it declares the t buffer and various constants statically for performance. -func bitReverseCobraInPlace_9_23(v []fr.Element) { - const ( - logTileSize = uint64(9) - tileSize = uint64(1) << logTileSize - logN = 23 - logBLen = logN - 2*logTileSize - bShift = logBLen + logTileSize - bLen = uint64(1) << logBLen - ) - - var t [tileSize * tileSize]fr.Element - - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> 55) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> 55) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> 55 - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> 55 - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> 55) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } - -} - -// bitReverseCobraInPlace_9_24 applies the bit-reversal permutation to v. -// len(v) must be 1 << 24. -// see bitReverseCobraInPlace for more details; this function is specialized for 9, -// as it declares the t buffer and various constants statically for performance. -func bitReverseCobraInPlace_9_24(v []fr.Element) { - const ( - logTileSize = uint64(9) - tileSize = uint64(1) << logTileSize - logN = 24 - logBLen = logN - 2*logTileSize - bShift = logBLen + logTileSize - bLen = uint64(1) << logBLen - ) - - var t [tileSize * tileSize]fr.Element - - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> 55) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> 55) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> 55 - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> 55 - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> 55) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } - -} - -// bitReverseCobraInPlace_9_25 applies the bit-reversal permutation to v. -// len(v) must be 1 << 25. -// see bitReverseCobraInPlace for more details; this function is specialized for 9, -// as it declares the t buffer and various constants statically for performance. -func bitReverseCobraInPlace_9_25(v []fr.Element) { - const ( - logTileSize = uint64(9) - tileSize = uint64(1) << logTileSize - logN = 25 - logBLen = logN - 2*logTileSize - bShift = logBLen + logTileSize - bLen = uint64(1) << logBLen - ) - - var t [tileSize * tileSize]fr.Element - - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> 55) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> 55) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> 55 - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> 55 - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> 55) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } - -} - -// bitReverseCobraInPlace_9_26 applies the bit-reversal permutation to v. -// len(v) must be 1 << 26. -// see bitReverseCobraInPlace for more details; this function is specialized for 9, -// as it declares the t buffer and various constants statically for performance. -func bitReverseCobraInPlace_9_26(v []fr.Element) { - const ( - logTileSize = uint64(9) - tileSize = uint64(1) << logTileSize - logN = 26 - logBLen = logN - 2*logTileSize - bShift = logBLen + logTileSize - bLen = uint64(1) << logBLen - ) - - var t [tileSize * tileSize]fr.Element - - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> 55) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> 55) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> 55 - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> 55 - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> 55) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } - -} - -// bitReverseCobraInPlace_9_27 applies the bit-reversal permutation to v. -// len(v) must be 1 << 27. -// see bitReverseCobraInPlace for more details; this function is specialized for 9, -// as it declares the t buffer and various constants statically for performance. -func bitReverseCobraInPlace_9_27(v []fr.Element) { - const ( - logTileSize = uint64(9) - tileSize = uint64(1) << logTileSize - logN = 27 - logBLen = logN - 2*logTileSize - bShift = logBLen + logTileSize - bLen = uint64(1) << logBLen - ) - - var t [tileSize * tileSize]fr.Element - - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> 55) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> 55) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> 55 - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> 55 - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> 55) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } - -} diff --git a/ecc/bls12-378/fr/fft/bitreverse_test.go b/ecc/bls12-378/fr/fft/bitreverse_test.go deleted file mode 100644 index e6930a4726..0000000000 --- a/ecc/bls12-378/fr/fft/bitreverse_test.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fft - -import ( - "fmt" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" -) - -type bitReverseVariant struct { - name string - buf []fr.Element - fn func([]fr.Element) -} - -const maxSizeBitReverse = 1 << 23 - -var bitReverse = []bitReverseVariant{ - {name: "bitReverseNaive", buf: make([]fr.Element, maxSizeBitReverse), fn: bitReverseNaive}, - {name: "BitReverse", buf: make([]fr.Element, maxSizeBitReverse), fn: BitReverse}, - {name: "bitReverseCobraInPlace", buf: make([]fr.Element, maxSizeBitReverse), fn: bitReverseCobraInPlace}, -} - -func TestBitReverse(t *testing.T) { - - // generate a random []fr.Element array of size 2**20 - pol := make([]fr.Element, maxSizeBitReverse) - one := fr.One() - pol[0].SetRandom() - for i := 1; i < maxSizeBitReverse; i++ { - pol[i].Add(&pol[i-1], &one) - } - - // for each size, check that all the bitReverse functions fn compute the same result. - for size := 2; size <= maxSizeBitReverse; size <<= 1 { - - // copy pol into the buffers - for _, data := range bitReverse { - copy(data.buf, pol[:size]) - } - - // compute bit reverse shuffling - for _, data := range bitReverse { - data.fn(data.buf[:size]) - } - - // all bitReverse.buf should hold the same result - for i := 0; i < size; i++ { - for j := 1; j < len(bitReverse); j++ { - if !bitReverse[0].buf[i].Equal(&bitReverse[j].buf[i]) { - t.Fatalf("bitReverse %s and %s do not compute the same result", bitReverse[0].name, bitReverse[j].name) - } - } - } - - // bitReverse back should be identity - for _, data := range bitReverse { - data.fn(data.buf[:size]) - } - - for i := 0; i < size; i++ { - for j := 1; j < len(bitReverse); j++ { - if !bitReverse[0].buf[i].Equal(&bitReverse[j].buf[i]) { - t.Fatalf("(fn-1) bitReverse %s and %s do not compute the same result", bitReverse[0].name, bitReverse[j].name) - } - } - } - } - -} - -func BenchmarkBitReverse(b *testing.B) { - // generate a random []fr.Element array of size 2**22 - pol := make([]fr.Element, maxSizeBitReverse) - one := fr.One() - pol[0].SetRandom() - for i := 1; i < maxSizeBitReverse; i++ { - pol[i].Add(&pol[i-1], &one) - } - - // copy pol into the buffers - for _, data := range bitReverse { - copy(data.buf, pol[:maxSizeBitReverse]) - } - - // benchmark for each size, each bitReverse function - for size := 1 << 18; size <= maxSizeBitReverse; size <<= 1 { - for _, data := range bitReverse { - b.Run(fmt.Sprintf("name=%s/size=%d", data.name, size), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - data.fn(data.buf[:size]) - } - }) - } - } -} diff --git a/ecc/bls12-378/fr/fft/doc.go b/ecc/bls12-378/fr/fft/doc.go deleted file mode 100644 index b5dd44e642..0000000000 --- a/ecc/bls12-378/fr/fft/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package fft provides in-place discrete Fourier transform on powers-of-two subgroups -// of 𝔽ᵣˣ (the multiplicative group (ℤ/rℤ, x) ). -package fft diff --git a/ecc/bls12-378/fr/fft/domain.go b/ecc/bls12-378/fr/fft/domain.go deleted file mode 100644 index cd43eca06b..0000000000 --- a/ecc/bls12-378/fr/fft/domain.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fft - -import ( - "errors" - "io" - "math/big" - "math/bits" - "runtime" - "sync" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - - curve "github.com/consensys/gnark-crypto/ecc/bls12-378" - - "github.com/consensys/gnark-crypto/ecc" -) - -// Domain with a power of 2 cardinality -// compute a field element of order 2x and store it in FinerGenerator -// all other values can be derived from x, GeneratorSqrt -type Domain struct { - Cardinality uint64 - CardinalityInv fr.Element - Generator fr.Element - GeneratorInv fr.Element - FrMultiplicativeGen fr.Element // generator of Fr* - FrMultiplicativeGenInv fr.Element - - // this is set with the WithoutPrecompute option; - // if true, the domain does some pre-computation and stores it. - // if false, the FFT will compute the twiddles on the fly (this is less CPU efficient, but uses less memory) - withPrecompute bool - - // the following slices are not serialized and are (re)computed through domain.preComputeTwiddles() - - // twiddles factor for the FFT using Generator for each stage of the recursive FFT - twiddles [][]fr.Element - - // twiddles factor for the FFT using GeneratorInv for each stage of the recursive FFT - twiddlesInv [][]fr.Element - - // we precompute these mostly to avoid the memory intensive bit reverse permutation in the groth16.Prover - - // cosetTable u*<1,g,..,g^(n-1)> - cosetTable []fr.Element - - // cosetTable[i][j] = domain.Generator(i-th)SqrtInv ^ j - cosetTableInv []fr.Element -} - -// GeneratorFullMultiplicativeGroup returns a generator of 𝔽ᵣˣ -func GeneratorFullMultiplicativeGroup() fr.Element { - var res fr.Element - - res.SetUint64(22) - - return res -} - -// NewDomain returns a subgroup with a power of 2 cardinality -// cardinality >= m -// shift: when specified, it's the element by which the set of root of unity is shifted. -func NewDomain(m uint64, opts ...DomainOption) *Domain { - opt := domainOptions(opts...) - domain := &Domain{} - x := ecc.NextPowerOfTwo(m) - domain.Cardinality = uint64(x) - domain.FrMultiplicativeGen = GeneratorFullMultiplicativeGroup() - - if opt.shift != nil { - domain.FrMultiplicativeGen.Set(opt.shift) - } - domain.FrMultiplicativeGenInv.Inverse(&domain.FrMultiplicativeGen) - - var err error - domain.Generator, err = Generator(m) - if err != nil { - panic(err) - } - domain.GeneratorInv.Inverse(&domain.Generator) - domain.CardinalityInv.SetUint64(uint64(x)).Inverse(&domain.CardinalityInv) - - // twiddle factors - domain.withPrecompute = opt.withPrecompute - if domain.withPrecompute { - domain.preComputeTwiddles() - } - - return domain -} - -// Generator returns a generator for Z/2^(log(m))Z -// or an error if m is too big (required root of unity doesn't exist) -func Generator(m uint64) (fr.Element, error) { - return fr.Generator(m) -} - -// Twiddles returns the twiddles factor for the FFT using Generator for each stage of the recursive FFT -// or an error if the domain was created with the WithoutPrecompute option -func (d *Domain) Twiddles() ([][]fr.Element, error) { - if d.twiddles == nil { - return nil, errors.New("twiddles not precomputed") - } - return d.twiddles, nil -} - -// TwiddlesInv returns the twiddles factor for the FFT using GeneratorInv for each stage of the recursive FFT -// or an error if the domain was created with the WithoutPrecompute option -func (d *Domain) TwiddlesInv() ([][]fr.Element, error) { - if d.twiddlesInv == nil { - return nil, errors.New("twiddles not precomputed") - } - return d.twiddlesInv, nil -} - -// CosetTable returns the cosetTable u*<1,g,..,g^(n-1)> -// or an error if the domain was created with the WithoutPrecompute option -func (d *Domain) CosetTable() ([]fr.Element, error) { - if d.cosetTable == nil { - return nil, errors.New("cosetTable not precomputed") - } - return d.cosetTable, nil -} - -// CosetTableInv returns the cosetTableInv u*<1,g,..,g^(n-1)> -// or an error if the domain was created with the WithoutPrecompute option -func (d *Domain) CosetTableInv() ([]fr.Element, error) { - if d.cosetTableInv == nil { - return nil, errors.New("cosetTableInv not precomputed") - } - return d.cosetTableInv, nil -} - -func (d *Domain) preComputeTwiddles() { - - // nb fft stages - nbStages := uint64(bits.TrailingZeros64(d.Cardinality)) - - d.twiddles = make([][]fr.Element, nbStages) - d.twiddlesInv = make([][]fr.Element, nbStages) - d.cosetTable = make([]fr.Element, d.Cardinality) - d.cosetTableInv = make([]fr.Element, d.Cardinality) - - var wg sync.WaitGroup - - expTable := func(sqrt fr.Element, t []fr.Element) { - BuildExpTable(sqrt, t) - wg.Done() - } - - wg.Add(4) - go func() { - buildTwiddles(d.twiddles, d.Generator, nbStages) - wg.Done() - }() - go func() { - buildTwiddles(d.twiddlesInv, d.GeneratorInv, nbStages) - wg.Done() - }() - go expTable(d.FrMultiplicativeGen, d.cosetTable) - go expTable(d.FrMultiplicativeGenInv, d.cosetTableInv) - - wg.Wait() - -} - -func buildTwiddles(t [][]fr.Element, omega fr.Element, nbStages uint64) { - if nbStages == 0 { - return - } - if len(t) != int(nbStages) { - panic("invalid twiddle table") - } - // we just compute the first stage - t[0] = make([]fr.Element, 1+(1<<(nbStages-1))) - BuildExpTable(omega, t[0]) - - // for the next stages, we just iterate on the first stage with larger stride - for i := uint64(1); i < nbStages; i++ { - t[i] = make([]fr.Element, 1+(1<<(nbStages-i-1))) - k := 0 - for j := 0; j < len(t[i]); j++ { - t[i][j] = t[0][k] - k += 1 << i - } - } - -} - -// BuildExpTable precomputes the first n powers of w in parallel -// table[0] = w^0 -// table[1] = w^1 -// ... -func BuildExpTable(w fr.Element, table []fr.Element) { - table[0].SetOne() - n := len(table) - - // see if it makes sense to parallelize exp tables pre-computation - interval := 0 - if runtime.NumCPU() >= 4 { - interval = (n - 1) / (runtime.NumCPU() / 4) - } - - // this ratio roughly correspond to the number of multiplication one can do in place of a Exp operation - // TODO @gbotrel revisit this; Exps in this context will be by a "small power of 2" so faster than this ref ratio. - const ratioExpMul = 6000 / 17 - - if interval < ratioExpMul { - precomputeExpTableChunk(w, 1, table[1:]) - return - } - - // we parallelize - var wg sync.WaitGroup - for i := 1; i < n; i += interval { - start := i - end := i + interval - if end > n { - end = n - } - wg.Add(1) - go func() { - precomputeExpTableChunk(w, uint64(start), table[start:end]) - wg.Done() - }() - } - wg.Wait() -} - -func precomputeExpTableChunk(w fr.Element, power uint64, table []fr.Element) { - - // this condition ensures that creating a domain of size 1 with cosets don't fail - if len(table) > 0 { - table[0].Exp(w, new(big.Int).SetUint64(power)) - for i := 1; i < len(table); i++ { - table[i].Mul(&table[i-1], &w) - } - } -} - -// WriteTo writes a binary representation of the domain (without the precomputed twiddle factors) -// to the provided writer -func (d *Domain) WriteTo(w io.Writer) (int64, error) { - - enc := curve.NewEncoder(w) - - toEncode := []interface{}{d.Cardinality, &d.CardinalityInv, &d.Generator, &d.GeneratorInv, &d.FrMultiplicativeGen, &d.FrMultiplicativeGenInv, &d.withPrecompute} - - for _, v := range toEncode { - if err := enc.Encode(v); err != nil { - return enc.BytesWritten(), err - } - } - - return enc.BytesWritten(), nil -} - -// ReadFrom attempts to decode a domain from Reader -func (d *Domain) ReadFrom(r io.Reader) (int64, error) { - - dec := curve.NewDecoder(r) - - toDecode := []interface{}{&d.Cardinality, &d.CardinalityInv, &d.Generator, &d.GeneratorInv, &d.FrMultiplicativeGen, &d.FrMultiplicativeGenInv, &d.withPrecompute} - - for _, v := range toDecode { - if err := dec.Decode(v); err != nil { - return dec.BytesRead(), err - } - } - - if d.withPrecompute { - d.preComputeTwiddles() - } - - return dec.BytesRead(), nil -} diff --git a/ecc/bls12-378/fr/fft/domain_test.go b/ecc/bls12-378/fr/fft/domain_test.go deleted file mode 100644 index 83186cb7c9..0000000000 --- a/ecc/bls12-378/fr/fft/domain_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fft - -import ( - "bytes" - "reflect" - "testing" -) - -func TestDomainSerialization(t *testing.T) { - - domain := NewDomain(1 << 6) - var reconstructed Domain - - var buf bytes.Buffer - written, err := domain.WriteTo(&buf) - if err != nil { - t.Fatal(err) - } - var read int64 - read, err = reconstructed.ReadFrom(&buf) - if err != nil { - t.Fatal(err) - } - - if written != read { - t.Fatal("didn't read as many bytes as we wrote") - } - if !reflect.DeepEqual(domain, &reconstructed) { - t.Fatal("Domain.SetBytes(Bytes()) failed") - } -} diff --git a/ecc/bls12-378/fr/fft/fft.go b/ecc/bls12-378/fr/fft/fft.go deleted file mode 100644 index 47fe4b19e0..0000000000 --- a/ecc/bls12-378/fr/fft/fft.go +++ /dev/null @@ -1,421 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fft - -import ( - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/internal/parallel" - "math/big" - "math/bits" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" -) - -// Decimation is used in the FFT call to select decimation in time or in frequency -type Decimation uint8 - -const ( - DIT Decimation = iota - DIF -) - -// parallelize threshold for a single butterfly op, if the fft stage is not parallelized already -const butterflyThreshold = 16 - -// FFT computes (recursively) the discrete Fourier transform of a and stores the result in a -// if decimation == DIT (decimation in time), the input must be in bit-reversed order -// if decimation == DIF (decimation in frequency), the output will be in bit-reversed order -func (domain *Domain) FFT(a []fr.Element, decimation Decimation, opts ...Option) { - - opt := fftOptions(opts...) - - // find the stage where we should stop spawning go routines in our recursive calls - // (ie when we have as many go routines running as we have available CPUs) - maxSplits := bits.TrailingZeros64(ecc.NextPowerOfTwo(uint64(opt.nbTasks))) - if opt.nbTasks == 1 { - maxSplits = -1 - } - - // if coset != 0, scale by coset table - if opt.coset { - if decimation == DIT { - // scale by coset table (in bit reversed order) - cosetTable := domain.cosetTable - if !domain.withPrecompute { - // we need to build the full table or do a bit reverse dance. - cosetTable = make([]fr.Element, len(a)) - BuildExpTable(domain.FrMultiplicativeGen, cosetTable) - } - parallel.Execute(len(a), func(start, end int) { - n := uint64(len(a)) - nn := uint64(64 - bits.TrailingZeros64(n)) - for i := start; i < end; i++ { - irev := int(bits.Reverse64(uint64(i)) >> nn) - a[i].Mul(&a[i], &cosetTable[irev]) - } - }, opt.nbTasks) - } else { - if domain.withPrecompute { - parallel.Execute(len(a), func(start, end int) { - for i := start; i < end; i++ { - a[i].Mul(&a[i], &domain.cosetTable[i]) - } - }, opt.nbTasks) - } else { - c := domain.FrMultiplicativeGen - parallel.Execute(len(a), func(start, end int) { - var at fr.Element - at.Exp(c, big.NewInt(int64(start))) - for i := start; i < end; i++ { - a[i].Mul(&a[i], &at) - at.Mul(&at, &c) - } - }, opt.nbTasks) - } - - } - } - - twiddles := domain.twiddles - twiddlesStartStage := 0 - if !domain.withPrecompute { - twiddlesStartStage = 3 - nbStages := int(bits.TrailingZeros64(domain.Cardinality)) - if nbStages-twiddlesStartStage > 0 { - twiddles = make([][]fr.Element, nbStages-twiddlesStartStage) - w := domain.Generator - w.Exp(w, big.NewInt(int64(1< 0 { - twiddlesInv = make([][]fr.Element, nbStages-twiddlesStartStage) - w := domain.GeneratorInv - w.Exp(w, big.NewInt(int64(1<> nn) - a[i].Mul(&a[i], &cosetTableInv[irev]). - Mul(&a[i], &domain.CardinalityInv) - } - }, opt.nbTasks) - -} - -func difFFT(a []fr.Element, w fr.Element, twiddles [][]fr.Element, twiddlesStartStage, stage, maxSplits int, chDone chan struct{}, nbTasks int) { - if chDone != nil { - defer close(chDone) - } - - n := len(a) - if n == 1 { - return - } else if n == 256 && stage >= twiddlesStartStage { - kerDIFNP_256(a, twiddles, stage-twiddlesStartStage) - return - } - m := n >> 1 - - parallelButterfly := (m > butterflyThreshold) && (stage < maxSplits) - - if stage < twiddlesStartStage { - if parallelButterfly { - w := w - parallel.Execute(m, func(start, end int) { - if start == 0 { - fr.Butterfly(&a[0], &a[m]) - start++ - } - var at fr.Element - at.Exp(w, big.NewInt(int64(start))) - innerDIFWithoutTwiddles(a, at, w, start, end, m) - }, nbTasks/(1<<(stage))) // 1 << stage == estimated used CPUs - } else { - innerDIFWithoutTwiddles(a, w, w, 0, m, m) - } - // compute next twiddle - w.Square(&w) - } else { - if parallelButterfly { - parallel.Execute(m, func(start, end int) { - innerDIFWithTwiddles(a, twiddles[stage-twiddlesStartStage], start, end, m) - }, nbTasks/(1<<(stage))) - } else { - innerDIFWithTwiddles(a, twiddles[stage-twiddlesStartStage], 0, m, m) - } - } - - if m == 1 { - return - } - - nextStage := stage + 1 - if stage < maxSplits { - chDone := make(chan struct{}, 1) - go difFFT(a[m:n], w, twiddles, twiddlesStartStage, nextStage, maxSplits, chDone, nbTasks) - difFFT(a[0:m], w, twiddles, twiddlesStartStage, nextStage, maxSplits, nil, nbTasks) - <-chDone - } else { - difFFT(a[0:m], w, twiddles, twiddlesStartStage, nextStage, maxSplits, nil, nbTasks) - difFFT(a[m:n], w, twiddles, twiddlesStartStage, nextStage, maxSplits, nil, nbTasks) - } - -} - -func innerDIFWithTwiddles(a []fr.Element, twiddles []fr.Element, start, end, m int) { - if start == 0 { - fr.Butterfly(&a[0], &a[m]) - start++ - } - for i := start; i < end; i++ { - fr.Butterfly(&a[i], &a[i+m]) - a[i+m].Mul(&a[i+m], &twiddles[i]) - } -} - -func innerDIFWithoutTwiddles(a []fr.Element, at, w fr.Element, start, end, m int) { - if start == 0 { - fr.Butterfly(&a[0], &a[m]) - start++ - } - for i := start; i < end; i++ { - fr.Butterfly(&a[i], &a[i+m]) - a[i+m].Mul(&a[i+m], &at) - at.Mul(&at, &w) - } -} - -func ditFFT(a []fr.Element, w fr.Element, twiddles [][]fr.Element, twiddlesStartStage, stage, maxSplits int, chDone chan struct{}, nbTasks int) { - if chDone != nil { - defer close(chDone) - } - n := len(a) - if n == 1 { - return - } else if n == 256 && stage >= twiddlesStartStage { - kerDITNP_256(a, twiddles, stage-twiddlesStartStage) - return - } - m := n >> 1 - - nextStage := stage + 1 - nextW := w - nextW.Square(&nextW) - - if stage < maxSplits { - // that's the only time we fire go routines - chDone := make(chan struct{}, 1) - go ditFFT(a[m:], nextW, twiddles, twiddlesStartStage, nextStage, maxSplits, chDone, nbTasks) - ditFFT(a[0:m], nextW, twiddles, twiddlesStartStage, nextStage, maxSplits, nil, nbTasks) - <-chDone - } else { - ditFFT(a[0:m], nextW, twiddles, twiddlesStartStage, nextStage, maxSplits, nil, nbTasks) - ditFFT(a[m:n], nextW, twiddles, twiddlesStartStage, nextStage, maxSplits, nil, nbTasks) - } - - parallelButterfly := (m > butterflyThreshold) && (stage < maxSplits) - - if stage < twiddlesStartStage { - // we need to compute the twiddles for this stage on the fly. - if parallelButterfly { - w := w - parallel.Execute(m, func(start, end int) { - if start == 0 { - fr.Butterfly(&a[0], &a[m]) - start++ - } - var at fr.Element - at.Exp(w, big.NewInt(int64(start))) - innerDITWithoutTwiddles(a, at, w, start, end, m) - }, nbTasks/(1<<(stage))) // 1 << stage == estimated used CPUs - - } else { - innerDITWithoutTwiddles(a, w, w, 0, m, m) - } - return - } - if parallelButterfly { - parallel.Execute(m, func(start, end int) { - innerDITWithTwiddles(a, twiddles[stage-twiddlesStartStage], start, end, m) - }, nbTasks/(1<<(stage))) - } else { - innerDITWithTwiddles(a, twiddles[stage-twiddlesStartStage], 0, m, m) - } -} - -func innerDITWithTwiddles(a []fr.Element, twiddles []fr.Element, start, end, m int) { - if start == 0 { - fr.Butterfly(&a[0], &a[m]) - start++ - } - for i := start; i < end; i++ { - a[i+m].Mul(&a[i+m], &twiddles[i]) - fr.Butterfly(&a[i], &a[i+m]) - } -} - -func innerDITWithoutTwiddles(a []fr.Element, at, w fr.Element, start, end, m int) { - if start == 0 { - fr.Butterfly(&a[0], &a[m]) - start++ - } - for i := start; i < end; i++ { - a[i+m].Mul(&a[i+m], &at) - fr.Butterfly(&a[i], &a[i+m]) - at.Mul(&at, &w) - } -} - -func kerDIFNP_256(a []fr.Element, twiddles [][]fr.Element, stage int) { - // code unrolled & generated by internal/generator/fft/template/fft.go.tmpl - - innerDIFWithTwiddles(a[:256], twiddles[stage+0], 0, 128, 128) - for offset := 0; offset < 256; offset += 128 { - innerDIFWithTwiddles(a[offset:offset+128], twiddles[stage+1], 0, 64, 64) - } - for offset := 0; offset < 256; offset += 64 { - innerDIFWithTwiddles(a[offset:offset+64], twiddles[stage+2], 0, 32, 32) - } - for offset := 0; offset < 256; offset += 32 { - innerDIFWithTwiddles(a[offset:offset+32], twiddles[stage+3], 0, 16, 16) - } - for offset := 0; offset < 256; offset += 16 { - innerDIFWithTwiddles(a[offset:offset+16], twiddles[stage+4], 0, 8, 8) - } - for offset := 0; offset < 256; offset += 8 { - innerDIFWithTwiddles(a[offset:offset+8], twiddles[stage+5], 0, 4, 4) - } - for offset := 0; offset < 256; offset += 4 { - innerDIFWithTwiddles(a[offset:offset+4], twiddles[stage+6], 0, 2, 2) - } - for offset := 0; offset < 256; offset += 2 { - fr.Butterfly(&a[offset], &a[offset+1]) - } -} - -func kerDITNP_256(a []fr.Element, twiddles [][]fr.Element, stage int) { - // code unrolled & generated by internal/generator/fft/template/fft.go.tmpl - - for offset := 0; offset < 256; offset += 2 { - fr.Butterfly(&a[offset], &a[offset+1]) - } - for offset := 0; offset < 256; offset += 4 { - innerDITWithTwiddles(a[offset:offset+4], twiddles[stage+6], 0, 2, 2) - } - for offset := 0; offset < 256; offset += 8 { - innerDITWithTwiddles(a[offset:offset+8], twiddles[stage+5], 0, 4, 4) - } - for offset := 0; offset < 256; offset += 16 { - innerDITWithTwiddles(a[offset:offset+16], twiddles[stage+4], 0, 8, 8) - } - for offset := 0; offset < 256; offset += 32 { - innerDITWithTwiddles(a[offset:offset+32], twiddles[stage+3], 0, 16, 16) - } - for offset := 0; offset < 256; offset += 64 { - innerDITWithTwiddles(a[offset:offset+64], twiddles[stage+2], 0, 32, 32) - } - for offset := 0; offset < 256; offset += 128 { - innerDITWithTwiddles(a[offset:offset+128], twiddles[stage+1], 0, 64, 64) - } - innerDITWithTwiddles(a[:256], twiddles[stage+0], 0, 128, 128) -} diff --git a/ecc/bls12-378/fr/fft/fft_test.go b/ecc/bls12-378/fr/fft/fft_test.go deleted file mode 100644 index dd71f4160b..0000000000 --- a/ecc/bls12-378/fr/fft/fft_test.go +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fft - -import ( - "math/big" - "strconv" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/gen" - "github.com/leanovate/gopter/prop" - - "fmt" -) - -func TestFFT(t *testing.T) { - parameters := gopter.DefaultTestParameters() - parameters.MinSuccessfulTests = 5 - properties := gopter.NewProperties(parameters) - - for maxSize := 2; maxSize <= 1<<10; maxSize <<= 1 { - - domainWithPrecompute := NewDomain(uint64(maxSize)) - domainWithoutPrecompute := NewDomain(uint64(maxSize), WithoutPrecompute()) - - for domainName, domain := range map[string]*Domain{ - "with precompute": domainWithPrecompute, - "without precompute": domainWithoutPrecompute, - } { - domainName := domainName - domain := domain - t.Logf("domain: %s", domainName) - properties.Property("DIF FFT should be consistent with dual basis", prop.ForAll( - - // checks that a random evaluation of a dual function eval(gen**ithpower) is consistent with the FFT result - func(ithpower int) bool { - - pol := make([]fr.Element, maxSize) - backupPol := make([]fr.Element, maxSize) - - for i := 0; i < maxSize; i++ { - pol[i].SetRandom() - } - copy(backupPol, pol) - - domain.FFT(pol, DIF) - BitReverse(pol) - - sample := domain.Generator - sample.Exp(sample, big.NewInt(int64(ithpower))) - - eval := evaluatePolynomial(backupPol, sample) - - return eval.Equal(&pol[ithpower]) - - }, - gen.IntRange(0, maxSize-1), - )) - - properties.Property("DIF FFT on cosets should be consistent with dual basis", prop.ForAll( - - // checks that a random evaluation of a dual function eval(gen**ithpower) is consistent with the FFT result - func(ithpower int) bool { - - pol := make([]fr.Element, maxSize) - backupPol := make([]fr.Element, maxSize) - - for i := 0; i < maxSize; i++ { - pol[i].SetRandom() - } - copy(backupPol, pol) - - domain.FFT(pol, DIF, OnCoset()) - BitReverse(pol) - - sample := domain.Generator - sample.Exp(sample, big.NewInt(int64(ithpower))). - Mul(&sample, &domain.FrMultiplicativeGen) - - eval := evaluatePolynomial(backupPol, sample) - - return eval.Equal(&pol[ithpower]) - - }, - gen.IntRange(0, maxSize-1), - )) - - properties.Property("DIT FFT should be consistent with dual basis", prop.ForAll( - - // checks that a random evaluation of a dual function eval(gen**ithpower) is consistent with the FFT result - func(ithpower int) bool { - - pol := make([]fr.Element, maxSize) - backupPol := make([]fr.Element, maxSize) - - for i := 0; i < maxSize; i++ { - pol[i].SetRandom() - } - copy(backupPol, pol) - - BitReverse(pol) - domain.FFT(pol, DIT) - - sample := domain.Generator - sample.Exp(sample, big.NewInt(int64(ithpower))) - - eval := evaluatePolynomial(backupPol, sample) - - return eval.Equal(&pol[ithpower]) - - }, - gen.IntRange(0, maxSize-1), - )) - - properties.Property("bitReverse(DIF FFT(DIT FFT (bitReverse))))==id", prop.ForAll( - - func() bool { - - pol := make([]fr.Element, maxSize) - backupPol := make([]fr.Element, maxSize) - - for i := 0; i < maxSize; i++ { - pol[i].SetRandom() - } - copy(backupPol, pol) - - BitReverse(pol) - domain.FFT(pol, DIT) - domain.FFTInverse(pol, DIF) - BitReverse(pol) - - check := true - for i := 0; i < len(pol); i++ { - check = check && pol[i].Equal(&backupPol[i]) - } - return check - }, - )) - - for nbCosets := 2; nbCosets < 5; nbCosets++ { - properties.Property(fmt.Sprintf("bitReverse(DIF FFT(DIT FFT (bitReverse))))==id on %d cosets", nbCosets), prop.ForAll( - - func() bool { - - pol := make([]fr.Element, maxSize) - backupPol := make([]fr.Element, maxSize) - - for i := 0; i < maxSize; i++ { - pol[i].SetRandom() - } - copy(backupPol, pol) - - check := true - - for i := 1; i <= nbCosets; i++ { - - BitReverse(pol) - domain.FFT(pol, DIT, OnCoset()) - domain.FFTInverse(pol, DIF, OnCoset()) - BitReverse(pol) - - for i := 0; i < len(pol); i++ { - check = check && pol[i].Equal(&backupPol[i]) - } - } - - return check - }, - )) - } - - properties.Property("DIT FFT(DIF FFT)==id", prop.ForAll( - - func() bool { - - pol := make([]fr.Element, maxSize) - backupPol := make([]fr.Element, maxSize) - - for i := 0; i < maxSize; i++ { - pol[i].SetRandom() - } - copy(backupPol, pol) - - domain.FFTInverse(pol, DIF) - domain.FFT(pol, DIT) - - check := true - for i := 0; i < len(pol); i++ { - check = check && (pol[i] == backupPol[i]) - } - return check - }, - )) - - properties.Property("DIT FFT(DIF FFT)==id on cosets", prop.ForAll( - - func() bool { - - pol := make([]fr.Element, maxSize) - backupPol := make([]fr.Element, maxSize) - - for i := 0; i < maxSize; i++ { - pol[i].SetRandom() - } - copy(backupPol, pol) - - domain.FFTInverse(pol, DIF, OnCoset()) - domain.FFT(pol, DIT, OnCoset()) - - for i := 0; i < len(pol); i++ { - if !(pol[i].Equal(&backupPol[i])) { - return false - } - } - - // compute with nbTasks == 1 - domain.FFTInverse(pol, DIF, OnCoset(), WithNbTasks(1)) - domain.FFT(pol, DIT, OnCoset(), WithNbTasks(1)) - - for i := 0; i < len(pol); i++ { - if !(pol[i].Equal(&backupPol[i])) { - return false - } - } - - return true - }, - )) - } - properties.TestingRun(t, gopter.ConsoleReporter(false)) - } - -} - -// -------------------------------------------------------------------- -// benches - -func BenchmarkFFT(b *testing.B) { - - const maxSize = 1 << 20 - - pol := make([]fr.Element, maxSize) - pol[0].SetRandom() - for i := 1; i < maxSize; i++ { - pol[i] = pol[i-1] - } - - for i := 8; i < 20; i++ { - sizeDomain := 1 << i - b.Run("fft 2**"+strconv.Itoa(i)+"bits", func(b *testing.B) { - domain := NewDomain(uint64(sizeDomain)) - b.ResetTimer() - for j := 0; j < b.N; j++ { - domain.FFT(pol[:sizeDomain], DIT) - } - }) - b.Run("fft 2**"+strconv.Itoa(i)+"bits (coset)", func(b *testing.B) { - domain := NewDomain(uint64(sizeDomain)) - b.ResetTimer() - for j := 0; j < b.N; j++ { - domain.FFT(pol[:sizeDomain], DIT, OnCoset()) - } - }) - } - -} - -func BenchmarkFFTDITCosetReference(b *testing.B) { - const maxSize = 1 << 20 - - pol := make([]fr.Element, maxSize) - pol[0].SetRandom() - for i := 1; i < maxSize; i++ { - pol[i] = pol[i-1] - } - - domain := NewDomain(maxSize) - - b.ResetTimer() - for j := 0; j < b.N; j++ { - domain.FFT(pol, DIT, OnCoset()) - } -} - -func BenchmarkFFTDIFReference(b *testing.B) { - const maxSize = 1 << 20 - - pol := make([]fr.Element, maxSize) - pol[0].SetRandom() - for i := 1; i < maxSize; i++ { - pol[i] = pol[i-1] - } - - domain := NewDomain(maxSize) - - b.ResetTimer() - for j := 0; j < b.N; j++ { - domain.FFT(pol, DIF) - } -} - -func evaluatePolynomial(pol []fr.Element, val fr.Element) fr.Element { - var acc, res, tmp fr.Element - res.Set(&pol[0]) - acc.Set(&val) - for i := 1; i < len(pol); i++ { - tmp.Mul(&acc, &pol[i]) - res.Add(&res, &tmp) - acc.Mul(&acc, &val) - } - return res -} diff --git a/ecc/bls12-378/fr/fft/options.go b/ecc/bls12-378/fr/fft/options.go deleted file mode 100644 index 81316225ad..0000000000 --- a/ecc/bls12-378/fr/fft/options.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fft - -import ( - "runtime" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" -) - -// Option defines option for altering the behavior of FFT methods. -// See the descriptions of functions returning instances of this type for -// particular options. -type Option func(*fftConfig) - -type fftConfig struct { - coset bool - nbTasks int -} - -// OnCoset if provided, FFT(a) returns the evaluation of a on a coset. -func OnCoset() Option { - return func(opt *fftConfig) { - opt.coset = true - } -} - -// WithNbTasks sets the max number of task (go routine) to spawn. Must be between 1 and 512. -func WithNbTasks(nbTasks int) Option { - if nbTasks < 1 { - nbTasks = 1 - } else if nbTasks > 512 { - nbTasks = 512 - } - return func(opt *fftConfig) { - opt.nbTasks = nbTasks - } -} - -// default options -func fftOptions(opts ...Option) fftConfig { - // apply options - opt := fftConfig{ - coset: false, - nbTasks: runtime.NumCPU(), - } - for _, option := range opts { - option(&opt) - } - return opt -} - -// DomainOption defines option for altering the definition of the FFT domain -// See the descriptions of functions returning instances of this type for -// particular options. -type DomainOption func(*domainConfig) - -type domainConfig struct { - shift *fr.Element - withPrecompute bool -} - -// WithShift sets the FrMultiplicativeGen of the domain. -// Default is generator of the largest 2-adic subgroup. -func WithShift(shift fr.Element) DomainOption { - return func(opt *domainConfig) { - opt.shift = new(fr.Element).Set(&shift) - } -} - -// WithoutPrecompute disables precomputation of twiddles in the domain. -// When this option is set, FFTs will be slower, but will use less memory. -func WithoutPrecompute() DomainOption { - return func(opt *domainConfig) { - opt.withPrecompute = false - } -} - -// default options -func domainOptions(opts ...DomainOption) domainConfig { - // apply options - opt := domainConfig{ - withPrecompute: true, - } - for _, option := range opts { - option(&opt) - } - return opt -} diff --git a/ecc/bls12-378/fr/fri/doc.go b/ecc/bls12-378/fr/fri/doc.go deleted file mode 100644 index 3333ad41f5..0000000000 --- a/ecc/bls12-378/fr/fri/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package fri provides the FRI (multiplicative) commitment scheme. -package fri diff --git a/ecc/bls12-378/fr/fri/fri.go b/ecc/bls12-378/fr/fri/fri.go deleted file mode 100644 index fe392d5332..0000000000 --- a/ecc/bls12-378/fr/fri/fri.go +++ /dev/null @@ -1,698 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fri - -import ( - "bytes" - "errors" - "fmt" - "hash" - "math/big" - "math/bits" - - "github.com/consensys/gnark-crypto/accumulator/merkletree" - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/fft" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -var ( - ErrLowDegree = errors.New("the fully folded polynomial in not of degree 1") - ErrProximityTestFolding = errors.New("one round of interaction failed") - ErrOddSize = errors.New("the size should be even") - ErrMerkleRoot = errors.New("merkle roots of the opening and the proof of proximity don't coincide") - ErrMerklePath = errors.New("merkle path proof is wrong") - ErrRangePosition = errors.New("the asked opening position is out of range") -) - -const rho = 8 - -const nbRounds = 1 - -// 2^{-1}, used several times -var twoInv fr.Element - -// Digest commitment of a polynomial. -type Digest []byte - -// merkleProof helper structure to build the merkle proof -// At each round, two contiguous values from the evaluated polynomial -// are queried. For one value, the full Merkle path will be provided. -// For the neighbor value, only the leaf is provided (so ProofSet will -// be empty), since the Merkle path is the same as for the first value. -type MerkleProof struct { - - // Merkle root - MerkleRoot []byte - - // ProofSet stores [leaf ∥ node_1 ∥ .. ∥ merkleRoot ], where the leaf is not - // hashed. - ProofSet [][]byte - - // number of leaves of the tree. - numLeaves uint64 -} - -// MerkleProof used to open a polynomial -type OpeningProof struct { - - // those fields are private since they are only needed for - // the verification, which is abstracted in the VerifyOpening - // method. - merkleRoot []byte - ProofSet [][]byte - numLeaves uint64 - index uint64 - - // ClaimedValue value of the leaf. This field is exported - // because it's needed for protocols using polynomial commitment - // schemes (to verify an algebraic relation). - ClaimedValue fr.Element -} - -// IOPP Interactive Oracle Proof of Proximity -type IOPP uint - -const ( - // Multiplicative version of FRI, using the map x->x², on a - // power of 2 subgroup of Fr^{*}. - RADIX_2_FRI IOPP = iota -) - -// round contains the data corresponding to a single round -// of fri. -// It consists of a list of Interactions between the prover and the verifier, -// where each interaction contains a challenge provided by the verifier, as -// well as MerkleProofs for the queries of the verifier. The Merkle proofs -// correspond to the openings of the i-th folded polynomial at 2 points that -// belong to the same fiber of x -> x². -type Round struct { - - // stores the Interactions between the prover and the verifier. - // Each interaction results in a set or merkle proofs, corresponding - // to the queries of the verifier. - Interactions [][2]MerkleProof - - // evaluation stores the evaluation of the fully folded polynomial. - // The fully folded polynomial is constant, and is evaluated on a - // a set of size \rho. Since the polynomial is supposed to be constant, - // only one evaluation, corresponding to the polynomial, is given. Since - // the prover cannot know in advance which entry the verifier will query, - // providing a single evaluation - Evaluation fr.Element -} - -// ProofOfProximity proof of proximity, attesting that -// a function is d-close to a low degree polynomial. -// -// It is composed of a series of Interactions, emulated with Fiat Shamir, -type ProofOfProximity struct { - - // ID unique ID attached to the proof of proximity. It's needed for - // protocols using Fiat Shamir for instance, where challenges are derived - // from the proof of proximity. - ID []byte - - // round contains the data corresponding to a single round - // of fri. There are nbRounds rounds of Interactions. - Rounds []Round -} - -// Iopp interface that an iopp should implement -type Iopp interface { - - // BuildProofOfProximity creates a proof of proximity that p is d-close to a polynomial - // of degree len(p). The proof is built non interactively using Fiat Shamir. - BuildProofOfProximity(p []fr.Element) (ProofOfProximity, error) - - // VerifyProofOfProximity verifies the proof of proximity. It returns an error if the - // verification fails. - VerifyProofOfProximity(proof ProofOfProximity) error - - // Opens a polynomial at gⁱ where i = position. - Open(p []fr.Element, position uint64) (OpeningProof, error) - - // Verifies the opening of a polynomial at gⁱ where i = position. - VerifyOpening(position uint64, openingProof OpeningProof, pp ProofOfProximity) error -} - -// GetRho returns the factor ρ = size_code_word/size_polynomial -func GetRho() int { - return rho -} - -func init() { - twoInv.SetUint64(2).Inverse(&twoInv) -} - -// New creates a new IOPP capable to handle degree(size) polynomials. -func (iopp IOPP) New(size uint64, h hash.Hash) Iopp { - switch iopp { - case RADIX_2_FRI: - return newRadixTwoFri(size, h) - default: - panic("iopp name is not recognized") - } -} - -// radixTwoFri empty structs implementing compressionFunction for -// the squaring function. -type radixTwoFri struct { - - // hash function that is used for Fiat Shamir and for committing to - // the oracles. - h hash.Hash - - // nbSteps number of Interactions between the prover and the verifier - nbSteps int - - // domain used to build the Reed Solomon code from the given polynomial. - // The size of the domain is ρ*size_polynomial. - domain *fft.Domain -} - -func newRadixTwoFri(size uint64, h hash.Hash) radixTwoFri { - - var res radixTwoFri - - // computing the number of steps - n := ecc.NextPowerOfTwo(size) - nbSteps := bits.TrailingZeros(uint(n)) - res.nbSteps = nbSteps - - // extending the domain - n = n * rho - - // building the domains - res.domain = fft.NewDomain(n) - - // hash function - res.h = h - - return res -} - -// convertCanonicalSorted convert the index i, an entry in a -// sorted polynomial, to the corresponding entry in canonical -// representation. n is the size of the polynomial. -func convertCanonicalSorted(i, n int) int { - - if i < n/2 { - return 2 * i - } else { - l := n - (i + 1) - l = 2 * l - return n - l - 1 - } - -} - -// deriveQueriesPositions derives the indices of the oracle -// function that the verifier has to pick, in sorted form. -// * pos is the initial position, i.e. the logarithm of the first challenge -// * size is the size of the initial polynomial -// * The result is a slice of []int, where each entry is a tuple (iₖ), such that -// the verifier needs to evaluate ∑ₖ oracle(iₖ)xᵏ to build -// the folded function. -func (s radixTwoFri) deriveQueriesPositions(pos int, size int) []int { - - _s := size / 2 - res := make([]int, s.nbSteps) - res[0] = pos - for i := 1; i < s.nbSteps; i++ { - t := (res[i-1] - (res[i-1] % 2)) / 2 - res[i] = convertCanonicalSorted(t, _s) - _s = _s / 2 - } - - return res -} - -// sort orders the evaluation of a polynomial on a domain -// such that contiguous entries are in the same fiber: -// {q(g⁰), q(g^{n/2}), q(g¹), q(g^{1+n/2}),...,q(g^{n/2-1}), q(gⁿ⁻¹)} -func sort(evaluations []fr.Element) []fr.Element { - q := make([]fr.Element, len(evaluations)) - n := len(evaluations) / 2 - for i := 0; i < n; i++ { - q[2*i].Set(&evaluations[i]) - q[2*i+1].Set(&evaluations[i+n]) - } - return q -} - -// Opens a polynomial at gⁱ where i = position. -func (s radixTwoFri) Open(p []fr.Element, position uint64) (OpeningProof, error) { - - // check that position is in the correct range - if position >= s.domain.Cardinality { - return OpeningProof{}, ErrRangePosition - } - - // put q in evaluation form - q := make([]fr.Element, s.domain.Cardinality) - copy(q, p) - s.domain.FFT(q, fft.DIF) - fft.BitReverse(q) - - // sort q to have fibers in contiguous entries. The goal is to have one - // Merkle path for both openings of entries which are in the same fiber. - q = sort(q) - - // build the Merkle proof, we the position is converted to fit the sorted polynomial - pos := convertCanonicalSorted(int(position), len(q)) - - tree := merkletree.New(s.h) - err := tree.SetIndex(uint64(pos)) - if err != nil { - return OpeningProof{}, err - } - for i := 0; i < len(q); i++ { - tree.Push(q[i].Marshal()) - } - var res OpeningProof - res.merkleRoot, res.ProofSet, res.index, res.numLeaves = tree.Prove() - - // set the claimed value, which is the first entry of the Merkle proof - res.ClaimedValue.SetBytes(res.ProofSet[0]) - - return res, nil -} - -// Verifies the opening of a polynomial. -// * position the point at which the proof is opened (the point is gⁱ where i = position) -// * openingProof Merkle path proof -// * pp proof of proximity, needed because before opening Merkle path proof one should be sure that the -// committed values come from a polynomial. During the verification of the Merkle path proof, the root -// hash of the Merkle path is compared to the root hash of the first interaction of the proof of proximity, -// those should be equal, if not an error is raised. -func (s radixTwoFri) VerifyOpening(position uint64, openingProof OpeningProof, pp ProofOfProximity) error { - - // To query the Merkle path, we look at the first series of Interactions, and check whether it's the point - // at 'position' or its neighbor that contains the full Merkle path. - var fullMerkleProof int - if len(pp.Rounds[0].Interactions[0][0].ProofSet) > len(pp.Rounds[0].Interactions[0][1].ProofSet) { - fullMerkleProof = 0 - } else { - fullMerkleProof = 1 - } - - // check that the merkle roots coincide - if !bytes.Equal(openingProof.merkleRoot, pp.Rounds[0].Interactions[0][fullMerkleProof].MerkleRoot) { - return ErrMerkleRoot - } - - // convert position to the sorted version - sizePoly := s.domain.Cardinality - pos := convertCanonicalSorted(int(position), int(sizePoly)) - - // check the Merkle proof - res := merkletree.VerifyProof(s.h, openingProof.merkleRoot, openingProof.ProofSet, uint64(pos), openingProof.numLeaves) - if !res { - return ErrMerklePath - } - return nil - -} - -// foldPolynomialLagrangeBasis folds a polynomial p, expressed in Lagrange basis. -// -// Fᵣ[X]/(Xⁿ-1) is a free module of rank 2 on Fᵣ[Y]/(Y^{n/2}-1). If -// p∈ Fᵣ[X]/(Xⁿ-1), expressed in Lagrange basis, the function finds the coordinates -// p₁, p₂ of p in Fᵣ[Y]/(Y^{n/2}-1), expressed in Lagrange basis. Finally, it computes -// p₁ + x*p₂ and returns it. -// -// * p is the polynomial to fold, in Lagrange basis, sorted like this: p = [p(1),p(-1),p(g),p(-g),p(g²),p(-g²),...] -// * g is a generator of the subgroup of Fᵣ^{*} of size len(p) -// * x is the folding challenge x, used to return p₁+x*p₂ -func foldPolynomialLagrangeBasis(pSorted []fr.Element, gInv, x fr.Element) []fr.Element { - - // we have the following system - // p₁(g²ⁱ)+gⁱp₂(g²ⁱ) = p(gⁱ) - // p₁(g²ⁱ)-gⁱp₂(g²ⁱ) = p(-gⁱ) - // we solve the system for p₁(g²ⁱ),p₂(g²ⁱ) - s := len(pSorted) - res := make([]fr.Element, s/2) - - var p1, p2, acc fr.Element - acc.SetOne() - - for i := 0; i < s/2; i++ { - - p1.Add(&pSorted[2*i], &pSorted[2*i+1]) - p2.Sub(&pSorted[2*i], &pSorted[2*i+1]).Mul(&p2, &acc) - res[i].Mul(&p2, &x).Add(&res[i], &p1).Mul(&res[i], &twoInv) - - acc.Mul(&acc, &gInv) - - } - - return res -} - -// buildProofOfProximitySingleRound generates a proof that a function, given as an oracle from -// the verifier point of view, is in fact δ-close to a polynomial. -// * salt is a variable for multi rounds, it allows to generate different challenges using Fiat Shamir -// * p is in evaluation form -func (s radixTwoFri) buildProofOfProximitySingleRound(salt fr.Element, p []fr.Element) (Round, error) { - - // the proof will contain nbSteps Interactions - var res Round - res.Interactions = make([][2]MerkleProof, s.nbSteps) - - // Fiat Shamir transcript to derive the challenges. The xᵢ are used to fold the - // polynomials. - // During the i-th round, the prover has a polynomial P of degree n. The verifier sends - // xᵢ∈ Fᵣ to the prover. The prover expresses F in Fᵣ[X,Y]/ as - // P₀(Y)+X P₁(Y) where P₀, P₁ are of degree n/2, and he then folds the polynomial - // by replacing x by xᵢ. - xis := make([]string, s.nbSteps+1) - for i := 0; i < s.nbSteps; i++ { - xis[i] = fmt.Sprintf("x%d", i) - } - xis[s.nbSteps] = "s0" - fs := fiatshamir.NewTranscript(s.h, xis...) - - // the salt is binded to the first challenge, to ensure the challenges - // are different at each round. - err := fs.Bind(xis[0], salt.Marshal()) - if err != nil { - return Round{}, err - } - - // step 1 : fold the polynomial using the xi - - // evalsAtRound stores the list of the nbSteps polynomial evaluations, each evaluation - // corresponds to the evaluation o the folded polynomial at round i. - evalsAtRound := make([][]fr.Element, s.nbSteps) - - // evaluate p and sort the result - _p := make([]fr.Element, s.domain.Cardinality) - copy(_p, p) - - // gInv inverse of the generator of the cyclic group of size the size of the polynomial. - // The size of the cyclic group is ρ*s.domainSize, and not s.domainSize. - var gInv fr.Element - gInv.Set(&s.domain.GeneratorInv) - - for i := 0; i < s.nbSteps; i++ { - - evalsAtRound[i] = sort(_p) - - // compute the root hash, needed to derive xi - t := merkletree.New(s.h) - for k := 0; k < len(_p); k++ { - t.Push(evalsAtRound[i][k].Marshal()) - } - rh := t.Root() - err := fs.Bind(xis[i], rh) - if err != nil { - return res, err - } - - // derive the challenge - bxi, err := fs.ComputeChallenge(xis[i]) - if err != nil { - return res, err - } - var xi fr.Element - xi.SetBytes(bxi) - - // fold _p, reusing its memory - _p = foldPolynomialLagrangeBasis(evalsAtRound[i], gInv, xi) - - // g <- g² - gInv.Square(&gInv) - - } - - // last round, provide the evaluation. The fully folded polynomial is of size rho. It should - // correspond to the evaluation of a polynomial of degree 1 on ρ points, so those points - // are supposed to be on a line. - res.Evaluation.Set(&_p[0]) - - // step 2: provide the Merkle proofs of the queries - - // derive the verifier queries - err = fs.Bind(xis[s.nbSteps], res.Evaluation.Marshal()) - if err != nil { - return res, err - } - binSeed, err := fs.ComputeChallenge(xis[s.nbSteps]) - if err != nil { - return res, err - } - var bPos, bCardinality big.Int - bPos.SetBytes(binSeed) - bCardinality.SetUint64(s.domain.Cardinality) - bPos.Mod(&bPos, &bCardinality) - si := s.deriveQueriesPositions(int(bPos.Uint64()), int(s.domain.Cardinality)) - - for i := 0; i < s.nbSteps; i++ { - - // build proofs of queries at s[i] - t := merkletree.New(s.h) - err := t.SetIndex(uint64(si[i])) - if err != nil { - return res, err - } - for k := 0; k < len(evalsAtRound[i]); k++ { - t.Push(evalsAtRound[i][k].Marshal()) - } - mr, ProofSet, _, numLeaves := t.Prove() - - // c denotes the entry that contains the full Merkle proof. The entry 1-c will - // only contain 2 elements, which are the neighbor point, and the hash of the - // first point. The remaining of the Merkle path is common to both the original - // point and its neighbor. - c := si[i] % 2 - res.Interactions[i][c] = MerkleProof{mr, ProofSet, numLeaves} - res.Interactions[i][1-c] = MerkleProof{ - mr, - make([][]byte, 2), - numLeaves, - } - res.Interactions[i][1-c].ProofSet[0] = evalsAtRound[i][si[i]+1-2*c].Marshal() - s.h.Reset() - _, err = s.h.Write(res.Interactions[i][c].ProofSet[0]) - if err != nil { - return res, err - } - res.Interactions[i][1-c].ProofSet[1] = s.h.Sum(nil) - - } - - return res, nil - -} - -// BuildProofOfProximity generates a proof that a function, given as an oracle from -// the verifier point of view, is in fact δ-close to a polynomial. -func (s radixTwoFri) BuildProofOfProximity(p []fr.Element) (ProofOfProximity, error) { - - // the proof will contain nbSteps Interactions - var proof ProofOfProximity - proof.Rounds = make([]Round, nbRounds) - - // evaluate p - // evaluate p and sort the result - _p := make([]fr.Element, s.domain.Cardinality) - copy(_p, p) - s.domain.FFT(_p, fft.DIF) - fft.BitReverse(_p) - - var err error - var salt, one fr.Element - one.SetOne() - for i := 0; i < nbRounds; i++ { - proof.Rounds[i], err = s.buildProofOfProximitySingleRound(salt, _p) - if err != nil { - return proof, err - } - salt.Add(&salt, &one) - } - - return proof, nil -} - -// verifyProofOfProximitySingleRound verifies the proof of proximity. It returns an error if the -// verification fails. -func (s radixTwoFri) verifyProofOfProximitySingleRound(salt fr.Element, proof Round) error { - - // Fiat Shamir transcript to derive the challenges - xis := make([]string, s.nbSteps+1) - for i := 0; i < s.nbSteps; i++ { - xis[i] = fmt.Sprintf("x%d", i) - } - xis[s.nbSteps] = "s0" - fs := fiatshamir.NewTranscript(s.h, xis...) - - xi := make([]fr.Element, s.nbSteps) - - // the salt is binded to the first challenge, to ensure the challenges - // are different at each round. - err := fs.Bind(xis[0], salt.Marshal()) - if err != nil { - return err - } - - for i := 0; i < s.nbSteps; i++ { - err := fs.Bind(xis[i], proof.Interactions[i][0].MerkleRoot) - if err != nil { - return err - } - bxi, err := fs.ComputeChallenge(xis[i]) - if err != nil { - return err - } - xi[i].SetBytes(bxi) - } - - // derive the verifier queries - // for i := 0; i < len(proof.evaluation); i++ { - // err := fs.Bind(xis[s.nbSteps], proof.evaluation[i].Marshal()) - // if err != nil { - // return err - // } - // } - err = fs.Bind(xis[s.nbSteps], proof.Evaluation.Marshal()) - if err != nil { - return err - } - binSeed, err := fs.ComputeChallenge(xis[s.nbSteps]) - if err != nil { - return err - } - var bPos, bCardinality big.Int - bPos.SetBytes(binSeed) - bCardinality.SetUint64(s.domain.Cardinality) - bPos.Mod(&bPos, &bCardinality) - si := s.deriveQueriesPositions(int(bPos.Uint64()), int(s.domain.Cardinality)) - - // for each round check the Merkle proof and the correctness of the folding - - // current size of the polynomial - var accGInv fr.Element - accGInv.Set(&s.domain.GeneratorInv) - for i := 0; i < s.nbSteps; i++ { - - // correctness of Merkle proof - // c is the entry containing the full Merkle proof. - c := si[i] % 2 - res := merkletree.VerifyProof( - s.h, - proof.Interactions[i][c].MerkleRoot, - proof.Interactions[i][c].ProofSet, - uint64(si[i]), - proof.Interactions[i][c].numLeaves, - ) - if !res { - return ErrMerklePath - } - - // we verify the Merkle proof for the neighbor query, to do that we have - // to pick the full Merkle proof of the first entry, stripped off of the leaf and - // the first node. We replace the leaf and the first node by the leaf and the first - // node of the partial Merkle proof, since the leaf and the first node of both proofs - // are the only entries that differ. - ProofSet := make([][]byte, len(proof.Interactions[i][c].ProofSet)) - copy(ProofSet[2:], proof.Interactions[i][c].ProofSet[2:]) - ProofSet[0] = proof.Interactions[i][1-c].ProofSet[0] - ProofSet[1] = proof.Interactions[i][1-c].ProofSet[1] - res = merkletree.VerifyProof( - s.h, - proof.Interactions[i][1-c].MerkleRoot, - ProofSet, - uint64(si[i]+1-2*c), - proof.Interactions[i][1-c].numLeaves, - ) - if !res { - return ErrMerklePath - } - - // correctness of the folding - if i < s.nbSteps-1 { - - var fe, fo, l, r, fn fr.Element - - // l = P(gⁱ), r = P(g^{i+n/2}) - l.SetBytes(proof.Interactions[i][0].ProofSet[0]) - r.SetBytes(proof.Interactions[i][1].ProofSet[0]) - - // (g^{si[i]}, g^{si[i]+1}) is the fiber of g^{2*si[i]}. The system to solve - // (for P₀(g^{2si[i]}), P₀(g^{2si[i]}) ) is: - // P(g^{si[i]}) = P₀(g^{2si[i]}) + g^{si[i]/2}*P₀(g^{2si[i]}) - // P(g^{si[i]+1}) = P₀(g^{2si[i]}) - g^{si[i]/2}*P₀(g^{2si[i]}) - bm := big.NewInt(int64(si[i] / 2)) - var ginv fr.Element - ginv.Exp(accGInv, bm) - fe.Add(&l, &r) // P₁(g²ⁱ) (to be multiplied by 2⁻¹) - fo.Sub(&l, &r).Mul(&fo, &ginv) // P₀(g²ⁱ) (to be multiplied by 2⁻¹) - fo.Mul(&fo, &xi[i]).Add(&fo, &fe).Mul(&fo, &twoInv) // P₀(g²ⁱ) + xᵢ * P₁(g²ⁱ) - - fn.SetBytes(proof.Interactions[i+1][si[i+1]%2].ProofSet[0]) - - if !fo.Equal(&fn) { - return ErrProximityTestFolding - } - - // next inverse generator - accGInv.Square(&accGInv) - } - - } - - // last transition - var fe, fo, l, r fr.Element - - l.SetBytes(proof.Interactions[s.nbSteps-1][0].ProofSet[0]) - r.SetBytes(proof.Interactions[s.nbSteps-1][1].ProofSet[0]) - - _si := si[s.nbSteps-1] / 2 - - accGInv.Exp(accGInv, big.NewInt(int64(_si))) - - fe.Add(&l, &r) // P₁(g²ⁱ) (to be multiplied by 2⁻¹) - fo.Sub(&l, &r).Mul(&fo, &accGInv) // P₀(g²ⁱ) (to be multiplied by 2⁻¹) - fo.Mul(&fo, &xi[s.nbSteps-1]).Add(&fo, &fe).Mul(&fo, &twoInv) // P₀(g²ⁱ) + xᵢ * P₁(g²ⁱ) - - // Last step: the final evaluation should be the evaluation of a degree 0 polynomial, - // so it must be constant. - if !fo.Equal(&proof.Evaluation) { - return ErrProximityTestFolding - } - - return nil -} - -// VerifyProofOfProximity verifies the proof, by checking each interaction one -// by one. -func (s radixTwoFri) VerifyProofOfProximity(proof ProofOfProximity) error { - - var salt, one fr.Element - one.SetOne() - for i := 0; i < nbRounds; i++ { - err := s.verifyProofOfProximitySingleRound(salt, proof.Rounds[i]) - if err != nil { - return err - } - salt.Add(&salt, &one) - } - return nil - -} diff --git a/ecc/bls12-378/fr/fri/fri_test.go b/ecc/bls12-378/fr/fri/fri_test.go deleted file mode 100644 index 8e613e8105..0000000000 --- a/ecc/bls12-378/fr/fri/fri_test.go +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fri - -import ( - "crypto/sha256" - "fmt" - "math/big" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/gen" - "github.com/leanovate/gopter/prop" -) - -// logFiber returns u, v such that {g^u, g^v} = f⁻¹((g²)^{_p}) -func logFiber(_p, _n int) (_u, _v big.Int) { - if _p%2 == 0 { - _u.SetInt64(int64(_p / 2)) - _v.SetInt64(int64(_p/2 + _n/2)) - } else { - l := (_n - 1 - _p) / 2 - _u.SetInt64(int64(_n - 1 - l)) - _v.SetInt64(int64(_n - 1 - l - _n/2)) - } - return -} - -func randomPolynomial(size uint64, seed int32) []fr.Element { - p := make([]fr.Element, size) - p[0].SetUint64(uint64(seed)) - for i := 1; i < len(p); i++ { - p[i].Square(&p[i-1]) - } - return p -} - -// convertOrderCanonical convert the index i, an entry in a -// sorted polynomial, to the corresponding entry in canonical -// representation. n is the size of the polynomial. -func convertSortedCanonical(i, n int) int { - if i%2 == 0 { - return i / 2 - } else { - l := (n - 1 - i) / 2 - return n - 1 - l - } -} - -func TestFRI(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - parameters.MinSuccessfulTests = 10 - - properties := gopter.NewProperties(parameters) - - size := 4096 - - properties.Property("verifying wrong opening should fail", prop.ForAll( - - func(m int32) bool { - - _s := RADIX_2_FRI.New(uint64(size), sha256.New()) - s := _s.(radixTwoFri) - - p := randomPolynomial(uint64(size), m) - - pos := int64(m % 4096) - pp, _ := s.BuildProofOfProximity(p) - - openingProof, err := s.Open(p, uint64(pos)) - if err != nil { - t.Fatal(err) - } - - // check the Merkle path - tamperedPosition := pos + 1 - err = s.VerifyOpening(uint64(tamperedPosition), openingProof, pp) - - return err != nil - - }, - gen.Int32Range(1, int32(rho*size)), - )) - - properties.Property("verifying correct opening should succeed", prop.ForAll( - - func(m int32) bool { - - _s := RADIX_2_FRI.New(uint64(size), sha256.New()) - s := _s.(radixTwoFri) - - p := randomPolynomial(uint64(size), m) - - pos := uint64(m % int32(size)) - pp, _ := s.BuildProofOfProximity(p) - - openingProof, err := s.Open(p, uint64(pos)) - if err != nil { - t.Fatal(err) - } - - // check the Merkle path - err = s.VerifyOpening(uint64(pos), openingProof, pp) - - return err == nil - - }, - gen.Int32Range(0, int32(rho*size)), - )) - - properties.Property("The claimed value of a polynomial should match P(x)", prop.ForAll( - func(m int32) bool { - - _s := RADIX_2_FRI.New(uint64(size), sha256.New()) - s := _s.(radixTwoFri) - - p := randomPolynomial(uint64(size), m) - - // check the opening value - var g fr.Element - pos := int64(m % 4096) - g.Set(&s.domain.Generator) - g.Exp(g, big.NewInt(pos)) - - var val fr.Element - for i := len(p) - 1; i >= 0; i-- { - val.Mul(&val, &g) - val.Add(&p[i], &val) - } - - openingProof, err := s.Open(p, uint64(pos)) - if err != nil { - t.Fatal(err) - } - - return openingProof.ClaimedValue.Equal(&val) - - }, - gen.Int32Range(0, int32(rho*size)), - )) - - properties.Property("Derive queries position: points should belong the correct fiber", prop.ForAll( - - func(m int32) bool { - - _s := RADIX_2_FRI.New(uint64(size), sha256.New()) - s := _s.(radixTwoFri) - - var g fr.Element - - _m := int(m) % size - pos := s.deriveQueriesPositions(_m, int(s.domain.Cardinality)) - g.Set(&s.domain.Generator) - n := int(s.domain.Cardinality) - - for i := 0; i < len(pos)-1; i++ { - - u, v := logFiber(pos[i], n) - - var g1, g2, g3 fr.Element - g1.Exp(g, &u).Square(&g1) - g2.Exp(g, &v).Square(&g2) - nextPos := convertSortedCanonical(pos[i+1], n/2) - g3.Square(&g).Exp(g3, big.NewInt(int64(nextPos))) - - if !g1.Equal(&g2) || !g1.Equal(&g3) { - return false - } - g.Square(&g) - n = n >> 1 - } - return true - }, - gen.Int32Range(0, int32(rho*size)), - )) - - properties.Property("verifying a correctly formed proof should succeed", prop.ForAll( - - func(s int32) bool { - - p := randomPolynomial(uint64(size), s) - - iop := RADIX_2_FRI.New(uint64(size), sha256.New()) - proof, err := iop.BuildProofOfProximity(p) - if err != nil { - t.Fatal(err) - } - - err = iop.VerifyProofOfProximity(proof) - return err == nil - }, - gen.Int32Range(0, int32(rho*size)), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -// Benchmarks - -func BenchmarkProximityVerification(b *testing.B) { - - baseSize := 16 - - for i := 0; i < 10; i++ { - - size := baseSize << i - p := make([]fr.Element, size) - for k := 0; k < size; k++ { - p[k].SetRandom() - } - - iop := RADIX_2_FRI.New(uint64(size), sha256.New()) - proof, _ := iop.BuildProofOfProximity(p) - - b.Run(fmt.Sprintf("Polynomial size %d", size), func(b *testing.B) { - b.ResetTimer() - for l := 0; l < b.N; l++ { - iop.VerifyProofOfProximity(proof) - } - }) - - } -} diff --git a/ecc/bls12-378/fr/generator.go b/ecc/bls12-378/fr/generator.go deleted file mode 100644 index ec6a7e8fea..0000000000 --- a/ecc/bls12-378/fr/generator.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import ( - "fmt" - "math/big" - "math/bits" - - "github.com/consensys/gnark-crypto/ecc" -) - -// Generator returns a generator for Z/2^(log(m))Z -// or an error if m is too big (required root of unity doesn't exist) -func Generator(m uint64) (Element, error) { - x := ecc.NextPowerOfTwo(m) - - var rootOfUnity Element - - rootOfUnity.SetString("4045585818372166415418670827807793147093034396422209590578257013290761627990") - const maxOrderRoot uint64 = 42 - - // find generator for Z/2^(log(m))Z - logx := uint64(bits.TrailingZeros64(x)) - if logx > maxOrderRoot { - return Element{}, fmt.Errorf("m (%d) is too big: the required root of unity does not exist", m) - } - - expo := uint64(1 << (maxOrderRoot - logx)) - var generator Element - generator.Exp(rootOfUnity, big.NewInt(int64(expo))) // order x - return generator, nil -} diff --git a/ecc/bls12-378/fr/gkr/gkr.go b/ecc/bls12-378/fr/gkr/gkr.go deleted file mode 100644 index ceada9579d..0000000000 --- a/ecc/bls12-378/fr/gkr/gkr.go +++ /dev/null @@ -1,944 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package gkr - -import ( - "fmt" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/polynomial" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/sumcheck" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - "github.com/consensys/gnark-crypto/internal/parallel" - "github.com/consensys/gnark-crypto/utils" - "math/big" - "strconv" - "sync" -) - -// The goal is to prove/verify evaluations of many instances of the same circuit - -// Gate must be a low-degree polynomial -type Gate interface { - Evaluate(...fr.Element) fr.Element - Degree() int -} - -type Wire struct { - Gate Gate - Inputs []*Wire // if there are no Inputs, the wire is assumed an input wire - nbUniqueOutputs int // number of other wires using it as input, not counting duplicates (i.e. providing two inputs to the same gate counts as one) -} - -type Circuit []Wire - -func (w Wire) IsInput() bool { - return len(w.Inputs) == 0 -} - -func (w Wire) IsOutput() bool { - return w.nbUniqueOutputs == 0 -} - -func (w Wire) NbClaims() int { - if w.IsOutput() { - return 1 - } - return w.nbUniqueOutputs -} - -func (w Wire) noProof() bool { - return w.IsInput() && w.NbClaims() == 1 -} - -func (c Circuit) maxGateDegree() int { - res := 1 - for i := range c { - if !c[i].IsInput() { - res = utils.Max(res, c[i].Gate.Degree()) - } - } - return res -} - -// WireAssignment is assignment of values to the same wire across many instances of the circuit -type WireAssignment map[*Wire]polynomial.MultiLin - -type Proof []sumcheck.Proof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) - -type eqTimesGateEvalSumcheckLazyClaims struct { - wire *Wire - evaluationPoints [][]fr.Element - claimedEvaluations []fr.Element - manager *claimsManager // WARNING: Circular references -} - -func (e *eqTimesGateEvalSumcheckLazyClaims) ClaimsNum() int { - return len(e.evaluationPoints) -} - -func (e *eqTimesGateEvalSumcheckLazyClaims) VarsNum() int { - return len(e.evaluationPoints[0]) -} - -func (e *eqTimesGateEvalSumcheckLazyClaims) CombinedSum(a fr.Element) fr.Element { - evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) - return evalsAsPoly.Eval(&a) -} - -func (e *eqTimesGateEvalSumcheckLazyClaims) Degree(int) int { - return 1 + e.wire.Gate.Degree() -} - -func (e *eqTimesGateEvalSumcheckLazyClaims) VerifyFinalEval(r []fr.Element, combinationCoeff fr.Element, purportedValue fr.Element, proof interface{}) error { - inputEvaluationsNoRedundancy := proof.([]fr.Element) - - // the eq terms - numClaims := len(e.evaluationPoints) - evaluation := polynomial.EvalEq(e.evaluationPoints[numClaims-1], r) - for i := numClaims - 2; i >= 0; i-- { - evaluation.Mul(&evaluation, &combinationCoeff) - eq := polynomial.EvalEq(e.evaluationPoints[i], r) - evaluation.Add(&evaluation, &eq) - } - - // the g(...) term - var gateEvaluation fr.Element - if e.wire.IsInput() { - gateEvaluation = e.manager.assignment[e.wire].Evaluate(r, e.manager.memPool) - } else { - inputEvaluations := make([]fr.Element, len(e.wire.Inputs)) - indexesInProof := make(map[*Wire]int, len(inputEvaluationsNoRedundancy)) - - proofI := 0 - for inI, in := range e.wire.Inputs { - indexInProof, found := indexesInProof[in] - if !found { - indexInProof = proofI - indexesInProof[in] = indexInProof - - // defer verification, store new claim - e.manager.add(in, r, inputEvaluationsNoRedundancy[indexInProof]) - proofI++ - } - inputEvaluations[inI] = inputEvaluationsNoRedundancy[indexInProof] - } - if proofI != len(inputEvaluationsNoRedundancy) { - return fmt.Errorf("%d input wire evaluations given, %d expected", len(inputEvaluationsNoRedundancy), proofI) - } - gateEvaluation = e.wire.Gate.Evaluate(inputEvaluations...) - } - - evaluation.Mul(&evaluation, &gateEvaluation) - - if evaluation.Equal(&purportedValue) { - return nil - } - return fmt.Errorf("incompatible evaluations") -} - -type eqTimesGateEvalSumcheckClaims struct { - wire *Wire - evaluationPoints [][]fr.Element // x in the paper - claimedEvaluations []fr.Element // y in the paper - manager *claimsManager - - inputPreprocessors []polynomial.MultiLin // P_u in the paper, so that we don't need to pass along all the circuit's evaluations - - eq polynomial.MultiLin // ∑_i τ_i eq(x_i, -) -} - -func (c *eqTimesGateEvalSumcheckClaims) Combine(combinationCoeff fr.Element) polynomial.Polynomial { - varsNum := c.VarsNum() - eqLength := 1 << varsNum - claimsNum := c.ClaimsNum() - // initialize the eq tables - c.eq = c.manager.memPool.Make(eqLength) - - c.eq[0].SetOne() - c.eq.Eq(c.evaluationPoints[0]) - - newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) - aI := combinationCoeff - - for k := 1; k < claimsNum; k++ { //TODO: parallelizable? - // define eq_k = aᵏ eq(x_k1, ..., x_kn, *, ..., *) where x_ki are the evaluation points - newEq[0].Set(&aI) - - c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) - - // newEq.Eq(c.evaluationPoints[k]) - // eqAsPoly := polynomial.Polynomial(c.eq) //just semantics - // eqAsPoly.Add(eqAsPoly, polynomial.Polynomial(newEq)) - - if k+1 < claimsNum { - aI.Mul(&aI, &combinationCoeff) - } - } - - c.manager.memPool.Dump(newEq) - - // from this point on the claim is a rather simple one: g = E(h) × R_v (P_u0(h), ...) where E and the P_u are multilinear and R_v is of low-degree - - return c.computeGJ() -} - -// eqAcc sets m to an eq table at q and then adds it to e -func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { - n := len(q) - - //At the end of each iteration, m(h₁, ..., hₙ) = Eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) - for i := range q { // In the comments we use a 1-based index so q[i] = qᵢ₊₁ - // go through all assignments of (b₁, ..., bᵢ) ∈ {0,1}ⁱ - const threshold = 1 << 6 - k := 1 << i - if k < threshold { - for j := 0; j < k; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // Eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) Eq(qᵢ₊₁, 1) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // Eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) Eq(qᵢ₊₁, 0) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - } else { - c.manager.workers.Submit(k, func(start, end int) { - for j := start; j < end; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // Eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) Eq(qᵢ₊₁, 1) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // Eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) Eq(qᵢ₊₁, 0) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - }, 1024).Wait() - } - - } - c.manager.workers.Submit(len(e), func(start, end int) { - for i := start; i < end; i++ { - e[i].Add(&e[i], &m[i]) - } - }, 512).Wait() - - // e.Add(e, polynomial.Polynomial(m)) -} - -// computeGJ: gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., X_j, i...) R_v( P_u0(r₁, ..., X_j, i...), ... ) where E = ∑ eq_k -// the polynomial is represented by the evaluations g_j(1), g_j(2), ..., g_j(deg(g_j)). -// The value g_j(0) is inferred from the equation g_j(0) + g_j(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. -func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { - - degGJ := 1 + c.wire.Gate.Degree() // guaranteed to be no smaller than the actual deg(g_j) - nbGateIn := len(c.inputPreprocessors) - - // Let f ∈ { E(r₁, ..., X_j, d...) } ∪ {P_ul(r₁, ..., X_j, d...) }. It is linear in X_j, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables - s := make([]polynomial.MultiLin, nbGateIn+1) - s[0] = c.eq - copy(s[1:], c.inputPreprocessors) - - // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called - nbInner := len(s) // wrt output, which has high nbOuter and low nbInner - nbOuter := len(s[0]) / 2 - - gJ := make([]fr.Element, degGJ) - var mu sync.Mutex - computeAll := func(start, end int) { - var step fr.Element - - res := make([]fr.Element, degGJ) - operands := make([]fr.Element, degGJ*nbInner) - - for i := start; i < end; i++ { - - block := nbOuter + i - for j := 0; j < nbInner; j++ { - step.Set(&s[j][i]) - operands[j].Set(&s[j][block]) - step.Sub(&operands[j], &step) - for d := 1; d < degGJ; d++ { - operands[d*nbInner+j].Add(&operands[(d-1)*nbInner+j], &step) - } - } - - _s := 0 - _e := nbInner - for d := 0; d < degGJ; d++ { - summand := c.wire.Gate.Evaluate(operands[_s+1 : _e]...) - summand.Mul(&summand, &operands[_s]) - res[d].Add(&res[d], &summand) - _s, _e = _e, _e+nbInner - } - } - mu.Lock() - for i := 0; i < len(gJ); i++ { - gJ[i].Add(&gJ[i], &res[i]) - } - mu.Unlock() - } - - const minBlockSize = 64 - - if nbOuter < minBlockSize { - // no parallelization - computeAll(0, nbOuter) - } else { - c.manager.workers.Submit(nbOuter, computeAll, minBlockSize).Wait() - } - - // Perf-TODO: Separate functions Gate.TotalDegree and Gate.Degree(i) so that we get to use possibly smaller values for degGJ. Won't help with MiMC though - - return gJ -} - -// Next first folds the "preprocessing" and "eq" polynomials then compute the new g_j -func (c *eqTimesGateEvalSumcheckClaims) Next(element fr.Element) polynomial.Polynomial { - const minBlockSize = 512 - n := len(c.eq) / 2 - if n < minBlockSize { - // no parallelization - for i := 0; i < len(c.inputPreprocessors); i++ { - c.inputPreprocessors[i].Fold(element) - } - c.eq.Fold(element) - } else { - wgs := make([]*sync.WaitGroup, len(c.inputPreprocessors)) - for i := 0; i < len(c.inputPreprocessors); i++ { - wgs[i] = c.manager.workers.Submit(n, c.inputPreprocessors[i].FoldParallel(element), minBlockSize) - } - c.manager.workers.Submit(n, c.eq.FoldParallel(element), minBlockSize).Wait() - for _, wg := range wgs { - wg.Wait() - } - } - - return c.computeGJ() -} - -func (c *eqTimesGateEvalSumcheckClaims) VarsNum() int { - return len(c.evaluationPoints[0]) -} - -func (c *eqTimesGateEvalSumcheckClaims) ClaimsNum() int { - return len(c.claimedEvaluations) -} - -func (c *eqTimesGateEvalSumcheckClaims) ProveFinalEval(r []fr.Element) interface{} { - - //defer the proof, return list of claims - evaluations := make([]fr.Element, 0, len(c.wire.Inputs)) - noMoreClaimsAllowed := make(map[*Wire]struct{}, len(c.inputPreprocessors)) - noMoreClaimsAllowed[c.wire] = struct{}{} - - for inI, in := range c.wire.Inputs { - puI := c.inputPreprocessors[inI] - if _, found := noMoreClaimsAllowed[in]; !found { - noMoreClaimsAllowed[in] = struct{}{} - puI.Fold(r[len(r)-1]) - c.manager.add(in, r, puI[0]) - evaluations = append(evaluations, puI[0]) - } - c.manager.memPool.Dump(puI) - } - - c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - - return evaluations -} - -type claimsManager struct { - claimsMap map[*Wire]*eqTimesGateEvalSumcheckLazyClaims - assignment WireAssignment - memPool *polynomial.Pool - workers *utils.WorkerPool -} - -func newClaimsManager(c Circuit, assignment WireAssignment, o settings) (claims claimsManager) { - claims.assignment = assignment - claims.claimsMap = make(map[*Wire]*eqTimesGateEvalSumcheckLazyClaims, len(c)) - claims.memPool = o.pool - claims.workers = o.workers - - for i := range c { - wire := &c[i] - - claims.claimsMap[wire] = &eqTimesGateEvalSumcheckLazyClaims{ - wire: wire, - evaluationPoints: make([][]fr.Element, 0, wire.NbClaims()), - claimedEvaluations: claims.memPool.Make(wire.NbClaims()), - manager: &claims, - } - } - return -} - -func (m *claimsManager) add(wire *Wire, evaluationPoint []fr.Element, evaluation fr.Element) { - claim := m.claimsMap[wire] - i := len(claim.evaluationPoints) - claim.claimedEvaluations[i] = evaluation - claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) -} - -func (m *claimsManager) getLazyClaim(wire *Wire) *eqTimesGateEvalSumcheckLazyClaims { - return m.claimsMap[wire] -} - -func (m *claimsManager) getClaim(wire *Wire) *eqTimesGateEvalSumcheckClaims { - lazy := m.claimsMap[wire] - res := &eqTimesGateEvalSumcheckClaims{ - wire: wire, - evaluationPoints: lazy.evaluationPoints, - claimedEvaluations: lazy.claimedEvaluations, - manager: m, - } - - if wire.IsInput() { - res.inputPreprocessors = []polynomial.MultiLin{m.memPool.Clone(m.assignment[wire])} - } else { - res.inputPreprocessors = make([]polynomial.MultiLin, len(wire.Inputs)) - - for inputI, inputW := range wire.Inputs { - res.inputPreprocessors[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied - } - } - return res -} - -func (m *claimsManager) deleteClaim(wire *Wire) { - delete(m.claimsMap, wire) -} - -type settings struct { - pool *polynomial.Pool - sorted []*Wire - transcript *fiatshamir.Transcript - transcriptPrefix string - nbVars int - workers *utils.WorkerPool -} - -type Option func(*settings) - -func WithPool(pool *polynomial.Pool) Option { - return func(options *settings) { - options.pool = pool - } -} - -func WithSortedCircuit(sorted []*Wire) Option { - return func(options *settings) { - options.sorted = sorted - } -} - -func WithWorkers(workers *utils.WorkerPool) Option { - return func(options *settings) { - options.workers = workers - } -} - -// MemoryRequirements returns an increasing vector of memory allocation sizes required for proving a GKR statement -func (c Circuit) MemoryRequirements(nbInstances int) []int { - res := []int{256, nbInstances, nbInstances * (c.maxGateDegree() + 1)} - - if res[0] > res[1] { // make sure it's sorted - res[0], res[1] = res[1], res[0] - if res[1] > res[2] { - res[1], res[2] = res[2], res[1] - } - } - - return res -} - -func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { - var o settings - var err error - for _, option := range options { - option(&o) - } - - o.nbVars = assignment.NumVars() - nbInstances := assignment.NumInstances() - if 1< 1 { //combine the claims - size++ - } - size += logNbInstances // full run of sumcheck on logNbInstances variables - } - - nums := make([]string, utils.Max(len(sorted), logNbInstances)) - for i := range nums { - nums[i] = strconv.Itoa(i) - } - - challenges := make([]string, size) - - // output wire claims - firstChallengePrefix := prefix + "fC." - for j := 0; j < logNbInstances; j++ { - challenges[j] = firstChallengePrefix + nums[j] - } - j := logNbInstances - for i := len(sorted) - 1; i >= 0; i-- { - if sorted[i].noProof() { - continue - } - wirePrefix := prefix + "w" + nums[i] + "." - - if sorted[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "comb" - j++ - } - - partialSumPrefix := wirePrefix + "pSP." - for k := 0; k < logNbInstances; k++ { - challenges[j] = partialSumPrefix + nums[k] - j++ - } - } - return challenges -} - -func getFirstChallengeNames(logNbInstances int, prefix string) []string { - res := make([]string, logNbInstances) - firstChallengePrefix := prefix + "fC." - for i := 0; i < logNbInstances; i++ { - res[i] = firstChallengePrefix + strconv.Itoa(i) - } - return res -} - -func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Element, error) { - res := make([]fr.Element, len(names)) - for i, name := range names { - if bytes, err := transcript.ComputeChallenge(name); err == nil { - res[i].SetBytes(bytes) - } else { - return nil, err - } - } - return res, nil -} - -// Prove consistency of the claimed assignment -func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { - o, err := setup(c, assignment, transcriptSettings, options...) - if err != nil { - return nil, err - } - defer o.workers.Stop() - - claims := newClaimsManager(c, assignment, o) - - proof := make(Proof, len(c)) - // firstChallenge called rho in the paper - var firstChallenge []fr.Element - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return nil, err - } - - wirePrefix := o.transcriptPrefix + "w" - var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - - wire := o.sorted[i] - - if wire.IsOutput() { - claims.add(wire, firstChallenge, assignment[wire].Evaluate(firstChallenge, claims.memPool)) - } - - claim := claims.getClaim(wire) - if wire.noProof() { // input wires with one claim only - proof[i] = sumcheck.Proof{ - PartialSumPolys: []polynomial.Polynomial{}, - FinalEvalProof: []fr.Element{}, - } - } else { - if proof[i], err = sumcheck.Prove( - claim, fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err != nil { - return proof, err - } - - finalEvalProof := proof[i].FinalEvalProof.([]fr.Element) - baseChallenge = make([][]byte, len(finalEvalProof)) - for j := range finalEvalProof { - bytes := finalEvalProof[j].Bytes() - baseChallenge[j] = bytes[:] - } - } - // the verifier checks a single claim about input wires itself - claims.deleteClaim(wire) - } - - return proof, nil -} - -// Verify the consistency of the claimed output with the claimed input -// Unlike in Prove, the assignment argument need not be complete -func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { - o, err := setup(c, assignment, transcriptSettings, options...) - if err != nil { - return err - } - defer o.workers.Stop() - - claims := newClaimsManager(c, assignment, o) - - var firstChallenge []fr.Element - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return err - } - - wirePrefix := o.transcriptPrefix + "w" - var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - wire := o.sorted[i] - - if wire.IsOutput() { - claims.add(wire, firstChallenge, assignment[wire].Evaluate(firstChallenge, claims.memPool)) - } - - proofW := proof[i] - finalEvalProof := proofW.FinalEvalProof.([]fr.Element) - claim := claims.getLazyClaim(wire) - if wire.noProof() { // input wires with one claim only - // make sure the proof is empty - if len(finalEvalProof) != 0 || len(proofW.PartialSumPolys) != 0 { - return fmt.Errorf("no proof allowed for input wire with a single claim") - } - - if wire.NbClaims() == 1 { // input wire - // simply evaluate and see if it matches - evaluation := assignment[wire].Evaluate(claim.evaluationPoints[0], claims.memPool) - if !claim.claimedEvaluations[0].Equal(&evaluation) { - return fmt.Errorf("incorrect input wire claim") - } - } - } else if err = sumcheck.Verify( - claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { - baseChallenge = make([][]byte, len(finalEvalProof)) - for j := range finalEvalProof { - bytes := finalEvalProof[j].Bytes() - baseChallenge[j] = bytes[:] - } - } else { - return fmt.Errorf("sumcheck proof rejected: %v", err) //TODO: Any polynomials to dump? - } - claims.deleteClaim(wire) - } - return nil -} - -// outputsList also sets the nbUniqueOutputs fields. It also sets the wire metadata. -func outputsList(c Circuit, indexes map[*Wire]int) [][]int { - res := make([][]int, len(c)) - for i := range c { - res[i] = make([]int, 0) - c[i].nbUniqueOutputs = 0 - if c[i].IsInput() { - c[i].Gate = IdentityGate{} - } - } - ins := make(map[int]struct{}, len(c)) - for i := range c { - for k := range ins { // clear map - delete(ins, k) - } - for _, in := range c[i].Inputs { - inI := indexes[in] - res[inI] = append(res[inI], i) - if _, ok := ins[inI]; !ok { - in.nbUniqueOutputs++ - ins[inI] = struct{}{} - } - } - } - return res -} - -type topSortData struct { - outputs [][]int - status []int // status > 0 indicates number of inputs left to be ready. status = 0 means ready. status = -1 means done - index map[*Wire]int - leastReady int -} - -func (d *topSortData) markDone(i int) { - - d.status[i] = -1 - - for _, outI := range d.outputs[i] { - d.status[outI]-- - if d.status[outI] == 0 && outI < d.leastReady { - d.leastReady = outI - } - } - - for d.leastReady < len(d.status) && d.status[d.leastReady] != 0 { - d.leastReady++ - } -} - -func indexMap(c Circuit) map[*Wire]int { - res := make(map[*Wire]int, len(c)) - for i := range c { - res[&c[i]] = i - } - return res -} - -func statusList(c Circuit) []int { - res := make([]int, len(c)) - for i := range c { - res[i] = len(c[i].Inputs) - } - return res -} - -// topologicalSort sorts the wires in order of dependence. Such that for any wire, any one it depends on -// occurs before it. It tries to stick to the input order as much as possible. An already sorted list will remain unchanged. -// It also sets the nbOutput flags, and a dummy IdentityGate for input wires. -// Worst-case inefficient O(n^2), but that probably won't matter since the circuits are small. -// Furthermore, it is efficient with already-close-to-sorted lists, which are the expected input -func topologicalSort(c Circuit) []*Wire { - var data topSortData - data.index = indexMap(c) - data.outputs = outputsList(c, data.index) - data.status = statusList(c) - sorted := make([]*Wire, len(c)) - - for data.leastReady = 0; data.status[data.leastReady] != 0; data.leastReady++ { - } - - for i := range c { - sorted[i] = &c[data.leastReady] - data.markDone(data.leastReady) - } - - return sorted -} - -// Complete the circuit evaluation from input values -func (a WireAssignment) Complete(c Circuit) WireAssignment { - - sortedWires := topologicalSort(c) - nbInstances := a.NumInstances() - maxNbIns := 0 - - for _, w := range sortedWires { - maxNbIns = utils.Max(maxNbIns, len(w.Inputs)) - if a[w] == nil { - a[w] = make([]fr.Element, nbInstances) - } - } - - parallel.Execute(nbInstances, func(start, end int) { - ins := make([]fr.Element, maxNbIns) - for i := start; i < end; i++ { - for _, w := range sortedWires { - if !w.IsInput() { - for inI, in := range w.Inputs { - ins[inI] = a[in][i] - } - a[w][i] = w.Gate.Evaluate(ins[:len(w.Inputs)]...) - } - } - } - }) - - return a -} - -func (a WireAssignment) NumInstances() int { - for _, aW := range a { - return len(aW) - } - panic("empty assignment") -} - -func (a WireAssignment) NumVars() int { - for _, aW := range a { - return aW.NumVars() - } - panic("empty assignment") -} - -// SerializeToBigInts flattens a proof object into the given slice of big.Ints -// useful in gnark hints. TODO: Change propagation: Once this is merged, it will duplicate some code in std/gkr/bn254Prover.go. Remove that in favor of this -func (p Proof) SerializeToBigInts(outs []*big.Int) { - offset := 0 - for i := range p { - for _, poly := range p[i].PartialSumPolys { - frToBigInts(outs[offset:], poly) - offset += len(poly) - } - if p[i].FinalEvalProof != nil { - finalEvalProof := p[i].FinalEvalProof.([]fr.Element) - frToBigInts(outs[offset:], finalEvalProof) - offset += len(finalEvalProof) - } - } -} - -func frToBigInts(dst []*big.Int, src []fr.Element) { - for i := range src { - src[i].BigInt(dst[i]) - } -} - -// Gates defined by name -var Gates = map[string]Gate{ - "identity": IdentityGate{}, - "add": AddGate{}, - "sub": SubGate{}, - "neg": NegGate{}, - "mul": MulGate(2), -} - -type IdentityGate struct{} -type AddGate struct{} -type MulGate int -type SubGate struct{} -type NegGate struct{} - -func (IdentityGate) Evaluate(input ...fr.Element) fr.Element { - return input[0] -} - -func (IdentityGate) Degree() int { - return 1 -} - -func (g AddGate) Evaluate(x ...fr.Element) (res fr.Element) { - switch len(x) { - case 0: - // set zero - case 1: - res.Set(&x[0]) - default: - res.Add(&x[0], &x[1]) - for i := 2; i < len(x); i++ { - res.Add(&res, &x[i]) - } - } - return -} - -func (g AddGate) Degree() int { - return 1 -} - -func (g MulGate) Evaluate(x ...fr.Element) (res fr.Element) { - if len(x) != int(g) { - panic("wrong input count") - } - switch len(x) { - case 0: - res.SetOne() - case 1: - res.Set(&x[0]) - default: - res.Mul(&x[0], &x[1]) - for i := 2; i < len(x); i++ { - res.Mul(&res, &x[i]) - } - } - return -} - -func (g MulGate) Degree() int { - return int(g) -} - -func (g SubGate) Evaluate(element ...fr.Element) (diff fr.Element) { - if len(element) > 2 { - panic("not implemented") //TODO - } - diff.Sub(&element[0], &element[1]) - return -} - -func (g SubGate) Degree() int { - return 1 -} - -func (g NegGate) Evaluate(element ...fr.Element) (neg fr.Element) { - if len(element) != 1 { - panic("univariate gate") - } - neg.Neg(&element[0]) - return -} - -func (g NegGate) Degree() int { - return 1 -} diff --git a/ecc/bls12-378/fr/gkr/gkr_test.go b/ecc/bls12-378/fr/gkr/gkr_test.go deleted file mode 100644 index aef3b7b07d..0000000000 --- a/ecc/bls12-378/fr/gkr/gkr_test.go +++ /dev/null @@ -1,750 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package gkr - -import ( - "encoding/json" - "fmt" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/mimc" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/polynomial" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/sumcheck" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/test_vector_utils" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - "github.com/consensys/gnark-crypto/utils" - "github.com/stretchr/testify/assert" - "hash" - "os" - "path/filepath" - "reflect" - "strconv" - "testing" - "time" -) - -func TestNoGateTwoInstances(t *testing.T) { - // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case - testNoGate(t, []fr.Element{four, three}) -} - -func TestNoGate(t *testing.T) { - testManyInstances(t, 1, testNoGate) -} - -func TestSingleAddGateTwoInstances(t *testing.T) { - testSingleAddGate(t, []fr.Element{four, three}, []fr.Element{two, three}) -} - -func TestSingleAddGate(t *testing.T) { - testManyInstances(t, 2, testSingleAddGate) -} - -func TestSingleMulGateTwoInstances(t *testing.T) { - testSingleMulGate(t, []fr.Element{four, three}, []fr.Element{two, three}) -} - -func TestSingleMulGate(t *testing.T) { - testManyInstances(t, 2, testSingleMulGate) -} - -func TestSingleInputTwoIdentityGatesTwoInstances(t *testing.T) { - - testSingleInputTwoIdentityGates(t, []fr.Element{two, three}) -} - -func TestSingleInputTwoIdentityGates(t *testing.T) { - - testManyInstances(t, 2, testSingleInputTwoIdentityGates) -} - -func TestSingleInputTwoIdentityGatesComposedTwoInstances(t *testing.T) { - testSingleInputTwoIdentityGatesComposed(t, []fr.Element{two, one}) -} - -func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - testManyInstances(t, 1, testSingleInputTwoIdentityGatesComposed) -} - -func TestSingleMimcCipherGateTwoInstances(t *testing.T) { - testSingleMimcCipherGate(t, []fr.Element{one, one}, []fr.Element{one, two}) -} - -func TestSingleMimcCipherGate(t *testing.T) { - testManyInstances(t, 2, testSingleMimcCipherGate) -} - -func TestATimesBSquaredTwoInstances(t *testing.T) { - testATimesBSquared(t, 2, []fr.Element{one, one}, []fr.Element{one, two}) -} - -func TestShallowMimcTwoInstances(t *testing.T) { - testMimc(t, 2, []fr.Element{one, one}, []fr.Element{one, two}) -} -func TestMimcTwoInstances(t *testing.T) { - testMimc(t, 93, []fr.Element{one, one}, []fr.Element{one, two}) -} - -func TestMimc(t *testing.T) { - testManyInstances(t, 2, generateTestMimc(93)) -} - -func generateTestMimc(numRounds int) func(*testing.T, ...[]fr.Element) { - return func(t *testing.T, inputAssignments ...[]fr.Element) { - testMimc(t, numRounds, inputAssignments...) - } -} - -func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := Circuit{Wire{ - Gate: IdentityGate{}, - Inputs: []*Wire{}, - nbUniqueOutputs: 2, - }} - - wire := &circuit[0] - - assignment := WireAssignment{&circuit[0]: []fr.Element{two, three}} - var o settings - pool := polynomial.NewPool(256, 1<<11) - workers := utils.NewWorkerPool() - o.pool = &pool - o.workers = workers - - claimsManagerGen := func() *claimsManager { - manager := newClaimsManager(circuit, assignment, o) - manager.add(wire, []fr.Element{three}, five) - manager.add(wire, []fr.Element{four}, six) - return &manager - } - - transcriptGen := test_vector_utils.NewMessageCounterGenerator(4, 1) - - proof, err := sumcheck.Prove(claimsManagerGen().getClaim(wire), fiatshamir.WithHash(transcriptGen(), nil)) - assert.NoError(t, err) - err = sumcheck.Verify(claimsManagerGen().getLazyClaim(wire), proof, fiatshamir.WithHash(transcriptGen(), nil)) - assert.NoError(t, err) -} - -var one, two, three, four, five, six fr.Element - -func init() { - one.SetOne() - two.Double(&one) - three.Add(&two, &one) - four.Double(&two) - five.Add(&three, &two) - six.Double(&three) -} - -var testManyInstancesLogMaxInstances = -1 - -func getLogMaxInstances(t *testing.T) int { - if testManyInstancesLogMaxInstances == -1 { - - s := os.Getenv("GKR_LOG_INSTANCES") - if s == "" { - testManyInstancesLogMaxInstances = 5 - } else { - var err error - testManyInstancesLogMaxInstances, err = strconv.Atoi(s) - if err != nil { - t.Error(err) - } - } - - } - return testManyInstancesLogMaxInstances -} - -func testManyInstances(t *testing.T, numInput int, test func(*testing.T, ...[]fr.Element)) { - fullAssignments := make([][]fr.Element, numInput) - maxSize := 1 << getLogMaxInstances(t) - - t.Log("Entered test orchestrator, assigning and randomizing inputs") - - for i := range fullAssignments { - fullAssignments[i] = make([]fr.Element, maxSize) - setRandom(fullAssignments[i]) - } - - inputAssignments := make([][]fr.Element, numInput) - for numEvals := maxSize; numEvals <= maxSize; numEvals *= 2 { - for i, fullAssignment := range fullAssignments { - inputAssignments[i] = fullAssignment[:numEvals] - } - - t.Log("Selected inputs for test") - test(t, inputAssignments...) - } -} - -func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := Circuit{ - { - Inputs: []*Wire{}, - Gate: nil, - }, - } - - assignment := WireAssignment{&c[0]: inputAssignments[0]} - - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NoError(t, err) - - // Even though a hash is called here, the proof is empty - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NoError(t, err, "proof rejected") -} - -func testSingleAddGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := make(Circuit, 3) - c[2] = Wire{ - Gate: Gates["add"], - Inputs: []*Wire{&c[0], &c[1]}, - } - - assignment := WireAssignment{&c[0]: inputAssignments[0], &c[1]: inputAssignments[1]}.Complete(c) - - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NoError(t, err) - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NoError(t, err, "proof rejected") - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NotNil(t, err, "bad proof accepted") -} - -func testSingleMulGate(t *testing.T, inputAssignments ...[]fr.Element) { - - c := make(Circuit, 3) - c[2] = Wire{ - Gate: Gates["mul"], - Inputs: []*Wire{&c[0], &c[1]}, - } - - assignment := WireAssignment{&c[0]: inputAssignments[0], &c[1]: inputAssignments[1]}.Complete(c) - - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NoError(t, err) - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NoError(t, err, "proof rejected") - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NotNil(t, err, "bad proof accepted") -} - -func testSingleInputTwoIdentityGates(t *testing.T, inputAssignments ...[]fr.Element) { - c := make(Circuit, 3) - - c[1] = Wire{ - Gate: IdentityGate{}, - Inputs: []*Wire{&c[0]}, - } - - c[2] = Wire{ - Gate: IdentityGate{}, - Inputs: []*Wire{&c[0]}, - } - - assignment := WireAssignment{&c[0]: inputAssignments[0]}.Complete(c) - - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err) - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err, "proof rejected") - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NotNil(t, err, "bad proof accepted") -} - -func testSingleMimcCipherGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := make(Circuit, 3) - - c[2] = Wire{ - Gate: mimcCipherGate{}, - Inputs: []*Wire{&c[0], &c[1]}, - } - - t.Log("Evaluating all circuit wires") - assignment := WireAssignment{&c[0]: inputAssignments[0], &c[1]: inputAssignments[1]}.Complete(c) - t.Log("Circuit evaluation complete") - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err) - t.Log("Proof complete") - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err, "proof rejected") - - t.Log("Successful verification complete") - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NotNil(t, err, "bad proof accepted") - t.Log("Unsuccessful verification complete") -} - -func testSingleInputTwoIdentityGatesComposed(t *testing.T, inputAssignments ...[]fr.Element) { - c := make(Circuit, 3) - - c[1] = Wire{ - Gate: IdentityGate{}, - Inputs: []*Wire{&c[0]}, - } - c[2] = Wire{ - Gate: IdentityGate{}, - Inputs: []*Wire{&c[1]}, - } - - assignment := WireAssignment{&c[0]: inputAssignments[0]}.Complete(c) - - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err) - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err, "proof rejected") - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NotNil(t, err, "bad proof accepted") -} - -func mimcCircuit(numRounds int) Circuit { - c := make(Circuit, numRounds+2) - - for i := 2; i < len(c); i++ { - c[i] = Wire{ - Gate: mimcCipherGate{}, - Inputs: []*Wire{&c[i-1], &c[0]}, - } - } - return c -} - -func testMimc(t *testing.T, numRounds int, inputAssignments ...[]fr.Element) { - //TODO: Implement mimc correctly. Currently, the computation is mimc(a,b) = cipher( cipher( ... cipher(a, b), b) ..., b) - // @AlexandreBelling: Please explain the extra layers in https://github.com/ConsenSys/gkr-mimc/blob/81eada039ab4ed403b7726b535adb63026e8011f/examples/mimc.go#L10 - - c := mimcCircuit(numRounds) - - t.Log("Evaluating all circuit wires") - assignment := WireAssignment{&c[0]: inputAssignments[0], &c[1]: inputAssignments[1]}.Complete(c) - t.Log("Circuit evaluation complete") - - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err) - - t.Log("Proof finished") - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err, "proof rejected") - - t.Log("Successful verification finished") - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NotNil(t, err, "bad proof accepted") - t.Log("Unsuccessful verification finished") -} - -func testATimesBSquared(t *testing.T, numRounds int, inputAssignments ...[]fr.Element) { - // This imitates the MiMC circuit - - c := make(Circuit, numRounds+2) - - for i := 2; i < len(c); i++ { - c[i] = Wire{ - Gate: Gates["mul"], - Inputs: []*Wire{&c[i-1], &c[0]}, - } - } - - assignment := WireAssignment{&c[0]: inputAssignments[0], &c[1]: inputAssignments[1]}.Complete(c) - - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err) - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err, "proof rejected") - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NotNil(t, err, "bad proof accepted") -} - -func setRandom(slice []fr.Element) { - for i := range slice { - slice[i].SetRandom() - } -} - -func generateTestProver(path string) func(t *testing.T) { - return func(t *testing.T) { - testCase, err := newTestCase(path) - assert.NoError(t, err) - proof, err := Prove(testCase.Circuit, testCase.FullAssignment, fiatshamir.WithHash(testCase.Hash)) - assert.NoError(t, err) - assert.NoError(t, proofEquals(testCase.Proof, proof)) - } -} - -func generateTestVerifier(path string) func(t *testing.T) { - return func(t *testing.T) { - testCase, err := newTestCase(path) - assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(testCase.Hash)) - assert.NoError(t, err, "proof rejected") - testCase, err = newTestCase(path) - assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(2, 0))) - assert.NotNil(t, err, "bad proof accepted") - } -} - -func TestGkrVectors(t *testing.T) { - - testDirPath := "../../../../internal/generator/gkr/test_vectors" - dirEntries, err := os.ReadDir(testDirPath) - assert.NoError(t, err) - for _, dirEntry := range dirEntries { - if !dirEntry.IsDir() { - - if filepath.Ext(dirEntry.Name()) == ".json" { - path := filepath.Join(testDirPath, dirEntry.Name()) - noExt := dirEntry.Name()[:len(dirEntry.Name())-len(".json")] - - t.Run(noExt+"_prover", generateTestProver(path)) - t.Run(noExt+"_verifier", generateTestVerifier(path)) - - } - } - } -} - -func proofEquals(expected Proof, seen Proof) error { - if len(expected) != len(seen) { - return fmt.Errorf("length mismatch %d ≠ %d", len(expected), len(seen)) - } - for i, x := range expected { - xSeen := seen[i] - - if xSeen.FinalEvalProof == nil { - if seenFinalEval := x.FinalEvalProof.([]fr.Element); len(seenFinalEval) != 0 { - return fmt.Errorf("length mismatch %d ≠ %d", 0, len(seenFinalEval)) - } - } else { - if err := test_vector_utils.SliceEquals(x.FinalEvalProof.([]fr.Element), xSeen.FinalEvalProof.([]fr.Element)); err != nil { - return fmt.Errorf("final evaluation proof mismatch") - } - } - if err := test_vector_utils.PolynomialSliceEquals(x.PartialSumPolys, xSeen.PartialSumPolys); err != nil { - return err - } - } - return nil -} - -func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { - fmt.Println("creating circuit structure") - c := mimcCircuit(mimcDepth) - - in0 := make([]fr.Element, nbInstances) - in1 := make([]fr.Element, nbInstances) - setRandom(in0) - setRandom(in1) - - fmt.Println("evaluating circuit") - start := time.Now().UnixMicro() - assignment := WireAssignment{&c[0]: in0, &c[1]: in1}.Complete(c) - solved := time.Now().UnixMicro() - start - fmt.Println("solved in", solved, "μs") - - //b.ResetTimer() - fmt.Println("constructing proof") - start = time.Now().UnixMicro() - _, err := Prove(c, assignment, fiatshamir.WithHash(mimc.NewMiMC())) - proved := time.Now().UnixMicro() - start - fmt.Println("proved in", proved, "μs") - assert.NoError(b, err) -} - -func BenchmarkGkrMimc19(b *testing.B) { - benchmarkGkrMiMC(b, 1<<19, 91) -} - -func BenchmarkGkrMimc17(b *testing.B) { - benchmarkGkrMiMC(b, 1<<17, 91) -} - -func TestTopSortTrivial(t *testing.T) { - c := make(Circuit, 2) - c[0].Inputs = []*Wire{&c[1]} - sorted := topologicalSort(c) - assert.Equal(t, []*Wire{&c[1], &c[0]}, sorted) -} - -func TestTopSortDeep(t *testing.T) { - c := make(Circuit, 4) - c[0].Inputs = []*Wire{&c[2]} - c[1].Inputs = []*Wire{&c[3]} - c[2].Inputs = []*Wire{} - c[3].Inputs = []*Wire{&c[0]} - sorted := topologicalSort(c) - assert.Equal(t, []*Wire{&c[2], &c[0], &c[3], &c[1]}, sorted) -} - -func TestTopSortWide(t *testing.T) { - c := make(Circuit, 10) - c[0].Inputs = []*Wire{&c[3], &c[8]} - c[1].Inputs = []*Wire{&c[6]} - c[2].Inputs = []*Wire{&c[4]} - c[3].Inputs = []*Wire{} - c[4].Inputs = []*Wire{} - c[5].Inputs = []*Wire{&c[9]} - c[6].Inputs = []*Wire{&c[9]} - c[7].Inputs = []*Wire{&c[9], &c[5], &c[2]} - c[8].Inputs = []*Wire{&c[4], &c[3]} - c[9].Inputs = []*Wire{} - - sorted := topologicalSort(c) - sortedExpected := []*Wire{&c[3], &c[4], &c[2], &c[8], &c[0], &c[9], &c[5], &c[6], &c[1], &c[7]} - - assert.Equal(t, sortedExpected, sorted) -} - -type WireInfo struct { - Gate string `json:"gate"` - Inputs []int `json:"inputs"` -} - -type CircuitInfo []WireInfo - -var circuitCache = make(map[string]Circuit) - -func getCircuit(path string) (Circuit, error) { - path, err := filepath.Abs(path) - if err != nil { - return nil, err - } - if circuit, ok := circuitCache[path]; ok { - return circuit, nil - } - var bytes []byte - if bytes, err = os.ReadFile(path); err == nil { - var circuitInfo CircuitInfo - if err = json.Unmarshal(bytes, &circuitInfo); err == nil { - circuit := circuitInfo.toCircuit() - circuitCache[path] = circuit - return circuit, nil - } else { - return nil, err - } - } else { - return nil, err - } -} - -func (c CircuitInfo) toCircuit() (circuit Circuit) { - circuit = make(Circuit, len(c)) - for i := range c { - circuit[i].Gate = Gates[c[i].Gate] - circuit[i].Inputs = make([]*Wire, len(c[i].Inputs)) - for k, inputCoord := range c[i].Inputs { - input := &circuit[inputCoord] - circuit[i].Inputs[k] = input - } - } - return -} - -func init() { - Gates["mimc"] = mimcCipherGate{} //TODO: Add ark - Gates["select-input-3"] = _select(2) -} - -type mimcCipherGate struct { - ark fr.Element -} - -func (m mimcCipherGate) Evaluate(input ...fr.Element) (res fr.Element) { - var sum fr.Element - - sum. - Add(&input[0], &input[1]). - Add(&sum, &m.ark) - - res.Square(&sum) // sum^2 - res.Mul(&res, &sum) // sum^3 - res.Square(&res) //sum^6 - res.Mul(&res, &sum) //sum^7 - - return -} - -func (m mimcCipherGate) Degree() int { - return 7 -} - -type PrintableProof []PrintableSumcheckProof - -type PrintableSumcheckProof struct { - FinalEvalProof interface{} `json:"finalEvalProof"` - PartialSumPolys [][]interface{} `json:"partialSumPolys"` -} - -func unmarshalProof(printable PrintableProof) (Proof, error) { - proof := make(Proof, len(printable)) - for i := range printable { - finalEvalProof := []fr.Element(nil) - - if printable[i].FinalEvalProof != nil { - finalEvalSlice := reflect.ValueOf(printable[i].FinalEvalProof) - finalEvalProof = make([]fr.Element, finalEvalSlice.Len()) - for k := range finalEvalProof { - if _, err := test_vector_utils.SetElement(&finalEvalProof[k], finalEvalSlice.Index(k).Interface()); err != nil { - return nil, err - } - } - } - - proof[i] = sumcheck.Proof{ - PartialSumPolys: make([]polynomial.Polynomial, len(printable[i].PartialSumPolys)), - FinalEvalProof: finalEvalProof, - } - for k := range printable[i].PartialSumPolys { - var err error - if proof[i].PartialSumPolys[k], err = test_vector_utils.SliceToElementSlice(printable[i].PartialSumPolys[k]); err != nil { - return nil, err - } - } - } - return proof, nil -} - -type TestCase struct { - Circuit Circuit - Hash hash.Hash - Proof Proof - FullAssignment WireAssignment - InOutAssignment WireAssignment -} - -type TestCaseInfo struct { - Hash test_vector_utils.HashDescription `json:"hash"` - Circuit string `json:"circuit"` - Input [][]interface{} `json:"input"` - Output [][]interface{} `json:"output"` - Proof PrintableProof `json:"proof"` -} - -var testCases = make(map[string]*TestCase) - -func newTestCase(path string) (*TestCase, error) { - path, err := filepath.Abs(path) - if err != nil { - return nil, err - } - dir := filepath.Dir(path) - - tCase, ok := testCases[path] - if !ok { - var bytes []byte - if bytes, err = os.ReadFile(path); err == nil { - var info TestCaseInfo - err = json.Unmarshal(bytes, &info) - if err != nil { - return nil, err - } - - var circuit Circuit - if circuit, err = getCircuit(filepath.Join(dir, info.Circuit)); err != nil { - return nil, err - } - var _hash hash.Hash - if _hash, err = test_vector_utils.HashFromDescription(info.Hash); err != nil { - return nil, err - } - var proof Proof - if proof, err = unmarshalProof(info.Proof); err != nil { - return nil, err - } - - fullAssignment := make(WireAssignment) - inOutAssignment := make(WireAssignment) - - sorted := topologicalSort(circuit) - - inI, outI := 0, 0 - for _, w := range sorted { - var assignmentRaw []interface{} - if w.IsInput() { - if inI == len(info.Input) { - return nil, fmt.Errorf("fewer input in vector than in circuit") - } - assignmentRaw = info.Input[inI] - inI++ - } else if w.IsOutput() { - if outI == len(info.Output) { - return nil, fmt.Errorf("fewer output in vector than in circuit") - } - assignmentRaw = info.Output[outI] - outI++ - } - if assignmentRaw != nil { - var wireAssignment []fr.Element - if wireAssignment, err = test_vector_utils.SliceToElementSlice(assignmentRaw); err != nil { - return nil, err - } - - fullAssignment[w] = wireAssignment - inOutAssignment[w] = wireAssignment - } - } - - fullAssignment.Complete(circuit) - - for _, w := range sorted { - if w.IsOutput() { - - if err = test_vector_utils.SliceEquals(inOutAssignment[w], fullAssignment[w]); err != nil { - return nil, fmt.Errorf("assignment mismatch: %v", err) - } - - } - } - - tCase = &TestCase{ - FullAssignment: fullAssignment, - InOutAssignment: inOutAssignment, - Proof: proof, - Hash: _hash, - Circuit: circuit, - } - - testCases[path] = tCase - } else { - return nil, err - } - } - - return tCase, nil -} - -type _select int - -func (g _select) Evaluate(in ...fr.Element) fr.Element { - return in[g] -} - -func (g _select) Degree() int { - return 1 -} diff --git a/ecc/bls12-378/fr/hash_to_field/doc.go b/ecc/bls12-378/fr/hash_to_field/doc.go deleted file mode 100644 index 132d9980ff..0000000000 --- a/ecc/bls12-378/fr/hash_to_field/doc.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package htf provides hasher based on RFC 9380 Section 5. -// -// The [RFC 9380] defines a method for hashing bytes to elliptic curves. Section -// 5 of the RFC describes a method for uniformly hashing bytes into a field -// using a domain separation. The hashing is implemented in [fp], but this -// package provides a wrapper for the method which implements [hash.Hash] for -// using the method recursively. -// -// [RFC 9380]: https://datatracker.ietf.org/doc/html/rfc9380 -package hash_to_field - -import ( - _ "hash" - - _ "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" -) diff --git a/ecc/bls12-378/fr/hash_to_field/hash_to_field.go b/ecc/bls12-378/fr/hash_to_field/hash_to_field.go deleted file mode 100644 index 8793567027..0000000000 --- a/ecc/bls12-378/fr/hash_to_field/hash_to_field.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package hash_to_field - -import ( - "fmt" - "hash" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" -) - -type wrappedHashToField struct { - domain []byte - toHash []byte -} - -// New returns a new hasher instance which uses [fr.Hash] to hash all the -// written bytes to a field element, returning the byte representation of the -// field element. The domain separator is passed as-is to hashing method. -func New(domainSeparator []byte) hash.Hash { - return &wrappedHashToField{ - domain: append([]byte{}, domainSeparator...), // copy in case the argument is modified - } -} - -func (w *wrappedHashToField) Write(p []byte) (n int, err error) { - w.toHash = append(w.toHash, p...) - return len(p), nil -} - -func (w *wrappedHashToField) Sum(b []byte) []byte { - res, err := fr.Hash(w.toHash, w.domain, 1) - if err != nil { - // we want to follow the interface, cannot return error and have to panic - // but by default the method shouldn't return an error internally - panic(fmt.Sprintf("native field to hash: %v", err)) - } - bts := res[0].Bytes() - return append(b, bts[:]...) -} - -func (w *wrappedHashToField) Reset() { - w.toHash = nil -} - -func (w *wrappedHashToField) Size() int { - return fr.Bytes -} - -func (w *wrappedHashToField) BlockSize() int { - return fr.Bytes -} diff --git a/ecc/bls12-378/fr/hash_to_field/hash_to_field_test.go b/ecc/bls12-378/fr/hash_to_field/hash_to_field_test.go deleted file mode 100644 index dfb2425447..0000000000 --- a/ecc/bls12-378/fr/hash_to_field/hash_to_field_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package hash_to_field - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" -) - -func TestHashInterface(t *testing.T) { - msg := []byte("test") - sep := []byte("separator") - res, err := fr.Hash(msg, sep, 1) - if err != nil { - t.Fatal("hash to field", err) - } - - htfFn := New(sep) - htfFn.Write(msg) - bts := htfFn.Sum(nil) - var res2 fr.Element - res2.SetBytes(bts[:fr.Bytes]) - if !res[0].Equal(&res2) { - t.Error("not equal") - } -} diff --git a/ecc/bls12-378/fr/iop/doc.go b/ecc/bls12-378/fr/iop/doc.go deleted file mode 100644 index 20f8b1a302..0000000000 --- a/ecc/bls12-378/fr/iop/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package iop provides an API to computations common -// to iop backends (permutation, quotient). -package iop diff --git a/ecc/bls12-378/fr/iop/expressions.go b/ecc/bls12-378/fr/iop/expressions.go deleted file mode 100644 index d9c8b77d78..0000000000 --- a/ecc/bls12-378/fr/iop/expressions.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "errors" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/internal/parallel" - "math/bits" -) - -// Expression represents a multivariate polynomial. -type Expression func(i int, x ...fr.Element) fr.Element - -// Evaluate evaluates f on each entry of x. The returned value is -// the vector of evaluations of e on x. -// The form of the result is form. -// if r is provided (not nil), it is used as the result vector, -// that is the memory space for the coefficients of the resulting polynomial. -// The Size field of the result is the same as the one of x[0]. -// The blindedSize field of the result is the same as Size. -// The Shift field of the result is 0. -func Evaluate(f Expression, r []fr.Element, form Form, x ...*Polynomial) (*Polynomial, error) { - if len(x) == 0 { - return nil, errors.New("need at lest one input") - } - - // check that the sizes are consistent - n := x[0].coefficients.Len() - m := len(x) - for i := 1; i < m; i++ { - if n != x[i].coefficients.Len() { - return nil, ErrInconsistentSize - } - } - - // check result len - if r == nil { - r = make([]fr.Element, n) - } else if len(r) != n { - return nil, ErrInconsistentSize - } - - // result coefficients - idx := func(i int) int { - return i - } - if form.Layout != Regular { - nn := uint64(64 - bits.TrailingZeros(uint(n))) - idx = func(i int) int { - return int(bits.Reverse64(uint64(i)) >> nn) - } - } - - parallel.Execute(n, func(start, end int) { - vx := make([]fr.Element, m) - for i := start; i < end; i++ { - for j := 0; j < m; j++ { - vx[j] = x[j].GetCoeff(i) - } - r[idx(i)] = f(i, vx...) - } - }) - - res := NewPolynomial(&r, form) - res.size = x[0].size - res.blindedSize = x[0].size - res.shift = 0 - - return res, nil -} diff --git a/ecc/bls12-378/fr/iop/expressions_test.go b/ecc/bls12-378/fr/iop/expressions_test.go deleted file mode 100644 index cded38a9b2..0000000000 --- a/ecc/bls12-378/fr/iop/expressions_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" -) - -func TestEvaluate(t *testing.T) { - - f := func(_ int, x ...fr.Element) fr.Element { - var a fr.Element - a.Add(&x[0], &x[1]).Add(&a, &x[2]) - return a - } - - size := 64 - u := make([]fr.Element, size) - v := make([]fr.Element, size) - w := make([]fr.Element, size) - for i := 0; i < size; i++ { - u[i].SetUint64(uint64(i)) - v[i].SetUint64(uint64(i + 1)) - w[i].SetUint64(uint64(i + 2)) - } - r := make([]fr.Element, size) - for i := 0; i < size; i++ { - r[i].SetUint64(uint64(3 * (i + 1))) - } - form := Form{Layout: Regular, Basis: Canonical} - wu := NewPolynomial(&u, form) - wv := NewPolynomial(&v, form) - ww := NewPolynomial(&w, form) - - rr, err := Evaluate(f, nil, form, wu, wv, ww) - if err != nil { - t.Fatal(err) - } - - wu.ToBitReverse() - rrb, err := Evaluate(f, nil, form, wu, wv, ww) - if err != nil { - t.Fatal(err) - } - - wv.ToBitReverse() - ww.ToBitReverse() - rrc, err := Evaluate(f, nil, form, wu, wv, ww) - if err != nil { - t.Fatal(err) - } - - // compare with the expected result - for i := 0; i < size; i++ { - if !rr.Coefficients()[i].Equal(&r[i]) { - t.Fatal("error evaluation") - } - if !rrb.Coefficients()[i].Equal(&r[i]) { - t.Fatal("error evaluation") - } - if !rrc.Coefficients()[i].Equal(&r[i]) { - t.Fatal("error evaluation") - } - - } -} diff --git a/ecc/bls12-378/fr/iop/polynomial.go b/ecc/bls12-378/fr/iop/polynomial.go deleted file mode 100644 index d002265421..0000000000 --- a/ecc/bls12-378/fr/iop/polynomial.go +++ /dev/null @@ -1,462 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "encoding/binary" - "io" - "math/big" - "math/bits" - "runtime" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/fft" -) - -// Basis indicates the basis in which a polynomial is represented. -type Basis uint32 - -const ( - Canonical Basis = 1 << iota - Lagrange - LagrangeCoset -) - -// Layout indicates if a polynomial has a BitReverse or a Regular layout -type Layout uint32 - -const ( - Regular Layout = 8 << iota - BitReverse -) - -// Form describes the form of a polynomial. -// TODO should be a regular enum? -type Form struct { - Basis Basis - Layout Layout -} - -// enum of the possible Form values for type-safe switches -// in this package -var ( - canonicalRegular = Form{Canonical, Regular} - canonicalBitReverse = Form{Canonical, BitReverse} - lagrangeRegular = Form{Lagrange, Regular} - lagrangeBitReverse = Form{Lagrange, BitReverse} - lagrangeCosetRegular = Form{LagrangeCoset, Regular} - lagrangeCosetBitReverse = Form{LagrangeCoset, BitReverse} -) - -// Polynomial wraps a polynomial so that it is -// interpreted as P'(X)=P(\omega^{s}X). -// Size is the real size of the polynomial (seen as a vector). -// For instance if len(P)=32 but P.Size=8, it means that P has been -// extended (e.g. it is evaluated on a larger set) but P is a polynomial -// of degree 7. -// blindedSize is the size of the polynomial when it is blinded. By -// default blindedSize=Size, until the polynomial is blinded. -type Polynomial struct { - *polynomial - shift int - size int - blindedSize int -} - -// NewPolynomial returned a Polynomial from the provided coefficients in the given form. -// A Polynomial can be seen as a "shared pointer" on a list of coefficients. -// It is the responsibility of the user to call the Clone method if the coefficients -// shouldn't be mutated. -func NewPolynomial(coeffs *[]fr.Element, form Form) *Polynomial { - return &Polynomial{ - polynomial: newPolynomial(coeffs, form), - size: len(*coeffs), - blindedSize: len(*coeffs), - } -} - -// Shift the wrapped polynomial; it doesn't modify the underlying data structure, -// but flag the Polynomial such that it will be interpreted as p(\omega^shift X) -func (p *Polynomial) Shift(shift int) *Polynomial { - p.shift = shift - return p -} - -// BlindedSize returns the the size of the polynomial when it is blinded. By -// default blindedSize=Size, until the polynomial is blinded. -func (p *Polynomial) BlindedSize() int { - return p.blindedSize -} - -// Size returns the real size of the polynomial (seen as a vector). -// For instance if len(P)=32 but P.Size=8, it means that P has been -// extended (e.g. it is evaluated on a larger set) but P is a polynomial -// of degree 7. -func (p *Polynomial) Size() int { - return p.size -} - -// SetSize sets the size of the polynomial. -// size is the real size of the polynomial (seen as a vector). -// For instance if len(P)=32 but P.size=8, it means that P has been -// extended (e.g. it is evaluated on a larger set) but P is a polynomial -// of degree 7. -func (p *Polynomial) SetSize(size int) { - p.size = size -} - -// SetBlindedSize sets the blinded size of the polynomial. -func (p *Polynomial) SetBlindedSize(size int) { - p.blindedSize = size -} - -// Blind blinds a polynomial q by adding Q(X)*(X^{n}-1), -// where deg Q = blindingOrder and Q is random, and n is the -// size of q. Sets the result to p and returns it. -// -// blindingOrder is the degree of Q, where the blinding is Q(X)*(X^{n}-1) -// where n is the size of p. The size of p is modified since the underlying -// polynomial is of bigger degree now. The new size is p.Size+1+blindingOrder. -// -// /!\ The code panics if wq is not in canonical, regular layout -func (p *Polynomial) Blind(blindingOrder int) *Polynomial { - // check that p is in canonical basis - if p.Form != canonicalRegular { - panic("the input must be in canonical basis, regular layout") - } - - // we add Q*(x^{n}-1) so the new size is deg(Q)+n+1 - // where n is the size of wq. - newSize := p.size + blindingOrder + 1 - - // Resize p. The size of wq might has already been increased - // (e.g. when the polynomial is evaluated on a larger domain), - // if that's the case we don't resize the polynomial. - p.grow(newSize) - - // blinding: we add Q(X)(X^{n}-1) to P, where deg(Q)=blindingOrder - var r fr.Element - - for i := 0; i <= blindingOrder; i++ { - r.SetRandom() - (*p.coefficients)[i].Sub(&(*p.coefficients)[i], &r) - (*p.coefficients)[i+p.size].Add(&(*p.coefficients)[i+p.size], &r) - } - p.blindedSize = newSize - - return p -} - -// Evaluate evaluates p at x. -// The code panics if the function is not in canonical form. -func (p *Polynomial) Evaluate(x fr.Element) fr.Element { - - if p.shift == 0 { - return p.polynomial.evaluate(x) - } - - var g fr.Element - if p.shift <= 5 { - gen, err := fft.Generator(uint64(p.size)) - if err != nil { - panic(err) - } - g = smallExp(gen, p.shift) - x.Mul(&x, &g) - return p.polynomial.evaluate(x) - } - - bs := big.NewInt(int64(p.shift)) - g = *g.Exp(g, bs) - x.Mul(&x, &g) - return p.polynomial.evaluate(x) -} - -// Clone returns a deep copy of p. The underlying polynomial is cloned; -// see also ShallowClone to perform a ShallowClone on the underlying polynomial. -// If capacity is provided, the new coefficient slice capacity will be set accordingly. -func (p *Polynomial) Clone(capacity ...int) *Polynomial { - res := p.ShallowClone() - res.polynomial = p.polynomial.clone(capacity...) - return res -} - -// ShallowClone returns a shallow copy of p. The underlying polynomial coefficient -// is NOT cloned and both objects will point to the same coefficient vector. -func (p *Polynomial) ShallowClone() *Polynomial { - res := *p - return &res -} - -// GetCoeff returns the i-th entry of p, taking the layout in account. -func (p *Polynomial) GetCoeff(i int) fr.Element { - - n := p.coefficients.Len() - rho := n / p.size - if p.polynomial.Form.Layout == Regular { - return (*p.coefficients)[(i+rho*p.shift)%n] - } else { - nn := uint64(64 - bits.TrailingZeros(uint(n))) - iRev := bits.Reverse64(uint64((i+rho*p.shift)%n)) >> nn - return (*p.coefficients)[iRev] - } - -} - -// polynomial represents a polynomial, the vector of coefficients -// along with the basis and the layout. -type polynomial struct { - coefficients *fr.Vector - Form -} - -// Coefficients returns a slice on the underlying data structure. -func (p *polynomial) Coefficients() []fr.Element { - return (*p.coefficients) -} - -// newPolynomial creates a new polynomial. The slice coeff NOT copied -// but directly assigned to the new polynomial. -func newPolynomial(coeffs *[]fr.Element, form Form) *polynomial { - return &polynomial{coefficients: (*fr.Vector)(coeffs), Form: form} -} - -// clone returns a deep copy of the underlying data structure. -func (p *polynomial) clone(capacity ...int) *polynomial { - c := p.coefficients.Len() - if len(capacity) == 1 && capacity[0] > c { - c = capacity[0] - } - newCoeffs := make(fr.Vector, p.coefficients.Len(), c) - r := &polynomial{ - coefficients: &newCoeffs, - Form: p.Form, - } - copy((*r.coefficients), (*p.coefficients)) - return r -} - -// evaluate evaluates p at x. -// The code panics if the function is not in canonical form. -func (p *polynomial) evaluate(x fr.Element) fr.Element { - - var r fr.Element - if p.Basis != Canonical { - panic("p must be in canonical basis") - } - - if p.Layout == Regular { - for i := p.coefficients.Len() - 1; i >= 0; i-- { - r.Mul(&r, &x).Add(&r, &(*p.coefficients)[i]) - } - } else { - nn := uint64(64 - bits.TrailingZeros(uint(p.coefficients.Len()))) - for i := p.coefficients.Len() - 1; i >= 0; i-- { - iRev := bits.Reverse64(uint64(i)) >> nn - r.Mul(&r, &x).Add(&r, &(*p.coefficients)[iRev]) - } - } - - return r - -} - -// ToRegular changes the layout of p to Regular. -// Leaves p unchanged if p's layout was already Regular. -func (p *Polynomial) ToRegular() *Polynomial { - if p.Layout == Regular { - return p - } - fft.BitReverse((*p.coefficients)) - p.Layout = Regular - return p -} - -// ToBitReverse changes the layout of p to BitReverse. -// Leaves p unchanged if p's layout was already BitReverse. -func (p *Polynomial) ToBitReverse() *Polynomial { - if p.Layout == BitReverse { - return p - } - fft.BitReverse((*p.coefficients)) - p.Layout = BitReverse - return p -} - -// ToLagrange converts p to Lagrange form. -// Leaves p unchanged if p was already in Lagrange form. -func (p *Polynomial) ToLagrange(d *fft.Domain, nbTasks ...int) *Polynomial { - id := p.Form - p.grow(int(d.Cardinality)) - - n := runtime.NumCPU() - if len(nbTasks) > 0 { - n = nbTasks[0] - } - - switch id { - case canonicalRegular: - p.Layout = BitReverse - d.FFT((*p.coefficients), fft.DIF, fft.WithNbTasks(n)) - case canonicalBitReverse: - p.Layout = Regular - d.FFT((*p.coefficients), fft.DIT, fft.WithNbTasks(n)) - case lagrangeRegular, lagrangeBitReverse: - return p - case lagrangeCosetRegular: - p.Layout = Regular - d.FFTInverse((*p.coefficients), fft.DIF, fft.OnCoset(), fft.WithNbTasks(n)) - d.FFT((*p.coefficients), fft.DIT) - case lagrangeCosetBitReverse: - p.Layout = BitReverse - d.FFTInverse((*p.coefficients), fft.DIT, fft.OnCoset(), fft.WithNbTasks(n)) - d.FFT((*p.coefficients), fft.DIF) - default: - panic("unknown ID") - } - p.Basis = Lagrange - return p -} - -// ToCanonical converts p to canonical form. -// Leaves p unchanged if p was already in Canonical form. -func (p *Polynomial) ToCanonical(d *fft.Domain, nbTasks ...int) *Polynomial { - id := p.Form - p.grow(int(d.Cardinality)) - n := runtime.NumCPU() - if len(nbTasks) > 0 { - n = nbTasks[0] - } - switch id { - case canonicalRegular, canonicalBitReverse: - return p - case lagrangeRegular: - p.Layout = BitReverse - d.FFTInverse((*p.coefficients), fft.DIF, fft.WithNbTasks(n)) - case lagrangeBitReverse: - p.Layout = Regular - d.FFTInverse((*p.coefficients), fft.DIT, fft.WithNbTasks(n)) - case lagrangeCosetRegular: - p.Layout = BitReverse - d.FFTInverse((*p.coefficients), fft.DIF, fft.OnCoset(), fft.WithNbTasks(n)) - case lagrangeCosetBitReverse: - p.Layout = Regular - d.FFTInverse((*p.coefficients), fft.DIT, fft.OnCoset(), fft.WithNbTasks(n)) - default: - panic("unknown ID") - } - p.Basis = Canonical - return p -} - -func (p *polynomial) grow(newSize int) { - offset := newSize - p.coefficients.Len() - if offset > 0 { - (*p.coefficients) = append((*p.coefficients), make(fr.Vector, offset)...) - } -} - -// ToLagrangeCoset Sets p to q, in LagrangeCoset form and returns it. -func (p *Polynomial) ToLagrangeCoset(d *fft.Domain) *Polynomial { - id := p.Form - p.grow(int(d.Cardinality)) - switch id { - case canonicalRegular: - p.Layout = BitReverse - d.FFT((*p.coefficients), fft.DIF, fft.OnCoset()) - case canonicalBitReverse: - p.Layout = Regular - d.FFT((*p.coefficients), fft.DIT, fft.OnCoset()) - case lagrangeRegular: - p.Layout = Regular - d.FFTInverse((*p.coefficients), fft.DIF) - d.FFT((*p.coefficients), fft.DIT, fft.OnCoset()) - case lagrangeBitReverse: - p.Layout = BitReverse - d.FFTInverse((*p.coefficients), fft.DIT) - d.FFT((*p.coefficients), fft.DIF, fft.OnCoset()) - case lagrangeCosetRegular, lagrangeCosetBitReverse: - return p - default: - panic("unknown ID") - } - - p.Basis = LagrangeCoset - return p -} - -// WriteTo implements io.WriterTo -func (p *Polynomial) WriteTo(w io.Writer) (int64, error) { - // encode coefficients - n, err := p.polynomial.coefficients.WriteTo(w) - if err != nil { - return n, err - } - - // encode Form.Basis, Form.Layout, shift, size & blindedSize as uint32 - var data = []uint32{ - uint32(p.Basis), - uint32(p.Layout), - uint32(p.shift), - uint32(p.size), - uint32(p.blindedSize), - } - for _, v := range data { - err = binary.Write(w, binary.BigEndian, v) - if err != nil { - return n, err - } - n += 4 - } - return n, nil -} - -// ReadFrom implements io.ReaderFrom -func (p *Polynomial) ReadFrom(r io.Reader) (int64, error) { - // decode coefficients - if p.polynomial == nil { - p.polynomial = new(polynomial) - } - if p.polynomial.coefficients == nil { - v := make(fr.Vector, 0) - p.polynomial.coefficients = &v - } - n, err := p.polynomial.coefficients.ReadFrom(r) - if err != nil { - return n, err - } - - // decode Form.Basis, Form.Layout, shift, size & blindedSize as uint32 - var data [5]uint32 - var buf [4]byte - for i := range data { - read, err := io.ReadFull(r, buf[:4]) - n += int64(read) - if err != nil { - return n, err - } - data[i] = binary.BigEndian.Uint32(buf[:4]) - } - - p.Basis = Basis(data[0]) - p.Layout = Layout(data[1]) - p.shift = int(data[2]) - p.size = int(data[3]) - p.blindedSize = int(data[4]) - - return n, nil -} diff --git a/ecc/bls12-378/fr/iop/polynomial_test.go b/ecc/bls12-378/fr/iop/polynomial_test.go deleted file mode 100644 index 5421cb7811..0000000000 --- a/ecc/bls12-378/fr/iop/polynomial_test.go +++ /dev/null @@ -1,729 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/fft" - - "github.com/stretchr/testify/require" - - "bytes" - "reflect" -) - -func TestEvaluation(t *testing.T) { - - size := 8 - shift := 2 - d := fft.NewDomain(uint64(size)) - c := randomVector(size) - wp := NewPolynomial(c, Form{Basis: Canonical, Layout: Regular}) - wps := wp.ShallowClone().Shift(shift) - ref := wp.Clone() - ref.ToLagrange(d).ToRegular() - - // regular layout - a := wp.Evaluate(d.Generator) - b := wps.Evaluate(d.Generator) - if !a.Equal(&ref.Coefficients()[1]) { - t.Fatal("error evaluation") - } - if !b.Equal(&ref.Coefficients()[1+shift]) { - t.Fatal("error evaluation shifted") - } - - // bit reversed layout - wp.ToBitReverse() - wps.ToBitReverse() - a = wp.Evaluate(d.Generator) - b = wps.Evaluate(d.Generator) - if !a.Equal(&ref.Coefficients()[1]) { - t.Fatal("error evaluation") - } - if !b.Equal(&ref.Coefficients()[1+shift]) { - t.Fatal("error evaluation shifted") - } - -} - -func randomVector(size int) *[]fr.Element { - - r := make([]fr.Element, size) - for i := 0; i < size; i++ { - r[i].SetRandom() - } - return &r -} - -func TestGetCoeff(t *testing.T) { - - size := 8 - v := make([]fr.Element, size) - for i := 0; i < size; i++ { - v[i].SetUint64(uint64(i)) - } - wp := NewPolynomial(&v, Form{Layout: Regular, Basis: Canonical}) - wsp := wp.ShallowClone().Shift(1) - - var aa, bb fr.Element - - // regular layout - for i := 0; i < size; i++ { - - a := wp.GetCoeff(i) - b := wsp.GetCoeff(i) - aa.SetUint64(uint64(i)) - bb.SetUint64(uint64((i + 1) % size)) - if !a.Equal(&aa) { - t.Fatal("error GetCoeff") - } - if !b.Equal(&bb) { - t.Fatal("error GetCoeff") - } - } - - // bit reverse + bitReverse and shifted - wp.ToBitReverse() - wsp.ToBitReverse() - for i := 0; i < size; i++ { - - a := wp.GetCoeff(i) - b := wsp.GetCoeff(i) - aa.SetUint64(uint64(i)) - bb.SetUint64(uint64((i + 1) % size)) - if !a.Equal(&aa) { - t.Fatal("error GetCoeff") - } - if !b.Equal(&bb) { - t.Fatal("error GetCoeff") - } - } - -} - -func TestRoundTrip(t *testing.T) { - assert := require.New(t) - var buf bytes.Buffer - - size := 8 - d := fft.NewDomain(uint64(8)) - blindingOrder := 2 - - p := NewPolynomial(randomVector(size), Form{Basis: Lagrange, Layout: Regular}).ToCanonical(d).ToRegular() - p.Blind(blindingOrder) - - // serialize - written, err := p.WriteTo(&buf) - assert.NoError(err) - - // deserialize - var reconstructed Polynomial - read, err := reconstructed.ReadFrom(&buf) - assert.NoError(err) - - assert.Equal(read, written, "number of bytes written != number of bytes read") - - // compare - assert.Equal(p.Basis, reconstructed.Basis) - assert.Equal(p.Layout, reconstructed.Layout) - assert.Equal(p.shift, reconstructed.shift) - assert.Equal(p.size, reconstructed.size) - assert.Equal(p.blindedSize, reconstructed.blindedSize) - c1, c2 := p.Coefficients(), reconstructed.Coefficients() - assert.True(reflect.DeepEqual(c1, c2)) -} - -func TestBlinding(t *testing.T) { - - size := 8 - d := fft.NewDomain(uint64(8)) - blindingOrder := 2 - - // generate a random polynomial in Lagrange form for the moment - // to check that an error is raised when the polynomial is not - // in canonical form. - wp := NewPolynomial(randomVector(size), Form{Basis: Lagrange, Layout: Regular}) - - // checks the blinding is correct: the evaluation of the blinded polynomial - // should be the same as the original on d's domain - wp.Basis = Canonical - wt := wp.Clone() - wt.Blind(blindingOrder) - if wt.coefficients.Len() != blindingOrder+size+1 { - t.Fatal("size of blinded polynomial is incorrect") - } - if wt.blindedSize != size+blindingOrder+1 { - t.Fatal("Size field of blinded polynomial is incorrect") - } - if wt.size != size { - t.Fatal("the size should not have been modified") - } - x := make([]fr.Element, size) - x[0].SetOne() - for i := 1; i < size; i++ { - x[i].Mul(&x[i-1], &d.Generator) - } - var a, b fr.Element - for i := 0; i < size; i++ { - a = wt.Evaluate(x[i]) - b = wp.Evaluate(x[i]) - if a != b { - t.Fatal("polynomial and its blinded version should be equal on V(X^{n}-1)") - } - } - -} - -// list of functions to turn a polynomial in Lagrange-regular form -// to all different forms in ordered using this encoding: -// int(p.Basis)*4 + int(p.Layout)*2 + int(p.Status) -// p is in Lagrange/Regular here. This function is for testing purpose -// only. -type TransfoTest func(p polynomial, d *fft.Domain) polynomial - -// CANONICAL REGULAR -func fromLagrange0(p *Polynomial, d *fft.Domain) *Polynomial { - r := p.Clone() - r.Basis = Canonical - r.Layout = Regular - d.FFTInverse(r.Coefficients(), fft.DIF) - fft.BitReverse(r.Coefficients()) - return r -} - -// CANONICAL BITREVERSE -func fromLagrange1(p *Polynomial, d *fft.Domain) *Polynomial { - r := p.Clone() - r.Basis = Canonical - r.Layout = BitReverse - d.FFTInverse(r.Coefficients(), fft.DIF) - return r -} - -// LAGRANGE REGULAR -func fromLagrange2(p *Polynomial, d *fft.Domain) *Polynomial { - r := p.Clone() - r.Basis = Lagrange - r.Layout = Regular - return r -} - -// LAGRANGE BITREVERSE -func fromLagrange3(p *Polynomial, d *fft.Domain) *Polynomial { - r := p.Clone() - r.Basis = Lagrange - r.Layout = BitReverse - fft.BitReverse(r.Coefficients()) - return r -} - -// LAGRANGE_COSET REGULAR -func fromLagrange4(p *Polynomial, d *fft.Domain) *Polynomial { - r := p.Clone() - r.Basis = LagrangeCoset - r.Layout = Regular - d.FFTInverse(r.Coefficients(), fft.DIF) - d.FFT(r.Coefficients(), fft.DIT, fft.OnCoset()) - return r -} - -// LAGRANGE_COSET BITREVERSE -func fromLagrange5(p *Polynomial, d *fft.Domain) *Polynomial { - r := p.Clone() - r.Basis = LagrangeCoset - r.Layout = BitReverse - d.FFTInverse(r.Coefficients(), fft.DIF) - d.FFT(r.Coefficients(), fft.DIT, fft.OnCoset()) - fft.BitReverse(r.Coefficients()) - return r -} - -func fromLagrange(p *Polynomial, d *fft.Domain) *Polynomial { - id := p.Form - switch id { - case canonicalRegular: - return fromLagrange0(p, d) - case canonicalBitReverse: - return fromLagrange1(p, d) - case lagrangeRegular: - return fromLagrange2(p, d) - case lagrangeBitReverse: - return fromLagrange3(p, d) - case lagrangeCosetRegular: - return fromLagrange4(p, d) - case lagrangeCosetBitReverse: - return fromLagrange5(p, d) - default: - panic("unknown id") - } -} - -func cmpCoefficents(p, q *fr.Vector) bool { - if p.Len() != q.Len() { - return false - } - for i := 0; i < p.Len(); i++ { - if !((*p)[i].Equal(&(*q)[i])) { - return false - } - } - return true -} - -func TestPutInLagrangeForm(t *testing.T) { - - size := 64 - domain := fft.NewDomain(uint64(size)) - - // reference vector in Lagrange-regular form - c := randomVector(size) - p := NewPolynomial(c, Form{Basis: Canonical, Layout: Regular}) - - // CANONICAL REGULAR - { - _p := fromLagrange(p, domain) - q := _p.Clone() - q.ToLagrange(domain) - if q.Basis != Lagrange { - t.Fatal("expected basis is Lagrange") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is BitReverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // CANONICAL BITREVERSE - { - _p := fromLagrange1(p, domain) - q := _p.Clone() - q.ToLagrange(domain) - if q.Basis != Lagrange { - t.Fatal("expected basis is Lagrange") - } - if q.Layout != Regular { - t.Fatal("expected layout is Regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE REGULAR - { - _p := fromLagrange2(p, domain) - q := _p.Clone() - q.ToLagrange(domain) - - if q.Basis != Lagrange { - t.Fatal("expected basis is Lagrange") - } - if q.Layout != Regular { - t.Fatal("expected layout is Regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE BITREVERSE - { - _p := fromLagrange3(p, domain) - q := _p.Clone() - q.ToLagrange(domain) - if q.Basis != Lagrange { - t.Fatal("expected basis is Lagrange") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is BitReverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE_COSET REGULAR - { - _p := fromLagrange4(p, domain) - q := _p.Clone() - q.ToLagrange(domain) - if q.Basis != Lagrange { - t.Fatal("expected basis is Lagrange") - } - if q.Layout != Regular { - t.Fatal("expected layout is Regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE_COSET BITREVERSE - { - _p := fromLagrange5(p, domain) - q := _p.Clone() - q.ToLagrange(domain) - if q.Basis != Lagrange { - t.Fatal("expected basis is Lagrange") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is BitReverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - -} - -// CANONICAL REGULAR -func fromCanonical0(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Canonical - _p.Layout = Regular - return _p -} - -// CANONICAL BITREVERSE -func fromCanonical1(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Canonical - _p.Layout = BitReverse - return _p -} - -// LAGRANGE REGULAR -func fromCanonical2(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Lagrange - _p.Layout = Regular - d.FFT(_p.Coefficients(), fft.DIF) - fft.BitReverse(_p.Coefficients()) - return _p -} - -// LAGRANGE BITREVERSE -func fromCanonical3(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Lagrange - _p.Layout = BitReverse - d.FFT(_p.Coefficients(), fft.DIF) - return _p -} - -// LAGRANGE_COSET REGULAR -func fromCanonical4(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = LagrangeCoset - _p.Layout = Regular - d.FFT(_p.Coefficients(), fft.DIF, fft.OnCoset()) - fft.BitReverse(_p.Coefficients()) - return _p -} - -// LAGRANGE_COSET BITREVERSE -func fromCanonical5(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = LagrangeCoset - _p.Layout = BitReverse - d.FFT(_p.Coefficients(), fft.DIF, fft.OnCoset()) - return _p -} - -func TestPutInCanonicalForm(t *testing.T) { - - size := 64 - domain := fft.NewDomain(uint64(size)) - - // reference vector in canonical-regular form - c := randomVector(size) - p := NewPolynomial(c, Form{Basis: Canonical, Layout: Regular}) - - // CANONICAL REGULAR - { - _p := fromCanonical0(p, domain) - q := _p.Clone() - q.ToCanonical(domain) - if q.Basis != Canonical { - t.Fatal("expected basis is canonical") - } - if q.Layout != Regular { - t.Fatal("expected layout is regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // CANONICAL BITREVERSE - { - _p := fromCanonical1(p, domain) - q := _p.Clone() - q.ToCanonical(domain) - if q.Basis != Canonical { - t.Fatal("expected basis is canonical") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is bitReverse") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE REGULAR - { - _p := fromCanonical2(p, domain) - q := _p.Clone() - q.ToCanonical(domain) - if q.Basis != Canonical { - t.Fatal("expected basis is canonical") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is bitReverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(p.coefficients, q.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE BITREVERSE - { - _p := fromCanonical3(p, domain) - q := _p.Clone() - q.ToCanonical(domain) - if q.Basis != Canonical { - t.Fatal("expected basis is canonical") - } - if q.Layout != Regular { - t.Fatal("expected layout is regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE_COSET REGULAR - { - _p := fromCanonical4(p, domain) - q := _p.Clone() - q.ToCanonical(domain) - if q.Basis != Canonical { - t.Fatal("expected basis is canonical") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is BitReverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE_COSET BITREVERSE - { - _p := fromCanonical5(p, domain) - q := _p.Clone() - q.ToCanonical(domain) - if q.Basis != Canonical { - t.Fatal("expected basis is canonical") - } - if q.Layout != Regular { - t.Fatal("expected layout is regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - -} - -// CANONICAL REGULAR -func fromLagrangeCoset0(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Canonical - _p.Layout = Regular - d.FFTInverse(_p.Coefficients(), fft.DIF, fft.OnCoset()) - fft.BitReverse(_p.Coefficients()) - return _p -} - -// CANONICAL BITREVERSE -func fromLagrangeCoset1(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Canonical - _p.Layout = BitReverse - d.FFTInverse(_p.Coefficients(), fft.DIF, fft.OnCoset()) - return _p -} - -// LAGRANGE REGULAR -func fromLagrangeCoset2(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Lagrange - _p.Layout = Regular - d.FFTInverse(_p.Coefficients(), fft.DIF, fft.OnCoset()) - d.FFT(_p.Coefficients(), fft.DIT) - return _p -} - -// LAGRANGE BITREVERSE -func fromLagrangeCoset3(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Lagrange - _p.Layout = BitReverse - d.FFTInverse(_p.Coefficients(), fft.DIF, fft.OnCoset()) - d.FFT(_p.Coefficients(), fft.DIT) - fft.BitReverse(_p.Coefficients()) - return _p -} - -// LAGRANGE_COSET REGULAR -func fromLagrangeCoset4(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = LagrangeCoset - _p.Layout = Regular - return _p -} - -// LAGRANGE_COSET BITREVERSE -func fromLagrangeCoset5(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = LagrangeCoset - _p.Layout = BitReverse - fft.BitReverse(p.Coefficients()) - return _p -} - -func TestPutInLagrangeCosetForm(t *testing.T) { - - size := 64 - domain := fft.NewDomain(uint64(size)) - - // reference vector in canonical-regular form - c := randomVector(size) - p := NewPolynomial(c, Form{Basis: LagrangeCoset, Layout: Regular}) - - // CANONICAL REGULAR - { - _p := fromLagrangeCoset0(p, domain) - q := _p.Clone() - q.ToLagrangeCoset(domain) - if q.Basis != LagrangeCoset { - t.Fatal("expected basis is lagrange coset") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is bit reverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // CANONICAL BITREVERSE - { - _p := fromLagrangeCoset1(p, domain) - q := _p.Clone() - q.ToLagrangeCoset(domain) - if q.Basis != LagrangeCoset { - t.Fatal("expected basis is lagrange coset") - } - if q.Layout != Regular { - t.Fatal("expected layout is regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE REGULAR - { - _p := fromLagrangeCoset2(p, domain) - q := _p.Clone() - q.ToLagrangeCoset(domain) - if q.Basis != LagrangeCoset { - t.Fatal("expected basis is lagrange coset") - } - if q.Layout != Regular { - t.Fatal("expected layout is regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE BITREVERSE - { - _p := fromLagrangeCoset3(p, domain) - q := _p.Clone() - q.ToLagrangeCoset(domain) - if q.Basis != LagrangeCoset { - t.Fatal("expected basis is lagrange coset") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is bit reverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE_COSET REGULAR - { - _p := fromLagrangeCoset4(p, domain) - q := _p.Clone() - q.ToLagrangeCoset(domain) - if q.Basis != LagrangeCoset { - t.Fatal("expected basis is lagrange coset") - } - if q.Layout != Regular { - t.Fatal("expected layout is regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE_COSET BITREVERSE - { - _p := fromLagrangeCoset5(p, domain) - q := _p.Clone() - q.ToLagrangeCoset(domain) - if q.Basis != LagrangeCoset { - t.Fatal("expected basis is lagrange coset") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is bit reverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - -} diff --git a/ecc/bls12-378/fr/iop/quotient.go b/ecc/bls12-378/fr/iop/quotient.go deleted file mode 100644 index 49583f4f12..0000000000 --- a/ecc/bls12-378/fr/iop/quotient.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "math/big" - "math/bits" - - "github.com/consensys/gnark-crypto/internal/parallel" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/fft" -) - -// DivideByXMinusOne -// The input must be in LagrangeCoset. -// The result is in Canonical Regular. -func DivideByXMinusOne(a *Polynomial, domains [2]*fft.Domain) (*Polynomial, error) { - - // check that the basis is LagrangeCoset - if a.Basis != LagrangeCoset { - return nil, ErrMustBeLagrangeCoset - } - - // prepare the evaluations of x^n-1 on the big domain's coset - xnMinusOneInverseLagrangeCoset := evaluateXnMinusOneDomainBigCoset(domains) - - rho := a.coefficients.Len() / a.size - - nbElmts := a.coefficients.Len() - - coeffs := make([]fr.Element, a.coefficients.Len()) - res := NewPolynomial(&coeffs, Form{Layout: BitReverse, Basis: LagrangeCoset}) - res.size = a.size - res.blindedSize = a.blindedSize - - nn := uint64(64 - bits.TrailingZeros(uint(nbElmts))) - parallel.Execute(a.coefficients.Len(), func(start, end int) { - for i := start; i < end; i++ { - iRev := bits.Reverse64(uint64(i)) >> nn - c := a.GetCoeff(i) - (*res.coefficients)[iRev]. - Mul(&c, &xnMinusOneInverseLagrangeCoset[i%rho]) - } - }) - - res.ToCanonical(domains[1]) - - return res, nil - -} - -// evaluateXnMinusOneDomainBigCoset evaluates Xᵐ-1 on DomainBig coset -func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { - - ratio := domains[1].Cardinality / domains[0].Cardinality - - res := make([]fr.Element, ratio) - - expo := big.NewInt(int64(domains[0].Cardinality)) - res[0].Exp(domains[1].FrMultiplicativeGen, expo) - - var t fr.Element - t.Exp(domains[1].Generator, big.NewInt(int64(domains[0].Cardinality))) - - one := fr.One() - - for i := 1; i < int(ratio); i++ { - res[i].Mul(&res[i-1], &t) - res[i-1].Sub(&res[i-1], &one) - } - res[len(res)-1].Sub(&res[len(res)-1], &one) - - res = fr.BatchInvert(res) - - return res -} diff --git a/ecc/bls12-378/fr/iop/quotient_test.go b/ecc/bls12-378/fr/iop/quotient_test.go deleted file mode 100644 index 45fd8dd5de..0000000000 --- a/ecc/bls12-378/fr/iop/quotient_test.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/fft" -) - -// computes x₃ in h(x₁,x₂,x₃) = x₁^{2}*x₂ + x₃ - x₁^{3} -// from x₁ and x₂. -func computex3(x []fr.Element) fr.Element { - - var a, b fr.Element - a.Square(&x[0]).Mul(&a, &x[1]) - b.Square(&x[0]).Mul(&b, &x[0]) - a.Sub(&b, &a) - return a - -} - -func buildPoly(size int, form Form) *Polynomial { - v := make([]fr.Element, size) - return NewPolynomial(&v, form) -} - -func TestDivideByXMinusOne(t *testing.T) { - - f := func(_ int, x ...fr.Element) fr.Element { - var a, b fr.Element - a.Square(&x[0]).Mul(&a, &x[1]).Add(&a, &x[2]) - b.Square(&x[0]).Mul(&b, &x[0]) - a.Sub(&a, &b) - return a - } - - // create the multivariate polynomial h - // h(x₁,x₂,x₃) = x₁^{2}*x₂ + x₃ - x₁^{3} - nbEntries := 3 - - // create an instance (f_i) where h holds - sizeSystem := 8 - - form := Form{Basis: Lagrange, Layout: Regular} - - entries := make([]*Polynomial, nbEntries) - entries[0] = buildPoly(sizeSystem, form) - entries[1] = buildPoly(sizeSystem, form) - entries[2] = buildPoly(sizeSystem, form) - - for i := 0; i < sizeSystem; i++ { - - entries[0].Coefficients()[i].SetRandom() - entries[1].Coefficients()[i].SetRandom() - tmp := computex3( - []fr.Element{entries[0].Coefficients()[i], - entries[1].Coefficients()[i]}) - entries[2].Coefficients()[i].Set(&tmp) - - x := []fr.Element{ - entries[0].GetCoeff(i), - entries[1].GetCoeff(i), - entries[2].GetCoeff(i), - } - a := f(0, x...) - if !a.IsZero() { - t.Fatal("system does not vanish on x^n-1") - } - } - - // compute the quotient where the entries are in Regular layout - var domains [2]*fft.Domain - domains[0] = fft.NewDomain(uint64(sizeSystem)) - domains[1] = fft.NewDomain(ecc.NextPowerOfTwo(uint64(3 * sizeSystem))) - - entries[0].ToCanonical(domains[0]). - ToRegular(). - ToLagrangeCoset(domains[1]). - ToRegular() - - entries[1].ToCanonical(domains[0]). - ToRegular(). - ToLagrangeCoset(domains[1]). - ToRegular() - - entries[2].ToCanonical(domains[0]). - ToRegular(). - ToLagrangeCoset(domains[1]). - ToRegular() - - expectedForm := Form{Layout: BitReverse, Basis: LagrangeCoset} - h, err := Evaluate(f, nil, expectedForm, entries...) - if err != nil { - t.Fatal(err) - } - - q, err := DivideByXMinusOne(h, domains) - if err != nil { - t.Fatal(err) - } - - // evaluate the quotient at a random point and check that - // the relation holds. - var x fr.Element - x.SetRandom() - qx := q.Evaluate(x) - entries[0].ToCanonical(domains[1]) - entries[1].ToCanonical(domains[1]) - entries[2].ToCanonical(domains[1]) - ax := entries[0].Evaluate(x) - bx := entries[1].Evaluate(x) - cx := entries[2].Evaluate(x) - hx := f(0, ax, bx, cx) - - var xnminusone, one fr.Element - one.SetOne() - xnminusone.Set(&x). - Square(&xnminusone). - Square(&xnminusone). - Square(&xnminusone). - Sub(&xnminusone, &one) - qx.Mul(&qx, &xnminusone) - if !qx.Equal(&hx) { - t.Fatal("error computing quotient") - } -} diff --git a/ecc/bls12-378/fr/iop/ratios.go b/ecc/bls12-378/fr/iop/ratios.go deleted file mode 100644 index 1eac2992b3..0000000000 --- a/ecc/bls12-378/fr/iop/ratios.go +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "errors" - "math/big" - "math/bits" - "runtime" - "sync" - - "github.com/consensys/gnark-crypto/internal/parallel" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/fft" -) - -// errors related to the computation of the quotient and the ratios. -var ( - ErrMustBeRegular = errors.New("the layout must be Regular") - ErrMustBeCanonical = errors.New("the basis must be Canonical") - ErrMustBeLagrangeCoset = errors.New("the basis must be LagrangeCoset") - ErrInconsistentFormat = errors.New("the format of the polynomials must be the same") - ErrInconsistentSize = errors.New("the sizes of the polynomial must be the same as the size of the domain") - ErrNumberPolynomials = errors.New("the number of polynomials in the denominator and the numerator must be the same") - ErrSizeNotPowerOfTwo = errors.New("the size of the polynomials must be a power of two") - ErrInconsistentSizeDomain = errors.New("the size of the domain must be consistent with the size of the polynomials") - ErrIncorrectNumberOfVariables = errors.New("the number of variables is incorrect") -) - -// Build an 'accumulating ratio' polynomial. -// * numerator list of polynomials that will form the numerator of the ratio -// * denominator list of polynomials that will form the denominator of the ratio -// The polynomials in the denominator and the numerator are expected to be of -// the same size and the size must be a power of 2. The polynomials are given as -// pointers in case the caller wants to FFTInv the polynomials during the process. -// * beta variable at which the numerator and denominators are evaluated -// * expectedForm expected form of the resulting polynomial -// * Return: say beta=β, numerator = [P₁,...,P_m], denominator = [Q₁,..,Q_m]. The function -// returns a polynomial whose evaluation on the j-th root of unity is -// (Π_{k> nn - - for j := 0; j < nbPolynomials; j++ { - - if numerator[j].Layout == BitReverse { - a.Sub(&beta, &numerator[j].Coefficients()[iRev]) - } else { - a.Sub(&beta, &numerator[j].Coefficients()[i]) - } - b.Mul(&b, &a) - - if denominator[j].Layout == BitReverse { - c.Sub(&beta, &denominator[j].Coefficients()[iRev]) - } else { - c.Sub(&beta, &denominator[j].Coefficients()[i]) - } - d.Mul(&d, &c) - } - // b = Πₖ (β-Pₖ(ωⁱ⁻¹)) - // d = Πₖ (β-Qₖ(ωⁱ⁻¹)) - - coeffs[i+1].Mul(&coeffs[i], &b) - t[i+1].Mul(&t[i], &d) - - } - - t = fr.BatchInvert(t) - for i := 1; i < n; i++ { - coeffs[i].Mul(&coeffs[i], &t[i]) - } - - res := NewPolynomial(&coeffs, expectedForm) - - // at this stage the result is in Lagrange form, Regular layout - putInExpectedFormFromLagrangeRegular(res, domain, expectedForm) - - return res, nil -} - -// BuildRatioCopyConstraint builds the accumulating ratio polynomial to prove that -// [P₁ ∥ .. ∥ P_{n—1}] is invariant by the permutation \sigma. -// Namely it returns the polynomial Z whose evaluation on the j-th root of unity is -// Z(ω^j) = Π_{i> nn) - - for j, p := range entries { - idx := i - if p.Layout == BitReverse { - idx = iRev - } - - a.Mul(&beta, &evaluationIDSmallDomain[i+j*n]). - Add(&a, &gamma). - Add(&a, &p.Coefficients()[idx]) - - b.Mul(&b, &a) - - c.Mul(&beta, &evaluationIDSmallDomain[permutation[i+j*n]]). - Add(&c, &gamma). - Add(&c, &p.Coefficients()[idx]) - d.Mul(&d, &c) - } - - // b = Πⱼ(Pⱼ(ωⁱ)+β*ωⁱνʲ+γ) - // d = Πⱼ(Qⱼ(ωⁱ)+β*σ(j*n+i)+γ) - coeffs[i+1].Set(&b) - t[i+1].Set(&d) - } - }) - - chCoeffs := make(chan struct{}, 1) - go func() { - for i := 2; i < n; i++ { - // ignoring coeffs[0] - coeffs[i].Mul(&coeffs[i], &coeffs[i-1]) - } - close(chCoeffs) - }() - - for i := 2; i < n; i++ { - // ignoring t[0] - t[i].Mul(&t[i], &t[i-1]) - } - <-chCoeffs - - // rough ratio inverse to mul; see if it makes sense to parallelize the batch inverse. - const ratioInvMul = 1000 / 17 - nbTasks := runtime.NumCPU() - if ratio := n / ratioInvMul; ratio < nbTasks { - nbTasks = ratio - } - - parallel.Execute(n-1, func(start, end int) { - // ignoring t[0] and coeff[0] - start++ - end++ - tInv := fr.BatchInvert(t[start:end]) - for i := start; i < end; i++ { - coeffs[i].Mul(&coeffs[i], &tInv[i-start]) - } - }, nbTasks) - - res := NewPolynomial(&coeffs, expectedForm) - // at this stage the result is in Lagrange form, Regular layout - putInExpectedFormFromLagrangeRegular(res, domain, expectedForm) - - return res, nil - -} - -func putInExpectedFormFromLagrangeRegular(p *Polynomial, domain *fft.Domain, expectedForm Form) { - p.Basis = expectedForm.Basis - p.Layout = expectedForm.Layout - - if expectedForm.Basis == Canonical { - domain.FFTInverse(p.Coefficients(), fft.DIF) - if expectedForm.Layout == Regular { - fft.BitReverse(p.Coefficients()) - } - return - } - - if expectedForm.Basis == LagrangeCoset { - domain.FFTInverse(p.Coefficients(), fft.DIF) - domain.FFT(p.Coefficients(), fft.DIT, fft.OnCoset()) - if expectedForm.Layout == BitReverse { - fft.BitReverse(p.Coefficients()) - } - return - } - - if expectedForm.Layout == BitReverse { - fft.BitReverse(p.Coefficients()) - } - -} - -// check that the polynomials are of the same size. -// It assumes that pols contains slices of the same size. -func checkSize(pols ...[]*Polynomial) error { - - // check sizes between one another - m := len(pols) - n := pols[0][0].coefficients.Len() - for i := 0; i < m; i++ { - for j := 0; j < len(pols); j++ { - if pols[i][j].coefficients.Len() != n { - return ErrInconsistentSize - } - } - } - - return nil -} - -// buildDomain builds the fft domain necessary to do FFTs. -// n is the cardinality of the domain, it must be a power of 2. -func buildDomain(n int, domain *fft.Domain) (*fft.Domain, error) { - - // check if the sizes are a power of 2 - if n&(n-1) != 0 { - return nil, ErrSizeNotPowerOfTwo - } - - // if the domain doesn't exist we create it. - if domain == nil { - domain = fft.NewDomain(uint64(n)) - } - - // in case domain was not nil, it must match the size of the polynomials. - if domain.Cardinality != uint64(n) { - return nil, ErrInconsistentSizeDomain - } - - return domain, nil -} - -// getSupportIdentityPermutation returns the support on which the permutation acts. -// Concretely it's X evaluated on -// [1,ω,..,ωˢ⁻¹,g,g*ω,..,g*ωˢ⁻¹,..,gⁿ⁻¹,gⁿ⁻¹*ω,..,gⁿ⁻¹*ωˢ⁻¹] -// nbCopies is the number of cosets of the roots of unity that are needed, including the set of -// roots of unity itself. -func getSupportIdentityPermutation(nbCopies int, domain *fft.Domain) []fr.Element { - if nbCopies <= 0 { - panic("getSupportIdentityPermutation: nbCopies must be positive") - } - - res := make([]fr.Element, uint64(nbCopies)*domain.Cardinality) - sizePoly := int(domain.Cardinality) - - // TODO @gbotrel check if we can reuse the pre-computed twiddles from the domain. - res[0].SetOne() - if len(res) > 1 { - res[1].Set(&domain.Generator) - for i := 2; i < len(res); i++ { - res[i].Mul(&res[i-1], &domain.Generator) - } - } - - if nbCopies <= 1 { - return res - } - var wg sync.WaitGroup - wg.Add(nbCopies - 1) - for i := 1; i < nbCopies; i++ { - i := i - - var coset fr.Element - coset.Exp(domain.FrMultiplicativeGen, big.NewInt(int64(i))) - - go func() { - parallel.Execute(sizePoly, func(start, end int) { - for j := start; j < end; j++ { - res[i*sizePoly+j].Mul(&res[j], &coset) - } - }, (runtime.NumCPU()/(nbCopies-1))+1) - wg.Done() - }() - } - - wg.Wait() - - return res -} diff --git a/ecc/bls12-378/fr/iop/ratios_test.go b/ecc/bls12-378/fr/iop/ratios_test.go deleted file mode 100644 index 5c4694841e..0000000000 --- a/ecc/bls12-378/fr/iop/ratios_test.go +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/fft" -) - -// getPermutation returns a deterministic permutation -// of n elements where n is even. The result should be -// interpreted as -// a permutation σ(i)=permutation[i] -// g is a generator of ℤ/nℤ -func getPermutation(n, g int) []int { - - res := make([]int, n) - a := g - for i := 0; i < n; i++ { - res[i] = a - a += g - a %= n - } - return res -} - -func getPermutedPolynomials(sizePolynomials, nbPolynomials int) ([]*Polynomial, []*Polynomial, []int) { - - numerator := make([]*Polynomial, nbPolynomials) - for i := 0; i < nbPolynomials; i++ { - numerator[i] = NewPolynomial(randomVector(sizePolynomials), Form{Basis: Lagrange, Layout: Regular}) - } - - // get permutation - sigma := getPermutation(sizePolynomials*nbPolynomials, 3) - - // the denominator is the permuted version of the numerators - // concatenated - denominator := make([]*Polynomial, nbPolynomials) - for i := 0; i < nbPolynomials; i++ { - denominator[i] = NewPolynomial(randomVector(sizePolynomials), Form{Basis: Lagrange, Layout: Regular}) - } - for i := 0; i < len(sigma); i++ { - id := int(sigma[i] / sizePolynomials) - od := sigma[i] % sizePolynomials - in := int(i / sizePolynomials) - on := i % sizePolynomials - denominator[in].Coefficients()[on].Set(&numerator[id].Coefficients()[od]) - } - - return numerator, denominator, sigma - -} - -func TestBuildRatioShuffledVectors(t *testing.T) { - - // generate random vectors, interpreted in Lagrange form, - // regular layout. It is enough for this test if TestPutInLagrangeForm - // passes. - sizePolynomials := 8 - nbPolynomials := 4 - numerator, denominator, _ := getPermutedPolynomials(sizePolynomials, nbPolynomials) - - // save the originals for further tests with polynomials in different forms - backupNumerator := make([]*Polynomial, nbPolynomials) - backupDenominator := make([]*Polynomial, nbPolynomials) - for i := 0; i < nbPolynomials; i++ { - backupNumerator[i] = numerator[i].Clone() - backupDenominator[i] = denominator[i].Clone() - } - - // build the ratio polynomial - expectedForm := Form{Basis: Lagrange, Layout: Regular} - domain := fft.NewDomain(uint64(sizePolynomials)) - var beta fr.Element - beta.SetRandom() - ratio, err := BuildRatioShuffledVectors(numerator, denominator, beta, expectedForm, domain) - if err != nil { - t.Fatal() - } - - // check that the whole product is equal to one - var a, b, c, d fr.Element - b.SetOne() - d.SetOne() - for i := 0; i < nbPolynomials; i++ { - a.Sub(&beta, &numerator[i].Coefficients()[sizePolynomials-1]) - b.Mul(&a, &b) - c.Sub(&beta, &denominator[i].Coefficients()[sizePolynomials-1]) - d.Mul(&c, &d) - } - a.Mul(&b, &ratio.Coefficients()[sizePolynomials-1]). - Div(&a, &d) - var one fr.Element - one.SetOne() - if !a.Equal(&one) { - t.Fatal("accumulating ratio is not equal to one") - } - - // check that the ratio is correct when the inputs are - // bit reversed - for i := 0; i < nbPolynomials; i++ { - numerator[i] = backupNumerator[i].Clone() - fft.BitReverse(numerator[i].Coefficients()) - numerator[i].Layout = BitReverse - - denominator[i] = backupDenominator[i].Clone() - fft.BitReverse(denominator[i].Coefficients()) - denominator[i].Layout = BitReverse - } - { - var err error - _ratio, err := BuildRatioShuffledVectors(numerator, denominator, beta, expectedForm, domain) - if err != nil { - t.Fatal(err) - } - checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients) - if !checkCoeffs { - t.Fatal(err) - } - } - - // check that the ratio is correct when the inputs are in - // canonical form, regular - for i := 0; i < nbPolynomials; i++ { - numerator[i] = backupNumerator[i].Clone() - domain.FFTInverse(numerator[i].Coefficients(), fft.DIF) - fft.BitReverse(numerator[i].Coefficients()) - numerator[i].Basis = Canonical - - denominator[i] = backupDenominator[i].Clone() - domain.FFTInverse(denominator[i].Coefficients(), fft.DIF) - fft.BitReverse(denominator[i].Coefficients()) - denominator[i].Basis = Canonical - } - { - var err error - _ratio, err := BuildRatioShuffledVectors(numerator, denominator, beta, expectedForm, domain) - if err != nil { - t.Fatal(err) - } - checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients) - if !checkCoeffs { - t.Fatal("coefficients of ratio are not consistent") - } - } - - // check that the ratio is correct when the inputs are in - // canonical form, bit reverse - for i := 0; i < nbPolynomials; i++ { - numerator[i] = backupNumerator[i].Clone() - domain.FFTInverse(numerator[i].Coefficients(), fft.DIF) - numerator[i].Layout = BitReverse - numerator[i].Basis = Canonical - - denominator[i] = backupDenominator[i].Clone() - domain.FFTInverse(denominator[i].Coefficients(), fft.DIF) - denominator[i].Layout = BitReverse - denominator[i].Basis = Canonical - } - { - var err error - _ratio, err := BuildRatioShuffledVectors(numerator, denominator, beta, expectedForm, domain) - if err != nil { - t.Fatal(err) - } - checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients) - if !checkCoeffs { - t.Fatal("coefficients of ratio are not consistent") - } - } - -} - -// sizePolynomial*nbPolynomial must be divisible by 2. -// The function generates a list of nbPolynomials (P_i) of size n=sizePolynomials -// such that [P₁ ∥ .. ∥ P₂ ] is invariant under the permutation -// σ defined by: -// σ = (12)(34)..(2n-1 2n) -// so σ is a product of cycles length 2. -func getInvariantEntriesUnderPermutation(sizePolynomials, nbPolynomials int) ([]*Polynomial, []int64) { - res := make([]*Polynomial, nbPolynomials) - form := Form{Layout: Regular, Basis: Lagrange} - for i := 0; i < nbPolynomials; i++ { - v := make([]fr.Element, sizePolynomials) - res[i] = NewPolynomial(&v, form) - for j := 0; j < sizePolynomials/2; j++ { - res[i].Coefficients()[2*j].SetRandom() - res[i].Coefficients()[2*j+1].Set(&res[i].Coefficients()[2*j]) - } - } - permutation := make([]int64, nbPolynomials*sizePolynomials) - for i := int64(0); i < int64(nbPolynomials*sizePolynomials/2); i++ { - permutation[2*i] = 2*i + 1 - permutation[2*i+1] = 2 * i - } - return res, permutation -} - -func TestBuildRatioCopyConstraint(t *testing.T) { - - // generate random vectors, interpreted in Lagrange form, - // regular layout. It is enough for this test if TestPutInLagrangeForm - // passes. - sizePolynomials := 8 - nbPolynomials := 4 - entries, sigma := getInvariantEntriesUnderPermutation(sizePolynomials, nbPolynomials) - - // save the originals for further tests with polynomials in different forms - backupEntries := make([]*Polynomial, nbPolynomials) - for i := 0; i < nbPolynomials; i++ { - backupEntries[i] = entries[i].Clone() - } - - // build the ratio polynomial - expectedForm := Form{Basis: Lagrange, Layout: Regular} - domain := fft.NewDomain(uint64(sizePolynomials)) - var beta, gamma fr.Element - beta.SetRandom() - gamma.SetRandom() - ratio, err := BuildRatioCopyConstraint(entries, sigma, beta, gamma, expectedForm, domain) - if err != nil { - t.Fatal() - } - - // check that the whole product is equal to one - suppID := getSupportIdentityPermutation(nbPolynomials, domain) - var a, b, c, d fr.Element - b.SetOne() - d.SetOne() - for i := 0; i < nbPolynomials; i++ { - a.Mul(&beta, &suppID[(i+1)*sizePolynomials-1]). - Add(&a, &entries[i].Coefficients()[sizePolynomials-1]). - Add(&a, &gamma) - b.Mul(&b, &a) - - c.Mul(&beta, &suppID[sigma[(i+1)*sizePolynomials-1]]). - Add(&c, &entries[i].Coefficients()[sizePolynomials-1]). - Add(&c, &gamma) - d.Mul(&d, &c) - } - a.Mul(&b, &ratio.Coefficients()[sizePolynomials-1]). - Div(&a, &d) - var one fr.Element - one.SetOne() - if !a.Equal(&one) { - t.Fatal("accumulating ratio is not equal to one") - } - - // check that the ratio is correct when the inputs are - // bit reversed - for i := 0; i < nbPolynomials; i++ { - entries[i] = backupEntries[i].Clone() - fft.BitReverse(entries[i].Coefficients()) - entries[i].Layout = BitReverse - } - { - var err error - _ratio, err := BuildRatioCopyConstraint(entries, sigma, beta, gamma, expectedForm, domain) - if err != nil { - t.Fatal(err) - } - checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients) - if !checkCoeffs { - t.Fatal(err) - } - } - - // check that the ratio is correct when the inputs are in - // canonical form, regular - for i := 0; i < nbPolynomials; i++ { - entries[i] = backupEntries[i].Clone() - domain.FFTInverse(entries[i].Coefficients(), fft.DIF) - fft.BitReverse(entries[i].Coefficients()) - entries[i].Layout = Regular - entries[i].Basis = Canonical - } - { - var err error - _ratio, err := BuildRatioCopyConstraint(entries, sigma, beta, gamma, expectedForm, domain) - if err != nil { - t.Fatal(err) - } - checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients) - if !checkCoeffs { - t.Fatal("coefficients of ratio are not consistent") - } - } - - // check that the ratio is correct when the inputs are in - // canonical form, bit reverse - for i := 0; i < nbPolynomials; i++ { - entries[i] = backupEntries[i].Clone() - domain.FFTInverse(entries[i].Coefficients(), fft.DIF) - entries[i].Layout = BitReverse - entries[i].Basis = Canonical - } - - { - var err error - _ratio, err := BuildRatioCopyConstraint(entries, sigma, beta, gamma, expectedForm, domain) - if err != nil { - t.Fatal(err) - } - checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients) - if !checkCoeffs { - t.Fatal("coefficients of ratio are not consistent") - } - } -} diff --git a/ecc/bls12-378/fr/iop/utils.go b/ecc/bls12-378/fr/iop/utils.go deleted file mode 100644 index b3dce97a06..0000000000 --- a/ecc/bls12-378/fr/iop/utils.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" -) - -//---------------------------------------------------- -// exp functions until 5 - -func exp0(x fr.Element) fr.Element { - var res fr.Element - res.SetOne() - return res -} - -func exp1(x fr.Element) fr.Element { - return x -} - -func exp2(x fr.Element) fr.Element { - return *x.Square(&x) -} - -func exp3(x fr.Element) fr.Element { - var res fr.Element - res.Square(&x).Mul(&res, &x) - return res -} - -func exp4(x fr.Element) fr.Element { - x.Square(&x).Square(&x) - return x -} - -func exp5(x fr.Element) fr.Element { - var res fr.Element - res.Square(&x).Square(&res).Mul(&res, &x) - return res -} - -// doesn't return any errors, it is a private method, that -// is assumed to be called with correct arguments. -func smallExp(x fr.Element, n int) fr.Element { - if n == 0 { - return exp0(x) - } - if n == 1 { - return exp1(x) - } - if n == 2 { - return exp2(x) - } - if n == 3 { - return exp3(x) - } - if n == 4 { - return exp4(x) - } - if n == 5 { - return exp5(x) - } - return fr.Element{} -} diff --git a/ecc/bls12-378/fr/mimc/doc.go b/ecc/bls12-378/fr/mimc/doc.go deleted file mode 100644 index 78837e1c80..0000000000 --- a/ecc/bls12-378/fr/mimc/doc.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package mimc provides MiMC hash function using Miyaguchi–Preneel construction. -// -// # Length extension attack -// -// The MiMC hash function is vulnerable to a length extension attack. For -// example when we have a hash -// -// h = MiMC(k || m) -// -// and we want to hash a new message -// -// m' = m || m2, -// -// we can compute -// -// h' = MiMC(k || m || m2) -// -// without knowing k by computing -// -// h' = MiMC(h || m2). -// -// This is because the MiMC hash function is a simple iterated cipher, and the -// hash value is the state of the cipher after encrypting the message. -// -// There are several ways to mitigate this attack: -// - use a random key for each hash -// - use a domain separation tag for different use cases: -// h = MiMC(k || tag || m) -// - use the secret input as last input: -// h = MiMC(m || k) -// -// In general, inside a circuit the length-extension attack is not a concern as -// due to the circuit definition the attacker can not append messages to -// existing hash. But the user has to consider the cases when using a secret key -// and MiMC in different contexts. -// -// # Hash input format -// -// The MiMC hash function is defined over a field. The input to the hash -// function is a byte slice. The byte slice is interpreted as a sequence of -// field elements. Due to this interpretation, the input byte slice length must -// be multiple of the field modulus size. And every secuence of byte slice for a -// single field element must be strictly less than the field modulus. -package mimc diff --git a/ecc/bls12-378/fr/mimc/mimc.go b/ecc/bls12-378/fr/mimc/mimc.go deleted file mode 100644 index c351124ae4..0000000000 --- a/ecc/bls12-378/fr/mimc/mimc.go +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package mimc - -import ( - "errors" - "hash" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "golang.org/x/crypto/sha3" - "math/big" - "sync" -) - -const ( - mimcNbRounds = 109 - seed = "seed" // seed to derive the constants - BlockSize = fr.Bytes // BlockSize size that mimc consumes -) - -// Params constants for the mimc hash function -var ( - mimcConstants [mimcNbRounds]fr.Element - once sync.Once -) - -// digest represents the partial evaluation of the checksum -// along with the params of the mimc function -type digest struct { - h fr.Element - data []fr.Element // data to hash - byteOrder fr.ByteOrder -} - -// GetConstants exposed to be used in gnark -func GetConstants() []big.Int { - once.Do(initConstants) // init constants - res := make([]big.Int, mimcNbRounds) - for i := 0; i < mimcNbRounds; i++ { - mimcConstants[i].BigInt(&res[i]) - } - return res -} - -// NewMiMC returns a MiMCImpl object, pure-go reference implementation -func NewMiMC(opts ...Option) hash.Hash { - d := new(digest) - d.Reset() - cfg := mimcOptions(opts...) - d.byteOrder = cfg.byteOrder - return d -} - -// Reset resets the Hash to its initial state. -func (d *digest) Reset() { - d.data = d.data[:0] - d.h = fr.Element{0, 0, 0, 0} -} - -// Sum appends the current hash to b and returns the resulting slice. -// It does not change the underlying hash state. -func (d *digest) Sum(b []byte) []byte { - buffer := d.checksum() - d.data = nil // flush the data already hashed - hash := buffer.Bytes() - b = append(b, hash[:]...) - return b -} - -// BlockSize returns the hash's underlying block size. -// The Write method must be able to accept any amount -// of data, but it may operate more efficiently if all writes -// are a multiple of the block size. -func (d *digest) Size() int { - return BlockSize -} - -// BlockSize returns the number of bytes Sum will return. -func (d *digest) BlockSize() int { - return BlockSize -} - -// Write (via the embedded io.Writer interface) adds more data to the running hash. -// -// Each []byte block of size BlockSize represents a big endian fr.Element. -// -// If len(p) is not a multiple of BlockSize and any of the []byte in p represent an integer -// larger than fr.Modulus, this function returns an error. -// -// To hash arbitrary data ([]byte not representing canonical field elements) use fr.Hash first -func (d *digest) Write(p []byte) (int, error) { - // we usually expect multiple of block size. But sometimes we hash short - // values (FS transcript). Instead of forcing to hash to field, we left-pad the - // input here. - if len(p) > 0 && len(p) < BlockSize { - pp := make([]byte, BlockSize) - copy(pp[len(pp)-len(p):], p) - p = pp - } - - var start int - for start = 0; start < len(p); start += BlockSize { - if elem, err := d.byteOrder.Element((*[BlockSize]byte)(p[start : start+BlockSize])); err == nil { - d.data = append(d.data, elem) - } else { - return 0, err - } - } - - if start != len(p) { - return 0, errors.New("invalid input length: must represent a list of field elements, expects a []byte of len m*BlockSize") - } - return len(p), nil -} - -// Hash hash using Miyaguchi-Preneel: -// https://en.wikipedia.org/wiki/One-way_compression_function -// The XOR operation is replaced by field addition, data is in Montgomery form -func (d *digest) checksum() fr.Element { - // Write guarantees len(data) % BlockSize == 0 - - // TODO @ThomasPiellard shouldn't Sum() returns an error if there is no data? - // TODO: @Tabaie, @Thomas Piellard Now sure what to make of this - /*if len(d.data) == 0 { - d.data = make([]byte, BlockSize) - }*/ - - for i := range d.data { - r := d.encrypt(d.data[i]) - d.h.Add(&r, &d.h).Add(&d.h, &d.data[i]) - } - - return d.h -} - -// plain execution of a mimc run -// m: message -// k: encryption key -func (d *digest) encrypt(m fr.Element) fr.Element { - once.Do(initConstants) // init constants - - var tmp fr.Element - for i := 0; i < mimcNbRounds; i++ { - // m = (m+k+c)^5 - tmp.Add(&m, &d.h).Add(&tmp, &mimcConstants[i]) - m.Square(&tmp). - Square(&m). - Mul(&m, &tmp) - } - m.Add(&m, &d.h) - return m -} - -// Sum computes the mimc hash of msg from seed -func Sum(msg []byte) ([]byte, error) { - var d digest - if _, err := d.Write(msg); err != nil { - return nil, err - } - h := d.checksum() - bytes := h.Bytes() - return bytes[:], nil -} - -func initConstants() { - bseed := ([]byte)(seed) - - hash := sha3.NewLegacyKeccak256() - _, _ = hash.Write(bseed) - rnd := hash.Sum(nil) // pre hash before use - hash.Reset() - _, _ = hash.Write(rnd) - - for i := 0; i < mimcNbRounds; i++ { - rnd = hash.Sum(nil) - mimcConstants[i].SetBytes(rnd) - hash.Reset() - _, _ = hash.Write(rnd) - } -} - -// WriteString writes a string that doesn't necessarily consist of field elements -func (d *digest) WriteString(rawBytes []byte) error { - if elems, err := fr.Hash(rawBytes, []byte("string:"), 1); err != nil { - return err - } else { - d.data = append(d.data, elems[0]) - } - return nil -} diff --git a/ecc/bls12-378/fr/mimc/options.go b/ecc/bls12-378/fr/mimc/options.go deleted file mode 100644 index c5ae735e15..0000000000 --- a/ecc/bls12-378/fr/mimc/options.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package mimc - -import ( - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" -) - -// Option defines option for altering the behavior of the MiMC hasher. -// See the descriptions of functions returning instances of this type for -// particular options. -type Option func(*mimcConfig) - -type mimcConfig struct { - byteOrder fr.ByteOrder -} - -// default options -func mimcOptions(opts ...Option) mimcConfig { - // apply options - opt := mimcConfig{ - byteOrder: fr.BigEndian, - } - for _, option := range opts { - option(&opt) - } - return opt -} - -// WithByteOrder sets the byte order used to decode the input -// in the Write method. Default is BigEndian. -func WithByteOrder(byteOrder fr.ByteOrder) Option { - return func(opt *mimcConfig) { - opt.byteOrder = byteOrder - } -} diff --git a/ecc/bls12-378/fr/pedersen/doc.go b/ecc/bls12-378/fr/pedersen/doc.go deleted file mode 100644 index 944679f5c5..0000000000 --- a/ecc/bls12-378/fr/pedersen/doc.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package pedersen allows to compute and verify Pedersen vector commitments -// -// Pedersen vector commitments are a type of homomorphic commitments that allow -// to commit to a vector of values and prove knowledge of the committed values. -// The commitments can be batched and verified in a single operation. -// -// The commitments are computed using a set of basis elements. The proving key -// contains the basis elements and their exponentiations by a random value. The -// verifying key contains the G2 generator and its exponentiation by the inverse -// of the random value. -// -// The setup process is a trusted setup and must be done securely, preferably using MPC. -// After the setup, the proving key does not have to be secret, but the randomness -// used during the setup must be discarded. -package pedersen diff --git a/ecc/bls12-378/fr/pedersen/example_test.go b/ecc/bls12-378/fr/pedersen/example_test.go deleted file mode 100644 index 056357859e..0000000000 --- a/ecc/bls12-378/fr/pedersen/example_test.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package pedersen - -import ( - "crypto/rand" - "fmt" - - "github.com/consensys/gnark-crypto/ecc" - curve "github.com/consensys/gnark-crypto/ecc/bls12-378" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" -) - -// This example demonstrates how to use the Pedersen commitment scheme -// to commit to a set of values and prove knowledge of the committed values. -// -// Does not perform any batching or multi-proof optimization. -func Example_singleProof() { - const nbElem = 4 - // create a proving key with independent basis elements - var buf [32]byte - basis := make([]curve.G1Affine, nbElem) - for i := range basis { - _, err := rand.Read(buf[:]) - if err != nil { - panic(err) - } - // we use hash-to-curve to avoid linear dependencies between basis elements - basis[i], err = curve.HashToG1(buf[:], []byte(fmt.Sprintf("basis %d", i))) - if err != nil { - panic(err) - } - } - // create a proving and verifying key. NB! Must be done using MPC - pks, vk, err := Setup([][]curve.G1Affine{basis}) - if err != nil { - panic(err) - } - // currently we only have a single proving key - pk := pks[0] - toCommit := make([]fr.Element, nbElem) - for i := range toCommit { - toCommit[i].SetRandom() - } - // commit to the values - commitment, err := pk.Commit(toCommit) - if err != nil { - panic(err) - } - // prove knowledge of the committed values - pok, err := pk.ProveKnowledge(toCommit) - if err != nil { - panic(err) - } - // verify the proof - if err := vk.Verify(commitment, pok); err != nil { - panic(err) - } - - fmt.Println("verified") - // output: verified -} - -// This example shows how to batch the commitment and proof generation. -func ExampleBatchProve() { - const nbPks = 3 - const nbElem = 4 - // create a proving key with independent basis elements - var buf [32]byte - basis := make([][]curve.G1Affine, nbPks) - for i := range basis { - basis[i] = make([]curve.G1Affine, nbElem) - for j := range basis[i] { - _, err := rand.Read(buf[:]) - if err != nil { - panic(err) - } - // we use hash-to-curve to avoid linear dependencies between basis elements - basis[i][j], err = curve.HashToG1(buf[:], []byte(fmt.Sprintf("basis %d", i))) - if err != nil { - panic(err) - } - } - } - // create a proving and verifying key. NB! Must be done using MPC - pks, vk, err := Setup(basis) - if err != nil { - panic(err) - } - // generate random values to commit to - toCommit := make([][]fr.Element, nbPks) - for i := range toCommit { - toCommit[i] = make([]fr.Element, nbElem) - for j := range toCommit[i] { - toCommit[i][j].SetRandom() - } - } - // commit to the values - commitments := make([]curve.G1Affine, nbPks) - for i := range commitments { - commitments[i], err = pks[i].Commit(toCommit[i]) - if err != nil { - panic(err) - } - } - // combination coefficient is randomly sampled by the verifier. NB! In non-interactive protocol use Fiat-Shamir! - var combinationCoeff fr.Element - combinationCoeff.SetRandom() - proof, err := BatchProve(pks, toCommit, combinationCoeff) - if err != nil { - panic(err) - } - // fold the commitments - foldedCommitment, err := new(curve.G1Affine).Fold(commitments, combinationCoeff, ecc.MultiExpConfig{NbTasks: 1}) - if err != nil { - panic(err) - } - // verify the proof - if err := vk.Verify(*foldedCommitment, proof); err != nil { - panic(err) - } - fmt.Println("verified") - - // Output: verified -} - -// This example shows how to batch verify multiple proofs using multiple -// verifying keys. -func ExampleBatchVerifyMultiVk() { - const nbPks = 3 - const nbElem = 4 - // create a proving key with independent basis elements - var buf [32]byte - basis := make([][]curve.G1Affine, nbPks) - for i := range basis { - basis[i] = make([]curve.G1Affine, nbElem) - for j := range basis[i] { - _, err := rand.Read(buf[:]) - if err != nil { - panic(err) - } - // we use hash-to-curve to avoid linear dependencies between basis elements - basis[i][j], err = curve.HashToG1(buf[:], []byte(fmt.Sprintf("basis %d", i))) - if err != nil { - panic(err) - } - } - } - // we create independent proving keys (different sigmas) with same G2 - // g2Point does not have to be generated in a trusted manner - _, _, _, g2Point := curve.Generators() - pks := make([]ProvingKey, nbPks) - vks := make([]VerifyingKey, nbPks) - for i := range basis { - pkss, vkss, err := Setup(basis[i:i+1], WithG2Point(g2Point)) - if err != nil { - panic(err) - } - pks[i] = pkss[0] - vks[i] = vkss - } - // generate random values to commit to - toCommit := make([][]fr.Element, nbPks) - for i := range toCommit { - toCommit[i] = make([]fr.Element, nbElem) - for j := range toCommit[i] { - toCommit[i][j].SetRandom() - } - } - // commit to the values - commitments := make([]curve.G1Affine, nbPks) - for i := range commitments { - var err error - commitments[i], err = pks[i].Commit(toCommit[i]) - if err != nil { - panic(err) - } - } - // prove the commitments - proofs := make([]curve.G1Affine, nbPks) - for i := range proofs { - var err error - proofs[i], err = pks[i].ProveKnowledge(toCommit[i]) - if err != nil { - panic(err) - } - } - // combination coefficient is randomly sampled by the verifier. NB! In non-interactive protocol use Fiat-Shamir! - var combinationCoeff fr.Element - combinationCoeff.SetRandom() - // batch verify the proofs - if err := BatchVerifyMultiVk(vks, commitments, proofs, combinationCoeff); err != nil { - panic(err) - } - - // alternatively, we can also provide the folded proof - foldedProof, err := new(curve.G1Affine).Fold(proofs, combinationCoeff, ecc.MultiExpConfig{NbTasks: 1}) - if err != nil { - panic(err) - } - if err := BatchVerifyMultiVk(vks, commitments, []curve.G1Affine{*foldedProof}, combinationCoeff); err != nil { - panic(err) - } - - fmt.Println("verified") - // Output: verified -} diff --git a/ecc/bls12-378/fr/pedersen/pedersen.go b/ecc/bls12-378/fr/pedersen/pedersen.go deleted file mode 100644 index 5b2a3e5835..0000000000 --- a/ecc/bls12-378/fr/pedersen/pedersen.go +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package pedersen - -import ( - "crypto/rand" - "errors" - "github.com/consensys/gnark-crypto/ecc" - curve "github.com/consensys/gnark-crypto/ecc/bls12-378" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "io" - "math/big" -) - -// ProvingKey for committing and proofs of knowledge -type ProvingKey struct { - Basis []curve.G1Affine - BasisExpSigma []curve.G1Affine // basisExpSigma[i] = Basis[i]^{σ} -} - -type VerifyingKey struct { - G curve.G2Affine - GSigma curve.G2Affine // GRootSigmaNeg = G^{-σ} -} - -func randomFrSizedBytes() ([]byte, error) { - res := make([]byte, fr.Bytes) - _, err := rand.Read(res) - return res, err -} - -type setupConfig struct { - g2Gen *curve.G2Affine -} - -// SetupOption allows to customize Pedersen vector commitment setup. -type SetupOption func(cfg *setupConfig) - -// WithG2Point allows to set the G2 generator for the Pedersen vector commitment -// setup. If this is not set, we sample a random G2 point. -func WithG2Point(g2 curve.G2Affine) SetupOption { - return func(cfg *setupConfig) { - cfg.g2Gen = &g2 - } -} - -// Setup generates the proving keys for Pedersen commitments over the given -// bases allowing for batch proving. The common verifying key can be used to -// verify the batched proof of knowledge. -// -// By default the G2 generator is sampled randomly. This can be overridden by -// providing a custom G2 generator using [WithG2Point] option. -// -// The input bases do not have to be of the same length for individual -// committing and proving. The elements in bases[i] should be linearly -// independent of each other. Otherwise the prover may be able to construct -// multiple valid openings for a commitment. -// -// NB! This is a trusted setup process. The randomness during the setup must be discarded. -// Failing to do so allows to create proofs without knowing the committed values. -func Setup(bases [][]curve.G1Affine, options ...SetupOption) (pk []ProvingKey, vk VerifyingKey, err error) { - var cfg setupConfig - for _, o := range options { - o(&cfg) - } - if cfg.g2Gen == nil { - if vk.G, err = curve.RandomOnG2(); err != nil { - return - } - } else { - vk.G = *cfg.g2Gen - } - - var modMinusOne big.Int - modMinusOne.Sub(fr.Modulus(), big.NewInt(1)) - var sigma *big.Int - if sigma, err = rand.Int(rand.Reader, &modMinusOne); err != nil { - return - } - sigma.Add(sigma, big.NewInt(1)) - - sigmaNeg := new(big.Int).Neg(sigma) - vk.GSigma.ScalarMultiplication(&vk.G, sigmaNeg) - - pk = make([]ProvingKey, len(bases)) - for i := range bases { - pk[i].BasisExpSigma = make([]curve.G1Affine, len(bases[i])) - for j := range bases[i] { - pk[i].BasisExpSigma[j].ScalarMultiplication(&bases[i][j], sigma) - } - pk[i].Basis = bases[i] - } - return -} - -// ProveKnowledge generates a proof of knowledge of a commitment to the given -// values over proving key's basis. -func (pk *ProvingKey) ProveKnowledge(values []fr.Element) (pok curve.G1Affine, err error) { - if len(values) != len(pk.Basis) { - err = errors.New("must have as many values as basis elements") - return - } - - // TODO @gbotrel this will spawn more than one task, see - // https://github.com/ConsenSys/gnark-crypto/issues/269 - config := ecc.MultiExpConfig{ - NbTasks: 1, // TODO Experiment - } - - _, err = pok.MultiExp(pk.BasisExpSigma, values, config) - return -} - -// Commit computes a commitment to the values over proving key's basis -func (pk *ProvingKey) Commit(values []fr.Element) (commitment curve.G1Affine, err error) { - - if len(values) != len(pk.Basis) { - err = errors.New("must have as many values as basis elements") - return - } - - // TODO @gbotrel this will spawn more than one task, see - // https://github.com/ConsenSys/gnark-crypto/issues/269 - config := ecc.MultiExpConfig{ - NbTasks: 1, - } - _, err = commitment.MultiExp(pk.Basis, values, config) - - return -} - -// BatchProve computes a single proof of knowledge for multiple commitments. The -// single PoK can be verified with a single call to [VerifyingKey.Verify] with -// folded commitments. The commitments can be folded into one using [curve.G1Affine.Fold]. -// -// The argument combinationCoeff is used as a linear combination coefficient to -// fold separate proofs into one. It must be the same for batch proving and when -// folding commitments. This means that in an interactive setting, it must be -// randomly generated by the verifier and sent to the prover. Otherwise, it must -// be generated via Fiat-Shamir. -func BatchProve(pk []ProvingKey, values [][]fr.Element, combinationCoeff fr.Element) (pok curve.G1Affine, err error) { - if len(pk) != len(values) { - err = errors.New("must have as many value vectors as bases") - return - } - - if len(pk) == 1 { // no need to fold - pok, err = pk[0].ProveKnowledge(values[0]) - return - } else if len(pk) == 0 { // nothing to do at all - return - } - - offset := 0 - for i := range pk { - if len(values[i]) != len(pk[i].Basis) { - err = errors.New("must have as many values as basis elements") - return - } - offset += len(values[i]) - } - - // prepare one amalgamated MSM - scaledValues := make([]fr.Element, offset) - basis := make([]curve.G1Affine, offset) - - copy(basis, pk[0].BasisExpSigma) // #nosec G602 false positive - copy(scaledValues, values[0]) // #nosec G602 false positive - - offset = len(values[0]) // #nosec G602 false positive - rI := combinationCoeff - for i := 1; i < len(pk); i++ { - copy(basis[offset:], pk[i].BasisExpSigma) - for j := range pk[i].Basis { - scaledValues[offset].Mul(&values[i][j], &rI) - offset++ - } - if i+1 < len(pk) { - rI.Mul(&rI, &combinationCoeff) - } - } - - // TODO @gbotrel this will spawn more than one task, see - // https://github.com/ConsenSys/gnark-crypto/issues/269 - config := ecc.MultiExpConfig{ - NbTasks: 1, - } - - _, err = pok.MultiExp(basis, scaledValues, config) - return -} - -// Verify checks if the proof of knowledge is valid for a given commitment. -func (vk *VerifyingKey) Verify(commitment curve.G1Affine, knowledgeProof curve.G1Affine) error { - - if !commitment.IsInSubGroup() || !knowledgeProof.IsInSubGroup() { - return errors.New("subgroup check failed") - } - - if isOne, err := curve.PairingCheck([]curve.G1Affine{commitment, knowledgeProof}, []curve.G2Affine{vk.GSigma, vk.G}); err != nil { - return err - } else if !isOne { - return errors.New("proof rejected") - } - return nil -} - -// BatchVerifyMultiVk verifies multiple separate proofs of knowledge using n+1 -// pairings instead of 2n pairings. -// -// The verifying keys may be from different setup ceremonies, but the G2 point -// must be the same. This can be enforced using [WithG2Point] option during -// setup. -// -// The argument combinationCoeff is used as a linear combination coefficient to -// fold separate proofs into one. This means that in an interactive setting, it -// must be randomly generated by the verifier and sent to the prover. Otherwise, -// it must be generated via Fiat-Shamir. -// -// The prover can fold the proofs using [curve.G1Affine.Fold] itself using the -// random challenge, providing the verifier only the folded proof. In this case -// the argument pok should contain only the single folded proof. -func BatchVerifyMultiVk(vk []VerifyingKey, commitments []curve.G1Affine, pok []curve.G1Affine, combinationCoeff fr.Element) error { - if len(commitments) != len(vk) { - return errors.New("commitments length mismatch") - } - // we use folded POK if provided - if len(vk) != len(pok) && len(pok) != 1 { - return errors.New("pok length mismatch") - } - for i := range commitments { - if !commitments[i].IsInSubGroup() { - return errors.New("commitment subgroup check failed") - } - if i != 0 && vk[i].G != vk[0].G { - return errors.New("parameter mismatch: G2 element") - } - } - for i := range pok { - if !pok[i].IsInSubGroup() { - return errors.New("pok subgroup check failed") - } - } - - pairingG1 := make([]curve.G1Affine, len(vk)+1) - pairingG2 := make([]curve.G2Affine, len(vk)+1) - r := combinationCoeff - pairingG1[0] = commitments[0] - var rI big.Int - for i := range vk { - pairingG2[i] = vk[i].GSigma - if i != 0 { - r.BigInt(&rI) - pairingG1[i].ScalarMultiplication(&commitments[i], &rI) - if i+1 != len(vk) { - r.Mul(&r, &combinationCoeff) - } - } - } - if foldedPok, err := new(curve.G1Affine).Fold(pok, combinationCoeff, ecc.MultiExpConfig{NbTasks: 1}); err != nil { - return err - } else { - pairingG1[len(vk)] = *foldedPok - } - pairingG2[len(vk)] = vk[0].G - - if isOne, err := curve.PairingCheck(pairingG1, pairingG2); err != nil { - return err - } else if !isOne { - return errors.New("proof rejected") - } - return nil -} - -// Marshal - -func (pk *ProvingKey) writeTo(enc *curve.Encoder) (int64, error) { - if err := enc.Encode(pk.Basis); err != nil { - return enc.BytesWritten(), err - } - - err := enc.Encode(pk.BasisExpSigma) - - return enc.BytesWritten(), err -} - -func (pk *ProvingKey) WriteTo(w io.Writer) (int64, error) { - return pk.writeTo(curve.NewEncoder(w)) -} - -func (pk *ProvingKey) WriteRawTo(w io.Writer) (int64, error) { - return pk.writeTo(curve.NewEncoder(w, curve.RawEncoding())) -} - -func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { - dec := curve.NewDecoder(r) - - if err := dec.Decode(&pk.Basis); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&pk.BasisExpSigma); err != nil { - return dec.BytesRead(), err - } - - if len(pk.Basis) != len(pk.BasisExpSigma) { - return dec.BytesRead(), errors.New("commitment/proof length mismatch") - } - - return dec.BytesRead(), nil -} - -func (vk *VerifyingKey) WriteTo(w io.Writer) (int64, error) { - return vk.writeTo(curve.NewEncoder(w)) -} - -func (vk *VerifyingKey) WriteRawTo(w io.Writer) (int64, error) { - return vk.writeTo(curve.NewEncoder(w, curve.RawEncoding())) -} - -func (vk *VerifyingKey) writeTo(enc *curve.Encoder) (int64, error) { - var err error - - if err = enc.Encode(&vk.G); err != nil { - return enc.BytesWritten(), err - } - err = enc.Encode(&vk.GSigma) - return enc.BytesWritten(), err -} - -func (vk *VerifyingKey) ReadFrom(r io.Reader) (int64, error) { - return vk.readFrom(r) -} - -func (vk *VerifyingKey) UnsafeReadFrom(r io.Reader) (int64, error) { - return vk.readFrom(r, curve.NoSubgroupChecks()) -} - -func (vk *VerifyingKey) readFrom(r io.Reader, decOptions ...func(*curve.Decoder)) (int64, error) { - dec := curve.NewDecoder(r, decOptions...) - var err error - - if err = dec.Decode(&vk.G); err != nil { - return dec.BytesRead(), err - } - err = dec.Decode(&vk.GSigma) - return dec.BytesRead(), err -} diff --git a/ecc/bls12-378/fr/pedersen/pedersen_test.go b/ecc/bls12-378/fr/pedersen/pedersen_test.go deleted file mode 100644 index be19b0065d..0000000000 --- a/ecc/bls12-378/fr/pedersen/pedersen_test.go +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package pedersen - -import ( - "fmt" - "testing" - - "github.com/consensys/gnark-crypto/ecc" - curve "github.com/consensys/gnark-crypto/ecc/bls12-378" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/utils/testutils" - "github.com/stretchr/testify/assert" -) - -func interfaceSliceToFrSlice(t *testing.T, values ...interface{}) []fr.Element { - res := make([]fr.Element, len(values)) - for i, v := range values { - _, err := res[i].SetInterface(v) - assert.NoError(t, err) - } - return res -} - -func randomFrSlice(t *testing.T, size int) []interface{} { - res := make([]interface{}, size) - var err error - for i := range res { - var v fr.Element - res[i], err = v.SetRandom() - assert.NoError(t, err) - } - return res -} - -func randomOnG1() (curve.G1Affine, error) { // TODO: Add to G1.go? - if gBytes, err := randomFrSizedBytes(); err != nil { - return curve.G1Affine{}, err - } else { - return curve.HashToG1(gBytes, []byte("random on g1")) - } -} - -func randomG1Slice(t *testing.T, size int) []curve.G1Affine { - res := make([]curve.G1Affine, size) - for i := range res { - var err error - res[i], err = randomOnG1() - assert.NoError(t, err) - } - return res -} - -func testCommit(t *testing.T, values ...interface{}) { - - basis := randomG1Slice(t, len(values)) - - var ( - pk []ProvingKey - vk VerifyingKey - err error - commitment, pok curve.G1Affine - ) - valuesFr := interfaceSliceToFrSlice(t, values...) - - pk, vk, err = Setup([][]curve.G1Affine{basis}) - assert.NoError(t, err) - commitment, err = pk[0].Commit(valuesFr) - assert.NoError(t, err) - pok, err = pk[0].ProveKnowledge(valuesFr) - assert.NoError(t, err) - assert.NoError(t, vk.Verify(commitment, pok)) - - pok.Neg(&pok) - assert.NotNil(t, vk.Verify(commitment, pok)) -} - -func TestFoldProofs(t *testing.T) { - - values := [][]fr.Element{ - interfaceSliceToFrSlice(t, randomFrSlice(t, 5)...), - interfaceSliceToFrSlice(t, randomFrSlice(t, 5)...), - interfaceSliceToFrSlice(t, randomFrSlice(t, 5)...), - } - - bases := make([][]curve.G1Affine, len(values)) - for i := range bases { - bases[i] = randomG1Slice(t, len(values[i])) - } - - pk, vk, err := Setup(bases) - assert.NoError(t, err) - - commitments := make([]curve.G1Affine, len(values)) - for i := range values { - commitments[i], err = pk[i].Commit(values[i]) - assert.NoError(t, err) - } - - hashes, err := fr.Hash([]byte("test"), []byte("pedersen"), 1) - assert.NoError(t, err) - - t.Run("folding with zeros", func(t *testing.T) { - pokFolded, err := BatchProve(pk[:2], [][]fr.Element{ - values[0], - make([]fr.Element, len(values[1])), - }, hashes[0]) - assert.NoError(t, err) - var pok curve.G1Affine - pok, err = pk[0].ProveKnowledge(values[0]) - assert.NoError(t, err) - assert.Equal(t, pok, pokFolded) - }) - - t.Run("run empty", func(t *testing.T) { - var foldedCommitment curve.G1Affine - pok, err := BatchProve([]ProvingKey{}, [][]fr.Element{}, hashes[0]) - assert.NoError(t, err) - - _, err = foldedCommitment.Fold([]curve.G1Affine{}, hashes[0], ecc.MultiExpConfig{NbTasks: 1}) - assert.NoError(t, err) - assert.NoError(t, vk.Verify(foldedCommitment, pok)) - }) - - run := func(values [][]fr.Element) func(t *testing.T) { - return func(t *testing.T) { - - var foldedCommitment curve.G1Affine - pok, err := BatchProve(pk[:len(values)], values, hashes[0]) - assert.NoError(t, err) - - _, err = foldedCommitment.Fold(commitments[:len(values)], hashes[0], ecc.MultiExpConfig{NbTasks: 1}) - assert.NoError(t, err) - assert.NoError(t, vk.Verify(foldedCommitment, pok)) - - pok.Neg(&pok) - assert.NotNil(t, vk.Verify(foldedCommitment, pok)) - } - } - - for i := range values { - t.Run(fmt.Sprintf("folding %d", i+1), run(values[:i+1])) - } -} - -func TestCommitToOne(t *testing.T) { - testCommit(t, 1) -} - -func TestCommitSingle(t *testing.T) { - testCommit(t, randomFrSlice(t, 1)...) -} - -func TestCommitFiveElements(t *testing.T) { - testCommit(t, randomFrSlice(t, 5)...) -} - -func TestMarshal(t *testing.T) { - var pk ProvingKey - pk.BasisExpSigma = randomG1Slice(t, 5) - pk.Basis = randomG1Slice(t, 5) - - var ( - vk VerifyingKey - err error - ) - vk.G, err = curve.RandomOnG2() - assert.NoError(t, err) - vk.GSigma, err = curve.RandomOnG2() - assert.NoError(t, err) - - t.Run("ProvingKey -> Bytes -> ProvingKey must remain identical.", testutils.SerializationRoundTrip(&pk)) - t.Run("ProvingKey -> Bytes (raw) -> ProvingKey must remain identical.", testutils.SerializationRoundTripRaw(&pk)) - t.Run("VerifyingKey -> Bytes -> VerifyingKey must remain identical.", testutils.SerializationRoundTrip(&vk)) - t.Run("VerifyingKey -> Bytes (raw) -> ProvingKey must remain identical.", testutils.SerializationRoundTripRaw(&vk)) -} - -func TestSemiFoldProofs(t *testing.T) { - const ( - commitmentLength = 5 - nbCommitments = 5 - ) - g, err := curve.RandomOnG2() - assert.NoError(t, err) - - basis := randomG1Slice(t, commitmentLength*nbCommitments) - - vk, pk := make([]VerifyingKey, nbCommitments), make([]ProvingKey, nbCommitments) - for i := range pk { - var pk0 []ProvingKey - pk0, vk[i], err = Setup([][]curve.G1Affine{basis[i*commitmentLength : (i+1)*commitmentLength]}, WithG2Point(g)) - assert.NoError(t, err) - pk[i] = pk0[0] - } - - values := make([][]fr.Element, nbCommitments) - for i := range values { - values[i] = make([]fr.Element, commitmentLength) - for j := range values[i] { - _, err = values[i][j].SetRandom() - assert.NoError(t, err) - } - } - - commitments := make([]curve.G1Affine, nbCommitments) - proofs := make([]curve.G1Affine, nbCommitments) - for i := range commitments { - commitments[i], err = pk[i].Commit(values[i]) - assert.NoError(t, err) - proofs[i], err = pk[i].ProveKnowledge(values[i]) - assert.NoError(t, err) - } - - var challenge fr.Element - _, err = challenge.SetRandom() - assert.NoError(t, err) - - assert.NoError(t, BatchVerifyMultiVk(vk, commitments, proofs, challenge)) - - // send folded proof - proof, err := new(curve.G1Affine).Fold(proofs, challenge, ecc.MultiExpConfig{NbTasks: 1}) - assert.NoError(t, err) - assert.NoError(t, BatchVerifyMultiVk(vk, commitments, []curve.G1Affine{*proof}, challenge)) -} diff --git a/ecc/bls12-378/fr/permutation/doc.go b/ecc/bls12-378/fr/permutation/doc.go deleted file mode 100644 index 7ef21ffb9e..0000000000 --- a/ecc/bls12-378/fr/permutation/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package permutation provides an API to build permutation proofs. -package permutation diff --git a/ecc/bls12-378/fr/permutation/permutation.go b/ecc/bls12-378/fr/permutation/permutation.go deleted file mode 100644 index 0f7b60f1b3..0000000000 --- a/ecc/bls12-378/fr/permutation/permutation.go +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package permutation - -import ( - "crypto/sha256" - "errors" - "math/big" - "math/bits" - - "github.com/consensys/gnark-crypto/ecc/bls12-378" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/fft" - "github.com/consensys/gnark-crypto/ecc/bls12-378/kzg" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -var ( - ErrIncompatibleSize = errors.New("t1 and t2 should be of the same size") - ErrSize = errors.New("t1 and t2 should be of size a power of 2") - ErrPermutationProof = errors.New("permutation proof verification failed") - ErrGenerator = errors.New("wrong generator") -) - -// Proof proof that the commitments of t1 and t2 come from -// the same vector but permuted. -type Proof struct { - - // size of the polynomials - size int - - // generator of the fft domain, used for shifting the evaluation point - g fr.Element - - // commitments of t1 & t2, the permuted vectors, and z, the accumulation - // polynomial - t1, t2, z kzg.Digest - - // commitment to the quotient polynomial - q kzg.Digest - - // opening proofs of t1, t2, z, q (in that order) - batchedProof kzg.BatchOpeningProof - - // shifted opening proof of z - shiftedProof kzg.OpeningProof -} - -// evaluateAccumulationPolynomialBitReversed returns the accumulation polynomial in Lagrange basis. -func evaluateAccumulationPolynomialBitReversed(lt1, lt2 []fr.Element, epsilon fr.Element) []fr.Element { - - s := len(lt1) - z := make([]fr.Element, s) - d := make([]fr.Element, s) - z[0].SetOne() - d[0].SetOne() - nn := uint64(64 - bits.TrailingZeros64(uint64(s))) - var t fr.Element - for i := 0; i < s-1; i++ { - _i := int(bits.Reverse64(uint64(i)) >> nn) - _ii := int(bits.Reverse64(uint64((i+1)%s)) >> nn) - z[_ii].Mul(&z[_i], t.Sub(&epsilon, <1[i])) - d[i+1].Mul(&d[i], t.Sub(&epsilon, <2[i])) - } - d = fr.BatchInvert(d) - for i := 0; i < s-1; i++ { - _ii := int(bits.Reverse64(uint64((i+1)%s)) >> nn) - z[_ii].Mul(&z[_ii], &d[i+1]) - } - - return z -} - -// evaluateFirstPartNumReverse computes lt2*z(gx) - lt1*z -func evaluateFirstPartNumReverse(lt1, lt2, lz []fr.Element, epsilon fr.Element) []fr.Element { - - s := len(lt1) - res := make([]fr.Element, s) - var a, b fr.Element - nn := uint64(64 - bits.TrailingZeros64(uint64(s))) - for i := 0; i < s; i++ { - _i := int(bits.Reverse64(uint64(i)) >> nn) - _ii := int(bits.Reverse64(uint64((i+1)%s)) >> nn) - a.Sub(&epsilon, <2[_i]) - a.Mul(&lz[_ii], &a) - b.Sub(&epsilon, <1[_i]) - b.Mul(&lz[_i], &b) - res[_i].Sub(&a, &b) - } - return res -} - -// evaluateSecondPartNumReverse computes L0 * (z-1) -func evaluateSecondPartNumReverse(lz []fr.Element, d *fft.Domain) []fr.Element { - - var tn, o, g fr.Element - o.SetOne() - tn.Exp(d.FrMultiplicativeGen, big.NewInt(int64(d.Cardinality))). - Sub(&tn, &o) - s := len(lz) - u := make([]fr.Element, s) - g.Set(&d.FrMultiplicativeGen) - for i := 0; i < s; i++ { - u[i].Sub(&g, &o) - g.Mul(&g, &d.Generator) - } - u = fr.BatchInvert(u) - res := make([]fr.Element, s) - nn := uint64(64 - bits.TrailingZeros64(uint64(s))) - for i := 0; i < s; i++ { - _i := int(bits.Reverse64(uint64(i)) >> nn) - res[_i].Sub(&lz[_i], &o). - Mul(&res[_i], &u[i]). - Mul(&res[_i], &tn) - } - return res -} - -// Prove generates a proof that t1 and t2 are the same but permuted. -// The size of t1 and t2 should be the same and a power of 2. -func Prove(pk kzg.ProvingKey, t1, t2 []fr.Element) (Proof, error) { - - // res - var proof Proof - var err error - - // size checking - if len(t1) != len(t2) { - return proof, ErrIncompatibleSize - } - - // create the domains - d := fft.NewDomain(uint64(len(t1))) - if d.Cardinality != uint64(len(t1)) { - return proof, ErrSize - } - s := int(d.Cardinality) - proof.size = s - proof.g.Set(&d.Generator) - - // hash function for Fiat Shamir - hFunc := sha256.New() - - // transcript to derive the challenge - fs := fiatshamir.NewTranscript(hFunc, "epsilon", "omega", "eta") - - // commit t1, t2 - ct1 := make([]fr.Element, s) - ct2 := make([]fr.Element, s) - copy(ct1, t1) - copy(ct2, t2) - d.FFTInverse(ct1, fft.DIF) - d.FFTInverse(ct2, fft.DIF) - fft.BitReverse(ct1) - fft.BitReverse(ct2) - proof.t1, err = kzg.Commit(ct1, pk) - if err != nil { - return proof, err - } - proof.t2, err = kzg.Commit(ct2, pk) - if err != nil { - return proof, err - } - - // derive challenge for z - epsilon, err := deriveRandomness(fs, "epsilon", &proof.t1, &proof.t2) - if err != nil { - return proof, err - } - - // compute Z and commit it - cz := evaluateAccumulationPolynomialBitReversed(t1, t2, epsilon) - d.FFTInverse(cz, fft.DIT) - proof.z, err = kzg.Commit(cz, pk) - if err != nil { - return proof, err - } - lz := make([]fr.Element, s) - copy(lz, cz) - d.FFT(lz, fft.DIF, fft.OnCoset()) - - // compute the first part of the numerator - lt1 := make([]fr.Element, s) - lt2 := make([]fr.Element, s) - copy(lt1, ct1) - copy(lt2, ct2) - d.FFT(lt1, fft.DIF, fft.OnCoset()) - d.FFT(lt2, fft.DIF, fft.OnCoset()) - lsNumFirstPart := evaluateFirstPartNumReverse(lt1, lt2, lz, epsilon) - - // compute second part of the numerator - lsNum := evaluateSecondPartNumReverse(lz, d) - - // derive challenge used for the folding - omega, err := deriveRandomness(fs, "omega", &proof.z) - if err != nil { - return proof, err - } - - // fold the numerator and divide it by x^n-1 - var t, one fr.Element - one.SetOne() - t.Exp(d.FrMultiplicativeGen, big.NewInt(int64(d.Cardinality))).Sub(&t, &one).Inverse(&t) - for i := 0; i < s; i++ { - lsNum[i].Mul(&omega, &lsNum[i]). - Add(&lsNum[i], &lsNumFirstPart[i]). - Mul(&lsNum[i], &t) - } - - // get the quotient and commit it - d.FFTInverse(lsNum, fft.DIT, fft.OnCoset()) - proof.q, err = kzg.Commit(lsNum, pk) - if err != nil { - return proof, err - } - - // derive the evaluation challenge - eta, err := deriveRandomness(fs, "eta", &proof.q) - if err != nil { - return proof, err - } - - // compute the opening proofs - proof.batchedProof, err = kzg.BatchOpenSinglePoint( - [][]fr.Element{ - ct1, - ct2, - cz, - lsNum, - }, - []kzg.Digest{ - proof.t1, - proof.t2, - proof.z, - proof.q, - }, - eta, - hFunc, - pk, - ) - if err != nil { - return proof, err - } - - var shiftedEta fr.Element - shiftedEta.Mul(&eta, &d.Generator) - proof.shiftedProof, err = kzg.Open( - cz, - shiftedEta, - pk, - ) - if err != nil { - return proof, err - } - - // done - return proof, nil - -} - -// Verify verifies a permutation proof. -func Verify(vk kzg.VerifyingKey, proof Proof) error { - - // hash function that is used for Fiat Shamir - hFunc := sha256.New() - - // transcript to derive the challenge - fs := fiatshamir.NewTranscript(hFunc, "epsilon", "omega", "eta") - - // derive the challenges - epsilon, err := deriveRandomness(fs, "epsilon", &proof.t1, &proof.t2) - if err != nil { - return err - } - - omega, err := deriveRandomness(fs, "omega", &proof.z) - if err != nil { - return err - } - - eta, err := deriveRandomness(fs, "eta", &proof.q) - if err != nil { - return err - } - - // check the relation - bs := big.NewInt(int64(proof.size)) - var l0, a, b, one, rhs, lhs fr.Element - one.SetOne() - rhs.Exp(eta, bs). - Sub(&rhs, &one) - a.Sub(&eta, &one) - l0.Div(&rhs, &a) - rhs.Mul(&rhs, &proof.batchedProof.ClaimedValues[3]) - a.Sub(&epsilon, &proof.batchedProof.ClaimedValues[1]). - Mul(&a, &proof.shiftedProof.ClaimedValue) - b.Sub(&epsilon, &proof.batchedProof.ClaimedValues[0]). - Mul(&b, &proof.batchedProof.ClaimedValues[2]) - lhs.Sub(&a, &b) - a.Sub(&proof.batchedProof.ClaimedValues[2], &one). - Mul(&a, &l0). - Mul(&a, &omega) - lhs.Add(&a, &lhs) - if !lhs.Equal(&rhs) { - return ErrPermutationProof - } - - // check the opening proofs - err = kzg.BatchVerifySinglePoint( - []kzg.Digest{ - proof.t1, - proof.t2, - proof.z, - proof.q, - }, - &proof.batchedProof, - eta, - hFunc, - vk, - ) - if err != nil { - return err - } - - var shiftedEta fr.Element - shiftedEta.Mul(&eta, &proof.g) - err = kzg.Verify(&proof.z, &proof.shiftedProof, shiftedEta, vk) - if err != nil { - return err - } - - // check the generator is correct - var checkOrder fr.Element - checkOrder.Exp(proof.g, big.NewInt(int64(proof.size/2))) - if checkOrder.Equal(&one) { - return ErrGenerator - } - checkOrder.Square(&checkOrder) - if !checkOrder.Equal(&one) { - return ErrGenerator - } - - return nil -} - -// TODO put that in fiat-shamir package -func deriveRandomness(fs *fiatshamir.Transcript, challenge string, points ...*bls12378.G1Affine) (fr.Element, error) { - - var buf [bls12378.SizeOfG1AffineUncompressed]byte - var r fr.Element - - for _, p := range points { - buf = p.RawBytes() - if err := fs.Bind(challenge, buf[:]); err != nil { - return r, err - } - } - - b, err := fs.ComputeChallenge(challenge) - if err != nil { - return r, err - } - r.SetBytes(b) - return r, nil -} diff --git a/ecc/bls12-378/fr/permutation/permutation_test.go b/ecc/bls12-378/fr/permutation/permutation_test.go deleted file mode 100644 index c88504c9a8..0000000000 --- a/ecc/bls12-378/fr/permutation/permutation_test.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package permutation - -import ( - "math/big" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/kzg" -) - -func TestProof(t *testing.T) { - - kzgSrs, err := kzg.NewSRS(64, big.NewInt(13)) - assert.NoError(t, err) - - a := make([]fr.Element, 8) - b := make([]fr.Element, 8) - - for i := 0; i < 8; i++ { - a[i].SetUint64(uint64(4*i + 1)) - } - for i := 0; i < 8; i++ { - b[i].Set(&a[(5*i)%8]) - } - - // correct proof - { - proof, err := Prove(kzgSrs.Pk, a, b) - if err != nil { - t.Fatal(err) - } - - err = Verify(kzgSrs.Vk, proof) - if err != nil { - t.Fatal(err) - } - } - - // wrong proof - { - a[0].SetRandom() - proof, err := Prove(kzgSrs.Pk, a, b) - if err != nil { - t.Fatal(err) - } - - err = Verify(kzgSrs.Vk, proof) - if err == nil { - t.Fatal(err) - } - } - -} - -func BenchmarkProver(b *testing.B) { - - srsSize := 1 << 15 - polySize := 1 << 14 - - kzgSrs, _ := kzg.NewSRS(uint64(srsSize), big.NewInt(13)) - a := make([]fr.Element, polySize) - c := make([]fr.Element, polySize) - - for i := 0; i < polySize; i++ { - a[i].SetUint64(uint64(i)) - } - for i := 0; i < polySize; i++ { - c[i].Set(&a[(5*i)%(polySize)]) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - Prove(kzgSrs.Pk, a, c) - } - -} diff --git a/ecc/bls12-378/fr/plookup/doc.go b/ecc/bls12-378/fr/plookup/doc.go deleted file mode 100644 index b3af019687..0000000000 --- a/ecc/bls12-378/fr/plookup/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package plookup provides an API to build plookup proofs. -package plookup diff --git a/ecc/bls12-378/fr/plookup/plookup_test.go b/ecc/bls12-378/fr/plookup/plookup_test.go deleted file mode 100644 index 207695138c..0000000000 --- a/ecc/bls12-378/fr/plookup/plookup_test.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package plookup - -import ( - "math/big" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/kzg" -) - -func TestLookupVector(t *testing.T) { - - lookupVector := make(fr.Vector, 8) - fvector := make(fr.Vector, 7) - for i := 0; i < 8; i++ { - lookupVector[i].SetUint64(uint64(2 * i)) - } - for i := 0; i < 7; i++ { - fvector[i].Set(&lookupVector[(4*i+1)%8]) - } - - kzgSrs, err := kzg.NewSRS(64, big.NewInt(13)) - if err != nil { - t.Fatal(err) - } - - // correct proof vector - { - proof, err := ProveLookupVector(kzgSrs.Pk, fvector, lookupVector) - if err != nil { - t.Fatal(err) - } - - err = VerifyLookupVector(kzgSrs.Vk, proof) - if err != nil { - t.Fatal(err) - } - } - - // wrong proofs vector - { - fvector[0].SetRandom() - - proof, err := ProveLookupVector(kzgSrs.Pk, fvector, lookupVector) - if err != nil { - t.Fatal(err) - } - - err = VerifyLookupVector(kzgSrs.Vk, proof) - if err == nil { - t.Fatal(err) - } - } - -} - -func TestLookupTable(t *testing.T) { - - kzgSrs, err := kzg.NewSRS(64, big.NewInt(13)) - if err != nil { - t.Fatal(err) - } - - lookupTable := make([]fr.Vector, 3) - fTable := make([]fr.Vector, 3) - for i := 0; i < 3; i++ { - lookupTable[i] = make(fr.Vector, 8) - fTable[i] = make(fr.Vector, 7) - for j := 0; j < 8; j++ { - lookupTable[i][j].SetUint64(uint64(2*i + j)) - } - for j := 0; j < 7; j++ { - fTable[i][j].Set(&lookupTable[i][(4*j+1)%8]) - } - } - - // correct proof - { - proof, err := ProveLookupTables(kzgSrs.Pk, fTable, lookupTable) - if err != nil { - t.Fatal(err) - } - - err = VerifyLookupTables(kzgSrs.Vk, proof) - if err != nil { - t.Fatal(err) - } - } - - // wrong proof - { - fTable[0][0].SetRandom() - proof, err := ProveLookupTables(kzgSrs.Pk, fTable, lookupTable) - if err != nil { - t.Fatal(err) - } - - err = VerifyLookupTables(kzgSrs.Vk, proof) - if err == nil { - t.Fatal(err) - } - } - -} - -func BenchmarkPlookup(b *testing.B) { - - srsSize := 1 << 15 - polySize := 1 << 14 - - kzgSrs, _ := kzg.NewSRS(uint64(srsSize), big.NewInt(13)) - a := make(fr.Vector, polySize) - c := make(fr.Vector, polySize) - - for i := 0; i < 1<<14; i++ { - a[i].SetUint64(uint64(i)) - c[i].SetUint64(uint64((8 * i) % polySize)) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - ProveLookupVector(kzgSrs.Pk, a, c) - } -} diff --git a/ecc/bls12-378/fr/plookup/table.go b/ecc/bls12-378/fr/plookup/table.go deleted file mode 100644 index cff6a8cf61..0000000000 --- a/ecc/bls12-378/fr/plookup/table.go +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package plookup - -import ( - "crypto/sha256" - "errors" - "math/big" - "sort" - - bls12378 "github.com/consensys/gnark-crypto/ecc/bls12-378" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/fft" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/permutation" - "github.com/consensys/gnark-crypto/ecc/bls12-378/kzg" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -var ( - ErrIncompatibleSize = errors.New("the tables in f and t are not of the same size") - ErrFoldedCommitment = errors.New("the folded commitment is malformed") - ErrNumberDigests = errors.New("proof.ts and proof.fs are not of the same length") -) - -// ProofLookupTables proofs that a list of tables -type ProofLookupTables struct { - - // commitments to the rows f - fs []kzg.Digest - - // commitments to the rows of t - ts []kzg.Digest - - // lookup proof for the f and t folded - foldedProof ProofLookupVector - - // proof that the ts folded correspond to t in the folded proof - permutationProof permutation.Proof -} - -// ProveLookupTables generates a proof that f, seen as a multi dimensional table, -// consists of vectors that are in t. In other words for each i, f[:][i] must be one -// of the t[:][j]. -// -// For instance, if t is the truth table of the XOR function, t will be populated such -// that t[:][i] contains the i-th entry of the truth table, so t[0][i] XOR t[1][i] = t[2][i]. -// -// The fr.Vector in f and t are supposed to be of the same size constant size. -func ProveLookupTables(pk kzg.ProvingKey, f, t []fr.Vector) (ProofLookupTables, error) { - - // res - proof := ProofLookupTables{} - var err error - - // hash function used for Fiat Shamir - hFunc := sha256.New() - - // transcript to derive the challenge - fs := fiatshamir.NewTranscript(hFunc, "lambda") - - // check the sizes - if len(f) != len(t) { - return proof, ErrIncompatibleSize - } - s := len(f[0]) - for i := 1; i < len(f); i++ { - if len(f[i]) != s { - return proof, ErrIncompatibleSize - } - } - s = len(t[0]) - for i := 1; i < len(t); i++ { - if len(t[i]) != s { - return proof, ErrIncompatibleSize - } - } - - // commit to the tables in f and t - nbRows := len(t) - proof.fs = make([]kzg.Digest, nbRows) - proof.ts = make([]kzg.Digest, nbRows) - _nbColumns := len(f[0]) + 1 - if _nbColumns < len(t[0]) { - _nbColumns = len(t[0]) - } - d := fft.NewDomain(uint64(_nbColumns)) - nbColumns := d.Cardinality - lfs := make([][]fr.Element, nbRows) - cfs := make([][]fr.Element, nbRows) - lts := make([][]fr.Element, nbRows) - cts := make([][]fr.Element, nbRows) - - for i := 0; i < nbRows; i++ { - - cfs[i] = make([]fr.Element, nbColumns) - lfs[i] = make([]fr.Element, nbColumns) - copy(cfs[i], f[i]) - copy(lfs[i], f[i]) - for j := len(f[i]); j < int(nbColumns); j++ { - cfs[i][j] = f[i][len(f[i])-1] - lfs[i][j] = f[i][len(f[i])-1] - } - d.FFTInverse(cfs[i], fft.DIF) - fft.BitReverse(cfs[i]) - proof.fs[i], err = kzg.Commit(cfs[i], pk) - if err != nil { - return proof, err - } - - cts[i] = make([]fr.Element, nbColumns) - lts[i] = make([]fr.Element, nbColumns) - copy(cts[i], t[i]) - copy(lts[i], t[i]) - for j := len(t[i]); j < int(d.Cardinality); j++ { - cts[i][j] = t[i][len(t[i])-1] - lts[i][j] = t[i][len(t[i])-1] - } - d.FFTInverse(cts[i], fft.DIF) - fft.BitReverse(cts[i]) - proof.ts[i], err = kzg.Commit(cts[i], pk) - if err != nil { - return proof, err - } - } - - // fold f and t - comms := make([]*kzg.Digest, 2*nbRows) - for i := 0; i < nbRows; i++ { - comms[i] = new(kzg.Digest) - comms[i].Set(&proof.fs[i]) - comms[nbRows+i] = new(kzg.Digest) - comms[nbRows+i].Set(&proof.ts[i]) - } - lambda, err := deriveRandomness(fs, "lambda", comms...) - if err != nil { - return proof, err - } - foldedf := make(fr.Vector, nbColumns) - foldedt := make(fr.Vector, nbColumns) - for i := 0; i < int(nbColumns); i++ { - for j := nbRows - 1; j >= 0; j-- { - foldedf[i].Mul(&foldedf[i], &lambda). - Add(&foldedf[i], &lfs[j][i]) - foldedt[i].Mul(&foldedt[i], &lambda). - Add(&foldedt[i], <s[j][i]) - } - } - - // generate a proof of permutation of the foldedt and sort(foldedt) - foldedtSorted := make(fr.Vector, nbColumns) - copy(foldedtSorted, foldedt) - sort.Sort(foldedtSorted) - proof.permutationProof, err = permutation.Prove(pk, foldedt, foldedtSorted) - if err != nil { - return proof, err - } - - // call plookupVector, on foldedf[:len(foldedf)-1] to ensure that the domain size - // in ProveLookupVector is the same as d's - proof.foldedProof, err = ProveLookupVector(pk, foldedf[:len(foldedf)-1], foldedt) - - return proof, err -} - -// VerifyLookupTables verifies that a ProofLookupTables proof is correct. -func VerifyLookupTables(vk kzg.VerifyingKey, proof ProofLookupTables) error { - - // hash function used for Fiat Shamir - hFunc := sha256.New() - - // transcript to derive the challenge - fs := fiatshamir.NewTranscript(hFunc, "lambda") - - // check that the number of digests is the same - if len(proof.fs) != len(proof.ts) { - return ErrNumberDigests - } - - // fold the commitments fs and ts - nbRows := len(proof.fs) - comms := make([]*kzg.Digest, 2*nbRows) - for i := 0; i < nbRows; i++ { - comms[i] = &proof.fs[i] - comms[i+nbRows] = &proof.ts[i] - } - lambda, err := deriveRandomness(fs, "lambda", comms...) - if err != nil { - return err - } - - // fold the commitments of the rows of t and f - var comf, comt kzg.Digest - comf.Set(&proof.fs[nbRows-1]) - comt.Set(&proof.ts[nbRows-1]) - var blambda big.Int - lambda.BigInt(&blambda) - for i := nbRows - 2; i >= 0; i-- { - comf.ScalarMultiplication(&comf, &blambda). - Add(&comf, &proof.fs[i]) - comt.ScalarMultiplication(&comt, &blambda). - Add(&comt, &proof.ts[i]) - } - - // check that the folded commitment of the fs correspond to foldedProof.f - if !comf.Equal(&proof.foldedProof.f) { - return ErrFoldedCommitment - } - - // check that the folded commitment of the ts is a permutation of proof.FoldedProof.t - err = permutation.Verify(vk, proof.permutationProof) - if err != nil { - return err - } - - // verify the inner proof - return VerifyLookupVector(vk, proof.foldedProof) -} - -// TODO put that in fiat-shamir package -func deriveRandomness(fs *fiatshamir.Transcript, challenge string, points ...*bls12378.G1Affine) (fr.Element, error) { - - var buf [bls12378.SizeOfG1AffineUncompressed]byte - var r fr.Element - - for _, p := range points { - buf = p.RawBytes() - if err := fs.Bind(challenge, buf[:]); err != nil { - return r, err - } - } - - b, err := fs.ComputeChallenge(challenge) - if err != nil { - return r, err - } - r.SetBytes(b) - return r, nil -} diff --git a/ecc/bls12-378/fr/plookup/vector.go b/ecc/bls12-378/fr/plookup/vector.go deleted file mode 100644 index 59d37e7e12..0000000000 --- a/ecc/bls12-378/fr/plookup/vector.go +++ /dev/null @@ -1,717 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package plookup - -import ( - "crypto/sha256" - "errors" - "math/big" - "math/bits" - "sort" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/fft" - "github.com/consensys/gnark-crypto/ecc/bls12-378/kzg" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -var ( - ErrNotInTable = errors.New("some value in the vector is not in the lookup table") - ErrPlookupVerification = errors.New("plookup verification failed") - ErrGenerator = errors.New("wrong generator") -) - -// Proof Plookup proof, containing opening proofs -type ProofLookupVector struct { - - // size of the system - size uint64 - - // generator of the fft domain, used for shifting the evaluation point - g fr.Element - - // Commitments to h1, h2, t, z, f, h - h1, h2, t, z, f, h kzg.Digest - - // Batch opening proof of h1, h2, z, t - BatchedProof kzg.BatchOpeningProof - - // Batch opening proof of h1, h2, z shifted by g - BatchedProofShifted kzg.BatchOpeningProof -} - -// evaluateAccumulationPolynomial computes Z, in Lagrange basis. Z is the accumulation of the partial -// ratios of 2 fully split polynomials (cf https://eprint.iacr.org/2020/315.pdf) -// * lf is the list of values that should be in lt -// * lt is the lookup table -// * lh1, lh2 is lf sorted by lt split in 2 overlapping slices -// * beta, gamma are challenges (Schwartz-zippel: they are the random evaluations point) -func evaluateAccumulationPolynomial(lf, lt, lh1, lh2 []fr.Element, beta, gamma fr.Element) []fr.Element { - - z := make([]fr.Element, len(lt)) - - n := len(lt) - d := make([]fr.Element, n-1) - var u, c fr.Element - c.SetOne(). - Add(&c, &beta). - Mul(&c, &gamma) - for i := 0; i < n-1; i++ { - - d[i].Mul(&beta, &lh1[i+1]). - Add(&d[i], &lh1[i]). - Add(&d[i], &c) - - u.Mul(&beta, &lh2[i+1]). - Add(&u, &lh2[i]). - Add(&u, &c) - - d[i].Mul(&d[i], &u) - } - d = fr.BatchInvert(d) - - z[0].SetOne() - var a, b, e fr.Element - e.SetOne().Add(&e, &beta) - for i := 0; i < n-1; i++ { - - a.Add(&gamma, &lf[i]) - - b.Mul(&beta, <[i+1]). - Add(&b, <[i]). - Add(&b, &c) - - a.Mul(&a, &b). - Mul(&a, &e) - - z[i+1].Mul(&z[i], &a). - Mul(&z[i+1], &d[i]) - } - - return z -} - -// evaluateNumBitReversed computes the evaluation (shifted, bit reversed) of h where -// h = (x-1)*z*(1+\beta)*(\gamma+f)*(\gamma(1+\beta) + t+ \beta*t(gX)) - -// -// (x-1)*z(gX)*(\gamma(1+\beta) + h_{1} + \beta*h_{1}(gX))*(\gamma(1+\beta) + h_{2} + \beta*h_{2}(gX) ) -// -// * cz, ch1, ch2, ct, cf are the polynomials z, h1, h2, t, f in canonical basis -// * _lz, _lh1, _lh2, _lt, _lf are the polynomials z, h1, h2, t, f in shifted Lagrange basis (domainBig) -// * beta, gamma are the challenges -// * it returns h in canonical basis -func evaluateNumBitReversed(_lz, _lh1, _lh2, _lt, _lf []fr.Element, beta, gamma fr.Element, domainBig *fft.Domain) []fr.Element { - - // result - s := int(domainBig.Cardinality) - num := make([]fr.Element, domainBig.Cardinality) - - var u, onePlusBeta, GammaTimesOnePlusBeta, m, n, one fr.Element - - one.SetOne() - onePlusBeta.Add(&one, &beta) - GammaTimesOnePlusBeta.Mul(&onePlusBeta, &gamma) - - g := make([]fr.Element, s) - g[0].Set(&domainBig.FrMultiplicativeGen) - for i := 1; i < s; i++ { - g[i].Mul(&g[i-1], &domainBig.Generator) - } - - var gg fr.Element - expo := big.NewInt(int64(domainBig.Cardinality>>1 - 1)) - gg.Square(&domainBig.Generator).Exp(gg, expo) - - nn := uint64(64 - bits.TrailingZeros64(domainBig.Cardinality)) - - for i := 0; i < s; i++ { - - _i := int(bits.Reverse64(uint64(i)) >> nn) - _is := int(bits.Reverse64(uint64((i+2)%s)) >> nn) - - // m = z*(1+\beta)*(\gamma+f)*(\gamma(1+\beta) + t+ \beta*t(gX)) - m.Mul(&onePlusBeta, &_lz[_i]) - u.Add(&gamma, &_lf[_i]) - m.Mul(&m, &u) - u.Mul(&beta, &_lt[_is]). - Add(&u, &_lt[_i]). - Add(&u, &GammaTimesOnePlusBeta) - m.Mul(&m, &u) - - // n = z(gX)*(\gamma(1+\beta) + h_{1} + \beta*h_{1}(gX))*(\gamma(1+\beta) + h_{2} + \beta*h_{2}(gX) - n.Mul(&beta, &_lh1[_is]). - Add(&n, &_lh1[_i]). - Add(&n, &GammaTimesOnePlusBeta) - u.Mul(&beta, &_lh2[_is]). - Add(&u, &_lh2[_i]). - Add(&u, &GammaTimesOnePlusBeta) - n.Mul(&n, &u). - Mul(&n, &_lz[_is]) - - // (x-gg**(n-1))*(m-n) - num[_i].Sub(&m, &n) - u.Sub(&g[i], &gg) - num[_i].Mul(&num[_i], &u) - - } - - return num -} - -// evaluateXnMinusOneDomainBig returns the evaluation of (x^{n}-1) on FrMultiplicativeGen*< g > -func evaluateXnMinusOneDomainBig(domainBig *fft.Domain) [2]fr.Element { - - sizeDomainSmall := domainBig.Cardinality / 2 - - var one fr.Element - one.SetOne() - - // x^{n}-1 on FrMultiplicativeGen*< g > - var res [2]fr.Element - var shift fr.Element - shift.Exp(domainBig.FrMultiplicativeGen, big.NewInt(int64(sizeDomainSmall))) - res[0].Sub(&shift, &one) - res[1].Add(&shift, &one).Neg(&res[1]) - - return res - -} - -// evaluateL0DomainBig returns the evaluation of (x^{n}-1)/(x-1) on -// x^{n}-1 on FrMultiplicativeGen*< g > -func evaluateL0DomainBig(domainBig *fft.Domain) ([2]fr.Element, []fr.Element) { - - var one fr.Element - one.SetOne() - - // x^{n}-1 on FrMultiplicativeGen*< g > - xnMinusOne := evaluateXnMinusOneDomainBig(domainBig) - - // 1/(x-1) on FrMultiplicativeGen*< g > - var acc fr.Element - denL0 := make([]fr.Element, domainBig.Cardinality) - acc.Set(&domainBig.FrMultiplicativeGen) - for i := 0; i < int(domainBig.Cardinality); i++ { - denL0[i].Sub(&acc, &one) - acc.Mul(&acc, &domainBig.Generator) - } - denL0 = fr.BatchInvert(denL0) - - return xnMinusOne, denL0 -} - -// evaluationLnDomainBig returns the evaluation of (x^{n}-1)/(x-g^{n-1}) on -// x^{n}-1 on FrMultiplicativeGen*< g > -func evaluationLnDomainBig(domainBig *fft.Domain) ([2]fr.Element, []fr.Element) { - - sizeDomainSmall := domainBig.Cardinality / 2 - - var one fr.Element - one.SetOne() - - // x^{n}-1 on FrMultiplicativeGen*< g > - numLn := evaluateXnMinusOneDomainBig(domainBig) - - // 1/(x-g^{n-1}) on FrMultiplicativeGen*< g > - var gg, acc fr.Element - gg.Square(&domainBig.Generator).Exp(gg, big.NewInt(int64(sizeDomainSmall-1))) - denLn := make([]fr.Element, domainBig.Cardinality) - acc.Set(&domainBig.FrMultiplicativeGen) - for i := 0; i < int(domainBig.Cardinality); i++ { - denLn[i].Sub(&acc, &gg) - acc.Mul(&acc, &domainBig.Generator) - } - denLn = fr.BatchInvert(denLn) - - return numLn, denLn - -} - -// evaluateZStartsByOneBitReversed returns l0 * (z-1), in Lagrange basis and bit reversed order -func evaluateZStartsByOneBitReversed(lsZBitReversed []fr.Element, domainBig *fft.Domain) []fr.Element { - - var one fr.Element - one.SetOne() - - res := make([]fr.Element, domainBig.Cardinality) - - nn := uint64(64 - bits.TrailingZeros64(domainBig.Cardinality)) - - xnMinusOne, denL0 := evaluateL0DomainBig(domainBig) - - for i := 0; i < len(lsZBitReversed); i++ { - _i := int(bits.Reverse64(uint64(i)) >> nn) - res[_i].Sub(&lsZBitReversed[_i], &one). - Mul(&res[_i], &xnMinusOne[i%2]). - Mul(&res[_i], &denL0[i]) - } - - return res -} - -// evaluateZEndsByOneBitReversed returns ln * (z-1), in Lagrange basis and bit reversed order -func evaluateZEndsByOneBitReversed(lsZBitReversed []fr.Element, domainBig *fft.Domain) []fr.Element { - - var one fr.Element - one.SetOne() - - numLn, denLn := evaluationLnDomainBig(domainBig) - - res := make([]fr.Element, len(lsZBitReversed)) - nn := uint64(64 - bits.TrailingZeros64(domainBig.Cardinality)) - - for i := 0; i < len(lsZBitReversed); i++ { - _i := int(bits.Reverse64(uint64(i)) >> nn) - res[_i].Sub(&lsZBitReversed[_i], &one). - Mul(&res[_i], &numLn[i%2]). - Mul(&res[_i], &denLn[i]) - } - - return res -} - -// evaluateOverlapH1h2BitReversed returns ln * (h1 - h2(g.x)), in Lagrange basis and bit reversed order -func evaluateOverlapH1h2BitReversed(_lh1, _lh2 []fr.Element, domainBig *fft.Domain) []fr.Element { - - var one fr.Element - one.SetOne() - - numLn, denLn := evaluationLnDomainBig(domainBig) - - res := make([]fr.Element, len(_lh1)) - nn := uint64(64 - bits.TrailingZeros64(domainBig.Cardinality)) - - s := len(_lh1) - for i := 0; i < s; i++ { - - _i := int(bits.Reverse64(uint64(i)) >> nn) - _is := int(bits.Reverse64(uint64((i+2)%s)) >> nn) - - res[_i].Sub(&_lh1[_i], &_lh2[_is]). - Mul(&res[_i], &numLn[i%2]). - Mul(&res[_i], &denLn[i]) - } - - return res -} - -// computeQuotientCanonical computes the full quotient of the plookup protocol. -// * alpha is the challenge to fold the numerator -// * lh, lh0, lhn, lh1h2 are the various pieces of the numerator (Lagrange shifted form, bit reversed order) -// * domainBig fft domain -// It returns the quotient, in canonical basis -func computeQuotientCanonical(alpha fr.Element, lh, lh0, lhn, lh1h2 []fr.Element, domainBig *fft.Domain) []fr.Element { - - sizeDomainBig := int(domainBig.Cardinality) - res := make([]fr.Element, sizeDomainBig) - - var one fr.Element - one.SetOne() - - numLn := evaluateXnMinusOneDomainBig(domainBig) - numLn[0].Inverse(&numLn[0]) - numLn[1].Inverse(&numLn[1]) - nn := uint64(64 - bits.TrailingZeros64(domainBig.Cardinality)) - - for i := 0; i < sizeDomainBig; i++ { - - _i := int(bits.Reverse64(uint64(i)) >> nn) - - res[_i].Mul(&lh1h2[_i], &alpha). - Add(&res[_i], &lhn[_i]). - Mul(&res[_i], &alpha). - Add(&res[_i], &lh0[_i]). - Mul(&res[_i], &alpha). - Add(&res[_i], &lh[_i]). - Mul(&res[_i], &numLn[i%2]) - } - - domainBig.FFTInverse(res, fft.DIT, fft.OnCoset()) - - return res -} - -// ProveLookupVector returns proof that the values in f are in t. -// -// /!\IMPORTANT/!\ -// -// If the table t is already committed somewhere (which is the normal workflow -// before generating a lookup proof), the commitment needs to be done on the -// table sorted. Otherwise the commitment in proof.t will not be the same as -// the public commitment: it will contain the same values, but permuted. -func ProveLookupVector(pk kzg.ProvingKey, f, t fr.Vector) (ProofLookupVector, error) { - - // res - var proof ProofLookupVector - var err error - - // hash function used for Fiat Shamir - hFunc := sha256.New() - - // transcript to derive the challenge - fs := fiatshamir.NewTranscript(hFunc, "beta", "gamma", "alpha", "nu") - - // create domains - var domainSmall *fft.Domain - if len(t) <= len(f) { - domainSmall = fft.NewDomain(uint64(len(f) + 1)) - } else { - domainSmall = fft.NewDomain(uint64(len(t))) - } - sizeDomainSmall := int(domainSmall.Cardinality) - - // set the size - proof.size = domainSmall.Cardinality - - // set the generator - proof.g.Set(&domainSmall.Generator) - - // resize f and t - // note: the last element of lf does not matter - lf := make([]fr.Element, sizeDomainSmall) - lt := make([]fr.Element, sizeDomainSmall) - cf := make([]fr.Element, sizeDomainSmall) - ct := make([]fr.Element, sizeDomainSmall) - copy(lt, t) - copy(lf, f) - for i := len(f); i < sizeDomainSmall; i++ { - lf[i] = f[len(f)-1] - } - for i := len(t); i < sizeDomainSmall; i++ { - lt[i] = t[len(t)-1] - } - sort.Sort(fr.Vector(lt)) - copy(ct, lt) - copy(cf, lf) - domainSmall.FFTInverse(ct, fft.DIF) - domainSmall.FFTInverse(cf, fft.DIF) - fft.BitReverse(ct) - fft.BitReverse(cf) - proof.t, err = kzg.Commit(ct, pk) - if err != nil { - return proof, err - } - proof.f, err = kzg.Commit(cf, pk) - if err != nil { - return proof, err - } - - // write f sorted by t - lfSortedByt := make(fr.Vector, 2*domainSmall.Cardinality-1) - copy(lfSortedByt, lt) - copy(lfSortedByt[domainSmall.Cardinality:], lf) - sort.Sort(lfSortedByt) - - // compute h1, h2, commit to them - lh1 := make([]fr.Element, sizeDomainSmall) - lh2 := make([]fr.Element, sizeDomainSmall) - ch1 := make([]fr.Element, sizeDomainSmall) - ch2 := make([]fr.Element, sizeDomainSmall) - copy(lh1, lfSortedByt[:sizeDomainSmall]) - copy(lh2, lfSortedByt[sizeDomainSmall-1:]) - - copy(ch1, lfSortedByt[:sizeDomainSmall]) - copy(ch2, lfSortedByt[sizeDomainSmall-1:]) - domainSmall.FFTInverse(ch1, fft.DIF) - domainSmall.FFTInverse(ch2, fft.DIF) - fft.BitReverse(ch1) - fft.BitReverse(ch2) - - proof.h1, err = kzg.Commit(ch1, pk) - if err != nil { - return proof, err - } - proof.h2, err = kzg.Commit(ch2, pk) - if err != nil { - return proof, err - } - - // derive beta, gamma - beta, err := deriveRandomness(fs, "beta", &proof.t, &proof.f, &proof.h1, &proof.h2) - if err != nil { - return proof, err - } - gamma, err := deriveRandomness(fs, "gamma") - if err != nil { - return proof, err - } - - // Compute to Z - lz := evaluateAccumulationPolynomial(lf, lt, lh1, lh2, beta, gamma) - cz := make([]fr.Element, len(lz)) - copy(cz, lz) - domainSmall.FFTInverse(cz, fft.DIF) - fft.BitReverse(cz) - proof.z, err = kzg.Commit(cz, pk) - if err != nil { - return proof, err - } - - // prepare data for computing the quotient - // compute the numerator - s := domainSmall.Cardinality - domainBig := fft.NewDomain(uint64(2 * s)) - - _lz := make([]fr.Element, 2*s) - _lh1 := make([]fr.Element, 2*s) - _lh2 := make([]fr.Element, 2*s) - _lt := make([]fr.Element, 2*s) - _lf := make([]fr.Element, 2*s) - copy(_lz, cz) - copy(_lh1, ch1) - copy(_lh2, ch2) - copy(_lt, ct) - copy(_lf, cf) - domainBig.FFT(_lz, fft.DIF, fft.OnCoset()) - domainBig.FFT(_lh1, fft.DIF, fft.OnCoset()) - domainBig.FFT(_lh2, fft.DIF, fft.OnCoset()) - domainBig.FFT(_lt, fft.DIF, fft.OnCoset()) - domainBig.FFT(_lf, fft.DIF, fft.OnCoset()) - - // compute h - lh := evaluateNumBitReversed(_lz, _lh1, _lh2, _lt, _lf, beta, gamma, domainBig) - - // compute l0*(z-1) - lh0 := evaluateZStartsByOneBitReversed(_lz, domainBig) - - // compute ln(z-1) - lhn := evaluateZEndsByOneBitReversed(_lz, domainBig) - - // compute ln*(h1-h2(g*X)) - lh1h2 := evaluateOverlapH1h2BitReversed(_lh1, _lh2, domainBig) - - // compute the quotient - alpha, err := deriveRandomness(fs, "alpha", &proof.z) - if err != nil { - return proof, err - } - ch := computeQuotientCanonical(alpha, lh, lh0, lhn, lh1h2, domainBig) - proof.h, err = kzg.Commit(ch, pk) - if err != nil { - return proof, err - } - - // build the opening proofs - nu, err := deriveRandomness(fs, "nu", &proof.h) - if err != nil { - return proof, err - } - proof.BatchedProof, err = kzg.BatchOpenSinglePoint( - [][]fr.Element{ - ch1, - ch2, - ct, - cz, - cf, - ch, - }, - []kzg.Digest{ - proof.h1, - proof.h2, - proof.t, - proof.z, - proof.f, - proof.h, - }, - nu, - hFunc, - pk, - ) - if err != nil { - return proof, err - } - - nu.Mul(&nu, &domainSmall.Generator) - proof.BatchedProofShifted, err = kzg.BatchOpenSinglePoint( - [][]fr.Element{ - ch1, - ch2, - ct, - cz, - }, - []kzg.Digest{ - proof.h1, - proof.h2, - proof.t, - proof.z, - }, - nu, - hFunc, - pk, - ) - if err != nil { - return proof, err - } - - return proof, nil -} - -// VerifyLookupVector verifies that a ProofLookupVector proof is correct -func VerifyLookupVector(vk kzg.VerifyingKey, proof ProofLookupVector) error { - - // hash function that is used for Fiat Shamir - hFunc := sha256.New() - - // transcript to derive the challenge - fs := fiatshamir.NewTranscript(hFunc, "beta", "gamma", "alpha", "nu") - - // derive the various challenges - beta, err := deriveRandomness(fs, "beta", &proof.t, &proof.f, &proof.h1, &proof.h2) - if err != nil { - return err - } - - gamma, err := deriveRandomness(fs, "gamma") - if err != nil { - return err - } - - alpha, err := deriveRandomness(fs, "alpha", &proof.z) - if err != nil { - return err - } - - nu, err := deriveRandomness(fs, "nu", &proof.h) - if err != nil { - return err - } - - // check opening proofs - err = kzg.BatchVerifySinglePoint( - []kzg.Digest{ - proof.h1, - proof.h2, - proof.t, - proof.z, - proof.f, - proof.h, - }, - &proof.BatchedProof, - nu, - hFunc, - vk, - ) - if err != nil { - return err - } - - // shift the point and verify shifted proof - var shiftedNu fr.Element - shiftedNu.Mul(&nu, &proof.g) - err = kzg.BatchVerifySinglePoint( - []kzg.Digest{ - proof.h1, - proof.h2, - proof.t, - proof.z, - }, - &proof.BatchedProofShifted, - shiftedNu, - hFunc, - vk, - ) - if err != nil { - return err - } - - // check the generator is correct - var checkOrder, one fr.Element - one.SetOne() - checkOrder.Exp(proof.g, big.NewInt(int64(proof.size/2))) - if checkOrder.Equal(&one) { - return ErrGenerator - } - checkOrder.Square(&checkOrder) - if !checkOrder.Equal(&one) { - return ErrGenerator - } - - // check polynomial relation using Schwartz Zippel - var lhs, rhs, nun, g, _g, a, v, w fr.Element - g.Exp(proof.g, big.NewInt(int64(proof.size-1))) - - v.Add(&one, &beta) - w.Mul(&v, &gamma) - - // h(ν) where - // h = (xⁿ⁻¹-1)*z*(1+β)*(γ+f)*(γ(1+β) + t+ β*t(gX)) - - // (xⁿ⁻¹-1)*z(gX)*(γ(1+β) + h₁ + β*h₁(gX))*(γ(1+β) + h₂ + β*h₂(gX) ) - lhs.Sub(&nu, &g). // (ν-gⁿ⁻¹) - Mul(&lhs, &proof.BatchedProof.ClaimedValues[3]). - Mul(&lhs, &v) - a.Add(&gamma, &proof.BatchedProof.ClaimedValues[4]) - lhs.Mul(&lhs, &a) - a.Mul(&beta, &proof.BatchedProofShifted.ClaimedValues[2]). - Add(&a, &proof.BatchedProof.ClaimedValues[2]). - Add(&a, &w) - lhs.Mul(&lhs, &a) - - rhs.Sub(&nu, &g). - Mul(&rhs, &proof.BatchedProofShifted.ClaimedValues[3]) - a.Mul(&beta, &proof.BatchedProofShifted.ClaimedValues[0]). - Add(&a, &proof.BatchedProof.ClaimedValues[0]). - Add(&a, &w) - rhs.Mul(&rhs, &a) - a.Mul(&beta, &proof.BatchedProofShifted.ClaimedValues[1]). - Add(&a, &proof.BatchedProof.ClaimedValues[1]). - Add(&a, &w) - rhs.Mul(&rhs, &a) - - lhs.Sub(&lhs, &rhs) - - // check consistency of bounds - var l0, ln, d1, d2 fr.Element - l0.Exp(nu, big.NewInt(int64(proof.size))).Sub(&l0, &one) - ln.Set(&l0) - d1.Sub(&nu, &one) - d2.Sub(&nu, &g) - l0.Div(&l0, &d1) // (νⁿ-1)/(ν-1) - ln.Div(&ln, &d2) // (νⁿ-1)/(ν-gⁿ⁻¹) - - // l₀*(z-1) = (νⁿ-1)/(ν-1)*(z-1) - var l0z fr.Element - l0z.Sub(&proof.BatchedProof.ClaimedValues[3], &one). - Mul(&l0z, &l0) - - // lₙ*(z-1) = (νⁿ-1)/(ν-gⁿ⁻¹)*(z-1) - var lnz fr.Element - lnz.Sub(&proof.BatchedProof.ClaimedValues[3], &one). - Mul(&ln, &lnz) - - // lₙ*(h1 - h₂(g.x)) - var lnh1h2 fr.Element - lnh1h2.Sub(&proof.BatchedProof.ClaimedValues[0], &proof.BatchedProofShifted.ClaimedValues[1]). - Mul(&lnh1h2, &ln) - - // fold the numerator - lnh1h2.Mul(&lnh1h2, &alpha). - Add(&lnh1h2, &lnz). - Mul(&lnh1h2, &alpha). - Add(&lnh1h2, &l0z). - Mul(&lnh1h2, &alpha). - Add(&lnh1h2, &lhs) - - // (xⁿ-1) * h(x) evaluated at ν - nun.Exp(nu, big.NewInt(int64(proof.size))) - _g.Sub(&nun, &one) - _g.Mul(&proof.BatchedProof.ClaimedValues[5], &_g) - if !lnh1h2.Equal(&_g) { - return ErrPlookupVerification - } - - return nil -} diff --git a/ecc/bls12-378/fr/polynomial/doc.go b/ecc/bls12-378/fr/polynomial/doc.go deleted file mode 100644 index 747da41f2c..0000000000 --- a/ecc/bls12-378/fr/polynomial/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package polynomial provides polynomial methods and commitment schemes. -package polynomial diff --git a/ecc/bls12-378/fr/polynomial/multilin.go b/ecc/bls12-378/fr/polynomial/multilin.go deleted file mode 100644 index 2f916dc2c2..0000000000 --- a/ecc/bls12-378/fr/polynomial/multilin.go +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package polynomial - -import ( - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/utils" - "math/bits" -) - -// MultiLin tracks the values of a (dense i.e. not sparse) multilinear polynomial -// The variables are X₁ through Xₙ where n = log(len(.)) -// .[∑ᵢ 2ⁱ⁻¹ bₙ₋ᵢ] = the polynomial evaluated at (b₁, b₂, ..., bₙ) -// It is understood that any hypercube evaluation can be extrapolated to a multilinear polynomial -type MultiLin []fr.Element - -// Fold is partial evaluation function k[X₁, X₂, ..., Xₙ] → k[X₂, ..., Xₙ] by setting X₁=r -func (m *MultiLin) Fold(r fr.Element) { - mid := len(*m) / 2 - - bottom, top := (*m)[:mid], (*m)[mid:] - - var t fr.Element // no need to update the top part - - // updating bookkeeping table - // knowing that the polynomial f ∈ (k[X₂, ..., Xₙ])[X₁] is linear, we would get f(r) = f(0) + r(f(1) - f(0)) - // the following loop computes the evaluations of f(r) accordingly: - // f(r, b₂, ..., bₙ) = f(0, b₂, ..., bₙ) + r(f(1, b₂, ..., bₙ) - f(0, b₂, ..., bₙ)) - for i := 0; i < mid; i++ { - // table[i] ← table[i] + r (table[i + mid] - table[i]) - t.Sub(&top[i], &bottom[i]) - t.Mul(&t, &r) - bottom[i].Add(&bottom[i], &t) - } - - *m = (*m)[:mid] -} - -func (m *MultiLin) FoldParallel(r fr.Element) utils.Task { - mid := len(*m) / 2 - bottom, top := (*m)[:mid], (*m)[mid:] - - *m = bottom - - return func(start, end int) { - var t fr.Element // no need to update the top part - for i := start; i < end; i++ { - // table[i] ← table[i] + r (table[i + mid] - table[i]) - t.Sub(&top[i], &bottom[i]) - t.Mul(&t, &r) - bottom[i].Add(&bottom[i], &t) - } - } -} - -func (m MultiLin) Sum() fr.Element { - s := m[0] - for i := 1; i < len(m); i++ { - s.Add(&s, &m[i]) - } - return s -} - -func _clone(m MultiLin, p *Pool) MultiLin { - if p == nil { - return m.Clone() - } else { - return p.Clone(m) - } -} - -func _dump(m MultiLin, p *Pool) { - if p != nil { - p.Dump(m) - } -} - -// Evaluate extrapolate the value of the multilinear polynomial corresponding to m -// on the given coordinates -func (m MultiLin) Evaluate(coordinates []fr.Element, p *Pool) fr.Element { - // Folding is a mutating operation - bkCopy := _clone(m, p) - - // Evaluate step by step through repeated folding (i.e. evaluation at the first remaining variable) - for _, r := range coordinates { - bkCopy.Fold(r) - } - - result := bkCopy[0] - - _dump(bkCopy, p) - return result -} - -// Clone creates a deep copy of a bookkeeping table. -// Both multilinear interpolation and sumcheck require folding an underlying -// array, but folding changes the array. To do both one requires a deep copy -// of the bookkeeping table. -func (m MultiLin) Clone() MultiLin { - res := make(MultiLin, len(m)) - copy(res, m) - return res -} - -// Add two bookKeepingTables -func (m *MultiLin) Add(left, right MultiLin) { - size := len(left) - // Check that left and right have the same size - if len(right) != size || len(*m) != size { - panic("left, right and destination must have the right size") - } - - // Add elementwise - for i := 0; i < size; i++ { - (*m)[i].Add(&left[i], &right[i]) - } -} - -// EvalEq computes Eq(q₁, ... , qₙ, h₁, ... , hₙ) = Π₁ⁿ Eq(qᵢ, hᵢ) -// where Eq(x,y) = xy + (1-x)(1-y) = 1 - x - y + xy + xy interpolates -// -// _________________ -// | | | -// | 0 | 1 | -// |_______|_______| -// y | | | -// | 1 | 0 | -// |_______|_______| -// -// x -// -// In other words the polynomial evaluated here is the multilinear extrapolation of -// one that evaluates to q' == h' for vectors q', h' of binary values -func EvalEq(q, h []fr.Element) fr.Element { - var res, nxt, one, sum fr.Element - one.SetOne() - for i := 0; i < len(q); i++ { - nxt.Mul(&q[i], &h[i]) // nxt <- qᵢ * hᵢ - nxt.Double(&nxt) // nxt <- 2 * qᵢ * hᵢ - nxt.Add(&nxt, &one) // nxt <- 1 + 2 * qᵢ * hᵢ - sum.Add(&q[i], &h[i]) // sum <- qᵢ + hᵢ TODO: Why not subtract one by one from nxt? More parallel? - - if i == 0 { - res.Sub(&nxt, &sum) // nxt <- 1 + 2 * qᵢ * hᵢ - qᵢ - hᵢ - } else { - nxt.Sub(&nxt, &sum) // nxt <- 1 + 2 * qᵢ * hᵢ - qᵢ - hᵢ - res.Mul(&res, &nxt) // res <- res * nxt - } - } - return res -} - -// Eq sets m to the representation of the polynomial Eq(q₁, ..., qₙ, *, ..., *) × m[0] -func (m *MultiLin) Eq(q []fr.Element) { - n := len(q) - - if len(*m) != 1<= 0; i-- { - res.Mul(&res, v) - res.Add(&res, &(*p)[i]) - } - - return res -} - -// Clone returns a copy of the polynomial -func (p *Polynomial) Clone() Polynomial { - _p := make(Polynomial, len(*p)) - copy(_p, *p) - return _p -} - -// Set to another polynomial -func (p *Polynomial) Set(p1 Polynomial) { - if len(*p) != len(p1) { - *p = p1.Clone() - return - } - - for i := 0; i < len(p1); i++ { - (*p)[i].Set(&p1[i]) - } -} - -// AddConstantInPlace adds a constant to the polynomial, modifying p -func (p *Polynomial) AddConstantInPlace(c *fr.Element) { - for i := 0; i < len(*p); i++ { - (*p)[i].Add(&(*p)[i], c) - } -} - -// SubConstantInPlace subs a constant to the polynomial, modifying p -func (p *Polynomial) SubConstantInPlace(c *fr.Element) { - for i := 0; i < len(*p); i++ { - (*p)[i].Sub(&(*p)[i], c) - } -} - -// ScaleInPlace multiplies p by v, modifying p -func (p *Polynomial) ScaleInPlace(c *fr.Element) { - for i := 0; i < len(*p); i++ { - (*p)[i].Mul(&(*p)[i], c) - } -} - -// Scale multiplies p0 by v, storing the result in p -func (p *Polynomial) Scale(c *fr.Element, p0 Polynomial) { - if len(*p) != len(p0) { - *p = make(Polynomial, len(p0)) - } - for i := 0; i < len(p0); i++ { - (*p)[i].Mul(c, &p0[i]) - } -} - -// Add adds p1 to p2 -// This function allocates a new slice unless p == p1 or p == p2 -func (p *Polynomial) Add(p1, p2 Polynomial) *Polynomial { - - bigger := p1 - smaller := p2 - if len(bigger) < len(smaller) { - bigger, smaller = smaller, bigger - } - - if len(*p) == len(bigger) && (&(*p)[0] == &bigger[0]) { - for i := 0; i < len(smaller); i++ { - (*p)[i].Add(&(*p)[i], &smaller[i]) - } - return p - } - - if len(*p) == len(smaller) && (&(*p)[0] == &smaller[0]) { - for i := 0; i < len(smaller); i++ { - (*p)[i].Add(&(*p)[i], &bigger[i]) - } - *p = append(*p, bigger[len(smaller):]...) - return p - } - - res := make(Polynomial, len(bigger)) - copy(res, bigger) - for i := 0; i < len(smaller); i++ { - res[i].Add(&res[i], &smaller[i]) - } - *p = res - return p -} - -// Sub subtracts p2 from p1 -// TODO make interface more consistent with Add -func (p *Polynomial) Sub(p1, p2 Polynomial) *Polynomial { - if len(p1) != len(p2) || len(p2) != len(*p) { - return nil - } - for i := 0; i < len(*p); i++ { - (*p)[i].Sub(&p1[i], &p2[i]) - } - return p -} - -// Equal checks equality between two polynomials -func (p *Polynomial) Equal(p1 Polynomial) bool { - if (*p == nil) != (p1 == nil) { - return false - } - - if len(*p) != len(p1) { - return false - } - - for i := range p1 { - if !(*p)[i].Equal(&p1[i]) { - return false - } - } - - return true -} - -func (p Polynomial) SetZero() { - for i := 0; i < len(p); i++ { - p[i].SetZero() - } -} - -func (p Polynomial) Text(base int) string { - - var builder strings.Builder - - first := true - for d := len(p) - 1; d >= 0; d-- { - if p[d].IsZero() { - continue - } - - pD := p[d] - pDText := pD.Text(base) - - initialLen := builder.Len() - - if pDText[0] == '-' { - pDText = pDText[1:] - if first { - builder.WriteString("-") - } else { - builder.WriteString(" - ") - } - } else if !first { - builder.WriteString(" + ") - } - - first = false - - if !pD.IsOne() || d == 0 { - builder.WriteString(pDText) - } - - if builder.Len()-initialLen > 10 { - builder.WriteString("×") - } - - if d != 0 { - builder.WriteString("X") - } - if d > 1 { - builder.WriteString( - utils.ToSuperscript(strconv.Itoa(d)), - ) - } - - } - - if first { - return "0" - } - - return builder.String() -} diff --git a/ecc/bls12-378/fr/polynomial/polynomial_test.go b/ecc/bls12-378/fr/polynomial/polynomial_test.go deleted file mode 100644 index 3a10109606..0000000000 --- a/ecc/bls12-378/fr/polynomial/polynomial_test.go +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package polynomial - -import ( - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/stretchr/testify/assert" - "math/big" - "testing" -) - -func TestPolynomialEval(t *testing.T) { - - // build polynomial - f := make(Polynomial, 20) - for i := 0; i < 20; i++ { - f[i].SetOne() - } - - // random value - var point fr.Element - point.SetRandom() - - // compute manually f(val) - var expectedEval, one, den fr.Element - var expo big.Int - one.SetOne() - expo.SetUint64(20) - expectedEval.Exp(point, &expo). - Sub(&expectedEval, &one) - den.Sub(&point, &one) - expectedEval.Div(&expectedEval, &den) - - // compute purported evaluation - purportedEval := f.Eval(&point) - - // check - if !purportedEval.Equal(&expectedEval) { - t.Fatal("polynomial evaluation failed") - } -} - -func TestPolynomialAddConstantInPlace(t *testing.T) { - - // build polynomial - f := make(Polynomial, 20) - for i := 0; i < 20; i++ { - f[i].SetOne() - } - - // constant to add - var c fr.Element - c.SetRandom() - - // add constant - f.AddConstantInPlace(&c) - - // check - var expectedCoeffs, one fr.Element - one.SetOne() - expectedCoeffs.Add(&one, &c) - for i := 0; i < 20; i++ { - if !f[i].Equal(&expectedCoeffs) { - t.Fatal("AddConstantInPlace failed") - } - } -} - -func TestPolynomialSubConstantInPlace(t *testing.T) { - - // build polynomial - f := make(Polynomial, 20) - for i := 0; i < 20; i++ { - f[i].SetOne() - } - - // constant to sub - var c fr.Element - c.SetRandom() - - // sub constant - f.SubConstantInPlace(&c) - - // check - var expectedCoeffs, one fr.Element - one.SetOne() - expectedCoeffs.Sub(&one, &c) - for i := 0; i < 20; i++ { - if !f[i].Equal(&expectedCoeffs) { - t.Fatal("SubConstantInPlace failed") - } - } -} - -func TestPolynomialScaleInPlace(t *testing.T) { - - // build polynomial - f := make(Polynomial, 20) - for i := 0; i < 20; i++ { - f[i].SetOne() - } - - // constant to scale by - var c fr.Element - c.SetRandom() - - // scale by constant - f.ScaleInPlace(&c) - - // check - for i := 0; i < 20; i++ { - if !f[i].Equal(&c) { - t.Fatal("ScaleInPlace failed") - } - } - -} - -func TestPolynomialAdd(t *testing.T) { - - // build unbalanced polynomials - f1 := make(Polynomial, 20) - f1Backup := make(Polynomial, 20) - for i := 0; i < 20; i++ { - f1[i].SetOne() - f1Backup[i].SetOne() - } - f2 := make(Polynomial, 10) - f2Backup := make(Polynomial, 10) - for i := 0; i < 10; i++ { - f2[i].SetOne() - f2Backup[i].SetOne() - } - - // expected result - var one, two fr.Element - one.SetOne() - two.Double(&one) - expectedSum := make(Polynomial, 20) - for i := 0; i < 10; i++ { - expectedSum[i].Set(&two) - } - for i := 10; i < 20; i++ { - expectedSum[i].Set(&one) - } - - // caller is empty - var g Polynomial - g.Add(f1, f2) - if !g.Equal(expectedSum) { - t.Fatal("add polynomials fails") - } - if !f1.Equal(f1Backup) { - t.Fatal("side effect, f1 should not have been modified") - } - if !f2.Equal(f2Backup) { - t.Fatal("side effect, f2 should not have been modified") - } - - // all operands are distinct - _f1 := f1.Clone() - _f1.Add(f1, f2) - if !_f1.Equal(expectedSum) { - t.Fatal("add polynomials fails") - } - if !f1.Equal(f1Backup) { - t.Fatal("side effect, f1 should not have been modified") - } - if !f2.Equal(f2Backup) { - t.Fatal("side effect, f2 should not have been modified") - } - - // first operand = caller - _f1 = f1.Clone() - _f2 := f2.Clone() - _f1.Add(_f1, _f2) - if !_f1.Equal(expectedSum) { - t.Fatal("add polynomials fails") - } - if !_f2.Equal(f2Backup) { - t.Fatal("side effect, _f2 should not have been modified") - } - - // second operand = caller - _f1 = f1.Clone() - _f2 = f2.Clone() - _f1.Add(_f2, _f1) - if !_f1.Equal(expectedSum) { - t.Fatal("add polynomials fails") - } - if !_f2.Equal(f2Backup) { - t.Fatal("side effect, _f2 should not have been modified") - } -} - -func TestPolynomialText(t *testing.T) { - var one, negTwo fr.Element - one.SetOne() - negTwo.SetInt64(-2) - - p := Polynomial{one, negTwo, one} - - assert.Equal(t, "X² - 2X + 1", p.Text(10)) -} diff --git a/ecc/bls12-378/fr/polynomial/pool.go b/ecc/bls12-378/fr/polynomial/pool.go deleted file mode 100644 index 9f0787b387..0000000000 --- a/ecc/bls12-378/fr/polynomial/pool.go +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package polynomial - -import ( - "encoding/json" - "fmt" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "runtime" - "sort" - "sync" - "unsafe" -) - -// Memory management for polynomials -// WARNING: This is not thread safe TODO: Make sure that is not a problem -// TODO: There is a lot of "unsafe" memory management here and needs to be vetted thoroughly - -type sizedPool struct { - maxN int - pool sync.Pool - stats poolStats -} - -type inUseData struct { - allocatedFor []uintptr - pool *sizedPool -} - -type Pool struct { - //lock sync.Mutex - inUse sync.Map - subPools []sizedPool -} - -func (p *sizedPool) get(n int) *fr.Element { - p.stats.make(n) - return p.pool.Get().(*fr.Element) -} - -func (p *sizedPool) put(ptr *fr.Element) { - p.stats.dump() - p.pool.Put(ptr) -} - -func NewPool(maxN ...int) (pool Pool) { - - sort.Ints(maxN) - pool = Pool{ - subPools: make([]sizedPool, len(maxN)), - } - - for i := range pool.subPools { - subPool := &pool.subPools[i] - subPool.maxN = maxN[i] - subPool.pool = sync.Pool{ - New: func() interface{} { - subPool.stats.Allocated++ - return getDataPointer(make([]fr.Element, 0, subPool.maxN)) - }, - } - } - return -} - -func (p *Pool) findCorrespondingPool(n int) *sizedPool { - poolI := 0 - for poolI < len(p.subPools) && n > p.subPools[poolI].maxN { - poolI++ - } - return &p.subPools[poolI] // out of bounds error here would mean that n is too large -} - -func (p *Pool) Make(n int) []fr.Element { - pool := p.findCorrespondingPool(n) - ptr := pool.get(n) - p.addInUse(ptr, pool) - return unsafe.Slice(ptr, n) -} - -// Dump dumps a set of polynomials into the pool -func (p *Pool) Dump(slices ...[]fr.Element) { - for _, slice := range slices { - ptr := getDataPointer(slice) - if metadata, ok := p.inUse.Load(ptr); ok { - p.inUse.Delete(ptr) - metadata.(inUseData).pool.put(ptr) - } else { - panic("attempting to dump a slice not created by the pool") - } - } -} - -func (p *Pool) addInUse(ptr *fr.Element, pool *sizedPool) { - pcs := make([]uintptr, 2) - n := runtime.Callers(3, pcs) - - if prevPcs, ok := p.inUse.Load(ptr); ok { // TODO: remove if unnecessary for security - panic(fmt.Errorf("re-allocated non-dumped slice, previously allocated at %v", runtime.CallersFrames(prevPcs.(inUseData).allocatedFor))) - } - p.inUse.Store(ptr, inUseData{ - allocatedFor: pcs[:n], - pool: pool, - }) -} - -func printFrame(frame runtime.Frame) { - fmt.Printf("\t%s line %d, function %s\n", frame.File, frame.Line, frame.Function) -} - -func (p *Pool) printInUse() { - fmt.Println("slices never dumped allocated at:") - p.inUse.Range(func(_, pcs any) bool { - fmt.Println("-------------------------") - - var frame runtime.Frame - frames := runtime.CallersFrames(pcs.(inUseData).allocatedFor) - more := true - for more { - frame, more = frames.Next() - printFrame(frame) - } - return true - }) -} - -type poolStats struct { - Used int - Allocated int - ReuseRate float64 - InUse int - GreatestNUsed int - SmallestNUsed int -} - -type poolsStats struct { - SubPools []poolStats - InUse int -} - -func (s *poolStats) make(n int) { - s.Used++ - s.InUse++ - if n > s.GreatestNUsed { - s.GreatestNUsed = n - } - if s.SmallestNUsed == 0 || s.SmallestNUsed > n { - s.SmallestNUsed = n - } -} - -func (s *poolStats) dump() { - s.InUse-- -} - -func (s *poolStats) finalize() { - s.ReuseRate = float64(s.Used) / float64(s.Allocated) -} - -func getDataPointer(slice []fr.Element) *fr.Element { - return (*fr.Element)(unsafe.SliceData(slice)) -} - -func (p *Pool) PrintPoolStats() { - InUse := 0 - subStats := make([]poolStats, len(p.subPools)) - for i := range p.subPools { - subPool := &p.subPools[i] - subPool.stats.finalize() - subStats[i] = subPool.stats - InUse += subPool.stats.InUse - } - - stats := poolsStats{ - SubPools: subStats, - InUse: InUse, - } - serialized, _ := json.MarshalIndent(stats, "", " ") - fmt.Println(string(serialized)) - p.printInUse() -} - -func (p *Pool) Clone(slice []fr.Element) []fr.Element { - res := p.Make(len(slice)) - copy(res, slice) - return res -} diff --git a/ecc/bls12-378/fr/sumcheck/sumcheck.go b/ecc/bls12-378/fr/sumcheck/sumcheck.go deleted file mode 100644 index 88c1ed8612..0000000000 --- a/ecc/bls12-378/fr/sumcheck/sumcheck.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package sumcheck - -import ( - "fmt" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - "strconv" -) - -// This does not make use of parallelism and represents polynomials as lists of coefficients -// It is currently geared towards arithmetic hashes. Once we have a more unified hash function interface, this can be generified. - -// Claims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. -// Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) -type Claims interface { - Combine(a fr.Element) polynomial.Polynomial // Combine into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. - Next(fr.Element) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ - VarsNum() int //number of variables - ClaimsNum() int //number of claims - ProveFinalEval(r []fr.Element) interface{} //in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof -} - -// LazyClaims is the Claims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. -type LazyClaims interface { - ClaimsNum() int // ClaimsNum = m - VarsNum() int // VarsNum = n - CombinedSum(a fr.Element) fr.Element // CombinedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - Degree(i int) int //Degree of the total claim in the i'th variable - VerifyFinalEval(r []fr.Element, combinationCoeff fr.Element, purportedValue fr.Element, proof interface{}) error -} - -// Proof of a multi-sumcheck statement. -type Proof struct { - PartialSumPolys []polynomial.Polynomial `json:"partialSumPolys"` - FinalEvalProof interface{} `json:"finalEvalProof"` //in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof -} - -func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) (challengeNames []string, err error) { - numChallenges := varsNum - if claimsNum >= 2 { - numChallenges++ - } - challengeNames = make([]string, numChallenges) - if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "comb" - } - prefix := settings.Prefix + "pSP." - for i := 0; i < varsNum; i++ { - challengeNames[i+numChallenges-varsNum] = prefix + strconv.Itoa(i) - } - if settings.Transcript == nil { - transcript := fiatshamir.NewTranscript(settings.Hash, challengeNames...) - settings.Transcript = transcript - } - - for i := range settings.BaseChallenges { - if err = settings.Transcript.Bind(challengeNames[0], settings.BaseChallenges[i]); err != nil { - return - } - } - return -} - -func next(transcript *fiatshamir.Transcript, bindings []fr.Element, remainingChallengeNames *[]string) (fr.Element, error) { - challengeName := (*remainingChallengeNames)[0] - for i := range bindings { - bytes := bindings[i].Bytes() - if err := transcript.Bind(challengeName, bytes[:]); err != nil { - return fr.Element{}, err - } - } - var res fr.Element - bytes, err := transcript.ComputeChallenge(challengeName) - res.SetBytes(bytes) - - *remainingChallengeNames = (*remainingChallengeNames)[1:] - - return res, err -} - -// Prove create a non-interactive sumcheck proof -func Prove(claims Claims, transcriptSettings fiatshamir.Settings) (Proof, error) { - - var proof Proof - remainingChallengeNames, err := setupTranscript(claims.ClaimsNum(), claims.VarsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return proof, err - } - - var combinationCoeff fr.Element - if claims.ClaimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { - return proof, err - } - } - - varsNum := claims.VarsNum() - proof.PartialSumPolys = make([]polynomial.Polynomial, varsNum) - proof.PartialSumPolys[0] = claims.Combine(combinationCoeff) - challenges := make([]fr.Element, varsNum) - - for j := 0; j+1 < varsNum; j++ { - if challenges[j], err = next(transcript, proof.PartialSumPolys[j], &remainingChallengeNames); err != nil { - return proof, err - } - proof.PartialSumPolys[j+1] = claims.Next(challenges[j]) - } - - if challenges[varsNum-1], err = next(transcript, proof.PartialSumPolys[varsNum-1], &remainingChallengeNames); err != nil { - return proof, err - } - - proof.FinalEvalProof = claims.ProveFinalEval(challenges) - - return proof, nil -} - -func Verify(claims LazyClaims, proof Proof, transcriptSettings fiatshamir.Settings) error { - remainingChallengeNames, err := setupTranscript(claims.ClaimsNum(), claims.VarsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return err - } - - var combinationCoeff fr.Element - - if claims.ClaimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { - return err - } - } - - r := make([]fr.Element, claims.VarsNum()) - - // Just so that there is enough room for gJ to be reused - maxDegree := claims.Degree(0) - for j := 1; j < claims.VarsNum(); j++ { - if d := claims.Degree(j); d > maxDegree { - maxDegree = d - } - } - gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.VarsNum() - gJR := claims.CombinedSum(combinationCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) - - for j := 0; j < claims.VarsNum(); j++ { - if len(proof.PartialSumPolys[j]) != claims.Degree(j) { - return fmt.Errorf("malformed proof") - } - copy(gJ[1:], proof.PartialSumPolys[j]) - gJ[0].Sub(&gJR, &proof.PartialSumPolys[j][0]) // Requirement that gⱼ(0) + gⱼ(1) = gⱼ₋₁(r) - // gJ is ready - - //Prepare for the next iteration - if r[j], err = next(transcript, proof.PartialSumPolys[j], &remainingChallengeNames); err != nil { - return err - } - // This is an extremely inefficient way of interpolating. TODO: Interpolate without symbolically computing a polynomial - gJCoeffs := polynomial.InterpolateOnRange(gJ[:(claims.Degree(j) + 1)]) - gJR = gJCoeffs.Eval(&r[j]) - } - - return claims.VerifyFinalEval(r, combinationCoeff, gJR, proof.FinalEvalProof) -} diff --git a/ecc/bls12-378/fr/sumcheck/sumcheck_test.go b/ecc/bls12-378/fr/sumcheck/sumcheck_test.go deleted file mode 100644 index d030435359..0000000000 --- a/ecc/bls12-378/fr/sumcheck/sumcheck_test.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package sumcheck - -import ( - "fmt" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/polynomial" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/test_vector_utils" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - "github.com/stretchr/testify/assert" - "hash" - "math/bits" - "strings" - "testing" -) - -type singleMultilinClaim struct { - g polynomial.MultiLin -} - -func (c singleMultilinClaim) ProveFinalEval(r []fr.Element) interface{} { - return nil // verifier can compute the final eval itself -} - -func (c singleMultilinClaim) VarsNum() int { - return bits.TrailingZeros(uint(len(c.g))) -} - -func (c singleMultilinClaim) ClaimsNum() int { - return 1 -} - -func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { - sum := g[len(g)/2] - for i := len(g)/2 + 1; i < len(g); i++ { - sum.Add(&sum, &g[i]) - } - return []fr.Element{sum} -} - -func (c singleMultilinClaim) Combine(fr.Element) polynomial.Polynomial { - return sumForX1One(c.g) -} - -func (c *singleMultilinClaim) Next(r fr.Element) polynomial.Polynomial { - c.g.Fold(r) - return sumForX1One(c.g) -} - -type singleMultilinLazyClaim struct { - g polynomial.MultiLin - claimedSum fr.Element -} - -func (c singleMultilinLazyClaim) VerifyFinalEval(r []fr.Element, combinationCoeff fr.Element, purportedValue fr.Element, proof interface{}) error { - val := c.g.Evaluate(r, nil) - if val.Equal(&purportedValue) { - return nil - } - return fmt.Errorf("mismatch") -} - -func (c singleMultilinLazyClaim) CombinedSum(combinationCoeffs fr.Element) fr.Element { - return c.claimedSum -} - -func (c singleMultilinLazyClaim) Degree(i int) int { - return 1 -} - -func (c singleMultilinLazyClaim) ClaimsNum() int { - return 1 -} - -func (c singleMultilinLazyClaim) VarsNum() int { - return bits.TrailingZeros(uint(len(c.g))) -} - -func testSumcheckSingleClaimMultilin(polyInt []uint64, hashGenerator func() hash.Hash) error { - poly := make(polynomial.MultiLin, len(polyInt)) - for i, n := range polyInt { - poly[i].SetUint64(n) - } - - claim := singleMultilinClaim{g: poly.Clone()} - - proof, err := Prove(&claim, fiatshamir.WithHash(hashGenerator())) - if err != nil { - return err - } - - var sb strings.Builder - for _, p := range proof.PartialSumPolys { - - sb.WriteString("\t{") - for i := 0; i < len(p); i++ { - sb.WriteString(p[i].String()) - if i+1 < len(p) { - sb.WriteString(", ") - } - } - sb.WriteString("}\n") - } - - lazyClaim := singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if err = Verify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())); err != nil { - return err - } - - proof.PartialSumPolys[0][0].Add(&proof.PartialSumPolys[0][0], test_vector_utils.ToElement(1)) - lazyClaim = singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if Verify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())) == nil { - return fmt.Errorf("bad proof accepted") - } - return nil -} - -func TestSumcheckDeterministicHashSingleClaimMultilin(t *testing.T) { - //printMsws(36) - - polys := [][]uint64{ - {1, 2, 3, 4}, // 1 + 2X₁ + X₂ - {1, 2, 3, 4, 5, 6, 7, 8}, // 1 + 4X₁ + 2X₂ + X₃ - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, // 1 + 8X₁ + 4X₂ + 2X₃ + X₄ - } - - const MaxStep = 4 - const MaxStart = 4 - hashGens := make([]func() hash.Hash, 0, MaxStart*MaxStep) - - for step := 0; step < MaxStep; step++ { - for startState := 0; startState < MaxStart; startState++ { - if step == 0 && startState == 1 { // unlucky case where a bad proof would be accepted - continue - } - hashGens = append(hashGens, test_vector_utils.NewMessageCounterGenerator(startState, step)) - } - } - - for _, poly := range polys { - for _, hashGen := range hashGens { - assert.NoError(t, testSumcheckSingleClaimMultilin(poly, hashGen), - "failed with poly %v and hashGen %v", poly, hashGen()) - } - } -} diff --git a/ecc/bls12-378/fr/test_vector_utils/test_vector_utils.go b/ecc/bls12-378/fr/test_vector_utils/test_vector_utils.go deleted file mode 100644 index 4f9f133caf..0000000000 --- a/ecc/bls12-378/fr/test_vector_utils/test_vector_utils.go +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package test_vector_utils - -import ( - "fmt" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/polynomial" - "hash" - "reflect" - "strings" -) - -func ToElement(i int64) *fr.Element { - var res fr.Element - res.SetInt64(i) - return &res -} - -type HashDescription map[string]interface{} - -func HashFromDescription(d HashDescription) (hash.Hash, error) { - if _type, ok := d["type"]; ok { - switch _type { - case "const": - startState := int64(d["val"].(float64)) - return &MessageCounter{startState: startState, step: 0, state: startState}, nil - default: - return nil, fmt.Errorf("unknown fake hash type \"%s\"", _type) - } - } - return nil, fmt.Errorf("hash description missing type") -} - -type MessageCounter struct { - startState int64 - state int64 - step int64 -} - -func (m *MessageCounter) Write(p []byte) (n int, err error) { - inputBlockSize := (len(p)-1)/fr.Bytes + 1 - m.state += int64(inputBlockSize) * m.step - return len(p), nil -} - -func (m *MessageCounter) Sum(b []byte) []byte { - inputBlockSize := (len(b)-1)/fr.Bytes + 1 - resI := m.state + int64(inputBlockSize)*m.step - var res fr.Element - res.SetInt64(int64(resI)) - resBytes := res.Bytes() - return resBytes[:] -} - -func (m *MessageCounter) Reset() { - m.state = m.startState -} - -func (m *MessageCounter) Size() int { - return fr.Bytes -} - -func (m *MessageCounter) BlockSize() int { - return fr.Bytes -} - -func NewMessageCounter(startState, step int) hash.Hash { - transcript := &MessageCounter{startState: int64(startState), state: int64(startState), step: int64(step)} - return transcript -} - -func NewMessageCounterGenerator(startState, step int) func() hash.Hash { - return func() hash.Hash { - return NewMessageCounter(startState, step) - } -} - -type ListHash []fr.Element - -func (h *ListHash) Write(p []byte) (n int, err error) { - return len(p), nil -} - -func (h *ListHash) Sum(b []byte) []byte { - res := (*h)[0].Bytes() - *h = (*h)[1:] - return res[:] -} - -func (h *ListHash) Reset() { -} - -func (h *ListHash) Size() int { - return fr.Bytes -} - -func (h *ListHash) BlockSize() int { - return fr.Bytes -} -func SetElement(z *fr.Element, value interface{}) (*fr.Element, error) { - - // TODO: Put this in element.SetString? - switch v := value.(type) { - case string: - - if sep := strings.Split(v, "/"); len(sep) == 2 { - var denom fr.Element - if _, err := z.SetString(sep[0]); err != nil { - return nil, err - } - if _, err := denom.SetString(sep[1]); err != nil { - return nil, err - } - denom.Inverse(&denom) - z.Mul(z, &denom) - return z, nil - } - - case float64: - asInt := int64(v) - if float64(asInt) != v { - return nil, fmt.Errorf("cannot currently parse float") - } - z.SetInt64(asInt) - return z, nil - } - - return z.SetInterface(value) -} - -func SliceToElementSlice[T any](slice []T) ([]fr.Element, error) { - elementSlice := make([]fr.Element, len(slice)) - for i, v := range slice { - if _, err := SetElement(&elementSlice[i], v); err != nil { - return nil, err - } - } - return elementSlice, nil -} - -func SliceEquals(a []fr.Element, b []fr.Element) error { - if len(a) != len(b) { - return fmt.Errorf("length mismatch %d≠%d", len(a), len(b)) - } - for i := range a { - if !a[i].Equal(&b[i]) { - return fmt.Errorf("at index %d: %s ≠ %s", i, a[i].String(), b[i].String()) - } - } - return nil -} - -func SliceSliceEquals(a [][]fr.Element, b [][]fr.Element) error { - if len(a) != len(b) { - return fmt.Errorf("length mismatch %d≠%d", len(a), len(b)) - } - for i := range a { - if err := SliceEquals(a[i], b[i]); err != nil { - return fmt.Errorf("at index %d: %w", i, err) - } - } - return nil -} - -func PolynomialSliceEquals(a []polynomial.Polynomial, b []polynomial.Polynomial) error { - if len(a) != len(b) { - return fmt.Errorf("length mismatch %d≠%d", len(a), len(b)) - } - for i := range a { - if err := SliceEquals(a[i], b[i]); err != nil { - return fmt.Errorf("at index %d: %w", i, err) - } - } - return nil -} - -func ElementToInterface(x *fr.Element) interface{} { - if i := x.BigInt(nil); i != nil { - return i - } - return x.Text(10) -} - -func ElementSliceToInterfaceSlice(x interface{}) []interface{} { - if x == nil { - return nil - } - - X := reflect.ValueOf(x) - - res := make([]interface{}, X.Len()) - for i := range res { - xI := X.Index(i).Interface().(fr.Element) - res[i] = ElementToInterface(&xI) - } - return res -} - -func ElementSliceSliceToInterfaceSliceSlice(x interface{}) [][]interface{} { - if x == nil { - return nil - } - - X := reflect.ValueOf(x) - - res := make([][]interface{}, X.Len()) - for i := range res { - res[i] = ElementSliceToInterfaceSlice(X.Index(i).Interface()) - } - - return res -} diff --git a/ecc/bls12-378/fr/vector.go b/ecc/bls12-378/fr/vector.go deleted file mode 100644 index 00ad8a8986..0000000000 --- a/ecc/bls12-378/fr/vector.go +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" - "runtime" - "strings" - "sync" - "sync/atomic" - "unsafe" -) - -// Vector represents a slice of Element. -// -// It implements the following interfaces: -// - Stringer -// - io.WriterTo -// - io.ReaderFrom -// - encoding.BinaryMarshaler -// - encoding.BinaryUnmarshaler -// - sort.Interface -type Vector []Element - -// MarshalBinary implements encoding.BinaryMarshaler -func (vector *Vector) MarshalBinary() (data []byte, err error) { - var buf bytes.Buffer - - if _, err = vector.WriteTo(&buf); err != nil { - return - } - return buf.Bytes(), nil -} - -// UnmarshalBinary implements encoding.BinaryUnmarshaler -func (vector *Vector) UnmarshalBinary(data []byte) error { - r := bytes.NewReader(data) - _, err := vector.ReadFrom(r) - return err -} - -// WriteTo implements io.WriterTo and writes a vector of big endian encoded Element. -// Length of the vector is encoded as a uint32 on the first 4 bytes. -func (vector *Vector) WriteTo(w io.Writer) (int64, error) { - // encode slice length - if err := binary.Write(w, binary.BigEndian, uint32(len(*vector))); err != nil { - return 0, err - } - - n := int64(4) - - var buf [Bytes]byte - for i := 0; i < len(*vector); i++ { - BigEndian.PutElement(&buf, (*vector)[i]) - m, err := w.Write(buf[:]) - n += int64(m) - if err != nil { - return n, err - } - } - return n, nil -} - -// AsyncReadFrom reads a vector of big endian encoded Element. -// Length of the vector must be encoded as a uint32 on the first 4 bytes. -// It consumes the needed bytes from the reader and returns the number of bytes read and an error if any. -// It also returns a channel that will be closed when the validation is done. -// The validation consist of checking that the elements are smaller than the modulus, and -// converting them to montgomery form. -func (vector *Vector) AsyncReadFrom(r io.Reader) (int64, error, chan error) { - chErr := make(chan error, 1) - var buf [Bytes]byte - if read, err := io.ReadFull(r, buf[:4]); err != nil { - close(chErr) - return int64(read), err, chErr - } - sliceLen := binary.BigEndian.Uint32(buf[:4]) - - n := int64(4) - (*vector) = make(Vector, sliceLen) - if sliceLen == 0 { - close(chErr) - return n, nil, chErr - } - - bSlice := unsafe.Slice((*byte)(unsafe.Pointer(&(*vector)[0])), sliceLen*Bytes) - read, err := io.ReadFull(r, bSlice) - n += int64(read) - if err != nil { - close(chErr) - return n, err, chErr - } - - go func() { - var cptErrors uint64 - // process the elements in parallel - execute(int(sliceLen), func(start, end int) { - - var z Element - for i := start; i < end; i++ { - // we have to set vector[i] - bstart := i * Bytes - bend := bstart + Bytes - b := bSlice[bstart:bend] - z[0] = binary.BigEndian.Uint64(b[24:32]) - z[1] = binary.BigEndian.Uint64(b[16:24]) - z[2] = binary.BigEndian.Uint64(b[8:16]) - z[3] = binary.BigEndian.Uint64(b[0:8]) - - if !z.smallerThanModulus() { - atomic.AddUint64(&cptErrors, 1) - return - } - z.toMont() - (*vector)[i] = z - } - }) - - if cptErrors > 0 { - chErr <- fmt.Errorf("async read: %d elements failed validation", cptErrors) - } - close(chErr) - }() - return n, nil, chErr -} - -// ReadFrom implements io.ReaderFrom and reads a vector of big endian encoded Element. -// Length of the vector must be encoded as a uint32 on the first 4 bytes. -func (vector *Vector) ReadFrom(r io.Reader) (int64, error) { - - var buf [Bytes]byte - if read, err := io.ReadFull(r, buf[:4]); err != nil { - return int64(read), err - } - sliceLen := binary.BigEndian.Uint32(buf[:4]) - - n := int64(4) - (*vector) = make(Vector, sliceLen) - - for i := 0; i < int(sliceLen); i++ { - read, err := io.ReadFull(r, buf[:]) - n += int64(read) - if err != nil { - return n, err - } - (*vector)[i], err = BigEndian.Element(&buf) - if err != nil { - return n, err - } - } - - return n, nil -} - -// String implements fmt.Stringer interface -func (vector Vector) String() string { - var sbb strings.Builder - sbb.WriteByte('[') - for i := 0; i < len(vector); i++ { - sbb.WriteString(vector[i].String()) - if i != len(vector)-1 { - sbb.WriteByte(',') - } - } - sbb.WriteByte(']') - return sbb.String() -} - -// Len is the number of elements in the collection. -func (vector Vector) Len() int { - return len(vector) -} - -// Less reports whether the element with -// index i should sort before the element with index j. -func (vector Vector) Less(i, j int) bool { - return vector[i].Cmp(&vector[j]) == -1 -} - -// Swap swaps the elements with indexes i and j. -func (vector Vector) Swap(i, j int) { - vector[i], vector[j] = vector[j], vector[i] -} - -// TODO @gbotrel make a public package out of that. -// execute executes the work function in parallel. -// this is copy paste from internal/parallel/parallel.go -// as we don't want to generate code importing internal/ -func execute(nbIterations int, work func(int, int), maxCpus ...int) { - - nbTasks := runtime.NumCPU() - if len(maxCpus) == 1 { - nbTasks = maxCpus[0] - if nbTasks < 1 { - nbTasks = 1 - } else if nbTasks > 512 { - nbTasks = 512 - } - } - - if nbTasks == 1 { - // no go routines - work(0, nbIterations) - return - } - - nbIterationsPerCpus := nbIterations / nbTasks - - // more CPUs than tasks: a CPU will work on exactly one iteration - if nbIterationsPerCpus < 1 { - nbIterationsPerCpus = 1 - nbTasks = nbIterations - } - - var wg sync.WaitGroup - - extraTasks := nbIterations - (nbTasks * nbIterationsPerCpus) - extraTasksOffset := 0 - - for i := 0; i < nbTasks; i++ { - wg.Add(1) - _start := i*nbIterationsPerCpus + extraTasksOffset - _end := _start + nbIterationsPerCpus - if extraTasks > 0 { - _end++ - extraTasks-- - extraTasksOffset++ - } - go func() { - work(_start, _end) - wg.Done() - }() - } - - wg.Wait() -} diff --git a/ecc/bls12-378/fr/vector_test.go b/ecc/bls12-378/fr/vector_test.go deleted file mode 100644 index e58f2d9a38..0000000000 --- a/ecc/bls12-378/fr/vector_test.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import ( - "bytes" - "github.com/stretchr/testify/require" - "reflect" - "sort" - "testing" -) - -func TestVectorSort(t *testing.T) { - assert := require.New(t) - - v := make(Vector, 3) - v[0].SetUint64(2) - v[1].SetUint64(3) - v[2].SetUint64(1) - - sort.Sort(v) - - assert.Equal("[1,2,3]", v.String()) -} - -func TestVectorRoundTrip(t *testing.T) { - assert := require.New(t) - - v1 := make(Vector, 3) - v1[0].SetUint64(2) - v1[1].SetUint64(3) - v1[2].SetUint64(1) - - b, err := v1.MarshalBinary() - assert.NoError(err) - - var v2, v3 Vector - - err = v2.UnmarshalBinary(b) - assert.NoError(err) - - err = v3.unmarshalBinaryAsync(b) - assert.NoError(err) - - assert.True(reflect.DeepEqual(v1, v2)) - assert.True(reflect.DeepEqual(v3, v2)) -} - -func TestVectorEmptyRoundTrip(t *testing.T) { - assert := require.New(t) - - v1 := make(Vector, 0) - - b, err := v1.MarshalBinary() - assert.NoError(err) - - var v2, v3 Vector - - err = v2.UnmarshalBinary(b) - assert.NoError(err) - - err = v3.unmarshalBinaryAsync(b) - assert.NoError(err) - - assert.True(reflect.DeepEqual(v1, v2)) - assert.True(reflect.DeepEqual(v3, v2)) -} - -func (vector *Vector) unmarshalBinaryAsync(data []byte) error { - r := bytes.NewReader(data) - _, err, chErr := vector.AsyncReadFrom(r) - if err != nil { - return err - } - return <-chErr -} diff --git a/ecc/bls12-378/g1.go b/ecc/bls12-378/g1.go deleted file mode 100644 index c484bfa141..0000000000 --- a/ecc/bls12-378/g1.go +++ /dev/null @@ -1,1207 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bls12378 - -import ( - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/internal/parallel" - "math/big" - "runtime" -) - -// G1Affine is a point in affine coordinates (x,y) -type G1Affine struct { - X, Y fp.Element -} - -// G1Jac is a point in Jacobian coordinates (x=X/Z², y=Y/Z³) -type G1Jac struct { - X, Y, Z fp.Element -} - -// g1JacExtended is a point in extended Jacobian coordinates (x=X/ZZ, y=Y/ZZZ, ZZ³=ZZZ²) -type g1JacExtended struct { - X, Y, ZZ, ZZZ fp.Element -} - -// ------------------------------------------------------------------------------------------------- -// Affine coordinates - -// Set sets p to a in affine coordinates. -func (p *G1Affine) Set(a *G1Affine) *G1Affine { - p.X, p.Y = a.X, a.Y - return p -} - -// setInfinity sets p to the infinity point, which is encoded as (0,0). -// N.B.: (0,0) is never on the curve for j=0 curves (Y²=X³+B). -func (p *G1Affine) setInfinity() *G1Affine { - p.X.SetZero() - p.Y.SetZero() - return p -} - -// ScalarMultiplication computes and returns p = [s]a -// where p and a are affine points. -func (p *G1Affine) ScalarMultiplication(a *G1Affine, s *big.Int) *G1Affine { - var _p G1Jac - _p.FromAffine(a) - _p.mulGLV(&_p, s) - p.FromJacobian(&_p) - return p -} - -// ScalarMultiplicationBase computes and returns p = [s]g -// where g is the affine point generating the prime subgroup. -func (p *G1Affine) ScalarMultiplicationBase(s *big.Int) *G1Affine { - var _p G1Jac - _p.mulGLV(&g1Gen, s) - p.FromJacobian(&_p) - return p -} - -// Add adds two points in affine coordinates. -// It uses the Jacobian addition with a.Z=b.Z=1 and converts the result to affine coordinates. -// -// https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-mmadd-2007-bl -func (p *G1Affine) Add(a, b *G1Affine) *G1Affine { - var q G1Jac - // a is infinity, return b - if a.IsInfinity() { - p.Set(b) - return p - } - // b is infinity, return a - if b.IsInfinity() { - p.Set(a) - return p - } - if a.X.Equal(&b.X) { - // if b == a, we double instead - if a.Y.Equal(&b.Y) { - q.DoubleMixed(a) - return p.FromJacobian(&q) - } else { - // if b == -a, we return 0 - return p.setInfinity() - } - } - var H, HH, I, J, r, V fp.Element - H.Sub(&b.X, &a.X) - HH.Square(&H) - I.Double(&HH).Double(&I) - J.Mul(&H, &I) - r.Sub(&b.Y, &a.Y) - r.Double(&r) - V.Mul(&a.X, &I) - q.X.Square(&r). - Sub(&q.X, &J). - Sub(&q.X, &V). - Sub(&q.X, &V) - q.Y.Sub(&V, &q.X). - Mul(&q.Y, &r) - J.Mul(&a.Y, &J).Double(&J) - q.Y.Sub(&q.Y, &J) - q.Z.Double(&H) - - return p.FromJacobian(&q) -} - -// Double doubles a point in affine coordinates. -// It converts the point to Jacobian coordinates, doubles it using Jacobian -// addition with a.Z=1, and converts it back to affine coordinates. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-mdbl-2007-bl -func (p *G1Affine) Double(a *G1Affine) *G1Affine { - var q G1Jac - q.FromAffine(a) - q.DoubleMixed(a) - p.FromJacobian(&q) - return p -} - -// Sub subtracts two points in affine coordinates. -// It uses a similar approach to Add, but negates the second point before adding. -func (p *G1Affine) Sub(a, b *G1Affine) *G1Affine { - var bneg G1Affine - bneg.Neg(b) - p.Add(a, &bneg) - return p -} - -// Equal tests if two points in affine coordinates are equal. -func (p *G1Affine) Equal(a *G1Affine) bool { - return p.X.Equal(&a.X) && p.Y.Equal(&a.Y) -} - -// Neg sets p to the affine negative point -a = (a.X, -a.Y). -func (p *G1Affine) Neg(a *G1Affine) *G1Affine { - p.X = a.X - p.Y.Neg(&a.Y) - return p -} - -// FromJacobian converts a point p1 from Jacobian to affine coordinates. -func (p *G1Affine) FromJacobian(p1 *G1Jac) *G1Affine { - - var a, b fp.Element - - if p1.Z.IsZero() { - p.X.SetZero() - p.Y.SetZero() - return p - } - - a.Inverse(&p1.Z) - b.Square(&a) - p.X.Mul(&p1.X, &b) - p.Y.Mul(&p1.Y, &b).Mul(&p.Y, &a) - - return p -} - -// String returns the string representation E(x,y) of the affine point p or "O" if it is infinity. -func (p *G1Affine) String() string { - if p.IsInfinity() { - return "O" - } - return "E([" + p.X.String() + "," + p.Y.String() + "])" -} - -// IsInfinity checks if the affine point p is infinity, which is encoded as (0,0). -// N.B.: (0,0) is never on the curve for j=0 curves (Y²=X³+B). -func (p *G1Affine) IsInfinity() bool { - return p.X.IsZero() && p.Y.IsZero() -} - -// IsOnCurve returns true if the affine point p in on the curve. -func (p *G1Affine) IsOnCurve() bool { - var point G1Jac - point.FromAffine(p) - return point.IsOnCurve() // call this function to handle infinity point -} - -// IsInSubGroup returns true if the affine point p is in the correct subgroup, false otherwise. -func (p *G1Affine) IsInSubGroup() bool { - var _p G1Jac - _p.FromAffine(p) - return _p.IsInSubGroup() -} - -// ------------------------------------------------------------------------------------------------- -// Jacobian coordinates - -// Set sets p to a in Jacobian coordinates. -func (p *G1Jac) Set(q *G1Jac) *G1Jac { - p.X, p.Y, p.Z = q.X, q.Y, q.Z - return p -} - -// Equal tests if two points in Jacobian coordinates are equal. -func (p *G1Jac) Equal(q *G1Jac) bool { - // If one point is infinity, the other must also be infinity. - if p.Z.IsZero() { - return q.Z.IsZero() - } - // If the other point is infinity, return false since we can't - // the following checks would be incorrect. - if q.Z.IsZero() { - return false - } - - var pZSquare, aZSquare fp.Element - pZSquare.Square(&p.Z) - aZSquare.Square(&q.Z) - - var lhs, rhs fp.Element - lhs.Mul(&p.X, &aZSquare) - rhs.Mul(&q.X, &pZSquare) - if !lhs.Equal(&rhs) { - return false - } - lhs.Mul(&p.Y, &aZSquare).Mul(&lhs, &q.Z) - rhs.Mul(&q.Y, &pZSquare).Mul(&rhs, &p.Z) - - return lhs.Equal(&rhs) -} - -// Neg sets p to the Jacobian negative point -q = (q.X, -q.Y, q.Z). -func (p *G1Jac) Neg(q *G1Jac) *G1Jac { - *p = *q - p.Y.Neg(&q.Y) - return p -} - -// AddAssign sets p to p+a in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl -func (p *G1Jac) AddAssign(q *G1Jac) *G1Jac { - - // p is infinity, return q - if p.Z.IsZero() { - p.Set(q) - return p - } - - // q is infinity, return p - if q.Z.IsZero() { - return p - } - - var Z1Z1, Z2Z2, U1, U2, S1, S2, H, I, J, r, V fp.Element - Z1Z1.Square(&q.Z) - Z2Z2.Square(&p.Z) - U1.Mul(&q.X, &Z2Z2) - U2.Mul(&p.X, &Z1Z1) - S1.Mul(&q.Y, &p.Z). - Mul(&S1, &Z2Z2) - S2.Mul(&p.Y, &q.Z). - Mul(&S2, &Z1Z1) - - // if p == q, we double instead - if U1.Equal(&U2) && S1.Equal(&S2) { - return p.DoubleAssign() - } - - H.Sub(&U2, &U1) - I.Double(&H). - Square(&I) - J.Mul(&H, &I) - r.Sub(&S2, &S1).Double(&r) - V.Mul(&U1, &I) - p.X.Square(&r). - Sub(&p.X, &J). - Sub(&p.X, &V). - Sub(&p.X, &V) - p.Y.Sub(&V, &p.X). - Mul(&p.Y, &r) - S1.Mul(&S1, &J).Double(&S1) - p.Y.Sub(&p.Y, &S1) - p.Z.Add(&p.Z, &q.Z) - p.Z.Square(&p.Z). - Sub(&p.Z, &Z1Z1). - Sub(&p.Z, &Z2Z2). - Mul(&p.Z, &H) - - return p -} - -// SubAssign sets p to p-a in Jacobian coordinates. -// It uses a similar approach to AddAssign, but negates the point a before adding. -func (p *G1Jac) SubAssign(q *G1Jac) *G1Jac { - var tmp G1Jac - tmp.Set(q) - tmp.Y.Neg(&tmp.Y) - p.AddAssign(&tmp) - return p -} - -// Double sets p to [2]q in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2007-bl -func (p *G1Jac) DoubleMixed(a *G1Affine) *G1Jac { - var XX, YY, YYYY, S, M, T fp.Element - XX.Square(&a.X) - YY.Square(&a.Y) - YYYY.Square(&YY) - S.Add(&a.X, &YY). - Square(&S). - Sub(&S, &XX). - Sub(&S, &YYYY). - Double(&S) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - T.Square(&M). - Sub(&T, &S). - Sub(&T, &S) - p.X.Set(&T) - p.Y.Sub(&S, &T). - Mul(&p.Y, &M) - YYYY.Double(&YYYY). - Double(&YYYY). - Double(&YYYY) - p.Y.Sub(&p.Y, &YYYY) - p.Z.Double(&a.Y) - - return p -} - -// AddMixed sets p to p+a in Jacobian coordinates, where a.Z = 1. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl -func (p *G1Jac) AddMixed(a *G1Affine) *G1Jac { - - //if a is infinity return p - if a.IsInfinity() { - return p - } - // p is infinity, return a - if p.Z.IsZero() { - p.X = a.X - p.Y = a.Y - p.Z.SetOne() - return p - } - - var Z1Z1, U2, S2, H, HH, I, J, r, V fp.Element - Z1Z1.Square(&p.Z) - U2.Mul(&a.X, &Z1Z1) - S2.Mul(&a.Y, &p.Z). - Mul(&S2, &Z1Z1) - - // if p == a, we double instead - if U2.Equal(&p.X) && S2.Equal(&p.Y) { - return p.DoubleMixed(a) - } - - H.Sub(&U2, &p.X) - HH.Square(&H) - I.Double(&HH).Double(&I) - J.Mul(&H, &I) - r.Sub(&S2, &p.Y).Double(&r) - V.Mul(&p.X, &I) - p.X.Square(&r). - Sub(&p.X, &J). - Sub(&p.X, &V). - Sub(&p.X, &V) - J.Mul(&J, &p.Y).Double(&J) - p.Y.Sub(&V, &p.X). - Mul(&p.Y, &r) - p.Y.Sub(&p.Y, &J) - p.Z.Add(&p.Z, &H) - p.Z.Square(&p.Z). - Sub(&p.Z, &Z1Z1). - Sub(&p.Z, &HH) - - return p -} - -// Double sets p to [2]q in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2007-bl -func (p *G1Jac) Double(q *G1Jac) *G1Jac { - p.Set(q) - p.DoubleAssign() - return p -} - -// DoubleAssign doubles p in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2007-bl -func (p *G1Jac) DoubleAssign() *G1Jac { - - var XX, YY, YYYY, ZZ, S, M, T fp.Element - - XX.Square(&p.X) - YY.Square(&p.Y) - YYYY.Square(&YY) - ZZ.Square(&p.Z) - S.Add(&p.X, &YY) - S.Square(&S). - Sub(&S, &XX). - Sub(&S, &YYYY). - Double(&S) - M.Double(&XX).Add(&M, &XX) - p.Z.Add(&p.Z, &p.Y). - Square(&p.Z). - Sub(&p.Z, &YY). - Sub(&p.Z, &ZZ) - T.Square(&M) - p.X = T - T.Double(&S) - p.X.Sub(&p.X, &T) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M) - YYYY.Double(&YYYY).Double(&YYYY).Double(&YYYY) - p.Y.Sub(&p.Y, &YYYY) - - return p -} - -// ScalarMultiplication computes and returns p = [s]a -// where p and a are Jacobian points. -// using the GLV technique. -// see https://www.iacr.org/archive/crypto2001/21390189.pdf -func (p *G1Jac) ScalarMultiplication(q *G1Jac, s *big.Int) *G1Jac { - return p.mulGLV(q, s) -} - -// ScalarMultiplicationBase computes and returns p = [s]g -// where g is the prime subgroup generator. -func (p *G1Jac) ScalarMultiplicationBase(s *big.Int) *G1Jac { - return p.mulGLV(&g1Gen, s) - -} - -// String converts p to affine coordinates and returns its string representation E(x,y) or "O" if it is infinity. -func (p *G1Jac) String() string { - _p := G1Affine{} - _p.FromJacobian(p) - return _p.String() -} - -// FromAffine converts a point a from affine to Jacobian coordinates. -func (p *G1Jac) FromAffine(a *G1Affine) *G1Jac { - if a.IsInfinity() { - p.Z.SetZero() - p.X.SetOne() - p.Y.SetOne() - return p - } - p.Z.SetOne() - p.X.Set(&a.X) - p.Y.Set(&a.Y) - return p -} - -// IsOnCurve returns true if the Jacobian point p in on the curve. -func (p *G1Jac) IsOnCurve() bool { - var left, right, tmp, ZZ fp.Element - left.Square(&p.Y) - right.Square(&p.X).Mul(&right, &p.X) - ZZ.Square(&p.Z) - tmp.Square(&ZZ).Mul(&tmp, &ZZ) - // Mul tmp by bCurveCoeff=1 (nothing to do) - right.Add(&right, &tmp) - return left.Equal(&right) -} - -// IsInSubGroup returns true if p is on the r-torsion, false otherwise. -// Z[r,0]+Z[-lambdaG1Affine, 1] is the kernel -// of (u,v)->u+lambdaG1Affinev mod r. Expressing r, lambdaG1Affine as -// polynomials in x, a short vector of this Zmodule is -// 1, x². So we check that p+x²ϕ(p) -// is the infinity. -func (p *G1Jac) IsInSubGroup() bool { - - var res G1Jac - - res.phi(p). - ScalarMultiplication(&res, &xGen). - ScalarMultiplication(&res, &xGen). - AddAssign(p) - - return res.IsOnCurve() && res.Z.IsZero() - -} - -// mulWindowed computes the 2-bits windowed double-and-add scalar -// multiplication p=[s]q in Jacobian coordinates. -func (p *G1Jac) mulWindowed(q *G1Jac, s *big.Int) *G1Jac { - - var res G1Jac - var ops [3]G1Jac - - ops[0].Set(q) - if s.Sign() == -1 { - ops[0].Neg(&ops[0]) - } - res.Set(&g1Infinity) - ops[1].Double(&ops[0]) - ops[2].Set(&ops[0]).AddAssign(&ops[1]) - - b := s.Bytes() - for i := range b { - w := b[i] - mask := byte(0xc0) - for j := 0; j < 4; j++ { - res.DoubleAssign().DoubleAssign() - c := (w & mask) >> (6 - 2*j) - if c != 0 { - res.AddAssign(&ops[c-1]) - } - mask = mask >> 2 - } - } - p.Set(&res) - - return p - -} - -// phi sets p to ϕ(a) where ϕ: (x,y) → (w x,y), -// where w is a third root of unity. -func (p *G1Jac) phi(q *G1Jac) *G1Jac { - p.Set(q) - p.X.Mul(&p.X, &thirdRootOneG1) - return p -} - -// mulGLV computes the scalar multiplication using a windowed-GLV method -// -// see https://www.iacr.org/archive/crypto2001/21390189.pdf -func (p *G1Jac) mulGLV(q *G1Jac, s *big.Int) *G1Jac { - - var table [15]G1Jac - var res G1Jac - var k1, k2 fr.Element - - res.Set(&g1Infinity) - - // table[b3b2b1b0-1] = b3b2 ⋅ ϕ(q) + b1b0*q - table[0].Set(q) - table[3].phi(q) - - // split the scalar, modifies ±q, ϕ(q) accordingly - k := ecc.SplitScalar(s, &glvBasis) - - if k[0].Sign() == -1 { - k[0].Neg(&k[0]) - table[0].Neg(&table[0]) - } - if k[1].Sign() == -1 { - k[1].Neg(&k[1]) - table[3].Neg(&table[3]) - } - - // precompute table (2 bits sliding window) - // table[b3b2b1b0-1] = b3b2 ⋅ ϕ(q) + b1b0 ⋅ q if b3b2b1b0 != 0 - table[1].Double(&table[0]) - table[2].Set(&table[1]).AddAssign(&table[0]) - table[4].Set(&table[3]).AddAssign(&table[0]) - table[5].Set(&table[3]).AddAssign(&table[1]) - table[6].Set(&table[3]).AddAssign(&table[2]) - table[7].Double(&table[3]) - table[8].Set(&table[7]).AddAssign(&table[0]) - table[9].Set(&table[7]).AddAssign(&table[1]) - table[10].Set(&table[7]).AddAssign(&table[2]) - table[11].Set(&table[7]).AddAssign(&table[3]) - table[12].Set(&table[11]).AddAssign(&table[0]) - table[13].Set(&table[11]).AddAssign(&table[1]) - table[14].Set(&table[11]).AddAssign(&table[2]) - - // bounds on the lattice base vectors guarantee that k1, k2 are len(r)/2 or len(r)/2+1 bits long max - // this is because we use a probabilistic scalar decomposition that replaces a division by a right-shift - k1 = k1.SetBigInt(&k[0]).Bits() - k2 = k2.SetBigInt(&k[1]).Bits() - - // we don't target constant-timeness so we check first if we increase the bounds or not - maxBit := k1.BitLen() - if k2.BitLen() > maxBit { - maxBit = k2.BitLen() - } - hiWordIndex := (maxBit - 1) / 64 - - // loop starts from len(k1)/2 or len(k1)/2+1 due to the bounds - for i := hiWordIndex; i >= 0; i-- { - mask := uint64(3) << 62 - for j := 0; j < 32; j++ { - res.Double(&res).Double(&res) - b1 := (k1[i] & mask) >> (62 - 2*j) - b2 := (k2[i] & mask) >> (62 - 2*j) - if b1|b2 != 0 { - s := (b2<<2 | b1) - res.AddAssign(&table[s-1]) - } - mask = mask >> 2 - } - } - - p.Set(&res) - return p -} - -// ClearCofactor maps a point in curve to r-torsion -func (p *G1Affine) ClearCofactor(a *G1Affine) *G1Affine { - var _p G1Jac - _p.FromAffine(a) - _p.ClearCofactor(&_p) - p.FromJacobian(&_p) - return p -} - -// ClearCofactor maps a point in E(Fp) to E(Fp)[r] -func (p *G1Jac) ClearCofactor(q *G1Jac) *G1Jac { - // cf https://eprint.iacr.org/2019/403.pdf, 5 - var res G1Jac - res.ScalarMultiplication(q, &xGen).Neg(&res).AddAssign(q) - p.Set(&res) - return p - -} - -// JointScalarMultiplication computes [s1]a1+[s2]a2 using Strauss-Shamir technique -// where a1 and a2 are affine points. -func (p *G1Jac) JointScalarMultiplication(a1, a2 *G1Affine, s1, s2 *big.Int) *G1Jac { - - var res, p1, p2 G1Jac - res.Set(&g1Infinity) - p1.FromAffine(a1) - p2.FromAffine(a2) - - var table [15]G1Jac - - var k1, k2 big.Int - if s1.Sign() == -1 { - k1.Neg(s1) - table[0].Neg(&p1) - } else { - k1.Set(s1) - table[0].Set(&p1) - } - if s2.Sign() == -1 { - k2.Neg(s2) - table[3].Neg(&p2) - } else { - k2.Set(s2) - table[3].Set(&p2) - } - - // precompute table (2 bits sliding window) - table[1].Double(&table[0]) - table[2].Set(&table[1]).AddAssign(&table[0]) - table[4].Set(&table[3]).AddAssign(&table[0]) - table[5].Set(&table[3]).AddAssign(&table[1]) - table[6].Set(&table[3]).AddAssign(&table[2]) - table[7].Double(&table[3]) - table[8].Set(&table[7]).AddAssign(&table[0]) - table[9].Set(&table[7]).AddAssign(&table[1]) - table[10].Set(&table[7]).AddAssign(&table[2]) - table[11].Set(&table[7]).AddAssign(&table[3]) - table[12].Set(&table[11]).AddAssign(&table[0]) - table[13].Set(&table[11]).AddAssign(&table[1]) - table[14].Set(&table[11]).AddAssign(&table[2]) - - var s [2]fr.Element - s[0] = s[0].SetBigInt(&k1).Bits() - s[1] = s[1].SetBigInt(&k2).Bits() - - maxBit := k1.BitLen() - if k2.BitLen() > maxBit { - maxBit = k2.BitLen() - } - hiWordIndex := (maxBit - 1) / 64 - - for i := hiWordIndex; i >= 0; i-- { - mask := uint64(3) << 62 - for j := 0; j < 32; j++ { - res.Double(&res).Double(&res) - b1 := (s[0][i] & mask) >> (62 - 2*j) - b2 := (s[1][i] & mask) >> (62 - 2*j) - if b1|b2 != 0 { - s := (b2<<2 | b1) - res.AddAssign(&table[s-1]) - } - mask = mask >> 2 - } - } - - p.Set(&res) - return p - -} - -// JointScalarMultiplicationBase computes [s1]g+[s2]a using Straus-Shamir technique -// where g is the prime subgroup generator. -func (p *G1Jac) JointScalarMultiplicationBase(a *G1Affine, s1, s2 *big.Int) *G1Jac { - return p.JointScalarMultiplication(&g1GenAff, a, s1, s2) - -} - -// ------------------------------------------------------------------------------------------------- -// extended Jacobian coordinates - -// Set sets p to a in extended Jacobian coordinates. -func (p *g1JacExtended) Set(q *g1JacExtended) *g1JacExtended { - p.X, p.Y, p.ZZ, p.ZZZ = q.X, q.Y, q.ZZ, q.ZZZ - return p -} - -// setInfinity sets p to the infinity point (1,1,0,0). -func (p *g1JacExtended) setInfinity() *g1JacExtended { - p.X.SetOne() - p.Y.SetOne() - p.ZZ = fp.Element{} - p.ZZZ = fp.Element{} - return p -} - -// IsInfinity checks if the p is infinity, i.e. p.ZZ=0. -func (p *g1JacExtended) IsInfinity() bool { - return p.ZZ.IsZero() -} - -// fromJacExtended converts an extended Jacobian point to an affine point. -func (p *G1Affine) fromJacExtended(q *g1JacExtended) *G1Affine { - if q.ZZ.IsZero() { - p.X = fp.Element{} - p.Y = fp.Element{} - return p - } - p.X.Inverse(&q.ZZ).Mul(&p.X, &q.X) - p.Y.Inverse(&q.ZZZ).Mul(&p.Y, &q.Y) - return p -} - -// fromJacExtended converts an extended Jacobian point to a Jacobian point. -func (p *G1Jac) fromJacExtended(q *g1JacExtended) *G1Jac { - if q.ZZ.IsZero() { - p.Set(&g1Infinity) - return p - } - p.X.Mul(&q.ZZ, &q.X).Mul(&p.X, &q.ZZ) - p.Y.Mul(&q.ZZZ, &q.Y).Mul(&p.Y, &q.ZZZ) - p.Z.Set(&q.ZZZ) - return p -} - -// unsafeFromJacExtended converts an extended Jacobian point, distinct from Infinity, to a Jacobian point. -func (p *G1Jac) unsafeFromJacExtended(q *g1JacExtended) *G1Jac { - p.X.Square(&q.ZZ).Mul(&p.X, &q.X) - p.Y.Square(&q.ZZZ).Mul(&p.Y, &q.Y) - p.Z = q.ZZZ - return p -} - -// add sets p to p+q in extended Jacobian coordinates. -// -// https://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#addition-add-2008-s -func (p *g1JacExtended) add(q *g1JacExtended) *g1JacExtended { - //if q is infinity return p - if q.ZZ.IsZero() { - return p - } - // p is infinity, return q - if p.ZZ.IsZero() { - p.Set(q) - return p - } - - var A, B, U1, U2, S1, S2 fp.Element - - // p2: q, p1: p - U2.Mul(&q.X, &p.ZZ) - U1.Mul(&p.X, &q.ZZ) - A.Sub(&U2, &U1) - S2.Mul(&q.Y, &p.ZZZ) - S1.Mul(&p.Y, &q.ZZZ) - B.Sub(&S2, &S1) - - if A.IsZero() { - if B.IsZero() { - return p.double(q) - - } - p.ZZ = fp.Element{} - p.ZZZ = fp.Element{} - return p - } - - var P, R, PP, PPP, Q, V fp.Element - P.Sub(&U2, &U1) - R.Sub(&S2, &S1) - PP.Square(&P) - PPP.Mul(&P, &PP) - Q.Mul(&U1, &PP) - V.Mul(&S1, &PPP) - - p.X.Square(&R). - Sub(&p.X, &PPP). - Sub(&p.X, &Q). - Sub(&p.X, &Q) - p.Y.Sub(&Q, &p.X). - Mul(&p.Y, &R). - Sub(&p.Y, &V) - p.ZZ.Mul(&p.ZZ, &q.ZZ). - Mul(&p.ZZ, &PP) - p.ZZZ.Mul(&p.ZZZ, &q.ZZZ). - Mul(&p.ZZZ, &PPP) - - return p -} - -// double sets p to [2]q in Jacobian extended coordinates. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-dbl-2008-s-1 -// N.B.: since we consider any point on Z=0 as the point at infinity -// this doubling formula works for infinity points as well. -func (p *g1JacExtended) double(q *g1JacExtended) *g1JacExtended { - var U, V, W, S, XX, M fp.Element - - U.Double(&q.Y) - V.Square(&U) - W.Mul(&U, &V) - S.Mul(&q.X, &V) - XX.Square(&q.X) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - U.Mul(&W, &q.Y) - - p.X.Square(&M). - Sub(&p.X, &S). - Sub(&p.X, &S) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M). - Sub(&p.Y, &U) - p.ZZ.Mul(&V, &q.ZZ) - p.ZZZ.Mul(&W, &q.ZZZ) - - return p -} - -// addMixed sets p to p+q in extended Jacobian coordinates, where a.ZZ=1. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#addition-madd-2008-s -func (p *g1JacExtended) addMixed(a *G1Affine) *g1JacExtended { - - //if a is infinity return p - if a.IsInfinity() { - return p - } - // p is infinity, return a - if p.ZZ.IsZero() { - p.X = a.X - p.Y = a.Y - p.ZZ.SetOne() - p.ZZZ.SetOne() - return p - } - - var P, R fp.Element - - // p2: a, p1: p - P.Mul(&a.X, &p.ZZ) - P.Sub(&P, &p.X) - - R.Mul(&a.Y, &p.ZZZ) - R.Sub(&R, &p.Y) - - if P.IsZero() { - if R.IsZero() { - return p.doubleMixed(a) - - } - p.ZZ = fp.Element{} - p.ZZZ = fp.Element{} - return p - } - - var PP, PPP, Q, Q2, RR, X3, Y3 fp.Element - - PP.Square(&P) - PPP.Mul(&P, &PP) - Q.Mul(&p.X, &PP) - RR.Square(&R) - X3.Sub(&RR, &PPP) - Q2.Double(&Q) - p.X.Sub(&X3, &Q2) - Y3.Sub(&Q, &p.X).Mul(&Y3, &R) - R.Mul(&p.Y, &PPP) - p.Y.Sub(&Y3, &R) - p.ZZ.Mul(&p.ZZ, &PP) - p.ZZZ.Mul(&p.ZZZ, &PPP) - - return p - -} - -// subMixed works the same as addMixed, but negates a.Y. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#addition-madd-2008-s -func (p *g1JacExtended) subMixed(a *G1Affine) *g1JacExtended { - - //if a is infinity return p - if a.IsInfinity() { - return p - } - // p is infinity, return a - if p.ZZ.IsZero() { - p.X = a.X - p.Y.Neg(&a.Y) - p.ZZ.SetOne() - p.ZZZ.SetOne() - return p - } - - var P, R fp.Element - - // p2: a, p1: p - P.Mul(&a.X, &p.ZZ) - P.Sub(&P, &p.X) - - R.Mul(&a.Y, &p.ZZZ) - R.Neg(&R) - R.Sub(&R, &p.Y) - - if P.IsZero() { - if R.IsZero() { - return p.doubleNegMixed(a) - - } - p.ZZ = fp.Element{} - p.ZZZ = fp.Element{} - return p - } - - var PP, PPP, Q, Q2, RR, X3, Y3 fp.Element - - PP.Square(&P) - PPP.Mul(&P, &PP) - Q.Mul(&p.X, &PP) - RR.Square(&R) - X3.Sub(&RR, &PPP) - Q2.Double(&Q) - p.X.Sub(&X3, &Q2) - Y3.Sub(&Q, &p.X).Mul(&Y3, &R) - R.Mul(&p.Y, &PPP) - p.Y.Sub(&Y3, &R) - p.ZZ.Mul(&p.ZZ, &PP) - p.ZZZ.Mul(&p.ZZZ, &PPP) - - return p - -} - -// doubleNegMixed works the same as double, but negates q.Y. -func (p *g1JacExtended) doubleNegMixed(a *G1Affine) *g1JacExtended { - - var U, V, W, S, XX, M, S2, L fp.Element - - U.Double(&a.Y) - U.Neg(&U) - V.Square(&U) - W.Mul(&U, &V) - S.Mul(&a.X, &V) - XX.Square(&a.X) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - S2.Double(&S) - L.Mul(&W, &a.Y) - - p.X.Square(&M). - Sub(&p.X, &S2) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M). - Add(&p.Y, &L) - p.ZZ.Set(&V) - p.ZZZ.Set(&W) - - return p -} - -// doubleMixed sets p to [2]a in Jacobian extended coordinates, where a.ZZ=1. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-dbl-2008-s-1 -func (p *g1JacExtended) doubleMixed(a *G1Affine) *g1JacExtended { - - var U, V, W, S, XX, M, S2, L fp.Element - - U.Double(&a.Y) - V.Square(&U) - W.Mul(&U, &V) - S.Mul(&a.X, &V) - XX.Square(&a.X) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - S2.Double(&S) - L.Mul(&W, &a.Y) - - p.X.Square(&M). - Sub(&p.X, &S2) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M). - Sub(&p.Y, &L) - p.ZZ.Set(&V) - p.ZZZ.Set(&W) - - return p -} - -// BatchJacobianToAffineG1 converts points in Jacobian coordinates to Affine coordinates -// performing a single field inversion using the Montgomery batch inversion trick. -func BatchJacobianToAffineG1(points []G1Jac) []G1Affine { - result := make([]G1Affine, len(points)) - zeroes := make([]bool, len(points)) - accumulator := fp.One() - - // batch invert all points[].Z coordinates with Montgomery batch inversion trick - // (stores points[].Z^-1 in result[i].X to avoid allocating a slice of fr.Elements) - for i := 0; i < len(points); i++ { - if points[i].Z.IsZero() { - zeroes[i] = true - continue - } - result[i].X = accumulator - accumulator.Mul(&accumulator, &points[i].Z) - } - - var accInverse fp.Element - accInverse.Inverse(&accumulator) - - for i := len(points) - 1; i >= 0; i-- { - if zeroes[i] { - // do nothing, (X=0, Y=0) is infinity point in affine - continue - } - result[i].X.Mul(&result[i].X, &accInverse) - accInverse.Mul(&accInverse, &points[i].Z) - } - - // batch convert to affine. - parallel.Execute(len(points), func(start, end int) { - for i := start; i < end; i++ { - if zeroes[i] { - // do nothing, (X=0, Y=0) is infinity point in affine - continue - } - var a, b fp.Element - a = result[i].X - b.Square(&a) - result[i].X.Mul(&points[i].X, &b) - result[i].Y.Mul(&points[i].Y, &b). - Mul(&result[i].Y, &a) - } - }) - - return result -} - -// BatchScalarMultiplicationG1 multiplies the same base by all scalars -// and return resulting points in affine coordinates -// uses a simple windowed-NAF-like multiplication algorithm. -func BatchScalarMultiplicationG1(base *G1Affine, scalars []fr.Element) []G1Affine { - // approximate cost in group ops is - // cost = 2^{c-1} + n(scalar.nbBits+nbChunks) - - nbPoints := uint64(len(scalars)) - min := ^uint64(0) - bestC := 0 - for c := 2; c <= 16; c++ { - cost := uint64(1 << (c - 1)) // pre compute the table - nbChunks := computeNbChunks(uint64(c)) - cost += nbPoints * (uint64(c) + 1) * nbChunks // doublings + point add - if cost < min { - min = cost - bestC = c - } - } - c := uint64(bestC) // window size - nbChunks := int(computeNbChunks(c)) - - // last window may be slightly larger than c; in which case we need to compute one - // extra element in the baseTable - maxC := lastC(c) - if c > maxC { - maxC = c - } - - // precompute all powers of base for our window - // note here that if performance is critical, we can implement as in the msmX methods - // this allocation to be on the stack - baseTable := make([]G1Jac, (1 << (maxC - 1))) - baseTable[0].FromAffine(base) - for i := 1; i < len(baseTable); i++ { - baseTable[i] = baseTable[i-1] - baseTable[i].AddMixed(base) - } - // convert our base exp table into affine to use AddMixed - baseTableAff := BatchJacobianToAffineG1(baseTable) - toReturn := make([]G1Jac, len(scalars)) - - // partition the scalars into digits - digits, _ := partitionScalars(scalars, c, runtime.NumCPU()) - - // for each digit, take value in the base table, double it c time, voilà. - parallel.Execute(len(scalars), func(start, end int) { - var p G1Jac - for i := start; i < end; i++ { - p.Set(&g1Infinity) - for chunk := nbChunks - 1; chunk >= 0; chunk-- { - if chunk != nbChunks-1 { - for j := uint64(0); j < c; j++ { - p.DoubleAssign() - } - } - offset := chunk * len(scalars) - digit := digits[i+offset] - - if digit == 0 { - continue - } - - // if msbWindow bit is set, we need to subtract - if digit&1 == 0 { - // add - p.AddMixed(&baseTableAff[(digit>>1)-1]) - } else { - // sub - t := baseTableAff[digit>>1] - t.Neg(&t) - p.AddMixed(&t) - } - } - - // set our result point - toReturn[i] = p - - } - }) - toReturnAff := BatchJacobianToAffineG1(toReturn) - return toReturnAff -} - -// batchAddG1Affine adds affine points using the Montgomery batch inversion trick. -// Special cases (doubling, infinity) must be filtered out before this call. -func batchAddG1Affine[TP pG1Affine, TPP ppG1Affine, TC cG1Affine](R *TPP, P *TP, batchSize int) { - var lambda, lambdain TC - - // add part - for j := 0; j < batchSize; j++ { - lambdain[j].Sub(&(*P)[j].X, &(*R)[j].X) - } - - // invert denominator using montgomery batch invert technique - { - var accumulator fp.Element - lambda[0].SetOne() - accumulator.Set(&lambdain[0]) - - for i := 1; i < batchSize; i++ { - lambda[i] = accumulator - accumulator.Mul(&accumulator, &lambdain[i]) - } - - accumulator.Inverse(&accumulator) - - for i := batchSize - 1; i > 0; i-- { - lambda[i].Mul(&lambda[i], &accumulator) - accumulator.Mul(&accumulator, &lambdain[i]) - } - lambda[0].Set(&accumulator) - } - - var d fp.Element - var rr G1Affine - - // add part - for j := 0; j < batchSize; j++ { - // computa lambda - d.Sub(&(*P)[j].Y, &(*R)[j].Y) - lambda[j].Mul(&lambda[j], &d) - - // compute X, Y - rr.X.Square(&lambda[j]) - rr.X.Sub(&rr.X, &(*R)[j].X) - rr.X.Sub(&rr.X, &(*P)[j].X) - d.Sub(&(*R)[j].X, &rr.X) - rr.Y.Mul(&lambda[j], &d) - rr.Y.Sub(&rr.Y, &(*R)[j].Y) - (*R)[j].Set(&rr) - } -} diff --git a/ecc/bls12-378/g1_test.go b/ecc/bls12-378/g1_test.go deleted file mode 100644 index 02e3753ef0..0000000000 --- a/ecc/bls12-378/g1_test.go +++ /dev/null @@ -1,885 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bls12378 - -import ( - "fmt" - "math/big" - "math/rand/v2" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -func TestG1AffineEndomorphism(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BLS12-378] check that phi(P) = lambdaGLV * P", prop.ForAll( - func(a fp.Element) bool { - var p, res1, res2 G1Jac - g := MapToG1(a) - p.FromAffine(&g) - res1.phi(&p) - res2.mulWindowed(&p, &lambdaGLV) - - return p.IsInSubGroup() && res1.Equal(&res2) - }, - GenFp(), - )) - - properties.Property("[BLS12-378] check that phi^2(P) + phi(P) + P = 0", prop.ForAll( - func(a fp.Element) bool { - var p, res, tmp G1Jac - g := MapToG1(a) - p.FromAffine(&g) - tmp.phi(&p) - res.phi(&tmp). - AddAssign(&tmp). - AddAssign(&p) - - return res.Z.IsZero() - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG1AffineIsOnCurve(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BLS12-378] g1Gen (affine) should be on the curve", prop.ForAll( - func(a fp.Element) bool { - var op1, op2 G1Affine - op1.FromJacobian(&g1Gen) - op2.Set(&op1) - op2.Y.Mul(&op2.Y, &a) - return op1.IsOnCurve() && !op2.IsOnCurve() - }, - GenFp(), - )) - - properties.Property("[BLS12-378] g1Gen (Jacobian) should be on the curve", prop.ForAll( - func(a fp.Element) bool { - var op1, op2, op3 G1Jac - op1.Set(&g1Gen) - op3.Set(&g1Gen) - - op2 = fuzzG1Jac(&g1Gen, a) - op3.Y.Mul(&op3.Y, &a) - return op1.IsOnCurve() && op2.IsOnCurve() && !op3.IsOnCurve() - }, - GenFp(), - )) - - properties.Property("[BLS12-378] IsInSubGroup and MulBy subgroup order should be the same", prop.ForAll( - func(a fp.Element) bool { - var op1, op2 G1Jac - op1 = fuzzG1Jac(&g1Gen, a) - _r := fr.Modulus() - op2.ScalarMultiplication(&op1, _r) - return op1.IsInSubGroup() && op2.Z.IsZero() - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG1AffineConversions(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BLS12-378] Affine representation should be independent of the Jacobian representative", prop.ForAll( - func(a fp.Element) bool { - g := fuzzG1Jac(&g1Gen, a) - var op1 G1Affine - op1.FromJacobian(&g) - return op1.X.Equal(&g1Gen.X) && op1.Y.Equal(&g1Gen.Y) - }, - GenFp(), - )) - - properties.Property("[BLS12-378] Affine representation should be independent of a Extended Jacobian representative", prop.ForAll( - func(a fp.Element) bool { - var g g1JacExtended - g.X.Set(&g1Gen.X) - g.Y.Set(&g1Gen.Y) - g.ZZ.Set(&g1Gen.Z) - g.ZZZ.Set(&g1Gen.Z) - gfuzz := fuzzg1JacExtended(&g, a) - - var op1 G1Affine - op1.fromJacExtended(&gfuzz) - return op1.X.Equal(&g1Gen.X) && op1.Y.Equal(&g1Gen.Y) - }, - GenFp(), - )) - - properties.Property("[BLS12-378] Jacobian representation should be the same as the affine representative", prop.ForAll( - func(a fp.Element) bool { - var g G1Jac - var op1 G1Affine - op1.X.Set(&g1Gen.X) - op1.Y.Set(&g1Gen.Y) - - var one fp.Element - one.SetOne() - - g.FromAffine(&op1) - - return g.X.Equal(&g1Gen.X) && g.Y.Equal(&g1Gen.Y) && g.Z.Equal(&one) - }, - GenFp(), - )) - - properties.Property("[BLS12-378] Converting affine symbol for infinity to Jacobian should output correct infinity in Jacobian", prop.ForAll( - func() bool { - var g G1Affine - g.X.SetZero() - g.Y.SetZero() - var op1 G1Jac - op1.FromAffine(&g) - var one, zero fp.Element - one.SetOne() - return op1.X.Equal(&one) && op1.Y.Equal(&one) && op1.Z.Equal(&zero) - }, - )) - - properties.Property("[BLS12-378] Converting infinity in extended Jacobian to affine should output infinity symbol in Affine", prop.ForAll( - func() bool { - var g G1Affine - var op1 g1JacExtended - var zero fp.Element - op1.X.Set(&g1Gen.X) - op1.Y.Set(&g1Gen.Y) - g.fromJacExtended(&op1) - return g.X.Equal(&zero) && g.Y.Equal(&zero) - }, - )) - - properties.Property("[BLS12-378] Converting infinity in extended Jacobian to Jacobian should output infinity in Jacobian", prop.ForAll( - func() bool { - var g G1Jac - var op1 g1JacExtended - var zero, one fp.Element - one.SetOne() - op1.X.Set(&g1Gen.X) - op1.Y.Set(&g1Gen.Y) - g.fromJacExtended(&op1) - return g.X.Equal(&one) && g.Y.Equal(&one) && g.Z.Equal(&zero) - }, - )) - - properties.Property("[BLS12-378] [Jacobian] Two representatives of the same class should be equal", prop.ForAll( - func(a, b fp.Element) bool { - op1 := fuzzG1Jac(&g1Gen, a) - op2 := fuzzG1Jac(&g1Gen, b) - return op1.Equal(&op2) - }, - GenFp(), - GenFp(), - )) - properties.Property("[BLS12-378] BatchJacobianToAffineG1 and FromJacobian should output the same result", prop.ForAll( - func(a, b fp.Element) bool { - g1 := fuzzG1Jac(&g1Gen, a) - g2 := fuzzG1Jac(&g1Gen, b) - var op1, op2 G1Affine - op1.FromJacobian(&g1) - op2.FromJacobian(&g2) - baseTableAff := BatchJacobianToAffineG1([]G1Jac{g1, g2}) - return op1.Equal(&baseTableAff[0]) && op2.Equal(&baseTableAff[1]) - }, - GenFp(), - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG1AffineOps(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - parameters.MinSuccessfulTests = 10 - - properties := gopter.NewProperties(parameters) - - genScalar := GenFr() - - properties.Property("[BLS12-378] Add(P,-P) should return the point at infinity", prop.ForAll( - func(s fr.Element) bool { - var op1, op2 G1Affine - var sInt big.Int - g := g1GenAff - s.BigInt(&sInt) - op1.ScalarMultiplication(&g, &sInt) - op2.Neg(&op1) - - op1.Add(&op1, &op2) - return op1.IsInfinity() - - }, - GenFr(), - )) - - properties.Property("[BLS12-378] Add(P,0) and Add(0,P) should return P", prop.ForAll( - func(s fr.Element) bool { - var op1, op2 G1Affine - var sInt big.Int - g := g1GenAff - s.BigInt(&sInt) - op1.ScalarMultiplication(&g, &sInt) - op2.setInfinity() - - op1.Add(&op1, &op2) - op2.Add(&op2, &op1) - return op1.Equal(&op2) - - }, - GenFr(), - )) - - properties.Property("[BLS12-378] Add should call double when adding the same point", prop.ForAll( - func(s fr.Element) bool { - var op1, op2 G1Affine - var sInt big.Int - g := g1GenAff - s.BigInt(&sInt) - op1.ScalarMultiplication(&g, &sInt) - - op2.Double(&op1) - op1.Add(&op1, &op1) - return op1.Equal(&op2) - - }, - GenFr(), - )) - - properties.Property("[BLS12-378] [2]G = double(G) + G - G", prop.ForAll( - func(s fr.Element) bool { - var sInt big.Int - g := g1GenAff - s.BigInt(&sInt) - g.ScalarMultiplication(&g, &sInt) - var op1, op2 G1Affine - op1.ScalarMultiplication(&g, big.NewInt(2)) - op2.Double(&g) - op2.Add(&op2, &g) - op2.Sub(&op2, &g) - return op1.Equal(&op2) - }, - GenFr(), - )) - - properties.Property("[BLS12-378] [-s]G = -[s]G", prop.ForAll( - func(s fr.Element) bool { - g := g1GenAff - var gj G1Jac - var nbs, bs big.Int - s.BigInt(&bs) - nbs.Neg(&bs) - - var res = true - - // mulGLV - { - var op1, op2 G1Affine - op1.ScalarMultiplication(&g, &bs).Neg(&op1) - op2.ScalarMultiplication(&g, &nbs) - res = res && op1.Equal(&op2) - } - - // mulWindowed - { - var op1, op2 G1Jac - op1.mulWindowed(&gj, &bs).Neg(&op1) - op2.mulWindowed(&gj, &nbs) - res = res && op1.Equal(&op2) - } - - return res - }, - GenFr(), - )) - - properties.Property("[BLS12-378] [Jacobian] Add should call double when adding the same point", prop.ForAll( - func(a, b fp.Element) bool { - fop1 := fuzzG1Jac(&g1Gen, a) - fop2 := fuzzG1Jac(&g1Gen, b) - var op1, op2 G1Jac - op1.Set(&fop1).AddAssign(&fop2) - op2.Double(&fop2) - return op1.Equal(&op2) - }, - GenFp(), - GenFp(), - )) - - properties.Property("[BLS12-378] [Jacobian] Adding the opposite of a point to itself should output inf", prop.ForAll( - func(a, b fp.Element) bool { - fop1 := fuzzG1Jac(&g1Gen, a) - fop2 := fuzzG1Jac(&g1Gen, b) - fop2.Neg(&fop2) - fop1.AddAssign(&fop2) - return fop1.Equal(&g1Infinity) - }, - GenFp(), - GenFp(), - )) - - properties.Property("[BLS12-378] [Jacobian] Adding the inf to a point should not modify the point", prop.ForAll( - func(a fp.Element) bool { - fop1 := fuzzG1Jac(&g1Gen, a) - fop1.AddAssign(&g1Infinity) - var op2 G1Jac - op2.Set(&g1Infinity) - op2.AddAssign(&g1Gen) - return fop1.Equal(&g1Gen) && op2.Equal(&g1Gen) - }, - GenFp(), - )) - - properties.Property("[BLS12-378] [Jacobian Extended] addMixed (-G) should equal subMixed(G)", prop.ForAll( - func(a fp.Element) bool { - fop1 := fuzzG1Jac(&g1Gen, a) - var p1, p1Neg G1Affine - p1.FromJacobian(&fop1) - p1Neg = p1 - p1Neg.Y.Neg(&p1Neg.Y) - var o1, o2 g1JacExtended - o1.addMixed(&p1Neg) - o2.subMixed(&p1) - - return o1.X.Equal(&o2.X) && - o1.Y.Equal(&o2.Y) && - o1.ZZ.Equal(&o2.ZZ) && - o1.ZZZ.Equal(&o2.ZZZ) - }, - GenFp(), - )) - - properties.Property("[BLS12-378] [Jacobian Extended] doubleMixed (-G) should equal doubleNegMixed(G)", prop.ForAll( - func(a fp.Element) bool { - fop1 := fuzzG1Jac(&g1Gen, a) - var p1, p1Neg G1Affine - p1.FromJacobian(&fop1) - p1Neg = p1 - p1Neg.Y.Neg(&p1Neg.Y) - var o1, o2 g1JacExtended - o1.doubleMixed(&p1Neg) - o2.doubleNegMixed(&p1) - - return o1.X.Equal(&o2.X) && - o1.Y.Equal(&o2.Y) && - o1.ZZ.Equal(&o2.ZZ) && - o1.ZZZ.Equal(&o2.ZZZ) - }, - GenFp(), - )) - - properties.Property("[BLS12-378] [Jacobian] Addmix the negation to itself should output 0", prop.ForAll( - func(a fp.Element) bool { - fop1 := fuzzG1Jac(&g1Gen, a) - fop1.Neg(&fop1) - var op2 G1Affine - op2.FromJacobian(&g1Gen) - fop1.AddMixed(&op2) - return fop1.Equal(&g1Infinity) - }, - GenFp(), - )) - - properties.Property("[BLS12-378] scalar multiplication (double and add) should depend only on the scalar mod r", prop.ForAll( - func(s fr.Element) bool { - - r := fr.Modulus() - var g G1Jac - g.ScalarMultiplication(&g1Gen, r) - - var scalar, blindedScalar, rminusone big.Int - var op1, op2, op3, gneg G1Jac - rminusone.SetUint64(1).Sub(r, &rminusone) - op3.mulWindowed(&g1Gen, &rminusone) - gneg.Neg(&g1Gen) - s.BigInt(&scalar) - blindedScalar.Mul(&scalar, r).Add(&blindedScalar, &scalar) - op1.mulWindowed(&g1Gen, &scalar) - op2.mulWindowed(&g1Gen, &blindedScalar) - - return op1.Equal(&op2) && g.Equal(&g1Infinity) && !op1.Equal(&g1Infinity) && gneg.Equal(&op3) - - }, - genScalar, - )) - - properties.Property("[BLS12-378] scalar multiplication (GLV) should depend only on the scalar mod r", prop.ForAll( - func(s fr.Element) bool { - - r := fr.Modulus() - var g G1Jac - g.mulGLV(&g1Gen, r) - - var scalar, blindedScalar, rminusone big.Int - var op1, op2, op3, gneg G1Jac - rminusone.SetUint64(1).Sub(r, &rminusone) - op3.ScalarMultiplication(&g1Gen, &rminusone) - gneg.Neg(&g1Gen) - s.BigInt(&scalar) - blindedScalar.Mul(&scalar, r).Add(&blindedScalar, &scalar) - op1.ScalarMultiplication(&g1Gen, &scalar) - op2.ScalarMultiplication(&g1Gen, &blindedScalar) - - return op1.Equal(&op2) && g.Equal(&g1Infinity) && !op1.Equal(&g1Infinity) && gneg.Equal(&op3) - - }, - genScalar, - )) - - properties.Property("[BLS12-378] GLV and Double and Add should output the same result", prop.ForAll( - func(s fr.Element) bool { - - var r big.Int - var op1, op2 G1Jac - s.BigInt(&r) - op1.mulWindowed(&g1Gen, &r) - op2.mulGLV(&g1Gen, &r) - return op1.Equal(&op2) && !op1.Equal(&g1Infinity) - - }, - genScalar, - )) - - properties.Property("[BLS12-378] JointScalarMultiplicationBase and ScalarMultiplication should output the same results", prop.ForAll( - func(s1, s2 fr.Element) bool { - - var op1, op2, temp G1Jac - - op1.JointScalarMultiplicationBase(&g1GenAff, s1.BigInt(new(big.Int)), s2.BigInt(new(big.Int))) - temp.ScalarMultiplication(&g1Gen, s2.BigInt(new(big.Int))) - op2.ScalarMultiplication(&g1Gen, s1.BigInt(new(big.Int))). - AddAssign(&temp) - - return op1.Equal(&op2) - - }, - genScalar, - genScalar, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG1AffineCofactorCleaning(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BLS12-378] Clearing the cofactor of a random point should set it in the r-torsion", prop.ForAll( - func() bool { - var a, x, b fp.Element - a.SetRandom() - - x.Square(&a).Mul(&x, &a).Add(&x, &bCurveCoeff) - - for x.Legendre() != 1 { - a.SetRandom() - - x.Square(&a).Mul(&x, &a).Add(&x, &bCurveCoeff) - - } - - b.Sqrt(&x) - var point, pointCleared, infinity G1Jac - point.X.Set(&a) - point.Y.Set(&b) - point.Z.SetOne() - pointCleared.ClearCofactor(&point) - infinity.Set(&g1Infinity) - return point.IsOnCurve() && pointCleared.IsInSubGroup() && !pointCleared.Equal(&infinity) - }, - )) - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestG1AffineBatchScalarMultiplication(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzzShort - } - - properties := gopter.NewProperties(parameters) - - genScalar := GenFr() - - // size of the multiExps - const nbSamples = 10 - - properties.Property("[BLS12-378] BatchScalarMultiplication should be consistent with individual scalar multiplications", prop.ForAll( - func(mixer fr.Element) bool { - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - result := BatchScalarMultiplicationG1(&g1GenAff, sampleScalars[:]) - - if len(result) != len(sampleScalars) { - return false - } - - for i := 0; i < len(result); i++ { - var expectedJac G1Jac - var expected G1Affine - var b big.Int - expectedJac.ScalarMultiplication(&g1Gen, sampleScalars[i].BigInt(&b)) - expected.FromJacobian(&expectedJac) - if !result[i].Equal(&expected) { - return false - } - } - return true - }, - genScalar, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -// ------------------------------------------------------------ -// benches - -func BenchmarkG1JacIsInSubGroup(b *testing.B) { - var a G1Jac - a.Set(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.IsInSubGroup() - } - -} - -func BenchmarkG1JacEqual(b *testing.B) { - var scalar fp.Element - if _, err := scalar.SetRandom(); err != nil { - b.Fatalf("failed to set scalar: %s", err) - } - - var a G1Jac - a.ScalarMultiplication(&g1Gen, big.NewInt(42)) - - b.Run("equal", func(b *testing.B) { - var scalarSquared fp.Element - scalarSquared.Square(&scalar) - - aZScaled := a - aZScaled.X.Mul(&aZScaled.X, &scalarSquared) - aZScaled.Y.Mul(&aZScaled.Y, &scalarSquared).Mul(&aZScaled.Y, &scalar) - aZScaled.Z.Mul(&aZScaled.Z, &scalar) - - // Check the setup. - if !a.Equal(&aZScaled) { - b.Fatalf("invalid test setup") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Equal(&aZScaled) - } - }) - - b.Run("not equal", func(b *testing.B) { - var aPlus1 G1Jac - aPlus1.AddAssign(&g1Gen) - - // Check the setup. - if a.Equal(&aPlus1) { - b.Fatalf("invalid test setup") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Equal(&aPlus1) - } - }) -} - -func BenchmarkBatchAddG1Affine(b *testing.B) { - - var P, R pG1AffineC16 - var RR ppG1AffineC16 - ridx := make([]int, len(P)) - - // TODO P == R may produce skewed benches - fillBenchBasesG1(P[:]) - fillBenchBasesG1(R[:]) - - for i := 0; i < len(ridx); i++ { - ridx[i] = i - } - - // random permute - rand.Shuffle(len(ridx), func(i, j int) { ridx[i], ridx[j] = ridx[j], ridx[i] }) - - for i, ri := range ridx { - RR[i] = &R[ri] - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - batchAddG1Affine[pG1AffineC16, ppG1AffineC16, cG1AffineC16](&RR, &P, len(P)) - } -} - -func BenchmarkG1AffineBatchScalarMultiplication(b *testing.B) { - // ensure every words of the scalars are filled - var mixer fr.Element - mixer.SetString("7716837800905789770901243404444209691916730933998574719964609384059111546487") - - const pow = 15 - const nbSamples = 1 << pow - - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - for i := 5; i <= pow; i++ { - using := 1 << i - - b.Run(fmt.Sprintf("%d points", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - _ = BatchScalarMultiplicationG1(&g1GenAff, sampleScalars[:using]) - } - }) - } -} - -func BenchmarkG1JacScalarMultiplication(b *testing.B) { - - var scalar big.Int - r := fr.Modulus() - scalar.SetString("5243587517512619047944770508185965837690552500527637822603658699938581184513", 10) - scalar.Add(&scalar, r) - - var doubleAndAdd G1Jac - - b.Run("double and add", func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - doubleAndAdd.mulWindowed(&g1Gen, &scalar) - } - }) - - var glv G1Jac - b.Run("GLV", func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - glv.mulGLV(&g1Gen, &scalar) - } - }) - -} - -func BenchmarkG1AffineCofactorClearing(b *testing.B) { - var a G1Jac - a.Set(&g1Gen) - for i := 0; i < b.N; i++ { - a.ClearCofactor(&a) - } -} - -func BenchmarkG1JacAdd(b *testing.B) { - var a G1Jac - a.Double(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.AddAssign(&g1Gen) - } -} - -func BenchmarkG1JacAddMixed(b *testing.B) { - var a G1Jac - a.Double(&g1Gen) - - var c G1Affine - c.FromJacobian(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.AddMixed(&c) - } - -} - -func BenchmarkG1JacDouble(b *testing.B) { - var a G1Jac - a.Set(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.DoubleAssign() - } - -} - -func BenchmarkG1JacExtAddMixed(b *testing.B) { - var a g1JacExtended - a.doubleMixed(&g1GenAff) - - var c G1Affine - c.FromJacobian(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.addMixed(&c) - } -} - -func BenchmarkG1JacExtSubMixed(b *testing.B) { - var a g1JacExtended - a.doubleMixed(&g1GenAff) - - var c G1Affine - c.FromJacobian(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.subMixed(&c) - } -} - -func BenchmarkG1JacExtDoubleMixed(b *testing.B) { - var a g1JacExtended - a.doubleMixed(&g1GenAff) - - var c G1Affine - c.FromJacobian(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.doubleMixed(&c) - } -} - -func BenchmarkG1JacExtDoubleNegMixed(b *testing.B) { - var a g1JacExtended - a.doubleMixed(&g1GenAff) - - var c G1Affine - c.FromJacobian(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.doubleNegMixed(&c) - } -} - -func BenchmarkG1JacExtAdd(b *testing.B) { - var a, c g1JacExtended - a.doubleMixed(&g1GenAff) - c.double(&a) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.add(&c) - } -} - -func BenchmarkG1JacExtDouble(b *testing.B) { - var a g1JacExtended - a.doubleMixed(&g1GenAff) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.double(&a) - } -} - -func BenchmarkG1AffineAdd(b *testing.B) { - var a G1Affine - a.Double(&g1GenAff) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Add(&a, &g1GenAff) - } -} - -func BenchmarkG1AffineDouble(b *testing.B) { - var a G1Affine - a.Double(&g1GenAff) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Double(&a) - } -} - -func fuzzG1Jac(p *G1Jac, f fp.Element) G1Jac { - var res G1Jac - res.X.Mul(&p.X, &f).Mul(&res.X, &f) - res.Y.Mul(&p.Y, &f).Mul(&res.Y, &f).Mul(&res.Y, &f) - res.Z.Mul(&p.Z, &f) - return res -} - -func fuzzg1JacExtended(p *g1JacExtended, f fp.Element) g1JacExtended { - var res g1JacExtended - var ff, fff fp.Element - ff.Square(&f) - fff.Mul(&ff, &f) - res.X.Mul(&p.X, &ff) - res.Y.Mul(&p.Y, &fff) - res.ZZ.Mul(&p.ZZ, &ff) - res.ZZZ.Mul(&p.ZZZ, &fff) - return res -} diff --git a/ecc/bls12-378/g2.go b/ecc/bls12-378/g2.go deleted file mode 100644 index 12ed27de58..0000000000 --- a/ecc/bls12-378/g2.go +++ /dev/null @@ -1,1150 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bls12378 - -import ( - "crypto/rand" - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/internal/fptower" - "github.com/consensys/gnark-crypto/internal/parallel" - "math/big" - "runtime" -) - -// G2Affine is a point in affine coordinates (x,y) -type G2Affine struct { - X, Y fptower.E2 -} - -// G2Jac is a point in Jacobian coordinates (x=X/Z², y=Y/Z³) -type G2Jac struct { - X, Y, Z fptower.E2 -} - -// g2JacExtended is a point in extended Jacobian coordinates (x=X/ZZ, y=Y/ZZZ, ZZ³=ZZZ²) -type g2JacExtended struct { - X, Y, ZZ, ZZZ fptower.E2 -} - -// g2Proj point in projective coordinates -type g2Proj struct { - x, y, z fptower.E2 -} - -// ------------------------------------------------------------------------------------------------- -// Affine coordinates - -// Set sets p to a in affine coordinates. -func (p *G2Affine) Set(a *G2Affine) *G2Affine { - p.X, p.Y = a.X, a.Y - return p -} - -// setInfinity sets p to the infinity point, which is encoded as (0,0). -// N.B.: (0,0) is never on the curve for j=0 curves (Y²=X³+B). -func (p *G2Affine) setInfinity() *G2Affine { - p.X.SetZero() - p.Y.SetZero() - return p -} - -// ScalarMultiplication computes and returns p = [s]a -// where p and a are affine points. -func (p *G2Affine) ScalarMultiplication(a *G2Affine, s *big.Int) *G2Affine { - var _p G2Jac - _p.FromAffine(a) - _p.mulGLV(&_p, s) - p.FromJacobian(&_p) - return p -} - -// ScalarMultiplicationBase computes and returns p = [s]g -// where g is the affine point generating the prime subgroup. -func (p *G2Affine) ScalarMultiplicationBase(s *big.Int) *G2Affine { - var _p G2Jac - _p.mulGLV(&g2Gen, s) - p.FromJacobian(&_p) - return p -} - -// Add adds two points in affine coordinates. -// It uses the Jacobian addition with a.Z=b.Z=1 and converts the result to affine coordinates. -// -// https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-mmadd-2007-bl -func (p *G2Affine) Add(a, b *G2Affine) *G2Affine { - var q G2Jac - // a is infinity, return b - if a.IsInfinity() { - p.Set(b) - return p - } - // b is infinity, return a - if b.IsInfinity() { - p.Set(a) - return p - } - if a.X.Equal(&b.X) { - // if b == a, we double instead - if a.Y.Equal(&b.Y) { - q.DoubleMixed(a) - return p.FromJacobian(&q) - } else { - // if b == -a, we return 0 - return p.setInfinity() - } - } - var H, HH, I, J, r, V fptower.E2 - H.Sub(&b.X, &a.X) - HH.Square(&H) - I.Double(&HH).Double(&I) - J.Mul(&H, &I) - r.Sub(&b.Y, &a.Y) - r.Double(&r) - V.Mul(&a.X, &I) - q.X.Square(&r). - Sub(&q.X, &J). - Sub(&q.X, &V). - Sub(&q.X, &V) - q.Y.Sub(&V, &q.X). - Mul(&q.Y, &r) - J.Mul(&a.Y, &J).Double(&J) - q.Y.Sub(&q.Y, &J) - q.Z.Double(&H) - - return p.FromJacobian(&q) -} - -// Double doubles a point in affine coordinates. -// It converts the point to Jacobian coordinates, doubles it using Jacobian -// addition with a.Z=1, and converts it back to affine coordinates. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-mdbl-2007-bl -func (p *G2Affine) Double(a *G2Affine) *G2Affine { - var q G2Jac - q.FromAffine(a) - q.DoubleMixed(a) - p.FromJacobian(&q) - return p -} - -// Sub subtracts two points in affine coordinates. -// It uses a similar approach to Add, but negates the second point before adding. -func (p *G2Affine) Sub(a, b *G2Affine) *G2Affine { - var bneg G2Affine - bneg.Neg(b) - p.Add(a, &bneg) - return p -} - -// Equal tests if two points in affine coordinates are equal. -func (p *G2Affine) Equal(a *G2Affine) bool { - return p.X.Equal(&a.X) && p.Y.Equal(&a.Y) -} - -// Neg sets p to the affine negative point -a = (a.X, -a.Y). -func (p *G2Affine) Neg(a *G2Affine) *G2Affine { - p.X = a.X - p.Y.Neg(&a.Y) - return p -} - -// FromJacobian converts a point p1 from Jacobian to affine coordinates. -func (p *G2Affine) FromJacobian(p1 *G2Jac) *G2Affine { - - var a, b fptower.E2 - - if p1.Z.IsZero() { - p.X.SetZero() - p.Y.SetZero() - return p - } - - a.Inverse(&p1.Z) - b.Square(&a) - p.X.Mul(&p1.X, &b) - p.Y.Mul(&p1.Y, &b).Mul(&p.Y, &a) - - return p -} - -// String returns the string representation E(x,y) of the affine point p or "O" if it is infinity. -func (p *G2Affine) String() string { - if p.IsInfinity() { - return "O" - } - return "E([" + p.X.String() + "," + p.Y.String() + "])" -} - -// IsInfinity checks if the affine point p is infinity, which is encoded as (0,0). -// N.B.: (0,0) is never on the curve for j=0 curves (Y²=X³+B). -func (p *G2Affine) IsInfinity() bool { - return p.X.IsZero() && p.Y.IsZero() -} - -// IsOnCurve returns true if the affine point p in on the curve. -func (p *G2Affine) IsOnCurve() bool { - var point G2Jac - point.FromAffine(p) - return point.IsOnCurve() // call this function to handle infinity point -} - -// IsInSubGroup returns true if the affine point p is in the correct subgroup, false otherwise. -func (p *G2Affine) IsInSubGroup() bool { - var _p G2Jac - _p.FromAffine(p) - return _p.IsInSubGroup() -} - -// ------------------------------------------------------------------------------------------------- -// Jacobian coordinates - -// Set sets p to a in Jacobian coordinates. -func (p *G2Jac) Set(q *G2Jac) *G2Jac { - p.X, p.Y, p.Z = q.X, q.Y, q.Z - return p -} - -// Equal tests if two points in Jacobian coordinates are equal. -func (p *G2Jac) Equal(q *G2Jac) bool { - // If one point is infinity, the other must also be infinity. - if p.Z.IsZero() { - return q.Z.IsZero() - } - // If the other point is infinity, return false since we can't - // the following checks would be incorrect. - if q.Z.IsZero() { - return false - } - - var pZSquare, aZSquare fptower.E2 - pZSquare.Square(&p.Z) - aZSquare.Square(&q.Z) - - var lhs, rhs fptower.E2 - lhs.Mul(&p.X, &aZSquare) - rhs.Mul(&q.X, &pZSquare) - if !lhs.Equal(&rhs) { - return false - } - lhs.Mul(&p.Y, &aZSquare).Mul(&lhs, &q.Z) - rhs.Mul(&q.Y, &pZSquare).Mul(&rhs, &p.Z) - - return lhs.Equal(&rhs) -} - -// Neg sets p to the Jacobian negative point -q = (q.X, -q.Y, q.Z). -func (p *G2Jac) Neg(q *G2Jac) *G2Jac { - *p = *q - p.Y.Neg(&q.Y) - return p -} - -// AddAssign sets p to p+a in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl -func (p *G2Jac) AddAssign(q *G2Jac) *G2Jac { - - // p is infinity, return q - if p.Z.IsZero() { - p.Set(q) - return p - } - - // q is infinity, return p - if q.Z.IsZero() { - return p - } - - var Z1Z1, Z2Z2, U1, U2, S1, S2, H, I, J, r, V fptower.E2 - Z1Z1.Square(&q.Z) - Z2Z2.Square(&p.Z) - U1.Mul(&q.X, &Z2Z2) - U2.Mul(&p.X, &Z1Z1) - S1.Mul(&q.Y, &p.Z). - Mul(&S1, &Z2Z2) - S2.Mul(&p.Y, &q.Z). - Mul(&S2, &Z1Z1) - - // if p == q, we double instead - if U1.Equal(&U2) && S1.Equal(&S2) { - return p.DoubleAssign() - } - - H.Sub(&U2, &U1) - I.Double(&H). - Square(&I) - J.Mul(&H, &I) - r.Sub(&S2, &S1).Double(&r) - V.Mul(&U1, &I) - p.X.Square(&r). - Sub(&p.X, &J). - Sub(&p.X, &V). - Sub(&p.X, &V) - p.Y.Sub(&V, &p.X). - Mul(&p.Y, &r) - S1.Mul(&S1, &J).Double(&S1) - p.Y.Sub(&p.Y, &S1) - p.Z.Add(&p.Z, &q.Z) - p.Z.Square(&p.Z). - Sub(&p.Z, &Z1Z1). - Sub(&p.Z, &Z2Z2). - Mul(&p.Z, &H) - - return p -} - -// SubAssign sets p to p-a in Jacobian coordinates. -// It uses a similar approach to AddAssign, but negates the point a before adding. -func (p *G2Jac) SubAssign(q *G2Jac) *G2Jac { - var tmp G2Jac - tmp.Set(q) - tmp.Y.Neg(&tmp.Y) - p.AddAssign(&tmp) - return p -} - -// Double sets p to [2]q in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2007-bl -func (p *G2Jac) DoubleMixed(a *G2Affine) *G2Jac { - var XX, YY, YYYY, S, M, T fptower.E2 - XX.Square(&a.X) - YY.Square(&a.Y) - YYYY.Square(&YY) - S.Add(&a.X, &YY). - Square(&S). - Sub(&S, &XX). - Sub(&S, &YYYY). - Double(&S) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - T.Square(&M). - Sub(&T, &S). - Sub(&T, &S) - p.X.Set(&T) - p.Y.Sub(&S, &T). - Mul(&p.Y, &M) - YYYY.Double(&YYYY). - Double(&YYYY). - Double(&YYYY) - p.Y.Sub(&p.Y, &YYYY) - p.Z.Double(&a.Y) - - return p -} - -// AddMixed sets p to p+a in Jacobian coordinates, where a.Z = 1. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl -func (p *G2Jac) AddMixed(a *G2Affine) *G2Jac { - - //if a is infinity return p - if a.IsInfinity() { - return p - } - // p is infinity, return a - if p.Z.IsZero() { - p.X = a.X - p.Y = a.Y - p.Z.SetOne() - return p - } - - var Z1Z1, U2, S2, H, HH, I, J, r, V fptower.E2 - Z1Z1.Square(&p.Z) - U2.Mul(&a.X, &Z1Z1) - S2.Mul(&a.Y, &p.Z). - Mul(&S2, &Z1Z1) - - // if p == a, we double instead - if U2.Equal(&p.X) && S2.Equal(&p.Y) { - return p.DoubleMixed(a) - } - - H.Sub(&U2, &p.X) - HH.Square(&H) - I.Double(&HH).Double(&I) - J.Mul(&H, &I) - r.Sub(&S2, &p.Y).Double(&r) - V.Mul(&p.X, &I) - p.X.Square(&r). - Sub(&p.X, &J). - Sub(&p.X, &V). - Sub(&p.X, &V) - J.Mul(&J, &p.Y).Double(&J) - p.Y.Sub(&V, &p.X). - Mul(&p.Y, &r) - p.Y.Sub(&p.Y, &J) - p.Z.Add(&p.Z, &H) - p.Z.Square(&p.Z). - Sub(&p.Z, &Z1Z1). - Sub(&p.Z, &HH) - - return p -} - -// Double sets p to [2]q in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2007-bl -func (p *G2Jac) Double(q *G2Jac) *G2Jac { - p.Set(q) - p.DoubleAssign() - return p -} - -// DoubleAssign doubles p in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2007-bl -func (p *G2Jac) DoubleAssign() *G2Jac { - - var XX, YY, YYYY, ZZ, S, M, T fptower.E2 - - XX.Square(&p.X) - YY.Square(&p.Y) - YYYY.Square(&YY) - ZZ.Square(&p.Z) - S.Add(&p.X, &YY) - S.Square(&S). - Sub(&S, &XX). - Sub(&S, &YYYY). - Double(&S) - M.Double(&XX).Add(&M, &XX) - p.Z.Add(&p.Z, &p.Y). - Square(&p.Z). - Sub(&p.Z, &YY). - Sub(&p.Z, &ZZ) - T.Square(&M) - p.X = T - T.Double(&S) - p.X.Sub(&p.X, &T) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M) - YYYY.Double(&YYYY).Double(&YYYY).Double(&YYYY) - p.Y.Sub(&p.Y, &YYYY) - - return p -} - -// ScalarMultiplication computes and returns p = [s]a -// where p and a are Jacobian points. -// using the GLV technique. -// see https://www.iacr.org/archive/crypto2001/21390189.pdf -func (p *G2Jac) ScalarMultiplication(q *G2Jac, s *big.Int) *G2Jac { - return p.mulGLV(q, s) -} - -// ScalarMultiplicationBase computes and returns p = [s]g -// where g is the prime subgroup generator. -func (p *G2Jac) ScalarMultiplicationBase(s *big.Int) *G2Jac { - return p.mulGLV(&g2Gen, s) - -} - -// String converts p to affine coordinates and returns its string representation E(x,y) or "O" if it is infinity. -func (p *G2Jac) String() string { - _p := G2Affine{} - _p.FromJacobian(p) - return _p.String() -} - -// FromAffine converts a point a from affine to Jacobian coordinates. -func (p *G2Jac) FromAffine(a *G2Affine) *G2Jac { - if a.IsInfinity() { - p.Z.SetZero() - p.X.SetOne() - p.Y.SetOne() - return p - } - p.Z.SetOne() - p.X.Set(&a.X) - p.Y.Set(&a.Y) - return p -} - -// IsOnCurve returns true if the Jacobian point p in on the curve. -func (p *G2Jac) IsOnCurve() bool { - var left, right, tmp, ZZ fptower.E2 - left.Square(&p.Y) - right.Square(&p.X).Mul(&right, &p.X) - ZZ.Square(&p.Z) - tmp.Square(&ZZ).Mul(&tmp, &ZZ) - tmp.Mul(&tmp, &bTwistCurveCoeff) - right.Add(&right, &tmp) - return left.Equal(&right) -} - -// https://eprint.iacr.org/2021/1130.pdf, sec.4 -// and https://eprint.iacr.org/2022/352.pdf, sec. 4.2 -// ψ(p) = [x₀]P -func (p *G2Jac) IsInSubGroup() bool { - var res, tmp G2Jac - tmp.psi(p) - res.ScalarMultiplication(p, &xGen). - SubAssign(&tmp) - - return res.IsOnCurve() && res.Z.IsZero() -} - -// mulWindowed computes the 2-bits windowed double-and-add scalar -// multiplication p=[s]q in Jacobian coordinates. -func (p *G2Jac) mulWindowed(q *G2Jac, s *big.Int) *G2Jac { - - var res G2Jac - var ops [3]G2Jac - - ops[0].Set(q) - if s.Sign() == -1 { - ops[0].Neg(&ops[0]) - } - res.Set(&g2Infinity) - ops[1].Double(&ops[0]) - ops[2].Set(&ops[0]).AddAssign(&ops[1]) - - b := s.Bytes() - for i := range b { - w := b[i] - mask := byte(0xc0) - for j := 0; j < 4; j++ { - res.DoubleAssign().DoubleAssign() - c := (w & mask) >> (6 - 2*j) - if c != 0 { - res.AddAssign(&ops[c-1]) - } - mask = mask >> 2 - } - } - p.Set(&res) - - return p - -} - -// psi sets p to ψ(q) = u o π o u⁻¹ where u:E'→E is the isomorphism from the twist to the curve E and π is the Frobenius map. -func (p *G2Jac) psi(q *G2Jac) *G2Jac { - p.Set(q) - p.X.Conjugate(&p.X).Mul(&p.X, &endo.u) - p.Y.Conjugate(&p.Y).Mul(&p.Y, &endo.v) - p.Z.Conjugate(&p.Z) - return p -} - -// phi sets p to ϕ(a) where ϕ: (x,y) → (w x,y), -// where w is a third root of unity. -func (p *G2Jac) phi(q *G2Jac) *G2Jac { - p.Set(q) - p.X.MulByElement(&p.X, &thirdRootOneG2) - return p -} - -// mulGLV computes the scalar multiplication using a windowed-GLV method -// -// see https://www.iacr.org/archive/crypto2001/21390189.pdf -func (p *G2Jac) mulGLV(q *G2Jac, s *big.Int) *G2Jac { - - var table [15]G2Jac - var res G2Jac - var k1, k2 fr.Element - - res.Set(&g2Infinity) - - // table[b3b2b1b0-1] = b3b2 ⋅ ϕ(q) + b1b0*q - table[0].Set(q) - table[3].phi(q) - - // split the scalar, modifies ±q, ϕ(q) accordingly - k := ecc.SplitScalar(s, &glvBasis) - - if k[0].Sign() == -1 { - k[0].Neg(&k[0]) - table[0].Neg(&table[0]) - } - if k[1].Sign() == -1 { - k[1].Neg(&k[1]) - table[3].Neg(&table[3]) - } - - // precompute table (2 bits sliding window) - // table[b3b2b1b0-1] = b3b2 ⋅ ϕ(q) + b1b0 ⋅ q if b3b2b1b0 != 0 - table[1].Double(&table[0]) - table[2].Set(&table[1]).AddAssign(&table[0]) - table[4].Set(&table[3]).AddAssign(&table[0]) - table[5].Set(&table[3]).AddAssign(&table[1]) - table[6].Set(&table[3]).AddAssign(&table[2]) - table[7].Double(&table[3]) - table[8].Set(&table[7]).AddAssign(&table[0]) - table[9].Set(&table[7]).AddAssign(&table[1]) - table[10].Set(&table[7]).AddAssign(&table[2]) - table[11].Set(&table[7]).AddAssign(&table[3]) - table[12].Set(&table[11]).AddAssign(&table[0]) - table[13].Set(&table[11]).AddAssign(&table[1]) - table[14].Set(&table[11]).AddAssign(&table[2]) - - // bounds on the lattice base vectors guarantee that k1, k2 are len(r)/2 or len(r)/2+1 bits long max - // this is because we use a probabilistic scalar decomposition that replaces a division by a right-shift - k1 = k1.SetBigInt(&k[0]).Bits() - k2 = k2.SetBigInt(&k[1]).Bits() - - // we don't target constant-timeness so we check first if we increase the bounds or not - maxBit := k1.BitLen() - if k2.BitLen() > maxBit { - maxBit = k2.BitLen() - } - hiWordIndex := (maxBit - 1) / 64 - - // loop starts from len(k1)/2 or len(k1)/2+1 due to the bounds - for i := hiWordIndex; i >= 0; i-- { - mask := uint64(3) << 62 - for j := 0; j < 32; j++ { - res.Double(&res).Double(&res) - b1 := (k1[i] & mask) >> (62 - 2*j) - b2 := (k2[i] & mask) >> (62 - 2*j) - if b1|b2 != 0 { - s := (b2<<2 | b1) - res.AddAssign(&table[s-1]) - } - mask = mask >> 2 - } - } - - p.Set(&res) - return p -} - -// ClearCofactor maps a point in curve to r-torsion -func (p *G2Affine) ClearCofactor(a *G2Affine) *G2Affine { - var _p G2Jac - _p.FromAffine(a) - _p.ClearCofactor(&_p) - p.FromJacobian(&_p) - return p -} - -// ClearCofactor maps a point in curve to r-torsion -func (p *G2Jac) ClearCofactor(q *G2Jac) *G2Jac { - // https://eprint.iacr.org/2017/419.pdf, 4.1 - var xg, xxg, res, t G2Jac - xg.ScalarMultiplication(q, &xGen) - xxg.ScalarMultiplication(&xg, &xGen) - - res.Set(&xxg). - SubAssign(&xg). - SubAssign(q) - - t.Set(&xg). - SubAssign(q). - psi(&t) - - res.AddAssign(&t) - - t.Double(q) - t.X.MulByElement(&t.X, &thirdRootOneG1) - - res.SubAssign(&t) - - p.Set(&res) - - return p - -} - -// ------------------------------------------------------------------------------------------------- -// extended Jacobian coordinates - -// Set sets p to a in extended Jacobian coordinates. -func (p *g2JacExtended) Set(q *g2JacExtended) *g2JacExtended { - p.X, p.Y, p.ZZ, p.ZZZ = q.X, q.Y, q.ZZ, q.ZZZ - return p -} - -// setInfinity sets p to the infinity point (1,1,0,0). -func (p *g2JacExtended) setInfinity() *g2JacExtended { - p.X.SetOne() - p.Y.SetOne() - p.ZZ = fptower.E2{} - p.ZZZ = fptower.E2{} - return p -} - -// IsInfinity checks if the p is infinity, i.e. p.ZZ=0. -func (p *g2JacExtended) IsInfinity() bool { - return p.ZZ.IsZero() -} - -// fromJacExtended converts an extended Jacobian point to an affine point. -func (p *G2Affine) fromJacExtended(q *g2JacExtended) *G2Affine { - if q.ZZ.IsZero() { - p.X = fptower.E2{} - p.Y = fptower.E2{} - return p - } - p.X.Inverse(&q.ZZ).Mul(&p.X, &q.X) - p.Y.Inverse(&q.ZZZ).Mul(&p.Y, &q.Y) - return p -} - -// fromJacExtended converts an extended Jacobian point to a Jacobian point. -func (p *G2Jac) fromJacExtended(q *g2JacExtended) *G2Jac { - if q.ZZ.IsZero() { - p.Set(&g2Infinity) - return p - } - p.X.Mul(&q.ZZ, &q.X).Mul(&p.X, &q.ZZ) - p.Y.Mul(&q.ZZZ, &q.Y).Mul(&p.Y, &q.ZZZ) - p.Z.Set(&q.ZZZ) - return p -} - -// unsafeFromJacExtended converts an extended Jacobian point, distinct from Infinity, to a Jacobian point. -func (p *G2Jac) unsafeFromJacExtended(q *g2JacExtended) *G2Jac { - p.X.Square(&q.ZZ).Mul(&p.X, &q.X) - p.Y.Square(&q.ZZZ).Mul(&p.Y, &q.Y) - p.Z = q.ZZZ - return p -} - -// add sets p to p+q in extended Jacobian coordinates. -// -// https://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#addition-add-2008-s -func (p *g2JacExtended) add(q *g2JacExtended) *g2JacExtended { - //if q is infinity return p - if q.ZZ.IsZero() { - return p - } - // p is infinity, return q - if p.ZZ.IsZero() { - p.Set(q) - return p - } - - var A, B, U1, U2, S1, S2 fptower.E2 - - // p2: q, p1: p - U2.Mul(&q.X, &p.ZZ) - U1.Mul(&p.X, &q.ZZ) - A.Sub(&U2, &U1) - S2.Mul(&q.Y, &p.ZZZ) - S1.Mul(&p.Y, &q.ZZZ) - B.Sub(&S2, &S1) - - if A.IsZero() { - if B.IsZero() { - return p.double(q) - - } - p.ZZ = fptower.E2{} - p.ZZZ = fptower.E2{} - return p - } - - var P, R, PP, PPP, Q, V fptower.E2 - P.Sub(&U2, &U1) - R.Sub(&S2, &S1) - PP.Square(&P) - PPP.Mul(&P, &PP) - Q.Mul(&U1, &PP) - V.Mul(&S1, &PPP) - - p.X.Square(&R). - Sub(&p.X, &PPP). - Sub(&p.X, &Q). - Sub(&p.X, &Q) - p.Y.Sub(&Q, &p.X). - Mul(&p.Y, &R). - Sub(&p.Y, &V) - p.ZZ.Mul(&p.ZZ, &q.ZZ). - Mul(&p.ZZ, &PP) - p.ZZZ.Mul(&p.ZZZ, &q.ZZZ). - Mul(&p.ZZZ, &PPP) - - return p -} - -// double sets p to [2]q in Jacobian extended coordinates. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-dbl-2008-s-1 -// N.B.: since we consider any point on Z=0 as the point at infinity -// this doubling formula works for infinity points as well. -func (p *g2JacExtended) double(q *g2JacExtended) *g2JacExtended { - var U, V, W, S, XX, M fptower.E2 - - U.Double(&q.Y) - V.Square(&U) - W.Mul(&U, &V) - S.Mul(&q.X, &V) - XX.Square(&q.X) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - U.Mul(&W, &q.Y) - - p.X.Square(&M). - Sub(&p.X, &S). - Sub(&p.X, &S) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M). - Sub(&p.Y, &U) - p.ZZ.Mul(&V, &q.ZZ) - p.ZZZ.Mul(&W, &q.ZZZ) - - return p -} - -// addMixed sets p to p+q in extended Jacobian coordinates, where a.ZZ=1. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#addition-madd-2008-s -func (p *g2JacExtended) addMixed(a *G2Affine) *g2JacExtended { - - //if a is infinity return p - if a.IsInfinity() { - return p - } - // p is infinity, return a - if p.ZZ.IsZero() { - p.X = a.X - p.Y = a.Y - p.ZZ.SetOne() - p.ZZZ.SetOne() - return p - } - - var P, R fptower.E2 - - // p2: a, p1: p - P.Mul(&a.X, &p.ZZ) - P.Sub(&P, &p.X) - - R.Mul(&a.Y, &p.ZZZ) - R.Sub(&R, &p.Y) - - if P.IsZero() { - if R.IsZero() { - return p.doubleMixed(a) - - } - p.ZZ = fptower.E2{} - p.ZZZ = fptower.E2{} - return p - } - - var PP, PPP, Q, Q2, RR, X3, Y3 fptower.E2 - - PP.Square(&P) - PPP.Mul(&P, &PP) - Q.Mul(&p.X, &PP) - RR.Square(&R) - X3.Sub(&RR, &PPP) - Q2.Double(&Q) - p.X.Sub(&X3, &Q2) - Y3.Sub(&Q, &p.X).Mul(&Y3, &R) - R.Mul(&p.Y, &PPP) - p.Y.Sub(&Y3, &R) - p.ZZ.Mul(&p.ZZ, &PP) - p.ZZZ.Mul(&p.ZZZ, &PPP) - - return p - -} - -// subMixed works the same as addMixed, but negates a.Y. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#addition-madd-2008-s -func (p *g2JacExtended) subMixed(a *G2Affine) *g2JacExtended { - - //if a is infinity return p - if a.IsInfinity() { - return p - } - // p is infinity, return a - if p.ZZ.IsZero() { - p.X = a.X - p.Y.Neg(&a.Y) - p.ZZ.SetOne() - p.ZZZ.SetOne() - return p - } - - var P, R fptower.E2 - - // p2: a, p1: p - P.Mul(&a.X, &p.ZZ) - P.Sub(&P, &p.X) - - R.Mul(&a.Y, &p.ZZZ) - R.Neg(&R) - R.Sub(&R, &p.Y) - - if P.IsZero() { - if R.IsZero() { - return p.doubleNegMixed(a) - - } - p.ZZ = fptower.E2{} - p.ZZZ = fptower.E2{} - return p - } - - var PP, PPP, Q, Q2, RR, X3, Y3 fptower.E2 - - PP.Square(&P) - PPP.Mul(&P, &PP) - Q.Mul(&p.X, &PP) - RR.Square(&R) - X3.Sub(&RR, &PPP) - Q2.Double(&Q) - p.X.Sub(&X3, &Q2) - Y3.Sub(&Q, &p.X).Mul(&Y3, &R) - R.Mul(&p.Y, &PPP) - p.Y.Sub(&Y3, &R) - p.ZZ.Mul(&p.ZZ, &PP) - p.ZZZ.Mul(&p.ZZZ, &PPP) - - return p - -} - -// doubleNegMixed works the same as double, but negates q.Y. -func (p *g2JacExtended) doubleNegMixed(a *G2Affine) *g2JacExtended { - - var U, V, W, S, XX, M, S2, L fptower.E2 - - U.Double(&a.Y) - U.Neg(&U) - V.Square(&U) - W.Mul(&U, &V) - S.Mul(&a.X, &V) - XX.Square(&a.X) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - S2.Double(&S) - L.Mul(&W, &a.Y) - - p.X.Square(&M). - Sub(&p.X, &S2) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M). - Add(&p.Y, &L) - p.ZZ.Set(&V) - p.ZZZ.Set(&W) - - return p -} - -// doubleMixed sets p to [2]a in Jacobian extended coordinates, where a.ZZ=1. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-dbl-2008-s-1 -func (p *g2JacExtended) doubleMixed(a *G2Affine) *g2JacExtended { - - var U, V, W, S, XX, M, S2, L fptower.E2 - - U.Double(&a.Y) - V.Square(&U) - W.Mul(&U, &V) - S.Mul(&a.X, &V) - XX.Square(&a.X) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - S2.Double(&S) - L.Mul(&W, &a.Y) - - p.X.Square(&M). - Sub(&p.X, &S2) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M). - Sub(&p.Y, &L) - p.ZZ.Set(&V) - p.ZZZ.Set(&W) - - return p -} - -// ------------------------------------------------------------------------------------------------- -// Homogenous projective coordinates - -// Set sets p to a in projective coordinates. -func (p *g2Proj) Set(q *g2Proj) *g2Proj { - p.x, p.y, p.z = q.x, q.y, q.z - return p -} - -// Neg sets p to the projective negative point -q = (q.X, -q.Y). -func (p *g2Proj) Neg(q *g2Proj) *g2Proj { - *p = *q - p.y.Neg(&q.y) - return p -} - -// FromAffine converts q in affine to p in projective coordinates. -func (p *g2Proj) FromAffine(a *G2Affine) *g2Proj { - if a.X.IsZero() && a.Y.IsZero() { - p.z.SetZero() - p.x.SetOne() - p.y.SetOne() - return p - } - p.z.SetOne() - p.x.Set(&a.X) - p.y.Set(&a.Y) - return p -} - -// BatchScalarMultiplicationG2 multiplies the same base by all scalars -// and return resulting points in affine coordinates -// uses a simple windowed-NAF-like multiplication algorithm. -func BatchScalarMultiplicationG2(base *G2Affine, scalars []fr.Element) []G2Affine { - // approximate cost in group ops is - // cost = 2^{c-1} + n(scalar.nbBits+nbChunks) - - nbPoints := uint64(len(scalars)) - min := ^uint64(0) - bestC := 0 - for c := 2; c <= 16; c++ { - cost := uint64(1 << (c - 1)) // pre compute the table - nbChunks := computeNbChunks(uint64(c)) - cost += nbPoints * (uint64(c) + 1) * nbChunks // doublings + point add - if cost < min { - min = cost - bestC = c - } - } - c := uint64(bestC) // window size - nbChunks := int(computeNbChunks(c)) - - // last window may be slightly larger than c; in which case we need to compute one - // extra element in the baseTable - maxC := lastC(c) - if c > maxC { - maxC = c - } - - // precompute all powers of base for our window - // note here that if performance is critical, we can implement as in the msmX methods - // this allocation to be on the stack - baseTable := make([]G2Jac, (1 << (maxC - 1))) - baseTable[0].FromAffine(base) - for i := 1; i < len(baseTable); i++ { - baseTable[i] = baseTable[i-1] - baseTable[i].AddMixed(base) - } - toReturn := make([]G2Affine, len(scalars)) - - // partition the scalars into digits - digits, _ := partitionScalars(scalars, c, runtime.NumCPU()) - - // for each digit, take value in the base table, double it c time, voilà. - parallel.Execute(len(scalars), func(start, end int) { - var p G2Jac - for i := start; i < end; i++ { - p.Set(&g2Infinity) - for chunk := nbChunks - 1; chunk >= 0; chunk-- { - if chunk != nbChunks-1 { - for j := uint64(0); j < c; j++ { - p.DoubleAssign() - } - } - offset := chunk * len(scalars) - digit := digits[i+offset] - - if digit == 0 { - continue - } - - // if msbWindow bit is set, we need to subtract - if digit&1 == 0 { - // add - p.AddAssign(&baseTable[(digit>>1)-1]) - } else { - // sub - t := baseTable[digit>>1] - t.Neg(&t) - p.AddAssign(&t) - } - } - - // set our result point - toReturn[i].FromJacobian(&p) - - } - }) - return toReturn -} - -// batchAddG2Affine adds affine points using the Montgomery batch inversion trick. -// Special cases (doubling, infinity) must be filtered out before this call. -func batchAddG2Affine[TP pG2Affine, TPP ppG2Affine, TC cG2Affine](R *TPP, P *TP, batchSize int) { - var lambda, lambdain TC - - // add part - for j := 0; j < batchSize; j++ { - lambdain[j].Sub(&(*P)[j].X, &(*R)[j].X) - } - - // invert denominator using montgomery batch invert technique - { - var accumulator fptower.E2 - lambda[0].SetOne() - accumulator.Set(&lambdain[0]) - - for i := 1; i < batchSize; i++ { - lambda[i] = accumulator - accumulator.Mul(&accumulator, &lambdain[i]) - } - - accumulator.Inverse(&accumulator) - - for i := batchSize - 1; i > 0; i-- { - lambda[i].Mul(&lambda[i], &accumulator) - accumulator.Mul(&accumulator, &lambdain[i]) - } - lambda[0].Set(&accumulator) - } - - var d fptower.E2 - var rr G2Affine - - // add part - for j := 0; j < batchSize; j++ { - // computa lambda - d.Sub(&(*P)[j].Y, &(*R)[j].Y) - lambda[j].Mul(&lambda[j], &d) - - // compute X, Y - rr.X.Square(&lambda[j]) - rr.X.Sub(&rr.X, &(*R)[j].X) - rr.X.Sub(&rr.X, &(*P)[j].X) - d.Sub(&(*R)[j].X, &rr.X) - rr.Y.Mul(&lambda[j], &d) - rr.Y.Sub(&rr.Y, &(*R)[j].Y) - (*R)[j].Set(&rr) - } -} - -// RandomOnG2 produces a random point in G2 -// using standard map-to-curve methods, which means the relative discrete log -// of the generated point with respect to the canonical generator is not known. -func RandomOnG2() (G2Affine, error) { - if gBytes, err := randomFrSizedBytes(); err != nil { - return G2Affine{}, err - } else { - return HashToG2(gBytes, []byte("random on g2")) - } -} - -func randomFrSizedBytes() ([]byte, error) { - res := make([]byte, fr.Bytes) - _, err := rand.Read(res) - return res, err -} diff --git a/ecc/bls12-378/g2_test.go b/ecc/bls12-378/g2_test.go deleted file mode 100644 index e28b5e748b..0000000000 --- a/ecc/bls12-378/g2_test.go +++ /dev/null @@ -1,874 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bls12378 - -import ( - "fmt" - "math/big" - "math/rand/v2" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/internal/fptower" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -func TestG2AffineEndomorphism(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BLS12-378] check that phi(P) = lambdaGLV * P", prop.ForAll( - func(a fptower.E2) bool { - var p, res1, res2 G2Jac - g := MapToG2(a) - p.FromAffine(&g) - res1.phi(&p) - res2.mulWindowed(&p, &lambdaGLV) - - return p.IsInSubGroup() && res1.Equal(&res2) - }, - GenE2(), - )) - - properties.Property("[BLS12-378] check that phi^2(P) + phi(P) + P = 0", prop.ForAll( - func(a fptower.E2) bool { - var p, res, tmp G2Jac - g := MapToG2(a) - p.FromAffine(&g) - tmp.phi(&p) - res.phi(&tmp). - AddAssign(&tmp). - AddAssign(&p) - - return res.Z.IsZero() - }, - GenE2(), - )) - - properties.Property("[BLS12-378] check that psi^2(P) = -phi(P)", prop.ForAll( - func(a fptower.E2) bool { - var p, res1, res2 G2Jac - g := MapToG2(a) - p.FromAffine(&g) - res1.psi(&p).psi(&res1).Neg(&res1) - res2.Set(&p) - res2.X.MulByElement(&res2.X, &thirdRootOneG1) - - return p.IsInSubGroup() && res1.Equal(&res2) - }, - GenE2(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG2AffineIsOnCurve(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BLS12-378] g2Gen (affine) should be on the curve", prop.ForAll( - func(a fptower.E2) bool { - var op1, op2 G2Affine - op1.FromJacobian(&g2Gen) - op2.Set(&op1) - op2.Y.Mul(&op2.Y, &a) - return op1.IsOnCurve() && !op2.IsOnCurve() - }, - GenE2(), - )) - - properties.Property("[BLS12-378] g2Gen (Jacobian) should be on the curve", prop.ForAll( - func(a fptower.E2) bool { - var op1, op2, op3 G2Jac - op1.Set(&g2Gen) - op3.Set(&g2Gen) - - op2 = fuzzG2Jac(&g2Gen, a) - op3.Y.Mul(&op3.Y, &a) - return op1.IsOnCurve() && op2.IsOnCurve() && !op3.IsOnCurve() - }, - GenE2(), - )) - - properties.Property("[BLS12-378] IsInSubGroup and MulBy subgroup order should be the same", prop.ForAll( - func(a fptower.E2) bool { - var op1, op2 G2Jac - op1 = fuzzG2Jac(&g2Gen, a) - _r := fr.Modulus() - op2.ScalarMultiplication(&op1, _r) - return op1.IsInSubGroup() && op2.Z.IsZero() - }, - GenE2(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG2AffineConversions(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BLS12-378] Affine representation should be independent of the Jacobian representative", prop.ForAll( - func(a fptower.E2) bool { - g := fuzzG2Jac(&g2Gen, a) - var op1 G2Affine - op1.FromJacobian(&g) - return op1.X.Equal(&g2Gen.X) && op1.Y.Equal(&g2Gen.Y) - }, - GenE2(), - )) - - properties.Property("[BLS12-378] Affine representation should be independent of a Extended Jacobian representative", prop.ForAll( - func(a fptower.E2) bool { - var g g2JacExtended - g.X.Set(&g2Gen.X) - g.Y.Set(&g2Gen.Y) - g.ZZ.Set(&g2Gen.Z) - g.ZZZ.Set(&g2Gen.Z) - gfuzz := fuzzg2JacExtended(&g, a) - - var op1 G2Affine - op1.fromJacExtended(&gfuzz) - return op1.X.Equal(&g2Gen.X) && op1.Y.Equal(&g2Gen.Y) - }, - GenE2(), - )) - - properties.Property("[BLS12-378] Jacobian representation should be the same as the affine representative", prop.ForAll( - func(a fptower.E2) bool { - var g G2Jac - var op1 G2Affine - op1.X.Set(&g2Gen.X) - op1.Y.Set(&g2Gen.Y) - - var one fptower.E2 - one.SetOne() - - g.FromAffine(&op1) - - return g.X.Equal(&g2Gen.X) && g.Y.Equal(&g2Gen.Y) && g.Z.Equal(&one) - }, - GenE2(), - )) - - properties.Property("[BLS12-378] Converting affine symbol for infinity to Jacobian should output correct infinity in Jacobian", prop.ForAll( - func() bool { - var g G2Affine - g.X.SetZero() - g.Y.SetZero() - var op1 G2Jac - op1.FromAffine(&g) - var one, zero fptower.E2 - one.SetOne() - return op1.X.Equal(&one) && op1.Y.Equal(&one) && op1.Z.Equal(&zero) - }, - )) - - properties.Property("[BLS12-378] Converting infinity in extended Jacobian to affine should output infinity symbol in Affine", prop.ForAll( - func() bool { - var g G2Affine - var op1 g2JacExtended - var zero fptower.E2 - op1.X.Set(&g2Gen.X) - op1.Y.Set(&g2Gen.Y) - g.fromJacExtended(&op1) - return g.X.Equal(&zero) && g.Y.Equal(&zero) - }, - )) - - properties.Property("[BLS12-378] Converting infinity in extended Jacobian to Jacobian should output infinity in Jacobian", prop.ForAll( - func() bool { - var g G2Jac - var op1 g2JacExtended - var zero, one fptower.E2 - one.SetOne() - op1.X.Set(&g2Gen.X) - op1.Y.Set(&g2Gen.Y) - g.fromJacExtended(&op1) - return g.X.Equal(&one) && g.Y.Equal(&one) && g.Z.Equal(&zero) - }, - )) - - properties.Property("[BLS12-378] [Jacobian] Two representatives of the same class should be equal", prop.ForAll( - func(a, b fptower.E2) bool { - op1 := fuzzG2Jac(&g2Gen, a) - op2 := fuzzG2Jac(&g2Gen, b) - return op1.Equal(&op2) - }, - GenE2(), - GenE2(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG2AffineOps(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - parameters.MinSuccessfulTests = 10 - - properties := gopter.NewProperties(parameters) - - genScalar := GenFr() - - properties.Property("[BLS12-378] Add(P,-P) should return the point at infinity", prop.ForAll( - func(s fr.Element) bool { - var op1, op2 G2Affine - var sInt big.Int - g := g2GenAff - s.BigInt(&sInt) - op1.ScalarMultiplication(&g, &sInt) - op2.Neg(&op1) - - op1.Add(&op1, &op2) - return op1.IsInfinity() - - }, - GenFr(), - )) - - properties.Property("[BLS12-378] Add(P,0) and Add(0,P) should return P", prop.ForAll( - func(s fr.Element) bool { - var op1, op2 G2Affine - var sInt big.Int - g := g2GenAff - s.BigInt(&sInt) - op1.ScalarMultiplication(&g, &sInt) - op2.setInfinity() - - op1.Add(&op1, &op2) - op2.Add(&op2, &op1) - return op1.Equal(&op2) - - }, - GenFr(), - )) - - properties.Property("[BLS12-378] Add should call double when adding the same point", prop.ForAll( - func(s fr.Element) bool { - var op1, op2 G2Affine - var sInt big.Int - g := g2GenAff - s.BigInt(&sInt) - op1.ScalarMultiplication(&g, &sInt) - - op2.Double(&op1) - op1.Add(&op1, &op1) - return op1.Equal(&op2) - - }, - GenFr(), - )) - - properties.Property("[BLS12-378] [2]G = double(G) + G - G", prop.ForAll( - func(s fr.Element) bool { - var sInt big.Int - g := g2GenAff - s.BigInt(&sInt) - g.ScalarMultiplication(&g, &sInt) - var op1, op2 G2Affine - op1.ScalarMultiplication(&g, big.NewInt(2)) - op2.Double(&g) - op2.Add(&op2, &g) - op2.Sub(&op2, &g) - return op1.Equal(&op2) - }, - GenFr(), - )) - - properties.Property("[BLS12-378] [-s]G = -[s]G", prop.ForAll( - func(s fr.Element) bool { - g := g2GenAff - var gj G2Jac - var nbs, bs big.Int - s.BigInt(&bs) - nbs.Neg(&bs) - - var res = true - - // mulGLV - { - var op1, op2 G2Affine - op1.ScalarMultiplication(&g, &bs).Neg(&op1) - op2.ScalarMultiplication(&g, &nbs) - res = res && op1.Equal(&op2) - } - - // mulWindowed - { - var op1, op2 G2Jac - op1.mulWindowed(&gj, &bs).Neg(&op1) - op2.mulWindowed(&gj, &nbs) - res = res && op1.Equal(&op2) - } - - return res - }, - GenFr(), - )) - - properties.Property("[BLS12-378] [Jacobian] Add should call double when adding the same point", prop.ForAll( - func(a, b fptower.E2) bool { - fop1 := fuzzG2Jac(&g2Gen, a) - fop2 := fuzzG2Jac(&g2Gen, b) - var op1, op2 G2Jac - op1.Set(&fop1).AddAssign(&fop2) - op2.Double(&fop2) - return op1.Equal(&op2) - }, - GenE2(), - GenE2(), - )) - - properties.Property("[BLS12-378] [Jacobian] Adding the opposite of a point to itself should output inf", prop.ForAll( - func(a, b fptower.E2) bool { - fop1 := fuzzG2Jac(&g2Gen, a) - fop2 := fuzzG2Jac(&g2Gen, b) - fop2.Neg(&fop2) - fop1.AddAssign(&fop2) - return fop1.Equal(&g2Infinity) - }, - GenE2(), - GenE2(), - )) - - properties.Property("[BLS12-378] [Jacobian] Adding the inf to a point should not modify the point", prop.ForAll( - func(a fptower.E2) bool { - fop1 := fuzzG2Jac(&g2Gen, a) - fop1.AddAssign(&g2Infinity) - var op2 G2Jac - op2.Set(&g2Infinity) - op2.AddAssign(&g2Gen) - return fop1.Equal(&g2Gen) && op2.Equal(&g2Gen) - }, - GenE2(), - )) - - properties.Property("[BLS12-378] [Jacobian Extended] addMixed (-G) should equal subMixed(G)", prop.ForAll( - func(a fptower.E2) bool { - fop1 := fuzzG2Jac(&g2Gen, a) - var p1, p1Neg G2Affine - p1.FromJacobian(&fop1) - p1Neg = p1 - p1Neg.Y.Neg(&p1Neg.Y) - var o1, o2 g2JacExtended - o1.addMixed(&p1Neg) - o2.subMixed(&p1) - - return o1.X.Equal(&o2.X) && - o1.Y.Equal(&o2.Y) && - o1.ZZ.Equal(&o2.ZZ) && - o1.ZZZ.Equal(&o2.ZZZ) - }, - GenE2(), - )) - - properties.Property("[BLS12-378] [Jacobian Extended] doubleMixed (-G) should equal doubleNegMixed(G)", prop.ForAll( - func(a fptower.E2) bool { - fop1 := fuzzG2Jac(&g2Gen, a) - var p1, p1Neg G2Affine - p1.FromJacobian(&fop1) - p1Neg = p1 - p1Neg.Y.Neg(&p1Neg.Y) - var o1, o2 g2JacExtended - o1.doubleMixed(&p1Neg) - o2.doubleNegMixed(&p1) - - return o1.X.Equal(&o2.X) && - o1.Y.Equal(&o2.Y) && - o1.ZZ.Equal(&o2.ZZ) && - o1.ZZZ.Equal(&o2.ZZZ) - }, - GenE2(), - )) - - properties.Property("[BLS12-378] [Jacobian] Addmix the negation to itself should output 0", prop.ForAll( - func(a fptower.E2) bool { - fop1 := fuzzG2Jac(&g2Gen, a) - fop1.Neg(&fop1) - var op2 G2Affine - op2.FromJacobian(&g2Gen) - fop1.AddMixed(&op2) - return fop1.Equal(&g2Infinity) - }, - GenE2(), - )) - - properties.Property("[BLS12-378] scalar multiplication (double and add) should depend only on the scalar mod r", prop.ForAll( - func(s fr.Element) bool { - - r := fr.Modulus() - var g G2Jac - g.ScalarMultiplication(&g2Gen, r) - - var scalar, blindedScalar, rminusone big.Int - var op1, op2, op3, gneg G2Jac - rminusone.SetUint64(1).Sub(r, &rminusone) - op3.mulWindowed(&g2Gen, &rminusone) - gneg.Neg(&g2Gen) - s.BigInt(&scalar) - blindedScalar.Mul(&scalar, r).Add(&blindedScalar, &scalar) - op1.mulWindowed(&g2Gen, &scalar) - op2.mulWindowed(&g2Gen, &blindedScalar) - - return op1.Equal(&op2) && g.Equal(&g2Infinity) && !op1.Equal(&g2Infinity) && gneg.Equal(&op3) - - }, - genScalar, - )) - - properties.Property("[BLS12-378] psi should map points from E' to itself", prop.ForAll( - func() bool { - var a G2Jac - a.psi(&g2Gen) - return a.IsOnCurve() && !a.Equal(&g2Gen) - }, - )) - - properties.Property("[BLS12-378] scalar multiplication (GLV) should depend only on the scalar mod r", prop.ForAll( - func(s fr.Element) bool { - - r := fr.Modulus() - var g G2Jac - g.mulGLV(&g2Gen, r) - - var scalar, blindedScalar, rminusone big.Int - var op1, op2, op3, gneg G2Jac - rminusone.SetUint64(1).Sub(r, &rminusone) - op3.ScalarMultiplication(&g2Gen, &rminusone) - gneg.Neg(&g2Gen) - s.BigInt(&scalar) - blindedScalar.Mul(&scalar, r).Add(&blindedScalar, &scalar) - op1.ScalarMultiplication(&g2Gen, &scalar) - op2.ScalarMultiplication(&g2Gen, &blindedScalar) - - return op1.Equal(&op2) && g.Equal(&g2Infinity) && !op1.Equal(&g2Infinity) && gneg.Equal(&op3) - - }, - genScalar, - )) - - properties.Property("[BLS12-378] GLV and Double and Add should output the same result", prop.ForAll( - func(s fr.Element) bool { - - var r big.Int - var op1, op2 G2Jac - s.BigInt(&r) - op1.mulWindowed(&g2Gen, &r) - op2.mulGLV(&g2Gen, &r) - return op1.Equal(&op2) && !op1.Equal(&g2Infinity) - - }, - genScalar, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG2AffineCofactorCleaning(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BLS12-378] Clearing the cofactor of a random point should set it in the r-torsion", prop.ForAll( - func() bool { - var a, x, b fptower.E2 - a.SetRandom() - - x.Square(&a).Mul(&x, &a).Add(&x, &bTwistCurveCoeff) - for x.Legendre() != 1 { - a.SetRandom() - x.Square(&a).Mul(&x, &a).Add(&x, &bTwistCurveCoeff) - } - - b.Sqrt(&x) - var point, pointCleared, infinity G2Jac - point.X.Set(&a) - point.Y.Set(&b) - point.Z.SetOne() - pointCleared.ClearCofactor(&point) - infinity.Set(&g2Infinity) - return point.IsOnCurve() && pointCleared.IsInSubGroup() && !pointCleared.Equal(&infinity) - }, - )) - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestG2AffineBatchScalarMultiplication(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzzShort - } - - properties := gopter.NewProperties(parameters) - - genScalar := GenFr() - - // size of the multiExps - const nbSamples = 10 - - properties.Property("[BLS12-378] BatchScalarMultiplication should be consistent with individual scalar multiplications", prop.ForAll( - func(mixer fr.Element) bool { - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - result := BatchScalarMultiplicationG2(&g2GenAff, sampleScalars[:]) - - if len(result) != len(sampleScalars) { - return false - } - - for i := 0; i < len(result); i++ { - var expectedJac G2Jac - var expected G2Affine - var b big.Int - expectedJac.ScalarMultiplication(&g2Gen, sampleScalars[i].BigInt(&b)) - expected.FromJacobian(&expectedJac) - if !result[i].Equal(&expected) { - return false - } - } - return true - }, - genScalar, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -// ------------------------------------------------------------ -// benches - -func BenchmarkG2JacIsInSubGroup(b *testing.B) { - var a G2Jac - a.Set(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.IsInSubGroup() - } - -} - -func BenchmarkG2JacEqual(b *testing.B) { - var scalar fptower.E2 - if _, err := scalar.SetRandom(); err != nil { - b.Fatalf("failed to set scalar: %s", err) - } - - var a G2Jac - a.ScalarMultiplication(&g2Gen, big.NewInt(42)) - - b.Run("equal", func(b *testing.B) { - var scalarSquared fptower.E2 - scalarSquared.Square(&scalar) - - aZScaled := a - aZScaled.X.Mul(&aZScaled.X, &scalarSquared) - aZScaled.Y.Mul(&aZScaled.Y, &scalarSquared).Mul(&aZScaled.Y, &scalar) - aZScaled.Z.Mul(&aZScaled.Z, &scalar) - - // Check the setup. - if !a.Equal(&aZScaled) { - b.Fatalf("invalid test setup") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Equal(&aZScaled) - } - }) - - b.Run("not equal", func(b *testing.B) { - var aPlus1 G2Jac - aPlus1.AddAssign(&g2Gen) - - // Check the setup. - if a.Equal(&aPlus1) { - b.Fatalf("invalid test setup") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Equal(&aPlus1) - } - }) -} - -func BenchmarkBatchAddG2Affine(b *testing.B) { - - var P, R pG2AffineC16 - var RR ppG2AffineC16 - ridx := make([]int, len(P)) - - // TODO P == R may produce skewed benches - fillBenchBasesG2(P[:]) - fillBenchBasesG2(R[:]) - - for i := 0; i < len(ridx); i++ { - ridx[i] = i - } - - // random permute - rand.Shuffle(len(ridx), func(i, j int) { ridx[i], ridx[j] = ridx[j], ridx[i] }) - - for i, ri := range ridx { - RR[i] = &R[ri] - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - batchAddG2Affine[pG2AffineC16, ppG2AffineC16, cG2AffineC16](&RR, &P, len(P)) - } -} - -func BenchmarkG2AffineBatchScalarMultiplication(b *testing.B) { - // ensure every words of the scalars are filled - var mixer fr.Element - mixer.SetString("7716837800905789770901243404444209691916730933998574719964609384059111546487") - - const pow = 15 - const nbSamples = 1 << pow - - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - for i := 5; i <= pow; i++ { - using := 1 << i - - b.Run(fmt.Sprintf("%d points", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - _ = BatchScalarMultiplicationG2(&g2GenAff, sampleScalars[:using]) - } - }) - } -} - -func BenchmarkG2JacScalarMultiplication(b *testing.B) { - - var scalar big.Int - r := fr.Modulus() - scalar.SetString("5243587517512619047944770508185965837690552500527637822603658699938581184513", 10) - scalar.Add(&scalar, r) - - var doubleAndAdd G2Jac - - b.Run("double and add", func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - doubleAndAdd.mulWindowed(&g2Gen, &scalar) - } - }) - - var glv G2Jac - b.Run("GLV", func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - glv.mulGLV(&g2Gen, &scalar) - } - }) - -} - -func BenchmarkG2AffineCofactorClearing(b *testing.B) { - var a G2Jac - a.Set(&g2Gen) - for i := 0; i < b.N; i++ { - a.ClearCofactor(&a) - } -} - -func BenchmarkG2JacAdd(b *testing.B) { - var a G2Jac - a.Double(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.AddAssign(&g2Gen) - } -} - -func BenchmarkG2JacAddMixed(b *testing.B) { - var a G2Jac - a.Double(&g2Gen) - - var c G2Affine - c.FromJacobian(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.AddMixed(&c) - } - -} - -func BenchmarkG2JacDouble(b *testing.B) { - var a G2Jac - a.Set(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.DoubleAssign() - } - -} - -func BenchmarkG2JacExtAddMixed(b *testing.B) { - var a g2JacExtended - a.doubleMixed(&g2GenAff) - - var c G2Affine - c.FromJacobian(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.addMixed(&c) - } -} - -func BenchmarkG2JacExtSubMixed(b *testing.B) { - var a g2JacExtended - a.doubleMixed(&g2GenAff) - - var c G2Affine - c.FromJacobian(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.subMixed(&c) - } -} - -func BenchmarkG2JacExtDoubleMixed(b *testing.B) { - var a g2JacExtended - a.doubleMixed(&g2GenAff) - - var c G2Affine - c.FromJacobian(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.doubleMixed(&c) - } -} - -func BenchmarkG2JacExtDoubleNegMixed(b *testing.B) { - var a g2JacExtended - a.doubleMixed(&g2GenAff) - - var c G2Affine - c.FromJacobian(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.doubleNegMixed(&c) - } -} - -func BenchmarkG2JacExtAdd(b *testing.B) { - var a, c g2JacExtended - a.doubleMixed(&g2GenAff) - c.double(&a) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.add(&c) - } -} - -func BenchmarkG2JacExtDouble(b *testing.B) { - var a g2JacExtended - a.doubleMixed(&g2GenAff) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.double(&a) - } -} - -func BenchmarkG2AffineAdd(b *testing.B) { - var a G2Affine - a.Double(&g2GenAff) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Add(&a, &g2GenAff) - } -} - -func BenchmarkG2AffineDouble(b *testing.B) { - var a G2Affine - a.Double(&g2GenAff) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Double(&a) - } -} - -func fuzzG2Jac(p *G2Jac, f fptower.E2) G2Jac { - var res G2Jac - res.X.Mul(&p.X, &f).Mul(&res.X, &f) - res.Y.Mul(&p.Y, &f).Mul(&res.Y, &f).Mul(&res.Y, &f) - res.Z.Mul(&p.Z, &f) - return res -} - -func fuzzg2JacExtended(p *g2JacExtended, f fptower.E2) g2JacExtended { - var res g2JacExtended - var ff, fff fptower.E2 - ff.Square(&f) - fff.Mul(&ff, &f) - res.X.Mul(&p.X, &ff) - res.Y.Mul(&p.Y, &fff) - res.ZZ.Mul(&p.ZZ, &ff) - res.ZZZ.Mul(&p.ZZZ, &fff) - return res -} diff --git a/ecc/bls12-378/hash_to_g1.go b/ecc/bls12-378/hash_to_g1.go deleted file mode 100644 index 692e1506e9..0000000000 --- a/ecc/bls12-378/hash_to_g1.go +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bls12378 - -import ( - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - - "math/big" -) - -//Note: This only works for simple extensions - -func g1IsogenyXNumerator(dst *fp.Element, x *fp.Element) { - g1EvalPolynomial(dst, - false, - []fp.Element{ - {6470543203100547353, 14665988170059032885, 1939515187828768580, 3603560708219821999, 1095026321559208988, 183854362073986052}, - {14854739450145697573, 10725610731781000990, 8384146146676813741, 3792524792234600218, 10980132992245118504, 127811114313784269}, - {7743341424938057712, 1621446876320497654, 9161388112343345155, 13809747965729688982, 14072110284352226775, 77964074263176954}, - }, - x) -} - -func g1IsogenyXDenominator(dst *fp.Element, x *fp.Element) { - g1EvalPolynomial(dst, - true, - []fp.Element{ - {11480213446153845907, 9569059723295472762, 4133212223950692662, 5656914875334883651, 989021686692303102, 227886835744873894}, - }, - x) -} - -func g1IsogenyYNumerator(dst *fp.Element, x *fp.Element, y *fp.Element) { - var _dst fp.Element - g1EvalPolynomial(&_dst, - false, - []fp.Element{ - {4780610586872381554, 18423150125994475588, 13123772012819260137, 14591853195646969647, 5804992647820306014, 20966723178287685}, - {2213410472918458692, 9774999437004642556, 13319410716190829459, 14774058963459238058, 12357489533274646735, 248758148590692830}, - {16759481071713625783, 8645096532612011693, 7097905075491715268, 932195041550141716, 4227816384078368107, 50037860715544812}, - {3871670712469028856, 10034095475015024635, 4580694056171672577, 16128246019719620299, 7036055142176113387, 38982037131588477}, - }, - x) - - dst.Mul(&_dst, y) -} - -func g1IsogenyYDenominator(dst *fp.Element, x *fp.Element) { - g1EvalPolynomial(dst, - true, - []fp.Element{ - {17641076928456688137, 8306475833976684855, 8359419817241119003, 12641605213272639883, 9863039736160487870, 55368217170706106}, - {17969127027499495035, 2884196005202455728, 15703691879418613809, 10094567750702434230, 12004334191193297464, 175264043429118194}, - {12350127924441855415, 17380644983358010734, 8933124167467608227, 16391120112507168124, 9337764864048325557, 116945264214095313}, - }, - x) -} - -func g1Isogeny(p *G1Affine) { - - den := make([]fp.Element, 2) - - g1IsogenyYDenominator(&den[1], &p.X) - g1IsogenyXDenominator(&den[0], &p.X) - - g1IsogenyYNumerator(&p.Y, &p.X, &p.Y) - g1IsogenyXNumerator(&p.X, &p.X) - - den = fp.BatchInvert(den) - - p.X.Mul(&p.X, &den[0]) - p.Y.Mul(&p.Y, &den[1]) -} - -// g1SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue -// if not, we get sqrt(Z * u / v). Recall that Z is non-residue -// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. -// The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided -func g1SqrtRatio(z *fp.Element, u *fp.Element, v *fp.Element) uint64 { - - // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-sqrt_ratio-for-any-field - - tv1 := fp.Element{3422016347327078217, 15952935974507985473, 10210560017327941857, 6195437588884472512, 1531492004832937820, 17090488542823369} //tv1 = c6 - - var tv2, tv3, tv4, tv5 fp.Element - var exp big.Int - // c4 = 2199023255551 = 2⁴¹ - 1 - // q is odd so c1 is at least 1. - exp.SetBytes([]byte{1, 255, 255, 255, 255, 255}) - - tv2.Exp(*v, &exp) // 2. tv2 = vᶜ⁴ - tv3.Square(&tv2) // 3. tv3 = tv2² - tv3.Mul(&tv3, v) // 4. tv3 = tv3 * v - tv5.Mul(u, &tv3) // 5. tv5 = u * tv3 - - // c3 = 137617509170765099891752579783724504691201148437113468788429769127729045045134922651478473733013131816 - exp.SetBytes([]byte{251, 172, 16, 89, 161, 52, 100, 20, 242, 215, 73, 3, 180, 65, 232, 161, 1, 103, 173, 145, 196, 8, 201, 166, 3, 112, 216, 52, 41, 39, 95, 243, 165, 253, 218, 160, 139, 0, 0, 38, 82, 40}) - - tv5.Exp(tv5, &exp) // 6. tv5 = tv5ᶜ³ - tv5.Mul(&tv5, &tv2) // 7. tv5 = tv5 * tv2 - tv2.Mul(&tv5, v) // 8. tv2 = tv5 * v - tv3.Mul(&tv5, u) // 9. tv3 = tv5 * u - tv4.Mul(&tv3, &tv2) // 10. tv4 = tv3 * tv2 - - // c5 = 1099511627776 - exp.SetBytes([]byte{1, 0, 0, 0, 0, 0}) - tv5.Exp(tv4, &exp) // 11. tv5 = tv4ᶜ⁵ - isQNr := g1NotOne(&tv5) // 12. isQR = tv5 == 1 - c7 := fp.Element{17614810958234635860, 11393801269165528284, 8781501035240632779, 8106712880529013806, 4971838157288047198, 122121039825317715} - tv2.Mul(&tv3, &c7) // 13. tv2 = tv3 * c7 - tv5.Mul(&tv4, &tv1) // 14. tv5 = tv4 * tv1 - tv3.Select(int(isQNr), &tv3, &tv2) // 15. tv3 = CMOV(tv2, tv3, isQR) - tv4.Select(int(isQNr), &tv4, &tv5) // 16. tv4 = CMOV(tv5, tv4, isQR) - exp.Lsh(big.NewInt(1), 41-2) // 18, 19: tv5 = 2ⁱ⁻² for i = c1 - - for i := 41; i >= 2; i-- { // 17. for i in (c1, c1 - 1, ..., 2): - - tv5.Exp(tv4, &exp) // 20. tv5 = tv4ᵗᵛ⁵ - nE1 := g1NotOne(&tv5) // 21. e1 = tv5 == 1 - tv2.Mul(&tv3, &tv1) // 22. tv2 = tv3 * tv1 - tv1.Mul(&tv1, &tv1) // 23. tv1 = tv1 * tv1 Why not write square? - tv5.Mul(&tv4, &tv1) // 24. tv5 = tv4 * tv1 - tv3.Select(int(nE1), &tv3, &tv2) // 25. tv3 = CMOV(tv2, tv3, e1) - tv4.Select(int(nE1), &tv4, &tv5) // 26. tv4 = CMOV(tv5, tv4, e1) - - if i > 2 { - exp.Rsh(&exp, 1) // 18, 19. tv5 = 2ⁱ⁻² - } - } - - *z = tv3 - return isQNr -} - -func g1NotOne(x *fp.Element) uint64 { - - var one fp.Element - return one.SetOne().NotEqual(x) - -} - -// g1MulByZ multiplies x by [11] and stores the result in z -func g1MulByZ(z *fp.Element, x *fp.Element) { - - res := *x - - res.Double(&res) - res.Double(&res) - res.Add(&res, x) - res.Double(&res) - res.Add(&res, x) - - *z = res -} - -// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method -// MapToCurve1 implements the SSWU map -// No cofactor clearing or isogeny -func MapToCurve1(u *fp.Element) G1Affine { - - var sswuIsoCurveCoeffA = fp.Element{15314533651602404840, 3999629397495592995, 17991228730268553058, 13253234862282888158, 4784493033884022421, 276795783356562829} - var sswuIsoCurveCoeffB = fp.Element{10499526804702755432, 6768914877862902950, 8287496811509120276, 9263962031121981469, 5075273437274786541, 60255618913255595} - - var tv1 fp.Element - tv1.Square(u) // 1. tv1 = u² - - //mul tv1 by Z - g1MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 - - var tv2 fp.Element - tv2.Square(&tv1) // 3. tv2 = tv1² - tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 - - var tv3 fp.Element - var tv4 fp.Element - tv4.SetOne() - tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 - tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 - - tv2NZero := g1NotZero(&tv2) - - // tv4 = Z - tv4 = fp.Element{5249763402351377716, 3384457438931451475, 13367120442609335946, 13855353052415766542, 11761008755492169078, 30127809456627797} - - tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) - tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - - tv2.Square(&tv3) // 9. tv2 = tv3² - - var tv6 fp.Element - tv6.Square(&tv4) // 10. tv6 = tv4² - - var tv5 fp.Element - tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - - tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 - tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 - tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - - tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 - tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 - - var x fp.Element - x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 - - var y1 fp.Element - gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) - - var y fp.Element - y.Mul(&tv1, u) // 19. y = tv1 * u - - y.Mul(&y, &y1) // 20. y = y * y1 - - x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) - y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) - - y1.Neg(&y) - y.Select(int(g1Sgn0(u)^g1Sgn0(&y)), &y, &y1) - - // 23. e1 = sgn0(u) == sgn0(y) - // 24. y = CMOV(-y, y, e1) - - x.Div(&x, &tv4) // 25. x = x / tv4 - - return G1Affine{x, y} -} - -func g1EvalPolynomial(z *fp.Element, monic bool, coefficients []fp.Element, x *fp.Element) { - dst := coefficients[len(coefficients)-1] - - if monic { - dst.Add(&dst, x) - } - - for i := len(coefficients) - 2; i >= 0; i-- { - dst.Mul(&dst, x) - dst.Add(&dst, &coefficients[i]) - } - - z.Set(&dst) -} - -// g1Sgn0 is an algebraic substitute for the notion of sign in ordered fields -// Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function -// The sign of an element is not obviously related to that of its Montgomery form -func g1Sgn0(z *fp.Element) uint64 { - - nonMont := z.Bits() - - // m == 1 - return nonMont[0] % 2 - -} - -// MapToG1 invokes the SSWU map, and guarantees that the result is in g1 -func MapToG1(u fp.Element) G1Affine { - res := MapToCurve1(&u) - //this is in an isogenous curve - g1Isogeny(&res) - res.ClearCofactor(&res) - return res -} - -// EncodeToG1 hashes a message to a point on the G1 curve using the SSWU map. -// It is faster than HashToG1, but the result is not uniformly distributed. Unsuitable as a random oracle. -// dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap -func EncodeToG1(msg, dst []byte) (G1Affine, error) { - - var res G1Affine - u, err := fp.Hash(msg, dst, 1) - if err != nil { - return res, err - } - - res = MapToCurve1(&u[0]) - - //this is in an isogenous curve - g1Isogeny(&res) - res.ClearCofactor(&res) - return res, nil -} - -// HashToG1 hashes a message to a point on the G1 curve using the SSWU map. -// Slower than EncodeToG1, but usable as a random oracle. -// dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap -func HashToG1(msg, dst []byte) (G1Affine, error) { - u, err := fp.Hash(msg, dst, 2*1) - if err != nil { - return G1Affine{}, err - } - - Q0 := MapToCurve1(&u[0]) - Q1 := MapToCurve1(&u[1]) - - //TODO (perf): Add in E' first, then apply isogeny - g1Isogeny(&Q0) - g1Isogeny(&Q1) - - var _Q0, _Q1 G1Jac - _Q0.FromAffine(&Q0) - _Q1.FromAffine(&Q1).AddAssign(&_Q0) - - _Q1.ClearCofactor(&_Q1) - - Q1.FromJacobian(&_Q1) - return Q1, nil -} - -func g1NotZero(x *fp.Element) uint64 { - - return x[0] | x[1] | x[2] | x[3] | x[4] | x[5] - -} diff --git a/ecc/bls12-378/hash_to_g1_test.go b/ecc/bls12-378/hash_to_g1_test.go deleted file mode 100644 index 3a3187f044..0000000000 --- a/ecc/bls12-378/hash_to_g1_test.go +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bls12378 - -import ( - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" - "math/rand" - "testing" -) - -func TestG1SqrtRatio(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - gen := GenFp() - - properties.Property("G1SqrtRatio must square back to the right value", prop.ForAll( - func(u fp.Element, v fp.Element) bool { - - var seen fp.Element - qr := g1SqrtRatio(&seen, &u, &v) == 0 - - seen. - Square(&seen). - Mul(&seen, &v) - - var ref fp.Element - if qr { - ref = u - } else { - g1MulByZ(&ref, &u) - } - - return seen.Equal(&ref) - }, gen, gen)) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestHashToFpG1(t *testing.T) { - for _, c := range encodeToG1Vector.cases { - elems, err := fp.Hash([]byte(c.msg), encodeToG1Vector.dst, 1) - if err != nil { - t.Error(err) - } - g1TestMatchCoord(t, "u", c.msg, c.u, g1CoordAt(elems, 0)) - } - - for _, c := range hashToG1Vector.cases { - elems, err := fp.Hash([]byte(c.msg), hashToG1Vector.dst, 2*1) - if err != nil { - t.Error(err) - } - g1TestMatchCoord(t, "u0", c.msg, c.u0, g1CoordAt(elems, 0)) - g1TestMatchCoord(t, "u1", c.msg, c.u1, g1CoordAt(elems, 1)) - } -} - -func TestMapToCurve1(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[G1] mapping output must be on curve", prop.ForAll( - func(a fp.Element) bool { - - g := MapToCurve1(&a) - - if !isOnE1Prime(g) { - t.Log("Mapping output not on E' curve") - return false - } - g1Isogeny(&g) - - if !g.IsOnCurve() { - t.Log("Isogeny∘SSWU output not on curve") - return false - } - - return true - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - - for _, c := range encodeToG1Vector.cases { - var u fp.Element - g1CoordSetString(&u, c.u) - q := MapToCurve1(&u) - g1Isogeny(&q) - g1TestMatchPoint(t, "Q", c.msg, c.Q, &q) - } - - for _, c := range hashToG1Vector.cases { - var u fp.Element - g1CoordSetString(&u, c.u0) - q := MapToCurve1(&u) - g1Isogeny(&q) - g1TestMatchPoint(t, "Q0", c.msg, c.Q0, &q) - - g1CoordSetString(&u, c.u1) - q = MapToCurve1(&u) - g1Isogeny(&q) - g1TestMatchPoint(t, "Q1", c.msg, c.Q1, &q) - } -} - -func TestMapToG1(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[G1] mapping to curve should output point on the curve", prop.ForAll( - func(a fp.Element) bool { - g := MapToG1(a) - return g.IsInSubGroup() - }, - GenFp(), - )) - - properties.Property("[G1] mapping to curve should be deterministic", prop.ForAll( - func(a fp.Element) bool { - g1 := MapToG1(a) - g2 := MapToG1(a) - return g1.Equal(&g2) - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestEncodeToG1(t *testing.T) { - t.Parallel() - for _, c := range encodeToG1Vector.cases { - p, err := EncodeToG1([]byte(c.msg), encodeToG1Vector.dst) - if err != nil { - t.Fatal(err) - } - g1TestMatchPoint(t, "P", c.msg, c.P, &p) - } -} - -func TestHashToG1(t *testing.T) { - t.Parallel() - for _, c := range hashToG1Vector.cases { - p, err := HashToG1([]byte(c.msg), hashToG1Vector.dst) - if err != nil { - t.Fatal(err) - } - g1TestMatchPoint(t, "P", c.msg, c.P, &p) - } -} - -func BenchmarkEncodeToG1(b *testing.B) { - const size = 54 - bytes := make([]byte, size) - dst := encodeToG1Vector.dst - b.ResetTimer() - - for i := 0; i < b.N; i++ { - - bytes[rand.Int()%size] = byte(rand.Int()) //#nosec G404 weak rng is fine here - - if _, err := EncodeToG1(bytes, dst); err != nil { - b.Fail() - } - } -} - -func BenchmarkHashToG1(b *testing.B) { - const size = 54 - bytes := make([]byte, size) - dst := hashToG1Vector.dst - b.ResetTimer() - - for i := 0; i < b.N; i++ { - - bytes[rand.Int()%size] = byte(rand.Int()) //#nosec G404 weak rng is fine here - - if _, err := HashToG1(bytes, dst); err != nil { - b.Fail() - } - } -} - -// TODO: Crude. Do something clever in Jacobian -func isOnE1Prime(p G1Affine) bool { - - var A, B fp.Element - - A.SetString( - "605248206075306169267378178265213465432939015833023814077444651494632463253805259225148550772266952686222752874482", - ) - - B.SetString( - "22", - ) - - var LHS fp.Element - LHS. - Square(&p.Y). - Sub(&LHS, &B) - - var RHS fp.Element - RHS. - Square(&p.X). - Add(&RHS, &A). - Mul(&RHS, &p.X) - - return LHS.Equal(&RHS) -} - -// Only works on simple extensions (two-story towers) -func g1CoordSetString(z *fp.Element, s string) { - z.SetString(s) -} - -func g1CoordAt(slice []fp.Element, i int) fp.Element { - return slice[i] -} - -func g1TestMatchCoord(t *testing.T, coordName string, msg string, expectedStr string, seen fp.Element) { - var expected fp.Element - - g1CoordSetString(&expected, expectedStr) - - if !expected.Equal(&seen) { - t.Errorf("mismatch on \"%s\", %s:\n\texpected %s\n\tsaw %s", msg, coordName, expected.String(), &seen) - } -} - -func g1TestMatchPoint(t *testing.T, pointName string, msg string, expected point, seen *G1Affine) { - g1TestMatchCoord(t, pointName+".x", msg, expected.x, seen.X) - g1TestMatchCoord(t, pointName+".y", msg, expected.y, seen.Y) -} - -type hashTestVector struct { - dst []byte - cases []hashTestCase -} - -type encodeTestVector struct { - dst []byte - cases []encodeTestCase -} - -type point struct { - x string - y string -} - -type encodeTestCase struct { - msg string - P point //pY a coordinate of P, the final output - u string //u hashed onto the field - Q point //Q map to curve output -} - -type hashTestCase struct { - msg string - P point //pY a coordinate of P, the final output - u0 string //u0 hashed onto the field - u1 string //u1 extra hashed onto the field - Q0 point //Q0 map to curve output - Q1 point //Q1 extra map to curve output -} - -var encodeToG1Vector encodeTestVector -var hashToG1Vector hashTestVector diff --git a/ecc/bls12-378/hash_to_g2.go b/ecc/bls12-378/hash_to_g2.go deleted file mode 100644 index 4c72d0f6f8..0000000000 --- a/ecc/bls12-378/hash_to_g2.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2020 ConsenSys AG -// -// 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 bls12378 - -import ( - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - "github.com/consensys/gnark-crypto/ecc/bls12-378/internal/fptower" -) - -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-4.1 -// Shallue and van de Woestijne method, works for any elliptic curve in Weierstrass curve -func MapToCurve2(u fptower.E2) G2Affine { - - var res G2Affine - - // constants - // sage script to find z: https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#appendix-E.1 - var z, c1, c2, c3, c4 fptower.E2 - z.A0.SetOne() - z.A1.SetOne() - c1.A0.SetString("605248206075306171733248481581800960739847691770924913753520744034740935903401304776283802348837311170974282940403") - c1.A1.SetString("605248206075306171733248481581800960739847691770924913753520744034740935903401304776283802348837311170974282940416") - c2.A0.SetString("302624103037653085866624240790900480369923845885462456876760372017370467951700652388141901174418655585487141470208") - c2.A1.SetString("302624103037653085866624240790900480369923845885462456876760372017370467951700652388141901174418655585487141470208") - c3.A0.SetString("296552843788751288906244499216725356684281694271241895700730864223961612014909088554048735457137528455181151573749") - c3.A1.SetString("181388265705333345538985517067130917207305732282979825233670477511990909086507141331244586890249042878909613862256") - c4.A0.SetString("224166002250113396938240178363629985459202848804046264353155831123978124408667149917142149018087893026286771459412") - c4.A1.SetString("313832403150158755713536249709081979642883988325664770094418163573569374172134009883999008625323050236801480043178") - - var tv1, tv2, tv3, tv4, one, x1, gx1, x2, gx2, x3, x, gx, y fptower.E2 - one.SetOne() - tv1.Square(&u).Mul(&tv1, &c1) - tv2.Add(&one, &tv1) - tv1.Sub(&one, &tv1) - tv3.Mul(&tv2, &tv1).Inverse(&tv3) - tv4.Mul(&u, &tv1) - tv4.Mul(&tv4, &tv3) - tv4.Mul(&tv4, &c3) - x1.Sub(&c2, &tv4) - gx1.Square(&x1) - // 12. gx1 = gx1 + A - gx1.Mul(&gx1, &x1) - gx1.Add(&gx1, &bTwistCurveCoeff) - e1 := gx1.Legendre() - x2.Add(&c2, &tv4) - gx2.Square(&x2) - // 18. gx2 = gx2 + A - gx2.Mul(&gx2, &x2) - gx2.Add(&gx2, &bTwistCurveCoeff) - e2 := gx2.Legendre() - e1 // 2 if is_square(gx2) AND NOT e1 - x3.Square(&tv2) - x3.Mul(&x3, &tv3) - x3.Square(&x3) - x3.Mul(&x3, &c4) - x3.Add(&x3, &z) - if e1 == 1 { - x.Set(&x1) - } else { - x.Set(&x3) - } - if e2 == 2 { - x.Set(&x2) - } - gx.Square(&x) - // gx = gx + A - gx.Mul(&gx, &x) - gx.Add(&gx, &bTwistCurveCoeff) - y.Sqrt(&gx) - e3 := sign0(u.A0) && sign0(y.A0) - if !e3 { - y.Neg(&y) - } - res.X.Set(&x) - res.Y.Set(&y) - - return res -} - -// MapToG2 maps an fp.Element to a point on the curve using the Shallue and van de Woestijne map -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-2.2.1 -func MapToG2(t fptower.E2) G2Affine { - res := MapToCurve2(t) - res.ClearCofactor(&res) - return res -} - -// EncodeToG2 maps an fp.Element to a point on the curve using the Shallue and van de Woestijne map -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-2.2.2 -func EncodeToG2(msg, dst []byte) (G2Affine, error) { - var res G2Affine - _t, err := fp.Hash(msg, dst, 2) - if err != nil { - return res, err - } - var t fptower.E2 - t.A0.Set(&_t[0]) - t.A1.Set(&_t[1]) - res = MapToG2(t) - return res, nil -} - -// HashToG2 maps an fp.Element to a point on the curve using the Shallue and van de Woestijne map -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 -func HashToG2(msg, dst []byte) (G2Affine, error) { - var res G2Affine - u, err := fp.Hash(msg, dst, 4) - if err != nil { - return res, err - } - var u0, u1 fptower.E2 - u0.A0.Set(&u[0]) - u0.A1.Set(&u[1]) - u1.A0.Set(&u[2]) - u1.A1.Set(&u[3]) - Q0 := MapToG2(u0) - Q1 := MapToG2(u1) - var _Q0, _Q1, _res G2Jac - _Q0.FromAffine(&Q0) - _Q1.FromAffine(&Q1) - _res.Set(&_Q1).AddAssign(&_Q0) - res.FromJacobian(&_res) - return res, nil -} - -// returns false if u>-u when seen as a bigInt -func sign0(u fp.Element) bool { - return !u.LexicographicallyLargest() -} diff --git a/ecc/bls12-378/internal/fptower/asm.go b/ecc/bls12-378/internal/fptower/asm.go deleted file mode 100644 index 49751a9396..0000000000 --- a/ecc/bls12-378/internal/fptower/asm.go +++ /dev/null @@ -1,28 +0,0 @@ -//go:build !noadx -// +build !noadx - -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fptower - -import "golang.org/x/sys/cpu" - -// supportAdx will be set only on amd64 that has MULX and ADDX instructions -var ( - supportAdx = cpu.X86.HasADX && cpu.X86.HasBMI2 - _ = supportAdx // used in asm -) diff --git a/ecc/bls12-378/internal/fptower/asm_noadx.go b/ecc/bls12-378/internal/fptower/asm_noadx.go deleted file mode 100644 index c6a97081fc..0000000000 --- a/ecc/bls12-378/internal/fptower/asm_noadx.go +++ /dev/null @@ -1,25 +0,0 @@ -//go:build noadx -// +build noadx - -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fptower - -// note: this is needed for test purposes, as dynamically changing supportAdx doesn't flag -// certain errors (like fatal error: missing stackmap) -// this ensures we test all asm path. -var supportAdx = false diff --git a/ecc/bls12-378/internal/fptower/e12.go b/ecc/bls12-378/internal/fptower/e12.go deleted file mode 100644 index 72b6664565..0000000000 --- a/ecc/bls12-378/internal/fptower/e12.go +++ /dev/null @@ -1,866 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fptower - -import ( - "errors" - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "math/big" - "sync" -) - -var bigIntPool = sync.Pool{ - New: func() interface{} { - return new(big.Int) - }, -} - -// E12 is a degree two finite field extension of fp6 -type E12 struct { - C0, C1 E6 -} - -// Equal returns true if z equals x, false otherwise -func (z *E12) Equal(x *E12) bool { - return z.C0.Equal(&x.C0) && z.C1.Equal(&x.C1) -} - -// String puts E12 in string form -func (z *E12) String() string { - return (z.C0.String() + "+(" + z.C1.String() + ")*w") -} - -// SetString sets a E12 from string -func (z *E12) SetString(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11 string) *E12 { - z.C0.SetString(s0, s1, s2, s3, s4, s5) - z.C1.SetString(s6, s7, s8, s9, s10, s11) - return z -} - -// Set copies x into z and returns z -func (z *E12) Set(x *E12) *E12 { - z.C0 = x.C0 - z.C1 = x.C1 - return z -} - -// SetOne sets z to 1 in Montgomery form and returns z -func (z *E12) SetOne() *E12 { - *z = E12{} - z.C0.B0.A0.SetOne() - return z -} - -// Add sets z=x+y in E12 and returns z -func (z *E12) Add(x, y *E12) *E12 { - z.C0.Add(&x.C0, &y.C0) - z.C1.Add(&x.C1, &y.C1) - return z -} - -// Sub sets z to x-y and returns z -func (z *E12) Sub(x, y *E12) *E12 { - z.C0.Sub(&x.C0, &y.C0) - z.C1.Sub(&x.C1, &y.C1) - return z -} - -// Double sets z=2*x and returns z -func (z *E12) Double(x *E12) *E12 { - z.C0.Double(&x.C0) - z.C1.Double(&x.C1) - return z -} - -// SetRandom used only in tests -func (z *E12) SetRandom() (*E12, error) { - if _, err := z.C0.SetRandom(); err != nil { - return nil, err - } - if _, err := z.C1.SetRandom(); err != nil { - return nil, err - } - return z, nil -} - -// IsZero returns true if z is zero, false otherwise -func (z *E12) IsZero() bool { - return z.C0.IsZero() && z.C1.IsZero() -} - -// IsOne returns true if z is one, false otherwise -func (z *E12) IsOne() bool { - return z.C0.IsOne() && z.C1.IsZero() -} - -// Mul sets z=x*y in E12 and returns z -func (z *E12) Mul(x, y *E12) *E12 { - var a, b, c E6 - a.Add(&x.C0, &x.C1) - b.Add(&y.C0, &y.C1) - a.Mul(&a, &b) - b.Mul(&x.C0, &y.C0) - c.Mul(&x.C1, &y.C1) - z.C1.Sub(&a, &b).Sub(&z.C1, &c) - z.C0.MulByNonResidue(&c).Add(&z.C0, &b) - return z -} - -// Square sets z=x*x in E12 and returns z -func (z *E12) Square(x *E12) *E12 { - - //Algorithm 22 from https://eprint.iacr.org/2010/354.pdf - var c0, c2, c3 E6 - c0.Sub(&x.C0, &x.C1) - c3.MulByNonResidue(&x.C1).Neg(&c3).Add(&x.C0, &c3) - c2.Mul(&x.C0, &x.C1) - c0.Mul(&c0, &c3).Add(&c0, &c2) - z.C1.Double(&c2) - c2.MulByNonResidue(&c2) - z.C0.Add(&c0, &c2) - - return z -} - -// Karabina's compressed cyclotomic square -// https://eprint.iacr.org/2010/542.pdf -// Th. 3.2 with minor modifications to fit our tower -func (z *E12) CyclotomicSquareCompressed(x *E12) *E12 { - - var t [7]E2 - - // t0 = g1^2 - t[0].Square(&x.C0.B1) - // t1 = g5^2 - t[1].Square(&x.C1.B2) - // t5 = g1 + g5 - t[5].Add(&x.C0.B1, &x.C1.B2) - // t2 = (g1 + g5)^2 - t[2].Square(&t[5]) - - // t3 = g1^2 + g5^2 - t[3].Add(&t[0], &t[1]) - // t5 = 2 * g1 * g5 - t[5].Sub(&t[2], &t[3]) - - // t6 = g3 + g2 - t[6].Add(&x.C1.B0, &x.C0.B2) - // t3 = (g3 + g2)^2 - t[3].Square(&t[6]) - // t2 = g3^2 - t[2].Square(&x.C1.B0) - - // t6 = 2 * nr * g1 * g5 - t[6].MulByNonResidue(&t[5]) - // t5 = 4 * nr * g1 * g5 + 2 * g3 - t[5].Add(&t[6], &x.C1.B0). - Double(&t[5]) - // z3 = 6 * nr * g1 * g5 + 2 * g3 - z.C1.B0.Add(&t[5], &t[6]) - - // t4 = nr * g5^2 - t[4].MulByNonResidue(&t[1]) - // t5 = nr * g5^2 + g1^2 - t[5].Add(&t[0], &t[4]) - // t6 = nr * g5^2 + g1^2 - g2 - t[6].Sub(&t[5], &x.C0.B2) - - // t1 = g2^2 - t[1].Square(&x.C0.B2) - - // t6 = 2 * nr * g5^2 + 2 * g1^2 - 2*g2 - t[6].Double(&t[6]) - // z2 = 3 * nr * g5^2 + 3 * g1^2 - 2*g2 - z.C0.B2.Add(&t[6], &t[5]) - - // t4 = nr * g2^2 - t[4].MulByNonResidue(&t[1]) - // t5 = g3^2 + nr * g2^2 - t[5].Add(&t[2], &t[4]) - // t6 = g3^2 + nr * g2^2 - g1 - t[6].Sub(&t[5], &x.C0.B1) - // t6 = 2 * g3^2 + 2 * nr * g2^2 - 2 * g1 - t[6].Double(&t[6]) - // z1 = 3 * g3^2 + 3 * nr * g2^2 - 2 * g1 - z.C0.B1.Add(&t[6], &t[5]) - - // t0 = g2^2 + g3^2 - t[0].Add(&t[2], &t[1]) - // t5 = 2 * g3 * g2 - t[5].Sub(&t[3], &t[0]) - // t6 = 2 * g3 * g2 + g5 - t[6].Add(&t[5], &x.C1.B2) - // t6 = 4 * g3 * g2 + 2 * g5 - t[6].Double(&t[6]) - // z5 = 6 * g3 * g2 + 2 * g5 - z.C1.B2.Add(&t[5], &t[6]) - - return z -} - -// DecompressKarabina Karabina's cyclotomic square result -// if g3 != 0 -// -// g4 = (E * g5^2 + 3 * g1^2 - 2 * g2)/4g3 -// -// if g3 == 0 -// -// g4 = 2g1g5/g2 -// -// if g3=g2=0 then g4=g5=g1=0 and g0=1 (x=1) -// Theorem 3.1 is well-defined for all x in Gϕₙ\{1} -func (z *E12) DecompressKarabina(x *E12) *E12 { - - var t [3]E2 - var one E2 - one.SetOne() - - if x.C1.B2.IsZero() /* g3 == 0 */ { - t[0].Mul(&x.C0.B1, &x.C1.B2). - Double(&t[0]) - // t1 = g2 - t[1].Set(&x.C0.B2) - - if t[1].IsZero() /* g2 == g3 == 0 */ { - return z.SetOne() - } - } else /* g3 != 0 */ { - - // t0 = g1^2 - t[0].Square(&x.C0.B1) - // t1 = 3 * g1^2 - 2 * g2 - t[1].Sub(&t[0], &x.C0.B2). - Double(&t[1]). - Add(&t[1], &t[0]) - // t0 = E * g5^2 + t1 - t[2].Square(&x.C1.B2) - t[0].MulByNonResidue(&t[2]). - Add(&t[0], &t[1]) - // t1 = 4 * g3 - t[1].Double(&x.C1.B0). - Double(&t[1]) - } - - // z4 = g4 - z.C1.B1.Div(&t[0], &t[1]) // costly - - // t1 = g2 * g1 - t[1].Mul(&x.C0.B2, &x.C0.B1) - // t2 = 2 * g4^2 - 3 * g2 * g1 - t[2].Square(&z.C1.B1). - Sub(&t[2], &t[1]). - Double(&t[2]). - Sub(&t[2], &t[1]) - // t1 = g3 * g5 (g3 can be 0) - t[1].Mul(&x.C1.B0, &x.C1.B2) - // c_0 = E * (2 * g4^2 + g3 * g5 - 3 * g2 * g1) + 1 - t[2].Add(&t[2], &t[1]) - z.C0.B0.MulByNonResidue(&t[2]). - Add(&z.C0.B0, &one) - - z.C0.B1.Set(&x.C0.B1) - z.C0.B2.Set(&x.C0.B2) - z.C1.B0.Set(&x.C1.B0) - z.C1.B2.Set(&x.C1.B2) - - return z -} - -// BatchDecompressKarabina multiple Karabina's cyclotomic square results -// if g3 != 0 -// -// g4 = (E * g5^2 + 3 * g1^2 - 2 * g2)/4g3 -// -// if g3 == 0 -// -// g4 = 2g1g5/g2 -// -// if g3=g2=0 then g4=g5=g1=0 and g0=1 (x=1) -// Theorem 3.1 is well-defined for all x in Gϕₙ\{1} -// -// Divisions by 4g3 or g2 is batched using Montgomery batch inverse -func BatchDecompressKarabina(x []E12) []E12 { - - n := len(x) - if n == 0 { - return x - } - - t0 := make([]E2, n) - t1 := make([]E2, n) - t2 := make([]E2, n) - zeroes := make([]bool, n) - - var one E2 - one.SetOne() - - for i := 0; i < n; i++ { - if x[i].C1.B2.IsZero() /* g3 == 0 */ { - t0[i].Mul(&x[i].C0.B1, &x[i].C1.B2). - Double(&t0[i]) - // t1 = g2 - t1[i].Set(&x[i].C0.B2) - - if t1[i].IsZero() /* g3 == g2 == 0 */ { - x[i].SetOne() - zeroes[i] = true - continue - } - } else /* g3 != 0 */ { - // t0 = g1^2 - t0[i].Square(&x[i].C0.B1) - // t1 = 3 * g1^2 - 2 * g2 - t1[i].Sub(&t0[i], &x[i].C0.B2). - Double(&t1[i]). - Add(&t1[i], &t0[i]) - // t0 = E * g5^2 + t1 - t2[i].Square(&x[i].C1.B2) - t0[i].MulByNonResidue(&t2[i]). - Add(&t0[i], &t1[i]) - // t1 = 4 * g3 - t1[i].Double(&x[i].C1.B0). - Double(&t1[i]) - } - } - - t1 = BatchInvertE2(t1) // costs 1 inverse - - for i := 0; i < n; i++ { - if zeroes[i] { - continue - } - - // z4 = g4 - x[i].C1.B1.Mul(&t0[i], &t1[i]) - - // t1 = g2 * g1 - t1[i].Mul(&x[i].C0.B2, &x[i].C0.B1) - // t2 = 2 * g4^2 - 3 * g2 * g1 - t2[i].Square(&x[i].C1.B1) - t2[i].Sub(&t2[i], &t1[i]) - t2[i].Double(&t2[i]) - t2[i].Sub(&t2[i], &t1[i]) - - // t1 = g3 * g5 (g3s can be 0s) - t1[i].Mul(&x[i].C1.B0, &x[i].C1.B2) - // z0 = E * (2 * g4^2 + g3 * g5 - 3 * g2 * g1) + 1 - t2[i].Add(&t2[i], &t1[i]) - x[i].C0.B0.MulByNonResidue(&t2[i]). - Add(&x[i].C0.B0, &one) - } - - return x -} - -// Granger-Scott's cyclotomic square -// https://eprint.iacr.org/2009/565.pdf, 3.2 -func (z *E12) CyclotomicSquare(x *E12) *E12 { - - // x=(x0,x1,x2,x3,x4,x5,x6,x7) in E2^6 - // cyclosquare(x)=(3*x4^2*u + 3*x0^2 - 2*x0, - // 3*x2^2*u + 3*x3^2 - 2*x1, - // 3*x5^2*u + 3*x1^2 - 2*x2, - // 6*x1*x5*u + 2*x3, - // 6*x0*x4 + 2*x4, - // 6*x2*x3 + 2*x5) - - var t [9]E2 - - t[0].Square(&x.C1.B1) - t[1].Square(&x.C0.B0) - t[6].Add(&x.C1.B1, &x.C0.B0).Square(&t[6]).Sub(&t[6], &t[0]).Sub(&t[6], &t[1]) // 2*x4*x0 - t[2].Square(&x.C0.B2) - t[3].Square(&x.C1.B0) - t[7].Add(&x.C0.B2, &x.C1.B0).Square(&t[7]).Sub(&t[7], &t[2]).Sub(&t[7], &t[3]) // 2*x2*x3 - t[4].Square(&x.C1.B2) - t[5].Square(&x.C0.B1) - t[8].Add(&x.C1.B2, &x.C0.B1).Square(&t[8]).Sub(&t[8], &t[4]).Sub(&t[8], &t[5]).MulByNonResidue(&t[8]) // 2*x5*x1*u - - t[0].MulByNonResidue(&t[0]).Add(&t[0], &t[1]) // x4^2*u + x0^2 - t[2].MulByNonResidue(&t[2]).Add(&t[2], &t[3]) // x2^2*u + x3^2 - t[4].MulByNonResidue(&t[4]).Add(&t[4], &t[5]) // x5^2*u + x1^2 - - z.C0.B0.Sub(&t[0], &x.C0.B0).Double(&z.C0.B0).Add(&z.C0.B0, &t[0]) - z.C0.B1.Sub(&t[2], &x.C0.B1).Double(&z.C0.B1).Add(&z.C0.B1, &t[2]) - z.C0.B2.Sub(&t[4], &x.C0.B2).Double(&z.C0.B2).Add(&z.C0.B2, &t[4]) - - z.C1.B0.Add(&t[8], &x.C1.B0).Double(&z.C1.B0).Add(&z.C1.B0, &t[8]) - z.C1.B1.Add(&t[6], &x.C1.B1).Double(&z.C1.B1).Add(&z.C1.B1, &t[6]) - z.C1.B2.Add(&t[7], &x.C1.B2).Double(&z.C1.B2).Add(&z.C1.B2, &t[7]) - - return z -} - -// Inverse sets z to the inverse of x in E12 and returns z -// -// if x == 0, sets and returns z = x -func (z *E12) Inverse(x *E12) *E12 { - // Algorithm 23 from https://eprint.iacr.org/2010/354.pdf - - var t0, t1, tmp E6 - t0.Square(&x.C0) - t1.Square(&x.C1) - tmp.MulByNonResidue(&t1) - t0.Sub(&t0, &tmp) - t1.Inverse(&t0) - z.C0.Mul(&x.C0, &t1) - z.C1.Mul(&x.C1, &t1).Neg(&z.C1) - - return z -} - -// BatchInvertE12 returns a new slice with every element in a inverted. -// It uses Montgomery batch inversion trick. -// -// if a[i] == 0, returns result[i] = a[i] -func BatchInvertE12(a []E12) []E12 { - res := make([]E12, len(a)) - if len(a) == 0 { - return res - } - - zeroes := make([]bool, len(a)) - var accumulator E12 - accumulator.SetOne() - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - zeroes[i] = true - continue - } - res[i].Set(&accumulator) - accumulator.Mul(&accumulator, &a[i]) - } - - accumulator.Inverse(&accumulator) - - for i := len(a) - 1; i >= 0; i-- { - if zeroes[i] { - continue - } - res[i].Mul(&res[i], &accumulator) - accumulator.Mul(&accumulator, &a[i]) - } - - return res -} - -// Exp sets z=xᵏ (mod q¹²) and returns it -// uses 2-bits windowed method -func (z *E12) Exp(x E12, k *big.Int) *E12 { - if k.IsUint64() && k.Uint64() == 0 { - return z.SetOne() - } - - e := k - if k.Sign() == -1 { - // negative k, we invert - // if k < 0: xᵏ (mod q¹²) == (x⁻¹)ᵏ (mod q¹²) - x.Inverse(&x) - - // we negate k in a temp big.Int since - // Int.Bit(_) of k and -k is different - e = bigIntPool.Get().(*big.Int) - defer bigIntPool.Put(e) - e.Neg(k) - } - - var res E12 - var ops [3]E12 - - res.SetOne() - ops[0].Set(&x) - ops[1].Square(&ops[0]) - ops[2].Set(&ops[0]).Mul(&ops[2], &ops[1]) - - b := e.Bytes() - for i := range b { - w := b[i] - mask := byte(0xc0) - for j := 0; j < 4; j++ { - res.Square(&res).Square(&res) - c := (w & mask) >> (6 - 2*j) - if c != 0 { - res.Mul(&res, &ops[c-1]) - } - mask = mask >> 2 - } - } - z.Set(&res) - - return z -} - -// CyclotomicExp sets z=xᵏ (mod q¹²) and returns it -// uses 2-NAF decomposition -// x must be in the cyclotomic subgroup -// TODO: use a windowed method -func (z *E12) CyclotomicExp(x E12, k *big.Int) *E12 { - if k.IsUint64() && k.Uint64() == 0 { - return z.SetOne() - } - - e := k - if k.Sign() == -1 { - // negative k, we invert (=conjugate) - // if k < 0: xᵏ (mod q¹²) == (x⁻¹)ᵏ (mod q¹²) - x.Conjugate(&x) - - // we negate k in a temp big.Int since - // Int.Bit(_) of k and -k is different - e = bigIntPool.Get().(*big.Int) - defer bigIntPool.Put(e) - e.Neg(k) - } - - var res, xInv E12 - xInv.InverseUnitary(&x) - res.SetOne() - eNAF := make([]int8, e.BitLen()+3) - n := ecc.NafDecomposition(e, eNAF[:]) - for i := n - 1; i >= 0; i-- { - res.CyclotomicSquare(&res) - if eNAF[i] == 1 { - res.Mul(&res, &x) - } else if eNAF[i] == -1 { - res.Mul(&res, &xInv) - } - } - z.Set(&res) - return z -} - -// ExpGLV sets z=xᵏ (q¹²) and returns it -// uses 2-dimensional GLV with 2-bits windowed method -// x must be in GT -// TODO: use 2-NAF -// TODO: use higher dimensional decomposition -func (z *E12) ExpGLV(x E12, k *big.Int) *E12 { - if k.IsUint64() && k.Uint64() == 0 { - return z.SetOne() - } - - e := k - if k.Sign() == -1 { - // negative k, we invert (=conjugate) - // if k < 0: xᵏ (mod q¹²) == (x⁻¹)ᵏ (mod q¹²) - x.Conjugate(&x) - - // we negate k in a temp big.Int since - // Int.Bit(_) of k and -k is different - e = bigIntPool.Get().(*big.Int) - defer bigIntPool.Put(e) - e.Neg(k) - } - - var table [15]E12 - var res E12 - var s1, s2 fr.Element - - res.SetOne() - - // table[b3b2b1b0-1] = b3b2*Frobinius(x) + b1b0*x - table[0].Set(&x) - table[3].Frobenius(&x) - - // split the scalar, modifies ±x, Frob(x) accordingly - s := ecc.SplitScalar(e, &glvBasis) - - if s[0].Sign() == -1 { - s[0].Neg(&s[0]) - table[0].InverseUnitary(&table[0]) - } - if s[1].Sign() == -1 { - s[1].Neg(&s[1]) - table[3].InverseUnitary(&table[3]) - } - - // precompute table (2 bits sliding window) - // table[b3b2b1b0-1] = b3b2*Frobenius(x) + b1b0*x if b3b2b1b0 != 0 - table[1].CyclotomicSquare(&table[0]) - table[2].Mul(&table[1], &table[0]) - table[4].Mul(&table[3], &table[0]) - table[5].Mul(&table[3], &table[1]) - table[6].Mul(&table[3], &table[2]) - table[7].CyclotomicSquare(&table[3]) - table[8].Mul(&table[7], &table[0]) - table[9].Mul(&table[7], &table[1]) - table[10].Mul(&table[7], &table[2]) - table[11].Mul(&table[7], &table[3]) - table[12].Mul(&table[11], &table[0]) - table[13].Mul(&table[11], &table[1]) - table[14].Mul(&table[11], &table[2]) - - // bounds on the lattice base vectors guarantee that s1, s2 are len(r)/2 bits long max - s1 = s1.SetBigInt(&s[0]).Bits() - s2 = s2.SetBigInt(&s[1]).Bits() - - maxBit := s1.BitLen() - if s2.BitLen() > maxBit { - maxBit = s2.BitLen() - } - hiWordIndex := (maxBit - 1) / 64 - - // loop starts from len(s1)/2 due to the bounds - for i := hiWordIndex; i >= 0; i-- { - mask := uint64(3) << 62 - for j := 0; j < 32; j++ { - res.CyclotomicSquare(&res).CyclotomicSquare(&res) - b1 := (s1[i] & mask) >> (62 - 2*j) - b2 := (s2[i] & mask) >> (62 - 2*j) - if b1|b2 != 0 { - s := (b2<<2 | b1) - res.Mul(&res, &table[s-1]) - } - mask = mask >> 2 - } - } - - z.Set(&res) - return z -} - -// InverseUnitary inverses a unitary element -func (z *E12) InverseUnitary(x *E12) *E12 { - return z.Conjugate(x) -} - -// Conjugate sets z to x conjugated and returns z -func (z *E12) Conjugate(x *E12) *E12 { - *z = *x - z.C1.Neg(&z.C1) - return z -} - -// SizeOfGT represents the size in bytes that a GT element need in binary form -const SizeOfGT = 48 * 12 - -// Marshal converts z to a byte slice -func (z *E12) Marshal() []byte { - b := z.Bytes() - return b[:] -} - -// Unmarshal is an alias to SetBytes() -func (z *E12) Unmarshal(buf []byte) error { - return z.SetBytes(buf) -} - -// Bytes returns the regular (non montgomery) value -// of z as a big-endian byte array. -// z.C1.B2.A1 | z.C1.B2.A0 | z.C1.B1.A1 | ... -func (z *E12) Bytes() (r [SizeOfGT]byte) { - fp.BigEndian.PutElement((*[fp.Bytes]byte)(r[528:528+fp.Bytes]), z.C0.B0.A0) - fp.BigEndian.PutElement((*[fp.Bytes]byte)(r[480:480+fp.Bytes]), z.C0.B0.A1) - fp.BigEndian.PutElement((*[fp.Bytes]byte)(r[432:432+fp.Bytes]), z.C0.B1.A0) - fp.BigEndian.PutElement((*[fp.Bytes]byte)(r[384:384+fp.Bytes]), z.C0.B1.A1) - fp.BigEndian.PutElement((*[fp.Bytes]byte)(r[336:336+fp.Bytes]), z.C0.B2.A0) - fp.BigEndian.PutElement((*[fp.Bytes]byte)(r[288:288+fp.Bytes]), z.C0.B2.A1) - fp.BigEndian.PutElement((*[fp.Bytes]byte)(r[240:240+fp.Bytes]), z.C1.B0.A0) - fp.BigEndian.PutElement((*[fp.Bytes]byte)(r[192:192+fp.Bytes]), z.C1.B0.A1) - fp.BigEndian.PutElement((*[fp.Bytes]byte)(r[144:144+fp.Bytes]), z.C1.B1.A0) - fp.BigEndian.PutElement((*[fp.Bytes]byte)(r[96:96+fp.Bytes]), z.C1.B1.A1) - fp.BigEndian.PutElement((*[fp.Bytes]byte)(r[48:48+fp.Bytes]), z.C1.B2.A0) - fp.BigEndian.PutElement((*[fp.Bytes]byte)(r[0:0+fp.Bytes]), z.C1.B2.A1) - - return -} - -// SetBytes interprets e as the bytes of a big-endian GT -// sets z to that value (in Montgomery form), and returns z. -// size(e) == 48 * 12 -// z.C1.B2.A1 | z.C1.B2.A0 | z.C1.B1.A1 | ... -func (z *E12) SetBytes(e []byte) error { - if len(e) != SizeOfGT { - return errors.New("invalid buffer size") - } - if err := z.C0.B0.A0.SetBytesCanonical(e[528 : 528+fp.Bytes]); err != nil { - return err - } - if err := z.C0.B0.A1.SetBytesCanonical(e[480 : 480+fp.Bytes]); err != nil { - return err - } - if err := z.C0.B1.A0.SetBytesCanonical(e[432 : 432+fp.Bytes]); err != nil { - return err - } - if err := z.C0.B1.A1.SetBytesCanonical(e[384 : 384+fp.Bytes]); err != nil { - return err - } - if err := z.C0.B2.A0.SetBytesCanonical(e[336 : 336+fp.Bytes]); err != nil { - return err - } - if err := z.C0.B2.A1.SetBytesCanonical(e[288 : 288+fp.Bytes]); err != nil { - return err - } - if err := z.C1.B0.A0.SetBytesCanonical(e[240 : 240+fp.Bytes]); err != nil { - return err - } - if err := z.C1.B0.A1.SetBytesCanonical(e[192 : 192+fp.Bytes]); err != nil { - return err - } - if err := z.C1.B1.A0.SetBytesCanonical(e[144 : 144+fp.Bytes]); err != nil { - return err - } - if err := z.C1.B1.A1.SetBytesCanonical(e[96 : 96+fp.Bytes]); err != nil { - return err - } - if err := z.C1.B2.A0.SetBytesCanonical(e[48 : 48+fp.Bytes]); err != nil { - return err - } - if err := z.C1.B2.A1.SetBytesCanonical(e[0 : 0+fp.Bytes]); err != nil { - return err - } - - return nil -} - -// IsInSubGroup ensures GT/E12 is in correct subgroup -func (z *E12) IsInSubGroup() bool { - var a, b E12 - - // check z^(phi_k(p)) == 1 - a.FrobeniusSquare(z) - b.FrobeniusSquare(&a).Mul(&b, z) - - if !a.Equal(&b) { - return false - } - - // check z^(p+1-t) == 1 - a.Frobenius(z) - b.Expt(z) - - return a.Equal(&b) -} - -// CompressTorus GT/E12 element to half its size -// z must be in the cyclotomic subgroup -// i.e. z^(p^4-p^2+1)=1 -// e.g. GT -// "COMPRESSION IN FINITE FIELDS AND TORUS-BASED CRYPTOGRAPHY", K. RUBIN AND A. SILVERBERG -// z.C1 == 0 only when z \in {-1,1} -func (z *E12) CompressTorus() (E6, error) { - - if z.C1.IsZero() { - return E6{}, errors.New("invalid input") - } - - var res, tmp, one E6 - one.SetOne() - tmp.Inverse(&z.C1) - res.Add(&z.C0, &one). - Mul(&res, &tmp) - - return res, nil -} - -// BatchCompressTorus GT/E12 elements to half their size using a batch inversion. -// -// if len(x) == 0 or if any of the x[i].C1 coordinate is 0, this function returns an error. -func BatchCompressTorus(x []E12) ([]E6, error) { - - n := len(x) - if n == 0 { - return nil, errors.New("invalid input size") - } - - var one E6 - one.SetOne() - res := make([]E6, n) - - for i := 0; i < n; i++ { - res[i].Set(&x[i].C1) - // throw an error if any of the x[i].C1 is 0 - if res[i].IsZero() { - return nil, errors.New("invalid input; C1 is 0") - } - } - - t := BatchInvertE6(res) // costs 1 inverse - - for i := 0; i < n; i++ { - res[i].Add(&x[i].C0, &one). - Mul(&res[i], &t[i]) - } - - return res, nil -} - -// DecompressTorus GT/E12 a compressed element -// element must be in the cyclotomic subgroup -// "COMPRESSION IN FINITE FIELDS AND TORUS-BASED CRYPTOGRAPHY", K. RUBIN AND A. SILVERBERG -func (z *E6) DecompressTorus() E12 { - - var res, num, denum E12 - num.C0.Set(z) - num.C1.SetOne() - denum.C0.Set(z) - denum.C1.SetOne().Neg(&denum.C1) - res.Inverse(&denum). - Mul(&res, &num) - - return res -} - -// BatchDecompressTorus GT/E12 compressed elements -// using a batch inversion -func BatchDecompressTorus(x []E6) ([]E12, error) { - - n := len(x) - if n == 0 { - return []E12{}, errors.New("invalid input size") - } - - res := make([]E12, n) - num := make([]E12, n) - denum := make([]E12, n) - - for i := 0; i < n; i++ { - num[i].C0.Set(&x[i]) - num[i].C1.SetOne() - denum[i].C0.Set(&x[i]) - denum[i].C1.SetOne().Neg(&denum[i].C1) - } - - denum = BatchInvertE12(denum) // costs 1 inverse - - for i := 0; i < n; i++ { - res[i].Mul(&num[i], &denum[i]) - } - - return res, nil -} - -// Select is conditional move. -// If cond = 0, it sets z to caseZ and returns it. otherwise caseNz. -func (z *E12) Select(cond int, caseZ *E12, caseNz *E12) *E12 { - //Might be able to save a nanosecond or two by an aggregate implementation - - z.C0.Select(cond, &caseZ.C0, &caseNz.C0) - z.C1.Select(cond, &caseZ.C1, &caseNz.C1) - - return z -} - -// Div divides an element in E12 by an element in E12 -func (z *E12) Div(x *E12, y *E12) *E12 { - var r E12 - r.Inverse(y).Mul(x, &r) - return z.Set(&r) -} diff --git a/ecc/bls12-378/internal/fptower/e12_pairing.go b/ecc/bls12-378/internal/fptower/e12_pairing.go deleted file mode 100644 index 71839fe666..0000000000 --- a/ecc/bls12-378/internal/fptower/e12_pairing.go +++ /dev/null @@ -1,187 +0,0 @@ -package fptower - -func (z *E12) nSquare(n int) { - for i := 0; i < n; i++ { - z.CyclotomicSquare(z) - } -} - -func (z *E12) nSquareCompressed(n int) { - for i := 0; i < n; i++ { - z.CyclotomicSquareCompressed(z) - } -} - -// Expt set z to x^t in E12 and return z -func (z *E12) Expt(x *E12) *E12 { - - // Expt computation is derived from the addition chain: - // - // _1000 = 1 << 3 - // _1001 = 1 + _1000 - // _1001000 = _1001 << 3 - // _1010001 = _1001 + _1001000 - // _10011001 = _1001000 + _1010001 - // i67 = ((_10011001 << 5 + _1001) << 10 + _1010001) << 41 - // return 1 + i67 - // - // Operations: 62 squares 6 multiplies - // - // Generated by github.com/mmcloughlin/addchain v0.4.0. - - // Allocate Temporaries. - var result, t0, t1 E12 - - // Step 3: result = x^0x8 - result.CyclotomicSquare(x) - result.nSquare(2) - - // Step 4: t0 = x^0x9 - t0.Mul(x, &result) - - // Step 7: t1 = x^0x48 - t1.CyclotomicSquare(&t0) - t1.nSquare(2) - - // Step 8: result = x^0x51 - result.Mul(&t0, &t1) - - // Step 9: t1 = x^0x99 - t1.Mul(&t1, &result) - - // Step 14: t1 = x^0x1320 - t1.nSquare(5) - - // Step 15: t0 = x^0x1329 - t0.Mul(&t0, &t1) - - // Step 25: t0 = x^0x4ca400 - t0.nSquare(10) - - // Step 26: result = x^0x4ca451 - result.Mul(&result, &t0) - - // Step 67: result = x^0x9948a20000000000 - result.nSquareCompressed(41) - result.DecompressKarabina(&result) - - // Step 68: result = x^0x9948a20000000001 - z.Mul(x, &result) - - return z -} - -// MulBy014 multiplication by sparse element (c0, c1, 0, 0, c4) -func (z *E12) MulBy014(c0, c1, c4 *E2) *E12 { - - var a, b E6 - var d E2 - - a.Set(&z.C0) - a.MulBy01(c0, c1) - - b.Set(&z.C1) - b.MulBy1(c4) - d.Add(c1, c4) - - z.C1.Add(&z.C1, &z.C0) - z.C1.MulBy01(c0, &d) - z.C1.Sub(&z.C1, &a) - z.C1.Sub(&z.C1, &b) - z.C0.MulByNonResidue(&b) - z.C0.Add(&z.C0, &a) - - return z -} - -// MulBy01 multiplication by sparse element (c0, c1, 0, 0, 1) -func (z *E12) MulBy01(c0, c1 *E2) *E12 { - - var a, b E6 - var d E2 - - a.Set(&z.C0) - a.MulBy01(c0, c1) - - b.MulByNonResidue(&z.C1) - d.SetOne().Add(c1, &d) - - z.C1.Add(&z.C1, &z.C0) - z.C1.MulBy01(c0, &d) - z.C1.Sub(&z.C1, &a) - z.C1.Sub(&z.C1, &b) - z.C0.MulByNonResidue(&b) - z.C0.Add(&z.C0, &a) - - return z -} - -// Mul014By014 multiplication of sparse element (c0,c1,0,0,c4,0) by sparse element (d0,d1,0,0,d4,0) -func Mul014By014(d0, d1, d4, c0, c1, c4 *E2) [5]E2 { - var z00, tmp, x0, x1, x4, x04, x01, x14 E2 - x0.Mul(c0, d0) - x1.Mul(c1, d1) - x4.Mul(c4, d4) - tmp.Add(c0, c4) - x04.Add(d0, d4). - Mul(&x04, &tmp). - Sub(&x04, &x0). - Sub(&x04, &x4) - tmp.Add(c0, c1) - x01.Add(d0, d1). - Mul(&x01, &tmp). - Sub(&x01, &x0). - Sub(&x01, &x1) - tmp.Add(c1, c4) - x14.Add(d1, d4). - Mul(&x14, &tmp). - Sub(&x14, &x1). - Sub(&x14, &x4) - - z00.MulByNonResidue(&x4). - Add(&z00, &x0) - - return [5]E2{z00, x01, x1, x04, x14} -} - -// Mul01By01 multiplication of sparse element (c0,c1,0,0,1,0) by sparse element (d0,d1,0,0,1,0) -func Mul01By01(d0, d1, c0, c1 *E2) [5]E2 { - var z00, tmp, x0, x1, x4, x04, x01, x14 E2 - x0.Mul(c0, d0) - x1.Mul(c1, d1) - x4.SetOne() - x04.Add(d0, c0) - tmp.Add(c0, c1) - x01.Add(d0, d1). - Mul(&x01, &tmp). - Sub(&x01, &x0). - Sub(&x01, &x1) - x14.Add(d1, c1) - - z00.MulByNonResidue(&x4). - Add(&z00, &x0) - - return [5]E2{z00, x01, x1, x04, x14} -} - -// MulBy01245 multiplies z by an E12 sparse element of the form (x0, x1, x2, 0, x4, x5) -func (z *E12) MulBy01245(x *[5]E2) *E12 { - var c1, a, b, c, z0, z1 E6 - c0 := &E6{B0: x[0], B1: x[1], B2: x[2]} - c1.B1 = x[3] - c1.B2 = x[4] - a.Add(&z.C0, &z.C1) - b.Add(c0, &c1) - a.Mul(&a, &b) - b.Mul(&z.C0, c0) - c.Set(&z.C1).MulBy12(&x[3], &x[4]) - z1.Sub(&a, &b) - z1.Sub(&z1, &c) - z0.MulByNonResidue(&c) - z0.Add(&z0, &b) - - z.C0 = z0 - z.C1 = z1 - - return z -} diff --git a/ecc/bls12-378/internal/fptower/e12_test.go b/ecc/bls12-378/internal/fptower/e12_test.go deleted file mode 100644 index 3a8e267661..0000000000 --- a/ecc/bls12-378/internal/fptower/e12_test.go +++ /dev/null @@ -1,569 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fptower - -import ( - "math/big" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -// ------------------------------------------------------------ -// tests - -func TestE12Serialization(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := GenE12() - - properties.Property("[BLS12-378] SetBytes(Bytes()) should stay constant", prop.ForAll( - func(a *E12) bool { - var b E12 - buf := a.Bytes() - if err := b.SetBytes(buf[:]); err != nil { - return false - } - return a.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestE12ReceiverIsOperand(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := GenE12() - genB := GenE12() - - properties.Property("[BLS12-378] Having the receiver as operand (addition) should output the same result", prop.ForAll( - func(a, b *E12) bool { - var c, d E12 - d.Set(a) - c.Add(a, b) - a.Add(a, b) - b.Add(&d, b) - return a.Equal(b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (sub) should output the same result", prop.ForAll( - func(a, b *E12) bool { - var c, d E12 - d.Set(a) - c.Sub(a, b) - a.Sub(a, b) - b.Sub(&d, b) - return a.Equal(b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (mul) should output the same result", prop.ForAll( - func(a, b *E12) bool { - var c, d E12 - d.Set(a) - c.Mul(a, b) - a.Mul(a, b) - b.Mul(&d, b) - return a.Equal(b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (square) should output the same result", prop.ForAll( - func(a *E12) bool { - var b E12 - b.Square(a) - a.Square(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (double) should output the same result", prop.ForAll( - func(a *E12) bool { - var b E12 - b.Double(a) - a.Double(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (Inverse) should output the same result", prop.ForAll( - func(a *E12) bool { - var b E12 - b.Inverse(a) - a.Inverse(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (Cyclotomic square) should output the same result", prop.ForAll( - func(a *E12) bool { - var b E12 - b.CyclotomicSquare(a) - a.CyclotomicSquare(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (Conjugate) should output the same result", prop.ForAll( - func(a *E12) bool { - var b E12 - b.Conjugate(a) - a.Conjugate(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (Frobenius) should output the same result", prop.ForAll( - func(a *E12) bool { - var b E12 - b.Frobenius(a) - a.Frobenius(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (FrobeniusSquare) should output the same result", prop.ForAll( - func(a *E12) bool { - var b E12 - b.FrobeniusSquare(a) - a.FrobeniusSquare(a) - return a.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestE12Ops(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := GenE12() - genB := GenE12() - genExp := GenFp() - - properties.Property("[BLS12-378] sub & add should leave an element invariant", prop.ForAll( - func(a, b *E12) bool { - var c E12 - c.Set(a) - c.Add(&c, b).Sub(&c, b) - return c.Equal(a) - }, - genA, - genB, - )) - - properties.Property("[BLS12-378] mul & inverse should leave an element invariant", prop.ForAll( - func(a, b *E12) bool { - var c, d E12 - d.Inverse(b) - c.Set(a) - c.Mul(&c, b).Mul(&c, &d) - return c.Equal(a) - }, - genA, - genB, - )) - - properties.Property("[BLS12-378] inverse twice should leave an element invariant", prop.ForAll( - func(a *E12) bool { - var b E12 - b.Inverse(a).Inverse(&b) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] square and mul should output the same result", prop.ForAll( - func(a *E12) bool { - var b, c E12 - b.Mul(a, a) - c.Square(a) - return b.Equal(&c) - }, - genA, - )) - - properties.Property("[BLS12-378] a + pi(a), a-pi(a) should be real", prop.ForAll( - func(a *E12) bool { - var b, c, d E12 - var e, f, g E6 - b.Conjugate(a) - c.Add(a, &b) - d.Sub(a, &b) - e.Double(&a.C0) - f.Double(&a.C1) - return c.C1.Equal(&g) && d.C0.Equal(&g) && e.Equal(&c.C0) && f.Equal(&d.C1) - }, - genA, - )) - - properties.Property("[BLS12-378] Torus-based Compress/decompress E12 elements in the cyclotomic subgroup", prop.ForAll( - func(a *E12) bool { - var b E12 - b.Conjugate(a) - a.Inverse(a) - b.Mul(&b, a) - a.FrobeniusSquare(&b).Mul(a, &b) - - c, _ := a.CompressTorus() - d := c.DecompressTorus() - return a.Equal(&d) - }, - genA, - )) - - properties.Property("[BLS12-378] Torus-based batch Compress/decompress E12 elements in the cyclotomic subgroup", prop.ForAll( - func(a, e, f *E12) bool { - var b E12 - b.Conjugate(a) - a.Inverse(a) - b.Mul(&b, a) - a.FrobeniusSquare(&b).Mul(a, &b) - - e.CyclotomicSquare(a) - f.CyclotomicSquare(e) - - c, _ := BatchCompressTorus([]E12{*a, *e, *f}) - d, _ := BatchDecompressTorus(c) - return a.Equal(&d[0]) && e.Equal(&d[1]) && f.Equal(&d[2]) - }, - genA, - genA, - genA, - )) - - properties.Property("[BLS12-378] pi**12=id", prop.ForAll( - func(a *E12) bool { - var b E12 - b.Frobenius(a). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b) - return b.Equal(a) - }, - genA, - )) - - properties.Property("[BLS12-378] (pi**2)**6=id", prop.ForAll( - func(a *E12) bool { - var b E12 - b.FrobeniusSquare(a). - FrobeniusSquare(&b). - FrobeniusSquare(&b). - FrobeniusSquare(&b). - FrobeniusSquare(&b). - FrobeniusSquare(&b) - return b.Equal(a) - }, - genA, - )) - - properties.Property("[BLS12-378] cyclotomic square (Granger-Scott) and square should be the same in the cyclotomic subgroup", prop.ForAll( - func(a *E12) bool { - var b, c, d E12 - b.Conjugate(a) - a.Inverse(a) - b.Mul(&b, a) - a.FrobeniusSquare(&b).Mul(a, &b) - c.Square(a) - d.CyclotomicSquare(a) - return c.Equal(&d) - }, - genA, - )) - - properties.Property("[BLS12-378] compressed cyclotomic square (Karabina) and square should be the same in the cyclotomic subgroup", prop.ForAll( - func(a *E12) bool { - var _a, b, c, d, _c, _d E12 - _a.SetOne().Double(&_a) - - // put a and _a in the cyclotomic subgroup - // a (g3 != 0 probably) - b.Conjugate(a) - a.Inverse(a) - b.Mul(&b, a) - a.FrobeniusSquare(&b).Mul(a, &b) - // _a (g3 == 0) - b.Conjugate(&_a) - _a.Inverse(&_a) - b.Mul(&b, &_a) - _a.FrobeniusSquare(&b).Mul(&_a, &b) - - // case g3 != 0 - c.Square(a) - d.CyclotomicSquareCompressed(a).DecompressKarabina(&d) - - // case g3 == 0 - _c.Square(&_a) - _d.CyclotomicSquareCompressed(&_a).DecompressKarabina(&_d) - - return c.Equal(&d) - }, - genA, - )) - - properties.Property("[BLS12-378] batch decompress and individual decompress (Karabina) should be the same", prop.ForAll( - func(a *E12) bool { - var _a, b E12 - _a.SetOne().Double(&_a) - - // put a and _a in the cyclotomic subgroup - // a (g3 !=0 probably) - b.Conjugate(a) - a.Inverse(a) - b.Mul(&b, a) - a.FrobeniusSquare(&b).Mul(a, &b) - // _a (g3 == 0) - b.Conjugate(&_a) - _a.Inverse(&_a) - b.Mul(&b, &_a) - _a.FrobeniusSquare(&b).Mul(&_a, &b) - - var a2, a4, a17 E12 - a2.Set(&_a) - a4.Set(a) - a17.Set(a) - a2.nSquareCompressed(2) // case g3 == 0 - a4.nSquareCompressed(4) - a17.nSquareCompressed(17) - batch := BatchDecompressKarabina([]E12{a2, a4, a17}) - a2.DecompressKarabina(&a2) - a4.DecompressKarabina(&a4) - a17.DecompressKarabina(&a17) - - return a2.Equal(&batch[0]) && a4.Equal(&batch[1]) && a17.Equal(&batch[2]) - }, - genA, - )) - - properties.Property("[BLS12-378] Exp and CyclotomicExp results must be the same in the cyclotomic subgroup", prop.ForAll( - func(a *E12, e fp.Element) bool { - var b, c, d E12 - // put in the cyclo subgroup - b.Conjugate(a) - a.Inverse(a) - b.Mul(&b, a) - a.FrobeniusSquare(&b).Mul(a, &b) - - var _e big.Int - k := new(big.Int).SetUint64(12) - e.Exp(e, k) - e.BigInt(&_e) - - c.Exp(*a, &_e) - d.CyclotomicExp(*a, &_e) - - return c.Equal(&d) - }, - genA, - genExp, - )) - - properties.Property("[BLS12-378] Frobenius of x in E12 should be equal to x^q", prop.ForAll( - func(a *E12) bool { - var b, c E12 - q := fp.Modulus() - b.Frobenius(a) - c.Exp(*a, q) - return c.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] FrobeniusSquare of x in E12 should be equal to x^(q^2)", prop.ForAll( - func(a *E12) bool { - var b, c E12 - q := fp.Modulus() - b.FrobeniusSquare(a) - c.Exp(*a, q).Exp(c, q) - return c.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -// ------------------------------------------------------------ -// benches - -func BenchmarkE12Add(b *testing.B) { - var a, c E12 - _, _ = a.SetRandom() - _, _ = c.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Add(&a, &c) - } -} - -func BenchmarkE12Sub(b *testing.B) { - var a, c E12 - _, _ = a.SetRandom() - _, _ = c.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Sub(&a, &c) - } -} - -func BenchmarkE12Mul(b *testing.B) { - var a, c E12 - _, _ = a.SetRandom() - _, _ = c.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Mul(&a, &c) - } -} - -func BenchmarkE12Cyclosquare(b *testing.B) { - var a E12 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.CyclotomicSquare(&a) - } -} - -func BenchmarkE12Square(b *testing.B) { - var a E12 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Square(&a) - } -} - -func BenchmarkE12Inverse(b *testing.B) { - var a E12 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Inverse(&a) - } -} - -func BenchmarkE12Conjugate(b *testing.B) { - var a E12 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Conjugate(&a) - } -} - -func BenchmarkE12Frobenius(b *testing.B) { - var a E12 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Frobenius(&a) - } -} - -func BenchmarkE12FrobeniusSquare(b *testing.B) { - var a E12 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.FrobeniusSquare(&a) - } -} - -func BenchmarkE12Expt(b *testing.B) { - var a E12 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Expt(&a) - } -} - -func TestE12Div(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - properties := gopter.NewProperties(parameters) - - genA := GenE12() - genB := GenE12() - - properties.Property("[BLS12-378] dividing then multiplying by the same element does nothing", prop.ForAll( - func(a, b *E12) bool { - var c E12 - c.Div(a, b) - c.Mul(&c, b) - return c.Equal(a) - }, - genA, - genB, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} diff --git a/ecc/bls12-378/internal/fptower/e2.go b/ecc/bls12-378/internal/fptower/e2.go deleted file mode 100644 index e6a6c350c8..0000000000 --- a/ecc/bls12-378/internal/fptower/e2.go +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fptower - -import ( - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - "math/big" -) - -// E2 is a degree two finite field extension of fp.Element -type E2 struct { - A0, A1 fp.Element -} - -// Equal returns true if z equals x, false otherwise -func (z *E2) Equal(x *E2) bool { - return z.A0.Equal(&x.A0) && z.A1.Equal(&x.A1) -} - -// Bits -// TODO @gbotrel fixme this shouldn't return a E2 -func (z *E2) Bits() E2 { - r := E2{} - r.A0 = z.A0.Bits() - r.A1 = z.A1.Bits() - return r -} - -// Cmp compares (lexicographic order) z and x and returns: -// -// -1 if z < x -// 0 if z == x -// +1 if z > x -func (z *E2) Cmp(x *E2) int { - if a1 := z.A1.Cmp(&x.A1); a1 != 0 { - return a1 - } - return z.A0.Cmp(&x.A0) -} - -// LexicographicallyLargest returns true if this element is strictly lexicographically -// larger than its negation, false otherwise -func (z *E2) LexicographicallyLargest() bool { - // adapted from github.com/zkcrypto/bls12_381 - if z.A1.IsZero() { - return z.A0.LexicographicallyLargest() - } - return z.A1.LexicographicallyLargest() -} - -// SetString sets a E2 element from strings -func (z *E2) SetString(s1, s2 string) *E2 { - z.A0.SetString(s1) - z.A1.SetString(s2) - return z -} - -// SetZero sets an E2 elmt to zero -func (z *E2) SetZero() *E2 { - z.A0.SetZero() - z.A1.SetZero() - return z -} - -// Set sets an E2 from x -func (z *E2) Set(x *E2) *E2 { - z.A0 = x.A0 - z.A1 = x.A1 - return z -} - -// SetOne sets z to 1 in Montgomery form and returns z -func (z *E2) SetOne() *E2 { - z.A0.SetOne() - z.A1.SetZero() - return z -} - -// SetRandom sets a0 and a1 to random values -func (z *E2) SetRandom() (*E2, error) { - if _, err := z.A0.SetRandom(); err != nil { - return nil, err - } - if _, err := z.A1.SetRandom(); err != nil { - return nil, err - } - return z, nil -} - -// IsZero returns true if z is zero, false otherwise -func (z *E2) IsZero() bool { - return z.A0.IsZero() && z.A1.IsZero() -} - -// IsOne returns true if z is one, false otherwise -func (z *E2) IsOne() bool { - return z.A0.IsOne() && z.A1.IsZero() -} - -// Add adds two elements of E2 -func (z *E2) Add(x, y *E2) *E2 { - addE2(z, x, y) - return z -} - -// Sub subtracts two elements of E2 -func (z *E2) Sub(x, y *E2) *E2 { - subE2(z, x, y) - return z -} - -// Double doubles an E2 element -func (z *E2) Double(x *E2) *E2 { - doubleE2(z, x) - return z -} - -// Neg negates an E2 element -func (z *E2) Neg(x *E2) *E2 { - negE2(z, x) - return z -} - -// String implements Stringer interface for fancy printing -func (z *E2) String() string { - return z.A0.String() + "+" + z.A1.String() + "*u" -} - -// MulByElement multiplies an element in E2 by an element in fp -func (z *E2) MulByElement(x *E2, y *fp.Element) *E2 { - var yCopy fp.Element - yCopy.Set(y) - z.A0.Mul(&x.A0, &yCopy) - z.A1.Mul(&x.A1, &yCopy) - return z -} - -// Conjugate conjugates an element in E2 -func (z *E2) Conjugate(x *E2) *E2 { - z.A0 = x.A0 - z.A1.Neg(&x.A1) - return z -} - -// Halve sets z to z / 2 -func (z *E2) Halve() { - z.A0.Halve() - z.A1.Halve() -} - -// Legendre returns the Legendre symbol of z -func (z *E2) Legendre() int { - var n fp.Element - z.norm(&n) - return n.Legendre() -} - -// Exp sets z=xᵏ (mod q²) and returns it -func (z *E2) Exp(x E2, k *big.Int) *E2 { - if k.IsUint64() && k.Uint64() == 0 { - return z.SetOne() - } - - e := k - if k.Sign() == -1 { - // negative k, we invert - // if k < 0: xᵏ (mod q²) == (x⁻¹)ᵏ (mod q²) - x.Inverse(&x) - - // we negate k in a temp big.Int since - // Int.Bit(_) of k and -k is different - e = bigIntPool.Get().(*big.Int) - defer bigIntPool.Put(e) - e.Neg(k) - } - - z.SetOne() - b := e.Bytes() - for i := 0; i < len(b); i++ { - w := b[i] - for j := 0; j < 8; j++ { - z.Square(z) - if (w & (0b10000000 >> j)) != 0 { - z.Mul(z, &x) - } - } - } - - return z -} - -// Sqrt sets z to the square root of and returns z -// The function does not test whether the square root -// exists or not, it's up to the caller to call -// Legendre beforehand. -// cf https://eprint.iacr.org/2012/685.pdf (algo 10) -func (z *E2) Sqrt(x *E2) *E2 { - - // precomputation - var b, c, d, e, f, x0 E2 - var _b, o fp.Element - - // c must be a non square (works for p=1 mod 12 hence 1 mod 4, only bls377 has such a p currently) - c.A1.SetOne() - - q := fp.Modulus() - var exp, one big.Int - one.SetUint64(1) - exp.Set(q).Sub(&exp, &one).Rsh(&exp, 1) - d.Exp(c, &exp) - e.Mul(&d, &c).Inverse(&e) - f.Mul(&d, &c).Square(&f) - - // computation - exp.Rsh(&exp, 1) - b.Exp(*x, &exp) - b.norm(&_b) - o.SetOne() - if _b.Equal(&o) { - x0.Square(&b).Mul(&x0, x) - _b.Set(&x0.A0).Sqrt(&_b) - z.Conjugate(&b).MulByElement(z, &_b) - return z - } - x0.Square(&b).Mul(&x0, x).Mul(&x0, &f) - _b.Set(&x0.A0).Sqrt(&_b) - z.Conjugate(&b).MulByElement(z, &_b).Mul(z, &e) - - return z -} - -// BatchInvertE2 returns a new slice with every element in a inverted. -// It uses Montgomery batch inversion trick. -// -// if a[i] == 0, returns result[i] = a[i] -func BatchInvertE2(a []E2) []E2 { - res := make([]E2, len(a)) - if len(a) == 0 { - return res - } - - zeroes := make([]bool, len(a)) - var accumulator E2 - accumulator.SetOne() - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - zeroes[i] = true - continue - } - res[i].Set(&accumulator) - accumulator.Mul(&accumulator, &a[i]) - } - - accumulator.Inverse(&accumulator) - - for i := len(a) - 1; i >= 0; i-- { - if zeroes[i] { - continue - } - res[i].Mul(&res[i], &accumulator) - accumulator.Mul(&accumulator, &a[i]) - } - - return res -} - -// Select is conditional move. -// If cond = 0, it sets z to caseZ and returns it. otherwise caseNz. -func (z *E2) Select(cond int, caseZ *E2, caseNz *E2) *E2 { - //Might be able to save a nanosecond or two by an aggregate implementation - - z.A0.Select(cond, &caseZ.A0, &caseNz.A0) - z.A1.Select(cond, &caseZ.A1, &caseNz.A1) - - return z -} - -// Div divides an element in E2 by an element in E2 -func (z *E2) Div(x *E2, y *E2) *E2 { - var r E2 - r.Inverse(y).Mul(x, &r) - return z.Set(&r) -} diff --git a/ecc/bls12-378/internal/fptower/e2_amd64.go b/ecc/bls12-378/internal/fptower/e2_amd64.go deleted file mode 100644 index ac68ffa578..0000000000 --- a/ecc/bls12-378/internal/fptower/e2_amd64.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fptower - -//go:noescape -func addE2(res, x, y *E2) - -//go:noescape -func subE2(res, x, y *E2) - -//go:noescape -func doubleE2(res, x *E2) - -//go:noescape -func negE2(res, x *E2) diff --git a/ecc/bls12-378/internal/fptower/e2_amd64.s b/ecc/bls12-378/internal/fptower/e2_amd64.s deleted file mode 100644 index db266c3089..0000000000 --- a/ecc/bls12-378/internal/fptower/e2_amd64.s +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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. - -#include "textflag.h" -#include "funcdata.h" - -// modulus q -DATA q<>+0(SB)/8, $0x9948a20000000001 -DATA q<>+8(SB)/8, $0xce97f76a822c0000 -DATA q<>+16(SB)/8, $0x980dc360d0a49d7f -DATA q<>+24(SB)/8, $0x84059eb647102326 -DATA q<>+32(SB)/8, $0x53cb5d240ed107a2 -DATA q<>+40(SB)/8, $0x03eeb0416684d190 -GLOBL q<>(SB), (RODATA+NOPTR), $48 - -// qInv0 q'[0] -DATA qInv0<>(SB)/8, $0x9948a1ffffffffff -GLOBL qInv0<>(SB), (RODATA+NOPTR), $8 - -#define REDUCE(ra0, ra1, ra2, ra3, ra4, ra5, rb0, rb1, rb2, rb3, rb4, rb5) \ - MOVQ ra0, rb0; \ - SUBQ q<>(SB), ra0; \ - MOVQ ra1, rb1; \ - SBBQ q<>+8(SB), ra1; \ - MOVQ ra2, rb2; \ - SBBQ q<>+16(SB), ra2; \ - MOVQ ra3, rb3; \ - SBBQ q<>+24(SB), ra3; \ - MOVQ ra4, rb4; \ - SBBQ q<>+32(SB), ra4; \ - MOVQ ra5, rb5; \ - SBBQ q<>+40(SB), ra5; \ - CMOVQCS rb0, ra0; \ - CMOVQCS rb1, ra1; \ - CMOVQCS rb2, ra2; \ - CMOVQCS rb3, ra3; \ - CMOVQCS rb4, ra4; \ - CMOVQCS rb5, ra5; \ - -TEXT ·addE2(SB), NOSPLIT, $0-24 - MOVQ x+8(FP), AX - MOVQ 0(AX), BX - MOVQ 8(AX), SI - MOVQ 16(AX), DI - MOVQ 24(AX), R8 - MOVQ 32(AX), R9 - MOVQ 40(AX), R10 - MOVQ y+16(FP), DX - ADDQ 0(DX), BX - ADCQ 8(DX), SI - ADCQ 16(DX), DI - ADCQ 24(DX), R8 - ADCQ 32(DX), R9 - ADCQ 40(DX), R10 - - // reduce element(BX,SI,DI,R8,R9,R10) using temp registers (R11,R12,R13,R14,R15,s0-8(SP)) - REDUCE(BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15,s0-8(SP)) - - MOVQ res+0(FP), CX - MOVQ BX, 0(CX) - MOVQ SI, 8(CX) - MOVQ DI, 16(CX) - MOVQ R8, 24(CX) - MOVQ R9, 32(CX) - MOVQ R10, 40(CX) - MOVQ 48(AX), BX - MOVQ 56(AX), SI - MOVQ 64(AX), DI - MOVQ 72(AX), R8 - MOVQ 80(AX), R9 - MOVQ 88(AX), R10 - ADDQ 48(DX), BX - ADCQ 56(DX), SI - ADCQ 64(DX), DI - ADCQ 72(DX), R8 - ADCQ 80(DX), R9 - ADCQ 88(DX), R10 - - // reduce element(BX,SI,DI,R8,R9,R10) using temp registers (R11,R12,R13,R14,R15,s0-8(SP)) - REDUCE(BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15,s0-8(SP)) - - MOVQ BX, 48(CX) - MOVQ SI, 56(CX) - MOVQ DI, 64(CX) - MOVQ R8, 72(CX) - MOVQ R9, 80(CX) - MOVQ R10, 88(CX) - RET - -TEXT ·doubleE2(SB), NOSPLIT, $0-16 - MOVQ res+0(FP), DX - MOVQ x+8(FP), AX - MOVQ 0(AX), CX - MOVQ 8(AX), BX - MOVQ 16(AX), SI - MOVQ 24(AX), DI - MOVQ 32(AX), R8 - MOVQ 40(AX), R9 - ADDQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - ADCQ R9, R9 - - // reduce element(CX,BX,SI,DI,R8,R9) using temp registers (R10,R11,R12,R13,R14,R15) - REDUCE(CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15) - - MOVQ CX, 0(DX) - MOVQ BX, 8(DX) - MOVQ SI, 16(DX) - MOVQ DI, 24(DX) - MOVQ R8, 32(DX) - MOVQ R9, 40(DX) - MOVQ 48(AX), CX - MOVQ 56(AX), BX - MOVQ 64(AX), SI - MOVQ 72(AX), DI - MOVQ 80(AX), R8 - MOVQ 88(AX), R9 - ADDQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - ADCQ R9, R9 - - // reduce element(CX,BX,SI,DI,R8,R9) using temp registers (R10,R11,R12,R13,R14,R15) - REDUCE(CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15) - - MOVQ CX, 48(DX) - MOVQ BX, 56(DX) - MOVQ SI, 64(DX) - MOVQ DI, 72(DX) - MOVQ R8, 80(DX) - MOVQ R9, 88(DX) - RET - -TEXT ·subE2(SB), NOSPLIT, $0-24 - XORQ R9, R9 - MOVQ x+8(FP), R8 - MOVQ 0(R8), AX - MOVQ 8(R8), DX - MOVQ 16(R8), CX - MOVQ 24(R8), BX - MOVQ 32(R8), SI - MOVQ 40(R8), DI - MOVQ y+16(FP), R8 - SUBQ 0(R8), AX - SBBQ 8(R8), DX - SBBQ 16(R8), CX - SBBQ 24(R8), BX - SBBQ 32(R8), SI - SBBQ 40(R8), DI - MOVQ x+8(FP), R8 - MOVQ $0x9948a20000000001, R10 - MOVQ $0xce97f76a822c0000, R11 - MOVQ $0x980dc360d0a49d7f, R12 - MOVQ $0x84059eb647102326, R13 - MOVQ $0x53cb5d240ed107a2, R14 - MOVQ $0x03eeb0416684d190, R15 - CMOVQCC R9, R10 - CMOVQCC R9, R11 - CMOVQCC R9, R12 - CMOVQCC R9, R13 - CMOVQCC R9, R14 - CMOVQCC R9, R15 - ADDQ R10, AX - ADCQ R11, DX - ADCQ R12, CX - ADCQ R13, BX - ADCQ R14, SI - ADCQ R15, DI - MOVQ res+0(FP), R10 - MOVQ AX, 0(R10) - MOVQ DX, 8(R10) - MOVQ CX, 16(R10) - MOVQ BX, 24(R10) - MOVQ SI, 32(R10) - MOVQ DI, 40(R10) - MOVQ 48(R8), AX - MOVQ 56(R8), DX - MOVQ 64(R8), CX - MOVQ 72(R8), BX - MOVQ 80(R8), SI - MOVQ 88(R8), DI - MOVQ y+16(FP), R8 - SUBQ 48(R8), AX - SBBQ 56(R8), DX - SBBQ 64(R8), CX - SBBQ 72(R8), BX - SBBQ 80(R8), SI - SBBQ 88(R8), DI - MOVQ $0x9948a20000000001, R11 - MOVQ $0xce97f76a822c0000, R12 - MOVQ $0x980dc360d0a49d7f, R13 - MOVQ $0x84059eb647102326, R14 - MOVQ $0x53cb5d240ed107a2, R15 - MOVQ $0x03eeb0416684d190, R10 - CMOVQCC R9, R11 - CMOVQCC R9, R12 - CMOVQCC R9, R13 - CMOVQCC R9, R14 - CMOVQCC R9, R15 - CMOVQCC R9, R10 - ADDQ R11, AX - ADCQ R12, DX - ADCQ R13, CX - ADCQ R14, BX - ADCQ R15, SI - ADCQ R10, DI - MOVQ res+0(FP), R8 - MOVQ AX, 48(R8) - MOVQ DX, 56(R8) - MOVQ CX, 64(R8) - MOVQ BX, 72(R8) - MOVQ SI, 80(R8) - MOVQ DI, 88(R8) - RET - -TEXT ·negE2(SB), NOSPLIT, $0-16 - MOVQ res+0(FP), DX - MOVQ x+8(FP), AX - MOVQ 0(AX), BX - MOVQ 8(AX), SI - MOVQ 16(AX), DI - MOVQ 24(AX), R8 - MOVQ 32(AX), R9 - MOVQ 40(AX), R10 - MOVQ BX, AX - ORQ SI, AX - ORQ DI, AX - ORQ R8, AX - ORQ R9, AX - ORQ R10, AX - TESTQ AX, AX - JNE l1 - MOVQ AX, 0(DX) - MOVQ AX, 8(DX) - MOVQ AX, 16(DX) - MOVQ AX, 24(DX) - MOVQ AX, 32(DX) - MOVQ AX, 40(DX) - JMP l3 - -l1: - MOVQ $0x9948a20000000001, CX - SUBQ BX, CX - MOVQ CX, 0(DX) - MOVQ $0xce97f76a822c0000, CX - SBBQ SI, CX - MOVQ CX, 8(DX) - MOVQ $0x980dc360d0a49d7f, CX - SBBQ DI, CX - MOVQ CX, 16(DX) - MOVQ $0x84059eb647102326, CX - SBBQ R8, CX - MOVQ CX, 24(DX) - MOVQ $0x53cb5d240ed107a2, CX - SBBQ R9, CX - MOVQ CX, 32(DX) - MOVQ $0x03eeb0416684d190, CX - SBBQ R10, CX - MOVQ CX, 40(DX) - -l3: - MOVQ x+8(FP), AX - MOVQ 48(AX), BX - MOVQ 56(AX), SI - MOVQ 64(AX), DI - MOVQ 72(AX), R8 - MOVQ 80(AX), R9 - MOVQ 88(AX), R10 - MOVQ BX, AX - ORQ SI, AX - ORQ DI, AX - ORQ R8, AX - ORQ R9, AX - ORQ R10, AX - TESTQ AX, AX - JNE l2 - MOVQ AX, 48(DX) - MOVQ AX, 56(DX) - MOVQ AX, 64(DX) - MOVQ AX, 72(DX) - MOVQ AX, 80(DX) - MOVQ AX, 88(DX) - RET - -l2: - MOVQ $0x9948a20000000001, CX - SUBQ BX, CX - MOVQ CX, 48(DX) - MOVQ $0xce97f76a822c0000, CX - SBBQ SI, CX - MOVQ CX, 56(DX) - MOVQ $0x980dc360d0a49d7f, CX - SBBQ DI, CX - MOVQ CX, 64(DX) - MOVQ $0x84059eb647102326, CX - SBBQ R8, CX - MOVQ CX, 72(DX) - MOVQ $0x53cb5d240ed107a2, CX - SBBQ R9, CX - MOVQ CX, 80(DX) - MOVQ $0x03eeb0416684d190, CX - SBBQ R10, CX - MOVQ CX, 88(DX) - RET diff --git a/ecc/bls12-378/internal/fptower/e2_bls378.go b/ecc/bls12-378/internal/fptower/e2_bls378.go deleted file mode 100644 index 677180aefe..0000000000 --- a/ecc/bls12-378/internal/fptower/e2_bls378.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2020 ConsenSys AG -// -// 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 fptower - -import "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - -// Mul sets z to the E2-product of x,y, returns z -func (z *E2) Mul(x, y *E2) *E2 { - var a, b, c fp.Element - a.Add(&x.A0, &x.A1) - b.Add(&y.A0, &y.A1) - a.Mul(&a, &b) - b.Mul(&x.A0, &y.A0) - c.Mul(&x.A1, &y.A1) - z.A1.Sub(&a, &b).Sub(&z.A1, &c) - fp.MulBy5(&c) - z.A0.Sub(&b, &c) - return z -} - -// Square sets z to the E2-product of x,x returns z -func (z *E2) Square(x *E2) *E2 { - //algo 22 https://eprint.iacr.org/2010/354.pdf - var c0, c2 fp.Element - c0.Add(&x.A0, &x.A1) - c2.Neg(&x.A1) - fp.MulBy5(&c2) - c2.Add(&c2, &x.A0) - - c0.Mul(&c0, &c2) // (x1+x2)*(x1+(u**2)x2) - c2.Mul(&x.A0, &x.A1).Double(&c2) - z.A1 = c2 - c2.Double(&c2) - z.A0.Add(&c0, &c2) - - return z -} - -// MulByNonResidue multiplies a E2 by (0,1) -func (z *E2) MulByNonResidue(x *E2) *E2 { - a := x.A0 - b := x.A1 // fetching x.A1 in the function below is slower - fp.MulBy5(&b) - z.A0.Neg(&b) - z.A1 = a - return z -} - -// MulByNonResidueInv multiplies a E2 by (0,1)^{-1} -func (z *E2) MulByNonResidueInv(x *E2) *E2 { - //z.A1.MulByNonResidueInv(&x.A0) - a := x.A1 - fiveinv := fp.Element{ - 4714375566610504077, - 585136512338283717, - 16899133777167898908, - 1882388787078723660, - 12465292654455594957, - 119042783712594200, - } - z.A1.Mul(&x.A0, &fiveinv).Neg(&z.A1) - z.A0 = a - return z -} - -// Inverse sets z to the E2-inverse of x, returns z -func (z *E2) Inverse(x *E2) *E2 { - // Algorithm 8 from https://eprint.iacr.org/2010/354.pdf - //var a, b, t0, t1, tmp fp.Element - var t0, t1, tmp fp.Element - a := &x.A0 // creating the buffers a, b is faster than querying &x.A0, &x.A1 in the functions call below - b := &x.A1 - t0.Square(a) - t1.Square(b) - tmp.Set(&t1) - fp.MulBy5(&tmp) - t0.Add(&t0, &tmp) - t1.Inverse(&t0) - z.A0.Mul(a, &t1) - z.A1.Mul(b, &t1).Neg(&z.A1) - - return z -} - -// norm sets x to the norm of z -func (z *E2) norm(x *fp.Element) { - var tmp fp.Element - x.Square(&z.A1) - tmp.Set(x) - fp.MulBy5(&tmp) - x.Square(&z.A0).Add(x, &tmp) -} diff --git a/ecc/bls12-378/internal/fptower/e2_fallback.go b/ecc/bls12-378/internal/fptower/e2_fallback.go deleted file mode 100644 index 6fe47c4111..0000000000 --- a/ecc/bls12-378/internal/fptower/e2_fallback.go +++ /dev/null @@ -1,40 +0,0 @@ -//go:build !amd64 -// +build !amd64 - -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fptower - -func addE2(z, x, y *E2) { - z.A0.Add(&x.A0, &y.A0) - z.A1.Add(&x.A1, &y.A1) -} - -func subE2(z, x, y *E2) { - z.A0.Sub(&x.A0, &y.A0) - z.A1.Sub(&x.A1, &y.A1) -} - -func doubleE2(z, x *E2) { - z.A0.Double(&x.A0) - z.A1.Double(&x.A1) -} - -func negE2(z, x *E2) { - z.A0.Neg(&x.A0) - z.A1.Neg(&x.A1) -} diff --git a/ecc/bls12-378/internal/fptower/e2_test.go b/ecc/bls12-378/internal/fptower/e2_test.go deleted file mode 100644 index b3ad2558cd..0000000000 --- a/ecc/bls12-378/internal/fptower/e2_test.go +++ /dev/null @@ -1,531 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fptower - -import ( - "crypto/rand" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -// ------------------------------------------------------------ -// tests - -const ( - nbFuzzShort = 10 - nbFuzz = 50 -) - -func TestE2ReceiverIsOperand(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := GenE2() - genB := GenE2() - genfp := GenFp() - - properties.Property("[BLS12-378] Having the receiver as operand (addition) should output the same result", prop.ForAll( - func(a, b *E2) bool { - var c, d E2 - d.Set(a) - c.Add(a, b) - a.Add(a, b) - b.Add(&d, b) - return a.Equal(b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (sub) should output the same result", prop.ForAll( - func(a, b *E2) bool { - var c, d E2 - d.Set(a) - c.Sub(a, b) - a.Sub(a, b) - b.Sub(&d, b) - return a.Equal(b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (mul) should output the same result", prop.ForAll( - func(a, b *E2) bool { - var c, d E2 - d.Set(a) - c.Mul(a, b) - a.Mul(a, b) - b.Mul(&d, b) - return a.Equal(b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (square) should output the same result", prop.ForAll( - func(a *E2) bool { - var b E2 - b.Square(a) - a.Square(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (neg) should output the same result", prop.ForAll( - func(a *E2) bool { - var b E2 - b.Neg(a) - a.Neg(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (double) should output the same result", prop.ForAll( - func(a *E2) bool { - var b E2 - b.Double(a) - a.Double(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (mul by non residue) should output the same result", prop.ForAll( - func(a *E2) bool { - var b E2 - b.MulByNonResidue(a) - a.MulByNonResidue(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (mul by non residue inverse) should output the same result", prop.ForAll( - func(a *E2) bool { - var b E2 - b.MulByNonResidueInv(a) - a.MulByNonResidueInv(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (Inverse) should output the same result", prop.ForAll( - func(a *E2) bool { - var b E2 - b.Inverse(a) - a.Inverse(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (Conjugate) should output the same result", prop.ForAll( - func(a *E2) bool { - var b E2 - b.Conjugate(a) - a.Conjugate(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (mul by element) should output the same result", prop.ForAll( - func(a *E2, b fp.Element) bool { - var c E2 - c.MulByElement(a, &b) - a.MulByElement(a, &b) - return a.Equal(&c) - }, - genA, - genfp, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (Sqrt) should output the same result", prop.ForAll( - func(a *E2) bool { - var b, c, d, s E2 - - s.Square(a) - a.Set(&s) - b.Set(&s) - - a.Sqrt(a) - b.Sqrt(&b) - - c.Square(a) - d.Square(&b) - return c.Equal(&d) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestE2MulMaxed(t *testing.T) { - // let's pick a and b, with maxed A0 and A1 - var a, b E2 - fpMaxValue := fp.Element{ - 11045256207009841153, - 14886639130118979584, - 10956628289047010687, - 9513184293603517222, - 6038022134869067682, - 283357621510263184, - } - fpMaxValue[0]-- - - a.A0 = fpMaxValue - a.A1 = fpMaxValue - b.A0 = fpMaxValue - b.A1 = fpMaxValue - - var c, d E2 - d.Inverse(&b) - c.Set(&a) - c.Mul(&c, &b).Mul(&c, &d) - if !c.Equal(&a) { - t.Fatal("mul with max fp failed") - } -} - -func TestE2Ops(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := GenE2() - genB := GenE2() - genfp := GenFp() - - properties.Property("[BLS12-378] sub & add should leave an element invariant", prop.ForAll( - func(a, b *E2) bool { - var c E2 - c.Set(a) - c.Add(&c, b).Sub(&c, b) - return c.Equal(a) - }, - genA, - genB, - )) - - properties.Property("[BLS12-378] mul & inverse should leave an element invariant", prop.ForAll( - func(a, b *E2) bool { - var c, d E2 - d.Inverse(b) - c.Set(a) - c.Mul(&c, b).Mul(&c, &d) - return c.Equal(a) - }, - genA, - genB, - )) - - properties.Property("[BLS12-378] BatchInvertE2 should output the same result as Inverse", prop.ForAll( - func(a, b, c *E2) bool { - - batch := BatchInvertE2([]E2{*a, *b, *c}) - a.Inverse(a) - b.Inverse(b) - c.Inverse(c) - return a.Equal(&batch[0]) && b.Equal(&batch[1]) && c.Equal(&batch[2]) - }, - genA, - genA, - genA, - )) - - properties.Property("[BLS12-378] inverse twice should leave an element invariant", prop.ForAll( - func(a *E2) bool { - var b E2 - b.Inverse(a).Inverse(&b) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] neg twice should leave an element invariant", prop.ForAll( - func(a *E2) bool { - var b E2 - b.Neg(a).Neg(&b) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] square and mul should output the same result", prop.ForAll( - func(a *E2) bool { - var b, c E2 - b.Mul(a, a) - c.Square(a) - return b.Equal(&c) - }, - genA, - )) - - properties.Property("[BLS12-378] MulByElement MulByElement inverse should leave an element invariant", prop.ForAll( - func(a *E2, b fp.Element) bool { - var c E2 - var d fp.Element - d.Inverse(&b) - c.MulByElement(a, &b).MulByElement(&c, &d) - return c.Equal(a) - }, - genA, - genfp, - )) - - properties.Property("[BLS12-378] Double and mul by 2 should output the same result", prop.ForAll( - func(a *E2) bool { - var b E2 - var c fp.Element - c.SetUint64(2) - b.Double(a) - a.MulByElement(a, &c) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Mulbynonres mulbynonresinv should leave the element invariant", prop.ForAll( - func(a *E2) bool { - var b E2 - b.MulByNonResidue(a).MulByNonResidueInv(&b) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] a + pi(a), a-pi(a) should be real", prop.ForAll( - func(a *E2) bool { - var b, c, d E2 - var e, f fp.Element - b.Conjugate(a) - c.Add(a, &b) - d.Sub(a, &b) - e.Double(&a.A0) - f.Double(&a.A1) - return c.A1.IsZero() && d.A0.IsZero() && e.Equal(&c.A0) && f.Equal(&d.A1) - }, - genA, - )) - - properties.Property("[BLS12-378] Legendre on square should output 1", prop.ForAll( - func(a *E2) bool { - var b E2 - b.Square(a) - c := b.Legendre() - return c == 1 - }, - genA, - )) - - properties.Property("[BLS12-378] square(sqrt) should leave an element invariant", prop.ForAll( - func(a *E2) bool { - var b, c, d, e E2 - b.Square(a) - c.Sqrt(&b) - d.Square(&c) - e.Neg(a) - return (c.Equal(a) || c.Equal(&e)) && d.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] neg(E2) == neg(E2.A0, E2.A1)", prop.ForAll( - func(a *E2) bool { - var b, c E2 - b.Neg(a) - c.A0.Neg(&a.A0) - c.A1.Neg(&a.A1) - return c.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Cmp and LexicographicallyLargest should be consistent", prop.ForAll( - func(a *E2) bool { - var negA E2 - negA.Neg(a) - cmpResult := a.Cmp(&negA) - lResult := a.LexicographicallyLargest() - if lResult && cmpResult == 1 { - return true - } - if !lResult && cmpResult != 1 { - return true - } - return false - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -// ------------------------------------------------------------ -// benches - -func BenchmarkE2Add(b *testing.B) { - var a, c E2 - _, _ = a.SetRandom() - _, _ = c.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Add(&a, &c) - } -} - -func BenchmarkE2Sub(b *testing.B) { - var a, c E2 - _, _ = a.SetRandom() - _, _ = c.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Sub(&a, &c) - } -} - -func BenchmarkE2Mul(b *testing.B) { - var a, c E2 - _, _ = a.SetRandom() - _, _ = c.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Mul(&a, &c) - } -} - -func BenchmarkE2MulByElement(b *testing.B) { - var a E2 - var c fp.Element - _, _ = c.SetRandom() - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.MulByElement(&a, &c) - } -} - -func BenchmarkE2Square(b *testing.B) { - var a E2 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Square(&a) - } -} - -func BenchmarkE2Sqrt(b *testing.B) { - var a E2 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Sqrt(&a) - } -} - -func BenchmarkE2Exp(b *testing.B) { - var x E2 - _, _ = x.SetRandom() - b1, _ := rand.Int(rand.Reader, fp.Modulus()) - b.ResetTimer() - for i := 0; i < b.N; i++ { - x.Exp(x, b1) - } -} - -func BenchmarkE2Inverse(b *testing.B) { - var a E2 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Inverse(&a) - } -} - -func BenchmarkE2MulNonRes(b *testing.B) { - var a E2 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.MulByNonResidue(&a) - } -} - -func BenchmarkE2MulNonResInv(b *testing.B) { - var a E2 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.MulByNonResidueInv(&a) - } -} - -func BenchmarkE2Conjugate(b *testing.B) { - var a E2 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Conjugate(&a) - } -} - -func TestE2Div(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - properties := gopter.NewProperties(parameters) - - genA := GenE2() - genB := GenE2() - - properties.Property("[BLS12-378] dividing then multiplying by the same element does nothing", prop.ForAll( - func(a, b *E2) bool { - var c E2 - c.Div(a, b) - c.Mul(&c, b) - return c.Equal(a) - }, - genA, - genB, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} diff --git a/ecc/bls12-378/internal/fptower/e6.go b/ecc/bls12-378/internal/fptower/e6.go deleted file mode 100644 index 1867470a88..0000000000 --- a/ecc/bls12-378/internal/fptower/e6.go +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fptower - -// E6 is a degree three finite field extension of fp2 -type E6 struct { - B0, B1, B2 E2 -} - -// Equal returns true if z equals x, false otherwise -func (z *E6) Equal(x *E6) bool { - return z.B0.Equal(&x.B0) && z.B1.Equal(&x.B1) && z.B2.Equal(&x.B2) -} - -// SetString sets a E6 elmt from stringf -func (z *E6) SetString(s1, s2, s3, s4, s5, s6 string) *E6 { - z.B0.SetString(s1, s2) - z.B1.SetString(s3, s4) - z.B2.SetString(s5, s6) - return z -} - -// Set Sets a E6 elmt form another E6 elmt -func (z *E6) Set(x *E6) *E6 { - z.B0 = x.B0 - z.B1 = x.B1 - z.B2 = x.B2 - return z -} - -// SetOne sets z to 1 in Montgomery form and returns z -func (z *E6) SetOne() *E6 { - *z = E6{} - z.B0.A0.SetOne() - return z -} - -// SetRandom set z to a random elmt -func (z *E6) SetRandom() (*E6, error) { - if _, err := z.B0.SetRandom(); err != nil { - return nil, err - } - if _, err := z.B1.SetRandom(); err != nil { - return nil, err - } - if _, err := z.B2.SetRandom(); err != nil { - return nil, err - } - return z, nil -} - -// IsZero returns true if z is zero, false otherwise -func (z *E6) IsZero() bool { - return z.B0.IsZero() && z.B1.IsZero() && z.B2.IsZero() -} - -// IsOne returns true if z is one, false otherwise -func (z *E6) IsOne() bool { - return z.B0.IsOne() && z.B1.IsZero() && z.B2.IsZero() -} - -// Add adds two elements of E6 -func (z *E6) Add(x, y *E6) *E6 { - z.B0.Add(&x.B0, &y.B0) - z.B1.Add(&x.B1, &y.B1) - z.B2.Add(&x.B2, &y.B2) - return z -} - -// Neg negates the E6 number -func (z *E6) Neg(x *E6) *E6 { - z.B0.Neg(&x.B0) - z.B1.Neg(&x.B1) - z.B2.Neg(&x.B2) - return z -} - -// Sub subtracts two elements of E6 -func (z *E6) Sub(x, y *E6) *E6 { - z.B0.Sub(&x.B0, &y.B0) - z.B1.Sub(&x.B1, &y.B1) - z.B2.Sub(&x.B2, &y.B2) - return z -} - -// Double doubles an element in E6 -func (z *E6) Double(x *E6) *E6 { - z.B0.Double(&x.B0) - z.B1.Double(&x.B1) - z.B2.Double(&x.B2) - return z -} - -// String puts E6 elmt in string form -func (z *E6) String() string { - return (z.B0.String() + "+(" + z.B1.String() + ")*v+(" + z.B2.String() + ")*v**2") -} - -// MulByNonResidue mul x by (0,1,0) -func (z *E6) MulByNonResidue(x *E6) *E6 { - z.B2, z.B1, z.B0 = x.B1, x.B0, x.B2 - z.B0.MulByNonResidue(&z.B0) - return z -} - -// MulByE2 multiplies an element in E6 by an element in E2 -func (z *E6) MulByE2(x *E6, y *E2) *E6 { - var yCopy E2 - yCopy.Set(y) - z.B0.Mul(&x.B0, &yCopy) - z.B1.Mul(&x.B1, &yCopy) - z.B2.Mul(&x.B2, &yCopy) - return z -} - -// MulBy12 multiplication by sparse element (0,b1,b2) -func (x *E6) MulBy12(b1, b2 *E2) *E6 { - var t1, t2, c0, tmp, c1, c2 E2 - t1.Mul(&x.B1, b1) - t2.Mul(&x.B2, b2) - c0.Add(&x.B1, &x.B2) - tmp.Add(b1, b2) - c0.Mul(&c0, &tmp) - c0.Sub(&c0, &t1) - c0.Sub(&c0, &t2) - c0.MulByNonResidue(&c0) - c1.Add(&x.B0, &x.B1) - c1.Mul(&c1, b1) - c1.Sub(&c1, &t1) - tmp.MulByNonResidue(&t2) - c1.Add(&c1, &tmp) - tmp.Add(&x.B0, &x.B2) - c2.Mul(b2, &tmp) - c2.Sub(&c2, &t2) - c2.Add(&c2, &t1) - - x.B0 = c0 - x.B1 = c1 - x.B2 = c2 - - return x -} - -// MulBy01 multiplication by sparse element (c0,c1,0) -func (z *E6) MulBy01(c0, c1 *E2) *E6 { - - var a, b, tmp, t0, t1, t2 E2 - - a.Mul(&z.B0, c0) - b.Mul(&z.B1, c1) - - tmp.Add(&z.B1, &z.B2) - t0.Mul(c1, &tmp) - t0.Sub(&t0, &b) - t0.MulByNonResidue(&t0) - t0.Add(&t0, &a) - - tmp.Add(&z.B0, &z.B2) - t2.Mul(c0, &tmp) - t2.Sub(&t2, &a) - t2.Add(&t2, &b) - - t1.Add(c0, c1) - tmp.Add(&z.B0, &z.B1) - t1.Mul(&t1, &tmp) - t1.Sub(&t1, &a) - t1.Sub(&t1, &b) - - z.B0.Set(&t0) - z.B1.Set(&t1) - z.B2.Set(&t2) - - return z -} - -// MulBy1 multiplication of E6 by sparse element (0, c1, 0) -func (z *E6) MulBy1(c1 *E2) *E6 { - - var b, tmp, t0, t1 E2 - b.Mul(&z.B1, c1) - - tmp.Add(&z.B1, &z.B2) - t0.Mul(c1, &tmp) - t0.Sub(&t0, &b) - t0.MulByNonResidue(&t0) - - tmp.Add(&z.B0, &z.B1) - t1.Mul(c1, &tmp) - t1.Sub(&t1, &b) - - z.B0.Set(&t0) - z.B1.Set(&t1) - z.B2.Set(&b) - - return z -} - -// Mul sets z to the E6 product of x,y, returns z -func (z *E6) Mul(x, y *E6) *E6 { - // Algorithm 13 from https://eprint.iacr.org/2010/354.pdf - var t0, t1, t2, c0, c1, c2, tmp E2 - t0.Mul(&x.B0, &y.B0) - t1.Mul(&x.B1, &y.B1) - t2.Mul(&x.B2, &y.B2) - - c0.Add(&x.B1, &x.B2) - tmp.Add(&y.B1, &y.B2) - c0.Mul(&c0, &tmp).Sub(&c0, &t1).Sub(&c0, &t2).MulByNonResidue(&c0).Add(&c0, &t0) - - c1.Add(&x.B0, &x.B1) - tmp.Add(&y.B0, &y.B1) - c1.Mul(&c1, &tmp).Sub(&c1, &t0).Sub(&c1, &t1) - tmp.MulByNonResidue(&t2) - c1.Add(&c1, &tmp) - - tmp.Add(&x.B0, &x.B2) - c2.Add(&y.B0, &y.B2).Mul(&c2, &tmp).Sub(&c2, &t0).Sub(&c2, &t2).Add(&c2, &t1) - - z.B0.Set(&c0) - z.B1.Set(&c1) - z.B2.Set(&c2) - - return z -} - -// Square sets z to the E6 product of x,x, returns z -func (z *E6) Square(x *E6) *E6 { - - // Algorithm 16 from https://eprint.iacr.org/2010/354.pdf - var c4, c5, c1, c2, c3, c0 E2 - c4.Mul(&x.B0, &x.B1).Double(&c4) - c5.Square(&x.B2) - c1.MulByNonResidue(&c5).Add(&c1, &c4) - c2.Sub(&c4, &c5) - c3.Square(&x.B0) - c4.Sub(&x.B0, &x.B1).Add(&c4, &x.B2) - c5.Mul(&x.B1, &x.B2).Double(&c5) - c4.Square(&c4) - c0.MulByNonResidue(&c5).Add(&c0, &c3) - z.B2.Add(&c2, &c4).Add(&z.B2, &c5).Sub(&z.B2, &c3) - z.B0.Set(&c0) - z.B1.Set(&c1) - - return z -} - -// Inverse an element in E6 -// -// if x == 0, sets and returns z = x -func (z *E6) Inverse(x *E6) *E6 { - // Algorithm 17 from https://eprint.iacr.org/2010/354.pdf - // step 9 is wrong in the paper it's t1-t4 - var t0, t1, t2, t3, t4, t5, t6, c0, c1, c2, d1, d2 E2 - t0.Square(&x.B0) - t1.Square(&x.B1) - t2.Square(&x.B2) - t3.Mul(&x.B0, &x.B1) - t4.Mul(&x.B0, &x.B2) - t5.Mul(&x.B1, &x.B2) - c0.MulByNonResidue(&t5).Neg(&c0).Add(&c0, &t0) - c1.MulByNonResidue(&t2).Sub(&c1, &t3) - c2.Sub(&t1, &t4) - t6.Mul(&x.B0, &c0) - d1.Mul(&x.B2, &c1) - d2.Mul(&x.B1, &c2) - d1.Add(&d1, &d2).MulByNonResidue(&d1) - t6.Add(&t6, &d1) - t6.Inverse(&t6) - z.B0.Mul(&c0, &t6) - z.B1.Mul(&c1, &t6) - z.B2.Mul(&c2, &t6) - - return z -} - -// BatchInvertE6 returns a new slice with every element in a inverted. -// It uses Montgomery batch inversion trick. -// -// if a[i] == 0, returns result[i] = a[i] -func BatchInvertE6(a []E6) []E6 { - res := make([]E6, len(a)) - if len(a) == 0 { - return res - } - - zeroes := make([]bool, len(a)) - var accumulator E6 - accumulator.SetOne() - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - zeroes[i] = true - continue - } - res[i].Set(&accumulator) - accumulator.Mul(&accumulator, &a[i]) - } - - accumulator.Inverse(&accumulator) - - for i := len(a) - 1; i >= 0; i-- { - if zeroes[i] { - continue - } - res[i].Mul(&res[i], &accumulator) - accumulator.Mul(&accumulator, &a[i]) - } - - return res -} - -// Select is conditional move. -// If cond = 0, it sets z to caseZ and returns it. otherwise caseNz. -func (z *E6) Select(cond int, caseZ *E6, caseNz *E6) *E6 { - //Might be able to save a nanosecond or two by an aggregate implementation - - z.B0.Select(cond, &caseZ.B0, &caseNz.B0) - z.B1.Select(cond, &caseZ.B1, &caseNz.B1) - z.B2.Select(cond, &caseZ.B2, &caseNz.B2) - - return z -} - -// Div divides an element in E6 by an element in E6 -func (z *E6) Div(x *E6, y *E6) *E6 { - var r E6 - r.Inverse(y).Mul(x, &r) - return z.Set(&r) -} diff --git a/ecc/bls12-378/internal/fptower/e6_test.go b/ecc/bls12-378/internal/fptower/e6_test.go deleted file mode 100644 index d964c07870..0000000000 --- a/ecc/bls12-378/internal/fptower/e6_test.go +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fptower - -import ( - "testing" - - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -// ------------------------------------------------------------ -// tests - -func TestE6ReceiverIsOperand(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := GenE6() - genB := GenE6() - genE2 := GenE2() - - properties.Property("[BLS12-378] Having the receiver as operand (addition) should output the same result", prop.ForAll( - func(a, b *E6) bool { - var c, d E6 - d.Set(a) - c.Add(a, b) - a.Add(a, b) - b.Add(&d, b) - return a.Equal(b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (sub) should output the same result", prop.ForAll( - func(a, b *E6) bool { - var c, d E6 - d.Set(a) - c.Sub(a, b) - a.Sub(a, b) - b.Sub(&d, b) - return a.Equal(b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (mul) should output the same result", prop.ForAll( - func(a, b *E6) bool { - var c, d E6 - d.Set(a) - c.Mul(a, b) - a.Mul(a, b) - b.Mul(&d, b) - return a.Equal(b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (square) should output the same result", prop.ForAll( - func(a *E6) bool { - var b E6 - b.Square(a) - a.Square(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (neg) should output the same result", prop.ForAll( - func(a *E6) bool { - var b E6 - b.Neg(a) - a.Neg(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (double) should output the same result", prop.ForAll( - func(a *E6) bool { - var b E6 - b.Double(a) - a.Double(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (mul by non residue) should output the same result", prop.ForAll( - func(a *E6) bool { - var b E6 - b.MulByNonResidue(a) - a.MulByNonResidue(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (Inverse) should output the same result", prop.ForAll( - func(a *E6) bool { - var b E6 - b.Inverse(a) - a.Inverse(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Having the receiver as operand (mul by E2) should output the same result", prop.ForAll( - func(a *E6, b *E2) bool { - var c E6 - c.MulByE2(a, b) - a.MulByE2(a, b) - return a.Equal(&c) - }, - genA, - genE2, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestE6Ops(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := GenE6() - genB := GenE6() - genE2 := GenE2() - - properties.Property("[BLS12-378] sub & add should leave an element invariant", prop.ForAll( - func(a, b *E6) bool { - var c E6 - c.Set(a) - c.Add(&c, b).Sub(&c, b) - return c.Equal(a) - }, - genA, - genB, - )) - - properties.Property("[BLS12-378] mul & inverse should leave an element invariant", prop.ForAll( - func(a, b *E6) bool { - var c, d E6 - d.Inverse(b) - c.Set(a) - c.Mul(&c, b).Mul(&c, &d) - return c.Equal(a) - }, - genA, - genB, - )) - - properties.Property("[BLS12-378] inverse twice should leave an element invariant", prop.ForAll( - func(a *E6) bool { - var b E6 - b.Inverse(a).Inverse(&b) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] BatchInvertE6 should output the same result as Inverse", prop.ForAll( - func(a, b, c *E6) bool { - - batch := BatchInvertE6([]E6{*a, *b, *c}) - a.Inverse(a) - b.Inverse(b) - c.Inverse(c) - return a.Equal(&batch[0]) && b.Equal(&batch[1]) && c.Equal(&batch[2]) - }, - genA, - genA, - genA, - )) - - properties.Property("[BLS12-378] neg twice should leave an element invariant", prop.ForAll( - func(a *E6) bool { - var b E6 - b.Neg(a).Neg(&b) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] square and mul should output the same result", prop.ForAll( - func(a *E6) bool { - var b, c E6 - b.Mul(a, a) - c.Square(a) - return b.Equal(&c) - }, - genA, - )) - - properties.Property("[BLS12-378] Double and add twice should output the same result", prop.ForAll( - func(a *E6) bool { - var b E6 - b.Add(a, a) - a.Double(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Mul by non residue should be the same as multiplying by (0,1,0)", prop.ForAll( - func(a *E6) bool { - var b, c E6 - b.B1.A0.SetOne() - c.Mul(a, &b) - a.MulByNonResidue(a) - return a.Equal(&c) - }, - genA, - )) - - properties.Property("[BLS12-378] MulByE2 MulByE2 inverse should leave an element invariant", prop.ForAll( - func(a *E6, b *E2) bool { - var c E6 - var d E2 - d.Inverse(b) - c.MulByE2(a, b).MulByE2(&c, &d) - return c.Equal(a) - }, - genA, - genE2, - )) - - properties.Property("[BLS12-378] Mul and MulBy01 should output the same result", prop.ForAll( - func(a *E6, c0, c1 *E2) bool { - var b E6 - b.B0.Set(c0) - b.B1.Set(c1) - b.Mul(&b, a) - a.MulBy01(c0, c1) - return b.Equal(a) - }, - genA, - genE2, - genE2, - )) - - properties.Property("[BLS12-378] Mul and MulBy1 should output the same result", prop.ForAll( - func(a *E6, c1 *E2) bool { - var b E6 - b.B1.Set(c1) - b.Mul(&b, a) - a.MulBy1(c1) - return b.Equal(a) - }, - genA, - genE2, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -// ------------------------------------------------------------ -// benches - -func BenchmarkE6Add(b *testing.B) { - var a, c E6 - _, _ = a.SetRandom() - _, _ = c.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Add(&a, &c) - } -} - -func BenchmarkE6Sub(b *testing.B) { - var a, c E6 - _, _ = a.SetRandom() - _, _ = c.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Sub(&a, &c) - } -} - -func BenchmarkE6Mul(b *testing.B) { - var a, c E6 - _, _ = a.SetRandom() - _, _ = c.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Mul(&a, &c) - } -} - -func BenchmarkE6Square(b *testing.B) { - var a E6 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Square(&a) - } -} - -func BenchmarkE6Inverse(b *testing.B) { - var a E6 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Inverse(&a) - } -} - -func TestE6Div(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - properties := gopter.NewProperties(parameters) - - genA := GenE6() - genB := GenE6() - - properties.Property("[BLS12-378] dividing then multiplying by the same element does nothing", prop.ForAll( - func(a, b *E6) bool { - var c E6 - c.Div(a, b) - c.Mul(&c, b) - return c.Equal(a) - }, - genA, - genB, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} diff --git a/ecc/bls12-378/internal/fptower/frobenius.go b/ecc/bls12-378/internal/fptower/frobenius.go deleted file mode 100644 index 24b8bb40c9..0000000000 --- a/ecc/bls12-378/internal/fptower/frobenius.go +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2020 ConsenSys AG -// -// 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 fptower - -import "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - -// Frobenius set z to Frobenius(x), return z -func (z *E12) Frobenius(x *E12) *E12 { - // Algorithm 28 from https://eprint.iacr.org/2010/354.pdf (beware typos!) - var t [6]E2 - - // Frobenius acts on fp2 by conjugation - t[0].Conjugate(&x.C0.B0) - t[1].Conjugate(&x.C0.B1) - t[2].Conjugate(&x.C0.B2) - t[3].Conjugate(&x.C1.B0) - t[4].Conjugate(&x.C1.B1) - t[5].Conjugate(&x.C1.B2) - - t[1].MulByNonResidue1Power2(&t[1]) - t[2].MulByNonResidue1Power4(&t[2]) - t[3].MulByNonResidue1Power1(&t[3]) - t[4].MulByNonResidue1Power3(&t[4]) - t[5].MulByNonResidue1Power5(&t[5]) - - z.C0.B0 = t[0] - z.C0.B1 = t[1] - z.C0.B2 = t[2] - z.C1.B0 = t[3] - z.C1.B1 = t[4] - z.C1.B2 = t[5] - - return z -} - -// FrobeniusSquare set z to Frobenius^2(x), and return z -func (z *E12) FrobeniusSquare(x *E12) *E12 { - // Algorithm 29 from https://eprint.iacr.org/2010/354.pdf (beware typos!) - var t [6]E2 - - t[1].MulByNonResidue2Power2(&x.C0.B1) - t[2].MulByNonResidue2Power4(&x.C0.B2) - t[3].MulByNonResidue2Power1(&x.C1.B0) - t[4].MulByNonResidue2Power3(&x.C1.B1) - t[5].MulByNonResidue2Power5(&x.C1.B2) - - z.C0.B0 = x.C0.B0 - z.C0.B1 = t[1] - z.C0.B2 = t[2] - z.C1.B0 = t[3] - z.C1.B1 = t[4] - z.C1.B2 = t[5] - - return z -} - -// MulByNonResidue1Power1 set z=x*(0,1)^(1*(p^1-1)/6) and return z -func (z *E2) MulByNonResidue1Power1(x *E2) *E2 { - b := fp.Element{ - 9424304261440581301, - 15622662318784019360, - 5704744713545767383, - 7376930514650170538, - 2328236726423359970, - 256435709676028998, - } - z.A0.Mul(&x.A0, &b) - z.A1.Mul(&x.A1, &b) - return z -} - -// MulByNonResidue1Power2 set z=x*(0,1)^(2*(p^1-1)/6) and return z -func (z *E2) MulByNonResidue1Power2(x *E2) *E2 { - b := fp.Element{ - 1263886799460835702, - 3481310115429540252, - 1430516082310201521, - 10760454131030452261, - 15881431079209118478, - 56234068425139279, - } - z.A0.Mul(&x.A0, &b) - z.A1.Mul(&x.A1, &b) - return z -} - -// MulByNonResidue1Power3 set z=x*(0,1)^(3*(p^1-1)/6) and return z -func (z *E2) MulByNonResidue1Power3(x *E2) *E2 { - b := fp.Element{ - 6315024805150803022, - 16048962212196301574, - 10554832649293981783, - 14109148363171599309, - 4153042273623539198, - 250647462785784749, - } - z.A0.Mul(&x.A0, &b) - z.A1.Mul(&x.A1, &b) - return z -} - -// MulByNonResidue1Power4 set z=x*(0,1)^(4*(p^1-1)/6) and return z -func (z *E2) MulByNonResidue1Power4(x *E2) *E2 { - b := fp.Element{ - 18229265454137549239, - 11882161740266529218, - 12635080069402934820, - 1928134709134316785, - 2524500224088382290, - 27735392882694645, - } - z.A0.Mul(&x.A0, &b) - z.A1.Mul(&x.A1, &b) - return z -} - -// MulByNonResidue1Power5 set z=x*(0,1)^(5*(p^1-1)/6) and return z -func (z *E2) MulByNonResidue1Power5(x *E2) *E2 { - b := fp.Element{ - 7935976750720062874, - 15312939023531261798, - 15806716224795225087, - 16245402142124945993, - 7862827682069246910, - 277569374620018935, - } - z.A0.Mul(&x.A0, &b) - z.A1.Mul(&x.A1, &b) - return z -} - -// MulByNonResidue2Power1 set z=x*(0,1)^(1*(p^2-1)/6) and return z -func (z *E2) MulByNonResidue2Power1(x *E2) *E2 { - b := fp.Element{ - 1263886799460835702, - 3481310115429540252, - 1430516082310201521, - 10760454131030452261, - 15881431079209118478, - 56234068425139279, - } - z.A0.Mul(&x.A0, &b) - z.A1.Mul(&x.A1, &b) - return z -} - -// MulByNonResidue2Power2 set z=x*(0,1)^(2*(p^2-1)/6) and return z -func (z *E2) MulByNonResidue2Power2(x *E2) *E2 { - b := fp.Element{ - 18229265454137549239, - 11882161740266529218, - 12635080069402934820, - 1928134709134316785, - 2524500224088382290, - 27735392882694645, - } - z.A0.Mul(&x.A0, &b) - z.A1.Mul(&x.A1, &b) - return z -} - -// MulByNonResidue2Power3 set z=x*(0,1)^(3*(p^2-1)/6) and return z -func (z *E2) MulByNonResidue2Power3(x *E2) *E2 { - b := fp.Element{ - 9563890787977003074, - 4840746681246416935, - 3714448202430192371, - 680864871707381747, - 11127835353457883110, - 254858945967818549, - } - z.A0.Mul(&x.A0, &b) - z.A1.Mul(&x.A1, &b) - return z -} - -// MulByNonResidue2Power4 set z=x*(0,1)^(4*(p^2-1)/6) and return z -func (z *E2) MulByNonResidue2Power4(x *E2) *E2 { - b := fp.Element{ - 9781369407549005451, - 11405329014689439332, - 9526112206736809166, - 17199474236282616577, - 8603335129369500819, - 227123553085123904, - } - z.A0.Mul(&x.A0, &b) - z.A1.Mul(&x.A1, &b) - return z -} - -// MulByNonResidue2Power5 set z=x*(0,1)^(5*(p^2-1)/6) and return z -func (z *E2) MulByNonResidue2Power5(x *E2) *E2 { - b := fp.Element{ - 11262734826581843530, - 3004477389852450365, - 16768292293353627483, - 7585049584469200436, - 3513521910780685392, - 255622228627568539, - } - z.A0.Mul(&x.A0, &b) - z.A1.Mul(&x.A1, &b) - return z -} diff --git a/ecc/bls12-378/internal/fptower/generators_test.go b/ecc/bls12-378/internal/fptower/generators_test.go deleted file mode 100644 index fdfbefbae9..0000000000 --- a/ecc/bls12-378/internal/fptower/generators_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package fptower - -import ( - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - "github.com/leanovate/gopter" -) - -// Fp generates an Fp element -func GenFp() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var elmt fp.Element - - if _, err := elmt.SetRandom(); err != nil { - panic(err) - } - genResult := gopter.NewGenResult(elmt, gopter.NoShrinker) - return genResult - } -} - -// E2 generates an E2 elmt -func GenE2() gopter.Gen { - return gopter.CombineGens( - GenFp(), - GenFp(), - ).Map(func(values []interface{}) *E2 { - return &E2{A0: values[0].(fp.Element), A1: values[1].(fp.Element)} - }) -} - -// E6 generates an E6 elmt -func GenE6() gopter.Gen { - return gopter.CombineGens( - GenE2(), - GenE2(), - GenE2(), - ).Map(func(values []interface{}) *E6 { - return &E6{B0: *values[0].(*E2), B1: *values[1].(*E2), B2: *values[2].(*E2)} - }) -} - -// E12 generates an E6 elmt -func GenE12() gopter.Gen { - return gopter.CombineGens( - GenE6(), - GenE6(), - ).Map(func(values []interface{}) *E12 { - return &E12{C0: *values[0].(*E6), C1: *values[1].(*E6)} - }) -} diff --git a/ecc/bls12-378/internal/fptower/parameters.go b/ecc/bls12-378/internal/fptower/parameters.go deleted file mode 100644 index 7d5ea1a4c3..0000000000 --- a/ecc/bls12-378/internal/fptower/parameters.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2020 ConsenSys AG -// -// 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 fptower - -import ( - "math/big" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" -) - -// generator of the curve -var xGen big.Int - -var glvBasis ecc.Lattice - -func init() { - xGen.SetString("11045256207009841153", 10) - _r := fr.Modulus() - ecc.PrecomputeLattice(_r, &xGen, &glvBasis) -} diff --git a/ecc/bls12-378/kzg/doc.go b/ecc/bls12-378/kzg/doc.go deleted file mode 100644 index d1045726f2..0000000000 --- a/ecc/bls12-378/kzg/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package kzg provides a KZG commitment scheme. -package kzg diff --git a/ecc/bls12-378/kzg/kzg.go b/ecc/bls12-378/kzg/kzg.go deleted file mode 100644 index 1a2b7f26e7..0000000000 --- a/ecc/bls12-378/kzg/kzg.go +++ /dev/null @@ -1,593 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package kzg - -import ( - "errors" - "hash" - "math/big" - "sync" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls12-378" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/fiat-shamir" - - "github.com/consensys/gnark-crypto/internal/parallel" -) - -var ( - ErrInvalidNbDigests = errors.New("number of digests is not the same as the number of polynomials") - ErrZeroNbDigests = errors.New("number of digests is zero") - ErrInvalidPolynomialSize = errors.New("invalid polynomial size (larger than SRS or == 0)") - ErrVerifyOpeningProof = errors.New("can't verify opening proof") - ErrVerifyBatchOpeningSinglePoint = errors.New("can't verify batch opening proof at single point") - ErrMinSRSSize = errors.New("minimum srs size is 2") -) - -// Digest commitment of a polynomial. -type Digest = bls12378.G1Affine - -// ProvingKey used to create or open commitments -type ProvingKey struct { - G1 []bls12378.G1Affine // [G₁ [α]G₁ , [α²]G₁, ... ] -} - -// VerifyingKey used to verify opening proofs -type VerifyingKey struct { - G2 [2]bls12378.G2Affine // [G₂, [α]G₂ ] - G1 bls12378.G1Affine - Lines [2][2][len(bls12378.LoopCounter) - 1]bls12378.LineEvaluationAff // precomputed pairing lines corresponding to G₂, [α]G₂ -} - -// SRS must be computed through MPC and comprises the ProvingKey and the VerifyingKey -type SRS struct { - Pk ProvingKey - Vk VerifyingKey -} - -// TODO @Tabaie get rid of this and use the polynomial package -// eval returns p(point) where p is interpreted as a polynomial -// ∑_{i= 0; i-- { - res.Mul(&res, &point).Add(&res, &p[i]) - } - return res -} - -// NewSRS returns a new SRS using alpha as randomness source -// -// In production, a SRS generated through MPC should be used. -// -// Set Alpha = -1 to generate quickly a balanced, valid SRS (useful for benchmarking). -// -// implements io.ReaderFrom and io.WriterTo -func NewSRS(size uint64, bAlpha *big.Int) (*SRS, error) { - - if size < 2 { - return nil, ErrMinSRSSize - } - var srs SRS - srs.Pk.G1 = make([]bls12378.G1Affine, size) - - var alpha fr.Element - alpha.SetBigInt(bAlpha) - - var bMOne big.Int - bMOne.SetInt64(-1) - - _, _, gen1Aff, gen2Aff := bls12378.Generators() - - // in this case, the SRS is <αⁱ[G₁]> whera α is of order 4 - // so no need to run the batch scalar multiplication. We cannot use alpha=1 - // because it tampers the benchmarks (the SRS is not balanced). - if bAlpha.Cmp(&bMOne) == 0 { - - t, err := fr.Generator(4) - if err != nil { - return &srs, nil - } - var bt big.Int - t.BigInt(&bt) - - var g [4]bls12378.G1Affine - g[0] = gen1Aff - for i := 1; i < 4; i++ { - g[i].ScalarMultiplication(&g[i-1], &bt) - } - parallel.Execute(int(size), func(start, end int) { - for i := start; i < int(end); i++ { - srs.Pk.G1[i] = g[i%4] - } - }) - srs.Vk.G1 = gen1Aff - srs.Vk.G2[0] = gen2Aff - srs.Vk.G2[1].ScalarMultiplication(&srs.Vk.G2[0], &bt) - srs.Vk.Lines[0] = bls12378.PrecomputeLines(srs.Vk.G2[0]) - srs.Vk.Lines[1] = bls12378.PrecomputeLines(srs.Vk.G2[1]) - return &srs, nil - } - srs.Pk.G1[0] = gen1Aff - srs.Vk.G1 = gen1Aff - srs.Vk.G2[0] = gen2Aff - srs.Vk.G2[1].ScalarMultiplication(&gen2Aff, bAlpha) - srs.Vk.Lines[0] = bls12378.PrecomputeLines(srs.Vk.G2[0]) - srs.Vk.Lines[1] = bls12378.PrecomputeLines(srs.Vk.G2[1]) - - alphas := make([]fr.Element, size-1) - alphas[0] = alpha - for i := 1; i < len(alphas); i++ { - alphas[i].Mul(&alphas[i-1], &alpha) - } - g1s := bls12378.BatchScalarMultiplicationG1(&gen1Aff, alphas) - copy(srs.Pk.G1[1:], g1s) - - return &srs, nil -} - -// OpeningProof KZG proof for opening at a single point. -// -// implements io.ReaderFrom and io.WriterTo -type OpeningProof struct { - // H quotient polynomial (f - f(z))/(x-z) - H bls12378.G1Affine - - // ClaimedValue purported value - ClaimedValue fr.Element -} - -// BatchOpeningProof opening proof for many polynomials at the same point -// -// implements io.ReaderFrom and io.WriterTo -type BatchOpeningProof struct { - // H quotient polynomial Sum_i gamma**i*(f - f(z))/(x-z) - H bls12378.G1Affine - - // ClaimedValues purported values - ClaimedValues []fr.Element -} - -// Commit commits to a polynomial using a multi exponentiation with the SRS. -// It is assumed that the polynomial is in canonical form, in Montgomery form. -func Commit(p []fr.Element, pk ProvingKey, nbTasks ...int) (Digest, error) { - - if len(p) == 0 || len(p) > len(pk.G1) { - return Digest{}, ErrInvalidPolynomialSize - } - - var res bls12378.G1Affine - - config := ecc.MultiExpConfig{} - if len(nbTasks) > 0 { - config.NbTasks = nbTasks[0] - } - if _, err := res.MultiExp(pk.G1[:len(p)], p, config); err != nil { - return Digest{}, err - } - - return res, nil -} - -// Open computes an opening proof of polynomial p at given point. -// fft.Domain Cardinality must be larger than p.Degree() -func Open(p []fr.Element, point fr.Element, pk ProvingKey) (OpeningProof, error) { - if len(p) == 0 || len(p) > len(pk.G1) { - return OpeningProof{}, ErrInvalidPolynomialSize - } - - // build the proof - res := OpeningProof{ - ClaimedValue: eval(p, point), - } - - // compute H - // h reuses memory from _p - _p := make([]fr.Element, len(p)) - copy(_p, p) - h := dividePolyByXminusA(_p, res.ClaimedValue, point) - - // commit to H - hCommit, err := Commit(h, pk) - if err != nil { - return OpeningProof{}, err - } - res.H.Set(&hCommit) - - return res, nil -} - -// Verify verifies a KZG opening proof at a single point -func Verify(commitment *Digest, proof *OpeningProof, point fr.Element, vk VerifyingKey) error { - - // [f(a)]G₁ + [-a]([H(α)]G₁) = [f(a) - a*H(α)]G₁ - var totalG1 bls12378.G1Jac - var pointNeg fr.Element - var cmInt, pointInt big.Int - proof.ClaimedValue.BigInt(&cmInt) - pointNeg.Neg(&point).BigInt(&pointInt) - totalG1.JointScalarMultiplication(&vk.G1, &proof.H, &cmInt, &pointInt) - - // [f(a) - a*H(α)]G₁ + [-f(α)]G₁ = [f(a) - f(α) - a*H(α)]G₁ - var commitmentJac bls12378.G1Jac - commitmentJac.FromAffine(commitment) - totalG1.SubAssign(&commitmentJac) - - // e([f(α)-f(a)+aH(α)]G₁], G₂).e([-H(α)]G₁, [α]G₂) == 1 - var totalG1Aff bls12378.G1Affine - totalG1Aff.FromJacobian(&totalG1) - check, err := bls12378.PairingCheckFixedQ( - []bls12378.G1Affine{totalG1Aff, proof.H}, - vk.Lines[:], - ) - - if err != nil { - return err - } - if !check { - return ErrVerifyOpeningProof - } - return nil -} - -// BatchOpenSinglePoint creates a batch opening proof at point of a list of polynomials. -// It's an interactive protocol, made non-interactive using Fiat Shamir. -// -// * point is the point at which the polynomials are opened. -// * digests is the list of committed polynomials to open, need to derive the challenge using Fiat Shamir. -// * polynomials is the list of polynomials to open, they are supposed to be of the same size. -// * dataTranscript extra data that might be needed to derive the challenge used for folding -func BatchOpenSinglePoint(polynomials [][]fr.Element, digests []Digest, point fr.Element, hf hash.Hash, pk ProvingKey, dataTranscript ...[]byte) (BatchOpeningProof, error) { - - // check for invalid sizes - nbDigests := len(digests) - if nbDigests != len(polynomials) { - return BatchOpeningProof{}, ErrInvalidNbDigests - } - - // TODO ensure the polynomials are of the same size - largestPoly := -1 - for _, p := range polynomials { - if len(p) == 0 || len(p) > len(pk.G1) { - return BatchOpeningProof{}, ErrInvalidPolynomialSize - } - if len(p) > largestPoly { - largestPoly = len(p) - } - } - - var res BatchOpeningProof - - // compute the purported values - res.ClaimedValues = make([]fr.Element, len(polynomials)) - var wg sync.WaitGroup - wg.Add(len(polynomials)) - for i := 0; i < len(polynomials); i++ { - go func(_i int) { - res.ClaimedValues[_i] = eval(polynomials[_i], point) - wg.Done() - }(i) - } - - // wait for polynomial evaluations to be completed (res.ClaimedValues) - wg.Wait() - - // derive the challenge γ, binded to the point and the commitments - gamma, err := deriveGamma(point, digests, res.ClaimedValues, hf, dataTranscript...) - if err != nil { - return BatchOpeningProof{}, err - } - - // ∑ᵢγⁱf(a) - var foldedEvaluations fr.Element - chSumGammai := make(chan struct{}, 1) - go func() { - foldedEvaluations = res.ClaimedValues[nbDigests-1] - for i := nbDigests - 2; i >= 0; i-- { - foldedEvaluations.Mul(&foldedEvaluations, &gamma). - Add(&foldedEvaluations, &res.ClaimedValues[i]) - } - close(chSumGammai) - }() - - // compute ∑ᵢγⁱfᵢ - // note: if we are willing to parallelize that, we could clone the poly and scale them by - // gamma n in parallel, before reducing into foldedPolynomials - foldedPolynomials := make([]fr.Element, largestPoly) - copy(foldedPolynomials, polynomials[0]) - gammas := make([]fr.Element, len(polynomials)) - gammas[0] = gamma - for i := 1; i < len(polynomials); i++ { - gammas[i].Mul(&gammas[i-1], &gamma) - } - - for i := 1; i < len(polynomials); i++ { - i := i - parallel.Execute(len(polynomials[i]), func(start, end int) { - var pj fr.Element - for j := start; j < end; j++ { - pj.Mul(&polynomials[i][j], &gammas[i-1]) - foldedPolynomials[j].Add(&foldedPolynomials[j], &pj) - } - }) - } - - // compute H - <-chSumGammai - h := dividePolyByXminusA(foldedPolynomials, foldedEvaluations, point) - foldedPolynomials = nil // same memory as h - - res.H, err = Commit(h, pk) - if err != nil { - return BatchOpeningProof{}, err - } - - return res, nil -} - -// FoldProof fold the digests and the proofs in batchOpeningProof using Fiat Shamir -// to obtain an opening proof at a single point. -// -// * digests list of digests on which batchOpeningProof is based -// * batchOpeningProof opening proof of digests -// * transcript extra data needed to derive the challenge used for folding. -// * returns the folded version of batchOpeningProof, Digest, the folded version of digests -func FoldProof(digests []Digest, batchOpeningProof *BatchOpeningProof, point fr.Element, hf hash.Hash, dataTranscript ...[]byte) (OpeningProof, Digest, error) { - - nbDigests := len(digests) - - // check consistency between numbers of claims vs number of digests - if nbDigests != len(batchOpeningProof.ClaimedValues) { - return OpeningProof{}, Digest{}, ErrInvalidNbDigests - } - - // derive the challenge γ, binded to the point and the commitments - gamma, err := deriveGamma(point, digests, batchOpeningProof.ClaimedValues, hf, dataTranscript...) - if err != nil { - return OpeningProof{}, Digest{}, ErrInvalidNbDigests - } - - // fold the claimed values and digests - // gammai = [1,γ,γ²,..,γⁿ⁻¹] - gammai := make([]fr.Element, nbDigests) - gammai[0].SetOne() - if nbDigests > 1 { - gammai[1] = gamma - } - for i := 2; i < nbDigests; i++ { - gammai[i].Mul(&gammai[i-1], &gamma) - } - - foldedDigests, foldedEvaluations, err := fold(digests, batchOpeningProof.ClaimedValues, gammai) - if err != nil { - return OpeningProof{}, Digest{}, err - } - - // create the folded opening proof - var res OpeningProof - res.ClaimedValue.Set(&foldedEvaluations) - res.H.Set(&batchOpeningProof.H) - - return res, foldedDigests, nil -} - -// BatchVerifySinglePoint verifies a batched opening proof at a single point of a list of polynomials. -// -// * digests list of digests on which opening proof is done -// * batchOpeningProof proof of correct opening on the digests -// * dataTranscript extra data that might be needed to derive the challenge used for the folding -func BatchVerifySinglePoint(digests []Digest, batchOpeningProof *BatchOpeningProof, point fr.Element, hf hash.Hash, vk VerifyingKey, dataTranscript ...[]byte) error { - - // fold the proof - foldedProof, foldedDigest, err := FoldProof(digests, batchOpeningProof, point, hf, dataTranscript...) - if err != nil { - return err - } - - // verify the foldedProof against the foldedDigest - err = Verify(&foldedDigest, &foldedProof, point, vk) - return err - -} - -// BatchVerifyMultiPoints batch verifies a list of opening proofs at different points. -// The purpose of the batching is to have only one pairing for verifying several proofs. -// -// * digests list of committed polynomials -// * proofs list of opening proofs, one for each digest -// * points the list of points at which the opening are done -func BatchVerifyMultiPoints(digests []Digest, proofs []OpeningProof, points []fr.Element, vk VerifyingKey) error { - - // check consistency nb proogs vs nb digests - if len(digests) != len(proofs) || len(digests) != len(points) { - return ErrInvalidNbDigests - } - - // len(digests) should be nonzero because of randomNumbers - if len(digests) == 0 { - return ErrZeroNbDigests - } - - // if only one digest, call Verify - if len(digests) == 1 { - return Verify(&digests[0], &proofs[0], points[0], vk) - } - - // sample random numbers λᵢ for sampling - randomNumbers := make([]fr.Element, len(digests)) - randomNumbers[0].SetOne() - for i := 1; i < len(randomNumbers); i++ { - _, err := randomNumbers[i].SetRandom() - if err != nil { - return err - } - } - - // fold the committed quotients compute ∑ᵢλᵢ[Hᵢ(α)]G₁ - var foldedQuotients bls12378.G1Affine - quotients := make([]bls12378.G1Affine, len(proofs)) - for i := 0; i < len(randomNumbers); i++ { - quotients[i].Set(&proofs[i].H) - } - config := ecc.MultiExpConfig{} - if _, err := foldedQuotients.MultiExp(quotients, randomNumbers, config); err != nil { - return err - } - - // fold digests and evals - evals := make([]fr.Element, len(digests)) - for i := 0; i < len(randomNumbers); i++ { - evals[i].Set(&proofs[i].ClaimedValue) - } - - // fold the digests: ∑ᵢλᵢ[f_i(α)]G₁ - // fold the evals : ∑ᵢλᵢfᵢ(aᵢ) - foldedDigests, foldedEvals, err := fold(digests, evals, randomNumbers) - if err != nil { - return err - } - - // compute commitment to folded Eval [∑ᵢλᵢfᵢ(aᵢ)]G₁ - var foldedEvalsCommit bls12378.G1Affine - var foldedEvalsBigInt big.Int - foldedEvals.BigInt(&foldedEvalsBigInt) - foldedEvalsCommit.ScalarMultiplication(&vk.G1, &foldedEvalsBigInt) - - // compute foldedDigests = ∑ᵢλᵢ[fᵢ(α)]G₁ - [∑ᵢλᵢfᵢ(aᵢ)]G₁ - foldedDigests.Sub(&foldedDigests, &foldedEvalsCommit) - - // combien the points and the quotients using γᵢ - // ∑ᵢλᵢ[p_i]([Hᵢ(α)]G₁) - var foldedPointsQuotients bls12378.G1Affine - for i := 0; i < len(randomNumbers); i++ { - randomNumbers[i].Mul(&randomNumbers[i], &points[i]) - } - _, err = foldedPointsQuotients.MultiExp(quotients, randomNumbers, config) - if err != nil { - return err - } - - // ∑ᵢλᵢ[f_i(α)]G₁ - [∑ᵢλᵢfᵢ(aᵢ)]G₁ + ∑ᵢλᵢ[p_i]([Hᵢ(α)]G₁) - // = [∑ᵢλᵢf_i(α) - ∑ᵢλᵢfᵢ(aᵢ) + ∑ᵢλᵢpᵢHᵢ(α)]G₁ - foldedDigests.Add(&foldedDigests, &foldedPointsQuotients) - - // -∑ᵢλᵢ[Qᵢ(α)]G₁ - foldedQuotients.Neg(&foldedQuotients) - - // pairing check - // e([∑ᵢλᵢ(fᵢ(α) - fᵢ(pᵢ) + pᵢHᵢ(α))]G₁, G₂).e([-∑ᵢλᵢ[Hᵢ(α)]G₁), [α]G₂) - check, err := bls12378.PairingCheckFixedQ( - []bls12378.G1Affine{foldedDigests, foldedQuotients}, - vk.Lines[:], - ) - if err != nil { - return err - } - if !check { - return ErrVerifyOpeningProof - } - return nil - -} - -// fold folds digests and evaluations using the list of factors as random numbers. -// -// * digests list of digests to fold -// * evaluations list of evaluations to fold -// * factors list of multiplicative factors used for the folding (in Montgomery form) -// -// * Returns ∑ᵢcᵢdᵢ, ∑ᵢcᵢf(aᵢ) -func fold(di []Digest, fai []fr.Element, ci []fr.Element) (Digest, fr.Element, error) { - - // length inconsistency between digests and evaluations should have been done before calling this function - nbDigests := len(di) - - // fold the claimed values ∑ᵢcᵢf(aᵢ) - var foldedEvaluations, tmp fr.Element - for i := 0; i < nbDigests; i++ { - tmp.Mul(&fai[i], &ci[i]) - foldedEvaluations.Add(&foldedEvaluations, &tmp) - } - - // fold the digests ∑ᵢ[cᵢ]([fᵢ(α)]G₁) - var foldedDigests Digest - _, err := foldedDigests.MultiExp(di, ci, ecc.MultiExpConfig{}) - if err != nil { - return foldedDigests, foldedEvaluations, err - } - - // folding done - return foldedDigests, foldedEvaluations, nil - -} - -// deriveGamma derives a challenge using Fiat Shamir to fold proofs. -func deriveGamma(point fr.Element, digests []Digest, claimedValues []fr.Element, hf hash.Hash, dataTranscript ...[]byte) (fr.Element, error) { - - // derive the challenge gamma, binded to the point and the commitments - fs := fiatshamir.NewTranscript(hf, "gamma") - if err := fs.Bind("gamma", point.Marshal()); err != nil { - return fr.Element{}, err - } - for i := range digests { - if err := fs.Bind("gamma", digests[i].Marshal()); err != nil { - return fr.Element{}, err - } - } - for i := range claimedValues { - if err := fs.Bind("gamma", claimedValues[i].Marshal()); err != nil { - return fr.Element{}, err - } - } - - for i := 0; i < len(dataTranscript); i++ { - if err := fs.Bind("gamma", dataTranscript[i]); err != nil { - return fr.Element{}, err - } - } - - gammaByte, err := fs.ComputeChallenge("gamma") - if err != nil { - return fr.Element{}, err - } - var gamma fr.Element - gamma.SetBytes(gammaByte) - - return gamma, nil -} - -// dividePolyByXminusA computes (f-f(a))/(x-a), in canonical basis, in regular form -// f memory is re-used for the result -func dividePolyByXminusA(f []fr.Element, fa, a fr.Element) []fr.Element { - - // first we compute f-f(a) - f[0].Sub(&f[0], &fa) - - // now we use synthetic division to divide by x-a - var t fr.Element - for i := len(f) - 2; i >= 0; i-- { - t.Mul(&f[i+1], &a) - - f[i].Add(&f[i], &t) - } - - // the result is of degree deg(f)-1 - return f[1:] -} diff --git a/ecc/bls12-378/kzg/kzg_test.go b/ecc/bls12-378/kzg/kzg_test.go deleted file mode 100644 index 156a1730de..0000000000 --- a/ecc/bls12-378/kzg/kzg_test.go +++ /dev/null @@ -1,758 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package kzg - -import ( - "bytes" - "crypto/sha256" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "math/big" - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls12-378" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/fft" - - "github.com/consensys/gnark-crypto/utils/testutils" -) - -// Test SRS re-used across tests of the KZG scheme -var testSrs *SRS -var bAlpha *big.Int - -func init() { - const srsSize = 230 - bAlpha = new(big.Int).SetInt64(42) // randomise ? - testSrs, _ = NewSRS(ecc.NextPowerOfTwo(srsSize), bAlpha) -} - -func TestToLagrangeG1(t *testing.T) { - assert := require.New(t) - - const size = 32 - - // convert the test SRS to Lagrange form - lagrange, err := ToLagrangeG1(testSrs.Pk.G1[:size]) - assert.NoError(err) - - // generate the Lagrange SRS manually and compare - w, err := fr.Generator(uint64(size)) - assert.NoError(err) - - var li, n, d, one, acc, alpha fr.Element - alpha.SetBigInt(bAlpha) - li.SetUint64(uint64(size)).Inverse(&li) - one.SetOne() - n.Exp(alpha, big.NewInt(int64(size))).Sub(&n, &one) - d.Sub(&alpha, &one) - li.Mul(&li, &n).Div(&li, &d) - expectedSrsLagrange := make([]bls12378.G1Affine, size) - _, _, g1Gen, _ := bls12378.Generators() - var s big.Int - acc.SetOne() - for i := 0; i < size; i++ { - li.BigInt(&s) - expectedSrsLagrange[i].ScalarMultiplication(&g1Gen, &s) - - li.Mul(&li, &w).Mul(&li, &d) - acc.Mul(&acc, &w) - d.Sub(&alpha, &acc) - li.Div(&li, &d) - } - - for i := 0; i < size; i++ { - assert.True(expectedSrsLagrange[i].Equal(&lagrange[i]), "error lagrange conversion") - } -} - -func TestCommitLagrange(t *testing.T) { - - assert := require.New(t) - - // sample a sparse polynomial (here in Lagrange form) - size := 64 - pol := make([]fr.Element, size) - pol[0].SetRandom() - for i := 0; i < size; i = i + 8 { - pol[i].SetRandom() - } - - // commitment using Lagrange SRS - lagrange, err := ToLagrangeG1(testSrs.Pk.G1[:size]) - assert.NoError(err) - var pkLagrange ProvingKey - pkLagrange.G1 = lagrange - - digestLagrange, err := Commit(pol, pkLagrange) - assert.NoError(err) - - // commitment using canonical SRS - d := fft.NewDomain(uint64(size)) - d.FFTInverse(pol, fft.DIF) - fft.BitReverse(pol) - digestCanonical, err := Commit(pol, testSrs.Pk) - assert.NoError(err) - - // compare the results - assert.True(digestCanonical.Equal(&digestLagrange), "error CommitLagrange") -} - -func TestDividePolyByXminusA(t *testing.T) { - - const pSize = 230 - - // build random polynomial - pol := make([]fr.Element, pSize) - pol[0].SetRandom() - for i := 1; i < pSize; i++ { - pol[i] = pol[i-1] - } - - // evaluate the polynomial at a random point - var point fr.Element - point.SetRandom() - evaluation := eval(pol, point) - - // probabilistic test (using Schwartz Zippel lemma, evaluation at one point is enough) - var randPoint, xminusa fr.Element - randPoint.SetRandom() - polRandpoint := eval(pol, randPoint) - polRandpoint.Sub(&polRandpoint, &evaluation) // f(rand)-f(point) - - // compute f-f(a)/x-a - // h re-uses the memory of pol - h := dividePolyByXminusA(pol, evaluation, point) - - if len(h) != 229 { - t.Fatal("inconsistent size of quotient") - } - - hRandPoint := eval(h, randPoint) - xminusa.Sub(&randPoint, &point) // rand-point - - // f(rand)-f(point) ==? h(rand)*(rand-point) - hRandPoint.Mul(&hRandPoint, &xminusa) - - if !hRandPoint.Equal(&polRandpoint) { - t.Fatal("Error f-f(a)/x-a") - } -} - -func TestSerializationSRS(t *testing.T) { - // create a SRS - srs, err := NewSRS(64, new(big.Int).SetInt64(42)) - assert.NoError(t, err) - t.Run("proving key round-trip", testutils.SerializationRoundTrip(&srs.Pk)) - t.Run("proving key raw round-trip", testutils.SerializationRoundTripRaw(&srs.Pk)) - t.Run("verifying key round-trip", testutils.SerializationRoundTrip(&srs.Vk)) - t.Run("whole SRS round-trip", testutils.SerializationRoundTrip(srs)) - t.Run("unsafe whole SRS round-trip", testutils.UnsafeBinaryMarshalerRoundTrip(srs)) -} - -func TestCommit(t *testing.T) { - - // create a polynomial - f := make([]fr.Element, 60) - for i := 0; i < 60; i++ { - f[i].SetRandom() - } - - // commit using the method from KZG - _kzgCommit, err := Commit(f, testSrs.Pk) - if err != nil { - t.Fatal(err) - } - var kzgCommit bls12378.G1Affine - kzgCommit.Unmarshal(_kzgCommit.Marshal()) - - // check commitment using manual commit - var x fr.Element - x.SetString("42") - fx := eval(f, x) - var fxbi big.Int - fx.BigInt(&fxbi) - var manualCommit bls12378.G1Affine - manualCommit.Set(&testSrs.Vk.G1) - manualCommit.ScalarMultiplication(&manualCommit, &fxbi) - - // compare both results - if !kzgCommit.Equal(&manualCommit) { - t.Fatal("error KZG commitment") - } - -} - -func TestVerifySinglePoint(t *testing.T) { - - // create a polynomial - f := randomPolynomial(60) - - // commit the polynomial - digest, err := Commit(f, testSrs.Pk) - if err != nil { - t.Fatal(err) - } - - // compute opening proof at a random point - var point fr.Element - point.SetString("4321") - proof, err := Open(f, point, testSrs.Pk) - if err != nil { - t.Fatal(err) - } - - // verify the claimed valued - expected := eval(f, point) - if !proof.ClaimedValue.Equal(&expected) { - t.Fatal("inconsistent claimed value") - } - - // verify correct proof - err = Verify(&digest, &proof, point, testSrs.Vk) - if err != nil { - t.Fatal(err) - } - - { - // verify wrong proof - proof.ClaimedValue.Double(&proof.ClaimedValue) - err = Verify(&digest, &proof, point, testSrs.Vk) - if err == nil { - t.Fatal("verifying wrong proof should have failed") - } - } - { - // verify wrong proof with quotient set to zero - // see https://cryptosubtlety.medium.com/00-8d4adcf4d255 - proof.H.X.SetZero() - proof.H.Y.SetZero() - err = Verify(&digest, &proof, point, testSrs.Vk) - if err == nil { - t.Fatal("verifying wrong proof should have failed") - } - } -} - -func TestVerifySinglePointQuickSRS(t *testing.T) { - - size := 64 - srs, err := NewSRS(64, big.NewInt(-1)) - if err != nil { - t.Fatal(err) - } - - // random polynomial - p := make([]fr.Element, size) - for i := 0; i < size; i++ { - p[i].SetRandom() - } - - // random value - var x fr.Element - x.SetRandom() - - // verify valid proof - d, err := Commit(p, srs.Pk) - if err != nil { - t.Fatal(err) - } - proof, err := Open(p, x, srs.Pk) - if err != nil { - t.Fatal(err) - } - err = Verify(&d, &proof, x, srs.Vk) - if err != nil { - t.Fatal(err) - } - - // verify wrong proof - proof.ClaimedValue.SetRandom() - err = Verify(&d, &proof, x, srs.Vk) - if err == nil { - t.Fatal(err) - } - -} - -func TestBatchVerifySinglePoint(t *testing.T) { - - size := 40 - - // create polynomials - f := make([][]fr.Element, 10) - for i := range f { - f[i] = randomPolynomial(size) - } - - // commit the polynomials - digests := make([]Digest, len(f)) - for i := range f { - digests[i], _ = Commit(f[i], testSrs.Pk) - - } - - // pick a hash function - hf := sha256.New() - - // compute opening proof at a random point - var point fr.Element - point.SetString("4321") - proof, err := BatchOpenSinglePoint(f, digests, point, hf, testSrs.Pk) - if err != nil { - t.Fatal(err) - } - - var salt fr.Element - salt.SetRandom() - proofExtendedTranscript, err := BatchOpenSinglePoint(f, digests, point, hf, testSrs.Pk, salt.Marshal()) - if err != nil { - t.Fatal(err) - } - - // verify the claimed values - for i := range f { - expectedClaim := eval(f[i], point) - if !expectedClaim.Equal(&proof.ClaimedValues[i]) { - t.Fatal("inconsistent claimed values") - } - } - - // verify correct proof - err = BatchVerifySinglePoint(digests, &proof, point, hf, testSrs.Vk) - if err != nil { - t.Fatal(err) - } - - // verify correct proof with extended transcript - err = BatchVerifySinglePoint(digests, &proofExtendedTranscript, point, hf, testSrs.Vk, salt.Marshal()) - if err != nil { - t.Fatal(err) - } - - { - // verify wrong proof - proof.ClaimedValues[0].Double(&proof.ClaimedValues[0]) - err = BatchVerifySinglePoint(digests, &proof, point, hf, testSrs.Vk) - if err == nil { - t.Fatal("verifying wrong proof should have failed") - } - } - { - // verify wrong proof with quotient set to zero - // see https://cryptosubtlety.medium.com/00-8d4adcf4d255 - proof.H.X.SetZero() - proof.H.Y.SetZero() - err = BatchVerifySinglePoint(digests, &proof, point, hf, testSrs.Vk) - if err == nil { - t.Fatal("verifying wrong proof should have failed") - } - } -} - -func TestBatchVerifyMultiPoints(t *testing.T) { - - // create polynomials - f := make([][]fr.Element, 10) - for i := 0; i < 10; i++ { - f[i] = randomPolynomial(40) - } - - // commit the polynomials - digests := make([]Digest, 10) - for i := 0; i < 10; i++ { - digests[i], _ = Commit(f[i], testSrs.Pk) - } - - // pick a hash function - hf := sha256.New() - - // compute 2 batch opening proofs at 2 random points - points := make([]fr.Element, 2) - batchProofs := make([]BatchOpeningProof, 2) - points[0].SetRandom() - batchProofs[0], _ = BatchOpenSinglePoint(f[:5], digests[:5], points[0], hf, testSrs.Pk) - points[1].SetRandom() - batchProofs[1], _ = BatchOpenSinglePoint(f[5:], digests[5:], points[1], hf, testSrs.Pk) - - // fold the 2 batch opening proofs - proofs := make([]OpeningProof, 2) - foldedDigests := make([]Digest, 2) - proofs[0], foldedDigests[0], _ = FoldProof(digests[:5], &batchProofs[0], points[0], hf) - proofs[1], foldedDigests[1], _ = FoldProof(digests[5:], &batchProofs[1], points[1], hf) - - // check that the individual batch proofs are correct - err := Verify(&foldedDigests[0], &proofs[0], points[0], testSrs.Vk) - if err != nil { - t.Fatal(err) - } - err = Verify(&foldedDigests[1], &proofs[1], points[1], testSrs.Vk) - if err != nil { - t.Fatal(err) - } - - // batch verify correct folded proofs - err = BatchVerifyMultiPoints(foldedDigests, proofs, points, testSrs.Vk) - if err != nil { - t.Fatal(err) - } - - { - // batch verify tampered folded proofs - proofs[0].ClaimedValue.Double(&proofs[0].ClaimedValue) - - err = BatchVerifyMultiPoints(foldedDigests, proofs, points, testSrs.Vk) - if err == nil { - t.Fatal(err) - } - } - { - // batch verify tampered folded proofs with quotients set to infinity - // see https://cryptosubtlety.medium.com/00-8d4adcf4d255 - proofs[0].H.X.SetZero() - proofs[0].H.Y.SetZero() - proofs[1].H.X.SetZero() - proofs[1].H.Y.SetZero() - err = BatchVerifyMultiPoints(foldedDigests, proofs, points, testSrs.Vk) - if err == nil { - t.Fatal(err) - } - } -} - -func TestUnsafeToBytesTruncating(t *testing.T) { - assert := require.New(t) - srs, err := NewSRS(ecc.NextPowerOfTwo(1<<10), big.NewInt(-1)) - assert.NoError(err) - - // marshal the SRS, but explicitly with less points. - var buf bytes.Buffer - err = srs.WriteDump(&buf, 1<<9) - assert.NoError(err) - - r := bytes.NewReader(buf.Bytes()) - - // unmarshal the SRS - var newSRS SRS - err = newSRS.ReadDump(r) - assert.NoError(err) - - // check that the SRS proving key has only 1 << 9 points - assert.Equal(1<<9, len(newSRS.Pk.G1)) - - // ensure they are equal to the original SRS - assert.Equal(srs.Pk.G1[:1<<9], newSRS.Pk.G1) - - // read even less points. - var newSRSPartial SRS - r = bytes.NewReader(buf.Bytes()) - err = newSRSPartial.ReadDump(r, 1<<8) - assert.NoError(err) - - // check that the SRS proving key has only 1 << 8 points - assert.Equal(1<<8, len(newSRSPartial.Pk.G1)) - - // ensure they are equal to the original SRS - assert.Equal(srs.Pk.G1[:1<<8], newSRSPartial.Pk.G1) -} - -const benchSize = 1 << 16 - -func BenchmarkSRSGen(b *testing.B) { - - b.Run("real SRS", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) - } - }) - b.Run("quick SRS", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - NewSRS(ecc.NextPowerOfTwo(benchSize), big.NewInt(-1)) - } - }) -} - -func BenchmarkKZGCommit(b *testing.B) { - - b.Run("real SRS", func(b *testing.B) { - srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) - assert.NoError(b, err) - // random polynomial - p := randomPolynomial(benchSize / 2) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = Commit(p, srs.Pk) - } - }) - b.Run("quick SRS", func(b *testing.B) { - srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), big.NewInt(-1)) - assert.NoError(b, err) - // random polynomial - p := randomPolynomial(benchSize / 2) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = Commit(p, srs.Pk) - } - }) -} - -func BenchmarkDivideByXMinusA(b *testing.B) { - const pSize = 1 << 22 - - // build random polynomial - pol := make([]fr.Element, pSize) - pol[0].SetRandom() - for i := 1; i < pSize; i++ { - pol[i] = pol[i-1] - } - var a, fa fr.Element - a.SetRandom() - fa.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - dividePolyByXminusA(pol, fa, a) - pol = pol[:pSize] - pol[pSize-1] = pol[0] - } -} - -func BenchmarkKZGOpen(b *testing.B) { - srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) - assert.NoError(b, err) - - // random polynomial - p := randomPolynomial(benchSize / 2) - var r fr.Element - r.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = Open(p, r, srs.Pk) - } -} - -func BenchmarkKZGVerify(b *testing.B) { - srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) - assert.NoError(b, err) - - // random polynomial - p := randomPolynomial(benchSize / 2) - var r fr.Element - r.SetRandom() - - // commit - comm, err := Commit(p, srs.Pk) - assert.NoError(b, err) - - // open - openingProof, err := Open(p, r, srs.Pk) - assert.NoError(b, err) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - Verify(&comm, &openingProof, r, srs.Vk) - } -} - -func BenchmarkKZGBatchOpen10(b *testing.B) { - srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) - assert.NoError(b, err) - - // 10 random polynomials - var ps [10][]fr.Element - for i := 0; i < 10; i++ { - ps[i] = randomPolynomial(benchSize / 2) - } - - // commitments - var commitments [10]Digest - for i := 0; i < 10; i++ { - commitments[i], _ = Commit(ps[i], srs.Pk) - } - - // pick a hash function - hf := sha256.New() - - var r fr.Element - r.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - BatchOpenSinglePoint(ps[:], commitments[:], r, hf, srs.Pk) - } -} - -func BenchmarkKZGBatchVerify10(b *testing.B) { - srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) - if err != nil { - b.Fatal(err) - } - - // 10 random polynomials - var ps [10][]fr.Element - for i := 0; i < 10; i++ { - ps[i] = randomPolynomial(benchSize / 2) - } - - // commitments - var commitments [10]Digest - for i := 0; i < 10; i++ { - commitments[i], _ = Commit(ps[i], srs.Pk) - } - - // pick a hash function - hf := sha256.New() - - var r fr.Element - r.SetRandom() - - proof, err := BatchOpenSinglePoint(ps[:], commitments[:], r, hf, srs.Pk) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - BatchVerifySinglePoint(commitments[:], &proof, r, hf, testSrs.Vk) - } -} - -func randomPolynomial(size int) []fr.Element { - f := make([]fr.Element, size) - for i := 0; i < size; i++ { - f[i].SetRandom() - } - return f -} - -func BenchmarkToLagrangeG1(b *testing.B) { - const size = 1 << 14 - - var samplePoints [size]bls12378.G1Affine - fillBenchBasesG1(samplePoints[:]) - b.ResetTimer() - - for i := 0; i < b.N; i++ { - if _, err := ToLagrangeG1(samplePoints[:]); err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkSerializeSRS(b *testing.B) { - // let's create a quick SRS - srs, err := NewSRS(ecc.NextPowerOfTwo(1<<24), big.NewInt(-1)) - if err != nil { - b.Fatal(err) - } - - // now we can benchmark the WriteTo, WriteRawTo and WriteDump methods - b.Run("WriteTo", func(b *testing.B) { - b.ResetTimer() - var buf bytes.Buffer - for i := 0; i < b.N; i++ { - buf.Reset() - _, err := srs.WriteTo(&buf) - if err != nil { - b.Fatal(err) - } - } - }) - - b.Run("WriteRawTo", func(b *testing.B) { - b.ResetTimer() - var buf bytes.Buffer - for i := 0; i < b.N; i++ { - buf.Reset() - _, err := srs.WriteRawTo(&buf) - if err != nil { - b.Fatal(err) - } - } - }) - - b.Run("WriteDump", func(b *testing.B) { - b.ResetTimer() - var buf bytes.Buffer - for i := 0; i < b.N; i++ { - buf.Reset() - if err := srs.WriteDump(&buf); err != nil { - b.Fatal(err) - } - } - }) - -} - -func BenchmarkDeserializeSRS(b *testing.B) { - // let's create a quick SRS - srs, err := NewSRS(ecc.NextPowerOfTwo(1<<24), big.NewInt(-1)) - if err != nil { - b.Fatal(err) - } - - b.Run("UnsafeReadFrom", func(b *testing.B) { - var buf bytes.Buffer - if _, err := srs.WriteRawTo(&buf); err != nil { - b.Fatal(err) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - var newSRS SRS - _, err := newSRS.UnsafeReadFrom(bytes.NewReader(buf.Bytes())) - if err != nil { - b.Fatal(err) - } - } - }) - - b.Run("ReadDump", func(b *testing.B) { - var buf bytes.Buffer - err := srs.WriteDump(&buf) - if err != nil { - b.Fatal(err) - } - data := buf.Bytes() - b.ResetTimer() - for i := 0; i < b.N; i++ { - var newSRS SRS - if err := newSRS.ReadDump(bytes.NewReader(data)); err != nil { - b.Fatal(err) - } - } - }) -} - -func fillBenchBasesG1(samplePoints []bls12378.G1Affine) { - var r big.Int - r.SetString("340444420969191673093399857471996460938405", 10) - samplePoints[0].ScalarMultiplication(&samplePoints[0], &r) - - one := samplePoints[0].X - one.SetOne() - - for i := 1; i < len(samplePoints); i++ { - samplePoints[i].X.Add(&samplePoints[i-1].X, &one) - samplePoints[i].Y.Sub(&samplePoints[i-1].Y, &one) - } -} diff --git a/ecc/bls12-378/kzg/marshal.go b/ecc/bls12-378/kzg/marshal.go deleted file mode 100644 index f3349849c9..0000000000 --- a/ecc/bls12-378/kzg/marshal.go +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package kzg - -import ( - "github.com/consensys/gnark-crypto/ecc/bls12-378" - "io" - - "github.com/consensys/gnark-crypto/utils/unsafe" -) - -// WriteTo writes binary encoding of the ProvingKey -func (pk *ProvingKey) WriteTo(w io.Writer) (int64, error) { - return pk.writeTo(w) -} - -// WriteRawTo writes binary encoding of ProvingKey to w without point compression -func (pk *ProvingKey) WriteRawTo(w io.Writer) (int64, error) { - return pk.writeTo(w, bls12378.RawEncoding()) -} - -func (pk *ProvingKey) writeTo(w io.Writer, options ...func(*bls12378.Encoder)) (int64, error) { - // encode the ProvingKey - enc := bls12378.NewEncoder(w, options...) - if err := enc.Encode(pk.G1); err != nil { - return enc.BytesWritten(), err - } - return enc.BytesWritten(), nil -} - -// WriteRawTo writes binary encoding of VerifyingKey to w without point compression -func (vk *VerifyingKey) WriteRawTo(w io.Writer) (int64, error) { - return vk.writeTo(w, bls12378.RawEncoding()) -} - -// WriteTo writes binary encoding of the VerifyingKey -func (vk *VerifyingKey) WriteTo(w io.Writer) (int64, error) { - return vk.writeTo(w) -} - -func (vk *VerifyingKey) writeTo(w io.Writer, options ...func(*bls12378.Encoder)) (int64, error) { - // encode the VerifyingKey - enc := bls12378.NewEncoder(w, options...) - nLines := 63 - toEncode := make([]interface{}, 0, 4*nLines+3) - toEncode = append(toEncode, &vk.G2[0]) - toEncode = append(toEncode, &vk.G2[1]) - toEncode = append(toEncode, &vk.G1) - for k := 0; k < 2; k++ { - for j := 0; j < 2; j++ { - for i := nLines - 1; i >= 0; i-- { - toEncode = append(toEncode, &vk.Lines[k][j][i].R0) - toEncode = append(toEncode, &vk.Lines[k][j][i].R1) - } - } - } - - for _, v := range toEncode { - if err := enc.Encode(v); err != nil { - return enc.BytesWritten(), err - } - } - - return enc.BytesWritten(), nil -} - -// WriteDump writes the binary encoding of the entire SRS memory representation -// It is meant to be use to achieve fast serialization/deserialization and -// is not compatible with WriteTo / ReadFrom. It does not do any validation -// and doesn't encode points in a canonical form. -// @unsafe: this is platform dependent and may not be compatible with other platforms -// @unstable: the format may change in the future -// If maxPkPoints is provided, the number of points in the ProvingKey will be limited to maxPkPoints -func (srs *SRS) WriteDump(w io.Writer, maxPkPoints ...int) error { - maxG1 := len(srs.Pk.G1) - if len(maxPkPoints) > 0 && maxPkPoints[0] < maxG1 && maxPkPoints[0] > 0 { - maxG1 = maxPkPoints[0] - } - // first we write the VerifyingKey; it is small so we re-use WriteTo - - if _, err := srs.Vk.writeTo(w, bls12378.RawEncoding()); err != nil { - return err - } - - // write the marker - if err := unsafe.WriteMarker(w); err != nil { - return err - } - - // write the slice - return unsafe.WriteSlice(w, srs.Pk.G1[:maxG1]) -} - -// ReadDump deserializes the SRS from a reader, as written by WriteDump -func (srs *SRS) ReadDump(r io.Reader, maxPkPoints ...int) error { - // first we read the VerifyingKey; it is small so we re-use ReadFrom - _, err := srs.Vk.ReadFrom(r) - if err != nil { - return err - } - - // read the marker - if err := unsafe.ReadMarker(r); err != nil { - return err - } - - // read the slice - srs.Pk.G1, _, err = unsafe.ReadSlice[[]bls12378.G1Affine](r, maxPkPoints...) - return err -} - -// WriteTo writes binary encoding of the entire SRS -func (srs *SRS) WriteTo(w io.Writer) (int64, error) { - // encode the SRS - var pn, vn int64 - var err error - if pn, err = srs.Pk.WriteTo(w); err != nil { - return pn, err - } - vn, err = srs.Vk.WriteTo(w) - return pn + vn, err -} - -// WriteRawTo writes binary encoding of the entire SRS without point compression -func (srs *SRS) WriteRawTo(w io.Writer) (int64, error) { - // encode the SRS - var pn, vn int64 - var err error - if pn, err = srs.Pk.WriteRawTo(w); err != nil { - return pn, err - } - vn, err = srs.Vk.WriteRawTo(w) - return pn + vn, err -} - -// ReadFrom decodes ProvingKey data from reader. -func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { - // decode the ProvingKey - dec := bls12378.NewDecoder(r) - if err := dec.Decode(&pk.G1); err != nil { - return dec.BytesRead(), err - } - return dec.BytesRead(), nil -} - -// UnsafeReadFrom decodes ProvingKey data from reader without checking -// that point are in the correct subgroup. -func (pk *ProvingKey) UnsafeReadFrom(r io.Reader) (int64, error) { - // decode the ProvingKey - dec := bls12378.NewDecoder(r, bls12378.NoSubgroupChecks()) - if err := dec.Decode(&pk.G1); err != nil { - return dec.BytesRead(), err - } - return dec.BytesRead(), nil -} - -// ReadFrom decodes VerifyingKey data from reader. -func (vk *VerifyingKey) ReadFrom(r io.Reader) (int64, error) { - // decode the VerifyingKey - dec := bls12378.NewDecoder(r) - nLines := 63 - toDecode := make([]interface{}, 0, 4*nLines+3) - toDecode = append(toDecode, &vk.G2[0]) - toDecode = append(toDecode, &vk.G2[1]) - toDecode = append(toDecode, &vk.G1) - for k := 0; k < 2; k++ { - for j := 0; j < 2; j++ { - for i := nLines - 1; i >= 0; i-- { - toDecode = append(toDecode, &vk.Lines[k][j][i].R0) - toDecode = append(toDecode, &vk.Lines[k][j][i].R1) - } - } - } - - for _, v := range toDecode { - if err := dec.Decode(v); err != nil { - return dec.BytesRead(), err - } - } - - return dec.BytesRead(), nil -} - -// ReadFrom decodes SRS data from reader. -func (srs *SRS) ReadFrom(r io.Reader) (int64, error) { - // decode the VerifyingKey - var pn, vn int64 - var err error - if pn, err = srs.Pk.ReadFrom(r); err != nil { - return pn, err - } - vn, err = srs.Vk.ReadFrom(r) - return pn + vn, err -} - -// UnsafeReadFrom decodes SRS data from reader without sub group checks -func (srs *SRS) UnsafeReadFrom(r io.Reader) (int64, error) { - // decode the VerifyingKey - var pn, vn int64 - var err error - if pn, err = srs.Pk.UnsafeReadFrom(r); err != nil { - return pn, err - } - vn, err = srs.Vk.ReadFrom(r) - return pn + vn, err -} - -// WriteTo writes binary encoding of a OpeningProof -func (proof *OpeningProof) WriteTo(w io.Writer) (int64, error) { - enc := bls12378.NewEncoder(w) - - toEncode := []interface{}{ - &proof.H, - &proof.ClaimedValue, - } - - for _, v := range toEncode { - if err := enc.Encode(v); err != nil { - return enc.BytesWritten(), err - } - } - - return enc.BytesWritten(), nil -} - -// ReadFrom decodes OpeningProof data from reader. -func (proof *OpeningProof) ReadFrom(r io.Reader) (int64, error) { - dec := bls12378.NewDecoder(r) - - toDecode := []interface{}{ - &proof.H, - &proof.ClaimedValue, - } - - for _, v := range toDecode { - if err := dec.Decode(v); err != nil { - return dec.BytesRead(), err - } - } - - return dec.BytesRead(), nil -} - -// WriteTo writes binary encoding of a BatchOpeningProof -func (proof *BatchOpeningProof) WriteTo(w io.Writer) (int64, error) { - enc := bls12378.NewEncoder(w) - - toEncode := []interface{}{ - &proof.H, - proof.ClaimedValues, - } - - for _, v := range toEncode { - if err := enc.Encode(v); err != nil { - return enc.BytesWritten(), err - } - } - - return enc.BytesWritten(), nil -} - -// ReadFrom decodes BatchOpeningProof data from reader. -func (proof *BatchOpeningProof) ReadFrom(r io.Reader) (int64, error) { - dec := bls12378.NewDecoder(r) - toDecode := []interface{}{ - &proof.H, - &proof.ClaimedValues, - } - - for _, v := range toDecode { - if err := dec.Decode(v); err != nil { - return dec.BytesRead(), err - } - } - - return dec.BytesRead(), nil -} diff --git a/ecc/bls12-378/kzg/utils.go b/ecc/bls12-378/kzg/utils.go deleted file mode 100644 index 4060548af4..0000000000 --- a/ecc/bls12-378/kzg/utils.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package kzg - -import ( - "fmt" - "math/big" - "math/bits" - "runtime" - - "github.com/consensys/gnark-crypto/ecc" - curve "github.com/consensys/gnark-crypto/ecc/bls12-378" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/internal/parallel" -) - -// ToLagrangeG1 in place transform of coeffs canonical form into Lagrange form. -// From the formula Lᵢ(τ) = 1/n∑_{j> nn - if irev > i { - a[i], a[irev] = a[irev], a[i] - } - } -} - -func butterflyG1(a *curve.G1Jac, b *curve.G1Jac) { - t := *a - a.AddAssign(b) - t.SubAssign(b) - b.Set(&t) -} - -func difFFTG1(a []curve.G1Jac, twiddles []*big.Int, stage, maxSplits int, chDone chan struct{}) { - if chDone != nil { - defer close(chDone) - } - - n := len(a) - if n == 1 { - return - } - m := n >> 1 - - butterflyG1(&a[0], &a[m]) - // stage determines the stride - // if stage == 0, then we use 1, w, w**2, w**3, w**4, w**5, w**6, ... - // if stage == 1, then we use 1, w**2, w**4, w**6, ... that is, indexes 0, 2, 4, 6, ... of stage 0 - // if stage == 2, then we use 1, w**4, w**8, w**12, ... that is indexes 0, 4, 8, 12, ... of stage 0 - stride := 1 << stage - - const butterflyThreshold = 8 - if m >= butterflyThreshold { - // 1 << stage == estimated used CPUs - numCPU := runtime.NumCPU() / (1 << (stage)) - parallel.Execute(m, func(start, end int) { - if start == 0 { - start = 1 - } - j := start * stride - for i := start; i < end; i++ { - butterflyG1(&a[i], &a[i+m]) - a[i+m].ScalarMultiplication(&a[i+m], twiddles[j]) - j += stride - } - }, numCPU) - } else { - j := stride - for i := 1; i < m; i++ { - butterflyG1(&a[i], &a[i+m]) - a[i+m].ScalarMultiplication(&a[i+m], twiddles[j]) - j += stride - } - } - - if m == 1 { - return - } - - nextStage := stage + 1 - if stage < maxSplits { - chDone := make(chan struct{}, 1) - go difFFTG1(a[m:n], twiddles, nextStage, maxSplits, chDone) - difFFTG1(a[0:m], twiddles, nextStage, maxSplits, nil) - <-chDone - } else { - difFFTG1(a[0:m], twiddles, nextStage, maxSplits, nil) - difFFTG1(a[m:n], twiddles, nextStage, maxSplits, nil) - } -} diff --git a/ecc/bls12-378/marshal.go b/ecc/bls12-378/marshal.go deleted file mode 100644 index 5bfd945ad1..0000000000 --- a/ecc/bls12-378/marshal.go +++ /dev/null @@ -1,1297 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bls12378 - -import ( - "encoding/binary" - "errors" - "io" - "reflect" - "sync/atomic" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/internal/fptower" - "github.com/consensys/gnark-crypto/internal/parallel" -) - -// To encode G1Affine and G2Affine points, we mask the most significant bits with these bits to specify without ambiguity -// metadata needed for point (de)compression -// we follow the BLS12-381 style encoding as specified in ZCash and now IETF -// see https://datatracker.ietf.org/doc/draft-irtf-cfrg-pairing-friendly-curves/11/ -// Appendix C. ZCash serialization format for BLS12_381 -const ( - mMask byte = 0b111 << 5 - mUncompressed byte = 0b000 << 5 - _ byte = 0b001 << 5 // invalid - mUncompressedInfinity byte = 0b010 << 5 - _ byte = 0b011 << 5 // invalid - mCompressedSmallest byte = 0b100 << 5 - mCompressedLargest byte = 0b101 << 5 - mCompressedInfinity byte = 0b110 << 5 - _ byte = 0b111 << 5 // invalid -) - -// SizeOfGT represents the size in bytes that a GT element need in binary form -const SizeOfGT = fptower.SizeOfGT - -var ( - ErrInvalidInfinityEncoding = errors.New("invalid infinity point encoding") - ErrInvalidEncoding = errors.New("invalid point encoding") -) - -// Encoder writes bls12-378 object values to an output stream -type Encoder struct { - w io.Writer - n int64 // written bytes - raw bool // raw vs compressed encoding -} - -// Decoder reads bls12-378 object values from an inbound stream -type Decoder struct { - r io.Reader - n int64 // read bytes - subGroupCheck bool // default to true -} - -// NewDecoder returns a binary decoder supporting curve bls12-378 objects in both -// compressed and uncompressed (raw) forms -func NewDecoder(r io.Reader, options ...func(*Decoder)) *Decoder { - d := &Decoder{r: r, subGroupCheck: true} - - for _, o := range options { - o(d) - } - - return d -} - -// Decode reads the binary encoding of v from the stream -// type must be *uint64, *fr.Element, *fp.Element, *G1Affine, *G2Affine, *[]G1Affine or *[]G2Affine -func (dec *Decoder) Decode(v interface{}) (err error) { - rv := reflect.ValueOf(v) - if v == nil || rv.Kind() != reflect.Ptr || rv.IsNil() || !rv.Elem().CanSet() { - return errors.New("bls12-378 decoder: unsupported type, need pointer") - } - - // implementation note: code is a bit verbose (abusing code generation), but minimize allocations on the heap - // in particular, careful attention must be given to usage of Bytes() method on Elements and Points - // that return an array (not a slice) of bytes. Using this is beneficial to minimize memory allocations - // in very large (de)serialization upstream in gnark. - // (but detrimental to code readability here) - - var read64 int64 - if vf, ok := v.(io.ReaderFrom); ok { - read64, err = vf.ReadFrom(dec.r) - dec.n += read64 - return - } - - var buf [SizeOfG2AffineUncompressed]byte - var read int - var sliceLen uint32 - - switch t := v.(type) { - case *[][]uint64: - if sliceLen, err = dec.readUint32(); err != nil { - return - } - *t = make([][]uint64, sliceLen) - - for i := range *t { - if sliceLen, err = dec.readUint32(); err != nil { - return - } - (*t)[i] = make([]uint64, sliceLen) - for j := range (*t)[i] { - if (*t)[i][j], err = dec.readUint64(); err != nil { - return - } - } - } - return - case *[]uint64: - if sliceLen, err = dec.readUint32(); err != nil { - return - } - *t = make([]uint64, sliceLen) - for i := range *t { - if (*t)[i], err = dec.readUint64(); err != nil { - return - } - } - return - case *fr.Element: - read, err = io.ReadFull(dec.r, buf[:fr.Bytes]) - dec.n += int64(read) - if err != nil { - return - } - err = t.SetBytesCanonical(buf[:fr.Bytes]) - return - case *fp.Element: - read, err = io.ReadFull(dec.r, buf[:fp.Bytes]) - dec.n += int64(read) - if err != nil { - return - } - err = t.SetBytesCanonical(buf[:fp.Bytes]) - return - case *[]fr.Element: - read64, err = (*fr.Vector)(t).ReadFrom(dec.r) - dec.n += read64 - return - case *[]fp.Element: - read64, err = (*fp.Vector)(t).ReadFrom(dec.r) - dec.n += read64 - return - case *[][]fr.Element: - if sliceLen, err = dec.readUint32(); err != nil { - return - } - if len(*t) != int(sliceLen) { - *t = make([][]fr.Element, sliceLen) - } - for i := range *t { - read64, err = (*fr.Vector)(&(*t)[i]).ReadFrom(dec.r) - dec.n += read64 - } - return - case *G1Affine: - // we start by reading compressed point size, if metadata tells us it is uncompressed, we read more. - read, err = io.ReadFull(dec.r, buf[:SizeOfG1AffineCompressed]) - dec.n += int64(read) - if err != nil { - return - } - nbBytes := SizeOfG1AffineCompressed - - // 111, 011, 001 --> invalid mask - if isMaskInvalid(buf[0]) { - err = ErrInvalidEncoding - return - } - - // most significant byte contains metadata - if !isCompressed(buf[0]) { - nbBytes = SizeOfG1AffineUncompressed - // we read more. - read, err = io.ReadFull(dec.r, buf[SizeOfG1AffineCompressed:SizeOfG1AffineUncompressed]) - dec.n += int64(read) - if err != nil { - return - } - } - _, err = t.setBytes(buf[:nbBytes], dec.subGroupCheck) - return - case *G2Affine: - // we start by reading compressed point size, if metadata tells us it is uncompressed, we read more. - read, err = io.ReadFull(dec.r, buf[:SizeOfG2AffineCompressed]) - dec.n += int64(read) - if err != nil { - return - } - nbBytes := SizeOfG2AffineCompressed - - // 111, 011, 001 --> invalid mask - if isMaskInvalid(buf[0]) { - err = ErrInvalidEncoding - return - } - - // most significant byte contains metadata - if !isCompressed(buf[0]) { - nbBytes = SizeOfG2AffineUncompressed - // we read more. - read, err = io.ReadFull(dec.r, buf[SizeOfG2AffineCompressed:SizeOfG2AffineUncompressed]) - dec.n += int64(read) - if err != nil { - return - } - } - _, err = t.setBytes(buf[:nbBytes], dec.subGroupCheck) - return - case *[]G1Affine: - sliceLen, err = dec.readUint32() - if err != nil { - return - } - if len(*t) != int(sliceLen) || *t == nil { - *t = make([]G1Affine, sliceLen) - } - compressed := make([]bool, sliceLen) - for i := 0; i < len(*t); i++ { - - // we start by reading compressed point size, if metadata tells us it is uncompressed, we read more. - read, err = io.ReadFull(dec.r, buf[:SizeOfG1AffineCompressed]) - dec.n += int64(read) - if err != nil { - return - } - nbBytes := SizeOfG1AffineCompressed - - // 111, 011, 001 --> invalid mask - if isMaskInvalid(buf[0]) { - err = ErrInvalidEncoding - return - } - - // most significant byte contains metadata - if !isCompressed(buf[0]) { - nbBytes = SizeOfG1AffineUncompressed - // we read more. - read, err = io.ReadFull(dec.r, buf[SizeOfG1AffineCompressed:SizeOfG1AffineUncompressed]) - dec.n += int64(read) - if err != nil { - return - } - _, err = (*t)[i].setBytes(buf[:nbBytes], false) - if err != nil { - return - } - } else { - var r bool - if r, err = (*t)[i].unsafeSetCompressedBytes(buf[:nbBytes]); err != nil { - return - } - compressed[i] = !r - } - } - var nbErrs uint64 - parallel.Execute(len(compressed), func(start, end int) { - for i := start; i < end; i++ { - if compressed[i] { - if err := (*t)[i].unsafeComputeY(dec.subGroupCheck); err != nil { - atomic.AddUint64(&nbErrs, 1) - } - } else if dec.subGroupCheck { - if !(*t)[i].IsInSubGroup() { - atomic.AddUint64(&nbErrs, 1) - } - } - } - }) - if nbErrs != 0 { - return errors.New("point decompression failed") - } - - return nil - case *[]G2Affine: - sliceLen, err = dec.readUint32() - if err != nil { - return - } - if len(*t) != int(sliceLen) { - *t = make([]G2Affine, sliceLen) - } - compressed := make([]bool, sliceLen) - for i := 0; i < len(*t); i++ { - - // we start by reading compressed point size, if metadata tells us it is uncompressed, we read more. - read, err = io.ReadFull(dec.r, buf[:SizeOfG2AffineCompressed]) - dec.n += int64(read) - if err != nil { - return - } - nbBytes := SizeOfG2AffineCompressed - - // 111, 011, 001 --> invalid mask - if isMaskInvalid(buf[0]) { - err = ErrInvalidEncoding - return - } - - // most significant byte contains metadata - if !isCompressed(buf[0]) { - nbBytes = SizeOfG2AffineUncompressed - // we read more. - read, err = io.ReadFull(dec.r, buf[SizeOfG2AffineCompressed:SizeOfG2AffineUncompressed]) - dec.n += int64(read) - if err != nil { - return - } - _, err = (*t)[i].setBytes(buf[:nbBytes], false) - if err != nil { - return - } - } else { - var r bool - if r, err = (*t)[i].unsafeSetCompressedBytes(buf[:nbBytes]); err != nil { - return - } - compressed[i] = !r - } - } - var nbErrs uint64 - parallel.Execute(len(compressed), func(start, end int) { - for i := start; i < end; i++ { - if compressed[i] { - if err := (*t)[i].unsafeComputeY(dec.subGroupCheck); err != nil { - atomic.AddUint64(&nbErrs, 1) - } - } else if dec.subGroupCheck { - if !(*t)[i].IsInSubGroup() { - atomic.AddUint64(&nbErrs, 1) - } - } - } - }) - if nbErrs != 0 { - return errors.New("point decompression failed") - } - - return nil - default: - n := binary.Size(t) - if n == -1 { - return errors.New("bls12-378 encoder: unsupported type") - } - err = binary.Read(dec.r, binary.BigEndian, t) - if err == nil { - dec.n += int64(n) - } - return - } -} - -// BytesRead return total bytes read from reader -func (dec *Decoder) BytesRead() int64 { - return dec.n -} - -func (dec *Decoder) readUint32() (r uint32, err error) { - var read int - var buf [4]byte - read, err = io.ReadFull(dec.r, buf[:4]) - dec.n += int64(read) - if err != nil { - return - } - r = binary.BigEndian.Uint32(buf[:4]) - return -} - -func (dec *Decoder) readUint64() (r uint64, err error) { - var read int - var buf [8]byte - read, err = io.ReadFull(dec.r, buf[:]) - dec.n += int64(read) - if err != nil { - return - } - r = binary.BigEndian.Uint64(buf[:]) - return -} - -// isMaskInvalid returns true if the mask is invalid -func isMaskInvalid(msb byte) bool { - mData := msb & mMask - return ((mData == (0b111 << 5)) || (mData == (0b011 << 5)) || (mData == (0b001 << 5))) -} - -func isCompressed(msb byte) bool { - mData := msb & mMask - return !((mData == mUncompressed) || (mData == mUncompressedInfinity)) -} - -// NewEncoder returns a binary encoder supporting curve bls12-378 objects -func NewEncoder(w io.Writer, options ...func(*Encoder)) *Encoder { - // default settings - enc := &Encoder{ - w: w, - n: 0, - raw: false, - } - - // handle options - for _, option := range options { - option(enc) - } - - return enc -} - -// Encode writes the binary encoding of v to the stream -// type must be uint64, *fr.Element, *fp.Element, *G1Affine, *G2Affine, []G1Affine or []G2Affine -func (enc *Encoder) Encode(v interface{}) (err error) { - if enc.raw { - return enc.encodeRaw(v) - } - return enc.encode(v) -} - -// BytesWritten return total bytes written on writer -func (enc *Encoder) BytesWritten() int64 { - return enc.n -} - -// RawEncoding returns an option to use in NewEncoder(...) which sets raw encoding mode to true -// points will not be compressed using this option -func RawEncoding() func(*Encoder) { - return func(enc *Encoder) { - enc.raw = true - } -} - -// NoSubgroupChecks returns an option to use in NewDecoder(...) which disable subgroup checks on the points -// the decoder will read. Use with caution, as crafted points from an untrusted source can lead to crypto-attacks. -func NoSubgroupChecks() func(*Decoder) { - return func(dec *Decoder) { - dec.subGroupCheck = false - } -} - -// isZeroed checks that the provided bytes are at 0 -func isZeroed(firstByte byte, buf []byte) bool { - if firstByte != 0 { - return false - } - for _, b := range buf { - if b != 0 { - return false - } - } - return true -} - -func (enc *Encoder) encode(v interface{}) (err error) { - rv := reflect.ValueOf(v) - if v == nil || (rv.Kind() == reflect.Ptr && rv.IsNil()) { - return errors.New(" encoder: can't encode ") - } - - // implementation note: code is a bit verbose (abusing code generation), but minimize allocations on the heap - - var written64 int64 - if vw, ok := v.(io.WriterTo); ok { - written64, err = vw.WriteTo(enc.w) - enc.n += written64 - return - } - - var written int - - switch t := v.(type) { - case []uint64: - return enc.writeUint64Slice(t) - case [][]uint64: - return enc.writeUint64SliceSlice(t) - case *fr.Element: - buf := t.Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case *fp.Element: - buf := t.Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case *G1Affine: - buf := t.Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case *G2Affine: - buf := t.Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case fr.Vector: - written64, err = t.WriteTo(enc.w) - enc.n += written64 - return - case fp.Vector: - written64, err = t.WriteTo(enc.w) - enc.n += written64 - return - case []fr.Element: - written64, err = (*fr.Vector)(&t).WriteTo(enc.w) - enc.n += written64 - return - case []fp.Element: - written64, err = (*fp.Vector)(&t).WriteTo(enc.w) - enc.n += written64 - return - case [][]fr.Element: - // write slice length - if err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))); err != nil { - return - } - enc.n += 4 - for i := range t { - written64, err = (*fr.Vector)(&t[i]).WriteTo(enc.w) - enc.n += written64 - } - return - case []G1Affine: - // write slice length - err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))) - if err != nil { - return - } - enc.n += 4 - - var buf [SizeOfG1AffineCompressed]byte - - for i := 0; i < len(t); i++ { - buf = t[i].Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - if err != nil { - return - } - } - return nil - case []G2Affine: - // write slice length - err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))) - if err != nil { - return - } - enc.n += 4 - - var buf [SizeOfG2AffineCompressed]byte - - for i := 0; i < len(t); i++ { - buf = t[i].Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - if err != nil { - return - } - } - return nil - default: - n := binary.Size(t) - if n == -1 { - return errors.New(" encoder: unsupported type") - } - err = binary.Write(enc.w, binary.BigEndian, t) - enc.n += int64(n) - return - } -} - -func (enc *Encoder) encodeRaw(v interface{}) (err error) { - rv := reflect.ValueOf(v) - if v == nil || (rv.Kind() == reflect.Ptr && rv.IsNil()) { - return errors.New(" encoder: can't encode ") - } - - // implementation note: code is a bit verbose (abusing code generation), but minimize allocations on the heap - - var written64 int64 - if vw, ok := v.(io.WriterTo); ok { - written64, err = vw.WriteTo(enc.w) - enc.n += written64 - return - } - - var written int - - switch t := v.(type) { - case []uint64: - return enc.writeUint64Slice(t) - case [][]uint64: - return enc.writeUint64SliceSlice(t) - case *fr.Element: - buf := t.Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case *fp.Element: - buf := t.Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case *G1Affine: - buf := t.RawBytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case *G2Affine: - buf := t.RawBytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case fr.Vector: - written64, err = t.WriteTo(enc.w) - enc.n += written64 - return - case fp.Vector: - written64, err = t.WriteTo(enc.w) - enc.n += written64 - return - case []fr.Element: - written64, err = (*fr.Vector)(&t).WriteTo(enc.w) - enc.n += written64 - return - case []fp.Element: - written64, err = (*fp.Vector)(&t).WriteTo(enc.w) - enc.n += written64 - return - case [][]fr.Element: - // write slice length - if err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))); err != nil { - return - } - enc.n += 4 - for i := range t { - written64, err = (*fr.Vector)(&t[i]).WriteTo(enc.w) - enc.n += written64 - } - return - case []G1Affine: - // write slice length - err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))) - if err != nil { - return - } - enc.n += 4 - - var buf [SizeOfG1AffineUncompressed]byte - - for i := 0; i < len(t); i++ { - buf = t[i].RawBytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - if err != nil { - return - } - } - return nil - case []G2Affine: - // write slice length - err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))) - if err != nil { - return - } - enc.n += 4 - - var buf [SizeOfG2AffineUncompressed]byte - - for i := 0; i < len(t); i++ { - buf = t[i].RawBytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - if err != nil { - return - } - } - return nil - default: - n := binary.Size(t) - if n == -1 { - return errors.New(" encoder: unsupported type") - } - err = binary.Write(enc.w, binary.BigEndian, t) - enc.n += int64(n) - return - } -} - -func (enc *Encoder) writeUint64Slice(t []uint64) (err error) { - if err = enc.writeUint32(uint32(len(t))); err != nil { - return - } - for i := range t { - if err = enc.writeUint64(t[i]); err != nil { - return - } - } - return nil -} - -func (enc *Encoder) writeUint64SliceSlice(t [][]uint64) (err error) { - if err = enc.writeUint32(uint32(len(t))); err != nil { - return - } - for i := range t { - if err = enc.writeUint32(uint32(len(t[i]))); err != nil { - return - } - for j := range t[i] { - if err = enc.writeUint64(t[i][j]); err != nil { - return - } - } - } - return nil -} - -func (enc *Encoder) writeUint64(a uint64) error { - var buff [64 / 8]byte - binary.BigEndian.PutUint64(buff[:], a) - written, err := enc.w.Write(buff[:]) - enc.n += int64(written) - return err -} - -func (enc *Encoder) writeUint32(a uint32) error { - var buff [32 / 8]byte - binary.BigEndian.PutUint32(buff[:], a) - written, err := enc.w.Write(buff[:]) - enc.n += int64(written) - return err -} - -// SizeOfG1AffineCompressed represents the size in bytes that a G1Affine need in binary form, compressed -const SizeOfG1AffineCompressed = 48 - -// SizeOfG1AffineUncompressed represents the size in bytes that a G1Affine need in binary form, uncompressed -const SizeOfG1AffineUncompressed = SizeOfG1AffineCompressed * 2 - -// Marshal converts p to a byte slice (without point compression) -func (p *G1Affine) Marshal() []byte { - b := p.RawBytes() - return b[:] -} - -// Unmarshal is an alias to SetBytes() -func (p *G1Affine) Unmarshal(buf []byte) error { - _, err := p.SetBytes(buf) - return err -} - -// Bytes returns binary representation of p -// will store X coordinate in regular form and a parity bit -// we follow the BLS12-381 style encoding as specified in ZCash and now IETF -// -// The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form. -// -// The second-most significant bit indicates that the point is at infinity. If this bit is set, the remaining bits of the group element's encoding should be set to zero. -// -// The third-most significant bit is set if (and only if) this point is in compressed form and it is not the point at infinity and its y-coordinate is the lexicographically largest of the two associated with the encoded x-coordinate. -func (p *G1Affine) Bytes() (res [SizeOfG1AffineCompressed]byte) { - - // check if p is infinity point - if p.X.IsZero() && p.Y.IsZero() { - res[0] = mCompressedInfinity - return - } - - msbMask := mCompressedSmallest - // compressed, we need to know if Y is lexicographically bigger than -Y - // if p.Y ">" -p.Y - if p.Y.LexicographicallyLargest() { - msbMask = mCompressedLargest - } - - // we store X and mask the most significant word with our metadata mask - fp.BigEndian.PutElement((*[fp.Bytes]byte)(res[0:0+fp.Bytes]), p.X) - - res[0] |= msbMask - - return -} - -// RawBytes returns binary representation of p (stores X and Y coordinate) -// see Bytes() for a compressed representation -func (p *G1Affine) RawBytes() (res [SizeOfG1AffineUncompressed]byte) { - - // check if p is infinity point - if p.X.IsZero() && p.Y.IsZero() { - - res[0] = mUncompressedInfinity - - return - } - - // not compressed - // we store the Y coordinate - fp.BigEndian.PutElement((*[fp.Bytes]byte)(res[48:48+fp.Bytes]), p.Y) - - // we store X and mask the most significant word with our metadata mask - fp.BigEndian.PutElement((*[fp.Bytes]byte)(res[0:0+fp.Bytes]), p.X) - - res[0] |= mUncompressed - - return -} - -// SetBytes sets p from binary representation in buf and returns number of consumed bytes -// -// bytes in buf must match either RawBytes() or Bytes() output -// -// if buf is too short io.ErrShortBuffer is returned -// -// if buf contains compressed representation (output from Bytes()) and we're unable to compute -// the Y coordinate (i.e the square root doesn't exist) this function returns an error -// -// this check if the resulting point is on the curve and in the correct subgroup -func (p *G1Affine) SetBytes(buf []byte) (int, error) { - return p.setBytes(buf, true) -} - -func (p *G1Affine) setBytes(buf []byte, subGroupCheck bool) (int, error) { - if len(buf) < SizeOfG1AffineCompressed { - return 0, io.ErrShortBuffer - } - - // most significant byte - mData := buf[0] & mMask - - // 111, 011, 001 --> invalid mask - if isMaskInvalid(mData) { - return 0, ErrInvalidEncoding - } - - // check buffer size - if (mData == mUncompressed) || (mData == mUncompressedInfinity) { - if len(buf) < SizeOfG1AffineUncompressed { - return 0, io.ErrShortBuffer - } - } - - // infinity encoded, we still check that the buffer is full of zeroes. - if mData == mCompressedInfinity { - if !isZeroed(buf[0] & ^mMask, buf[1:SizeOfG1AffineCompressed]) { - return 0, ErrInvalidInfinityEncoding - } - p.X.SetZero() - p.Y.SetZero() - return SizeOfG1AffineCompressed, nil - } - if mData == mUncompressedInfinity { - if !isZeroed(buf[0] & ^mMask, buf[1:SizeOfG1AffineUncompressed]) { - return 0, ErrInvalidInfinityEncoding - } - p.X.SetZero() - p.Y.SetZero() - return SizeOfG1AffineUncompressed, nil - } - - // uncompressed point - if mData == mUncompressed { - // read X and Y coordinates - if err := p.X.SetBytesCanonical(buf[:fp.Bytes]); err != nil { - return 0, err - } - if err := p.Y.SetBytesCanonical(buf[fp.Bytes : fp.Bytes*2]); err != nil { - return 0, err - } - - // subgroup check - if subGroupCheck && !p.IsInSubGroup() { - return 0, errors.New("invalid point: subgroup check failed") - } - - return SizeOfG1AffineUncompressed, nil - } - - // we have a compressed coordinate - // we need to - // 1. copy the buffer (to keep this method thread safe) - // 2. we need to solve the curve equation to compute Y - - var bufX [fp.Bytes]byte - copy(bufX[:fp.Bytes], buf[:fp.Bytes]) - bufX[0] &= ^mMask - - // read X coordinate - if err := p.X.SetBytesCanonical(bufX[:fp.Bytes]); err != nil { - return 0, err - } - - var YSquared, Y fp.Element - - YSquared.Square(&p.X).Mul(&YSquared, &p.X) - YSquared.Add(&YSquared, &bCurveCoeff) - if Y.Sqrt(&YSquared) == nil { - return 0, errors.New("invalid compressed coordinate: square root doesn't exist") - } - - if Y.LexicographicallyLargest() { - // Y ">" -Y - if mData == mCompressedSmallest { - Y.Neg(&Y) - } - } else { - // Y "<=" -Y - if mData == mCompressedLargest { - Y.Neg(&Y) - } - } - - p.Y = Y - - // subgroup check - if subGroupCheck && !p.IsInSubGroup() { - return 0, errors.New("invalid point: subgroup check failed") - } - - return SizeOfG1AffineCompressed, nil -} - -// unsafeComputeY called by Decoder when processing slices of compressed point in parallel (step 2) -// it computes the Y coordinate from the already set X coordinate and is compute intensive -func (p *G1Affine) unsafeComputeY(subGroupCheck bool) error { - // stored in unsafeSetCompressedBytes - - mData := byte(p.Y[0]) - - // we have a compressed coordinate, we need to solve the curve equation to compute Y - var YSquared, Y fp.Element - - YSquared.Square(&p.X).Mul(&YSquared, &p.X) - YSquared.Add(&YSquared, &bCurveCoeff) - if Y.Sqrt(&YSquared) == nil { - return errors.New("invalid compressed coordinate: square root doesn't exist") - } - - if Y.LexicographicallyLargest() { - // Y ">" -Y - if mData == mCompressedSmallest { - Y.Neg(&Y) - } - } else { - // Y "<=" -Y - if mData == mCompressedLargest { - Y.Neg(&Y) - } - } - - p.Y = Y - - // subgroup check - if subGroupCheck && !p.IsInSubGroup() { - return errors.New("invalid point: subgroup check failed") - } - - return nil -} - -// unsafeSetCompressedBytes is called by Decoder when processing slices of compressed point in parallel (step 1) -// assumes buf[:8] mask is set to compressed -// returns true if point is infinity and need no further processing -// it sets X coordinate and uses Y for scratch space to store decompression metadata -func (p *G1Affine) unsafeSetCompressedBytes(buf []byte) (isInfinity bool, err error) { - - // read the most significant byte - mData := buf[0] & mMask - - if mData == mCompressedInfinity { - isInfinity = true - if !isZeroed(buf[0] & ^mMask, buf[1:SizeOfG1AffineCompressed]) { - return isInfinity, ErrInvalidInfinityEncoding - } - p.X.SetZero() - p.Y.SetZero() - return isInfinity, nil - } - - // we need to copy the input buffer (to keep this method thread safe) - var bufX [fp.Bytes]byte - copy(bufX[:fp.Bytes], buf[:fp.Bytes]) - bufX[0] &= ^mMask - - // read X coordinate - if err := p.X.SetBytesCanonical(bufX[:fp.Bytes]); err != nil { - return false, err - } - // store mData in p.Y[0] - p.Y[0] = uint64(mData) - - // recomputing Y will be done asynchronously - return isInfinity, nil -} - -// SizeOfG2AffineCompressed represents the size in bytes that a G2Affine need in binary form, compressed -const SizeOfG2AffineCompressed = 48 * 2 - -// SizeOfG2AffineUncompressed represents the size in bytes that a G2Affine need in binary form, uncompressed -const SizeOfG2AffineUncompressed = SizeOfG2AffineCompressed * 2 - -// Marshal converts p to a byte slice (without point compression) -func (p *G2Affine) Marshal() []byte { - b := p.RawBytes() - return b[:] -} - -// Unmarshal is an alias to SetBytes() -func (p *G2Affine) Unmarshal(buf []byte) error { - _, err := p.SetBytes(buf) - return err -} - -// Bytes returns binary representation of p -// will store X coordinate in regular form and a parity bit -// we follow the BLS12-381 style encoding as specified in ZCash and now IETF -// -// The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form. -// -// The second-most significant bit indicates that the point is at infinity. If this bit is set, the remaining bits of the group element's encoding should be set to zero. -// -// The third-most significant bit is set if (and only if) this point is in compressed form and it is not the point at infinity and its y-coordinate is the lexicographically largest of the two associated with the encoded x-coordinate. -func (p *G2Affine) Bytes() (res [SizeOfG2AffineCompressed]byte) { - - // check if p is infinity point - if p.X.IsZero() && p.Y.IsZero() { - res[0] = mCompressedInfinity - return - } - - msbMask := mCompressedSmallest - // compressed, we need to know if Y is lexicographically bigger than -Y - // if p.Y ">" -p.Y - if p.Y.LexicographicallyLargest() { - msbMask = mCompressedLargest - } - - // we store X and mask the most significant word with our metadata mask - // p.X.A1 | p.X.A0 - fp.BigEndian.PutElement((*[fp.Bytes]byte)(res[48:48+fp.Bytes]), p.X.A0) - fp.BigEndian.PutElement((*[fp.Bytes]byte)(res[0:0+fp.Bytes]), p.X.A1) - - res[0] |= msbMask - - return -} - -// RawBytes returns binary representation of p (stores X and Y coordinate) -// see Bytes() for a compressed representation -func (p *G2Affine) RawBytes() (res [SizeOfG2AffineUncompressed]byte) { - - // check if p is infinity point - if p.X.IsZero() && p.Y.IsZero() { - - res[0] = mUncompressedInfinity - - return - } - - // not compressed - // we store the Y coordinate - // p.Y.A1 | p.Y.A0 - fp.BigEndian.PutElement((*[fp.Bytes]byte)(res[144:144+fp.Bytes]), p.Y.A0) - fp.BigEndian.PutElement((*[fp.Bytes]byte)(res[96:96+fp.Bytes]), p.Y.A1) - - // we store X and mask the most significant word with our metadata mask - // p.X.A1 | p.X.A0 - fp.BigEndian.PutElement((*[fp.Bytes]byte)(res[0:0+fp.Bytes]), p.X.A1) - fp.BigEndian.PutElement((*[fp.Bytes]byte)(res[48:48+fp.Bytes]), p.X.A0) - - res[0] |= mUncompressed - - return -} - -// SetBytes sets p from binary representation in buf and returns number of consumed bytes -// -// bytes in buf must match either RawBytes() or Bytes() output -// -// if buf is too short io.ErrShortBuffer is returned -// -// if buf contains compressed representation (output from Bytes()) and we're unable to compute -// the Y coordinate (i.e the square root doesn't exist) this function returns an error -// -// this check if the resulting point is on the curve and in the correct subgroup -func (p *G2Affine) SetBytes(buf []byte) (int, error) { - return p.setBytes(buf, true) -} - -func (p *G2Affine) setBytes(buf []byte, subGroupCheck bool) (int, error) { - if len(buf) < SizeOfG2AffineCompressed { - return 0, io.ErrShortBuffer - } - - // most significant byte - mData := buf[0] & mMask - - // 111, 011, 001 --> invalid mask - if isMaskInvalid(mData) { - return 0, ErrInvalidEncoding - } - - // check buffer size - if (mData == mUncompressed) || (mData == mUncompressedInfinity) { - if len(buf) < SizeOfG2AffineUncompressed { - return 0, io.ErrShortBuffer - } - } - - // infinity encoded, we still check that the buffer is full of zeroes. - if mData == mCompressedInfinity { - if !isZeroed(buf[0] & ^mMask, buf[1:SizeOfG2AffineCompressed]) { - return 0, ErrInvalidInfinityEncoding - } - p.X.SetZero() - p.Y.SetZero() - return SizeOfG2AffineCompressed, nil - } - if mData == mUncompressedInfinity { - if !isZeroed(buf[0] & ^mMask, buf[1:SizeOfG2AffineUncompressed]) { - return 0, ErrInvalidInfinityEncoding - } - p.X.SetZero() - p.Y.SetZero() - return SizeOfG2AffineUncompressed, nil - } - - // uncompressed point - if mData == mUncompressed { - // read X and Y coordinates - // p.X.A1 | p.X.A0 - if err := p.X.A1.SetBytesCanonical(buf[:fp.Bytes]); err != nil { - return 0, err - } - if err := p.X.A0.SetBytesCanonical(buf[fp.Bytes : fp.Bytes*2]); err != nil { - return 0, err - } - // p.Y.A1 | p.Y.A0 - if err := p.Y.A1.SetBytesCanonical(buf[fp.Bytes*2 : fp.Bytes*3]); err != nil { - return 0, err - } - if err := p.Y.A0.SetBytesCanonical(buf[fp.Bytes*3 : fp.Bytes*4]); err != nil { - return 0, err - } - - // subgroup check - if subGroupCheck && !p.IsInSubGroup() { - return 0, errors.New("invalid point: subgroup check failed") - } - - return SizeOfG2AffineUncompressed, nil - } - - // we have a compressed coordinate - // we need to - // 1. copy the buffer (to keep this method thread safe) - // 2. we need to solve the curve equation to compute Y - - var bufX [fp.Bytes]byte - copy(bufX[:fp.Bytes], buf[:fp.Bytes]) - bufX[0] &= ^mMask - - // read X coordinate - // p.X.A1 | p.X.A0 - if err := p.X.A1.SetBytesCanonical(bufX[:fp.Bytes]); err != nil { - return 0, err - } - if err := p.X.A0.SetBytesCanonical(buf[fp.Bytes : fp.Bytes*2]); err != nil { - return 0, err - } - - var YSquared, Y fptower.E2 - - YSquared.Square(&p.X).Mul(&YSquared, &p.X) - YSquared.Add(&YSquared, &bTwistCurveCoeff) - if YSquared.Legendre() == -1 { - return 0, errors.New("invalid compressed coordinate: square root doesn't exist") - } - Y.Sqrt(&YSquared) - - if Y.LexicographicallyLargest() { - // Y ">" -Y - if mData == mCompressedSmallest { - Y.Neg(&Y) - } - } else { - // Y "<=" -Y - if mData == mCompressedLargest { - Y.Neg(&Y) - } - } - - p.Y = Y - - // subgroup check - if subGroupCheck && !p.IsInSubGroup() { - return 0, errors.New("invalid point: subgroup check failed") - } - - return SizeOfG2AffineCompressed, nil -} - -// unsafeComputeY called by Decoder when processing slices of compressed point in parallel (step 2) -// it computes the Y coordinate from the already set X coordinate and is compute intensive -func (p *G2Affine) unsafeComputeY(subGroupCheck bool) error { - // stored in unsafeSetCompressedBytes - - mData := byte(p.Y.A0[0]) - - // we have a compressed coordinate, we need to solve the curve equation to compute Y - var YSquared, Y fptower.E2 - - YSquared.Square(&p.X).Mul(&YSquared, &p.X) - YSquared.Add(&YSquared, &bTwistCurveCoeff) - if YSquared.Legendre() == -1 { - return errors.New("invalid compressed coordinate: square root doesn't exist") - } - Y.Sqrt(&YSquared) - - if Y.LexicographicallyLargest() { - // Y ">" -Y - if mData == mCompressedSmallest { - Y.Neg(&Y) - } - } else { - // Y "<=" -Y - if mData == mCompressedLargest { - Y.Neg(&Y) - } - } - - p.Y = Y - - // subgroup check - if subGroupCheck && !p.IsInSubGroup() { - return errors.New("invalid point: subgroup check failed") - } - - return nil -} - -// unsafeSetCompressedBytes is called by Decoder when processing slices of compressed point in parallel (step 1) -// assumes buf[:8] mask is set to compressed -// returns true if point is infinity and need no further processing -// it sets X coordinate and uses Y for scratch space to store decompression metadata -func (p *G2Affine) unsafeSetCompressedBytes(buf []byte) (isInfinity bool, err error) { - - // read the most significant byte - mData := buf[0] & mMask - - if mData == mCompressedInfinity { - isInfinity = true - if !isZeroed(buf[0] & ^mMask, buf[1:SizeOfG2AffineCompressed]) { - return isInfinity, ErrInvalidInfinityEncoding - } - p.X.SetZero() - p.Y.SetZero() - return isInfinity, nil - } - - // we need to copy the input buffer (to keep this method thread safe) - var bufX [fp.Bytes]byte - copy(bufX[:fp.Bytes], buf[:fp.Bytes]) - bufX[0] &= ^mMask - - // read X coordinate - // p.X.A1 | p.X.A0 - if err := p.X.A1.SetBytesCanonical(bufX[:fp.Bytes]); err != nil { - return false, err - } - if err := p.X.A0.SetBytesCanonical(buf[fp.Bytes : fp.Bytes*2]); err != nil { - return false, err - } - - // store mData in p.Y.A0[0] - p.Y.A0[0] = uint64(mData) - - // recomputing Y will be done asynchronously - return isInfinity, nil -} diff --git a/ecc/bls12-378/marshal_test.go b/ecc/bls12-378/marshal_test.go deleted file mode 100644 index d3347e5bcd..0000000000 --- a/ecc/bls12-378/marshal_test.go +++ /dev/null @@ -1,530 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bls12378 - -import ( - "bytes" - crand "crypto/rand" - "io" - "math/big" - "math/rand/v2" - "reflect" - "testing" - - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/internal/fptower" -) - -const ( - nbFuzzShort = 10 - nbFuzz = 100 -) - -func TestEncoder(t *testing.T) { - t.Parallel() - // TODO need proper fuzz testing here - - var inA uint64 - var inB fr.Element - var inC fp.Element - var inD G1Affine - var inE G1Affine - var inF G2Affine - var inG []G1Affine - var inH []G2Affine - var inI []fp.Element - var inJ []fr.Element - var inK fr.Vector - var inL [][]fr.Element - var inM [][]uint64 - - // set values of inputs - inA = rand.Uint64() //#nosec G404 weak rng is fine here - inB.SetRandom() - inC.SetRandom() - inD.ScalarMultiplication(&g1GenAff, new(big.Int).SetUint64(rand.Uint64())) //#nosec G404 weak rng is fine here - // inE --> infinity - inF.ScalarMultiplication(&g2GenAff, new(big.Int).SetUint64(rand.Uint64())) //#nosec G404 weak rng is fine here - inG = make([]G1Affine, 2) - inH = make([]G2Affine, 0) - inG[1] = inD - inI = make([]fp.Element, 3) - inI[2] = inD.X - inJ = make([]fr.Element, 0) - inK = make(fr.Vector, 42) - inK[41].SetUint64(42) - inL = [][]fr.Element{inJ, inK} - inM = [][]uint64{{1, 2}, {4}, {}} - - // encode them, compressed and raw - var buf, bufRaw bytes.Buffer - enc := NewEncoder(&buf) - encRaw := NewEncoder(&bufRaw, RawEncoding()) - toEncode := []interface{}{inA, &inB, &inC, &inD, &inE, &inF, inG, inH, inI, inJ, inK, inL, inM} - for _, v := range toEncode { - if err := enc.Encode(v); err != nil { - t.Fatal(err) - } - if err := encRaw.Encode(v); err != nil { - t.Fatal(err) - } - } - - testDecode := func(t *testing.T, r io.Reader, n int64) { - dec := NewDecoder(r) - var outA uint64 - var outB fr.Element - var outC fp.Element - var outD G1Affine - var outE G1Affine - outE.X.SetOne() - outE.Y.SetUint64(42) - var outF G2Affine - var outG []G1Affine - var outH []G2Affine - var outI []fp.Element - var outJ []fr.Element - var outK fr.Vector - var outL [][]fr.Element - var outM [][]uint64 - - toDecode := []interface{}{&outA, &outB, &outC, &outD, &outE, &outF, &outG, &outH, &outI, &outJ, &outK, &outL, &outM} - for _, v := range toDecode { - if err := dec.Decode(v); err != nil { - t.Fatal(err) - } - } - - // compare values - if inA != outA { - t.Fatal("didn't encode/decode uint64 value properly") - } - - if !inB.Equal(&outB) || !inC.Equal(&outC) { - t.Fatal("decode(encode(Element) failed") - } - if !inD.Equal(&outD) || !inE.Equal(&outE) { - t.Fatal("decode(encode(G1Affine) failed") - } - if !inF.Equal(&outF) { - t.Fatal("decode(encode(G2Affine) failed") - } - if (len(inG) != len(outG)) || (len(inH) != len(outH)) { - t.Fatal("decode(encode(slice(points))) failed") - } - for i := 0; i < len(inG); i++ { - if !inG[i].Equal(&outG[i]) { - t.Fatal("decode(encode(slice(points))) failed") - } - } - if (len(inI) != len(outI)) || (len(inJ) != len(outJ)) { - t.Fatal("decode(encode(slice(elements))) failed") - } - for i := 0; i < len(inI); i++ { - if !inI[i].Equal(&outI[i]) { - t.Fatal("decode(encode(slice(elements))) failed") - } - } - if !reflect.DeepEqual(inK, outK) { - t.Fatal("decode(encode(vector)) failed") - } - if !reflect.DeepEqual(inL, outL) { - t.Fatal("decode(encode(slice²(elements))) failed") - } - if !reflect.DeepEqual(inM, outM) { - t.Fatal("decode(encode(slice²(uint64))) failed") - } - if n != dec.BytesRead() { - t.Fatal("bytes read don't match bytes written") - } - } - - // decode them - testDecode(t, &buf, enc.BytesWritten()) - testDecode(t, &bufRaw, encRaw.BytesWritten()) - -} - -func TestIsCompressed(t *testing.T) { - t.Parallel() - var g1Inf, g1 G1Affine - var g2Inf, g2 G2Affine - - g1 = g1GenAff - g2 = g2GenAff - - { - b := g1Inf.Bytes() - if !isCompressed(b[0]) { - t.Fatal("g1Inf.Bytes() should be compressed") - } - } - - { - b := g1Inf.RawBytes() - if isCompressed(b[0]) { - t.Fatal("g1Inf.RawBytes() should be uncompressed") - } - } - - { - b := g1.Bytes() - if !isCompressed(b[0]) { - t.Fatal("g1.Bytes() should be compressed") - } - } - - { - b := g1.RawBytes() - if isCompressed(b[0]) { - t.Fatal("g1.RawBytes() should be uncompressed") - } - } - - { - b := g2Inf.Bytes() - if !isCompressed(b[0]) { - t.Fatal("g2Inf.Bytes() should be compressed") - } - } - - { - b := g2Inf.RawBytes() - if isCompressed(b[0]) { - t.Fatal("g2Inf.RawBytes() should be uncompressed") - } - } - - { - b := g2.Bytes() - if !isCompressed(b[0]) { - t.Fatal("g2.Bytes() should be compressed") - } - } - - { - b := g2.RawBytes() - if isCompressed(b[0]) { - t.Fatal("g2.RawBytes() should be uncompressed") - } - } - -} - -func TestG1AffineInvalidBitMask(t *testing.T) { - t.Parallel() - var buf [SizeOfG1AffineCompressed]byte - crand.Read(buf[:]) - - var p G1Affine - buf[0] = 0b111 << 5 - if _, err := p.SetBytes(buf[:]); err != ErrInvalidEncoding { - t.Fatal("should error on invalid bit mask") - } - buf[0] = 0b011 << 5 - if _, err := p.SetBytes(buf[:]); err != ErrInvalidEncoding { - t.Fatal("should error on invalid bit mask") - } - buf[0] = 0b001 << 5 - if _, err := p.SetBytes(buf[:]); err != ErrInvalidEncoding { - t.Fatal("should error on invalid bit mask") - } -} - -func TestG1AffineSerialization(t *testing.T) { - t.Parallel() - // test round trip serialization of infinity - { - // compressed - { - var p1, p2 G1Affine - p2.X.SetRandom() - p2.Y.SetRandom() - buf := p1.Bytes() - n, err := p2.SetBytes(buf[:]) - if err != nil { - t.Fatal(err) - } - if n != SizeOfG1AffineCompressed { - t.Fatal("invalid number of bytes consumed in buffer") - } - if !(p2.X.IsZero() && p2.Y.IsZero()) { - t.Fatal("deserialization of uncompressed infinity point is not infinity") - } - } - - // uncompressed - { - var p1, p2 G1Affine - p2.X.SetRandom() - p2.Y.SetRandom() - buf := p1.RawBytes() - n, err := p2.SetBytes(buf[:]) - if err != nil { - t.Fatal(err) - } - if n != SizeOfG1AffineUncompressed { - t.Fatal("invalid number of bytes consumed in buffer") - } - if !(p2.X.IsZero() && p2.Y.IsZero()) { - t.Fatal("deserialization of uncompressed infinity point is not infinity") - } - } - } - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[G1] Affine SetBytes(RawBytes) should stay the same", prop.ForAll( - func(a fp.Element) bool { - var start, end G1Affine - var ab big.Int - a.BigInt(&ab) - start.ScalarMultiplication(&g1GenAff, &ab) - - buf := start.RawBytes() - n, err := end.SetBytes(buf[:]) - if err != nil { - return false - } - if n != SizeOfG1AffineUncompressed { - return false - } - return start.X.Equal(&end.X) && start.Y.Equal(&end.Y) - }, - GenFp(), - )) - - properties.Property("[G1] Affine SetBytes(Bytes()) should stay the same", prop.ForAll( - func(a fp.Element) bool { - var start, end G1Affine - var ab big.Int - a.BigInt(&ab) - start.ScalarMultiplication(&g1GenAff, &ab) - - buf := start.Bytes() - n, err := end.SetBytes(buf[:]) - if err != nil { - return false - } - if n != SizeOfG1AffineCompressed { - return false - } - return start.X.Equal(&end.X) && start.Y.Equal(&end.Y) - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG2AffineInvalidBitMask(t *testing.T) { - t.Parallel() - var buf [SizeOfG2AffineCompressed]byte - crand.Read(buf[:]) - - var p G2Affine - buf[0] = 0b111 << 5 - if _, err := p.SetBytes(buf[:]); err != ErrInvalidEncoding { - t.Fatal("should error on invalid bit mask") - } - buf[0] = 0b011 << 5 - if _, err := p.SetBytes(buf[:]); err != ErrInvalidEncoding { - t.Fatal("should error on invalid bit mask") - } - buf[0] = 0b001 << 5 - if _, err := p.SetBytes(buf[:]); err != ErrInvalidEncoding { - t.Fatal("should error on invalid bit mask") - } -} - -func TestG2AffineSerialization(t *testing.T) { - t.Parallel() - // test round trip serialization of infinity - { - // compressed - { - var p1, p2 G2Affine - p2.X.SetRandom() - p2.Y.SetRandom() - buf := p1.Bytes() - n, err := p2.SetBytes(buf[:]) - if err != nil { - t.Fatal(err) - } - if n != SizeOfG2AffineCompressed { - t.Fatal("invalid number of bytes consumed in buffer") - } - if !(p2.X.IsZero() && p2.Y.IsZero()) { - t.Fatal("deserialization of uncompressed infinity point is not infinity") - } - } - - // uncompressed - { - var p1, p2 G2Affine - p2.X.SetRandom() - p2.Y.SetRandom() - buf := p1.RawBytes() - n, err := p2.SetBytes(buf[:]) - if err != nil { - t.Fatal(err) - } - if n != SizeOfG2AffineUncompressed { - t.Fatal("invalid number of bytes consumed in buffer") - } - if !(p2.X.IsZero() && p2.Y.IsZero()) { - t.Fatal("deserialization of uncompressed infinity point is not infinity") - } - } - } - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[G2] Affine SetBytes(RawBytes) should stay the same", prop.ForAll( - func(a fp.Element) bool { - var start, end G2Affine - var ab big.Int - a.BigInt(&ab) - start.ScalarMultiplication(&g2GenAff, &ab) - - buf := start.RawBytes() - n, err := end.SetBytes(buf[:]) - if err != nil { - return false - } - if n != SizeOfG2AffineUncompressed { - return false - } - return start.X.Equal(&end.X) && start.Y.Equal(&end.Y) - }, - GenFp(), - )) - - properties.Property("[G2] Affine SetBytes(Bytes()) should stay the same", prop.ForAll( - func(a fp.Element) bool { - var start, end G2Affine - var ab big.Int - a.BigInt(&ab) - start.ScalarMultiplication(&g2GenAff, &ab) - - buf := start.Bytes() - n, err := end.SetBytes(buf[:]) - if err != nil { - return false - } - if n != SizeOfG2AffineCompressed { - return false - } - return start.X.Equal(&end.X) && start.Y.Equal(&end.Y) - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -// define Gopters generators - -// GenFr generates an Fr element -func GenFr() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var elmt fr.Element - - if _, err := elmt.SetRandom(); err != nil { - panic(err) - } - - return gopter.NewGenResult(elmt, gopter.NoShrinker) - } -} - -// GenFp generates an Fp element -func GenFp() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var elmt fp.Element - - if _, err := elmt.SetRandom(); err != nil { - panic(err) - } - - return gopter.NewGenResult(elmt, gopter.NoShrinker) - } -} - -// GenE2 generates an fptower.E2 elmt -func GenE2() gopter.Gen { - return gopter.CombineGens( - GenFp(), - GenFp(), - ).Map(func(values []interface{}) fptower.E2 { - return fptower.E2{A0: values[0].(fp.Element), A1: values[1].(fp.Element)} - }) -} - -// GenE6 generates an fptower.E6 elmt -func GenE6() gopter.Gen { - return gopter.CombineGens( - GenE2(), - GenE2(), - GenE2(), - ).Map(func(values []interface{}) fptower.E6 { - return fptower.E6{B0: values[0].(fptower.E2), B1: values[1].(fptower.E2), B2: values[2].(fptower.E2)} - }) -} - -// GenE12 generates an fptower.E6 elmt -func GenE12() gopter.Gen { - return gopter.CombineGens( - GenE6(), - GenE6(), - ).Map(func(values []interface{}) fptower.E12 { - return fptower.E12{C0: values[0].(fptower.E6), C1: values[1].(fptower.E6)} - }) -} - -// GenBigInt generates a big.Int -func GenBigInt() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var s big.Int - var b [fp.Bytes]byte - _, err := crand.Read(b[:]) - if err != nil { - panic(err) - } - s.SetBytes(b[:]) - genResult := gopter.NewGenResult(s, gopter.NoShrinker) - return genResult - } -} diff --git a/ecc/bls12-378/multiexp.go b/ecc/bls12-378/multiexp.go deleted file mode 100644 index 5133f657f1..0000000000 --- a/ecc/bls12-378/multiexp.go +++ /dev/null @@ -1,867 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bls12378 - -import ( - "errors" - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/internal/parallel" - "math" - "runtime" -) - -// MultiExp implements section 4 of https://eprint.iacr.org/2012/549.pdf -// -// This call return an error if len(scalars) != len(points) or if provided config is invalid. -func (p *G1Affine) MultiExp(points []G1Affine, scalars []fr.Element, config ecc.MultiExpConfig) (*G1Affine, error) { - var _p G1Jac - if _, err := _p.MultiExp(points, scalars, config); err != nil { - return nil, err - } - p.FromJacobian(&_p) - return p, nil -} - -// MultiExp implements section 4 of https://eprint.iacr.org/2012/549.pdf -// -// This call return an error if len(scalars) != len(points) or if provided config is invalid. -func (p *G1Jac) MultiExp(points []G1Affine, scalars []fr.Element, config ecc.MultiExpConfig) (*G1Jac, error) { - // TODO @gbotrel replace the ecc.MultiExpConfig by a Option pattern for maintainability. - // note: - // each of the msmCX method is the same, except for the c constant it declares - // duplicating (through template generation) these methods allows to declare the buckets on the stack - // the choice of c needs to be improved: - // there is a theoretical value that gives optimal asymptotics - // but in practice, other factors come into play, including: - // * if c doesn't divide 64, the word size, then we're bound to select bits over 2 words of our scalars, instead of 1 - // * number of CPUs - // * cache friendliness (which depends on the host, G1 or G2... ) - // --> for example, on BN254, a G1 point fits into one cache line of 64bytes, but a G2 point don't. - - // for each msmCX - // step 1 - // we compute, for each scalars over c-bit wide windows, nbChunk digits - // if the digit is larger than 2^{c-1}, then, we borrow 2^c from the next window and subtract - // 2^{c} to the current digit, making it negative. - // negative digits will be processed in the next step as adding -G into the bucket instead of G - // (computing -G is cheap, and this saves us half of the buckets) - // step 2 - // buckets are declared on the stack - // notice that we have 2^{c-1} buckets instead of 2^{c} (see step1) - // we use jacobian extended formulas here as they are faster than mixed addition - // msmProcessChunk places points into buckets base on their selector and return the weighted bucket sum in given channel - // step 3 - // reduce the buckets weighed sums into our result (msmReduceChunk) - - // ensure len(points) == len(scalars) - nbPoints := len(points) - if nbPoints != len(scalars) { - return nil, errors.New("len(points) != len(scalars)") - } - - // if nbTasks is not set, use all available CPUs - if config.NbTasks <= 0 { - config.NbTasks = runtime.NumCPU() * 2 - } else if config.NbTasks > 1024 { - return nil, errors.New("invalid config: config.NbTasks > 1024") - } - - // here, we compute the best C for nbPoints - // we split recursively until nbChunks(c) >= nbTasks, - bestC := func(nbPoints int) uint64 { - // implemented msmC methods (the c we use must be in this slice) - implementedCs := []uint64{4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} - var C uint64 - // approximate cost (in group operations) - // cost = bits/c * (nbPoints + 2^{c}) - // this needs to be verified empirically. - // for example, on a MBP 2016, for G2 MultiExp > 8M points, hand picking c gives better results - min := math.MaxFloat64 - for _, c := range implementedCs { - cc := (fr.Bits + 1) * (nbPoints + (1 << c)) - cost := float64(cc) / float64(c) - if cost < min { - min = cost - C = c - } - } - return C - } - - C := bestC(nbPoints) - nbChunks := int(computeNbChunks(C)) - - // should we recursively split the msm in half? (see below) - // we want to minimize the execution time of the algorithm; - // splitting the msm will **add** operations, but if it allows to use more CPU, it might be worth it. - - // costFunction returns a metric that represent the "wall time" of the algorithm - costFunction := func(nbTasks, nbCpus, costPerTask int) int { - // cost for the reduction of all tasks (msmReduceChunk) - totalCost := nbTasks - - // cost for the computation of each task (msmProcessChunk) - for nbTasks >= nbCpus { - nbTasks -= nbCpus - totalCost += costPerTask - } - if nbTasks > 0 { - totalCost += costPerTask - } - return totalCost - } - - // costPerTask is the approximate number of group ops per task - costPerTask := func(c uint64, nbPoints int) int { return (nbPoints + int((1 << c))) } - - costPreSplit := costFunction(nbChunks, config.NbTasks, costPerTask(C, nbPoints)) - - cPostSplit := bestC(nbPoints / 2) - nbChunksPostSplit := int(computeNbChunks(cPostSplit)) - costPostSplit := costFunction(nbChunksPostSplit*2, config.NbTasks, costPerTask(cPostSplit, nbPoints/2)) - - // if the cost of the split msm is lower than the cost of the non split msm, we split - if costPostSplit < costPreSplit { - config.NbTasks = int(math.Ceil(float64(config.NbTasks) / 2.0)) - var _p G1Jac - chDone := make(chan struct{}, 1) - go func() { - _p.MultiExp(points[:nbPoints/2], scalars[:nbPoints/2], config) - close(chDone) - }() - p.MultiExp(points[nbPoints/2:], scalars[nbPoints/2:], config) - <-chDone - p.AddAssign(&_p) - return p, nil - } - - // if we don't split, we use the best C we found - _innerMsmG1(p, C, points, scalars, config) - - return p, nil -} - -func _innerMsmG1(p *G1Jac, c uint64, points []G1Affine, scalars []fr.Element, config ecc.MultiExpConfig) *G1Jac { - // partition the scalars - digits, chunkStats := partitionScalars(scalars, c, config.NbTasks) - - nbChunks := computeNbChunks(c) - - // for each chunk, spawn one go routine that'll loop through all the scalars in the - // corresponding bit-window - // note that buckets is an array allocated on the stack and this is critical for performance - - // each go routine sends its result in chChunks[i] channel - chChunks := make([]chan g1JacExtended, nbChunks) - for i := 0; i < len(chChunks); i++ { - chChunks[i] = make(chan g1JacExtended, 1) - } - - // we use a semaphore to limit the number of go routines running concurrently - // (only if nbTasks < nbCPU) - var sem chan struct{} - if config.NbTasks < runtime.NumCPU() { - // we add nbChunks because if chunk is overweight we split it in two - sem = make(chan struct{}, config.NbTasks+int(nbChunks)) - for i := 0; i < config.NbTasks; i++ { - sem <- struct{}{} - } - defer func() { - close(sem) - }() - } - - // the last chunk may be processed with a different method than the rest, as it could be smaller. - n := len(points) - for j := int(nbChunks - 1); j >= 0; j-- { - processChunk := getChunkProcessorG1(c, chunkStats[j]) - if j == int(nbChunks-1) { - processChunk = getChunkProcessorG1(lastC(c), chunkStats[j]) - } - if chunkStats[j].weight >= 115 { - // we split this in more go routines since this chunk has more work to do than the others. - // else what would happen is this go routine would finish much later than the others. - chSplit := make(chan g1JacExtended, 2) - split := n / 2 - - if sem != nil { - sem <- struct{}{} // add another token to the semaphore, since we split in two. - } - go processChunk(uint64(j), chSplit, c, points[:split], digits[j*n:(j*n)+split], sem) - go processChunk(uint64(j), chSplit, c, points[split:], digits[(j*n)+split:(j+1)*n], sem) - go func(chunkID int) { - s1 := <-chSplit - s2 := <-chSplit - close(chSplit) - s1.add(&s2) - chChunks[chunkID] <- s1 - }(j) - continue - } - go processChunk(uint64(j), chChunks[j], c, points, digits[j*n:(j+1)*n], sem) - } - - return msmReduceChunkG1Affine(p, int(c), chChunks[:]) -} - -// getChunkProcessorG1 decides, depending on c window size and statistics for the chunk -// to return the best algorithm to process the chunk. -func getChunkProcessorG1(c uint64, stat chunkStat) func(chunkID uint64, chRes chan<- g1JacExtended, c uint64, points []G1Affine, digits []uint16, sem chan struct{}) { - switch c { - - case 2: - return processChunkG1Jacobian[bucketg1JacExtendedC2] - case 3: - return processChunkG1Jacobian[bucketg1JacExtendedC3] - case 4: - return processChunkG1Jacobian[bucketg1JacExtendedC4] - case 5: - return processChunkG1Jacobian[bucketg1JacExtendedC5] - case 6: - return processChunkG1Jacobian[bucketg1JacExtendedC6] - case 7: - return processChunkG1Jacobian[bucketg1JacExtendedC7] - case 8: - return processChunkG1Jacobian[bucketg1JacExtendedC8] - case 9: - return processChunkG1Jacobian[bucketg1JacExtendedC9] - case 10: - const batchSize = 80 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG1Jacobian[bucketg1JacExtendedC10] - } - return processChunkG1BatchAffine[bucketg1JacExtendedC10, bucketG1AffineC10, bitSetC10, pG1AffineC10, ppG1AffineC10, qG1AffineC10, cG1AffineC10] - case 11: - const batchSize = 150 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG1Jacobian[bucketg1JacExtendedC11] - } - return processChunkG1BatchAffine[bucketg1JacExtendedC11, bucketG1AffineC11, bitSetC11, pG1AffineC11, ppG1AffineC11, qG1AffineC11, cG1AffineC11] - case 12: - const batchSize = 200 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG1Jacobian[bucketg1JacExtendedC12] - } - return processChunkG1BatchAffine[bucketg1JacExtendedC12, bucketG1AffineC12, bitSetC12, pG1AffineC12, ppG1AffineC12, qG1AffineC12, cG1AffineC12] - case 13: - const batchSize = 350 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG1Jacobian[bucketg1JacExtendedC13] - } - return processChunkG1BatchAffine[bucketg1JacExtendedC13, bucketG1AffineC13, bitSetC13, pG1AffineC13, ppG1AffineC13, qG1AffineC13, cG1AffineC13] - case 14: - const batchSize = 400 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG1Jacobian[bucketg1JacExtendedC14] - } - return processChunkG1BatchAffine[bucketg1JacExtendedC14, bucketG1AffineC14, bitSetC14, pG1AffineC14, ppG1AffineC14, qG1AffineC14, cG1AffineC14] - case 15: - const batchSize = 500 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG1Jacobian[bucketg1JacExtendedC15] - } - return processChunkG1BatchAffine[bucketg1JacExtendedC15, bucketG1AffineC15, bitSetC15, pG1AffineC15, ppG1AffineC15, qG1AffineC15, cG1AffineC15] - case 16: - const batchSize = 640 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG1Jacobian[bucketg1JacExtendedC16] - } - return processChunkG1BatchAffine[bucketg1JacExtendedC16, bucketG1AffineC16, bitSetC16, pG1AffineC16, ppG1AffineC16, qG1AffineC16, cG1AffineC16] - default: - // panic("will not happen c != previous values is not generated by templates") - return processChunkG1Jacobian[bucketg1JacExtendedC16] - } -} - -// msmReduceChunkG1Affine reduces the weighted sum of the buckets into the result of the multiExp -func msmReduceChunkG1Affine(p *G1Jac, c int, chChunks []chan g1JacExtended) *G1Jac { - var _p g1JacExtended - totalj := <-chChunks[len(chChunks)-1] - _p.Set(&totalj) - for j := len(chChunks) - 2; j >= 0; j-- { - for l := 0; l < c; l++ { - _p.double(&_p) - } - totalj := <-chChunks[j] - _p.add(&totalj) - } - - return p.unsafeFromJacExtended(&_p) -} - -// Fold computes the multi-exponentiation \sum_{i=0}^{len(points)-1} points[i] * -// combinationCoeff^i and stores the result in p. It returns error in case -// configuration is invalid. -func (p *G1Affine) Fold(points []G1Affine, combinationCoeff fr.Element, config ecc.MultiExpConfig) (*G1Affine, error) { - var _p G1Jac - if _, err := _p.Fold(points, combinationCoeff, config); err != nil { - return nil, err - } - p.FromJacobian(&_p) - return p, nil -} - -// Fold computes the multi-exponentiation \sum_{i=0}^{len(points)-1} points[i] * -// combinationCoeff^i and stores the result in p. It returns error in case -// configuration is invalid. -func (p *G1Jac) Fold(points []G1Affine, combinationCoeff fr.Element, config ecc.MultiExpConfig) (*G1Jac, error) { - scalars := make([]fr.Element, len(points)) - scalar := fr.NewElement(1) - for i := 0; i < len(points); i++ { - scalars[i].Set(&scalar) - scalar.Mul(&scalar, &combinationCoeff) - } - return p.MultiExp(points, scalars, config) -} - -// MultiExp implements section 4 of https://eprint.iacr.org/2012/549.pdf -// -// This call return an error if len(scalars) != len(points) or if provided config is invalid. -func (p *G2Affine) MultiExp(points []G2Affine, scalars []fr.Element, config ecc.MultiExpConfig) (*G2Affine, error) { - var _p G2Jac - if _, err := _p.MultiExp(points, scalars, config); err != nil { - return nil, err - } - p.FromJacobian(&_p) - return p, nil -} - -// MultiExp implements section 4 of https://eprint.iacr.org/2012/549.pdf -// -// This call return an error if len(scalars) != len(points) or if provided config is invalid. -func (p *G2Jac) MultiExp(points []G2Affine, scalars []fr.Element, config ecc.MultiExpConfig) (*G2Jac, error) { - // TODO @gbotrel replace the ecc.MultiExpConfig by a Option pattern for maintainability. - // note: - // each of the msmCX method is the same, except for the c constant it declares - // duplicating (through template generation) these methods allows to declare the buckets on the stack - // the choice of c needs to be improved: - // there is a theoretical value that gives optimal asymptotics - // but in practice, other factors come into play, including: - // * if c doesn't divide 64, the word size, then we're bound to select bits over 2 words of our scalars, instead of 1 - // * number of CPUs - // * cache friendliness (which depends on the host, G1 or G2... ) - // --> for example, on BN254, a G1 point fits into one cache line of 64bytes, but a G2 point don't. - - // for each msmCX - // step 1 - // we compute, for each scalars over c-bit wide windows, nbChunk digits - // if the digit is larger than 2^{c-1}, then, we borrow 2^c from the next window and subtract - // 2^{c} to the current digit, making it negative. - // negative digits will be processed in the next step as adding -G into the bucket instead of G - // (computing -G is cheap, and this saves us half of the buckets) - // step 2 - // buckets are declared on the stack - // notice that we have 2^{c-1} buckets instead of 2^{c} (see step1) - // we use jacobian extended formulas here as they are faster than mixed addition - // msmProcessChunk places points into buckets base on their selector and return the weighted bucket sum in given channel - // step 3 - // reduce the buckets weighed sums into our result (msmReduceChunk) - - // ensure len(points) == len(scalars) - nbPoints := len(points) - if nbPoints != len(scalars) { - return nil, errors.New("len(points) != len(scalars)") - } - - // if nbTasks is not set, use all available CPUs - if config.NbTasks <= 0 { - config.NbTasks = runtime.NumCPU() * 2 - } else if config.NbTasks > 1024 { - return nil, errors.New("invalid config: config.NbTasks > 1024") - } - - // here, we compute the best C for nbPoints - // we split recursively until nbChunks(c) >= nbTasks, - bestC := func(nbPoints int) uint64 { - // implemented msmC methods (the c we use must be in this slice) - implementedCs := []uint64{4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} - var C uint64 - // approximate cost (in group operations) - // cost = bits/c * (nbPoints + 2^{c}) - // this needs to be verified empirically. - // for example, on a MBP 2016, for G2 MultiExp > 8M points, hand picking c gives better results - min := math.MaxFloat64 - for _, c := range implementedCs { - cc := (fr.Bits + 1) * (nbPoints + (1 << c)) - cost := float64(cc) / float64(c) - if cost < min { - min = cost - C = c - } - } - return C - } - - C := bestC(nbPoints) - nbChunks := int(computeNbChunks(C)) - - // should we recursively split the msm in half? (see below) - // we want to minimize the execution time of the algorithm; - // splitting the msm will **add** operations, but if it allows to use more CPU, it might be worth it. - - // costFunction returns a metric that represent the "wall time" of the algorithm - costFunction := func(nbTasks, nbCpus, costPerTask int) int { - // cost for the reduction of all tasks (msmReduceChunk) - totalCost := nbTasks - - // cost for the computation of each task (msmProcessChunk) - for nbTasks >= nbCpus { - nbTasks -= nbCpus - totalCost += costPerTask - } - if nbTasks > 0 { - totalCost += costPerTask - } - return totalCost - } - - // costPerTask is the approximate number of group ops per task - costPerTask := func(c uint64, nbPoints int) int { return (nbPoints + int((1 << c))) } - - costPreSplit := costFunction(nbChunks, config.NbTasks, costPerTask(C, nbPoints)) - - cPostSplit := bestC(nbPoints / 2) - nbChunksPostSplit := int(computeNbChunks(cPostSplit)) - costPostSplit := costFunction(nbChunksPostSplit*2, config.NbTasks, costPerTask(cPostSplit, nbPoints/2)) - - // if the cost of the split msm is lower than the cost of the non split msm, we split - if costPostSplit < costPreSplit { - config.NbTasks = int(math.Ceil(float64(config.NbTasks) / 2.0)) - var _p G2Jac - chDone := make(chan struct{}, 1) - go func() { - _p.MultiExp(points[:nbPoints/2], scalars[:nbPoints/2], config) - close(chDone) - }() - p.MultiExp(points[nbPoints/2:], scalars[nbPoints/2:], config) - <-chDone - p.AddAssign(&_p) - return p, nil - } - - // if we don't split, we use the best C we found - _innerMsmG2(p, C, points, scalars, config) - - return p, nil -} - -func _innerMsmG2(p *G2Jac, c uint64, points []G2Affine, scalars []fr.Element, config ecc.MultiExpConfig) *G2Jac { - // partition the scalars - digits, chunkStats := partitionScalars(scalars, c, config.NbTasks) - - nbChunks := computeNbChunks(c) - - // for each chunk, spawn one go routine that'll loop through all the scalars in the - // corresponding bit-window - // note that buckets is an array allocated on the stack and this is critical for performance - - // each go routine sends its result in chChunks[i] channel - chChunks := make([]chan g2JacExtended, nbChunks) - for i := 0; i < len(chChunks); i++ { - chChunks[i] = make(chan g2JacExtended, 1) - } - - // we use a semaphore to limit the number of go routines running concurrently - // (only if nbTasks < nbCPU) - var sem chan struct{} - if config.NbTasks < runtime.NumCPU() { - // we add nbChunks because if chunk is overweight we split it in two - sem = make(chan struct{}, config.NbTasks+int(nbChunks)) - for i := 0; i < config.NbTasks; i++ { - sem <- struct{}{} - } - defer func() { - close(sem) - }() - } - - // the last chunk may be processed with a different method than the rest, as it could be smaller. - n := len(points) - for j := int(nbChunks - 1); j >= 0; j-- { - processChunk := getChunkProcessorG2(c, chunkStats[j]) - if j == int(nbChunks-1) { - processChunk = getChunkProcessorG2(lastC(c), chunkStats[j]) - } - if chunkStats[j].weight >= 115 { - // we split this in more go routines since this chunk has more work to do than the others. - // else what would happen is this go routine would finish much later than the others. - chSplit := make(chan g2JacExtended, 2) - split := n / 2 - - if sem != nil { - sem <- struct{}{} // add another token to the semaphore, since we split in two. - } - go processChunk(uint64(j), chSplit, c, points[:split], digits[j*n:(j*n)+split], sem) - go processChunk(uint64(j), chSplit, c, points[split:], digits[(j*n)+split:(j+1)*n], sem) - go func(chunkID int) { - s1 := <-chSplit - s2 := <-chSplit - close(chSplit) - s1.add(&s2) - chChunks[chunkID] <- s1 - }(j) - continue - } - go processChunk(uint64(j), chChunks[j], c, points, digits[j*n:(j+1)*n], sem) - } - - return msmReduceChunkG2Affine(p, int(c), chChunks[:]) -} - -// getChunkProcessorG2 decides, depending on c window size and statistics for the chunk -// to return the best algorithm to process the chunk. -func getChunkProcessorG2(c uint64, stat chunkStat) func(chunkID uint64, chRes chan<- g2JacExtended, c uint64, points []G2Affine, digits []uint16, sem chan struct{}) { - switch c { - - case 2: - return processChunkG2Jacobian[bucketg2JacExtendedC2] - case 3: - return processChunkG2Jacobian[bucketg2JacExtendedC3] - case 4: - return processChunkG2Jacobian[bucketg2JacExtendedC4] - case 5: - return processChunkG2Jacobian[bucketg2JacExtendedC5] - case 6: - return processChunkG2Jacobian[bucketg2JacExtendedC6] - case 7: - return processChunkG2Jacobian[bucketg2JacExtendedC7] - case 8: - return processChunkG2Jacobian[bucketg2JacExtendedC8] - case 9: - return processChunkG2Jacobian[bucketg2JacExtendedC9] - case 10: - const batchSize = 80 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG2Jacobian[bucketg2JacExtendedC10] - } - return processChunkG2BatchAffine[bucketg2JacExtendedC10, bucketG2AffineC10, bitSetC10, pG2AffineC10, ppG2AffineC10, qG2AffineC10, cG2AffineC10] - case 11: - const batchSize = 150 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG2Jacobian[bucketg2JacExtendedC11] - } - return processChunkG2BatchAffine[bucketg2JacExtendedC11, bucketG2AffineC11, bitSetC11, pG2AffineC11, ppG2AffineC11, qG2AffineC11, cG2AffineC11] - case 12: - const batchSize = 200 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG2Jacobian[bucketg2JacExtendedC12] - } - return processChunkG2BatchAffine[bucketg2JacExtendedC12, bucketG2AffineC12, bitSetC12, pG2AffineC12, ppG2AffineC12, qG2AffineC12, cG2AffineC12] - case 13: - const batchSize = 350 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG2Jacobian[bucketg2JacExtendedC13] - } - return processChunkG2BatchAffine[bucketg2JacExtendedC13, bucketG2AffineC13, bitSetC13, pG2AffineC13, ppG2AffineC13, qG2AffineC13, cG2AffineC13] - case 14: - const batchSize = 400 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG2Jacobian[bucketg2JacExtendedC14] - } - return processChunkG2BatchAffine[bucketg2JacExtendedC14, bucketG2AffineC14, bitSetC14, pG2AffineC14, ppG2AffineC14, qG2AffineC14, cG2AffineC14] - case 15: - const batchSize = 500 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG2Jacobian[bucketg2JacExtendedC15] - } - return processChunkG2BatchAffine[bucketg2JacExtendedC15, bucketG2AffineC15, bitSetC15, pG2AffineC15, ppG2AffineC15, qG2AffineC15, cG2AffineC15] - case 16: - const batchSize = 640 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG2Jacobian[bucketg2JacExtendedC16] - } - return processChunkG2BatchAffine[bucketg2JacExtendedC16, bucketG2AffineC16, bitSetC16, pG2AffineC16, ppG2AffineC16, qG2AffineC16, cG2AffineC16] - default: - // panic("will not happen c != previous values is not generated by templates") - return processChunkG2Jacobian[bucketg2JacExtendedC16] - } -} - -// msmReduceChunkG2Affine reduces the weighted sum of the buckets into the result of the multiExp -func msmReduceChunkG2Affine(p *G2Jac, c int, chChunks []chan g2JacExtended) *G2Jac { - var _p g2JacExtended - totalj := <-chChunks[len(chChunks)-1] - _p.Set(&totalj) - for j := len(chChunks) - 2; j >= 0; j-- { - for l := 0; l < c; l++ { - _p.double(&_p) - } - totalj := <-chChunks[j] - _p.add(&totalj) - } - - return p.unsafeFromJacExtended(&_p) -} - -// Fold computes the multi-exponentiation \sum_{i=0}^{len(points)-1} points[i] * -// combinationCoeff^i and stores the result in p. It returns error in case -// configuration is invalid. -func (p *G2Affine) Fold(points []G2Affine, combinationCoeff fr.Element, config ecc.MultiExpConfig) (*G2Affine, error) { - var _p G2Jac - if _, err := _p.Fold(points, combinationCoeff, config); err != nil { - return nil, err - } - p.FromJacobian(&_p) - return p, nil -} - -// Fold computes the multi-exponentiation \sum_{i=0}^{len(points)-1} points[i] * -// combinationCoeff^i and stores the result in p. It returns error in case -// configuration is invalid. -func (p *G2Jac) Fold(points []G2Affine, combinationCoeff fr.Element, config ecc.MultiExpConfig) (*G2Jac, error) { - scalars := make([]fr.Element, len(points)) - scalar := fr.NewElement(1) - for i := 0; i < len(points); i++ { - scalars[i].Set(&scalar) - scalar.Mul(&scalar, &combinationCoeff) - } - return p.MultiExp(points, scalars, config) -} - -// selector stores the index, mask and shifts needed to select bits from a scalar -// it is used during the multiExp algorithm or the batch scalar multiplication -type selector struct { - index uint64 // index in the multi-word scalar to select bits from - mask uint64 // mask (c-bit wide) - shift uint64 // shift needed to get our bits on low positions - - multiWordSelect bool // set to true if we need to select bits from 2 words (case where c doesn't divide 64) - maskHigh uint64 // same than mask, for index+1 - shiftHigh uint64 // same than shift, for index+1 -} - -// return number of chunks for a given window size c -// the last chunk may be bigger to accommodate a potential carry from the NAF decomposition -func computeNbChunks(c uint64) uint64 { - return (fr.Bits + c - 1) / c -} - -// return the last window size for a scalar; -// this last window should accommodate a carry (from the NAF decomposition) -// it can be == c if we have 1 available bit -// it can be > c if we have 0 available bit -// it can be < c if we have 2+ available bits -func lastC(c uint64) uint64 { - nbAvailableBits := (computeNbChunks(c) * c) - fr.Bits - return c + 1 - nbAvailableBits -} - -type chunkStat struct { - // relative weight of work compared to other chunks. 100.0 -> nominal weight. - weight float32 - - // percentage of bucket filled in the window; - ppBucketFilled float32 - nbBucketFilled int -} - -// partitionScalars compute, for each scalars over c-bit wide windows, nbChunk digits -// if the digit is larger than 2^{c-1}, then, we borrow 2^c from the next window and subtract -// 2^{c} to the current digit, making it negative. -// negative digits can be processed in a later step as adding -G into the bucket instead of G -// (computing -G is cheap, and this saves us half of the buckets in the MultiExp or BatchScalarMultiplication) -func partitionScalars(scalars []fr.Element, c uint64, nbTasks int) ([]uint16, []chunkStat) { - // no benefit here to have more tasks than CPUs - if nbTasks > runtime.NumCPU() { - nbTasks = runtime.NumCPU() - } - - // number of c-bit radixes in a scalar - nbChunks := computeNbChunks(c) - - digits := make([]uint16, len(scalars)*int(nbChunks)) - - mask := uint64((1 << c) - 1) // low c bits are 1 - max := int(1<<(c-1)) - 1 // max value (inclusive) we want for our digits - cDivides64 := (64 % c) == 0 // if c doesn't divide 64, we may need to select over multiple words - - // compute offset and word selector / shift to select the right bits of our windows - selectors := make([]selector, nbChunks) - for chunk := uint64(0); chunk < nbChunks; chunk++ { - jc := uint64(chunk * c) - d := selector{} - d.index = jc / 64 - d.shift = jc - (d.index * 64) - d.mask = mask << d.shift - d.multiWordSelect = !cDivides64 && d.shift > (64-c) && d.index < (fr.Limbs-1) - if d.multiWordSelect { - nbBitsHigh := d.shift - uint64(64-c) - d.maskHigh = (1 << nbBitsHigh) - 1 - d.shiftHigh = (c - nbBitsHigh) - } - selectors[chunk] = d - } - - parallel.Execute(len(scalars), func(start, end int) { - for i := start; i < end; i++ { - if scalars[i].IsZero() { - // everything is 0, no need to process this scalar - continue - } - scalar := scalars[i].Bits() - - var carry int - - // for each chunk in the scalar, compute the current digit, and an eventual carry - for chunk := uint64(0); chunk < nbChunks-1; chunk++ { - s := selectors[chunk] - - // init with carry if any - digit := carry - carry = 0 - - // digit = value of the c-bit window - digit += int((scalar[s.index] & s.mask) >> s.shift) - - if s.multiWordSelect { - // we are selecting bits over 2 words - digit += int(scalar[s.index+1]&s.maskHigh) << s.shiftHigh - } - - // if the digit is larger than 2^{c-1}, then, we borrow 2^c from the next window and subtract - // 2^{c} to the current digit, making it negative. - if digit > max { - digit -= (1 << c) - carry = 1 - } - - // if digit is zero, no impact on result - if digit == 0 { - continue - } - - var bits uint16 - if digit > 0 { - bits = uint16(digit) << 1 - } else { - bits = (uint16(-digit-1) << 1) + 1 - } - digits[int(chunk)*len(scalars)+i] = bits - } - - // for the last chunk, we don't want to borrow from a next window - // (but may have a larger max value) - chunk := nbChunks - 1 - s := selectors[chunk] - // init with carry if any - digit := carry - // digit = value of the c-bit window - digit += int((scalar[s.index] & s.mask) >> s.shift) - if s.multiWordSelect { - // we are selecting bits over 2 words - digit += int(scalar[s.index+1]&s.maskHigh) << s.shiftHigh - } - digits[int(chunk)*len(scalars)+i] = uint16(digit) << 1 - } - - }, nbTasks) - - // aggregate chunk stats - chunkStats := make([]chunkStat, nbChunks) - if c <= 9 { - // no need to compute stats for small window sizes - return digits, chunkStats - } - parallel.Execute(len(chunkStats), func(start, end int) { - // for each chunk compute the statistics - for chunkID := start; chunkID < end; chunkID++ { - // indicates if a bucket is hit. - var b bitSetC16 - - // digits for the chunk - chunkDigits := digits[chunkID*len(scalars) : (chunkID+1)*len(scalars)] - - totalOps := 0 - nz := 0 // non zero buckets count - for _, digit := range chunkDigits { - if digit == 0 { - continue - } - totalOps++ - bucketID := digit >> 1 - if digit&1 == 0 { - bucketID -= 1 - } - if !b[bucketID] { - nz++ - b[bucketID] = true - } - } - chunkStats[chunkID].weight = float32(totalOps) // count number of ops for now, we will compute the weight after - chunkStats[chunkID].ppBucketFilled = (float32(nz) * 100.0) / float32(int(1<<(c-1))) - chunkStats[chunkID].nbBucketFilled = nz - } - }, nbTasks) - - totalOps := float32(0.0) - for _, stat := range chunkStats { - totalOps += stat.weight - } - - target := totalOps / float32(nbChunks) - if target != 0.0 { - // if target == 0, it means all the scalars are 0 everywhere, there is no work to be done. - for i := 0; i < len(chunkStats); i++ { - chunkStats[i].weight = (chunkStats[i].weight * 100.0) / target - } - } - - return digits, chunkStats -} diff --git a/ecc/bls12-378/multiexp_affine.go b/ecc/bls12-378/multiexp_affine.go deleted file mode 100644 index 54d8c71a50..0000000000 --- a/ecc/bls12-378/multiexp_affine.go +++ /dev/null @@ -1,710 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bls12378 - -import ( - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - "github.com/consensys/gnark-crypto/ecc/bls12-378/internal/fptower" -) - -type batchOpG1Affine struct { - bucketID uint16 - point G1Affine -} - -// processChunkG1BatchAffine process a chunk of the scalars during the msm -// using affine coordinates for the buckets. To amortize the cost of the inverse in the affine addition -// we use a batch affine addition. -// -// this is derived from a PR by 0x0ece : https://github.com/ConsenSys/gnark-crypto/pull/249 -// See Section 5.3: ia.cr/2022/1396 -func processChunkG1BatchAffine[BJE ibg1JacExtended, B ibG1Affine, BS bitSet, TP pG1Affine, TPP ppG1Affine, TQ qOpsG1Affine, TC cG1Affine]( - chunk uint64, - chRes chan<- g1JacExtended, - c uint64, - points []G1Affine, - digits []uint16, - sem chan struct{}) { - - if sem != nil { - // if we are limited, wait for a token in the semaphore - <-sem - } - - // the batch affine addition needs independent points; in other words, for a window of batchSize - // we want to hit independent bucketIDs when processing the digit. if there is a conflict (we're trying - // to add 2 different points to the same bucket), then we push the conflicted point to a queue. - // each time the batch is full, we execute it, and tentatively put the points (if not conflict) - // from the top of the queue into the next batch. - // if the queue is full, we "flush it"; we sequentially add the points to the buckets in - // g1JacExtended coordinates. - // The reasoning behind this is the following; batchSize is chosen such as, for a uniformly random - // input, the number of conflicts is going to be low, and the element added to the queue should be immediately - // processed in the next batch. If it's not the case, then our inputs are not random; and we fallback to - // non-batch-affine version. - - // note that we have 2 sets of buckets - // 1 in G1Affine used with the batch affine additions - // 1 in g1JacExtended used in case the queue of conflicting points - var buckets B // in G1Affine coordinates, infinity point is represented as (0,0), no need to init - var bucketsJE BJE - for i := 0; i < len(buckets); i++ { - bucketsJE[i].setInfinity() - } - - // setup for the batch affine; - var ( - bucketIds BS // bitSet to signify presence of a bucket in current batch - cptAdd int // count the number of bucket + point added to current batch - R TPP // bucket references - P TP // points to be added to R (buckets); it is beneficial to store them on the stack (ie copy) - queue TQ // queue of points that conflict the current batch - qID int // current position in queue - ) - - batchSize := len(P) - - isFull := func() bool { return cptAdd == batchSize } - - executeAndReset := func() { - batchAddG1Affine[TP, TPP, TC](&R, &P, cptAdd) - var tmp BS - bucketIds = tmp - cptAdd = 0 - } - - addFromQueue := func(op batchOpG1Affine) { - // @precondition: must ensures bucket is not "used" in current batch - // note that there is a bit of duplicate logic between add and addFromQueue - // the reason is that as of Go 1.19.3, if we pass a pointer to the queue item (see add signature) - // the compiler will put the queue on the heap. - BK := &buckets[op.bucketID] - - // handle special cases with inf or -P / P - if BK.IsInfinity() { - BK.Set(&op.point) - return - } - if BK.X.Equal(&op.point.X) { - if BK.Y.Equal(&op.point.Y) { - // P + P: doubling, which should be quite rare -- - // we use the other set of buckets - bucketsJE[op.bucketID].addMixed(&op.point) - return - } - BK.setInfinity() - return - } - - bucketIds[op.bucketID] = true - R[cptAdd] = BK - P[cptAdd] = op.point - cptAdd++ - } - - add := func(bucketID uint16, PP *G1Affine, isAdd bool) { - // @precondition: ensures bucket is not "used" in current batch - BK := &buckets[bucketID] - // handle special cases with inf or -P / P - if BK.IsInfinity() { - if isAdd { - BK.Set(PP) - } else { - BK.Neg(PP) - } - return - } - if BK.X.Equal(&PP.X) { - if BK.Y.Equal(&PP.Y) { - // P + P: doubling, which should be quite rare -- - if isAdd { - bucketsJE[bucketID].addMixed(PP) - } else { - BK.setInfinity() - } - return - } - if isAdd { - BK.setInfinity() - } else { - bucketsJE[bucketID].subMixed(PP) - } - return - } - - bucketIds[bucketID] = true - R[cptAdd] = BK - if isAdd { - P[cptAdd].Set(PP) - } else { - P[cptAdd].Neg(PP) - } - cptAdd++ - } - - flushQueue := func() { - for i := 0; i < qID; i++ { - bucketsJE[queue[i].bucketID].addMixed(&queue[i].point) - } - qID = 0 - } - - processTopQueue := func() { - for i := qID - 1; i >= 0; i-- { - if bucketIds[queue[i].bucketID] { - return - } - addFromQueue(queue[i]) - // len(queue) < batchSize so no need to check for full batch. - qID-- - } - } - - for i, digit := range digits { - - if digit == 0 || points[i].IsInfinity() { - continue - } - - bucketID := uint16((digit >> 1)) - isAdd := digit&1 == 0 - if isAdd { - // add - bucketID -= 1 - } - - if bucketIds[bucketID] { - // put it in queue - queue[qID].bucketID = bucketID - if isAdd { - queue[qID].point.Set(&points[i]) - } else { - queue[qID].point.Neg(&points[i]) - } - qID++ - - // queue is full, flush it. - if qID == len(queue)-1 { - flushQueue() - } - continue - } - - // we add the point to the batch. - add(bucketID, &points[i], isAdd) - if isFull() { - executeAndReset() - processTopQueue() - } - } - - // flush items in batch. - executeAndReset() - - // empty the queue - flushQueue() - - // reduce buckets into total - // total = bucket[0] + 2*bucket[1] + 3*bucket[2] ... + n*bucket[n-1] - var runningSum, total g1JacExtended - runningSum.setInfinity() - total.setInfinity() - for k := len(buckets) - 1; k >= 0; k-- { - runningSum.addMixed(&buckets[k]) - if !bucketsJE[k].IsInfinity() { - runningSum.add(&bucketsJE[k]) - } - total.add(&runningSum) - } - - if sem != nil { - // release a token to the semaphore - // before sending to chRes - sem <- struct{}{} - } - - chRes <- total - -} - -// we declare the buckets as fixed-size array types -// this allow us to allocate the buckets on the stack -type bucketG1AffineC10 [512]G1Affine -type bucketG1AffineC11 [1024]G1Affine -type bucketG1AffineC12 [2048]G1Affine -type bucketG1AffineC13 [4096]G1Affine -type bucketG1AffineC14 [8192]G1Affine -type bucketG1AffineC15 [16384]G1Affine -type bucketG1AffineC16 [32768]G1Affine - -// buckets: array of G1Affine points of size 1 << (c-1) -type ibG1Affine interface { - bucketG1AffineC10 | - bucketG1AffineC11 | - bucketG1AffineC12 | - bucketG1AffineC13 | - bucketG1AffineC14 | - bucketG1AffineC15 | - bucketG1AffineC16 -} - -// array of coordinates fp.Element -type cG1Affine interface { - cG1AffineC10 | - cG1AffineC11 | - cG1AffineC12 | - cG1AffineC13 | - cG1AffineC14 | - cG1AffineC15 | - cG1AffineC16 -} - -// buckets: array of G1Affine points (for the batch addition) -type pG1Affine interface { - pG1AffineC10 | - pG1AffineC11 | - pG1AffineC12 | - pG1AffineC13 | - pG1AffineC14 | - pG1AffineC15 | - pG1AffineC16 -} - -// buckets: array of *G1Affine points (for the batch addition) -type ppG1Affine interface { - ppG1AffineC10 | - ppG1AffineC11 | - ppG1AffineC12 | - ppG1AffineC13 | - ppG1AffineC14 | - ppG1AffineC15 | - ppG1AffineC16 -} - -// buckets: array of G1Affine queue operations (for the batch addition) -type qOpsG1Affine interface { - qG1AffineC10 | - qG1AffineC11 | - qG1AffineC12 | - qG1AffineC13 | - qG1AffineC14 | - qG1AffineC15 | - qG1AffineC16 -} - -// batch size 80 when c = 10 -type cG1AffineC10 [80]fp.Element -type pG1AffineC10 [80]G1Affine -type ppG1AffineC10 [80]*G1Affine -type qG1AffineC10 [80]batchOpG1Affine - -// batch size 150 when c = 11 -type cG1AffineC11 [150]fp.Element -type pG1AffineC11 [150]G1Affine -type ppG1AffineC11 [150]*G1Affine -type qG1AffineC11 [150]batchOpG1Affine - -// batch size 200 when c = 12 -type cG1AffineC12 [200]fp.Element -type pG1AffineC12 [200]G1Affine -type ppG1AffineC12 [200]*G1Affine -type qG1AffineC12 [200]batchOpG1Affine - -// batch size 350 when c = 13 -type cG1AffineC13 [350]fp.Element -type pG1AffineC13 [350]G1Affine -type ppG1AffineC13 [350]*G1Affine -type qG1AffineC13 [350]batchOpG1Affine - -// batch size 400 when c = 14 -type cG1AffineC14 [400]fp.Element -type pG1AffineC14 [400]G1Affine -type ppG1AffineC14 [400]*G1Affine -type qG1AffineC14 [400]batchOpG1Affine - -// batch size 500 when c = 15 -type cG1AffineC15 [500]fp.Element -type pG1AffineC15 [500]G1Affine -type ppG1AffineC15 [500]*G1Affine -type qG1AffineC15 [500]batchOpG1Affine - -// batch size 640 when c = 16 -type cG1AffineC16 [640]fp.Element -type pG1AffineC16 [640]G1Affine -type ppG1AffineC16 [640]*G1Affine -type qG1AffineC16 [640]batchOpG1Affine - -type batchOpG2Affine struct { - bucketID uint16 - point G2Affine -} - -// processChunkG2BatchAffine process a chunk of the scalars during the msm -// using affine coordinates for the buckets. To amortize the cost of the inverse in the affine addition -// we use a batch affine addition. -// -// this is derived from a PR by 0x0ece : https://github.com/ConsenSys/gnark-crypto/pull/249 -// See Section 5.3: ia.cr/2022/1396 -func processChunkG2BatchAffine[BJE ibg2JacExtended, B ibG2Affine, BS bitSet, TP pG2Affine, TPP ppG2Affine, TQ qOpsG2Affine, TC cG2Affine]( - chunk uint64, - chRes chan<- g2JacExtended, - c uint64, - points []G2Affine, - digits []uint16, - sem chan struct{}) { - - if sem != nil { - // if we are limited, wait for a token in the semaphore - <-sem - } - - // the batch affine addition needs independent points; in other words, for a window of batchSize - // we want to hit independent bucketIDs when processing the digit. if there is a conflict (we're trying - // to add 2 different points to the same bucket), then we push the conflicted point to a queue. - // each time the batch is full, we execute it, and tentatively put the points (if not conflict) - // from the top of the queue into the next batch. - // if the queue is full, we "flush it"; we sequentially add the points to the buckets in - // g2JacExtended coordinates. - // The reasoning behind this is the following; batchSize is chosen such as, for a uniformly random - // input, the number of conflicts is going to be low, and the element added to the queue should be immediately - // processed in the next batch. If it's not the case, then our inputs are not random; and we fallback to - // non-batch-affine version. - - // note that we have 2 sets of buckets - // 1 in G2Affine used with the batch affine additions - // 1 in g2JacExtended used in case the queue of conflicting points - var buckets B // in G2Affine coordinates, infinity point is represented as (0,0), no need to init - var bucketsJE BJE - for i := 0; i < len(buckets); i++ { - bucketsJE[i].setInfinity() - } - - // setup for the batch affine; - var ( - bucketIds BS // bitSet to signify presence of a bucket in current batch - cptAdd int // count the number of bucket + point added to current batch - R TPP // bucket references - P TP // points to be added to R (buckets); it is beneficial to store them on the stack (ie copy) - queue TQ // queue of points that conflict the current batch - qID int // current position in queue - ) - - batchSize := len(P) - - isFull := func() bool { return cptAdd == batchSize } - - executeAndReset := func() { - batchAddG2Affine[TP, TPP, TC](&R, &P, cptAdd) - var tmp BS - bucketIds = tmp - cptAdd = 0 - } - - addFromQueue := func(op batchOpG2Affine) { - // @precondition: must ensures bucket is not "used" in current batch - // note that there is a bit of duplicate logic between add and addFromQueue - // the reason is that as of Go 1.19.3, if we pass a pointer to the queue item (see add signature) - // the compiler will put the queue on the heap. - BK := &buckets[op.bucketID] - - // handle special cases with inf or -P / P - if BK.IsInfinity() { - BK.Set(&op.point) - return - } - if BK.X.Equal(&op.point.X) { - if BK.Y.Equal(&op.point.Y) { - // P + P: doubling, which should be quite rare -- - // we use the other set of buckets - bucketsJE[op.bucketID].addMixed(&op.point) - return - } - BK.setInfinity() - return - } - - bucketIds[op.bucketID] = true - R[cptAdd] = BK - P[cptAdd] = op.point - cptAdd++ - } - - add := func(bucketID uint16, PP *G2Affine, isAdd bool) { - // @precondition: ensures bucket is not "used" in current batch - BK := &buckets[bucketID] - // handle special cases with inf or -P / P - if BK.IsInfinity() { - if isAdd { - BK.Set(PP) - } else { - BK.Neg(PP) - } - return - } - if BK.X.Equal(&PP.X) { - if BK.Y.Equal(&PP.Y) { - // P + P: doubling, which should be quite rare -- - if isAdd { - bucketsJE[bucketID].addMixed(PP) - } else { - BK.setInfinity() - } - return - } - if isAdd { - BK.setInfinity() - } else { - bucketsJE[bucketID].subMixed(PP) - } - return - } - - bucketIds[bucketID] = true - R[cptAdd] = BK - if isAdd { - P[cptAdd].Set(PP) - } else { - P[cptAdd].Neg(PP) - } - cptAdd++ - } - - flushQueue := func() { - for i := 0; i < qID; i++ { - bucketsJE[queue[i].bucketID].addMixed(&queue[i].point) - } - qID = 0 - } - - processTopQueue := func() { - for i := qID - 1; i >= 0; i-- { - if bucketIds[queue[i].bucketID] { - return - } - addFromQueue(queue[i]) - // len(queue) < batchSize so no need to check for full batch. - qID-- - } - } - - for i, digit := range digits { - - if digit == 0 || points[i].IsInfinity() { - continue - } - - bucketID := uint16((digit >> 1)) - isAdd := digit&1 == 0 - if isAdd { - // add - bucketID -= 1 - } - - if bucketIds[bucketID] { - // put it in queue - queue[qID].bucketID = bucketID - if isAdd { - queue[qID].point.Set(&points[i]) - } else { - queue[qID].point.Neg(&points[i]) - } - qID++ - - // queue is full, flush it. - if qID == len(queue)-1 { - flushQueue() - } - continue - } - - // we add the point to the batch. - add(bucketID, &points[i], isAdd) - if isFull() { - executeAndReset() - processTopQueue() - } - } - - // flush items in batch. - executeAndReset() - - // empty the queue - flushQueue() - - // reduce buckets into total - // total = bucket[0] + 2*bucket[1] + 3*bucket[2] ... + n*bucket[n-1] - var runningSum, total g2JacExtended - runningSum.setInfinity() - total.setInfinity() - for k := len(buckets) - 1; k >= 0; k-- { - runningSum.addMixed(&buckets[k]) - if !bucketsJE[k].IsInfinity() { - runningSum.add(&bucketsJE[k]) - } - total.add(&runningSum) - } - - if sem != nil { - // release a token to the semaphore - // before sending to chRes - sem <- struct{}{} - } - - chRes <- total - -} - -// we declare the buckets as fixed-size array types -// this allow us to allocate the buckets on the stack -type bucketG2AffineC10 [512]G2Affine -type bucketG2AffineC11 [1024]G2Affine -type bucketG2AffineC12 [2048]G2Affine -type bucketG2AffineC13 [4096]G2Affine -type bucketG2AffineC14 [8192]G2Affine -type bucketG2AffineC15 [16384]G2Affine -type bucketG2AffineC16 [32768]G2Affine - -// buckets: array of G2Affine points of size 1 << (c-1) -type ibG2Affine interface { - bucketG2AffineC10 | - bucketG2AffineC11 | - bucketG2AffineC12 | - bucketG2AffineC13 | - bucketG2AffineC14 | - bucketG2AffineC15 | - bucketG2AffineC16 -} - -// array of coordinates fptower.E2 -type cG2Affine interface { - cG2AffineC10 | - cG2AffineC11 | - cG2AffineC12 | - cG2AffineC13 | - cG2AffineC14 | - cG2AffineC15 | - cG2AffineC16 -} - -// buckets: array of G2Affine points (for the batch addition) -type pG2Affine interface { - pG2AffineC10 | - pG2AffineC11 | - pG2AffineC12 | - pG2AffineC13 | - pG2AffineC14 | - pG2AffineC15 | - pG2AffineC16 -} - -// buckets: array of *G2Affine points (for the batch addition) -type ppG2Affine interface { - ppG2AffineC10 | - ppG2AffineC11 | - ppG2AffineC12 | - ppG2AffineC13 | - ppG2AffineC14 | - ppG2AffineC15 | - ppG2AffineC16 -} - -// buckets: array of G2Affine queue operations (for the batch addition) -type qOpsG2Affine interface { - qG2AffineC10 | - qG2AffineC11 | - qG2AffineC12 | - qG2AffineC13 | - qG2AffineC14 | - qG2AffineC15 | - qG2AffineC16 -} - -// batch size 80 when c = 10 -type cG2AffineC10 [80]fptower.E2 -type pG2AffineC10 [80]G2Affine -type ppG2AffineC10 [80]*G2Affine -type qG2AffineC10 [80]batchOpG2Affine - -// batch size 150 when c = 11 -type cG2AffineC11 [150]fptower.E2 -type pG2AffineC11 [150]G2Affine -type ppG2AffineC11 [150]*G2Affine -type qG2AffineC11 [150]batchOpG2Affine - -// batch size 200 when c = 12 -type cG2AffineC12 [200]fptower.E2 -type pG2AffineC12 [200]G2Affine -type ppG2AffineC12 [200]*G2Affine -type qG2AffineC12 [200]batchOpG2Affine - -// batch size 350 when c = 13 -type cG2AffineC13 [350]fptower.E2 -type pG2AffineC13 [350]G2Affine -type ppG2AffineC13 [350]*G2Affine -type qG2AffineC13 [350]batchOpG2Affine - -// batch size 400 when c = 14 -type cG2AffineC14 [400]fptower.E2 -type pG2AffineC14 [400]G2Affine -type ppG2AffineC14 [400]*G2Affine -type qG2AffineC14 [400]batchOpG2Affine - -// batch size 500 when c = 15 -type cG2AffineC15 [500]fptower.E2 -type pG2AffineC15 [500]G2Affine -type ppG2AffineC15 [500]*G2Affine -type qG2AffineC15 [500]batchOpG2Affine - -// batch size 640 when c = 16 -type cG2AffineC16 [640]fptower.E2 -type pG2AffineC16 [640]G2Affine -type ppG2AffineC16 [640]*G2Affine -type qG2AffineC16 [640]batchOpG2Affine - -type bitSetC2 [2]bool -type bitSetC3 [4]bool -type bitSetC4 [8]bool -type bitSetC5 [16]bool -type bitSetC6 [32]bool -type bitSetC7 [64]bool -type bitSetC8 [128]bool -type bitSetC9 [256]bool -type bitSetC10 [512]bool -type bitSetC11 [1024]bool -type bitSetC12 [2048]bool -type bitSetC13 [4096]bool -type bitSetC14 [8192]bool -type bitSetC15 [16384]bool -type bitSetC16 [32768]bool - -type bitSet interface { - bitSetC2 | - bitSetC3 | - bitSetC4 | - bitSetC5 | - bitSetC6 | - bitSetC7 | - bitSetC8 | - bitSetC9 | - bitSetC10 | - bitSetC11 | - bitSetC12 | - bitSetC13 | - bitSetC14 | - bitSetC15 | - bitSetC16 -} diff --git a/ecc/bls12-378/multiexp_jacobian.go b/ecc/bls12-378/multiexp_jacobian.go deleted file mode 100644 index 952a76711c..0000000000 --- a/ecc/bls12-378/multiexp_jacobian.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bls12378 - -func processChunkG1Jacobian[B ibg1JacExtended](chunk uint64, - chRes chan<- g1JacExtended, - c uint64, - points []G1Affine, - digits []uint16, - sem chan struct{}) { - - if sem != nil { - // if we are limited, wait for a token in the semaphore - <-sem - } - - var buckets B - for i := 0; i < len(buckets); i++ { - buckets[i].setInfinity() - } - - // for each scalars, get the digit corresponding to the chunk we're processing. - for i, digit := range digits { - if digit == 0 { - continue - } - - // if msbWindow bit is set, we need to subtract - if digit&1 == 0 { - // add - buckets[(digit>>1)-1].addMixed(&points[i]) - } else { - // sub - buckets[(digit >> 1)].subMixed(&points[i]) - } - } - - // reduce buckets into total - // total = bucket[0] + 2*bucket[1] + 3*bucket[2] ... + n*bucket[n-1] - - var runningSum, total g1JacExtended - runningSum.setInfinity() - total.setInfinity() - for k := len(buckets) - 1; k >= 0; k-- { - if !buckets[k].IsInfinity() { - runningSum.add(&buckets[k]) - } - total.add(&runningSum) - } - - if sem != nil { - // release a token to the semaphore - // before sending to chRes - sem <- struct{}{} - } - - chRes <- total -} - -// we declare the buckets as fixed-size array types -// this allow us to allocate the buckets on the stack -type bucketg1JacExtendedC2 [2]g1JacExtended -type bucketg1JacExtendedC3 [4]g1JacExtended -type bucketg1JacExtendedC4 [8]g1JacExtended -type bucketg1JacExtendedC5 [16]g1JacExtended -type bucketg1JacExtendedC6 [32]g1JacExtended -type bucketg1JacExtendedC7 [64]g1JacExtended -type bucketg1JacExtendedC8 [128]g1JacExtended -type bucketg1JacExtendedC9 [256]g1JacExtended -type bucketg1JacExtendedC10 [512]g1JacExtended -type bucketg1JacExtendedC11 [1024]g1JacExtended -type bucketg1JacExtendedC12 [2048]g1JacExtended -type bucketg1JacExtendedC13 [4096]g1JacExtended -type bucketg1JacExtendedC14 [8192]g1JacExtended -type bucketg1JacExtendedC15 [16384]g1JacExtended -type bucketg1JacExtendedC16 [32768]g1JacExtended - -type ibg1JacExtended interface { - bucketg1JacExtendedC2 | - bucketg1JacExtendedC3 | - bucketg1JacExtendedC4 | - bucketg1JacExtendedC5 | - bucketg1JacExtendedC6 | - bucketg1JacExtendedC7 | - bucketg1JacExtendedC8 | - bucketg1JacExtendedC9 | - bucketg1JacExtendedC10 | - bucketg1JacExtendedC11 | - bucketg1JacExtendedC12 | - bucketg1JacExtendedC13 | - bucketg1JacExtendedC14 | - bucketg1JacExtendedC15 | - bucketg1JacExtendedC16 -} - -func processChunkG2Jacobian[B ibg2JacExtended](chunk uint64, - chRes chan<- g2JacExtended, - c uint64, - points []G2Affine, - digits []uint16, - sem chan struct{}) { - - if sem != nil { - // if we are limited, wait for a token in the semaphore - <-sem - } - - var buckets B - for i := 0; i < len(buckets); i++ { - buckets[i].setInfinity() - } - - // for each scalars, get the digit corresponding to the chunk we're processing. - for i, digit := range digits { - if digit == 0 { - continue - } - - // if msbWindow bit is set, we need to subtract - if digit&1 == 0 { - // add - buckets[(digit>>1)-1].addMixed(&points[i]) - } else { - // sub - buckets[(digit >> 1)].subMixed(&points[i]) - } - } - - // reduce buckets into total - // total = bucket[0] + 2*bucket[1] + 3*bucket[2] ... + n*bucket[n-1] - - var runningSum, total g2JacExtended - runningSum.setInfinity() - total.setInfinity() - for k := len(buckets) - 1; k >= 0; k-- { - if !buckets[k].IsInfinity() { - runningSum.add(&buckets[k]) - } - total.add(&runningSum) - } - - if sem != nil { - // release a token to the semaphore - // before sending to chRes - sem <- struct{}{} - } - - chRes <- total -} - -// we declare the buckets as fixed-size array types -// this allow us to allocate the buckets on the stack -type bucketg2JacExtendedC2 [2]g2JacExtended -type bucketg2JacExtendedC3 [4]g2JacExtended -type bucketg2JacExtendedC4 [8]g2JacExtended -type bucketg2JacExtendedC5 [16]g2JacExtended -type bucketg2JacExtendedC6 [32]g2JacExtended -type bucketg2JacExtendedC7 [64]g2JacExtended -type bucketg2JacExtendedC8 [128]g2JacExtended -type bucketg2JacExtendedC9 [256]g2JacExtended -type bucketg2JacExtendedC10 [512]g2JacExtended -type bucketg2JacExtendedC11 [1024]g2JacExtended -type bucketg2JacExtendedC12 [2048]g2JacExtended -type bucketg2JacExtendedC13 [4096]g2JacExtended -type bucketg2JacExtendedC14 [8192]g2JacExtended -type bucketg2JacExtendedC15 [16384]g2JacExtended -type bucketg2JacExtendedC16 [32768]g2JacExtended - -type ibg2JacExtended interface { - bucketg2JacExtendedC2 | - bucketg2JacExtendedC3 | - bucketg2JacExtendedC4 | - bucketg2JacExtendedC5 | - bucketg2JacExtendedC6 | - bucketg2JacExtendedC7 | - bucketg2JacExtendedC8 | - bucketg2JacExtendedC9 | - bucketg2JacExtendedC10 | - bucketg2JacExtendedC11 | - bucketg2JacExtendedC12 | - bucketg2JacExtendedC13 | - bucketg2JacExtendedC14 | - bucketg2JacExtendedC15 | - bucketg2JacExtendedC16 -} diff --git a/ecc/bls12-378/multiexp_test.go b/ecc/bls12-378/multiexp_test.go deleted file mode 100644 index 2624ad4311..0000000000 --- a/ecc/bls12-378/multiexp_test.go +++ /dev/null @@ -1,863 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bls12378 - -import ( - "fmt" - "math/big" - "math/bits" - "math/rand/v2" - "runtime" - "sync" - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -func TestMultiExpG1(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = 3 - } else { - parameters.MinSuccessfulTests = nbFuzzShort * 2 - } - - properties := gopter.NewProperties(parameters) - - genScalar := GenFr() - - // size of the multiExps - const nbSamples = 73 - - // multi exp points - var samplePoints [nbSamples]G1Affine - var g G1Jac - g.Set(&g1Gen) - for i := 1; i <= nbSamples; i++ { - samplePoints[i-1].FromJacobian(&g) - g.AddAssign(&g1Gen) - } - - // sprinkle some points at infinity - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - - // final scalar to use in double and add method (without mixer factor) - // n(n+1)(2n+1)/6 (sum of the squares from 1 to n) - var scalar big.Int - scalar.SetInt64(nbSamples) - scalar.Mul(&scalar, new(big.Int).SetInt64(nbSamples+1)) - scalar.Mul(&scalar, new(big.Int).SetInt64(2*nbSamples+1)) - scalar.Div(&scalar, new(big.Int).SetInt64(6)) - - // ensure a multiexp that's splitted has the same result as a non-splitted one.. - properties.Property("[G1] Multi exponentiation (cmax) should be consistent with splitted multiexp", prop.ForAll( - func(mixer fr.Element) bool { - var samplePointsLarge [nbSamples * 13]G1Affine - for i := 0; i < 13; i++ { - copy(samplePointsLarge[i*nbSamples:], samplePoints[:]) - } - - var rmax, splitted1, splitted2 G1Jac - - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples * 13]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - rmax.MultiExp(samplePointsLarge[:], sampleScalars[:], ecc.MultiExpConfig{}) - splitted1.MultiExp(samplePointsLarge[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: 128}) - splitted2.MultiExp(samplePointsLarge[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: 51}) - return rmax.Equal(&splitted1) && rmax.Equal(&splitted2) - }, - genScalar, - )) - - // cRange is generated from template and contains the available parameters for the multiexp window size - cRange := []uint64{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} - if testing.Short() { - // test only "odd" and "even" (ie windows size divide word size vs not) - cRange = []uint64{5, 14} - } - - properties.Property(fmt.Sprintf("[G1] Multi exponentiation (c in %v) should be consistent with sum of square", cRange), prop.ForAll( - func(mixer fr.Element) bool { - - var expected G1Jac - - // compute expected result with double and add - var finalScalar, mixerBigInt big.Int - finalScalar.Mul(&scalar, mixer.BigInt(&mixerBigInt)) - expected.ScalarMultiplication(&g1Gen, &finalScalar) - - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - results := make([]G1Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG1(&results[i], c, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - for i := 1; i < len(results); i++ { - if !results[i].Equal(&results[i-1]) { - t.Logf("result for c=%d != c=%d", cRange[i-1], cRange[i]) - return false - } - } - return true - }, - genScalar, - )) - - properties.Property(fmt.Sprintf("[G1] Multi exponentiation (c in %v) of points at infinity should output a point at infinity", cRange), prop.ForAll( - func(mixer fr.Element) bool { - - var samplePointsZero [nbSamples]G1Affine - - var expected G1Jac - - // compute expected result with double and add - var finalScalar, mixerBigInt big.Int - finalScalar.Mul(&scalar, mixer.BigInt(&mixerBigInt)) - expected.ScalarMultiplication(&g1Gen, &finalScalar) - - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - samplePointsZero[i-1].setInfinity() - } - - results := make([]G1Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG1(&results[i], c, samplePointsZero[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - for i := 0; i < len(results); i++ { - if !results[i].Z.IsZero() { - t.Logf("result for c=%d is not infinity", cRange[i]) - return false - } - } - return true - }, - genScalar, - )) - - properties.Property(fmt.Sprintf("[G1] Multi exponentiation (c in %v) with a vector of 0s as input should output a point at infinity", cRange), prop.ForAll( - func(mixer fr.Element) bool { - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - results := make([]G1Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG1(&results[i], c, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - for i := 0; i < len(results); i++ { - if !results[i].Z.IsZero() { - t.Logf("result for c=%d is not infinity", cRange[i]) - return false - } - } - return true - }, - genScalar, - )) - - // note : this test is here as we expect to have a different multiExp than the above bucket method - // for small number of points - properties.Property("[G1] Multi exponentiation (<50points) should be consistent with sum of square", prop.ForAll( - func(mixer fr.Element) bool { - - var g G1Jac - g.Set(&g1Gen) - - // mixer ensures that all the words of a fpElement are set - samplePoints := make([]G1Affine, 30) - sampleScalars := make([]fr.Element, 30) - - for i := 1; i <= 30; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - samplePoints[i-1].FromJacobian(&g) - g.AddAssign(&g1Gen) - } - - var op1MultiExp G1Affine - op1MultiExp.MultiExp(samplePoints, sampleScalars, ecc.MultiExpConfig{}) - - var finalBigScalar fr.Element - var finalBigScalarBi big.Int - var op1ScalarMul G1Affine - finalBigScalar.SetUint64(9455).Mul(&finalBigScalar, &mixer) - finalBigScalar.BigInt(&finalBigScalarBi) - op1ScalarMul.ScalarMultiplication(&g1GenAff, &finalBigScalarBi) - - return op1ScalarMul.Equal(&op1MultiExp) - }, - genScalar, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestCrossMultiExpG1(t *testing.T) { - const nbSamples = 1 << 14 - // multi exp points - var samplePoints [nbSamples]G1Affine - var g G1Jac - g.Set(&g1Gen) - for i := 1; i <= nbSamples; i++ { - samplePoints[i-1].FromJacobian(&g) - g.AddAssign(&g1Gen) - } - - // sprinkle some points at infinity - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - - var sampleScalars [nbSamples]fr.Element - fillBenchScalars(sampleScalars[:]) - - // sprinkle some doublings - for i := 10; i < 100; i++ { - samplePoints[i] = samplePoints[0] - sampleScalars[i] = sampleScalars[0] - } - - // cRange is generated from template and contains the available parameters for the multiexp window size - cRange := []uint64{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} - if testing.Short() { - // test only "odd" and "even" (ie windows size divide word size vs not) - cRange = []uint64{5, 14} - } - - results := make([]G1Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG1(&results[i], c, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - - var r G1Jac - _innerMsmG1Reference(&r, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - - var expected, got G1Affine - expected.FromJacobian(&r) - - for i := 0; i < len(results); i++ { - got.FromJacobian(&results[i]) - if !expected.Equal(&got) { - t.Fatalf("cross msm failed with c=%d", cRange[i]) - } - } - -} - -// _innerMsmG1Reference always do ext jacobian with c == 16 -func _innerMsmG1Reference(p *G1Jac, points []G1Affine, scalars []fr.Element, config ecc.MultiExpConfig) *G1Jac { - // partition the scalars - digits, _ := partitionScalars(scalars, 16, config.NbTasks) - - nbChunks := computeNbChunks(16) - - // for each chunk, spawn one go routine that'll loop through all the scalars in the - // corresponding bit-window - // note that buckets is an array allocated on the stack and this is critical for performance - - // each go routine sends its result in chChunks[i] channel - chChunks := make([]chan g1JacExtended, nbChunks) - for i := 0; i < len(chChunks); i++ { - chChunks[i] = make(chan g1JacExtended, 1) - } - - // the last chunk may be processed with a different method than the rest, as it could be smaller. - n := len(points) - for j := int(nbChunks - 1); j >= 0; j-- { - processChunk := processChunkG1Jacobian[bucketg1JacExtendedC16] - go processChunk(uint64(j), chChunks[j], 16, points, digits[j*n:(j+1)*n], nil) - } - - return msmReduceChunkG1Affine(p, int(16), chChunks[:]) -} - -func BenchmarkMultiExpG1(b *testing.B) { - - const ( - pow = (bits.UintSize / 2) - (bits.UintSize / 8) // 24 on 64 bits arch, 12 on 32 bits - nbSamples = 1 << pow - ) - - var ( - samplePoints [nbSamples]G1Affine - sampleScalars [nbSamples]fr.Element - sampleScalarsSmallValues [nbSamples]fr.Element - sampleScalarsRedundant [nbSamples]fr.Element - ) - - fillBenchScalars(sampleScalars[:]) - copy(sampleScalarsSmallValues[:], sampleScalars[:]) - copy(sampleScalarsRedundant[:], sampleScalars[:]) - - // this means first chunk is going to have more work to do and should be split into several go routines - for i := 0; i < len(sampleScalarsSmallValues); i++ { - if i%5 == 0 { - sampleScalarsSmallValues[i].SetZero() - sampleScalarsSmallValues[i][0] = 1 - } - } - - // bad case for batch affine because scalar distribution might look uniform - // but over batchSize windows, we may hit a lot of conflicts and force the msm-affine - // to process small batches of additions to flush its queue of conflicted points. - for i := 0; i < len(sampleScalarsRedundant); i += 100 { - for j := i + 1; j < i+100 && j < len(sampleScalarsRedundant); j++ { - sampleScalarsRedundant[j] = sampleScalarsRedundant[i] - } - } - - fillBenchBasesG1(samplePoints[:]) - - var testPoint G1Affine - - for i := 5; i <= pow; i++ { - using := 1 << i - - b.Run(fmt.Sprintf("%d points", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:using], sampleScalars[:using], ecc.MultiExpConfig{}) - } - }) - - b.Run(fmt.Sprintf("%d points-smallvalues", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:using], sampleScalarsSmallValues[:using], ecc.MultiExpConfig{}) - } - }) - - b.Run(fmt.Sprintf("%d points-redundancy", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:using], sampleScalarsRedundant[:using], ecc.MultiExpConfig{}) - } - }) - } -} - -func BenchmarkMultiExpG1Reference(b *testing.B) { - const nbSamples = 1 << 20 - - var ( - samplePoints [nbSamples]G1Affine - sampleScalars [nbSamples]fr.Element - ) - - fillBenchScalars(sampleScalars[:]) - fillBenchBasesG1(samplePoints[:]) - - var testPoint G1Affine - - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - } -} - -func BenchmarkManyMultiExpG1Reference(b *testing.B) { - const nbSamples = 1 << 20 - - var ( - samplePoints [nbSamples]G1Affine - sampleScalars [nbSamples]fr.Element - ) - - fillBenchScalars(sampleScalars[:]) - fillBenchBasesG1(samplePoints[:]) - - var t1, t2, t3 G1Affine - b.ResetTimer() - for j := 0; j < b.N; j++ { - var wg sync.WaitGroup - wg.Add(3) - go func() { - t1.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - wg.Done() - }() - go func() { - t2.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - wg.Done() - }() - go func() { - t3.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - wg.Done() - }() - wg.Wait() - } -} - -// WARNING: this return points that are NOT on the curve and is meant to be use for benchmarking -// purposes only. We don't check that the result is valid but just measure "computational complexity". -// -// Rationale for generating points that are not on the curve is that for large benchmarks, generating -// a vector of different points can take minutes. Using the same point or subset will bias the benchmark result -// since bucket additions in extended jacobian coordinates will hit doubling algorithm instead of add. -func fillBenchBasesG1(samplePoints []G1Affine) { - var r big.Int - r.SetString("340444420969191673093399857471996460938405", 10) - samplePoints[0].ScalarMultiplication(&samplePoints[0], &r) - - one := samplePoints[0].X - one.SetOne() - - for i := 1; i < len(samplePoints); i++ { - samplePoints[i].X.Add(&samplePoints[i-1].X, &one) - samplePoints[i].Y.Sub(&samplePoints[i-1].Y, &one) - } -} - -func TestMultiExpG2(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = 3 - } else { - parameters.MinSuccessfulTests = nbFuzzShort * 2 - } - - properties := gopter.NewProperties(parameters) - - genScalar := GenFr() - - // size of the multiExps - const nbSamples = 73 - - // multi exp points - var samplePoints [nbSamples]G2Affine - var g G2Jac - g.Set(&g2Gen) - for i := 1; i <= nbSamples; i++ { - samplePoints[i-1].FromJacobian(&g) - g.AddAssign(&g2Gen) - } - - // sprinkle some points at infinity - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - - // final scalar to use in double and add method (without mixer factor) - // n(n+1)(2n+1)/6 (sum of the squares from 1 to n) - var scalar big.Int - scalar.SetInt64(nbSamples) - scalar.Mul(&scalar, new(big.Int).SetInt64(nbSamples+1)) - scalar.Mul(&scalar, new(big.Int).SetInt64(2*nbSamples+1)) - scalar.Div(&scalar, new(big.Int).SetInt64(6)) - - // ensure a multiexp that's splitted has the same result as a non-splitted one.. - properties.Property("[G2] Multi exponentiation (cmax) should be consistent with splitted multiexp", prop.ForAll( - func(mixer fr.Element) bool { - var samplePointsLarge [nbSamples * 13]G2Affine - for i := 0; i < 13; i++ { - copy(samplePointsLarge[i*nbSamples:], samplePoints[:]) - } - - var rmax, splitted1, splitted2 G2Jac - - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples * 13]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - rmax.MultiExp(samplePointsLarge[:], sampleScalars[:], ecc.MultiExpConfig{}) - splitted1.MultiExp(samplePointsLarge[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: 128}) - splitted2.MultiExp(samplePointsLarge[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: 51}) - return rmax.Equal(&splitted1) && rmax.Equal(&splitted2) - }, - genScalar, - )) - - // cRange is generated from template and contains the available parameters for the multiexp window size - // for g2, CI suffers with large c size since it needs to allocate a lot of memory for the buckets. - // test only "odd" and "even" (ie windows size divide word size vs not) - cRange := []uint64{5, 14} - - properties.Property(fmt.Sprintf("[G2] Multi exponentiation (c in %v) should be consistent with sum of square", cRange), prop.ForAll( - func(mixer fr.Element) bool { - - var expected G2Jac - - // compute expected result with double and add - var finalScalar, mixerBigInt big.Int - finalScalar.Mul(&scalar, mixer.BigInt(&mixerBigInt)) - expected.ScalarMultiplication(&g2Gen, &finalScalar) - - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - results := make([]G2Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG2(&results[i], c, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - for i := 1; i < len(results); i++ { - if !results[i].Equal(&results[i-1]) { - t.Logf("result for c=%d != c=%d", cRange[i-1], cRange[i]) - return false - } - } - return true - }, - genScalar, - )) - - properties.Property(fmt.Sprintf("[G2] Multi exponentiation (c in %v) of points at infinity should output a point at infinity", cRange), prop.ForAll( - func(mixer fr.Element) bool { - - var samplePointsZero [nbSamples]G2Affine - - var expected G2Jac - - // compute expected result with double and add - var finalScalar, mixerBigInt big.Int - finalScalar.Mul(&scalar, mixer.BigInt(&mixerBigInt)) - expected.ScalarMultiplication(&g2Gen, &finalScalar) - - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - samplePointsZero[i-1].setInfinity() - } - - results := make([]G2Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG2(&results[i], c, samplePointsZero[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - for i := 0; i < len(results); i++ { - if !results[i].Z.IsZero() { - t.Logf("result for c=%d is not infinity", cRange[i]) - return false - } - } - return true - }, - genScalar, - )) - - properties.Property(fmt.Sprintf("[G2] Multi exponentiation (c in %v) with a vector of 0s as input should output a point at infinity", cRange), prop.ForAll( - func(mixer fr.Element) bool { - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - results := make([]G2Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG2(&results[i], c, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - for i := 0; i < len(results); i++ { - if !results[i].Z.IsZero() { - t.Logf("result for c=%d is not infinity", cRange[i]) - return false - } - } - return true - }, - genScalar, - )) - - // note : this test is here as we expect to have a different multiExp than the above bucket method - // for small number of points - properties.Property("[G2] Multi exponentiation (<50points) should be consistent with sum of square", prop.ForAll( - func(mixer fr.Element) bool { - - var g G2Jac - g.Set(&g2Gen) - - // mixer ensures that all the words of a fpElement are set - samplePoints := make([]G2Affine, 30) - sampleScalars := make([]fr.Element, 30) - - for i := 1; i <= 30; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - samplePoints[i-1].FromJacobian(&g) - g.AddAssign(&g2Gen) - } - - var op1MultiExp G2Affine - op1MultiExp.MultiExp(samplePoints, sampleScalars, ecc.MultiExpConfig{}) - - var finalBigScalar fr.Element - var finalBigScalarBi big.Int - var op1ScalarMul G2Affine - finalBigScalar.SetUint64(9455).Mul(&finalBigScalar, &mixer) - finalBigScalar.BigInt(&finalBigScalarBi) - op1ScalarMul.ScalarMultiplication(&g2GenAff, &finalBigScalarBi) - - return op1ScalarMul.Equal(&op1MultiExp) - }, - genScalar, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestCrossMultiExpG2(t *testing.T) { - const nbSamples = 1 << 14 - // multi exp points - var samplePoints [nbSamples]G2Affine - var g G2Jac - g.Set(&g2Gen) - for i := 1; i <= nbSamples; i++ { - samplePoints[i-1].FromJacobian(&g) - g.AddAssign(&g2Gen) - } - - // sprinkle some points at infinity - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - - var sampleScalars [nbSamples]fr.Element - fillBenchScalars(sampleScalars[:]) - - // sprinkle some doublings - for i := 10; i < 100; i++ { - samplePoints[i] = samplePoints[0] - sampleScalars[i] = sampleScalars[0] - } - - // cRange is generated from template and contains the available parameters for the multiexp window size - // for g2, CI suffers with large c size since it needs to allocate a lot of memory for the buckets. - // test only "odd" and "even" (ie windows size divide word size vs not) - cRange := []uint64{5, 14} - - results := make([]G2Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG2(&results[i], c, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - - var r G2Jac - _innerMsmG2Reference(&r, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - - var expected, got G2Affine - expected.FromJacobian(&r) - - for i := 0; i < len(results); i++ { - got.FromJacobian(&results[i]) - if !expected.Equal(&got) { - t.Fatalf("cross msm failed with c=%d", cRange[i]) - } - } - -} - -// _innerMsmG2Reference always do ext jacobian with c == 16 -func _innerMsmG2Reference(p *G2Jac, points []G2Affine, scalars []fr.Element, config ecc.MultiExpConfig) *G2Jac { - // partition the scalars - digits, _ := partitionScalars(scalars, 16, config.NbTasks) - - nbChunks := computeNbChunks(16) - - // for each chunk, spawn one go routine that'll loop through all the scalars in the - // corresponding bit-window - // note that buckets is an array allocated on the stack and this is critical for performance - - // each go routine sends its result in chChunks[i] channel - chChunks := make([]chan g2JacExtended, nbChunks) - for i := 0; i < len(chChunks); i++ { - chChunks[i] = make(chan g2JacExtended, 1) - } - - // the last chunk may be processed with a different method than the rest, as it could be smaller. - n := len(points) - for j := int(nbChunks - 1); j >= 0; j-- { - processChunk := processChunkG2Jacobian[bucketg2JacExtendedC16] - go processChunk(uint64(j), chChunks[j], 16, points, digits[j*n:(j+1)*n], nil) - } - - return msmReduceChunkG2Affine(p, int(16), chChunks[:]) -} - -func BenchmarkMultiExpG2(b *testing.B) { - - const ( - pow = (bits.UintSize / 2) - (bits.UintSize / 8) // 24 on 64 bits arch, 12 on 32 bits - nbSamples = 1 << pow - ) - - var ( - samplePoints [nbSamples]G2Affine - sampleScalars [nbSamples]fr.Element - sampleScalarsSmallValues [nbSamples]fr.Element - sampleScalarsRedundant [nbSamples]fr.Element - ) - - fillBenchScalars(sampleScalars[:]) - copy(sampleScalarsSmallValues[:], sampleScalars[:]) - copy(sampleScalarsRedundant[:], sampleScalars[:]) - - // this means first chunk is going to have more work to do and should be split into several go routines - for i := 0; i < len(sampleScalarsSmallValues); i++ { - if i%5 == 0 { - sampleScalarsSmallValues[i].SetZero() - sampleScalarsSmallValues[i][0] = 1 - } - } - - // bad case for batch affine because scalar distribution might look uniform - // but over batchSize windows, we may hit a lot of conflicts and force the msm-affine - // to process small batches of additions to flush its queue of conflicted points. - for i := 0; i < len(sampleScalarsRedundant); i += 100 { - for j := i + 1; j < i+100 && j < len(sampleScalarsRedundant); j++ { - sampleScalarsRedundant[j] = sampleScalarsRedundant[i] - } - } - - fillBenchBasesG2(samplePoints[:]) - - var testPoint G2Affine - - for i := 5; i <= pow; i++ { - using := 1 << i - - b.Run(fmt.Sprintf("%d points", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:using], sampleScalars[:using], ecc.MultiExpConfig{}) - } - }) - - b.Run(fmt.Sprintf("%d points-smallvalues", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:using], sampleScalarsSmallValues[:using], ecc.MultiExpConfig{}) - } - }) - - b.Run(fmt.Sprintf("%d points-redundancy", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:using], sampleScalarsRedundant[:using], ecc.MultiExpConfig{}) - } - }) - } -} - -func BenchmarkMultiExpG2Reference(b *testing.B) { - const nbSamples = 1 << 20 - - var ( - samplePoints [nbSamples]G2Affine - sampleScalars [nbSamples]fr.Element - ) - - fillBenchScalars(sampleScalars[:]) - fillBenchBasesG2(samplePoints[:]) - - var testPoint G2Affine - - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - } -} - -func BenchmarkManyMultiExpG2Reference(b *testing.B) { - const nbSamples = 1 << 20 - - var ( - samplePoints [nbSamples]G2Affine - sampleScalars [nbSamples]fr.Element - ) - - fillBenchScalars(sampleScalars[:]) - fillBenchBasesG2(samplePoints[:]) - - var t1, t2, t3 G2Affine - b.ResetTimer() - for j := 0; j < b.N; j++ { - var wg sync.WaitGroup - wg.Add(3) - go func() { - t1.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - wg.Done() - }() - go func() { - t2.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - wg.Done() - }() - go func() { - t3.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - wg.Done() - }() - wg.Wait() - } -} - -// WARNING: this return points that are NOT on the curve and is meant to be use for benchmarking -// purposes only. We don't check that the result is valid but just measure "computational complexity". -// -// Rationale for generating points that are not on the curve is that for large benchmarks, generating -// a vector of different points can take minutes. Using the same point or subset will bias the benchmark result -// since bucket additions in extended jacobian coordinates will hit doubling algorithm instead of add. -func fillBenchBasesG2(samplePoints []G2Affine) { - var r big.Int - r.SetString("340444420969191673093399857471996460938405", 10) - samplePoints[0].ScalarMultiplication(&samplePoints[0], &r) - - one := samplePoints[0].X - one.SetOne() - - for i := 1; i < len(samplePoints); i++ { - samplePoints[i].X.Add(&samplePoints[i-1].X, &one) - samplePoints[i].Y.Sub(&samplePoints[i-1].Y, &one) - } -} - -func fillBenchScalars(sampleScalars []fr.Element) { - // ensure every words of the scalars are filled - for i := 0; i < len(sampleScalars); i++ { - sampleScalars[i].SetRandom() - } -} diff --git a/ecc/bls12-378/pairing.go b/ecc/bls12-378/pairing.go deleted file mode 100644 index bc6b3900c8..0000000000 --- a/ecc/bls12-378/pairing.go +++ /dev/null @@ -1,535 +0,0 @@ -// Copyright 2020 ConsenSys AG -// -// 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 bls12378 - -import ( - "errors" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - "github.com/consensys/gnark-crypto/ecc/bls12-378/internal/fptower" -) - -// GT target group of the pairing -type GT = fptower.E12 - -type lineEvaluation struct { - r0 fptower.E2 - r1 fptower.E2 - r2 fptower.E2 -} - -// Pair calculates the reduced pairing for a set of points -// ∏ᵢ e(Pᵢ, Qᵢ). -// -// This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. -func Pair(P []G1Affine, Q []G2Affine) (GT, error) { - f, err := MillerLoop(P, Q) - if err != nil { - return GT{}, err - } - return FinalExponentiation(&f), nil -} - -// PairingCheck calculates the reduced pairing for a set of points and returns True if the result is One -// ∏ᵢ e(Pᵢ, Qᵢ) =? 1 -// -// This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. -func PairingCheck(P []G1Affine, Q []G2Affine) (bool, error) { - f, err := Pair(P, Q) - if err != nil { - return false, err - } - var one GT - one.SetOne() - return f.Equal(&one), nil -} - -// FinalExponentiation computes the exponentiation (∏ᵢ zᵢ)ᵈ -// where d = (p¹²-1)/r = (p¹²-1)/Φ₁₂(p) ⋅ Φ₁₂(p)/r = (p⁶-1)(p²+1)(p⁴ - p² +1)/r -// we use instead d=s ⋅ (p⁶-1)(p²+1)(p⁴ - p² +1)/r -// where s is the cofactor 3 (Hayashida et al.) -func FinalExponentiation(z *GT, _z ...*GT) GT { - var result GT - result.Set(z) - - for _, e := range _z { - result.Mul(&result, e) - } - - var t [3]GT - - // Easy part - // (p⁶-1)(p²+1) - t[0].Conjugate(&result) - result.Inverse(&result) - t[0].Mul(&t[0], &result) - result.FrobeniusSquare(&t[0]). - Mul(&result, &t[0]) - - var one GT - one.SetOne() - if result.Equal(&one) { - return result - } - - // Hard part (up to permutation) - // Daiki Hayashida, Kenichiro Hayasaka and Tadanori Teruya - // https://eprint.iacr.org/2020/875.pdf - t[0].CyclotomicSquare(&result) - t[1].Expt(&result) - t[2].InverseUnitary(&result) - t[1].Mul(&t[1], &t[2]) - t[2].Expt(&t[1]) - t[1].InverseUnitary(&t[1]) - t[1].Mul(&t[1], &t[2]) - t[2].Expt(&t[1]) - t[1].Frobenius(&t[1]) - t[1].Mul(&t[1], &t[2]) - result.Mul(&result, &t[0]) - t[0].Expt(&t[1]) - t[2].Expt(&t[0]) - t[0].FrobeniusSquare(&t[1]) - t[1].InverseUnitary(&t[1]) - t[1].Mul(&t[1], &t[2]) - t[1].Mul(&t[1], &t[0]) - result.Mul(&result, &t[1]) - - return result -} - -// MillerLoop computes the multi-Miller loop -// ∏ᵢ MillerLoop(Pᵢ, Qᵢ) = ∏ᵢ { fᵢ_{x,Qᵢ}(Pᵢ) } -func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { - // check input size match - n := len(P) - if n == 0 || n != len(Q) { - return GT{}, errors.New("invalid inputs sizes") - } - - // filter infinity points - p := make([]G1Affine, 0, n) - q := make([]G2Affine, 0, n) - - for k := 0; k < n; k++ { - if P[k].IsInfinity() || Q[k].IsInfinity() { - continue - } - p = append(p, P[k]) - q = append(q, Q[k]) - } - - n = len(p) - - // projective points for Q - qProj := make([]g2Proj, n) - for k := 0; k < n; k++ { - qProj[k].FromAffine(&q[k]) - } - - var result GT - result.SetOne() - var l1, l2 lineEvaluation - var prodLines [5]E2 - - // Compute ∏ᵢ { fᵢ_{x₀,Q}(P) } - if n >= 1 { - // i = 62, separately to avoid an E12 Square - // (Square(res) = 1² = 1) - // LoopCounter[62] = 0 - // k = 0, separately to avoid MulBy014 (res × ℓ) - // (assign line to res) - - // qProj[0] ← 2qProj[0] and l1 the tangent ℓ passing 2qProj[0] - qProj[0].doubleStep(&l1) - // line evaluation at P[0] (assign) - result.C0.B0.Set(&l1.r0) - result.C0.B1.MulByElement(&l1.r1, &p[0].X) - result.C1.B1.MulByElement(&l1.r2, &p[0].Y) - } - - if n >= 2 { - // k = 1, separately to avoid MulBy014 (res × ℓ) - // (res is also a line at this point, so we use Mul014By014 ℓ × ℓ) - - // qProj[1] ← 2qProj[1] and l1 the tangent ℓ passing 2qProj[1] - qProj[1].doubleStep(&l1) - // line evaluation at P[1] - l1.r1.MulByElement(&l1.r1, &p[1].X) - l1.r2.MulByElement(&l1.r2, &p[1].Y) - // ℓ × res - prodLines = fptower.Mul014By014(&l1.r0, &l1.r1, &l1.r2, &result.C0.B0, &result.C0.B1, &result.C1.B1) - result.C0.B0 = prodLines[0] - result.C0.B1 = prodLines[1] - result.C0.B2 = prodLines[2] - result.C1.B1 = prodLines[3] - result.C1.B2 = prodLines[4] - } - - // k >= 2 - for k := 2; k < n; k++ { - // qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k] - qProj[k].doubleStep(&l1) - // line evaluation at P[k] - l1.r1.MulByElement(&l1.r1, &p[k].X) - l1.r2.MulByElement(&l1.r2, &p[k].Y) - // ℓ × res - result.MulBy014(&l1.r0, &l1.r1, &l1.r2) - } - - // i <= 61 - for i := len(LoopCounter) - 3; i >= 1; i-- { - // mutualize the square among n Miller loops - // (∏ᵢfᵢ)² - result.Square(&result) - - for k := 0; k < n; k++ { - // qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k] - qProj[k].doubleStep(&l1) - // line evaluation at P[k] - l1.r1.MulByElement(&l1.r1, &p[k].X) - l1.r2.MulByElement(&l1.r2, &p[k].Y) - - if LoopCounter[i] == 0 { - // ℓ × res - result.MulBy014(&l1.r0, &l1.r1, &l1.r2) - } else { - // qProj[k] ← qProj[k]+Q[k] and - // l2 the line ℓ passing qProj[k] and Q[k] - qProj[k].addMixedStep(&l2, &q[k]) - // line evaluation at P[k] - l2.r1.MulByElement(&l2.r1, &p[k].X) - l2.r2.MulByElement(&l2.r2, &p[k].Y) - // ℓ × ℓ - prodLines = fptower.Mul014By014(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2) - // (ℓ × ℓ) × result - result.MulBy01245(&prodLines) - } - } - } - - // i = 0, separately to avoid a point addition - // LoopCounter[0] = 1 - result.Square(&result) - for k := 0; k < n; k++ { - // qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k] - qProj[k].doubleStep(&l1) - // line evaluation at P[k] - l1.r1.MulByElement(&l1.r1, &p[k].X) - l1.r2.MulByElement(&l1.r2, &p[k].Y) - - // l2 the line passing qProj[k] and Q - qProj[k].lineCompute(&l2, &q[k]) - // line evaluation at P[k] - l2.r1.MulByElement(&l2.r1, &p[k].X) - l2.r2.MulByElement(&l2.r2, &p[k].Y) - // ℓ × ℓ - prodLines = fptower.Mul014By014(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2) - // (ℓ × ℓ) × result - result.MulBy01245(&prodLines) - } - - return result, nil -} - -// doubleStep doubles a point in Homogenous projective coordinates, and evaluates the line in Miller loop -// https://eprint.iacr.org/2013/722.pdf (Section 4.3) -func (p *g2Proj) doubleStep(l *lineEvaluation) { - - // get some Element from our pool - var t1, A, B, C, D, E, EE, F, G, H, I, J, K fptower.E2 - A.Mul(&p.x, &p.y) - A.Halve() - B.Square(&p.y) - C.Square(&p.z) - D.Double(&C). - Add(&D, &C) - E.Mul(&D, &bTwistCurveCoeff) - F.Double(&E). - Add(&F, &E) - G.Add(&B, &F) - G.Halve() - H.Add(&p.y, &p.z). - Square(&H) - t1.Add(&B, &C) - H.Sub(&H, &t1) - I.Sub(&E, &B) - J.Square(&p.x) - EE.Square(&E) - K.Double(&EE). - Add(&K, &EE) - - // X, Y, Z - p.x.Sub(&B, &F). - Mul(&p.x, &A) - p.y.Square(&G). - Sub(&p.y, &K) - p.z.Mul(&B, &H) - - // Line evaluation - l.r0.Set(&I) - l.r1.Double(&J). - Add(&l.r1, &J) - l.r2.Neg(&H) - -} - -// addMixedStep point addition in Mixed Homogenous projective and Affine coordinates -// https://eprint.iacr.org/2013/722.pdf (Section 4.3) -func (p *g2Proj) addMixedStep(l *lineEvaluation, a *G2Affine) { - - // get some Element from our pool - var Y2Z1, X2Z1, O, L, C, D, E, F, G, H, t0, t1, t2, J fptower.E2 - Y2Z1.Mul(&a.Y, &p.z) - O.Sub(&p.y, &Y2Z1) - X2Z1.Mul(&a.X, &p.z) - L.Sub(&p.x, &X2Z1) - C.Square(&O) - D.Square(&L) - E.Mul(&L, &D) - F.Mul(&p.z, &C) - G.Mul(&p.x, &D) - t0.Double(&G) - H.Add(&E, &F). - Sub(&H, &t0) - t1.Mul(&p.y, &E) - - // X, Y, Z - p.x.Mul(&L, &H) - p.y.Sub(&G, &H). - Mul(&p.y, &O). - Sub(&p.y, &t1) - p.z.Mul(&E, &p.z) - - t2.Mul(&L, &a.Y) - J.Mul(&a.X, &O). - Sub(&J, &t2) - - // Line evaluation - l.r0.Set(&J) - l.r1.Neg(&O) - l.r2.Set(&L) -} - -// lineCompute computes the line through p in Homogenous projective coordinates -// and a in affine coordinates. It does not compute the resulting point p+a. -func (p *g2Proj) lineCompute(evaluations *lineEvaluation, a *G2Affine) { - - // get some Element from our pool - var Y2Z1, X2Z1, O, L, t2, J fptower.E2 - Y2Z1.Mul(&a.Y, &p.z) - O.Sub(&p.y, &Y2Z1) - X2Z1.Mul(&a.X, &p.z) - L.Sub(&p.x, &X2Z1) - t2.Mul(&L, &a.Y) - J.Mul(&a.X, &O). - Sub(&J, &t2) - - // Line evaluation - evaluations.r0.Set(&J) - evaluations.r1.Neg(&O) - evaluations.r2.Set(&L) -} - -// ---------------------- -// Fixed-argument pairing -// ---------------------- - -type LineEvaluationAff struct { - R0 fptower.E2 - R1 fptower.E2 -} - -// PairFixedQ calculates the reduced pairing for a set of points -// ∏ᵢ e(Pᵢ, Qᵢ) where Q are fixed points in G2. -// -// This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. -func PairFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) { - f, err := MillerLoopFixedQ(P, lines) - if err != nil { - return GT{}, err - } - return FinalExponentiation(&f), nil -} - -// PairingCheckFixedQ calculates the reduced pairing for a set of points and returns True if the result is One -// ∏ᵢ e(Pᵢ, Qᵢ) =? 1 where Q are fixed points in G2. -// -// This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. -func PairingCheckFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (bool, error) { - f, err := PairFixedQ(P, lines) - if err != nil { - return false, err - } - var one GT - one.SetOne() - return f.Equal(&one), nil -} - -// PrecomputeLines precomputes the lines for the fixed-argument Miller loop -func PrecomputeLines(Q G2Affine) (PrecomputedLines [2][len(LoopCounter) - 1]LineEvaluationAff) { - var accQ G2Affine - accQ.Set(&Q) - - n := len(LoopCounter) - for i := n - 2; i >= 0; i-- { - accQ.doubleStep(&PrecomputedLines[0][i]) - if LoopCounter[i] == 0 { - continue - } else { - accQ.addStep(&PrecomputedLines[1][i], &Q) - } - } - return PrecomputedLines -} - -// MillerLoopFixedQ computes the multi-Miller loop as in MillerLoop -// but Qᵢ are fixed points in G2 known in advance. -func MillerLoopFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) { - n := len(P) - if n == 0 || n != len(lines) { - return GT{}, errors.New("invalid inputs sizes") - } - - // no need to filter infinity points: - // 1. if Pᵢ=(0,0) then -x/y=1/y=0 by gnark-crypto convention and so - // lines R0 and R1 are 0. It happens that result will stay, through - // the Miller loop, in 𝔽p⁶ because MulBy01(0,0,1), - // Mul01By01(0,0,1,0,0,1) and MulBy01245 set result.C0 to 0. At the - // end result will be in a proper subgroup of Fp¹² so it be reduced to - // 1 in FinalExponentiation. - // - // and/or - // - // 2. if Qᵢ=(0,0) then PrecomputeLines(Qᵢ) will return lines R0 and R1 - // that are 0 because of gnark-convention (*/0==0) in doubleStep and - // addStep. Similarly to Pᵢ=(0,0) it happens that result be 1 - // after the FinalExponentiation. - - // precomputations - yInv := make([]fp.Element, n) - xNegOverY := make([]fp.Element, n) - for k := 0; k < n; k++ { - yInv[k].Set(&P[k].Y) - } - yInv = fp.BatchInvert(yInv) - for k := 0; k < n; k++ { - xNegOverY[k].Mul(&P[k].X, &yInv[k]). - Neg(&xNegOverY[k]) - } - - var result GT - result.SetOne() - var prodLines [5]E2 - - // Compute ∏ᵢ { fᵢ_{x₀,Q}(P) } - for i := len(LoopCounter) - 2; i >= 0; i-- { - // mutualize the square among n Miller loops - // (∏ᵢfᵢ)² - result.Square(&result) - - for k := 0; k < n; k++ { - // line evaluation at P[k] - lines[k][0][i].R1. - MulByElement( - &lines[k][0][i].R1, - &yInv[k], - ) - lines[k][0][i].R0. - MulByElement(&lines[k][0][i].R0, - &xNegOverY[k], - ) - if LoopCounter[i] == 0 { - // ℓ × res - result.MulBy01( - &lines[k][0][i].R1, - &lines[k][0][i].R0, - ) - - } else { - lines[k][1][i].R1. - MulByElement( - &lines[k][1][i].R1, - &yInv[k], - ) - lines[k][1][i].R0. - MulByElement( - &lines[k][1][i].R0, - &xNegOverY[k], - ) - prodLines = fptower.Mul01By01( - &lines[k][0][i].R1, &lines[k][0][i].R0, - &lines[k][1][i].R1, &lines[k][1][i].R0, - ) - result.MulBy01245(&prodLines) - } - } - } - - return result, nil -} - -func (p *G2Affine) doubleStep(evaluations *LineEvaluationAff) { - - var n, d, λ, xr, yr fptower.E2 - // λ = 3x²/2y - n.Square(&p.X) - λ.Double(&n). - Add(&λ, &n) - d.Double(&p.Y) - λ.Div(&λ, &d) - - // xr = λ²-2x - xr.Square(&λ). - Sub(&xr, &p.X). - Sub(&xr, &p.X) - - // yr = λ(x-xr)-y - yr.Sub(&p.X, &xr). - Mul(&yr, &λ). - Sub(&yr, &p.Y) - - evaluations.R0.Set(&λ) - evaluations.R1.Mul(&λ, &p.X). - Sub(&evaluations.R1, &p.Y) - - p.X.Set(&xr) - p.Y.Set(&yr) -} - -func (p *G2Affine) addStep(evaluations *LineEvaluationAff, a *G2Affine) { - var n, d, λ, λλ, xr, yr fptower.E2 - - // compute λ = (y2-y1)/(x2-x1) - n.Sub(&a.Y, &p.Y) - d.Sub(&a.X, &p.X) - λ.Div(&n, &d) - - // xr = λ²-x1-x2 - λλ.Square(&λ) - n.Add(&p.X, &a.X) - xr.Sub(&λλ, &n) - - // yr = λ(x1-xr) - y1 - yr.Sub(&p.X, &xr). - Mul(&yr, &λ). - Sub(&yr, &p.Y) - - evaluations.R0.Set(&λ) - evaluations.R1.Mul(&λ, &p.X). - Sub(&evaluations.R1, &p.Y) - - p.X.Set(&xr) - p.Y.Set(&yr) -} diff --git a/ecc/bls12-378/pairing_test.go b/ecc/bls12-378/pairing_test.go deleted file mode 100644 index 9a78abecdb..0000000000 --- a/ecc/bls12-378/pairing_test.go +++ /dev/null @@ -1,504 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bls12378 - -import ( - "fmt" - "math/big" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fp" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -// ------------------------------------------------------------ -// tests - -func TestPairing(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := GenE12() - genR1 := GenFr() - genR2 := GenFr() - - properties.Property("[BLS12-378] Having the receiver as operand (final expo) should output the same result", prop.ForAll( - func(a GT) bool { - b := FinalExponentiation(&a) - a = FinalExponentiation(&a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BLS12-378] Exponentiating FinalExpo(a) to r should output 1", prop.ForAll( - func(a GT) bool { - b := FinalExponentiation(&a) - return !a.IsInSubGroup() && b.IsInSubGroup() - }, - genA, - )) - - properties.Property("[BLS12-378] Exp, CyclotomicExp and ExpGLV results must be the same in GT (small and big exponents)", prop.ForAll( - func(a GT, e fr.Element) bool { - - var res bool - - // exponent > r - { - a = FinalExponentiation(&a) - var _e big.Int - _e.SetString("169893631828481842931290008859743243489098146141979830311893424751855271950692001433356165550548410610101138388623573573742608490725625288296502860183437011025036209791574001140592327223981416956942076610555083128655330944007957223952510233203018053264066056080064687038560794652180979019775788172491868553073169893631828481842931290008859743243489098146141979830311893424751855271950692001433356165550548410610101138388623573573742608490725625288296502860183437011025036209791574001140592327223981416956942076610555083128655330944007957223952510233203018053264066056080064687038560794652180979019775788172491868553073", 10) - var b, c, d GT - b.Exp(a, &_e) - c.ExpGLV(a, &_e) - d.CyclotomicExp(a, &_e) - res = b.Equal(&c) && c.Equal(&d) - } - - // exponent < r - { - a = FinalExponentiation(&a) - var _e big.Int - e.BigInt(&_e) - var b, c, d GT - b.Exp(a, &_e) - c.ExpGLV(a, &_e) - d.CyclotomicExp(a, &_e) - res = res && b.Equal(&c) && c.Equal(&d) - } - - return res - }, - genA, - genR1, - )) - - properties.Property("[BLS12-378] Expt(Expt) and Exp(t^2) should output the same result in the cyclotomic subgroup", prop.ForAll( - func(a GT) bool { - var b, c, d GT - b.Conjugate(&a) - a.Inverse(&a) - b.Mul(&b, &a) - - a.FrobeniusSquare(&b). - Mul(&a, &b) - - c.Expt(&a).Expt(&c) - d.Exp(a, &xGen).Exp(d, &xGen) - return c.Equal(&d) - }, - genA, - )) - - properties.Property("[BLS12-378] bilinearity", prop.ForAll( - func(a, b fr.Element) bool { - - var res, resa, resb, resab, zero GT - - var ag1 G1Affine - var bg2 G2Affine - - var abigint, bbigint, ab big.Int - - a.BigInt(&abigint) - b.BigInt(&bbigint) - ab.Mul(&abigint, &bbigint) - - ag1.ScalarMultiplication(&g1GenAff, &abigint) - bg2.ScalarMultiplication(&g2GenAff, &bbigint) - - res, _ = Pair([]G1Affine{g1GenAff}, []G2Affine{g2GenAff}) - resa, _ = Pair([]G1Affine{ag1}, []G2Affine{g2GenAff}) - resb, _ = Pair([]G1Affine{g1GenAff}, []G2Affine{bg2}) - - resab.Exp(res, &ab) - resa.Exp(resa, &bbigint) - resb.Exp(resb, &abigint) - - return resab.Equal(&resa) && resab.Equal(&resb) && !res.Equal(&zero) - - }, - genR1, - genR2, - )) - - properties.Property("[BLS12-378] PairingCheck", prop.ForAll( - func(a, b fr.Element) bool { - - var g1GenAffNeg G1Affine - g1GenAffNeg.Neg(&g1GenAff) - tabP := []G1Affine{g1GenAff, g1GenAffNeg} - tabQ := []G2Affine{g2GenAff, g2GenAff} - - res, _ := PairingCheck(tabP, tabQ) - - return res - }, - genR1, - genR2, - )) - - properties.Property("[BLS12-378] Pair should output the same result with MillerLoop or MillerLoopFixedQ", prop.ForAll( - func(a, b fr.Element) bool { - - var ag1 G1Affine - var bg2 G2Affine - - var abigint, bbigint big.Int - - a.BigInt(&abigint) - b.BigInt(&bbigint) - - ag1.ScalarMultiplication(&g1GenAff, &abigint) - bg2.ScalarMultiplication(&g2GenAff, &bbigint) - - P := []G1Affine{g1GenAff, ag1} - Q := []G2Affine{g2GenAff, bg2} - - ml1, _ := MillerLoop(P, Q) - ml2, _ := MillerLoopFixedQ( - P, - [][2][len(LoopCounter) - 1]LineEvaluationAff{ - PrecomputeLines(Q[0]), - PrecomputeLines(Q[1]), - }) - - res1 := FinalExponentiation(&ml1) - res2 := FinalExponentiation(&ml2) - - return res1.Equal(&res2) - }, - genR1, - genR2, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestMillerLoop(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genR1 := GenFr() - genR2 := GenFr() - - properties.Property("[BLS12-378] MillerLoop of pairs should be equal to the product of MillerLoops", prop.ForAll( - func(a, b fr.Element) bool { - - var simpleProd, factorizedProd GT - - var ag1 G1Affine - var bg2 G2Affine - - var abigint, bbigint big.Int - - a.BigInt(&abigint) - b.BigInt(&bbigint) - - ag1.ScalarMultiplication(&g1GenAff, &abigint) - bg2.ScalarMultiplication(&g2GenAff, &bbigint) - - P0 := []G1Affine{g1GenAff} - P1 := []G1Affine{ag1} - Q0 := []G2Affine{g2GenAff} - Q1 := []G2Affine{bg2} - - // FE( ML(a,b) * ML(c,d) * ML(e,f) * ML(g,h) ) - M1, _ := MillerLoop(P0, Q0) - M2, _ := MillerLoop(P1, Q0) - M3, _ := MillerLoop(P0, Q1) - M4, _ := MillerLoop(P1, Q1) - simpleProd.Mul(&M1, &M2).Mul(&simpleProd, &M3).Mul(&simpleProd, &M4) - simpleProd = FinalExponentiation(&simpleProd) - - tabP := []G1Affine{g1GenAff, ag1, g1GenAff, ag1} - tabQ := []G2Affine{g2GenAff, g2GenAff, bg2, bg2} - - // FE( ML([a,c,e,g] ; [b,d,f,h]) ) -> saves 3 squares in Fqk - factorizedProd, _ = Pair(tabP, tabQ) - - return simpleProd.Equal(&factorizedProd) - }, - genR1, - genR2, - )) - - properties.Property("[BLS12-378] MillerLoop and MillerLoopFixedQ should skip pairs with a point at infinity", prop.ForAll( - func(a, b fr.Element) bool { - - var one GT - - var ag1, g1Inf G1Affine - var bg2, g2Inf G2Affine - - var abigint, bbigint big.Int - - one.SetOne() - - a.BigInt(&abigint) - b.BigInt(&bbigint) - - ag1.ScalarMultiplication(&g1GenAff, &abigint) - bg2.ScalarMultiplication(&g2GenAff, &bbigint) - - g1Inf.FromJacobian(&g1Infinity) - g2Inf.FromJacobian(&g2Infinity) - - // e([0,c] ; [b,d]) - // -> should be equal to e(c,d) - tabP := []G1Affine{g1Inf, ag1} - tabQ := []G2Affine{g2GenAff, bg2} - res1, _ := Pair(tabP, tabQ) - - // e([a,c] ; [0,d]) - // -> should be equal to e(c,d) - tabP = []G1Affine{g1GenAff, ag1} - tabQ = []G2Affine{g2Inf, bg2} - res2, _ := Pair(tabP, tabQ) - - // e([0,c] ; [b,d]) with fixed points b and d - // -> should be equal to e(c,d) - tabP = []G1Affine{g1Inf, ag1} - linesQ := [][2][len(LoopCounter) - 1]LineEvaluationAff{ - PrecomputeLines(g2GenAff), - PrecomputeLines(bg2), - } - res3, _ := PairFixedQ(tabP, linesQ) - - // e([a,c] ; [0,d]) with fixed points 0 and d - // -> should be equal to e(c,d) - tabP = []G1Affine{g1GenAff, ag1} - linesQ = [][2][len(LoopCounter) - 1]LineEvaluationAff{ - PrecomputeLines(g2Inf), - PrecomputeLines(bg2), - } - res4, _ := PairFixedQ(tabP, linesQ) - - // e([0,c] ; [d,0]) - // -> should be equal to 1 - tabP = []G1Affine{g1Inf, ag1} - tabQ = []G2Affine{bg2, g2Inf} - res5, _ := Pair(tabP, tabQ) - - // e([0,c] ; [d,0]) with fixed points d and 0 - // -> should be equal to 1 - tabP = []G1Affine{g1Inf, ag1} - linesQ = [][2][len(LoopCounter) - 1]LineEvaluationAff{ - PrecomputeLines(bg2), - PrecomputeLines(g2Inf), - } - res6, _ := PairFixedQ(tabP, linesQ) - - // e([0,0]) - // -> should be equal to 1 - tabP = []G1Affine{g1Inf} - tabQ = []G2Affine{g2Inf} - res7, _ := Pair(tabP, tabQ) - - // e([0,0]) with fixed point 0 - // -> should be equal to 1 - tabP = []G1Affine{g1Inf} - linesQ = [][2][len(LoopCounter) - 1]LineEvaluationAff{ - PrecomputeLines(g2Inf), - } - res8, _ := PairFixedQ(tabP, linesQ) - - return res1.Equal(&res2) && res2.Equal(&res3) && res3.Equal(&res4) && - res5.Equal(&one) && res6.Equal(&one) && res7.Equal(&one) && res8.Equal(&one) - }, - genR1, - genR2, - )) - - properties.Property("[BLS12-378] compressed pairing", prop.ForAll( - func(a, b fr.Element) bool { - - var ag1 G1Affine - var bg2 G2Affine - - var abigint, bbigint big.Int - - a.BigInt(&abigint) - b.BigInt(&bbigint) - - ag1.ScalarMultiplication(&g1GenAff, &abigint) - bg2.ScalarMultiplication(&g2GenAff, &bbigint) - - res, _ := Pair([]G1Affine{ag1}, []G2Affine{bg2}) - - compressed, _ := res.CompressTorus() - decompressed := compressed.DecompressTorus() - - return decompressed.Equal(&res) - - }, - genR1, - genR2, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -// ------------------------------------------------------------ -// benches - -func BenchmarkPairing(b *testing.B) { - - var g1GenAff G1Affine - var g2GenAff G2Affine - - g1GenAff.FromJacobian(&g1Gen) - g2GenAff.FromJacobian(&g2Gen) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - Pair([]G1Affine{g1GenAff}, []G2Affine{g2GenAff}) - } -} - -func BenchmarkMillerLoop(b *testing.B) { - - var g1GenAff G1Affine - var g2GenAff G2Affine - - g1GenAff.FromJacobian(&g1Gen) - g2GenAff.FromJacobian(&g2Gen) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - MillerLoop([]G1Affine{g1GenAff}, []G2Affine{g2GenAff}) - } -} - -func BenchmarkFinalExponentiation(b *testing.B) { - - var a GT - a.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - FinalExponentiation(&a) - } - -} - -func BenchmarkMultiMiller(b *testing.B) { - - var g1GenAff G1Affine - var g2GenAff G2Affine - - g1GenAff.FromJacobian(&g1Gen) - g2GenAff.FromJacobian(&g2Gen) - - n := 10 - P := make([]G1Affine, n) - Q := make([]G2Affine, n) - - for i := 2; i <= n; i++ { - for j := 0; j < i; j++ { - P[j].Set(&g1GenAff) - Q[j].Set(&g2GenAff) - } - b.Run(fmt.Sprintf("%d pairs", i), func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - MillerLoop(P, Q) - } - }) - } -} - -func BenchmarkMultiPair(b *testing.B) { - - var g1GenAff G1Affine - var g2GenAff G2Affine - - g1GenAff.FromJacobian(&g1Gen) - g2GenAff.FromJacobian(&g2Gen) - - n := 10 - P := make([]G1Affine, n) - Q := make([]G2Affine, n) - - for i := 2; i <= n; i++ { - for j := 0; j < i; j++ { - P[j].Set(&g1GenAff) - Q[j].Set(&g2GenAff) - } - b.Run(fmt.Sprintf("%d pairs", i), func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - Pair(P, Q) - } - }) - } -} - -func BenchmarkExpGT(b *testing.B) { - - var a GT - a.SetRandom() - a = FinalExponentiation(&a) - - var e fp.Element - e.SetRandom() - - k := new(big.Int).SetUint64(12) - e.Exp(e, k) - var _e big.Int - e.BigInt(&_e) - - b.Run("Naive windowed Exp", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Exp(a, &_e) - } - }) - - b.Run("2-NAF cyclotomic Exp", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.CyclotomicExp(a, &_e) - } - }) - - b.Run("windowed 2-dim GLV Exp", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.ExpGLV(a, &_e) - } - }) -} diff --git a/ecc/bls12-378/twistededwards/curve.go b/ecc/bls12-378/twistededwards/curve.go deleted file mode 100644 index dcb550bada..0000000000 --- a/ecc/bls12-378/twistededwards/curve.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package twistededwards - -import ( - "math/big" - "sync" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" -) - -// CurveParams curve parameters: ax^2 + y^2 = 1 + d*x^2*y^2 -type CurveParams struct { - A, D fr.Element - Cofactor fr.Element - Order big.Int - Base PointAffine -} - -// GetEdwardsCurve returns the twisted Edwards curve on bls12-378/Fr -func GetEdwardsCurve() CurveParams { - initOnce.Do(initCurveParams) - // copy to keep Order private - var res CurveParams - - res.A.Set(&curveParams.A) - res.D.Set(&curveParams.D) - res.Cofactor.Set(&curveParams.Cofactor) - res.Order.Set(&curveParams.Order) - res.Base.Set(&curveParams.Base) - - return res -} - -var ( - initOnce sync.Once - curveParams CurveParams -) - -func initCurveParams() { - curveParams.A.SetString("16249") - curveParams.D.SetString("826857503717340716663906603396009292766308904506333520048618402505612607353") - curveParams.Cofactor.SetString("8") - curveParams.Order.SetString("1860429383364016612493789857641020908721690454530426945748883177201355593303", 10) - - curveParams.Base.X.SetString("6772953896463446981848394912418300623023000177913479948380771331313783560843") - curveParams.Base.Y.SetString("9922290044608088599966879240752111513195706854076002240583420830067351093249") -} - -// mulByA multiplies fr.Element by curveParams.A -func mulByA(x *fr.Element) { - x.Mul(x, &curveParams.A) -} diff --git a/ecc/bls12-378/twistededwards/doc.go b/ecc/bls12-378/twistededwards/doc.go deleted file mode 100644 index 4d6f9d5f8c..0000000000 --- a/ecc/bls12-378/twistededwards/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package twistededwards provides bls12-378's twisted edwards "companion curve" defined on fr. -package twistededwards diff --git a/ecc/bls12-378/twistededwards/eddsa/doc.go b/ecc/bls12-378/twistededwards/eddsa/doc.go deleted file mode 100644 index f05b8ecb1e..0000000000 --- a/ecc/bls12-378/twistededwards/eddsa/doc.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package eddsa provides EdDSA signature scheme on bls12-378's twisted edwards curve. -// -// # See also -// -// https://en.wikipedia.org/wiki/EdDSA -package eddsa diff --git a/ecc/bls12-378/twistededwards/eddsa/eddsa.go b/ecc/bls12-378/twistededwards/eddsa/eddsa.go deleted file mode 100644 index 6443a7d98e..0000000000 --- a/ecc/bls12-378/twistededwards/eddsa/eddsa.go +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package eddsa - -import ( - "crypto/subtle" - "errors" - "hash" - "io" - "math/big" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/twistededwards" - "github.com/consensys/gnark-crypto/signature" - "golang.org/x/crypto/blake2b" -) - -var errNotOnCurve = errors.New("point not on curve") -var errHashNeeded = errors.New("hFunc cannot be nil. We need a hash for Fiat-Shamir") - -const ( - sizeFr = fr.Bytes - sizePublicKey = sizeFr - sizeSignature = 2 * sizeFr - sizePrivateKey = 2*sizeFr + 32 -) - -// PublicKey eddsa signature object -// cf https://en.wikipedia.org/wiki/EdDSA for notation -type PublicKey struct { - A twistededwards.PointAffine -} - -// PrivateKey private key of an eddsa instance -type PrivateKey struct { - PublicKey PublicKey // copy of the associated public key - scalar [sizeFr]byte // secret scalar, in big Endian - randSrc [32]byte // source -} - -// Signature represents an eddsa signature -// cf https://en.wikipedia.org/wiki/EdDSA for notation -type Signature struct { - R twistededwards.PointAffine - S [sizeFr]byte -} - -// GenerateKey generates a public and private key pair. -func GenerateKey(r io.Reader) (*PrivateKey, error) { - c := twistededwards.GetEdwardsCurve() - - var pub PublicKey - var priv PrivateKey - // hash(h) = private_key || random_source, on 32 bytes each - seed := make([]byte, 32) - _, err := r.Read(seed) - if err != nil { - return nil, err - } - h := blake2b.Sum512(seed[:]) - for i := 0; i < 32; i++ { - priv.randSrc[i] = h[i+32] - } - - // prune the key - // https://tools.ietf.org/html/rfc8032#section-5.1.5, key generation - h[0] &= 0xF8 - h[31] &= 0x7F - h[31] |= 0x40 - - // reverse first bytes because setBytes interpret stream as big endian - // but in eddsa specs s is the first 32 bytes in little endian - for i, j := 0, sizeFr-1; i < sizeFr; i, j = i+1, j-1 { - priv.scalar[i] = h[j] - } - - var bScalar big.Int - bScalar.SetBytes(priv.scalar[:]) - pub.A.ScalarMultiplication(&c.Base, &bScalar) - - priv.PublicKey = pub - - return &priv, nil -} - -// Equal compares 2 public keys -func (pub *PublicKey) Equal(x signature.PublicKey) bool { - xx, ok := x.(*PublicKey) - if !ok { - return false - } - bpk := pub.Bytes() - bxx := xx.Bytes() - return subtle.ConstantTimeCompare(bpk, bxx) == 1 -} - -// Public returns the public key associated to the private key. -func (privKey *PrivateKey) Public() signature.PublicKey { - var pub PublicKey - pub.A.Set(&privKey.PublicKey.A) - return &pub -} - -// Sign sign a sequence of field elements -// For arbitrary strings use fr.Hash first -// Pure Eddsa version (see https://tools.ietf.org/html/rfc8032#page-8) -func (privKey *PrivateKey) Sign(message []byte, hFunc hash.Hash) ([]byte, error) { - - // hFunc cannot be nil. - // We need a hash function for the Fiat-Shamir. - if hFunc == nil { - return nil, errHashNeeded - } - - curveParams := twistededwards.GetEdwardsCurve() - - var res Signature - - // blinding factor for the private key - // blindingFactorBigInt must be the same size as the private key, - // blindingFactorBigInt = h(randomness_source||message)[:sizeFr] - var blindingFactorBigInt big.Int - - // randSrc = privKey.randSrc || msg (-> message = MSB message .. LSB message) - randSrc := make([]byte, 32+len(message)) - copy(randSrc, privKey.randSrc[:]) - copy(randSrc[32:], message) - - // randBytes = H(randSrc) - blindingFactorBytes := blake2b.Sum512(randSrc[:]) // TODO ensures that the hash used to build the key and the one used here is the same - blindingFactorBigInt.SetBytes(blindingFactorBytes[:sizeFr]) - - // compute R = randScalar*Base - res.R.ScalarMultiplication(&curveParams.Base, &blindingFactorBigInt) - if !res.R.IsOnCurve() { - return nil, errNotOnCurve - } - - // compute H(R, A, M), all parameters in data are in Montgomery form - hFunc.Reset() - - resRX := res.R.X.Bytes() - resRY := res.R.Y.Bytes() - resAX := privKey.PublicKey.A.X.Bytes() - resAY := privKey.PublicKey.A.Y.Bytes() - toWrite := [][]byte{resRX[:], resRY[:], resAX[:], resAY[:], message} - for _, bytes := range toWrite { - if _, err := hFunc.Write(bytes); err != nil { - return nil, err - } - } - - var hramInt big.Int - hramBin := hFunc.Sum(nil) - hramInt.SetBytes(hramBin) - - // Compute s = randScalarInt + H(R,A,M)*S - // going with big int to do ops mod curve order - var bscalar, bs big.Int - bscalar.SetBytes(privKey.scalar[:]) - bs.Mul(&hramInt, &bscalar). - Add(&bs, &blindingFactorBigInt). - Mod(&bs, &curveParams.Order) - sb := bs.Bytes() - if len(sb) < sizeFr { - offset := make([]byte, sizeFr-len(sb)) - sb = append(offset, sb...) - } - copy(res.S[:], sb[:]) - - return res.Bytes(), nil -} - -// Verify verifies an eddsa signature -func (pub *PublicKey) Verify(sigBin, message []byte, hFunc hash.Hash) (bool, error) { - - // hFunc cannot be nil. - // We need a hash function for the Fiat-Shamir. - if hFunc == nil { - return false, errHashNeeded - } - - curveParams := twistededwards.GetEdwardsCurve() - - // verify that pubKey and R are on the curve - if !pub.A.IsOnCurve() { - return false, errNotOnCurve - } - - // Deserialize the signature - var sig Signature - if _, err := sig.SetBytes(sigBin); err != nil { - return false, err - } - - // compute H(R, A, M), all parameters in data are in Montgomery form - - hFunc.Reset() - - sigRX := sig.R.X.Bytes() - sigRY := sig.R.Y.Bytes() - sigAX := pub.A.X.Bytes() - sigAY := pub.A.Y.Bytes() - - toWrite := [][]byte{sigRX[:], sigRY[:], sigAX[:], sigAY[:], message} - for _, bytes := range toWrite { - if _, err := hFunc.Write(bytes); err != nil { - return false, err - } - } - - var hramInt big.Int - hramBin := hFunc.Sum(nil) - hramInt.SetBytes(hramBin) - - // lhs = cofactor*S*Base - var lhs twistededwards.PointAffine - var bCofactor, bs big.Int - curveParams.Cofactor.BigInt(&bCofactor) - bs.SetBytes(sig.S[:]) - lhs.ScalarMultiplication(&curveParams.Base, &bs). - ScalarMultiplication(&lhs, &bCofactor) - - if !lhs.IsOnCurve() { - return false, errNotOnCurve - } - - // rhs = cofactor*(R + H(R,A,M)*A) - var rhs twistededwards.PointAffine - rhs.ScalarMultiplication(&pub.A, &hramInt). - Add(&rhs, &sig.R). - ScalarMultiplication(&rhs, &bCofactor) - if !rhs.IsOnCurve() { - return false, errNotOnCurve - } - - // verifies that cofactor*S*Base=cofactor*(R + H(R,A,M)*A) - if !lhs.X.Equal(&rhs.X) || !lhs.Y.Equal(&rhs.Y) { - return false, nil - } - - return true, nil -} diff --git a/ecc/bls12-378/twistededwards/eddsa/eddsa_test.go b/ecc/bls12-378/twistededwards/eddsa/eddsa_test.go deleted file mode 100644 index 6aec0ae366..0000000000 --- a/ecc/bls12-378/twistededwards/eddsa/eddsa_test.go +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package eddsa - -import ( - "crypto/sha256" - "math/big" - "math/rand" - "testing" - - crand "crypto/rand" - - "fmt" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/twistededwards" - "github.com/consensys/gnark-crypto/hash" -) - -func Example() { - // instantiate hash function - hFunc := hash.MIMC_BLS12_378.New() - - // create a eddsa key pair - privateKey, _ := GenerateKey(crand.Reader) - publicKey := privateKey.PublicKey - - // generate a message (the size must be a multiple of the size of Fr) - var _msg fr.Element - _msg.SetRandom() - msg := _msg.Marshal() - - // sign the message - signature, _ := privateKey.Sign(msg, hFunc) - - // verifies signature - isValid, _ := publicKey.Verify(signature, msg, hFunc) - if !isValid { - fmt.Println("1. invalid signature") - } else { - fmt.Println("1. valid signature") - } - - // Output: 1. valid signature -} - -func TestNonMalleability(t *testing.T) { - - // buffer too big - t.Run("buffer_overflow", func(t *testing.T) { - bsig := make([]byte, 2*sizeFr+1) - var sig Signature - _, err := sig.SetBytes(bsig) - if err != errWrongSize { - t.Fatal("should raise wrong size error") - } - }) - - // R overflows p_mod - t.Run("R_overflow", func(t *testing.T) { - bsig := make([]byte, 2*sizeFr) - frMod := fr.Modulus() - r := big.NewInt(1) - r.Add(frMod, r) - buf := r.Bytes() - for i := 0; i < sizeFr; i++ { - bsig[sizeFr-1-i] = buf[i] - } - - var sig Signature - _, err := sig.SetBytes(bsig) - if err != errRBiggerThanPMod { - t.Fatal("should raise error r >= p_mod") - } - }) - - // S overflows r_mod - t.Run("S_overflow", func(t *testing.T) { - bsig := make([]byte, 2*sizeFr) - o := big.NewInt(1) - cp := twistededwards.GetEdwardsCurve() - o.Add(&cp.Order, o) - buf := o.Bytes() - copy(bsig[sizeFr:], buf[:]) - big.NewInt(1).FillBytes(bsig[:sizeFr]) - - var sig Signature - _, err := sig.SetBytes(bsig) - if err != errSBiggerThanRMod { - t.Fatal("should raise error s >= r_mod") - } - }) - -} - -func TestNoZeros(t *testing.T) { - t.Run("R.Y=0", func(t *testing.T) { - // R points are 0 - var sig Signature - sig.R.X.SetInt64(1) - sig.R.Y.SetInt64(0) - s := big.NewInt(1) - s.FillBytes(sig.S[:]) - bts := sig.Bytes() - var newSig Signature - _, err := newSig.SetBytes(bts) - if err != errZero { - t.Fatal("expected error for zero R.Y") - } - }) - t.Run("S=0", func(t *testing.T) { - // S is 0 - var R twistededwards.PointAffine - cp := twistededwards.GetEdwardsCurve() - R.ScalarMultiplication(&cp.Base, big.NewInt(1)) - var sig Signature - sig.R.Set(&R) - bts := sig.Bytes() - var newSig Signature - _, err := newSig.SetBytes(bts) - if err != errZero { - t.Fatal("expected error for zero S") - } - }) -} - -func TestSerialization(t *testing.T) { - - src := rand.NewSource(0) - r := rand.New(src) //#nosec G404 weak rng is fine here - - privKey1, err := GenerateKey(r) - if err != nil { - t.Fatal(err) - } - pubKey1 := privKey1.PublicKey - - privKey2, err := GenerateKey(r) - if err != nil { - t.Fatal(err) - } - pubKey2 := privKey2.PublicKey - - pubKeyBin1 := pubKey1.Bytes() - pubKey2.SetBytes(pubKeyBin1) - pubKeyBin2 := pubKey2.Bytes() - if len(pubKeyBin1) != len(pubKeyBin2) { - t.Fatal("Inconsistent size") - } - for i := 0; i < len(pubKeyBin1); i++ { - if pubKeyBin1[i] != pubKeyBin2[i] { - t.Fatal("Error serialize(deserialize(.))") - } - } - - privKeyBin1 := privKey1.Bytes() - privKey2.SetBytes(privKeyBin1) - privKeyBin2 := privKey2.Bytes() - if len(privKeyBin1) != len(privKeyBin2) { - t.Fatal("Inconsistent size") - } - for i := 0; i < len(privKeyBin1); i++ { - if privKeyBin1[i] != privKeyBin2[i] { - t.Fatal("Error serialize(deserialize(.))") - } - } -} - -func TestEddsaMIMC(t *testing.T) { - - src := rand.NewSource(0) - r := rand.New(src) //#nosec G404 weak rng is fine here - - // create eddsa obj and sign a message - privKey, err := GenerateKey(r) - if err != nil { - t.Fatal(nil) - } - pubKey := privKey.PublicKey - hFunc := hash.MIMC_BLS12_378.New() - - var frMsg fr.Element - frMsg.SetString("44717650746155748460101257525078853138837311576962212923649547644148297035978") - msgBin := frMsg.Bytes() - signature, err := privKey.Sign(msgBin[:], hFunc) - if err != nil { - t.Fatal(err) - } - - // verifies correct msg - res, err := pubKey.Verify(signature, msgBin[:], hFunc) - if err != nil { - t.Fatal(err) - } - if !res { - t.Fatal("Verify correct signature should return true") - } - - // verifies wrong msg - frMsg.SetString("44717650746155748460101257525078853138837311576962212923649547644148297035979") - msgBin = frMsg.Bytes() - res, err = pubKey.Verify(signature, msgBin[:], hFunc) - if err != nil { - t.Fatal(err) - } - if res { - t.Fatal("Verify wrong signature should be false") - } - -} - -func TestEddsaSHA256(t *testing.T) { - - src := rand.NewSource(0) - r := rand.New(src) //#nosec G404 weak rng is fine here - - hFunc := sha256.New() - - // create eddsa obj and sign a message - // create eddsa obj and sign a message - - privKey, err := GenerateKey(r) - pubKey := privKey.PublicKey - if err != nil { - t.Fatal(err) - } - - signature, err := privKey.Sign([]byte("message"), hFunc) - if err != nil { - t.Fatal(err) - } - - // verifies correct msg - res, err := pubKey.Verify(signature, []byte("message"), hFunc) - if err != nil { - t.Fatal(err) - } - if !res { - t.Fatal("Verify correct signature should return true") - } - - // verifies wrong msg - res, err = pubKey.Verify(signature, []byte("wrong_message"), hFunc) - if err != nil { - t.Fatal(err) - } - if res { - t.Fatal("Verify wrong signature should be false") - } - -} - -// benchmarks - -func BenchmarkVerify(b *testing.B) { - - src := rand.NewSource(0) - r := rand.New(src) //#nosec G404 weak rng is fine here - - hFunc := hash.MIMC_BLS12_378.New() - - // create eddsa obj and sign a message - privKey, err := GenerateKey(r) - pubKey := privKey.PublicKey - if err != nil { - b.Fatal(err) - } - var frMsg fr.Element - frMsg.SetString("44717650746155748460101257525078853138837311576962212923649547644148297035978") - msgBin := frMsg.Bytes() - signature, _ := privKey.Sign(msgBin[:], hFunc) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - pubKey.Verify(signature, msgBin[:], hFunc) - } -} diff --git a/ecc/bls12-378/twistededwards/eddsa/marshal.go b/ecc/bls12-378/twistededwards/eddsa/marshal.go deleted file mode 100644 index 5a9b147b76..0000000000 --- a/ecc/bls12-378/twistededwards/eddsa/marshal.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package eddsa - -import ( - "crypto/subtle" - "errors" - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/consensys/gnark-crypto/ecc/bls12-378/twistededwards" - "io" - "math/big" -) - -// cf point.go (ugly copy) -const mUnmask = 0x7f - -var errWrongSize = errors.New("wrong size buffer") -var errSBiggerThanRMod = errors.New("s >= r_mod") -var errRBiggerThanPMod = errors.New("r >= p_mod") -var errZero = errors.New("zero value") - -// Bytes returns the binary representation of the public key -// follows https://tools.ietf.org/html/rfc8032#section-3.1 -// and returns a compressed representation of the point (x,y) -// -// x, y are the coordinates of the point -// on the twisted Edwards as big endian integers. -// compressed representation store x with a parity bit to recompute y -func (pk *PublicKey) Bytes() []byte { - var res [sizePublicKey]byte - pkBin := pk.A.Bytes() - subtle.ConstantTimeCopy(1, res[:sizeFr], pkBin[:]) - return res[:] -} - -// SetBytes sets p from binary representation in buf. -// buf represents a public key as x||y where x, y are -// interpreted as big endian binary numbers corresponding -// to the coordinates of a point on the twisted Edwards. -// It returns the number of bytes read from the buffer. -func (pk *PublicKey) SetBytes(buf []byte) (int, error) { - n := 0 - if len(buf) < sizePublicKey { - return n, io.ErrShortBuffer - } - if _, err := pk.A.SetBytes(buf[:sizeFr]); err != nil { - return 0, err - } - n += sizeFr - if !pk.A.IsOnCurve() { - return n, errNotOnCurve - } - return n, nil -} - -// Bytes returns the binary representation of pk, -// as byte array publicKey||scalar||randSrc -// where publicKey is as publicKey.Bytes(), and -// scalar is in big endian, of size sizeFr. -func (privKey *PrivateKey) Bytes() []byte { - var res [sizePrivateKey]byte - pubkBin := privKey.PublicKey.A.Bytes() - subtle.ConstantTimeCopy(1, res[:sizeFr], pubkBin[:]) - subtle.ConstantTimeCopy(1, res[sizeFr:2*sizeFr], privKey.scalar[:]) - subtle.ConstantTimeCopy(1, res[2*sizeFr:], privKey.randSrc[:]) - return res[:] -} - -// SetBytes sets pk from buf, where buf is interpreted -// as publicKey||scalar||randSrc -// where publicKey is as publicKey.Bytes(), and -// scalar is in big endian, of size sizeFr. -// It returns the number byte read. -func (privKey *PrivateKey) SetBytes(buf []byte) (int, error) { - n := 0 - if len(buf) < sizePrivateKey { - return n, io.ErrShortBuffer - } - if _, err := privKey.PublicKey.A.SetBytes(buf[:sizeFr]); err != nil { - return 0, err - } - n += sizeFr - if !privKey.PublicKey.A.IsOnCurve() { - return n, errNotOnCurve - } - subtle.ConstantTimeCopy(1, privKey.scalar[:], buf[sizeFr:2*sizeFr]) - n += sizeFr - subtle.ConstantTimeCopy(1, privKey.randSrc[:], buf[2*sizeFr:]) - n += sizeFr - return n, nil -} - -// Bytes returns the binary representation of sig -// as a byte array of size 3*sizeFr x||y||s where -// - x, y are the coordinates of a point on the twisted -// Edwards represented in big endian -// - s=r+h(r,a,m) mod l, the Hasse bound guarantees that -// s is smaller than sizeFr (in particular it is supposed -// s is NOT blinded) -func (sig *Signature) Bytes() []byte { - var res [sizeSignature]byte - sigRBin := sig.R.Bytes() - subtle.ConstantTimeCopy(1, res[:sizeFr], sigRBin[:]) - subtle.ConstantTimeCopy(1, res[sizeFr:], sig.S[:]) - return res[:] -} - -// SetBytes sets sig from a buffer in binary. -// buf is read interpreted as x||y||s where -// - x,y are the coordinates of a point on the twisted -// Edwards represented in big endian -// - s=r+h(r,a,m) mod l, the Hasse bound guarantees that -// s is smaller than sizeFr (in particular it is supposed -// s is NOT blinded) -// -// It returns the number of bytes read from buf. -func (sig *Signature) SetBytes(buf []byte) (int, error) { - n := 0 - if len(buf) != sizeSignature { - return n, errWrongSize - } - - // R < P_mod (to avoid malleability) - // P_mod = field of def of the twisted Edwards = Fr snark field - fpMod := fr.Modulus() - zero := big.NewInt(0) - bufBigInt := new(big.Int) - bufCopy := make([]byte, fr.Bytes) - for i := 0; i < sizeFr; i++ { - bufCopy[sizeFr-1-i] = buf[i] - } - bufCopy[0] &= mUnmask - bufBigInt.SetBytes(bufCopy) - if bufBigInt.Cmp(zero) == 0 { - return 0, errZero - } - if bufBigInt.Cmp(fpMod) != -1 { - return 0, errRBiggerThanPMod - } - - // S < R_mod (to avoid malleability) - // R_mod is the relevant group size of the twisted Edwards NOT the fr snark field so it's supposedly smaller - bufBigInt.SetBytes(buf[sizeFr : 2*sizeFr]) - if bufBigInt.Cmp(zero) == 0 { - return 0, errZero - } - cp := twistededwards.GetEdwardsCurve() - if bufBigInt.Cmp(&cp.Order) != -1 { - return 0, errSBiggerThanRMod - } - - // deserialisation - if _, err := sig.R.SetBytes(buf[:sizeFr]); err != nil { - return 0, err - } - n += sizeFr - if !sig.R.IsOnCurve() { - return n, errNotOnCurve - } - subtle.ConstantTimeCopy(1, sig.S[:], buf[sizeFr:2*sizeFr]) - n += sizeFr - return n, nil -} diff --git a/ecc/bls12-378/twistededwards/point.go b/ecc/bls12-378/twistededwards/point.go deleted file mode 100644 index db5ed27dc4..0000000000 --- a/ecc/bls12-378/twistededwards/point.go +++ /dev/null @@ -1,673 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package twistededwards - -import ( - "crypto/subtle" - "io" - "math/big" - "math/bits" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" -) - -// PointAffine point on a twisted Edwards curve -type PointAffine struct { - X, Y fr.Element -} - -// PointProj point in projective coordinates -type PointProj struct { - X, Y, Z fr.Element -} - -// PointExtended point in extended coordinates -type PointExtended struct { - X, Y, Z, T fr.Element -} - -const ( - //following https://tools.ietf.org/html/rfc8032#section-3.1, - // an fr element x is negative if its binary encoding is - // lexicographically larger than -x. - mCompressedNegative = 0x80 - mCompressedPositive = 0x00 - mUnmask = 0x7f - - // size in byte of a compressed point (point.Y --> fr.Element) - sizePointCompressed = fr.Bytes -) - -// Bytes returns the compressed point as a byte array -// Follows https://tools.ietf.org/html/rfc8032#section-3.1, -// as the twisted Edwards implementation is primarily used -// for eddsa. -func (p *PointAffine) Bytes() [sizePointCompressed]byte { - - var res [sizePointCompressed]byte - var mask uint - - y := p.Y.Bytes() - - if p.X.LexicographicallyLargest() { - mask = mCompressedNegative - } else { - mask = mCompressedPositive - } - // p.Y must be in little endian - y[0] |= byte(mask) // msb of y - for i, j := 0, sizePointCompressed-1; i < j; i, j = i+1, j-1 { - y[i], y[j] = y[j], y[i] - } - subtle.ConstantTimeCopy(1, res[:], y[:]) - return res -} - -// Marshal converts p to a byte slice -func (p *PointAffine) Marshal() []byte { - b := p.Bytes() - return b[:] -} - -func computeX(y *fr.Element) (x fr.Element) { - initOnce.Do(initCurveParams) - - var one, num, den fr.Element - one.SetOne() - num.Square(y) - den.Mul(&num, &curveParams.D) - num.Sub(&one, &num) - den.Sub(&curveParams.A, &den) - x.Div(&num, &den) - x.Sqrt(&x) - return -} - -// SetBytes sets p from buf -// len(buf) >= sizePointCompressed -// buf contains the Y coordinate masked with a parity bit to recompute the X coordinate -// from the curve equation. See Bytes() and https://tools.ietf.org/html/rfc8032#section-3.1 -// Returns the number of read bytes and an error if the buffer is too short. -func (p *PointAffine) SetBytes(buf []byte) (int, error) { - - if len(buf) < sizePointCompressed { - return 0, io.ErrShortBuffer - } - bufCopy := make([]byte, sizePointCompressed) - subtle.ConstantTimeCopy(1, bufCopy, buf[:sizePointCompressed]) - for i, j := 0, sizePointCompressed-1; i < j; i, j = i+1, j-1 { - bufCopy[i], bufCopy[j] = bufCopy[j], bufCopy[i] - } - isLexicographicallyLargest := (mCompressedNegative&bufCopy[0])>>7 == 1 - bufCopy[0] &= mUnmask - p.Y.SetBytes(bufCopy) - p.X = computeX(&p.Y) - if isLexicographicallyLargest { - if !p.X.LexicographicallyLargest() { - p.X.Neg(&p.X) - } - } else { - if p.X.LexicographicallyLargest() { - p.X.Neg(&p.X) - } - } - - return sizePointCompressed, nil -} - -// Unmarshal alias to SetBytes() -func (p *PointAffine) Unmarshal(b []byte) error { - _, err := p.SetBytes(b) - return err -} - -// Set sets p to p1 and return it -func (p *PointAffine) Set(p1 *PointAffine) *PointAffine { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - return p -} - -// Equal returns true if p=p1 false otherwise -func (p *PointAffine) Equal(p1 *PointAffine) bool { - return p.X.Equal(&p1.X) && p.Y.Equal(&p1.Y) -} - -// IsZero returns true if p=0 false otherwise -func (p *PointAffine) IsZero() bool { - var one fr.Element - one.SetOne() - return p.X.IsZero() && p.Y.Equal(&one) -} - -// NewPointAffine creates a new instance of PointAffine -func NewPointAffine(x, y fr.Element) PointAffine { - return PointAffine{x, y} -} - -// IsOnCurve checks if a point is on the twisted Edwards curve -func (p *PointAffine) IsOnCurve() bool { - initOnce.Do(initCurveParams) - - var lhs, rhs, tmp fr.Element - - tmp.Mul(&p.Y, &p.Y) - lhs.Mul(&p.X, &p.X) - mulByA(&lhs) - lhs.Add(&lhs, &tmp) - - tmp.Mul(&p.X, &p.X). - Mul(&tmp, &p.Y). - Mul(&tmp, &p.Y). - Mul(&tmp, &curveParams.D) - rhs.SetOne().Add(&rhs, &tmp) - - return lhs.Equal(&rhs) -} - -// Neg sets p to -p1 and returns it -func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { - p.X.Neg(&p1.X) - p.Y = p1.Y - return p -} - -// Add adds two points (x,y), (u,v) on a twisted Edwards curve with parameters a, d -// modifies p -func (p *PointAffine) Add(p1, p2 *PointAffine) *PointAffine { - initOnce.Do(initCurveParams) - - var xu, yv, xv, yu, dxyuv, one, denx, deny fr.Element - pRes := new(PointAffine) - xv.Mul(&p1.X, &p2.Y) - yu.Mul(&p1.Y, &p2.X) - pRes.X.Add(&xv, &yu) - - xu.Mul(&p1.X, &p2.X) - mulByA(&xu) - yv.Mul(&p1.Y, &p2.Y) - pRes.Y.Sub(&yv, &xu) - - dxyuv.Mul(&xv, &yu).Mul(&dxyuv, &curveParams.D) - one.SetOne() - denx.Add(&one, &dxyuv) - deny.Sub(&one, &dxyuv) - - p.X.Div(&pRes.X, &denx) - p.Y.Div(&pRes.Y, &deny) - - return p -} - -// Double doubles point (x,y) on a twisted Edwards curve with parameters a, d -// modifies p -func (p *PointAffine) Double(p1 *PointAffine) *PointAffine { - - p.Set(p1) - var xx, yy, xy, denum, two fr.Element - - xx.Square(&p.X) - yy.Square(&p.Y) - xy.Mul(&p.X, &p.Y) - mulByA(&xx) - denum.Add(&xx, &yy) - - p.X.Double(&xy).Div(&p.X, &denum) - - two.SetOne().Double(&two) - denum.Neg(&denum).Add(&denum, &two) - - p.Y.Sub(&yy, &xx).Div(&p.Y, &denum) - - return p -} - -// FromProj sets p in affine from p in projective -func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { - var I fr.Element - I.Inverse(&p1.Z) - p.X.Mul(&p1.X, &I) - p.Y.Mul(&p1.Y, &I) - return p -} - -// FromExtended sets p in affine from p in extended coordinates -func (p *PointAffine) FromExtended(p1 *PointExtended) *PointAffine { - var I fr.Element - I.Inverse(&p1.Z) - p.X.Mul(&p1.X, &I) - p.Y.Mul(&p1.Y, &I) - return p -} - -// ScalarMultiplication scalar multiplication of a point -// p1 in affine coordinates with a scalar in big.Int -func (p *PointAffine) ScalarMultiplication(p1 *PointAffine, scalar *big.Int) *PointAffine { - - var p1Extended, resExtended PointExtended - p1Extended.FromAffine(p1) - resExtended.ScalarMultiplication(&p1Extended, scalar) - p.FromExtended(&resExtended) - - return p -} - -// setInfinity sets p to O (0:1) -func (p *PointAffine) setInfinity() *PointAffine { - p.X.SetZero() - p.Y.SetOne() - return p -} - -//-------- Projective coordinates - -// Set sets p to p1 and return it -func (p *PointProj) Set(p1 *PointProj) *PointProj { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - p.Z.Set(&p1.Z) - return p -} - -// setInfinity sets p to O (0:1:1) -func (p *PointProj) setInfinity() *PointProj { - p.X.SetZero() - p.Y.SetOne() - p.Z.SetOne() - return p -} - -// Equal returns true if p=p1 false otherwise -// If one point is on the affine chart Z=0 it returns false -func (p *PointProj) Equal(p1 *PointProj) bool { - // If one point is infinity, the other must also be infinity. - if p.Z.IsZero() { - return p1.Z.IsZero() - } - // If the other point is infinity, return false since we can't - // the following checks would be incorrect. - if p1.Z.IsZero() { - return false - } - - var lhs, rhs fr.Element - lhs.Mul(&p.X, &p1.Z) - rhs.Mul(&p1.X, &p.Z) - if !lhs.Equal(&rhs) { - return false - } - lhs.Mul(&p.Y, &p1.Z) - rhs.Mul(&p1.Y, &p.Z) - - return lhs.Equal(&rhs) -} - -// IsZero returns true if p=0 false otherwise -func (p *PointProj) IsZero() bool { - return p.X.IsZero() && p.Y.Equal(&p.Z) -} - -// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d -// modifies p -func (p *PointProj) Neg(p1 *PointProj) *PointProj { - p.X.Neg(&p1.X) - p.Y = p1.Y - p.Z = p1.Z - return p -} - -// FromAffine sets p in projective from p in affine -func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - p.Z.SetOne() - return p -} - -// MixedAdd adds a point in projective to a point in affine coordinates -// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-madd-2008-bbjlp -func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { - initOnce.Do(initCurveParams) - - var B, C, D, E, F, G, H, I fr.Element - B.Square(&p1.Z) - C.Mul(&p1.X, &p2.X) - D.Mul(&p1.Y, &p2.Y) - E.Mul(&curveParams.D, &C).Mul(&E, &D) - F.Sub(&B, &E) - G.Add(&B, &E) - H.Add(&p1.X, &p1.Y) - I.Add(&p2.X, &p2.Y) - p.X.Mul(&H, &I). - Sub(&p.X, &C). - Sub(&p.X, &D). - Mul(&p.X, &p1.Z). - Mul(&p.X, &F) - mulByA(&C) - p.Y.Sub(&D, &C). - Mul(&p.Y, &p1.Z). - Mul(&p.Y, &G) - p.Z.Mul(&F, &G) - - return p -} - -// Double adds points in projective coordinates -// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#doubling-dbl-2008-bbjlp -func (p *PointProj) Double(p1 *PointProj) *PointProj { - - var B, C, D, E, F, H, J fr.Element - - B.Add(&p1.X, &p1.Y).Square(&B) - C.Square(&p1.X) - D.Square(&p1.Y) - E.Set(&C) - mulByA(&E) - F.Add(&E, &D) - H.Square(&p1.Z) - J.Sub(&F, &H).Sub(&J, &H) - p.X.Sub(&B, &C). - Sub(&p.X, &D). - Mul(&p.X, &J) - p.Y.Sub(&E, &D).Mul(&p.Y, &F) - p.Z.Mul(&F, &J) - - return p -} - -// Add adds points in projective coordinates -// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp -func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { - initOnce.Do(initCurveParams) - - var A, B, C, D, E, F, G, H, I fr.Element - A.Mul(&p1.Z, &p2.Z) - B.Square(&A) - C.Mul(&p1.X, &p2.X) - D.Mul(&p1.Y, &p2.Y) - E.Mul(&curveParams.D, &C).Mul(&E, &D) - F.Sub(&B, &E) - G.Add(&B, &E) - H.Add(&p1.X, &p1.Y) - I.Add(&p2.X, &p2.Y) - p.X.Mul(&H, &I). - Sub(&p.X, &C). - Sub(&p.X, &D). - Mul(&p.X, &A). - Mul(&p.X, &F) - mulByA(&C) - C.Neg(&C) - p.Y.Add(&D, &C). - Mul(&p.Y, &A). - Mul(&p.Y, &G) - p.Z.Mul(&F, &G) - - return p -} - -// scalarMulWindowed scalar multiplication of a point -// p1 in projective coordinates with a scalar in big.Int -// using the windowed double-and-add method. -func (p *PointProj) scalarMulWindowed(p1 *PointProj, scalar *big.Int) *PointProj { - var _scalar big.Int - _scalar.Set(scalar) - p.Set(p1) - if _scalar.Sign() == -1 { - _scalar.Neg(&_scalar) - p.Neg(p) - } - var resProj PointProj - resProj.setInfinity() - const wordSize = bits.UintSize - sWords := _scalar.Bits() - - for i := len(sWords) - 1; i >= 0; i-- { - ithWord := sWords[i] - for k := 0; k < wordSize; k++ { - resProj.Double(&resProj) - kthBit := (ithWord >> (wordSize - 1 - k)) & 1 - if kthBit == 1 { - resProj.Add(&resProj, p) - } - } - } - - p.Set(&resProj) - return p -} - -// ScalarMultiplication scalar multiplication of a point -// p1 in projective coordinates with a scalar in big.Int -func (p *PointProj) ScalarMultiplication(p1 *PointProj, scalar *big.Int) *PointProj { - return p.scalarMulWindowed(p1, scalar) -} - -// ------- Extended coordinates - -// Set sets p to p1 and return it -func (p *PointExtended) Set(p1 *PointExtended) *PointExtended { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - p.T.Set(&p1.T) - p.Z.Set(&p1.Z) - return p -} - -// IsZero returns true if p=0 false otherwise -func (p *PointExtended) IsZero() bool { - return p.X.IsZero() && p.Y.Equal(&p.Z) && p.T.IsZero() -} - -// Equal returns true if p=p1 false otherwise -// If one point is on the affine chart Z=0 it returns false -func (p *PointExtended) Equal(p1 *PointExtended) bool { - if p.Z.IsZero() || p1.Z.IsZero() { - return false - } - var pAffine, p1Affine PointAffine - pAffine.FromExtended(p) - p1Affine.FromExtended(p1) - return pAffine.Equal(&p1Affine) -} - -// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d -// modifies p -func (p *PointExtended) Neg(p1 *PointExtended) *PointExtended { - p.X.Neg(&p1.X) - p.Y = p1.Y - p.Z = p1.Z - p.T.Neg(&p1.T) - return p -} - -// FromAffine sets p in projective from p in affine -func (p *PointExtended) FromAffine(p1 *PointAffine) *PointExtended { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - p.Z.SetOne() - p.T.Mul(&p1.X, &p1.Y) - return p -} - -// Add adds points in extended coordinates -// See https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd -func (p *PointExtended) Add(p1, p2 *PointExtended) *PointExtended { - var A, B, C, D, E, F, G, H, tmp fr.Element - A.Mul(&p1.X, &p2.X) - B.Mul(&p1.Y, &p2.Y) - C.Mul(&p1.T, &p2.T).Mul(&C, &curveParams.D) - D.Mul(&p1.Z, &p2.Z) - tmp.Add(&p1.X, &p1.Y) - E.Add(&p2.X, &p2.Y). - Mul(&E, &tmp). - Sub(&E, &A). - Sub(&E, &B) - F.Sub(&D, &C) - G.Add(&D, &C) - H.Set(&A) - mulByA(&H) - H.Sub(&B, &H) - - p.X.Mul(&E, &F) - p.Y.Mul(&G, &H) - p.T.Mul(&E, &H) - p.Z.Mul(&F, &G) - - return p -} - -// MixedAdd adds a point in extended coordinates to a point in affine coordinates -// See https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-madd-2008-hwcd-2 -func (p *PointExtended) MixedAdd(p1 *PointExtended, p2 *PointAffine) *PointExtended { - var A, B, C, D, E, F, G, H, tmp fr.Element - - A.Mul(&p2.X, &p1.Z) - B.Mul(&p2.Y, &p1.Z) - - if p1.X.Equal(&A) && p1.Y.Equal(&B) { - p.MixedDouble(p1) - return p - } - - A.Mul(&p1.X, &p2.X) - B.Mul(&p1.Y, &p2.Y) - C.Mul(&p1.Z, &p2.X). - Mul(&C, &p2.Y) - D.Set(&p1.T) - E.Add(&D, &C) - tmp.Sub(&p1.X, &p1.Y) - F.Add(&p2.X, &p2.Y). - Mul(&F, &tmp). - Add(&F, &B). - Sub(&F, &A) - G.Set(&A) - mulByA(&G) - G.Add(&G, &B) - H.Sub(&D, &C) - - p.X.Mul(&E, &F) - p.Y.Mul(&G, &H) - p.T.Mul(&E, &H) - p.Z.Mul(&F, &G) - - return p -} - -// Double adds points in extended coordinates -// Dedicated doubling -// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd -func (p *PointExtended) Double(p1 *PointExtended) *PointExtended { - - var A, B, C, D, E, F, G, H fr.Element - - A.Square(&p1.X) - B.Square(&p1.Y) - C.Square(&p1.Z). - Double(&C) - D.Set(&A) - mulByA(&D) - E.Add(&p1.X, &p1.Y). - Square(&E). - Sub(&E, &A). - Sub(&E, &B) - G.Add(&D, &B) - F.Sub(&G, &C) - H.Sub(&D, &B) - - p.X.Mul(&E, &F) - p.Y.Mul(&G, &H) - p.T.Mul(&H, &E) - p.Z.Mul(&F, &G) - - return p -} - -// MixedDouble adds points in extended coordinates -// Dedicated mixed doubling -// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-mdbl-2008-hwcd -func (p *PointExtended) MixedDouble(p1 *PointExtended) *PointExtended { - - var A, B, D, E, G, H, two fr.Element - two.SetUint64(2) - - A.Square(&p1.X) - B.Square(&p1.Y) - D.Set(&A) - mulByA(&D) - E.Add(&p1.X, &p1.Y). - Square(&E). - Sub(&E, &A). - Sub(&E, &B) - G.Add(&D, &B) - H.Sub(&D, &B) - - p.X.Sub(&G, &two). - Mul(&p.X, &E) - p.Y.Mul(&G, &H) - p.T.Mul(&H, &E) - p.Z.Square(&G). - Sub(&p.Z, &G). - Sub(&p.Z, &G) - - return p -} - -// setInfinity sets p to O (0:1:1:0) -func (p *PointExtended) setInfinity() *PointExtended { - p.X.SetZero() - p.Y.SetOne() - p.Z.SetOne() - p.T.SetZero() - return p -} - -// scalarMulWindowed scalar multiplication of a point -// p1 in extended coordinates with a scalar in big.Int -// using the windowed double-and-add method. -func (p *PointExtended) scalarMulWindowed(p1 *PointExtended, scalar *big.Int) *PointExtended { - var _scalar big.Int - _scalar.Set(scalar) - p.Set(p1) - if _scalar.Sign() == -1 { - _scalar.Neg(&_scalar) - p.Neg(p) - } - var resExtended PointExtended - resExtended.setInfinity() - const wordSize = bits.UintSize - sWords := _scalar.Bits() - - for i := len(sWords) - 1; i >= 0; i-- { - ithWord := sWords[i] - for k := 0; k < wordSize; k++ { - resExtended.Double(&resExtended) - kthBit := (ithWord >> (wordSize - 1 - k)) & 1 - if kthBit == 1 { - resExtended.Add(&resExtended, p) - } - } - } - - p.Set(&resExtended) - return p -} - -// ScalarMultiplication scalar multiplication of a point -// p1 in extended coordinates with a scalar in big.Int -func (p *PointExtended) ScalarMultiplication(p1 *PointExtended, scalar *big.Int) *PointExtended { - return p.scalarMulWindowed(p1, scalar) -} diff --git a/ecc/bls12-378/twistededwards/point_test.go b/ecc/bls12-378/twistededwards/point_test.go deleted file mode 100644 index 78415ebb55..0000000000 --- a/ecc/bls12-378/twistededwards/point_test.go +++ /dev/null @@ -1,969 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package twistededwards - -import ( - "crypto/rand" - "math/big" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -// ------------------------------------------------------------ -// tests - -const ( - nbFuzzShort = 10 - nbFuzz = 100 -) - -func TestReceiverIsOperand(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - // affine - properties.Property("Equal affine: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - params := GetEdwardsCurve() - var p1 PointAffine - p1.Set(¶ms.Base) - - return p1.Equal(&p1) && p1.Equal(¶ms.Base) - }, - )) - - properties.Property("Add affine: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2, p3 PointAffine - p1.Set(¶ms.Base) - p2.Set(¶ms.Base) - p3.Set(¶ms.Base) - - res := true - - p3.Add(&p1, &p2) - p1.Add(&p1, &p2) - res = res && p3.Equal(&p1) - - p1.Set(¶ms.Base) - p2.Add(&p1, &p2) - res = res && p2.Equal(&p3) - - return res - }, - )) - - properties.Property("Double affine: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2 PointAffine - p1.Set(¶ms.Base) - p2.Set(¶ms.Base) - - p2.Double(&p1) - p1.Double(&p1) - - return p2.Equal(&p1) - }, - )) - - properties.Property("Neg affine: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2 PointAffine - p1.Set(¶ms.Base) - p2.Set(¶ms.Base) - - p2.Neg(&p1) - p1.Neg(&p1) - - return p2.Equal(&p1) - }, - )) - - properties.Property("Neg affine: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2 PointAffine - p1.Set(¶ms.Base) - p2.Set(¶ms.Base) - - var s big.Int - s.SetUint64(10) - - p2.ScalarMultiplication(&p1, &s) - p1.ScalarMultiplication(&p1, &s) - - return p2.Equal(&p1) - }, - )) - properties.TestingRun(t, gopter.ConsoleReporter(false)) - - // projective - properties.Property("Equal projective: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - params := GetEdwardsCurve() - var p1, baseProj PointProj - p1.FromAffine(¶ms.Base) - baseProj.FromAffine(¶ms.Base) - - return p1.Equal(&p1) && p1.Equal(&baseProj) - }, - )) - - properties.Property("Add projective: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2, p3 PointProj - p1.FromAffine(¶ms.Base) - p2.FromAffine(¶ms.Base) - p3.FromAffine(¶ms.Base) - - res := true - - p3.Add(&p1, &p2) - p1.Add(&p1, &p2) - res = res && p3.Equal(&p1) - - p1.FromAffine(¶ms.Base) - p2.Add(&p1, &p2) - res = res && p2.Equal(&p3) - - return res - }, - )) - - properties.Property("Double projective: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2 PointProj - p1.FromAffine(¶ms.Base) - p2.FromAffine(¶ms.Base) - - p2.Double(&p1) - p1.Double(&p1) - - return p2.Equal(&p1) - }, - )) - - properties.Property("Neg projective: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2 PointProj - p1.FromAffine(¶ms.Base) - p2.FromAffine(¶ms.Base) - - p2.Neg(&p1) - p1.Neg(&p1) - - return p2.Equal(&p1) - }, - )) - - // extended - properties.Property("Equal extended: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - params := GetEdwardsCurve() - var p1, baseProj PointProj - p1.FromAffine(¶ms.Base) - baseProj.FromAffine(¶ms.Base) - - return p1.Equal(&p1) && p1.Equal(&baseProj) - }, - )) - - properties.Property("Add extended: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2, p3 PointProj - p1.FromAffine(¶ms.Base) - p2.FromAffine(¶ms.Base) - p3.FromAffine(¶ms.Base) - - res := true - - p3.Add(&p1, &p2) - p1.Add(&p1, &p2) - res = res && p3.Equal(&p1) - - p1.FromAffine(¶ms.Base) - p2.Add(&p1, &p2) - res = res && p2.Equal(&p3) - - return res - }, - )) - - properties.Property("Double extended: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2 PointProj - p1.FromAffine(¶ms.Base) - p2.FromAffine(¶ms.Base) - - p2.Double(&p1) - p1.Double(&p1) - - return p2.Equal(&p1) - }, - )) - - properties.Property("Neg extended: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2 PointProj - p1.FromAffine(¶ms.Base) - p2.FromAffine(¶ms.Base) - - p2.Neg(&p1) - p1.Neg(&p1) - - return p2.Equal(&p1) - }, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestField(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - genS := GenBigInt() - - properties.Property("MulByA(x) should match Mul(x, curve.A)", prop.ForAll( - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var z1, z2 fr.Element - z1.SetBigInt(&s) - z2.Mul(&z1, ¶ms.A) - mulByA(&z1) - - return z1.Equal(&z2) - }, - genS, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestOps(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - genS1 := GenBigInt() - genS2 := GenBigInt() - - // affine - properties.Property("(affine) 0+0=2*0=0", prop.ForAll( - func(s1 big.Int) bool { - - var p1, p2, zero PointAffine - zero.setInfinity() - - p1.Add(&zero, &zero) - p2.Double(&zero) - - return p1.IsOnCurve() && p1.Equal(&zero) && p1.Equal(&p2) - }, - genS1, - )) - - properties.Property("(affine) P+0=P", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var p1, p2, zero PointAffine - p1.ScalarMultiplication(¶ms.Base, &s1) - zero.setInfinity() - - p2.Add(&p1, &zero) - - return p2.IsOnCurve() && p2.Equal(&p1) - }, - genS1, - )) - - properties.Property("(affine) P+(-P)=O", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var p1, p2 PointAffine - p1.ScalarMultiplication(¶ms.Base, &s1) - p2.Neg(&p1) - - p1.Add(&p1, &p2) - - var one fr.Element - one.SetOne() - - return p1.IsOnCurve() && p1.IsZero() - }, - genS1, - )) - - properties.Property("(affine) P+P=2*P", prop.ForAll( - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var p1, p2, inf PointAffine - p1.ScalarMultiplication(¶ms.Base, &s) - p2.ScalarMultiplication(¶ms.Base, &s) - - p1.Add(&p1, &p2) - p2.Double(&p2) - - return p1.IsOnCurve() && p1.Equal(&p2) && !p1.Equal(&inf) - }, - genS1, - )) - - properties.Property("(affine) [a]P+[b]P = [a+b]P", prop.ForAll( - func(s1, s2 big.Int) bool { - - params := GetEdwardsCurve() - - var p1, p2, p3, inf PointAffine - inf.X.SetZero() - inf.Y.SetZero() - p1.ScalarMultiplication(¶ms.Base, &s1) - p2.ScalarMultiplication(¶ms.Base, &s2) - p3.Set(¶ms.Base) - - p2.Add(&p1, &p2) - - s1.Add(&s1, &s2) - p3.ScalarMultiplication(¶ms.Base, &s1) - - return p2.IsOnCurve() && p3.Equal(&p2) && !p3.Equal(&inf) - }, - genS1, - genS2, - )) - - properties.Property("(affine) [a]P+[-a]P = O", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var p1, p2, inf PointAffine - inf.X.SetZero() - inf.Y.SetOne() - p1.ScalarMultiplication(¶ms.Base, &s1) - s1.Neg(&s1) - p2.ScalarMultiplication(¶ms.Base, &s1) - - p2.Add(&p1, &p2) - - return p2.IsOnCurve() && p2.Equal(&inf) - }, - genS1, - )) - - properties.Property("(affine) [5]P=[2][2]P+P", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var p1, p2 PointAffine - p1.ScalarMultiplication(¶ms.Base, &s1) - - five := big.NewInt(5) - p2.Double(&p1).Double(&p2).Add(&p2, &p1) - p1.ScalarMultiplication(&p1, five) - - return p2.IsOnCurve() && p2.Equal(&p1) - }, - genS1, - )) - - // projective - properties.Property("(projective) 0+0=2*0=0", prop.ForAll( - func(s1 big.Int) bool { - - var p1, p2, zero PointProj - zero.setInfinity() - - p1.Add(&zero, &zero) - p2.Double(&zero) - - return p1.Equal(&zero) && p1.Equal(&p2) - }, - genS1, - )) - - properties.Property("(projective) P+0=P", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var baseProj, p1, p2, zero PointProj - baseProj.FromAffine(¶ms.Base) - p1.ScalarMultiplication(&baseProj, &s1) - zero.setInfinity() - - p2.Add(&p1, &zero) - - return p2.Equal(&p1) - }, - genS1, - )) - - properties.Property("(projective) P+(-P)=O", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var baseProj, p1, p2, p PointProj - baseProj.FromAffine(¶ms.Base) - p1.ScalarMultiplication(&baseProj, &s1) - p2.Neg(&p1) - - p.Add(&p1, &p2) - - return p.IsZero() - }, - genS1, - )) - - properties.Property("(projective) P+P=2*P", prop.ForAll( - - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var baseProj, p1, p2, p PointProj - baseProj.FromAffine(¶ms.Base) - p.ScalarMultiplication(&baseProj, &s) - - p1.Add(&p, &p) - p2.Double(&p) - - return p1.Equal(&p2) - }, - genS1, - )) - - properties.Property("(projective) [5]P=[2][2]P+P", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var baseProj, p1, p2 PointProj - baseProj.FromAffine(¶ms.Base) - p1.ScalarMultiplication(&baseProj, &s1) - - five := big.NewInt(5) - p2.Double(&p1).Double(&p2).Add(&p2, &p1) - p1.ScalarMultiplication(&p1, five) - - return p2.Equal(&p1) - }, - genS1, - )) - - // extended - properties.Property("(extended) 0+0=0", prop.ForAll( - func(s1 big.Int) bool { - - var p1, zero PointExtended - zero.setInfinity() - - p1.Add(&zero, &zero) - - return p1.Equal(&zero) - }, - genS1, - )) - - properties.Property("(extended) P+0=P", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var baseExtended, p1, p2, zero PointExtended - baseExtended.FromAffine(¶ms.Base) - p1.ScalarMultiplication(&baseExtended, &s1) - zero.setInfinity() - - p2.Add(&p1, &zero) - - return p2.Equal(&p1) - }, - genS1, - )) - - properties.Property("(extended) P+(-P)=O", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var baseExtended, p1, p2, p PointExtended - baseExtended.FromAffine(¶ms.Base) - p1.ScalarMultiplication(&baseExtended, &s1) - p2.Neg(&p1) - - p.Add(&p1, &p2) - - return p.IsZero() - }, - genS1, - )) - - properties.Property("(extended) P+P=2*P", prop.ForAll( - - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var baseExtended, p1, p2, p PointExtended - baseExtended.FromAffine(¶ms.Base) - p.ScalarMultiplication(&baseExtended, &s) - - p1.Add(&p, &p) - p2.Double(&p) - - return p1.Equal(&p2) - }, - genS1, - )) - - properties.Property("(extended) [5]P=[2][2]P+P", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var baseExtended, p1, p2 PointExtended - baseExtended.FromAffine(¶ms.Base) - p1.ScalarMultiplication(&baseExtended, &s1) - - five := big.NewInt(5) - p2.Double(&p1).Double(&p2).Add(&p2, &p1) - p1.ScalarMultiplication(&p1, five) - - return p2.Equal(&p1) - }, - genS1, - )) - - // mixed affine+extended - properties.Property("(mixed affine+extended) P+(-P)=O", prop.ForAll( - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var baseExtended, pExtended, p PointExtended - var pAffine PointAffine - baseExtended.FromAffine(¶ms.Base) - pExtended.ScalarMultiplication(&baseExtended, &s) - pAffine.ScalarMultiplication(¶ms.Base, &s) - pAffine.Neg(&pAffine) - - p.MixedAdd(&pExtended, &pAffine) - - return p.IsZero() - }, - genS1, - )) - - properties.Property("(mixed affine+extended) P+P=2*P", prop.ForAll( - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var baseExtended, pExtended, p, p2 PointExtended - var pAffine PointAffine - baseExtended.FromAffine(¶ms.Base) - pExtended.ScalarMultiplication(&baseExtended, &s) - pAffine.ScalarMultiplication(¶ms.Base, &s) - - p.MixedAdd(&pExtended, &pAffine) - p2.MixedDouble(&pExtended) - - return p.Equal(&p2) - }, - genS1, - )) - - // mixed affine+projective - properties.Property("(mixed affine+proj) P+(-P)=O", prop.ForAll( - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var baseProj, pProj, p PointProj - var pAffine PointAffine - baseProj.FromAffine(¶ms.Base) - pProj.ScalarMultiplication(&baseProj, &s) - pAffine.ScalarMultiplication(¶ms.Base, &s) - pAffine.Neg(&pAffine) - - p.MixedAdd(&pProj, &pAffine) - - return p.IsZero() - }, - genS1, - )) - - properties.Property("(mixed affine+proj) P+P=2*P", prop.ForAll( - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var baseProj, pProj, p, p2 PointProj - var pAffine PointAffine - baseProj.FromAffine(¶ms.Base) - pProj.ScalarMultiplication(&baseProj, &s) - pAffine.ScalarMultiplication(¶ms.Base, &s) - - p.MixedAdd(&pProj, &pAffine) - p2.Double(&pProj) - - return p.Equal(&p2) - }, - genS1, - )) - - properties.Property("scalar multiplication in Proj vs Ext should be consistent", prop.ForAll( - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var baseProj PointProj - var baseExt PointExtended - var p1, p2 PointAffine - baseProj.FromAffine(¶ms.Base) - baseProj.ScalarMultiplication(&baseProj, &s) - baseExt.FromAffine(¶ms.Base) - baseExt.ScalarMultiplication(&baseExt, &s) - - p1.FromProj(&baseProj) - p2.FromExtended(&baseExt) - - return p1.Equal(&p2) - }, - genS1, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestMarshal(t *testing.T) { - t.Parallel() - initOnce.Do(initCurveParams) - - var point, unmarshalPoint PointAffine - point.Set(&curveParams.Base) - for i := 0; i < 20; i++ { - b := point.Marshal() - unmarshalPoint.Unmarshal(b) - if !point.Equal(&unmarshalPoint) { - t.Fatal("error unmarshal(marshal(point))") - } - point.Add(&point, &curveParams.Base) - } -} - -// GenBigInt generates a big.Int -// TODO @thomas we use fr size as max bound here -func GenBigInt() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var s big.Int - var b [fr.Bytes]byte - _, err := rand.Read(b[:]) //#nosec G404 weak rng is fine here - if err != nil { - panic(err) - } - s.SetBytes(b[:]) - genResult := gopter.NewGenResult(s, gopter.NoShrinker) - return genResult - } -} - -// ------------------------------------------------------------ -// benches - -func BenchmarkProjEqual(b *testing.B) { - params := GetEdwardsCurve() - - var scalar fr.Element - if _, err := scalar.SetRandom(); err != nil { - b.Fatalf("error generating random scalar: %v", err) - } - - var baseProj PointProj - baseProj.FromAffine(¶ms.Base) - var a PointProj - a.ScalarMultiplication(&baseProj, big.NewInt(42)) - - b.Run("equal", func(b *testing.B) { - aZScaled := a - aZScaled.X.Mul(&aZScaled.X, &scalar) - aZScaled.Y.Mul(&aZScaled.Y, &scalar) - aZScaled.Z.Mul(&aZScaled.Z, &scalar) - - // Check the setup. - if !a.Equal(&aZScaled) { - b.Fatalf("invalid test setup") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Equal(&aZScaled) - } - }) - - b.Run("not equal", func(b *testing.B) { - var aPlus1 PointProj - aPlus1.Add(&a, &baseProj) - - // Check the setup. - if a.Equal(&aPlus1) { - b.Fatalf("invalid test setup") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Equal(&aPlus1) - } - }) -} - -func BenchmarkScalarMulExtended(b *testing.B) { - params := GetEdwardsCurve() - var a PointExtended - var s big.Int - a.FromAffine(¶ms.Base) - s.SetString("52435875175126190479447705081859658376581184513", 10) - s.Add(&s, ¶ms.Order) - - var doubleAndAdd PointExtended - - b.ResetTimer() - for j := 0; j < b.N; j++ { - doubleAndAdd.ScalarMultiplication(&a, &s) - } -} - -func BenchmarkScalarMulProjective(b *testing.B) { - params := GetEdwardsCurve() - var a PointProj - var s big.Int - a.FromAffine(¶ms.Base) - s.SetString("52435875175126190479447705081859658376581184513", 10) - s.Add(&s, ¶ms.Order) - - var doubleAndAdd PointProj - - b.ResetTimer() - for j := 0; j < b.N; j++ { - doubleAndAdd.ScalarMultiplication(&a, &s) - } -} - -func BenchmarkNeg(b *testing.B) { - params := GetEdwardsCurve() - var s big.Int - s.SetString("52435875175126190479447705081859658376581184513", 10) - - b.Run("Affine", func(b *testing.B) { - var point PointAffine - point.ScalarMultiplication(¶ms.Base, &s) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - point.Neg(&point) - } - }) - b.Run("Projective", func(b *testing.B) { - var baseProj PointProj - baseProj.FromAffine(¶ms.Base) - var point PointProj - point.ScalarMultiplication(&baseProj, &s) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - point.Neg(&point) - } - }) - b.Run("Extended", func(b *testing.B) { - var baseProj PointExtended - baseProj.FromAffine(¶ms.Base) - var point PointExtended - point.ScalarMultiplication(&baseProj, &s) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - point.Neg(&point) - } - }) -} - -func BenchmarkMixedAdd(b *testing.B) { - params := GetEdwardsCurve() - var s big.Int - s.SetString("52435875175126190479447705081859658376581184513", 10) - var point PointAffine - point.ScalarMultiplication(¶ms.Base, &s) - - b.Run("Projective", func(b *testing.B) { - var accum PointProj - accum.setInfinity() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - accum.MixedAdd(&accum, &point) - } - }) - b.Run("Extended", func(b *testing.B) { - var accum PointExtended - accum.setInfinity() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - accum.MixedAdd(&accum, &point) - } - }) -} - -func BenchmarkAdd(b *testing.B) { - params := GetEdwardsCurve() - var s big.Int - s.SetString("52435875175126190479447705081859658376581184513", 10) - - b.Run("Affine", func(b *testing.B) { - var point PointAffine - point.ScalarMultiplication(¶ms.Base, &s) - var accum PointAffine - accum.setInfinity() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - accum.Add(&accum, &point) - } - }) - b.Run("Projective", func(b *testing.B) { - var pointAff PointAffine - pointAff.ScalarMultiplication(¶ms.Base, &s) - var accum, point PointProj - point.FromAffine(&pointAff) - accum.setInfinity() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - accum.Add(&accum, &point) - } - }) - b.Run("Extended", func(b *testing.B) { - var pointAff PointAffine - pointAff.ScalarMultiplication(¶ms.Base, &s) - var accum, point PointExtended - point.FromAffine(&pointAff) - accum.setInfinity() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - accum.Add(&accum, &point) - } - }) -} - -func BenchmarkIsOnCurve(b *testing.B) { - params := GetEdwardsCurve() - var s big.Int - s.SetString("52435875175126190479447705081859658376581184513", 10) - - b.Run("positive", func(b *testing.B) { - var point PointAffine - point.ScalarMultiplication(¶ms.Base, &s) - - if !point.IsOnCurve() { - b.Fatal("point should must be on curve") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = point.IsOnCurve() - } - }) - - b.Run("negative", func(b *testing.B) { - var point PointAffine - point.ScalarMultiplication(¶ms.Base, &s) - point.X.Add(&point.X, &point.X) - - if point.IsOnCurve() { - b.Fatal("point should not be on curve") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = point.IsOnCurve() - } - }) -} diff --git a/ecc/bw6-633/g1.go b/ecc/bw6-633/g1.go index b1673bccdd..db4e88dcaa 100644 --- a/ecc/bw6-633/g1.go +++ b/ecc/bw6-633/g1.go @@ -659,7 +659,6 @@ func (p *G1Jac) ClearCofactor(q *G1Jac) *G1Jac { p.phi(&L1).AddAssign(&L0) return p - } // JointScalarMultiplication computes [s1]a1+[s2]a2 using Strauss-Shamir technique diff --git a/ecc/bw6-633/g2.go b/ecc/bw6-633/g2.go index 6a236a3fd8..6dfbaa0033 100644 --- a/ecc/bw6-633/g2.go +++ b/ecc/bw6-633/g2.go @@ -667,7 +667,6 @@ func (p *G2Jac) ClearCofactor(q *G2Jac) *G2Jac { p.phi(&L1).AddAssign(&L0) return p - } // ------------------------------------------------------------------------------------------------- diff --git a/ecc/bw6-756/bw6-756.go b/ecc/bw6-756/bw6-756.go deleted file mode 100644 index 8161ced274..0000000000 --- a/ecc/bw6-756/bw6-756.go +++ /dev/null @@ -1,134 +0,0 @@ -// Package bw6756 efficient elliptic curve, pairing and hash to curve implementation for bw6-756. -// -// bw6-756: A Brezing--Weng curve (2-chain with bls12-378) -// -// embedding degree k=6 -// seed x₀=11045256207009841153 -// 𝔽p: p=366325390957376286590726555727219947825377821289246188278797409783441745356050456327989347160777465284190855125642086860525706497928518803244008749360363712553766506755227344593404398783886857865261088226271336335268413437902849 -// 𝔽r: r=605248206075306171733248481581800960739847691770924913753520744034740935903401304776283802348837311170974282940417 -// (E/𝔽p): Y²=X³+1 -// (Eₜ/𝔽p): Y² = X³+33 (M-type twist) -// r ∣ #E(Fp) and r ∣ #Eₜ(𝔽p) -// -// case t % r % x₀ = 3 -// -// Extension fields tower: -// -// 𝔽p³[u] = 𝔽p/u³-33 -// 𝔽p⁶[v] = 𝔽p³/v²-u -// -// optimal Ate loops: -// -// x₀+1, x₀²-x₀-1 -// -// Security: estimated 126-bit level following [https://eprint.iacr.org/2019/885.pdf] -// (r is 378 bits and p⁶ is 4536 bits) -// -// # Warning -// -// This code has not been audited and is provided as-is. In particular, there is no security guarantees such as constant time implementation or side-channel attack resistance. -package bw6756 - -import ( - "math/big" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) - -// ID BW6_756 ID -const ID = ecc.BW6_756 - -// aCurveCoeff is the a coefficients of the curve Y²=X³+ax+b -var aCurveCoeff fp.Element -var bCurveCoeff fp.Element - -// bTwistCurveCoeff b coeff of the twist (defined over 𝔽p) curve -var bTwistCurveCoeff fp.Element - -// generators of the r-torsion group, resp. in ker(pi-id), ker(Tr) -var g1Gen G1Jac -var g2Gen G2Jac - -var g1GenAff G1Affine -var g2GenAff G2Affine - -// point at infinity -var g1Infinity G1Jac -var g2Infinity G2Jac - -// optimal Ate loop counters -var LoopCounter [191]int8 -var LoopCounter1 [191]int8 - -// Parameters useful for the GLV scalar multiplication. The third roots define the -// endomorphisms ϕ₁ and ϕ₂ for and . lambda is such that lies above -// in the ring Z[ϕ]. More concretely it's the associated eigenvalue -// of ϕ₁ (resp ϕ₂) restricted to (resp ) -// see https://www.cosic.esat.kuleuven.be/nessie/reports/phase2/GLV.pdf -var thirdRootOneG1 fp.Element -var thirdRootOneG2 fp.Element -var lambdaGLV big.Int - -// glvBasis stores R-linearly independent vectors (a,b), (c,d) -// in ker((u,v) → u+vλ[r]), and their determinant -var glvBasis ecc.Lattice - -// generator of the curve -var xGen big.Int - -func init() { - aCurveCoeff.SetUint64(0) - bCurveCoeff.SetOne() - bTwistCurveCoeff.MulByNonResidue(&bCurveCoeff) - - // E(3,y) * cofactor - g1Gen.X.SetString("286035407532233812057489253822435660910062665263942803649298092690795938518721117964189338863504082781482751182899097859005716378386344565362972291164604792882058761734674709131229927253172681714645554597102571818586966737895501") - g1Gen.Y.SetString("250540671634276190125882738767359258920233951524378923555904955920886135268516617166458911260101792169356480449980342047600821278990712908224386045486820019065641642853528653616206514851361917670279865872746658429844440125628329") - g1Gen.Z.SetOne() - - // E(1,y) * cofactor - g2Gen.X.SetString("270164867145533700243149075881223225204067215320977230235816769808318087164726583740674261721395147407122688542569094772405350936550575160051166652281373572919753182191250641388443572739372443497834910784618354592418817138212395") - g2Gen.Y.SetString("296695446824796322573519291690935001172593568823998954880196613542512471119971074118215403545906873458039024520146929054366200365532511334310660691775675887531695313103875249166779149013653038059140912965769351316868363001510735") - g2Gen.Z.SetOne() - - g1GenAff.FromJacobian(&g1Gen) - g2GenAff.FromJacobian(&g2Gen) - - // x₀+1 - LoopCounter = [191]int8{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, -1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - - // x₀³-x₀²-x₀ - T, _ := new(big.Int).SetString("1347495683935914696108087318582641220368021451587784278015", 10) - ecc.NafDecomposition(T, LoopCounter1[:]) - - // (X,Y,Z) = (1,1,0) - g1Infinity.X.SetOne() - g1Infinity.Y.SetOne() - g2Infinity.X.SetOne() - g2Infinity.Y.SetOne() - - thirdRootOneG2.SetString("99497571833115712246976573293861816254377473715694998268521440373748988342600853091641405554217584221455319677515385376103078837731420131015700054219263015095146628991433981753068027965212839748934246550470657") - thirdRootOneG1.Square(&thirdRootOneG2) - lambdaGLV.SetString("164391353554439166353793911729193406645071739502673898176639736370075683438438023898983435337729", 10) // (x⁵-3x⁴+3x³-x+1) - _r := fr.Modulus() - ecc.PrecomputeLattice(_r, &lambdaGLV, &glvBasis) - - xGen.SetString("11045256207009841153", 10) - -} - -// Generators return the generators of the r-torsion group, resp. in ker(pi-id), ker(Tr) -func Generators() (g1Jac G1Jac, g2Jac G2Jac, g1Aff G1Affine, g2Aff G2Affine) { - g1Aff = g1GenAff - g2Aff = g2GenAff - g1Jac = g1Gen - g2Jac = g2Gen - return -} - -// CurveCoefficients returns the a, b coefficients of the curve equation. -func CurveCoefficients() (a, b fp.Element) { - return aCurveCoeff, bCurveCoeff -} diff --git a/ecc/bw6-756/ecdsa/doc.go b/ecc/bw6-756/ecdsa/doc.go deleted file mode 100644 index b8c0f5fcfb..0000000000 --- a/ecc/bw6-756/ecdsa/doc.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package ecdsa provides ECDSA signature scheme on the bw6-756 curve. -// -// The implementation is adapted from https://pkg.go.dev/crypto/ecdsa. -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// -// Documentation: -// - Wikipedia: https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm -// - FIPS 186-4: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf -// - SEC 1, v-2: https://www.secg.org/sec1-v2.pdf -package ecdsa diff --git a/ecc/bw6-756/ecdsa/ecdsa.go b/ecc/bw6-756/ecdsa/ecdsa.go deleted file mode 100644 index bf32e9cacc..0000000000 --- a/ecc/bw6-756/ecdsa/ecdsa.go +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package ecdsa - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "crypto/sha512" - "crypto/subtle" - "hash" - "io" - "math/big" - - "github.com/consensys/gnark-crypto/ecc/bw6-756" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/signature" -) - -const ( - sizeFr = fr.Bytes - sizeFrBits = fr.Bits - sizeFp = fp.Bytes - sizePublicKey = sizeFp - sizePrivateKey = sizeFr + sizePublicKey - sizeSignature = 2 * sizeFr -) - -var order = fr.Modulus() - -// PublicKey represents an ECDSA public key -type PublicKey struct { - A bw6756.G1Affine -} - -// PrivateKey represents an ECDSA private key -type PrivateKey struct { - PublicKey PublicKey - scalar [sizeFr]byte // secret scalar, in big Endian -} - -// Signature represents an ECDSA signature -type Signature struct { - R, S [sizeFr]byte -} - -var one = new(big.Int).SetInt64(1) - -// randFieldElement returns a random element of the order of the given -// curve using the procedure given in FIPS 186-4, Appendix B.5.1. -func randFieldElement(rand io.Reader) (k *big.Int, err error) { - b := make([]byte, fr.Bits/8+8) - _, err = io.ReadFull(rand, b) - if err != nil { - return - } - - k = new(big.Int).SetBytes(b) - n := new(big.Int).Sub(order, one) - k.Mod(k, n) - k.Add(k, one) - return -} - -// GenerateKey generates a public and private key pair. -func GenerateKey(rand io.Reader) (*PrivateKey, error) { - - k, err := randFieldElement(rand) - if err != nil { - return nil, err - - } - _, _, g, _ := bw6756.Generators() - - privateKey := new(PrivateKey) - k.FillBytes(privateKey.scalar[:sizeFr]) - privateKey.PublicKey.A.ScalarMultiplication(&g, k) - return privateKey, nil -} - -// HashToInt converts a hash value to an integer. Per FIPS 186-4, Section 6.4, -// we use the left-most bits of the hash to match the bit-length of the order of -// the curve. This also performs Step 5 of SEC 1, Version 2.0, Section 4.1.3. -func HashToInt(hash []byte) *big.Int { - if len(hash) > sizeFr { - hash = hash[:sizeFr] - } - ret := new(big.Int).SetBytes(hash) - excess := ret.BitLen() - sizeFrBits - if excess > 0 { - ret.Rsh(ret, uint(excess)) - } - return ret -} - -type zr struct{} - -// Read replaces the contents of dst with zeros. It is safe for concurrent use. -func (zr) Read(dst []byte) (n int, err error) { - for i := range dst { - dst[i] = 0 - } - return len(dst), nil -} - -var zeroReader = zr{} - -const ( - aesIV = "gnark-crypto IV." // must be 16 chars (equal block size) -) - -func nonce(privateKey *PrivateKey, hash []byte) (csprng *cipher.StreamReader, err error) { - // This implementation derives the nonce from an AES-CTR CSPRNG keyed by: - // - // SHA2-512(privateKey.scalar ∥ entropy ∥ hash)[:32] - // - // The CSPRNG key is indifferentiable from a random oracle as shown in - // [Coron], the AES-CTR stream is indifferentiable from a random oracle - // under standard cryptographic assumptions (see [Larsson] for examples). - // - // [Coron]: https://cs.nyu.edu/~dodis/ps/merkle.pdf - // [Larsson]: https://web.archive.org/web/20040719170906/https://www.nada.kth.se/kurser/kth/2D1441/semteo03/lecturenotes/assump.pdf - - // Get 256 bits of entropy from rand. - entropy := make([]byte, 32) - _, err = io.ReadFull(rand.Reader, entropy) - if err != nil { - return - - } - - // Initialize an SHA-512 hash context; digest... - md := sha512.New() - md.Write(privateKey.scalar[:sizeFr]) // the private key, - md.Write(entropy) // the entropy, - md.Write(hash) // and the input hash; - key := md.Sum(nil)[:32] // and compute ChopMD-256(SHA-512), - // which is an indifferentiable MAC. - - // Create an AES-CTR instance to use as a CSPRNG. - block, _ := aes.NewCipher(key) - - // Create a CSPRNG that xors a stream of zeros with - // the output of the AES-CTR instance. - csprng = &cipher.StreamReader{ - R: zeroReader, - S: cipher.NewCTR(block, []byte(aesIV)), - } - - return csprng, err -} - -// Equal compares 2 public keys -func (pub *PublicKey) Equal(x signature.PublicKey) bool { - xx, ok := x.(*PublicKey) - if !ok { - return false - } - bpk := pub.Bytes() - bxx := xx.Bytes() - return subtle.ConstantTimeCompare(bpk, bxx) == 1 -} - -// Public returns the public key associated to the private key. -func (privKey *PrivateKey) Public() signature.PublicKey { - var pub PublicKey - pub.A.Set(&privKey.PublicKey.A) - return &pub -} - -// Sign performs the ECDSA signature -// -// k ← 𝔽r (random) -// P = k ⋅ g1Gen -// r = x_P (mod order) -// s = k⁻¹ . (m + sk ⋅ r) -// signature = {r, s} -// -// SEC 1, Version 2.0, Section 4.1.3 -func (privKey *PrivateKey) Sign(message []byte, hFunc hash.Hash) ([]byte, error) { - scalar, r, s, kInv := new(big.Int), new(big.Int), new(big.Int), new(big.Int) - scalar.SetBytes(privKey.scalar[:sizeFr]) - for { - for { - csprng, err := nonce(privKey, message) - if err != nil { - return nil, err - } - k, err := randFieldElement(csprng) - if err != nil { - return nil, err - } - - var P bw6756.G1Affine - P.ScalarMultiplicationBase(k) - kInv.ModInverse(k, order) - - P.X.BigInt(r) - - r.Mod(r, order) - if r.Sign() != 0 { - break - } - } - s.Mul(r, scalar) - - var m *big.Int - if hFunc != nil { - // compute the hash of the message as an integer - dataToHash := make([]byte, len(message)) - copy(dataToHash[:], message[:]) - hFunc.Reset() - _, err := hFunc.Write(dataToHash[:]) - if err != nil { - return nil, err - } - hramBin := hFunc.Sum(nil) - m = HashToInt(hramBin) - } else { - m = HashToInt(message) - } - - s.Add(m, s). - Mul(kInv, s). - Mod(s, order) // order != 0 - if s.Sign() != 0 { - break - } - } - - var sig Signature - r.FillBytes(sig.R[:sizeFr]) - s.FillBytes(sig.S[:sizeFr]) - - return sig.Bytes(), nil -} - -// Verify validates the ECDSA signature -// -// R ?= (s⁻¹ ⋅ m ⋅ Base + s⁻¹ ⋅ R ⋅ publiKey)_x -// -// SEC 1, Version 2.0, Section 4.1.4 -func (publicKey *PublicKey) Verify(sigBin, message []byte, hFunc hash.Hash) (bool, error) { - - // Deserialize the signature - var sig Signature - if _, err := sig.SetBytes(sigBin); err != nil { - return false, err - } - - r, s := new(big.Int), new(big.Int) - r.SetBytes(sig.R[:sizeFr]) - s.SetBytes(sig.S[:sizeFr]) - - sInv := new(big.Int).ModInverse(s, order) - - var m *big.Int - if hFunc != nil { - // compute the hash of the message as an integer - dataToHash := make([]byte, len(message)) - copy(dataToHash[:], message[:]) - hFunc.Reset() - _, err := hFunc.Write(dataToHash[:]) - if err != nil { - return false, err - } - hramBin := hFunc.Sum(nil) - m = HashToInt(hramBin) - } else { - m = HashToInt(message) - } - - u1 := new(big.Int).Mul(m, sInv) - u1.Mod(u1, order) - u2 := new(big.Int).Mul(r, sInv) - u2.Mod(u2, order) - var U bw6756.G1Jac - U.JointScalarMultiplicationBase(&publicKey.A, u1, u2) - - var z big.Int - U.Z.Square(&U.Z). - Inverse(&U.Z). - Mul(&U.Z, &U.X). - BigInt(&z) - - z.Mod(&z, order) - - return z.Cmp(r) == 0, nil - -} diff --git a/ecc/bw6-756/ecdsa/ecdsa_test.go b/ecc/bw6-756/ecdsa/ecdsa_test.go deleted file mode 100644 index 09d9c9a26a..0000000000 --- a/ecc/bw6-756/ecdsa/ecdsa_test.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package ecdsa - -import ( - "crypto/rand" - "crypto/sha256" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "math/big" - "testing" - - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -func TestECDSA(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - properties := gopter.NewProperties(parameters) - - properties.Property("[BW6-756] test the signing and verification", prop.ForAll( - func() bool { - - privKey, _ := GenerateKey(rand.Reader) - publicKey := privKey.PublicKey - - msg := []byte("testing ECDSA") - hFunc := sha256.New() - sig, _ := privKey.Sign(msg, hFunc) - flag, _ := publicKey.Verify(sig, msg, hFunc) - - return flag - }, - )) - - properties.Property("[BW6-756] test the signing and verification (pre-hashed)", prop.ForAll( - func() bool { - - privKey, _ := GenerateKey(rand.Reader) - publicKey := privKey.PublicKey - - msg := []byte("testing ECDSA") - sig, _ := privKey.Sign(msg, nil) - flag, _ := publicKey.Verify(sig, msg, nil) - - return flag - }, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestNonMalleability(t *testing.T) { - - // buffer too big - t.Run("buffer_overflow", func(t *testing.T) { - bsig := make([]byte, 2*sizeFr+1) - var sig Signature - _, err := sig.SetBytes(bsig) - if err != errWrongSize { - t.Fatal("should raise wrong size error") - } - }) - - // R overflows p_mod - t.Run("R_overflow", func(t *testing.T) { - bsig := make([]byte, 2*sizeFr) - r := big.NewInt(1) - frMod := fr.Modulus() - r.Add(r, frMod) - buf := r.Bytes() - copy(bsig, buf[:]) - - var sig Signature - _, err := sig.SetBytes(bsig) - if err != errRBiggerThanRMod { - t.Fatal("should raise error r >= r_mod") - } - }) - - // S overflows p_mod - t.Run("S_overflow", func(t *testing.T) { - bsig := make([]byte, 2*sizeFr) - r := big.NewInt(1) - frMod := fr.Modulus() - r.Add(r, frMod) - buf := r.Bytes() - copy(bsig[sizeFr:], buf[:]) - big.NewInt(1).FillBytes(bsig[:sizeFr]) - - var sig Signature - _, err := sig.SetBytes(bsig) - if err != errSBiggerThanRMod { - t.Fatal("should raise error s >= r_mod") - } - }) - -} - -func TestNoZeros(t *testing.T) { - t.Run("R=0", func(t *testing.T) { - // R is 0 - var sig Signature - big.NewInt(0).FillBytes(sig.R[:]) - big.NewInt(1).FillBytes(sig.S[:]) - bts := sig.Bytes() - var newSig Signature - _, err := newSig.SetBytes(bts) - if err != errZero { - t.Fatal("expected error for zero R") - } - }) - t.Run("S=0", func(t *testing.T) { - // S is 0 - var sig Signature - big.NewInt(1).FillBytes(sig.R[:]) - big.NewInt(0).FillBytes(sig.S[:]) - bts := sig.Bytes() - var newSig Signature - _, err := newSig.SetBytes(bts) - if err != errZero { - t.Fatal("expected error for zero S") - } - }) -} - -// ------------------------------------------------------------ -// benches - -func BenchmarkSignECDSA(b *testing.B) { - - privKey, _ := GenerateKey(rand.Reader) - - msg := []byte("benchmarking ECDSA sign()") - b.ResetTimer() - for i := 0; i < b.N; i++ { - privKey.Sign(msg, nil) - } -} - -func BenchmarkVerifyECDSA(b *testing.B) { - - privKey, _ := GenerateKey(rand.Reader) - msg := []byte("benchmarking ECDSA sign()") - sig, _ := privKey.Sign(msg, nil) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - privKey.PublicKey.Verify(sig, msg, nil) - } -} diff --git a/ecc/bw6-756/ecdsa/marshal.go b/ecc/bw6-756/ecdsa/marshal.go deleted file mode 100644 index 83166dbe7b..0000000000 --- a/ecc/bw6-756/ecdsa/marshal.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package ecdsa - -import ( - "crypto/subtle" - "errors" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "io" - "math/big" -) - -var errWrongSize = errors.New("wrong size buffer") -var errRBiggerThanRMod = errors.New("r >= r_mod") -var errSBiggerThanRMod = errors.New("s >= r_mod") -var errZero = errors.New("zero value") - -// Bytes returns the binary representation of the public key -// follows https://tools.ietf.org/html/rfc8032#section-3.1 -// and returns a compressed representation of the point (x,y) -// -// x, y are the coordinates of the point -// on the curve as big endian integers. -// compressed representation store x with a parity bit to recompute y -func (pk *PublicKey) Bytes() []byte { - var res [sizePublicKey]byte - pkBin := pk.A.Bytes() - subtle.ConstantTimeCopy(1, res[:sizePublicKey], pkBin[:]) - return res[:] -} - -// SetBytes sets p from binary representation in buf. -// buf represents a public key as x||y where x, y are -// interpreted as big endian binary numbers corresponding -// to the coordinates of a point on the curve. -// It returns the number of bytes read from the buffer. -func (pk *PublicKey) SetBytes(buf []byte) (int, error) { - n := 0 - if len(buf) < sizePublicKey { - return n, io.ErrShortBuffer - } - if _, err := pk.A.SetBytes(buf[:sizePublicKey]); err != nil { - return 0, err - } - n += sizeFp - return n, nil -} - -// Bytes returns the binary representation of pk, -// as byte array publicKey||scalar -// where publicKey is as publicKey.Bytes(), and -// scalar is in big endian, of size sizeFr. -func (privKey *PrivateKey) Bytes() []byte { - var res [sizePrivateKey]byte - pubkBin := privKey.PublicKey.A.Bytes() - subtle.ConstantTimeCopy(1, res[:sizePublicKey], pubkBin[:]) - subtle.ConstantTimeCopy(1, res[sizePublicKey:sizePrivateKey], privKey.scalar[:]) - return res[:] -} - -// SetBytes sets pk from buf, where buf is interpreted -// as publicKey||scalar -// where publicKey is as publicKey.Bytes(), and -// scalar is in big endian, of size sizeFr. -// It returns the number byte read. -func (privKey *PrivateKey) SetBytes(buf []byte) (int, error) { - n := 0 - if len(buf) < sizePrivateKey { - return n, io.ErrShortBuffer - } - if _, err := privKey.PublicKey.A.SetBytes(buf[:sizePublicKey]); err != nil { - return 0, err - } - n += sizePublicKey - subtle.ConstantTimeCopy(1, privKey.scalar[:], buf[sizePublicKey:sizePrivateKey]) - n += sizeFr - return n, nil -} - -// Bytes returns the binary representation of sig -// as a byte array of size 2*sizeFr r||s -func (sig *Signature) Bytes() []byte { - var res [sizeSignature]byte - subtle.ConstantTimeCopy(1, res[:sizeFr], sig.R[:]) - subtle.ConstantTimeCopy(1, res[sizeFr:], sig.S[:]) - return res[:] -} - -// SetBytes sets sig from a buffer in binary. -// buf is read interpreted as r||s -// It returns the number of bytes read from buf. -func (sig *Signature) SetBytes(buf []byte) (int, error) { - n := 0 - if len(buf) != sizeSignature { - return n, errWrongSize - } - - // S, R < R_mod (to avoid malleability) - frMod := fr.Modulus() - zero := big.NewInt(0) - bufBigInt := new(big.Int) - bufBigInt.SetBytes(buf[:sizeFr]) - if bufBigInt.Cmp(zero) == 0 { - return 0, errZero - } - if bufBigInt.Cmp(frMod) != -1 { - return 0, errRBiggerThanRMod - } - bufBigInt.SetBytes(buf[sizeFr : 2*sizeFr]) - if bufBigInt.Cmp(zero) == 0 { - return 0, errZero - } - if bufBigInt.Cmp(frMod) != -1 { - return 0, errSBiggerThanRMod - } - - subtle.ConstantTimeCopy(1, sig.R[:], buf[:sizeFr]) - n += sizeFr - subtle.ConstantTimeCopy(1, sig.S[:], buf[sizeFr:2*sizeFr]) - n += sizeFr - return n, nil -} diff --git a/ecc/bw6-756/ecdsa/marshal_test.go b/ecc/bw6-756/ecdsa/marshal_test.go deleted file mode 100644 index 5a73204ca0..0000000000 --- a/ecc/bw6-756/ecdsa/marshal_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package ecdsa - -import ( - "crypto/rand" - "crypto/subtle" - "testing" - - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -const ( - nbFuzzShort = 10 - nbFuzz = 100 -) - -func TestSerialization(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BW6-756] ECDSA serialization: SetBytes(Bytes()) should stay the same", prop.ForAll( - func() bool { - privKey, _ := GenerateKey(rand.Reader) - - var end PrivateKey - buf := privKey.Bytes() - n, err := end.SetBytes(buf[:]) - if err != nil { - return false - } - if n != sizePrivateKey { - return false - } - - return end.PublicKey.Equal(&privKey.PublicKey) && subtle.ConstantTimeCompare(end.scalar[:], privKey.scalar[:]) == 1 - - }, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} diff --git a/ecc/bw6-756/fp/arith.go b/ecc/bw6-756/fp/arith.go deleted file mode 100644 index 6f281563b3..0000000000 --- a/ecc/bw6-756/fp/arith.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -import ( - "math/bits" -) - -// madd0 hi = a*b + c (discards lo bits) -func madd0(a, b, c uint64) (hi uint64) { - var carry, lo uint64 - hi, lo = bits.Mul64(a, b) - _, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - return -} - -// madd1 hi, lo = a*b + c -func madd1(a, b, c uint64) (hi uint64, lo uint64) { - var carry uint64 - hi, lo = bits.Mul64(a, b) - lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - return -} - -// madd2 hi, lo = a*b + c + d -func madd2(a, b, c, d uint64) (hi uint64, lo uint64) { - var carry uint64 - hi, lo = bits.Mul64(a, b) - c, carry = bits.Add64(c, d, 0) - hi, _ = bits.Add64(hi, 0, carry) - lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - return -} - -func madd3(a, b, c, d, e uint64) (hi uint64, lo uint64) { - var carry uint64 - hi, lo = bits.Mul64(a, b) - c, carry = bits.Add64(c, d, 0) - hi, _ = bits.Add64(hi, 0, carry) - lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, e, carry) - return -} -func max(a int, b int) int { - if a > b { - return a - } - return b -} - -func min(a int, b int) int { - if a < b { - return a - } - return b -} diff --git a/ecc/bw6-756/fp/asm.go b/ecc/bw6-756/fp/asm.go deleted file mode 100644 index 0481989ec6..0000000000 --- a/ecc/bw6-756/fp/asm.go +++ /dev/null @@ -1,27 +0,0 @@ -//go:build !noadx -// +build !noadx - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -import "golang.org/x/sys/cpu" - -var ( - supportAdx = cpu.X86.HasADX && cpu.X86.HasBMI2 - _ = supportAdx -) diff --git a/ecc/bw6-756/fp/asm_noadx.go b/ecc/bw6-756/fp/asm_noadx.go deleted file mode 100644 index 92f8cc0f42..0000000000 --- a/ecc/bw6-756/fp/asm_noadx.go +++ /dev/null @@ -1,28 +0,0 @@ -//go:build noadx -// +build noadx - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -// note: this is needed for test purposes, as dynamically changing supportAdx doesn't flag -// certain errors (like fatal error: missing stackmap) -// this ensures we test all asm path. -var ( - supportAdx = false - _ = supportAdx -) diff --git a/ecc/bw6-756/fp/bw6_utils.go b/ecc/bw6-756/fp/bw6_utils.go deleted file mode 100644 index 58a2d0d107..0000000000 --- a/ecc/bw6-756/fp/bw6_utils.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2020 ConsenSys AG -// -// 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 fp - -// MulByNonResidue multiplies a fp.Element by 33 -func (z *Element) MulByNonResidue(x *Element) *Element { - var t Element - t.Double(x). - Double(&t). - Double(&t). - Double(&t). - Double(&t) - z.Add(&t, x) - return z -} diff --git a/ecc/bw6-756/fp/doc.go b/ecc/bw6-756/fp/doc.go deleted file mode 100644 index a94273407e..0000000000 --- a/ecc/bw6-756/fp/doc.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package fp contains field arithmetic operations for modulus = 0xf76adb...000001. -// -// The API is similar to math/big (big.Int), but the operations are significantly faster (up to 20x for the modular multiplication on amd64, see also https://hackmd.io/@gnark/modular_multiplication) -// -// The modulus is hardcoded in all the operations. -// -// Field elements are represented as an array, and assumed to be in Montgomery form in all methods: -// -// type Element [12]uint64 -// -// # Usage -// -// Example API signature: -// -// // Mul z = x * y (mod q) -// func (z *Element) Mul(x, y *Element) *Element -// -// and can be used like so: -// -// var a, b Element -// a.SetUint64(2) -// b.SetString("984896738") -// a.Mul(a, b) -// a.Sub(a, a) -// .Add(a, b) -// .Inv(a) -// b.Exp(b, new(big.Int).SetUint64(42)) -// -// Modulus q = -// -// q[base10] = 366325390957376286590726555727219947825377821289246188278797409783441745356050456327989347160777465284190855125642086860525706497928518803244008749360363712553766506755227344593404398783886857865261088226271336335268413437902849 -// q[base16] = 0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7ded0b685e868524ec033c7e63f868400000000000000000001 -// -// # Warning -// -// This code has not been audited and is provided as-is. In particular, there is no security guarantees such as constant time implementation or side-channel attack resistance. -package fp diff --git a/ecc/bw6-756/fp/element.go b/ecc/bw6-756/fp/element.go deleted file mode 100644 index f3e79a5bad..0000000000 --- a/ecc/bw6-756/fp/element.go +++ /dev/null @@ -1,2724 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -import ( - "crypto/rand" - "encoding/binary" - "errors" - "io" - "math/big" - "math/bits" - "reflect" - "strconv" - "strings" - - "github.com/bits-and-blooms/bitset" - "github.com/consensys/gnark-crypto/field/hash" - "github.com/consensys/gnark-crypto/field/pool" -) - -// Element represents a field element stored on 12 words (uint64) -// -// Element are assumed to be in Montgomery form in all methods. -// -// Modulus q = -// -// q[base10] = 366325390957376286590726555727219947825377821289246188278797409783441745356050456327989347160777465284190855125642086860525706497928518803244008749360363712553766506755227344593404398783886857865261088226271336335268413437902849 -// q[base16] = 0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7ded0b685e868524ec033c7e63f868400000000000000000001 -// -// # Warning -// -// This code has not been audited and is provided as-is. In particular, there is no security guarantees such as constant time implementation or side-channel attack resistance. -type Element [12]uint64 - -const ( - Limbs = 12 // number of 64 bits words needed to represent a Element - Bits = 756 // number of bits needed to represent a Element - Bytes = 96 // number of bytes needed to represent a Element -) - -// Field modulus q -const ( - q0 uint64 = 1 - q1 uint64 = 3731203976813871104 - q2 uint64 = 15039355238879481536 - q3 uint64 = 4828608925799409630 - q4 uint64 = 16326337093237622437 - q5 uint64 = 756237273905161798 - q6 uint64 = 16934317532427647658 - q7 uint64 = 14755673041361585881 - q8 uint64 = 18154628166362162086 - q9 uint64 = 6671956210750770825 - q10 uint64 = 16333450281447942351 - q11 uint64 = 4352613195430282 -) - -var qElement = Element{ - q0, - q1, - q2, - q3, - q4, - q5, - q6, - q7, - q8, - q9, - q10, - q11, -} - -var _modulus big.Int // q stored as big.Int - -// Modulus returns q as a big.Int -// -// q[base10] = 366325390957376286590726555727219947825377821289246188278797409783441745356050456327989347160777465284190855125642086860525706497928518803244008749360363712553766506755227344593404398783886857865261088226271336335268413437902849 -// q[base16] = 0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7ded0b685e868524ec033c7e63f868400000000000000000001 -func Modulus() *big.Int { - return new(big.Int).Set(&_modulus) -} - -// q + r'.r = 1, i.e., qInvNeg = - q⁻¹ mod r -// used for Montgomery reduction -const qInvNeg uint64 = 18446744073709551615 - -func init() { - _modulus.SetString("f76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7ded0b685e868524ec033c7e63f868400000000000000000001", 16) -} - -// NewElement returns a new Element from a uint64 value -// -// it is equivalent to -// -// var v Element -// v.SetUint64(...) -func NewElement(v uint64) Element { - z := Element{v} - z.Mul(&z, &rSquare) - return z -} - -// SetUint64 sets z to v and returns z -func (z *Element) SetUint64(v uint64) *Element { - // sets z LSB to v (non-Montgomery form) and convert z to Montgomery form - *z = Element{v} - return z.Mul(z, &rSquare) // z.toMont() -} - -// SetInt64 sets z to v and returns z -func (z *Element) SetInt64(v int64) *Element { - - // absolute value of v - m := v >> 63 - z.SetUint64(uint64((v ^ m) - m)) - - if m != 0 { - // v is negative - z.Neg(z) - } - - return z -} - -// Set z = x and returns z -func (z *Element) Set(x *Element) *Element { - z[0] = x[0] - z[1] = x[1] - z[2] = x[2] - z[3] = x[3] - z[4] = x[4] - z[5] = x[5] - z[6] = x[6] - z[7] = x[7] - z[8] = x[8] - z[9] = x[9] - z[10] = x[10] - z[11] = x[11] - return z -} - -// SetInterface converts provided interface into Element -// returns an error if provided type is not supported -// supported types: -// -// Element -// *Element -// uint64 -// int -// string (see SetString for valid formats) -// *big.Int -// big.Int -// []byte -func (z *Element) SetInterface(i1 interface{}) (*Element, error) { - if i1 == nil { - return nil, errors.New("can't set fp.Element with ") - } - - switch c1 := i1.(type) { - case Element: - return z.Set(&c1), nil - case *Element: - if c1 == nil { - return nil, errors.New("can't set fp.Element with ") - } - return z.Set(c1), nil - case uint8: - return z.SetUint64(uint64(c1)), nil - case uint16: - return z.SetUint64(uint64(c1)), nil - case uint32: - return z.SetUint64(uint64(c1)), nil - case uint: - return z.SetUint64(uint64(c1)), nil - case uint64: - return z.SetUint64(c1), nil - case int8: - return z.SetInt64(int64(c1)), nil - case int16: - return z.SetInt64(int64(c1)), nil - case int32: - return z.SetInt64(int64(c1)), nil - case int64: - return z.SetInt64(c1), nil - case int: - return z.SetInt64(int64(c1)), nil - case string: - return z.SetString(c1) - case *big.Int: - if c1 == nil { - return nil, errors.New("can't set fp.Element with ") - } - return z.SetBigInt(c1), nil - case big.Int: - return z.SetBigInt(&c1), nil - case []byte: - return z.SetBytes(c1), nil - default: - return nil, errors.New("can't set fp.Element from type " + reflect.TypeOf(i1).String()) - } -} - -// SetZero z = 0 -func (z *Element) SetZero() *Element { - z[0] = 0 - z[1] = 0 - z[2] = 0 - z[3] = 0 - z[4] = 0 - z[5] = 0 - z[6] = 0 - z[7] = 0 - z[8] = 0 - z[9] = 0 - z[10] = 0 - z[11] = 0 - return z -} - -// SetOne z = 1 (in Montgomery form) -func (z *Element) SetOne() *Element { - z[0] = 18446744073709547378 - z[1] = 14463961505609547775 - z[2] = 15160016368967634470 - z[3] = 12241294279704278364 - z[4] = 2720419343484222500 - z[5] = 4799902015386277509 - z[6] = 8643488375494563078 - z[7] = 18366804658688562287 - z[8] = 2055362399696866477 - z[9] = 3108243834975866807 - z[10] = 9468215855567529777 - z[11] = 369351476012747 - return z -} - -// Div z = x*y⁻¹ (mod q) -func (z *Element) Div(x, y *Element) *Element { - var yInv Element - yInv.Inverse(y) - z.Mul(x, &yInv) - return z -} - -// Equal returns z == x; constant-time -func (z *Element) Equal(x *Element) bool { - return z.NotEqual(x) == 0 -} - -// NotEqual returns 0 if and only if z == x; constant-time -func (z *Element) NotEqual(x *Element) uint64 { - return (z[11] ^ x[11]) | (z[10] ^ x[10]) | (z[9] ^ x[9]) | (z[8] ^ x[8]) | (z[7] ^ x[7]) | (z[6] ^ x[6]) | (z[5] ^ x[5]) | (z[4] ^ x[4]) | (z[3] ^ x[3]) | (z[2] ^ x[2]) | (z[1] ^ x[1]) | (z[0] ^ x[0]) -} - -// IsZero returns z == 0 -func (z *Element) IsZero() bool { - return (z[11] | z[10] | z[9] | z[8] | z[7] | z[6] | z[5] | z[4] | z[3] | z[2] | z[1] | z[0]) == 0 -} - -// IsOne returns z == 1 -func (z *Element) IsOne() bool { - return ((z[11] ^ 369351476012747) | (z[10] ^ 9468215855567529777) | (z[9] ^ 3108243834975866807) | (z[8] ^ 2055362399696866477) | (z[7] ^ 18366804658688562287) | (z[6] ^ 8643488375494563078) | (z[5] ^ 4799902015386277509) | (z[4] ^ 2720419343484222500) | (z[3] ^ 12241294279704278364) | (z[2] ^ 15160016368967634470) | (z[1] ^ 14463961505609547775) | (z[0] ^ 18446744073709547378)) == 0 -} - -// IsUint64 reports whether z can be represented as an uint64. -func (z *Element) IsUint64() bool { - zz := *z - zz.fromMont() - return zz.FitsOnOneWord() -} - -// Uint64 returns the uint64 representation of x. If x cannot be represented in a uint64, the result is undefined. -func (z *Element) Uint64() uint64 { - return z.Bits()[0] -} - -// FitsOnOneWord reports whether z words (except the least significant word) are 0 -// -// It is the responsibility of the caller to convert from Montgomery to Regular form if needed. -func (z *Element) FitsOnOneWord() bool { - return (z[11] | z[10] | z[9] | z[8] | z[7] | z[6] | z[5] | z[4] | z[3] | z[2] | z[1]) == 0 -} - -// Cmp compares (lexicographic order) z and x and returns: -// -// -1 if z < x -// 0 if z == x -// +1 if z > x -func (z *Element) Cmp(x *Element) int { - _z := z.Bits() - _x := x.Bits() - if _z[11] > _x[11] { - return 1 - } else if _z[11] < _x[11] { - return -1 - } - if _z[10] > _x[10] { - return 1 - } else if _z[10] < _x[10] { - return -1 - } - if _z[9] > _x[9] { - return 1 - } else if _z[9] < _x[9] { - return -1 - } - if _z[8] > _x[8] { - return 1 - } else if _z[8] < _x[8] { - return -1 - } - if _z[7] > _x[7] { - return 1 - } else if _z[7] < _x[7] { - return -1 - } - if _z[6] > _x[6] { - return 1 - } else if _z[6] < _x[6] { - return -1 - } - if _z[5] > _x[5] { - return 1 - } else if _z[5] < _x[5] { - return -1 - } - if _z[4] > _x[4] { - return 1 - } else if _z[4] < _x[4] { - return -1 - } - if _z[3] > _x[3] { - return 1 - } else if _z[3] < _x[3] { - return -1 - } - if _z[2] > _x[2] { - return 1 - } else if _z[2] < _x[2] { - return -1 - } - if _z[1] > _x[1] { - return 1 - } else if _z[1] < _x[1] { - return -1 - } - if _z[0] > _x[0] { - return 1 - } else if _z[0] < _x[0] { - return -1 - } - return 0 -} - -// LexicographicallyLargest returns true if this element is strictly lexicographically -// larger than its negation, false otherwise -func (z *Element) LexicographicallyLargest() bool { - // adapted from github.com/zkcrypto/bls12_381 - // we check if the element is larger than (q-1) / 2 - // if z - (((q -1) / 2) + 1) have no underflow, then z > (q-1) / 2 - - _z := z.Bits() - - var b uint64 - _, b = bits.Sub64(_z[0], 1, 0) - _, b = bits.Sub64(_z[1], 1865601988406935552, b) - _, b = bits.Sub64(_z[2], 7519677619439740768, b) - _, b = bits.Sub64(_z[3], 11637676499754480623, b) - _, b = bits.Sub64(_z[4], 8163168546618811218, b) - _, b = bits.Sub64(_z[5], 378118636952580899, b) - _, b = bits.Sub64(_z[6], 17690530803068599637, b) - _, b = bits.Sub64(_z[7], 7377836520680792940, b) - _, b = bits.Sub64(_z[8], 18300686120035856851, b) - _, b = bits.Sub64(_z[9], 12559350142230161220, b) - _, b = bits.Sub64(_z[10], 8166725140723971175, b) - _, b = bits.Sub64(_z[11], 2176306597715141, b) - - return b == 0 -} - -// SetRandom sets z to a uniform random value in [0, q). -// -// This might error only if reading from crypto/rand.Reader errors, -// in which case, value of z is undefined. -func (z *Element) SetRandom() (*Element, error) { - // this code is generated for all modulus - // and derived from go/src/crypto/rand/util.go - - // l is number of limbs * 8; the number of bytes needed to reconstruct 12 uint64 - const l = 96 - - // bitLen is the maximum bit length needed to encode a value < q. - const bitLen = 756 - - // k is the maximum byte length needed to encode a value < q. - const k = (bitLen + 7) / 8 - - // b is the number of bits in the most significant byte of q-1. - b := uint(bitLen % 8) - if b == 0 { - b = 8 - } - - var bytes [l]byte - - for { - // note that bytes[k:l] is always 0 - if _, err := io.ReadFull(rand.Reader, bytes[:k]); err != nil { - return nil, err - } - - // Clear unused bits in in the most significant byte to increase probability - // that the candidate is < q. - bytes[k-1] &= uint8(int(1<> 1 - z[0] = z[0]>>1 | z[1]<<63 - z[1] = z[1]>>1 | z[2]<<63 - z[2] = z[2]>>1 | z[3]<<63 - z[3] = z[3]>>1 | z[4]<<63 - z[4] = z[4]>>1 | z[5]<<63 - z[5] = z[5]>>1 | z[6]<<63 - z[6] = z[6]>>1 | z[7]<<63 - z[7] = z[7]>>1 | z[8]<<63 - z[8] = z[8]>>1 | z[9]<<63 - z[9] = z[9]>>1 | z[10]<<63 - z[10] = z[10]>>1 | z[11]<<63 - z[11] >>= 1 - -} - -// fromMont converts z in place (i.e. mutates) from Montgomery to regular representation -// sets and returns z = z * 1 -func (z *Element) fromMont() *Element { - fromMont(z) - return z -} - -// Add z = x + y (mod q) -func (z *Element) Add(x, y *Element) *Element { - - var carry uint64 - z[0], carry = bits.Add64(x[0], y[0], 0) - z[1], carry = bits.Add64(x[1], y[1], carry) - z[2], carry = bits.Add64(x[2], y[2], carry) - z[3], carry = bits.Add64(x[3], y[3], carry) - z[4], carry = bits.Add64(x[4], y[4], carry) - z[5], carry = bits.Add64(x[5], y[5], carry) - z[6], carry = bits.Add64(x[6], y[6], carry) - z[7], carry = bits.Add64(x[7], y[7], carry) - z[8], carry = bits.Add64(x[8], y[8], carry) - z[9], carry = bits.Add64(x[9], y[9], carry) - z[10], carry = bits.Add64(x[10], y[10], carry) - z[11], _ = bits.Add64(x[11], y[11], carry) - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], b = bits.Sub64(z[5], q5, b) - z[6], b = bits.Sub64(z[6], q6, b) - z[7], b = bits.Sub64(z[7], q7, b) - z[8], b = bits.Sub64(z[8], q8, b) - z[9], b = bits.Sub64(z[9], q9, b) - z[10], b = bits.Sub64(z[10], q10, b) - z[11], _ = bits.Sub64(z[11], q11, b) - } - return z -} - -// Double z = x + x (mod q), aka Lsh 1 -func (z *Element) Double(x *Element) *Element { - - var carry uint64 - z[0], carry = bits.Add64(x[0], x[0], 0) - z[1], carry = bits.Add64(x[1], x[1], carry) - z[2], carry = bits.Add64(x[2], x[2], carry) - z[3], carry = bits.Add64(x[3], x[3], carry) - z[4], carry = bits.Add64(x[4], x[4], carry) - z[5], carry = bits.Add64(x[5], x[5], carry) - z[6], carry = bits.Add64(x[6], x[6], carry) - z[7], carry = bits.Add64(x[7], x[7], carry) - z[8], carry = bits.Add64(x[8], x[8], carry) - z[9], carry = bits.Add64(x[9], x[9], carry) - z[10], carry = bits.Add64(x[10], x[10], carry) - z[11], _ = bits.Add64(x[11], x[11], carry) - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], b = bits.Sub64(z[5], q5, b) - z[6], b = bits.Sub64(z[6], q6, b) - z[7], b = bits.Sub64(z[7], q7, b) - z[8], b = bits.Sub64(z[8], q8, b) - z[9], b = bits.Sub64(z[9], q9, b) - z[10], b = bits.Sub64(z[10], q10, b) - z[11], _ = bits.Sub64(z[11], q11, b) - } - return z -} - -// Sub z = x - y (mod q) -func (z *Element) Sub(x, y *Element) *Element { - var b uint64 - z[0], b = bits.Sub64(x[0], y[0], 0) - z[1], b = bits.Sub64(x[1], y[1], b) - z[2], b = bits.Sub64(x[2], y[2], b) - z[3], b = bits.Sub64(x[3], y[3], b) - z[4], b = bits.Sub64(x[4], y[4], b) - z[5], b = bits.Sub64(x[5], y[5], b) - z[6], b = bits.Sub64(x[6], y[6], b) - z[7], b = bits.Sub64(x[7], y[7], b) - z[8], b = bits.Sub64(x[8], y[8], b) - z[9], b = bits.Sub64(x[9], y[9], b) - z[10], b = bits.Sub64(x[10], y[10], b) - z[11], b = bits.Sub64(x[11], y[11], b) - if b != 0 { - var c uint64 - z[0], c = bits.Add64(z[0], q0, 0) - z[1], c = bits.Add64(z[1], q1, c) - z[2], c = bits.Add64(z[2], q2, c) - z[3], c = bits.Add64(z[3], q3, c) - z[4], c = bits.Add64(z[4], q4, c) - z[5], c = bits.Add64(z[5], q5, c) - z[6], c = bits.Add64(z[6], q6, c) - z[7], c = bits.Add64(z[7], q7, c) - z[8], c = bits.Add64(z[8], q8, c) - z[9], c = bits.Add64(z[9], q9, c) - z[10], c = bits.Add64(z[10], q10, c) - z[11], _ = bits.Add64(z[11], q11, c) - } - return z -} - -// Neg z = q - x -func (z *Element) Neg(x *Element) *Element { - if x.IsZero() { - z.SetZero() - return z - } - var borrow uint64 - z[0], borrow = bits.Sub64(q0, x[0], 0) - z[1], borrow = bits.Sub64(q1, x[1], borrow) - z[2], borrow = bits.Sub64(q2, x[2], borrow) - z[3], borrow = bits.Sub64(q3, x[3], borrow) - z[4], borrow = bits.Sub64(q4, x[4], borrow) - z[5], borrow = bits.Sub64(q5, x[5], borrow) - z[6], borrow = bits.Sub64(q6, x[6], borrow) - z[7], borrow = bits.Sub64(q7, x[7], borrow) - z[8], borrow = bits.Sub64(q8, x[8], borrow) - z[9], borrow = bits.Sub64(q9, x[9], borrow) - z[10], borrow = bits.Sub64(q10, x[10], borrow) - z[11], _ = bits.Sub64(q11, x[11], borrow) - return z -} - -// Select is a constant-time conditional move. -// If c=0, z = x0. Else z = x1 -func (z *Element) Select(c int, x0 *Element, x1 *Element) *Element { - cC := uint64((int64(c) | -int64(c)) >> 63) // "canonicized" into: 0 if c=0, -1 otherwise - z[0] = x0[0] ^ cC&(x0[0]^x1[0]) - z[1] = x0[1] ^ cC&(x0[1]^x1[1]) - z[2] = x0[2] ^ cC&(x0[2]^x1[2]) - z[3] = x0[3] ^ cC&(x0[3]^x1[3]) - z[4] = x0[4] ^ cC&(x0[4]^x1[4]) - z[5] = x0[5] ^ cC&(x0[5]^x1[5]) - z[6] = x0[6] ^ cC&(x0[6]^x1[6]) - z[7] = x0[7] ^ cC&(x0[7]^x1[7]) - z[8] = x0[8] ^ cC&(x0[8]^x1[8]) - z[9] = x0[9] ^ cC&(x0[9]^x1[9]) - z[10] = x0[10] ^ cC&(x0[10]^x1[10]) - z[11] = x0[11] ^ cC&(x0[11]^x1[11]) - return z -} - -// _mulGeneric is unoptimized textbook CIOS -// it is a fallback solution on x86 when ADX instruction set is not available -// and is used for testing purposes. -func _mulGeneric(z, x, y *Element) { - - // Implements CIOS multiplication -- section 2.3.2 of Tolga Acar's thesis - // https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf - // - // The algorithm: - // - // for i=0 to N-1 - // C := 0 - // for j=0 to N-1 - // (C,t[j]) := t[j] + x[j]*y[i] + C - // (t[N+1],t[N]) := t[N] + C - // - // C := 0 - // m := t[0]*q'[0] mod D - // (C,_) := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // (C,t[N-1]) := t[N] + C - // t[N] := t[N+1] + C - // - // → N is the number of machine words needed to store the modulus q - // → D is the word size. For example, on a 64-bit architecture D is 2 64 - // → x[i], y[i], q[i] is the ith word of the numbers x,y,q - // → q'[0] is the lowest word of the number -q⁻¹ mod r. This quantity is pre-computed, as it does not depend on the inputs. - // → t is a temporary array of size N+2 - // → C, S are machine words. A pair (C,S) refers to (hi-bits, lo-bits) of a two-word number - - var t [13]uint64 - var D uint64 - var m, C uint64 - // ----------------------------------- - // First loop - - C, t[0] = bits.Mul64(y[0], x[0]) - C, t[1] = madd1(y[0], x[1], C) - C, t[2] = madd1(y[0], x[2], C) - C, t[3] = madd1(y[0], x[3], C) - C, t[4] = madd1(y[0], x[4], C) - C, t[5] = madd1(y[0], x[5], C) - C, t[6] = madd1(y[0], x[6], C) - C, t[7] = madd1(y[0], x[7], C) - C, t[8] = madd1(y[0], x[8], C) - C, t[9] = madd1(y[0], x[9], C) - C, t[10] = madd1(y[0], x[10], C) - C, t[11] = madd1(y[0], x[11], C) - - t[12], D = bits.Add64(t[12], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - C, t[5] = madd2(m, q6, t[6], C) - C, t[6] = madd2(m, q7, t[7], C) - C, t[7] = madd2(m, q8, t[8], C) - C, t[8] = madd2(m, q9, t[9], C) - C, t[9] = madd2(m, q10, t[10], C) - C, t[10] = madd2(m, q11, t[11], C) - - t[11], C = bits.Add64(t[12], C, 0) - t[12], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[1], x[0], t[0]) - C, t[1] = madd2(y[1], x[1], t[1], C) - C, t[2] = madd2(y[1], x[2], t[2], C) - C, t[3] = madd2(y[1], x[3], t[3], C) - C, t[4] = madd2(y[1], x[4], t[4], C) - C, t[5] = madd2(y[1], x[5], t[5], C) - C, t[6] = madd2(y[1], x[6], t[6], C) - C, t[7] = madd2(y[1], x[7], t[7], C) - C, t[8] = madd2(y[1], x[8], t[8], C) - C, t[9] = madd2(y[1], x[9], t[9], C) - C, t[10] = madd2(y[1], x[10], t[10], C) - C, t[11] = madd2(y[1], x[11], t[11], C) - - t[12], D = bits.Add64(t[12], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - C, t[5] = madd2(m, q6, t[6], C) - C, t[6] = madd2(m, q7, t[7], C) - C, t[7] = madd2(m, q8, t[8], C) - C, t[8] = madd2(m, q9, t[9], C) - C, t[9] = madd2(m, q10, t[10], C) - C, t[10] = madd2(m, q11, t[11], C) - - t[11], C = bits.Add64(t[12], C, 0) - t[12], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[2], x[0], t[0]) - C, t[1] = madd2(y[2], x[1], t[1], C) - C, t[2] = madd2(y[2], x[2], t[2], C) - C, t[3] = madd2(y[2], x[3], t[3], C) - C, t[4] = madd2(y[2], x[4], t[4], C) - C, t[5] = madd2(y[2], x[5], t[5], C) - C, t[6] = madd2(y[2], x[6], t[6], C) - C, t[7] = madd2(y[2], x[7], t[7], C) - C, t[8] = madd2(y[2], x[8], t[8], C) - C, t[9] = madd2(y[2], x[9], t[9], C) - C, t[10] = madd2(y[2], x[10], t[10], C) - C, t[11] = madd2(y[2], x[11], t[11], C) - - t[12], D = bits.Add64(t[12], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - C, t[5] = madd2(m, q6, t[6], C) - C, t[6] = madd2(m, q7, t[7], C) - C, t[7] = madd2(m, q8, t[8], C) - C, t[8] = madd2(m, q9, t[9], C) - C, t[9] = madd2(m, q10, t[10], C) - C, t[10] = madd2(m, q11, t[11], C) - - t[11], C = bits.Add64(t[12], C, 0) - t[12], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[3], x[0], t[0]) - C, t[1] = madd2(y[3], x[1], t[1], C) - C, t[2] = madd2(y[3], x[2], t[2], C) - C, t[3] = madd2(y[3], x[3], t[3], C) - C, t[4] = madd2(y[3], x[4], t[4], C) - C, t[5] = madd2(y[3], x[5], t[5], C) - C, t[6] = madd2(y[3], x[6], t[6], C) - C, t[7] = madd2(y[3], x[7], t[7], C) - C, t[8] = madd2(y[3], x[8], t[8], C) - C, t[9] = madd2(y[3], x[9], t[9], C) - C, t[10] = madd2(y[3], x[10], t[10], C) - C, t[11] = madd2(y[3], x[11], t[11], C) - - t[12], D = bits.Add64(t[12], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - C, t[5] = madd2(m, q6, t[6], C) - C, t[6] = madd2(m, q7, t[7], C) - C, t[7] = madd2(m, q8, t[8], C) - C, t[8] = madd2(m, q9, t[9], C) - C, t[9] = madd2(m, q10, t[10], C) - C, t[10] = madd2(m, q11, t[11], C) - - t[11], C = bits.Add64(t[12], C, 0) - t[12], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[4], x[0], t[0]) - C, t[1] = madd2(y[4], x[1], t[1], C) - C, t[2] = madd2(y[4], x[2], t[2], C) - C, t[3] = madd2(y[4], x[3], t[3], C) - C, t[4] = madd2(y[4], x[4], t[4], C) - C, t[5] = madd2(y[4], x[5], t[5], C) - C, t[6] = madd2(y[4], x[6], t[6], C) - C, t[7] = madd2(y[4], x[7], t[7], C) - C, t[8] = madd2(y[4], x[8], t[8], C) - C, t[9] = madd2(y[4], x[9], t[9], C) - C, t[10] = madd2(y[4], x[10], t[10], C) - C, t[11] = madd2(y[4], x[11], t[11], C) - - t[12], D = bits.Add64(t[12], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - C, t[5] = madd2(m, q6, t[6], C) - C, t[6] = madd2(m, q7, t[7], C) - C, t[7] = madd2(m, q8, t[8], C) - C, t[8] = madd2(m, q9, t[9], C) - C, t[9] = madd2(m, q10, t[10], C) - C, t[10] = madd2(m, q11, t[11], C) - - t[11], C = bits.Add64(t[12], C, 0) - t[12], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[5], x[0], t[0]) - C, t[1] = madd2(y[5], x[1], t[1], C) - C, t[2] = madd2(y[5], x[2], t[2], C) - C, t[3] = madd2(y[5], x[3], t[3], C) - C, t[4] = madd2(y[5], x[4], t[4], C) - C, t[5] = madd2(y[5], x[5], t[5], C) - C, t[6] = madd2(y[5], x[6], t[6], C) - C, t[7] = madd2(y[5], x[7], t[7], C) - C, t[8] = madd2(y[5], x[8], t[8], C) - C, t[9] = madd2(y[5], x[9], t[9], C) - C, t[10] = madd2(y[5], x[10], t[10], C) - C, t[11] = madd2(y[5], x[11], t[11], C) - - t[12], D = bits.Add64(t[12], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - C, t[5] = madd2(m, q6, t[6], C) - C, t[6] = madd2(m, q7, t[7], C) - C, t[7] = madd2(m, q8, t[8], C) - C, t[8] = madd2(m, q9, t[9], C) - C, t[9] = madd2(m, q10, t[10], C) - C, t[10] = madd2(m, q11, t[11], C) - - t[11], C = bits.Add64(t[12], C, 0) - t[12], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[6], x[0], t[0]) - C, t[1] = madd2(y[6], x[1], t[1], C) - C, t[2] = madd2(y[6], x[2], t[2], C) - C, t[3] = madd2(y[6], x[3], t[3], C) - C, t[4] = madd2(y[6], x[4], t[4], C) - C, t[5] = madd2(y[6], x[5], t[5], C) - C, t[6] = madd2(y[6], x[6], t[6], C) - C, t[7] = madd2(y[6], x[7], t[7], C) - C, t[8] = madd2(y[6], x[8], t[8], C) - C, t[9] = madd2(y[6], x[9], t[9], C) - C, t[10] = madd2(y[6], x[10], t[10], C) - C, t[11] = madd2(y[6], x[11], t[11], C) - - t[12], D = bits.Add64(t[12], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - C, t[5] = madd2(m, q6, t[6], C) - C, t[6] = madd2(m, q7, t[7], C) - C, t[7] = madd2(m, q8, t[8], C) - C, t[8] = madd2(m, q9, t[9], C) - C, t[9] = madd2(m, q10, t[10], C) - C, t[10] = madd2(m, q11, t[11], C) - - t[11], C = bits.Add64(t[12], C, 0) - t[12], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[7], x[0], t[0]) - C, t[1] = madd2(y[7], x[1], t[1], C) - C, t[2] = madd2(y[7], x[2], t[2], C) - C, t[3] = madd2(y[7], x[3], t[3], C) - C, t[4] = madd2(y[7], x[4], t[4], C) - C, t[5] = madd2(y[7], x[5], t[5], C) - C, t[6] = madd2(y[7], x[6], t[6], C) - C, t[7] = madd2(y[7], x[7], t[7], C) - C, t[8] = madd2(y[7], x[8], t[8], C) - C, t[9] = madd2(y[7], x[9], t[9], C) - C, t[10] = madd2(y[7], x[10], t[10], C) - C, t[11] = madd2(y[7], x[11], t[11], C) - - t[12], D = bits.Add64(t[12], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - C, t[5] = madd2(m, q6, t[6], C) - C, t[6] = madd2(m, q7, t[7], C) - C, t[7] = madd2(m, q8, t[8], C) - C, t[8] = madd2(m, q9, t[9], C) - C, t[9] = madd2(m, q10, t[10], C) - C, t[10] = madd2(m, q11, t[11], C) - - t[11], C = bits.Add64(t[12], C, 0) - t[12], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[8], x[0], t[0]) - C, t[1] = madd2(y[8], x[1], t[1], C) - C, t[2] = madd2(y[8], x[2], t[2], C) - C, t[3] = madd2(y[8], x[3], t[3], C) - C, t[4] = madd2(y[8], x[4], t[4], C) - C, t[5] = madd2(y[8], x[5], t[5], C) - C, t[6] = madd2(y[8], x[6], t[6], C) - C, t[7] = madd2(y[8], x[7], t[7], C) - C, t[8] = madd2(y[8], x[8], t[8], C) - C, t[9] = madd2(y[8], x[9], t[9], C) - C, t[10] = madd2(y[8], x[10], t[10], C) - C, t[11] = madd2(y[8], x[11], t[11], C) - - t[12], D = bits.Add64(t[12], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - C, t[5] = madd2(m, q6, t[6], C) - C, t[6] = madd2(m, q7, t[7], C) - C, t[7] = madd2(m, q8, t[8], C) - C, t[8] = madd2(m, q9, t[9], C) - C, t[9] = madd2(m, q10, t[10], C) - C, t[10] = madd2(m, q11, t[11], C) - - t[11], C = bits.Add64(t[12], C, 0) - t[12], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[9], x[0], t[0]) - C, t[1] = madd2(y[9], x[1], t[1], C) - C, t[2] = madd2(y[9], x[2], t[2], C) - C, t[3] = madd2(y[9], x[3], t[3], C) - C, t[4] = madd2(y[9], x[4], t[4], C) - C, t[5] = madd2(y[9], x[5], t[5], C) - C, t[6] = madd2(y[9], x[6], t[6], C) - C, t[7] = madd2(y[9], x[7], t[7], C) - C, t[8] = madd2(y[9], x[8], t[8], C) - C, t[9] = madd2(y[9], x[9], t[9], C) - C, t[10] = madd2(y[9], x[10], t[10], C) - C, t[11] = madd2(y[9], x[11], t[11], C) - - t[12], D = bits.Add64(t[12], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - C, t[5] = madd2(m, q6, t[6], C) - C, t[6] = madd2(m, q7, t[7], C) - C, t[7] = madd2(m, q8, t[8], C) - C, t[8] = madd2(m, q9, t[9], C) - C, t[9] = madd2(m, q10, t[10], C) - C, t[10] = madd2(m, q11, t[11], C) - - t[11], C = bits.Add64(t[12], C, 0) - t[12], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[10], x[0], t[0]) - C, t[1] = madd2(y[10], x[1], t[1], C) - C, t[2] = madd2(y[10], x[2], t[2], C) - C, t[3] = madd2(y[10], x[3], t[3], C) - C, t[4] = madd2(y[10], x[4], t[4], C) - C, t[5] = madd2(y[10], x[5], t[5], C) - C, t[6] = madd2(y[10], x[6], t[6], C) - C, t[7] = madd2(y[10], x[7], t[7], C) - C, t[8] = madd2(y[10], x[8], t[8], C) - C, t[9] = madd2(y[10], x[9], t[9], C) - C, t[10] = madd2(y[10], x[10], t[10], C) - C, t[11] = madd2(y[10], x[11], t[11], C) - - t[12], D = bits.Add64(t[12], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - C, t[5] = madd2(m, q6, t[6], C) - C, t[6] = madd2(m, q7, t[7], C) - C, t[7] = madd2(m, q8, t[8], C) - C, t[8] = madd2(m, q9, t[9], C) - C, t[9] = madd2(m, q10, t[10], C) - C, t[10] = madd2(m, q11, t[11], C) - - t[11], C = bits.Add64(t[12], C, 0) - t[12], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[11], x[0], t[0]) - C, t[1] = madd2(y[11], x[1], t[1], C) - C, t[2] = madd2(y[11], x[2], t[2], C) - C, t[3] = madd2(y[11], x[3], t[3], C) - C, t[4] = madd2(y[11], x[4], t[4], C) - C, t[5] = madd2(y[11], x[5], t[5], C) - C, t[6] = madd2(y[11], x[6], t[6], C) - C, t[7] = madd2(y[11], x[7], t[7], C) - C, t[8] = madd2(y[11], x[8], t[8], C) - C, t[9] = madd2(y[11], x[9], t[9], C) - C, t[10] = madd2(y[11], x[10], t[10], C) - C, t[11] = madd2(y[11], x[11], t[11], C) - - t[12], D = bits.Add64(t[12], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - C, t[5] = madd2(m, q6, t[6], C) - C, t[6] = madd2(m, q7, t[7], C) - C, t[7] = madd2(m, q8, t[8], C) - C, t[8] = madd2(m, q9, t[9], C) - C, t[9] = madd2(m, q10, t[10], C) - C, t[10] = madd2(m, q11, t[11], C) - - t[11], C = bits.Add64(t[12], C, 0) - t[12], _ = bits.Add64(0, D, C) - - if t[12] != 0 { - // we need to reduce, we have a result on 13 words - var b uint64 - z[0], b = bits.Sub64(t[0], q0, 0) - z[1], b = bits.Sub64(t[1], q1, b) - z[2], b = bits.Sub64(t[2], q2, b) - z[3], b = bits.Sub64(t[3], q3, b) - z[4], b = bits.Sub64(t[4], q4, b) - z[5], b = bits.Sub64(t[5], q5, b) - z[6], b = bits.Sub64(t[6], q6, b) - z[7], b = bits.Sub64(t[7], q7, b) - z[8], b = bits.Sub64(t[8], q8, b) - z[9], b = bits.Sub64(t[9], q9, b) - z[10], b = bits.Sub64(t[10], q10, b) - z[11], _ = bits.Sub64(t[11], q11, b) - return - } - - // copy t into z - z[0] = t[0] - z[1] = t[1] - z[2] = t[2] - z[3] = t[3] - z[4] = t[4] - z[5] = t[5] - z[6] = t[6] - z[7] = t[7] - z[8] = t[8] - z[9] = t[9] - z[10] = t[10] - z[11] = t[11] - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], b = bits.Sub64(z[5], q5, b) - z[6], b = bits.Sub64(z[6], q6, b) - z[7], b = bits.Sub64(z[7], q7, b) - z[8], b = bits.Sub64(z[8], q8, b) - z[9], b = bits.Sub64(z[9], q9, b) - z[10], b = bits.Sub64(z[10], q10, b) - z[11], _ = bits.Sub64(z[11], q11, b) - } -} - -func _fromMontGeneric(z *Element) { - // the following lines implement z = z * 1 - // with a modified CIOS montgomery multiplication - // see Mul for algorithm documentation - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - C, z[5] = madd2(m, q6, z[6], C) - C, z[6] = madd2(m, q7, z[7], C) - C, z[7] = madd2(m, q8, z[8], C) - C, z[8] = madd2(m, q9, z[9], C) - C, z[9] = madd2(m, q10, z[10], C) - C, z[10] = madd2(m, q11, z[11], C) - z[11] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - C, z[5] = madd2(m, q6, z[6], C) - C, z[6] = madd2(m, q7, z[7], C) - C, z[7] = madd2(m, q8, z[8], C) - C, z[8] = madd2(m, q9, z[9], C) - C, z[9] = madd2(m, q10, z[10], C) - C, z[10] = madd2(m, q11, z[11], C) - z[11] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - C, z[5] = madd2(m, q6, z[6], C) - C, z[6] = madd2(m, q7, z[7], C) - C, z[7] = madd2(m, q8, z[8], C) - C, z[8] = madd2(m, q9, z[9], C) - C, z[9] = madd2(m, q10, z[10], C) - C, z[10] = madd2(m, q11, z[11], C) - z[11] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - C, z[5] = madd2(m, q6, z[6], C) - C, z[6] = madd2(m, q7, z[7], C) - C, z[7] = madd2(m, q8, z[8], C) - C, z[8] = madd2(m, q9, z[9], C) - C, z[9] = madd2(m, q10, z[10], C) - C, z[10] = madd2(m, q11, z[11], C) - z[11] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - C, z[5] = madd2(m, q6, z[6], C) - C, z[6] = madd2(m, q7, z[7], C) - C, z[7] = madd2(m, q8, z[8], C) - C, z[8] = madd2(m, q9, z[9], C) - C, z[9] = madd2(m, q10, z[10], C) - C, z[10] = madd2(m, q11, z[11], C) - z[11] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - C, z[5] = madd2(m, q6, z[6], C) - C, z[6] = madd2(m, q7, z[7], C) - C, z[7] = madd2(m, q8, z[8], C) - C, z[8] = madd2(m, q9, z[9], C) - C, z[9] = madd2(m, q10, z[10], C) - C, z[10] = madd2(m, q11, z[11], C) - z[11] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - C, z[5] = madd2(m, q6, z[6], C) - C, z[6] = madd2(m, q7, z[7], C) - C, z[7] = madd2(m, q8, z[8], C) - C, z[8] = madd2(m, q9, z[9], C) - C, z[9] = madd2(m, q10, z[10], C) - C, z[10] = madd2(m, q11, z[11], C) - z[11] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - C, z[5] = madd2(m, q6, z[6], C) - C, z[6] = madd2(m, q7, z[7], C) - C, z[7] = madd2(m, q8, z[8], C) - C, z[8] = madd2(m, q9, z[9], C) - C, z[9] = madd2(m, q10, z[10], C) - C, z[10] = madd2(m, q11, z[11], C) - z[11] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - C, z[5] = madd2(m, q6, z[6], C) - C, z[6] = madd2(m, q7, z[7], C) - C, z[7] = madd2(m, q8, z[8], C) - C, z[8] = madd2(m, q9, z[9], C) - C, z[9] = madd2(m, q10, z[10], C) - C, z[10] = madd2(m, q11, z[11], C) - z[11] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - C, z[5] = madd2(m, q6, z[6], C) - C, z[6] = madd2(m, q7, z[7], C) - C, z[7] = madd2(m, q8, z[8], C) - C, z[8] = madd2(m, q9, z[9], C) - C, z[9] = madd2(m, q10, z[10], C) - C, z[10] = madd2(m, q11, z[11], C) - z[11] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - C, z[5] = madd2(m, q6, z[6], C) - C, z[6] = madd2(m, q7, z[7], C) - C, z[7] = madd2(m, q8, z[8], C) - C, z[8] = madd2(m, q9, z[9], C) - C, z[9] = madd2(m, q10, z[10], C) - C, z[10] = madd2(m, q11, z[11], C) - z[11] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - C, z[5] = madd2(m, q6, z[6], C) - C, z[6] = madd2(m, q7, z[7], C) - C, z[7] = madd2(m, q8, z[8], C) - C, z[8] = madd2(m, q9, z[9], C) - C, z[9] = madd2(m, q10, z[10], C) - C, z[10] = madd2(m, q11, z[11], C) - z[11] = C - } - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], b = bits.Sub64(z[5], q5, b) - z[6], b = bits.Sub64(z[6], q6, b) - z[7], b = bits.Sub64(z[7], q7, b) - z[8], b = bits.Sub64(z[8], q8, b) - z[9], b = bits.Sub64(z[9], q9, b) - z[10], b = bits.Sub64(z[10], q10, b) - z[11], _ = bits.Sub64(z[11], q11, b) - } -} - -func _reduceGeneric(z *Element) { - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], b = bits.Sub64(z[5], q5, b) - z[6], b = bits.Sub64(z[6], q6, b) - z[7], b = bits.Sub64(z[7], q7, b) - z[8], b = bits.Sub64(z[8], q8, b) - z[9], b = bits.Sub64(z[9], q9, b) - z[10], b = bits.Sub64(z[10], q10, b) - z[11], _ = bits.Sub64(z[11], q11, b) - } -} - -// BatchInvert returns a new slice with every element inverted. -// Uses Montgomery batch inversion trick -func BatchInvert(a []Element) []Element { - res := make([]Element, len(a)) - if len(a) == 0 { - return res - } - - zeroes := bitset.New(uint(len(a))) - accumulator := One() - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - zeroes.Set(uint(i)) - continue - } - res[i] = accumulator - accumulator.Mul(&accumulator, &a[i]) - } - - accumulator.Inverse(&accumulator) - - for i := len(a) - 1; i >= 0; i-- { - if zeroes.Test(uint(i)) { - continue - } - res[i].Mul(&res[i], &accumulator) - accumulator.Mul(&accumulator, &a[i]) - } - - return res -} - -func _butterflyGeneric(a, b *Element) { - t := *a - a.Add(a, b) - b.Sub(&t, b) -} - -// BitLen returns the minimum number of bits needed to represent z -// returns 0 if z == 0 -func (z *Element) BitLen() int { - if z[11] != 0 { - return 704 + bits.Len64(z[11]) - } - if z[10] != 0 { - return 640 + bits.Len64(z[10]) - } - if z[9] != 0 { - return 576 + bits.Len64(z[9]) - } - if z[8] != 0 { - return 512 + bits.Len64(z[8]) - } - if z[7] != 0 { - return 448 + bits.Len64(z[7]) - } - if z[6] != 0 { - return 384 + bits.Len64(z[6]) - } - if z[5] != 0 { - return 320 + bits.Len64(z[5]) - } - if z[4] != 0 { - return 256 + bits.Len64(z[4]) - } - if z[3] != 0 { - return 192 + bits.Len64(z[3]) - } - if z[2] != 0 { - return 128 + bits.Len64(z[2]) - } - if z[1] != 0 { - return 64 + bits.Len64(z[1]) - } - return bits.Len64(z[0]) -} - -// Hash msg to count prime field elements. -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-5.2 -func Hash(msg, dst []byte, count int) ([]Element, error) { - // 128 bits of security - // L = ceil((ceil(log2(p)) + k) / 8), where k is the security parameter = 128 - const Bytes = 1 + (Bits-1)/8 - const L = 16 + Bytes - - lenInBytes := count * L - pseudoRandomBytes, err := hash.ExpandMsgXmd(msg, dst, lenInBytes) - if err != nil { - return nil, err - } - - // get temporary big int from the pool - vv := pool.BigInt.Get() - - res := make([]Element, count) - for i := 0; i < count; i++ { - vv.SetBytes(pseudoRandomBytes[i*L : (i+1)*L]) - res[i].SetBigInt(vv) - } - - // release object into pool - pool.BigInt.Put(vv) - - return res, nil -} - -// Exp z = xᵏ (mod q) -func (z *Element) Exp(x Element, k *big.Int) *Element { - if k.IsUint64() && k.Uint64() == 0 { - return z.SetOne() - } - - e := k - if k.Sign() == -1 { - // negative k, we invert - // if k < 0: xᵏ (mod q) == (x⁻¹)ᵏ (mod q) - x.Inverse(&x) - - // we negate k in a temp big.Int since - // Int.Bit(_) of k and -k is different - e = pool.BigInt.Get() - defer pool.BigInt.Put(e) - e.Neg(k) - } - - z.Set(&x) - - for i := e.BitLen() - 2; i >= 0; i-- { - z.Square(z) - if e.Bit(i) == 1 { - z.Mul(z, &x) - } - } - - return z -} - -// rSquare where r is the Montgommery constant -// see section 2.3.2 of Tolga Acar's thesis -// https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf -var rSquare = Element{ - 11214533042317621956, - 4418601975293183768, - 2233550636059863627, - 13772400071271951950, - 13010224617750716256, - 15582310590478290871, - 6301429202206019695, - 15624904615961126890, - 14411832617204527559, - 10495912060283172777, - 8432856701560321958, - 4166778949326216, -} - -// toMont converts z to Montgomery form -// sets and returns z = z * r² -func (z *Element) toMont() *Element { - return z.Mul(z, &rSquare) -} - -// String returns the decimal representation of z as generated by -// z.Text(10). -func (z *Element) String() string { - return z.Text(10) -} - -// toBigInt returns z as a big.Int in Montgomery form -func (z *Element) toBigInt(res *big.Int) *big.Int { - var b [Bytes]byte - binary.BigEndian.PutUint64(b[88:96], z[0]) - binary.BigEndian.PutUint64(b[80:88], z[1]) - binary.BigEndian.PutUint64(b[72:80], z[2]) - binary.BigEndian.PutUint64(b[64:72], z[3]) - binary.BigEndian.PutUint64(b[56:64], z[4]) - binary.BigEndian.PutUint64(b[48:56], z[5]) - binary.BigEndian.PutUint64(b[40:48], z[6]) - binary.BigEndian.PutUint64(b[32:40], z[7]) - binary.BigEndian.PutUint64(b[24:32], z[8]) - binary.BigEndian.PutUint64(b[16:24], z[9]) - binary.BigEndian.PutUint64(b[8:16], z[10]) - binary.BigEndian.PutUint64(b[0:8], z[11]) - - return res.SetBytes(b[:]) -} - -// Text returns the string representation of z in the given base. -// Base must be between 2 and 36, inclusive. The result uses the -// lower-case letters 'a' to 'z' for digit values 10 to 35. -// No prefix (such as "0x") is added to the string. If z is a nil -// pointer it returns "". -// If base == 10 and -z fits in a uint16 prefix "-" is added to the string. -func (z *Element) Text(base int) string { - if base < 2 || base > 36 { - panic("invalid base") - } - if z == nil { - return "" - } - - const maxUint16 = 65535 - if base == 10 { - var zzNeg Element - zzNeg.Neg(z) - zzNeg.fromMont() - if zzNeg.FitsOnOneWord() && zzNeg[0] <= maxUint16 && zzNeg[0] != 0 { - return "-" + strconv.FormatUint(zzNeg[0], base) - } - } - zz := *z - zz.fromMont() - if zz.FitsOnOneWord() { - return strconv.FormatUint(zz[0], base) - } - vv := pool.BigInt.Get() - r := zz.toBigInt(vv).Text(base) - pool.BigInt.Put(vv) - return r -} - -// BigInt sets and return z as a *big.Int -func (z *Element) BigInt(res *big.Int) *big.Int { - _z := *z - _z.fromMont() - return _z.toBigInt(res) -} - -// ToBigIntRegular returns z as a big.Int in regular form -// -// Deprecated: use BigInt(*big.Int) instead -func (z Element) ToBigIntRegular(res *big.Int) *big.Int { - z.fromMont() - return z.toBigInt(res) -} - -// Bits provides access to z by returning its value as a little-endian [12]uint64 array. -// Bits is intended to support implementation of missing low-level Element -// functionality outside this package; it should be avoided otherwise. -func (z *Element) Bits() [12]uint64 { - _z := *z - fromMont(&_z) - return _z -} - -// Bytes returns the value of z as a big-endian byte array -func (z *Element) Bytes() (res [Bytes]byte) { - BigEndian.PutElement(&res, *z) - return -} - -// Marshal returns the value of z as a big-endian byte slice -func (z *Element) Marshal() []byte { - b := z.Bytes() - return b[:] -} - -// Unmarshal is an alias for SetBytes, it sets z to the value of e. -func (z *Element) Unmarshal(e []byte) { - z.SetBytes(e) -} - -// SetBytes interprets e as the bytes of a big-endian unsigned integer, -// sets z to that value, and returns z. -func (z *Element) SetBytes(e []byte) *Element { - if len(e) == Bytes { - // fast path - v, err := BigEndian.Element((*[Bytes]byte)(e)) - if err == nil { - *z = v - return z - } - } - - // slow path. - // get a big int from our pool - vv := pool.BigInt.Get() - vv.SetBytes(e) - - // set big int - z.SetBigInt(vv) - - // put temporary object back in pool - pool.BigInt.Put(vv) - - return z -} - -// SetBytesCanonical interprets e as the bytes of a big-endian 96-byte integer. -// If e is not a 96-byte slice or encodes a value higher than q, -// SetBytesCanonical returns an error. -func (z *Element) SetBytesCanonical(e []byte) error { - if len(e) != Bytes { - return errors.New("invalid fp.Element encoding") - } - v, err := BigEndian.Element((*[Bytes]byte)(e)) - if err != nil { - return err - } - *z = v - return nil -} - -// SetBigInt sets z to v and returns z -func (z *Element) SetBigInt(v *big.Int) *Element { - z.SetZero() - - var zero big.Int - - // fast path - c := v.Cmp(&_modulus) - if c == 0 { - // v == 0 - return z - } else if c != 1 && v.Cmp(&zero) != -1 { - // 0 < v < q - return z.setBigInt(v) - } - - // get temporary big int from the pool - vv := pool.BigInt.Get() - - // copy input + modular reduction - vv.Mod(v, &_modulus) - - // set big int byte value - z.setBigInt(vv) - - // release object into pool - pool.BigInt.Put(vv) - return z -} - -// setBigInt assumes 0 ⩽ v < q -func (z *Element) setBigInt(v *big.Int) *Element { - vBits := v.Bits() - - if bits.UintSize == 64 { - for i := 0; i < len(vBits); i++ { - z[i] = uint64(vBits[i]) - } - } else { - for i := 0; i < len(vBits); i++ { - if i%2 == 0 { - z[i/2] = uint64(vBits[i]) - } else { - z[i/2] |= uint64(vBits[i]) << 32 - } - } - } - - return z.toMont() -} - -// SetString creates a big.Int with number and calls SetBigInt on z -// -// The number prefix determines the actual base: A prefix of -// ”0b” or ”0B” selects base 2, ”0”, ”0o” or ”0O” selects base 8, -// and ”0x” or ”0X” selects base 16. Otherwise, the selected base is 10 -// and no prefix is accepted. -// -// For base 16, lower and upper case letters are considered the same: -// The letters 'a' to 'f' and 'A' to 'F' represent digit values 10 to 15. -// -// An underscore character ”_” may appear between a base -// prefix and an adjacent digit, and between successive digits; such -// underscores do not change the value of the number. -// Incorrect placement of underscores is reported as a panic if there -// are no other errors. -// -// If the number is invalid this method leaves z unchanged and returns nil, error. -func (z *Element) SetString(number string) (*Element, error) { - // get temporary big int from the pool - vv := pool.BigInt.Get() - - if _, ok := vv.SetString(number, 0); !ok { - return nil, errors.New("Element.SetString failed -> can't parse number into a big.Int " + number) - } - - z.SetBigInt(vv) - - // release object into pool - pool.BigInt.Put(vv) - - return z, nil -} - -// MarshalJSON returns json encoding of z (z.Text(10)) -// If z == nil, returns null -func (z *Element) MarshalJSON() ([]byte, error) { - if z == nil { - return []byte("null"), nil - } - const maxSafeBound = 15 // we encode it as number if it's small - s := z.Text(10) - if len(s) <= maxSafeBound { - return []byte(s), nil - } - var sbb strings.Builder - sbb.WriteByte('"') - sbb.WriteString(s) - sbb.WriteByte('"') - return []byte(sbb.String()), nil -} - -// UnmarshalJSON accepts numbers and strings as input -// See Element.SetString for valid prefixes (0x, 0b, ...) -func (z *Element) UnmarshalJSON(data []byte) error { - s := string(data) - if len(s) > Bits*3 { - return errors.New("value too large (max = Element.Bits * 3)") - } - - // we accept numbers and strings, remove leading and trailing quotes if any - if len(s) > 0 && s[0] == '"' { - s = s[1:] - } - if len(s) > 0 && s[len(s)-1] == '"' { - s = s[:len(s)-1] - } - - // get temporary big int from the pool - vv := pool.BigInt.Get() - - if _, ok := vv.SetString(s, 0); !ok { - return errors.New("can't parse into a big.Int: " + s) - } - - z.SetBigInt(vv) - - // release object into pool - pool.BigInt.Put(vv) - return nil -} - -// A ByteOrder specifies how to convert byte slices into a Element -type ByteOrder interface { - Element(*[Bytes]byte) (Element, error) - PutElement(*[Bytes]byte, Element) - String() string -} - -// BigEndian is the big-endian implementation of ByteOrder and AppendByteOrder. -var BigEndian bigEndian - -type bigEndian struct{} - -// Element interpret b is a big-endian 96-byte slice. -// If b encodes a value higher than q, Element returns error. -func (bigEndian) Element(b *[Bytes]byte) (Element, error) { - var z Element - z[0] = binary.BigEndian.Uint64((*b)[88:96]) - z[1] = binary.BigEndian.Uint64((*b)[80:88]) - z[2] = binary.BigEndian.Uint64((*b)[72:80]) - z[3] = binary.BigEndian.Uint64((*b)[64:72]) - z[4] = binary.BigEndian.Uint64((*b)[56:64]) - z[5] = binary.BigEndian.Uint64((*b)[48:56]) - z[6] = binary.BigEndian.Uint64((*b)[40:48]) - z[7] = binary.BigEndian.Uint64((*b)[32:40]) - z[8] = binary.BigEndian.Uint64((*b)[24:32]) - z[9] = binary.BigEndian.Uint64((*b)[16:24]) - z[10] = binary.BigEndian.Uint64((*b)[8:16]) - z[11] = binary.BigEndian.Uint64((*b)[0:8]) - - if !z.smallerThanModulus() { - return Element{}, errors.New("invalid fp.Element encoding") - } - - z.toMont() - return z, nil -} - -func (bigEndian) PutElement(b *[Bytes]byte, e Element) { - e.fromMont() - binary.BigEndian.PutUint64((*b)[88:96], e[0]) - binary.BigEndian.PutUint64((*b)[80:88], e[1]) - binary.BigEndian.PutUint64((*b)[72:80], e[2]) - binary.BigEndian.PutUint64((*b)[64:72], e[3]) - binary.BigEndian.PutUint64((*b)[56:64], e[4]) - binary.BigEndian.PutUint64((*b)[48:56], e[5]) - binary.BigEndian.PutUint64((*b)[40:48], e[6]) - binary.BigEndian.PutUint64((*b)[32:40], e[7]) - binary.BigEndian.PutUint64((*b)[24:32], e[8]) - binary.BigEndian.PutUint64((*b)[16:24], e[9]) - binary.BigEndian.PutUint64((*b)[8:16], e[10]) - binary.BigEndian.PutUint64((*b)[0:8], e[11]) -} - -func (bigEndian) String() string { return "BigEndian" } - -// LittleEndian is the little-endian implementation of ByteOrder and AppendByteOrder. -var LittleEndian littleEndian - -type littleEndian struct{} - -func (littleEndian) Element(b *[Bytes]byte) (Element, error) { - var z Element - z[0] = binary.LittleEndian.Uint64((*b)[0:8]) - z[1] = binary.LittleEndian.Uint64((*b)[8:16]) - z[2] = binary.LittleEndian.Uint64((*b)[16:24]) - z[3] = binary.LittleEndian.Uint64((*b)[24:32]) - z[4] = binary.LittleEndian.Uint64((*b)[32:40]) - z[5] = binary.LittleEndian.Uint64((*b)[40:48]) - z[6] = binary.LittleEndian.Uint64((*b)[48:56]) - z[7] = binary.LittleEndian.Uint64((*b)[56:64]) - z[8] = binary.LittleEndian.Uint64((*b)[64:72]) - z[9] = binary.LittleEndian.Uint64((*b)[72:80]) - z[10] = binary.LittleEndian.Uint64((*b)[80:88]) - z[11] = binary.LittleEndian.Uint64((*b)[88:96]) - - if !z.smallerThanModulus() { - return Element{}, errors.New("invalid fp.Element encoding") - } - - z.toMont() - return z, nil -} - -func (littleEndian) PutElement(b *[Bytes]byte, e Element) { - e.fromMont() - binary.LittleEndian.PutUint64((*b)[0:8], e[0]) - binary.LittleEndian.PutUint64((*b)[8:16], e[1]) - binary.LittleEndian.PutUint64((*b)[16:24], e[2]) - binary.LittleEndian.PutUint64((*b)[24:32], e[3]) - binary.LittleEndian.PutUint64((*b)[32:40], e[4]) - binary.LittleEndian.PutUint64((*b)[40:48], e[5]) - binary.LittleEndian.PutUint64((*b)[48:56], e[6]) - binary.LittleEndian.PutUint64((*b)[56:64], e[7]) - binary.LittleEndian.PutUint64((*b)[64:72], e[8]) - binary.LittleEndian.PutUint64((*b)[72:80], e[9]) - binary.LittleEndian.PutUint64((*b)[80:88], e[10]) - binary.LittleEndian.PutUint64((*b)[88:96], e[11]) -} - -func (littleEndian) String() string { return "LittleEndian" } - -// Legendre returns the Legendre symbol of z (either +1, -1, or 0.) -func (z *Element) Legendre() int { - var l Element - // z^((q-1)/2) - l.expByLegendreExp(*z) - - if l.IsZero() { - return 0 - } - - // if l == 1 - if l.IsOne() { - return 1 - } - return -1 -} - -// Sqrt z = √x (mod q) -// if the square root doesn't exist (x is not a square mod q) -// Sqrt leaves z unchanged and returns nil -func (z *Element) Sqrt(x *Element) *Element { - // q ≡ 1 (mod 4) - // see modSqrtTonelliShanks in math/big/int.go - // using https://www.maa.org/sites/default/files/pdf/upload_library/22/Polya/07468342.di020786.02p0470a.pdf - - var y, b, t, w Element - // w = x^((s-1)/2)) - w.expBySqrtExp(*x) - - // y = x^((s+1)/2)) = w * x - y.Mul(x, &w) - - // b = xˢ = w * w * x = y * x - b.Mul(&w, &y) - - // g = nonResidue ^ s - var g = Element{ - 17302715199413996045, - 15077845457253267709, - 8842885729139027579, - 12189878420705505575, - 12380986790262239346, - 585111498723936856, - 4947215576903759546, - 1186632482028566920, - 14543050817583235372, - 5644943604719368358, - 9440830989708189862, - 1039766423535362, - } - r := uint64(82) - - // compute legendre symbol - // t = x^((q-1)/2) = r-1 squaring of xˢ - t = b - for i := uint64(0); i < r-1; i++ { - t.Square(&t) - } - if t.IsZero() { - return z.SetZero() - } - if !t.IsOne() { - // t != 1, we don't have a square root - return nil - } - for { - var m uint64 - t = b - - // for t != 1 - for !t.IsOne() { - t.Square(&t) - m++ - } - - if m == 0 { - return z.Set(&y) - } - // t = g^(2^(r-m-1)) (mod q) - ge := int(r - m - 1) - t = g - for ge > 0 { - t.Square(&t) - ge-- - } - - g.Square(&t) - y.Mul(&y, &t) - b.Mul(&b, &g) - r = m - } -} - -const ( - k = 32 // word size / 2 - signBitSelector = uint64(1) << 63 - approxLowBitsN = k - 1 - approxHighBitsN = k + 1 -) - -const ( - inversionCorrectionFactorWord0 = 16061512306393401370 - inversionCorrectionFactorWord1 = 12469388396993975658 - inversionCorrectionFactorWord2 = 12941199289357671440 - inversionCorrectionFactorWord3 = 7124172912896157387 - inversionCorrectionFactorWord4 = 7772575019676086033 - inversionCorrectionFactorWord5 = 5410978411075096125 - inversionCorrectionFactorWord6 = 15135850590536056079 - inversionCorrectionFactorWord7 = 14366933837510102702 - inversionCorrectionFactorWord8 = 17864238268145908760 - inversionCorrectionFactorWord9 = 11845167622525040086 - inversionCorrectionFactorWord10 = 12428223085045138512 - inversionCorrectionFactorWord11 = 2992926161591192 - invIterationsN = 50 -) - -// Inverse z = x⁻¹ (mod q) -// -// if x == 0, sets and returns z = x -func (z *Element) Inverse(x *Element) *Element { - // Implements "Optimized Binary GCD for Modular Inversion" - // https://github.com/pornin/bingcd/blob/main/doc/bingcd.pdf - - a := *x - b := Element{ - q0, - q1, - q2, - q3, - q4, - q5, - q6, - q7, - q8, - q9, - q10, - q11, - } // b := q - - u := Element{1} - - // Update factors: we get [u; v] ← [f₀ g₀; f₁ g₁] [u; v] - // cᵢ = fᵢ + 2³¹ - 1 + 2³² * (gᵢ + 2³¹ - 1) - var c0, c1 int64 - - // Saved update factors to reduce the number of field multiplications - var pf0, pf1, pg0, pg1 int64 - - var i uint - - var v, s Element - - // Since u,v are updated every other iteration, we must make sure we terminate after evenly many iterations - // This also lets us get away with half as many updates to u,v - // To make this constant-time-ish, replace the condition with i < invIterationsN - for i = 0; i&1 == 1 || !a.IsZero(); i++ { - n := max(a.BitLen(), b.BitLen()) - aApprox, bApprox := approximate(&a, n), approximate(&b, n) - - // f₀, g₀, f₁, g₁ = 1, 0, 0, 1 - c0, c1 = updateFactorIdentityMatrixRow0, updateFactorIdentityMatrixRow1 - - for j := 0; j < approxLowBitsN; j++ { - - // -2ʲ < f₀, f₁ ≤ 2ʲ - // |f₀| + |f₁| < 2ʲ⁺¹ - - if aApprox&1 == 0 { - aApprox /= 2 - } else { - s, borrow := bits.Sub64(aApprox, bApprox, 0) - if borrow == 1 { - s = bApprox - aApprox - bApprox = aApprox - c0, c1 = c1, c0 - // invariants unchanged - } - - aApprox = s / 2 - c0 = c0 - c1 - - // Now |f₀| < 2ʲ⁺¹ ≤ 2ʲ⁺¹ (only the weaker inequality is needed, strictly speaking) - // Started with f₀ > -2ʲ and f₁ ≤ 2ʲ, so f₀ - f₁ > -2ʲ⁺¹ - // Invariants unchanged for f₁ - } - - c1 *= 2 - // -2ʲ⁺¹ < f₁ ≤ 2ʲ⁺¹ - // So now |f₀| + |f₁| < 2ʲ⁺² - } - - s = a - - var g0 int64 - // from this point on c0 aliases for f0 - c0, g0 = updateFactorsDecompose(c0) - aHi := a.linearCombNonModular(&s, c0, &b, g0) - if aHi&signBitSelector != 0 { - // if aHi < 0 - c0, g0 = -c0, -g0 - aHi = negL(&a, aHi) - } - // right-shift a by k-1 bits - a[0] = (a[0] >> approxLowBitsN) | ((a[1]) << approxHighBitsN) - a[1] = (a[1] >> approxLowBitsN) | ((a[2]) << approxHighBitsN) - a[2] = (a[2] >> approxLowBitsN) | ((a[3]) << approxHighBitsN) - a[3] = (a[3] >> approxLowBitsN) | ((a[4]) << approxHighBitsN) - a[4] = (a[4] >> approxLowBitsN) | ((a[5]) << approxHighBitsN) - a[5] = (a[5] >> approxLowBitsN) | ((a[6]) << approxHighBitsN) - a[6] = (a[6] >> approxLowBitsN) | ((a[7]) << approxHighBitsN) - a[7] = (a[7] >> approxLowBitsN) | ((a[8]) << approxHighBitsN) - a[8] = (a[8] >> approxLowBitsN) | ((a[9]) << approxHighBitsN) - a[9] = (a[9] >> approxLowBitsN) | ((a[10]) << approxHighBitsN) - a[10] = (a[10] >> approxLowBitsN) | ((a[11]) << approxHighBitsN) - a[11] = (a[11] >> approxLowBitsN) | (aHi << approxHighBitsN) - - var f1 int64 - // from this point on c1 aliases for g0 - f1, c1 = updateFactorsDecompose(c1) - bHi := b.linearCombNonModular(&s, f1, &b, c1) - if bHi&signBitSelector != 0 { - // if bHi < 0 - f1, c1 = -f1, -c1 - bHi = negL(&b, bHi) - } - // right-shift b by k-1 bits - b[0] = (b[0] >> approxLowBitsN) | ((b[1]) << approxHighBitsN) - b[1] = (b[1] >> approxLowBitsN) | ((b[2]) << approxHighBitsN) - b[2] = (b[2] >> approxLowBitsN) | ((b[3]) << approxHighBitsN) - b[3] = (b[3] >> approxLowBitsN) | ((b[4]) << approxHighBitsN) - b[4] = (b[4] >> approxLowBitsN) | ((b[5]) << approxHighBitsN) - b[5] = (b[5] >> approxLowBitsN) | ((b[6]) << approxHighBitsN) - b[6] = (b[6] >> approxLowBitsN) | ((b[7]) << approxHighBitsN) - b[7] = (b[7] >> approxLowBitsN) | ((b[8]) << approxHighBitsN) - b[8] = (b[8] >> approxLowBitsN) | ((b[9]) << approxHighBitsN) - b[9] = (b[9] >> approxLowBitsN) | ((b[10]) << approxHighBitsN) - b[10] = (b[10] >> approxLowBitsN) | ((b[11]) << approxHighBitsN) - b[11] = (b[11] >> approxLowBitsN) | (bHi << approxHighBitsN) - - if i&1 == 1 { - // Combine current update factors with previously stored ones - // [F₀, G₀; F₁, G₁] ← [f₀, g₀; f₁, g₁] [pf₀, pg₀; pf₁, pg₁], with capital letters denoting new combined values - // We get |F₀| = | f₀pf₀ + g₀pf₁ | ≤ |f₀pf₀| + |g₀pf₁| = |f₀| |pf₀| + |g₀| |pf₁| ≤ 2ᵏ⁻¹|pf₀| + 2ᵏ⁻¹|pf₁| - // = 2ᵏ⁻¹ (|pf₀| + |pf₁|) < 2ᵏ⁻¹ 2ᵏ = 2²ᵏ⁻¹ - // So |F₀| < 2²ᵏ⁻¹ meaning it fits in a 2k-bit signed register - - // c₀ aliases f₀, c₁ aliases g₁ - c0, g0, f1, c1 = c0*pf0+g0*pf1, - c0*pg0+g0*pg1, - f1*pf0+c1*pf1, - f1*pg0+c1*pg1 - - s = u - - // 0 ≤ u, v < 2²⁵⁵ - // |F₀|, |G₀| < 2⁶³ - u.linearComb(&u, c0, &v, g0) - // |F₁|, |G₁| < 2⁶³ - v.linearComb(&s, f1, &v, c1) - - } else { - // Save update factors - pf0, pg0, pf1, pg1 = c0, g0, f1, c1 - } - } - - // For every iteration that we miss, v is not being multiplied by 2ᵏ⁻² - const pSq uint64 = 1 << (2 * (k - 1)) - a = Element{pSq} - // If the function is constant-time ish, this loop will not run (no need to take it out explicitly) - for ; i < invIterationsN; i += 2 { - // could optimize further with mul by word routine or by pre-computing a table since with k=26, - // we would multiply by pSq up to 13times; - // on x86, the assembly routine outperforms generic code for mul by word - // on arm64, we may loose up to ~5% for 6 limbs - v.Mul(&v, &a) - } - - u.Set(x) // for correctness check - - z.Mul(&v, &Element{ - inversionCorrectionFactorWord0, - inversionCorrectionFactorWord1, - inversionCorrectionFactorWord2, - inversionCorrectionFactorWord3, - inversionCorrectionFactorWord4, - inversionCorrectionFactorWord5, - inversionCorrectionFactorWord6, - inversionCorrectionFactorWord7, - inversionCorrectionFactorWord8, - inversionCorrectionFactorWord9, - inversionCorrectionFactorWord10, - inversionCorrectionFactorWord11, - }) - - // correctness check - v.Mul(&u, z) - if !v.IsOne() && !u.IsZero() { - return z.inverseExp(u) - } - - return z -} - -// inverseExp computes z = x⁻¹ (mod q) = x**(q-2) (mod q) -func (z *Element) inverseExp(x Element) *Element { - // e == q-2 - e := Modulus() - e.Sub(e, big.NewInt(2)) - - z.Set(&x) - - for i := e.BitLen() - 2; i >= 0; i-- { - z.Square(z) - if e.Bit(i) == 1 { - z.Mul(z, &x) - } - } - - return z -} - -// approximate a big number x into a single 64 bit word using its uppermost and lowermost bits -// if x fits in a word as is, no approximation necessary -func approximate(x *Element, nBits int) uint64 { - - if nBits <= 64 { - return x[0] - } - - const mask = (uint64(1) << (k - 1)) - 1 // k-1 ones - lo := mask & x[0] - - hiWordIndex := (nBits - 1) / 64 - - hiWordBitsAvailable := nBits - hiWordIndex*64 - hiWordBitsUsed := min(hiWordBitsAvailable, approxHighBitsN) - - mask_ := uint64(^((1 << (hiWordBitsAvailable - hiWordBitsUsed)) - 1)) - hi := (x[hiWordIndex] & mask_) << (64 - hiWordBitsAvailable) - - mask_ = ^(1<<(approxLowBitsN+hiWordBitsUsed) - 1) - mid := (mask_ & x[hiWordIndex-1]) >> hiWordBitsUsed - - return lo | mid | hi -} - -// linearComb z = xC * x + yC * y; -// 0 ≤ x, y < 2⁷⁵⁶ -// |xC|, |yC| < 2⁶³ -func (z *Element) linearComb(x *Element, xC int64, y *Element, yC int64) { - // | (hi, z) | < 2 * 2⁶³ * 2⁷⁵⁶ = 2⁸²⁰ - // therefore | hi | < 2⁵² ≤ 2⁶³ - hi := z.linearCombNonModular(x, xC, y, yC) - z.montReduceSigned(z, hi) -} - -// montReduceSigned z = (xHi * r + x) * r⁻¹ using the SOS algorithm -// Requires |xHi| < 2⁶³. Most significant bit of xHi is the sign bit. -func (z *Element) montReduceSigned(x *Element, xHi uint64) { - const signBitRemover = ^signBitSelector - mustNeg := xHi&signBitSelector != 0 - // the SOS implementation requires that most significant bit is 0 - // Let X be xHi*r + x - // If X is negative we would have initially stored it as 2⁶⁴ r + X (à la 2's complement) - xHi &= signBitRemover - // with this a negative X is now represented as 2⁶³ r + X - - var t [2*Limbs - 1]uint64 - var C uint64 - - m := x[0] * qInvNeg - - C = madd0(m, q0, x[0]) - C, t[1] = madd2(m, q1, x[1], C) - C, t[2] = madd2(m, q2, x[2], C) - C, t[3] = madd2(m, q3, x[3], C) - C, t[4] = madd2(m, q4, x[4], C) - C, t[5] = madd2(m, q5, x[5], C) - C, t[6] = madd2(m, q6, x[6], C) - C, t[7] = madd2(m, q7, x[7], C) - C, t[8] = madd2(m, q8, x[8], C) - C, t[9] = madd2(m, q9, x[9], C) - C, t[10] = madd2(m, q10, x[10], C) - C, t[11] = madd2(m, q11, x[11], C) - - // m * qElement[11] ≤ (2⁶⁴ - 1) * (2⁶³ - 1) = 2¹²⁷ - 2⁶⁴ - 2⁶³ + 1 - // x[11] + C ≤ 2*(2⁶⁴ - 1) = 2⁶⁵ - 2 - // On LHS, (C, t[11]) ≤ 2¹²⁷ - 2⁶⁴ - 2⁶³ + 1 + 2⁶⁵ - 2 = 2¹²⁷ + 2⁶³ - 1 - // So on LHS, C ≤ 2⁶³ - t[12] = xHi + C - // xHi + C < 2⁶³ + 2⁶³ = 2⁶⁴ - - // - { - const i = 1 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - C, t[i+6] = madd2(m, q6, t[i+6], C) - C, t[i+7] = madd2(m, q7, t[i+7], C) - C, t[i+8] = madd2(m, q8, t[i+8], C) - C, t[i+9] = madd2(m, q9, t[i+9], C) - C, t[i+10] = madd2(m, q10, t[i+10], C) - C, t[i+11] = madd2(m, q11, t[i+11], C) - - t[i+Limbs] += C - } - { - const i = 2 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - C, t[i+6] = madd2(m, q6, t[i+6], C) - C, t[i+7] = madd2(m, q7, t[i+7], C) - C, t[i+8] = madd2(m, q8, t[i+8], C) - C, t[i+9] = madd2(m, q9, t[i+9], C) - C, t[i+10] = madd2(m, q10, t[i+10], C) - C, t[i+11] = madd2(m, q11, t[i+11], C) - - t[i+Limbs] += C - } - { - const i = 3 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - C, t[i+6] = madd2(m, q6, t[i+6], C) - C, t[i+7] = madd2(m, q7, t[i+7], C) - C, t[i+8] = madd2(m, q8, t[i+8], C) - C, t[i+9] = madd2(m, q9, t[i+9], C) - C, t[i+10] = madd2(m, q10, t[i+10], C) - C, t[i+11] = madd2(m, q11, t[i+11], C) - - t[i+Limbs] += C - } - { - const i = 4 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - C, t[i+6] = madd2(m, q6, t[i+6], C) - C, t[i+7] = madd2(m, q7, t[i+7], C) - C, t[i+8] = madd2(m, q8, t[i+8], C) - C, t[i+9] = madd2(m, q9, t[i+9], C) - C, t[i+10] = madd2(m, q10, t[i+10], C) - C, t[i+11] = madd2(m, q11, t[i+11], C) - - t[i+Limbs] += C - } - { - const i = 5 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - C, t[i+6] = madd2(m, q6, t[i+6], C) - C, t[i+7] = madd2(m, q7, t[i+7], C) - C, t[i+8] = madd2(m, q8, t[i+8], C) - C, t[i+9] = madd2(m, q9, t[i+9], C) - C, t[i+10] = madd2(m, q10, t[i+10], C) - C, t[i+11] = madd2(m, q11, t[i+11], C) - - t[i+Limbs] += C - } - { - const i = 6 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - C, t[i+6] = madd2(m, q6, t[i+6], C) - C, t[i+7] = madd2(m, q7, t[i+7], C) - C, t[i+8] = madd2(m, q8, t[i+8], C) - C, t[i+9] = madd2(m, q9, t[i+9], C) - C, t[i+10] = madd2(m, q10, t[i+10], C) - C, t[i+11] = madd2(m, q11, t[i+11], C) - - t[i+Limbs] += C - } - { - const i = 7 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - C, t[i+6] = madd2(m, q6, t[i+6], C) - C, t[i+7] = madd2(m, q7, t[i+7], C) - C, t[i+8] = madd2(m, q8, t[i+8], C) - C, t[i+9] = madd2(m, q9, t[i+9], C) - C, t[i+10] = madd2(m, q10, t[i+10], C) - C, t[i+11] = madd2(m, q11, t[i+11], C) - - t[i+Limbs] += C - } - { - const i = 8 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - C, t[i+6] = madd2(m, q6, t[i+6], C) - C, t[i+7] = madd2(m, q7, t[i+7], C) - C, t[i+8] = madd2(m, q8, t[i+8], C) - C, t[i+9] = madd2(m, q9, t[i+9], C) - C, t[i+10] = madd2(m, q10, t[i+10], C) - C, t[i+11] = madd2(m, q11, t[i+11], C) - - t[i+Limbs] += C - } - { - const i = 9 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - C, t[i+6] = madd2(m, q6, t[i+6], C) - C, t[i+7] = madd2(m, q7, t[i+7], C) - C, t[i+8] = madd2(m, q8, t[i+8], C) - C, t[i+9] = madd2(m, q9, t[i+9], C) - C, t[i+10] = madd2(m, q10, t[i+10], C) - C, t[i+11] = madd2(m, q11, t[i+11], C) - - t[i+Limbs] += C - } - { - const i = 10 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - C, t[i+6] = madd2(m, q6, t[i+6], C) - C, t[i+7] = madd2(m, q7, t[i+7], C) - C, t[i+8] = madd2(m, q8, t[i+8], C) - C, t[i+9] = madd2(m, q9, t[i+9], C) - C, t[i+10] = madd2(m, q10, t[i+10], C) - C, t[i+11] = madd2(m, q11, t[i+11], C) - - t[i+Limbs] += C - } - { - const i = 11 - m := t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, z[0] = madd2(m, q1, t[i+1], C) - C, z[1] = madd2(m, q2, t[i+2], C) - C, z[2] = madd2(m, q3, t[i+3], C) - C, z[3] = madd2(m, q4, t[i+4], C) - C, z[4] = madd2(m, q5, t[i+5], C) - C, z[5] = madd2(m, q6, t[i+6], C) - C, z[6] = madd2(m, q7, t[i+7], C) - C, z[7] = madd2(m, q8, t[i+8], C) - C, z[8] = madd2(m, q9, t[i+9], C) - C, z[9] = madd2(m, q10, t[i+10], C) - z[11], z[10] = madd2(m, q11, t[i+11], C) - } - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], b = bits.Sub64(z[5], q5, b) - z[6], b = bits.Sub64(z[6], q6, b) - z[7], b = bits.Sub64(z[7], q7, b) - z[8], b = bits.Sub64(z[8], q8, b) - z[9], b = bits.Sub64(z[9], q9, b) - z[10], b = bits.Sub64(z[10], q10, b) - z[11], _ = bits.Sub64(z[11], q11, b) - } - // - - if mustNeg { - // We have computed ( 2⁶³ r + X ) r⁻¹ = 2⁶³ + X r⁻¹ instead - var b uint64 - z[0], b = bits.Sub64(z[0], signBitSelector, 0) - z[1], b = bits.Sub64(z[1], 0, b) - z[2], b = bits.Sub64(z[2], 0, b) - z[3], b = bits.Sub64(z[3], 0, b) - z[4], b = bits.Sub64(z[4], 0, b) - z[5], b = bits.Sub64(z[5], 0, b) - z[6], b = bits.Sub64(z[6], 0, b) - z[7], b = bits.Sub64(z[7], 0, b) - z[8], b = bits.Sub64(z[8], 0, b) - z[9], b = bits.Sub64(z[9], 0, b) - z[10], b = bits.Sub64(z[10], 0, b) - z[11], b = bits.Sub64(z[11], 0, b) - - // Occurs iff x == 0 && xHi < 0, i.e. X = rX' for -2⁶³ ≤ X' < 0 - - if b != 0 { - // z[11] = -1 - // negative: add q - const neg1 = 0xFFFFFFFFFFFFFFFF - - var carry uint64 - - z[0], carry = bits.Add64(z[0], q0, 0) - z[1], carry = bits.Add64(z[1], q1, carry) - z[2], carry = bits.Add64(z[2], q2, carry) - z[3], carry = bits.Add64(z[3], q3, carry) - z[4], carry = bits.Add64(z[4], q4, carry) - z[5], carry = bits.Add64(z[5], q5, carry) - z[6], carry = bits.Add64(z[6], q6, carry) - z[7], carry = bits.Add64(z[7], q7, carry) - z[8], carry = bits.Add64(z[8], q8, carry) - z[9], carry = bits.Add64(z[9], q9, carry) - z[10], carry = bits.Add64(z[10], q10, carry) - z[11], _ = bits.Add64(neg1, q11, carry) - } - } -} - -const ( - updateFactorsConversionBias int64 = 0x7fffffff7fffffff // (2³¹ - 1)(2³² + 1) - updateFactorIdentityMatrixRow0 = 1 - updateFactorIdentityMatrixRow1 = 1 << 32 -) - -func updateFactorsDecompose(c int64) (int64, int64) { - c += updateFactorsConversionBias - const low32BitsFilter int64 = 0xFFFFFFFF - f := c&low32BitsFilter - 0x7FFFFFFF - g := c>>32&low32BitsFilter - 0x7FFFFFFF - return f, g -} - -// negL negates in place [x | xHi] and return the new most significant word xHi -func negL(x *Element, xHi uint64) uint64 { - var b uint64 - - x[0], b = bits.Sub64(0, x[0], 0) - x[1], b = bits.Sub64(0, x[1], b) - x[2], b = bits.Sub64(0, x[2], b) - x[3], b = bits.Sub64(0, x[3], b) - x[4], b = bits.Sub64(0, x[4], b) - x[5], b = bits.Sub64(0, x[5], b) - x[6], b = bits.Sub64(0, x[6], b) - x[7], b = bits.Sub64(0, x[7], b) - x[8], b = bits.Sub64(0, x[8], b) - x[9], b = bits.Sub64(0, x[9], b) - x[10], b = bits.Sub64(0, x[10], b) - x[11], b = bits.Sub64(0, x[11], b) - xHi, _ = bits.Sub64(0, xHi, b) - - return xHi -} - -// mulWNonModular multiplies by one word in non-montgomery, without reducing -func (z *Element) mulWNonModular(x *Element, y int64) uint64 { - - // w := abs(y) - m := y >> 63 - w := uint64((y ^ m) - m) - - var c uint64 - c, z[0] = bits.Mul64(x[0], w) - c, z[1] = madd1(x[1], w, c) - c, z[2] = madd1(x[2], w, c) - c, z[3] = madd1(x[3], w, c) - c, z[4] = madd1(x[4], w, c) - c, z[5] = madd1(x[5], w, c) - c, z[6] = madd1(x[6], w, c) - c, z[7] = madd1(x[7], w, c) - c, z[8] = madd1(x[8], w, c) - c, z[9] = madd1(x[9], w, c) - c, z[10] = madd1(x[10], w, c) - c, z[11] = madd1(x[11], w, c) - - if y < 0 { - c = negL(z, c) - } - - return c -} - -// linearCombNonModular computes a linear combination without modular reduction -func (z *Element) linearCombNonModular(x *Element, xC int64, y *Element, yC int64) uint64 { - var yTimes Element - - yHi := yTimes.mulWNonModular(y, yC) - xHi := z.mulWNonModular(x, xC) - - var carry uint64 - z[0], carry = bits.Add64(z[0], yTimes[0], 0) - z[1], carry = bits.Add64(z[1], yTimes[1], carry) - z[2], carry = bits.Add64(z[2], yTimes[2], carry) - z[3], carry = bits.Add64(z[3], yTimes[3], carry) - z[4], carry = bits.Add64(z[4], yTimes[4], carry) - z[5], carry = bits.Add64(z[5], yTimes[5], carry) - z[6], carry = bits.Add64(z[6], yTimes[6], carry) - z[7], carry = bits.Add64(z[7], yTimes[7], carry) - z[8], carry = bits.Add64(z[8], yTimes[8], carry) - z[9], carry = bits.Add64(z[9], yTimes[9], carry) - z[10], carry = bits.Add64(z[10], yTimes[10], carry) - z[11], carry = bits.Add64(z[11], yTimes[11], carry) - - yHi, _ = bits.Add64(xHi, yHi, carry) - - return yHi -} diff --git a/ecc/bw6-756/fp/element_exp.go b/ecc/bw6-756/fp/element_exp.go deleted file mode 100644 index af741d6434..0000000000 --- a/ecc/bw6-756/fp/element_exp.go +++ /dev/null @@ -1,1993 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -// expBySqrtExp is equivalent to z.Exp(x, 1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d0bd0d0a49d80678fcc7f0d0) -// -// uses github.com/mmcloughlin/addchain v0.4.0 to generate a shorter addition chain -func (z *Element) expBySqrtExp(x Element) *Element { - // addition chain: - // - // _10 = 2*1 - // _11 = 1 + _10 - // _100 = 1 + _11 - // _101 = 1 + _100 - // _1001 = _100 + _101 - // _1011 = _10 + _1001 - // _1101 = _10 + _1011 - // _1111 = _10 + _1101 - // _10001 = _10 + _1111 - // _10101 = _100 + _10001 - // _10111 = _10 + _10101 - // _11001 = _10 + _10111 - // _11011 = _10 + _11001 - // _11101 = _10 + _11011 - // _11111 = _10 + _11101 - // _100001 = _10 + _11111 - // _100011 = _10 + _100001 - // _100101 = _10 + _100011 - // _100111 = _10 + _100101 - // _101001 = _10 + _100111 - // _101011 = _10 + _101001 - // _101101 = _10 + _101011 - // _101111 = _10 + _101101 - // _110001 = _10 + _101111 - // _110011 = _10 + _110001 - // _110101 = _10 + _110011 - // _110111 = _10 + _110101 - // _111001 = _10 + _110111 - // _111011 = _10 + _111001 - // _111101 = _10 + _111011 - // _111111 = _10 + _111101 - // _1111010 = _111011 + _111111 - // i52 = ((_1111010 << 4 + _11011) << 7 + _101011) << 7 - // i67 = ((_110111 + i52) << 7 + _110101) << 5 + _10111 - // i87 = ((i67 << 7 + _111001) << 5 + _10001) << 6 - // i101 = ((_10111 + i87) << 8 + _10101) << 3 + _11 - // i128 = ((i101 << 9 + _1001) << 8 + _111111) << 8 - // i145 = ((_1111 + i128) << 9 + _110101) << 5 + _1101 - // i167 = ((i145 << 9 + _110011) << 6 + _110101) << 5 - // i187 = ((_11001 + i167) << 8 + _101111) << 9 + _110011 - // i205 = ((i187 << 7 + _100101) << 6 + _111101) << 3 - // i223 = ((_11 + i205) << 8 + _1011) << 7 + _11101 - // i244 = ((i223 << 9 + _100111) << 6 + _111011) << 4 - // i262 = ((_1111 + i244) << 8 + _100011) << 7 + _10001 - // i285 = ((i262 << 7 + _101) << 8 + _10101) << 6 - // i299 = ((_10001 + i285) << 7 + _110001) << 4 + _1101 - // i325 = ((i299 << 7 + _11011) << 8 + _110011) << 9 - // i341 = ((_110101 + i325) << 7 + _111001) << 6 + _110011 - // i366 = ((i341 << 6 + _110001) << 9 + _10101) << 8 - // i383 = ((_100011 + i366) << 6 + _11011) << 8 + _111101 - // i401 = ((i383 << 3 + _11) << 10 + _1011) << 3 - // i422 = ((1 + i401) << 12 + _100101) << 6 + _110101 - // i448 = ((i422 << 12 + _100111) << 6 + _110101) << 6 - // i467 = ((_10101 + i448) << 11 + _101001) << 5 + _11111 - // i490 = ((i467 << 5 + _1011) << 9 + _111001) << 7 - // i508 = ((_110011 + i490) << 4 + _1101) << 11 + _110111 - // i535 = ((i508 << 7 + _11001) << 9 + _110111) << 9 - // i550 = ((_101001 + i535) << 6 + _1011) << 6 + _1101 - // i572 = ((i550 << 9 + _101011) << 5 + _11011) << 6 - // i590 = ((_11011 + i572) << 6 + _11001) << 9 + _110101 - // i616 = ((i590 << 7 + _10101) << 6 + _11) << 11 - // i630 = ((_10101 + i616) << 4 + _101) << 7 + _1111 - // i653 = ((i630 << 10 + _100101) << 6 + _100011) << 5 - // i670 = ((_1111 + i653) << 7 + _11111) << 7 + _111101 - // i688 = ((i670 << 3 + _101) << 10 + _101101) << 3 - // i708 = ((_101 + i688) << 10 + _101111) << 7 + _100001 - // i731 = ((i708 << 3 + _101) << 10 + _101001) << 8 - // i751 = ((_100111 + i731) << 3 + _11) << 14 + _110011 - // i768 = ((i751 << 6 + _110001) << 5 + _11111) << 4 - // i781 = 2*((_11 + i768) << 9 + _111111) + 1 - // return (i781 << 8 + _1101) << 4 - // - // Operations: 667 squares 127 multiplies - - // Allocate Temporaries. - var ( - t0 = new(Element) - t1 = new(Element) - t2 = new(Element) - t3 = new(Element) - t4 = new(Element) - t5 = new(Element) - t6 = new(Element) - t7 = new(Element) - t8 = new(Element) - t9 = new(Element) - t10 = new(Element) - t11 = new(Element) - t12 = new(Element) - t13 = new(Element) - t14 = new(Element) - t15 = new(Element) - t16 = new(Element) - t17 = new(Element) - t18 = new(Element) - t19 = new(Element) - t20 = new(Element) - t21 = new(Element) - t22 = new(Element) - t23 = new(Element) - t24 = new(Element) - t25 = new(Element) - t26 = new(Element) - t27 = new(Element) - t28 = new(Element) - ) - - // var t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21,t22,t23,t24,t25,t26,t27,t28 Element - // Step 1: t0 = x^0x2 - t0.Square(&x) - - // Step 2: t1 = x^0x3 - t1.Mul(&x, t0) - - // Step 3: t2 = x^0x4 - t2.Mul(&x, t1) - - // Step 4: t7 = x^0x5 - t7.Mul(&x, t2) - - // Step 5: t26 = x^0x9 - t26.Mul(t2, t7) - - // Step 6: t20 = x^0xb - t20.Mul(t0, t26) - - // Step 7: z = x^0xd - z.Mul(t0, t20) - - // Step 8: t12 = x^0xf - t12.Mul(t0, z) - - // Step 9: t23 = x^0x11 - t23.Mul(t0, t12) - - // Step 10: t15 = x^0x15 - t15.Mul(t2, t23) - - // Step 11: t27 = x^0x17 - t27.Mul(t0, t15) - - // Step 12: t17 = x^0x19 - t17.Mul(t0, t27) - - // Step 13: t18 = x^0x1b - t18.Mul(t0, t17) - - // Step 14: t25 = x^0x1d - t25.Mul(t0, t18) - - // Step 15: t2 = x^0x1f - t2.Mul(t0, t25) - - // Step 16: t8 = x^0x21 - t8.Mul(t0, t2) - - // Step 17: t13 = x^0x23 - t13.Mul(t0, t8) - - // Step 18: t14 = x^0x25 - t14.Mul(t0, t13) - - // Step 19: t5 = x^0x27 - t5.Mul(t0, t14) - - // Step 20: t6 = x^0x29 - t6.Mul(t0, t5) - - // Step 21: t19 = x^0x2b - t19.Mul(t0, t6) - - // Step 22: t10 = x^0x2d - t10.Mul(t0, t19) - - // Step 23: t9 = x^0x2f - t9.Mul(t0, t10) - - // Step 24: t3 = x^0x31 - t3.Mul(t0, t9) - - // Step 25: t4 = x^0x33 - t4.Mul(t0, t3) - - // Step 26: t16 = x^0x35 - t16.Mul(t0, t4) - - // Step 27: t21 = x^0x37 - t21.Mul(t0, t16) - - // Step 28: t22 = x^0x39 - t22.Mul(t0, t21) - - // Step 29: t24 = x^0x3b - t24.Mul(t0, t22) - - // Step 30: t11 = x^0x3d - t11.Mul(t0, t24) - - // Step 31: t0 = x^0x3f - t0.Mul(t0, t11) - - // Step 32: t28 = x^0x7a - t28.Mul(t24, t0) - - // Step 36: t28 = x^0x7a0 - for s := 0; s < 4; s++ { - t28.Square(t28) - } - - // Step 37: t28 = x^0x7bb - t28.Mul(t18, t28) - - // Step 44: t28 = x^0x3dd80 - for s := 0; s < 7; s++ { - t28.Square(t28) - } - - // Step 45: t28 = x^0x3ddab - t28.Mul(t19, t28) - - // Step 52: t28 = x^0x1eed580 - for s := 0; s < 7; s++ { - t28.Square(t28) - } - - // Step 53: t28 = x^0x1eed5b7 - t28.Mul(t21, t28) - - // Step 60: t28 = x^0xf76adb80 - for s := 0; s < 7; s++ { - t28.Square(t28) - } - - // Step 61: t28 = x^0xf76adbb5 - t28.Mul(t16, t28) - - // Step 66: t28 = x^0x1eed5b76a0 - for s := 0; s < 5; s++ { - t28.Square(t28) - } - - // Step 67: t28 = x^0x1eed5b76b7 - t28.Mul(t27, t28) - - // Step 74: t28 = x^0xf76adbb5b80 - for s := 0; s < 7; s++ { - t28.Square(t28) - } - - // Step 75: t28 = x^0xf76adbb5bb9 - t28.Mul(t22, t28) - - // Step 80: t28 = x^0x1eed5b76b7720 - for s := 0; s < 5; s++ { - t28.Square(t28) - } - - // Step 81: t28 = x^0x1eed5b76b7731 - t28.Mul(t23, t28) - - // Step 87: t28 = x^0x7bb56ddaddcc40 - for s := 0; s < 6; s++ { - t28.Square(t28) - } - - // Step 88: t27 = x^0x7bb56ddaddcc57 - t27.Mul(t27, t28) - - // Step 96: t27 = x^0x7bb56ddaddcc5700 - for s := 0; s < 8; s++ { - t27.Square(t27) - } - - // Step 97: t27 = x^0x7bb56ddaddcc5715 - t27.Mul(t15, t27) - - // Step 100: t27 = x^0x3ddab6ed6ee62b8a8 - for s := 0; s < 3; s++ { - t27.Square(t27) - } - - // Step 101: t27 = x^0x3ddab6ed6ee62b8ab - t27.Mul(t1, t27) - - // Step 110: t27 = x^0x7bb56ddaddcc5715600 - for s := 0; s < 9; s++ { - t27.Square(t27) - } - - // Step 111: t26 = x^0x7bb56ddaddcc5715609 - t26.Mul(t26, t27) - - // Step 119: t26 = x^0x7bb56ddaddcc571560900 - for s := 0; s < 8; s++ { - t26.Square(t26) - } - - // Step 120: t26 = x^0x7bb56ddaddcc57156093f - t26.Mul(t0, t26) - - // Step 128: t26 = x^0x7bb56ddaddcc57156093f00 - for s := 0; s < 8; s++ { - t26.Square(t26) - } - - // Step 129: t26 = x^0x7bb56ddaddcc57156093f0f - t26.Mul(t12, t26) - - // Step 138: t26 = x^0xf76adbb5bb98ae2ac127e1e00 - for s := 0; s < 9; s++ { - t26.Square(t26) - } - - // Step 139: t26 = x^0xf76adbb5bb98ae2ac127e1e35 - t26.Mul(t16, t26) - - // Step 144: t26 = x^0x1eed5b76b77315c55824fc3c6a0 - for s := 0; s < 5; s++ { - t26.Square(t26) - } - - // Step 145: t26 = x^0x1eed5b76b77315c55824fc3c6ad - t26.Mul(z, t26) - - // Step 154: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a00 - for s := 0; s < 9; s++ { - t26.Square(t26) - } - - // Step 155: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a33 - t26.Mul(t4, t26) - - // Step 161: t26 = x^0xf76adbb5bb98ae2ac127e1e3568cc0 - for s := 0; s < 6; s++ { - t26.Square(t26) - } - - // Step 162: t26 = x^0xf76adbb5bb98ae2ac127e1e3568cf5 - t26.Mul(t16, t26) - - // Step 167: t26 = x^0x1eed5b76b77315c55824fc3c6ad19ea0 - for s := 0; s < 5; s++ { - t26.Square(t26) - } - - // Step 168: t26 = x^0x1eed5b76b77315c55824fc3c6ad19eb9 - t26.Mul(t17, t26) - - // Step 176: t26 = x^0x1eed5b76b77315c55824fc3c6ad19eb900 - for s := 0; s < 8; s++ { - t26.Square(t26) - } - - // Step 177: t26 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f - t26.Mul(t9, t26) - - // Step 186: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e00 - for s := 0; s < 9; s++ { - t26.Square(t26) - } - - // Step 187: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e33 - t26.Mul(t4, t26) - - // Step 194: t26 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f1980 - for s := 0; s < 7; s++ { - t26.Square(t26) - } - - // Step 195: t26 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5 - t26.Mul(t14, t26) - - // Step 201: t26 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc66940 - for s := 0; s < 6; s++ { - t26.Square(t26) - } - - // Step 202: t26 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d - t26.Mul(t11, t26) - - // Step 205: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334be8 - for s := 0; s < 3; s++ { - t26.Square(t26) - } - - // Step 206: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb - t26.Mul(t1, t26) - - // Step 214: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb00 - for s := 0; s < 8; s++ { - t26.Square(t26) - } - - // Step 215: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b - t26.Mul(t20, t26) - - // Step 222: t26 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f58580 - for s := 0; s < 7; s++ { - t26.Square(t26) - } - - // Step 223: t25 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d - t25.Mul(t25, t26) - - // Step 232: t25 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a00 - for s := 0; s < 9; s++ { - t25.Square(t25) - } - - // Step 233: t25 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27 - t25.Mul(t5, t25) - - // Step 239: t25 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89c0 - for s := 0; s < 6; s++ { - t25.Square(t25) - } - - // Step 240: t24 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fb - t24.Mul(t24, t25) - - // Step 244: t24 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fb0 - for s := 0; s < 4; s++ { - t24.Square(t24) - } - - // Step 245: t24 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf - t24.Mul(t12, t24) - - // Step 253: t24 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf00 - for s := 0; s < 8; s++ { - t24.Square(t24) - } - - // Step 254: t24 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23 - t24.Mul(t13, t24) - - // Step 261: t24 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf9180 - for s := 0; s < 7; s++ { - t24.Square(t24) - } - - // Step 262: t24 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf9191 - t24.Mul(t23, t24) - - // Step 269: t24 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c880 - for s := 0; s < 7; s++ { - t24.Square(t24) - } - - // Step 270: t24 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c885 - t24.Mul(t7, t24) - - // Step 278: t24 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88500 - for s := 0; s < 8; s++ { - t24.Square(t24) - } - - // Step 279: t24 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515 - t24.Mul(t15, t24) - - // Step 285: t24 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf232214540 - for s := 0; s < 6; s++ { - t24.Square(t24) - } - - // Step 286: t23 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf232214551 - t23.Mul(t23, t24) - - // Step 293: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a880 - for s := 0; s < 7; s++ { - t23.Square(t23) - } - - // Step 294: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1 - t23.Mul(t3, t23) - - // Step 298: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b10 - for s := 0; s < 4; s++ { - t23.Square(t23) - } - - // Step 299: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d - t23.Mul(z, t23) - - // Step 306: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e80 - for s := 0; s < 7; s++ { - t23.Square(t23) - } - - // Step 307: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b - t23.Mul(t18, t23) - - // Step 315: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b00 - for s := 0; s < 8; s++ { - t23.Square(t23) - } - - // Step 316: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b33 - t23.Mul(t4, t23) - - // Step 325: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366600 - for s := 0; s < 9; s++ { - t23.Square(t23) - } - - // Step 326: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635 - t23.Mul(t16, t23) - - // Step 333: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331a80 - for s := 0; s < 7; s++ { - t23.Square(t23) - } - - // Step 334: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9 - t23.Mul(t22, t23) - - // Step 340: t23 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae40 - for s := 0; s < 6; s++ { - t23.Square(t23) - } - - // Step 341: t23 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73 - t23.Mul(t4, t23) - - // Step 347: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cc0 - for s := 0; s < 6; s++ { - t23.Square(t23) - } - - // Step 348: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf1 - t23.Mul(t3, t23) - - // Step 357: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e200 - for s := 0; s < 9; s++ { - t23.Square(t23) - } - - // Step 358: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215 - t23.Mul(t15, t23) - - // Step 366: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e21500 - for s := 0; s < 8; s++ { - t23.Square(t23) - } - - // Step 367: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e21523 - t23.Mul(t13, t23) - - // Step 373: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548c0 - for s := 0; s < 6; s++ { - t23.Square(t23) - } - - // Step 374: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db - t23.Mul(t18, t23) - - // Step 382: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db00 - for s := 0; s < 8; s++ { - t23.Square(t23) - } - - // Step 383: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d - t23.Mul(t11, t23) - - // Step 386: t23 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9e8 - for s := 0; s < 3; s++ { - t23.Square(t23) - } - - // Step 387: t23 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb - t23.Mul(t1, t23) - - // Step 397: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac00 - for s := 0; s < 10; s++ { - t23.Square(t23) - } - - // Step 398: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b - t23.Mul(t20, t23) - - // Step 401: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6058 - for s := 0; s < 3; s++ { - t23.Square(t23) - } - - // Step 402: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059 - t23.Mul(&x, t23) - - // Step 414: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059000 - for s := 0; s < 12; s++ { - t23.Square(t23) - } - - // Step 415: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025 - t23.Mul(t14, t23) - - // Step 421: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640940 - for s := 0; s < 6; s++ { - t23.Square(t23) - } - - // Step 422: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975 - t23.Mul(t16, t23) - - // Step 434: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975000 - for s := 0; s < 12; s++ { - t23.Square(t23) - } - - // Step 435: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027 - t23.Mul(t5, t23) - - // Step 441: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409c0 - for s := 0; s < 6; s++ { - t23.Square(t23) - } - - // Step 442: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f5 - t23.Mul(t16, t23) - - // Step 448: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d40 - for s := 0; s < 6; s++ { - t23.Square(t23) - } - - // Step 449: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55 - t23.Mul(t15, t23) - - // Step 460: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa800 - for s := 0; s < 11; s++ { - t23.Square(t23) - } - - // Step 461: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829 - t23.Mul(t6, t23) - - // Step 466: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d550520 - for s := 0; s < 5; s++ { - t23.Square(t23) - } - - // Step 467: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f - t23.Mul(t2, t23) - - // Step 472: t23 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7e0 - for s := 0; s < 5; s++ { - t23.Square(t23) - } - - // Step 473: t23 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb - t23.Mul(t20, t23) - - // Step 482: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd600 - for s := 0; s < 9; s++ { - t23.Square(t23) - } - - // Step 483: t22 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd639 - t22.Mul(t22, t23) - - // Step 490: t22 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1c80 - for s := 0; s < 7; s++ { - t22.Square(t22) - } - - // Step 491: t22 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3 - t22.Mul(t4, t22) - - // Step 495: t22 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb30 - for s := 0; s < 4; s++ { - t22.Square(t22) - } - - // Step 496: t22 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d - t22.Mul(z, t22) - - // Step 507: t22 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e800 - for s := 0; s < 11; s++ { - t22.Square(t22) - } - - // Step 508: t22 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e837 - t22.Mul(t21, t22) - - // Step 515: t22 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b80 - for s := 0; s < 7; s++ { - t22.Square(t22) - } - - // Step 516: t22 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b99 - t22.Mul(t17, t22) - - // Step 525: t22 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373200 - for s := 0; s < 9; s++ { - t22.Square(t22) - } - - // Step 526: t21 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237 - t21.Mul(t21, t22) - - // Step 535: t21 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e00 - for s := 0; s < 9; s++ { - t21.Square(t21) - } - - // Step 536: t21 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e29 - t21.Mul(t6, t21) - - // Step 542: t21 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a40 - for s := 0; s < 6; s++ { - t21.Square(t21) - } - - // Step 543: t20 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b - t20.Mul(t20, t21) - - // Step 549: t20 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292c0 - for s := 0; s < 6; s++ { - t20.Square(t20) - } - - // Step 550: t20 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd - t20.Mul(z, t20) - - // Step 559: t20 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a00 - for s := 0; s < 9; s++ { - t20.Square(t20) - } - - // Step 560: t19 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2b - t19.Mul(t19, t20) - - // Step 565: t19 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b34560 - for s := 0; s < 5; s++ { - t19.Square(t19) - } - - // Step 566: t19 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b - t19.Mul(t18, t19) - - // Step 572: t19 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15ec0 - for s := 0; s < 6; s++ { - t19.Square(t19) - } - - // Step 573: t18 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb - t18.Mul(t18, t19) - - // Step 579: t18 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6c0 - for s := 0; s < 6; s++ { - t18.Square(t18) - } - - // Step 580: t17 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d9 - t17.Mul(t17, t18) - - // Step 589: t17 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db200 - for s := 0; s < 9; s++ { - t17.Square(t17) - } - - // Step 590: t16 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db235 - t16.Mul(t16, t17) - - // Step 597: t16 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a80 - for s := 0; s < 7; s++ { - t16.Square(t16) - } - - // Step 598: t16 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a95 - t16.Mul(t15, t16) - - // Step 604: t16 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a540 - for s := 0; s < 6; s++ { - t16.Square(t16) - } - - // Step 605: t16 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a543 - t16.Mul(t1, t16) - - // Step 616: t16 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a1800 - for s := 0; s < 11; s++ { - t16.Square(t16) - } - - // Step 617: t15 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a1815 - t15.Mul(t15, t16) - - // Step 621: t15 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a18150 - for s := 0; s < 4; s++ { - t15.Square(t15) - } - - // Step 622: t15 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a18155 - t15.Mul(t7, t15) - - // Step 629: t15 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa80 - for s := 0; s < 7; s++ { - t15.Square(t15) - } - - // Step 630: t15 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f - t15.Mul(t12, t15) - - // Step 640: t15 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c00 - for s := 0; s < 10; s++ { - t15.Square(t15) - } - - // Step 641: t14 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c25 - t14.Mul(t14, t15) - - // Step 647: t14 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f0940 - for s := 0; s < 6; s++ { - t14.Square(t14) - } - - // Step 648: t13 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f0963 - t13.Mul(t13, t14) - - // Step 653: t13 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c60 - for s := 0; s < 5; s++ { - t13.Square(t13) - } - - // Step 654: t12 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f - t12.Mul(t12, t13) - - // Step 661: t12 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f0963780 - for s := 0; s < 7; s++ { - t12.Square(t12) - } - - // Step 662: t12 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f - t12.Mul(t2, t12) - - // Step 669: t12 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcf80 - for s := 0; s < 7; s++ { - t12.Square(t12) - } - - // Step 670: t11 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbd - t11.Mul(t11, t12) - - // Step 673: t11 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7de8 - for s := 0; s < 3; s++ { - t11.Square(t11) - } - - // Step 674: t11 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7ded - t11.Mul(t7, t11) - - // Step 684: t11 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b400 - for s := 0; s < 10; s++ { - t11.Square(t11) - } - - // Step 685: t10 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42d - t10.Mul(t10, t11) - - // Step 688: t10 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda168 - for s := 0; s < 3; s++ { - t10.Square(t10) - } - - // Step 689: t10 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d - t10.Mul(t7, t10) - - // Step 699: t10 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b400 - for s := 0; s < 10; s++ { - t10.Square(t10) - } - - // Step 700: t9 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f - t9.Mul(t9, t10) - - // Step 707: t9 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42da1780 - for s := 0; s < 7; s++ { - t9.Square(t9) - } - - // Step 708: t8 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42da17a1 - t8.Mul(t8, t9) - - // Step 711: t8 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d0bd08 - for s := 0; s < 3; s++ { - t8.Square(t8) - } - - // Step 712: t7 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d0bd0d - t7.Mul(t7, t8) - - // Step 722: t7 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43400 - for s := 0; s < 10; s++ { - t7.Square(t7) - } - - // Step 723: t6 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43429 - t6.Mul(t6, t7) - - // Step 731: t6 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f4342900 - for s := 0; s < 8; s++ { - t6.Square(t6) - } - - // Step 732: t5 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f4342927 - t5.Mul(t5, t6) - - // Step 735: t5 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42da17a1a14938 - for s := 0; s < 3; s++ { - t5.Square(t5) - } - - // Step 736: t5 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42da17a1a1493b - t5.Mul(t1, t5) - - // Step 750: t5 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7ded0b685e868524ec000 - for s := 0; s < 14; s++ { - t5.Square(t5) - } - - // Step 751: t4 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7ded0b685e868524ec033 - t4.Mul(t4, t5) - - // Step 757: t4 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42da17a1a1493b00cc0 - for s := 0; s < 6; s++ { - t4.Square(t4) - } - - // Step 758: t3 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42da17a1a1493b00cf1 - t3.Mul(t3, t4) - - // Step 763: t3 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43429276019e20 - for s := 0; s < 5; s++ { - t3.Square(t3) - } - - // Step 764: t2 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43429276019e3f - t2.Mul(t2, t3) - - // Step 768: t2 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43429276019e3f0 - for s := 0; s < 4; s++ { - t2.Square(t2) - } - - // Step 769: t1 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43429276019e3f3 - t1.Mul(t1, t2) - - // Step 778: t1 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7ded0b685e868524ec033c7e600 - for s := 0; s < 9; s++ { - t1.Square(t1) - } - - // Step 779: t0 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7ded0b685e868524ec033c7e63f - t0.Mul(t0, t1) - - // Step 780: t0 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d0bd0d0a49d80678fcc7e - t0.Square(t0) - - // Step 781: t0 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d0bd0d0a49d80678fcc7f - t0.Mul(&x, t0) - - // Step 789: t0 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d0bd0d0a49d80678fcc7f00 - for s := 0; s < 8; s++ { - t0.Square(t0) - } - - // Step 790: z = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d0bd0d0a49d80678fcc7f0d - z.Mul(z, t0) - - // Step 794: z = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d0bd0d0a49d80678fcc7f0d0 - for s := 0; s < 4; s++ { - z.Square(z) - } - - return z -} - -// expByLegendreExp is equivalent to z.Exp(x, 7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43429276019e3f31fc34200000000000000000000) -// -// uses github.com/mmcloughlin/addchain v0.4.0 to generate a shorter addition chain -func (z *Element) expByLegendreExp(x Element) *Element { - // addition chain: - // - // _10 = 2*1 - // _11 = 1 + _10 - // _100 = 1 + _11 - // _101 = 1 + _100 - // _1001 = _100 + _101 - // _1011 = _10 + _1001 - // _1101 = _10 + _1011 - // _1111 = _10 + _1101 - // _10001 = _10 + _1111 - // _10101 = _100 + _10001 - // _10111 = _10 + _10101 - // _11001 = _10 + _10111 - // _11011 = _10 + _11001 - // _11101 = _10 + _11011 - // _11111 = _10 + _11101 - // _100001 = _10 + _11111 - // _100011 = _10 + _100001 - // _100101 = _10 + _100011 - // _100111 = _10 + _100101 - // _101001 = _10 + _100111 - // _101011 = _10 + _101001 - // _101101 = _10 + _101011 - // _101111 = _10 + _101101 - // _110001 = _10 + _101111 - // _110011 = _10 + _110001 - // _110101 = _10 + _110011 - // _110111 = _10 + _110101 - // _111001 = _10 + _110111 - // _111011 = _10 + _111001 - // _111101 = _10 + _111011 - // _111111 = _10 + _111101 - // _1111010 = _111011 + _111111 - // i52 = ((_1111010 << 4 + _11011) << 7 + _101011) << 7 - // i67 = ((_110111 + i52) << 7 + _110101) << 5 + _10111 - // i87 = ((i67 << 7 + _111001) << 5 + _10001) << 6 - // i101 = ((_10111 + i87) << 8 + _10101) << 3 + _11 - // i128 = ((i101 << 9 + _1001) << 8 + _111111) << 8 - // i145 = ((_1111 + i128) << 9 + _110101) << 5 + _1101 - // i167 = ((i145 << 9 + _110011) << 6 + _110101) << 5 - // i187 = ((_11001 + i167) << 8 + _101111) << 9 + _110011 - // i205 = ((i187 << 7 + _100101) << 6 + _111101) << 3 - // i223 = ((_11 + i205) << 8 + _1011) << 7 + _11101 - // i244 = ((i223 << 9 + _100111) << 6 + _111011) << 4 - // i262 = ((_1111 + i244) << 8 + _100011) << 7 + _10001 - // i285 = ((i262 << 7 + _101) << 8 + _10101) << 6 - // i299 = ((_10001 + i285) << 7 + _110001) << 4 + _1101 - // i325 = ((i299 << 7 + _11011) << 8 + _110011) << 9 - // i341 = ((_110101 + i325) << 7 + _111001) << 6 + _110011 - // i366 = ((i341 << 6 + _110001) << 9 + _10101) << 8 - // i383 = ((_100011 + i366) << 6 + _11011) << 8 + _111101 - // i401 = ((i383 << 3 + _11) << 10 + _1011) << 3 - // i422 = ((1 + i401) << 12 + _100101) << 6 + _110101 - // i448 = ((i422 << 12 + _100111) << 6 + _110101) << 6 - // i467 = ((_10101 + i448) << 11 + _101001) << 5 + _11111 - // i490 = ((i467 << 5 + _1011) << 9 + _111001) << 7 - // i508 = ((_110011 + i490) << 4 + _1101) << 11 + _110111 - // i535 = ((i508 << 7 + _11001) << 9 + _110111) << 9 - // i550 = ((_101001 + i535) << 6 + _1011) << 6 + _1101 - // i572 = ((i550 << 9 + _101011) << 5 + _11011) << 6 - // i590 = ((_11011 + i572) << 6 + _11001) << 9 + _110101 - // i616 = ((i590 << 7 + _10101) << 6 + _11) << 11 - // i630 = ((_10101 + i616) << 4 + _101) << 7 + _1111 - // i653 = ((i630 << 10 + _100101) << 6 + _100011) << 5 - // i670 = ((_1111 + i653) << 7 + _11111) << 7 + _111101 - // i688 = ((i670 << 3 + _101) << 10 + _101101) << 3 - // i708 = ((_101 + i688) << 10 + _101111) << 7 + _100001 - // i731 = ((i708 << 3 + _101) << 10 + _101001) << 8 - // i751 = ((_100111 + i731) << 3 + _11) << 14 + _110011 - // i768 = ((i751 << 6 + _110001) << 5 + _11111) << 4 - // i781 = 2*((_11 + i768) << 9 + _111111) + 1 - // return ((i781 << 8 + _1101) << 5 + 1) << 81 - // - // Operations: 749 squares 128 multiplies - - // Allocate Temporaries. - var ( - t0 = new(Element) - t1 = new(Element) - t2 = new(Element) - t3 = new(Element) - t4 = new(Element) - t5 = new(Element) - t6 = new(Element) - t7 = new(Element) - t8 = new(Element) - t9 = new(Element) - t10 = new(Element) - t11 = new(Element) - t12 = new(Element) - t13 = new(Element) - t14 = new(Element) - t15 = new(Element) - t16 = new(Element) - t17 = new(Element) - t18 = new(Element) - t19 = new(Element) - t20 = new(Element) - t21 = new(Element) - t22 = new(Element) - t23 = new(Element) - t24 = new(Element) - t25 = new(Element) - t26 = new(Element) - t27 = new(Element) - t28 = new(Element) - ) - - // var t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21,t22,t23,t24,t25,t26,t27,t28 Element - // Step 1: t0 = x^0x2 - t0.Square(&x) - - // Step 2: t1 = x^0x3 - t1.Mul(&x, t0) - - // Step 3: t2 = x^0x4 - t2.Mul(&x, t1) - - // Step 4: t7 = x^0x5 - t7.Mul(&x, t2) - - // Step 5: t26 = x^0x9 - t26.Mul(t2, t7) - - // Step 6: t20 = x^0xb - t20.Mul(t0, t26) - - // Step 7: z = x^0xd - z.Mul(t0, t20) - - // Step 8: t12 = x^0xf - t12.Mul(t0, z) - - // Step 9: t23 = x^0x11 - t23.Mul(t0, t12) - - // Step 10: t15 = x^0x15 - t15.Mul(t2, t23) - - // Step 11: t27 = x^0x17 - t27.Mul(t0, t15) - - // Step 12: t17 = x^0x19 - t17.Mul(t0, t27) - - // Step 13: t18 = x^0x1b - t18.Mul(t0, t17) - - // Step 14: t25 = x^0x1d - t25.Mul(t0, t18) - - // Step 15: t2 = x^0x1f - t2.Mul(t0, t25) - - // Step 16: t8 = x^0x21 - t8.Mul(t0, t2) - - // Step 17: t13 = x^0x23 - t13.Mul(t0, t8) - - // Step 18: t14 = x^0x25 - t14.Mul(t0, t13) - - // Step 19: t5 = x^0x27 - t5.Mul(t0, t14) - - // Step 20: t6 = x^0x29 - t6.Mul(t0, t5) - - // Step 21: t19 = x^0x2b - t19.Mul(t0, t6) - - // Step 22: t10 = x^0x2d - t10.Mul(t0, t19) - - // Step 23: t9 = x^0x2f - t9.Mul(t0, t10) - - // Step 24: t3 = x^0x31 - t3.Mul(t0, t9) - - // Step 25: t4 = x^0x33 - t4.Mul(t0, t3) - - // Step 26: t16 = x^0x35 - t16.Mul(t0, t4) - - // Step 27: t21 = x^0x37 - t21.Mul(t0, t16) - - // Step 28: t22 = x^0x39 - t22.Mul(t0, t21) - - // Step 29: t24 = x^0x3b - t24.Mul(t0, t22) - - // Step 30: t11 = x^0x3d - t11.Mul(t0, t24) - - // Step 31: t0 = x^0x3f - t0.Mul(t0, t11) - - // Step 32: t28 = x^0x7a - t28.Mul(t24, t0) - - // Step 36: t28 = x^0x7a0 - for s := 0; s < 4; s++ { - t28.Square(t28) - } - - // Step 37: t28 = x^0x7bb - t28.Mul(t18, t28) - - // Step 44: t28 = x^0x3dd80 - for s := 0; s < 7; s++ { - t28.Square(t28) - } - - // Step 45: t28 = x^0x3ddab - t28.Mul(t19, t28) - - // Step 52: t28 = x^0x1eed580 - for s := 0; s < 7; s++ { - t28.Square(t28) - } - - // Step 53: t28 = x^0x1eed5b7 - t28.Mul(t21, t28) - - // Step 60: t28 = x^0xf76adb80 - for s := 0; s < 7; s++ { - t28.Square(t28) - } - - // Step 61: t28 = x^0xf76adbb5 - t28.Mul(t16, t28) - - // Step 66: t28 = x^0x1eed5b76a0 - for s := 0; s < 5; s++ { - t28.Square(t28) - } - - // Step 67: t28 = x^0x1eed5b76b7 - t28.Mul(t27, t28) - - // Step 74: t28 = x^0xf76adbb5b80 - for s := 0; s < 7; s++ { - t28.Square(t28) - } - - // Step 75: t28 = x^0xf76adbb5bb9 - t28.Mul(t22, t28) - - // Step 80: t28 = x^0x1eed5b76b7720 - for s := 0; s < 5; s++ { - t28.Square(t28) - } - - // Step 81: t28 = x^0x1eed5b76b7731 - t28.Mul(t23, t28) - - // Step 87: t28 = x^0x7bb56ddaddcc40 - for s := 0; s < 6; s++ { - t28.Square(t28) - } - - // Step 88: t27 = x^0x7bb56ddaddcc57 - t27.Mul(t27, t28) - - // Step 96: t27 = x^0x7bb56ddaddcc5700 - for s := 0; s < 8; s++ { - t27.Square(t27) - } - - // Step 97: t27 = x^0x7bb56ddaddcc5715 - t27.Mul(t15, t27) - - // Step 100: t27 = x^0x3ddab6ed6ee62b8a8 - for s := 0; s < 3; s++ { - t27.Square(t27) - } - - // Step 101: t27 = x^0x3ddab6ed6ee62b8ab - t27.Mul(t1, t27) - - // Step 110: t27 = x^0x7bb56ddaddcc5715600 - for s := 0; s < 9; s++ { - t27.Square(t27) - } - - // Step 111: t26 = x^0x7bb56ddaddcc5715609 - t26.Mul(t26, t27) - - // Step 119: t26 = x^0x7bb56ddaddcc571560900 - for s := 0; s < 8; s++ { - t26.Square(t26) - } - - // Step 120: t26 = x^0x7bb56ddaddcc57156093f - t26.Mul(t0, t26) - - // Step 128: t26 = x^0x7bb56ddaddcc57156093f00 - for s := 0; s < 8; s++ { - t26.Square(t26) - } - - // Step 129: t26 = x^0x7bb56ddaddcc57156093f0f - t26.Mul(t12, t26) - - // Step 138: t26 = x^0xf76adbb5bb98ae2ac127e1e00 - for s := 0; s < 9; s++ { - t26.Square(t26) - } - - // Step 139: t26 = x^0xf76adbb5bb98ae2ac127e1e35 - t26.Mul(t16, t26) - - // Step 144: t26 = x^0x1eed5b76b77315c55824fc3c6a0 - for s := 0; s < 5; s++ { - t26.Square(t26) - } - - // Step 145: t26 = x^0x1eed5b76b77315c55824fc3c6ad - t26.Mul(z, t26) - - // Step 154: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a00 - for s := 0; s < 9; s++ { - t26.Square(t26) - } - - // Step 155: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a33 - t26.Mul(t4, t26) - - // Step 161: t26 = x^0xf76adbb5bb98ae2ac127e1e3568cc0 - for s := 0; s < 6; s++ { - t26.Square(t26) - } - - // Step 162: t26 = x^0xf76adbb5bb98ae2ac127e1e3568cf5 - t26.Mul(t16, t26) - - // Step 167: t26 = x^0x1eed5b76b77315c55824fc3c6ad19ea0 - for s := 0; s < 5; s++ { - t26.Square(t26) - } - - // Step 168: t26 = x^0x1eed5b76b77315c55824fc3c6ad19eb9 - t26.Mul(t17, t26) - - // Step 176: t26 = x^0x1eed5b76b77315c55824fc3c6ad19eb900 - for s := 0; s < 8; s++ { - t26.Square(t26) - } - - // Step 177: t26 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f - t26.Mul(t9, t26) - - // Step 186: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e00 - for s := 0; s < 9; s++ { - t26.Square(t26) - } - - // Step 187: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e33 - t26.Mul(t4, t26) - - // Step 194: t26 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f1980 - for s := 0; s < 7; s++ { - t26.Square(t26) - } - - // Step 195: t26 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5 - t26.Mul(t14, t26) - - // Step 201: t26 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc66940 - for s := 0; s < 6; s++ { - t26.Square(t26) - } - - // Step 202: t26 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d - t26.Mul(t11, t26) - - // Step 205: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334be8 - for s := 0; s < 3; s++ { - t26.Square(t26) - } - - // Step 206: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb - t26.Mul(t1, t26) - - // Step 214: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb00 - for s := 0; s < 8; s++ { - t26.Square(t26) - } - - // Step 215: t26 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b - t26.Mul(t20, t26) - - // Step 222: t26 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f58580 - for s := 0; s < 7; s++ { - t26.Square(t26) - } - - // Step 223: t25 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d - t25.Mul(t25, t26) - - // Step 232: t25 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a00 - for s := 0; s < 9; s++ { - t25.Square(t25) - } - - // Step 233: t25 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27 - t25.Mul(t5, t25) - - // Step 239: t25 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89c0 - for s := 0; s < 6; s++ { - t25.Square(t25) - } - - // Step 240: t24 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fb - t24.Mul(t24, t25) - - // Step 244: t24 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fb0 - for s := 0; s < 4; s++ { - t24.Square(t24) - } - - // Step 245: t24 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf - t24.Mul(t12, t24) - - // Step 253: t24 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf00 - for s := 0; s < 8; s++ { - t24.Square(t24) - } - - // Step 254: t24 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23 - t24.Mul(t13, t24) - - // Step 261: t24 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf9180 - for s := 0; s < 7; s++ { - t24.Square(t24) - } - - // Step 262: t24 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf9191 - t24.Mul(t23, t24) - - // Step 269: t24 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c880 - for s := 0; s < 7; s++ { - t24.Square(t24) - } - - // Step 270: t24 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c885 - t24.Mul(t7, t24) - - // Step 278: t24 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88500 - for s := 0; s < 8; s++ { - t24.Square(t24) - } - - // Step 279: t24 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515 - t24.Mul(t15, t24) - - // Step 285: t24 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf232214540 - for s := 0; s < 6; s++ { - t24.Square(t24) - } - - // Step 286: t23 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf232214551 - t23.Mul(t23, t24) - - // Step 293: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a880 - for s := 0; s < 7; s++ { - t23.Square(t23) - } - - // Step 294: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1 - t23.Mul(t3, t23) - - // Step 298: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b10 - for s := 0; s < 4; s++ { - t23.Square(t23) - } - - // Step 299: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d - t23.Mul(z, t23) - - // Step 306: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e80 - for s := 0; s < 7; s++ { - t23.Square(t23) - } - - // Step 307: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b - t23.Mul(t18, t23) - - // Step 315: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b00 - for s := 0; s < 8; s++ { - t23.Square(t23) - } - - // Step 316: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b33 - t23.Mul(t4, t23) - - // Step 325: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366600 - for s := 0; s < 9; s++ { - t23.Square(t23) - } - - // Step 326: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635 - t23.Mul(t16, t23) - - // Step 333: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331a80 - for s := 0; s < 7; s++ { - t23.Square(t23) - } - - // Step 334: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9 - t23.Mul(t22, t23) - - // Step 340: t23 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae40 - for s := 0; s < 6; s++ { - t23.Square(t23) - } - - // Step 341: t23 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73 - t23.Mul(t4, t23) - - // Step 347: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cc0 - for s := 0; s < 6; s++ { - t23.Square(t23) - } - - // Step 348: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf1 - t23.Mul(t3, t23) - - // Step 357: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e200 - for s := 0; s < 9; s++ { - t23.Square(t23) - } - - // Step 358: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215 - t23.Mul(t15, t23) - - // Step 366: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e21500 - for s := 0; s < 8; s++ { - t23.Square(t23) - } - - // Step 367: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e21523 - t23.Mul(t13, t23) - - // Step 373: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548c0 - for s := 0; s < 6; s++ { - t23.Square(t23) - } - - // Step 374: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db - t23.Mul(t18, t23) - - // Step 382: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db00 - for s := 0; s < 8; s++ { - t23.Square(t23) - } - - // Step 383: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d - t23.Mul(t11, t23) - - // Step 386: t23 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9e8 - for s := 0; s < 3; s++ { - t23.Square(t23) - } - - // Step 387: t23 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb - t23.Mul(t1, t23) - - // Step 397: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac00 - for s := 0; s < 10; s++ { - t23.Square(t23) - } - - // Step 398: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b - t23.Mul(t20, t23) - - // Step 401: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6058 - for s := 0; s < 3; s++ { - t23.Square(t23) - } - - // Step 402: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059 - t23.Mul(&x, t23) - - // Step 414: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059000 - for s := 0; s < 12; s++ { - t23.Square(t23) - } - - // Step 415: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025 - t23.Mul(t14, t23) - - // Step 421: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640940 - for s := 0; s < 6; s++ { - t23.Square(t23) - } - - // Step 422: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975 - t23.Mul(t16, t23) - - // Step 434: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975000 - for s := 0; s < 12; s++ { - t23.Square(t23) - } - - // Step 435: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027 - t23.Mul(t5, t23) - - // Step 441: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409c0 - for s := 0; s < 6; s++ { - t23.Square(t23) - } - - // Step 442: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f5 - t23.Mul(t16, t23) - - // Step 448: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d40 - for s := 0; s < 6; s++ { - t23.Square(t23) - } - - // Step 449: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55 - t23.Mul(t15, t23) - - // Step 460: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa800 - for s := 0; s < 11; s++ { - t23.Square(t23) - } - - // Step 461: t23 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829 - t23.Mul(t6, t23) - - // Step 466: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d550520 - for s := 0; s < 5; s++ { - t23.Square(t23) - } - - // Step 467: t23 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f - t23.Mul(t2, t23) - - // Step 472: t23 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7e0 - for s := 0; s < 5; s++ { - t23.Square(t23) - } - - // Step 473: t23 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb - t23.Mul(t20, t23) - - // Step 482: t23 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd600 - for s := 0; s < 9; s++ { - t23.Square(t23) - } - - // Step 483: t22 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd639 - t22.Mul(t22, t23) - - // Step 490: t22 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1c80 - for s := 0; s < 7; s++ { - t22.Square(t22) - } - - // Step 491: t22 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3 - t22.Mul(t4, t22) - - // Step 495: t22 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb30 - for s := 0; s < 4; s++ { - t22.Square(t22) - } - - // Step 496: t22 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d - t22.Mul(z, t22) - - // Step 507: t22 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e800 - for s := 0; s < 11; s++ { - t22.Square(t22) - } - - // Step 508: t22 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e837 - t22.Mul(t21, t22) - - // Step 515: t22 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b80 - for s := 0; s < 7; s++ { - t22.Square(t22) - } - - // Step 516: t22 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b99 - t22.Mul(t17, t22) - - // Step 525: t22 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373200 - for s := 0; s < 9; s++ { - t22.Square(t22) - } - - // Step 526: t21 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237 - t21.Mul(t21, t22) - - // Step 535: t21 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e00 - for s := 0; s < 9; s++ { - t21.Square(t21) - } - - // Step 536: t21 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e29 - t21.Mul(t6, t21) - - // Step 542: t21 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a40 - for s := 0; s < 6; s++ { - t21.Square(t21) - } - - // Step 543: t20 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b - t20.Mul(t20, t21) - - // Step 549: t20 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292c0 - for s := 0; s < 6; s++ { - t20.Square(t20) - } - - // Step 550: t20 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd - t20.Mul(z, t20) - - // Step 559: t20 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a00 - for s := 0; s < 9; s++ { - t20.Square(t20) - } - - // Step 560: t19 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2b - t19.Mul(t19, t20) - - // Step 565: t19 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b34560 - for s := 0; s < 5; s++ { - t19.Square(t19) - } - - // Step 566: t19 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b - t19.Mul(t18, t19) - - // Step 572: t19 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15ec0 - for s := 0; s < 6; s++ { - t19.Square(t19) - } - - // Step 573: t18 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb - t18.Mul(t18, t19) - - // Step 579: t18 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6c0 - for s := 0; s < 6; s++ { - t18.Square(t18) - } - - // Step 580: t17 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d9 - t17.Mul(t17, t18) - - // Step 589: t17 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db200 - for s := 0; s < 9; s++ { - t17.Square(t17) - } - - // Step 590: t16 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db235 - t16.Mul(t16, t17) - - // Step 597: t16 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a80 - for s := 0; s < 7; s++ { - t16.Square(t16) - } - - // Step 598: t16 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a95 - t16.Mul(t15, t16) - - // Step 604: t16 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a540 - for s := 0; s < 6; s++ { - t16.Square(t16) - } - - // Step 605: t16 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a543 - t16.Mul(t1, t16) - - // Step 616: t16 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a1800 - for s := 0; s < 11; s++ { - t16.Square(t16) - } - - // Step 617: t15 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a1815 - t15.Mul(t15, t16) - - // Step 621: t15 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a18150 - for s := 0; s < 4; s++ { - t15.Square(t15) - } - - // Step 622: t15 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a18155 - t15.Mul(t7, t15) - - // Step 629: t15 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa80 - for s := 0; s < 7; s++ { - t15.Square(t15) - } - - // Step 630: t15 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f - t15.Mul(t12, t15) - - // Step 640: t15 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c00 - for s := 0; s < 10; s++ { - t15.Square(t15) - } - - // Step 641: t14 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c25 - t14.Mul(t14, t15) - - // Step 647: t14 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f0940 - for s := 0; s < 6; s++ { - t14.Square(t14) - } - - // Step 648: t13 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f0963 - t13.Mul(t13, t14) - - // Step 653: t13 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c60 - for s := 0; s < 5; s++ { - t13.Square(t13) - } - - // Step 654: t12 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f - t12.Mul(t12, t13) - - // Step 661: t12 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f0963780 - for s := 0; s < 7; s++ { - t12.Square(t12) - } - - // Step 662: t12 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f - t12.Mul(t2, t12) - - // Step 669: t12 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcf80 - for s := 0; s < 7; s++ { - t12.Square(t12) - } - - // Step 670: t11 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbd - t11.Mul(t11, t12) - - // Step 673: t11 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7de8 - for s := 0; s < 3; s++ { - t11.Square(t11) - } - - // Step 674: t11 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7ded - t11.Mul(t7, t11) - - // Step 684: t11 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b400 - for s := 0; s < 10; s++ { - t11.Square(t11) - } - - // Step 685: t10 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42d - t10.Mul(t10, t11) - - // Step 688: t10 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda168 - for s := 0; s < 3; s++ { - t10.Square(t10) - } - - // Step 689: t10 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d - t10.Mul(t7, t10) - - // Step 699: t10 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b400 - for s := 0; s < 10; s++ { - t10.Square(t10) - } - - // Step 700: t9 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f - t9.Mul(t9, t10) - - // Step 707: t9 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42da1780 - for s := 0; s < 7; s++ { - t9.Square(t9) - } - - // Step 708: t8 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42da17a1 - t8.Mul(t8, t9) - - // Step 711: t8 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d0bd08 - for s := 0; s < 3; s++ { - t8.Square(t8) - } - - // Step 712: t7 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d0bd0d - t7.Mul(t7, t8) - - // Step 722: t7 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43400 - for s := 0; s < 10; s++ { - t7.Square(t7) - } - - // Step 723: t6 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43429 - t6.Mul(t6, t7) - - // Step 731: t6 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f4342900 - for s := 0; s < 8; s++ { - t6.Square(t6) - } - - // Step 732: t5 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f4342927 - t5.Mul(t5, t6) - - // Step 735: t5 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42da17a1a14938 - for s := 0; s < 3; s++ { - t5.Square(t5) - } - - // Step 736: t5 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42da17a1a1493b - t5.Mul(t1, t5) - - // Step 750: t5 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7ded0b685e868524ec000 - for s := 0; s < 14; s++ { - t5.Square(t5) - } - - // Step 751: t4 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7ded0b685e868524ec033 - t4.Mul(t4, t5) - - // Step 757: t4 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42da17a1a1493b00cc0 - for s := 0; s < 6; s++ { - t4.Square(t4) - } - - // Step 758: t3 = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42da17a1a1493b00cf1 - t3.Mul(t3, t4) - - // Step 763: t3 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43429276019e20 - for s := 0; s < 5; s++ { - t3.Square(t3) - } - - // Step 764: t2 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43429276019e3f - t2.Mul(t2, t3) - - // Step 768: t2 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43429276019e3f0 - for s := 0; s < 4; s++ { - t2.Square(t2) - } - - // Step 769: t1 = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43429276019e3f3 - t1.Mul(t1, t2) - - // Step 778: t1 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7ded0b685e868524ec033c7e600 - for s := 0; s < 9; s++ { - t1.Square(t1) - } - - // Step 779: t0 = x^0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7ded0b685e868524ec033c7e63f - t0.Mul(t0, t1) - - // Step 780: t0 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d0bd0d0a49d80678fcc7e - t0.Square(t0) - - // Step 781: t0 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d0bd0d0a49d80678fcc7f - t0.Mul(&x, t0) - - // Step 789: t0 = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d0bd0d0a49d80678fcc7f00 - for s := 0; s < 8; s++ { - t0.Square(t0) - } - - // Step 790: z = x^0x1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d0bd0d0a49d80678fcc7f0d - z.Mul(z, t0) - - // Step 795: z = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42da17a1a1493b00cf1f98fe1a0 - for s := 0; s < 5; s++ { - z.Square(z) - } - - // Step 796: z = x^0x3ddab6ed6ee62b8ab049f878d5a33d725e334beb0b3a27efc8c88515458e9b331ab9cf10a91b67ac0b204ba813eaa829fac72cf41b991b8a4b3457b6d91a950c0aa8f096379f7b42da17a1a1493b00cf1f98fe1a1 - z.Mul(&x, z) - - // Step 877: z = x^0x7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43429276019e3f31fc34200000000000000000000 - for s := 0; s < 81; s++ { - z.Square(z) - } - - return z -} diff --git a/ecc/bw6-756/fp/element_mul_amd64.s b/ecc/bw6-756/fp/element_mul_amd64.s deleted file mode 100644 index b01df63c25..0000000000 --- a/ecc/bw6-756/fp/element_mul_amd64.s +++ /dev/null @@ -1,2758 +0,0 @@ -// +build !purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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. - -#include "textflag.h" -#include "funcdata.h" - -// modulus q -DATA q<>+0(SB)/8, $1 -DATA q<>+8(SB)/8, $0x33c7e63f86840000 -DATA q<>+16(SB)/8, $0xd0b685e868524ec0 -DATA q<>+24(SB)/8, $0x4302aa3c258de7de -DATA q<>+32(SB)/8, $0xe292cd15edb646a5 -DATA q<>+40(SB)/8, $0x0a7eb1cb3d06e646 -DATA q<>+48(SB)/8, $0xeb02c812ea04faaa -DATA q<>+56(SB)/8, $0xccc6ae73c42a46d9 -DATA q<>+64(SB)/8, $0xfbf23221455163a6 -DATA q<>+72(SB)/8, $0x5c978cd2fac2ce89 -DATA q<>+80(SB)/8, $0xe2ac127e1e3568cf -DATA q<>+88(SB)/8, $0x000f76adbb5bb98a -GLOBL q<>(SB), (RODATA+NOPTR), $96 - -// qInv0 q'[0] -DATA qInv0<>(SB)/8, $0xffffffffffffffff -GLOBL qInv0<>(SB), (RODATA+NOPTR), $8 - -#define REDUCE(ra0, ra1, ra2, ra3, ra4, ra5, ra6, ra7, ra8, ra9, ra10, ra11, rb0, rb1, rb2, rb3, rb4, rb5, rb6, rb7, rb8, rb9, rb10, rb11) \ - MOVQ ra0, rb0; \ - SUBQ q<>(SB), ra0; \ - MOVQ ra1, rb1; \ - SBBQ q<>+8(SB), ra1; \ - MOVQ ra2, rb2; \ - SBBQ q<>+16(SB), ra2; \ - MOVQ ra3, rb3; \ - SBBQ q<>+24(SB), ra3; \ - MOVQ ra4, rb4; \ - SBBQ q<>+32(SB), ra4; \ - MOVQ ra5, rb5; \ - SBBQ q<>+40(SB), ra5; \ - MOVQ ra6, rb6; \ - SBBQ q<>+48(SB), ra6; \ - MOVQ ra7, rb7; \ - SBBQ q<>+56(SB), ra7; \ - MOVQ ra8, rb8; \ - SBBQ q<>+64(SB), ra8; \ - MOVQ ra9, rb9; \ - SBBQ q<>+72(SB), ra9; \ - MOVQ ra10, rb10; \ - SBBQ q<>+80(SB), ra10; \ - MOVQ ra11, rb11; \ - SBBQ q<>+88(SB), ra11; \ - CMOVQCS rb0, ra0; \ - CMOVQCS rb1, ra1; \ - CMOVQCS rb2, ra2; \ - CMOVQCS rb3, ra3; \ - CMOVQCS rb4, ra4; \ - CMOVQCS rb5, ra5; \ - CMOVQCS rb6, ra6; \ - CMOVQCS rb7, ra7; \ - CMOVQCS rb8, ra8; \ - CMOVQCS rb9, ra9; \ - CMOVQCS rb10, ra10; \ - CMOVQCS rb11, ra11; \ - -// mul(res, x, y *Element) -TEXT ·mul(SB), $96-24 - - // the algorithm is described in the Element.Mul declaration (.go) - // however, to benefit from the ADCX and ADOX carry chains - // we split the inner loops in 2: - // for i=0 to N-1 - // for j=0 to N-1 - // (A,t[j]) := t[j] + x[j]*y[i] + A - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // t[N-1] = C + A - - NO_LOCAL_POINTERS - CMPB ·supportAdx(SB), $1 - JNE l1 - MOVQ x+8(FP), AX - - // x[0] -> s0-8(SP) - // x[1] -> s1-16(SP) - // x[2] -> s2-24(SP) - // x[3] -> s3-32(SP) - // x[4] -> s4-40(SP) - // x[5] -> s5-48(SP) - // x[6] -> s6-56(SP) - // x[7] -> s7-64(SP) - // x[8] -> s8-72(SP) - // x[9] -> s9-80(SP) - // x[10] -> s10-88(SP) - // x[11] -> s11-96(SP) - MOVQ 0(AX), R14 - MOVQ 8(AX), R15 - MOVQ 16(AX), CX - MOVQ 24(AX), BX - MOVQ 32(AX), SI - MOVQ 40(AX), DI - MOVQ 48(AX), R8 - MOVQ 56(AX), R9 - MOVQ 64(AX), R10 - MOVQ 72(AX), R11 - MOVQ 80(AX), R12 - MOVQ 88(AX), R13 - MOVQ R14, s0-8(SP) - MOVQ R15, s1-16(SP) - MOVQ CX, s2-24(SP) - MOVQ BX, s3-32(SP) - MOVQ SI, s4-40(SP) - MOVQ DI, s5-48(SP) - MOVQ R8, s6-56(SP) - MOVQ R9, s7-64(SP) - MOVQ R10, s8-72(SP) - MOVQ R11, s9-80(SP) - MOVQ R12, s10-88(SP) - MOVQ R13, s11-96(SP) - - // A -> BP - // t[0] -> R14 - // t[1] -> R15 - // t[2] -> CX - // t[3] -> BX - // t[4] -> SI - // t[5] -> DI - // t[6] -> R8 - // t[7] -> R9 - // t[8] -> R10 - // t[9] -> R11 - // t[10] -> R12 - // t[11] -> R13 - // clear the flags - XORQ AX, AX - MOVQ y+16(FP), AX - MOVQ 0(AX), DX - - // (A,t[0]) := x[0]*y[0] + A - MULXQ s0-8(SP), R14, R15 - - // (A,t[1]) := x[1]*y[0] + A - MULXQ s1-16(SP), AX, CX - ADOXQ AX, R15 - - // (A,t[2]) := x[2]*y[0] + A - MULXQ s2-24(SP), AX, BX - ADOXQ AX, CX - - // (A,t[3]) := x[3]*y[0] + A - MULXQ s3-32(SP), AX, SI - ADOXQ AX, BX - - // (A,t[4]) := x[4]*y[0] + A - MULXQ s4-40(SP), AX, DI - ADOXQ AX, SI - - // (A,t[5]) := x[5]*y[0] + A - MULXQ s5-48(SP), AX, R8 - ADOXQ AX, DI - - // (A,t[6]) := x[6]*y[0] + A - MULXQ s6-56(SP), AX, R9 - ADOXQ AX, R8 - - // (A,t[7]) := x[7]*y[0] + A - MULXQ s7-64(SP), AX, R10 - ADOXQ AX, R9 - - // (A,t[8]) := x[8]*y[0] + A - MULXQ s8-72(SP), AX, R11 - ADOXQ AX, R10 - - // (A,t[9]) := x[9]*y[0] + A - MULXQ s9-80(SP), AX, R12 - ADOXQ AX, R11 - - // (A,t[10]) := x[10]*y[0] + A - MULXQ s10-88(SP), AX, R13 - ADOXQ AX, R12 - - // (A,t[11]) := x[11]*y[0] + A - MULXQ s11-96(SP), AX, BP - ADOXQ AX, R13 - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADOXQ AX, BP - PUSHQ BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - POPQ BP - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - - // t[11] = C + A - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ BP, R13 - - // clear the flags - XORQ AX, AX - MOVQ y+16(FP), AX - MOVQ 8(AX), DX - - // (A,t[0]) := t[0] + x[0]*y[1] + A - MULXQ s0-8(SP), AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[1] + A - ADCXQ BP, R15 - MULXQ s1-16(SP), AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[1] + A - ADCXQ BP, CX - MULXQ s2-24(SP), AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[1] + A - ADCXQ BP, BX - MULXQ s3-32(SP), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[1] + A - ADCXQ BP, SI - MULXQ s4-40(SP), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[1] + A - ADCXQ BP, DI - MULXQ s5-48(SP), AX, BP - ADOXQ AX, DI - - // (A,t[6]) := t[6] + x[6]*y[1] + A - ADCXQ BP, R8 - MULXQ s6-56(SP), AX, BP - ADOXQ AX, R8 - - // (A,t[7]) := t[7] + x[7]*y[1] + A - ADCXQ BP, R9 - MULXQ s7-64(SP), AX, BP - ADOXQ AX, R9 - - // (A,t[8]) := t[8] + x[8]*y[1] + A - ADCXQ BP, R10 - MULXQ s8-72(SP), AX, BP - ADOXQ AX, R10 - - // (A,t[9]) := t[9] + x[9]*y[1] + A - ADCXQ BP, R11 - MULXQ s9-80(SP), AX, BP - ADOXQ AX, R11 - - // (A,t[10]) := t[10] + x[10]*y[1] + A - ADCXQ BP, R12 - MULXQ s10-88(SP), AX, BP - ADOXQ AX, R12 - - // (A,t[11]) := t[11] + x[11]*y[1] + A - ADCXQ BP, R13 - MULXQ s11-96(SP), AX, BP - ADOXQ AX, R13 - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - PUSHQ BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - POPQ BP - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - - // t[11] = C + A - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ BP, R13 - - // clear the flags - XORQ AX, AX - MOVQ y+16(FP), AX - MOVQ 16(AX), DX - - // (A,t[0]) := t[0] + x[0]*y[2] + A - MULXQ s0-8(SP), AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[2] + A - ADCXQ BP, R15 - MULXQ s1-16(SP), AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[2] + A - ADCXQ BP, CX - MULXQ s2-24(SP), AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[2] + A - ADCXQ BP, BX - MULXQ s3-32(SP), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[2] + A - ADCXQ BP, SI - MULXQ s4-40(SP), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[2] + A - ADCXQ BP, DI - MULXQ s5-48(SP), AX, BP - ADOXQ AX, DI - - // (A,t[6]) := t[6] + x[6]*y[2] + A - ADCXQ BP, R8 - MULXQ s6-56(SP), AX, BP - ADOXQ AX, R8 - - // (A,t[7]) := t[7] + x[7]*y[2] + A - ADCXQ BP, R9 - MULXQ s7-64(SP), AX, BP - ADOXQ AX, R9 - - // (A,t[8]) := t[8] + x[8]*y[2] + A - ADCXQ BP, R10 - MULXQ s8-72(SP), AX, BP - ADOXQ AX, R10 - - // (A,t[9]) := t[9] + x[9]*y[2] + A - ADCXQ BP, R11 - MULXQ s9-80(SP), AX, BP - ADOXQ AX, R11 - - // (A,t[10]) := t[10] + x[10]*y[2] + A - ADCXQ BP, R12 - MULXQ s10-88(SP), AX, BP - ADOXQ AX, R12 - - // (A,t[11]) := t[11] + x[11]*y[2] + A - ADCXQ BP, R13 - MULXQ s11-96(SP), AX, BP - ADOXQ AX, R13 - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - PUSHQ BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - POPQ BP - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - - // t[11] = C + A - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ BP, R13 - - // clear the flags - XORQ AX, AX - MOVQ y+16(FP), AX - MOVQ 24(AX), DX - - // (A,t[0]) := t[0] + x[0]*y[3] + A - MULXQ s0-8(SP), AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[3] + A - ADCXQ BP, R15 - MULXQ s1-16(SP), AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[3] + A - ADCXQ BP, CX - MULXQ s2-24(SP), AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[3] + A - ADCXQ BP, BX - MULXQ s3-32(SP), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[3] + A - ADCXQ BP, SI - MULXQ s4-40(SP), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[3] + A - ADCXQ BP, DI - MULXQ s5-48(SP), AX, BP - ADOXQ AX, DI - - // (A,t[6]) := t[6] + x[6]*y[3] + A - ADCXQ BP, R8 - MULXQ s6-56(SP), AX, BP - ADOXQ AX, R8 - - // (A,t[7]) := t[7] + x[7]*y[3] + A - ADCXQ BP, R9 - MULXQ s7-64(SP), AX, BP - ADOXQ AX, R9 - - // (A,t[8]) := t[8] + x[8]*y[3] + A - ADCXQ BP, R10 - MULXQ s8-72(SP), AX, BP - ADOXQ AX, R10 - - // (A,t[9]) := t[9] + x[9]*y[3] + A - ADCXQ BP, R11 - MULXQ s9-80(SP), AX, BP - ADOXQ AX, R11 - - // (A,t[10]) := t[10] + x[10]*y[3] + A - ADCXQ BP, R12 - MULXQ s10-88(SP), AX, BP - ADOXQ AX, R12 - - // (A,t[11]) := t[11] + x[11]*y[3] + A - ADCXQ BP, R13 - MULXQ s11-96(SP), AX, BP - ADOXQ AX, R13 - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - PUSHQ BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - POPQ BP - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - - // t[11] = C + A - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ BP, R13 - - // clear the flags - XORQ AX, AX - MOVQ y+16(FP), AX - MOVQ 32(AX), DX - - // (A,t[0]) := t[0] + x[0]*y[4] + A - MULXQ s0-8(SP), AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[4] + A - ADCXQ BP, R15 - MULXQ s1-16(SP), AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[4] + A - ADCXQ BP, CX - MULXQ s2-24(SP), AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[4] + A - ADCXQ BP, BX - MULXQ s3-32(SP), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[4] + A - ADCXQ BP, SI - MULXQ s4-40(SP), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[4] + A - ADCXQ BP, DI - MULXQ s5-48(SP), AX, BP - ADOXQ AX, DI - - // (A,t[6]) := t[6] + x[6]*y[4] + A - ADCXQ BP, R8 - MULXQ s6-56(SP), AX, BP - ADOXQ AX, R8 - - // (A,t[7]) := t[7] + x[7]*y[4] + A - ADCXQ BP, R9 - MULXQ s7-64(SP), AX, BP - ADOXQ AX, R9 - - // (A,t[8]) := t[8] + x[8]*y[4] + A - ADCXQ BP, R10 - MULXQ s8-72(SP), AX, BP - ADOXQ AX, R10 - - // (A,t[9]) := t[9] + x[9]*y[4] + A - ADCXQ BP, R11 - MULXQ s9-80(SP), AX, BP - ADOXQ AX, R11 - - // (A,t[10]) := t[10] + x[10]*y[4] + A - ADCXQ BP, R12 - MULXQ s10-88(SP), AX, BP - ADOXQ AX, R12 - - // (A,t[11]) := t[11] + x[11]*y[4] + A - ADCXQ BP, R13 - MULXQ s11-96(SP), AX, BP - ADOXQ AX, R13 - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - PUSHQ BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - POPQ BP - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - - // t[11] = C + A - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ BP, R13 - - // clear the flags - XORQ AX, AX - MOVQ y+16(FP), AX - MOVQ 40(AX), DX - - // (A,t[0]) := t[0] + x[0]*y[5] + A - MULXQ s0-8(SP), AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[5] + A - ADCXQ BP, R15 - MULXQ s1-16(SP), AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[5] + A - ADCXQ BP, CX - MULXQ s2-24(SP), AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[5] + A - ADCXQ BP, BX - MULXQ s3-32(SP), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[5] + A - ADCXQ BP, SI - MULXQ s4-40(SP), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[5] + A - ADCXQ BP, DI - MULXQ s5-48(SP), AX, BP - ADOXQ AX, DI - - // (A,t[6]) := t[6] + x[6]*y[5] + A - ADCXQ BP, R8 - MULXQ s6-56(SP), AX, BP - ADOXQ AX, R8 - - // (A,t[7]) := t[7] + x[7]*y[5] + A - ADCXQ BP, R9 - MULXQ s7-64(SP), AX, BP - ADOXQ AX, R9 - - // (A,t[8]) := t[8] + x[8]*y[5] + A - ADCXQ BP, R10 - MULXQ s8-72(SP), AX, BP - ADOXQ AX, R10 - - // (A,t[9]) := t[9] + x[9]*y[5] + A - ADCXQ BP, R11 - MULXQ s9-80(SP), AX, BP - ADOXQ AX, R11 - - // (A,t[10]) := t[10] + x[10]*y[5] + A - ADCXQ BP, R12 - MULXQ s10-88(SP), AX, BP - ADOXQ AX, R12 - - // (A,t[11]) := t[11] + x[11]*y[5] + A - ADCXQ BP, R13 - MULXQ s11-96(SP), AX, BP - ADOXQ AX, R13 - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - PUSHQ BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - POPQ BP - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - - // t[11] = C + A - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ BP, R13 - - // clear the flags - XORQ AX, AX - MOVQ y+16(FP), AX - MOVQ 48(AX), DX - - // (A,t[0]) := t[0] + x[0]*y[6] + A - MULXQ s0-8(SP), AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[6] + A - ADCXQ BP, R15 - MULXQ s1-16(SP), AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[6] + A - ADCXQ BP, CX - MULXQ s2-24(SP), AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[6] + A - ADCXQ BP, BX - MULXQ s3-32(SP), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[6] + A - ADCXQ BP, SI - MULXQ s4-40(SP), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[6] + A - ADCXQ BP, DI - MULXQ s5-48(SP), AX, BP - ADOXQ AX, DI - - // (A,t[6]) := t[6] + x[6]*y[6] + A - ADCXQ BP, R8 - MULXQ s6-56(SP), AX, BP - ADOXQ AX, R8 - - // (A,t[7]) := t[7] + x[7]*y[6] + A - ADCXQ BP, R9 - MULXQ s7-64(SP), AX, BP - ADOXQ AX, R9 - - // (A,t[8]) := t[8] + x[8]*y[6] + A - ADCXQ BP, R10 - MULXQ s8-72(SP), AX, BP - ADOXQ AX, R10 - - // (A,t[9]) := t[9] + x[9]*y[6] + A - ADCXQ BP, R11 - MULXQ s9-80(SP), AX, BP - ADOXQ AX, R11 - - // (A,t[10]) := t[10] + x[10]*y[6] + A - ADCXQ BP, R12 - MULXQ s10-88(SP), AX, BP - ADOXQ AX, R12 - - // (A,t[11]) := t[11] + x[11]*y[6] + A - ADCXQ BP, R13 - MULXQ s11-96(SP), AX, BP - ADOXQ AX, R13 - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - PUSHQ BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - POPQ BP - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - - // t[11] = C + A - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ BP, R13 - - // clear the flags - XORQ AX, AX - MOVQ y+16(FP), AX - MOVQ 56(AX), DX - - // (A,t[0]) := t[0] + x[0]*y[7] + A - MULXQ s0-8(SP), AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[7] + A - ADCXQ BP, R15 - MULXQ s1-16(SP), AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[7] + A - ADCXQ BP, CX - MULXQ s2-24(SP), AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[7] + A - ADCXQ BP, BX - MULXQ s3-32(SP), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[7] + A - ADCXQ BP, SI - MULXQ s4-40(SP), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[7] + A - ADCXQ BP, DI - MULXQ s5-48(SP), AX, BP - ADOXQ AX, DI - - // (A,t[6]) := t[6] + x[6]*y[7] + A - ADCXQ BP, R8 - MULXQ s6-56(SP), AX, BP - ADOXQ AX, R8 - - // (A,t[7]) := t[7] + x[7]*y[7] + A - ADCXQ BP, R9 - MULXQ s7-64(SP), AX, BP - ADOXQ AX, R9 - - // (A,t[8]) := t[8] + x[8]*y[7] + A - ADCXQ BP, R10 - MULXQ s8-72(SP), AX, BP - ADOXQ AX, R10 - - // (A,t[9]) := t[9] + x[9]*y[7] + A - ADCXQ BP, R11 - MULXQ s9-80(SP), AX, BP - ADOXQ AX, R11 - - // (A,t[10]) := t[10] + x[10]*y[7] + A - ADCXQ BP, R12 - MULXQ s10-88(SP), AX, BP - ADOXQ AX, R12 - - // (A,t[11]) := t[11] + x[11]*y[7] + A - ADCXQ BP, R13 - MULXQ s11-96(SP), AX, BP - ADOXQ AX, R13 - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - PUSHQ BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - POPQ BP - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - - // t[11] = C + A - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ BP, R13 - - // clear the flags - XORQ AX, AX - MOVQ y+16(FP), AX - MOVQ 64(AX), DX - - // (A,t[0]) := t[0] + x[0]*y[8] + A - MULXQ s0-8(SP), AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[8] + A - ADCXQ BP, R15 - MULXQ s1-16(SP), AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[8] + A - ADCXQ BP, CX - MULXQ s2-24(SP), AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[8] + A - ADCXQ BP, BX - MULXQ s3-32(SP), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[8] + A - ADCXQ BP, SI - MULXQ s4-40(SP), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[8] + A - ADCXQ BP, DI - MULXQ s5-48(SP), AX, BP - ADOXQ AX, DI - - // (A,t[6]) := t[6] + x[6]*y[8] + A - ADCXQ BP, R8 - MULXQ s6-56(SP), AX, BP - ADOXQ AX, R8 - - // (A,t[7]) := t[7] + x[7]*y[8] + A - ADCXQ BP, R9 - MULXQ s7-64(SP), AX, BP - ADOXQ AX, R9 - - // (A,t[8]) := t[8] + x[8]*y[8] + A - ADCXQ BP, R10 - MULXQ s8-72(SP), AX, BP - ADOXQ AX, R10 - - // (A,t[9]) := t[9] + x[9]*y[8] + A - ADCXQ BP, R11 - MULXQ s9-80(SP), AX, BP - ADOXQ AX, R11 - - // (A,t[10]) := t[10] + x[10]*y[8] + A - ADCXQ BP, R12 - MULXQ s10-88(SP), AX, BP - ADOXQ AX, R12 - - // (A,t[11]) := t[11] + x[11]*y[8] + A - ADCXQ BP, R13 - MULXQ s11-96(SP), AX, BP - ADOXQ AX, R13 - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - PUSHQ BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - POPQ BP - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - - // t[11] = C + A - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ BP, R13 - - // clear the flags - XORQ AX, AX - MOVQ y+16(FP), AX - MOVQ 72(AX), DX - - // (A,t[0]) := t[0] + x[0]*y[9] + A - MULXQ s0-8(SP), AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[9] + A - ADCXQ BP, R15 - MULXQ s1-16(SP), AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[9] + A - ADCXQ BP, CX - MULXQ s2-24(SP), AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[9] + A - ADCXQ BP, BX - MULXQ s3-32(SP), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[9] + A - ADCXQ BP, SI - MULXQ s4-40(SP), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[9] + A - ADCXQ BP, DI - MULXQ s5-48(SP), AX, BP - ADOXQ AX, DI - - // (A,t[6]) := t[6] + x[6]*y[9] + A - ADCXQ BP, R8 - MULXQ s6-56(SP), AX, BP - ADOXQ AX, R8 - - // (A,t[7]) := t[7] + x[7]*y[9] + A - ADCXQ BP, R9 - MULXQ s7-64(SP), AX, BP - ADOXQ AX, R9 - - // (A,t[8]) := t[8] + x[8]*y[9] + A - ADCXQ BP, R10 - MULXQ s8-72(SP), AX, BP - ADOXQ AX, R10 - - // (A,t[9]) := t[9] + x[9]*y[9] + A - ADCXQ BP, R11 - MULXQ s9-80(SP), AX, BP - ADOXQ AX, R11 - - // (A,t[10]) := t[10] + x[10]*y[9] + A - ADCXQ BP, R12 - MULXQ s10-88(SP), AX, BP - ADOXQ AX, R12 - - // (A,t[11]) := t[11] + x[11]*y[9] + A - ADCXQ BP, R13 - MULXQ s11-96(SP), AX, BP - ADOXQ AX, R13 - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - PUSHQ BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - POPQ BP - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - - // t[11] = C + A - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ BP, R13 - - // clear the flags - XORQ AX, AX - MOVQ y+16(FP), AX - MOVQ 80(AX), DX - - // (A,t[0]) := t[0] + x[0]*y[10] + A - MULXQ s0-8(SP), AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[10] + A - ADCXQ BP, R15 - MULXQ s1-16(SP), AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[10] + A - ADCXQ BP, CX - MULXQ s2-24(SP), AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[10] + A - ADCXQ BP, BX - MULXQ s3-32(SP), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[10] + A - ADCXQ BP, SI - MULXQ s4-40(SP), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[10] + A - ADCXQ BP, DI - MULXQ s5-48(SP), AX, BP - ADOXQ AX, DI - - // (A,t[6]) := t[6] + x[6]*y[10] + A - ADCXQ BP, R8 - MULXQ s6-56(SP), AX, BP - ADOXQ AX, R8 - - // (A,t[7]) := t[7] + x[7]*y[10] + A - ADCXQ BP, R9 - MULXQ s7-64(SP), AX, BP - ADOXQ AX, R9 - - // (A,t[8]) := t[8] + x[8]*y[10] + A - ADCXQ BP, R10 - MULXQ s8-72(SP), AX, BP - ADOXQ AX, R10 - - // (A,t[9]) := t[9] + x[9]*y[10] + A - ADCXQ BP, R11 - MULXQ s9-80(SP), AX, BP - ADOXQ AX, R11 - - // (A,t[10]) := t[10] + x[10]*y[10] + A - ADCXQ BP, R12 - MULXQ s10-88(SP), AX, BP - ADOXQ AX, R12 - - // (A,t[11]) := t[11] + x[11]*y[10] + A - ADCXQ BP, R13 - MULXQ s11-96(SP), AX, BP - ADOXQ AX, R13 - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - PUSHQ BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - POPQ BP - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - - // t[11] = C + A - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ BP, R13 - - // clear the flags - XORQ AX, AX - MOVQ y+16(FP), AX - MOVQ 88(AX), DX - - // (A,t[0]) := t[0] + x[0]*y[11] + A - MULXQ s0-8(SP), AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[11] + A - ADCXQ BP, R15 - MULXQ s1-16(SP), AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[11] + A - ADCXQ BP, CX - MULXQ s2-24(SP), AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[11] + A - ADCXQ BP, BX - MULXQ s3-32(SP), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[11] + A - ADCXQ BP, SI - MULXQ s4-40(SP), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[11] + A - ADCXQ BP, DI - MULXQ s5-48(SP), AX, BP - ADOXQ AX, DI - - // (A,t[6]) := t[6] + x[6]*y[11] + A - ADCXQ BP, R8 - MULXQ s6-56(SP), AX, BP - ADOXQ AX, R8 - - // (A,t[7]) := t[7] + x[7]*y[11] + A - ADCXQ BP, R9 - MULXQ s7-64(SP), AX, BP - ADOXQ AX, R9 - - // (A,t[8]) := t[8] + x[8]*y[11] + A - ADCXQ BP, R10 - MULXQ s8-72(SP), AX, BP - ADOXQ AX, R10 - - // (A,t[9]) := t[9] + x[9]*y[11] + A - ADCXQ BP, R11 - MULXQ s9-80(SP), AX, BP - ADOXQ AX, R11 - - // (A,t[10]) := t[10] + x[10]*y[11] + A - ADCXQ BP, R12 - MULXQ s10-88(SP), AX, BP - ADOXQ AX, R12 - - // (A,t[11]) := t[11] + x[11]*y[11] + A - ADCXQ BP, R13 - MULXQ s11-96(SP), AX, BP - ADOXQ AX, R13 - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - PUSHQ BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - POPQ BP - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - - // t[11] = C + A - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ BP, R13 - - // reduce element(R14,R15,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13) using temp registers (s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP),s11-96(SP)) - REDUCE(R14,R15,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP),s11-96(SP)) - - MOVQ res+0(FP), AX - MOVQ R14, 0(AX) - MOVQ R15, 8(AX) - MOVQ CX, 16(AX) - MOVQ BX, 24(AX) - MOVQ SI, 32(AX) - MOVQ DI, 40(AX) - MOVQ R8, 48(AX) - MOVQ R9, 56(AX) - MOVQ R10, 64(AX) - MOVQ R11, 72(AX) - MOVQ R12, 80(AX) - MOVQ R13, 88(AX) - RET - -l1: - MOVQ res+0(FP), AX - MOVQ AX, (SP) - MOVQ x+8(FP), AX - MOVQ AX, 8(SP) - MOVQ y+16(FP), AX - MOVQ AX, 16(SP) - CALL ·_mulGeneric(SB) - RET - -TEXT ·fromMont(SB), $96-8 - NO_LOCAL_POINTERS - - // the algorithm is described here - // https://hackmd.io/@gnark/modular_multiplication - // when y = 1 we have: - // for i=0 to N-1 - // t[i] = x[i] - // for i=0 to N-1 - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // t[N-1] = C - CMPB ·supportAdx(SB), $1 - JNE l2 - MOVQ res+0(FP), DX - MOVQ 0(DX), R14 - MOVQ 8(DX), R15 - MOVQ 16(DX), CX - MOVQ 24(DX), BX - MOVQ 32(DX), SI - MOVQ 40(DX), DI - MOVQ 48(DX), R8 - MOVQ 56(DX), R9 - MOVQ 64(DX), R10 - MOVQ 72(DX), R11 - MOVQ 80(DX), R12 - MOVQ 88(DX), R13 - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ AX, R13 - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ AX, R13 - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ AX, R13 - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ AX, R13 - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ AX, R13 - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ AX, R13 - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ AX, R13 - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ AX, R13 - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ AX, R13 - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ AX, R13 - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ AX, R13 - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // (C,t[5]) := t[6] + m*q[6] + C - ADCXQ R8, DI - MULXQ q<>+48(SB), AX, R8 - ADOXQ AX, DI - - // (C,t[6]) := t[7] + m*q[7] + C - ADCXQ R9, R8 - MULXQ q<>+56(SB), AX, R9 - ADOXQ AX, R8 - - // (C,t[7]) := t[8] + m*q[8] + C - ADCXQ R10, R9 - MULXQ q<>+64(SB), AX, R10 - ADOXQ AX, R9 - - // (C,t[8]) := t[9] + m*q[9] + C - ADCXQ R11, R10 - MULXQ q<>+72(SB), AX, R11 - ADOXQ AX, R10 - - // (C,t[9]) := t[10] + m*q[10] + C - ADCXQ R12, R11 - MULXQ q<>+80(SB), AX, R12 - ADOXQ AX, R11 - - // (C,t[10]) := t[11] + m*q[11] + C - ADCXQ R13, R12 - MULXQ q<>+88(SB), AX, R13 - ADOXQ AX, R12 - MOVQ $0, AX - ADCXQ AX, R13 - ADOXQ AX, R13 - - // reduce element(R14,R15,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13) using temp registers (s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP),s11-96(SP)) - REDUCE(R14,R15,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP),s11-96(SP)) - - MOVQ res+0(FP), AX - MOVQ R14, 0(AX) - MOVQ R15, 8(AX) - MOVQ CX, 16(AX) - MOVQ BX, 24(AX) - MOVQ SI, 32(AX) - MOVQ DI, 40(AX) - MOVQ R8, 48(AX) - MOVQ R9, 56(AX) - MOVQ R10, 64(AX) - MOVQ R11, 72(AX) - MOVQ R12, 80(AX) - MOVQ R13, 88(AX) - RET - -l2: - MOVQ res+0(FP), AX - MOVQ AX, (SP) - CALL ·_fromMontGeneric(SB) - RET diff --git a/ecc/bw6-756/fp/element_ops_amd64.go b/ecc/bw6-756/fp/element_ops_amd64.go deleted file mode 100644 index 83bba45aed..0000000000 --- a/ecc/bw6-756/fp/element_ops_amd64.go +++ /dev/null @@ -1,107 +0,0 @@ -//go:build !purego -// +build !purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -//go:noescape -func MulBy3(x *Element) - -//go:noescape -func MulBy5(x *Element) - -//go:noescape -func MulBy13(x *Element) - -//go:noescape -func mul(res, x, y *Element) - -//go:noescape -func fromMont(res *Element) - -//go:noescape -func reduce(res *Element) - -// Butterfly sets -// -// a = a + b (mod q) -// b = a - b (mod q) -// -//go:noescape -func Butterfly(a, b *Element) - -// Mul z = x * y (mod q) -// -// x and y must be less than q -func (z *Element) Mul(x, y *Element) *Element { - - // Implements CIOS multiplication -- section 2.3.2 of Tolga Acar's thesis - // https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf - // - // The algorithm: - // - // for i=0 to N-1 - // C := 0 - // for j=0 to N-1 - // (C,t[j]) := t[j] + x[j]*y[i] + C - // (t[N+1],t[N]) := t[N] + C - // - // C := 0 - // m := t[0]*q'[0] mod D - // (C,_) := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // (C,t[N-1]) := t[N] + C - // t[N] := t[N+1] + C - // - // → N is the number of machine words needed to store the modulus q - // → D is the word size. For example, on a 64-bit architecture D is 2 64 - // → x[i], y[i], q[i] is the ith word of the numbers x,y,q - // → q'[0] is the lowest word of the number -q⁻¹ mod r. This quantity is pre-computed, as it does not depend on the inputs. - // → t is a temporary array of size N+2 - // → C, S are machine words. A pair (C,S) refers to (hi-bits, lo-bits) of a two-word number - // - // As described here https://hackmd.io/@gnark/modular_multiplication we can get rid of one carry chain and simplify: - // (also described in https://eprint.iacr.org/2022/1400.pdf annex) - // - // for i=0 to N-1 - // (A,t[0]) := t[0] + x[0]*y[i] - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (A,t[j]) := t[j] + x[j]*y[i] + A - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // t[N-1] = C + A - // - // This optimization saves 5N + 2 additions in the algorithm, and can be used whenever the highest bit - // of the modulus is zero (and not all of the remaining bits are set). - - mul(z, x, y) - return z -} - -// Square z = x * x (mod q) -// -// x must be less than q -func (z *Element) Square(x *Element) *Element { - // see Mul for doc. - mul(z, x, x) - return z -} diff --git a/ecc/bw6-756/fp/element_ops_amd64.s b/ecc/bw6-756/fp/element_ops_amd64.s deleted file mode 100644 index 57ceebbe4f..0000000000 --- a/ecc/bw6-756/fp/element_ops_amd64.s +++ /dev/null @@ -1,502 +0,0 @@ -// +build !purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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. - -#include "textflag.h" -#include "funcdata.h" - -// modulus q -DATA q<>+0(SB)/8, $1 -DATA q<>+8(SB)/8, $0x33c7e63f86840000 -DATA q<>+16(SB)/8, $0xd0b685e868524ec0 -DATA q<>+24(SB)/8, $0x4302aa3c258de7de -DATA q<>+32(SB)/8, $0xe292cd15edb646a5 -DATA q<>+40(SB)/8, $0x0a7eb1cb3d06e646 -DATA q<>+48(SB)/8, $0xeb02c812ea04faaa -DATA q<>+56(SB)/8, $0xccc6ae73c42a46d9 -DATA q<>+64(SB)/8, $0xfbf23221455163a6 -DATA q<>+72(SB)/8, $0x5c978cd2fac2ce89 -DATA q<>+80(SB)/8, $0xe2ac127e1e3568cf -DATA q<>+88(SB)/8, $0x000f76adbb5bb98a -GLOBL q<>(SB), (RODATA+NOPTR), $96 - -// qInv0 q'[0] -DATA qInv0<>(SB)/8, $0xffffffffffffffff -GLOBL qInv0<>(SB), (RODATA+NOPTR), $8 - -#define REDUCE(ra0, ra1, ra2, ra3, ra4, ra5, ra6, ra7, ra8, ra9, ra10, ra11, rb0, rb1, rb2, rb3, rb4, rb5, rb6, rb7, rb8, rb9, rb10, rb11) \ - MOVQ ra0, rb0; \ - SUBQ q<>(SB), ra0; \ - MOVQ ra1, rb1; \ - SBBQ q<>+8(SB), ra1; \ - MOVQ ra2, rb2; \ - SBBQ q<>+16(SB), ra2; \ - MOVQ ra3, rb3; \ - SBBQ q<>+24(SB), ra3; \ - MOVQ ra4, rb4; \ - SBBQ q<>+32(SB), ra4; \ - MOVQ ra5, rb5; \ - SBBQ q<>+40(SB), ra5; \ - MOVQ ra6, rb6; \ - SBBQ q<>+48(SB), ra6; \ - MOVQ ra7, rb7; \ - SBBQ q<>+56(SB), ra7; \ - MOVQ ra8, rb8; \ - SBBQ q<>+64(SB), ra8; \ - MOVQ ra9, rb9; \ - SBBQ q<>+72(SB), ra9; \ - MOVQ ra10, rb10; \ - SBBQ q<>+80(SB), ra10; \ - MOVQ ra11, rb11; \ - SBBQ q<>+88(SB), ra11; \ - CMOVQCS rb0, ra0; \ - CMOVQCS rb1, ra1; \ - CMOVQCS rb2, ra2; \ - CMOVQCS rb3, ra3; \ - CMOVQCS rb4, ra4; \ - CMOVQCS rb5, ra5; \ - CMOVQCS rb6, ra6; \ - CMOVQCS rb7, ra7; \ - CMOVQCS rb8, ra8; \ - CMOVQCS rb9, ra9; \ - CMOVQCS rb10, ra10; \ - CMOVQCS rb11, ra11; \ - -TEXT ·reduce(SB), $88-8 - MOVQ res+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - MOVQ 32(AX), DI - MOVQ 40(AX), R8 - MOVQ 48(AX), R9 - MOVQ 56(AX), R10 - MOVQ 64(AX), R11 - MOVQ 72(AX), R12 - MOVQ 80(AX), R13 - MOVQ 88(AX), R14 - - // reduce element(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) using temp registers (R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - MOVQ DI, 32(AX) - MOVQ R8, 40(AX) - MOVQ R9, 48(AX) - MOVQ R10, 56(AX) - MOVQ R11, 64(AX) - MOVQ R12, 72(AX) - MOVQ R13, 80(AX) - MOVQ R14, 88(AX) - RET - -// MulBy3(x *Element) -TEXT ·MulBy3(SB), $88-8 - MOVQ x+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - MOVQ 32(AX), DI - MOVQ 40(AX), R8 - MOVQ 48(AX), R9 - MOVQ 56(AX), R10 - MOVQ 64(AX), R11 - MOVQ 72(AX), R12 - MOVQ 80(AX), R13 - MOVQ 88(AX), R14 - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - ADCQ R9, R9 - ADCQ R10, R10 - ADCQ R11, R11 - ADCQ R12, R12 - ADCQ R13, R13 - ADCQ R14, R14 - - // reduce element(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) using temp registers (R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - - ADDQ 0(AX), DX - ADCQ 8(AX), CX - ADCQ 16(AX), BX - ADCQ 24(AX), SI - ADCQ 32(AX), DI - ADCQ 40(AX), R8 - ADCQ 48(AX), R9 - ADCQ 56(AX), R10 - ADCQ 64(AX), R11 - ADCQ 72(AX), R12 - ADCQ 80(AX), R13 - ADCQ 88(AX), R14 - - // reduce element(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) using temp registers (R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - MOVQ DI, 32(AX) - MOVQ R8, 40(AX) - MOVQ R9, 48(AX) - MOVQ R10, 56(AX) - MOVQ R11, 64(AX) - MOVQ R12, 72(AX) - MOVQ R13, 80(AX) - MOVQ R14, 88(AX) - RET - -// MulBy5(x *Element) -TEXT ·MulBy5(SB), $88-8 - MOVQ x+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - MOVQ 32(AX), DI - MOVQ 40(AX), R8 - MOVQ 48(AX), R9 - MOVQ 56(AX), R10 - MOVQ 64(AX), R11 - MOVQ 72(AX), R12 - MOVQ 80(AX), R13 - MOVQ 88(AX), R14 - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - ADCQ R9, R9 - ADCQ R10, R10 - ADCQ R11, R11 - ADCQ R12, R12 - ADCQ R13, R13 - ADCQ R14, R14 - - // reduce element(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) using temp registers (R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - ADCQ R9, R9 - ADCQ R10, R10 - ADCQ R11, R11 - ADCQ R12, R12 - ADCQ R13, R13 - ADCQ R14, R14 - - // reduce element(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) using temp registers (R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - - ADDQ 0(AX), DX - ADCQ 8(AX), CX - ADCQ 16(AX), BX - ADCQ 24(AX), SI - ADCQ 32(AX), DI - ADCQ 40(AX), R8 - ADCQ 48(AX), R9 - ADCQ 56(AX), R10 - ADCQ 64(AX), R11 - ADCQ 72(AX), R12 - ADCQ 80(AX), R13 - ADCQ 88(AX), R14 - - // reduce element(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) using temp registers (R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - MOVQ DI, 32(AX) - MOVQ R8, 40(AX) - MOVQ R9, 48(AX) - MOVQ R10, 56(AX) - MOVQ R11, 64(AX) - MOVQ R12, 72(AX) - MOVQ R13, 80(AX) - MOVQ R14, 88(AX) - RET - -// MulBy13(x *Element) -TEXT ·MulBy13(SB), $184-8 - MOVQ x+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - MOVQ 32(AX), DI - MOVQ 40(AX), R8 - MOVQ 48(AX), R9 - MOVQ 56(AX), R10 - MOVQ 64(AX), R11 - MOVQ 72(AX), R12 - MOVQ 80(AX), R13 - MOVQ 88(AX), R14 - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - ADCQ R9, R9 - ADCQ R10, R10 - ADCQ R11, R11 - ADCQ R12, R12 - ADCQ R13, R13 - ADCQ R14, R14 - - // reduce element(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) using temp registers (R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - ADCQ R9, R9 - ADCQ R10, R10 - ADCQ R11, R11 - ADCQ R12, R12 - ADCQ R13, R13 - ADCQ R14, R14 - - // reduce element(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) using temp registers (s11-96(SP),s12-104(SP),s13-112(SP),s14-120(SP),s15-128(SP),s16-136(SP),s17-144(SP),s18-152(SP),s19-160(SP),s20-168(SP),s21-176(SP),s22-184(SP)) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,s11-96(SP),s12-104(SP),s13-112(SP),s14-120(SP),s15-128(SP),s16-136(SP),s17-144(SP),s18-152(SP),s19-160(SP),s20-168(SP),s21-176(SP),s22-184(SP)) - - MOVQ DX, s11-96(SP) - MOVQ CX, s12-104(SP) - MOVQ BX, s13-112(SP) - MOVQ SI, s14-120(SP) - MOVQ DI, s15-128(SP) - MOVQ R8, s16-136(SP) - MOVQ R9, s17-144(SP) - MOVQ R10, s18-152(SP) - MOVQ R11, s19-160(SP) - MOVQ R12, s20-168(SP) - MOVQ R13, s21-176(SP) - MOVQ R14, s22-184(SP) - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - ADCQ R9, R9 - ADCQ R10, R10 - ADCQ R11, R11 - ADCQ R12, R12 - ADCQ R13, R13 - ADCQ R14, R14 - - // reduce element(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) using temp registers (R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - - ADDQ s11-96(SP), DX - ADCQ s12-104(SP), CX - ADCQ s13-112(SP), BX - ADCQ s14-120(SP), SI - ADCQ s15-128(SP), DI - ADCQ s16-136(SP), R8 - ADCQ s17-144(SP), R9 - ADCQ s18-152(SP), R10 - ADCQ s19-160(SP), R11 - ADCQ s20-168(SP), R12 - ADCQ s21-176(SP), R13 - ADCQ s22-184(SP), R14 - - // reduce element(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) using temp registers (R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - - ADDQ 0(AX), DX - ADCQ 8(AX), CX - ADCQ 16(AX), BX - ADCQ 24(AX), SI - ADCQ 32(AX), DI - ADCQ 40(AX), R8 - ADCQ 48(AX), R9 - ADCQ 56(AX), R10 - ADCQ 64(AX), R11 - ADCQ 72(AX), R12 - ADCQ 80(AX), R13 - ADCQ 88(AX), R14 - - // reduce element(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) using temp registers (R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - MOVQ DI, 32(AX) - MOVQ R8, 40(AX) - MOVQ R9, 48(AX) - MOVQ R10, 56(AX) - MOVQ R11, 64(AX) - MOVQ R12, 72(AX) - MOVQ R13, 80(AX) - MOVQ R14, 88(AX) - RET - -// Butterfly(a, b *Element) sets a = a + b; b = a - b -TEXT ·Butterfly(SB), $88-16 - MOVQ b+8(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - MOVQ 32(AX), DI - MOVQ 40(AX), R8 - MOVQ 48(AX), R9 - MOVQ 56(AX), R10 - MOVQ 64(AX), R11 - MOVQ 72(AX), R12 - MOVQ 80(AX), R13 - MOVQ 88(AX), R14 - MOVQ a+0(FP), AX - ADDQ 0(AX), DX - ADCQ 8(AX), CX - ADCQ 16(AX), BX - ADCQ 24(AX), SI - ADCQ 32(AX), DI - ADCQ 40(AX), R8 - ADCQ 48(AX), R9 - ADCQ 56(AX), R10 - ADCQ 64(AX), R11 - ADCQ 72(AX), R12 - ADCQ 80(AX), R13 - ADCQ 88(AX), R14 - MOVQ DX, R15 - MOVQ CX, s0-8(SP) - MOVQ BX, s1-16(SP) - MOVQ SI, s2-24(SP) - MOVQ DI, s3-32(SP) - MOVQ R8, s4-40(SP) - MOVQ R9, s5-48(SP) - MOVQ R10, s6-56(SP) - MOVQ R11, s7-64(SP) - MOVQ R12, s8-72(SP) - MOVQ R13, s9-80(SP) - MOVQ R14, s10-88(SP) - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - MOVQ 32(AX), DI - MOVQ 40(AX), R8 - MOVQ 48(AX), R9 - MOVQ 56(AX), R10 - MOVQ 64(AX), R11 - MOVQ 72(AX), R12 - MOVQ 80(AX), R13 - MOVQ 88(AX), R14 - MOVQ b+8(FP), AX - SUBQ 0(AX), DX - SBBQ 8(AX), CX - SBBQ 16(AX), BX - SBBQ 24(AX), SI - SBBQ 32(AX), DI - SBBQ 40(AX), R8 - SBBQ 48(AX), R9 - SBBQ 56(AX), R10 - SBBQ 64(AX), R11 - SBBQ 72(AX), R12 - SBBQ 80(AX), R13 - SBBQ 88(AX), R14 - JCC l1 - MOVQ $1, AX - ADDQ AX, DX - MOVQ $0x33c7e63f86840000, AX - ADCQ AX, CX - MOVQ $0xd0b685e868524ec0, AX - ADCQ AX, BX - MOVQ $0x4302aa3c258de7de, AX - ADCQ AX, SI - MOVQ $0xe292cd15edb646a5, AX - ADCQ AX, DI - MOVQ $0x0a7eb1cb3d06e646, AX - ADCQ AX, R8 - MOVQ $0xeb02c812ea04faaa, AX - ADCQ AX, R9 - MOVQ $0xccc6ae73c42a46d9, AX - ADCQ AX, R10 - MOVQ $0xfbf23221455163a6, AX - ADCQ AX, R11 - MOVQ $0x5c978cd2fac2ce89, AX - ADCQ AX, R12 - MOVQ $0xe2ac127e1e3568cf, AX - ADCQ AX, R13 - MOVQ $0x000f76adbb5bb98a, AX - ADCQ AX, R14 - -l1: - MOVQ b+8(FP), AX - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - MOVQ DI, 32(AX) - MOVQ R8, 40(AX) - MOVQ R9, 48(AX) - MOVQ R10, 56(AX) - MOVQ R11, 64(AX) - MOVQ R12, 72(AX) - MOVQ R13, 80(AX) - MOVQ R14, 88(AX) - MOVQ R15, DX - MOVQ s0-8(SP), CX - MOVQ s1-16(SP), BX - MOVQ s2-24(SP), SI - MOVQ s3-32(SP), DI - MOVQ s4-40(SP), R8 - MOVQ s5-48(SP), R9 - MOVQ s6-56(SP), R10 - MOVQ s7-64(SP), R11 - MOVQ s8-72(SP), R12 - MOVQ s9-80(SP), R13 - MOVQ s10-88(SP), R14 - - // reduce element(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) using temp registers (R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP),s5-48(SP),s6-56(SP),s7-64(SP),s8-72(SP),s9-80(SP),s10-88(SP)) - - MOVQ a+0(FP), AX - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - MOVQ DI, 32(AX) - MOVQ R8, 40(AX) - MOVQ R9, 48(AX) - MOVQ R10, 56(AX) - MOVQ R11, 64(AX) - MOVQ R12, 72(AX) - MOVQ R13, 80(AX) - MOVQ R14, 88(AX) - RET diff --git a/ecc/bw6-756/fp/element_ops_purego.go b/ecc/bw6-756/fp/element_ops_purego.go deleted file mode 100644 index 2b12c05a0d..0000000000 --- a/ecc/bw6-756/fp/element_ops_purego.go +++ /dev/null @@ -1,2227 +0,0 @@ -//go:build !amd64 || purego -// +build !amd64 purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -import "math/bits" - -// MulBy3 x *= 3 (mod q) -func MulBy3(x *Element) { - _x := *x - x.Double(x).Add(x, &_x) -} - -// MulBy5 x *= 5 (mod q) -func MulBy5(x *Element) { - _x := *x - x.Double(x).Double(x).Add(x, &_x) -} - -// MulBy13 x *= 13 (mod q) -func MulBy13(x *Element) { - var y = Element{ - 18446744073709496521, - 18279598932724285439, - 16020160894313802039, - 6734264120679796183, - 592370298347718455, - 6302256704987790972, - 3197310980453914279, - 2651858637075104463, - 8565083029697102127, - 15288469570225946050, - 14519635472382186671, - 448955992735434, - } - x.Mul(x, &y) -} - -// Butterfly sets -// -// a = a + b (mod q) -// b = a - b (mod q) -func Butterfly(a, b *Element) { - _butterflyGeneric(a, b) -} - -func fromMont(z *Element) { - _fromMontGeneric(z) -} - -func reduce(z *Element) { - _reduceGeneric(z) -} - -// Mul z = x * y (mod q) -// -// x and y must be less than q -func (z *Element) Mul(x, y *Element) *Element { - - // Implements CIOS multiplication -- section 2.3.2 of Tolga Acar's thesis - // https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf - // - // The algorithm: - // - // for i=0 to N-1 - // C := 0 - // for j=0 to N-1 - // (C,t[j]) := t[j] + x[j]*y[i] + C - // (t[N+1],t[N]) := t[N] + C - // - // C := 0 - // m := t[0]*q'[0] mod D - // (C,_) := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // (C,t[N-1]) := t[N] + C - // t[N] := t[N+1] + C - // - // → N is the number of machine words needed to store the modulus q - // → D is the word size. For example, on a 64-bit architecture D is 2 64 - // → x[i], y[i], q[i] is the ith word of the numbers x,y,q - // → q'[0] is the lowest word of the number -q⁻¹ mod r. This quantity is pre-computed, as it does not depend on the inputs. - // → t is a temporary array of size N+2 - // → C, S are machine words. A pair (C,S) refers to (hi-bits, lo-bits) of a two-word number - // - // As described here https://hackmd.io/@gnark/modular_multiplication we can get rid of one carry chain and simplify: - // (also described in https://eprint.iacr.org/2022/1400.pdf annex) - // - // for i=0 to N-1 - // (A,t[0]) := t[0] + x[0]*y[i] - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (A,t[j]) := t[j] + x[j]*y[i] + A - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // t[N-1] = C + A - // - // This optimization saves 5N + 2 additions in the algorithm, and can be used whenever the highest bit - // of the modulus is zero (and not all of the remaining bits are set). - - var t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11 uint64 - var u0, u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11 uint64 - { - var c0, c1, c2 uint64 - v := x[0] - u0, t0 = bits.Mul64(v, y[0]) - u1, t1 = bits.Mul64(v, y[1]) - u2, t2 = bits.Mul64(v, y[2]) - u3, t3 = bits.Mul64(v, y[3]) - u4, t4 = bits.Mul64(v, y[4]) - u5, t5 = bits.Mul64(v, y[5]) - u6, t6 = bits.Mul64(v, y[6]) - u7, t7 = bits.Mul64(v, y[7]) - u8, t8 = bits.Mul64(v, y[8]) - u9, t9 = bits.Mul64(v, y[9]) - u10, t10 = bits.Mul64(v, y[10]) - u11, t11 = bits.Mul64(v, y[11]) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, 0, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[1] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, y[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, y[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, y[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, y[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, y[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, y[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[2] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, y[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, y[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, y[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, y[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, y[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, y[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[3] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, y[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, y[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, y[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, y[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, y[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, y[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[4] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, y[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, y[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, y[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, y[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, y[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, y[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[5] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, y[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, y[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, y[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, y[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, y[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, y[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[6] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, y[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, y[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, y[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, y[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, y[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, y[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[7] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, y[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, y[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, y[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, y[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, y[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, y[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[8] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, y[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, y[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, y[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, y[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, y[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, y[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[9] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, y[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, y[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, y[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, y[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, y[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, y[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[10] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, y[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, y[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, y[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, y[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, y[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, y[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[11] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, y[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, y[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, y[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, y[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, y[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, y[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - z[0] = t0 - z[1] = t1 - z[2] = t2 - z[3] = t3 - z[4] = t4 - z[5] = t5 - z[6] = t6 - z[7] = t7 - z[8] = t8 - z[9] = t9 - z[10] = t10 - z[11] = t11 - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], b = bits.Sub64(z[5], q5, b) - z[6], b = bits.Sub64(z[6], q6, b) - z[7], b = bits.Sub64(z[7], q7, b) - z[8], b = bits.Sub64(z[8], q8, b) - z[9], b = bits.Sub64(z[9], q9, b) - z[10], b = bits.Sub64(z[10], q10, b) - z[11], _ = bits.Sub64(z[11], q11, b) - } - return z -} - -// Square z = x * x (mod q) -// -// x must be less than q -func (z *Element) Square(x *Element) *Element { - // see Mul for algorithm documentation - - var t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11 uint64 - var u0, u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11 uint64 - { - var c0, c1, c2 uint64 - v := x[0] - u0, t0 = bits.Mul64(v, x[0]) - u1, t1 = bits.Mul64(v, x[1]) - u2, t2 = bits.Mul64(v, x[2]) - u3, t3 = bits.Mul64(v, x[3]) - u4, t4 = bits.Mul64(v, x[4]) - u5, t5 = bits.Mul64(v, x[5]) - u6, t6 = bits.Mul64(v, x[6]) - u7, t7 = bits.Mul64(v, x[7]) - u8, t8 = bits.Mul64(v, x[8]) - u9, t9 = bits.Mul64(v, x[9]) - u10, t10 = bits.Mul64(v, x[10]) - u11, t11 = bits.Mul64(v, x[11]) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, 0, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[1] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, x[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, x[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, x[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, x[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, x[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, x[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[2] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, x[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, x[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, x[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, x[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, x[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, x[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[3] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, x[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, x[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, x[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, x[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, x[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, x[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[4] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, x[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, x[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, x[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, x[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, x[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, x[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[5] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, x[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, x[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, x[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, x[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, x[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, x[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[6] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, x[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, x[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, x[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, x[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, x[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, x[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[7] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, x[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, x[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, x[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, x[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, x[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, x[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[8] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, x[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, x[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, x[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, x[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, x[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, x[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[9] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, x[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, x[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, x[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, x[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, x[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, x[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[10] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, x[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, x[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, x[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, x[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, x[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, x[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[11] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - u6, c1 = bits.Mul64(v, x[6]) - t6, c0 = bits.Add64(c1, t6, c0) - u7, c1 = bits.Mul64(v, x[7]) - t7, c0 = bits.Add64(c1, t7, c0) - u8, c1 = bits.Mul64(v, x[8]) - t8, c0 = bits.Add64(c1, t8, c0) - u9, c1 = bits.Mul64(v, x[9]) - t9, c0 = bits.Add64(c1, t9, c0) - u10, c1 = bits.Mul64(v, x[10]) - t10, c0 = bits.Add64(c1, t10, c0) - u11, c1 = bits.Mul64(v, x[11]) - t11, c0 = bits.Add64(c1, t11, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - t6, c0 = bits.Add64(u5, t6, c0) - t7, c0 = bits.Add64(u6, t7, c0) - t8, c0 = bits.Add64(u7, t8, c0) - t9, c0 = bits.Add64(u8, t9, c0) - t10, c0 = bits.Add64(u9, t10, c0) - t11, c0 = bits.Add64(u10, t11, c0) - c2, _ = bits.Add64(u11, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - t4, c0 = bits.Add64(t5, c1, c0) - u6, c1 = bits.Mul64(m, q6) - t5, c0 = bits.Add64(t6, c1, c0) - u7, c1 = bits.Mul64(m, q7) - t6, c0 = bits.Add64(t7, c1, c0) - u8, c1 = bits.Mul64(m, q8) - t7, c0 = bits.Add64(t8, c1, c0) - u9, c1 = bits.Mul64(m, q9) - t8, c0 = bits.Add64(t9, c1, c0) - u10, c1 = bits.Mul64(m, q10) - t9, c0 = bits.Add64(t10, c1, c0) - u11, c1 = bits.Mul64(m, q11) - - t10, c0 = bits.Add64(0, c1, c0) - u11, _ = bits.Add64(u11, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - t5, c0 = bits.Add64(u5, t5, c0) - t6, c0 = bits.Add64(u6, t6, c0) - t7, c0 = bits.Add64(u7, t7, c0) - t8, c0 = bits.Add64(u8, t8, c0) - t9, c0 = bits.Add64(u9, t9, c0) - t10, c0 = bits.Add64(u10, t10, c0) - c2, _ = bits.Add64(c2, 0, c0) - t10, c0 = bits.Add64(t11, t10, 0) - t11, _ = bits.Add64(u11, c2, c0) - - } - z[0] = t0 - z[1] = t1 - z[2] = t2 - z[3] = t3 - z[4] = t4 - z[5] = t5 - z[6] = t6 - z[7] = t7 - z[8] = t8 - z[9] = t9 - z[10] = t10 - z[11] = t11 - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], b = bits.Sub64(z[5], q5, b) - z[6], b = bits.Sub64(z[6], q6, b) - z[7], b = bits.Sub64(z[7], q7, b) - z[8], b = bits.Sub64(z[8], q8, b) - z[9], b = bits.Sub64(z[9], q9, b) - z[10], b = bits.Sub64(z[10], q10, b) - z[11], _ = bits.Sub64(z[11], q11, b) - } - return z -} diff --git a/ecc/bw6-756/fp/element_test.go b/ecc/bw6-756/fp/element_test.go deleted file mode 100644 index e500871a62..0000000000 --- a/ecc/bw6-756/fp/element_test.go +++ /dev/null @@ -1,2969 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -import ( - "crypto/rand" - "encoding/json" - "fmt" - "math/big" - "math/bits" - - mrand "math/rand" - - "testing" - - "github.com/leanovate/gopter" - ggen "github.com/leanovate/gopter/gen" - "github.com/leanovate/gopter/prop" - - "github.com/stretchr/testify/require" -) - -// ------------------------------------------------------------------------------------------------- -// benchmarks -// most benchmarks are rudimentary and should sample a large number of random inputs -// or be run multiple times to ensure it didn't measure the fastest path of the function - -var benchResElement Element - -func BenchmarkElementSelect(b *testing.B) { - var x, y Element - x.SetRandom() - y.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Select(i%3, &x, &y) - } -} - -func BenchmarkElementSetRandom(b *testing.B) { - var x Element - x.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = x.SetRandom() - } -} - -func BenchmarkElementSetBytes(b *testing.B) { - var x Element - x.SetRandom() - bb := x.Bytes() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - benchResElement.SetBytes(bb[:]) - } - -} - -func BenchmarkElementMulByConstants(b *testing.B) { - b.Run("mulBy3", func(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - MulBy3(&benchResElement) - } - }) - b.Run("mulBy5", func(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - MulBy5(&benchResElement) - } - }) - b.Run("mulBy13", func(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - MulBy13(&benchResElement) - } - }) -} - -func BenchmarkElementInverse(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - benchResElement.Inverse(&x) - } - -} - -func BenchmarkElementButterfly(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - Butterfly(&x, &benchResElement) - } -} - -func BenchmarkElementExp(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b1, _ := rand.Int(rand.Reader, Modulus()) - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Exp(x, b1) - } -} - -func BenchmarkElementDouble(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Double(&benchResElement) - } -} - -func BenchmarkElementAdd(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Add(&x, &benchResElement) - } -} - -func BenchmarkElementSub(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Sub(&x, &benchResElement) - } -} - -func BenchmarkElementNeg(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Neg(&benchResElement) - } -} - -func BenchmarkElementDiv(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Div(&x, &benchResElement) - } -} - -func BenchmarkElementFromMont(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.fromMont() - } -} - -func BenchmarkElementSquare(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Square(&benchResElement) - } -} - -func BenchmarkElementSqrt(b *testing.B) { - var a Element - a.SetUint64(4) - a.Neg(&a) - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Sqrt(&a) - } -} - -func BenchmarkElementMul(b *testing.B) { - x := Element{ - 11214533042317621956, - 4418601975293183768, - 2233550636059863627, - 13772400071271951950, - 13010224617750716256, - 15582310590478290871, - 6301429202206019695, - 15624904615961126890, - 14411832617204527559, - 10495912060283172777, - 8432856701560321958, - 4166778949326216, - } - benchResElement.SetOne() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Mul(&benchResElement, &x) - } -} - -func BenchmarkElementCmp(b *testing.B) { - x := Element{ - 11214533042317621956, - 4418601975293183768, - 2233550636059863627, - 13772400071271951950, - 13010224617750716256, - 15582310590478290871, - 6301429202206019695, - 15624904615961126890, - 14411832617204527559, - 10495912060283172777, - 8432856701560321958, - 4166778949326216, - } - benchResElement = x - benchResElement[0] = 0 - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Cmp(&x) - } -} - -func TestElementCmp(t *testing.T) { - var x, y Element - - if x.Cmp(&y) != 0 { - t.Fatal("x == y") - } - - one := One() - y.Sub(&y, &one) - - if x.Cmp(&y) != -1 { - t.Fatal("x < y") - } - if y.Cmp(&x) != 1 { - t.Fatal("x < y") - } - - x = y - if x.Cmp(&y) != 0 { - t.Fatal("x == y") - } - - x.Sub(&x, &one) - if x.Cmp(&y) != -1 { - t.Fatal("x < y") - } - if y.Cmp(&x) != 1 { - t.Fatal("x < y") - } -} -func TestElementIsRandom(t *testing.T) { - for i := 0; i < 50; i++ { - var x, y Element - x.SetRandom() - y.SetRandom() - if x.Equal(&y) { - t.Fatal("2 random numbers are unlikely to be equal") - } - } -} - -func TestElementIsUint64(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("reduce should output a result smaller than modulus", prop.ForAll( - func(v uint64) bool { - var e Element - e.SetUint64(v) - - if !e.IsUint64() { - return false - } - - return e.Uint64() == v - }, - ggen.UInt64(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementNegZero(t *testing.T) { - var a, b Element - b.SetZero() - for a.IsZero() { - a.SetRandom() - } - a.Neg(&b) - if !a.IsZero() { - t.Fatal("neg(0) != 0") - } -} - -// ------------------------------------------------------------------------------------------------- -// Gopter tests -// most of them are generated with a template - -const ( - nbFuzzShort = 20 - nbFuzz = 100 -) - -// special values to be used in tests -var staticTestValues []Element - -func init() { - staticTestValues = append(staticTestValues, Element{}) // zero - staticTestValues = append(staticTestValues, One()) // one - staticTestValues = append(staticTestValues, rSquare) // r² - var e, one Element - one.SetOne() - e.Sub(&qElement, &one) - staticTestValues = append(staticTestValues, e) // q - 1 - e.Double(&one) - staticTestValues = append(staticTestValues, e) // 2 - - { - a := qElement - a[0]-- - staticTestValues = append(staticTestValues, a) - } - staticTestValues = append(staticTestValues, Element{0}) - staticTestValues = append(staticTestValues, Element{0, 0}) - staticTestValues = append(staticTestValues, Element{1}) - staticTestValues = append(staticTestValues, Element{0, 1}) - staticTestValues = append(staticTestValues, Element{2}) - staticTestValues = append(staticTestValues, Element{0, 2}) - - { - a := qElement - a[11]-- - staticTestValues = append(staticTestValues, a) - } - { - a := qElement - a[11]-- - a[0]++ - staticTestValues = append(staticTestValues, a) - } - - { - a := qElement - a[11] = 0 - staticTestValues = append(staticTestValues, a) - } - -} - -func TestElementReduce(t *testing.T) { - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - s := testValues[i] - expected := s - reduce(&s) - _reduceGeneric(&expected) - if !s.Equal(&expected) { - t.Fatal("reduce failed: asm and generic impl don't match") - } - } - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := genFull() - - properties.Property("reduce should output a result smaller than modulus", prop.ForAll( - func(a Element) bool { - b := a - reduce(&a) - _reduceGeneric(&b) - return a.smallerThanModulus() && a.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementEqual(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("x.Equal(&y) iff x == y; likely false for random pairs", prop.ForAll( - func(a testPairElement, b testPairElement) bool { - return a.element.Equal(&b.element) == (a.element == b.element) - }, - genA, - genB, - )) - - properties.Property("x.Equal(&y) if x == y", prop.ForAll( - func(a testPairElement) bool { - b := a.element - return a.element.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementBytes(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("SetBytes(Bytes()) should stay constant", prop.ForAll( - func(a testPairElement) bool { - var b Element - bytes := a.element.Bytes() - b.SetBytes(bytes[:]) - return a.element.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementInverseExp(t *testing.T) { - // inverse must be equal to exp^-2 - exp := Modulus() - exp.Sub(exp, new(big.Int).SetUint64(2)) - - invMatchExp := func(a testPairElement) bool { - var b Element - b.Set(&a.element) - a.element.Inverse(&a.element) - b.Exp(b, exp) - - return a.element.Equal(&b) - } - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - properties := gopter.NewProperties(parameters) - genA := gen() - properties.Property("inv == exp^-2", prop.ForAll(invMatchExp, genA)) - properties.TestingRun(t, gopter.ConsoleReporter(false)) - - parameters.MinSuccessfulTests = 1 - properties = gopter.NewProperties(parameters) - properties.Property("inv(0) == 0", prop.ForAll(invMatchExp, ggen.OneConstOf(testPairElement{}))) - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func mulByConstant(z *Element, c uint8) { - var y Element - y.SetUint64(uint64(c)) - z.Mul(z, &y) -} - -func TestElementMulByConstants(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - implemented := []uint8{0, 1, 2, 3, 5, 13} - properties.Property("mulByConstant", prop.ForAll( - func(a testPairElement) bool { - for _, c := range implemented { - var constant Element - constant.SetUint64(uint64(c)) - - b := a.element - b.Mul(&b, &constant) - - aa := a.element - mulByConstant(&aa, c) - - if !aa.Equal(&b) { - return false - } - } - - return true - }, - genA, - )) - - properties.Property("MulBy3(x) == Mul(x, 3)", prop.ForAll( - func(a testPairElement) bool { - var constant Element - constant.SetUint64(3) - - b := a.element - b.Mul(&b, &constant) - - MulBy3(&a.element) - - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("MulBy5(x) == Mul(x, 5)", prop.ForAll( - func(a testPairElement) bool { - var constant Element - constant.SetUint64(5) - - b := a.element - b.Mul(&b, &constant) - - MulBy5(&a.element) - - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("MulBy13(x) == Mul(x, 13)", prop.ForAll( - func(a testPairElement) bool { - var constant Element - constant.SetUint64(13) - - b := a.element - b.Mul(&b, &constant) - - MulBy13(&a.element) - - return a.element.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementLegendre(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("legendre should output same result than big.Int.Jacobi", prop.ForAll( - func(a testPairElement) bool { - return a.element.Legendre() == big.Jacobi(&a.bigint, Modulus()) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementBitLen(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("BitLen should output same result than big.Int.BitLen", prop.ForAll( - func(a testPairElement) bool { - return a.element.fromMont().BitLen() == a.bigint.BitLen() - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementButterflies(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("butterfly0 == a -b; a +b", prop.ForAll( - func(a, b testPairElement) bool { - a0, b0 := a.element, b.element - - _butterflyGeneric(&a.element, &b.element) - Butterfly(&a0, &b0) - - return a.element.Equal(&a0) && b.element.Equal(&b0) - }, - genA, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementLexicographicallyLargest(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("element.Cmp should match LexicographicallyLargest output", prop.ForAll( - func(a testPairElement) bool { - var negA Element - negA.Neg(&a.element) - - cmpResult := a.element.Cmp(&negA) - lResult := a.element.LexicographicallyLargest() - - if lResult && cmpResult == 1 { - return true - } - if !lResult && cmpResult != 1 { - return true - } - return false - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementAdd(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Add: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Add(&a.element, &b.element) - a.element.Add(&a.element, &b.element) - b.element.Add(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Add: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Add(&a.element, &b.element) - - var d, e big.Int - d.Add(&a.bigint, &b.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Add(&a.element, &r) - d.Add(&a.bigint, &rb).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Add: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Add(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Add(&a, &b) - d.Add(&aBig, &bBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Add failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementSub(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Sub: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Sub(&a.element, &b.element) - a.element.Sub(&a.element, &b.element) - b.element.Sub(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Sub: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Sub(&a.element, &b.element) - - var d, e big.Int - d.Sub(&a.bigint, &b.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Sub(&a.element, &r) - d.Sub(&a.bigint, &rb).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Sub: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Sub(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Sub(&a, &b) - d.Sub(&aBig, &bBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Sub failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementMul(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Mul: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Mul(&a.element, &b.element) - a.element.Mul(&a.element, &b.element) - b.element.Mul(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Mul: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Mul(&a.element, &b.element) - - var d, e big.Int - d.Mul(&a.bigint, &b.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Mul(&a.element, &r) - d.Mul(&a.bigint, &rb).Mod(&d, Modulus()) - - // checking generic impl against asm path - var cGeneric Element - _mulGeneric(&cGeneric, &a.element, &r) - if !cGeneric.Equal(&c) { - // need to give context to failing error. - return false - } - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Mul: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Mul(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - properties.Property("Mul: assembly implementation must be consistent with generic one", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - c.Mul(&a.element, &b.element) - _mulGeneric(&d, &a.element, &b.element) - return c.Equal(&d) - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Mul(&a, &b) - d.Mul(&aBig, &bBig).Mod(&d, Modulus()) - - // checking asm against generic impl - var cGeneric Element - _mulGeneric(&cGeneric, &a, &b) - if !cGeneric.Equal(&c) { - t.Fatal("Mul failed special test values: asm and generic impl don't match") - } - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Mul failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementDiv(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Div: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Div(&a.element, &b.element) - a.element.Div(&a.element, &b.element) - b.element.Div(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Div: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Div(&a.element, &b.element) - - var d, e big.Int - d.ModInverse(&b.bigint, Modulus()) - d.Mul(&d, &a.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Div(&a.element, &r) - d.ModInverse(&rb, Modulus()) - d.Mul(&d, &a.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Div: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Div(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Div(&a, &b) - d.ModInverse(&bBig, Modulus()) - d.Mul(&d, &aBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Div failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementExp(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Exp: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Exp(a.element, &b.bigint) - a.element.Exp(a.element, &b.bigint) - b.element.Exp(d, &b.bigint) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Exp: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Exp(a.element, &b.bigint) - - var d, e big.Int - d.Exp(&a.bigint, &b.bigint, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Exp(a.element, &rb) - d.Exp(&a.bigint, &rb, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Exp: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Exp(a.element, &b.bigint) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Exp(a, &bBig) - d.Exp(&aBig, &bBig, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Exp failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementSquare(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Square: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Square(&a.element) - a.element.Square(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Square: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Square(&a.element) - - var d, e big.Int - d.Mul(&a.bigint, &a.bigint).Mod(&d, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Square: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Square(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Square(&a) - - var d, e big.Int - d.Mul(&aBig, &aBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Square failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementInverse(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Inverse: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Inverse(&a.element) - a.element.Inverse(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Inverse: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Inverse(&a.element) - - var d, e big.Int - d.ModInverse(&a.bigint, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Inverse: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Inverse(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Inverse(&a) - - var d, e big.Int - d.ModInverse(&aBig, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Inverse failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementSqrt(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Sqrt: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - b := a.element - - b.Sqrt(&a.element) - a.element.Sqrt(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Sqrt: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Sqrt(&a.element) - - var d, e big.Int - d.ModSqrt(&a.bigint, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Sqrt: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Sqrt(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Sqrt(&a) - - var d, e big.Int - d.ModSqrt(&aBig, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Sqrt failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementDouble(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Double: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Double(&a.element) - a.element.Double(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Double: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Double(&a.element) - - var d, e big.Int - d.Lsh(&a.bigint, 1).Mod(&d, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Double: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Double(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Double(&a) - - var d, e big.Int - d.Lsh(&aBig, 1).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Double failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementNeg(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Neg: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Neg(&a.element) - a.element.Neg(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Neg: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Neg(&a.element) - - var d, e big.Int - d.Neg(&a.bigint).Mod(&d, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Neg: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Neg(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Neg(&a) - - var d, e big.Int - d.Neg(&aBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Neg failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementFixedExp(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - var ( - _bLegendreExponentElement *big.Int - _bSqrtExponentElement *big.Int - ) - - _bLegendreExponentElement, _ = new(big.Int).SetString("7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43429276019e3f31fc34200000000000000000000", 16) - const sqrtExponentElement = "1eed5b76b77315c55824fc3c6ad19eb92f19a5f5859d13f7e464428aa2c74d998d5ce788548db3d6059025d409f55414fd63967a0dcc8dc5259a2bdb6c8d4a860554784b1bcfbda16d0bd0d0a49d80678fcc7f0d0" - _bSqrtExponentElement, _ = new(big.Int).SetString(sqrtExponentElement, 16) - - genA := gen() - - properties.Property(fmt.Sprintf("expBySqrtExp must match Exp(%s)", sqrtExponentElement), prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.expBySqrtExp(c) - d.Exp(d, _bSqrtExponentElement) - return c.Equal(&d) - }, - genA, - )) - - properties.Property("expByLegendreExp must match Exp(7bb56ddaddcc57156093f0f1ab467ae4bc6697d616744fdf91910a2a8b1d366635739e215236cf581640975027d55053f58e59e8373237149668af6db2352a181551e12c6f3ef685b42f43429276019e3f31fc34200000000000000000000)", prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.expByLegendreExp(c) - d.Exp(d, _bLegendreExponentElement) - return c.Equal(&d) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementHalve(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - var twoInv Element - twoInv.SetUint64(2) - twoInv.Inverse(&twoInv) - - properties.Property("z.Halve must match z / 2", prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.Halve() - d.Mul(&d, &twoInv) - return c.Equal(&d) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func combineSelectionArguments(c int64, z int8) int { - if z%3 == 0 { - return 0 - } - return int(c) -} - -func TestElementSelect(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := genFull() - genB := genFull() - genC := ggen.Int64() //the condition - genZ := ggen.Int8() //to make zeros artificially more likely - - properties.Property("Select: must select correctly", prop.ForAll( - func(a, b Element, cond int64, z int8) bool { - condC := combineSelectionArguments(cond, z) - - var c Element - c.Select(condC, &a, &b) - - if condC == 0 { - return c.Equal(&a) - } - return c.Equal(&b) - }, - genA, - genB, - genC, - genZ, - )) - - properties.Property("Select: having the receiver as operand should output the same result", prop.ForAll( - func(a, b Element, cond int64, z int8) bool { - condC := combineSelectionArguments(cond, z) - - var c, d Element - d.Set(&a) - c.Select(condC, &a, &b) - a.Select(condC, &a, &b) - b.Select(condC, &d, &b) - return a.Equal(&b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - genC, - genZ, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementSetInt64(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("z.SetInt64 must match z.SetString", prop.ForAll( - func(a testPairElement, v int64) bool { - c := a.element - d := a.element - - c.SetInt64(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, ggen.Int64(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementSetInterface(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genInt := ggen.Int - genInt8 := ggen.Int8 - genInt16 := ggen.Int16 - genInt32 := ggen.Int32 - genInt64 := ggen.Int64 - - genUint := ggen.UInt - genUint8 := ggen.UInt8 - genUint16 := ggen.UInt16 - genUint32 := ggen.UInt32 - genUint64 := ggen.UInt64 - - properties.Property("z.SetInterface must match z.SetString with int8", prop.ForAll( - func(a testPairElement, v int8) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt8(), - )) - - properties.Property("z.SetInterface must match z.SetString with int16", prop.ForAll( - func(a testPairElement, v int16) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt16(), - )) - - properties.Property("z.SetInterface must match z.SetString with int32", prop.ForAll( - func(a testPairElement, v int32) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt32(), - )) - - properties.Property("z.SetInterface must match z.SetString with int64", prop.ForAll( - func(a testPairElement, v int64) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt64(), - )) - - properties.Property("z.SetInterface must match z.SetString with int", prop.ForAll( - func(a testPairElement, v int) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint8", prop.ForAll( - func(a testPairElement, v uint8) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint8(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint16", prop.ForAll( - func(a testPairElement, v uint16) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint16(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint32", prop.ForAll( - func(a testPairElement, v uint32) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint32(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint64", prop.ForAll( - func(a testPairElement, v uint64) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint64(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint", prop.ForAll( - func(a testPairElement, v uint) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - - { - assert := require.New(t) - var e Element - r, err := e.SetInterface(nil) - assert.Nil(r) - assert.Error(err) - - var ptE *Element - var ptB *big.Int - - r, err = e.SetInterface(ptE) - assert.Nil(r) - assert.Error(err) - ptE = new(Element).SetOne() - r, err = e.SetInterface(ptE) - assert.NoError(err) - assert.True(r.IsOne()) - - r, err = e.SetInterface(ptB) - assert.Nil(r) - assert.Error(err) - - } -} - -func TestElementNegativeExp(t *testing.T) { - t.Parallel() - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("x⁻ᵏ == 1/xᵏ", prop.ForAll( - func(a, b testPairElement) bool { - - var nb, d, e big.Int - nb.Neg(&b.bigint) - - var c Element - c.Exp(a.element, &nb) - - d.Exp(&a.bigint, &nb, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementNewElement(t *testing.T) { - assert := require.New(t) - - t.Parallel() - - e := NewElement(1) - assert.True(e.IsOne()) - - e = NewElement(0) - assert.True(e.IsZero()) -} - -func TestElementBatchInvert(t *testing.T) { - assert := require.New(t) - - t.Parallel() - - // ensure batchInvert([x]) == invert(x) - for i := int64(-1); i <= 2; i++ { - var e, eInv Element - e.SetInt64(i) - eInv.Inverse(&e) - - a := []Element{e} - aInv := BatchInvert(a) - - assert.True(aInv[0].Equal(&eInv), "batchInvert != invert") - - } - - // test x * x⁻¹ == 1 - tData := [][]int64{ - {-1, 1, 2, 3}, - {0, -1, 1, 2, 3, 0}, - {0, -1, 1, 0, 2, 3, 0}, - {-1, 1, 0, 2, 3}, - {0, 0, 1}, - {1, 0, 0}, - {0, 0, 0}, - } - - for _, t := range tData { - a := make([]Element, len(t)) - for i := 0; i < len(a); i++ { - a[i].SetInt64(t[i]) - } - - aInv := BatchInvert(a) - - assert.True(len(aInv) == len(a)) - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - assert.True(aInv[i].IsZero(), "0⁻¹ != 0") - } else { - assert.True(a[i].Mul(&a[i], &aInv[i]).IsOne(), "x * x⁻¹ != 1") - } - } - } - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("batchInvert --> x * x⁻¹ == 1", prop.ForAll( - func(tp testPairElement, r uint8) bool { - - a := make([]Element, r) - if r != 0 { - a[0] = tp.element - - } - one := One() - for i := 1; i < len(a); i++ { - a[i].Add(&a[i-1], &one) - } - - aInv := BatchInvert(a) - - assert.True(len(aInv) == len(a)) - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - if !aInv[i].IsZero() { - return false - } - } else { - if !a[i].Mul(&a[i], &aInv[i]).IsOne() { - return false - } - } - } - return true - }, - genA, ggen.UInt8(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementFromMont(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Assembly implementation must be consistent with generic one", prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.fromMont() - _fromMontGeneric(&d) - return c.Equal(&d) - }, - genA, - )) - - properties.Property("x.fromMont().toMont() == x", prop.ForAll( - func(a testPairElement) bool { - c := a.element - c.fromMont().toMont() - return c.Equal(&a.element) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementJSON(t *testing.T) { - assert := require.New(t) - - type S struct { - A Element - B [3]Element - C *Element - D *Element - } - - // encode to JSON - var s S - s.A.SetString("-1") - s.B[2].SetUint64(42) - s.D = new(Element).SetUint64(8000) - - encoded, err := json.Marshal(&s) - assert.NoError(err) - // we may need to adjust "42" and "8000" values for some moduli; see Text() method for more details. - formatValue := func(v int64) string { - var a big.Int - a.SetInt64(v) - a.Mod(&a, Modulus()) - const maxUint16 = 65535 - var aNeg big.Int - aNeg.Neg(&a).Mod(&aNeg, Modulus()) - if aNeg.Uint64() != 0 && aNeg.Uint64() <= maxUint16 { - return "-" + aNeg.Text(10) - } - return a.Text(10) - } - expected := fmt.Sprintf("{\"A\":%s,\"B\":[0,0,%s],\"C\":null,\"D\":%s}", formatValue(-1), formatValue(42), formatValue(8000)) - assert.Equal(expected, string(encoded)) - - // decode valid - var decoded S - err = json.Unmarshal([]byte(expected), &decoded) - assert.NoError(err) - - assert.Equal(s, decoded, "element -> json -> element round trip failed") - - // decode hex and string values - withHexValues := "{\"A\":\"-1\",\"B\":[0,\"0x00000\",\"0x2A\"],\"C\":null,\"D\":\"8000\"}" - - var decodedS S - err = json.Unmarshal([]byte(withHexValues), &decodedS) - assert.NoError(err) - - assert.Equal(s, decodedS, " json with strings -> element failed") - -} - -type testPairElement struct { - element Element - bigint big.Int -} - -func gen() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var g testPairElement - - g.element = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - if qElement[11] != ^uint64(0) { - g.element[11] %= (qElement[11] + 1) - } - - for !g.element.smallerThanModulus() { - g.element = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - if qElement[11] != ^uint64(0) { - g.element[11] %= (qElement[11] + 1) - } - } - - g.element.BigInt(&g.bigint) - genResult := gopter.NewGenResult(g, gopter.NoShrinker) - return genResult - } -} - -func genFull() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - - genRandomFq := func() Element { - var g Element - - g = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - - if qElement[11] != ^uint64(0) { - g[11] %= (qElement[11] + 1) - } - - for !g.smallerThanModulus() { - g = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - if qElement[11] != ^uint64(0) { - g[11] %= (qElement[11] + 1) - } - } - - return g - } - a := genRandomFq() - - var carry uint64 - a[0], carry = bits.Add64(a[0], qElement[0], carry) - a[1], carry = bits.Add64(a[1], qElement[1], carry) - a[2], carry = bits.Add64(a[2], qElement[2], carry) - a[3], carry = bits.Add64(a[3], qElement[3], carry) - a[4], carry = bits.Add64(a[4], qElement[4], carry) - a[5], carry = bits.Add64(a[5], qElement[5], carry) - a[6], carry = bits.Add64(a[6], qElement[6], carry) - a[7], carry = bits.Add64(a[7], qElement[7], carry) - a[8], carry = bits.Add64(a[8], qElement[8], carry) - a[9], carry = bits.Add64(a[9], qElement[9], carry) - a[10], carry = bits.Add64(a[10], qElement[10], carry) - a[11], _ = bits.Add64(a[11], qElement[11], carry) - - genResult := gopter.NewGenResult(a, gopter.NoShrinker) - return genResult - } -} - -func (z *Element) matchVeryBigInt(aHi uint64, aInt *big.Int) error { - var modulus big.Int - var aIntMod big.Int - modulus.SetInt64(1) - modulus.Lsh(&modulus, (Limbs+1)*64) - aIntMod.Mod(aInt, &modulus) - - slice := append(z[:], aHi) - - return bigIntMatchUint64Slice(&aIntMod, slice) -} - -// TODO: Phase out in favor of property based testing -func (z *Element) assertMatchVeryBigInt(t *testing.T, aHi uint64, aInt *big.Int) { - - if err := z.matchVeryBigInt(aHi, aInt); err != nil { - t.Error(err) - } -} - -// bigIntMatchUint64Slice is a test helper to match big.Int words against a uint64 slice -func bigIntMatchUint64Slice(aInt *big.Int, a []uint64) error { - - words := aInt.Bits() - - const steps = 64 / bits.UintSize - const filter uint64 = 0xFFFFFFFFFFFFFFFF >> (64 - bits.UintSize) - for i := 0; i < len(a)*steps; i++ { - - var wI big.Word - - if i < len(words) { - wI = words[i] - } - - aI := a[i/steps] >> ((i * bits.UintSize) % 64) - aI &= filter - - if uint64(wI) != aI { - return fmt.Errorf("bignum mismatch: disagreement on word %d: %x ≠ %x; %d ≠ %d", i, uint64(wI), aI, uint64(wI), aI) - } - } - - return nil -} - -func TestElementInversionApproximation(t *testing.T) { - var x Element - for i := 0; i < 1000; i++ { - x.SetRandom() - - // Normally small elements are unlikely. Here we give them a higher chance - xZeros := mrand.Int() % Limbs //#nosec G404 weak rng is fine here - for j := 1; j < xZeros; j++ { - x[Limbs-j] = 0 - } - - a := approximate(&x, x.BitLen()) - aRef := approximateRef(&x) - - if a != aRef { - t.Error("Approximation mismatch") - } - } -} - -func TestElementInversionCorrectionFactorFormula(t *testing.T) { - const kLimbs = k * Limbs - const power = kLimbs*6 + invIterationsN*(kLimbs-k+1) - factorInt := big.NewInt(1) - factorInt.Lsh(factorInt, power) - factorInt.Mod(factorInt, Modulus()) - - var refFactorInt big.Int - inversionCorrectionFactor := Element{ - inversionCorrectionFactorWord0, - inversionCorrectionFactorWord1, - inversionCorrectionFactorWord2, - inversionCorrectionFactorWord3, - inversionCorrectionFactorWord4, - inversionCorrectionFactorWord5, - inversionCorrectionFactorWord6, - inversionCorrectionFactorWord7, - inversionCorrectionFactorWord8, - inversionCorrectionFactorWord9, - inversionCorrectionFactorWord10, - inversionCorrectionFactorWord11, - } - inversionCorrectionFactor.toBigInt(&refFactorInt) - - if refFactorInt.Cmp(factorInt) != 0 { - t.Error("mismatch") - } -} - -func TestElementLinearComb(t *testing.T) { - var x Element - var y Element - - for i := 0; i < 1000; i++ { - x.SetRandom() - y.SetRandom() - testLinearComb(t, &x, mrand.Int63(), &y, mrand.Int63()) //#nosec G404 weak rng is fine here - } -} - -// Probably unnecessary post-dev. In case the output of inv is wrong, this checks whether it's only off by a constant factor. -func TestElementInversionCorrectionFactor(t *testing.T) { - - // (1/x)/inv(x) = (1/1)/inv(1) ⇔ inv(1) = x inv(x) - - var one Element - var oneInv Element - one.SetOne() - oneInv.Inverse(&one) - - for i := 0; i < 100; i++ { - var x Element - var xInv Element - x.SetRandom() - xInv.Inverse(&x) - - x.Mul(&x, &xInv) - if !x.Equal(&oneInv) { - t.Error("Correction factor is inconsistent") - } - } - - if !oneInv.Equal(&one) { - var i big.Int - oneInv.BigInt(&i) // no montgomery - i.ModInverse(&i, Modulus()) - var fac Element - fac.setBigInt(&i) // back to montgomery - - var facTimesFac Element - facTimesFac.Mul(&fac, &Element{ - inversionCorrectionFactorWord0, - inversionCorrectionFactorWord1, - inversionCorrectionFactorWord2, - inversionCorrectionFactorWord3, - inversionCorrectionFactorWord4, - inversionCorrectionFactorWord5, - inversionCorrectionFactorWord6, - inversionCorrectionFactorWord7, - inversionCorrectionFactorWord8, - inversionCorrectionFactorWord9, - inversionCorrectionFactorWord10, - inversionCorrectionFactorWord11, - }) - - t.Error("Correction factor is consistently off by", fac, "Should be", facTimesFac) - } -} - -func TestElementBigNumNeg(t *testing.T) { - var a Element - aHi := negL(&a, 0) - if !a.IsZero() || aHi != 0 { - t.Error("-0 != 0") - } -} - -func TestElementBigNumWMul(t *testing.T) { - var x Element - - for i := 0; i < 1000; i++ { - x.SetRandom() - w := mrand.Int63() //#nosec G404 weak rng is fine here - testBigNumWMul(t, &x, w) - } -} - -func TestElementVeryBigIntConversion(t *testing.T) { - xHi := mrand.Uint64() //#nosec G404 weak rng is fine here - var x Element - x.SetRandom() - var xInt big.Int - x.toVeryBigIntSigned(&xInt, xHi) - x.assertMatchVeryBigInt(t, xHi, &xInt) -} - -type veryBigInt struct { - asInt big.Int - low Element - hi uint64 -} - -// genVeryBigIntSigned if sign == 0, no sign is forced -func genVeryBigIntSigned(sign int) gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var g veryBigInt - - g.low = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - - g.hi = genParams.NextUint64() - - if sign < 0 { - g.hi |= signBitSelector - } else if sign > 0 { - g.hi &= ^signBitSelector - } - - g.low.toVeryBigIntSigned(&g.asInt, g.hi) - - genResult := gopter.NewGenResult(g, gopter.NoShrinker) - return genResult - } -} - -func TestElementMontReduce(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - gen := genVeryBigIntSigned(0) - - properties.Property("Montgomery reduction is correct", prop.ForAll( - func(g veryBigInt) bool { - var res Element - var resInt big.Int - - montReduce(&resInt, &g.asInt) - res.montReduceSigned(&g.low, g.hi) - - return res.matchVeryBigInt(0, &resInt) == nil - }, - gen, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementMontReduceMultipleOfR(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - gen := ggen.UInt64() - - properties.Property("Montgomery reduction is correct", prop.ForAll( - func(hi uint64) bool { - var zero, res Element - var asInt, resInt big.Int - - zero.toVeryBigIntSigned(&asInt, hi) - - montReduce(&resInt, &asInt) - res.montReduceSigned(&zero, hi) - - return res.matchVeryBigInt(0, &resInt) == nil - }, - gen, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElement0Inverse(t *testing.T) { - var x Element - x.Inverse(&x) - if !x.IsZero() { - t.Fail() - } -} - -// TODO: Tests like this (update factor related) are common to all fields. Move them to somewhere non-autogen -func TestUpdateFactorSubtraction(t *testing.T) { - for i := 0; i < 1000; i++ { - - f0, g0 := randomizeUpdateFactors() - f1, g1 := randomizeUpdateFactors() - - for f0-f1 > 1<<31 || f0-f1 <= -1<<31 { - f1 /= 2 - } - - for g0-g1 > 1<<31 || g0-g1 <= -1<<31 { - g1 /= 2 - } - - c0 := updateFactorsCompose(f0, g0) - c1 := updateFactorsCompose(f1, g1) - - cRes := c0 - c1 - fRes, gRes := updateFactorsDecompose(cRes) - - if fRes != f0-f1 || gRes != g0-g1 { - t.Error(i) - } - } -} - -func TestUpdateFactorsDouble(t *testing.T) { - for i := 0; i < 1000; i++ { - f, g := randomizeUpdateFactors() - - if f > 1<<30 || f < (-1<<31+1)/2 { - f /= 2 - if g <= 1<<29 && g >= (-1<<31+1)/4 { - g *= 2 //g was kept small on f's account. Now that we're halving f, we can double g - } - } - - if g > 1<<30 || g < (-1<<31+1)/2 { - g /= 2 - - if f <= 1<<29 && f >= (-1<<31+1)/4 { - f *= 2 //f was kept small on g's account. Now that we're halving g, we can double f - } - } - - c := updateFactorsCompose(f, g) - cD := c * 2 - fD, gD := updateFactorsDecompose(cD) - - if fD != 2*f || gD != 2*g { - t.Error(i) - } - } -} - -func TestUpdateFactorsNeg(t *testing.T) { - var fMistake bool - for i := 0; i < 1000; i++ { - f, g := randomizeUpdateFactors() - - if f == 0x80000000 || g == 0x80000000 { - // Update factors this large can only have been obtained after 31 iterations and will therefore never be negated - // We don't have capacity to store -2³¹ - // Repeat this iteration - i-- - continue - } - - c := updateFactorsCompose(f, g) - nc := -c - nf, ng := updateFactorsDecompose(nc) - fMistake = fMistake || nf != -f - if nf != -f || ng != -g { - t.Errorf("Mismatch iteration #%d:\n%d, %d ->\n %d -> %d ->\n %d, %d\n Inputs in hex: %X, %X", - i, f, g, c, nc, nf, ng, f, g) - } - } - if fMistake { - t.Error("Mistake with f detected") - } else { - t.Log("All good with f") - } -} - -func TestUpdateFactorsNeg0(t *testing.T) { - c := updateFactorsCompose(0, 0) - t.Logf("c(0,0) = %X", c) - cn := -c - - if c != cn { - t.Error("Negation of zero update factors should yield the same result.") - } -} - -func TestUpdateFactorDecomposition(t *testing.T) { - var negSeen bool - - for i := 0; i < 1000; i++ { - - f, g := randomizeUpdateFactors() - - if f <= -(1<<31) || f > 1<<31 { - t.Fatal("f out of range") - } - - negSeen = negSeen || f < 0 - - c := updateFactorsCompose(f, g) - - fBack, gBack := updateFactorsDecompose(c) - - if f != fBack || g != gBack { - t.Errorf("(%d, %d) -> %d -> (%d, %d)\n", f, g, c, fBack, gBack) - } - } - - if !negSeen { - t.Fatal("No negative f factors") - } -} - -func TestUpdateFactorInitialValues(t *testing.T) { - - f0, g0 := updateFactorsDecompose(updateFactorIdentityMatrixRow0) - f1, g1 := updateFactorsDecompose(updateFactorIdentityMatrixRow1) - - if f0 != 1 || g0 != 0 || f1 != 0 || g1 != 1 { - t.Error("Update factor initial value constants are incorrect") - } -} - -func TestUpdateFactorsRandomization(t *testing.T) { - var maxLen int - - //t.Log("|f| + |g| is not to exceed", 1 << 31) - for i := 0; i < 1000; i++ { - f, g := randomizeUpdateFactors() - lf, lg := abs64T32(f), abs64T32(g) - absSum := lf + lg - if absSum >= 1<<31 { - - if absSum == 1<<31 { - maxLen++ - } else { - t.Error(i, "Sum of absolute values too large, f =", f, ",g =", g, ",|f| + |g| =", absSum) - } - } - } - - if maxLen == 0 { - t.Error("max len not observed") - } else { - t.Log(maxLen, "maxLens observed") - } -} - -func randomizeUpdateFactor(absLimit uint32) int64 { - const maxSizeLikelihood = 10 - maxSize := mrand.Intn(maxSizeLikelihood) //#nosec G404 weak rng is fine here - - absLimit64 := int64(absLimit) - var f int64 - switch maxSize { - case 0: - f = absLimit64 - case 1: - f = -absLimit64 - default: - f = int64(mrand.Uint64()%(2*uint64(absLimit64)+1)) - absLimit64 //#nosec G404 weak rng is fine here - } - - if f > 1<<31 { - return 1 << 31 - } else if f < -1<<31+1 { - return -1<<31 + 1 - } - - return f -} - -func abs64T32(f int64) uint32 { - if f >= 1<<32 || f < -1<<32 { - panic("f out of range") - } - - if f < 0 { - return uint32(-f) - } - return uint32(f) -} - -func randomizeUpdateFactors() (int64, int64) { - var f [2]int64 - b := mrand.Int() % 2 //#nosec G404 weak rng is fine here - - f[b] = randomizeUpdateFactor(1 << 31) - - //As per the paper, |f| + |g| \le 2³¹. - f[1-b] = randomizeUpdateFactor(1<<31 - abs64T32(f[b])) - - //Patching another edge case - if f[0]+f[1] == -1<<31 { - b = mrand.Int() % 2 //#nosec G404 weak rng is fine here - f[b]++ - } - - return f[0], f[1] -} - -func testLinearComb(t *testing.T, x *Element, xC int64, y *Element, yC int64) { - - var p1 big.Int - x.toBigInt(&p1) - p1.Mul(&p1, big.NewInt(xC)) - - var p2 big.Int - y.toBigInt(&p2) - p2.Mul(&p2, big.NewInt(yC)) - - p1.Add(&p1, &p2) - p1.Mod(&p1, Modulus()) - montReduce(&p1, &p1) - - var z Element - z.linearComb(x, xC, y, yC) - z.assertMatchVeryBigInt(t, 0, &p1) -} - -func testBigNumWMul(t *testing.T, a *Element, c int64) { - var aHi uint64 - var aTimes Element - aHi = aTimes.mulWNonModular(a, c) - - assertMulProduct(t, a, c, &aTimes, aHi) -} - -func updateFactorsCompose(f int64, g int64) int64 { - return f + g<<32 -} - -var rInv big.Int - -func montReduce(res *big.Int, x *big.Int) { - if rInv.BitLen() == 0 { // initialization - rInv.SetUint64(1) - rInv.Lsh(&rInv, Limbs*64) - rInv.ModInverse(&rInv, Modulus()) - } - res.Mul(x, &rInv) - res.Mod(res, Modulus()) -} - -func (z *Element) toVeryBigIntUnsigned(i *big.Int, xHi uint64) { - z.toBigInt(i) - var upperWord big.Int - upperWord.SetUint64(xHi) - upperWord.Lsh(&upperWord, Limbs*64) - i.Add(&upperWord, i) -} - -func (z *Element) toVeryBigIntSigned(i *big.Int, xHi uint64) { - z.toVeryBigIntUnsigned(i, xHi) - if signBitSelector&xHi != 0 { - twosCompModulus := big.NewInt(1) - twosCompModulus.Lsh(twosCompModulus, (Limbs+1)*64) - i.Sub(i, twosCompModulus) - } -} - -func assertMulProduct(t *testing.T, x *Element, c int64, result *Element, resultHi uint64) big.Int { - var xInt big.Int - x.toBigInt(&xInt) - - xInt.Mul(&xInt, big.NewInt(c)) - - result.assertMatchVeryBigInt(t, resultHi, &xInt) - return xInt -} - -func approximateRef(x *Element) uint64 { - - var asInt big.Int - x.toBigInt(&asInt) - n := x.BitLen() - - if n <= 64 { - return asInt.Uint64() - } - - modulus := big.NewInt(1 << 31) - var lo big.Int - lo.Mod(&asInt, modulus) - - modulus.Lsh(modulus, uint(n-64)) - var hi big.Int - hi.Div(&asInt, modulus) - hi.Lsh(&hi, 31) - - hi.Add(&hi, &lo) - return hi.Uint64() -} diff --git a/ecc/bw6-756/fp/hash_to_field/doc.go b/ecc/bw6-756/fp/hash_to_field/doc.go deleted file mode 100644 index 47a4f762a5..0000000000 --- a/ecc/bw6-756/fp/hash_to_field/doc.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package htf provides hasher based on RFC 9380 Section 5. -// -// The [RFC 9380] defines a method for hashing bytes to elliptic curves. Section -// 5 of the RFC describes a method for uniformly hashing bytes into a field -// using a domain separation. The hashing is implemented in [fp], but this -// package provides a wrapper for the method which implements [hash.Hash] for -// using the method recursively. -// -// [RFC 9380]: https://datatracker.ietf.org/doc/html/rfc9380 -package hash_to_field - -import ( - _ "hash" - - _ "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" -) diff --git a/ecc/bw6-756/fp/hash_to_field/hash_to_field.go b/ecc/bw6-756/fp/hash_to_field/hash_to_field.go deleted file mode 100644 index 4b6d1b7977..0000000000 --- a/ecc/bw6-756/fp/hash_to_field/hash_to_field.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package hash_to_field - -import ( - "fmt" - "hash" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" -) - -type wrappedHashToField struct { - domain []byte - toHash []byte -} - -// New returns a new hasher instance which uses [fp.Hash] to hash all the -// written bytes to a field element, returning the byte representation of the -// field element. The domain separator is passed as-is to hashing method. -func New(domainSeparator []byte) hash.Hash { - return &wrappedHashToField{ - domain: append([]byte{}, domainSeparator...), // copy in case the argument is modified - } -} - -func (w *wrappedHashToField) Write(p []byte) (n int, err error) { - w.toHash = append(w.toHash, p...) - return len(p), nil -} - -func (w *wrappedHashToField) Sum(b []byte) []byte { - res, err := fp.Hash(w.toHash, w.domain, 1) - if err != nil { - // we want to follow the interface, cannot return error and have to panic - // but by default the method shouldn't return an error internally - panic(fmt.Sprintf("native field to hash: %v", err)) - } - bts := res[0].Bytes() - return append(b, bts[:]...) -} - -func (w *wrappedHashToField) Reset() { - w.toHash = nil -} - -func (w *wrappedHashToField) Size() int { - return fp.Bytes -} - -func (w *wrappedHashToField) BlockSize() int { - return fp.Bytes -} diff --git a/ecc/bw6-756/fp/hash_to_field/hash_to_field_test.go b/ecc/bw6-756/fp/hash_to_field/hash_to_field_test.go deleted file mode 100644 index 160020aaea..0000000000 --- a/ecc/bw6-756/fp/hash_to_field/hash_to_field_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package hash_to_field - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" -) - -func TestHashInterface(t *testing.T) { - msg := []byte("test") - sep := []byte("separator") - res, err := fp.Hash(msg, sep, 1) - if err != nil { - t.Fatal("hash to field", err) - } - - htfFn := New(sep) - htfFn.Write(msg) - bts := htfFn.Sum(nil) - var res2 fp.Element - res2.SetBytes(bts[:fp.Bytes]) - if !res[0].Equal(&res2) { - t.Error("not equal") - } -} diff --git a/ecc/bw6-756/fp/vector.go b/ecc/bw6-756/fp/vector.go deleted file mode 100644 index f136ce9b30..0000000000 --- a/ecc/bw6-756/fp/vector.go +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" - "runtime" - "strings" - "sync" - "sync/atomic" - "unsafe" -) - -// Vector represents a slice of Element. -// -// It implements the following interfaces: -// - Stringer -// - io.WriterTo -// - io.ReaderFrom -// - encoding.BinaryMarshaler -// - encoding.BinaryUnmarshaler -// - sort.Interface -type Vector []Element - -// MarshalBinary implements encoding.BinaryMarshaler -func (vector *Vector) MarshalBinary() (data []byte, err error) { - var buf bytes.Buffer - - if _, err = vector.WriteTo(&buf); err != nil { - return - } - return buf.Bytes(), nil -} - -// UnmarshalBinary implements encoding.BinaryUnmarshaler -func (vector *Vector) UnmarshalBinary(data []byte) error { - r := bytes.NewReader(data) - _, err := vector.ReadFrom(r) - return err -} - -// WriteTo implements io.WriterTo and writes a vector of big endian encoded Element. -// Length of the vector is encoded as a uint32 on the first 4 bytes. -func (vector *Vector) WriteTo(w io.Writer) (int64, error) { - // encode slice length - if err := binary.Write(w, binary.BigEndian, uint32(len(*vector))); err != nil { - return 0, err - } - - n := int64(4) - - var buf [Bytes]byte - for i := 0; i < len(*vector); i++ { - BigEndian.PutElement(&buf, (*vector)[i]) - m, err := w.Write(buf[:]) - n += int64(m) - if err != nil { - return n, err - } - } - return n, nil -} - -// AsyncReadFrom reads a vector of big endian encoded Element. -// Length of the vector must be encoded as a uint32 on the first 4 bytes. -// It consumes the needed bytes from the reader and returns the number of bytes read and an error if any. -// It also returns a channel that will be closed when the validation is done. -// The validation consist of checking that the elements are smaller than the modulus, and -// converting them to montgomery form. -func (vector *Vector) AsyncReadFrom(r io.Reader) (int64, error, chan error) { - chErr := make(chan error, 1) - var buf [Bytes]byte - if read, err := io.ReadFull(r, buf[:4]); err != nil { - close(chErr) - return int64(read), err, chErr - } - sliceLen := binary.BigEndian.Uint32(buf[:4]) - - n := int64(4) - (*vector) = make(Vector, sliceLen) - if sliceLen == 0 { - close(chErr) - return n, nil, chErr - } - - bSlice := unsafe.Slice((*byte)(unsafe.Pointer(&(*vector)[0])), sliceLen*Bytes) - read, err := io.ReadFull(r, bSlice) - n += int64(read) - if err != nil { - close(chErr) - return n, err, chErr - } - - go func() { - var cptErrors uint64 - // process the elements in parallel - execute(int(sliceLen), func(start, end int) { - - var z Element - for i := start; i < end; i++ { - // we have to set vector[i] - bstart := i * Bytes - bend := bstart + Bytes - b := bSlice[bstart:bend] - z[0] = binary.BigEndian.Uint64(b[88:96]) - z[1] = binary.BigEndian.Uint64(b[80:88]) - z[2] = binary.BigEndian.Uint64(b[72:80]) - z[3] = binary.BigEndian.Uint64(b[64:72]) - z[4] = binary.BigEndian.Uint64(b[56:64]) - z[5] = binary.BigEndian.Uint64(b[48:56]) - z[6] = binary.BigEndian.Uint64(b[40:48]) - z[7] = binary.BigEndian.Uint64(b[32:40]) - z[8] = binary.BigEndian.Uint64(b[24:32]) - z[9] = binary.BigEndian.Uint64(b[16:24]) - z[10] = binary.BigEndian.Uint64(b[8:16]) - z[11] = binary.BigEndian.Uint64(b[0:8]) - - if !z.smallerThanModulus() { - atomic.AddUint64(&cptErrors, 1) - return - } - z.toMont() - (*vector)[i] = z - } - }) - - if cptErrors > 0 { - chErr <- fmt.Errorf("async read: %d elements failed validation", cptErrors) - } - close(chErr) - }() - return n, nil, chErr -} - -// ReadFrom implements io.ReaderFrom and reads a vector of big endian encoded Element. -// Length of the vector must be encoded as a uint32 on the first 4 bytes. -func (vector *Vector) ReadFrom(r io.Reader) (int64, error) { - - var buf [Bytes]byte - if read, err := io.ReadFull(r, buf[:4]); err != nil { - return int64(read), err - } - sliceLen := binary.BigEndian.Uint32(buf[:4]) - - n := int64(4) - (*vector) = make(Vector, sliceLen) - - for i := 0; i < int(sliceLen); i++ { - read, err := io.ReadFull(r, buf[:]) - n += int64(read) - if err != nil { - return n, err - } - (*vector)[i], err = BigEndian.Element(&buf) - if err != nil { - return n, err - } - } - - return n, nil -} - -// String implements fmt.Stringer interface -func (vector Vector) String() string { - var sbb strings.Builder - sbb.WriteByte('[') - for i := 0; i < len(vector); i++ { - sbb.WriteString(vector[i].String()) - if i != len(vector)-1 { - sbb.WriteByte(',') - } - } - sbb.WriteByte(']') - return sbb.String() -} - -// Len is the number of elements in the collection. -func (vector Vector) Len() int { - return len(vector) -} - -// Less reports whether the element with -// index i should sort before the element with index j. -func (vector Vector) Less(i, j int) bool { - return vector[i].Cmp(&vector[j]) == -1 -} - -// Swap swaps the elements with indexes i and j. -func (vector Vector) Swap(i, j int) { - vector[i], vector[j] = vector[j], vector[i] -} - -// TODO @gbotrel make a public package out of that. -// execute executes the work function in parallel. -// this is copy paste from internal/parallel/parallel.go -// as we don't want to generate code importing internal/ -func execute(nbIterations int, work func(int, int), maxCpus ...int) { - - nbTasks := runtime.NumCPU() - if len(maxCpus) == 1 { - nbTasks = maxCpus[0] - if nbTasks < 1 { - nbTasks = 1 - } else if nbTasks > 512 { - nbTasks = 512 - } - } - - if nbTasks == 1 { - // no go routines - work(0, nbIterations) - return - } - - nbIterationsPerCpus := nbIterations / nbTasks - - // more CPUs than tasks: a CPU will work on exactly one iteration - if nbIterationsPerCpus < 1 { - nbIterationsPerCpus = 1 - nbTasks = nbIterations - } - - var wg sync.WaitGroup - - extraTasks := nbIterations - (nbTasks * nbIterationsPerCpus) - extraTasksOffset := 0 - - for i := 0; i < nbTasks; i++ { - wg.Add(1) - _start := i*nbIterationsPerCpus + extraTasksOffset - _end := _start + nbIterationsPerCpus - if extraTasks > 0 { - _end++ - extraTasks-- - extraTasksOffset++ - } - go func() { - work(_start, _end) - wg.Done() - }() - } - - wg.Wait() -} diff --git a/ecc/bw6-756/fp/vector_test.go b/ecc/bw6-756/fp/vector_test.go deleted file mode 100644 index 5d88af91c0..0000000000 --- a/ecc/bw6-756/fp/vector_test.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fp - -import ( - "bytes" - "github.com/stretchr/testify/require" - "reflect" - "sort" - "testing" -) - -func TestVectorSort(t *testing.T) { - assert := require.New(t) - - v := make(Vector, 3) - v[0].SetUint64(2) - v[1].SetUint64(3) - v[2].SetUint64(1) - - sort.Sort(v) - - assert.Equal("[1,2,3]", v.String()) -} - -func TestVectorRoundTrip(t *testing.T) { - assert := require.New(t) - - v1 := make(Vector, 3) - v1[0].SetUint64(2) - v1[1].SetUint64(3) - v1[2].SetUint64(1) - - b, err := v1.MarshalBinary() - assert.NoError(err) - - var v2, v3 Vector - - err = v2.UnmarshalBinary(b) - assert.NoError(err) - - err = v3.unmarshalBinaryAsync(b) - assert.NoError(err) - - assert.True(reflect.DeepEqual(v1, v2)) - assert.True(reflect.DeepEqual(v3, v2)) -} - -func TestVectorEmptyRoundTrip(t *testing.T) { - assert := require.New(t) - - v1 := make(Vector, 0) - - b, err := v1.MarshalBinary() - assert.NoError(err) - - var v2, v3 Vector - - err = v2.UnmarshalBinary(b) - assert.NoError(err) - - err = v3.unmarshalBinaryAsync(b) - assert.NoError(err) - - assert.True(reflect.DeepEqual(v1, v2)) - assert.True(reflect.DeepEqual(v3, v2)) -} - -func (vector *Vector) unmarshalBinaryAsync(data []byte) error { - r := bytes.NewReader(data) - _, err, chErr := vector.AsyncReadFrom(r) - if err != nil { - return err - } - return <-chErr -} diff --git a/ecc/bw6-756/fr/arith.go b/ecc/bw6-756/fr/arith.go deleted file mode 100644 index 7cfd55da19..0000000000 --- a/ecc/bw6-756/fr/arith.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import ( - "math/bits" -) - -// madd0 hi = a*b + c (discards lo bits) -func madd0(a, b, c uint64) (hi uint64) { - var carry, lo uint64 - hi, lo = bits.Mul64(a, b) - _, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - return -} - -// madd1 hi, lo = a*b + c -func madd1(a, b, c uint64) (hi uint64, lo uint64) { - var carry uint64 - hi, lo = bits.Mul64(a, b) - lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - return -} - -// madd2 hi, lo = a*b + c + d -func madd2(a, b, c, d uint64) (hi uint64, lo uint64) { - var carry uint64 - hi, lo = bits.Mul64(a, b) - c, carry = bits.Add64(c, d, 0) - hi, _ = bits.Add64(hi, 0, carry) - lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, 0, carry) - return -} - -func madd3(a, b, c, d, e uint64) (hi uint64, lo uint64) { - var carry uint64 - hi, lo = bits.Mul64(a, b) - c, carry = bits.Add64(c, d, 0) - hi, _ = bits.Add64(hi, 0, carry) - lo, carry = bits.Add64(lo, c, 0) - hi, _ = bits.Add64(hi, e, carry) - return -} -func max(a int, b int) int { - if a > b { - return a - } - return b -} - -func min(a int, b int) int { - if a < b { - return a - } - return b -} diff --git a/ecc/bw6-756/fr/asm.go b/ecc/bw6-756/fr/asm.go deleted file mode 100644 index da061913ba..0000000000 --- a/ecc/bw6-756/fr/asm.go +++ /dev/null @@ -1,27 +0,0 @@ -//go:build !noadx -// +build !noadx - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import "golang.org/x/sys/cpu" - -var ( - supportAdx = cpu.X86.HasADX && cpu.X86.HasBMI2 - _ = supportAdx -) diff --git a/ecc/bw6-756/fr/asm_noadx.go b/ecc/bw6-756/fr/asm_noadx.go deleted file mode 100644 index 7f52ffa197..0000000000 --- a/ecc/bw6-756/fr/asm_noadx.go +++ /dev/null @@ -1,28 +0,0 @@ -//go:build noadx -// +build noadx - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -// note: this is needed for test purposes, as dynamically changing supportAdx doesn't flag -// certain errors (like fatal error: missing stackmap) -// this ensures we test all asm path. -var ( - supportAdx = false - _ = supportAdx -) diff --git a/ecc/bw6-756/fr/doc.go b/ecc/bw6-756/fr/doc.go deleted file mode 100644 index 38411d5d4b..0000000000 --- a/ecc/bw6-756/fr/doc.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package fr contains field arithmetic operations for modulus = 0x3eeb04...000001. -// -// The API is similar to math/big (big.Int), but the operations are significantly faster (up to 20x for the modular multiplication on amd64, see also https://hackmd.io/@gnark/modular_multiplication) -// -// The modulus is hardcoded in all the operations. -// -// Field elements are represented as an array, and assumed to be in Montgomery form in all methods: -// -// type Element [6]uint64 -// -// # Usage -// -// Example API signature: -// -// // Mul z = x * y (mod q) -// func (z *Element) Mul(x, y *Element) *Element -// -// and can be used like so: -// -// var a, b Element -// a.SetUint64(2) -// b.SetString("984896738") -// a.Mul(a, b) -// a.Sub(a, a) -// .Add(a, b) -// .Inv(a) -// b.Exp(b, new(big.Int).SetUint64(42)) -// -// Modulus q = -// -// q[base10] = 605248206075306171733248481581800960739847691770924913753520744034740935903401304776283802348837311170974282940417 -// q[base16] = 0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce97f76a822c00009948a20000000001 -// -// # Warning -// -// This code has not been audited and is provided as-is. In particular, there is no security guarantees such as constant time implementation or side-channel attack resistance. -package fr diff --git a/ecc/bw6-756/fr/element.go b/ecc/bw6-756/fr/element.go deleted file mode 100644 index f3fef250c1..0000000000 --- a/ecc/bw6-756/fr/element.go +++ /dev/null @@ -1,1848 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import ( - "crypto/rand" - "encoding/binary" - "errors" - "io" - "math/big" - "math/bits" - "reflect" - "strconv" - "strings" - - "github.com/bits-and-blooms/bitset" - "github.com/consensys/gnark-crypto/field/hash" - "github.com/consensys/gnark-crypto/field/pool" -) - -// Element represents a field element stored on 6 words (uint64) -// -// Element are assumed to be in Montgomery form in all methods. -// -// Modulus q = -// -// q[base10] = 605248206075306171733248481581800960739847691770924913753520744034740935903401304776283802348837311170974282940417 -// q[base16] = 0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce97f76a822c00009948a20000000001 -// -// # Warning -// -// This code has not been audited and is provided as-is. In particular, there is no security guarantees such as constant time implementation or side-channel attack resistance. -type Element [6]uint64 - -const ( - Limbs = 6 // number of 64 bits words needed to represent a Element - Bits = 378 // number of bits needed to represent a Element - Bytes = 48 // number of bytes needed to represent a Element -) - -// Field modulus q -const ( - q0 uint64 = 11045256207009841153 - q1 uint64 = 14886639130118979584 - q2 uint64 = 10956628289047010687 - q3 uint64 = 9513184293603517222 - q4 uint64 = 6038022134869067682 - q5 uint64 = 283357621510263184 -) - -var qElement = Element{ - q0, - q1, - q2, - q3, - q4, - q5, -} - -var _modulus big.Int // q stored as big.Int - -// Modulus returns q as a big.Int -// -// q[base10] = 605248206075306171733248481581800960739847691770924913753520744034740935903401304776283802348837311170974282940417 -// q[base16] = 0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce97f76a822c00009948a20000000001 -func Modulus() *big.Int { - return new(big.Int).Set(&_modulus) -} - -// q + r'.r = 1, i.e., qInvNeg = - q⁻¹ mod r -// used for Montgomery reduction -const qInvNeg uint64 = 11045256207009841151 - -func init() { - _modulus.SetString("3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce97f76a822c00009948a20000000001", 16) -} - -// NewElement returns a new Element from a uint64 value -// -// it is equivalent to -// -// var v Element -// v.SetUint64(...) -func NewElement(v uint64) Element { - z := Element{v} - z.Mul(&z, &rSquare) - return z -} - -// SetUint64 sets z to v and returns z -func (z *Element) SetUint64(v uint64) *Element { - // sets z LSB to v (non-Montgomery form) and convert z to Montgomery form - *z = Element{v} - return z.Mul(z, &rSquare) // z.toMont() -} - -// SetInt64 sets z to v and returns z -func (z *Element) SetInt64(v int64) *Element { - - // absolute value of v - m := v >> 63 - z.SetUint64(uint64((v ^ m) - m)) - - if m != 0 { - // v is negative - z.Neg(z) - } - - return z -} - -// Set z = x and returns z -func (z *Element) Set(x *Element) *Element { - z[0] = x[0] - z[1] = x[1] - z[2] = x[2] - z[3] = x[3] - z[4] = x[4] - z[5] = x[5] - return z -} - -// SetInterface converts provided interface into Element -// returns an error if provided type is not supported -// supported types: -// -// Element -// *Element -// uint64 -// int -// string (see SetString for valid formats) -// *big.Int -// big.Int -// []byte -func (z *Element) SetInterface(i1 interface{}) (*Element, error) { - if i1 == nil { - return nil, errors.New("can't set fr.Element with ") - } - - switch c1 := i1.(type) { - case Element: - return z.Set(&c1), nil - case *Element: - if c1 == nil { - return nil, errors.New("can't set fr.Element with ") - } - return z.Set(c1), nil - case uint8: - return z.SetUint64(uint64(c1)), nil - case uint16: - return z.SetUint64(uint64(c1)), nil - case uint32: - return z.SetUint64(uint64(c1)), nil - case uint: - return z.SetUint64(uint64(c1)), nil - case uint64: - return z.SetUint64(c1), nil - case int8: - return z.SetInt64(int64(c1)), nil - case int16: - return z.SetInt64(int64(c1)), nil - case int32: - return z.SetInt64(int64(c1)), nil - case int64: - return z.SetInt64(c1), nil - case int: - return z.SetInt64(int64(c1)), nil - case string: - return z.SetString(c1) - case *big.Int: - if c1 == nil { - return nil, errors.New("can't set fr.Element with ") - } - return z.SetBigInt(c1), nil - case big.Int: - return z.SetBigInt(&c1), nil - case []byte: - return z.SetBytes(c1), nil - default: - return nil, errors.New("can't set fr.Element from type " + reflect.TypeOf(i1).String()) - } -} - -// SetZero z = 0 -func (z *Element) SetZero() *Element { - z[0] = 0 - z[1] = 0 - z[2] = 0 - z[3] = 0 - z[4] = 0 - z[5] = 0 - return z -} - -// SetOne z = 1 (in Montgomery form) -func (z *Element) SetOne() *Element { - z[0] = 1481365419032838079 - z[1] = 10045892448872562649 - z[2] = 7242180086616818316 - z[3] = 8832319421896135475 - z[4] = 13356930855120736188 - z[5] = 28498675542444634 - return z -} - -// Div z = x*y⁻¹ (mod q) -func (z *Element) Div(x, y *Element) *Element { - var yInv Element - yInv.Inverse(y) - z.Mul(x, &yInv) - return z -} - -// Equal returns z == x; constant-time -func (z *Element) Equal(x *Element) bool { - return z.NotEqual(x) == 0 -} - -// NotEqual returns 0 if and only if z == x; constant-time -func (z *Element) NotEqual(x *Element) uint64 { - return (z[5] ^ x[5]) | (z[4] ^ x[4]) | (z[3] ^ x[3]) | (z[2] ^ x[2]) | (z[1] ^ x[1]) | (z[0] ^ x[0]) -} - -// IsZero returns z == 0 -func (z *Element) IsZero() bool { - return (z[5] | z[4] | z[3] | z[2] | z[1] | z[0]) == 0 -} - -// IsOne returns z == 1 -func (z *Element) IsOne() bool { - return ((z[5] ^ 28498675542444634) | (z[4] ^ 13356930855120736188) | (z[3] ^ 8832319421896135475) | (z[2] ^ 7242180086616818316) | (z[1] ^ 10045892448872562649) | (z[0] ^ 1481365419032838079)) == 0 -} - -// IsUint64 reports whether z can be represented as an uint64. -func (z *Element) IsUint64() bool { - zz := *z - zz.fromMont() - return zz.FitsOnOneWord() -} - -// Uint64 returns the uint64 representation of x. If x cannot be represented in a uint64, the result is undefined. -func (z *Element) Uint64() uint64 { - return z.Bits()[0] -} - -// FitsOnOneWord reports whether z words (except the least significant word) are 0 -// -// It is the responsibility of the caller to convert from Montgomery to Regular form if needed. -func (z *Element) FitsOnOneWord() bool { - return (z[5] | z[4] | z[3] | z[2] | z[1]) == 0 -} - -// Cmp compares (lexicographic order) z and x and returns: -// -// -1 if z < x -// 0 if z == x -// +1 if z > x -func (z *Element) Cmp(x *Element) int { - _z := z.Bits() - _x := x.Bits() - if _z[5] > _x[5] { - return 1 - } else if _z[5] < _x[5] { - return -1 - } - if _z[4] > _x[4] { - return 1 - } else if _z[4] < _x[4] { - return -1 - } - if _z[3] > _x[3] { - return 1 - } else if _z[3] < _x[3] { - return -1 - } - if _z[2] > _x[2] { - return 1 - } else if _z[2] < _x[2] { - return -1 - } - if _z[1] > _x[1] { - return 1 - } else if _z[1] < _x[1] { - return -1 - } - if _z[0] > _x[0] { - return 1 - } else if _z[0] < _x[0] { - return -1 - } - return 0 -} - -// LexicographicallyLargest returns true if this element is strictly lexicographically -// larger than its negation, false otherwise -func (z *Element) LexicographicallyLargest() bool { - // adapted from github.com/zkcrypto/bls12_381 - // we check if the element is larger than (q-1) / 2 - // if z - (((q -1) / 2) + 1) have no underflow, then z > (q-1) / 2 - - _z := z.Bits() - - var b uint64 - _, b = bits.Sub64(_z[0], 5522628103504920577, 0) - _, b = bits.Sub64(_z[1], 16666691601914265600, b) - _, b = bits.Sub64(_z[2], 5478314144523505343, b) - _, b = bits.Sub64(_z[3], 4756592146801758611, b) - _, b = bits.Sub64(_z[4], 3019011067434533841, b) - _, b = bits.Sub64(_z[5], 141678810755131592, b) - - return b == 0 -} - -// SetRandom sets z to a uniform random value in [0, q). -// -// This might error only if reading from crypto/rand.Reader errors, -// in which case, value of z is undefined. -func (z *Element) SetRandom() (*Element, error) { - // this code is generated for all modulus - // and derived from go/src/crypto/rand/util.go - - // l is number of limbs * 8; the number of bytes needed to reconstruct 6 uint64 - const l = 48 - - // bitLen is the maximum bit length needed to encode a value < q. - const bitLen = 378 - - // k is the maximum byte length needed to encode a value < q. - const k = (bitLen + 7) / 8 - - // b is the number of bits in the most significant byte of q-1. - b := uint(bitLen % 8) - if b == 0 { - b = 8 - } - - var bytes [l]byte - - for { - // note that bytes[k:l] is always 0 - if _, err := io.ReadFull(rand.Reader, bytes[:k]); err != nil { - return nil, err - } - - // Clear unused bits in in the most significant byte to increase probability - // that the candidate is < q. - bytes[k-1] &= uint8(int(1<> 1 - z[0] = z[0]>>1 | z[1]<<63 - z[1] = z[1]>>1 | z[2]<<63 - z[2] = z[2]>>1 | z[3]<<63 - z[3] = z[3]>>1 | z[4]<<63 - z[4] = z[4]>>1 | z[5]<<63 - z[5] >>= 1 - -} - -// fromMont converts z in place (i.e. mutates) from Montgomery to regular representation -// sets and returns z = z * 1 -func (z *Element) fromMont() *Element { - fromMont(z) - return z -} - -// Add z = x + y (mod q) -func (z *Element) Add(x, y *Element) *Element { - - var carry uint64 - z[0], carry = bits.Add64(x[0], y[0], 0) - z[1], carry = bits.Add64(x[1], y[1], carry) - z[2], carry = bits.Add64(x[2], y[2], carry) - z[3], carry = bits.Add64(x[3], y[3], carry) - z[4], carry = bits.Add64(x[4], y[4], carry) - z[5], _ = bits.Add64(x[5], y[5], carry) - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } - return z -} - -// Double z = x + x (mod q), aka Lsh 1 -func (z *Element) Double(x *Element) *Element { - - var carry uint64 - z[0], carry = bits.Add64(x[0], x[0], 0) - z[1], carry = bits.Add64(x[1], x[1], carry) - z[2], carry = bits.Add64(x[2], x[2], carry) - z[3], carry = bits.Add64(x[3], x[3], carry) - z[4], carry = bits.Add64(x[4], x[4], carry) - z[5], _ = bits.Add64(x[5], x[5], carry) - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } - return z -} - -// Sub z = x - y (mod q) -func (z *Element) Sub(x, y *Element) *Element { - var b uint64 - z[0], b = bits.Sub64(x[0], y[0], 0) - z[1], b = bits.Sub64(x[1], y[1], b) - z[2], b = bits.Sub64(x[2], y[2], b) - z[3], b = bits.Sub64(x[3], y[3], b) - z[4], b = bits.Sub64(x[4], y[4], b) - z[5], b = bits.Sub64(x[5], y[5], b) - if b != 0 { - var c uint64 - z[0], c = bits.Add64(z[0], q0, 0) - z[1], c = bits.Add64(z[1], q1, c) - z[2], c = bits.Add64(z[2], q2, c) - z[3], c = bits.Add64(z[3], q3, c) - z[4], c = bits.Add64(z[4], q4, c) - z[5], _ = bits.Add64(z[5], q5, c) - } - return z -} - -// Neg z = q - x -func (z *Element) Neg(x *Element) *Element { - if x.IsZero() { - z.SetZero() - return z - } - var borrow uint64 - z[0], borrow = bits.Sub64(q0, x[0], 0) - z[1], borrow = bits.Sub64(q1, x[1], borrow) - z[2], borrow = bits.Sub64(q2, x[2], borrow) - z[3], borrow = bits.Sub64(q3, x[3], borrow) - z[4], borrow = bits.Sub64(q4, x[4], borrow) - z[5], _ = bits.Sub64(q5, x[5], borrow) - return z -} - -// Select is a constant-time conditional move. -// If c=0, z = x0. Else z = x1 -func (z *Element) Select(c int, x0 *Element, x1 *Element) *Element { - cC := uint64((int64(c) | -int64(c)) >> 63) // "canonicized" into: 0 if c=0, -1 otherwise - z[0] = x0[0] ^ cC&(x0[0]^x1[0]) - z[1] = x0[1] ^ cC&(x0[1]^x1[1]) - z[2] = x0[2] ^ cC&(x0[2]^x1[2]) - z[3] = x0[3] ^ cC&(x0[3]^x1[3]) - z[4] = x0[4] ^ cC&(x0[4]^x1[4]) - z[5] = x0[5] ^ cC&(x0[5]^x1[5]) - return z -} - -// _mulGeneric is unoptimized textbook CIOS -// it is a fallback solution on x86 when ADX instruction set is not available -// and is used for testing purposes. -func _mulGeneric(z, x, y *Element) { - - // Implements CIOS multiplication -- section 2.3.2 of Tolga Acar's thesis - // https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf - // - // The algorithm: - // - // for i=0 to N-1 - // C := 0 - // for j=0 to N-1 - // (C,t[j]) := t[j] + x[j]*y[i] + C - // (t[N+1],t[N]) := t[N] + C - // - // C := 0 - // m := t[0]*q'[0] mod D - // (C,_) := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // (C,t[N-1]) := t[N] + C - // t[N] := t[N+1] + C - // - // → N is the number of machine words needed to store the modulus q - // → D is the word size. For example, on a 64-bit architecture D is 2 64 - // → x[i], y[i], q[i] is the ith word of the numbers x,y,q - // → q'[0] is the lowest word of the number -q⁻¹ mod r. This quantity is pre-computed, as it does not depend on the inputs. - // → t is a temporary array of size N+2 - // → C, S are machine words. A pair (C,S) refers to (hi-bits, lo-bits) of a two-word number - - var t [7]uint64 - var D uint64 - var m, C uint64 - // ----------------------------------- - // First loop - - C, t[0] = bits.Mul64(y[0], x[0]) - C, t[1] = madd1(y[0], x[1], C) - C, t[2] = madd1(y[0], x[2], C) - C, t[3] = madd1(y[0], x[3], C) - C, t[4] = madd1(y[0], x[4], C) - C, t[5] = madd1(y[0], x[5], C) - - t[6], D = bits.Add64(t[6], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - - t[5], C = bits.Add64(t[6], C, 0) - t[6], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[1], x[0], t[0]) - C, t[1] = madd2(y[1], x[1], t[1], C) - C, t[2] = madd2(y[1], x[2], t[2], C) - C, t[3] = madd2(y[1], x[3], t[3], C) - C, t[4] = madd2(y[1], x[4], t[4], C) - C, t[5] = madd2(y[1], x[5], t[5], C) - - t[6], D = bits.Add64(t[6], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - - t[5], C = bits.Add64(t[6], C, 0) - t[6], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[2], x[0], t[0]) - C, t[1] = madd2(y[2], x[1], t[1], C) - C, t[2] = madd2(y[2], x[2], t[2], C) - C, t[3] = madd2(y[2], x[3], t[3], C) - C, t[4] = madd2(y[2], x[4], t[4], C) - C, t[5] = madd2(y[2], x[5], t[5], C) - - t[6], D = bits.Add64(t[6], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - - t[5], C = bits.Add64(t[6], C, 0) - t[6], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[3], x[0], t[0]) - C, t[1] = madd2(y[3], x[1], t[1], C) - C, t[2] = madd2(y[3], x[2], t[2], C) - C, t[3] = madd2(y[3], x[3], t[3], C) - C, t[4] = madd2(y[3], x[4], t[4], C) - C, t[5] = madd2(y[3], x[5], t[5], C) - - t[6], D = bits.Add64(t[6], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - - t[5], C = bits.Add64(t[6], C, 0) - t[6], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[4], x[0], t[0]) - C, t[1] = madd2(y[4], x[1], t[1], C) - C, t[2] = madd2(y[4], x[2], t[2], C) - C, t[3] = madd2(y[4], x[3], t[3], C) - C, t[4] = madd2(y[4], x[4], t[4], C) - C, t[5] = madd2(y[4], x[5], t[5], C) - - t[6], D = bits.Add64(t[6], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - - t[5], C = bits.Add64(t[6], C, 0) - t[6], _ = bits.Add64(0, D, C) - // ----------------------------------- - // First loop - - C, t[0] = madd1(y[5], x[0], t[0]) - C, t[1] = madd2(y[5], x[1], t[1], C) - C, t[2] = madd2(y[5], x[2], t[2], C) - C, t[3] = madd2(y[5], x[3], t[3], C) - C, t[4] = madd2(y[5], x[4], t[4], C) - C, t[5] = madd2(y[5], x[5], t[5], C) - - t[6], D = bits.Add64(t[6], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - C, t[0] = madd2(m, q1, t[1], C) - C, t[1] = madd2(m, q2, t[2], C) - C, t[2] = madd2(m, q3, t[3], C) - C, t[3] = madd2(m, q4, t[4], C) - C, t[4] = madd2(m, q5, t[5], C) - - t[5], C = bits.Add64(t[6], C, 0) - t[6], _ = bits.Add64(0, D, C) - - if t[6] != 0 { - // we need to reduce, we have a result on 7 words - var b uint64 - z[0], b = bits.Sub64(t[0], q0, 0) - z[1], b = bits.Sub64(t[1], q1, b) - z[2], b = bits.Sub64(t[2], q2, b) - z[3], b = bits.Sub64(t[3], q3, b) - z[4], b = bits.Sub64(t[4], q4, b) - z[5], _ = bits.Sub64(t[5], q5, b) - return - } - - // copy t into z - z[0] = t[0] - z[1] = t[1] - z[2] = t[2] - z[3] = t[3] - z[4] = t[4] - z[5] = t[5] - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } -} - -func _fromMontGeneric(z *Element) { - // the following lines implement z = z * 1 - // with a modified CIOS montgomery multiplication - // see Mul for algorithm documentation - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - z[5] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - z[5] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - z[5] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - z[5] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - z[5] = C - } - { - // m = z[0]n'[0] mod W - m := z[0] * qInvNeg - C := madd0(m, q0, z[0]) - C, z[0] = madd2(m, q1, z[1], C) - C, z[1] = madd2(m, q2, z[2], C) - C, z[2] = madd2(m, q3, z[3], C) - C, z[3] = madd2(m, q4, z[4], C) - C, z[4] = madd2(m, q5, z[5], C) - z[5] = C - } - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } -} - -func _reduceGeneric(z *Element) { - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } -} - -// BatchInvert returns a new slice with every element inverted. -// Uses Montgomery batch inversion trick -func BatchInvert(a []Element) []Element { - res := make([]Element, len(a)) - if len(a) == 0 { - return res - } - - zeroes := bitset.New(uint(len(a))) - accumulator := One() - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - zeroes.Set(uint(i)) - continue - } - res[i] = accumulator - accumulator.Mul(&accumulator, &a[i]) - } - - accumulator.Inverse(&accumulator) - - for i := len(a) - 1; i >= 0; i-- { - if zeroes.Test(uint(i)) { - continue - } - res[i].Mul(&res[i], &accumulator) - accumulator.Mul(&accumulator, &a[i]) - } - - return res -} - -func _butterflyGeneric(a, b *Element) { - t := *a - a.Add(a, b) - b.Sub(&t, b) -} - -// BitLen returns the minimum number of bits needed to represent z -// returns 0 if z == 0 -func (z *Element) BitLen() int { - if z[5] != 0 { - return 320 + bits.Len64(z[5]) - } - if z[4] != 0 { - return 256 + bits.Len64(z[4]) - } - if z[3] != 0 { - return 192 + bits.Len64(z[3]) - } - if z[2] != 0 { - return 128 + bits.Len64(z[2]) - } - if z[1] != 0 { - return 64 + bits.Len64(z[1]) - } - return bits.Len64(z[0]) -} - -// Hash msg to count prime field elements. -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-5.2 -func Hash(msg, dst []byte, count int) ([]Element, error) { - // 128 bits of security - // L = ceil((ceil(log2(p)) + k) / 8), where k is the security parameter = 128 - const Bytes = 1 + (Bits-1)/8 - const L = 16 + Bytes - - lenInBytes := count * L - pseudoRandomBytes, err := hash.ExpandMsgXmd(msg, dst, lenInBytes) - if err != nil { - return nil, err - } - - // get temporary big int from the pool - vv := pool.BigInt.Get() - - res := make([]Element, count) - for i := 0; i < count; i++ { - vv.SetBytes(pseudoRandomBytes[i*L : (i+1)*L]) - res[i].SetBigInt(vv) - } - - // release object into pool - pool.BigInt.Put(vv) - - return res, nil -} - -// Exp z = xᵏ (mod q) -func (z *Element) Exp(x Element, k *big.Int) *Element { - if k.IsUint64() && k.Uint64() == 0 { - return z.SetOne() - } - - e := k - if k.Sign() == -1 { - // negative k, we invert - // if k < 0: xᵏ (mod q) == (x⁻¹)ᵏ (mod q) - x.Inverse(&x) - - // we negate k in a temp big.Int since - // Int.Bit(_) of k and -k is different - e = pool.BigInt.Get() - defer pool.BigInt.Put(e) - e.Neg(k) - } - - z.Set(&x) - - for i := e.BitLen() - 2; i >= 0; i-- { - z.Square(z) - if e.Bit(i) == 1 { - z.Mul(z, &x) - } - } - - return z -} - -// rSquare where r is the Montgommery constant -// see section 2.3.2 of Tolga Acar's thesis -// https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf -var rSquare = Element{ - 13541478318970833666, - 5510290684934426267, - 8467587974331926354, - 13931463632695577534, - 3531303697457869800, - 51529254522778566, -} - -// toMont converts z to Montgomery form -// sets and returns z = z * r² -func (z *Element) toMont() *Element { - return z.Mul(z, &rSquare) -} - -// String returns the decimal representation of z as generated by -// z.Text(10). -func (z *Element) String() string { - return z.Text(10) -} - -// toBigInt returns z as a big.Int in Montgomery form -func (z *Element) toBigInt(res *big.Int) *big.Int { - var b [Bytes]byte - binary.BigEndian.PutUint64(b[40:48], z[0]) - binary.BigEndian.PutUint64(b[32:40], z[1]) - binary.BigEndian.PutUint64(b[24:32], z[2]) - binary.BigEndian.PutUint64(b[16:24], z[3]) - binary.BigEndian.PutUint64(b[8:16], z[4]) - binary.BigEndian.PutUint64(b[0:8], z[5]) - - return res.SetBytes(b[:]) -} - -// Text returns the string representation of z in the given base. -// Base must be between 2 and 36, inclusive. The result uses the -// lower-case letters 'a' to 'z' for digit values 10 to 35. -// No prefix (such as "0x") is added to the string. If z is a nil -// pointer it returns "". -// If base == 10 and -z fits in a uint16 prefix "-" is added to the string. -func (z *Element) Text(base int) string { - if base < 2 || base > 36 { - panic("invalid base") - } - if z == nil { - return "" - } - - const maxUint16 = 65535 - if base == 10 { - var zzNeg Element - zzNeg.Neg(z) - zzNeg.fromMont() - if zzNeg.FitsOnOneWord() && zzNeg[0] <= maxUint16 && zzNeg[0] != 0 { - return "-" + strconv.FormatUint(zzNeg[0], base) - } - } - zz := *z - zz.fromMont() - if zz.FitsOnOneWord() { - return strconv.FormatUint(zz[0], base) - } - vv := pool.BigInt.Get() - r := zz.toBigInt(vv).Text(base) - pool.BigInt.Put(vv) - return r -} - -// BigInt sets and return z as a *big.Int -func (z *Element) BigInt(res *big.Int) *big.Int { - _z := *z - _z.fromMont() - return _z.toBigInt(res) -} - -// ToBigIntRegular returns z as a big.Int in regular form -// -// Deprecated: use BigInt(*big.Int) instead -func (z Element) ToBigIntRegular(res *big.Int) *big.Int { - z.fromMont() - return z.toBigInt(res) -} - -// Bits provides access to z by returning its value as a little-endian [6]uint64 array. -// Bits is intended to support implementation of missing low-level Element -// functionality outside this package; it should be avoided otherwise. -func (z *Element) Bits() [6]uint64 { - _z := *z - fromMont(&_z) - return _z -} - -// Bytes returns the value of z as a big-endian byte array -func (z *Element) Bytes() (res [Bytes]byte) { - BigEndian.PutElement(&res, *z) - return -} - -// Marshal returns the value of z as a big-endian byte slice -func (z *Element) Marshal() []byte { - b := z.Bytes() - return b[:] -} - -// Unmarshal is an alias for SetBytes, it sets z to the value of e. -func (z *Element) Unmarshal(e []byte) { - z.SetBytes(e) -} - -// SetBytes interprets e as the bytes of a big-endian unsigned integer, -// sets z to that value, and returns z. -func (z *Element) SetBytes(e []byte) *Element { - if len(e) == Bytes { - // fast path - v, err := BigEndian.Element((*[Bytes]byte)(e)) - if err == nil { - *z = v - return z - } - } - - // slow path. - // get a big int from our pool - vv := pool.BigInt.Get() - vv.SetBytes(e) - - // set big int - z.SetBigInt(vv) - - // put temporary object back in pool - pool.BigInt.Put(vv) - - return z -} - -// SetBytesCanonical interprets e as the bytes of a big-endian 48-byte integer. -// If e is not a 48-byte slice or encodes a value higher than q, -// SetBytesCanonical returns an error. -func (z *Element) SetBytesCanonical(e []byte) error { - if len(e) != Bytes { - return errors.New("invalid fr.Element encoding") - } - v, err := BigEndian.Element((*[Bytes]byte)(e)) - if err != nil { - return err - } - *z = v - return nil -} - -// SetBigInt sets z to v and returns z -func (z *Element) SetBigInt(v *big.Int) *Element { - z.SetZero() - - var zero big.Int - - // fast path - c := v.Cmp(&_modulus) - if c == 0 { - // v == 0 - return z - } else if c != 1 && v.Cmp(&zero) != -1 { - // 0 < v < q - return z.setBigInt(v) - } - - // get temporary big int from the pool - vv := pool.BigInt.Get() - - // copy input + modular reduction - vv.Mod(v, &_modulus) - - // set big int byte value - z.setBigInt(vv) - - // release object into pool - pool.BigInt.Put(vv) - return z -} - -// setBigInt assumes 0 ⩽ v < q -func (z *Element) setBigInt(v *big.Int) *Element { - vBits := v.Bits() - - if bits.UintSize == 64 { - for i := 0; i < len(vBits); i++ { - z[i] = uint64(vBits[i]) - } - } else { - for i := 0; i < len(vBits); i++ { - if i%2 == 0 { - z[i/2] = uint64(vBits[i]) - } else { - z[i/2] |= uint64(vBits[i]) << 32 - } - } - } - - return z.toMont() -} - -// SetString creates a big.Int with number and calls SetBigInt on z -// -// The number prefix determines the actual base: A prefix of -// ”0b” or ”0B” selects base 2, ”0”, ”0o” or ”0O” selects base 8, -// and ”0x” or ”0X” selects base 16. Otherwise, the selected base is 10 -// and no prefix is accepted. -// -// For base 16, lower and upper case letters are considered the same: -// The letters 'a' to 'f' and 'A' to 'F' represent digit values 10 to 15. -// -// An underscore character ”_” may appear between a base -// prefix and an adjacent digit, and between successive digits; such -// underscores do not change the value of the number. -// Incorrect placement of underscores is reported as a panic if there -// are no other errors. -// -// If the number is invalid this method leaves z unchanged and returns nil, error. -func (z *Element) SetString(number string) (*Element, error) { - // get temporary big int from the pool - vv := pool.BigInt.Get() - - if _, ok := vv.SetString(number, 0); !ok { - return nil, errors.New("Element.SetString failed -> can't parse number into a big.Int " + number) - } - - z.SetBigInt(vv) - - // release object into pool - pool.BigInt.Put(vv) - - return z, nil -} - -// MarshalJSON returns json encoding of z (z.Text(10)) -// If z == nil, returns null -func (z *Element) MarshalJSON() ([]byte, error) { - if z == nil { - return []byte("null"), nil - } - const maxSafeBound = 15 // we encode it as number if it's small - s := z.Text(10) - if len(s) <= maxSafeBound { - return []byte(s), nil - } - var sbb strings.Builder - sbb.WriteByte('"') - sbb.WriteString(s) - sbb.WriteByte('"') - return []byte(sbb.String()), nil -} - -// UnmarshalJSON accepts numbers and strings as input -// See Element.SetString for valid prefixes (0x, 0b, ...) -func (z *Element) UnmarshalJSON(data []byte) error { - s := string(data) - if len(s) > Bits*3 { - return errors.New("value too large (max = Element.Bits * 3)") - } - - // we accept numbers and strings, remove leading and trailing quotes if any - if len(s) > 0 && s[0] == '"' { - s = s[1:] - } - if len(s) > 0 && s[len(s)-1] == '"' { - s = s[:len(s)-1] - } - - // get temporary big int from the pool - vv := pool.BigInt.Get() - - if _, ok := vv.SetString(s, 0); !ok { - return errors.New("can't parse into a big.Int: " + s) - } - - z.SetBigInt(vv) - - // release object into pool - pool.BigInt.Put(vv) - return nil -} - -// A ByteOrder specifies how to convert byte slices into a Element -type ByteOrder interface { - Element(*[Bytes]byte) (Element, error) - PutElement(*[Bytes]byte, Element) - String() string -} - -// BigEndian is the big-endian implementation of ByteOrder and AppendByteOrder. -var BigEndian bigEndian - -type bigEndian struct{} - -// Element interpret b is a big-endian 48-byte slice. -// If b encodes a value higher than q, Element returns error. -func (bigEndian) Element(b *[Bytes]byte) (Element, error) { - var z Element - z[0] = binary.BigEndian.Uint64((*b)[40:48]) - z[1] = binary.BigEndian.Uint64((*b)[32:40]) - z[2] = binary.BigEndian.Uint64((*b)[24:32]) - z[3] = binary.BigEndian.Uint64((*b)[16:24]) - z[4] = binary.BigEndian.Uint64((*b)[8:16]) - z[5] = binary.BigEndian.Uint64((*b)[0:8]) - - if !z.smallerThanModulus() { - return Element{}, errors.New("invalid fr.Element encoding") - } - - z.toMont() - return z, nil -} - -func (bigEndian) PutElement(b *[Bytes]byte, e Element) { - e.fromMont() - binary.BigEndian.PutUint64((*b)[40:48], e[0]) - binary.BigEndian.PutUint64((*b)[32:40], e[1]) - binary.BigEndian.PutUint64((*b)[24:32], e[2]) - binary.BigEndian.PutUint64((*b)[16:24], e[3]) - binary.BigEndian.PutUint64((*b)[8:16], e[4]) - binary.BigEndian.PutUint64((*b)[0:8], e[5]) -} - -func (bigEndian) String() string { return "BigEndian" } - -// LittleEndian is the little-endian implementation of ByteOrder and AppendByteOrder. -var LittleEndian littleEndian - -type littleEndian struct{} - -func (littleEndian) Element(b *[Bytes]byte) (Element, error) { - var z Element - z[0] = binary.LittleEndian.Uint64((*b)[0:8]) - z[1] = binary.LittleEndian.Uint64((*b)[8:16]) - z[2] = binary.LittleEndian.Uint64((*b)[16:24]) - z[3] = binary.LittleEndian.Uint64((*b)[24:32]) - z[4] = binary.LittleEndian.Uint64((*b)[32:40]) - z[5] = binary.LittleEndian.Uint64((*b)[40:48]) - - if !z.smallerThanModulus() { - return Element{}, errors.New("invalid fr.Element encoding") - } - - z.toMont() - return z, nil -} - -func (littleEndian) PutElement(b *[Bytes]byte, e Element) { - e.fromMont() - binary.LittleEndian.PutUint64((*b)[0:8], e[0]) - binary.LittleEndian.PutUint64((*b)[8:16], e[1]) - binary.LittleEndian.PutUint64((*b)[16:24], e[2]) - binary.LittleEndian.PutUint64((*b)[24:32], e[3]) - binary.LittleEndian.PutUint64((*b)[32:40], e[4]) - binary.LittleEndian.PutUint64((*b)[40:48], e[5]) -} - -func (littleEndian) String() string { return "LittleEndian" } - -// Legendre returns the Legendre symbol of z (either +1, -1, or 0.) -func (z *Element) Legendre() int { - var l Element - // z^((q-1)/2) - l.expByLegendreExp(*z) - - if l.IsZero() { - return 0 - } - - // if l == 1 - if l.IsOne() { - return 1 - } - return -1 -} - -// Sqrt z = √x (mod q) -// if the square root doesn't exist (x is not a square mod q) -// Sqrt leaves z unchanged and returns nil -func (z *Element) Sqrt(x *Element) *Element { - // q ≡ 1 (mod 4) - // see modSqrtTonelliShanks in math/big/int.go - // using https://www.maa.org/sites/default/files/pdf/upload_library/22/Polya/07468342.di020786.02p0470a.pdf - - var y, b, t, w Element - // w = x^((s-1)/2)) - w.expBySqrtExp(*x) - - // y = x^((s+1)/2)) = w * x - y.Mul(x, &w) - - // b = xˢ = w * w * x = y * x - b.Mul(&w, &y) - - // g = nonResidue ^ s - var g = Element{ - 15655215628902554004, - 15894127656167592378, - 9702012166408397168, - 12335982559306940759, - 1313802173610541430, - 81629743607937133, - } - r := uint64(41) - - // compute legendre symbol - // t = x^((q-1)/2) = r-1 squaring of xˢ - t = b - for i := uint64(0); i < r-1; i++ { - t.Square(&t) - } - if t.IsZero() { - return z.SetZero() - } - if !t.IsOne() { - // t != 1, we don't have a square root - return nil - } - for { - var m uint64 - t = b - - // for t != 1 - for !t.IsOne() { - t.Square(&t) - m++ - } - - if m == 0 { - return z.Set(&y) - } - // t = g^(2^(r-m-1)) (mod q) - ge := int(r - m - 1) - t = g - for ge > 0 { - t.Square(&t) - ge-- - } - - g.Square(&t) - y.Mul(&y, &t) - b.Mul(&b, &g) - r = m - } -} - -const ( - k = 32 // word size / 2 - signBitSelector = uint64(1) << 63 - approxLowBitsN = k - 1 - approxHighBitsN = k + 1 -) - -const ( - inversionCorrectionFactorWord0 = 851295657643717122 - inversionCorrectionFactorWord1 = 10857859049187504913 - inversionCorrectionFactorWord2 = 7148604188520083019 - inversionCorrectionFactorWord3 = 1138623559447261654 - inversionCorrectionFactorWord4 = 1203095380280779597 - inversionCorrectionFactorWord5 = 148579538565968037 - invIterationsN = 26 -) - -// Inverse z = x⁻¹ (mod q) -// -// if x == 0, sets and returns z = x -func (z *Element) Inverse(x *Element) *Element { - // Implements "Optimized Binary GCD for Modular Inversion" - // https://github.com/pornin/bingcd/blob/main/doc/bingcd.pdf - - a := *x - b := Element{ - q0, - q1, - q2, - q3, - q4, - q5, - } // b := q - - u := Element{1} - - // Update factors: we get [u; v] ← [f₀ g₀; f₁ g₁] [u; v] - // cᵢ = fᵢ + 2³¹ - 1 + 2³² * (gᵢ + 2³¹ - 1) - var c0, c1 int64 - - // Saved update factors to reduce the number of field multiplications - var pf0, pf1, pg0, pg1 int64 - - var i uint - - var v, s Element - - // Since u,v are updated every other iteration, we must make sure we terminate after evenly many iterations - // This also lets us get away with half as many updates to u,v - // To make this constant-time-ish, replace the condition with i < invIterationsN - for i = 0; i&1 == 1 || !a.IsZero(); i++ { - n := max(a.BitLen(), b.BitLen()) - aApprox, bApprox := approximate(&a, n), approximate(&b, n) - - // f₀, g₀, f₁, g₁ = 1, 0, 0, 1 - c0, c1 = updateFactorIdentityMatrixRow0, updateFactorIdentityMatrixRow1 - - for j := 0; j < approxLowBitsN; j++ { - - // -2ʲ < f₀, f₁ ≤ 2ʲ - // |f₀| + |f₁| < 2ʲ⁺¹ - - if aApprox&1 == 0 { - aApprox /= 2 - } else { - s, borrow := bits.Sub64(aApprox, bApprox, 0) - if borrow == 1 { - s = bApprox - aApprox - bApprox = aApprox - c0, c1 = c1, c0 - // invariants unchanged - } - - aApprox = s / 2 - c0 = c0 - c1 - - // Now |f₀| < 2ʲ⁺¹ ≤ 2ʲ⁺¹ (only the weaker inequality is needed, strictly speaking) - // Started with f₀ > -2ʲ and f₁ ≤ 2ʲ, so f₀ - f₁ > -2ʲ⁺¹ - // Invariants unchanged for f₁ - } - - c1 *= 2 - // -2ʲ⁺¹ < f₁ ≤ 2ʲ⁺¹ - // So now |f₀| + |f₁| < 2ʲ⁺² - } - - s = a - - var g0 int64 - // from this point on c0 aliases for f0 - c0, g0 = updateFactorsDecompose(c0) - aHi := a.linearCombNonModular(&s, c0, &b, g0) - if aHi&signBitSelector != 0 { - // if aHi < 0 - c0, g0 = -c0, -g0 - aHi = negL(&a, aHi) - } - // right-shift a by k-1 bits - a[0] = (a[0] >> approxLowBitsN) | ((a[1]) << approxHighBitsN) - a[1] = (a[1] >> approxLowBitsN) | ((a[2]) << approxHighBitsN) - a[2] = (a[2] >> approxLowBitsN) | ((a[3]) << approxHighBitsN) - a[3] = (a[3] >> approxLowBitsN) | ((a[4]) << approxHighBitsN) - a[4] = (a[4] >> approxLowBitsN) | ((a[5]) << approxHighBitsN) - a[5] = (a[5] >> approxLowBitsN) | (aHi << approxHighBitsN) - - var f1 int64 - // from this point on c1 aliases for g0 - f1, c1 = updateFactorsDecompose(c1) - bHi := b.linearCombNonModular(&s, f1, &b, c1) - if bHi&signBitSelector != 0 { - // if bHi < 0 - f1, c1 = -f1, -c1 - bHi = negL(&b, bHi) - } - // right-shift b by k-1 bits - b[0] = (b[0] >> approxLowBitsN) | ((b[1]) << approxHighBitsN) - b[1] = (b[1] >> approxLowBitsN) | ((b[2]) << approxHighBitsN) - b[2] = (b[2] >> approxLowBitsN) | ((b[3]) << approxHighBitsN) - b[3] = (b[3] >> approxLowBitsN) | ((b[4]) << approxHighBitsN) - b[4] = (b[4] >> approxLowBitsN) | ((b[5]) << approxHighBitsN) - b[5] = (b[5] >> approxLowBitsN) | (bHi << approxHighBitsN) - - if i&1 == 1 { - // Combine current update factors with previously stored ones - // [F₀, G₀; F₁, G₁] ← [f₀, g₀; f₁, g₁] [pf₀, pg₀; pf₁, pg₁], with capital letters denoting new combined values - // We get |F₀| = | f₀pf₀ + g₀pf₁ | ≤ |f₀pf₀| + |g₀pf₁| = |f₀| |pf₀| + |g₀| |pf₁| ≤ 2ᵏ⁻¹|pf₀| + 2ᵏ⁻¹|pf₁| - // = 2ᵏ⁻¹ (|pf₀| + |pf₁|) < 2ᵏ⁻¹ 2ᵏ = 2²ᵏ⁻¹ - // So |F₀| < 2²ᵏ⁻¹ meaning it fits in a 2k-bit signed register - - // c₀ aliases f₀, c₁ aliases g₁ - c0, g0, f1, c1 = c0*pf0+g0*pf1, - c0*pg0+g0*pg1, - f1*pf0+c1*pf1, - f1*pg0+c1*pg1 - - s = u - - // 0 ≤ u, v < 2²⁵⁵ - // |F₀|, |G₀| < 2⁶³ - u.linearComb(&u, c0, &v, g0) - // |F₁|, |G₁| < 2⁶³ - v.linearComb(&s, f1, &v, c1) - - } else { - // Save update factors - pf0, pg0, pf1, pg1 = c0, g0, f1, c1 - } - } - - // For every iteration that we miss, v is not being multiplied by 2ᵏ⁻² - const pSq uint64 = 1 << (2 * (k - 1)) - a = Element{pSq} - // If the function is constant-time ish, this loop will not run (no need to take it out explicitly) - for ; i < invIterationsN; i += 2 { - // could optimize further with mul by word routine or by pre-computing a table since with k=26, - // we would multiply by pSq up to 13times; - // on x86, the assembly routine outperforms generic code for mul by word - // on arm64, we may loose up to ~5% for 6 limbs - v.Mul(&v, &a) - } - - u.Set(x) // for correctness check - - z.Mul(&v, &Element{ - inversionCorrectionFactorWord0, - inversionCorrectionFactorWord1, - inversionCorrectionFactorWord2, - inversionCorrectionFactorWord3, - inversionCorrectionFactorWord4, - inversionCorrectionFactorWord5, - }) - - // correctness check - v.Mul(&u, z) - if !v.IsOne() && !u.IsZero() { - return z.inverseExp(u) - } - - return z -} - -// inverseExp computes z = x⁻¹ (mod q) = x**(q-2) (mod q) -func (z *Element) inverseExp(x Element) *Element { - // e == q-2 - e := Modulus() - e.Sub(e, big.NewInt(2)) - - z.Set(&x) - - for i := e.BitLen() - 2; i >= 0; i-- { - z.Square(z) - if e.Bit(i) == 1 { - z.Mul(z, &x) - } - } - - return z -} - -// approximate a big number x into a single 64 bit word using its uppermost and lowermost bits -// if x fits in a word as is, no approximation necessary -func approximate(x *Element, nBits int) uint64 { - - if nBits <= 64 { - return x[0] - } - - const mask = (uint64(1) << (k - 1)) - 1 // k-1 ones - lo := mask & x[0] - - hiWordIndex := (nBits - 1) / 64 - - hiWordBitsAvailable := nBits - hiWordIndex*64 - hiWordBitsUsed := min(hiWordBitsAvailable, approxHighBitsN) - - mask_ := uint64(^((1 << (hiWordBitsAvailable - hiWordBitsUsed)) - 1)) - hi := (x[hiWordIndex] & mask_) << (64 - hiWordBitsAvailable) - - mask_ = ^(1<<(approxLowBitsN+hiWordBitsUsed) - 1) - mid := (mask_ & x[hiWordIndex-1]) >> hiWordBitsUsed - - return lo | mid | hi -} - -// linearComb z = xC * x + yC * y; -// 0 ≤ x, y < 2³⁷⁸ -// |xC|, |yC| < 2⁶³ -func (z *Element) linearComb(x *Element, xC int64, y *Element, yC int64) { - // | (hi, z) | < 2 * 2⁶³ * 2³⁷⁸ = 2⁴⁴² - // therefore | hi | < 2⁵⁸ ≤ 2⁶³ - hi := z.linearCombNonModular(x, xC, y, yC) - z.montReduceSigned(z, hi) -} - -// montReduceSigned z = (xHi * r + x) * r⁻¹ using the SOS algorithm -// Requires |xHi| < 2⁶³. Most significant bit of xHi is the sign bit. -func (z *Element) montReduceSigned(x *Element, xHi uint64) { - const signBitRemover = ^signBitSelector - mustNeg := xHi&signBitSelector != 0 - // the SOS implementation requires that most significant bit is 0 - // Let X be xHi*r + x - // If X is negative we would have initially stored it as 2⁶⁴ r + X (à la 2's complement) - xHi &= signBitRemover - // with this a negative X is now represented as 2⁶³ r + X - - var t [2*Limbs - 1]uint64 - var C uint64 - - m := x[0] * qInvNeg - - C = madd0(m, q0, x[0]) - C, t[1] = madd2(m, q1, x[1], C) - C, t[2] = madd2(m, q2, x[2], C) - C, t[3] = madd2(m, q3, x[3], C) - C, t[4] = madd2(m, q4, x[4], C) - C, t[5] = madd2(m, q5, x[5], C) - - // m * qElement[5] ≤ (2⁶⁴ - 1) * (2⁶³ - 1) = 2¹²⁷ - 2⁶⁴ - 2⁶³ + 1 - // x[5] + C ≤ 2*(2⁶⁴ - 1) = 2⁶⁵ - 2 - // On LHS, (C, t[5]) ≤ 2¹²⁷ - 2⁶⁴ - 2⁶³ + 1 + 2⁶⁵ - 2 = 2¹²⁷ + 2⁶³ - 1 - // So on LHS, C ≤ 2⁶³ - t[6] = xHi + C - // xHi + C < 2⁶³ + 2⁶³ = 2⁶⁴ - - // - { - const i = 1 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - - t[i+Limbs] += C - } - { - const i = 2 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - - t[i+Limbs] += C - } - { - const i = 3 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - - t[i+Limbs] += C - } - { - const i = 4 - m = t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, t[i+1] = madd2(m, q1, t[i+1], C) - C, t[i+2] = madd2(m, q2, t[i+2], C) - C, t[i+3] = madd2(m, q3, t[i+3], C) - C, t[i+4] = madd2(m, q4, t[i+4], C) - C, t[i+5] = madd2(m, q5, t[i+5], C) - - t[i+Limbs] += C - } - { - const i = 5 - m := t[i] * qInvNeg - - C = madd0(m, q0, t[i+0]) - C, z[0] = madd2(m, q1, t[i+1], C) - C, z[1] = madd2(m, q2, t[i+2], C) - C, z[2] = madd2(m, q3, t[i+3], C) - C, z[3] = madd2(m, q4, t[i+4], C) - z[5], z[4] = madd2(m, q5, t[i+5], C) - } - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } - // - - if mustNeg { - // We have computed ( 2⁶³ r + X ) r⁻¹ = 2⁶³ + X r⁻¹ instead - var b uint64 - z[0], b = bits.Sub64(z[0], signBitSelector, 0) - z[1], b = bits.Sub64(z[1], 0, b) - z[2], b = bits.Sub64(z[2], 0, b) - z[3], b = bits.Sub64(z[3], 0, b) - z[4], b = bits.Sub64(z[4], 0, b) - z[5], b = bits.Sub64(z[5], 0, b) - - // Occurs iff x == 0 && xHi < 0, i.e. X = rX' for -2⁶³ ≤ X' < 0 - - if b != 0 { - // z[5] = -1 - // negative: add q - const neg1 = 0xFFFFFFFFFFFFFFFF - - var carry uint64 - - z[0], carry = bits.Add64(z[0], q0, 0) - z[1], carry = bits.Add64(z[1], q1, carry) - z[2], carry = bits.Add64(z[2], q2, carry) - z[3], carry = bits.Add64(z[3], q3, carry) - z[4], carry = bits.Add64(z[4], q4, carry) - z[5], _ = bits.Add64(neg1, q5, carry) - } - } -} - -const ( - updateFactorsConversionBias int64 = 0x7fffffff7fffffff // (2³¹ - 1)(2³² + 1) - updateFactorIdentityMatrixRow0 = 1 - updateFactorIdentityMatrixRow1 = 1 << 32 -) - -func updateFactorsDecompose(c int64) (int64, int64) { - c += updateFactorsConversionBias - const low32BitsFilter int64 = 0xFFFFFFFF - f := c&low32BitsFilter - 0x7FFFFFFF - g := c>>32&low32BitsFilter - 0x7FFFFFFF - return f, g -} - -// negL negates in place [x | xHi] and return the new most significant word xHi -func negL(x *Element, xHi uint64) uint64 { - var b uint64 - - x[0], b = bits.Sub64(0, x[0], 0) - x[1], b = bits.Sub64(0, x[1], b) - x[2], b = bits.Sub64(0, x[2], b) - x[3], b = bits.Sub64(0, x[3], b) - x[4], b = bits.Sub64(0, x[4], b) - x[5], b = bits.Sub64(0, x[5], b) - xHi, _ = bits.Sub64(0, xHi, b) - - return xHi -} - -// mulWNonModular multiplies by one word in non-montgomery, without reducing -func (z *Element) mulWNonModular(x *Element, y int64) uint64 { - - // w := abs(y) - m := y >> 63 - w := uint64((y ^ m) - m) - - var c uint64 - c, z[0] = bits.Mul64(x[0], w) - c, z[1] = madd1(x[1], w, c) - c, z[2] = madd1(x[2], w, c) - c, z[3] = madd1(x[3], w, c) - c, z[4] = madd1(x[4], w, c) - c, z[5] = madd1(x[5], w, c) - - if y < 0 { - c = negL(z, c) - } - - return c -} - -// linearCombNonModular computes a linear combination without modular reduction -func (z *Element) linearCombNonModular(x *Element, xC int64, y *Element, yC int64) uint64 { - var yTimes Element - - yHi := yTimes.mulWNonModular(y, yC) - xHi := z.mulWNonModular(x, xC) - - var carry uint64 - z[0], carry = bits.Add64(z[0], yTimes[0], 0) - z[1], carry = bits.Add64(z[1], yTimes[1], carry) - z[2], carry = bits.Add64(z[2], yTimes[2], carry) - z[3], carry = bits.Add64(z[3], yTimes[3], carry) - z[4], carry = bits.Add64(z[4], yTimes[4], carry) - z[5], carry = bits.Add64(z[5], yTimes[5], carry) - - yHi, _ = bits.Add64(xHi, yHi, carry) - - return yHi -} diff --git a/ecc/bw6-756/fr/element_exp.go b/ecc/bw6-756/fr/element_exp.go deleted file mode 100644 index 7ac8671aa2..0000000000 --- a/ecc/bw6-756/fr/element_exp.go +++ /dev/null @@ -1,1040 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -// expBySqrtExp is equivalent to z.Exp(x, fbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff3a5fddaa08b0000265228) -// -// uses github.com/mmcloughlin/addchain v0.4.0 to generate a shorter addition chain -func (z *Element) expBySqrtExp(x Element) *Element { - // addition chain: - // - // _10 = 2*1 - // _11 = 1 + _10 - // _101 = _10 + _11 - // _110 = 1 + _101 - // _1001 = _11 + _110 - // _1011 = _10 + _1001 - // _1100 = 1 + _1011 - // _1101 = 1 + _1100 - // _10001 = _101 + _1100 - // _10011 = _10 + _10001 - // _10101 = _10 + _10011 - // _11011 = _110 + _10101 - // _11101 = _10 + _11011 - // _100011 = _110 + _11101 - // _100111 = _1100 + _11011 - // _101001 = _10 + _100111 - // _110101 = _1100 + _101001 - // _110111 = _10 + _110101 - // _111001 = _10 + _110111 - // _111011 = _10 + _111001 - // _111101 = _10 + _111011 - // _111111 = _10 + _111101 - // _1111100 = _111101 + _111111 - // _1111111 = _11 + _1111100 - // i39 = ((_1111100 << 4 + _11101) << 3 + _11) << 6 - // i57 = ((1 + i39) << 9 + _1011) << 6 + _1101 - // i78 = ((i57 << 9 + _10011) << 7 + _100011) << 3 - // i98 = ((1 + i78) << 11 + _101001) << 6 + _111001 - // i117 = ((i98 << 7 + _110101) << 4 + _1101) << 6 - // i138 = ((_1001 + i117) << 12 + _111011) << 6 + _10001 - // i162 = ((i138 << 11 + _111101) << 6 + _101) << 5 - // i184 = ((1 + i162) << 11 + _1011) << 8 + _111101 - // i205 = ((i184 << 6 + _11011) << 8 + _100011) << 5 - // i227 = ((_10001 + i205) << 12 + _100011) << 7 + _10011 - // i257 = ((i227 << 6 + _10011) << 13 + _110111) << 9 - // i279 = ((_11011 + i257) << 9 + _1101) << 10 + _101001 - // i299 = ((i279 << 8 + _100111) << 2 + 1) << 8 - // i311 = ((_1111111 + i299) << 2 + _11) << 7 + _11101 - // i331 = ((i311 << 3 + 1) << 8 + _1111111) << 7 - // i350 = ((_111011 + i331) << 6 + _10101) << 10 + _10001 - // i386 = ((i350 << 3 + _11) << 23 + _10011) << 8 - // return ((_101001 + i386) << 6 + _101) << 3 - // - // Operations: 330 squares 67 multiplies - - // Allocate Temporaries. - var ( - t0 = new(Element) - t1 = new(Element) - t2 = new(Element) - t3 = new(Element) - t4 = new(Element) - t5 = new(Element) - t6 = new(Element) - t7 = new(Element) - t8 = new(Element) - t9 = new(Element) - t10 = new(Element) - t11 = new(Element) - t12 = new(Element) - t13 = new(Element) - t14 = new(Element) - t15 = new(Element) - t16 = new(Element) - t17 = new(Element) - t18 = new(Element) - ) - - // var t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18 Element - // Step 1: t6 = x^0x2 - t6.Square(&x) - - // Step 2: t2 = x^0x3 - t2.Mul(&x, t6) - - // Step 3: z = x^0x5 - z.Mul(t6, t2) - - // Step 4: t0 = x^0x6 - t0.Mul(&x, z) - - // Step 5: t15 = x^0x9 - t15.Mul(t2, t0) - - // Step 6: t14 = x^0xb - t14.Mul(t6, t15) - - // Step 7: t5 = x^0xc - t5.Mul(&x, t14) - - // Step 8: t9 = x^0xd - t9.Mul(&x, t5) - - // Step 9: t3 = x^0x11 - t3.Mul(z, t5) - - // Step 10: t1 = x^0x13 - t1.Mul(t6, t3) - - // Step 11: t4 = x^0x15 - t4.Mul(t6, t1) - - // Step 12: t10 = x^0x1b - t10.Mul(t0, t4) - - // Step 13: t7 = x^0x1d - t7.Mul(t6, t10) - - // Step 14: t12 = x^0x23 - t12.Mul(t0, t7) - - // Step 15: t8 = x^0x27 - t8.Mul(t5, t10) - - // Step 16: t0 = x^0x29 - t0.Mul(t6, t8) - - // Step 17: t16 = x^0x35 - t16.Mul(t5, t0) - - // Step 18: t11 = x^0x37 - t11.Mul(t6, t16) - - // Step 19: t17 = x^0x39 - t17.Mul(t6, t11) - - // Step 20: t5 = x^0x3b - t5.Mul(t6, t17) - - // Step 21: t13 = x^0x3d - t13.Mul(t6, t5) - - // Step 22: t6 = x^0x3f - t6.Mul(t6, t13) - - // Step 23: t18 = x^0x7c - t18.Mul(t13, t6) - - // Step 24: t6 = x^0x7f - t6.Mul(t2, t18) - - // Step 28: t18 = x^0x7c0 - for s := 0; s < 4; s++ { - t18.Square(t18) - } - - // Step 29: t18 = x^0x7dd - t18.Mul(t7, t18) - - // Step 32: t18 = x^0x3ee8 - for s := 0; s < 3; s++ { - t18.Square(t18) - } - - // Step 33: t18 = x^0x3eeb - t18.Mul(t2, t18) - - // Step 39: t18 = x^0xfbac0 - for s := 0; s < 6; s++ { - t18.Square(t18) - } - - // Step 40: t18 = x^0xfbac1 - t18.Mul(&x, t18) - - // Step 49: t18 = x^0x1f758200 - for s := 0; s < 9; s++ { - t18.Square(t18) - } - - // Step 50: t18 = x^0x1f75820b - t18.Mul(t14, t18) - - // Step 56: t18 = x^0x7dd6082c0 - for s := 0; s < 6; s++ { - t18.Square(t18) - } - - // Step 57: t18 = x^0x7dd6082cd - t18.Mul(t9, t18) - - // Step 66: t18 = x^0xfbac1059a00 - for s := 0; s < 9; s++ { - t18.Square(t18) - } - - // Step 67: t18 = x^0xfbac1059a13 - t18.Mul(t1, t18) - - // Step 74: t18 = x^0x7dd6082cd0980 - for s := 0; s < 7; s++ { - t18.Square(t18) - } - - // Step 75: t18 = x^0x7dd6082cd09a3 - t18.Mul(t12, t18) - - // Step 78: t18 = x^0x3eeb0416684d18 - for s := 0; s < 3; s++ { - t18.Square(t18) - } - - // Step 79: t18 = x^0x3eeb0416684d19 - t18.Mul(&x, t18) - - // Step 90: t18 = x^0x1f75820b34268c800 - for s := 0; s < 11; s++ { - t18.Square(t18) - } - - // Step 91: t18 = x^0x1f75820b34268c829 - t18.Mul(t0, t18) - - // Step 97: t18 = x^0x7dd6082cd09a320a40 - for s := 0; s < 6; s++ { - t18.Square(t18) - } - - // Step 98: t17 = x^0x7dd6082cd09a320a79 - t17.Mul(t17, t18) - - // Step 105: t17 = x^0x3eeb0416684d19053c80 - for s := 0; s < 7; s++ { - t17.Square(t17) - } - - // Step 106: t16 = x^0x3eeb0416684d19053cb5 - t16.Mul(t16, t17) - - // Step 110: t16 = x^0x3eeb0416684d19053cb50 - for s := 0; s < 4; s++ { - t16.Square(t16) - } - - // Step 111: t16 = x^0x3eeb0416684d19053cb5d - t16.Mul(t9, t16) - - // Step 117: t16 = x^0xfbac1059a1346414f2d740 - for s := 0; s < 6; s++ { - t16.Square(t16) - } - - // Step 118: t15 = x^0xfbac1059a1346414f2d749 - t15.Mul(t15, t16) - - // Step 130: t15 = x^0xfbac1059a1346414f2d749000 - for s := 0; s < 12; s++ { - t15.Square(t15) - } - - // Step 131: t15 = x^0xfbac1059a1346414f2d74903b - t15.Mul(t5, t15) - - // Step 137: t15 = x^0x3eeb0416684d19053cb5d240ec0 - for s := 0; s < 6; s++ { - t15.Square(t15) - } - - // Step 138: t15 = x^0x3eeb0416684d19053cb5d240ed1 - t15.Mul(t3, t15) - - // Step 149: t15 = x^0x1f75820b34268c829e5ae920768800 - for s := 0; s < 11; s++ { - t15.Square(t15) - } - - // Step 150: t15 = x^0x1f75820b34268c829e5ae92076883d - t15.Mul(t13, t15) - - // Step 156: t15 = x^0x7dd6082cd09a320a796ba481da20f40 - for s := 0; s < 6; s++ { - t15.Square(t15) - } - - // Step 157: t15 = x^0x7dd6082cd09a320a796ba481da20f45 - t15.Mul(z, t15) - - // Step 162: t15 = x^0xfbac1059a1346414f2d74903b441e8a0 - for s := 0; s < 5; s++ { - t15.Square(t15) - } - - // Step 163: t15 = x^0xfbac1059a1346414f2d74903b441e8a1 - t15.Mul(&x, t15) - - // Step 174: t15 = x^0x7dd6082cd09a320a796ba481da20f450800 - for s := 0; s < 11; s++ { - t15.Square(t15) - } - - // Step 175: t14 = x^0x7dd6082cd09a320a796ba481da20f45080b - t14.Mul(t14, t15) - - // Step 183: t14 = x^0x7dd6082cd09a320a796ba481da20f45080b00 - for s := 0; s < 8; s++ { - t14.Square(t14) - } - - // Step 184: t13 = x^0x7dd6082cd09a320a796ba481da20f45080b3d - t13.Mul(t13, t14) - - // Step 190: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf40 - for s := 0; s < 6; s++ { - t13.Square(t13) - } - - // Step 191: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf5b - t13.Mul(t10, t13) - - // Step 199: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf5b00 - for s := 0; s < 8; s++ { - t13.Square(t13) - } - - // Step 200: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf5b23 - t13.Mul(t12, t13) - - // Step 205: t13 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6460 - for s := 0; s < 5; s++ { - t13.Square(t13) - } - - // Step 206: t13 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6471 - t13.Mul(t3, t13) - - // Step 218: t13 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6471000 - for s := 0; s < 12; s++ { - t13.Square(t13) - } - - // Step 219: t12 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6471023 - t12.Mul(t12, t13) - - // Step 226: t12 = x^0x1f75820b34268c829e5ae92076883d14202cf5b23881180 - for s := 0; s < 7; s++ { - t12.Square(t12) - } - - // Step 227: t12 = x^0x1f75820b34268c829e5ae92076883d14202cf5b23881193 - t12.Mul(t1, t12) - - // Step 233: t12 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464c0 - for s := 0; s < 6; s++ { - t12.Square(t12) - } - - // Step 234: t12 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d3 - t12.Mul(t1, t12) - - // Step 247: t12 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a6000 - for s := 0; s < 13; s++ { - t12.Square(t12) - } - - // Step 248: t11 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a6037 - t11.Mul(t11, t12) - - // Step 257: t11 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e00 - for s := 0; s < 9; s++ { - t11.Square(t11) - } - - // Step 258: t10 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b - t10.Mul(t10, t11) - - // Step 267: t10 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc3600 - for s := 0; s < 9; s++ { - t10.Square(t10) - } - - // Step 268: t9 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d - t9.Mul(t9, t10) - - // Step 278: t9 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83400 - for s := 0; s < 10; s++ { - t9.Square(t9) - } - - // Step 279: t9 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429 - t9.Mul(t0, t9) - - // Step 287: t9 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d8342900 - for s := 0; s < 8; s++ { - t9.Square(t9) - } - - // Step 288: t8 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d8342927 - t8.Mul(t8, t9) - - // Step 290: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49c - for s := 0; s < 2; s++ { - t8.Square(t8) - } - - // Step 291: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d - t8.Mul(&x, t8) - - // Step 299: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d00 - for s := 0; s < 8; s++ { - t8.Square(t8) - } - - // Step 300: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7f - t8.Mul(t6, t8) - - // Step 302: t8 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275fc - for s := 0; s < 2; s++ { - t8.Square(t8) - } - - // Step 303: t8 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff - t8.Mul(t2, t8) - - // Step 310: t8 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff80 - for s := 0; s < 7; s++ { - t8.Square(t8) - } - - // Step 311: t7 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d - t7.Mul(t7, t8) - - // Step 314: t7 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce8 - for s := 0; s < 3; s++ { - t7.Square(t7) - } - - // Step 315: t7 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce9 - t7.Mul(&x, t7) - - // Step 323: t7 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce900 - for s := 0; s < 8; s++ { - t7.Square(t7) - } - - // Step 324: t6 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce97f - t6.Mul(t6, t7) - - // Step 331: t6 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bf80 - for s := 0; s < 7; s++ { - t6.Square(t6) - } - - // Step 332: t5 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb - t5.Mul(t5, t6) - - // Step 338: t5 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feec0 - for s := 0; s < 6; s++ { - t5.Square(t5) - } - - // Step 339: t4 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed5 - t4.Mul(t4, t5) - - // Step 349: t4 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5400 - for s := 0; s < 10; s++ { - t4.Square(t4) - } - - // Step 350: t3 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411 - t3.Mul(t3, t4) - - // Step 353: t3 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff3a5fddaa088 - for s := 0; s < 3; s++ { - t3.Square(t3) - } - - // Step 354: t2 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff3a5fddaa08b - t2.Mul(t2, t3) - - // Step 377: t2 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed5045800000 - for s := 0; s < 23; s++ { - t2.Square(t2) - } - - // Step 378: t1 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed5045800013 - t1.Mul(t1, t2) - - // Step 386: t1 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed504580001300 - for s := 0; s < 8; s++ { - t1.Square(t1) - } - - // Step 387: t0 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed504580001329 - t0.Mul(t0, t1) - - // Step 393: t0 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca40 - for s := 0; s < 6; s++ { - t0.Square(t0) - } - - // Step 394: z = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca45 - z.Mul(z, t0) - - // Step 397: z = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff3a5fddaa08b0000265228 - for s := 0; s < 3; s++ { - z.Square(z) - } - - return z -} - -// expByLegendreExp is equivalent to z.Exp(x, 1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca4510000000000) -// -// uses github.com/mmcloughlin/addchain v0.4.0 to generate a shorter addition chain -func (z *Element) expByLegendreExp(x Element) *Element { - // addition chain: - // - // _10 = 2*1 - // _11 = 1 + _10 - // _101 = _10 + _11 - // _110 = 1 + _101 - // _1001 = _11 + _110 - // _1011 = _10 + _1001 - // _1100 = 1 + _1011 - // _1101 = 1 + _1100 - // _10001 = _101 + _1100 - // _10011 = _10 + _10001 - // _10101 = _10 + _10011 - // _11011 = _110 + _10101 - // _11101 = _10 + _11011 - // _100011 = _110 + _11101 - // _100111 = _1100 + _11011 - // _101001 = _10 + _100111 - // _110101 = _1100 + _101001 - // _110111 = _10 + _110101 - // _111001 = _10 + _110111 - // _111011 = _10 + _111001 - // _111101 = _10 + _111011 - // _111111 = _10 + _111101 - // _1111100 = _111101 + _111111 - // _1111111 = _11 + _1111100 - // i39 = ((_1111100 << 4 + _11101) << 3 + _11) << 6 - // i57 = ((1 + i39) << 9 + _1011) << 6 + _1101 - // i78 = ((i57 << 9 + _10011) << 7 + _100011) << 3 - // i98 = ((1 + i78) << 11 + _101001) << 6 + _111001 - // i117 = ((i98 << 7 + _110101) << 4 + _1101) << 6 - // i138 = ((_1001 + i117) << 12 + _111011) << 6 + _10001 - // i162 = ((i138 << 11 + _111101) << 6 + _101) << 5 - // i184 = ((1 + i162) << 11 + _1011) << 8 + _111101 - // i205 = ((i184 << 6 + _11011) << 8 + _100011) << 5 - // i227 = ((_10001 + i205) << 12 + _100011) << 7 + _10011 - // i257 = ((i227 << 6 + _10011) << 13 + _110111) << 9 - // i279 = ((_11011 + i257) << 9 + _1101) << 10 + _101001 - // i299 = ((i279 << 8 + _100111) << 2 + 1) << 8 - // i311 = ((_1111111 + i299) << 2 + _11) << 7 + _11101 - // i331 = ((i311 << 3 + 1) << 8 + _1111111) << 7 - // i350 = ((_111011 + i331) << 6 + _10101) << 10 + _10001 - // i386 = ((i350 << 3 + _11) << 23 + _10011) << 8 - // i399 = ((_101001 + i386) << 6 + _101) << 4 + 1 - // return i399 << 40 - // - // Operations: 371 squares 68 multiplies - - // Allocate Temporaries. - var ( - t0 = new(Element) - t1 = new(Element) - t2 = new(Element) - t3 = new(Element) - t4 = new(Element) - t5 = new(Element) - t6 = new(Element) - t7 = new(Element) - t8 = new(Element) - t9 = new(Element) - t10 = new(Element) - t11 = new(Element) - t12 = new(Element) - t13 = new(Element) - t14 = new(Element) - t15 = new(Element) - t16 = new(Element) - t17 = new(Element) - t18 = new(Element) - ) - - // var t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18 Element - // Step 1: t6 = x^0x2 - t6.Square(&x) - - // Step 2: t2 = x^0x3 - t2.Mul(&x, t6) - - // Step 3: z = x^0x5 - z.Mul(t6, t2) - - // Step 4: t0 = x^0x6 - t0.Mul(&x, z) - - // Step 5: t15 = x^0x9 - t15.Mul(t2, t0) - - // Step 6: t14 = x^0xb - t14.Mul(t6, t15) - - // Step 7: t5 = x^0xc - t5.Mul(&x, t14) - - // Step 8: t9 = x^0xd - t9.Mul(&x, t5) - - // Step 9: t3 = x^0x11 - t3.Mul(z, t5) - - // Step 10: t1 = x^0x13 - t1.Mul(t6, t3) - - // Step 11: t4 = x^0x15 - t4.Mul(t6, t1) - - // Step 12: t10 = x^0x1b - t10.Mul(t0, t4) - - // Step 13: t7 = x^0x1d - t7.Mul(t6, t10) - - // Step 14: t12 = x^0x23 - t12.Mul(t0, t7) - - // Step 15: t8 = x^0x27 - t8.Mul(t5, t10) - - // Step 16: t0 = x^0x29 - t0.Mul(t6, t8) - - // Step 17: t16 = x^0x35 - t16.Mul(t5, t0) - - // Step 18: t11 = x^0x37 - t11.Mul(t6, t16) - - // Step 19: t17 = x^0x39 - t17.Mul(t6, t11) - - // Step 20: t5 = x^0x3b - t5.Mul(t6, t17) - - // Step 21: t13 = x^0x3d - t13.Mul(t6, t5) - - // Step 22: t6 = x^0x3f - t6.Mul(t6, t13) - - // Step 23: t18 = x^0x7c - t18.Mul(t13, t6) - - // Step 24: t6 = x^0x7f - t6.Mul(t2, t18) - - // Step 28: t18 = x^0x7c0 - for s := 0; s < 4; s++ { - t18.Square(t18) - } - - // Step 29: t18 = x^0x7dd - t18.Mul(t7, t18) - - // Step 32: t18 = x^0x3ee8 - for s := 0; s < 3; s++ { - t18.Square(t18) - } - - // Step 33: t18 = x^0x3eeb - t18.Mul(t2, t18) - - // Step 39: t18 = x^0xfbac0 - for s := 0; s < 6; s++ { - t18.Square(t18) - } - - // Step 40: t18 = x^0xfbac1 - t18.Mul(&x, t18) - - // Step 49: t18 = x^0x1f758200 - for s := 0; s < 9; s++ { - t18.Square(t18) - } - - // Step 50: t18 = x^0x1f75820b - t18.Mul(t14, t18) - - // Step 56: t18 = x^0x7dd6082c0 - for s := 0; s < 6; s++ { - t18.Square(t18) - } - - // Step 57: t18 = x^0x7dd6082cd - t18.Mul(t9, t18) - - // Step 66: t18 = x^0xfbac1059a00 - for s := 0; s < 9; s++ { - t18.Square(t18) - } - - // Step 67: t18 = x^0xfbac1059a13 - t18.Mul(t1, t18) - - // Step 74: t18 = x^0x7dd6082cd0980 - for s := 0; s < 7; s++ { - t18.Square(t18) - } - - // Step 75: t18 = x^0x7dd6082cd09a3 - t18.Mul(t12, t18) - - // Step 78: t18 = x^0x3eeb0416684d18 - for s := 0; s < 3; s++ { - t18.Square(t18) - } - - // Step 79: t18 = x^0x3eeb0416684d19 - t18.Mul(&x, t18) - - // Step 90: t18 = x^0x1f75820b34268c800 - for s := 0; s < 11; s++ { - t18.Square(t18) - } - - // Step 91: t18 = x^0x1f75820b34268c829 - t18.Mul(t0, t18) - - // Step 97: t18 = x^0x7dd6082cd09a320a40 - for s := 0; s < 6; s++ { - t18.Square(t18) - } - - // Step 98: t17 = x^0x7dd6082cd09a320a79 - t17.Mul(t17, t18) - - // Step 105: t17 = x^0x3eeb0416684d19053c80 - for s := 0; s < 7; s++ { - t17.Square(t17) - } - - // Step 106: t16 = x^0x3eeb0416684d19053cb5 - t16.Mul(t16, t17) - - // Step 110: t16 = x^0x3eeb0416684d19053cb50 - for s := 0; s < 4; s++ { - t16.Square(t16) - } - - // Step 111: t16 = x^0x3eeb0416684d19053cb5d - t16.Mul(t9, t16) - - // Step 117: t16 = x^0xfbac1059a1346414f2d740 - for s := 0; s < 6; s++ { - t16.Square(t16) - } - - // Step 118: t15 = x^0xfbac1059a1346414f2d749 - t15.Mul(t15, t16) - - // Step 130: t15 = x^0xfbac1059a1346414f2d749000 - for s := 0; s < 12; s++ { - t15.Square(t15) - } - - // Step 131: t15 = x^0xfbac1059a1346414f2d74903b - t15.Mul(t5, t15) - - // Step 137: t15 = x^0x3eeb0416684d19053cb5d240ec0 - for s := 0; s < 6; s++ { - t15.Square(t15) - } - - // Step 138: t15 = x^0x3eeb0416684d19053cb5d240ed1 - t15.Mul(t3, t15) - - // Step 149: t15 = x^0x1f75820b34268c829e5ae920768800 - for s := 0; s < 11; s++ { - t15.Square(t15) - } - - // Step 150: t15 = x^0x1f75820b34268c829e5ae92076883d - t15.Mul(t13, t15) - - // Step 156: t15 = x^0x7dd6082cd09a320a796ba481da20f40 - for s := 0; s < 6; s++ { - t15.Square(t15) - } - - // Step 157: t15 = x^0x7dd6082cd09a320a796ba481da20f45 - t15.Mul(z, t15) - - // Step 162: t15 = x^0xfbac1059a1346414f2d74903b441e8a0 - for s := 0; s < 5; s++ { - t15.Square(t15) - } - - // Step 163: t15 = x^0xfbac1059a1346414f2d74903b441e8a1 - t15.Mul(&x, t15) - - // Step 174: t15 = x^0x7dd6082cd09a320a796ba481da20f450800 - for s := 0; s < 11; s++ { - t15.Square(t15) - } - - // Step 175: t14 = x^0x7dd6082cd09a320a796ba481da20f45080b - t14.Mul(t14, t15) - - // Step 183: t14 = x^0x7dd6082cd09a320a796ba481da20f45080b00 - for s := 0; s < 8; s++ { - t14.Square(t14) - } - - // Step 184: t13 = x^0x7dd6082cd09a320a796ba481da20f45080b3d - t13.Mul(t13, t14) - - // Step 190: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf40 - for s := 0; s < 6; s++ { - t13.Square(t13) - } - - // Step 191: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf5b - t13.Mul(t10, t13) - - // Step 199: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf5b00 - for s := 0; s < 8; s++ { - t13.Square(t13) - } - - // Step 200: t13 = x^0x1f75820b34268c829e5ae92076883d14202cf5b23 - t13.Mul(t12, t13) - - // Step 205: t13 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6460 - for s := 0; s < 5; s++ { - t13.Square(t13) - } - - // Step 206: t13 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6471 - t13.Mul(t3, t13) - - // Step 218: t13 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6471000 - for s := 0; s < 12; s++ { - t13.Square(t13) - } - - // Step 219: t12 = x^0x3eeb0416684d19053cb5d240ed107a284059eb6471023 - t12.Mul(t12, t13) - - // Step 226: t12 = x^0x1f75820b34268c829e5ae92076883d14202cf5b23881180 - for s := 0; s < 7; s++ { - t12.Square(t12) - } - - // Step 227: t12 = x^0x1f75820b34268c829e5ae92076883d14202cf5b23881193 - t12.Mul(t1, t12) - - // Step 233: t12 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464c0 - for s := 0; s < 6; s++ { - t12.Square(t12) - } - - // Step 234: t12 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d3 - t12.Mul(t1, t12) - - // Step 247: t12 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a6000 - for s := 0; s < 13; s++ { - t12.Square(t12) - } - - // Step 248: t11 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a6037 - t11.Mul(t11, t12) - - // Step 257: t11 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e00 - for s := 0; s < 9; s++ { - t11.Square(t11) - } - - // Step 258: t10 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b - t10.Mul(t10, t11) - - // Step 267: t10 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc3600 - for s := 0; s < 9; s++ { - t10.Square(t10) - } - - // Step 268: t9 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d - t9.Mul(t9, t10) - - // Step 278: t9 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83400 - for s := 0; s < 10; s++ { - t9.Square(t9) - } - - // Step 279: t9 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429 - t9.Mul(t0, t9) - - // Step 287: t9 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d8342900 - for s := 0; s < 8; s++ { - t9.Square(t9) - } - - // Step 288: t8 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d8342927 - t8.Mul(t8, t9) - - // Step 290: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49c - for s := 0; s < 2; s++ { - t8.Square(t8) - } - - // Step 291: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d - t8.Mul(&x, t8) - - // Step 299: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d00 - for s := 0; s < 8; s++ { - t8.Square(t8) - } - - // Step 300: t8 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7f - t8.Mul(t6, t8) - - // Step 302: t8 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275fc - for s := 0; s < 2; s++ { - t8.Square(t8) - } - - // Step 303: t8 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff - t8.Mul(t2, t8) - - // Step 310: t8 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff80 - for s := 0; s < 7; s++ { - t8.Square(t8) - } - - // Step 311: t7 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d - t7.Mul(t7, t8) - - // Step 314: t7 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce8 - for s := 0; s < 3; s++ { - t7.Square(t7) - } - - // Step 315: t7 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce9 - t7.Mul(&x, t7) - - // Step 323: t7 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce900 - for s := 0; s < 8; s++ { - t7.Square(t7) - } - - // Step 324: t6 = x^0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce97f - t6.Mul(t6, t7) - - // Step 331: t6 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bf80 - for s := 0; s < 7; s++ { - t6.Square(t6) - } - - // Step 332: t5 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb - t5.Mul(t5, t6) - - // Step 338: t5 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feec0 - for s := 0; s < 6; s++ { - t5.Square(t5) - } - - // Step 339: t4 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed5 - t4.Mul(t4, t5) - - // Step 349: t4 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5400 - for s := 0; s < 10; s++ { - t4.Square(t4) - } - - // Step 350: t3 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411 - t3.Mul(t3, t4) - - // Step 353: t3 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff3a5fddaa088 - for s := 0; s < 3; s++ { - t3.Square(t3) - } - - // Step 354: t2 = x^0xfbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff3a5fddaa08b - t2.Mul(t2, t3) - - // Step 377: t2 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed5045800000 - for s := 0; s < 23; s++ { - t2.Square(t2) - } - - // Step 378: t1 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed5045800013 - t1.Mul(t1, t2) - - // Step 386: t1 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed504580001300 - for s := 0; s < 8; s++ { - t1.Square(t1) - } - - // Step 387: t0 = x^0x7dd6082cd09a320a796ba481da20f45080b3d6c8e20464d301b86c1a1493aff9d2feed504580001329 - t0.Mul(t0, t1) - - // Step 393: t0 = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca40 - for s := 0; s < 6; s++ { - t0.Square(t0) - } - - // Step 394: z = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca45 - z.Mul(z, t0) - - // Step 398: z = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca450 - for s := 0; s < 4; s++ { - z.Square(z) - } - - // Step 399: z = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca451 - z.Mul(&x, z) - - // Step 439: z = x^0x1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca4510000000000 - for s := 0; s < 40; s++ { - z.Square(z) - } - - return z -} diff --git a/ecc/bw6-756/fr/element_mul_amd64.s b/ecc/bw6-756/fr/element_mul_amd64.s deleted file mode 100644 index 39ededda7e..0000000000 --- a/ecc/bw6-756/fr/element_mul_amd64.s +++ /dev/null @@ -1,857 +0,0 @@ -// +build !purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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. - -#include "textflag.h" -#include "funcdata.h" - -// modulus q -DATA q<>+0(SB)/8, $0x9948a20000000001 -DATA q<>+8(SB)/8, $0xce97f76a822c0000 -DATA q<>+16(SB)/8, $0x980dc360d0a49d7f -DATA q<>+24(SB)/8, $0x84059eb647102326 -DATA q<>+32(SB)/8, $0x53cb5d240ed107a2 -DATA q<>+40(SB)/8, $0x03eeb0416684d190 -GLOBL q<>(SB), (RODATA+NOPTR), $48 - -// qInv0 q'[0] -DATA qInv0<>(SB)/8, $0x9948a1ffffffffff -GLOBL qInv0<>(SB), (RODATA+NOPTR), $8 - -#define REDUCE(ra0, ra1, ra2, ra3, ra4, ra5, rb0, rb1, rb2, rb3, rb4, rb5) \ - MOVQ ra0, rb0; \ - SUBQ q<>(SB), ra0; \ - MOVQ ra1, rb1; \ - SBBQ q<>+8(SB), ra1; \ - MOVQ ra2, rb2; \ - SBBQ q<>+16(SB), ra2; \ - MOVQ ra3, rb3; \ - SBBQ q<>+24(SB), ra3; \ - MOVQ ra4, rb4; \ - SBBQ q<>+32(SB), ra4; \ - MOVQ ra5, rb5; \ - SBBQ q<>+40(SB), ra5; \ - CMOVQCS rb0, ra0; \ - CMOVQCS rb1, ra1; \ - CMOVQCS rb2, ra2; \ - CMOVQCS rb3, ra3; \ - CMOVQCS rb4, ra4; \ - CMOVQCS rb5, ra5; \ - -// mul(res, x, y *Element) -TEXT ·mul(SB), $24-24 - - // the algorithm is described in the Element.Mul declaration (.go) - // however, to benefit from the ADCX and ADOX carry chains - // we split the inner loops in 2: - // for i=0 to N-1 - // for j=0 to N-1 - // (A,t[j]) := t[j] + x[j]*y[i] + A - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // t[N-1] = C + A - - NO_LOCAL_POINTERS - CMPB ·supportAdx(SB), $1 - JNE l1 - MOVQ x+8(FP), R8 - - // x[0] -> R10 - // x[1] -> R11 - // x[2] -> R12 - MOVQ 0(R8), R10 - MOVQ 8(R8), R11 - MOVQ 16(R8), R12 - MOVQ y+16(FP), R13 - - // A -> BP - // t[0] -> R14 - // t[1] -> R15 - // t[2] -> CX - // t[3] -> BX - // t[4] -> SI - // t[5] -> DI - // clear the flags - XORQ AX, AX - MOVQ 0(R13), DX - - // (A,t[0]) := x[0]*y[0] + A - MULXQ R10, R14, R15 - - // (A,t[1]) := x[1]*y[0] + A - MULXQ R11, AX, CX - ADOXQ AX, R15 - - // (A,t[2]) := x[2]*y[0] + A - MULXQ R12, AX, BX - ADOXQ AX, CX - - // (A,t[3]) := x[3]*y[0] + A - MULXQ 24(R8), AX, SI - ADOXQ AX, BX - - // (A,t[4]) := x[4]*y[0] + A - MULXQ 32(R8), AX, DI - ADOXQ AX, SI - - // (A,t[5]) := x[5]*y[0] + A - MULXQ 40(R8), AX, BP - ADOXQ AX, DI - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R9 - ADCXQ R14, AX - MOVQ R9, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // t[5] = C + A - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ BP, DI - - // clear the flags - XORQ AX, AX - MOVQ 8(R13), DX - - // (A,t[0]) := t[0] + x[0]*y[1] + A - MULXQ R10, AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[1] + A - ADCXQ BP, R15 - MULXQ R11, AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[1] + A - ADCXQ BP, CX - MULXQ R12, AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[1] + A - ADCXQ BP, BX - MULXQ 24(R8), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[1] + A - ADCXQ BP, SI - MULXQ 32(R8), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[1] + A - ADCXQ BP, DI - MULXQ 40(R8), AX, BP - ADOXQ AX, DI - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R9 - ADCXQ R14, AX - MOVQ R9, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // t[5] = C + A - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ BP, DI - - // clear the flags - XORQ AX, AX - MOVQ 16(R13), DX - - // (A,t[0]) := t[0] + x[0]*y[2] + A - MULXQ R10, AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[2] + A - ADCXQ BP, R15 - MULXQ R11, AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[2] + A - ADCXQ BP, CX - MULXQ R12, AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[2] + A - ADCXQ BP, BX - MULXQ 24(R8), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[2] + A - ADCXQ BP, SI - MULXQ 32(R8), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[2] + A - ADCXQ BP, DI - MULXQ 40(R8), AX, BP - ADOXQ AX, DI - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R9 - ADCXQ R14, AX - MOVQ R9, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // t[5] = C + A - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ BP, DI - - // clear the flags - XORQ AX, AX - MOVQ 24(R13), DX - - // (A,t[0]) := t[0] + x[0]*y[3] + A - MULXQ R10, AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[3] + A - ADCXQ BP, R15 - MULXQ R11, AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[3] + A - ADCXQ BP, CX - MULXQ R12, AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[3] + A - ADCXQ BP, BX - MULXQ 24(R8), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[3] + A - ADCXQ BP, SI - MULXQ 32(R8), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[3] + A - ADCXQ BP, DI - MULXQ 40(R8), AX, BP - ADOXQ AX, DI - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R9 - ADCXQ R14, AX - MOVQ R9, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // t[5] = C + A - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ BP, DI - - // clear the flags - XORQ AX, AX - MOVQ 32(R13), DX - - // (A,t[0]) := t[0] + x[0]*y[4] + A - MULXQ R10, AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[4] + A - ADCXQ BP, R15 - MULXQ R11, AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[4] + A - ADCXQ BP, CX - MULXQ R12, AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[4] + A - ADCXQ BP, BX - MULXQ 24(R8), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[4] + A - ADCXQ BP, SI - MULXQ 32(R8), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[4] + A - ADCXQ BP, DI - MULXQ 40(R8), AX, BP - ADOXQ AX, DI - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R9 - ADCXQ R14, AX - MOVQ R9, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // t[5] = C + A - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ BP, DI - - // clear the flags - XORQ AX, AX - MOVQ 40(R13), DX - - // (A,t[0]) := t[0] + x[0]*y[5] + A - MULXQ R10, AX, BP - ADOXQ AX, R14 - - // (A,t[1]) := t[1] + x[1]*y[5] + A - ADCXQ BP, R15 - MULXQ R11, AX, BP - ADOXQ AX, R15 - - // (A,t[2]) := t[2] + x[2]*y[5] + A - ADCXQ BP, CX - MULXQ R12, AX, BP - ADOXQ AX, CX - - // (A,t[3]) := t[3] + x[3]*y[5] + A - ADCXQ BP, BX - MULXQ 24(R8), AX, BP - ADOXQ AX, BX - - // (A,t[4]) := t[4] + x[4]*y[5] + A - ADCXQ BP, SI - MULXQ 32(R8), AX, BP - ADOXQ AX, SI - - // (A,t[5]) := t[5] + x[5]*y[5] + A - ADCXQ BP, DI - MULXQ 40(R8), AX, BP - ADOXQ AX, DI - - // A += carries from ADCXQ and ADOXQ - MOVQ $0, AX - ADCXQ AX, BP - ADOXQ AX, BP - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - - // clear the flags - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, R9 - ADCXQ R14, AX - MOVQ R9, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - - // t[5] = C + A - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ BP, DI - - // reduce element(R14,R15,CX,BX,SI,DI) using temp registers (R9,R8,R13,R10,R11,R12) - REDUCE(R14,R15,CX,BX,SI,DI,R9,R8,R13,R10,R11,R12) - - MOVQ res+0(FP), AX - MOVQ R14, 0(AX) - MOVQ R15, 8(AX) - MOVQ CX, 16(AX) - MOVQ BX, 24(AX) - MOVQ SI, 32(AX) - MOVQ DI, 40(AX) - RET - -l1: - MOVQ res+0(FP), AX - MOVQ AX, (SP) - MOVQ x+8(FP), AX - MOVQ AX, 8(SP) - MOVQ y+16(FP), AX - MOVQ AX, 16(SP) - CALL ·_mulGeneric(SB) - RET - -TEXT ·fromMont(SB), $8-8 - NO_LOCAL_POINTERS - - // the algorithm is described here - // https://hackmd.io/@gnark/modular_multiplication - // when y = 1 we have: - // for i=0 to N-1 - // t[i] = x[i] - // for i=0 to N-1 - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // t[N-1] = C - CMPB ·supportAdx(SB), $1 - JNE l2 - MOVQ res+0(FP), DX - MOVQ 0(DX), R14 - MOVQ 8(DX), R15 - MOVQ 16(DX), CX - MOVQ 24(DX), BX - MOVQ 32(DX), SI - MOVQ 40(DX), DI - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ AX, DI - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ AX, DI - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ AX, DI - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ AX, DI - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ AX, DI - XORQ DX, DX - - // m := t[0]*q'[0] mod W - MOVQ qInv0<>(SB), DX - IMULQ R14, DX - XORQ AX, AX - - // C,_ := t[0] + m*q[0] - MULXQ q<>+0(SB), AX, BP - ADCXQ R14, AX - MOVQ BP, R14 - - // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 - ADOXQ AX, R14 - - // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 - MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 - - // (C,t[2]) := t[3] + m*q[3] + C - ADCXQ BX, CX - MULXQ q<>+24(SB), AX, BX - ADOXQ AX, CX - - // (C,t[3]) := t[4] + m*q[4] + C - ADCXQ SI, BX - MULXQ q<>+32(SB), AX, SI - ADOXQ AX, BX - - // (C,t[4]) := t[5] + m*q[5] + C - ADCXQ DI, SI - MULXQ q<>+40(SB), AX, DI - ADOXQ AX, SI - MOVQ $0, AX - ADCXQ AX, DI - ADOXQ AX, DI - - // reduce element(R14,R15,CX,BX,SI,DI) using temp registers (R8,R9,R10,R11,R12,R13) - REDUCE(R14,R15,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13) - - MOVQ res+0(FP), AX - MOVQ R14, 0(AX) - MOVQ R15, 8(AX) - MOVQ CX, 16(AX) - MOVQ BX, 24(AX) - MOVQ SI, 32(AX) - MOVQ DI, 40(AX) - RET - -l2: - MOVQ res+0(FP), AX - MOVQ AX, (SP) - CALL ·_fromMontGeneric(SB) - RET diff --git a/ecc/bw6-756/fr/element_ops_amd64.go b/ecc/bw6-756/fr/element_ops_amd64.go deleted file mode 100644 index e40a9caed5..0000000000 --- a/ecc/bw6-756/fr/element_ops_amd64.go +++ /dev/null @@ -1,107 +0,0 @@ -//go:build !purego -// +build !purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -//go:noescape -func MulBy3(x *Element) - -//go:noescape -func MulBy5(x *Element) - -//go:noescape -func MulBy13(x *Element) - -//go:noescape -func mul(res, x, y *Element) - -//go:noescape -func fromMont(res *Element) - -//go:noescape -func reduce(res *Element) - -// Butterfly sets -// -// a = a + b (mod q) -// b = a - b (mod q) -// -//go:noescape -func Butterfly(a, b *Element) - -// Mul z = x * y (mod q) -// -// x and y must be less than q -func (z *Element) Mul(x, y *Element) *Element { - - // Implements CIOS multiplication -- section 2.3.2 of Tolga Acar's thesis - // https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf - // - // The algorithm: - // - // for i=0 to N-1 - // C := 0 - // for j=0 to N-1 - // (C,t[j]) := t[j] + x[j]*y[i] + C - // (t[N+1],t[N]) := t[N] + C - // - // C := 0 - // m := t[0]*q'[0] mod D - // (C,_) := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // (C,t[N-1]) := t[N] + C - // t[N] := t[N+1] + C - // - // → N is the number of machine words needed to store the modulus q - // → D is the word size. For example, on a 64-bit architecture D is 2 64 - // → x[i], y[i], q[i] is the ith word of the numbers x,y,q - // → q'[0] is the lowest word of the number -q⁻¹ mod r. This quantity is pre-computed, as it does not depend on the inputs. - // → t is a temporary array of size N+2 - // → C, S are machine words. A pair (C,S) refers to (hi-bits, lo-bits) of a two-word number - // - // As described here https://hackmd.io/@gnark/modular_multiplication we can get rid of one carry chain and simplify: - // (also described in https://eprint.iacr.org/2022/1400.pdf annex) - // - // for i=0 to N-1 - // (A,t[0]) := t[0] + x[0]*y[i] - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (A,t[j]) := t[j] + x[j]*y[i] + A - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // t[N-1] = C + A - // - // This optimization saves 5N + 2 additions in the algorithm, and can be used whenever the highest bit - // of the modulus is zero (and not all of the remaining bits are set). - - mul(z, x, y) - return z -} - -// Square z = x * x (mod q) -// -// x must be less than q -func (z *Element) Square(x *Element) *Element { - // see Mul for doc. - mul(z, x, x) - return z -} diff --git a/ecc/bw6-756/fr/element_ops_amd64.s b/ecc/bw6-756/fr/element_ops_amd64.s deleted file mode 100644 index 9440e0ccbc..0000000000 --- a/ecc/bw6-756/fr/element_ops_amd64.s +++ /dev/null @@ -1,306 +0,0 @@ -// +build !purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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. - -#include "textflag.h" -#include "funcdata.h" - -// modulus q -DATA q<>+0(SB)/8, $0x9948a20000000001 -DATA q<>+8(SB)/8, $0xce97f76a822c0000 -DATA q<>+16(SB)/8, $0x980dc360d0a49d7f -DATA q<>+24(SB)/8, $0x84059eb647102326 -DATA q<>+32(SB)/8, $0x53cb5d240ed107a2 -DATA q<>+40(SB)/8, $0x03eeb0416684d190 -GLOBL q<>(SB), (RODATA+NOPTR), $48 - -// qInv0 q'[0] -DATA qInv0<>(SB)/8, $0x9948a1ffffffffff -GLOBL qInv0<>(SB), (RODATA+NOPTR), $8 - -#define REDUCE(ra0, ra1, ra2, ra3, ra4, ra5, rb0, rb1, rb2, rb3, rb4, rb5) \ - MOVQ ra0, rb0; \ - SUBQ q<>(SB), ra0; \ - MOVQ ra1, rb1; \ - SBBQ q<>+8(SB), ra1; \ - MOVQ ra2, rb2; \ - SBBQ q<>+16(SB), ra2; \ - MOVQ ra3, rb3; \ - SBBQ q<>+24(SB), ra3; \ - MOVQ ra4, rb4; \ - SBBQ q<>+32(SB), ra4; \ - MOVQ ra5, rb5; \ - SBBQ q<>+40(SB), ra5; \ - CMOVQCS rb0, ra0; \ - CMOVQCS rb1, ra1; \ - CMOVQCS rb2, ra2; \ - CMOVQCS rb3, ra3; \ - CMOVQCS rb4, ra4; \ - CMOVQCS rb5, ra5; \ - -TEXT ·reduce(SB), NOSPLIT, $0-8 - MOVQ res+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - MOVQ 32(AX), DI - MOVQ 40(AX), R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R9,R10,R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - MOVQ DI, 32(AX) - MOVQ R8, 40(AX) - RET - -// MulBy3(x *Element) -TEXT ·MulBy3(SB), NOSPLIT, $0-8 - MOVQ x+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - MOVQ 32(AX), DI - MOVQ 40(AX), R8 - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R9,R10,R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) - - ADDQ 0(AX), DX - ADCQ 8(AX), CX - ADCQ 16(AX), BX - ADCQ 24(AX), SI - ADCQ 32(AX), DI - ADCQ 40(AX), R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R15,R9,R10,R11,R12,R13) - REDUCE(DX,CX,BX,SI,DI,R8,R15,R9,R10,R11,R12,R13) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - MOVQ DI, 32(AX) - MOVQ R8, 40(AX) - RET - -// MulBy5(x *Element) -TEXT ·MulBy5(SB), NOSPLIT, $0-8 - MOVQ x+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - MOVQ 32(AX), DI - MOVQ 40(AX), R8 - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R9,R10,R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) - - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R15,R9,R10,R11,R12,R13) - REDUCE(DX,CX,BX,SI,DI,R8,R15,R9,R10,R11,R12,R13) - - ADDQ 0(AX), DX - ADCQ 8(AX), CX - ADCQ 16(AX), BX - ADCQ 24(AX), SI - ADCQ 32(AX), DI - ADCQ 40(AX), R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R14,R15,R9,R10,R11,R12) - REDUCE(DX,CX,BX,SI,DI,R8,R14,R15,R9,R10,R11,R12) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - MOVQ DI, 32(AX) - MOVQ R8, 40(AX) - RET - -// MulBy13(x *Element) -TEXT ·MulBy13(SB), $40-8 - MOVQ x+0(FP), AX - MOVQ 0(AX), DX - MOVQ 8(AX), CX - MOVQ 16(AX), BX - MOVQ 24(AX), SI - MOVQ 32(AX), DI - MOVQ 40(AX), R8 - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R9,R10,R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) - - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP)) - REDUCE(DX,CX,BX,SI,DI,R8,R15,s0-8(SP),s1-16(SP),s2-24(SP),s3-32(SP),s4-40(SP)) - - MOVQ DX, R15 - MOVQ CX, s0-8(SP) - MOVQ BX, s1-16(SP) - MOVQ SI, s2-24(SP) - MOVQ DI, s3-32(SP) - MOVQ R8, s4-40(SP) - ADDQ DX, DX - ADCQ CX, CX - ADCQ BX, BX - ADCQ SI, SI - ADCQ DI, DI - ADCQ R8, R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R9,R10,R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) - - ADDQ R15, DX - ADCQ s0-8(SP), CX - ADCQ s1-16(SP), BX - ADCQ s2-24(SP), SI - ADCQ s3-32(SP), DI - ADCQ s4-40(SP), R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R9,R10,R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) - - ADDQ 0(AX), DX - ADCQ 8(AX), CX - ADCQ 16(AX), BX - ADCQ 24(AX), SI - ADCQ 32(AX), DI - ADCQ 40(AX), R8 - - // reduce element(DX,CX,BX,SI,DI,R8) using temp registers (R9,R10,R11,R12,R13,R14) - REDUCE(DX,CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14) - - MOVQ DX, 0(AX) - MOVQ CX, 8(AX) - MOVQ BX, 16(AX) - MOVQ SI, 24(AX) - MOVQ DI, 32(AX) - MOVQ R8, 40(AX) - RET - -// Butterfly(a, b *Element) sets a = a + b; b = a - b -TEXT ·Butterfly(SB), $48-16 - MOVQ a+0(FP), AX - MOVQ 0(AX), CX - MOVQ 8(AX), BX - MOVQ 16(AX), SI - MOVQ 24(AX), DI - MOVQ 32(AX), R8 - MOVQ 40(AX), R9 - MOVQ CX, R10 - MOVQ BX, R11 - MOVQ SI, R12 - MOVQ DI, R13 - MOVQ R8, R14 - MOVQ R9, R15 - XORQ AX, AX - MOVQ b+8(FP), DX - ADDQ 0(DX), CX - ADCQ 8(DX), BX - ADCQ 16(DX), SI - ADCQ 24(DX), DI - ADCQ 32(DX), R8 - ADCQ 40(DX), R9 - SUBQ 0(DX), R10 - SBBQ 8(DX), R11 - SBBQ 16(DX), R12 - SBBQ 24(DX), R13 - SBBQ 32(DX), R14 - SBBQ 40(DX), R15 - MOVQ CX, s0-8(SP) - MOVQ BX, s1-16(SP) - MOVQ SI, s2-24(SP) - MOVQ DI, s3-32(SP) - MOVQ R8, s4-40(SP) - MOVQ R9, s5-48(SP) - MOVQ $0x9948a20000000001, CX - MOVQ $0xce97f76a822c0000, BX - MOVQ $0x980dc360d0a49d7f, SI - MOVQ $0x84059eb647102326, DI - MOVQ $0x53cb5d240ed107a2, R8 - MOVQ $0x03eeb0416684d190, R9 - CMOVQCC AX, CX - CMOVQCC AX, BX - CMOVQCC AX, SI - CMOVQCC AX, DI - CMOVQCC AX, R8 - CMOVQCC AX, R9 - ADDQ CX, R10 - ADCQ BX, R11 - ADCQ SI, R12 - ADCQ DI, R13 - ADCQ R8, R14 - ADCQ R9, R15 - MOVQ s0-8(SP), CX - MOVQ s1-16(SP), BX - MOVQ s2-24(SP), SI - MOVQ s3-32(SP), DI - MOVQ s4-40(SP), R8 - MOVQ s5-48(SP), R9 - MOVQ R10, 0(DX) - MOVQ R11, 8(DX) - MOVQ R12, 16(DX) - MOVQ R13, 24(DX) - MOVQ R14, 32(DX) - MOVQ R15, 40(DX) - - // reduce element(CX,BX,SI,DI,R8,R9) using temp registers (R10,R11,R12,R13,R14,R15) - REDUCE(CX,BX,SI,DI,R8,R9,R10,R11,R12,R13,R14,R15) - - MOVQ a+0(FP), AX - MOVQ CX, 0(AX) - MOVQ BX, 8(AX) - MOVQ SI, 16(AX) - MOVQ DI, 24(AX) - MOVQ R8, 32(AX) - MOVQ R9, 40(AX) - RET diff --git a/ecc/bw6-756/fr/element_ops_purego.go b/ecc/bw6-756/fr/element_ops_purego.go deleted file mode 100644 index 4b243fca51..0000000000 --- a/ecc/bw6-756/fr/element_ops_purego.go +++ /dev/null @@ -1,745 +0,0 @@ -//go:build !amd64 || purego -// +build !amd64 purego - -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import "math/bits" - -// MulBy3 x *= 3 (mod q) -func MulBy3(x *Element) { - _x := *x - x.Double(x).Add(x, &_x) -} - -// MulBy5 x *= 5 (mod q) -func MulBy5(x *Element) { - _x := *x - x.Double(x).Double(x).Add(x, &_x) -} - -// MulBy13 x *= 13 (mod q) -func MulBy13(x *Element) { - var y = Element{ - 8212494240417053874, - 5029498262967025157, - 9404736542133420963, - 13073247822498485877, - 1581382318314538223, - 87125160541517067, - } - x.Mul(x, &y) -} - -// Butterfly sets -// -// a = a + b (mod q) -// b = a - b (mod q) -func Butterfly(a, b *Element) { - _butterflyGeneric(a, b) -} - -func fromMont(z *Element) { - _fromMontGeneric(z) -} - -func reduce(z *Element) { - _reduceGeneric(z) -} - -// Mul z = x * y (mod q) -// -// x and y must be less than q -func (z *Element) Mul(x, y *Element) *Element { - - // Implements CIOS multiplication -- section 2.3.2 of Tolga Acar's thesis - // https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf - // - // The algorithm: - // - // for i=0 to N-1 - // C := 0 - // for j=0 to N-1 - // (C,t[j]) := t[j] + x[j]*y[i] + C - // (t[N+1],t[N]) := t[N] + C - // - // C := 0 - // m := t[0]*q'[0] mod D - // (C,_) := t[0] + m*q[0] - // for j=1 to N-1 - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // (C,t[N-1]) := t[N] + C - // t[N] := t[N+1] + C - // - // → N is the number of machine words needed to store the modulus q - // → D is the word size. For example, on a 64-bit architecture D is 2 64 - // → x[i], y[i], q[i] is the ith word of the numbers x,y,q - // → q'[0] is the lowest word of the number -q⁻¹ mod r. This quantity is pre-computed, as it does not depend on the inputs. - // → t is a temporary array of size N+2 - // → C, S are machine words. A pair (C,S) refers to (hi-bits, lo-bits) of a two-word number - // - // As described here https://hackmd.io/@gnark/modular_multiplication we can get rid of one carry chain and simplify: - // (also described in https://eprint.iacr.org/2022/1400.pdf annex) - // - // for i=0 to N-1 - // (A,t[0]) := t[0] + x[0]*y[i] - // m := t[0]*q'[0] mod W - // C,_ := t[0] + m*q[0] - // for j=1 to N-1 - // (A,t[j]) := t[j] + x[j]*y[i] + A - // (C,t[j-1]) := t[j] + m*q[j] + C - // - // t[N-1] = C + A - // - // This optimization saves 5N + 2 additions in the algorithm, and can be used whenever the highest bit - // of the modulus is zero (and not all of the remaining bits are set). - - var t0, t1, t2, t3, t4, t5 uint64 - var u0, u1, u2, u3, u4, u5 uint64 - { - var c0, c1, c2 uint64 - v := x[0] - u0, t0 = bits.Mul64(v, y[0]) - u1, t1 = bits.Mul64(v, y[1]) - u2, t2 = bits.Mul64(v, y[2]) - u3, t3 = bits.Mul64(v, y[3]) - u4, t4 = bits.Mul64(v, y[4]) - u5, t5 = bits.Mul64(v, y[5]) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, 0, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[1] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[2] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[3] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[4] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[5] - u0, c1 = bits.Mul64(v, y[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, y[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, y[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, y[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, y[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, y[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - z[0] = t0 - z[1] = t1 - z[2] = t2 - z[3] = t3 - z[4] = t4 - z[5] = t5 - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } - return z -} - -// Square z = x * x (mod q) -// -// x must be less than q -func (z *Element) Square(x *Element) *Element { - // see Mul for algorithm documentation - - var t0, t1, t2, t3, t4, t5 uint64 - var u0, u1, u2, u3, u4, u5 uint64 - { - var c0, c1, c2 uint64 - v := x[0] - u0, t0 = bits.Mul64(v, x[0]) - u1, t1 = bits.Mul64(v, x[1]) - u2, t2 = bits.Mul64(v, x[2]) - u3, t3 = bits.Mul64(v, x[3]) - u4, t4 = bits.Mul64(v, x[4]) - u5, t5 = bits.Mul64(v, x[5]) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, 0, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[1] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[2] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[3] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[4] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - { - var c0, c1, c2 uint64 - v := x[5] - u0, c1 = bits.Mul64(v, x[0]) - t0, c0 = bits.Add64(c1, t0, 0) - u1, c1 = bits.Mul64(v, x[1]) - t1, c0 = bits.Add64(c1, t1, c0) - u2, c1 = bits.Mul64(v, x[2]) - t2, c0 = bits.Add64(c1, t2, c0) - u3, c1 = bits.Mul64(v, x[3]) - t3, c0 = bits.Add64(c1, t3, c0) - u4, c1 = bits.Mul64(v, x[4]) - t4, c0 = bits.Add64(c1, t4, c0) - u5, c1 = bits.Mul64(v, x[5]) - t5, c0 = bits.Add64(c1, t5, c0) - - c2, _ = bits.Add64(0, 0, c0) - t1, c0 = bits.Add64(u0, t1, 0) - t2, c0 = bits.Add64(u1, t2, c0) - t3, c0 = bits.Add64(u2, t3, c0) - t4, c0 = bits.Add64(u3, t4, c0) - t5, c0 = bits.Add64(u4, t5, c0) - c2, _ = bits.Add64(u5, c2, c0) - - m := qInvNeg * t0 - - u0, c1 = bits.Mul64(m, q0) - _, c0 = bits.Add64(t0, c1, 0) - u1, c1 = bits.Mul64(m, q1) - t0, c0 = bits.Add64(t1, c1, c0) - u2, c1 = bits.Mul64(m, q2) - t1, c0 = bits.Add64(t2, c1, c0) - u3, c1 = bits.Mul64(m, q3) - t2, c0 = bits.Add64(t3, c1, c0) - u4, c1 = bits.Mul64(m, q4) - t3, c0 = bits.Add64(t4, c1, c0) - u5, c1 = bits.Mul64(m, q5) - - t4, c0 = bits.Add64(0, c1, c0) - u5, _ = bits.Add64(u5, 0, c0) - t0, c0 = bits.Add64(u0, t0, 0) - t1, c0 = bits.Add64(u1, t1, c0) - t2, c0 = bits.Add64(u2, t2, c0) - t3, c0 = bits.Add64(u3, t3, c0) - t4, c0 = bits.Add64(u4, t4, c0) - c2, _ = bits.Add64(c2, 0, c0) - t4, c0 = bits.Add64(t5, t4, 0) - t5, _ = bits.Add64(u5, c2, c0) - - } - z[0] = t0 - z[1] = t1 - z[2] = t2 - z[3] = t3 - z[4] = t4 - z[5] = t5 - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - var b uint64 - z[0], b = bits.Sub64(z[0], q0, 0) - z[1], b = bits.Sub64(z[1], q1, b) - z[2], b = bits.Sub64(z[2], q2, b) - z[3], b = bits.Sub64(z[3], q3, b) - z[4], b = bits.Sub64(z[4], q4, b) - z[5], _ = bits.Sub64(z[5], q5, b) - } - return z -} diff --git a/ecc/bw6-756/fr/element_test.go b/ecc/bw6-756/fr/element_test.go deleted file mode 100644 index 1a040e8560..0000000000 --- a/ecc/bw6-756/fr/element_test.go +++ /dev/null @@ -1,2909 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import ( - "crypto/rand" - "encoding/json" - "fmt" - "math/big" - "math/bits" - - mrand "math/rand" - - "testing" - - "github.com/leanovate/gopter" - ggen "github.com/leanovate/gopter/gen" - "github.com/leanovate/gopter/prop" - - "github.com/stretchr/testify/require" -) - -// ------------------------------------------------------------------------------------------------- -// benchmarks -// most benchmarks are rudimentary and should sample a large number of random inputs -// or be run multiple times to ensure it didn't measure the fastest path of the function - -var benchResElement Element - -func BenchmarkElementSelect(b *testing.B) { - var x, y Element - x.SetRandom() - y.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Select(i%3, &x, &y) - } -} - -func BenchmarkElementSetRandom(b *testing.B) { - var x Element - x.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = x.SetRandom() - } -} - -func BenchmarkElementSetBytes(b *testing.B) { - var x Element - x.SetRandom() - bb := x.Bytes() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - benchResElement.SetBytes(bb[:]) - } - -} - -func BenchmarkElementMulByConstants(b *testing.B) { - b.Run("mulBy3", func(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - MulBy3(&benchResElement) - } - }) - b.Run("mulBy5", func(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - MulBy5(&benchResElement) - } - }) - b.Run("mulBy13", func(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - MulBy13(&benchResElement) - } - }) -} - -func BenchmarkElementInverse(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - benchResElement.Inverse(&x) - } - -} - -func BenchmarkElementButterfly(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - Butterfly(&x, &benchResElement) - } -} - -func BenchmarkElementExp(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b1, _ := rand.Int(rand.Reader, Modulus()) - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Exp(x, b1) - } -} - -func BenchmarkElementDouble(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Double(&benchResElement) - } -} - -func BenchmarkElementAdd(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Add(&x, &benchResElement) - } -} - -func BenchmarkElementSub(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Sub(&x, &benchResElement) - } -} - -func BenchmarkElementNeg(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Neg(&benchResElement) - } -} - -func BenchmarkElementDiv(b *testing.B) { - var x Element - x.SetRandom() - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Div(&x, &benchResElement) - } -} - -func BenchmarkElementFromMont(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.fromMont() - } -} - -func BenchmarkElementSquare(b *testing.B) { - benchResElement.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Square(&benchResElement) - } -} - -func BenchmarkElementSqrt(b *testing.B) { - var a Element - a.SetUint64(4) - a.Neg(&a) - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Sqrt(&a) - } -} - -func BenchmarkElementMul(b *testing.B) { - x := Element{ - 13541478318970833666, - 5510290684934426267, - 8467587974331926354, - 13931463632695577534, - 3531303697457869800, - 51529254522778566, - } - benchResElement.SetOne() - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Mul(&benchResElement, &x) - } -} - -func BenchmarkElementCmp(b *testing.B) { - x := Element{ - 13541478318970833666, - 5510290684934426267, - 8467587974331926354, - 13931463632695577534, - 3531303697457869800, - 51529254522778566, - } - benchResElement = x - benchResElement[0] = 0 - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchResElement.Cmp(&x) - } -} - -func TestElementCmp(t *testing.T) { - var x, y Element - - if x.Cmp(&y) != 0 { - t.Fatal("x == y") - } - - one := One() - y.Sub(&y, &one) - - if x.Cmp(&y) != -1 { - t.Fatal("x < y") - } - if y.Cmp(&x) != 1 { - t.Fatal("x < y") - } - - x = y - if x.Cmp(&y) != 0 { - t.Fatal("x == y") - } - - x.Sub(&x, &one) - if x.Cmp(&y) != -1 { - t.Fatal("x < y") - } - if y.Cmp(&x) != 1 { - t.Fatal("x < y") - } -} -func TestElementIsRandom(t *testing.T) { - for i := 0; i < 50; i++ { - var x, y Element - x.SetRandom() - y.SetRandom() - if x.Equal(&y) { - t.Fatal("2 random numbers are unlikely to be equal") - } - } -} - -func TestElementIsUint64(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("reduce should output a result smaller than modulus", prop.ForAll( - func(v uint64) bool { - var e Element - e.SetUint64(v) - - if !e.IsUint64() { - return false - } - - return e.Uint64() == v - }, - ggen.UInt64(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementNegZero(t *testing.T) { - var a, b Element - b.SetZero() - for a.IsZero() { - a.SetRandom() - } - a.Neg(&b) - if !a.IsZero() { - t.Fatal("neg(0) != 0") - } -} - -// ------------------------------------------------------------------------------------------------- -// Gopter tests -// most of them are generated with a template - -const ( - nbFuzzShort = 200 - nbFuzz = 1000 -) - -// special values to be used in tests -var staticTestValues []Element - -func init() { - staticTestValues = append(staticTestValues, Element{}) // zero - staticTestValues = append(staticTestValues, One()) // one - staticTestValues = append(staticTestValues, rSquare) // r² - var e, one Element - one.SetOne() - e.Sub(&qElement, &one) - staticTestValues = append(staticTestValues, e) // q - 1 - e.Double(&one) - staticTestValues = append(staticTestValues, e) // 2 - - { - a := qElement - a[0]-- - staticTestValues = append(staticTestValues, a) - } - staticTestValues = append(staticTestValues, Element{0}) - staticTestValues = append(staticTestValues, Element{0, 0}) - staticTestValues = append(staticTestValues, Element{1}) - staticTestValues = append(staticTestValues, Element{0, 1}) - staticTestValues = append(staticTestValues, Element{2}) - staticTestValues = append(staticTestValues, Element{0, 2}) - - { - a := qElement - a[5]-- - staticTestValues = append(staticTestValues, a) - } - { - a := qElement - a[5]-- - a[0]++ - staticTestValues = append(staticTestValues, a) - } - - { - a := qElement - a[5] = 0 - staticTestValues = append(staticTestValues, a) - } - -} - -func TestElementReduce(t *testing.T) { - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - s := testValues[i] - expected := s - reduce(&s) - _reduceGeneric(&expected) - if !s.Equal(&expected) { - t.Fatal("reduce failed: asm and generic impl don't match") - } - } - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := genFull() - - properties.Property("reduce should output a result smaller than modulus", prop.ForAll( - func(a Element) bool { - b := a - reduce(&a) - _reduceGeneric(&b) - return a.smallerThanModulus() && a.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementEqual(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("x.Equal(&y) iff x == y; likely false for random pairs", prop.ForAll( - func(a testPairElement, b testPairElement) bool { - return a.element.Equal(&b.element) == (a.element == b.element) - }, - genA, - genB, - )) - - properties.Property("x.Equal(&y) if x == y", prop.ForAll( - func(a testPairElement) bool { - b := a.element - return a.element.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementBytes(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("SetBytes(Bytes()) should stay constant", prop.ForAll( - func(a testPairElement) bool { - var b Element - bytes := a.element.Bytes() - b.SetBytes(bytes[:]) - return a.element.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementInverseExp(t *testing.T) { - // inverse must be equal to exp^-2 - exp := Modulus() - exp.Sub(exp, new(big.Int).SetUint64(2)) - - invMatchExp := func(a testPairElement) bool { - var b Element - b.Set(&a.element) - a.element.Inverse(&a.element) - b.Exp(b, exp) - - return a.element.Equal(&b) - } - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - properties := gopter.NewProperties(parameters) - genA := gen() - properties.Property("inv == exp^-2", prop.ForAll(invMatchExp, genA)) - properties.TestingRun(t, gopter.ConsoleReporter(false)) - - parameters.MinSuccessfulTests = 1 - properties = gopter.NewProperties(parameters) - properties.Property("inv(0) == 0", prop.ForAll(invMatchExp, ggen.OneConstOf(testPairElement{}))) - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func mulByConstant(z *Element, c uint8) { - var y Element - y.SetUint64(uint64(c)) - z.Mul(z, &y) -} - -func TestElementMulByConstants(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - implemented := []uint8{0, 1, 2, 3, 5, 13} - properties.Property("mulByConstant", prop.ForAll( - func(a testPairElement) bool { - for _, c := range implemented { - var constant Element - constant.SetUint64(uint64(c)) - - b := a.element - b.Mul(&b, &constant) - - aa := a.element - mulByConstant(&aa, c) - - if !aa.Equal(&b) { - return false - } - } - - return true - }, - genA, - )) - - properties.Property("MulBy3(x) == Mul(x, 3)", prop.ForAll( - func(a testPairElement) bool { - var constant Element - constant.SetUint64(3) - - b := a.element - b.Mul(&b, &constant) - - MulBy3(&a.element) - - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("MulBy5(x) == Mul(x, 5)", prop.ForAll( - func(a testPairElement) bool { - var constant Element - constant.SetUint64(5) - - b := a.element - b.Mul(&b, &constant) - - MulBy5(&a.element) - - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("MulBy13(x) == Mul(x, 13)", prop.ForAll( - func(a testPairElement) bool { - var constant Element - constant.SetUint64(13) - - b := a.element - b.Mul(&b, &constant) - - MulBy13(&a.element) - - return a.element.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementLegendre(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("legendre should output same result than big.Int.Jacobi", prop.ForAll( - func(a testPairElement) bool { - return a.element.Legendre() == big.Jacobi(&a.bigint, Modulus()) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementBitLen(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("BitLen should output same result than big.Int.BitLen", prop.ForAll( - func(a testPairElement) bool { - return a.element.fromMont().BitLen() == a.bigint.BitLen() - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementButterflies(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("butterfly0 == a -b; a +b", prop.ForAll( - func(a, b testPairElement) bool { - a0, b0 := a.element, b.element - - _butterflyGeneric(&a.element, &b.element) - Butterfly(&a0, &b0) - - return a.element.Equal(&a0) && b.element.Equal(&b0) - }, - genA, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementLexicographicallyLargest(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("element.Cmp should match LexicographicallyLargest output", prop.ForAll( - func(a testPairElement) bool { - var negA Element - negA.Neg(&a.element) - - cmpResult := a.element.Cmp(&negA) - lResult := a.element.LexicographicallyLargest() - - if lResult && cmpResult == 1 { - return true - } - if !lResult && cmpResult != 1 { - return true - } - return false - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestElementAdd(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Add: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Add(&a.element, &b.element) - a.element.Add(&a.element, &b.element) - b.element.Add(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Add: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Add(&a.element, &b.element) - - var d, e big.Int - d.Add(&a.bigint, &b.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Add(&a.element, &r) - d.Add(&a.bigint, &rb).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Add: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Add(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Add(&a, &b) - d.Add(&aBig, &bBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Add failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementSub(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Sub: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Sub(&a.element, &b.element) - a.element.Sub(&a.element, &b.element) - b.element.Sub(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Sub: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Sub(&a.element, &b.element) - - var d, e big.Int - d.Sub(&a.bigint, &b.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Sub(&a.element, &r) - d.Sub(&a.bigint, &rb).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Sub: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Sub(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Sub(&a, &b) - d.Sub(&aBig, &bBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Sub failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementMul(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Mul: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Mul(&a.element, &b.element) - a.element.Mul(&a.element, &b.element) - b.element.Mul(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Mul: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Mul(&a.element, &b.element) - - var d, e big.Int - d.Mul(&a.bigint, &b.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Mul(&a.element, &r) - d.Mul(&a.bigint, &rb).Mod(&d, Modulus()) - - // checking generic impl against asm path - var cGeneric Element - _mulGeneric(&cGeneric, &a.element, &r) - if !cGeneric.Equal(&c) { - // need to give context to failing error. - return false - } - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Mul: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Mul(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - properties.Property("Mul: assembly implementation must be consistent with generic one", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - c.Mul(&a.element, &b.element) - _mulGeneric(&d, &a.element, &b.element) - return c.Equal(&d) - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Mul(&a, &b) - d.Mul(&aBig, &bBig).Mod(&d, Modulus()) - - // checking asm against generic impl - var cGeneric Element - _mulGeneric(&cGeneric, &a, &b) - if !cGeneric.Equal(&c) { - t.Fatal("Mul failed special test values: asm and generic impl don't match") - } - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Mul failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementDiv(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Div: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Div(&a.element, &b.element) - a.element.Div(&a.element, &b.element) - b.element.Div(&d, &b.element) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Div: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Div(&a.element, &b.element) - - var d, e big.Int - d.ModInverse(&b.bigint, Modulus()) - d.Mul(&d, &a.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Div(&a.element, &r) - d.ModInverse(&rb, Modulus()) - d.Mul(&d, &a.bigint).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Div: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Div(&a.element, &b.element) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Div(&a, &b) - d.ModInverse(&bBig, Modulus()) - d.Mul(&d, &aBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Div failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementExp(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genB := gen() - - properties.Property("Exp: having the receiver as operand should output the same result", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - d.Set(&a.element) - - c.Exp(a.element, &b.bigint) - a.element.Exp(a.element, &b.bigint) - b.element.Exp(d, &b.bigint) - - return a.element.Equal(&b.element) && a.element.Equal(&c) && b.element.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("Exp: operation result must match big.Int result", prop.ForAll( - func(a, b testPairElement) bool { - { - var c Element - - c.Exp(a.element, &b.bigint) - - var d, e big.Int - d.Exp(&a.bigint, &b.bigint, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - - // fixed elements - // a is random - // r takes special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - r := testValues[i] - var d, e, rb big.Int - r.BigInt(&rb) - - var c Element - c.Exp(a.element, &rb) - d.Exp(&a.bigint, &rb, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - return false - } - } - return true - }, - genA, - genB, - )) - - properties.Property("Exp: operation result must be smaller than modulus", prop.ForAll( - func(a, b testPairElement) bool { - var c Element - - c.Exp(a.element, &b.bigint) - - return c.smallerThanModulus() - }, - genA, - genB, - )) - - specialValueTest := func() { - // test special values against special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - for j := range testValues { - b := testValues[j] - var bBig, d, e big.Int - b.BigInt(&bBig) - - var c Element - c.Exp(a, &bBig) - d.Exp(&aBig, &bBig, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Exp failed special test values") - } - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementSquare(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Square: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Square(&a.element) - a.element.Square(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Square: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Square(&a.element) - - var d, e big.Int - d.Mul(&a.bigint, &a.bigint).Mod(&d, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Square: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Square(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Square(&a) - - var d, e big.Int - d.Mul(&aBig, &aBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Square failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementInverse(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Inverse: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Inverse(&a.element) - a.element.Inverse(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Inverse: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Inverse(&a.element) - - var d, e big.Int - d.ModInverse(&a.bigint, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Inverse: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Inverse(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Inverse(&a) - - var d, e big.Int - d.ModInverse(&aBig, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Inverse failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementSqrt(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Sqrt: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - b := a.element - - b.Sqrt(&a.element) - a.element.Sqrt(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Sqrt: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Sqrt(&a.element) - - var d, e big.Int - d.ModSqrt(&a.bigint, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Sqrt: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Sqrt(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Sqrt(&a) - - var d, e big.Int - d.ModSqrt(&aBig, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Sqrt failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementDouble(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Double: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Double(&a.element) - a.element.Double(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Double: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Double(&a.element) - - var d, e big.Int - d.Lsh(&a.bigint, 1).Mod(&d, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Double: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Double(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Double(&a) - - var d, e big.Int - d.Lsh(&aBig, 1).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Double failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementNeg(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Neg: having the receiver as operand should output the same result", prop.ForAll( - func(a testPairElement) bool { - - var b Element - - b.Neg(&a.element) - a.element.Neg(&a.element) - return a.element.Equal(&b) - }, - genA, - )) - - properties.Property("Neg: operation result must match big.Int result", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Neg(&a.element) - - var d, e big.Int - d.Neg(&a.bigint).Mod(&d, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, - )) - - properties.Property("Neg: operation result must be smaller than modulus", prop.ForAll( - func(a testPairElement) bool { - var c Element - c.Neg(&a.element) - return c.smallerThanModulus() - }, - genA, - )) - - specialValueTest := func() { - // test special values - testValues := make([]Element, len(staticTestValues)) - copy(testValues, staticTestValues) - - for i := range testValues { - a := testValues[i] - var aBig big.Int - a.BigInt(&aBig) - var c Element - c.Neg(&a) - - var d, e big.Int - d.Neg(&aBig).Mod(&d, Modulus()) - - if c.BigInt(&e).Cmp(&d) != 0 { - t.Fatal("Neg failed special test values") - } - } - } - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - specialValueTest() - -} - -func TestElementFixedExp(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - var ( - _bLegendreExponentElement *big.Int - _bSqrtExponentElement *big.Int - ) - - _bLegendreExponentElement, _ = new(big.Int).SetString("1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca4510000000000", 16) - const sqrtExponentElement = "fbac1059a1346414f2d74903b441e8a10167ad91c408c9a60370d83429275ff3a5fddaa08b0000265228" - _bSqrtExponentElement, _ = new(big.Int).SetString(sqrtExponentElement, 16) - - genA := gen() - - properties.Property(fmt.Sprintf("expBySqrtExp must match Exp(%s)", sqrtExponentElement), prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.expBySqrtExp(c) - d.Exp(d, _bSqrtExponentElement) - return c.Equal(&d) - }, - genA, - )) - - properties.Property("expByLegendreExp must match Exp(1f75820b34268c829e5ae92076883d14202cf5b238811934c06e1b068524ebfe74bfbb5411600004ca4510000000000)", prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.expByLegendreExp(c) - d.Exp(d, _bLegendreExponentElement) - return c.Equal(&d) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementHalve(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - var twoInv Element - twoInv.SetUint64(2) - twoInv.Inverse(&twoInv) - - properties.Property("z.Halve must match z / 2", prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.Halve() - d.Mul(&d, &twoInv) - return c.Equal(&d) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func combineSelectionArguments(c int64, z int8) int { - if z%3 == 0 { - return 0 - } - return int(c) -} - -func TestElementSelect(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := genFull() - genB := genFull() - genC := ggen.Int64() //the condition - genZ := ggen.Int8() //to make zeros artificially more likely - - properties.Property("Select: must select correctly", prop.ForAll( - func(a, b Element, cond int64, z int8) bool { - condC := combineSelectionArguments(cond, z) - - var c Element - c.Select(condC, &a, &b) - - if condC == 0 { - return c.Equal(&a) - } - return c.Equal(&b) - }, - genA, - genB, - genC, - genZ, - )) - - properties.Property("Select: having the receiver as operand should output the same result", prop.ForAll( - func(a, b Element, cond int64, z int8) bool { - condC := combineSelectionArguments(cond, z) - - var c, d Element - d.Set(&a) - c.Select(condC, &a, &b) - a.Select(condC, &a, &b) - b.Select(condC, &d, &b) - return a.Equal(&b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - genC, - genZ, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementSetInt64(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("z.SetInt64 must match z.SetString", prop.ForAll( - func(a testPairElement, v int64) bool { - c := a.element - d := a.element - - c.SetInt64(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, ggen.Int64(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementSetInterface(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - genInt := ggen.Int - genInt8 := ggen.Int8 - genInt16 := ggen.Int16 - genInt32 := ggen.Int32 - genInt64 := ggen.Int64 - - genUint := ggen.UInt - genUint8 := ggen.UInt8 - genUint16 := ggen.UInt16 - genUint32 := ggen.UInt32 - genUint64 := ggen.UInt64 - - properties.Property("z.SetInterface must match z.SetString with int8", prop.ForAll( - func(a testPairElement, v int8) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt8(), - )) - - properties.Property("z.SetInterface must match z.SetString with int16", prop.ForAll( - func(a testPairElement, v int16) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt16(), - )) - - properties.Property("z.SetInterface must match z.SetString with int32", prop.ForAll( - func(a testPairElement, v int32) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt32(), - )) - - properties.Property("z.SetInterface must match z.SetString with int64", prop.ForAll( - func(a testPairElement, v int64) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt64(), - )) - - properties.Property("z.SetInterface must match z.SetString with int", prop.ForAll( - func(a testPairElement, v int) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genInt(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint8", prop.ForAll( - func(a testPairElement, v uint8) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint8(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint16", prop.ForAll( - func(a testPairElement, v uint16) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint16(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint32", prop.ForAll( - func(a testPairElement, v uint32) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint32(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint64", prop.ForAll( - func(a testPairElement, v uint64) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint64(), - )) - - properties.Property("z.SetInterface must match z.SetString with uint", prop.ForAll( - func(a testPairElement, v uint) bool { - c := a.element - d := a.element - - c.SetInterface(v) - d.SetString(fmt.Sprintf("%v", v)) - - return c.Equal(&d) - }, - genA, genUint(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - - { - assert := require.New(t) - var e Element - r, err := e.SetInterface(nil) - assert.Nil(r) - assert.Error(err) - - var ptE *Element - var ptB *big.Int - - r, err = e.SetInterface(ptE) - assert.Nil(r) - assert.Error(err) - ptE = new(Element).SetOne() - r, err = e.SetInterface(ptE) - assert.NoError(err) - assert.True(r.IsOne()) - - r, err = e.SetInterface(ptB) - assert.Nil(r) - assert.Error(err) - - } -} - -func TestElementNegativeExp(t *testing.T) { - t.Parallel() - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("x⁻ᵏ == 1/xᵏ", prop.ForAll( - func(a, b testPairElement) bool { - - var nb, d, e big.Int - nb.Neg(&b.bigint) - - var c Element - c.Exp(a.element, &nb) - - d.Exp(&a.bigint, &nb, Modulus()) - - return c.BigInt(&e).Cmp(&d) == 0 - }, - genA, genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementNewElement(t *testing.T) { - assert := require.New(t) - - t.Parallel() - - e := NewElement(1) - assert.True(e.IsOne()) - - e = NewElement(0) - assert.True(e.IsZero()) -} - -func TestElementBatchInvert(t *testing.T) { - assert := require.New(t) - - t.Parallel() - - // ensure batchInvert([x]) == invert(x) - for i := int64(-1); i <= 2; i++ { - var e, eInv Element - e.SetInt64(i) - eInv.Inverse(&e) - - a := []Element{e} - aInv := BatchInvert(a) - - assert.True(aInv[0].Equal(&eInv), "batchInvert != invert") - - } - - // test x * x⁻¹ == 1 - tData := [][]int64{ - {-1, 1, 2, 3}, - {0, -1, 1, 2, 3, 0}, - {0, -1, 1, 0, 2, 3, 0}, - {-1, 1, 0, 2, 3}, - {0, 0, 1}, - {1, 0, 0}, - {0, 0, 0}, - } - - for _, t := range tData { - a := make([]Element, len(t)) - for i := 0; i < len(a); i++ { - a[i].SetInt64(t[i]) - } - - aInv := BatchInvert(a) - - assert.True(len(aInv) == len(a)) - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - assert.True(aInv[i].IsZero(), "0⁻¹ != 0") - } else { - assert.True(a[i].Mul(&a[i], &aInv[i]).IsOne(), "x * x⁻¹ != 1") - } - } - } - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("batchInvert --> x * x⁻¹ == 1", prop.ForAll( - func(tp testPairElement, r uint8) bool { - - a := make([]Element, r) - if r != 0 { - a[0] = tp.element - - } - one := One() - for i := 1; i < len(a); i++ { - a[i].Add(&a[i-1], &one) - } - - aInv := BatchInvert(a) - - assert.True(len(aInv) == len(a)) - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - if !aInv[i].IsZero() { - return false - } - } else { - if !a[i].Mul(&a[i], &aInv[i]).IsOne() { - return false - } - } - } - return true - }, - genA, ggen.UInt8(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementFromMont(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := gen() - - properties.Property("Assembly implementation must be consistent with generic one", prop.ForAll( - func(a testPairElement) bool { - c := a.element - d := a.element - c.fromMont() - _fromMontGeneric(&d) - return c.Equal(&d) - }, - genA, - )) - - properties.Property("x.fromMont().toMont() == x", prop.ForAll( - func(a testPairElement) bool { - c := a.element - c.fromMont().toMont() - return c.Equal(&a.element) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementJSON(t *testing.T) { - assert := require.New(t) - - type S struct { - A Element - B [3]Element - C *Element - D *Element - } - - // encode to JSON - var s S - s.A.SetString("-1") - s.B[2].SetUint64(42) - s.D = new(Element).SetUint64(8000) - - encoded, err := json.Marshal(&s) - assert.NoError(err) - // we may need to adjust "42" and "8000" values for some moduli; see Text() method for more details. - formatValue := func(v int64) string { - var a big.Int - a.SetInt64(v) - a.Mod(&a, Modulus()) - const maxUint16 = 65535 - var aNeg big.Int - aNeg.Neg(&a).Mod(&aNeg, Modulus()) - if aNeg.Uint64() != 0 && aNeg.Uint64() <= maxUint16 { - return "-" + aNeg.Text(10) - } - return a.Text(10) - } - expected := fmt.Sprintf("{\"A\":%s,\"B\":[0,0,%s],\"C\":null,\"D\":%s}", formatValue(-1), formatValue(42), formatValue(8000)) - assert.Equal(expected, string(encoded)) - - // decode valid - var decoded S - err = json.Unmarshal([]byte(expected), &decoded) - assert.NoError(err) - - assert.Equal(s, decoded, "element -> json -> element round trip failed") - - // decode hex and string values - withHexValues := "{\"A\":\"-1\",\"B\":[0,\"0x00000\",\"0x2A\"],\"C\":null,\"D\":\"8000\"}" - - var decodedS S - err = json.Unmarshal([]byte(withHexValues), &decodedS) - assert.NoError(err) - - assert.Equal(s, decodedS, " json with strings -> element failed") - -} - -type testPairElement struct { - element Element - bigint big.Int -} - -func gen() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var g testPairElement - - g.element = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - if qElement[5] != ^uint64(0) { - g.element[5] %= (qElement[5] + 1) - } - - for !g.element.smallerThanModulus() { - g.element = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - if qElement[5] != ^uint64(0) { - g.element[5] %= (qElement[5] + 1) - } - } - - g.element.BigInt(&g.bigint) - genResult := gopter.NewGenResult(g, gopter.NoShrinker) - return genResult - } -} - -func genFull() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - - genRandomFq := func() Element { - var g Element - - g = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - - if qElement[5] != ^uint64(0) { - g[5] %= (qElement[5] + 1) - } - - for !g.smallerThanModulus() { - g = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - if qElement[5] != ^uint64(0) { - g[5] %= (qElement[5] + 1) - } - } - - return g - } - a := genRandomFq() - - var carry uint64 - a[0], carry = bits.Add64(a[0], qElement[0], carry) - a[1], carry = bits.Add64(a[1], qElement[1], carry) - a[2], carry = bits.Add64(a[2], qElement[2], carry) - a[3], carry = bits.Add64(a[3], qElement[3], carry) - a[4], carry = bits.Add64(a[4], qElement[4], carry) - a[5], _ = bits.Add64(a[5], qElement[5], carry) - - genResult := gopter.NewGenResult(a, gopter.NoShrinker) - return genResult - } -} - -func (z *Element) matchVeryBigInt(aHi uint64, aInt *big.Int) error { - var modulus big.Int - var aIntMod big.Int - modulus.SetInt64(1) - modulus.Lsh(&modulus, (Limbs+1)*64) - aIntMod.Mod(aInt, &modulus) - - slice := append(z[:], aHi) - - return bigIntMatchUint64Slice(&aIntMod, slice) -} - -// TODO: Phase out in favor of property based testing -func (z *Element) assertMatchVeryBigInt(t *testing.T, aHi uint64, aInt *big.Int) { - - if err := z.matchVeryBigInt(aHi, aInt); err != nil { - t.Error(err) - } -} - -// bigIntMatchUint64Slice is a test helper to match big.Int words against a uint64 slice -func bigIntMatchUint64Slice(aInt *big.Int, a []uint64) error { - - words := aInt.Bits() - - const steps = 64 / bits.UintSize - const filter uint64 = 0xFFFFFFFFFFFFFFFF >> (64 - bits.UintSize) - for i := 0; i < len(a)*steps; i++ { - - var wI big.Word - - if i < len(words) { - wI = words[i] - } - - aI := a[i/steps] >> ((i * bits.UintSize) % 64) - aI &= filter - - if uint64(wI) != aI { - return fmt.Errorf("bignum mismatch: disagreement on word %d: %x ≠ %x; %d ≠ %d", i, uint64(wI), aI, uint64(wI), aI) - } - } - - return nil -} - -func TestElementInversionApproximation(t *testing.T) { - var x Element - for i := 0; i < 1000; i++ { - x.SetRandom() - - // Normally small elements are unlikely. Here we give them a higher chance - xZeros := mrand.Int() % Limbs //#nosec G404 weak rng is fine here - for j := 1; j < xZeros; j++ { - x[Limbs-j] = 0 - } - - a := approximate(&x, x.BitLen()) - aRef := approximateRef(&x) - - if a != aRef { - t.Error("Approximation mismatch") - } - } -} - -func TestElementInversionCorrectionFactorFormula(t *testing.T) { - const kLimbs = k * Limbs - const power = kLimbs*6 + invIterationsN*(kLimbs-k+1) - factorInt := big.NewInt(1) - factorInt.Lsh(factorInt, power) - factorInt.Mod(factorInt, Modulus()) - - var refFactorInt big.Int - inversionCorrectionFactor := Element{ - inversionCorrectionFactorWord0, - inversionCorrectionFactorWord1, - inversionCorrectionFactorWord2, - inversionCorrectionFactorWord3, - inversionCorrectionFactorWord4, - inversionCorrectionFactorWord5, - } - inversionCorrectionFactor.toBigInt(&refFactorInt) - - if refFactorInt.Cmp(factorInt) != 0 { - t.Error("mismatch") - } -} - -func TestElementLinearComb(t *testing.T) { - var x Element - var y Element - - for i := 0; i < 1000; i++ { - x.SetRandom() - y.SetRandom() - testLinearComb(t, &x, mrand.Int63(), &y, mrand.Int63()) //#nosec G404 weak rng is fine here - } -} - -// Probably unnecessary post-dev. In case the output of inv is wrong, this checks whether it's only off by a constant factor. -func TestElementInversionCorrectionFactor(t *testing.T) { - - // (1/x)/inv(x) = (1/1)/inv(1) ⇔ inv(1) = x inv(x) - - var one Element - var oneInv Element - one.SetOne() - oneInv.Inverse(&one) - - for i := 0; i < 100; i++ { - var x Element - var xInv Element - x.SetRandom() - xInv.Inverse(&x) - - x.Mul(&x, &xInv) - if !x.Equal(&oneInv) { - t.Error("Correction factor is inconsistent") - } - } - - if !oneInv.Equal(&one) { - var i big.Int - oneInv.BigInt(&i) // no montgomery - i.ModInverse(&i, Modulus()) - var fac Element - fac.setBigInt(&i) // back to montgomery - - var facTimesFac Element - facTimesFac.Mul(&fac, &Element{ - inversionCorrectionFactorWord0, - inversionCorrectionFactorWord1, - inversionCorrectionFactorWord2, - inversionCorrectionFactorWord3, - inversionCorrectionFactorWord4, - inversionCorrectionFactorWord5, - }) - - t.Error("Correction factor is consistently off by", fac, "Should be", facTimesFac) - } -} - -func TestElementBigNumNeg(t *testing.T) { - var a Element - aHi := negL(&a, 0) - if !a.IsZero() || aHi != 0 { - t.Error("-0 != 0") - } -} - -func TestElementBigNumWMul(t *testing.T) { - var x Element - - for i := 0; i < 1000; i++ { - x.SetRandom() - w := mrand.Int63() //#nosec G404 weak rng is fine here - testBigNumWMul(t, &x, w) - } -} - -func TestElementVeryBigIntConversion(t *testing.T) { - xHi := mrand.Uint64() //#nosec G404 weak rng is fine here - var x Element - x.SetRandom() - var xInt big.Int - x.toVeryBigIntSigned(&xInt, xHi) - x.assertMatchVeryBigInt(t, xHi, &xInt) -} - -type veryBigInt struct { - asInt big.Int - low Element - hi uint64 -} - -// genVeryBigIntSigned if sign == 0, no sign is forced -func genVeryBigIntSigned(sign int) gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var g veryBigInt - - g.low = Element{ - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - genParams.NextUint64(), - } - - g.hi = genParams.NextUint64() - - if sign < 0 { - g.hi |= signBitSelector - } else if sign > 0 { - g.hi &= ^signBitSelector - } - - g.low.toVeryBigIntSigned(&g.asInt, g.hi) - - genResult := gopter.NewGenResult(g, gopter.NoShrinker) - return genResult - } -} - -func TestElementMontReduce(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - gen := genVeryBigIntSigned(0) - - properties.Property("Montgomery reduction is correct", prop.ForAll( - func(g veryBigInt) bool { - var res Element - var resInt big.Int - - montReduce(&resInt, &g.asInt) - res.montReduceSigned(&g.low, g.hi) - - return res.matchVeryBigInt(0, &resInt) == nil - }, - gen, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElementMontReduceMultipleOfR(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - gen := ggen.UInt64() - - properties.Property("Montgomery reduction is correct", prop.ForAll( - func(hi uint64) bool { - var zero, res Element - var asInt, resInt big.Int - - zero.toVeryBigIntSigned(&asInt, hi) - - montReduce(&resInt, &asInt) - res.montReduceSigned(&zero, hi) - - return res.matchVeryBigInt(0, &resInt) == nil - }, - gen, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestElement0Inverse(t *testing.T) { - var x Element - x.Inverse(&x) - if !x.IsZero() { - t.Fail() - } -} - -// TODO: Tests like this (update factor related) are common to all fields. Move them to somewhere non-autogen -func TestUpdateFactorSubtraction(t *testing.T) { - for i := 0; i < 1000; i++ { - - f0, g0 := randomizeUpdateFactors() - f1, g1 := randomizeUpdateFactors() - - for f0-f1 > 1<<31 || f0-f1 <= -1<<31 { - f1 /= 2 - } - - for g0-g1 > 1<<31 || g0-g1 <= -1<<31 { - g1 /= 2 - } - - c0 := updateFactorsCompose(f0, g0) - c1 := updateFactorsCompose(f1, g1) - - cRes := c0 - c1 - fRes, gRes := updateFactorsDecompose(cRes) - - if fRes != f0-f1 || gRes != g0-g1 { - t.Error(i) - } - } -} - -func TestUpdateFactorsDouble(t *testing.T) { - for i := 0; i < 1000; i++ { - f, g := randomizeUpdateFactors() - - if f > 1<<30 || f < (-1<<31+1)/2 { - f /= 2 - if g <= 1<<29 && g >= (-1<<31+1)/4 { - g *= 2 //g was kept small on f's account. Now that we're halving f, we can double g - } - } - - if g > 1<<30 || g < (-1<<31+1)/2 { - g /= 2 - - if f <= 1<<29 && f >= (-1<<31+1)/4 { - f *= 2 //f was kept small on g's account. Now that we're halving g, we can double f - } - } - - c := updateFactorsCompose(f, g) - cD := c * 2 - fD, gD := updateFactorsDecompose(cD) - - if fD != 2*f || gD != 2*g { - t.Error(i) - } - } -} - -func TestUpdateFactorsNeg(t *testing.T) { - var fMistake bool - for i := 0; i < 1000; i++ { - f, g := randomizeUpdateFactors() - - if f == 0x80000000 || g == 0x80000000 { - // Update factors this large can only have been obtained after 31 iterations and will therefore never be negated - // We don't have capacity to store -2³¹ - // Repeat this iteration - i-- - continue - } - - c := updateFactorsCompose(f, g) - nc := -c - nf, ng := updateFactorsDecompose(nc) - fMistake = fMistake || nf != -f - if nf != -f || ng != -g { - t.Errorf("Mismatch iteration #%d:\n%d, %d ->\n %d -> %d ->\n %d, %d\n Inputs in hex: %X, %X", - i, f, g, c, nc, nf, ng, f, g) - } - } - if fMistake { - t.Error("Mistake with f detected") - } else { - t.Log("All good with f") - } -} - -func TestUpdateFactorsNeg0(t *testing.T) { - c := updateFactorsCompose(0, 0) - t.Logf("c(0,0) = %X", c) - cn := -c - - if c != cn { - t.Error("Negation of zero update factors should yield the same result.") - } -} - -func TestUpdateFactorDecomposition(t *testing.T) { - var negSeen bool - - for i := 0; i < 1000; i++ { - - f, g := randomizeUpdateFactors() - - if f <= -(1<<31) || f > 1<<31 { - t.Fatal("f out of range") - } - - negSeen = negSeen || f < 0 - - c := updateFactorsCompose(f, g) - - fBack, gBack := updateFactorsDecompose(c) - - if f != fBack || g != gBack { - t.Errorf("(%d, %d) -> %d -> (%d, %d)\n", f, g, c, fBack, gBack) - } - } - - if !negSeen { - t.Fatal("No negative f factors") - } -} - -func TestUpdateFactorInitialValues(t *testing.T) { - - f0, g0 := updateFactorsDecompose(updateFactorIdentityMatrixRow0) - f1, g1 := updateFactorsDecompose(updateFactorIdentityMatrixRow1) - - if f0 != 1 || g0 != 0 || f1 != 0 || g1 != 1 { - t.Error("Update factor initial value constants are incorrect") - } -} - -func TestUpdateFactorsRandomization(t *testing.T) { - var maxLen int - - //t.Log("|f| + |g| is not to exceed", 1 << 31) - for i := 0; i < 1000; i++ { - f, g := randomizeUpdateFactors() - lf, lg := abs64T32(f), abs64T32(g) - absSum := lf + lg - if absSum >= 1<<31 { - - if absSum == 1<<31 { - maxLen++ - } else { - t.Error(i, "Sum of absolute values too large, f =", f, ",g =", g, ",|f| + |g| =", absSum) - } - } - } - - if maxLen == 0 { - t.Error("max len not observed") - } else { - t.Log(maxLen, "maxLens observed") - } -} - -func randomizeUpdateFactor(absLimit uint32) int64 { - const maxSizeLikelihood = 10 - maxSize := mrand.Intn(maxSizeLikelihood) //#nosec G404 weak rng is fine here - - absLimit64 := int64(absLimit) - var f int64 - switch maxSize { - case 0: - f = absLimit64 - case 1: - f = -absLimit64 - default: - f = int64(mrand.Uint64()%(2*uint64(absLimit64)+1)) - absLimit64 //#nosec G404 weak rng is fine here - } - - if f > 1<<31 { - return 1 << 31 - } else if f < -1<<31+1 { - return -1<<31 + 1 - } - - return f -} - -func abs64T32(f int64) uint32 { - if f >= 1<<32 || f < -1<<32 { - panic("f out of range") - } - - if f < 0 { - return uint32(-f) - } - return uint32(f) -} - -func randomizeUpdateFactors() (int64, int64) { - var f [2]int64 - b := mrand.Int() % 2 //#nosec G404 weak rng is fine here - - f[b] = randomizeUpdateFactor(1 << 31) - - //As per the paper, |f| + |g| \le 2³¹. - f[1-b] = randomizeUpdateFactor(1<<31 - abs64T32(f[b])) - - //Patching another edge case - if f[0]+f[1] == -1<<31 { - b = mrand.Int() % 2 //#nosec G404 weak rng is fine here - f[b]++ - } - - return f[0], f[1] -} - -func testLinearComb(t *testing.T, x *Element, xC int64, y *Element, yC int64) { - - var p1 big.Int - x.toBigInt(&p1) - p1.Mul(&p1, big.NewInt(xC)) - - var p2 big.Int - y.toBigInt(&p2) - p2.Mul(&p2, big.NewInt(yC)) - - p1.Add(&p1, &p2) - p1.Mod(&p1, Modulus()) - montReduce(&p1, &p1) - - var z Element - z.linearComb(x, xC, y, yC) - z.assertMatchVeryBigInt(t, 0, &p1) -} - -func testBigNumWMul(t *testing.T, a *Element, c int64) { - var aHi uint64 - var aTimes Element - aHi = aTimes.mulWNonModular(a, c) - - assertMulProduct(t, a, c, &aTimes, aHi) -} - -func updateFactorsCompose(f int64, g int64) int64 { - return f + g<<32 -} - -var rInv big.Int - -func montReduce(res *big.Int, x *big.Int) { - if rInv.BitLen() == 0 { // initialization - rInv.SetUint64(1) - rInv.Lsh(&rInv, Limbs*64) - rInv.ModInverse(&rInv, Modulus()) - } - res.Mul(x, &rInv) - res.Mod(res, Modulus()) -} - -func (z *Element) toVeryBigIntUnsigned(i *big.Int, xHi uint64) { - z.toBigInt(i) - var upperWord big.Int - upperWord.SetUint64(xHi) - upperWord.Lsh(&upperWord, Limbs*64) - i.Add(&upperWord, i) -} - -func (z *Element) toVeryBigIntSigned(i *big.Int, xHi uint64) { - z.toVeryBigIntUnsigned(i, xHi) - if signBitSelector&xHi != 0 { - twosCompModulus := big.NewInt(1) - twosCompModulus.Lsh(twosCompModulus, (Limbs+1)*64) - i.Sub(i, twosCompModulus) - } -} - -func assertMulProduct(t *testing.T, x *Element, c int64, result *Element, resultHi uint64) big.Int { - var xInt big.Int - x.toBigInt(&xInt) - - xInt.Mul(&xInt, big.NewInt(c)) - - result.assertMatchVeryBigInt(t, resultHi, &xInt) - return xInt -} - -func approximateRef(x *Element) uint64 { - - var asInt big.Int - x.toBigInt(&asInt) - n := x.BitLen() - - if n <= 64 { - return asInt.Uint64() - } - - modulus := big.NewInt(1 << 31) - var lo big.Int - lo.Mod(&asInt, modulus) - - modulus.Lsh(modulus, uint(n-64)) - var hi big.Int - hi.Div(&asInt, modulus) - hi.Lsh(&hi, 31) - - hi.Add(&hi, &lo) - return hi.Uint64() -} diff --git a/ecc/bw6-756/fr/fft/bitreverse.go b/ecc/bw6-756/fr/fft/bitreverse.go deleted file mode 100644 index 1131a706ae..0000000000 --- a/ecc/bw6-756/fr/fft/bitreverse.go +++ /dev/null @@ -1,574 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fft - -import ( - "math/bits" - "runtime" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) - -// BitReverse applies the bit-reversal permutation to v. -// len(v) must be a power of 2 -func BitReverse(v []fr.Element) { - n := uint64(len(v)) - if bits.OnesCount64(n) != 1 { - panic("len(a) must be a power of 2") - } - - if runtime.GOARCH == "arm64" { - bitReverseNaive(v) - } else { - bitReverseCobra(v) - } -} - -// bitReverseNaive applies the bit-reversal permutation to v. -// len(v) must be a power of 2 -func bitReverseNaive(v []fr.Element) { - n := uint64(len(v)) - nn := uint64(64 - bits.TrailingZeros64(n)) - - for i := uint64(0); i < n; i++ { - iRev := bits.Reverse64(i) >> nn - if iRev > i { - v[i], v[iRev] = v[iRev], v[i] - } - } -} - -// bitReverseCobraInPlace applies the bit-reversal permutation to v. -// len(v) must be a power of 2 -// This is derived from: -// -// - Towards an Optimal Bit-Reversal Permutation Program -// Larry Carter and Kang Su Gatlin, 1998 -// https://csaws.cs.technion.ac.il/~itai/Courses/Cache/bit.pdf -// -// - Practically efficient methods for performing bit-reversed -// permutation in C++11 on the x86-64 architecture -// Knauth, Adas, Whitfield, Wang, Ickler, Conrad, Serang, 2017 -// https://arxiv.org/pdf/1708.01873.pdf -// -// - and more specifically, constantine implementation: -// https://github.com/mratsim/constantine/blob/d51699248db04e29c7b1ad97e0bafa1499db00b5/constantine/math/polynomials/fft.nim#L205 -// by Mamy Ratsimbazafy (@mratsim). -func bitReverseCobraInPlace(v []fr.Element) { - logN := uint64(bits.Len64(uint64(len(v))) - 1) - logTileSize := deriveLogTileSize(logN) - logBLen := logN - 2*logTileSize - bLen := uint64(1) << logBLen - bShift := logBLen + logTileSize - tileSize := uint64(1) << logTileSize - - // rough idea; - // bit reversal permutation naive implementation may have some cache associativity issues, - // since we are accessing elements by strides of powers of 2. - // on large inputs, this is noticeable and can be improved by using a t buffer. - // idea is for t buffer to be small enough to fit in cache. - // in the first inner loop, we copy the elements of v into t in a bit-reversed order. - // in the subsequent inner loops, accesses have much better cache locality than the naive implementation. - // hence even if we apparently do more work (swaps / copies), we are faster. - // - // on arm64 (and particularly on M1 macs), this is not noticeable, and the naive implementation is faster, - // in most cases. - // on x86 (and particularly on aws hpc6a) this is noticeable, and the t buffer implementation is faster (up to 3x). - // - // optimal choice for the tile size is cache dependent; in theory, we want the t buffer to fit in the L1 cache; - // in practice, a common size for L1 is 64kb, a field element is 32bytes or more. - // hence we can fit 2k elements in the L1 cache, which corresponds to a tile size of 2**5 with some margin for cache conflicts. - // - // for most sizes of interest, this tile size choice doesn't yield good results; - // we find that a tile size of 2**9 gives best results for input sizes from 2**21 up to 2**27+. - t := make([]fr.Element, tileSize*tileSize) - - // see https://csaws.cs.technion.ac.il/~itai/Courses/Cache/bit.pdf - // for a detailed explanation of the algorithm. - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> (64 - logTileSize)) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> (64 - logTileSize)) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> (64 - logTileSize) - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> (64 - logTileSize) - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> (64 - logTileSize)) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } -} - -func bitReverseCobra(v []fr.Element) { - switch len(v) { - case 1 << 21: - bitReverseCobraInPlace_9_21(v) - case 1 << 22: - bitReverseCobraInPlace_9_22(v) - case 1 << 23: - bitReverseCobraInPlace_9_23(v) - case 1 << 24: - bitReverseCobraInPlace_9_24(v) - case 1 << 25: - bitReverseCobraInPlace_9_25(v) - case 1 << 26: - bitReverseCobraInPlace_9_26(v) - case 1 << 27: - bitReverseCobraInPlace_9_27(v) - default: - if len(v) > 1<<27 { - bitReverseCobraInPlace(v) - } else { - bitReverseNaive(v) - } - } -} - -func deriveLogTileSize(logN uint64) uint64 { - q := uint64(9) // see bitReverseCobraInPlace for more details - - for int(logN)-int(2*q) <= 0 { - q-- - } - - return q -} - -// bitReverseCobraInPlace_9_21 applies the bit-reversal permutation to v. -// len(v) must be 1 << 21. -// see bitReverseCobraInPlace for more details; this function is specialized for 9, -// as it declares the t buffer and various constants statically for performance. -func bitReverseCobraInPlace_9_21(v []fr.Element) { - const ( - logTileSize = uint64(9) - tileSize = uint64(1) << logTileSize - logN = 21 - logBLen = logN - 2*logTileSize - bShift = logBLen + logTileSize - bLen = uint64(1) << logBLen - ) - - var t [tileSize * tileSize]fr.Element - - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> 55) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> 55) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> 55 - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> 55 - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> 55) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } - -} - -// bitReverseCobraInPlace_9_22 applies the bit-reversal permutation to v. -// len(v) must be 1 << 22. -// see bitReverseCobraInPlace for more details; this function is specialized for 9, -// as it declares the t buffer and various constants statically for performance. -func bitReverseCobraInPlace_9_22(v []fr.Element) { - const ( - logTileSize = uint64(9) - tileSize = uint64(1) << logTileSize - logN = 22 - logBLen = logN - 2*logTileSize - bShift = logBLen + logTileSize - bLen = uint64(1) << logBLen - ) - - var t [tileSize * tileSize]fr.Element - - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> 55) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> 55) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> 55 - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> 55 - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> 55) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } - -} - -// bitReverseCobraInPlace_9_23 applies the bit-reversal permutation to v. -// len(v) must be 1 << 23. -// see bitReverseCobraInPlace for more details; this function is specialized for 9, -// as it declares the t buffer and various constants statically for performance. -func bitReverseCobraInPlace_9_23(v []fr.Element) { - const ( - logTileSize = uint64(9) - tileSize = uint64(1) << logTileSize - logN = 23 - logBLen = logN - 2*logTileSize - bShift = logBLen + logTileSize - bLen = uint64(1) << logBLen - ) - - var t [tileSize * tileSize]fr.Element - - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> 55) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> 55) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> 55 - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> 55 - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> 55) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } - -} - -// bitReverseCobraInPlace_9_24 applies the bit-reversal permutation to v. -// len(v) must be 1 << 24. -// see bitReverseCobraInPlace for more details; this function is specialized for 9, -// as it declares the t buffer and various constants statically for performance. -func bitReverseCobraInPlace_9_24(v []fr.Element) { - const ( - logTileSize = uint64(9) - tileSize = uint64(1) << logTileSize - logN = 24 - logBLen = logN - 2*logTileSize - bShift = logBLen + logTileSize - bLen = uint64(1) << logBLen - ) - - var t [tileSize * tileSize]fr.Element - - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> 55) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> 55) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> 55 - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> 55 - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> 55) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } - -} - -// bitReverseCobraInPlace_9_25 applies the bit-reversal permutation to v. -// len(v) must be 1 << 25. -// see bitReverseCobraInPlace for more details; this function is specialized for 9, -// as it declares the t buffer and various constants statically for performance. -func bitReverseCobraInPlace_9_25(v []fr.Element) { - const ( - logTileSize = uint64(9) - tileSize = uint64(1) << logTileSize - logN = 25 - logBLen = logN - 2*logTileSize - bShift = logBLen + logTileSize - bLen = uint64(1) << logBLen - ) - - var t [tileSize * tileSize]fr.Element - - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> 55) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> 55) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> 55 - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> 55 - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> 55) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } - -} - -// bitReverseCobraInPlace_9_26 applies the bit-reversal permutation to v. -// len(v) must be 1 << 26. -// see bitReverseCobraInPlace for more details; this function is specialized for 9, -// as it declares the t buffer and various constants statically for performance. -func bitReverseCobraInPlace_9_26(v []fr.Element) { - const ( - logTileSize = uint64(9) - tileSize = uint64(1) << logTileSize - logN = 26 - logBLen = logN - 2*logTileSize - bShift = logBLen + logTileSize - bLen = uint64(1) << logBLen - ) - - var t [tileSize * tileSize]fr.Element - - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> 55) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> 55) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> 55 - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> 55 - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> 55) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } - -} - -// bitReverseCobraInPlace_9_27 applies the bit-reversal permutation to v. -// len(v) must be 1 << 27. -// see bitReverseCobraInPlace for more details; this function is specialized for 9, -// as it declares the t buffer and various constants statically for performance. -func bitReverseCobraInPlace_9_27(v []fr.Element) { - const ( - logTileSize = uint64(9) - tileSize = uint64(1) << logTileSize - logN = 27 - logBLen = logN - 2*logTileSize - bShift = logBLen + logTileSize - bLen = uint64(1) << logBLen - ) - - var t [tileSize * tileSize]fr.Element - - for b := uint64(0); b < bLen; b++ { - - for a := uint64(0); a < tileSize; a++ { - aRev := (bits.Reverse64(a) >> 55) << logTileSize - for c := uint64(0); c < tileSize; c++ { - idx := (a << bShift) | (b << logTileSize) | c - t[aRev|c] = v[idx] - } - } - - bRev := (bits.Reverse64(b) >> (64 - logBLen)) << logTileSize - - for c := uint64(0); c < tileSize; c++ { - cRev := ((bits.Reverse64(c) >> 55) << bShift) | bRev - for aRev := uint64(0); aRev < tileSize; aRev++ { - a := bits.Reverse64(aRev) >> 55 - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idxRev], t[tIdx] = t[tIdx], v[idxRev] - } - } - } - - for a := uint64(0); a < tileSize; a++ { - aRev := bits.Reverse64(a) >> 55 - for c := uint64(0); c < tileSize; c++ { - cRev := (bits.Reverse64(c) >> 55) << bShift - idx := (a << bShift) | (b << logTileSize) | c - idxRev := cRev | bRev | aRev - if idx < idxRev { - tIdx := (aRev << logTileSize) | c - v[idx], t[tIdx] = t[tIdx], v[idx] - } - } - } - } - -} diff --git a/ecc/bw6-756/fr/fft/bitreverse_test.go b/ecc/bw6-756/fr/fft/bitreverse_test.go deleted file mode 100644 index 500a2475f4..0000000000 --- a/ecc/bw6-756/fr/fft/bitreverse_test.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fft - -import ( - "fmt" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) - -type bitReverseVariant struct { - name string - buf []fr.Element - fn func([]fr.Element) -} - -const maxSizeBitReverse = 1 << 23 - -var bitReverse = []bitReverseVariant{ - {name: "bitReverseNaive", buf: make([]fr.Element, maxSizeBitReverse), fn: bitReverseNaive}, - {name: "BitReverse", buf: make([]fr.Element, maxSizeBitReverse), fn: BitReverse}, - {name: "bitReverseCobraInPlace", buf: make([]fr.Element, maxSizeBitReverse), fn: bitReverseCobraInPlace}, -} - -func TestBitReverse(t *testing.T) { - - // generate a random []fr.Element array of size 2**20 - pol := make([]fr.Element, maxSizeBitReverse) - one := fr.One() - pol[0].SetRandom() - for i := 1; i < maxSizeBitReverse; i++ { - pol[i].Add(&pol[i-1], &one) - } - - // for each size, check that all the bitReverse functions fn compute the same result. - for size := 2; size <= maxSizeBitReverse; size <<= 1 { - - // copy pol into the buffers - for _, data := range bitReverse { - copy(data.buf, pol[:size]) - } - - // compute bit reverse shuffling - for _, data := range bitReverse { - data.fn(data.buf[:size]) - } - - // all bitReverse.buf should hold the same result - for i := 0; i < size; i++ { - for j := 1; j < len(bitReverse); j++ { - if !bitReverse[0].buf[i].Equal(&bitReverse[j].buf[i]) { - t.Fatalf("bitReverse %s and %s do not compute the same result", bitReverse[0].name, bitReverse[j].name) - } - } - } - - // bitReverse back should be identity - for _, data := range bitReverse { - data.fn(data.buf[:size]) - } - - for i := 0; i < size; i++ { - for j := 1; j < len(bitReverse); j++ { - if !bitReverse[0].buf[i].Equal(&bitReverse[j].buf[i]) { - t.Fatalf("(fn-1) bitReverse %s and %s do not compute the same result", bitReverse[0].name, bitReverse[j].name) - } - } - } - } - -} - -func BenchmarkBitReverse(b *testing.B) { - // generate a random []fr.Element array of size 2**22 - pol := make([]fr.Element, maxSizeBitReverse) - one := fr.One() - pol[0].SetRandom() - for i := 1; i < maxSizeBitReverse; i++ { - pol[i].Add(&pol[i-1], &one) - } - - // copy pol into the buffers - for _, data := range bitReverse { - copy(data.buf, pol[:maxSizeBitReverse]) - } - - // benchmark for each size, each bitReverse function - for size := 1 << 18; size <= maxSizeBitReverse; size <<= 1 { - for _, data := range bitReverse { - b.Run(fmt.Sprintf("name=%s/size=%d", data.name, size), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - data.fn(data.buf[:size]) - } - }) - } - } -} diff --git a/ecc/bw6-756/fr/fft/doc.go b/ecc/bw6-756/fr/fft/doc.go deleted file mode 100644 index b5dd44e642..0000000000 --- a/ecc/bw6-756/fr/fft/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package fft provides in-place discrete Fourier transform on powers-of-two subgroups -// of 𝔽ᵣˣ (the multiplicative group (ℤ/rℤ, x) ). -package fft diff --git a/ecc/bw6-756/fr/fft/domain.go b/ecc/bw6-756/fr/fft/domain.go deleted file mode 100644 index 435b45912f..0000000000 --- a/ecc/bw6-756/fr/fft/domain.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fft - -import ( - "errors" - "io" - "math/big" - "math/bits" - "runtime" - "sync" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - - curve "github.com/consensys/gnark-crypto/ecc/bw6-756" - - "github.com/consensys/gnark-crypto/ecc" -) - -// Domain with a power of 2 cardinality -// compute a field element of order 2x and store it in FinerGenerator -// all other values can be derived from x, GeneratorSqrt -type Domain struct { - Cardinality uint64 - CardinalityInv fr.Element - Generator fr.Element - GeneratorInv fr.Element - FrMultiplicativeGen fr.Element // generator of Fr* - FrMultiplicativeGenInv fr.Element - - // this is set with the WithoutPrecompute option; - // if true, the domain does some pre-computation and stores it. - // if false, the FFT will compute the twiddles on the fly (this is less CPU efficient, but uses less memory) - withPrecompute bool - - // the following slices are not serialized and are (re)computed through domain.preComputeTwiddles() - - // twiddles factor for the FFT using Generator for each stage of the recursive FFT - twiddles [][]fr.Element - - // twiddles factor for the FFT using GeneratorInv for each stage of the recursive FFT - twiddlesInv [][]fr.Element - - // we precompute these mostly to avoid the memory intensive bit reverse permutation in the groth16.Prover - - // cosetTable u*<1,g,..,g^(n-1)> - cosetTable []fr.Element - - // cosetTable[i][j] = domain.Generator(i-th)SqrtInv ^ j - cosetTableInv []fr.Element -} - -// GeneratorFullMultiplicativeGroup returns a generator of 𝔽ᵣˣ -func GeneratorFullMultiplicativeGroup() fr.Element { - var res fr.Element - - res.SetUint64(5) - - return res -} - -// NewDomain returns a subgroup with a power of 2 cardinality -// cardinality >= m -// shift: when specified, it's the element by which the set of root of unity is shifted. -func NewDomain(m uint64, opts ...DomainOption) *Domain { - opt := domainOptions(opts...) - domain := &Domain{} - x := ecc.NextPowerOfTwo(m) - domain.Cardinality = uint64(x) - domain.FrMultiplicativeGen = GeneratorFullMultiplicativeGroup() - - if opt.shift != nil { - domain.FrMultiplicativeGen.Set(opt.shift) - } - domain.FrMultiplicativeGenInv.Inverse(&domain.FrMultiplicativeGen) - - var err error - domain.Generator, err = Generator(m) - if err != nil { - panic(err) - } - domain.GeneratorInv.Inverse(&domain.Generator) - domain.CardinalityInv.SetUint64(uint64(x)).Inverse(&domain.CardinalityInv) - - // twiddle factors - domain.withPrecompute = opt.withPrecompute - if domain.withPrecompute { - domain.preComputeTwiddles() - } - - return domain -} - -// Generator returns a generator for Z/2^(log(m))Z -// or an error if m is too big (required root of unity doesn't exist) -func Generator(m uint64) (fr.Element, error) { - return fr.Generator(m) -} - -// Twiddles returns the twiddles factor for the FFT using Generator for each stage of the recursive FFT -// or an error if the domain was created with the WithoutPrecompute option -func (d *Domain) Twiddles() ([][]fr.Element, error) { - if d.twiddles == nil { - return nil, errors.New("twiddles not precomputed") - } - return d.twiddles, nil -} - -// TwiddlesInv returns the twiddles factor for the FFT using GeneratorInv for each stage of the recursive FFT -// or an error if the domain was created with the WithoutPrecompute option -func (d *Domain) TwiddlesInv() ([][]fr.Element, error) { - if d.twiddlesInv == nil { - return nil, errors.New("twiddles not precomputed") - } - return d.twiddlesInv, nil -} - -// CosetTable returns the cosetTable u*<1,g,..,g^(n-1)> -// or an error if the domain was created with the WithoutPrecompute option -func (d *Domain) CosetTable() ([]fr.Element, error) { - if d.cosetTable == nil { - return nil, errors.New("cosetTable not precomputed") - } - return d.cosetTable, nil -} - -// CosetTableInv returns the cosetTableInv u*<1,g,..,g^(n-1)> -// or an error if the domain was created with the WithoutPrecompute option -func (d *Domain) CosetTableInv() ([]fr.Element, error) { - if d.cosetTableInv == nil { - return nil, errors.New("cosetTableInv not precomputed") - } - return d.cosetTableInv, nil -} - -func (d *Domain) preComputeTwiddles() { - - // nb fft stages - nbStages := uint64(bits.TrailingZeros64(d.Cardinality)) - - d.twiddles = make([][]fr.Element, nbStages) - d.twiddlesInv = make([][]fr.Element, nbStages) - d.cosetTable = make([]fr.Element, d.Cardinality) - d.cosetTableInv = make([]fr.Element, d.Cardinality) - - var wg sync.WaitGroup - - expTable := func(sqrt fr.Element, t []fr.Element) { - BuildExpTable(sqrt, t) - wg.Done() - } - - wg.Add(4) - go func() { - buildTwiddles(d.twiddles, d.Generator, nbStages) - wg.Done() - }() - go func() { - buildTwiddles(d.twiddlesInv, d.GeneratorInv, nbStages) - wg.Done() - }() - go expTable(d.FrMultiplicativeGen, d.cosetTable) - go expTable(d.FrMultiplicativeGenInv, d.cosetTableInv) - - wg.Wait() - -} - -func buildTwiddles(t [][]fr.Element, omega fr.Element, nbStages uint64) { - if nbStages == 0 { - return - } - if len(t) != int(nbStages) { - panic("invalid twiddle table") - } - // we just compute the first stage - t[0] = make([]fr.Element, 1+(1<<(nbStages-1))) - BuildExpTable(omega, t[0]) - - // for the next stages, we just iterate on the first stage with larger stride - for i := uint64(1); i < nbStages; i++ { - t[i] = make([]fr.Element, 1+(1<<(nbStages-i-1))) - k := 0 - for j := 0; j < len(t[i]); j++ { - t[i][j] = t[0][k] - k += 1 << i - } - } - -} - -// BuildExpTable precomputes the first n powers of w in parallel -// table[0] = w^0 -// table[1] = w^1 -// ... -func BuildExpTable(w fr.Element, table []fr.Element) { - table[0].SetOne() - n := len(table) - - // see if it makes sense to parallelize exp tables pre-computation - interval := 0 - if runtime.NumCPU() >= 4 { - interval = (n - 1) / (runtime.NumCPU() / 4) - } - - // this ratio roughly correspond to the number of multiplication one can do in place of a Exp operation - // TODO @gbotrel revisit this; Exps in this context will be by a "small power of 2" so faster than this ref ratio. - const ratioExpMul = 6000 / 17 - - if interval < ratioExpMul { - precomputeExpTableChunk(w, 1, table[1:]) - return - } - - // we parallelize - var wg sync.WaitGroup - for i := 1; i < n; i += interval { - start := i - end := i + interval - if end > n { - end = n - } - wg.Add(1) - go func() { - precomputeExpTableChunk(w, uint64(start), table[start:end]) - wg.Done() - }() - } - wg.Wait() -} - -func precomputeExpTableChunk(w fr.Element, power uint64, table []fr.Element) { - - // this condition ensures that creating a domain of size 1 with cosets don't fail - if len(table) > 0 { - table[0].Exp(w, new(big.Int).SetUint64(power)) - for i := 1; i < len(table); i++ { - table[i].Mul(&table[i-1], &w) - } - } -} - -// WriteTo writes a binary representation of the domain (without the precomputed twiddle factors) -// to the provided writer -func (d *Domain) WriteTo(w io.Writer) (int64, error) { - - enc := curve.NewEncoder(w) - - toEncode := []interface{}{d.Cardinality, &d.CardinalityInv, &d.Generator, &d.GeneratorInv, &d.FrMultiplicativeGen, &d.FrMultiplicativeGenInv, &d.withPrecompute} - - for _, v := range toEncode { - if err := enc.Encode(v); err != nil { - return enc.BytesWritten(), err - } - } - - return enc.BytesWritten(), nil -} - -// ReadFrom attempts to decode a domain from Reader -func (d *Domain) ReadFrom(r io.Reader) (int64, error) { - - dec := curve.NewDecoder(r) - - toDecode := []interface{}{&d.Cardinality, &d.CardinalityInv, &d.Generator, &d.GeneratorInv, &d.FrMultiplicativeGen, &d.FrMultiplicativeGenInv, &d.withPrecompute} - - for _, v := range toDecode { - if err := dec.Decode(v); err != nil { - return dec.BytesRead(), err - } - } - - if d.withPrecompute { - d.preComputeTwiddles() - } - - return dec.BytesRead(), nil -} diff --git a/ecc/bw6-756/fr/fft/domain_test.go b/ecc/bw6-756/fr/fft/domain_test.go deleted file mode 100644 index 83186cb7c9..0000000000 --- a/ecc/bw6-756/fr/fft/domain_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fft - -import ( - "bytes" - "reflect" - "testing" -) - -func TestDomainSerialization(t *testing.T) { - - domain := NewDomain(1 << 6) - var reconstructed Domain - - var buf bytes.Buffer - written, err := domain.WriteTo(&buf) - if err != nil { - t.Fatal(err) - } - var read int64 - read, err = reconstructed.ReadFrom(&buf) - if err != nil { - t.Fatal(err) - } - - if written != read { - t.Fatal("didn't read as many bytes as we wrote") - } - if !reflect.DeepEqual(domain, &reconstructed) { - t.Fatal("Domain.SetBytes(Bytes()) failed") - } -} diff --git a/ecc/bw6-756/fr/fft/fft.go b/ecc/bw6-756/fr/fft/fft.go deleted file mode 100644 index 28a9160e5a..0000000000 --- a/ecc/bw6-756/fr/fft/fft.go +++ /dev/null @@ -1,421 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fft - -import ( - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/internal/parallel" - "math/big" - "math/bits" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) - -// Decimation is used in the FFT call to select decimation in time or in frequency -type Decimation uint8 - -const ( - DIT Decimation = iota - DIF -) - -// parallelize threshold for a single butterfly op, if the fft stage is not parallelized already -const butterflyThreshold = 16 - -// FFT computes (recursively) the discrete Fourier transform of a and stores the result in a -// if decimation == DIT (decimation in time), the input must be in bit-reversed order -// if decimation == DIF (decimation in frequency), the output will be in bit-reversed order -func (domain *Domain) FFT(a []fr.Element, decimation Decimation, opts ...Option) { - - opt := fftOptions(opts...) - - // find the stage where we should stop spawning go routines in our recursive calls - // (ie when we have as many go routines running as we have available CPUs) - maxSplits := bits.TrailingZeros64(ecc.NextPowerOfTwo(uint64(opt.nbTasks))) - if opt.nbTasks == 1 { - maxSplits = -1 - } - - // if coset != 0, scale by coset table - if opt.coset { - if decimation == DIT { - // scale by coset table (in bit reversed order) - cosetTable := domain.cosetTable - if !domain.withPrecompute { - // we need to build the full table or do a bit reverse dance. - cosetTable = make([]fr.Element, len(a)) - BuildExpTable(domain.FrMultiplicativeGen, cosetTable) - } - parallel.Execute(len(a), func(start, end int) { - n := uint64(len(a)) - nn := uint64(64 - bits.TrailingZeros64(n)) - for i := start; i < end; i++ { - irev := int(bits.Reverse64(uint64(i)) >> nn) - a[i].Mul(&a[i], &cosetTable[irev]) - } - }, opt.nbTasks) - } else { - if domain.withPrecompute { - parallel.Execute(len(a), func(start, end int) { - for i := start; i < end; i++ { - a[i].Mul(&a[i], &domain.cosetTable[i]) - } - }, opt.nbTasks) - } else { - c := domain.FrMultiplicativeGen - parallel.Execute(len(a), func(start, end int) { - var at fr.Element - at.Exp(c, big.NewInt(int64(start))) - for i := start; i < end; i++ { - a[i].Mul(&a[i], &at) - at.Mul(&at, &c) - } - }, opt.nbTasks) - } - - } - } - - twiddles := domain.twiddles - twiddlesStartStage := 0 - if !domain.withPrecompute { - twiddlesStartStage = 3 - nbStages := int(bits.TrailingZeros64(domain.Cardinality)) - if nbStages-twiddlesStartStage > 0 { - twiddles = make([][]fr.Element, nbStages-twiddlesStartStage) - w := domain.Generator - w.Exp(w, big.NewInt(int64(1< 0 { - twiddlesInv = make([][]fr.Element, nbStages-twiddlesStartStage) - w := domain.GeneratorInv - w.Exp(w, big.NewInt(int64(1<> nn) - a[i].Mul(&a[i], &cosetTableInv[irev]). - Mul(&a[i], &domain.CardinalityInv) - } - }, opt.nbTasks) - -} - -func difFFT(a []fr.Element, w fr.Element, twiddles [][]fr.Element, twiddlesStartStage, stage, maxSplits int, chDone chan struct{}, nbTasks int) { - if chDone != nil { - defer close(chDone) - } - - n := len(a) - if n == 1 { - return - } else if n == 256 && stage >= twiddlesStartStage { - kerDIFNP_256(a, twiddles, stage-twiddlesStartStage) - return - } - m := n >> 1 - - parallelButterfly := (m > butterflyThreshold) && (stage < maxSplits) - - if stage < twiddlesStartStage { - if parallelButterfly { - w := w - parallel.Execute(m, func(start, end int) { - if start == 0 { - fr.Butterfly(&a[0], &a[m]) - start++ - } - var at fr.Element - at.Exp(w, big.NewInt(int64(start))) - innerDIFWithoutTwiddles(a, at, w, start, end, m) - }, nbTasks/(1<<(stage))) // 1 << stage == estimated used CPUs - } else { - innerDIFWithoutTwiddles(a, w, w, 0, m, m) - } - // compute next twiddle - w.Square(&w) - } else { - if parallelButterfly { - parallel.Execute(m, func(start, end int) { - innerDIFWithTwiddles(a, twiddles[stage-twiddlesStartStage], start, end, m) - }, nbTasks/(1<<(stage))) - } else { - innerDIFWithTwiddles(a, twiddles[stage-twiddlesStartStage], 0, m, m) - } - } - - if m == 1 { - return - } - - nextStage := stage + 1 - if stage < maxSplits { - chDone := make(chan struct{}, 1) - go difFFT(a[m:n], w, twiddles, twiddlesStartStage, nextStage, maxSplits, chDone, nbTasks) - difFFT(a[0:m], w, twiddles, twiddlesStartStage, nextStage, maxSplits, nil, nbTasks) - <-chDone - } else { - difFFT(a[0:m], w, twiddles, twiddlesStartStage, nextStage, maxSplits, nil, nbTasks) - difFFT(a[m:n], w, twiddles, twiddlesStartStage, nextStage, maxSplits, nil, nbTasks) - } - -} - -func innerDIFWithTwiddles(a []fr.Element, twiddles []fr.Element, start, end, m int) { - if start == 0 { - fr.Butterfly(&a[0], &a[m]) - start++ - } - for i := start; i < end; i++ { - fr.Butterfly(&a[i], &a[i+m]) - a[i+m].Mul(&a[i+m], &twiddles[i]) - } -} - -func innerDIFWithoutTwiddles(a []fr.Element, at, w fr.Element, start, end, m int) { - if start == 0 { - fr.Butterfly(&a[0], &a[m]) - start++ - } - for i := start; i < end; i++ { - fr.Butterfly(&a[i], &a[i+m]) - a[i+m].Mul(&a[i+m], &at) - at.Mul(&at, &w) - } -} - -func ditFFT(a []fr.Element, w fr.Element, twiddles [][]fr.Element, twiddlesStartStage, stage, maxSplits int, chDone chan struct{}, nbTasks int) { - if chDone != nil { - defer close(chDone) - } - n := len(a) - if n == 1 { - return - } else if n == 256 && stage >= twiddlesStartStage { - kerDITNP_256(a, twiddles, stage-twiddlesStartStage) - return - } - m := n >> 1 - - nextStage := stage + 1 - nextW := w - nextW.Square(&nextW) - - if stage < maxSplits { - // that's the only time we fire go routines - chDone := make(chan struct{}, 1) - go ditFFT(a[m:], nextW, twiddles, twiddlesStartStage, nextStage, maxSplits, chDone, nbTasks) - ditFFT(a[0:m], nextW, twiddles, twiddlesStartStage, nextStage, maxSplits, nil, nbTasks) - <-chDone - } else { - ditFFT(a[0:m], nextW, twiddles, twiddlesStartStage, nextStage, maxSplits, nil, nbTasks) - ditFFT(a[m:n], nextW, twiddles, twiddlesStartStage, nextStage, maxSplits, nil, nbTasks) - } - - parallelButterfly := (m > butterflyThreshold) && (stage < maxSplits) - - if stage < twiddlesStartStage { - // we need to compute the twiddles for this stage on the fly. - if parallelButterfly { - w := w - parallel.Execute(m, func(start, end int) { - if start == 0 { - fr.Butterfly(&a[0], &a[m]) - start++ - } - var at fr.Element - at.Exp(w, big.NewInt(int64(start))) - innerDITWithoutTwiddles(a, at, w, start, end, m) - }, nbTasks/(1<<(stage))) // 1 << stage == estimated used CPUs - - } else { - innerDITWithoutTwiddles(a, w, w, 0, m, m) - } - return - } - if parallelButterfly { - parallel.Execute(m, func(start, end int) { - innerDITWithTwiddles(a, twiddles[stage-twiddlesStartStage], start, end, m) - }, nbTasks/(1<<(stage))) - } else { - innerDITWithTwiddles(a, twiddles[stage-twiddlesStartStage], 0, m, m) - } -} - -func innerDITWithTwiddles(a []fr.Element, twiddles []fr.Element, start, end, m int) { - if start == 0 { - fr.Butterfly(&a[0], &a[m]) - start++ - } - for i := start; i < end; i++ { - a[i+m].Mul(&a[i+m], &twiddles[i]) - fr.Butterfly(&a[i], &a[i+m]) - } -} - -func innerDITWithoutTwiddles(a []fr.Element, at, w fr.Element, start, end, m int) { - if start == 0 { - fr.Butterfly(&a[0], &a[m]) - start++ - } - for i := start; i < end; i++ { - a[i+m].Mul(&a[i+m], &at) - fr.Butterfly(&a[i], &a[i+m]) - at.Mul(&at, &w) - } -} - -func kerDIFNP_256(a []fr.Element, twiddles [][]fr.Element, stage int) { - // code unrolled & generated by internal/generator/fft/template/fft.go.tmpl - - innerDIFWithTwiddles(a[:256], twiddles[stage+0], 0, 128, 128) - for offset := 0; offset < 256; offset += 128 { - innerDIFWithTwiddles(a[offset:offset+128], twiddles[stage+1], 0, 64, 64) - } - for offset := 0; offset < 256; offset += 64 { - innerDIFWithTwiddles(a[offset:offset+64], twiddles[stage+2], 0, 32, 32) - } - for offset := 0; offset < 256; offset += 32 { - innerDIFWithTwiddles(a[offset:offset+32], twiddles[stage+3], 0, 16, 16) - } - for offset := 0; offset < 256; offset += 16 { - innerDIFWithTwiddles(a[offset:offset+16], twiddles[stage+4], 0, 8, 8) - } - for offset := 0; offset < 256; offset += 8 { - innerDIFWithTwiddles(a[offset:offset+8], twiddles[stage+5], 0, 4, 4) - } - for offset := 0; offset < 256; offset += 4 { - innerDIFWithTwiddles(a[offset:offset+4], twiddles[stage+6], 0, 2, 2) - } - for offset := 0; offset < 256; offset += 2 { - fr.Butterfly(&a[offset], &a[offset+1]) - } -} - -func kerDITNP_256(a []fr.Element, twiddles [][]fr.Element, stage int) { - // code unrolled & generated by internal/generator/fft/template/fft.go.tmpl - - for offset := 0; offset < 256; offset += 2 { - fr.Butterfly(&a[offset], &a[offset+1]) - } - for offset := 0; offset < 256; offset += 4 { - innerDITWithTwiddles(a[offset:offset+4], twiddles[stage+6], 0, 2, 2) - } - for offset := 0; offset < 256; offset += 8 { - innerDITWithTwiddles(a[offset:offset+8], twiddles[stage+5], 0, 4, 4) - } - for offset := 0; offset < 256; offset += 16 { - innerDITWithTwiddles(a[offset:offset+16], twiddles[stage+4], 0, 8, 8) - } - for offset := 0; offset < 256; offset += 32 { - innerDITWithTwiddles(a[offset:offset+32], twiddles[stage+3], 0, 16, 16) - } - for offset := 0; offset < 256; offset += 64 { - innerDITWithTwiddles(a[offset:offset+64], twiddles[stage+2], 0, 32, 32) - } - for offset := 0; offset < 256; offset += 128 { - innerDITWithTwiddles(a[offset:offset+128], twiddles[stage+1], 0, 64, 64) - } - innerDITWithTwiddles(a[:256], twiddles[stage+0], 0, 128, 128) -} diff --git a/ecc/bw6-756/fr/fft/fft_test.go b/ecc/bw6-756/fr/fft/fft_test.go deleted file mode 100644 index 489ad7eed0..0000000000 --- a/ecc/bw6-756/fr/fft/fft_test.go +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fft - -import ( - "math/big" - "strconv" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/gen" - "github.com/leanovate/gopter/prop" - - "fmt" -) - -func TestFFT(t *testing.T) { - parameters := gopter.DefaultTestParameters() - parameters.MinSuccessfulTests = 5 - properties := gopter.NewProperties(parameters) - - for maxSize := 2; maxSize <= 1<<10; maxSize <<= 1 { - - domainWithPrecompute := NewDomain(uint64(maxSize)) - domainWithoutPrecompute := NewDomain(uint64(maxSize), WithoutPrecompute()) - - for domainName, domain := range map[string]*Domain{ - "with precompute": domainWithPrecompute, - "without precompute": domainWithoutPrecompute, - } { - domainName := domainName - domain := domain - t.Logf("domain: %s", domainName) - properties.Property("DIF FFT should be consistent with dual basis", prop.ForAll( - - // checks that a random evaluation of a dual function eval(gen**ithpower) is consistent with the FFT result - func(ithpower int) bool { - - pol := make([]fr.Element, maxSize) - backupPol := make([]fr.Element, maxSize) - - for i := 0; i < maxSize; i++ { - pol[i].SetRandom() - } - copy(backupPol, pol) - - domain.FFT(pol, DIF) - BitReverse(pol) - - sample := domain.Generator - sample.Exp(sample, big.NewInt(int64(ithpower))) - - eval := evaluatePolynomial(backupPol, sample) - - return eval.Equal(&pol[ithpower]) - - }, - gen.IntRange(0, maxSize-1), - )) - - properties.Property("DIF FFT on cosets should be consistent with dual basis", prop.ForAll( - - // checks that a random evaluation of a dual function eval(gen**ithpower) is consistent with the FFT result - func(ithpower int) bool { - - pol := make([]fr.Element, maxSize) - backupPol := make([]fr.Element, maxSize) - - for i := 0; i < maxSize; i++ { - pol[i].SetRandom() - } - copy(backupPol, pol) - - domain.FFT(pol, DIF, OnCoset()) - BitReverse(pol) - - sample := domain.Generator - sample.Exp(sample, big.NewInt(int64(ithpower))). - Mul(&sample, &domain.FrMultiplicativeGen) - - eval := evaluatePolynomial(backupPol, sample) - - return eval.Equal(&pol[ithpower]) - - }, - gen.IntRange(0, maxSize-1), - )) - - properties.Property("DIT FFT should be consistent with dual basis", prop.ForAll( - - // checks that a random evaluation of a dual function eval(gen**ithpower) is consistent with the FFT result - func(ithpower int) bool { - - pol := make([]fr.Element, maxSize) - backupPol := make([]fr.Element, maxSize) - - for i := 0; i < maxSize; i++ { - pol[i].SetRandom() - } - copy(backupPol, pol) - - BitReverse(pol) - domain.FFT(pol, DIT) - - sample := domain.Generator - sample.Exp(sample, big.NewInt(int64(ithpower))) - - eval := evaluatePolynomial(backupPol, sample) - - return eval.Equal(&pol[ithpower]) - - }, - gen.IntRange(0, maxSize-1), - )) - - properties.Property("bitReverse(DIF FFT(DIT FFT (bitReverse))))==id", prop.ForAll( - - func() bool { - - pol := make([]fr.Element, maxSize) - backupPol := make([]fr.Element, maxSize) - - for i := 0; i < maxSize; i++ { - pol[i].SetRandom() - } - copy(backupPol, pol) - - BitReverse(pol) - domain.FFT(pol, DIT) - domain.FFTInverse(pol, DIF) - BitReverse(pol) - - check := true - for i := 0; i < len(pol); i++ { - check = check && pol[i].Equal(&backupPol[i]) - } - return check - }, - )) - - for nbCosets := 2; nbCosets < 5; nbCosets++ { - properties.Property(fmt.Sprintf("bitReverse(DIF FFT(DIT FFT (bitReverse))))==id on %d cosets", nbCosets), prop.ForAll( - - func() bool { - - pol := make([]fr.Element, maxSize) - backupPol := make([]fr.Element, maxSize) - - for i := 0; i < maxSize; i++ { - pol[i].SetRandom() - } - copy(backupPol, pol) - - check := true - - for i := 1; i <= nbCosets; i++ { - - BitReverse(pol) - domain.FFT(pol, DIT, OnCoset()) - domain.FFTInverse(pol, DIF, OnCoset()) - BitReverse(pol) - - for i := 0; i < len(pol); i++ { - check = check && pol[i].Equal(&backupPol[i]) - } - } - - return check - }, - )) - } - - properties.Property("DIT FFT(DIF FFT)==id", prop.ForAll( - - func() bool { - - pol := make([]fr.Element, maxSize) - backupPol := make([]fr.Element, maxSize) - - for i := 0; i < maxSize; i++ { - pol[i].SetRandom() - } - copy(backupPol, pol) - - domain.FFTInverse(pol, DIF) - domain.FFT(pol, DIT) - - check := true - for i := 0; i < len(pol); i++ { - check = check && (pol[i] == backupPol[i]) - } - return check - }, - )) - - properties.Property("DIT FFT(DIF FFT)==id on cosets", prop.ForAll( - - func() bool { - - pol := make([]fr.Element, maxSize) - backupPol := make([]fr.Element, maxSize) - - for i := 0; i < maxSize; i++ { - pol[i].SetRandom() - } - copy(backupPol, pol) - - domain.FFTInverse(pol, DIF, OnCoset()) - domain.FFT(pol, DIT, OnCoset()) - - for i := 0; i < len(pol); i++ { - if !(pol[i].Equal(&backupPol[i])) { - return false - } - } - - // compute with nbTasks == 1 - domain.FFTInverse(pol, DIF, OnCoset(), WithNbTasks(1)) - domain.FFT(pol, DIT, OnCoset(), WithNbTasks(1)) - - for i := 0; i < len(pol); i++ { - if !(pol[i].Equal(&backupPol[i])) { - return false - } - } - - return true - }, - )) - } - properties.TestingRun(t, gopter.ConsoleReporter(false)) - } - -} - -// -------------------------------------------------------------------- -// benches - -func BenchmarkFFT(b *testing.B) { - - const maxSize = 1 << 20 - - pol := make([]fr.Element, maxSize) - pol[0].SetRandom() - for i := 1; i < maxSize; i++ { - pol[i] = pol[i-1] - } - - for i := 8; i < 20; i++ { - sizeDomain := 1 << i - b.Run("fft 2**"+strconv.Itoa(i)+"bits", func(b *testing.B) { - domain := NewDomain(uint64(sizeDomain)) - b.ResetTimer() - for j := 0; j < b.N; j++ { - domain.FFT(pol[:sizeDomain], DIT) - } - }) - b.Run("fft 2**"+strconv.Itoa(i)+"bits (coset)", func(b *testing.B) { - domain := NewDomain(uint64(sizeDomain)) - b.ResetTimer() - for j := 0; j < b.N; j++ { - domain.FFT(pol[:sizeDomain], DIT, OnCoset()) - } - }) - } - -} - -func BenchmarkFFTDITCosetReference(b *testing.B) { - const maxSize = 1 << 20 - - pol := make([]fr.Element, maxSize) - pol[0].SetRandom() - for i := 1; i < maxSize; i++ { - pol[i] = pol[i-1] - } - - domain := NewDomain(maxSize) - - b.ResetTimer() - for j := 0; j < b.N; j++ { - domain.FFT(pol, DIT, OnCoset()) - } -} - -func BenchmarkFFTDIFReference(b *testing.B) { - const maxSize = 1 << 20 - - pol := make([]fr.Element, maxSize) - pol[0].SetRandom() - for i := 1; i < maxSize; i++ { - pol[i] = pol[i-1] - } - - domain := NewDomain(maxSize) - - b.ResetTimer() - for j := 0; j < b.N; j++ { - domain.FFT(pol, DIF) - } -} - -func evaluatePolynomial(pol []fr.Element, val fr.Element) fr.Element { - var acc, res, tmp fr.Element - res.Set(&pol[0]) - acc.Set(&val) - for i := 1; i < len(pol); i++ { - tmp.Mul(&acc, &pol[i]) - res.Add(&res, &tmp) - acc.Mul(&acc, &val) - } - return res -} diff --git a/ecc/bw6-756/fr/fft/options.go b/ecc/bw6-756/fr/fft/options.go deleted file mode 100644 index 9091f81fc2..0000000000 --- a/ecc/bw6-756/fr/fft/options.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fft - -import ( - "runtime" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) - -// Option defines option for altering the behavior of FFT methods. -// See the descriptions of functions returning instances of this type for -// particular options. -type Option func(*fftConfig) - -type fftConfig struct { - coset bool - nbTasks int -} - -// OnCoset if provided, FFT(a) returns the evaluation of a on a coset. -func OnCoset() Option { - return func(opt *fftConfig) { - opt.coset = true - } -} - -// WithNbTasks sets the max number of task (go routine) to spawn. Must be between 1 and 512. -func WithNbTasks(nbTasks int) Option { - if nbTasks < 1 { - nbTasks = 1 - } else if nbTasks > 512 { - nbTasks = 512 - } - return func(opt *fftConfig) { - opt.nbTasks = nbTasks - } -} - -// default options -func fftOptions(opts ...Option) fftConfig { - // apply options - opt := fftConfig{ - coset: false, - nbTasks: runtime.NumCPU(), - } - for _, option := range opts { - option(&opt) - } - return opt -} - -// DomainOption defines option for altering the definition of the FFT domain -// See the descriptions of functions returning instances of this type for -// particular options. -type DomainOption func(*domainConfig) - -type domainConfig struct { - shift *fr.Element - withPrecompute bool -} - -// WithShift sets the FrMultiplicativeGen of the domain. -// Default is generator of the largest 2-adic subgroup. -func WithShift(shift fr.Element) DomainOption { - return func(opt *domainConfig) { - opt.shift = new(fr.Element).Set(&shift) - } -} - -// WithoutPrecompute disables precomputation of twiddles in the domain. -// When this option is set, FFTs will be slower, but will use less memory. -func WithoutPrecompute() DomainOption { - return func(opt *domainConfig) { - opt.withPrecompute = false - } -} - -// default options -func domainOptions(opts ...DomainOption) domainConfig { - // apply options - opt := domainConfig{ - withPrecompute: true, - } - for _, option := range opts { - option(&opt) - } - return opt -} diff --git a/ecc/bw6-756/fr/fri/doc.go b/ecc/bw6-756/fr/fri/doc.go deleted file mode 100644 index 3333ad41f5..0000000000 --- a/ecc/bw6-756/fr/fri/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package fri provides the FRI (multiplicative) commitment scheme. -package fri diff --git a/ecc/bw6-756/fr/fri/fri.go b/ecc/bw6-756/fr/fri/fri.go deleted file mode 100644 index 6ff1ee5396..0000000000 --- a/ecc/bw6-756/fr/fri/fri.go +++ /dev/null @@ -1,698 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fri - -import ( - "bytes" - "errors" - "fmt" - "hash" - "math/big" - "math/bits" - - "github.com/consensys/gnark-crypto/accumulator/merkletree" - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/fft" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -var ( - ErrLowDegree = errors.New("the fully folded polynomial in not of degree 1") - ErrProximityTestFolding = errors.New("one round of interaction failed") - ErrOddSize = errors.New("the size should be even") - ErrMerkleRoot = errors.New("merkle roots of the opening and the proof of proximity don't coincide") - ErrMerklePath = errors.New("merkle path proof is wrong") - ErrRangePosition = errors.New("the asked opening position is out of range") -) - -const rho = 8 - -const nbRounds = 1 - -// 2^{-1}, used several times -var twoInv fr.Element - -// Digest commitment of a polynomial. -type Digest []byte - -// merkleProof helper structure to build the merkle proof -// At each round, two contiguous values from the evaluated polynomial -// are queried. For one value, the full Merkle path will be provided. -// For the neighbor value, only the leaf is provided (so ProofSet will -// be empty), since the Merkle path is the same as for the first value. -type MerkleProof struct { - - // Merkle root - MerkleRoot []byte - - // ProofSet stores [leaf ∥ node_1 ∥ .. ∥ merkleRoot ], where the leaf is not - // hashed. - ProofSet [][]byte - - // number of leaves of the tree. - numLeaves uint64 -} - -// MerkleProof used to open a polynomial -type OpeningProof struct { - - // those fields are private since they are only needed for - // the verification, which is abstracted in the VerifyOpening - // method. - merkleRoot []byte - ProofSet [][]byte - numLeaves uint64 - index uint64 - - // ClaimedValue value of the leaf. This field is exported - // because it's needed for protocols using polynomial commitment - // schemes (to verify an algebraic relation). - ClaimedValue fr.Element -} - -// IOPP Interactive Oracle Proof of Proximity -type IOPP uint - -const ( - // Multiplicative version of FRI, using the map x->x², on a - // power of 2 subgroup of Fr^{*}. - RADIX_2_FRI IOPP = iota -) - -// round contains the data corresponding to a single round -// of fri. -// It consists of a list of Interactions between the prover and the verifier, -// where each interaction contains a challenge provided by the verifier, as -// well as MerkleProofs for the queries of the verifier. The Merkle proofs -// correspond to the openings of the i-th folded polynomial at 2 points that -// belong to the same fiber of x -> x². -type Round struct { - - // stores the Interactions between the prover and the verifier. - // Each interaction results in a set or merkle proofs, corresponding - // to the queries of the verifier. - Interactions [][2]MerkleProof - - // evaluation stores the evaluation of the fully folded polynomial. - // The fully folded polynomial is constant, and is evaluated on a - // a set of size \rho. Since the polynomial is supposed to be constant, - // only one evaluation, corresponding to the polynomial, is given. Since - // the prover cannot know in advance which entry the verifier will query, - // providing a single evaluation - Evaluation fr.Element -} - -// ProofOfProximity proof of proximity, attesting that -// a function is d-close to a low degree polynomial. -// -// It is composed of a series of Interactions, emulated with Fiat Shamir, -type ProofOfProximity struct { - - // ID unique ID attached to the proof of proximity. It's needed for - // protocols using Fiat Shamir for instance, where challenges are derived - // from the proof of proximity. - ID []byte - - // round contains the data corresponding to a single round - // of fri. There are nbRounds rounds of Interactions. - Rounds []Round -} - -// Iopp interface that an iopp should implement -type Iopp interface { - - // BuildProofOfProximity creates a proof of proximity that p is d-close to a polynomial - // of degree len(p). The proof is built non interactively using Fiat Shamir. - BuildProofOfProximity(p []fr.Element) (ProofOfProximity, error) - - // VerifyProofOfProximity verifies the proof of proximity. It returns an error if the - // verification fails. - VerifyProofOfProximity(proof ProofOfProximity) error - - // Opens a polynomial at gⁱ where i = position. - Open(p []fr.Element, position uint64) (OpeningProof, error) - - // Verifies the opening of a polynomial at gⁱ where i = position. - VerifyOpening(position uint64, openingProof OpeningProof, pp ProofOfProximity) error -} - -// GetRho returns the factor ρ = size_code_word/size_polynomial -func GetRho() int { - return rho -} - -func init() { - twoInv.SetUint64(2).Inverse(&twoInv) -} - -// New creates a new IOPP capable to handle degree(size) polynomials. -func (iopp IOPP) New(size uint64, h hash.Hash) Iopp { - switch iopp { - case RADIX_2_FRI: - return newRadixTwoFri(size, h) - default: - panic("iopp name is not recognized") - } -} - -// radixTwoFri empty structs implementing compressionFunction for -// the squaring function. -type radixTwoFri struct { - - // hash function that is used for Fiat Shamir and for committing to - // the oracles. - h hash.Hash - - // nbSteps number of Interactions between the prover and the verifier - nbSteps int - - // domain used to build the Reed Solomon code from the given polynomial. - // The size of the domain is ρ*size_polynomial. - domain *fft.Domain -} - -func newRadixTwoFri(size uint64, h hash.Hash) radixTwoFri { - - var res radixTwoFri - - // computing the number of steps - n := ecc.NextPowerOfTwo(size) - nbSteps := bits.TrailingZeros(uint(n)) - res.nbSteps = nbSteps - - // extending the domain - n = n * rho - - // building the domains - res.domain = fft.NewDomain(n) - - // hash function - res.h = h - - return res -} - -// convertCanonicalSorted convert the index i, an entry in a -// sorted polynomial, to the corresponding entry in canonical -// representation. n is the size of the polynomial. -func convertCanonicalSorted(i, n int) int { - - if i < n/2 { - return 2 * i - } else { - l := n - (i + 1) - l = 2 * l - return n - l - 1 - } - -} - -// deriveQueriesPositions derives the indices of the oracle -// function that the verifier has to pick, in sorted form. -// * pos is the initial position, i.e. the logarithm of the first challenge -// * size is the size of the initial polynomial -// * The result is a slice of []int, where each entry is a tuple (iₖ), such that -// the verifier needs to evaluate ∑ₖ oracle(iₖ)xᵏ to build -// the folded function. -func (s radixTwoFri) deriveQueriesPositions(pos int, size int) []int { - - _s := size / 2 - res := make([]int, s.nbSteps) - res[0] = pos - for i := 1; i < s.nbSteps; i++ { - t := (res[i-1] - (res[i-1] % 2)) / 2 - res[i] = convertCanonicalSorted(t, _s) - _s = _s / 2 - } - - return res -} - -// sort orders the evaluation of a polynomial on a domain -// such that contiguous entries are in the same fiber: -// {q(g⁰), q(g^{n/2}), q(g¹), q(g^{1+n/2}),...,q(g^{n/2-1}), q(gⁿ⁻¹)} -func sort(evaluations []fr.Element) []fr.Element { - q := make([]fr.Element, len(evaluations)) - n := len(evaluations) / 2 - for i := 0; i < n; i++ { - q[2*i].Set(&evaluations[i]) - q[2*i+1].Set(&evaluations[i+n]) - } - return q -} - -// Opens a polynomial at gⁱ where i = position. -func (s radixTwoFri) Open(p []fr.Element, position uint64) (OpeningProof, error) { - - // check that position is in the correct range - if position >= s.domain.Cardinality { - return OpeningProof{}, ErrRangePosition - } - - // put q in evaluation form - q := make([]fr.Element, s.domain.Cardinality) - copy(q, p) - s.domain.FFT(q, fft.DIF) - fft.BitReverse(q) - - // sort q to have fibers in contiguous entries. The goal is to have one - // Merkle path for both openings of entries which are in the same fiber. - q = sort(q) - - // build the Merkle proof, we the position is converted to fit the sorted polynomial - pos := convertCanonicalSorted(int(position), len(q)) - - tree := merkletree.New(s.h) - err := tree.SetIndex(uint64(pos)) - if err != nil { - return OpeningProof{}, err - } - for i := 0; i < len(q); i++ { - tree.Push(q[i].Marshal()) - } - var res OpeningProof - res.merkleRoot, res.ProofSet, res.index, res.numLeaves = tree.Prove() - - // set the claimed value, which is the first entry of the Merkle proof - res.ClaimedValue.SetBytes(res.ProofSet[0]) - - return res, nil -} - -// Verifies the opening of a polynomial. -// * position the point at which the proof is opened (the point is gⁱ where i = position) -// * openingProof Merkle path proof -// * pp proof of proximity, needed because before opening Merkle path proof one should be sure that the -// committed values come from a polynomial. During the verification of the Merkle path proof, the root -// hash of the Merkle path is compared to the root hash of the first interaction of the proof of proximity, -// those should be equal, if not an error is raised. -func (s radixTwoFri) VerifyOpening(position uint64, openingProof OpeningProof, pp ProofOfProximity) error { - - // To query the Merkle path, we look at the first series of Interactions, and check whether it's the point - // at 'position' or its neighbor that contains the full Merkle path. - var fullMerkleProof int - if len(pp.Rounds[0].Interactions[0][0].ProofSet) > len(pp.Rounds[0].Interactions[0][1].ProofSet) { - fullMerkleProof = 0 - } else { - fullMerkleProof = 1 - } - - // check that the merkle roots coincide - if !bytes.Equal(openingProof.merkleRoot, pp.Rounds[0].Interactions[0][fullMerkleProof].MerkleRoot) { - return ErrMerkleRoot - } - - // convert position to the sorted version - sizePoly := s.domain.Cardinality - pos := convertCanonicalSorted(int(position), int(sizePoly)) - - // check the Merkle proof - res := merkletree.VerifyProof(s.h, openingProof.merkleRoot, openingProof.ProofSet, uint64(pos), openingProof.numLeaves) - if !res { - return ErrMerklePath - } - return nil - -} - -// foldPolynomialLagrangeBasis folds a polynomial p, expressed in Lagrange basis. -// -// Fᵣ[X]/(Xⁿ-1) is a free module of rank 2 on Fᵣ[Y]/(Y^{n/2}-1). If -// p∈ Fᵣ[X]/(Xⁿ-1), expressed in Lagrange basis, the function finds the coordinates -// p₁, p₂ of p in Fᵣ[Y]/(Y^{n/2}-1), expressed in Lagrange basis. Finally, it computes -// p₁ + x*p₂ and returns it. -// -// * p is the polynomial to fold, in Lagrange basis, sorted like this: p = [p(1),p(-1),p(g),p(-g),p(g²),p(-g²),...] -// * g is a generator of the subgroup of Fᵣ^{*} of size len(p) -// * x is the folding challenge x, used to return p₁+x*p₂ -func foldPolynomialLagrangeBasis(pSorted []fr.Element, gInv, x fr.Element) []fr.Element { - - // we have the following system - // p₁(g²ⁱ)+gⁱp₂(g²ⁱ) = p(gⁱ) - // p₁(g²ⁱ)-gⁱp₂(g²ⁱ) = p(-gⁱ) - // we solve the system for p₁(g²ⁱ),p₂(g²ⁱ) - s := len(pSorted) - res := make([]fr.Element, s/2) - - var p1, p2, acc fr.Element - acc.SetOne() - - for i := 0; i < s/2; i++ { - - p1.Add(&pSorted[2*i], &pSorted[2*i+1]) - p2.Sub(&pSorted[2*i], &pSorted[2*i+1]).Mul(&p2, &acc) - res[i].Mul(&p2, &x).Add(&res[i], &p1).Mul(&res[i], &twoInv) - - acc.Mul(&acc, &gInv) - - } - - return res -} - -// buildProofOfProximitySingleRound generates a proof that a function, given as an oracle from -// the verifier point of view, is in fact δ-close to a polynomial. -// * salt is a variable for multi rounds, it allows to generate different challenges using Fiat Shamir -// * p is in evaluation form -func (s radixTwoFri) buildProofOfProximitySingleRound(salt fr.Element, p []fr.Element) (Round, error) { - - // the proof will contain nbSteps Interactions - var res Round - res.Interactions = make([][2]MerkleProof, s.nbSteps) - - // Fiat Shamir transcript to derive the challenges. The xᵢ are used to fold the - // polynomials. - // During the i-th round, the prover has a polynomial P of degree n. The verifier sends - // xᵢ∈ Fᵣ to the prover. The prover expresses F in Fᵣ[X,Y]/ as - // P₀(Y)+X P₁(Y) where P₀, P₁ are of degree n/2, and he then folds the polynomial - // by replacing x by xᵢ. - xis := make([]string, s.nbSteps+1) - for i := 0; i < s.nbSteps; i++ { - xis[i] = fmt.Sprintf("x%d", i) - } - xis[s.nbSteps] = "s0" - fs := fiatshamir.NewTranscript(s.h, xis...) - - // the salt is binded to the first challenge, to ensure the challenges - // are different at each round. - err := fs.Bind(xis[0], salt.Marshal()) - if err != nil { - return Round{}, err - } - - // step 1 : fold the polynomial using the xi - - // evalsAtRound stores the list of the nbSteps polynomial evaluations, each evaluation - // corresponds to the evaluation o the folded polynomial at round i. - evalsAtRound := make([][]fr.Element, s.nbSteps) - - // evaluate p and sort the result - _p := make([]fr.Element, s.domain.Cardinality) - copy(_p, p) - - // gInv inverse of the generator of the cyclic group of size the size of the polynomial. - // The size of the cyclic group is ρ*s.domainSize, and not s.domainSize. - var gInv fr.Element - gInv.Set(&s.domain.GeneratorInv) - - for i := 0; i < s.nbSteps; i++ { - - evalsAtRound[i] = sort(_p) - - // compute the root hash, needed to derive xi - t := merkletree.New(s.h) - for k := 0; k < len(_p); k++ { - t.Push(evalsAtRound[i][k].Marshal()) - } - rh := t.Root() - err := fs.Bind(xis[i], rh) - if err != nil { - return res, err - } - - // derive the challenge - bxi, err := fs.ComputeChallenge(xis[i]) - if err != nil { - return res, err - } - var xi fr.Element - xi.SetBytes(bxi) - - // fold _p, reusing its memory - _p = foldPolynomialLagrangeBasis(evalsAtRound[i], gInv, xi) - - // g <- g² - gInv.Square(&gInv) - - } - - // last round, provide the evaluation. The fully folded polynomial is of size rho. It should - // correspond to the evaluation of a polynomial of degree 1 on ρ points, so those points - // are supposed to be on a line. - res.Evaluation.Set(&_p[0]) - - // step 2: provide the Merkle proofs of the queries - - // derive the verifier queries - err = fs.Bind(xis[s.nbSteps], res.Evaluation.Marshal()) - if err != nil { - return res, err - } - binSeed, err := fs.ComputeChallenge(xis[s.nbSteps]) - if err != nil { - return res, err - } - var bPos, bCardinality big.Int - bPos.SetBytes(binSeed) - bCardinality.SetUint64(s.domain.Cardinality) - bPos.Mod(&bPos, &bCardinality) - si := s.deriveQueriesPositions(int(bPos.Uint64()), int(s.domain.Cardinality)) - - for i := 0; i < s.nbSteps; i++ { - - // build proofs of queries at s[i] - t := merkletree.New(s.h) - err := t.SetIndex(uint64(si[i])) - if err != nil { - return res, err - } - for k := 0; k < len(evalsAtRound[i]); k++ { - t.Push(evalsAtRound[i][k].Marshal()) - } - mr, ProofSet, _, numLeaves := t.Prove() - - // c denotes the entry that contains the full Merkle proof. The entry 1-c will - // only contain 2 elements, which are the neighbor point, and the hash of the - // first point. The remaining of the Merkle path is common to both the original - // point and its neighbor. - c := si[i] % 2 - res.Interactions[i][c] = MerkleProof{mr, ProofSet, numLeaves} - res.Interactions[i][1-c] = MerkleProof{ - mr, - make([][]byte, 2), - numLeaves, - } - res.Interactions[i][1-c].ProofSet[0] = evalsAtRound[i][si[i]+1-2*c].Marshal() - s.h.Reset() - _, err = s.h.Write(res.Interactions[i][c].ProofSet[0]) - if err != nil { - return res, err - } - res.Interactions[i][1-c].ProofSet[1] = s.h.Sum(nil) - - } - - return res, nil - -} - -// BuildProofOfProximity generates a proof that a function, given as an oracle from -// the verifier point of view, is in fact δ-close to a polynomial. -func (s radixTwoFri) BuildProofOfProximity(p []fr.Element) (ProofOfProximity, error) { - - // the proof will contain nbSteps Interactions - var proof ProofOfProximity - proof.Rounds = make([]Round, nbRounds) - - // evaluate p - // evaluate p and sort the result - _p := make([]fr.Element, s.domain.Cardinality) - copy(_p, p) - s.domain.FFT(_p, fft.DIF) - fft.BitReverse(_p) - - var err error - var salt, one fr.Element - one.SetOne() - for i := 0; i < nbRounds; i++ { - proof.Rounds[i], err = s.buildProofOfProximitySingleRound(salt, _p) - if err != nil { - return proof, err - } - salt.Add(&salt, &one) - } - - return proof, nil -} - -// verifyProofOfProximitySingleRound verifies the proof of proximity. It returns an error if the -// verification fails. -func (s radixTwoFri) verifyProofOfProximitySingleRound(salt fr.Element, proof Round) error { - - // Fiat Shamir transcript to derive the challenges - xis := make([]string, s.nbSteps+1) - for i := 0; i < s.nbSteps; i++ { - xis[i] = fmt.Sprintf("x%d", i) - } - xis[s.nbSteps] = "s0" - fs := fiatshamir.NewTranscript(s.h, xis...) - - xi := make([]fr.Element, s.nbSteps) - - // the salt is binded to the first challenge, to ensure the challenges - // are different at each round. - err := fs.Bind(xis[0], salt.Marshal()) - if err != nil { - return err - } - - for i := 0; i < s.nbSteps; i++ { - err := fs.Bind(xis[i], proof.Interactions[i][0].MerkleRoot) - if err != nil { - return err - } - bxi, err := fs.ComputeChallenge(xis[i]) - if err != nil { - return err - } - xi[i].SetBytes(bxi) - } - - // derive the verifier queries - // for i := 0; i < len(proof.evaluation); i++ { - // err := fs.Bind(xis[s.nbSteps], proof.evaluation[i].Marshal()) - // if err != nil { - // return err - // } - // } - err = fs.Bind(xis[s.nbSteps], proof.Evaluation.Marshal()) - if err != nil { - return err - } - binSeed, err := fs.ComputeChallenge(xis[s.nbSteps]) - if err != nil { - return err - } - var bPos, bCardinality big.Int - bPos.SetBytes(binSeed) - bCardinality.SetUint64(s.domain.Cardinality) - bPos.Mod(&bPos, &bCardinality) - si := s.deriveQueriesPositions(int(bPos.Uint64()), int(s.domain.Cardinality)) - - // for each round check the Merkle proof and the correctness of the folding - - // current size of the polynomial - var accGInv fr.Element - accGInv.Set(&s.domain.GeneratorInv) - for i := 0; i < s.nbSteps; i++ { - - // correctness of Merkle proof - // c is the entry containing the full Merkle proof. - c := si[i] % 2 - res := merkletree.VerifyProof( - s.h, - proof.Interactions[i][c].MerkleRoot, - proof.Interactions[i][c].ProofSet, - uint64(si[i]), - proof.Interactions[i][c].numLeaves, - ) - if !res { - return ErrMerklePath - } - - // we verify the Merkle proof for the neighbor query, to do that we have - // to pick the full Merkle proof of the first entry, stripped off of the leaf and - // the first node. We replace the leaf and the first node by the leaf and the first - // node of the partial Merkle proof, since the leaf and the first node of both proofs - // are the only entries that differ. - ProofSet := make([][]byte, len(proof.Interactions[i][c].ProofSet)) - copy(ProofSet[2:], proof.Interactions[i][c].ProofSet[2:]) - ProofSet[0] = proof.Interactions[i][1-c].ProofSet[0] - ProofSet[1] = proof.Interactions[i][1-c].ProofSet[1] - res = merkletree.VerifyProof( - s.h, - proof.Interactions[i][1-c].MerkleRoot, - ProofSet, - uint64(si[i]+1-2*c), - proof.Interactions[i][1-c].numLeaves, - ) - if !res { - return ErrMerklePath - } - - // correctness of the folding - if i < s.nbSteps-1 { - - var fe, fo, l, r, fn fr.Element - - // l = P(gⁱ), r = P(g^{i+n/2}) - l.SetBytes(proof.Interactions[i][0].ProofSet[0]) - r.SetBytes(proof.Interactions[i][1].ProofSet[0]) - - // (g^{si[i]}, g^{si[i]+1}) is the fiber of g^{2*si[i]}. The system to solve - // (for P₀(g^{2si[i]}), P₀(g^{2si[i]}) ) is: - // P(g^{si[i]}) = P₀(g^{2si[i]}) + g^{si[i]/2}*P₀(g^{2si[i]}) - // P(g^{si[i]+1}) = P₀(g^{2si[i]}) - g^{si[i]/2}*P₀(g^{2si[i]}) - bm := big.NewInt(int64(si[i] / 2)) - var ginv fr.Element - ginv.Exp(accGInv, bm) - fe.Add(&l, &r) // P₁(g²ⁱ) (to be multiplied by 2⁻¹) - fo.Sub(&l, &r).Mul(&fo, &ginv) // P₀(g²ⁱ) (to be multiplied by 2⁻¹) - fo.Mul(&fo, &xi[i]).Add(&fo, &fe).Mul(&fo, &twoInv) // P₀(g²ⁱ) + xᵢ * P₁(g²ⁱ) - - fn.SetBytes(proof.Interactions[i+1][si[i+1]%2].ProofSet[0]) - - if !fo.Equal(&fn) { - return ErrProximityTestFolding - } - - // next inverse generator - accGInv.Square(&accGInv) - } - - } - - // last transition - var fe, fo, l, r fr.Element - - l.SetBytes(proof.Interactions[s.nbSteps-1][0].ProofSet[0]) - r.SetBytes(proof.Interactions[s.nbSteps-1][1].ProofSet[0]) - - _si := si[s.nbSteps-1] / 2 - - accGInv.Exp(accGInv, big.NewInt(int64(_si))) - - fe.Add(&l, &r) // P₁(g²ⁱ) (to be multiplied by 2⁻¹) - fo.Sub(&l, &r).Mul(&fo, &accGInv) // P₀(g²ⁱ) (to be multiplied by 2⁻¹) - fo.Mul(&fo, &xi[s.nbSteps-1]).Add(&fo, &fe).Mul(&fo, &twoInv) // P₀(g²ⁱ) + xᵢ * P₁(g²ⁱ) - - // Last step: the final evaluation should be the evaluation of a degree 0 polynomial, - // so it must be constant. - if !fo.Equal(&proof.Evaluation) { - return ErrProximityTestFolding - } - - return nil -} - -// VerifyProofOfProximity verifies the proof, by checking each interaction one -// by one. -func (s radixTwoFri) VerifyProofOfProximity(proof ProofOfProximity) error { - - var salt, one fr.Element - one.SetOne() - for i := 0; i < nbRounds; i++ { - err := s.verifyProofOfProximitySingleRound(salt, proof.Rounds[i]) - if err != nil { - return err - } - salt.Add(&salt, &one) - } - return nil - -} diff --git a/ecc/bw6-756/fr/fri/fri_test.go b/ecc/bw6-756/fr/fri/fri_test.go deleted file mode 100644 index c302566bcf..0000000000 --- a/ecc/bw6-756/fr/fri/fri_test.go +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fri - -import ( - "crypto/sha256" - "fmt" - "math/big" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/gen" - "github.com/leanovate/gopter/prop" -) - -// logFiber returns u, v such that {g^u, g^v} = f⁻¹((g²)^{_p}) -func logFiber(_p, _n int) (_u, _v big.Int) { - if _p%2 == 0 { - _u.SetInt64(int64(_p / 2)) - _v.SetInt64(int64(_p/2 + _n/2)) - } else { - l := (_n - 1 - _p) / 2 - _u.SetInt64(int64(_n - 1 - l)) - _v.SetInt64(int64(_n - 1 - l - _n/2)) - } - return -} - -func randomPolynomial(size uint64, seed int32) []fr.Element { - p := make([]fr.Element, size) - p[0].SetUint64(uint64(seed)) - for i := 1; i < len(p); i++ { - p[i].Square(&p[i-1]) - } - return p -} - -// convertOrderCanonical convert the index i, an entry in a -// sorted polynomial, to the corresponding entry in canonical -// representation. n is the size of the polynomial. -func convertSortedCanonical(i, n int) int { - if i%2 == 0 { - return i / 2 - } else { - l := (n - 1 - i) / 2 - return n - 1 - l - } -} - -func TestFRI(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - parameters.MinSuccessfulTests = 10 - - properties := gopter.NewProperties(parameters) - - size := 4096 - - properties.Property("verifying wrong opening should fail", prop.ForAll( - - func(m int32) bool { - - _s := RADIX_2_FRI.New(uint64(size), sha256.New()) - s := _s.(radixTwoFri) - - p := randomPolynomial(uint64(size), m) - - pos := int64(m % 4096) - pp, _ := s.BuildProofOfProximity(p) - - openingProof, err := s.Open(p, uint64(pos)) - if err != nil { - t.Fatal(err) - } - - // check the Merkle path - tamperedPosition := pos + 1 - err = s.VerifyOpening(uint64(tamperedPosition), openingProof, pp) - - return err != nil - - }, - gen.Int32Range(1, int32(rho*size)), - )) - - properties.Property("verifying correct opening should succeed", prop.ForAll( - - func(m int32) bool { - - _s := RADIX_2_FRI.New(uint64(size), sha256.New()) - s := _s.(radixTwoFri) - - p := randomPolynomial(uint64(size), m) - - pos := uint64(m % int32(size)) - pp, _ := s.BuildProofOfProximity(p) - - openingProof, err := s.Open(p, uint64(pos)) - if err != nil { - t.Fatal(err) - } - - // check the Merkle path - err = s.VerifyOpening(uint64(pos), openingProof, pp) - - return err == nil - - }, - gen.Int32Range(0, int32(rho*size)), - )) - - properties.Property("The claimed value of a polynomial should match P(x)", prop.ForAll( - func(m int32) bool { - - _s := RADIX_2_FRI.New(uint64(size), sha256.New()) - s := _s.(radixTwoFri) - - p := randomPolynomial(uint64(size), m) - - // check the opening value - var g fr.Element - pos := int64(m % 4096) - g.Set(&s.domain.Generator) - g.Exp(g, big.NewInt(pos)) - - var val fr.Element - for i := len(p) - 1; i >= 0; i-- { - val.Mul(&val, &g) - val.Add(&p[i], &val) - } - - openingProof, err := s.Open(p, uint64(pos)) - if err != nil { - t.Fatal(err) - } - - return openingProof.ClaimedValue.Equal(&val) - - }, - gen.Int32Range(0, int32(rho*size)), - )) - - properties.Property("Derive queries position: points should belong the correct fiber", prop.ForAll( - - func(m int32) bool { - - _s := RADIX_2_FRI.New(uint64(size), sha256.New()) - s := _s.(radixTwoFri) - - var g fr.Element - - _m := int(m) % size - pos := s.deriveQueriesPositions(_m, int(s.domain.Cardinality)) - g.Set(&s.domain.Generator) - n := int(s.domain.Cardinality) - - for i := 0; i < len(pos)-1; i++ { - - u, v := logFiber(pos[i], n) - - var g1, g2, g3 fr.Element - g1.Exp(g, &u).Square(&g1) - g2.Exp(g, &v).Square(&g2) - nextPos := convertSortedCanonical(pos[i+1], n/2) - g3.Square(&g).Exp(g3, big.NewInt(int64(nextPos))) - - if !g1.Equal(&g2) || !g1.Equal(&g3) { - return false - } - g.Square(&g) - n = n >> 1 - } - return true - }, - gen.Int32Range(0, int32(rho*size)), - )) - - properties.Property("verifying a correctly formed proof should succeed", prop.ForAll( - - func(s int32) bool { - - p := randomPolynomial(uint64(size), s) - - iop := RADIX_2_FRI.New(uint64(size), sha256.New()) - proof, err := iop.BuildProofOfProximity(p) - if err != nil { - t.Fatal(err) - } - - err = iop.VerifyProofOfProximity(proof) - return err == nil - }, - gen.Int32Range(0, int32(rho*size)), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -// Benchmarks - -func BenchmarkProximityVerification(b *testing.B) { - - baseSize := 16 - - for i := 0; i < 10; i++ { - - size := baseSize << i - p := make([]fr.Element, size) - for k := 0; k < size; k++ { - p[k].SetRandom() - } - - iop := RADIX_2_FRI.New(uint64(size), sha256.New()) - proof, _ := iop.BuildProofOfProximity(p) - - b.Run(fmt.Sprintf("Polynomial size %d", size), func(b *testing.B) { - b.ResetTimer() - for l := 0; l < b.N; l++ { - iop.VerifyProofOfProximity(proof) - } - }) - - } -} diff --git a/ecc/bw6-756/fr/generator.go b/ecc/bw6-756/fr/generator.go deleted file mode 100644 index 5606adfa2f..0000000000 --- a/ecc/bw6-756/fr/generator.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import ( - "fmt" - "math/big" - "math/bits" - - "github.com/consensys/gnark-crypto/ecc" -) - -// Generator returns a generator for Z/2^(log(m))Z -// or an error if m is too big (required root of unity doesn't exist) -func Generator(m uint64) (Element, error) { - x := ecc.NextPowerOfTwo(m) - - var rootOfUnity Element - - rootOfUnity.SetString("199251335866470442271346949249090720992237796757894062992204115206570647302191425225605716521843542790404563904580") - const maxOrderRoot uint64 = 41 - - // find generator for Z/2^(log(m))Z - logx := uint64(bits.TrailingZeros64(x)) - if logx > maxOrderRoot { - return Element{}, fmt.Errorf("m (%d) is too big: the required root of unity does not exist", m) - } - - expo := uint64(1 << (maxOrderRoot - logx)) - var generator Element - generator.Exp(rootOfUnity, big.NewInt(int64(expo))) // order x - return generator, nil -} diff --git a/ecc/bw6-756/fr/gkr/gkr.go b/ecc/bw6-756/fr/gkr/gkr.go deleted file mode 100644 index ae9a3ecfa5..0000000000 --- a/ecc/bw6-756/fr/gkr/gkr.go +++ /dev/null @@ -1,944 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package gkr - -import ( - "fmt" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/polynomial" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/sumcheck" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - "github.com/consensys/gnark-crypto/internal/parallel" - "github.com/consensys/gnark-crypto/utils" - "math/big" - "strconv" - "sync" -) - -// The goal is to prove/verify evaluations of many instances of the same circuit - -// Gate must be a low-degree polynomial -type Gate interface { - Evaluate(...fr.Element) fr.Element - Degree() int -} - -type Wire struct { - Gate Gate - Inputs []*Wire // if there are no Inputs, the wire is assumed an input wire - nbUniqueOutputs int // number of other wires using it as input, not counting duplicates (i.e. providing two inputs to the same gate counts as one) -} - -type Circuit []Wire - -func (w Wire) IsInput() bool { - return len(w.Inputs) == 0 -} - -func (w Wire) IsOutput() bool { - return w.nbUniqueOutputs == 0 -} - -func (w Wire) NbClaims() int { - if w.IsOutput() { - return 1 - } - return w.nbUniqueOutputs -} - -func (w Wire) noProof() bool { - return w.IsInput() && w.NbClaims() == 1 -} - -func (c Circuit) maxGateDegree() int { - res := 1 - for i := range c { - if !c[i].IsInput() { - res = utils.Max(res, c[i].Gate.Degree()) - } - } - return res -} - -// WireAssignment is assignment of values to the same wire across many instances of the circuit -type WireAssignment map[*Wire]polynomial.MultiLin - -type Proof []sumcheck.Proof // for each layer, for each wire, a sumcheck (for each variable, a polynomial) - -type eqTimesGateEvalSumcheckLazyClaims struct { - wire *Wire - evaluationPoints [][]fr.Element - claimedEvaluations []fr.Element - manager *claimsManager // WARNING: Circular references -} - -func (e *eqTimesGateEvalSumcheckLazyClaims) ClaimsNum() int { - return len(e.evaluationPoints) -} - -func (e *eqTimesGateEvalSumcheckLazyClaims) VarsNum() int { - return len(e.evaluationPoints[0]) -} - -func (e *eqTimesGateEvalSumcheckLazyClaims) CombinedSum(a fr.Element) fr.Element { - evalsAsPoly := polynomial.Polynomial(e.claimedEvaluations) - return evalsAsPoly.Eval(&a) -} - -func (e *eqTimesGateEvalSumcheckLazyClaims) Degree(int) int { - return 1 + e.wire.Gate.Degree() -} - -func (e *eqTimesGateEvalSumcheckLazyClaims) VerifyFinalEval(r []fr.Element, combinationCoeff fr.Element, purportedValue fr.Element, proof interface{}) error { - inputEvaluationsNoRedundancy := proof.([]fr.Element) - - // the eq terms - numClaims := len(e.evaluationPoints) - evaluation := polynomial.EvalEq(e.evaluationPoints[numClaims-1], r) - for i := numClaims - 2; i >= 0; i-- { - evaluation.Mul(&evaluation, &combinationCoeff) - eq := polynomial.EvalEq(e.evaluationPoints[i], r) - evaluation.Add(&evaluation, &eq) - } - - // the g(...) term - var gateEvaluation fr.Element - if e.wire.IsInput() { - gateEvaluation = e.manager.assignment[e.wire].Evaluate(r, e.manager.memPool) - } else { - inputEvaluations := make([]fr.Element, len(e.wire.Inputs)) - indexesInProof := make(map[*Wire]int, len(inputEvaluationsNoRedundancy)) - - proofI := 0 - for inI, in := range e.wire.Inputs { - indexInProof, found := indexesInProof[in] - if !found { - indexInProof = proofI - indexesInProof[in] = indexInProof - - // defer verification, store new claim - e.manager.add(in, r, inputEvaluationsNoRedundancy[indexInProof]) - proofI++ - } - inputEvaluations[inI] = inputEvaluationsNoRedundancy[indexInProof] - } - if proofI != len(inputEvaluationsNoRedundancy) { - return fmt.Errorf("%d input wire evaluations given, %d expected", len(inputEvaluationsNoRedundancy), proofI) - } - gateEvaluation = e.wire.Gate.Evaluate(inputEvaluations...) - } - - evaluation.Mul(&evaluation, &gateEvaluation) - - if evaluation.Equal(&purportedValue) { - return nil - } - return fmt.Errorf("incompatible evaluations") -} - -type eqTimesGateEvalSumcheckClaims struct { - wire *Wire - evaluationPoints [][]fr.Element // x in the paper - claimedEvaluations []fr.Element // y in the paper - manager *claimsManager - - inputPreprocessors []polynomial.MultiLin // P_u in the paper, so that we don't need to pass along all the circuit's evaluations - - eq polynomial.MultiLin // ∑_i τ_i eq(x_i, -) -} - -func (c *eqTimesGateEvalSumcheckClaims) Combine(combinationCoeff fr.Element) polynomial.Polynomial { - varsNum := c.VarsNum() - eqLength := 1 << varsNum - claimsNum := c.ClaimsNum() - // initialize the eq tables - c.eq = c.manager.memPool.Make(eqLength) - - c.eq[0].SetOne() - c.eq.Eq(c.evaluationPoints[0]) - - newEq := polynomial.MultiLin(c.manager.memPool.Make(eqLength)) - aI := combinationCoeff - - for k := 1; k < claimsNum; k++ { //TODO: parallelizable? - // define eq_k = aᵏ eq(x_k1, ..., x_kn, *, ..., *) where x_ki are the evaluation points - newEq[0].Set(&aI) - - c.eqAcc(c.eq, newEq, c.evaluationPoints[k]) - - // newEq.Eq(c.evaluationPoints[k]) - // eqAsPoly := polynomial.Polynomial(c.eq) //just semantics - // eqAsPoly.Add(eqAsPoly, polynomial.Polynomial(newEq)) - - if k+1 < claimsNum { - aI.Mul(&aI, &combinationCoeff) - } - } - - c.manager.memPool.Dump(newEq) - - // from this point on the claim is a rather simple one: g = E(h) × R_v (P_u0(h), ...) where E and the P_u are multilinear and R_v is of low-degree - - return c.computeGJ() -} - -// eqAcc sets m to an eq table at q and then adds it to e -func (c *eqTimesGateEvalSumcheckClaims) eqAcc(e, m polynomial.MultiLin, q []fr.Element) { - n := len(q) - - //At the end of each iteration, m(h₁, ..., hₙ) = Eq(q₁, ..., qᵢ₊₁, h₁, ..., hᵢ₊₁) - for i := range q { // In the comments we use a 1-based index so q[i] = qᵢ₊₁ - // go through all assignments of (b₁, ..., bᵢ) ∈ {0,1}ⁱ - const threshold = 1 << 6 - k := 1 << i - if k < threshold { - for j := 0; j < k; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // Eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) Eq(qᵢ₊₁, 1) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // Eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) Eq(qᵢ₊₁, 0) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - } else { - c.manager.workers.Submit(k, func(start, end int) { - for j := start; j < end; j++ { - j0 := j << (n - i) // bᵢ₊₁ = 0 - j1 := j0 + 1<<(n-1-i) // bᵢ₊₁ = 1 - - m[j1].Mul(&q[i], &m[j0]) // Eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 1) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) Eq(qᵢ₊₁, 1) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) qᵢ₊₁ - m[j0].Sub(&m[j0], &m[j1]) // Eq(q₁, ..., qᵢ₊₁, b₁, ..., bᵢ, 0) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) Eq(qᵢ₊₁, 0) = Eq(q₁, ..., qᵢ, b₁, ..., bᵢ) (1-qᵢ₊₁) - } - }, 1024).Wait() - } - - } - c.manager.workers.Submit(len(e), func(start, end int) { - for i := start; i < end; i++ { - e[i].Add(&e[i], &m[i]) - } - }, 512).Wait() - - // e.Add(e, polynomial.Polynomial(m)) -} - -// computeGJ: gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) = ∑_{0≤i<2ⁿ⁻ʲ} E(r₁, ..., X_j, i...) R_v( P_u0(r₁, ..., X_j, i...), ... ) where E = ∑ eq_k -// the polynomial is represented by the evaluations g_j(1), g_j(2), ..., g_j(deg(g_j)). -// The value g_j(0) is inferred from the equation g_j(0) + g_j(1) = gⱼ₋₁(rⱼ₋₁). By convention, g₀ is a constant polynomial equal to the claimed sum. -func (c *eqTimesGateEvalSumcheckClaims) computeGJ() polynomial.Polynomial { - - degGJ := 1 + c.wire.Gate.Degree() // guaranteed to be no smaller than the actual deg(g_j) - nbGateIn := len(c.inputPreprocessors) - - // Let f ∈ { E(r₁, ..., X_j, d...) } ∪ {P_ul(r₁, ..., X_j, d...) }. It is linear in X_j, so f(m) = m×(f(1) - f(0)) + f(0), and f(0), f(1) are easily computed from the bookkeeping tables - s := make([]polynomial.MultiLin, nbGateIn+1) - s[0] = c.eq - copy(s[1:], c.inputPreprocessors) - - // Perf-TODO: Collate once at claim "combination" time and not again. then, even folding can be done in one operation every time "next" is called - nbInner := len(s) // wrt output, which has high nbOuter and low nbInner - nbOuter := len(s[0]) / 2 - - gJ := make([]fr.Element, degGJ) - var mu sync.Mutex - computeAll := func(start, end int) { - var step fr.Element - - res := make([]fr.Element, degGJ) - operands := make([]fr.Element, degGJ*nbInner) - - for i := start; i < end; i++ { - - block := nbOuter + i - for j := 0; j < nbInner; j++ { - step.Set(&s[j][i]) - operands[j].Set(&s[j][block]) - step.Sub(&operands[j], &step) - for d := 1; d < degGJ; d++ { - operands[d*nbInner+j].Add(&operands[(d-1)*nbInner+j], &step) - } - } - - _s := 0 - _e := nbInner - for d := 0; d < degGJ; d++ { - summand := c.wire.Gate.Evaluate(operands[_s+1 : _e]...) - summand.Mul(&summand, &operands[_s]) - res[d].Add(&res[d], &summand) - _s, _e = _e, _e+nbInner - } - } - mu.Lock() - for i := 0; i < len(gJ); i++ { - gJ[i].Add(&gJ[i], &res[i]) - } - mu.Unlock() - } - - const minBlockSize = 64 - - if nbOuter < minBlockSize { - // no parallelization - computeAll(0, nbOuter) - } else { - c.manager.workers.Submit(nbOuter, computeAll, minBlockSize).Wait() - } - - // Perf-TODO: Separate functions Gate.TotalDegree and Gate.Degree(i) so that we get to use possibly smaller values for degGJ. Won't help with MiMC though - - return gJ -} - -// Next first folds the "preprocessing" and "eq" polynomials then compute the new g_j -func (c *eqTimesGateEvalSumcheckClaims) Next(element fr.Element) polynomial.Polynomial { - const minBlockSize = 512 - n := len(c.eq) / 2 - if n < minBlockSize { - // no parallelization - for i := 0; i < len(c.inputPreprocessors); i++ { - c.inputPreprocessors[i].Fold(element) - } - c.eq.Fold(element) - } else { - wgs := make([]*sync.WaitGroup, len(c.inputPreprocessors)) - for i := 0; i < len(c.inputPreprocessors); i++ { - wgs[i] = c.manager.workers.Submit(n, c.inputPreprocessors[i].FoldParallel(element), minBlockSize) - } - c.manager.workers.Submit(n, c.eq.FoldParallel(element), minBlockSize).Wait() - for _, wg := range wgs { - wg.Wait() - } - } - - return c.computeGJ() -} - -func (c *eqTimesGateEvalSumcheckClaims) VarsNum() int { - return len(c.evaluationPoints[0]) -} - -func (c *eqTimesGateEvalSumcheckClaims) ClaimsNum() int { - return len(c.claimedEvaluations) -} - -func (c *eqTimesGateEvalSumcheckClaims) ProveFinalEval(r []fr.Element) interface{} { - - //defer the proof, return list of claims - evaluations := make([]fr.Element, 0, len(c.wire.Inputs)) - noMoreClaimsAllowed := make(map[*Wire]struct{}, len(c.inputPreprocessors)) - noMoreClaimsAllowed[c.wire] = struct{}{} - - for inI, in := range c.wire.Inputs { - puI := c.inputPreprocessors[inI] - if _, found := noMoreClaimsAllowed[in]; !found { - noMoreClaimsAllowed[in] = struct{}{} - puI.Fold(r[len(r)-1]) - c.manager.add(in, r, puI[0]) - evaluations = append(evaluations, puI[0]) - } - c.manager.memPool.Dump(puI) - } - - c.manager.memPool.Dump(c.claimedEvaluations, c.eq) - - return evaluations -} - -type claimsManager struct { - claimsMap map[*Wire]*eqTimesGateEvalSumcheckLazyClaims - assignment WireAssignment - memPool *polynomial.Pool - workers *utils.WorkerPool -} - -func newClaimsManager(c Circuit, assignment WireAssignment, o settings) (claims claimsManager) { - claims.assignment = assignment - claims.claimsMap = make(map[*Wire]*eqTimesGateEvalSumcheckLazyClaims, len(c)) - claims.memPool = o.pool - claims.workers = o.workers - - for i := range c { - wire := &c[i] - - claims.claimsMap[wire] = &eqTimesGateEvalSumcheckLazyClaims{ - wire: wire, - evaluationPoints: make([][]fr.Element, 0, wire.NbClaims()), - claimedEvaluations: claims.memPool.Make(wire.NbClaims()), - manager: &claims, - } - } - return -} - -func (m *claimsManager) add(wire *Wire, evaluationPoint []fr.Element, evaluation fr.Element) { - claim := m.claimsMap[wire] - i := len(claim.evaluationPoints) - claim.claimedEvaluations[i] = evaluation - claim.evaluationPoints = append(claim.evaluationPoints, evaluationPoint) -} - -func (m *claimsManager) getLazyClaim(wire *Wire) *eqTimesGateEvalSumcheckLazyClaims { - return m.claimsMap[wire] -} - -func (m *claimsManager) getClaim(wire *Wire) *eqTimesGateEvalSumcheckClaims { - lazy := m.claimsMap[wire] - res := &eqTimesGateEvalSumcheckClaims{ - wire: wire, - evaluationPoints: lazy.evaluationPoints, - claimedEvaluations: lazy.claimedEvaluations, - manager: m, - } - - if wire.IsInput() { - res.inputPreprocessors = []polynomial.MultiLin{m.memPool.Clone(m.assignment[wire])} - } else { - res.inputPreprocessors = make([]polynomial.MultiLin, len(wire.Inputs)) - - for inputI, inputW := range wire.Inputs { - res.inputPreprocessors[inputI] = m.memPool.Clone(m.assignment[inputW]) //will be edited later, so must be deep copied - } - } - return res -} - -func (m *claimsManager) deleteClaim(wire *Wire) { - delete(m.claimsMap, wire) -} - -type settings struct { - pool *polynomial.Pool - sorted []*Wire - transcript *fiatshamir.Transcript - transcriptPrefix string - nbVars int - workers *utils.WorkerPool -} - -type Option func(*settings) - -func WithPool(pool *polynomial.Pool) Option { - return func(options *settings) { - options.pool = pool - } -} - -func WithSortedCircuit(sorted []*Wire) Option { - return func(options *settings) { - options.sorted = sorted - } -} - -func WithWorkers(workers *utils.WorkerPool) Option { - return func(options *settings) { - options.workers = workers - } -} - -// MemoryRequirements returns an increasing vector of memory allocation sizes required for proving a GKR statement -func (c Circuit) MemoryRequirements(nbInstances int) []int { - res := []int{256, nbInstances, nbInstances * (c.maxGateDegree() + 1)} - - if res[0] > res[1] { // make sure it's sorted - res[0], res[1] = res[1], res[0] - if res[1] > res[2] { - res[1], res[2] = res[2], res[1] - } - } - - return res -} - -func setup(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (settings, error) { - var o settings - var err error - for _, option := range options { - option(&o) - } - - o.nbVars = assignment.NumVars() - nbInstances := assignment.NumInstances() - if 1< 1 { //combine the claims - size++ - } - size += logNbInstances // full run of sumcheck on logNbInstances variables - } - - nums := make([]string, utils.Max(len(sorted), logNbInstances)) - for i := range nums { - nums[i] = strconv.Itoa(i) - } - - challenges := make([]string, size) - - // output wire claims - firstChallengePrefix := prefix + "fC." - for j := 0; j < logNbInstances; j++ { - challenges[j] = firstChallengePrefix + nums[j] - } - j := logNbInstances - for i := len(sorted) - 1; i >= 0; i-- { - if sorted[i].noProof() { - continue - } - wirePrefix := prefix + "w" + nums[i] + "." - - if sorted[i].NbClaims() > 1 { - challenges[j] = wirePrefix + "comb" - j++ - } - - partialSumPrefix := wirePrefix + "pSP." - for k := 0; k < logNbInstances; k++ { - challenges[j] = partialSumPrefix + nums[k] - j++ - } - } - return challenges -} - -func getFirstChallengeNames(logNbInstances int, prefix string) []string { - res := make([]string, logNbInstances) - firstChallengePrefix := prefix + "fC." - for i := 0; i < logNbInstances; i++ { - res[i] = firstChallengePrefix + strconv.Itoa(i) - } - return res -} - -func getChallenges(transcript *fiatshamir.Transcript, names []string) ([]fr.Element, error) { - res := make([]fr.Element, len(names)) - for i, name := range names { - if bytes, err := transcript.ComputeChallenge(name); err == nil { - res[i].SetBytes(bytes) - } else { - return nil, err - } - } - return res, nil -} - -// Prove consistency of the claimed assignment -func Prove(c Circuit, assignment WireAssignment, transcriptSettings fiatshamir.Settings, options ...Option) (Proof, error) { - o, err := setup(c, assignment, transcriptSettings, options...) - if err != nil { - return nil, err - } - defer o.workers.Stop() - - claims := newClaimsManager(c, assignment, o) - - proof := make(Proof, len(c)) - // firstChallenge called rho in the paper - var firstChallenge []fr.Element - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return nil, err - } - - wirePrefix := o.transcriptPrefix + "w" - var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - - wire := o.sorted[i] - - if wire.IsOutput() { - claims.add(wire, firstChallenge, assignment[wire].Evaluate(firstChallenge, claims.memPool)) - } - - claim := claims.getClaim(wire) - if wire.noProof() { // input wires with one claim only - proof[i] = sumcheck.Proof{ - PartialSumPolys: []polynomial.Polynomial{}, - FinalEvalProof: []fr.Element{}, - } - } else { - if proof[i], err = sumcheck.Prove( - claim, fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err != nil { - return proof, err - } - - finalEvalProof := proof[i].FinalEvalProof.([]fr.Element) - baseChallenge = make([][]byte, len(finalEvalProof)) - for j := range finalEvalProof { - bytes := finalEvalProof[j].Bytes() - baseChallenge[j] = bytes[:] - } - } - // the verifier checks a single claim about input wires itself - claims.deleteClaim(wire) - } - - return proof, nil -} - -// Verify the consistency of the claimed output with the claimed input -// Unlike in Prove, the assignment argument need not be complete -func Verify(c Circuit, assignment WireAssignment, proof Proof, transcriptSettings fiatshamir.Settings, options ...Option) error { - o, err := setup(c, assignment, transcriptSettings, options...) - if err != nil { - return err - } - defer o.workers.Stop() - - claims := newClaimsManager(c, assignment, o) - - var firstChallenge []fr.Element - firstChallenge, err = getChallenges(o.transcript, getFirstChallengeNames(o.nbVars, o.transcriptPrefix)) - if err != nil { - return err - } - - wirePrefix := o.transcriptPrefix + "w" - var baseChallenge [][]byte - for i := len(c) - 1; i >= 0; i-- { - wire := o.sorted[i] - - if wire.IsOutput() { - claims.add(wire, firstChallenge, assignment[wire].Evaluate(firstChallenge, claims.memPool)) - } - - proofW := proof[i] - finalEvalProof := proofW.FinalEvalProof.([]fr.Element) - claim := claims.getLazyClaim(wire) - if wire.noProof() { // input wires with one claim only - // make sure the proof is empty - if len(finalEvalProof) != 0 || len(proofW.PartialSumPolys) != 0 { - return fmt.Errorf("no proof allowed for input wire with a single claim") - } - - if wire.NbClaims() == 1 { // input wire - // simply evaluate and see if it matches - evaluation := assignment[wire].Evaluate(claim.evaluationPoints[0], claims.memPool) - if !claim.claimedEvaluations[0].Equal(&evaluation) { - return fmt.Errorf("incorrect input wire claim") - } - } - } else if err = sumcheck.Verify( - claim, proof[i], fiatshamir.WithTranscript(o.transcript, wirePrefix+strconv.Itoa(i)+".", baseChallenge...), - ); err == nil { - baseChallenge = make([][]byte, len(finalEvalProof)) - for j := range finalEvalProof { - bytes := finalEvalProof[j].Bytes() - baseChallenge[j] = bytes[:] - } - } else { - return fmt.Errorf("sumcheck proof rejected: %v", err) //TODO: Any polynomials to dump? - } - claims.deleteClaim(wire) - } - return nil -} - -// outputsList also sets the nbUniqueOutputs fields. It also sets the wire metadata. -func outputsList(c Circuit, indexes map[*Wire]int) [][]int { - res := make([][]int, len(c)) - for i := range c { - res[i] = make([]int, 0) - c[i].nbUniqueOutputs = 0 - if c[i].IsInput() { - c[i].Gate = IdentityGate{} - } - } - ins := make(map[int]struct{}, len(c)) - for i := range c { - for k := range ins { // clear map - delete(ins, k) - } - for _, in := range c[i].Inputs { - inI := indexes[in] - res[inI] = append(res[inI], i) - if _, ok := ins[inI]; !ok { - in.nbUniqueOutputs++ - ins[inI] = struct{}{} - } - } - } - return res -} - -type topSortData struct { - outputs [][]int - status []int // status > 0 indicates number of inputs left to be ready. status = 0 means ready. status = -1 means done - index map[*Wire]int - leastReady int -} - -func (d *topSortData) markDone(i int) { - - d.status[i] = -1 - - for _, outI := range d.outputs[i] { - d.status[outI]-- - if d.status[outI] == 0 && outI < d.leastReady { - d.leastReady = outI - } - } - - for d.leastReady < len(d.status) && d.status[d.leastReady] != 0 { - d.leastReady++ - } -} - -func indexMap(c Circuit) map[*Wire]int { - res := make(map[*Wire]int, len(c)) - for i := range c { - res[&c[i]] = i - } - return res -} - -func statusList(c Circuit) []int { - res := make([]int, len(c)) - for i := range c { - res[i] = len(c[i].Inputs) - } - return res -} - -// topologicalSort sorts the wires in order of dependence. Such that for any wire, any one it depends on -// occurs before it. It tries to stick to the input order as much as possible. An already sorted list will remain unchanged. -// It also sets the nbOutput flags, and a dummy IdentityGate for input wires. -// Worst-case inefficient O(n^2), but that probably won't matter since the circuits are small. -// Furthermore, it is efficient with already-close-to-sorted lists, which are the expected input -func topologicalSort(c Circuit) []*Wire { - var data topSortData - data.index = indexMap(c) - data.outputs = outputsList(c, data.index) - data.status = statusList(c) - sorted := make([]*Wire, len(c)) - - for data.leastReady = 0; data.status[data.leastReady] != 0; data.leastReady++ { - } - - for i := range c { - sorted[i] = &c[data.leastReady] - data.markDone(data.leastReady) - } - - return sorted -} - -// Complete the circuit evaluation from input values -func (a WireAssignment) Complete(c Circuit) WireAssignment { - - sortedWires := topologicalSort(c) - nbInstances := a.NumInstances() - maxNbIns := 0 - - for _, w := range sortedWires { - maxNbIns = utils.Max(maxNbIns, len(w.Inputs)) - if a[w] == nil { - a[w] = make([]fr.Element, nbInstances) - } - } - - parallel.Execute(nbInstances, func(start, end int) { - ins := make([]fr.Element, maxNbIns) - for i := start; i < end; i++ { - for _, w := range sortedWires { - if !w.IsInput() { - for inI, in := range w.Inputs { - ins[inI] = a[in][i] - } - a[w][i] = w.Gate.Evaluate(ins[:len(w.Inputs)]...) - } - } - } - }) - - return a -} - -func (a WireAssignment) NumInstances() int { - for _, aW := range a { - return len(aW) - } - panic("empty assignment") -} - -func (a WireAssignment) NumVars() int { - for _, aW := range a { - return aW.NumVars() - } - panic("empty assignment") -} - -// SerializeToBigInts flattens a proof object into the given slice of big.Ints -// useful in gnark hints. TODO: Change propagation: Once this is merged, it will duplicate some code in std/gkr/bn254Prover.go. Remove that in favor of this -func (p Proof) SerializeToBigInts(outs []*big.Int) { - offset := 0 - for i := range p { - for _, poly := range p[i].PartialSumPolys { - frToBigInts(outs[offset:], poly) - offset += len(poly) - } - if p[i].FinalEvalProof != nil { - finalEvalProof := p[i].FinalEvalProof.([]fr.Element) - frToBigInts(outs[offset:], finalEvalProof) - offset += len(finalEvalProof) - } - } -} - -func frToBigInts(dst []*big.Int, src []fr.Element) { - for i := range src { - src[i].BigInt(dst[i]) - } -} - -// Gates defined by name -var Gates = map[string]Gate{ - "identity": IdentityGate{}, - "add": AddGate{}, - "sub": SubGate{}, - "neg": NegGate{}, - "mul": MulGate(2), -} - -type IdentityGate struct{} -type AddGate struct{} -type MulGate int -type SubGate struct{} -type NegGate struct{} - -func (IdentityGate) Evaluate(input ...fr.Element) fr.Element { - return input[0] -} - -func (IdentityGate) Degree() int { - return 1 -} - -func (g AddGate) Evaluate(x ...fr.Element) (res fr.Element) { - switch len(x) { - case 0: - // set zero - case 1: - res.Set(&x[0]) - default: - res.Add(&x[0], &x[1]) - for i := 2; i < len(x); i++ { - res.Add(&res, &x[i]) - } - } - return -} - -func (g AddGate) Degree() int { - return 1 -} - -func (g MulGate) Evaluate(x ...fr.Element) (res fr.Element) { - if len(x) != int(g) { - panic("wrong input count") - } - switch len(x) { - case 0: - res.SetOne() - case 1: - res.Set(&x[0]) - default: - res.Mul(&x[0], &x[1]) - for i := 2; i < len(x); i++ { - res.Mul(&res, &x[i]) - } - } - return -} - -func (g MulGate) Degree() int { - return int(g) -} - -func (g SubGate) Evaluate(element ...fr.Element) (diff fr.Element) { - if len(element) > 2 { - panic("not implemented") //TODO - } - diff.Sub(&element[0], &element[1]) - return -} - -func (g SubGate) Degree() int { - return 1 -} - -func (g NegGate) Evaluate(element ...fr.Element) (neg fr.Element) { - if len(element) != 1 { - panic("univariate gate") - } - neg.Neg(&element[0]) - return -} - -func (g NegGate) Degree() int { - return 1 -} diff --git a/ecc/bw6-756/fr/gkr/gkr_test.go b/ecc/bw6-756/fr/gkr/gkr_test.go deleted file mode 100644 index 7f24681ede..0000000000 --- a/ecc/bw6-756/fr/gkr/gkr_test.go +++ /dev/null @@ -1,750 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package gkr - -import ( - "encoding/json" - "fmt" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/mimc" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/polynomial" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/sumcheck" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/test_vector_utils" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - "github.com/consensys/gnark-crypto/utils" - "github.com/stretchr/testify/assert" - "hash" - "os" - "path/filepath" - "reflect" - "strconv" - "testing" - "time" -) - -func TestNoGateTwoInstances(t *testing.T) { - // Testing a single instance is not possible because the sumcheck implementation doesn't cover the trivial 0-variate case - testNoGate(t, []fr.Element{four, three}) -} - -func TestNoGate(t *testing.T) { - testManyInstances(t, 1, testNoGate) -} - -func TestSingleAddGateTwoInstances(t *testing.T) { - testSingleAddGate(t, []fr.Element{four, three}, []fr.Element{two, three}) -} - -func TestSingleAddGate(t *testing.T) { - testManyInstances(t, 2, testSingleAddGate) -} - -func TestSingleMulGateTwoInstances(t *testing.T) { - testSingleMulGate(t, []fr.Element{four, three}, []fr.Element{two, three}) -} - -func TestSingleMulGate(t *testing.T) { - testManyInstances(t, 2, testSingleMulGate) -} - -func TestSingleInputTwoIdentityGatesTwoInstances(t *testing.T) { - - testSingleInputTwoIdentityGates(t, []fr.Element{two, three}) -} - -func TestSingleInputTwoIdentityGates(t *testing.T) { - - testManyInstances(t, 2, testSingleInputTwoIdentityGates) -} - -func TestSingleInputTwoIdentityGatesComposedTwoInstances(t *testing.T) { - testSingleInputTwoIdentityGatesComposed(t, []fr.Element{two, one}) -} - -func TestSingleInputTwoIdentityGatesComposed(t *testing.T) { - testManyInstances(t, 1, testSingleInputTwoIdentityGatesComposed) -} - -func TestSingleMimcCipherGateTwoInstances(t *testing.T) { - testSingleMimcCipherGate(t, []fr.Element{one, one}, []fr.Element{one, two}) -} - -func TestSingleMimcCipherGate(t *testing.T) { - testManyInstances(t, 2, testSingleMimcCipherGate) -} - -func TestATimesBSquaredTwoInstances(t *testing.T) { - testATimesBSquared(t, 2, []fr.Element{one, one}, []fr.Element{one, two}) -} - -func TestShallowMimcTwoInstances(t *testing.T) { - testMimc(t, 2, []fr.Element{one, one}, []fr.Element{one, two}) -} -func TestMimcTwoInstances(t *testing.T) { - testMimc(t, 93, []fr.Element{one, one}, []fr.Element{one, two}) -} - -func TestMimc(t *testing.T) { - testManyInstances(t, 2, generateTestMimc(93)) -} - -func generateTestMimc(numRounds int) func(*testing.T, ...[]fr.Element) { - return func(t *testing.T, inputAssignments ...[]fr.Element) { - testMimc(t, numRounds, inputAssignments...) - } -} - -func TestSumcheckFromSingleInputTwoIdentityGatesGateTwoInstances(t *testing.T) { - circuit := Circuit{Wire{ - Gate: IdentityGate{}, - Inputs: []*Wire{}, - nbUniqueOutputs: 2, - }} - - wire := &circuit[0] - - assignment := WireAssignment{&circuit[0]: []fr.Element{two, three}} - var o settings - pool := polynomial.NewPool(256, 1<<11) - workers := utils.NewWorkerPool() - o.pool = &pool - o.workers = workers - - claimsManagerGen := func() *claimsManager { - manager := newClaimsManager(circuit, assignment, o) - manager.add(wire, []fr.Element{three}, five) - manager.add(wire, []fr.Element{four}, six) - return &manager - } - - transcriptGen := test_vector_utils.NewMessageCounterGenerator(4, 1) - - proof, err := sumcheck.Prove(claimsManagerGen().getClaim(wire), fiatshamir.WithHash(transcriptGen(), nil)) - assert.NoError(t, err) - err = sumcheck.Verify(claimsManagerGen().getLazyClaim(wire), proof, fiatshamir.WithHash(transcriptGen(), nil)) - assert.NoError(t, err) -} - -var one, two, three, four, five, six fr.Element - -func init() { - one.SetOne() - two.Double(&one) - three.Add(&two, &one) - four.Double(&two) - five.Add(&three, &two) - six.Double(&three) -} - -var testManyInstancesLogMaxInstances = -1 - -func getLogMaxInstances(t *testing.T) int { - if testManyInstancesLogMaxInstances == -1 { - - s := os.Getenv("GKR_LOG_INSTANCES") - if s == "" { - testManyInstancesLogMaxInstances = 5 - } else { - var err error - testManyInstancesLogMaxInstances, err = strconv.Atoi(s) - if err != nil { - t.Error(err) - } - } - - } - return testManyInstancesLogMaxInstances -} - -func testManyInstances(t *testing.T, numInput int, test func(*testing.T, ...[]fr.Element)) { - fullAssignments := make([][]fr.Element, numInput) - maxSize := 1 << getLogMaxInstances(t) - - t.Log("Entered test orchestrator, assigning and randomizing inputs") - - for i := range fullAssignments { - fullAssignments[i] = make([]fr.Element, maxSize) - setRandom(fullAssignments[i]) - } - - inputAssignments := make([][]fr.Element, numInput) - for numEvals := maxSize; numEvals <= maxSize; numEvals *= 2 { - for i, fullAssignment := range fullAssignments { - inputAssignments[i] = fullAssignment[:numEvals] - } - - t.Log("Selected inputs for test") - test(t, inputAssignments...) - } -} - -func testNoGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := Circuit{ - { - Inputs: []*Wire{}, - Gate: nil, - }, - } - - assignment := WireAssignment{&c[0]: inputAssignments[0]} - - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NoError(t, err) - - // Even though a hash is called here, the proof is empty - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NoError(t, err, "proof rejected") -} - -func testSingleAddGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := make(Circuit, 3) - c[2] = Wire{ - Gate: Gates["add"], - Inputs: []*Wire{&c[0], &c[1]}, - } - - assignment := WireAssignment{&c[0]: inputAssignments[0], &c[1]: inputAssignments[1]}.Complete(c) - - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NoError(t, err) - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NoError(t, err, "proof rejected") - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NotNil(t, err, "bad proof accepted") -} - -func testSingleMulGate(t *testing.T, inputAssignments ...[]fr.Element) { - - c := make(Circuit, 3) - c[2] = Wire{ - Gate: Gates["mul"], - Inputs: []*Wire{&c[0], &c[1]}, - } - - assignment := WireAssignment{&c[0]: inputAssignments[0], &c[1]: inputAssignments[1]}.Complete(c) - - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NoError(t, err) - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NoError(t, err, "proof rejected") - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NotNil(t, err, "bad proof accepted") -} - -func testSingleInputTwoIdentityGates(t *testing.T, inputAssignments ...[]fr.Element) { - c := make(Circuit, 3) - - c[1] = Wire{ - Gate: IdentityGate{}, - Inputs: []*Wire{&c[0]}, - } - - c[2] = Wire{ - Gate: IdentityGate{}, - Inputs: []*Wire{&c[0]}, - } - - assignment := WireAssignment{&c[0]: inputAssignments[0]}.Complete(c) - - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err) - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err, "proof rejected") - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NotNil(t, err, "bad proof accepted") -} - -func testSingleMimcCipherGate(t *testing.T, inputAssignments ...[]fr.Element) { - c := make(Circuit, 3) - - c[2] = Wire{ - Gate: mimcCipherGate{}, - Inputs: []*Wire{&c[0], &c[1]}, - } - - t.Log("Evaluating all circuit wires") - assignment := WireAssignment{&c[0]: inputAssignments[0], &c[1]: inputAssignments[1]}.Complete(c) - t.Log("Circuit evaluation complete") - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err) - t.Log("Proof complete") - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err, "proof rejected") - - t.Log("Successful verification complete") - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NotNil(t, err, "bad proof accepted") - t.Log("Unsuccessful verification complete") -} - -func testSingleInputTwoIdentityGatesComposed(t *testing.T, inputAssignments ...[]fr.Element) { - c := make(Circuit, 3) - - c[1] = Wire{ - Gate: IdentityGate{}, - Inputs: []*Wire{&c[0]}, - } - c[2] = Wire{ - Gate: IdentityGate{}, - Inputs: []*Wire{&c[1]}, - } - - assignment := WireAssignment{&c[0]: inputAssignments[0]}.Complete(c) - - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err) - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err, "proof rejected") - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NotNil(t, err, "bad proof accepted") -} - -func mimcCircuit(numRounds int) Circuit { - c := make(Circuit, numRounds+2) - - for i := 2; i < len(c); i++ { - c[i] = Wire{ - Gate: mimcCipherGate{}, - Inputs: []*Wire{&c[i-1], &c[0]}, - } - } - return c -} - -func testMimc(t *testing.T, numRounds int, inputAssignments ...[]fr.Element) { - //TODO: Implement mimc correctly. Currently, the computation is mimc(a,b) = cipher( cipher( ... cipher(a, b), b) ..., b) - // @AlexandreBelling: Please explain the extra layers in https://github.com/ConsenSys/gkr-mimc/blob/81eada039ab4ed403b7726b535adb63026e8011f/examples/mimc.go#L10 - - c := mimcCircuit(numRounds) - - t.Log("Evaluating all circuit wires") - assignment := WireAssignment{&c[0]: inputAssignments[0], &c[1]: inputAssignments[1]}.Complete(c) - t.Log("Circuit evaluation complete") - - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err) - - t.Log("Proof finished") - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err, "proof rejected") - - t.Log("Successful verification finished") - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NotNil(t, err, "bad proof accepted") - t.Log("Unsuccessful verification finished") -} - -func testATimesBSquared(t *testing.T, numRounds int, inputAssignments ...[]fr.Element) { - // This imitates the MiMC circuit - - c := make(Circuit, numRounds+2) - - for i := 2; i < len(c); i++ { - c[i] = Wire{ - Gate: Gates["mul"], - Inputs: []*Wire{&c[i-1], &c[0]}, - } - } - - assignment := WireAssignment{&c[0]: inputAssignments[0], &c[1]: inputAssignments[1]}.Complete(c) - - proof, err := Prove(c, assignment, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err) - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(0, 1))) - assert.NoError(t, err, "proof rejected") - - err = Verify(c, assignment, proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(1, 1))) - assert.NotNil(t, err, "bad proof accepted") -} - -func setRandom(slice []fr.Element) { - for i := range slice { - slice[i].SetRandom() - } -} - -func generateTestProver(path string) func(t *testing.T) { - return func(t *testing.T) { - testCase, err := newTestCase(path) - assert.NoError(t, err) - proof, err := Prove(testCase.Circuit, testCase.FullAssignment, fiatshamir.WithHash(testCase.Hash)) - assert.NoError(t, err) - assert.NoError(t, proofEquals(testCase.Proof, proof)) - } -} - -func generateTestVerifier(path string) func(t *testing.T) { - return func(t *testing.T) { - testCase, err := newTestCase(path) - assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(testCase.Hash)) - assert.NoError(t, err, "proof rejected") - testCase, err = newTestCase(path) - assert.NoError(t, err) - err = Verify(testCase.Circuit, testCase.InOutAssignment, testCase.Proof, fiatshamir.WithHash(test_vector_utils.NewMessageCounter(2, 0))) - assert.NotNil(t, err, "bad proof accepted") - } -} - -func TestGkrVectors(t *testing.T) { - - testDirPath := "../../../../internal/generator/gkr/test_vectors" - dirEntries, err := os.ReadDir(testDirPath) - assert.NoError(t, err) - for _, dirEntry := range dirEntries { - if !dirEntry.IsDir() { - - if filepath.Ext(dirEntry.Name()) == ".json" { - path := filepath.Join(testDirPath, dirEntry.Name()) - noExt := dirEntry.Name()[:len(dirEntry.Name())-len(".json")] - - t.Run(noExt+"_prover", generateTestProver(path)) - t.Run(noExt+"_verifier", generateTestVerifier(path)) - - } - } - } -} - -func proofEquals(expected Proof, seen Proof) error { - if len(expected) != len(seen) { - return fmt.Errorf("length mismatch %d ≠ %d", len(expected), len(seen)) - } - for i, x := range expected { - xSeen := seen[i] - - if xSeen.FinalEvalProof == nil { - if seenFinalEval := x.FinalEvalProof.([]fr.Element); len(seenFinalEval) != 0 { - return fmt.Errorf("length mismatch %d ≠ %d", 0, len(seenFinalEval)) - } - } else { - if err := test_vector_utils.SliceEquals(x.FinalEvalProof.([]fr.Element), xSeen.FinalEvalProof.([]fr.Element)); err != nil { - return fmt.Errorf("final evaluation proof mismatch") - } - } - if err := test_vector_utils.PolynomialSliceEquals(x.PartialSumPolys, xSeen.PartialSumPolys); err != nil { - return err - } - } - return nil -} - -func benchmarkGkrMiMC(b *testing.B, nbInstances, mimcDepth int) { - fmt.Println("creating circuit structure") - c := mimcCircuit(mimcDepth) - - in0 := make([]fr.Element, nbInstances) - in1 := make([]fr.Element, nbInstances) - setRandom(in0) - setRandom(in1) - - fmt.Println("evaluating circuit") - start := time.Now().UnixMicro() - assignment := WireAssignment{&c[0]: in0, &c[1]: in1}.Complete(c) - solved := time.Now().UnixMicro() - start - fmt.Println("solved in", solved, "μs") - - //b.ResetTimer() - fmt.Println("constructing proof") - start = time.Now().UnixMicro() - _, err := Prove(c, assignment, fiatshamir.WithHash(mimc.NewMiMC())) - proved := time.Now().UnixMicro() - start - fmt.Println("proved in", proved, "μs") - assert.NoError(b, err) -} - -func BenchmarkGkrMimc19(b *testing.B) { - benchmarkGkrMiMC(b, 1<<19, 91) -} - -func BenchmarkGkrMimc17(b *testing.B) { - benchmarkGkrMiMC(b, 1<<17, 91) -} - -func TestTopSortTrivial(t *testing.T) { - c := make(Circuit, 2) - c[0].Inputs = []*Wire{&c[1]} - sorted := topologicalSort(c) - assert.Equal(t, []*Wire{&c[1], &c[0]}, sorted) -} - -func TestTopSortDeep(t *testing.T) { - c := make(Circuit, 4) - c[0].Inputs = []*Wire{&c[2]} - c[1].Inputs = []*Wire{&c[3]} - c[2].Inputs = []*Wire{} - c[3].Inputs = []*Wire{&c[0]} - sorted := topologicalSort(c) - assert.Equal(t, []*Wire{&c[2], &c[0], &c[3], &c[1]}, sorted) -} - -func TestTopSortWide(t *testing.T) { - c := make(Circuit, 10) - c[0].Inputs = []*Wire{&c[3], &c[8]} - c[1].Inputs = []*Wire{&c[6]} - c[2].Inputs = []*Wire{&c[4]} - c[3].Inputs = []*Wire{} - c[4].Inputs = []*Wire{} - c[5].Inputs = []*Wire{&c[9]} - c[6].Inputs = []*Wire{&c[9]} - c[7].Inputs = []*Wire{&c[9], &c[5], &c[2]} - c[8].Inputs = []*Wire{&c[4], &c[3]} - c[9].Inputs = []*Wire{} - - sorted := topologicalSort(c) - sortedExpected := []*Wire{&c[3], &c[4], &c[2], &c[8], &c[0], &c[9], &c[5], &c[6], &c[1], &c[7]} - - assert.Equal(t, sortedExpected, sorted) -} - -type WireInfo struct { - Gate string `json:"gate"` - Inputs []int `json:"inputs"` -} - -type CircuitInfo []WireInfo - -var circuitCache = make(map[string]Circuit) - -func getCircuit(path string) (Circuit, error) { - path, err := filepath.Abs(path) - if err != nil { - return nil, err - } - if circuit, ok := circuitCache[path]; ok { - return circuit, nil - } - var bytes []byte - if bytes, err = os.ReadFile(path); err == nil { - var circuitInfo CircuitInfo - if err = json.Unmarshal(bytes, &circuitInfo); err == nil { - circuit := circuitInfo.toCircuit() - circuitCache[path] = circuit - return circuit, nil - } else { - return nil, err - } - } else { - return nil, err - } -} - -func (c CircuitInfo) toCircuit() (circuit Circuit) { - circuit = make(Circuit, len(c)) - for i := range c { - circuit[i].Gate = Gates[c[i].Gate] - circuit[i].Inputs = make([]*Wire, len(c[i].Inputs)) - for k, inputCoord := range c[i].Inputs { - input := &circuit[inputCoord] - circuit[i].Inputs[k] = input - } - } - return -} - -func init() { - Gates["mimc"] = mimcCipherGate{} //TODO: Add ark - Gates["select-input-3"] = _select(2) -} - -type mimcCipherGate struct { - ark fr.Element -} - -func (m mimcCipherGate) Evaluate(input ...fr.Element) (res fr.Element) { - var sum fr.Element - - sum. - Add(&input[0], &input[1]). - Add(&sum, &m.ark) - - res.Square(&sum) // sum^2 - res.Mul(&res, &sum) // sum^3 - res.Square(&res) //sum^6 - res.Mul(&res, &sum) //sum^7 - - return -} - -func (m mimcCipherGate) Degree() int { - return 7 -} - -type PrintableProof []PrintableSumcheckProof - -type PrintableSumcheckProof struct { - FinalEvalProof interface{} `json:"finalEvalProof"` - PartialSumPolys [][]interface{} `json:"partialSumPolys"` -} - -func unmarshalProof(printable PrintableProof) (Proof, error) { - proof := make(Proof, len(printable)) - for i := range printable { - finalEvalProof := []fr.Element(nil) - - if printable[i].FinalEvalProof != nil { - finalEvalSlice := reflect.ValueOf(printable[i].FinalEvalProof) - finalEvalProof = make([]fr.Element, finalEvalSlice.Len()) - for k := range finalEvalProof { - if _, err := test_vector_utils.SetElement(&finalEvalProof[k], finalEvalSlice.Index(k).Interface()); err != nil { - return nil, err - } - } - } - - proof[i] = sumcheck.Proof{ - PartialSumPolys: make([]polynomial.Polynomial, len(printable[i].PartialSumPolys)), - FinalEvalProof: finalEvalProof, - } - for k := range printable[i].PartialSumPolys { - var err error - if proof[i].PartialSumPolys[k], err = test_vector_utils.SliceToElementSlice(printable[i].PartialSumPolys[k]); err != nil { - return nil, err - } - } - } - return proof, nil -} - -type TestCase struct { - Circuit Circuit - Hash hash.Hash - Proof Proof - FullAssignment WireAssignment - InOutAssignment WireAssignment -} - -type TestCaseInfo struct { - Hash test_vector_utils.HashDescription `json:"hash"` - Circuit string `json:"circuit"` - Input [][]interface{} `json:"input"` - Output [][]interface{} `json:"output"` - Proof PrintableProof `json:"proof"` -} - -var testCases = make(map[string]*TestCase) - -func newTestCase(path string) (*TestCase, error) { - path, err := filepath.Abs(path) - if err != nil { - return nil, err - } - dir := filepath.Dir(path) - - tCase, ok := testCases[path] - if !ok { - var bytes []byte - if bytes, err = os.ReadFile(path); err == nil { - var info TestCaseInfo - err = json.Unmarshal(bytes, &info) - if err != nil { - return nil, err - } - - var circuit Circuit - if circuit, err = getCircuit(filepath.Join(dir, info.Circuit)); err != nil { - return nil, err - } - var _hash hash.Hash - if _hash, err = test_vector_utils.HashFromDescription(info.Hash); err != nil { - return nil, err - } - var proof Proof - if proof, err = unmarshalProof(info.Proof); err != nil { - return nil, err - } - - fullAssignment := make(WireAssignment) - inOutAssignment := make(WireAssignment) - - sorted := topologicalSort(circuit) - - inI, outI := 0, 0 - for _, w := range sorted { - var assignmentRaw []interface{} - if w.IsInput() { - if inI == len(info.Input) { - return nil, fmt.Errorf("fewer input in vector than in circuit") - } - assignmentRaw = info.Input[inI] - inI++ - } else if w.IsOutput() { - if outI == len(info.Output) { - return nil, fmt.Errorf("fewer output in vector than in circuit") - } - assignmentRaw = info.Output[outI] - outI++ - } - if assignmentRaw != nil { - var wireAssignment []fr.Element - if wireAssignment, err = test_vector_utils.SliceToElementSlice(assignmentRaw); err != nil { - return nil, err - } - - fullAssignment[w] = wireAssignment - inOutAssignment[w] = wireAssignment - } - } - - fullAssignment.Complete(circuit) - - for _, w := range sorted { - if w.IsOutput() { - - if err = test_vector_utils.SliceEquals(inOutAssignment[w], fullAssignment[w]); err != nil { - return nil, fmt.Errorf("assignment mismatch: %v", err) - } - - } - } - - tCase = &TestCase{ - FullAssignment: fullAssignment, - InOutAssignment: inOutAssignment, - Proof: proof, - Hash: _hash, - Circuit: circuit, - } - - testCases[path] = tCase - } else { - return nil, err - } - } - - return tCase, nil -} - -type _select int - -func (g _select) Evaluate(in ...fr.Element) fr.Element { - return in[g] -} - -func (g _select) Degree() int { - return 1 -} diff --git a/ecc/bw6-756/fr/hash_to_field/doc.go b/ecc/bw6-756/fr/hash_to_field/doc.go deleted file mode 100644 index 6f886cc857..0000000000 --- a/ecc/bw6-756/fr/hash_to_field/doc.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package htf provides hasher based on RFC 9380 Section 5. -// -// The [RFC 9380] defines a method for hashing bytes to elliptic curves. Section -// 5 of the RFC describes a method for uniformly hashing bytes into a field -// using a domain separation. The hashing is implemented in [fp], but this -// package provides a wrapper for the method which implements [hash.Hash] for -// using the method recursively. -// -// [RFC 9380]: https://datatracker.ietf.org/doc/html/rfc9380 -package hash_to_field - -import ( - _ "hash" - - _ "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) diff --git a/ecc/bw6-756/fr/hash_to_field/hash_to_field.go b/ecc/bw6-756/fr/hash_to_field/hash_to_field.go deleted file mode 100644 index b2ac7d066f..0000000000 --- a/ecc/bw6-756/fr/hash_to_field/hash_to_field.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package hash_to_field - -import ( - "fmt" - "hash" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) - -type wrappedHashToField struct { - domain []byte - toHash []byte -} - -// New returns a new hasher instance which uses [fr.Hash] to hash all the -// written bytes to a field element, returning the byte representation of the -// field element. The domain separator is passed as-is to hashing method. -func New(domainSeparator []byte) hash.Hash { - return &wrappedHashToField{ - domain: append([]byte{}, domainSeparator...), // copy in case the argument is modified - } -} - -func (w *wrappedHashToField) Write(p []byte) (n int, err error) { - w.toHash = append(w.toHash, p...) - return len(p), nil -} - -func (w *wrappedHashToField) Sum(b []byte) []byte { - res, err := fr.Hash(w.toHash, w.domain, 1) - if err != nil { - // we want to follow the interface, cannot return error and have to panic - // but by default the method shouldn't return an error internally - panic(fmt.Sprintf("native field to hash: %v", err)) - } - bts := res[0].Bytes() - return append(b, bts[:]...) -} - -func (w *wrappedHashToField) Reset() { - w.toHash = nil -} - -func (w *wrappedHashToField) Size() int { - return fr.Bytes -} - -func (w *wrappedHashToField) BlockSize() int { - return fr.Bytes -} diff --git a/ecc/bw6-756/fr/hash_to_field/hash_to_field_test.go b/ecc/bw6-756/fr/hash_to_field/hash_to_field_test.go deleted file mode 100644 index bcb28dac28..0000000000 --- a/ecc/bw6-756/fr/hash_to_field/hash_to_field_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package hash_to_field - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) - -func TestHashInterface(t *testing.T) { - msg := []byte("test") - sep := []byte("separator") - res, err := fr.Hash(msg, sep, 1) - if err != nil { - t.Fatal("hash to field", err) - } - - htfFn := New(sep) - htfFn.Write(msg) - bts := htfFn.Sum(nil) - var res2 fr.Element - res2.SetBytes(bts[:fr.Bytes]) - if !res[0].Equal(&res2) { - t.Error("not equal") - } -} diff --git a/ecc/bw6-756/fr/iop/doc.go b/ecc/bw6-756/fr/iop/doc.go deleted file mode 100644 index 20f8b1a302..0000000000 --- a/ecc/bw6-756/fr/iop/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package iop provides an API to computations common -// to iop backends (permutation, quotient). -package iop diff --git a/ecc/bw6-756/fr/iop/expressions.go b/ecc/bw6-756/fr/iop/expressions.go deleted file mode 100644 index 1b2c108f18..0000000000 --- a/ecc/bw6-756/fr/iop/expressions.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "errors" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/internal/parallel" - "math/bits" -) - -// Expression represents a multivariate polynomial. -type Expression func(i int, x ...fr.Element) fr.Element - -// Evaluate evaluates f on each entry of x. The returned value is -// the vector of evaluations of e on x. -// The form of the result is form. -// if r is provided (not nil), it is used as the result vector, -// that is the memory space for the coefficients of the resulting polynomial. -// The Size field of the result is the same as the one of x[0]. -// The blindedSize field of the result is the same as Size. -// The Shift field of the result is 0. -func Evaluate(f Expression, r []fr.Element, form Form, x ...*Polynomial) (*Polynomial, error) { - if len(x) == 0 { - return nil, errors.New("need at lest one input") - } - - // check that the sizes are consistent - n := x[0].coefficients.Len() - m := len(x) - for i := 1; i < m; i++ { - if n != x[i].coefficients.Len() { - return nil, ErrInconsistentSize - } - } - - // check result len - if r == nil { - r = make([]fr.Element, n) - } else if len(r) != n { - return nil, ErrInconsistentSize - } - - // result coefficients - idx := func(i int) int { - return i - } - if form.Layout != Regular { - nn := uint64(64 - bits.TrailingZeros(uint(n))) - idx = func(i int) int { - return int(bits.Reverse64(uint64(i)) >> nn) - } - } - - parallel.Execute(n, func(start, end int) { - vx := make([]fr.Element, m) - for i := start; i < end; i++ { - for j := 0; j < m; j++ { - vx[j] = x[j].GetCoeff(i) - } - r[idx(i)] = f(i, vx...) - } - }) - - res := NewPolynomial(&r, form) - res.size = x[0].size - res.blindedSize = x[0].size - res.shift = 0 - - return res, nil -} diff --git a/ecc/bw6-756/fr/iop/expressions_test.go b/ecc/bw6-756/fr/iop/expressions_test.go deleted file mode 100644 index 534b202bf3..0000000000 --- a/ecc/bw6-756/fr/iop/expressions_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) - -func TestEvaluate(t *testing.T) { - - f := func(_ int, x ...fr.Element) fr.Element { - var a fr.Element - a.Add(&x[0], &x[1]).Add(&a, &x[2]) - return a - } - - size := 64 - u := make([]fr.Element, size) - v := make([]fr.Element, size) - w := make([]fr.Element, size) - for i := 0; i < size; i++ { - u[i].SetUint64(uint64(i)) - v[i].SetUint64(uint64(i + 1)) - w[i].SetUint64(uint64(i + 2)) - } - r := make([]fr.Element, size) - for i := 0; i < size; i++ { - r[i].SetUint64(uint64(3 * (i + 1))) - } - form := Form{Layout: Regular, Basis: Canonical} - wu := NewPolynomial(&u, form) - wv := NewPolynomial(&v, form) - ww := NewPolynomial(&w, form) - - rr, err := Evaluate(f, nil, form, wu, wv, ww) - if err != nil { - t.Fatal(err) - } - - wu.ToBitReverse() - rrb, err := Evaluate(f, nil, form, wu, wv, ww) - if err != nil { - t.Fatal(err) - } - - wv.ToBitReverse() - ww.ToBitReverse() - rrc, err := Evaluate(f, nil, form, wu, wv, ww) - if err != nil { - t.Fatal(err) - } - - // compare with the expected result - for i := 0; i < size; i++ { - if !rr.Coefficients()[i].Equal(&r[i]) { - t.Fatal("error evaluation") - } - if !rrb.Coefficients()[i].Equal(&r[i]) { - t.Fatal("error evaluation") - } - if !rrc.Coefficients()[i].Equal(&r[i]) { - t.Fatal("error evaluation") - } - - } -} diff --git a/ecc/bw6-756/fr/iop/polynomial.go b/ecc/bw6-756/fr/iop/polynomial.go deleted file mode 100644 index 0559993120..0000000000 --- a/ecc/bw6-756/fr/iop/polynomial.go +++ /dev/null @@ -1,462 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "encoding/binary" - "io" - "math/big" - "math/bits" - "runtime" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/fft" -) - -// Basis indicates the basis in which a polynomial is represented. -type Basis uint32 - -const ( - Canonical Basis = 1 << iota - Lagrange - LagrangeCoset -) - -// Layout indicates if a polynomial has a BitReverse or a Regular layout -type Layout uint32 - -const ( - Regular Layout = 8 << iota - BitReverse -) - -// Form describes the form of a polynomial. -// TODO should be a regular enum? -type Form struct { - Basis Basis - Layout Layout -} - -// enum of the possible Form values for type-safe switches -// in this package -var ( - canonicalRegular = Form{Canonical, Regular} - canonicalBitReverse = Form{Canonical, BitReverse} - lagrangeRegular = Form{Lagrange, Regular} - lagrangeBitReverse = Form{Lagrange, BitReverse} - lagrangeCosetRegular = Form{LagrangeCoset, Regular} - lagrangeCosetBitReverse = Form{LagrangeCoset, BitReverse} -) - -// Polynomial wraps a polynomial so that it is -// interpreted as P'(X)=P(\omega^{s}X). -// Size is the real size of the polynomial (seen as a vector). -// For instance if len(P)=32 but P.Size=8, it means that P has been -// extended (e.g. it is evaluated on a larger set) but P is a polynomial -// of degree 7. -// blindedSize is the size of the polynomial when it is blinded. By -// default blindedSize=Size, until the polynomial is blinded. -type Polynomial struct { - *polynomial - shift int - size int - blindedSize int -} - -// NewPolynomial returned a Polynomial from the provided coefficients in the given form. -// A Polynomial can be seen as a "shared pointer" on a list of coefficients. -// It is the responsibility of the user to call the Clone method if the coefficients -// shouldn't be mutated. -func NewPolynomial(coeffs *[]fr.Element, form Form) *Polynomial { - return &Polynomial{ - polynomial: newPolynomial(coeffs, form), - size: len(*coeffs), - blindedSize: len(*coeffs), - } -} - -// Shift the wrapped polynomial; it doesn't modify the underlying data structure, -// but flag the Polynomial such that it will be interpreted as p(\omega^shift X) -func (p *Polynomial) Shift(shift int) *Polynomial { - p.shift = shift - return p -} - -// BlindedSize returns the the size of the polynomial when it is blinded. By -// default blindedSize=Size, until the polynomial is blinded. -func (p *Polynomial) BlindedSize() int { - return p.blindedSize -} - -// Size returns the real size of the polynomial (seen as a vector). -// For instance if len(P)=32 but P.Size=8, it means that P has been -// extended (e.g. it is evaluated on a larger set) but P is a polynomial -// of degree 7. -func (p *Polynomial) Size() int { - return p.size -} - -// SetSize sets the size of the polynomial. -// size is the real size of the polynomial (seen as a vector). -// For instance if len(P)=32 but P.size=8, it means that P has been -// extended (e.g. it is evaluated on a larger set) but P is a polynomial -// of degree 7. -func (p *Polynomial) SetSize(size int) { - p.size = size -} - -// SetBlindedSize sets the blinded size of the polynomial. -func (p *Polynomial) SetBlindedSize(size int) { - p.blindedSize = size -} - -// Blind blinds a polynomial q by adding Q(X)*(X^{n}-1), -// where deg Q = blindingOrder and Q is random, and n is the -// size of q. Sets the result to p and returns it. -// -// blindingOrder is the degree of Q, where the blinding is Q(X)*(X^{n}-1) -// where n is the size of p. The size of p is modified since the underlying -// polynomial is of bigger degree now. The new size is p.Size+1+blindingOrder. -// -// /!\ The code panics if wq is not in canonical, regular layout -func (p *Polynomial) Blind(blindingOrder int) *Polynomial { - // check that p is in canonical basis - if p.Form != canonicalRegular { - panic("the input must be in canonical basis, regular layout") - } - - // we add Q*(x^{n}-1) so the new size is deg(Q)+n+1 - // where n is the size of wq. - newSize := p.size + blindingOrder + 1 - - // Resize p. The size of wq might has already been increased - // (e.g. when the polynomial is evaluated on a larger domain), - // if that's the case we don't resize the polynomial. - p.grow(newSize) - - // blinding: we add Q(X)(X^{n}-1) to P, where deg(Q)=blindingOrder - var r fr.Element - - for i := 0; i <= blindingOrder; i++ { - r.SetRandom() - (*p.coefficients)[i].Sub(&(*p.coefficients)[i], &r) - (*p.coefficients)[i+p.size].Add(&(*p.coefficients)[i+p.size], &r) - } - p.blindedSize = newSize - - return p -} - -// Evaluate evaluates p at x. -// The code panics if the function is not in canonical form. -func (p *Polynomial) Evaluate(x fr.Element) fr.Element { - - if p.shift == 0 { - return p.polynomial.evaluate(x) - } - - var g fr.Element - if p.shift <= 5 { - gen, err := fft.Generator(uint64(p.size)) - if err != nil { - panic(err) - } - g = smallExp(gen, p.shift) - x.Mul(&x, &g) - return p.polynomial.evaluate(x) - } - - bs := big.NewInt(int64(p.shift)) - g = *g.Exp(g, bs) - x.Mul(&x, &g) - return p.polynomial.evaluate(x) -} - -// Clone returns a deep copy of p. The underlying polynomial is cloned; -// see also ShallowClone to perform a ShallowClone on the underlying polynomial. -// If capacity is provided, the new coefficient slice capacity will be set accordingly. -func (p *Polynomial) Clone(capacity ...int) *Polynomial { - res := p.ShallowClone() - res.polynomial = p.polynomial.clone(capacity...) - return res -} - -// ShallowClone returns a shallow copy of p. The underlying polynomial coefficient -// is NOT cloned and both objects will point to the same coefficient vector. -func (p *Polynomial) ShallowClone() *Polynomial { - res := *p - return &res -} - -// GetCoeff returns the i-th entry of p, taking the layout in account. -func (p *Polynomial) GetCoeff(i int) fr.Element { - - n := p.coefficients.Len() - rho := n / p.size - if p.polynomial.Form.Layout == Regular { - return (*p.coefficients)[(i+rho*p.shift)%n] - } else { - nn := uint64(64 - bits.TrailingZeros(uint(n))) - iRev := bits.Reverse64(uint64((i+rho*p.shift)%n)) >> nn - return (*p.coefficients)[iRev] - } - -} - -// polynomial represents a polynomial, the vector of coefficients -// along with the basis and the layout. -type polynomial struct { - coefficients *fr.Vector - Form -} - -// Coefficients returns a slice on the underlying data structure. -func (p *polynomial) Coefficients() []fr.Element { - return (*p.coefficients) -} - -// newPolynomial creates a new polynomial. The slice coeff NOT copied -// but directly assigned to the new polynomial. -func newPolynomial(coeffs *[]fr.Element, form Form) *polynomial { - return &polynomial{coefficients: (*fr.Vector)(coeffs), Form: form} -} - -// clone returns a deep copy of the underlying data structure. -func (p *polynomial) clone(capacity ...int) *polynomial { - c := p.coefficients.Len() - if len(capacity) == 1 && capacity[0] > c { - c = capacity[0] - } - newCoeffs := make(fr.Vector, p.coefficients.Len(), c) - r := &polynomial{ - coefficients: &newCoeffs, - Form: p.Form, - } - copy((*r.coefficients), (*p.coefficients)) - return r -} - -// evaluate evaluates p at x. -// The code panics if the function is not in canonical form. -func (p *polynomial) evaluate(x fr.Element) fr.Element { - - var r fr.Element - if p.Basis != Canonical { - panic("p must be in canonical basis") - } - - if p.Layout == Regular { - for i := p.coefficients.Len() - 1; i >= 0; i-- { - r.Mul(&r, &x).Add(&r, &(*p.coefficients)[i]) - } - } else { - nn := uint64(64 - bits.TrailingZeros(uint(p.coefficients.Len()))) - for i := p.coefficients.Len() - 1; i >= 0; i-- { - iRev := bits.Reverse64(uint64(i)) >> nn - r.Mul(&r, &x).Add(&r, &(*p.coefficients)[iRev]) - } - } - - return r - -} - -// ToRegular changes the layout of p to Regular. -// Leaves p unchanged if p's layout was already Regular. -func (p *Polynomial) ToRegular() *Polynomial { - if p.Layout == Regular { - return p - } - fft.BitReverse((*p.coefficients)) - p.Layout = Regular - return p -} - -// ToBitReverse changes the layout of p to BitReverse. -// Leaves p unchanged if p's layout was already BitReverse. -func (p *Polynomial) ToBitReverse() *Polynomial { - if p.Layout == BitReverse { - return p - } - fft.BitReverse((*p.coefficients)) - p.Layout = BitReverse - return p -} - -// ToLagrange converts p to Lagrange form. -// Leaves p unchanged if p was already in Lagrange form. -func (p *Polynomial) ToLagrange(d *fft.Domain, nbTasks ...int) *Polynomial { - id := p.Form - p.grow(int(d.Cardinality)) - - n := runtime.NumCPU() - if len(nbTasks) > 0 { - n = nbTasks[0] - } - - switch id { - case canonicalRegular: - p.Layout = BitReverse - d.FFT((*p.coefficients), fft.DIF, fft.WithNbTasks(n)) - case canonicalBitReverse: - p.Layout = Regular - d.FFT((*p.coefficients), fft.DIT, fft.WithNbTasks(n)) - case lagrangeRegular, lagrangeBitReverse: - return p - case lagrangeCosetRegular: - p.Layout = Regular - d.FFTInverse((*p.coefficients), fft.DIF, fft.OnCoset(), fft.WithNbTasks(n)) - d.FFT((*p.coefficients), fft.DIT) - case lagrangeCosetBitReverse: - p.Layout = BitReverse - d.FFTInverse((*p.coefficients), fft.DIT, fft.OnCoset(), fft.WithNbTasks(n)) - d.FFT((*p.coefficients), fft.DIF) - default: - panic("unknown ID") - } - p.Basis = Lagrange - return p -} - -// ToCanonical converts p to canonical form. -// Leaves p unchanged if p was already in Canonical form. -func (p *Polynomial) ToCanonical(d *fft.Domain, nbTasks ...int) *Polynomial { - id := p.Form - p.grow(int(d.Cardinality)) - n := runtime.NumCPU() - if len(nbTasks) > 0 { - n = nbTasks[0] - } - switch id { - case canonicalRegular, canonicalBitReverse: - return p - case lagrangeRegular: - p.Layout = BitReverse - d.FFTInverse((*p.coefficients), fft.DIF, fft.WithNbTasks(n)) - case lagrangeBitReverse: - p.Layout = Regular - d.FFTInverse((*p.coefficients), fft.DIT, fft.WithNbTasks(n)) - case lagrangeCosetRegular: - p.Layout = BitReverse - d.FFTInverse((*p.coefficients), fft.DIF, fft.OnCoset(), fft.WithNbTasks(n)) - case lagrangeCosetBitReverse: - p.Layout = Regular - d.FFTInverse((*p.coefficients), fft.DIT, fft.OnCoset(), fft.WithNbTasks(n)) - default: - panic("unknown ID") - } - p.Basis = Canonical - return p -} - -func (p *polynomial) grow(newSize int) { - offset := newSize - p.coefficients.Len() - if offset > 0 { - (*p.coefficients) = append((*p.coefficients), make(fr.Vector, offset)...) - } -} - -// ToLagrangeCoset Sets p to q, in LagrangeCoset form and returns it. -func (p *Polynomial) ToLagrangeCoset(d *fft.Domain) *Polynomial { - id := p.Form - p.grow(int(d.Cardinality)) - switch id { - case canonicalRegular: - p.Layout = BitReverse - d.FFT((*p.coefficients), fft.DIF, fft.OnCoset()) - case canonicalBitReverse: - p.Layout = Regular - d.FFT((*p.coefficients), fft.DIT, fft.OnCoset()) - case lagrangeRegular: - p.Layout = Regular - d.FFTInverse((*p.coefficients), fft.DIF) - d.FFT((*p.coefficients), fft.DIT, fft.OnCoset()) - case lagrangeBitReverse: - p.Layout = BitReverse - d.FFTInverse((*p.coefficients), fft.DIT) - d.FFT((*p.coefficients), fft.DIF, fft.OnCoset()) - case lagrangeCosetRegular, lagrangeCosetBitReverse: - return p - default: - panic("unknown ID") - } - - p.Basis = LagrangeCoset - return p -} - -// WriteTo implements io.WriterTo -func (p *Polynomial) WriteTo(w io.Writer) (int64, error) { - // encode coefficients - n, err := p.polynomial.coefficients.WriteTo(w) - if err != nil { - return n, err - } - - // encode Form.Basis, Form.Layout, shift, size & blindedSize as uint32 - var data = []uint32{ - uint32(p.Basis), - uint32(p.Layout), - uint32(p.shift), - uint32(p.size), - uint32(p.blindedSize), - } - for _, v := range data { - err = binary.Write(w, binary.BigEndian, v) - if err != nil { - return n, err - } - n += 4 - } - return n, nil -} - -// ReadFrom implements io.ReaderFrom -func (p *Polynomial) ReadFrom(r io.Reader) (int64, error) { - // decode coefficients - if p.polynomial == nil { - p.polynomial = new(polynomial) - } - if p.polynomial.coefficients == nil { - v := make(fr.Vector, 0) - p.polynomial.coefficients = &v - } - n, err := p.polynomial.coefficients.ReadFrom(r) - if err != nil { - return n, err - } - - // decode Form.Basis, Form.Layout, shift, size & blindedSize as uint32 - var data [5]uint32 - var buf [4]byte - for i := range data { - read, err := io.ReadFull(r, buf[:4]) - n += int64(read) - if err != nil { - return n, err - } - data[i] = binary.BigEndian.Uint32(buf[:4]) - } - - p.Basis = Basis(data[0]) - p.Layout = Layout(data[1]) - p.shift = int(data[2]) - p.size = int(data[3]) - p.blindedSize = int(data[4]) - - return n, nil -} diff --git a/ecc/bw6-756/fr/iop/polynomial_test.go b/ecc/bw6-756/fr/iop/polynomial_test.go deleted file mode 100644 index d7b42777bf..0000000000 --- a/ecc/bw6-756/fr/iop/polynomial_test.go +++ /dev/null @@ -1,729 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/fft" - - "github.com/stretchr/testify/require" - - "bytes" - "reflect" -) - -func TestEvaluation(t *testing.T) { - - size := 8 - shift := 2 - d := fft.NewDomain(uint64(size)) - c := randomVector(size) - wp := NewPolynomial(c, Form{Basis: Canonical, Layout: Regular}) - wps := wp.ShallowClone().Shift(shift) - ref := wp.Clone() - ref.ToLagrange(d).ToRegular() - - // regular layout - a := wp.Evaluate(d.Generator) - b := wps.Evaluate(d.Generator) - if !a.Equal(&ref.Coefficients()[1]) { - t.Fatal("error evaluation") - } - if !b.Equal(&ref.Coefficients()[1+shift]) { - t.Fatal("error evaluation shifted") - } - - // bit reversed layout - wp.ToBitReverse() - wps.ToBitReverse() - a = wp.Evaluate(d.Generator) - b = wps.Evaluate(d.Generator) - if !a.Equal(&ref.Coefficients()[1]) { - t.Fatal("error evaluation") - } - if !b.Equal(&ref.Coefficients()[1+shift]) { - t.Fatal("error evaluation shifted") - } - -} - -func randomVector(size int) *[]fr.Element { - - r := make([]fr.Element, size) - for i := 0; i < size; i++ { - r[i].SetRandom() - } - return &r -} - -func TestGetCoeff(t *testing.T) { - - size := 8 - v := make([]fr.Element, size) - for i := 0; i < size; i++ { - v[i].SetUint64(uint64(i)) - } - wp := NewPolynomial(&v, Form{Layout: Regular, Basis: Canonical}) - wsp := wp.ShallowClone().Shift(1) - - var aa, bb fr.Element - - // regular layout - for i := 0; i < size; i++ { - - a := wp.GetCoeff(i) - b := wsp.GetCoeff(i) - aa.SetUint64(uint64(i)) - bb.SetUint64(uint64((i + 1) % size)) - if !a.Equal(&aa) { - t.Fatal("error GetCoeff") - } - if !b.Equal(&bb) { - t.Fatal("error GetCoeff") - } - } - - // bit reverse + bitReverse and shifted - wp.ToBitReverse() - wsp.ToBitReverse() - for i := 0; i < size; i++ { - - a := wp.GetCoeff(i) - b := wsp.GetCoeff(i) - aa.SetUint64(uint64(i)) - bb.SetUint64(uint64((i + 1) % size)) - if !a.Equal(&aa) { - t.Fatal("error GetCoeff") - } - if !b.Equal(&bb) { - t.Fatal("error GetCoeff") - } - } - -} - -func TestRoundTrip(t *testing.T) { - assert := require.New(t) - var buf bytes.Buffer - - size := 8 - d := fft.NewDomain(uint64(8)) - blindingOrder := 2 - - p := NewPolynomial(randomVector(size), Form{Basis: Lagrange, Layout: Regular}).ToCanonical(d).ToRegular() - p.Blind(blindingOrder) - - // serialize - written, err := p.WriteTo(&buf) - assert.NoError(err) - - // deserialize - var reconstructed Polynomial - read, err := reconstructed.ReadFrom(&buf) - assert.NoError(err) - - assert.Equal(read, written, "number of bytes written != number of bytes read") - - // compare - assert.Equal(p.Basis, reconstructed.Basis) - assert.Equal(p.Layout, reconstructed.Layout) - assert.Equal(p.shift, reconstructed.shift) - assert.Equal(p.size, reconstructed.size) - assert.Equal(p.blindedSize, reconstructed.blindedSize) - c1, c2 := p.Coefficients(), reconstructed.Coefficients() - assert.True(reflect.DeepEqual(c1, c2)) -} - -func TestBlinding(t *testing.T) { - - size := 8 - d := fft.NewDomain(uint64(8)) - blindingOrder := 2 - - // generate a random polynomial in Lagrange form for the moment - // to check that an error is raised when the polynomial is not - // in canonical form. - wp := NewPolynomial(randomVector(size), Form{Basis: Lagrange, Layout: Regular}) - - // checks the blinding is correct: the evaluation of the blinded polynomial - // should be the same as the original on d's domain - wp.Basis = Canonical - wt := wp.Clone() - wt.Blind(blindingOrder) - if wt.coefficients.Len() != blindingOrder+size+1 { - t.Fatal("size of blinded polynomial is incorrect") - } - if wt.blindedSize != size+blindingOrder+1 { - t.Fatal("Size field of blinded polynomial is incorrect") - } - if wt.size != size { - t.Fatal("the size should not have been modified") - } - x := make([]fr.Element, size) - x[0].SetOne() - for i := 1; i < size; i++ { - x[i].Mul(&x[i-1], &d.Generator) - } - var a, b fr.Element - for i := 0; i < size; i++ { - a = wt.Evaluate(x[i]) - b = wp.Evaluate(x[i]) - if a != b { - t.Fatal("polynomial and its blinded version should be equal on V(X^{n}-1)") - } - } - -} - -// list of functions to turn a polynomial in Lagrange-regular form -// to all different forms in ordered using this encoding: -// int(p.Basis)*4 + int(p.Layout)*2 + int(p.Status) -// p is in Lagrange/Regular here. This function is for testing purpose -// only. -type TransfoTest func(p polynomial, d *fft.Domain) polynomial - -// CANONICAL REGULAR -func fromLagrange0(p *Polynomial, d *fft.Domain) *Polynomial { - r := p.Clone() - r.Basis = Canonical - r.Layout = Regular - d.FFTInverse(r.Coefficients(), fft.DIF) - fft.BitReverse(r.Coefficients()) - return r -} - -// CANONICAL BITREVERSE -func fromLagrange1(p *Polynomial, d *fft.Domain) *Polynomial { - r := p.Clone() - r.Basis = Canonical - r.Layout = BitReverse - d.FFTInverse(r.Coefficients(), fft.DIF) - return r -} - -// LAGRANGE REGULAR -func fromLagrange2(p *Polynomial, d *fft.Domain) *Polynomial { - r := p.Clone() - r.Basis = Lagrange - r.Layout = Regular - return r -} - -// LAGRANGE BITREVERSE -func fromLagrange3(p *Polynomial, d *fft.Domain) *Polynomial { - r := p.Clone() - r.Basis = Lagrange - r.Layout = BitReverse - fft.BitReverse(r.Coefficients()) - return r -} - -// LAGRANGE_COSET REGULAR -func fromLagrange4(p *Polynomial, d *fft.Domain) *Polynomial { - r := p.Clone() - r.Basis = LagrangeCoset - r.Layout = Regular - d.FFTInverse(r.Coefficients(), fft.DIF) - d.FFT(r.Coefficients(), fft.DIT, fft.OnCoset()) - return r -} - -// LAGRANGE_COSET BITREVERSE -func fromLagrange5(p *Polynomial, d *fft.Domain) *Polynomial { - r := p.Clone() - r.Basis = LagrangeCoset - r.Layout = BitReverse - d.FFTInverse(r.Coefficients(), fft.DIF) - d.FFT(r.Coefficients(), fft.DIT, fft.OnCoset()) - fft.BitReverse(r.Coefficients()) - return r -} - -func fromLagrange(p *Polynomial, d *fft.Domain) *Polynomial { - id := p.Form - switch id { - case canonicalRegular: - return fromLagrange0(p, d) - case canonicalBitReverse: - return fromLagrange1(p, d) - case lagrangeRegular: - return fromLagrange2(p, d) - case lagrangeBitReverse: - return fromLagrange3(p, d) - case lagrangeCosetRegular: - return fromLagrange4(p, d) - case lagrangeCosetBitReverse: - return fromLagrange5(p, d) - default: - panic("unknown id") - } -} - -func cmpCoefficents(p, q *fr.Vector) bool { - if p.Len() != q.Len() { - return false - } - for i := 0; i < p.Len(); i++ { - if !((*p)[i].Equal(&(*q)[i])) { - return false - } - } - return true -} - -func TestPutInLagrangeForm(t *testing.T) { - - size := 64 - domain := fft.NewDomain(uint64(size)) - - // reference vector in Lagrange-regular form - c := randomVector(size) - p := NewPolynomial(c, Form{Basis: Canonical, Layout: Regular}) - - // CANONICAL REGULAR - { - _p := fromLagrange(p, domain) - q := _p.Clone() - q.ToLagrange(domain) - if q.Basis != Lagrange { - t.Fatal("expected basis is Lagrange") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is BitReverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // CANONICAL BITREVERSE - { - _p := fromLagrange1(p, domain) - q := _p.Clone() - q.ToLagrange(domain) - if q.Basis != Lagrange { - t.Fatal("expected basis is Lagrange") - } - if q.Layout != Regular { - t.Fatal("expected layout is Regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE REGULAR - { - _p := fromLagrange2(p, domain) - q := _p.Clone() - q.ToLagrange(domain) - - if q.Basis != Lagrange { - t.Fatal("expected basis is Lagrange") - } - if q.Layout != Regular { - t.Fatal("expected layout is Regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE BITREVERSE - { - _p := fromLagrange3(p, domain) - q := _p.Clone() - q.ToLagrange(domain) - if q.Basis != Lagrange { - t.Fatal("expected basis is Lagrange") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is BitReverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE_COSET REGULAR - { - _p := fromLagrange4(p, domain) - q := _p.Clone() - q.ToLagrange(domain) - if q.Basis != Lagrange { - t.Fatal("expected basis is Lagrange") - } - if q.Layout != Regular { - t.Fatal("expected layout is Regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE_COSET BITREVERSE - { - _p := fromLagrange5(p, domain) - q := _p.Clone() - q.ToLagrange(domain) - if q.Basis != Lagrange { - t.Fatal("expected basis is Lagrange") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is BitReverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - -} - -// CANONICAL REGULAR -func fromCanonical0(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Canonical - _p.Layout = Regular - return _p -} - -// CANONICAL BITREVERSE -func fromCanonical1(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Canonical - _p.Layout = BitReverse - return _p -} - -// LAGRANGE REGULAR -func fromCanonical2(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Lagrange - _p.Layout = Regular - d.FFT(_p.Coefficients(), fft.DIF) - fft.BitReverse(_p.Coefficients()) - return _p -} - -// LAGRANGE BITREVERSE -func fromCanonical3(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Lagrange - _p.Layout = BitReverse - d.FFT(_p.Coefficients(), fft.DIF) - return _p -} - -// LAGRANGE_COSET REGULAR -func fromCanonical4(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = LagrangeCoset - _p.Layout = Regular - d.FFT(_p.Coefficients(), fft.DIF, fft.OnCoset()) - fft.BitReverse(_p.Coefficients()) - return _p -} - -// LAGRANGE_COSET BITREVERSE -func fromCanonical5(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = LagrangeCoset - _p.Layout = BitReverse - d.FFT(_p.Coefficients(), fft.DIF, fft.OnCoset()) - return _p -} - -func TestPutInCanonicalForm(t *testing.T) { - - size := 64 - domain := fft.NewDomain(uint64(size)) - - // reference vector in canonical-regular form - c := randomVector(size) - p := NewPolynomial(c, Form{Basis: Canonical, Layout: Regular}) - - // CANONICAL REGULAR - { - _p := fromCanonical0(p, domain) - q := _p.Clone() - q.ToCanonical(domain) - if q.Basis != Canonical { - t.Fatal("expected basis is canonical") - } - if q.Layout != Regular { - t.Fatal("expected layout is regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // CANONICAL BITREVERSE - { - _p := fromCanonical1(p, domain) - q := _p.Clone() - q.ToCanonical(domain) - if q.Basis != Canonical { - t.Fatal("expected basis is canonical") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is bitReverse") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE REGULAR - { - _p := fromCanonical2(p, domain) - q := _p.Clone() - q.ToCanonical(domain) - if q.Basis != Canonical { - t.Fatal("expected basis is canonical") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is bitReverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(p.coefficients, q.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE BITREVERSE - { - _p := fromCanonical3(p, domain) - q := _p.Clone() - q.ToCanonical(domain) - if q.Basis != Canonical { - t.Fatal("expected basis is canonical") - } - if q.Layout != Regular { - t.Fatal("expected layout is regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE_COSET REGULAR - { - _p := fromCanonical4(p, domain) - q := _p.Clone() - q.ToCanonical(domain) - if q.Basis != Canonical { - t.Fatal("expected basis is canonical") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is BitReverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE_COSET BITREVERSE - { - _p := fromCanonical5(p, domain) - q := _p.Clone() - q.ToCanonical(domain) - if q.Basis != Canonical { - t.Fatal("expected basis is canonical") - } - if q.Layout != Regular { - t.Fatal("expected layout is regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - -} - -// CANONICAL REGULAR -func fromLagrangeCoset0(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Canonical - _p.Layout = Regular - d.FFTInverse(_p.Coefficients(), fft.DIF, fft.OnCoset()) - fft.BitReverse(_p.Coefficients()) - return _p -} - -// CANONICAL BITREVERSE -func fromLagrangeCoset1(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Canonical - _p.Layout = BitReverse - d.FFTInverse(_p.Coefficients(), fft.DIF, fft.OnCoset()) - return _p -} - -// LAGRANGE REGULAR -func fromLagrangeCoset2(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Lagrange - _p.Layout = Regular - d.FFTInverse(_p.Coefficients(), fft.DIF, fft.OnCoset()) - d.FFT(_p.Coefficients(), fft.DIT) - return _p -} - -// LAGRANGE BITREVERSE -func fromLagrangeCoset3(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = Lagrange - _p.Layout = BitReverse - d.FFTInverse(_p.Coefficients(), fft.DIF, fft.OnCoset()) - d.FFT(_p.Coefficients(), fft.DIT) - fft.BitReverse(_p.Coefficients()) - return _p -} - -// LAGRANGE_COSET REGULAR -func fromLagrangeCoset4(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = LagrangeCoset - _p.Layout = Regular - return _p -} - -// LAGRANGE_COSET BITREVERSE -func fromLagrangeCoset5(p *Polynomial, d *fft.Domain) *Polynomial { - _p := p.Clone() - _p.Basis = LagrangeCoset - _p.Layout = BitReverse - fft.BitReverse(p.Coefficients()) - return _p -} - -func TestPutInLagrangeCosetForm(t *testing.T) { - - size := 64 - domain := fft.NewDomain(uint64(size)) - - // reference vector in canonical-regular form - c := randomVector(size) - p := NewPolynomial(c, Form{Basis: LagrangeCoset, Layout: Regular}) - - // CANONICAL REGULAR - { - _p := fromLagrangeCoset0(p, domain) - q := _p.Clone() - q.ToLagrangeCoset(domain) - if q.Basis != LagrangeCoset { - t.Fatal("expected basis is lagrange coset") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is bit reverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // CANONICAL BITREVERSE - { - _p := fromLagrangeCoset1(p, domain) - q := _p.Clone() - q.ToLagrangeCoset(domain) - if q.Basis != LagrangeCoset { - t.Fatal("expected basis is lagrange coset") - } - if q.Layout != Regular { - t.Fatal("expected layout is regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE REGULAR - { - _p := fromLagrangeCoset2(p, domain) - q := _p.Clone() - q.ToLagrangeCoset(domain) - if q.Basis != LagrangeCoset { - t.Fatal("expected basis is lagrange coset") - } - if q.Layout != Regular { - t.Fatal("expected layout is regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE BITREVERSE - { - _p := fromLagrangeCoset3(p, domain) - q := _p.Clone() - q.ToLagrangeCoset(domain) - if q.Basis != LagrangeCoset { - t.Fatal("expected basis is lagrange coset") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is bit reverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE_COSET REGULAR - { - _p := fromLagrangeCoset4(p, domain) - q := _p.Clone() - q.ToLagrangeCoset(domain) - if q.Basis != LagrangeCoset { - t.Fatal("expected basis is lagrange coset") - } - if q.Layout != Regular { - t.Fatal("expected layout is regular") - } - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - - // LAGRANGE_COSET BITREVERSE - { - _p := fromLagrangeCoset5(p, domain) - q := _p.Clone() - q.ToLagrangeCoset(domain) - if q.Basis != LagrangeCoset { - t.Fatal("expected basis is lagrange coset") - } - if q.Layout != BitReverse { - t.Fatal("expected layout is bit reverse") - } - fft.BitReverse(q.Coefficients()) - if !cmpCoefficents(q.coefficients, p.coefficients) { - t.Fatal("wrong coefficients") - } - } - -} diff --git a/ecc/bw6-756/fr/iop/quotient.go b/ecc/bw6-756/fr/iop/quotient.go deleted file mode 100644 index 3fe178cdeb..0000000000 --- a/ecc/bw6-756/fr/iop/quotient.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "math/big" - "math/bits" - - "github.com/consensys/gnark-crypto/internal/parallel" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/fft" -) - -// DivideByXMinusOne -// The input must be in LagrangeCoset. -// The result is in Canonical Regular. -func DivideByXMinusOne(a *Polynomial, domains [2]*fft.Domain) (*Polynomial, error) { - - // check that the basis is LagrangeCoset - if a.Basis != LagrangeCoset { - return nil, ErrMustBeLagrangeCoset - } - - // prepare the evaluations of x^n-1 on the big domain's coset - xnMinusOneInverseLagrangeCoset := evaluateXnMinusOneDomainBigCoset(domains) - - rho := a.coefficients.Len() / a.size - - nbElmts := a.coefficients.Len() - - coeffs := make([]fr.Element, a.coefficients.Len()) - res := NewPolynomial(&coeffs, Form{Layout: BitReverse, Basis: LagrangeCoset}) - res.size = a.size - res.blindedSize = a.blindedSize - - nn := uint64(64 - bits.TrailingZeros(uint(nbElmts))) - parallel.Execute(a.coefficients.Len(), func(start, end int) { - for i := start; i < end; i++ { - iRev := bits.Reverse64(uint64(i)) >> nn - c := a.GetCoeff(i) - (*res.coefficients)[iRev]. - Mul(&c, &xnMinusOneInverseLagrangeCoset[i%rho]) - } - }) - - res.ToCanonical(domains[1]) - - return res, nil - -} - -// evaluateXnMinusOneDomainBigCoset evaluates Xᵐ-1 on DomainBig coset -func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { - - ratio := domains[1].Cardinality / domains[0].Cardinality - - res := make([]fr.Element, ratio) - - expo := big.NewInt(int64(domains[0].Cardinality)) - res[0].Exp(domains[1].FrMultiplicativeGen, expo) - - var t fr.Element - t.Exp(domains[1].Generator, big.NewInt(int64(domains[0].Cardinality))) - - one := fr.One() - - for i := 1; i < int(ratio); i++ { - res[i].Mul(&res[i-1], &t) - res[i-1].Sub(&res[i-1], &one) - } - res[len(res)-1].Sub(&res[len(res)-1], &one) - - res = fr.BatchInvert(res) - - return res -} diff --git a/ecc/bw6-756/fr/iop/quotient_test.go b/ecc/bw6-756/fr/iop/quotient_test.go deleted file mode 100644 index 5a007c81fb..0000000000 --- a/ecc/bw6-756/fr/iop/quotient_test.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/fft" -) - -// computes x₃ in h(x₁,x₂,x₃) = x₁^{2}*x₂ + x₃ - x₁^{3} -// from x₁ and x₂. -func computex3(x []fr.Element) fr.Element { - - var a, b fr.Element - a.Square(&x[0]).Mul(&a, &x[1]) - b.Square(&x[0]).Mul(&b, &x[0]) - a.Sub(&b, &a) - return a - -} - -func buildPoly(size int, form Form) *Polynomial { - v := make([]fr.Element, size) - return NewPolynomial(&v, form) -} - -func TestDivideByXMinusOne(t *testing.T) { - - f := func(_ int, x ...fr.Element) fr.Element { - var a, b fr.Element - a.Square(&x[0]).Mul(&a, &x[1]).Add(&a, &x[2]) - b.Square(&x[0]).Mul(&b, &x[0]) - a.Sub(&a, &b) - return a - } - - // create the multivariate polynomial h - // h(x₁,x₂,x₃) = x₁^{2}*x₂ + x₃ - x₁^{3} - nbEntries := 3 - - // create an instance (f_i) where h holds - sizeSystem := 8 - - form := Form{Basis: Lagrange, Layout: Regular} - - entries := make([]*Polynomial, nbEntries) - entries[0] = buildPoly(sizeSystem, form) - entries[1] = buildPoly(sizeSystem, form) - entries[2] = buildPoly(sizeSystem, form) - - for i := 0; i < sizeSystem; i++ { - - entries[0].Coefficients()[i].SetRandom() - entries[1].Coefficients()[i].SetRandom() - tmp := computex3( - []fr.Element{entries[0].Coefficients()[i], - entries[1].Coefficients()[i]}) - entries[2].Coefficients()[i].Set(&tmp) - - x := []fr.Element{ - entries[0].GetCoeff(i), - entries[1].GetCoeff(i), - entries[2].GetCoeff(i), - } - a := f(0, x...) - if !a.IsZero() { - t.Fatal("system does not vanish on x^n-1") - } - } - - // compute the quotient where the entries are in Regular layout - var domains [2]*fft.Domain - domains[0] = fft.NewDomain(uint64(sizeSystem)) - domains[1] = fft.NewDomain(ecc.NextPowerOfTwo(uint64(3 * sizeSystem))) - - entries[0].ToCanonical(domains[0]). - ToRegular(). - ToLagrangeCoset(domains[1]). - ToRegular() - - entries[1].ToCanonical(domains[0]). - ToRegular(). - ToLagrangeCoset(domains[1]). - ToRegular() - - entries[2].ToCanonical(domains[0]). - ToRegular(). - ToLagrangeCoset(domains[1]). - ToRegular() - - expectedForm := Form{Layout: BitReverse, Basis: LagrangeCoset} - h, err := Evaluate(f, nil, expectedForm, entries...) - if err != nil { - t.Fatal(err) - } - - q, err := DivideByXMinusOne(h, domains) - if err != nil { - t.Fatal(err) - } - - // evaluate the quotient at a random point and check that - // the relation holds. - var x fr.Element - x.SetRandom() - qx := q.Evaluate(x) - entries[0].ToCanonical(domains[1]) - entries[1].ToCanonical(domains[1]) - entries[2].ToCanonical(domains[1]) - ax := entries[0].Evaluate(x) - bx := entries[1].Evaluate(x) - cx := entries[2].Evaluate(x) - hx := f(0, ax, bx, cx) - - var xnminusone, one fr.Element - one.SetOne() - xnminusone.Set(&x). - Square(&xnminusone). - Square(&xnminusone). - Square(&xnminusone). - Sub(&xnminusone, &one) - qx.Mul(&qx, &xnminusone) - if !qx.Equal(&hx) { - t.Fatal("error computing quotient") - } -} diff --git a/ecc/bw6-756/fr/iop/ratios.go b/ecc/bw6-756/fr/iop/ratios.go deleted file mode 100644 index 449ae8f539..0000000000 --- a/ecc/bw6-756/fr/iop/ratios.go +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "errors" - "math/big" - "math/bits" - "runtime" - "sync" - - "github.com/consensys/gnark-crypto/internal/parallel" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/fft" -) - -// errors related to the computation of the quotient and the ratios. -var ( - ErrMustBeRegular = errors.New("the layout must be Regular") - ErrMustBeCanonical = errors.New("the basis must be Canonical") - ErrMustBeLagrangeCoset = errors.New("the basis must be LagrangeCoset") - ErrInconsistentFormat = errors.New("the format of the polynomials must be the same") - ErrInconsistentSize = errors.New("the sizes of the polynomial must be the same as the size of the domain") - ErrNumberPolynomials = errors.New("the number of polynomials in the denominator and the numerator must be the same") - ErrSizeNotPowerOfTwo = errors.New("the size of the polynomials must be a power of two") - ErrInconsistentSizeDomain = errors.New("the size of the domain must be consistent with the size of the polynomials") - ErrIncorrectNumberOfVariables = errors.New("the number of variables is incorrect") -) - -// Build an 'accumulating ratio' polynomial. -// * numerator list of polynomials that will form the numerator of the ratio -// * denominator list of polynomials that will form the denominator of the ratio -// The polynomials in the denominator and the numerator are expected to be of -// the same size and the size must be a power of 2. The polynomials are given as -// pointers in case the caller wants to FFTInv the polynomials during the process. -// * beta variable at which the numerator and denominators are evaluated -// * expectedForm expected form of the resulting polynomial -// * Return: say beta=β, numerator = [P₁,...,P_m], denominator = [Q₁,..,Q_m]. The function -// returns a polynomial whose evaluation on the j-th root of unity is -// (Π_{k> nn - - for j := 0; j < nbPolynomials; j++ { - - if numerator[j].Layout == BitReverse { - a.Sub(&beta, &numerator[j].Coefficients()[iRev]) - } else { - a.Sub(&beta, &numerator[j].Coefficients()[i]) - } - b.Mul(&b, &a) - - if denominator[j].Layout == BitReverse { - c.Sub(&beta, &denominator[j].Coefficients()[iRev]) - } else { - c.Sub(&beta, &denominator[j].Coefficients()[i]) - } - d.Mul(&d, &c) - } - // b = Πₖ (β-Pₖ(ωⁱ⁻¹)) - // d = Πₖ (β-Qₖ(ωⁱ⁻¹)) - - coeffs[i+1].Mul(&coeffs[i], &b) - t[i+1].Mul(&t[i], &d) - - } - - t = fr.BatchInvert(t) - for i := 1; i < n; i++ { - coeffs[i].Mul(&coeffs[i], &t[i]) - } - - res := NewPolynomial(&coeffs, expectedForm) - - // at this stage the result is in Lagrange form, Regular layout - putInExpectedFormFromLagrangeRegular(res, domain, expectedForm) - - return res, nil -} - -// BuildRatioCopyConstraint builds the accumulating ratio polynomial to prove that -// [P₁ ∥ .. ∥ P_{n—1}] is invariant by the permutation \sigma. -// Namely it returns the polynomial Z whose evaluation on the j-th root of unity is -// Z(ω^j) = Π_{i> nn) - - for j, p := range entries { - idx := i - if p.Layout == BitReverse { - idx = iRev - } - - a.Mul(&beta, &evaluationIDSmallDomain[i+j*n]). - Add(&a, &gamma). - Add(&a, &p.Coefficients()[idx]) - - b.Mul(&b, &a) - - c.Mul(&beta, &evaluationIDSmallDomain[permutation[i+j*n]]). - Add(&c, &gamma). - Add(&c, &p.Coefficients()[idx]) - d.Mul(&d, &c) - } - - // b = Πⱼ(Pⱼ(ωⁱ)+β*ωⁱνʲ+γ) - // d = Πⱼ(Qⱼ(ωⁱ)+β*σ(j*n+i)+γ) - coeffs[i+1].Set(&b) - t[i+1].Set(&d) - } - }) - - chCoeffs := make(chan struct{}, 1) - go func() { - for i := 2; i < n; i++ { - // ignoring coeffs[0] - coeffs[i].Mul(&coeffs[i], &coeffs[i-1]) - } - close(chCoeffs) - }() - - for i := 2; i < n; i++ { - // ignoring t[0] - t[i].Mul(&t[i], &t[i-1]) - } - <-chCoeffs - - // rough ratio inverse to mul; see if it makes sense to parallelize the batch inverse. - const ratioInvMul = 1000 / 17 - nbTasks := runtime.NumCPU() - if ratio := n / ratioInvMul; ratio < nbTasks { - nbTasks = ratio - } - - parallel.Execute(n-1, func(start, end int) { - // ignoring t[0] and coeff[0] - start++ - end++ - tInv := fr.BatchInvert(t[start:end]) - for i := start; i < end; i++ { - coeffs[i].Mul(&coeffs[i], &tInv[i-start]) - } - }, nbTasks) - - res := NewPolynomial(&coeffs, expectedForm) - // at this stage the result is in Lagrange form, Regular layout - putInExpectedFormFromLagrangeRegular(res, domain, expectedForm) - - return res, nil - -} - -func putInExpectedFormFromLagrangeRegular(p *Polynomial, domain *fft.Domain, expectedForm Form) { - p.Basis = expectedForm.Basis - p.Layout = expectedForm.Layout - - if expectedForm.Basis == Canonical { - domain.FFTInverse(p.Coefficients(), fft.DIF) - if expectedForm.Layout == Regular { - fft.BitReverse(p.Coefficients()) - } - return - } - - if expectedForm.Basis == LagrangeCoset { - domain.FFTInverse(p.Coefficients(), fft.DIF) - domain.FFT(p.Coefficients(), fft.DIT, fft.OnCoset()) - if expectedForm.Layout == BitReverse { - fft.BitReverse(p.Coefficients()) - } - return - } - - if expectedForm.Layout == BitReverse { - fft.BitReverse(p.Coefficients()) - } - -} - -// check that the polynomials are of the same size. -// It assumes that pols contains slices of the same size. -func checkSize(pols ...[]*Polynomial) error { - - // check sizes between one another - m := len(pols) - n := pols[0][0].coefficients.Len() - for i := 0; i < m; i++ { - for j := 0; j < len(pols); j++ { - if pols[i][j].coefficients.Len() != n { - return ErrInconsistentSize - } - } - } - - return nil -} - -// buildDomain builds the fft domain necessary to do FFTs. -// n is the cardinality of the domain, it must be a power of 2. -func buildDomain(n int, domain *fft.Domain) (*fft.Domain, error) { - - // check if the sizes are a power of 2 - if n&(n-1) != 0 { - return nil, ErrSizeNotPowerOfTwo - } - - // if the domain doesn't exist we create it. - if domain == nil { - domain = fft.NewDomain(uint64(n)) - } - - // in case domain was not nil, it must match the size of the polynomials. - if domain.Cardinality != uint64(n) { - return nil, ErrInconsistentSizeDomain - } - - return domain, nil -} - -// getSupportIdentityPermutation returns the support on which the permutation acts. -// Concretely it's X evaluated on -// [1,ω,..,ωˢ⁻¹,g,g*ω,..,g*ωˢ⁻¹,..,gⁿ⁻¹,gⁿ⁻¹*ω,..,gⁿ⁻¹*ωˢ⁻¹] -// nbCopies is the number of cosets of the roots of unity that are needed, including the set of -// roots of unity itself. -func getSupportIdentityPermutation(nbCopies int, domain *fft.Domain) []fr.Element { - if nbCopies <= 0 { - panic("getSupportIdentityPermutation: nbCopies must be positive") - } - - res := make([]fr.Element, uint64(nbCopies)*domain.Cardinality) - sizePoly := int(domain.Cardinality) - - // TODO @gbotrel check if we can reuse the pre-computed twiddles from the domain. - res[0].SetOne() - if len(res) > 1 { - res[1].Set(&domain.Generator) - for i := 2; i < len(res); i++ { - res[i].Mul(&res[i-1], &domain.Generator) - } - } - - if nbCopies <= 1 { - return res - } - var wg sync.WaitGroup - wg.Add(nbCopies - 1) - for i := 1; i < nbCopies; i++ { - i := i - - var coset fr.Element - coset.Exp(domain.FrMultiplicativeGen, big.NewInt(int64(i))) - - go func() { - parallel.Execute(sizePoly, func(start, end int) { - for j := start; j < end; j++ { - res[i*sizePoly+j].Mul(&res[j], &coset) - } - }, (runtime.NumCPU()/(nbCopies-1))+1) - wg.Done() - }() - } - - wg.Wait() - - return res -} diff --git a/ecc/bw6-756/fr/iop/ratios_test.go b/ecc/bw6-756/fr/iop/ratios_test.go deleted file mode 100644 index 4b27c5f6b5..0000000000 --- a/ecc/bw6-756/fr/iop/ratios_test.go +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/fft" -) - -// getPermutation returns a deterministic permutation -// of n elements where n is even. The result should be -// interpreted as -// a permutation σ(i)=permutation[i] -// g is a generator of ℤ/nℤ -func getPermutation(n, g int) []int { - - res := make([]int, n) - a := g - for i := 0; i < n; i++ { - res[i] = a - a += g - a %= n - } - return res -} - -func getPermutedPolynomials(sizePolynomials, nbPolynomials int) ([]*Polynomial, []*Polynomial, []int) { - - numerator := make([]*Polynomial, nbPolynomials) - for i := 0; i < nbPolynomials; i++ { - numerator[i] = NewPolynomial(randomVector(sizePolynomials), Form{Basis: Lagrange, Layout: Regular}) - } - - // get permutation - sigma := getPermutation(sizePolynomials*nbPolynomials, 3) - - // the denominator is the permuted version of the numerators - // concatenated - denominator := make([]*Polynomial, nbPolynomials) - for i := 0; i < nbPolynomials; i++ { - denominator[i] = NewPolynomial(randomVector(sizePolynomials), Form{Basis: Lagrange, Layout: Regular}) - } - for i := 0; i < len(sigma); i++ { - id := int(sigma[i] / sizePolynomials) - od := sigma[i] % sizePolynomials - in := int(i / sizePolynomials) - on := i % sizePolynomials - denominator[in].Coefficients()[on].Set(&numerator[id].Coefficients()[od]) - } - - return numerator, denominator, sigma - -} - -func TestBuildRatioShuffledVectors(t *testing.T) { - - // generate random vectors, interpreted in Lagrange form, - // regular layout. It is enough for this test if TestPutInLagrangeForm - // passes. - sizePolynomials := 8 - nbPolynomials := 4 - numerator, denominator, _ := getPermutedPolynomials(sizePolynomials, nbPolynomials) - - // save the originals for further tests with polynomials in different forms - backupNumerator := make([]*Polynomial, nbPolynomials) - backupDenominator := make([]*Polynomial, nbPolynomials) - for i := 0; i < nbPolynomials; i++ { - backupNumerator[i] = numerator[i].Clone() - backupDenominator[i] = denominator[i].Clone() - } - - // build the ratio polynomial - expectedForm := Form{Basis: Lagrange, Layout: Regular} - domain := fft.NewDomain(uint64(sizePolynomials)) - var beta fr.Element - beta.SetRandom() - ratio, err := BuildRatioShuffledVectors(numerator, denominator, beta, expectedForm, domain) - if err != nil { - t.Fatal() - } - - // check that the whole product is equal to one - var a, b, c, d fr.Element - b.SetOne() - d.SetOne() - for i := 0; i < nbPolynomials; i++ { - a.Sub(&beta, &numerator[i].Coefficients()[sizePolynomials-1]) - b.Mul(&a, &b) - c.Sub(&beta, &denominator[i].Coefficients()[sizePolynomials-1]) - d.Mul(&c, &d) - } - a.Mul(&b, &ratio.Coefficients()[sizePolynomials-1]). - Div(&a, &d) - var one fr.Element - one.SetOne() - if !a.Equal(&one) { - t.Fatal("accumulating ratio is not equal to one") - } - - // check that the ratio is correct when the inputs are - // bit reversed - for i := 0; i < nbPolynomials; i++ { - numerator[i] = backupNumerator[i].Clone() - fft.BitReverse(numerator[i].Coefficients()) - numerator[i].Layout = BitReverse - - denominator[i] = backupDenominator[i].Clone() - fft.BitReverse(denominator[i].Coefficients()) - denominator[i].Layout = BitReverse - } - { - var err error - _ratio, err := BuildRatioShuffledVectors(numerator, denominator, beta, expectedForm, domain) - if err != nil { - t.Fatal(err) - } - checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients) - if !checkCoeffs { - t.Fatal(err) - } - } - - // check that the ratio is correct when the inputs are in - // canonical form, regular - for i := 0; i < nbPolynomials; i++ { - numerator[i] = backupNumerator[i].Clone() - domain.FFTInverse(numerator[i].Coefficients(), fft.DIF) - fft.BitReverse(numerator[i].Coefficients()) - numerator[i].Basis = Canonical - - denominator[i] = backupDenominator[i].Clone() - domain.FFTInverse(denominator[i].Coefficients(), fft.DIF) - fft.BitReverse(denominator[i].Coefficients()) - denominator[i].Basis = Canonical - } - { - var err error - _ratio, err := BuildRatioShuffledVectors(numerator, denominator, beta, expectedForm, domain) - if err != nil { - t.Fatal(err) - } - checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients) - if !checkCoeffs { - t.Fatal("coefficients of ratio are not consistent") - } - } - - // check that the ratio is correct when the inputs are in - // canonical form, bit reverse - for i := 0; i < nbPolynomials; i++ { - numerator[i] = backupNumerator[i].Clone() - domain.FFTInverse(numerator[i].Coefficients(), fft.DIF) - numerator[i].Layout = BitReverse - numerator[i].Basis = Canonical - - denominator[i] = backupDenominator[i].Clone() - domain.FFTInverse(denominator[i].Coefficients(), fft.DIF) - denominator[i].Layout = BitReverse - denominator[i].Basis = Canonical - } - { - var err error - _ratio, err := BuildRatioShuffledVectors(numerator, denominator, beta, expectedForm, domain) - if err != nil { - t.Fatal(err) - } - checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients) - if !checkCoeffs { - t.Fatal("coefficients of ratio are not consistent") - } - } - -} - -// sizePolynomial*nbPolynomial must be divisible by 2. -// The function generates a list of nbPolynomials (P_i) of size n=sizePolynomials -// such that [P₁ ∥ .. ∥ P₂ ] is invariant under the permutation -// σ defined by: -// σ = (12)(34)..(2n-1 2n) -// so σ is a product of cycles length 2. -func getInvariantEntriesUnderPermutation(sizePolynomials, nbPolynomials int) ([]*Polynomial, []int64) { - res := make([]*Polynomial, nbPolynomials) - form := Form{Layout: Regular, Basis: Lagrange} - for i := 0; i < nbPolynomials; i++ { - v := make([]fr.Element, sizePolynomials) - res[i] = NewPolynomial(&v, form) - for j := 0; j < sizePolynomials/2; j++ { - res[i].Coefficients()[2*j].SetRandom() - res[i].Coefficients()[2*j+1].Set(&res[i].Coefficients()[2*j]) - } - } - permutation := make([]int64, nbPolynomials*sizePolynomials) - for i := int64(0); i < int64(nbPolynomials*sizePolynomials/2); i++ { - permutation[2*i] = 2*i + 1 - permutation[2*i+1] = 2 * i - } - return res, permutation -} - -func TestBuildRatioCopyConstraint(t *testing.T) { - - // generate random vectors, interpreted in Lagrange form, - // regular layout. It is enough for this test if TestPutInLagrangeForm - // passes. - sizePolynomials := 8 - nbPolynomials := 4 - entries, sigma := getInvariantEntriesUnderPermutation(sizePolynomials, nbPolynomials) - - // save the originals for further tests with polynomials in different forms - backupEntries := make([]*Polynomial, nbPolynomials) - for i := 0; i < nbPolynomials; i++ { - backupEntries[i] = entries[i].Clone() - } - - // build the ratio polynomial - expectedForm := Form{Basis: Lagrange, Layout: Regular} - domain := fft.NewDomain(uint64(sizePolynomials)) - var beta, gamma fr.Element - beta.SetRandom() - gamma.SetRandom() - ratio, err := BuildRatioCopyConstraint(entries, sigma, beta, gamma, expectedForm, domain) - if err != nil { - t.Fatal() - } - - // check that the whole product is equal to one - suppID := getSupportIdentityPermutation(nbPolynomials, domain) - var a, b, c, d fr.Element - b.SetOne() - d.SetOne() - for i := 0; i < nbPolynomials; i++ { - a.Mul(&beta, &suppID[(i+1)*sizePolynomials-1]). - Add(&a, &entries[i].Coefficients()[sizePolynomials-1]). - Add(&a, &gamma) - b.Mul(&b, &a) - - c.Mul(&beta, &suppID[sigma[(i+1)*sizePolynomials-1]]). - Add(&c, &entries[i].Coefficients()[sizePolynomials-1]). - Add(&c, &gamma) - d.Mul(&d, &c) - } - a.Mul(&b, &ratio.Coefficients()[sizePolynomials-1]). - Div(&a, &d) - var one fr.Element - one.SetOne() - if !a.Equal(&one) { - t.Fatal("accumulating ratio is not equal to one") - } - - // check that the ratio is correct when the inputs are - // bit reversed - for i := 0; i < nbPolynomials; i++ { - entries[i] = backupEntries[i].Clone() - fft.BitReverse(entries[i].Coefficients()) - entries[i].Layout = BitReverse - } - { - var err error - _ratio, err := BuildRatioCopyConstraint(entries, sigma, beta, gamma, expectedForm, domain) - if err != nil { - t.Fatal(err) - } - checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients) - if !checkCoeffs { - t.Fatal(err) - } - } - - // check that the ratio is correct when the inputs are in - // canonical form, regular - for i := 0; i < nbPolynomials; i++ { - entries[i] = backupEntries[i].Clone() - domain.FFTInverse(entries[i].Coefficients(), fft.DIF) - fft.BitReverse(entries[i].Coefficients()) - entries[i].Layout = Regular - entries[i].Basis = Canonical - } - { - var err error - _ratio, err := BuildRatioCopyConstraint(entries, sigma, beta, gamma, expectedForm, domain) - if err != nil { - t.Fatal(err) - } - checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients) - if !checkCoeffs { - t.Fatal("coefficients of ratio are not consistent") - } - } - - // check that the ratio is correct when the inputs are in - // canonical form, bit reverse - for i := 0; i < nbPolynomials; i++ { - entries[i] = backupEntries[i].Clone() - domain.FFTInverse(entries[i].Coefficients(), fft.DIF) - entries[i].Layout = BitReverse - entries[i].Basis = Canonical - } - - { - var err error - _ratio, err := BuildRatioCopyConstraint(entries, sigma, beta, gamma, expectedForm, domain) - if err != nil { - t.Fatal(err) - } - checkCoeffs := cmpCoefficents(_ratio.coefficients, ratio.coefficients) - if !checkCoeffs { - t.Fatal("coefficients of ratio are not consistent") - } - } -} diff --git a/ecc/bw6-756/fr/iop/utils.go b/ecc/bw6-756/fr/iop/utils.go deleted file mode 100644 index 068cc65b70..0000000000 --- a/ecc/bw6-756/fr/iop/utils.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package iop - -import ( - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) - -//---------------------------------------------------- -// exp functions until 5 - -func exp0(x fr.Element) fr.Element { - var res fr.Element - res.SetOne() - return res -} - -func exp1(x fr.Element) fr.Element { - return x -} - -func exp2(x fr.Element) fr.Element { - return *x.Square(&x) -} - -func exp3(x fr.Element) fr.Element { - var res fr.Element - res.Square(&x).Mul(&res, &x) - return res -} - -func exp4(x fr.Element) fr.Element { - x.Square(&x).Square(&x) - return x -} - -func exp5(x fr.Element) fr.Element { - var res fr.Element - res.Square(&x).Square(&res).Mul(&res, &x) - return res -} - -// doesn't return any errors, it is a private method, that -// is assumed to be called with correct arguments. -func smallExp(x fr.Element, n int) fr.Element { - if n == 0 { - return exp0(x) - } - if n == 1 { - return exp1(x) - } - if n == 2 { - return exp2(x) - } - if n == 3 { - return exp3(x) - } - if n == 4 { - return exp4(x) - } - if n == 5 { - return exp5(x) - } - return fr.Element{} -} diff --git a/ecc/bw6-756/fr/mimc/doc.go b/ecc/bw6-756/fr/mimc/doc.go deleted file mode 100644 index 78837e1c80..0000000000 --- a/ecc/bw6-756/fr/mimc/doc.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package mimc provides MiMC hash function using Miyaguchi–Preneel construction. -// -// # Length extension attack -// -// The MiMC hash function is vulnerable to a length extension attack. For -// example when we have a hash -// -// h = MiMC(k || m) -// -// and we want to hash a new message -// -// m' = m || m2, -// -// we can compute -// -// h' = MiMC(k || m || m2) -// -// without knowing k by computing -// -// h' = MiMC(h || m2). -// -// This is because the MiMC hash function is a simple iterated cipher, and the -// hash value is the state of the cipher after encrypting the message. -// -// There are several ways to mitigate this attack: -// - use a random key for each hash -// - use a domain separation tag for different use cases: -// h = MiMC(k || tag || m) -// - use the secret input as last input: -// h = MiMC(m || k) -// -// In general, inside a circuit the length-extension attack is not a concern as -// due to the circuit definition the attacker can not append messages to -// existing hash. But the user has to consider the cases when using a secret key -// and MiMC in different contexts. -// -// # Hash input format -// -// The MiMC hash function is defined over a field. The input to the hash -// function is a byte slice. The byte slice is interpreted as a sequence of -// field elements. Due to this interpretation, the input byte slice length must -// be multiple of the field modulus size. And every secuence of byte slice for a -// single field element must be strictly less than the field modulus. -package mimc diff --git a/ecc/bw6-756/fr/mimc/mimc.go b/ecc/bw6-756/fr/mimc/mimc.go deleted file mode 100644 index d53a5b0b2d..0000000000 --- a/ecc/bw6-756/fr/mimc/mimc.go +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package mimc - -import ( - "errors" - "hash" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "golang.org/x/crypto/sha3" - "math/big" - "sync" -) - -const ( - mimcNbRounds = 163 - seed = "seed" // seed to derive the constants - BlockSize = fr.Bytes // BlockSize size that mimc consumes -) - -// Params constants for the mimc hash function -var ( - mimcConstants [mimcNbRounds]fr.Element - once sync.Once -) - -// digest represents the partial evaluation of the checksum -// along with the params of the mimc function -type digest struct { - h fr.Element - data []fr.Element // data to hash - byteOrder fr.ByteOrder -} - -// GetConstants exposed to be used in gnark -func GetConstants() []big.Int { - once.Do(initConstants) // init constants - res := make([]big.Int, mimcNbRounds) - for i := 0; i < mimcNbRounds; i++ { - mimcConstants[i].BigInt(&res[i]) - } - return res -} - -// NewMiMC returns a MiMCImpl object, pure-go reference implementation -func NewMiMC(opts ...Option) hash.Hash { - d := new(digest) - d.Reset() - cfg := mimcOptions(opts...) - d.byteOrder = cfg.byteOrder - return d -} - -// Reset resets the Hash to its initial state. -func (d *digest) Reset() { - d.data = d.data[:0] - d.h = fr.Element{0, 0, 0, 0} -} - -// Sum appends the current hash to b and returns the resulting slice. -// It does not change the underlying hash state. -func (d *digest) Sum(b []byte) []byte { - buffer := d.checksum() - d.data = nil // flush the data already hashed - hash := buffer.Bytes() - b = append(b, hash[:]...) - return b -} - -// BlockSize returns the hash's underlying block size. -// The Write method must be able to accept any amount -// of data, but it may operate more efficiently if all writes -// are a multiple of the block size. -func (d *digest) Size() int { - return BlockSize -} - -// BlockSize returns the number of bytes Sum will return. -func (d *digest) BlockSize() int { - return BlockSize -} - -// Write (via the embedded io.Writer interface) adds more data to the running hash. -// -// Each []byte block of size BlockSize represents a big endian fr.Element. -// -// If len(p) is not a multiple of BlockSize and any of the []byte in p represent an integer -// larger than fr.Modulus, this function returns an error. -// -// To hash arbitrary data ([]byte not representing canonical field elements) use fr.Hash first -func (d *digest) Write(p []byte) (int, error) { - // we usually expect multiple of block size. But sometimes we hash short - // values (FS transcript). Instead of forcing to hash to field, we left-pad the - // input here. - if len(p) > 0 && len(p) < BlockSize { - pp := make([]byte, BlockSize) - copy(pp[len(pp)-len(p):], p) - p = pp - } - - var start int - for start = 0; start < len(p); start += BlockSize { - if elem, err := d.byteOrder.Element((*[BlockSize]byte)(p[start : start+BlockSize])); err == nil { - d.data = append(d.data, elem) - } else { - return 0, err - } - } - - if start != len(p) { - return 0, errors.New("invalid input length: must represent a list of field elements, expects a []byte of len m*BlockSize") - } - return len(p), nil -} - -// Hash hash using Miyaguchi-Preneel: -// https://en.wikipedia.org/wiki/One-way_compression_function -// The XOR operation is replaced by field addition, data is in Montgomery form -func (d *digest) checksum() fr.Element { - // Write guarantees len(data) % BlockSize == 0 - - // TODO @ThomasPiellard shouldn't Sum() returns an error if there is no data? - // TODO: @Tabaie, @Thomas Piellard Now sure what to make of this - /*if len(d.data) == 0 { - d.data = make([]byte, BlockSize) - }*/ - - for i := range d.data { - r := d.encrypt(d.data[i]) - d.h.Add(&r, &d.h).Add(&d.h, &d.data[i]) - } - - return d.h -} - -// plain execution of a mimc run -// m: message -// k: encryption key -func (d *digest) encrypt(m fr.Element) fr.Element { - once.Do(initConstants) // init constants - - var tmp fr.Element - for i := 0; i < mimcNbRounds; i++ { - // m = (m+k+c)^5 - tmp.Add(&m, &d.h).Add(&tmp, &mimcConstants[i]) - m.Square(&tmp). - Square(&m). - Mul(&m, &tmp) - } - m.Add(&m, &d.h) - return m -} - -// Sum computes the mimc hash of msg from seed -func Sum(msg []byte) ([]byte, error) { - var d digest - if _, err := d.Write(msg); err != nil { - return nil, err - } - h := d.checksum() - bytes := h.Bytes() - return bytes[:], nil -} - -func initConstants() { - bseed := ([]byte)(seed) - - hash := sha3.NewLegacyKeccak256() - _, _ = hash.Write(bseed) - rnd := hash.Sum(nil) // pre hash before use - hash.Reset() - _, _ = hash.Write(rnd) - - for i := 0; i < mimcNbRounds; i++ { - rnd = hash.Sum(nil) - mimcConstants[i].SetBytes(rnd) - hash.Reset() - _, _ = hash.Write(rnd) - } -} - -// WriteString writes a string that doesn't necessarily consist of field elements -func (d *digest) WriteString(rawBytes []byte) error { - if elems, err := fr.Hash(rawBytes, []byte("string:"), 1); err != nil { - return err - } else { - d.data = append(d.data, elems[0]) - } - return nil -} diff --git a/ecc/bw6-756/fr/mimc/options.go b/ecc/bw6-756/fr/mimc/options.go deleted file mode 100644 index 7fbe2e2e1b..0000000000 --- a/ecc/bw6-756/fr/mimc/options.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package mimc - -import ( - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) - -// Option defines option for altering the behavior of the MiMC hasher. -// See the descriptions of functions returning instances of this type for -// particular options. -type Option func(*mimcConfig) - -type mimcConfig struct { - byteOrder fr.ByteOrder -} - -// default options -func mimcOptions(opts ...Option) mimcConfig { - // apply options - opt := mimcConfig{ - byteOrder: fr.BigEndian, - } - for _, option := range opts { - option(&opt) - } - return opt -} - -// WithByteOrder sets the byte order used to decode the input -// in the Write method. Default is BigEndian. -func WithByteOrder(byteOrder fr.ByteOrder) Option { - return func(opt *mimcConfig) { - opt.byteOrder = byteOrder - } -} diff --git a/ecc/bw6-756/fr/pedersen/doc.go b/ecc/bw6-756/fr/pedersen/doc.go deleted file mode 100644 index 944679f5c5..0000000000 --- a/ecc/bw6-756/fr/pedersen/doc.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package pedersen allows to compute and verify Pedersen vector commitments -// -// Pedersen vector commitments are a type of homomorphic commitments that allow -// to commit to a vector of values and prove knowledge of the committed values. -// The commitments can be batched and verified in a single operation. -// -// The commitments are computed using a set of basis elements. The proving key -// contains the basis elements and their exponentiations by a random value. The -// verifying key contains the G2 generator and its exponentiation by the inverse -// of the random value. -// -// The setup process is a trusted setup and must be done securely, preferably using MPC. -// After the setup, the proving key does not have to be secret, but the randomness -// used during the setup must be discarded. -package pedersen diff --git a/ecc/bw6-756/fr/pedersen/example_test.go b/ecc/bw6-756/fr/pedersen/example_test.go deleted file mode 100644 index 0c3cf792b3..0000000000 --- a/ecc/bw6-756/fr/pedersen/example_test.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package pedersen - -import ( - "crypto/rand" - "fmt" - - "github.com/consensys/gnark-crypto/ecc" - curve "github.com/consensys/gnark-crypto/ecc/bw6-756" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) - -// This example demonstrates how to use the Pedersen commitment scheme -// to commit to a set of values and prove knowledge of the committed values. -// -// Does not perform any batching or multi-proof optimization. -func Example_singleProof() { - const nbElem = 4 - // create a proving key with independent basis elements - var buf [32]byte - basis := make([]curve.G1Affine, nbElem) - for i := range basis { - _, err := rand.Read(buf[:]) - if err != nil { - panic(err) - } - // we use hash-to-curve to avoid linear dependencies between basis elements - basis[i], err = curve.HashToG1(buf[:], []byte(fmt.Sprintf("basis %d", i))) - if err != nil { - panic(err) - } - } - // create a proving and verifying key. NB! Must be done using MPC - pks, vk, err := Setup([][]curve.G1Affine{basis}) - if err != nil { - panic(err) - } - // currently we only have a single proving key - pk := pks[0] - toCommit := make([]fr.Element, nbElem) - for i := range toCommit { - toCommit[i].SetRandom() - } - // commit to the values - commitment, err := pk.Commit(toCommit) - if err != nil { - panic(err) - } - // prove knowledge of the committed values - pok, err := pk.ProveKnowledge(toCommit) - if err != nil { - panic(err) - } - // verify the proof - if err := vk.Verify(commitment, pok); err != nil { - panic(err) - } - - fmt.Println("verified") - // output: verified -} - -// This example shows how to batch the commitment and proof generation. -func ExampleBatchProve() { - const nbPks = 3 - const nbElem = 4 - // create a proving key with independent basis elements - var buf [32]byte - basis := make([][]curve.G1Affine, nbPks) - for i := range basis { - basis[i] = make([]curve.G1Affine, nbElem) - for j := range basis[i] { - _, err := rand.Read(buf[:]) - if err != nil { - panic(err) - } - // we use hash-to-curve to avoid linear dependencies between basis elements - basis[i][j], err = curve.HashToG1(buf[:], []byte(fmt.Sprintf("basis %d", i))) - if err != nil { - panic(err) - } - } - } - // create a proving and verifying key. NB! Must be done using MPC - pks, vk, err := Setup(basis) - if err != nil { - panic(err) - } - // generate random values to commit to - toCommit := make([][]fr.Element, nbPks) - for i := range toCommit { - toCommit[i] = make([]fr.Element, nbElem) - for j := range toCommit[i] { - toCommit[i][j].SetRandom() - } - } - // commit to the values - commitments := make([]curve.G1Affine, nbPks) - for i := range commitments { - commitments[i], err = pks[i].Commit(toCommit[i]) - if err != nil { - panic(err) - } - } - // combination coefficient is randomly sampled by the verifier. NB! In non-interactive protocol use Fiat-Shamir! - var combinationCoeff fr.Element - combinationCoeff.SetRandom() - proof, err := BatchProve(pks, toCommit, combinationCoeff) - if err != nil { - panic(err) - } - // fold the commitments - foldedCommitment, err := new(curve.G1Affine).Fold(commitments, combinationCoeff, ecc.MultiExpConfig{NbTasks: 1}) - if err != nil { - panic(err) - } - // verify the proof - if err := vk.Verify(*foldedCommitment, proof); err != nil { - panic(err) - } - fmt.Println("verified") - - // Output: verified -} - -// This example shows how to batch verify multiple proofs using multiple -// verifying keys. -func ExampleBatchVerifyMultiVk() { - const nbPks = 3 - const nbElem = 4 - // create a proving key with independent basis elements - var buf [32]byte - basis := make([][]curve.G1Affine, nbPks) - for i := range basis { - basis[i] = make([]curve.G1Affine, nbElem) - for j := range basis[i] { - _, err := rand.Read(buf[:]) - if err != nil { - panic(err) - } - // we use hash-to-curve to avoid linear dependencies between basis elements - basis[i][j], err = curve.HashToG1(buf[:], []byte(fmt.Sprintf("basis %d", i))) - if err != nil { - panic(err) - } - } - } - // we create independent proving keys (different sigmas) with same G2 - // g2Point does not have to be generated in a trusted manner - _, _, _, g2Point := curve.Generators() - pks := make([]ProvingKey, nbPks) - vks := make([]VerifyingKey, nbPks) - for i := range basis { - pkss, vkss, err := Setup(basis[i:i+1], WithG2Point(g2Point)) - if err != nil { - panic(err) - } - pks[i] = pkss[0] - vks[i] = vkss - } - // generate random values to commit to - toCommit := make([][]fr.Element, nbPks) - for i := range toCommit { - toCommit[i] = make([]fr.Element, nbElem) - for j := range toCommit[i] { - toCommit[i][j].SetRandom() - } - } - // commit to the values - commitments := make([]curve.G1Affine, nbPks) - for i := range commitments { - var err error - commitments[i], err = pks[i].Commit(toCommit[i]) - if err != nil { - panic(err) - } - } - // prove the commitments - proofs := make([]curve.G1Affine, nbPks) - for i := range proofs { - var err error - proofs[i], err = pks[i].ProveKnowledge(toCommit[i]) - if err != nil { - panic(err) - } - } - // combination coefficient is randomly sampled by the verifier. NB! In non-interactive protocol use Fiat-Shamir! - var combinationCoeff fr.Element - combinationCoeff.SetRandom() - // batch verify the proofs - if err := BatchVerifyMultiVk(vks, commitments, proofs, combinationCoeff); err != nil { - panic(err) - } - - // alternatively, we can also provide the folded proof - foldedProof, err := new(curve.G1Affine).Fold(proofs, combinationCoeff, ecc.MultiExpConfig{NbTasks: 1}) - if err != nil { - panic(err) - } - if err := BatchVerifyMultiVk(vks, commitments, []curve.G1Affine{*foldedProof}, combinationCoeff); err != nil { - panic(err) - } - - fmt.Println("verified") - // Output: verified -} diff --git a/ecc/bw6-756/fr/pedersen/pedersen.go b/ecc/bw6-756/fr/pedersen/pedersen.go deleted file mode 100644 index 2142b9138b..0000000000 --- a/ecc/bw6-756/fr/pedersen/pedersen.go +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package pedersen - -import ( - "crypto/rand" - "errors" - "github.com/consensys/gnark-crypto/ecc" - curve "github.com/consensys/gnark-crypto/ecc/bw6-756" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "io" - "math/big" -) - -// ProvingKey for committing and proofs of knowledge -type ProvingKey struct { - Basis []curve.G1Affine - BasisExpSigma []curve.G1Affine // basisExpSigma[i] = Basis[i]^{σ} -} - -type VerifyingKey struct { - G curve.G2Affine - GSigma curve.G2Affine // GRootSigmaNeg = G^{-σ} -} - -func randomFrSizedBytes() ([]byte, error) { - res := make([]byte, fr.Bytes) - _, err := rand.Read(res) - return res, err -} - -type setupConfig struct { - g2Gen *curve.G2Affine -} - -// SetupOption allows to customize Pedersen vector commitment setup. -type SetupOption func(cfg *setupConfig) - -// WithG2Point allows to set the G2 generator for the Pedersen vector commitment -// setup. If this is not set, we sample a random G2 point. -func WithG2Point(g2 curve.G2Affine) SetupOption { - return func(cfg *setupConfig) { - cfg.g2Gen = &g2 - } -} - -// Setup generates the proving keys for Pedersen commitments over the given -// bases allowing for batch proving. The common verifying key can be used to -// verify the batched proof of knowledge. -// -// By default the G2 generator is sampled randomly. This can be overridden by -// providing a custom G2 generator using [WithG2Point] option. -// -// The input bases do not have to be of the same length for individual -// committing and proving. The elements in bases[i] should be linearly -// independent of each other. Otherwise the prover may be able to construct -// multiple valid openings for a commitment. -// -// NB! This is a trusted setup process. The randomness during the setup must be discarded. -// Failing to do so allows to create proofs without knowing the committed values. -func Setup(bases [][]curve.G1Affine, options ...SetupOption) (pk []ProvingKey, vk VerifyingKey, err error) { - var cfg setupConfig - for _, o := range options { - o(&cfg) - } - if cfg.g2Gen == nil { - if vk.G, err = curve.RandomOnG2(); err != nil { - return - } - } else { - vk.G = *cfg.g2Gen - } - - var modMinusOne big.Int - modMinusOne.Sub(fr.Modulus(), big.NewInt(1)) - var sigma *big.Int - if sigma, err = rand.Int(rand.Reader, &modMinusOne); err != nil { - return - } - sigma.Add(sigma, big.NewInt(1)) - - sigmaNeg := new(big.Int).Neg(sigma) - vk.GSigma.ScalarMultiplication(&vk.G, sigmaNeg) - - pk = make([]ProvingKey, len(bases)) - for i := range bases { - pk[i].BasisExpSigma = make([]curve.G1Affine, len(bases[i])) - for j := range bases[i] { - pk[i].BasisExpSigma[j].ScalarMultiplication(&bases[i][j], sigma) - } - pk[i].Basis = bases[i] - } - return -} - -// ProveKnowledge generates a proof of knowledge of a commitment to the given -// values over proving key's basis. -func (pk *ProvingKey) ProveKnowledge(values []fr.Element) (pok curve.G1Affine, err error) { - if len(values) != len(pk.Basis) { - err = errors.New("must have as many values as basis elements") - return - } - - // TODO @gbotrel this will spawn more than one task, see - // https://github.com/ConsenSys/gnark-crypto/issues/269 - config := ecc.MultiExpConfig{ - NbTasks: 1, // TODO Experiment - } - - _, err = pok.MultiExp(pk.BasisExpSigma, values, config) - return -} - -// Commit computes a commitment to the values over proving key's basis -func (pk *ProvingKey) Commit(values []fr.Element) (commitment curve.G1Affine, err error) { - - if len(values) != len(pk.Basis) { - err = errors.New("must have as many values as basis elements") - return - } - - // TODO @gbotrel this will spawn more than one task, see - // https://github.com/ConsenSys/gnark-crypto/issues/269 - config := ecc.MultiExpConfig{ - NbTasks: 1, - } - _, err = commitment.MultiExp(pk.Basis, values, config) - - return -} - -// BatchProve computes a single proof of knowledge for multiple commitments. The -// single PoK can be verified with a single call to [VerifyingKey.Verify] with -// folded commitments. The commitments can be folded into one using [curve.G1Affine.Fold]. -// -// The argument combinationCoeff is used as a linear combination coefficient to -// fold separate proofs into one. It must be the same for batch proving and when -// folding commitments. This means that in an interactive setting, it must be -// randomly generated by the verifier and sent to the prover. Otherwise, it must -// be generated via Fiat-Shamir. -func BatchProve(pk []ProvingKey, values [][]fr.Element, combinationCoeff fr.Element) (pok curve.G1Affine, err error) { - if len(pk) != len(values) { - err = errors.New("must have as many value vectors as bases") - return - } - - if len(pk) == 1 { // no need to fold - pok, err = pk[0].ProveKnowledge(values[0]) - return - } else if len(pk) == 0 { // nothing to do at all - return - } - - offset := 0 - for i := range pk { - if len(values[i]) != len(pk[i].Basis) { - err = errors.New("must have as many values as basis elements") - return - } - offset += len(values[i]) - } - - // prepare one amalgamated MSM - scaledValues := make([]fr.Element, offset) - basis := make([]curve.G1Affine, offset) - - copy(basis, pk[0].BasisExpSigma) // #nosec G602 false positive - copy(scaledValues, values[0]) // #nosec G602 false positive - - offset = len(values[0]) // #nosec G602 false positive - rI := combinationCoeff - for i := 1; i < len(pk); i++ { - copy(basis[offset:], pk[i].BasisExpSigma) - for j := range pk[i].Basis { - scaledValues[offset].Mul(&values[i][j], &rI) - offset++ - } - if i+1 < len(pk) { - rI.Mul(&rI, &combinationCoeff) - } - } - - // TODO @gbotrel this will spawn more than one task, see - // https://github.com/ConsenSys/gnark-crypto/issues/269 - config := ecc.MultiExpConfig{ - NbTasks: 1, - } - - _, err = pok.MultiExp(basis, scaledValues, config) - return -} - -// Verify checks if the proof of knowledge is valid for a given commitment. -func (vk *VerifyingKey) Verify(commitment curve.G1Affine, knowledgeProof curve.G1Affine) error { - - if !commitment.IsInSubGroup() || !knowledgeProof.IsInSubGroup() { - return errors.New("subgroup check failed") - } - - if isOne, err := curve.PairingCheck([]curve.G1Affine{commitment, knowledgeProof}, []curve.G2Affine{vk.GSigma, vk.G}); err != nil { - return err - } else if !isOne { - return errors.New("proof rejected") - } - return nil -} - -// BatchVerifyMultiVk verifies multiple separate proofs of knowledge using n+1 -// pairings instead of 2n pairings. -// -// The verifying keys may be from different setup ceremonies, but the G2 point -// must be the same. This can be enforced using [WithG2Point] option during -// setup. -// -// The argument combinationCoeff is used as a linear combination coefficient to -// fold separate proofs into one. This means that in an interactive setting, it -// must be randomly generated by the verifier and sent to the prover. Otherwise, -// it must be generated via Fiat-Shamir. -// -// The prover can fold the proofs using [curve.G1Affine.Fold] itself using the -// random challenge, providing the verifier only the folded proof. In this case -// the argument pok should contain only the single folded proof. -func BatchVerifyMultiVk(vk []VerifyingKey, commitments []curve.G1Affine, pok []curve.G1Affine, combinationCoeff fr.Element) error { - if len(commitments) != len(vk) { - return errors.New("commitments length mismatch") - } - // we use folded POK if provided - if len(vk) != len(pok) && len(pok) != 1 { - return errors.New("pok length mismatch") - } - for i := range commitments { - if !commitments[i].IsInSubGroup() { - return errors.New("commitment subgroup check failed") - } - if i != 0 && vk[i].G != vk[0].G { - return errors.New("parameter mismatch: G2 element") - } - } - for i := range pok { - if !pok[i].IsInSubGroup() { - return errors.New("pok subgroup check failed") - } - } - - pairingG1 := make([]curve.G1Affine, len(vk)+1) - pairingG2 := make([]curve.G2Affine, len(vk)+1) - r := combinationCoeff - pairingG1[0] = commitments[0] - var rI big.Int - for i := range vk { - pairingG2[i] = vk[i].GSigma - if i != 0 { - r.BigInt(&rI) - pairingG1[i].ScalarMultiplication(&commitments[i], &rI) - if i+1 != len(vk) { - r.Mul(&r, &combinationCoeff) - } - } - } - if foldedPok, err := new(curve.G1Affine).Fold(pok, combinationCoeff, ecc.MultiExpConfig{NbTasks: 1}); err != nil { - return err - } else { - pairingG1[len(vk)] = *foldedPok - } - pairingG2[len(vk)] = vk[0].G - - if isOne, err := curve.PairingCheck(pairingG1, pairingG2); err != nil { - return err - } else if !isOne { - return errors.New("proof rejected") - } - return nil -} - -// Marshal - -func (pk *ProvingKey) writeTo(enc *curve.Encoder) (int64, error) { - if err := enc.Encode(pk.Basis); err != nil { - return enc.BytesWritten(), err - } - - err := enc.Encode(pk.BasisExpSigma) - - return enc.BytesWritten(), err -} - -func (pk *ProvingKey) WriteTo(w io.Writer) (int64, error) { - return pk.writeTo(curve.NewEncoder(w)) -} - -func (pk *ProvingKey) WriteRawTo(w io.Writer) (int64, error) { - return pk.writeTo(curve.NewEncoder(w, curve.RawEncoding())) -} - -func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { - dec := curve.NewDecoder(r) - - if err := dec.Decode(&pk.Basis); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&pk.BasisExpSigma); err != nil { - return dec.BytesRead(), err - } - - if len(pk.Basis) != len(pk.BasisExpSigma) { - return dec.BytesRead(), errors.New("commitment/proof length mismatch") - } - - return dec.BytesRead(), nil -} - -func (vk *VerifyingKey) WriteTo(w io.Writer) (int64, error) { - return vk.writeTo(curve.NewEncoder(w)) -} - -func (vk *VerifyingKey) WriteRawTo(w io.Writer) (int64, error) { - return vk.writeTo(curve.NewEncoder(w, curve.RawEncoding())) -} - -func (vk *VerifyingKey) writeTo(enc *curve.Encoder) (int64, error) { - var err error - - if err = enc.Encode(&vk.G); err != nil { - return enc.BytesWritten(), err - } - err = enc.Encode(&vk.GSigma) - return enc.BytesWritten(), err -} - -func (vk *VerifyingKey) ReadFrom(r io.Reader) (int64, error) { - return vk.readFrom(r) -} - -func (vk *VerifyingKey) UnsafeReadFrom(r io.Reader) (int64, error) { - return vk.readFrom(r, curve.NoSubgroupChecks()) -} - -func (vk *VerifyingKey) readFrom(r io.Reader, decOptions ...func(*curve.Decoder)) (int64, error) { - dec := curve.NewDecoder(r, decOptions...) - var err error - - if err = dec.Decode(&vk.G); err != nil { - return dec.BytesRead(), err - } - err = dec.Decode(&vk.GSigma) - return dec.BytesRead(), err -} diff --git a/ecc/bw6-756/fr/pedersen/pedersen_test.go b/ecc/bw6-756/fr/pedersen/pedersen_test.go deleted file mode 100644 index a1af21e2f1..0000000000 --- a/ecc/bw6-756/fr/pedersen/pedersen_test.go +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package pedersen - -import ( - "fmt" - "testing" - - "github.com/consensys/gnark-crypto/ecc" - curve "github.com/consensys/gnark-crypto/ecc/bw6-756" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/utils/testutils" - "github.com/stretchr/testify/assert" -) - -func interfaceSliceToFrSlice(t *testing.T, values ...interface{}) []fr.Element { - res := make([]fr.Element, len(values)) - for i, v := range values { - _, err := res[i].SetInterface(v) - assert.NoError(t, err) - } - return res -} - -func randomFrSlice(t *testing.T, size int) []interface{} { - res := make([]interface{}, size) - var err error - for i := range res { - var v fr.Element - res[i], err = v.SetRandom() - assert.NoError(t, err) - } - return res -} - -func randomOnG1() (curve.G1Affine, error) { // TODO: Add to G1.go? - if gBytes, err := randomFrSizedBytes(); err != nil { - return curve.G1Affine{}, err - } else { - return curve.HashToG1(gBytes, []byte("random on g1")) - } -} - -func randomG1Slice(t *testing.T, size int) []curve.G1Affine { - res := make([]curve.G1Affine, size) - for i := range res { - var err error - res[i], err = randomOnG1() - assert.NoError(t, err) - } - return res -} - -func testCommit(t *testing.T, values ...interface{}) { - - basis := randomG1Slice(t, len(values)) - - var ( - pk []ProvingKey - vk VerifyingKey - err error - commitment, pok curve.G1Affine - ) - valuesFr := interfaceSliceToFrSlice(t, values...) - - pk, vk, err = Setup([][]curve.G1Affine{basis}) - assert.NoError(t, err) - commitment, err = pk[0].Commit(valuesFr) - assert.NoError(t, err) - pok, err = pk[0].ProveKnowledge(valuesFr) - assert.NoError(t, err) - assert.NoError(t, vk.Verify(commitment, pok)) - - pok.Neg(&pok) - assert.NotNil(t, vk.Verify(commitment, pok)) -} - -func TestFoldProofs(t *testing.T) { - - values := [][]fr.Element{ - interfaceSliceToFrSlice(t, randomFrSlice(t, 5)...), - interfaceSliceToFrSlice(t, randomFrSlice(t, 5)...), - interfaceSliceToFrSlice(t, randomFrSlice(t, 5)...), - } - - bases := make([][]curve.G1Affine, len(values)) - for i := range bases { - bases[i] = randomG1Slice(t, len(values[i])) - } - - pk, vk, err := Setup(bases) - assert.NoError(t, err) - - commitments := make([]curve.G1Affine, len(values)) - for i := range values { - commitments[i], err = pk[i].Commit(values[i]) - assert.NoError(t, err) - } - - hashes, err := fr.Hash([]byte("test"), []byte("pedersen"), 1) - assert.NoError(t, err) - - t.Run("folding with zeros", func(t *testing.T) { - pokFolded, err := BatchProve(pk[:2], [][]fr.Element{ - values[0], - make([]fr.Element, len(values[1])), - }, hashes[0]) - assert.NoError(t, err) - var pok curve.G1Affine - pok, err = pk[0].ProveKnowledge(values[0]) - assert.NoError(t, err) - assert.Equal(t, pok, pokFolded) - }) - - t.Run("run empty", func(t *testing.T) { - var foldedCommitment curve.G1Affine - pok, err := BatchProve([]ProvingKey{}, [][]fr.Element{}, hashes[0]) - assert.NoError(t, err) - - _, err = foldedCommitment.Fold([]curve.G1Affine{}, hashes[0], ecc.MultiExpConfig{NbTasks: 1}) - assert.NoError(t, err) - assert.NoError(t, vk.Verify(foldedCommitment, pok)) - }) - - run := func(values [][]fr.Element) func(t *testing.T) { - return func(t *testing.T) { - - var foldedCommitment curve.G1Affine - pok, err := BatchProve(pk[:len(values)], values, hashes[0]) - assert.NoError(t, err) - - _, err = foldedCommitment.Fold(commitments[:len(values)], hashes[0], ecc.MultiExpConfig{NbTasks: 1}) - assert.NoError(t, err) - assert.NoError(t, vk.Verify(foldedCommitment, pok)) - - pok.Neg(&pok) - assert.NotNil(t, vk.Verify(foldedCommitment, pok)) - } - } - - for i := range values { - t.Run(fmt.Sprintf("folding %d", i+1), run(values[:i+1])) - } -} - -func TestCommitToOne(t *testing.T) { - testCommit(t, 1) -} - -func TestCommitSingle(t *testing.T) { - testCommit(t, randomFrSlice(t, 1)...) -} - -func TestCommitFiveElements(t *testing.T) { - testCommit(t, randomFrSlice(t, 5)...) -} - -func TestMarshal(t *testing.T) { - var pk ProvingKey - pk.BasisExpSigma = randomG1Slice(t, 5) - pk.Basis = randomG1Slice(t, 5) - - var ( - vk VerifyingKey - err error - ) - vk.G, err = curve.RandomOnG2() - assert.NoError(t, err) - vk.GSigma, err = curve.RandomOnG2() - assert.NoError(t, err) - - t.Run("ProvingKey -> Bytes -> ProvingKey must remain identical.", testutils.SerializationRoundTrip(&pk)) - t.Run("ProvingKey -> Bytes (raw) -> ProvingKey must remain identical.", testutils.SerializationRoundTripRaw(&pk)) - t.Run("VerifyingKey -> Bytes -> VerifyingKey must remain identical.", testutils.SerializationRoundTrip(&vk)) - t.Run("VerifyingKey -> Bytes (raw) -> ProvingKey must remain identical.", testutils.SerializationRoundTripRaw(&vk)) -} - -func TestSemiFoldProofs(t *testing.T) { - const ( - commitmentLength = 5 - nbCommitments = 5 - ) - g, err := curve.RandomOnG2() - assert.NoError(t, err) - - basis := randomG1Slice(t, commitmentLength*nbCommitments) - - vk, pk := make([]VerifyingKey, nbCommitments), make([]ProvingKey, nbCommitments) - for i := range pk { - var pk0 []ProvingKey - pk0, vk[i], err = Setup([][]curve.G1Affine{basis[i*commitmentLength : (i+1)*commitmentLength]}, WithG2Point(g)) - assert.NoError(t, err) - pk[i] = pk0[0] - } - - values := make([][]fr.Element, nbCommitments) - for i := range values { - values[i] = make([]fr.Element, commitmentLength) - for j := range values[i] { - _, err = values[i][j].SetRandom() - assert.NoError(t, err) - } - } - - commitments := make([]curve.G1Affine, nbCommitments) - proofs := make([]curve.G1Affine, nbCommitments) - for i := range commitments { - commitments[i], err = pk[i].Commit(values[i]) - assert.NoError(t, err) - proofs[i], err = pk[i].ProveKnowledge(values[i]) - assert.NoError(t, err) - } - - var challenge fr.Element - _, err = challenge.SetRandom() - assert.NoError(t, err) - - assert.NoError(t, BatchVerifyMultiVk(vk, commitments, proofs, challenge)) - - // send folded proof - proof, err := new(curve.G1Affine).Fold(proofs, challenge, ecc.MultiExpConfig{NbTasks: 1}) - assert.NoError(t, err) - assert.NoError(t, BatchVerifyMultiVk(vk, commitments, []curve.G1Affine{*proof}, challenge)) -} diff --git a/ecc/bw6-756/fr/permutation/doc.go b/ecc/bw6-756/fr/permutation/doc.go deleted file mode 100644 index 7ef21ffb9e..0000000000 --- a/ecc/bw6-756/fr/permutation/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package permutation provides an API to build permutation proofs. -package permutation diff --git a/ecc/bw6-756/fr/permutation/permutation.go b/ecc/bw6-756/fr/permutation/permutation.go deleted file mode 100644 index 416a3742bb..0000000000 --- a/ecc/bw6-756/fr/permutation/permutation.go +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package permutation - -import ( - "crypto/sha256" - "errors" - "math/big" - "math/bits" - - "github.com/consensys/gnark-crypto/ecc/bw6-756" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/fft" - "github.com/consensys/gnark-crypto/ecc/bw6-756/kzg" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -var ( - ErrIncompatibleSize = errors.New("t1 and t2 should be of the same size") - ErrSize = errors.New("t1 and t2 should be of size a power of 2") - ErrPermutationProof = errors.New("permutation proof verification failed") - ErrGenerator = errors.New("wrong generator") -) - -// Proof proof that the commitments of t1 and t2 come from -// the same vector but permuted. -type Proof struct { - - // size of the polynomials - size int - - // generator of the fft domain, used for shifting the evaluation point - g fr.Element - - // commitments of t1 & t2, the permuted vectors, and z, the accumulation - // polynomial - t1, t2, z kzg.Digest - - // commitment to the quotient polynomial - q kzg.Digest - - // opening proofs of t1, t2, z, q (in that order) - batchedProof kzg.BatchOpeningProof - - // shifted opening proof of z - shiftedProof kzg.OpeningProof -} - -// evaluateAccumulationPolynomialBitReversed returns the accumulation polynomial in Lagrange basis. -func evaluateAccumulationPolynomialBitReversed(lt1, lt2 []fr.Element, epsilon fr.Element) []fr.Element { - - s := len(lt1) - z := make([]fr.Element, s) - d := make([]fr.Element, s) - z[0].SetOne() - d[0].SetOne() - nn := uint64(64 - bits.TrailingZeros64(uint64(s))) - var t fr.Element - for i := 0; i < s-1; i++ { - _i := int(bits.Reverse64(uint64(i)) >> nn) - _ii := int(bits.Reverse64(uint64((i+1)%s)) >> nn) - z[_ii].Mul(&z[_i], t.Sub(&epsilon, <1[i])) - d[i+1].Mul(&d[i], t.Sub(&epsilon, <2[i])) - } - d = fr.BatchInvert(d) - for i := 0; i < s-1; i++ { - _ii := int(bits.Reverse64(uint64((i+1)%s)) >> nn) - z[_ii].Mul(&z[_ii], &d[i+1]) - } - - return z -} - -// evaluateFirstPartNumReverse computes lt2*z(gx) - lt1*z -func evaluateFirstPartNumReverse(lt1, lt2, lz []fr.Element, epsilon fr.Element) []fr.Element { - - s := len(lt1) - res := make([]fr.Element, s) - var a, b fr.Element - nn := uint64(64 - bits.TrailingZeros64(uint64(s))) - for i := 0; i < s; i++ { - _i := int(bits.Reverse64(uint64(i)) >> nn) - _ii := int(bits.Reverse64(uint64((i+1)%s)) >> nn) - a.Sub(&epsilon, <2[_i]) - a.Mul(&lz[_ii], &a) - b.Sub(&epsilon, <1[_i]) - b.Mul(&lz[_i], &b) - res[_i].Sub(&a, &b) - } - return res -} - -// evaluateSecondPartNumReverse computes L0 * (z-1) -func evaluateSecondPartNumReverse(lz []fr.Element, d *fft.Domain) []fr.Element { - - var tn, o, g fr.Element - o.SetOne() - tn.Exp(d.FrMultiplicativeGen, big.NewInt(int64(d.Cardinality))). - Sub(&tn, &o) - s := len(lz) - u := make([]fr.Element, s) - g.Set(&d.FrMultiplicativeGen) - for i := 0; i < s; i++ { - u[i].Sub(&g, &o) - g.Mul(&g, &d.Generator) - } - u = fr.BatchInvert(u) - res := make([]fr.Element, s) - nn := uint64(64 - bits.TrailingZeros64(uint64(s))) - for i := 0; i < s; i++ { - _i := int(bits.Reverse64(uint64(i)) >> nn) - res[_i].Sub(&lz[_i], &o). - Mul(&res[_i], &u[i]). - Mul(&res[_i], &tn) - } - return res -} - -// Prove generates a proof that t1 and t2 are the same but permuted. -// The size of t1 and t2 should be the same and a power of 2. -func Prove(pk kzg.ProvingKey, t1, t2 []fr.Element) (Proof, error) { - - // res - var proof Proof - var err error - - // size checking - if len(t1) != len(t2) { - return proof, ErrIncompatibleSize - } - - // create the domains - d := fft.NewDomain(uint64(len(t1))) - if d.Cardinality != uint64(len(t1)) { - return proof, ErrSize - } - s := int(d.Cardinality) - proof.size = s - proof.g.Set(&d.Generator) - - // hash function for Fiat Shamir - hFunc := sha256.New() - - // transcript to derive the challenge - fs := fiatshamir.NewTranscript(hFunc, "epsilon", "omega", "eta") - - // commit t1, t2 - ct1 := make([]fr.Element, s) - ct2 := make([]fr.Element, s) - copy(ct1, t1) - copy(ct2, t2) - d.FFTInverse(ct1, fft.DIF) - d.FFTInverse(ct2, fft.DIF) - fft.BitReverse(ct1) - fft.BitReverse(ct2) - proof.t1, err = kzg.Commit(ct1, pk) - if err != nil { - return proof, err - } - proof.t2, err = kzg.Commit(ct2, pk) - if err != nil { - return proof, err - } - - // derive challenge for z - epsilon, err := deriveRandomness(fs, "epsilon", &proof.t1, &proof.t2) - if err != nil { - return proof, err - } - - // compute Z and commit it - cz := evaluateAccumulationPolynomialBitReversed(t1, t2, epsilon) - d.FFTInverse(cz, fft.DIT) - proof.z, err = kzg.Commit(cz, pk) - if err != nil { - return proof, err - } - lz := make([]fr.Element, s) - copy(lz, cz) - d.FFT(lz, fft.DIF, fft.OnCoset()) - - // compute the first part of the numerator - lt1 := make([]fr.Element, s) - lt2 := make([]fr.Element, s) - copy(lt1, ct1) - copy(lt2, ct2) - d.FFT(lt1, fft.DIF, fft.OnCoset()) - d.FFT(lt2, fft.DIF, fft.OnCoset()) - lsNumFirstPart := evaluateFirstPartNumReverse(lt1, lt2, lz, epsilon) - - // compute second part of the numerator - lsNum := evaluateSecondPartNumReverse(lz, d) - - // derive challenge used for the folding - omega, err := deriveRandomness(fs, "omega", &proof.z) - if err != nil { - return proof, err - } - - // fold the numerator and divide it by x^n-1 - var t, one fr.Element - one.SetOne() - t.Exp(d.FrMultiplicativeGen, big.NewInt(int64(d.Cardinality))).Sub(&t, &one).Inverse(&t) - for i := 0; i < s; i++ { - lsNum[i].Mul(&omega, &lsNum[i]). - Add(&lsNum[i], &lsNumFirstPart[i]). - Mul(&lsNum[i], &t) - } - - // get the quotient and commit it - d.FFTInverse(lsNum, fft.DIT, fft.OnCoset()) - proof.q, err = kzg.Commit(lsNum, pk) - if err != nil { - return proof, err - } - - // derive the evaluation challenge - eta, err := deriveRandomness(fs, "eta", &proof.q) - if err != nil { - return proof, err - } - - // compute the opening proofs - proof.batchedProof, err = kzg.BatchOpenSinglePoint( - [][]fr.Element{ - ct1, - ct2, - cz, - lsNum, - }, - []kzg.Digest{ - proof.t1, - proof.t2, - proof.z, - proof.q, - }, - eta, - hFunc, - pk, - ) - if err != nil { - return proof, err - } - - var shiftedEta fr.Element - shiftedEta.Mul(&eta, &d.Generator) - proof.shiftedProof, err = kzg.Open( - cz, - shiftedEta, - pk, - ) - if err != nil { - return proof, err - } - - // done - return proof, nil - -} - -// Verify verifies a permutation proof. -func Verify(vk kzg.VerifyingKey, proof Proof) error { - - // hash function that is used for Fiat Shamir - hFunc := sha256.New() - - // transcript to derive the challenge - fs := fiatshamir.NewTranscript(hFunc, "epsilon", "omega", "eta") - - // derive the challenges - epsilon, err := deriveRandomness(fs, "epsilon", &proof.t1, &proof.t2) - if err != nil { - return err - } - - omega, err := deriveRandomness(fs, "omega", &proof.z) - if err != nil { - return err - } - - eta, err := deriveRandomness(fs, "eta", &proof.q) - if err != nil { - return err - } - - // check the relation - bs := big.NewInt(int64(proof.size)) - var l0, a, b, one, rhs, lhs fr.Element - one.SetOne() - rhs.Exp(eta, bs). - Sub(&rhs, &one) - a.Sub(&eta, &one) - l0.Div(&rhs, &a) - rhs.Mul(&rhs, &proof.batchedProof.ClaimedValues[3]) - a.Sub(&epsilon, &proof.batchedProof.ClaimedValues[1]). - Mul(&a, &proof.shiftedProof.ClaimedValue) - b.Sub(&epsilon, &proof.batchedProof.ClaimedValues[0]). - Mul(&b, &proof.batchedProof.ClaimedValues[2]) - lhs.Sub(&a, &b) - a.Sub(&proof.batchedProof.ClaimedValues[2], &one). - Mul(&a, &l0). - Mul(&a, &omega) - lhs.Add(&a, &lhs) - if !lhs.Equal(&rhs) { - return ErrPermutationProof - } - - // check the opening proofs - err = kzg.BatchVerifySinglePoint( - []kzg.Digest{ - proof.t1, - proof.t2, - proof.z, - proof.q, - }, - &proof.batchedProof, - eta, - hFunc, - vk, - ) - if err != nil { - return err - } - - var shiftedEta fr.Element - shiftedEta.Mul(&eta, &proof.g) - err = kzg.Verify(&proof.z, &proof.shiftedProof, shiftedEta, vk) - if err != nil { - return err - } - - // check the generator is correct - var checkOrder fr.Element - checkOrder.Exp(proof.g, big.NewInt(int64(proof.size/2))) - if checkOrder.Equal(&one) { - return ErrGenerator - } - checkOrder.Square(&checkOrder) - if !checkOrder.Equal(&one) { - return ErrGenerator - } - - return nil -} - -// TODO put that in fiat-shamir package -func deriveRandomness(fs *fiatshamir.Transcript, challenge string, points ...*bw6756.G1Affine) (fr.Element, error) { - - var buf [bw6756.SizeOfG1AffineUncompressed]byte - var r fr.Element - - for _, p := range points { - buf = p.RawBytes() - if err := fs.Bind(challenge, buf[:]); err != nil { - return r, err - } - } - - b, err := fs.ComputeChallenge(challenge) - if err != nil { - return r, err - } - r.SetBytes(b) - return r, nil -} diff --git a/ecc/bw6-756/fr/permutation/permutation_test.go b/ecc/bw6-756/fr/permutation/permutation_test.go deleted file mode 100644 index 20f09d85a3..0000000000 --- a/ecc/bw6-756/fr/permutation/permutation_test.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package permutation - -import ( - "math/big" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/kzg" -) - -func TestProof(t *testing.T) { - - kzgSrs, err := kzg.NewSRS(64, big.NewInt(13)) - assert.NoError(t, err) - - a := make([]fr.Element, 8) - b := make([]fr.Element, 8) - - for i := 0; i < 8; i++ { - a[i].SetUint64(uint64(4*i + 1)) - } - for i := 0; i < 8; i++ { - b[i].Set(&a[(5*i)%8]) - } - - // correct proof - { - proof, err := Prove(kzgSrs.Pk, a, b) - if err != nil { - t.Fatal(err) - } - - err = Verify(kzgSrs.Vk, proof) - if err != nil { - t.Fatal(err) - } - } - - // wrong proof - { - a[0].SetRandom() - proof, err := Prove(kzgSrs.Pk, a, b) - if err != nil { - t.Fatal(err) - } - - err = Verify(kzgSrs.Vk, proof) - if err == nil { - t.Fatal(err) - } - } - -} - -func BenchmarkProver(b *testing.B) { - - srsSize := 1 << 15 - polySize := 1 << 14 - - kzgSrs, _ := kzg.NewSRS(uint64(srsSize), big.NewInt(13)) - a := make([]fr.Element, polySize) - c := make([]fr.Element, polySize) - - for i := 0; i < polySize; i++ { - a[i].SetUint64(uint64(i)) - } - for i := 0; i < polySize; i++ { - c[i].Set(&a[(5*i)%(polySize)]) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - Prove(kzgSrs.Pk, a, c) - } - -} diff --git a/ecc/bw6-756/fr/plookup/doc.go b/ecc/bw6-756/fr/plookup/doc.go deleted file mode 100644 index b3af019687..0000000000 --- a/ecc/bw6-756/fr/plookup/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package plookup provides an API to build plookup proofs. -package plookup diff --git a/ecc/bw6-756/fr/plookup/plookup_test.go b/ecc/bw6-756/fr/plookup/plookup_test.go deleted file mode 100644 index 2bf9c2e29a..0000000000 --- a/ecc/bw6-756/fr/plookup/plookup_test.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package plookup - -import ( - "math/big" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/kzg" -) - -func TestLookupVector(t *testing.T) { - - lookupVector := make(fr.Vector, 8) - fvector := make(fr.Vector, 7) - for i := 0; i < 8; i++ { - lookupVector[i].SetUint64(uint64(2 * i)) - } - for i := 0; i < 7; i++ { - fvector[i].Set(&lookupVector[(4*i+1)%8]) - } - - kzgSrs, err := kzg.NewSRS(64, big.NewInt(13)) - if err != nil { - t.Fatal(err) - } - - // correct proof vector - { - proof, err := ProveLookupVector(kzgSrs.Pk, fvector, lookupVector) - if err != nil { - t.Fatal(err) - } - - err = VerifyLookupVector(kzgSrs.Vk, proof) - if err != nil { - t.Fatal(err) - } - } - - // wrong proofs vector - { - fvector[0].SetRandom() - - proof, err := ProveLookupVector(kzgSrs.Pk, fvector, lookupVector) - if err != nil { - t.Fatal(err) - } - - err = VerifyLookupVector(kzgSrs.Vk, proof) - if err == nil { - t.Fatal(err) - } - } - -} - -func TestLookupTable(t *testing.T) { - - kzgSrs, err := kzg.NewSRS(64, big.NewInt(13)) - if err != nil { - t.Fatal(err) - } - - lookupTable := make([]fr.Vector, 3) - fTable := make([]fr.Vector, 3) - for i := 0; i < 3; i++ { - lookupTable[i] = make(fr.Vector, 8) - fTable[i] = make(fr.Vector, 7) - for j := 0; j < 8; j++ { - lookupTable[i][j].SetUint64(uint64(2*i + j)) - } - for j := 0; j < 7; j++ { - fTable[i][j].Set(&lookupTable[i][(4*j+1)%8]) - } - } - - // correct proof - { - proof, err := ProveLookupTables(kzgSrs.Pk, fTable, lookupTable) - if err != nil { - t.Fatal(err) - } - - err = VerifyLookupTables(kzgSrs.Vk, proof) - if err != nil { - t.Fatal(err) - } - } - - // wrong proof - { - fTable[0][0].SetRandom() - proof, err := ProveLookupTables(kzgSrs.Pk, fTable, lookupTable) - if err != nil { - t.Fatal(err) - } - - err = VerifyLookupTables(kzgSrs.Vk, proof) - if err == nil { - t.Fatal(err) - } - } - -} - -func BenchmarkPlookup(b *testing.B) { - - srsSize := 1 << 15 - polySize := 1 << 14 - - kzgSrs, _ := kzg.NewSRS(uint64(srsSize), big.NewInt(13)) - a := make(fr.Vector, polySize) - c := make(fr.Vector, polySize) - - for i := 0; i < 1<<14; i++ { - a[i].SetUint64(uint64(i)) - c[i].SetUint64(uint64((8 * i) % polySize)) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - ProveLookupVector(kzgSrs.Pk, a, c) - } -} diff --git a/ecc/bw6-756/fr/plookup/table.go b/ecc/bw6-756/fr/plookup/table.go deleted file mode 100644 index e2f04587dc..0000000000 --- a/ecc/bw6-756/fr/plookup/table.go +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package plookup - -import ( - "crypto/sha256" - "errors" - "math/big" - "sort" - - bw6756 "github.com/consensys/gnark-crypto/ecc/bw6-756" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/fft" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/permutation" - "github.com/consensys/gnark-crypto/ecc/bw6-756/kzg" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -var ( - ErrIncompatibleSize = errors.New("the tables in f and t are not of the same size") - ErrFoldedCommitment = errors.New("the folded commitment is malformed") - ErrNumberDigests = errors.New("proof.ts and proof.fs are not of the same length") -) - -// ProofLookupTables proofs that a list of tables -type ProofLookupTables struct { - - // commitments to the rows f - fs []kzg.Digest - - // commitments to the rows of t - ts []kzg.Digest - - // lookup proof for the f and t folded - foldedProof ProofLookupVector - - // proof that the ts folded correspond to t in the folded proof - permutationProof permutation.Proof -} - -// ProveLookupTables generates a proof that f, seen as a multi dimensional table, -// consists of vectors that are in t. In other words for each i, f[:][i] must be one -// of the t[:][j]. -// -// For instance, if t is the truth table of the XOR function, t will be populated such -// that t[:][i] contains the i-th entry of the truth table, so t[0][i] XOR t[1][i] = t[2][i]. -// -// The fr.Vector in f and t are supposed to be of the same size constant size. -func ProveLookupTables(pk kzg.ProvingKey, f, t []fr.Vector) (ProofLookupTables, error) { - - // res - proof := ProofLookupTables{} - var err error - - // hash function used for Fiat Shamir - hFunc := sha256.New() - - // transcript to derive the challenge - fs := fiatshamir.NewTranscript(hFunc, "lambda") - - // check the sizes - if len(f) != len(t) { - return proof, ErrIncompatibleSize - } - s := len(f[0]) - for i := 1; i < len(f); i++ { - if len(f[i]) != s { - return proof, ErrIncompatibleSize - } - } - s = len(t[0]) - for i := 1; i < len(t); i++ { - if len(t[i]) != s { - return proof, ErrIncompatibleSize - } - } - - // commit to the tables in f and t - nbRows := len(t) - proof.fs = make([]kzg.Digest, nbRows) - proof.ts = make([]kzg.Digest, nbRows) - _nbColumns := len(f[0]) + 1 - if _nbColumns < len(t[0]) { - _nbColumns = len(t[0]) - } - d := fft.NewDomain(uint64(_nbColumns)) - nbColumns := d.Cardinality - lfs := make([][]fr.Element, nbRows) - cfs := make([][]fr.Element, nbRows) - lts := make([][]fr.Element, nbRows) - cts := make([][]fr.Element, nbRows) - - for i := 0; i < nbRows; i++ { - - cfs[i] = make([]fr.Element, nbColumns) - lfs[i] = make([]fr.Element, nbColumns) - copy(cfs[i], f[i]) - copy(lfs[i], f[i]) - for j := len(f[i]); j < int(nbColumns); j++ { - cfs[i][j] = f[i][len(f[i])-1] - lfs[i][j] = f[i][len(f[i])-1] - } - d.FFTInverse(cfs[i], fft.DIF) - fft.BitReverse(cfs[i]) - proof.fs[i], err = kzg.Commit(cfs[i], pk) - if err != nil { - return proof, err - } - - cts[i] = make([]fr.Element, nbColumns) - lts[i] = make([]fr.Element, nbColumns) - copy(cts[i], t[i]) - copy(lts[i], t[i]) - for j := len(t[i]); j < int(d.Cardinality); j++ { - cts[i][j] = t[i][len(t[i])-1] - lts[i][j] = t[i][len(t[i])-1] - } - d.FFTInverse(cts[i], fft.DIF) - fft.BitReverse(cts[i]) - proof.ts[i], err = kzg.Commit(cts[i], pk) - if err != nil { - return proof, err - } - } - - // fold f and t - comms := make([]*kzg.Digest, 2*nbRows) - for i := 0; i < nbRows; i++ { - comms[i] = new(kzg.Digest) - comms[i].Set(&proof.fs[i]) - comms[nbRows+i] = new(kzg.Digest) - comms[nbRows+i].Set(&proof.ts[i]) - } - lambda, err := deriveRandomness(fs, "lambda", comms...) - if err != nil { - return proof, err - } - foldedf := make(fr.Vector, nbColumns) - foldedt := make(fr.Vector, nbColumns) - for i := 0; i < int(nbColumns); i++ { - for j := nbRows - 1; j >= 0; j-- { - foldedf[i].Mul(&foldedf[i], &lambda). - Add(&foldedf[i], &lfs[j][i]) - foldedt[i].Mul(&foldedt[i], &lambda). - Add(&foldedt[i], <s[j][i]) - } - } - - // generate a proof of permutation of the foldedt and sort(foldedt) - foldedtSorted := make(fr.Vector, nbColumns) - copy(foldedtSorted, foldedt) - sort.Sort(foldedtSorted) - proof.permutationProof, err = permutation.Prove(pk, foldedt, foldedtSorted) - if err != nil { - return proof, err - } - - // call plookupVector, on foldedf[:len(foldedf)-1] to ensure that the domain size - // in ProveLookupVector is the same as d's - proof.foldedProof, err = ProveLookupVector(pk, foldedf[:len(foldedf)-1], foldedt) - - return proof, err -} - -// VerifyLookupTables verifies that a ProofLookupTables proof is correct. -func VerifyLookupTables(vk kzg.VerifyingKey, proof ProofLookupTables) error { - - // hash function used for Fiat Shamir - hFunc := sha256.New() - - // transcript to derive the challenge - fs := fiatshamir.NewTranscript(hFunc, "lambda") - - // check that the number of digests is the same - if len(proof.fs) != len(proof.ts) { - return ErrNumberDigests - } - - // fold the commitments fs and ts - nbRows := len(proof.fs) - comms := make([]*kzg.Digest, 2*nbRows) - for i := 0; i < nbRows; i++ { - comms[i] = &proof.fs[i] - comms[i+nbRows] = &proof.ts[i] - } - lambda, err := deriveRandomness(fs, "lambda", comms...) - if err != nil { - return err - } - - // fold the commitments of the rows of t and f - var comf, comt kzg.Digest - comf.Set(&proof.fs[nbRows-1]) - comt.Set(&proof.ts[nbRows-1]) - var blambda big.Int - lambda.BigInt(&blambda) - for i := nbRows - 2; i >= 0; i-- { - comf.ScalarMultiplication(&comf, &blambda). - Add(&comf, &proof.fs[i]) - comt.ScalarMultiplication(&comt, &blambda). - Add(&comt, &proof.ts[i]) - } - - // check that the folded commitment of the fs correspond to foldedProof.f - if !comf.Equal(&proof.foldedProof.f) { - return ErrFoldedCommitment - } - - // check that the folded commitment of the ts is a permutation of proof.FoldedProof.t - err = permutation.Verify(vk, proof.permutationProof) - if err != nil { - return err - } - - // verify the inner proof - return VerifyLookupVector(vk, proof.foldedProof) -} - -// TODO put that in fiat-shamir package -func deriveRandomness(fs *fiatshamir.Transcript, challenge string, points ...*bw6756.G1Affine) (fr.Element, error) { - - var buf [bw6756.SizeOfG1AffineUncompressed]byte - var r fr.Element - - for _, p := range points { - buf = p.RawBytes() - if err := fs.Bind(challenge, buf[:]); err != nil { - return r, err - } - } - - b, err := fs.ComputeChallenge(challenge) - if err != nil { - return r, err - } - r.SetBytes(b) - return r, nil -} diff --git a/ecc/bw6-756/fr/plookup/vector.go b/ecc/bw6-756/fr/plookup/vector.go deleted file mode 100644 index fd1b5ff61a..0000000000 --- a/ecc/bw6-756/fr/plookup/vector.go +++ /dev/null @@ -1,717 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package plookup - -import ( - "crypto/sha256" - "errors" - "math/big" - "math/bits" - "sort" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/fft" - "github.com/consensys/gnark-crypto/ecc/bw6-756/kzg" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" -) - -var ( - ErrNotInTable = errors.New("some value in the vector is not in the lookup table") - ErrPlookupVerification = errors.New("plookup verification failed") - ErrGenerator = errors.New("wrong generator") -) - -// Proof Plookup proof, containing opening proofs -type ProofLookupVector struct { - - // size of the system - size uint64 - - // generator of the fft domain, used for shifting the evaluation point - g fr.Element - - // Commitments to h1, h2, t, z, f, h - h1, h2, t, z, f, h kzg.Digest - - // Batch opening proof of h1, h2, z, t - BatchedProof kzg.BatchOpeningProof - - // Batch opening proof of h1, h2, z shifted by g - BatchedProofShifted kzg.BatchOpeningProof -} - -// evaluateAccumulationPolynomial computes Z, in Lagrange basis. Z is the accumulation of the partial -// ratios of 2 fully split polynomials (cf https://eprint.iacr.org/2020/315.pdf) -// * lf is the list of values that should be in lt -// * lt is the lookup table -// * lh1, lh2 is lf sorted by lt split in 2 overlapping slices -// * beta, gamma are challenges (Schwartz-zippel: they are the random evaluations point) -func evaluateAccumulationPolynomial(lf, lt, lh1, lh2 []fr.Element, beta, gamma fr.Element) []fr.Element { - - z := make([]fr.Element, len(lt)) - - n := len(lt) - d := make([]fr.Element, n-1) - var u, c fr.Element - c.SetOne(). - Add(&c, &beta). - Mul(&c, &gamma) - for i := 0; i < n-1; i++ { - - d[i].Mul(&beta, &lh1[i+1]). - Add(&d[i], &lh1[i]). - Add(&d[i], &c) - - u.Mul(&beta, &lh2[i+1]). - Add(&u, &lh2[i]). - Add(&u, &c) - - d[i].Mul(&d[i], &u) - } - d = fr.BatchInvert(d) - - z[0].SetOne() - var a, b, e fr.Element - e.SetOne().Add(&e, &beta) - for i := 0; i < n-1; i++ { - - a.Add(&gamma, &lf[i]) - - b.Mul(&beta, <[i+1]). - Add(&b, <[i]). - Add(&b, &c) - - a.Mul(&a, &b). - Mul(&a, &e) - - z[i+1].Mul(&z[i], &a). - Mul(&z[i+1], &d[i]) - } - - return z -} - -// evaluateNumBitReversed computes the evaluation (shifted, bit reversed) of h where -// h = (x-1)*z*(1+\beta)*(\gamma+f)*(\gamma(1+\beta) + t+ \beta*t(gX)) - -// -// (x-1)*z(gX)*(\gamma(1+\beta) + h_{1} + \beta*h_{1}(gX))*(\gamma(1+\beta) + h_{2} + \beta*h_{2}(gX) ) -// -// * cz, ch1, ch2, ct, cf are the polynomials z, h1, h2, t, f in canonical basis -// * _lz, _lh1, _lh2, _lt, _lf are the polynomials z, h1, h2, t, f in shifted Lagrange basis (domainBig) -// * beta, gamma are the challenges -// * it returns h in canonical basis -func evaluateNumBitReversed(_lz, _lh1, _lh2, _lt, _lf []fr.Element, beta, gamma fr.Element, domainBig *fft.Domain) []fr.Element { - - // result - s := int(domainBig.Cardinality) - num := make([]fr.Element, domainBig.Cardinality) - - var u, onePlusBeta, GammaTimesOnePlusBeta, m, n, one fr.Element - - one.SetOne() - onePlusBeta.Add(&one, &beta) - GammaTimesOnePlusBeta.Mul(&onePlusBeta, &gamma) - - g := make([]fr.Element, s) - g[0].Set(&domainBig.FrMultiplicativeGen) - for i := 1; i < s; i++ { - g[i].Mul(&g[i-1], &domainBig.Generator) - } - - var gg fr.Element - expo := big.NewInt(int64(domainBig.Cardinality>>1 - 1)) - gg.Square(&domainBig.Generator).Exp(gg, expo) - - nn := uint64(64 - bits.TrailingZeros64(domainBig.Cardinality)) - - for i := 0; i < s; i++ { - - _i := int(bits.Reverse64(uint64(i)) >> nn) - _is := int(bits.Reverse64(uint64((i+2)%s)) >> nn) - - // m = z*(1+\beta)*(\gamma+f)*(\gamma(1+\beta) + t+ \beta*t(gX)) - m.Mul(&onePlusBeta, &_lz[_i]) - u.Add(&gamma, &_lf[_i]) - m.Mul(&m, &u) - u.Mul(&beta, &_lt[_is]). - Add(&u, &_lt[_i]). - Add(&u, &GammaTimesOnePlusBeta) - m.Mul(&m, &u) - - // n = z(gX)*(\gamma(1+\beta) + h_{1} + \beta*h_{1}(gX))*(\gamma(1+\beta) + h_{2} + \beta*h_{2}(gX) - n.Mul(&beta, &_lh1[_is]). - Add(&n, &_lh1[_i]). - Add(&n, &GammaTimesOnePlusBeta) - u.Mul(&beta, &_lh2[_is]). - Add(&u, &_lh2[_i]). - Add(&u, &GammaTimesOnePlusBeta) - n.Mul(&n, &u). - Mul(&n, &_lz[_is]) - - // (x-gg**(n-1))*(m-n) - num[_i].Sub(&m, &n) - u.Sub(&g[i], &gg) - num[_i].Mul(&num[_i], &u) - - } - - return num -} - -// evaluateXnMinusOneDomainBig returns the evaluation of (x^{n}-1) on FrMultiplicativeGen*< g > -func evaluateXnMinusOneDomainBig(domainBig *fft.Domain) [2]fr.Element { - - sizeDomainSmall := domainBig.Cardinality / 2 - - var one fr.Element - one.SetOne() - - // x^{n}-1 on FrMultiplicativeGen*< g > - var res [2]fr.Element - var shift fr.Element - shift.Exp(domainBig.FrMultiplicativeGen, big.NewInt(int64(sizeDomainSmall))) - res[0].Sub(&shift, &one) - res[1].Add(&shift, &one).Neg(&res[1]) - - return res - -} - -// evaluateL0DomainBig returns the evaluation of (x^{n}-1)/(x-1) on -// x^{n}-1 on FrMultiplicativeGen*< g > -func evaluateL0DomainBig(domainBig *fft.Domain) ([2]fr.Element, []fr.Element) { - - var one fr.Element - one.SetOne() - - // x^{n}-1 on FrMultiplicativeGen*< g > - xnMinusOne := evaluateXnMinusOneDomainBig(domainBig) - - // 1/(x-1) on FrMultiplicativeGen*< g > - var acc fr.Element - denL0 := make([]fr.Element, domainBig.Cardinality) - acc.Set(&domainBig.FrMultiplicativeGen) - for i := 0; i < int(domainBig.Cardinality); i++ { - denL0[i].Sub(&acc, &one) - acc.Mul(&acc, &domainBig.Generator) - } - denL0 = fr.BatchInvert(denL0) - - return xnMinusOne, denL0 -} - -// evaluationLnDomainBig returns the evaluation of (x^{n}-1)/(x-g^{n-1}) on -// x^{n}-1 on FrMultiplicativeGen*< g > -func evaluationLnDomainBig(domainBig *fft.Domain) ([2]fr.Element, []fr.Element) { - - sizeDomainSmall := domainBig.Cardinality / 2 - - var one fr.Element - one.SetOne() - - // x^{n}-1 on FrMultiplicativeGen*< g > - numLn := evaluateXnMinusOneDomainBig(domainBig) - - // 1/(x-g^{n-1}) on FrMultiplicativeGen*< g > - var gg, acc fr.Element - gg.Square(&domainBig.Generator).Exp(gg, big.NewInt(int64(sizeDomainSmall-1))) - denLn := make([]fr.Element, domainBig.Cardinality) - acc.Set(&domainBig.FrMultiplicativeGen) - for i := 0; i < int(domainBig.Cardinality); i++ { - denLn[i].Sub(&acc, &gg) - acc.Mul(&acc, &domainBig.Generator) - } - denLn = fr.BatchInvert(denLn) - - return numLn, denLn - -} - -// evaluateZStartsByOneBitReversed returns l0 * (z-1), in Lagrange basis and bit reversed order -func evaluateZStartsByOneBitReversed(lsZBitReversed []fr.Element, domainBig *fft.Domain) []fr.Element { - - var one fr.Element - one.SetOne() - - res := make([]fr.Element, domainBig.Cardinality) - - nn := uint64(64 - bits.TrailingZeros64(domainBig.Cardinality)) - - xnMinusOne, denL0 := evaluateL0DomainBig(domainBig) - - for i := 0; i < len(lsZBitReversed); i++ { - _i := int(bits.Reverse64(uint64(i)) >> nn) - res[_i].Sub(&lsZBitReversed[_i], &one). - Mul(&res[_i], &xnMinusOne[i%2]). - Mul(&res[_i], &denL0[i]) - } - - return res -} - -// evaluateZEndsByOneBitReversed returns ln * (z-1), in Lagrange basis and bit reversed order -func evaluateZEndsByOneBitReversed(lsZBitReversed []fr.Element, domainBig *fft.Domain) []fr.Element { - - var one fr.Element - one.SetOne() - - numLn, denLn := evaluationLnDomainBig(domainBig) - - res := make([]fr.Element, len(lsZBitReversed)) - nn := uint64(64 - bits.TrailingZeros64(domainBig.Cardinality)) - - for i := 0; i < len(lsZBitReversed); i++ { - _i := int(bits.Reverse64(uint64(i)) >> nn) - res[_i].Sub(&lsZBitReversed[_i], &one). - Mul(&res[_i], &numLn[i%2]). - Mul(&res[_i], &denLn[i]) - } - - return res -} - -// evaluateOverlapH1h2BitReversed returns ln * (h1 - h2(g.x)), in Lagrange basis and bit reversed order -func evaluateOverlapH1h2BitReversed(_lh1, _lh2 []fr.Element, domainBig *fft.Domain) []fr.Element { - - var one fr.Element - one.SetOne() - - numLn, denLn := evaluationLnDomainBig(domainBig) - - res := make([]fr.Element, len(_lh1)) - nn := uint64(64 - bits.TrailingZeros64(domainBig.Cardinality)) - - s := len(_lh1) - for i := 0; i < s; i++ { - - _i := int(bits.Reverse64(uint64(i)) >> nn) - _is := int(bits.Reverse64(uint64((i+2)%s)) >> nn) - - res[_i].Sub(&_lh1[_i], &_lh2[_is]). - Mul(&res[_i], &numLn[i%2]). - Mul(&res[_i], &denLn[i]) - } - - return res -} - -// computeQuotientCanonical computes the full quotient of the plookup protocol. -// * alpha is the challenge to fold the numerator -// * lh, lh0, lhn, lh1h2 are the various pieces of the numerator (Lagrange shifted form, bit reversed order) -// * domainBig fft domain -// It returns the quotient, in canonical basis -func computeQuotientCanonical(alpha fr.Element, lh, lh0, lhn, lh1h2 []fr.Element, domainBig *fft.Domain) []fr.Element { - - sizeDomainBig := int(domainBig.Cardinality) - res := make([]fr.Element, sizeDomainBig) - - var one fr.Element - one.SetOne() - - numLn := evaluateXnMinusOneDomainBig(domainBig) - numLn[0].Inverse(&numLn[0]) - numLn[1].Inverse(&numLn[1]) - nn := uint64(64 - bits.TrailingZeros64(domainBig.Cardinality)) - - for i := 0; i < sizeDomainBig; i++ { - - _i := int(bits.Reverse64(uint64(i)) >> nn) - - res[_i].Mul(&lh1h2[_i], &alpha). - Add(&res[_i], &lhn[_i]). - Mul(&res[_i], &alpha). - Add(&res[_i], &lh0[_i]). - Mul(&res[_i], &alpha). - Add(&res[_i], &lh[_i]). - Mul(&res[_i], &numLn[i%2]) - } - - domainBig.FFTInverse(res, fft.DIT, fft.OnCoset()) - - return res -} - -// ProveLookupVector returns proof that the values in f are in t. -// -// /!\IMPORTANT/!\ -// -// If the table t is already committed somewhere (which is the normal workflow -// before generating a lookup proof), the commitment needs to be done on the -// table sorted. Otherwise the commitment in proof.t will not be the same as -// the public commitment: it will contain the same values, but permuted. -func ProveLookupVector(pk kzg.ProvingKey, f, t fr.Vector) (ProofLookupVector, error) { - - // res - var proof ProofLookupVector - var err error - - // hash function used for Fiat Shamir - hFunc := sha256.New() - - // transcript to derive the challenge - fs := fiatshamir.NewTranscript(hFunc, "beta", "gamma", "alpha", "nu") - - // create domains - var domainSmall *fft.Domain - if len(t) <= len(f) { - domainSmall = fft.NewDomain(uint64(len(f) + 1)) - } else { - domainSmall = fft.NewDomain(uint64(len(t))) - } - sizeDomainSmall := int(domainSmall.Cardinality) - - // set the size - proof.size = domainSmall.Cardinality - - // set the generator - proof.g.Set(&domainSmall.Generator) - - // resize f and t - // note: the last element of lf does not matter - lf := make([]fr.Element, sizeDomainSmall) - lt := make([]fr.Element, sizeDomainSmall) - cf := make([]fr.Element, sizeDomainSmall) - ct := make([]fr.Element, sizeDomainSmall) - copy(lt, t) - copy(lf, f) - for i := len(f); i < sizeDomainSmall; i++ { - lf[i] = f[len(f)-1] - } - for i := len(t); i < sizeDomainSmall; i++ { - lt[i] = t[len(t)-1] - } - sort.Sort(fr.Vector(lt)) - copy(ct, lt) - copy(cf, lf) - domainSmall.FFTInverse(ct, fft.DIF) - domainSmall.FFTInverse(cf, fft.DIF) - fft.BitReverse(ct) - fft.BitReverse(cf) - proof.t, err = kzg.Commit(ct, pk) - if err != nil { - return proof, err - } - proof.f, err = kzg.Commit(cf, pk) - if err != nil { - return proof, err - } - - // write f sorted by t - lfSortedByt := make(fr.Vector, 2*domainSmall.Cardinality-1) - copy(lfSortedByt, lt) - copy(lfSortedByt[domainSmall.Cardinality:], lf) - sort.Sort(lfSortedByt) - - // compute h1, h2, commit to them - lh1 := make([]fr.Element, sizeDomainSmall) - lh2 := make([]fr.Element, sizeDomainSmall) - ch1 := make([]fr.Element, sizeDomainSmall) - ch2 := make([]fr.Element, sizeDomainSmall) - copy(lh1, lfSortedByt[:sizeDomainSmall]) - copy(lh2, lfSortedByt[sizeDomainSmall-1:]) - - copy(ch1, lfSortedByt[:sizeDomainSmall]) - copy(ch2, lfSortedByt[sizeDomainSmall-1:]) - domainSmall.FFTInverse(ch1, fft.DIF) - domainSmall.FFTInverse(ch2, fft.DIF) - fft.BitReverse(ch1) - fft.BitReverse(ch2) - - proof.h1, err = kzg.Commit(ch1, pk) - if err != nil { - return proof, err - } - proof.h2, err = kzg.Commit(ch2, pk) - if err != nil { - return proof, err - } - - // derive beta, gamma - beta, err := deriveRandomness(fs, "beta", &proof.t, &proof.f, &proof.h1, &proof.h2) - if err != nil { - return proof, err - } - gamma, err := deriveRandomness(fs, "gamma") - if err != nil { - return proof, err - } - - // Compute to Z - lz := evaluateAccumulationPolynomial(lf, lt, lh1, lh2, beta, gamma) - cz := make([]fr.Element, len(lz)) - copy(cz, lz) - domainSmall.FFTInverse(cz, fft.DIF) - fft.BitReverse(cz) - proof.z, err = kzg.Commit(cz, pk) - if err != nil { - return proof, err - } - - // prepare data for computing the quotient - // compute the numerator - s := domainSmall.Cardinality - domainBig := fft.NewDomain(uint64(2 * s)) - - _lz := make([]fr.Element, 2*s) - _lh1 := make([]fr.Element, 2*s) - _lh2 := make([]fr.Element, 2*s) - _lt := make([]fr.Element, 2*s) - _lf := make([]fr.Element, 2*s) - copy(_lz, cz) - copy(_lh1, ch1) - copy(_lh2, ch2) - copy(_lt, ct) - copy(_lf, cf) - domainBig.FFT(_lz, fft.DIF, fft.OnCoset()) - domainBig.FFT(_lh1, fft.DIF, fft.OnCoset()) - domainBig.FFT(_lh2, fft.DIF, fft.OnCoset()) - domainBig.FFT(_lt, fft.DIF, fft.OnCoset()) - domainBig.FFT(_lf, fft.DIF, fft.OnCoset()) - - // compute h - lh := evaluateNumBitReversed(_lz, _lh1, _lh2, _lt, _lf, beta, gamma, domainBig) - - // compute l0*(z-1) - lh0 := evaluateZStartsByOneBitReversed(_lz, domainBig) - - // compute ln(z-1) - lhn := evaluateZEndsByOneBitReversed(_lz, domainBig) - - // compute ln*(h1-h2(g*X)) - lh1h2 := evaluateOverlapH1h2BitReversed(_lh1, _lh2, domainBig) - - // compute the quotient - alpha, err := deriveRandomness(fs, "alpha", &proof.z) - if err != nil { - return proof, err - } - ch := computeQuotientCanonical(alpha, lh, lh0, lhn, lh1h2, domainBig) - proof.h, err = kzg.Commit(ch, pk) - if err != nil { - return proof, err - } - - // build the opening proofs - nu, err := deriveRandomness(fs, "nu", &proof.h) - if err != nil { - return proof, err - } - proof.BatchedProof, err = kzg.BatchOpenSinglePoint( - [][]fr.Element{ - ch1, - ch2, - ct, - cz, - cf, - ch, - }, - []kzg.Digest{ - proof.h1, - proof.h2, - proof.t, - proof.z, - proof.f, - proof.h, - }, - nu, - hFunc, - pk, - ) - if err != nil { - return proof, err - } - - nu.Mul(&nu, &domainSmall.Generator) - proof.BatchedProofShifted, err = kzg.BatchOpenSinglePoint( - [][]fr.Element{ - ch1, - ch2, - ct, - cz, - }, - []kzg.Digest{ - proof.h1, - proof.h2, - proof.t, - proof.z, - }, - nu, - hFunc, - pk, - ) - if err != nil { - return proof, err - } - - return proof, nil -} - -// VerifyLookupVector verifies that a ProofLookupVector proof is correct -func VerifyLookupVector(vk kzg.VerifyingKey, proof ProofLookupVector) error { - - // hash function that is used for Fiat Shamir - hFunc := sha256.New() - - // transcript to derive the challenge - fs := fiatshamir.NewTranscript(hFunc, "beta", "gamma", "alpha", "nu") - - // derive the various challenges - beta, err := deriveRandomness(fs, "beta", &proof.t, &proof.f, &proof.h1, &proof.h2) - if err != nil { - return err - } - - gamma, err := deriveRandomness(fs, "gamma") - if err != nil { - return err - } - - alpha, err := deriveRandomness(fs, "alpha", &proof.z) - if err != nil { - return err - } - - nu, err := deriveRandomness(fs, "nu", &proof.h) - if err != nil { - return err - } - - // check opening proofs - err = kzg.BatchVerifySinglePoint( - []kzg.Digest{ - proof.h1, - proof.h2, - proof.t, - proof.z, - proof.f, - proof.h, - }, - &proof.BatchedProof, - nu, - hFunc, - vk, - ) - if err != nil { - return err - } - - // shift the point and verify shifted proof - var shiftedNu fr.Element - shiftedNu.Mul(&nu, &proof.g) - err = kzg.BatchVerifySinglePoint( - []kzg.Digest{ - proof.h1, - proof.h2, - proof.t, - proof.z, - }, - &proof.BatchedProofShifted, - shiftedNu, - hFunc, - vk, - ) - if err != nil { - return err - } - - // check the generator is correct - var checkOrder, one fr.Element - one.SetOne() - checkOrder.Exp(proof.g, big.NewInt(int64(proof.size/2))) - if checkOrder.Equal(&one) { - return ErrGenerator - } - checkOrder.Square(&checkOrder) - if !checkOrder.Equal(&one) { - return ErrGenerator - } - - // check polynomial relation using Schwartz Zippel - var lhs, rhs, nun, g, _g, a, v, w fr.Element - g.Exp(proof.g, big.NewInt(int64(proof.size-1))) - - v.Add(&one, &beta) - w.Mul(&v, &gamma) - - // h(ν) where - // h = (xⁿ⁻¹-1)*z*(1+β)*(γ+f)*(γ(1+β) + t+ β*t(gX)) - - // (xⁿ⁻¹-1)*z(gX)*(γ(1+β) + h₁ + β*h₁(gX))*(γ(1+β) + h₂ + β*h₂(gX) ) - lhs.Sub(&nu, &g). // (ν-gⁿ⁻¹) - Mul(&lhs, &proof.BatchedProof.ClaimedValues[3]). - Mul(&lhs, &v) - a.Add(&gamma, &proof.BatchedProof.ClaimedValues[4]) - lhs.Mul(&lhs, &a) - a.Mul(&beta, &proof.BatchedProofShifted.ClaimedValues[2]). - Add(&a, &proof.BatchedProof.ClaimedValues[2]). - Add(&a, &w) - lhs.Mul(&lhs, &a) - - rhs.Sub(&nu, &g). - Mul(&rhs, &proof.BatchedProofShifted.ClaimedValues[3]) - a.Mul(&beta, &proof.BatchedProofShifted.ClaimedValues[0]). - Add(&a, &proof.BatchedProof.ClaimedValues[0]). - Add(&a, &w) - rhs.Mul(&rhs, &a) - a.Mul(&beta, &proof.BatchedProofShifted.ClaimedValues[1]). - Add(&a, &proof.BatchedProof.ClaimedValues[1]). - Add(&a, &w) - rhs.Mul(&rhs, &a) - - lhs.Sub(&lhs, &rhs) - - // check consistency of bounds - var l0, ln, d1, d2 fr.Element - l0.Exp(nu, big.NewInt(int64(proof.size))).Sub(&l0, &one) - ln.Set(&l0) - d1.Sub(&nu, &one) - d2.Sub(&nu, &g) - l0.Div(&l0, &d1) // (νⁿ-1)/(ν-1) - ln.Div(&ln, &d2) // (νⁿ-1)/(ν-gⁿ⁻¹) - - // l₀*(z-1) = (νⁿ-1)/(ν-1)*(z-1) - var l0z fr.Element - l0z.Sub(&proof.BatchedProof.ClaimedValues[3], &one). - Mul(&l0z, &l0) - - // lₙ*(z-1) = (νⁿ-1)/(ν-gⁿ⁻¹)*(z-1) - var lnz fr.Element - lnz.Sub(&proof.BatchedProof.ClaimedValues[3], &one). - Mul(&ln, &lnz) - - // lₙ*(h1 - h₂(g.x)) - var lnh1h2 fr.Element - lnh1h2.Sub(&proof.BatchedProof.ClaimedValues[0], &proof.BatchedProofShifted.ClaimedValues[1]). - Mul(&lnh1h2, &ln) - - // fold the numerator - lnh1h2.Mul(&lnh1h2, &alpha). - Add(&lnh1h2, &lnz). - Mul(&lnh1h2, &alpha). - Add(&lnh1h2, &l0z). - Mul(&lnh1h2, &alpha). - Add(&lnh1h2, &lhs) - - // (xⁿ-1) * h(x) evaluated at ν - nun.Exp(nu, big.NewInt(int64(proof.size))) - _g.Sub(&nun, &one) - _g.Mul(&proof.BatchedProof.ClaimedValues[5], &_g) - if !lnh1h2.Equal(&_g) { - return ErrPlookupVerification - } - - return nil -} diff --git a/ecc/bw6-756/fr/polynomial/doc.go b/ecc/bw6-756/fr/polynomial/doc.go deleted file mode 100644 index 747da41f2c..0000000000 --- a/ecc/bw6-756/fr/polynomial/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package polynomial provides polynomial methods and commitment schemes. -package polynomial diff --git a/ecc/bw6-756/fr/polynomial/multilin.go b/ecc/bw6-756/fr/polynomial/multilin.go deleted file mode 100644 index 7c85cb5d94..0000000000 --- a/ecc/bw6-756/fr/polynomial/multilin.go +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package polynomial - -import ( - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/utils" - "math/bits" -) - -// MultiLin tracks the values of a (dense i.e. not sparse) multilinear polynomial -// The variables are X₁ through Xₙ where n = log(len(.)) -// .[∑ᵢ 2ⁱ⁻¹ bₙ₋ᵢ] = the polynomial evaluated at (b₁, b₂, ..., bₙ) -// It is understood that any hypercube evaluation can be extrapolated to a multilinear polynomial -type MultiLin []fr.Element - -// Fold is partial evaluation function k[X₁, X₂, ..., Xₙ] → k[X₂, ..., Xₙ] by setting X₁=r -func (m *MultiLin) Fold(r fr.Element) { - mid := len(*m) / 2 - - bottom, top := (*m)[:mid], (*m)[mid:] - - var t fr.Element // no need to update the top part - - // updating bookkeeping table - // knowing that the polynomial f ∈ (k[X₂, ..., Xₙ])[X₁] is linear, we would get f(r) = f(0) + r(f(1) - f(0)) - // the following loop computes the evaluations of f(r) accordingly: - // f(r, b₂, ..., bₙ) = f(0, b₂, ..., bₙ) + r(f(1, b₂, ..., bₙ) - f(0, b₂, ..., bₙ)) - for i := 0; i < mid; i++ { - // table[i] ← table[i] + r (table[i + mid] - table[i]) - t.Sub(&top[i], &bottom[i]) - t.Mul(&t, &r) - bottom[i].Add(&bottom[i], &t) - } - - *m = (*m)[:mid] -} - -func (m *MultiLin) FoldParallel(r fr.Element) utils.Task { - mid := len(*m) / 2 - bottom, top := (*m)[:mid], (*m)[mid:] - - *m = bottom - - return func(start, end int) { - var t fr.Element // no need to update the top part - for i := start; i < end; i++ { - // table[i] ← table[i] + r (table[i + mid] - table[i]) - t.Sub(&top[i], &bottom[i]) - t.Mul(&t, &r) - bottom[i].Add(&bottom[i], &t) - } - } -} - -func (m MultiLin) Sum() fr.Element { - s := m[0] - for i := 1; i < len(m); i++ { - s.Add(&s, &m[i]) - } - return s -} - -func _clone(m MultiLin, p *Pool) MultiLin { - if p == nil { - return m.Clone() - } else { - return p.Clone(m) - } -} - -func _dump(m MultiLin, p *Pool) { - if p != nil { - p.Dump(m) - } -} - -// Evaluate extrapolate the value of the multilinear polynomial corresponding to m -// on the given coordinates -func (m MultiLin) Evaluate(coordinates []fr.Element, p *Pool) fr.Element { - // Folding is a mutating operation - bkCopy := _clone(m, p) - - // Evaluate step by step through repeated folding (i.e. evaluation at the first remaining variable) - for _, r := range coordinates { - bkCopy.Fold(r) - } - - result := bkCopy[0] - - _dump(bkCopy, p) - return result -} - -// Clone creates a deep copy of a bookkeeping table. -// Both multilinear interpolation and sumcheck require folding an underlying -// array, but folding changes the array. To do both one requires a deep copy -// of the bookkeeping table. -func (m MultiLin) Clone() MultiLin { - res := make(MultiLin, len(m)) - copy(res, m) - return res -} - -// Add two bookKeepingTables -func (m *MultiLin) Add(left, right MultiLin) { - size := len(left) - // Check that left and right have the same size - if len(right) != size || len(*m) != size { - panic("left, right and destination must have the right size") - } - - // Add elementwise - for i := 0; i < size; i++ { - (*m)[i].Add(&left[i], &right[i]) - } -} - -// EvalEq computes Eq(q₁, ... , qₙ, h₁, ... , hₙ) = Π₁ⁿ Eq(qᵢ, hᵢ) -// where Eq(x,y) = xy + (1-x)(1-y) = 1 - x - y + xy + xy interpolates -// -// _________________ -// | | | -// | 0 | 1 | -// |_______|_______| -// y | | | -// | 1 | 0 | -// |_______|_______| -// -// x -// -// In other words the polynomial evaluated here is the multilinear extrapolation of -// one that evaluates to q' == h' for vectors q', h' of binary values -func EvalEq(q, h []fr.Element) fr.Element { - var res, nxt, one, sum fr.Element - one.SetOne() - for i := 0; i < len(q); i++ { - nxt.Mul(&q[i], &h[i]) // nxt <- qᵢ * hᵢ - nxt.Double(&nxt) // nxt <- 2 * qᵢ * hᵢ - nxt.Add(&nxt, &one) // nxt <- 1 + 2 * qᵢ * hᵢ - sum.Add(&q[i], &h[i]) // sum <- qᵢ + hᵢ TODO: Why not subtract one by one from nxt? More parallel? - - if i == 0 { - res.Sub(&nxt, &sum) // nxt <- 1 + 2 * qᵢ * hᵢ - qᵢ - hᵢ - } else { - nxt.Sub(&nxt, &sum) // nxt <- 1 + 2 * qᵢ * hᵢ - qᵢ - hᵢ - res.Mul(&res, &nxt) // res <- res * nxt - } - } - return res -} - -// Eq sets m to the representation of the polynomial Eq(q₁, ..., qₙ, *, ..., *) × m[0] -func (m *MultiLin) Eq(q []fr.Element) { - n := len(q) - - if len(*m) != 1<= 0; i-- { - res.Mul(&res, v) - res.Add(&res, &(*p)[i]) - } - - return res -} - -// Clone returns a copy of the polynomial -func (p *Polynomial) Clone() Polynomial { - _p := make(Polynomial, len(*p)) - copy(_p, *p) - return _p -} - -// Set to another polynomial -func (p *Polynomial) Set(p1 Polynomial) { - if len(*p) != len(p1) { - *p = p1.Clone() - return - } - - for i := 0; i < len(p1); i++ { - (*p)[i].Set(&p1[i]) - } -} - -// AddConstantInPlace adds a constant to the polynomial, modifying p -func (p *Polynomial) AddConstantInPlace(c *fr.Element) { - for i := 0; i < len(*p); i++ { - (*p)[i].Add(&(*p)[i], c) - } -} - -// SubConstantInPlace subs a constant to the polynomial, modifying p -func (p *Polynomial) SubConstantInPlace(c *fr.Element) { - for i := 0; i < len(*p); i++ { - (*p)[i].Sub(&(*p)[i], c) - } -} - -// ScaleInPlace multiplies p by v, modifying p -func (p *Polynomial) ScaleInPlace(c *fr.Element) { - for i := 0; i < len(*p); i++ { - (*p)[i].Mul(&(*p)[i], c) - } -} - -// Scale multiplies p0 by v, storing the result in p -func (p *Polynomial) Scale(c *fr.Element, p0 Polynomial) { - if len(*p) != len(p0) { - *p = make(Polynomial, len(p0)) - } - for i := 0; i < len(p0); i++ { - (*p)[i].Mul(c, &p0[i]) - } -} - -// Add adds p1 to p2 -// This function allocates a new slice unless p == p1 or p == p2 -func (p *Polynomial) Add(p1, p2 Polynomial) *Polynomial { - - bigger := p1 - smaller := p2 - if len(bigger) < len(smaller) { - bigger, smaller = smaller, bigger - } - - if len(*p) == len(bigger) && (&(*p)[0] == &bigger[0]) { - for i := 0; i < len(smaller); i++ { - (*p)[i].Add(&(*p)[i], &smaller[i]) - } - return p - } - - if len(*p) == len(smaller) && (&(*p)[0] == &smaller[0]) { - for i := 0; i < len(smaller); i++ { - (*p)[i].Add(&(*p)[i], &bigger[i]) - } - *p = append(*p, bigger[len(smaller):]...) - return p - } - - res := make(Polynomial, len(bigger)) - copy(res, bigger) - for i := 0; i < len(smaller); i++ { - res[i].Add(&res[i], &smaller[i]) - } - *p = res - return p -} - -// Sub subtracts p2 from p1 -// TODO make interface more consistent with Add -func (p *Polynomial) Sub(p1, p2 Polynomial) *Polynomial { - if len(p1) != len(p2) || len(p2) != len(*p) { - return nil - } - for i := 0; i < len(*p); i++ { - (*p)[i].Sub(&p1[i], &p2[i]) - } - return p -} - -// Equal checks equality between two polynomials -func (p *Polynomial) Equal(p1 Polynomial) bool { - if (*p == nil) != (p1 == nil) { - return false - } - - if len(*p) != len(p1) { - return false - } - - for i := range p1 { - if !(*p)[i].Equal(&p1[i]) { - return false - } - } - - return true -} - -func (p Polynomial) SetZero() { - for i := 0; i < len(p); i++ { - p[i].SetZero() - } -} - -func (p Polynomial) Text(base int) string { - - var builder strings.Builder - - first := true - for d := len(p) - 1; d >= 0; d-- { - if p[d].IsZero() { - continue - } - - pD := p[d] - pDText := pD.Text(base) - - initialLen := builder.Len() - - if pDText[0] == '-' { - pDText = pDText[1:] - if first { - builder.WriteString("-") - } else { - builder.WriteString(" - ") - } - } else if !first { - builder.WriteString(" + ") - } - - first = false - - if !pD.IsOne() || d == 0 { - builder.WriteString(pDText) - } - - if builder.Len()-initialLen > 10 { - builder.WriteString("×") - } - - if d != 0 { - builder.WriteString("X") - } - if d > 1 { - builder.WriteString( - utils.ToSuperscript(strconv.Itoa(d)), - ) - } - - } - - if first { - return "0" - } - - return builder.String() -} diff --git a/ecc/bw6-756/fr/polynomial/polynomial_test.go b/ecc/bw6-756/fr/polynomial/polynomial_test.go deleted file mode 100644 index 3ae83afcc3..0000000000 --- a/ecc/bw6-756/fr/polynomial/polynomial_test.go +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package polynomial - -import ( - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/stretchr/testify/assert" - "math/big" - "testing" -) - -func TestPolynomialEval(t *testing.T) { - - // build polynomial - f := make(Polynomial, 20) - for i := 0; i < 20; i++ { - f[i].SetOne() - } - - // random value - var point fr.Element - point.SetRandom() - - // compute manually f(val) - var expectedEval, one, den fr.Element - var expo big.Int - one.SetOne() - expo.SetUint64(20) - expectedEval.Exp(point, &expo). - Sub(&expectedEval, &one) - den.Sub(&point, &one) - expectedEval.Div(&expectedEval, &den) - - // compute purported evaluation - purportedEval := f.Eval(&point) - - // check - if !purportedEval.Equal(&expectedEval) { - t.Fatal("polynomial evaluation failed") - } -} - -func TestPolynomialAddConstantInPlace(t *testing.T) { - - // build polynomial - f := make(Polynomial, 20) - for i := 0; i < 20; i++ { - f[i].SetOne() - } - - // constant to add - var c fr.Element - c.SetRandom() - - // add constant - f.AddConstantInPlace(&c) - - // check - var expectedCoeffs, one fr.Element - one.SetOne() - expectedCoeffs.Add(&one, &c) - for i := 0; i < 20; i++ { - if !f[i].Equal(&expectedCoeffs) { - t.Fatal("AddConstantInPlace failed") - } - } -} - -func TestPolynomialSubConstantInPlace(t *testing.T) { - - // build polynomial - f := make(Polynomial, 20) - for i := 0; i < 20; i++ { - f[i].SetOne() - } - - // constant to sub - var c fr.Element - c.SetRandom() - - // sub constant - f.SubConstantInPlace(&c) - - // check - var expectedCoeffs, one fr.Element - one.SetOne() - expectedCoeffs.Sub(&one, &c) - for i := 0; i < 20; i++ { - if !f[i].Equal(&expectedCoeffs) { - t.Fatal("SubConstantInPlace failed") - } - } -} - -func TestPolynomialScaleInPlace(t *testing.T) { - - // build polynomial - f := make(Polynomial, 20) - for i := 0; i < 20; i++ { - f[i].SetOne() - } - - // constant to scale by - var c fr.Element - c.SetRandom() - - // scale by constant - f.ScaleInPlace(&c) - - // check - for i := 0; i < 20; i++ { - if !f[i].Equal(&c) { - t.Fatal("ScaleInPlace failed") - } - } - -} - -func TestPolynomialAdd(t *testing.T) { - - // build unbalanced polynomials - f1 := make(Polynomial, 20) - f1Backup := make(Polynomial, 20) - for i := 0; i < 20; i++ { - f1[i].SetOne() - f1Backup[i].SetOne() - } - f2 := make(Polynomial, 10) - f2Backup := make(Polynomial, 10) - for i := 0; i < 10; i++ { - f2[i].SetOne() - f2Backup[i].SetOne() - } - - // expected result - var one, two fr.Element - one.SetOne() - two.Double(&one) - expectedSum := make(Polynomial, 20) - for i := 0; i < 10; i++ { - expectedSum[i].Set(&two) - } - for i := 10; i < 20; i++ { - expectedSum[i].Set(&one) - } - - // caller is empty - var g Polynomial - g.Add(f1, f2) - if !g.Equal(expectedSum) { - t.Fatal("add polynomials fails") - } - if !f1.Equal(f1Backup) { - t.Fatal("side effect, f1 should not have been modified") - } - if !f2.Equal(f2Backup) { - t.Fatal("side effect, f2 should not have been modified") - } - - // all operands are distinct - _f1 := f1.Clone() - _f1.Add(f1, f2) - if !_f1.Equal(expectedSum) { - t.Fatal("add polynomials fails") - } - if !f1.Equal(f1Backup) { - t.Fatal("side effect, f1 should not have been modified") - } - if !f2.Equal(f2Backup) { - t.Fatal("side effect, f2 should not have been modified") - } - - // first operand = caller - _f1 = f1.Clone() - _f2 := f2.Clone() - _f1.Add(_f1, _f2) - if !_f1.Equal(expectedSum) { - t.Fatal("add polynomials fails") - } - if !_f2.Equal(f2Backup) { - t.Fatal("side effect, _f2 should not have been modified") - } - - // second operand = caller - _f1 = f1.Clone() - _f2 = f2.Clone() - _f1.Add(_f2, _f1) - if !_f1.Equal(expectedSum) { - t.Fatal("add polynomials fails") - } - if !_f2.Equal(f2Backup) { - t.Fatal("side effect, _f2 should not have been modified") - } -} - -func TestPolynomialText(t *testing.T) { - var one, negTwo fr.Element - one.SetOne() - negTwo.SetInt64(-2) - - p := Polynomial{one, negTwo, one} - - assert.Equal(t, "X² - 2X + 1", p.Text(10)) -} diff --git a/ecc/bw6-756/fr/polynomial/pool.go b/ecc/bw6-756/fr/polynomial/pool.go deleted file mode 100644 index d054fb2bad..0000000000 --- a/ecc/bw6-756/fr/polynomial/pool.go +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package polynomial - -import ( - "encoding/json" - "fmt" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "runtime" - "sort" - "sync" - "unsafe" -) - -// Memory management for polynomials -// WARNING: This is not thread safe TODO: Make sure that is not a problem -// TODO: There is a lot of "unsafe" memory management here and needs to be vetted thoroughly - -type sizedPool struct { - maxN int - pool sync.Pool - stats poolStats -} - -type inUseData struct { - allocatedFor []uintptr - pool *sizedPool -} - -type Pool struct { - //lock sync.Mutex - inUse sync.Map - subPools []sizedPool -} - -func (p *sizedPool) get(n int) *fr.Element { - p.stats.make(n) - return p.pool.Get().(*fr.Element) -} - -func (p *sizedPool) put(ptr *fr.Element) { - p.stats.dump() - p.pool.Put(ptr) -} - -func NewPool(maxN ...int) (pool Pool) { - - sort.Ints(maxN) - pool = Pool{ - subPools: make([]sizedPool, len(maxN)), - } - - for i := range pool.subPools { - subPool := &pool.subPools[i] - subPool.maxN = maxN[i] - subPool.pool = sync.Pool{ - New: func() interface{} { - subPool.stats.Allocated++ - return getDataPointer(make([]fr.Element, 0, subPool.maxN)) - }, - } - } - return -} - -func (p *Pool) findCorrespondingPool(n int) *sizedPool { - poolI := 0 - for poolI < len(p.subPools) && n > p.subPools[poolI].maxN { - poolI++ - } - return &p.subPools[poolI] // out of bounds error here would mean that n is too large -} - -func (p *Pool) Make(n int) []fr.Element { - pool := p.findCorrespondingPool(n) - ptr := pool.get(n) - p.addInUse(ptr, pool) - return unsafe.Slice(ptr, n) -} - -// Dump dumps a set of polynomials into the pool -func (p *Pool) Dump(slices ...[]fr.Element) { - for _, slice := range slices { - ptr := getDataPointer(slice) - if metadata, ok := p.inUse.Load(ptr); ok { - p.inUse.Delete(ptr) - metadata.(inUseData).pool.put(ptr) - } else { - panic("attempting to dump a slice not created by the pool") - } - } -} - -func (p *Pool) addInUse(ptr *fr.Element, pool *sizedPool) { - pcs := make([]uintptr, 2) - n := runtime.Callers(3, pcs) - - if prevPcs, ok := p.inUse.Load(ptr); ok { // TODO: remove if unnecessary for security - panic(fmt.Errorf("re-allocated non-dumped slice, previously allocated at %v", runtime.CallersFrames(prevPcs.(inUseData).allocatedFor))) - } - p.inUse.Store(ptr, inUseData{ - allocatedFor: pcs[:n], - pool: pool, - }) -} - -func printFrame(frame runtime.Frame) { - fmt.Printf("\t%s line %d, function %s\n", frame.File, frame.Line, frame.Function) -} - -func (p *Pool) printInUse() { - fmt.Println("slices never dumped allocated at:") - p.inUse.Range(func(_, pcs any) bool { - fmt.Println("-------------------------") - - var frame runtime.Frame - frames := runtime.CallersFrames(pcs.(inUseData).allocatedFor) - more := true - for more { - frame, more = frames.Next() - printFrame(frame) - } - return true - }) -} - -type poolStats struct { - Used int - Allocated int - ReuseRate float64 - InUse int - GreatestNUsed int - SmallestNUsed int -} - -type poolsStats struct { - SubPools []poolStats - InUse int -} - -func (s *poolStats) make(n int) { - s.Used++ - s.InUse++ - if n > s.GreatestNUsed { - s.GreatestNUsed = n - } - if s.SmallestNUsed == 0 || s.SmallestNUsed > n { - s.SmallestNUsed = n - } -} - -func (s *poolStats) dump() { - s.InUse-- -} - -func (s *poolStats) finalize() { - s.ReuseRate = float64(s.Used) / float64(s.Allocated) -} - -func getDataPointer(slice []fr.Element) *fr.Element { - return (*fr.Element)(unsafe.SliceData(slice)) -} - -func (p *Pool) PrintPoolStats() { - InUse := 0 - subStats := make([]poolStats, len(p.subPools)) - for i := range p.subPools { - subPool := &p.subPools[i] - subPool.stats.finalize() - subStats[i] = subPool.stats - InUse += subPool.stats.InUse - } - - stats := poolsStats{ - SubPools: subStats, - InUse: InUse, - } - serialized, _ := json.MarshalIndent(stats, "", " ") - fmt.Println(string(serialized)) - p.printInUse() -} - -func (p *Pool) Clone(slice []fr.Element) []fr.Element { - res := p.Make(len(slice)) - copy(res, slice) - return res -} diff --git a/ecc/bw6-756/fr/sumcheck/sumcheck.go b/ecc/bw6-756/fr/sumcheck/sumcheck.go deleted file mode 100644 index 0a42bdb25b..0000000000 --- a/ecc/bw6-756/fr/sumcheck/sumcheck.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package sumcheck - -import ( - "fmt" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/polynomial" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - "strconv" -) - -// This does not make use of parallelism and represents polynomials as lists of coefficients -// It is currently geared towards arithmetic hashes. Once we have a more unified hash function interface, this can be generified. - -// Claims to a multi-sumcheck statement. i.e. one of the form ∑_{0≤i<2ⁿ} fⱼ(i) = cⱼ for 1 ≤ j ≤ m. -// Later evolving into a claim of the form gⱼ = ∑_{0≤i<2ⁿ⁻ʲ} g(r₁, r₂, ..., rⱼ₋₁, Xⱼ, i...) -type Claims interface { - Combine(a fr.Element) polynomial.Polynomial // Combine into the 0ᵗʰ sumcheck subclaim. Create g := ∑_{1≤j≤m} aʲ⁻¹fⱼ for which now we seek to prove ∑_{0≤i<2ⁿ} g(i) = c := ∑_{1≤j≤m} aʲ⁻¹cⱼ. Return g₁. - Next(fr.Element) polynomial.Polynomial // Return the evaluations gⱼ(k) for 1 ≤ k < degⱼ(g). Update the claim to gⱼ₊₁ for the input value as rⱼ - VarsNum() int //number of variables - ClaimsNum() int //number of claims - ProveFinalEval(r []fr.Element) interface{} //in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof -} - -// LazyClaims is the Claims data structure on the verifier side. It is "lazy" in that it has to compute fewer things. -type LazyClaims interface { - ClaimsNum() int // ClaimsNum = m - VarsNum() int // VarsNum = n - CombinedSum(a fr.Element) fr.Element // CombinedSum returns c = ∑_{1≤j≤m} aʲ⁻¹cⱼ - Degree(i int) int //Degree of the total claim in the i'th variable - VerifyFinalEval(r []fr.Element, combinationCoeff fr.Element, purportedValue fr.Element, proof interface{}) error -} - -// Proof of a multi-sumcheck statement. -type Proof struct { - PartialSumPolys []polynomial.Polynomial `json:"partialSumPolys"` - FinalEvalProof interface{} `json:"finalEvalProof"` //in case it is difficult for the verifier to compute g(r₁, ..., rₙ) on its own, the prover can provide the value and a proof -} - -func setupTranscript(claimsNum int, varsNum int, settings *fiatshamir.Settings) (challengeNames []string, err error) { - numChallenges := varsNum - if claimsNum >= 2 { - numChallenges++ - } - challengeNames = make([]string, numChallenges) - if claimsNum >= 2 { - challengeNames[0] = settings.Prefix + "comb" - } - prefix := settings.Prefix + "pSP." - for i := 0; i < varsNum; i++ { - challengeNames[i+numChallenges-varsNum] = prefix + strconv.Itoa(i) - } - if settings.Transcript == nil { - transcript := fiatshamir.NewTranscript(settings.Hash, challengeNames...) - settings.Transcript = transcript - } - - for i := range settings.BaseChallenges { - if err = settings.Transcript.Bind(challengeNames[0], settings.BaseChallenges[i]); err != nil { - return - } - } - return -} - -func next(transcript *fiatshamir.Transcript, bindings []fr.Element, remainingChallengeNames *[]string) (fr.Element, error) { - challengeName := (*remainingChallengeNames)[0] - for i := range bindings { - bytes := bindings[i].Bytes() - if err := transcript.Bind(challengeName, bytes[:]); err != nil { - return fr.Element{}, err - } - } - var res fr.Element - bytes, err := transcript.ComputeChallenge(challengeName) - res.SetBytes(bytes) - - *remainingChallengeNames = (*remainingChallengeNames)[1:] - - return res, err -} - -// Prove create a non-interactive sumcheck proof -func Prove(claims Claims, transcriptSettings fiatshamir.Settings) (Proof, error) { - - var proof Proof - remainingChallengeNames, err := setupTranscript(claims.ClaimsNum(), claims.VarsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return proof, err - } - - var combinationCoeff fr.Element - if claims.ClaimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { - return proof, err - } - } - - varsNum := claims.VarsNum() - proof.PartialSumPolys = make([]polynomial.Polynomial, varsNum) - proof.PartialSumPolys[0] = claims.Combine(combinationCoeff) - challenges := make([]fr.Element, varsNum) - - for j := 0; j+1 < varsNum; j++ { - if challenges[j], err = next(transcript, proof.PartialSumPolys[j], &remainingChallengeNames); err != nil { - return proof, err - } - proof.PartialSumPolys[j+1] = claims.Next(challenges[j]) - } - - if challenges[varsNum-1], err = next(transcript, proof.PartialSumPolys[varsNum-1], &remainingChallengeNames); err != nil { - return proof, err - } - - proof.FinalEvalProof = claims.ProveFinalEval(challenges) - - return proof, nil -} - -func Verify(claims LazyClaims, proof Proof, transcriptSettings fiatshamir.Settings) error { - remainingChallengeNames, err := setupTranscript(claims.ClaimsNum(), claims.VarsNum(), &transcriptSettings) - transcript := transcriptSettings.Transcript - if err != nil { - return err - } - - var combinationCoeff fr.Element - - if claims.ClaimsNum() >= 2 { - if combinationCoeff, err = next(transcript, []fr.Element{}, &remainingChallengeNames); err != nil { - return err - } - } - - r := make([]fr.Element, claims.VarsNum()) - - // Just so that there is enough room for gJ to be reused - maxDegree := claims.Degree(0) - for j := 1; j < claims.VarsNum(); j++ { - if d := claims.Degree(j); d > maxDegree { - maxDegree = d - } - } - gJ := make(polynomial.Polynomial, maxDegree+1) //At the end of iteration j, gJ = ∑_{i < 2ⁿ⁻ʲ⁻¹} g(X₁, ..., Xⱼ₊₁, i...) NOTE: n is shorthand for claims.VarsNum() - gJR := claims.CombinedSum(combinationCoeff) // At the beginning of iteration j, gJR = ∑_{i < 2ⁿ⁻ʲ} g(r₁, ..., rⱼ, i...) - - for j := 0; j < claims.VarsNum(); j++ { - if len(proof.PartialSumPolys[j]) != claims.Degree(j) { - return fmt.Errorf("malformed proof") - } - copy(gJ[1:], proof.PartialSumPolys[j]) - gJ[0].Sub(&gJR, &proof.PartialSumPolys[j][0]) // Requirement that gⱼ(0) + gⱼ(1) = gⱼ₋₁(r) - // gJ is ready - - //Prepare for the next iteration - if r[j], err = next(transcript, proof.PartialSumPolys[j], &remainingChallengeNames); err != nil { - return err - } - // This is an extremely inefficient way of interpolating. TODO: Interpolate without symbolically computing a polynomial - gJCoeffs := polynomial.InterpolateOnRange(gJ[:(claims.Degree(j) + 1)]) - gJR = gJCoeffs.Eval(&r[j]) - } - - return claims.VerifyFinalEval(r, combinationCoeff, gJR, proof.FinalEvalProof) -} diff --git a/ecc/bw6-756/fr/sumcheck/sumcheck_test.go b/ecc/bw6-756/fr/sumcheck/sumcheck_test.go deleted file mode 100644 index 8daf3dec4a..0000000000 --- a/ecc/bw6-756/fr/sumcheck/sumcheck_test.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package sumcheck - -import ( - "fmt" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/polynomial" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/test_vector_utils" - fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" - "github.com/stretchr/testify/assert" - "hash" - "math/bits" - "strings" - "testing" -) - -type singleMultilinClaim struct { - g polynomial.MultiLin -} - -func (c singleMultilinClaim) ProveFinalEval(r []fr.Element) interface{} { - return nil // verifier can compute the final eval itself -} - -func (c singleMultilinClaim) VarsNum() int { - return bits.TrailingZeros(uint(len(c.g))) -} - -func (c singleMultilinClaim) ClaimsNum() int { - return 1 -} - -func sumForX1One(g polynomial.MultiLin) polynomial.Polynomial { - sum := g[len(g)/2] - for i := len(g)/2 + 1; i < len(g); i++ { - sum.Add(&sum, &g[i]) - } - return []fr.Element{sum} -} - -func (c singleMultilinClaim) Combine(fr.Element) polynomial.Polynomial { - return sumForX1One(c.g) -} - -func (c *singleMultilinClaim) Next(r fr.Element) polynomial.Polynomial { - c.g.Fold(r) - return sumForX1One(c.g) -} - -type singleMultilinLazyClaim struct { - g polynomial.MultiLin - claimedSum fr.Element -} - -func (c singleMultilinLazyClaim) VerifyFinalEval(r []fr.Element, combinationCoeff fr.Element, purportedValue fr.Element, proof interface{}) error { - val := c.g.Evaluate(r, nil) - if val.Equal(&purportedValue) { - return nil - } - return fmt.Errorf("mismatch") -} - -func (c singleMultilinLazyClaim) CombinedSum(combinationCoeffs fr.Element) fr.Element { - return c.claimedSum -} - -func (c singleMultilinLazyClaim) Degree(i int) int { - return 1 -} - -func (c singleMultilinLazyClaim) ClaimsNum() int { - return 1 -} - -func (c singleMultilinLazyClaim) VarsNum() int { - return bits.TrailingZeros(uint(len(c.g))) -} - -func testSumcheckSingleClaimMultilin(polyInt []uint64, hashGenerator func() hash.Hash) error { - poly := make(polynomial.MultiLin, len(polyInt)) - for i, n := range polyInt { - poly[i].SetUint64(n) - } - - claim := singleMultilinClaim{g: poly.Clone()} - - proof, err := Prove(&claim, fiatshamir.WithHash(hashGenerator())) - if err != nil { - return err - } - - var sb strings.Builder - for _, p := range proof.PartialSumPolys { - - sb.WriteString("\t{") - for i := 0; i < len(p); i++ { - sb.WriteString(p[i].String()) - if i+1 < len(p) { - sb.WriteString(", ") - } - } - sb.WriteString("}\n") - } - - lazyClaim := singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if err = Verify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())); err != nil { - return err - } - - proof.PartialSumPolys[0][0].Add(&proof.PartialSumPolys[0][0], test_vector_utils.ToElement(1)) - lazyClaim = singleMultilinLazyClaim{g: poly, claimedSum: poly.Sum()} - if Verify(lazyClaim, proof, fiatshamir.WithHash(hashGenerator())) == nil { - return fmt.Errorf("bad proof accepted") - } - return nil -} - -func TestSumcheckDeterministicHashSingleClaimMultilin(t *testing.T) { - //printMsws(36) - - polys := [][]uint64{ - {1, 2, 3, 4}, // 1 + 2X₁ + X₂ - {1, 2, 3, 4, 5, 6, 7, 8}, // 1 + 4X₁ + 2X₂ + X₃ - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, // 1 + 8X₁ + 4X₂ + 2X₃ + X₄ - } - - const MaxStep = 4 - const MaxStart = 4 - hashGens := make([]func() hash.Hash, 0, MaxStart*MaxStep) - - for step := 0; step < MaxStep; step++ { - for startState := 0; startState < MaxStart; startState++ { - if step == 0 && startState == 1 { // unlucky case where a bad proof would be accepted - continue - } - hashGens = append(hashGens, test_vector_utils.NewMessageCounterGenerator(startState, step)) - } - } - - for _, poly := range polys { - for _, hashGen := range hashGens { - assert.NoError(t, testSumcheckSingleClaimMultilin(poly, hashGen), - "failed with poly %v and hashGen %v", poly, hashGen()) - } - } -} diff --git a/ecc/bw6-756/fr/test_vector_utils/test_vector_utils.go b/ecc/bw6-756/fr/test_vector_utils/test_vector_utils.go deleted file mode 100644 index 125f2d838a..0000000000 --- a/ecc/bw6-756/fr/test_vector_utils/test_vector_utils.go +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package test_vector_utils - -import ( - "fmt" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/polynomial" - "hash" - "reflect" - "strings" -) - -func ToElement(i int64) *fr.Element { - var res fr.Element - res.SetInt64(i) - return &res -} - -type HashDescription map[string]interface{} - -func HashFromDescription(d HashDescription) (hash.Hash, error) { - if _type, ok := d["type"]; ok { - switch _type { - case "const": - startState := int64(d["val"].(float64)) - return &MessageCounter{startState: startState, step: 0, state: startState}, nil - default: - return nil, fmt.Errorf("unknown fake hash type \"%s\"", _type) - } - } - return nil, fmt.Errorf("hash description missing type") -} - -type MessageCounter struct { - startState int64 - state int64 - step int64 -} - -func (m *MessageCounter) Write(p []byte) (n int, err error) { - inputBlockSize := (len(p)-1)/fr.Bytes + 1 - m.state += int64(inputBlockSize) * m.step - return len(p), nil -} - -func (m *MessageCounter) Sum(b []byte) []byte { - inputBlockSize := (len(b)-1)/fr.Bytes + 1 - resI := m.state + int64(inputBlockSize)*m.step - var res fr.Element - res.SetInt64(int64(resI)) - resBytes := res.Bytes() - return resBytes[:] -} - -func (m *MessageCounter) Reset() { - m.state = m.startState -} - -func (m *MessageCounter) Size() int { - return fr.Bytes -} - -func (m *MessageCounter) BlockSize() int { - return fr.Bytes -} - -func NewMessageCounter(startState, step int) hash.Hash { - transcript := &MessageCounter{startState: int64(startState), state: int64(startState), step: int64(step)} - return transcript -} - -func NewMessageCounterGenerator(startState, step int) func() hash.Hash { - return func() hash.Hash { - return NewMessageCounter(startState, step) - } -} - -type ListHash []fr.Element - -func (h *ListHash) Write(p []byte) (n int, err error) { - return len(p), nil -} - -func (h *ListHash) Sum(b []byte) []byte { - res := (*h)[0].Bytes() - *h = (*h)[1:] - return res[:] -} - -func (h *ListHash) Reset() { -} - -func (h *ListHash) Size() int { - return fr.Bytes -} - -func (h *ListHash) BlockSize() int { - return fr.Bytes -} -func SetElement(z *fr.Element, value interface{}) (*fr.Element, error) { - - // TODO: Put this in element.SetString? - switch v := value.(type) { - case string: - - if sep := strings.Split(v, "/"); len(sep) == 2 { - var denom fr.Element - if _, err := z.SetString(sep[0]); err != nil { - return nil, err - } - if _, err := denom.SetString(sep[1]); err != nil { - return nil, err - } - denom.Inverse(&denom) - z.Mul(z, &denom) - return z, nil - } - - case float64: - asInt := int64(v) - if float64(asInt) != v { - return nil, fmt.Errorf("cannot currently parse float") - } - z.SetInt64(asInt) - return z, nil - } - - return z.SetInterface(value) -} - -func SliceToElementSlice[T any](slice []T) ([]fr.Element, error) { - elementSlice := make([]fr.Element, len(slice)) - for i, v := range slice { - if _, err := SetElement(&elementSlice[i], v); err != nil { - return nil, err - } - } - return elementSlice, nil -} - -func SliceEquals(a []fr.Element, b []fr.Element) error { - if len(a) != len(b) { - return fmt.Errorf("length mismatch %d≠%d", len(a), len(b)) - } - for i := range a { - if !a[i].Equal(&b[i]) { - return fmt.Errorf("at index %d: %s ≠ %s", i, a[i].String(), b[i].String()) - } - } - return nil -} - -func SliceSliceEquals(a [][]fr.Element, b [][]fr.Element) error { - if len(a) != len(b) { - return fmt.Errorf("length mismatch %d≠%d", len(a), len(b)) - } - for i := range a { - if err := SliceEquals(a[i], b[i]); err != nil { - return fmt.Errorf("at index %d: %w", i, err) - } - } - return nil -} - -func PolynomialSliceEquals(a []polynomial.Polynomial, b []polynomial.Polynomial) error { - if len(a) != len(b) { - return fmt.Errorf("length mismatch %d≠%d", len(a), len(b)) - } - for i := range a { - if err := SliceEquals(a[i], b[i]); err != nil { - return fmt.Errorf("at index %d: %w", i, err) - } - } - return nil -} - -func ElementToInterface(x *fr.Element) interface{} { - if i := x.BigInt(nil); i != nil { - return i - } - return x.Text(10) -} - -func ElementSliceToInterfaceSlice(x interface{}) []interface{} { - if x == nil { - return nil - } - - X := reflect.ValueOf(x) - - res := make([]interface{}, X.Len()) - for i := range res { - xI := X.Index(i).Interface().(fr.Element) - res[i] = ElementToInterface(&xI) - } - return res -} - -func ElementSliceSliceToInterfaceSliceSlice(x interface{}) [][]interface{} { - if x == nil { - return nil - } - - X := reflect.ValueOf(x) - - res := make([][]interface{}, X.Len()) - for i := range res { - res[i] = ElementSliceToInterfaceSlice(X.Index(i).Interface()) - } - - return res -} diff --git a/ecc/bw6-756/fr/vector.go b/ecc/bw6-756/fr/vector.go deleted file mode 100644 index ee8dc421db..0000000000 --- a/ecc/bw6-756/fr/vector.go +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" - "runtime" - "strings" - "sync" - "sync/atomic" - "unsafe" -) - -// Vector represents a slice of Element. -// -// It implements the following interfaces: -// - Stringer -// - io.WriterTo -// - io.ReaderFrom -// - encoding.BinaryMarshaler -// - encoding.BinaryUnmarshaler -// - sort.Interface -type Vector []Element - -// MarshalBinary implements encoding.BinaryMarshaler -func (vector *Vector) MarshalBinary() (data []byte, err error) { - var buf bytes.Buffer - - if _, err = vector.WriteTo(&buf); err != nil { - return - } - return buf.Bytes(), nil -} - -// UnmarshalBinary implements encoding.BinaryUnmarshaler -func (vector *Vector) UnmarshalBinary(data []byte) error { - r := bytes.NewReader(data) - _, err := vector.ReadFrom(r) - return err -} - -// WriteTo implements io.WriterTo and writes a vector of big endian encoded Element. -// Length of the vector is encoded as a uint32 on the first 4 bytes. -func (vector *Vector) WriteTo(w io.Writer) (int64, error) { - // encode slice length - if err := binary.Write(w, binary.BigEndian, uint32(len(*vector))); err != nil { - return 0, err - } - - n := int64(4) - - var buf [Bytes]byte - for i := 0; i < len(*vector); i++ { - BigEndian.PutElement(&buf, (*vector)[i]) - m, err := w.Write(buf[:]) - n += int64(m) - if err != nil { - return n, err - } - } - return n, nil -} - -// AsyncReadFrom reads a vector of big endian encoded Element. -// Length of the vector must be encoded as a uint32 on the first 4 bytes. -// It consumes the needed bytes from the reader and returns the number of bytes read and an error if any. -// It also returns a channel that will be closed when the validation is done. -// The validation consist of checking that the elements are smaller than the modulus, and -// converting them to montgomery form. -func (vector *Vector) AsyncReadFrom(r io.Reader) (int64, error, chan error) { - chErr := make(chan error, 1) - var buf [Bytes]byte - if read, err := io.ReadFull(r, buf[:4]); err != nil { - close(chErr) - return int64(read), err, chErr - } - sliceLen := binary.BigEndian.Uint32(buf[:4]) - - n := int64(4) - (*vector) = make(Vector, sliceLen) - if sliceLen == 0 { - close(chErr) - return n, nil, chErr - } - - bSlice := unsafe.Slice((*byte)(unsafe.Pointer(&(*vector)[0])), sliceLen*Bytes) - read, err := io.ReadFull(r, bSlice) - n += int64(read) - if err != nil { - close(chErr) - return n, err, chErr - } - - go func() { - var cptErrors uint64 - // process the elements in parallel - execute(int(sliceLen), func(start, end int) { - - var z Element - for i := start; i < end; i++ { - // we have to set vector[i] - bstart := i * Bytes - bend := bstart + Bytes - b := bSlice[bstart:bend] - z[0] = binary.BigEndian.Uint64(b[40:48]) - z[1] = binary.BigEndian.Uint64(b[32:40]) - z[2] = binary.BigEndian.Uint64(b[24:32]) - z[3] = binary.BigEndian.Uint64(b[16:24]) - z[4] = binary.BigEndian.Uint64(b[8:16]) - z[5] = binary.BigEndian.Uint64(b[0:8]) - - if !z.smallerThanModulus() { - atomic.AddUint64(&cptErrors, 1) - return - } - z.toMont() - (*vector)[i] = z - } - }) - - if cptErrors > 0 { - chErr <- fmt.Errorf("async read: %d elements failed validation", cptErrors) - } - close(chErr) - }() - return n, nil, chErr -} - -// ReadFrom implements io.ReaderFrom and reads a vector of big endian encoded Element. -// Length of the vector must be encoded as a uint32 on the first 4 bytes. -func (vector *Vector) ReadFrom(r io.Reader) (int64, error) { - - var buf [Bytes]byte - if read, err := io.ReadFull(r, buf[:4]); err != nil { - return int64(read), err - } - sliceLen := binary.BigEndian.Uint32(buf[:4]) - - n := int64(4) - (*vector) = make(Vector, sliceLen) - - for i := 0; i < int(sliceLen); i++ { - read, err := io.ReadFull(r, buf[:]) - n += int64(read) - if err != nil { - return n, err - } - (*vector)[i], err = BigEndian.Element(&buf) - if err != nil { - return n, err - } - } - - return n, nil -} - -// String implements fmt.Stringer interface -func (vector Vector) String() string { - var sbb strings.Builder - sbb.WriteByte('[') - for i := 0; i < len(vector); i++ { - sbb.WriteString(vector[i].String()) - if i != len(vector)-1 { - sbb.WriteByte(',') - } - } - sbb.WriteByte(']') - return sbb.String() -} - -// Len is the number of elements in the collection. -func (vector Vector) Len() int { - return len(vector) -} - -// Less reports whether the element with -// index i should sort before the element with index j. -func (vector Vector) Less(i, j int) bool { - return vector[i].Cmp(&vector[j]) == -1 -} - -// Swap swaps the elements with indexes i and j. -func (vector Vector) Swap(i, j int) { - vector[i], vector[j] = vector[j], vector[i] -} - -// TODO @gbotrel make a public package out of that. -// execute executes the work function in parallel. -// this is copy paste from internal/parallel/parallel.go -// as we don't want to generate code importing internal/ -func execute(nbIterations int, work func(int, int), maxCpus ...int) { - - nbTasks := runtime.NumCPU() - if len(maxCpus) == 1 { - nbTasks = maxCpus[0] - if nbTasks < 1 { - nbTasks = 1 - } else if nbTasks > 512 { - nbTasks = 512 - } - } - - if nbTasks == 1 { - // no go routines - work(0, nbIterations) - return - } - - nbIterationsPerCpus := nbIterations / nbTasks - - // more CPUs than tasks: a CPU will work on exactly one iteration - if nbIterationsPerCpus < 1 { - nbIterationsPerCpus = 1 - nbTasks = nbIterations - } - - var wg sync.WaitGroup - - extraTasks := nbIterations - (nbTasks * nbIterationsPerCpus) - extraTasksOffset := 0 - - for i := 0; i < nbTasks; i++ { - wg.Add(1) - _start := i*nbIterationsPerCpus + extraTasksOffset - _end := _start + nbIterationsPerCpus - if extraTasks > 0 { - _end++ - extraTasks-- - extraTasksOffset++ - } - go func() { - work(_start, _end) - wg.Done() - }() - } - - wg.Wait() -} diff --git a/ecc/bw6-756/fr/vector_test.go b/ecc/bw6-756/fr/vector_test.go deleted file mode 100644 index e58f2d9a38..0000000000 --- a/ecc/bw6-756/fr/vector_test.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package fr - -import ( - "bytes" - "github.com/stretchr/testify/require" - "reflect" - "sort" - "testing" -) - -func TestVectorSort(t *testing.T) { - assert := require.New(t) - - v := make(Vector, 3) - v[0].SetUint64(2) - v[1].SetUint64(3) - v[2].SetUint64(1) - - sort.Sort(v) - - assert.Equal("[1,2,3]", v.String()) -} - -func TestVectorRoundTrip(t *testing.T) { - assert := require.New(t) - - v1 := make(Vector, 3) - v1[0].SetUint64(2) - v1[1].SetUint64(3) - v1[2].SetUint64(1) - - b, err := v1.MarshalBinary() - assert.NoError(err) - - var v2, v3 Vector - - err = v2.UnmarshalBinary(b) - assert.NoError(err) - - err = v3.unmarshalBinaryAsync(b) - assert.NoError(err) - - assert.True(reflect.DeepEqual(v1, v2)) - assert.True(reflect.DeepEqual(v3, v2)) -} - -func TestVectorEmptyRoundTrip(t *testing.T) { - assert := require.New(t) - - v1 := make(Vector, 0) - - b, err := v1.MarshalBinary() - assert.NoError(err) - - var v2, v3 Vector - - err = v2.UnmarshalBinary(b) - assert.NoError(err) - - err = v3.unmarshalBinaryAsync(b) - assert.NoError(err) - - assert.True(reflect.DeepEqual(v1, v2)) - assert.True(reflect.DeepEqual(v3, v2)) -} - -func (vector *Vector) unmarshalBinaryAsync(data []byte) error { - r := bytes.NewReader(data) - _, err, chErr := vector.AsyncReadFrom(r) - if err != nil { - return err - } - return <-chErr -} diff --git a/ecc/bw6-756/g1.go b/ecc/bw6-756/g1.go deleted file mode 100644 index ff42d69f40..0000000000 --- a/ecc/bw6-756/g1.go +++ /dev/null @@ -1,1232 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bw6756 - -import ( - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/internal/parallel" - "math/big" - "runtime" -) - -// G1Affine is a point in affine coordinates (x,y) -type G1Affine struct { - X, Y fp.Element -} - -// G1Jac is a point in Jacobian coordinates (x=X/Z², y=Y/Z³) -type G1Jac struct { - X, Y, Z fp.Element -} - -// g1JacExtended is a point in extended Jacobian coordinates (x=X/ZZ, y=Y/ZZZ, ZZ³=ZZZ²) -type g1JacExtended struct { - X, Y, ZZ, ZZZ fp.Element -} - -// ------------------------------------------------------------------------------------------------- -// Affine coordinates - -// Set sets p to a in affine coordinates. -func (p *G1Affine) Set(a *G1Affine) *G1Affine { - p.X, p.Y = a.X, a.Y - return p -} - -// setInfinity sets p to the infinity point, which is encoded as (0,0). -// N.B.: (0,0) is never on the curve for j=0 curves (Y²=X³+B). -func (p *G1Affine) setInfinity() *G1Affine { - p.X.SetZero() - p.Y.SetZero() - return p -} - -// ScalarMultiplication computes and returns p = [s]a -// where p and a are affine points. -func (p *G1Affine) ScalarMultiplication(a *G1Affine, s *big.Int) *G1Affine { - var _p G1Jac - _p.FromAffine(a) - _p.mulGLV(&_p, s) - p.FromJacobian(&_p) - return p -} - -// ScalarMultiplicationBase computes and returns p = [s]g -// where g is the affine point generating the prime subgroup. -func (p *G1Affine) ScalarMultiplicationBase(s *big.Int) *G1Affine { - var _p G1Jac - _p.mulGLV(&g1Gen, s) - p.FromJacobian(&_p) - return p -} - -// Add adds two points in affine coordinates. -// It uses the Jacobian addition with a.Z=b.Z=1 and converts the result to affine coordinates. -// -// https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-mmadd-2007-bl -func (p *G1Affine) Add(a, b *G1Affine) *G1Affine { - var q G1Jac - // a is infinity, return b - if a.IsInfinity() { - p.Set(b) - return p - } - // b is infinity, return a - if b.IsInfinity() { - p.Set(a) - return p - } - if a.X.Equal(&b.X) { - // if b == a, we double instead - if a.Y.Equal(&b.Y) { - q.DoubleMixed(a) - return p.FromJacobian(&q) - } else { - // if b == -a, we return 0 - return p.setInfinity() - } - } - var H, HH, I, J, r, V fp.Element - H.Sub(&b.X, &a.X) - HH.Square(&H) - I.Double(&HH).Double(&I) - J.Mul(&H, &I) - r.Sub(&b.Y, &a.Y) - r.Double(&r) - V.Mul(&a.X, &I) - q.X.Square(&r). - Sub(&q.X, &J). - Sub(&q.X, &V). - Sub(&q.X, &V) - q.Y.Sub(&V, &q.X). - Mul(&q.Y, &r) - J.Mul(&a.Y, &J).Double(&J) - q.Y.Sub(&q.Y, &J) - q.Z.Double(&H) - - return p.FromJacobian(&q) -} - -// Double doubles a point in affine coordinates. -// It converts the point to Jacobian coordinates, doubles it using Jacobian -// addition with a.Z=1, and converts it back to affine coordinates. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-mdbl-2007-bl -func (p *G1Affine) Double(a *G1Affine) *G1Affine { - var q G1Jac - q.FromAffine(a) - q.DoubleMixed(a) - p.FromJacobian(&q) - return p -} - -// Sub subtracts two points in affine coordinates. -// It uses a similar approach to Add, but negates the second point before adding. -func (p *G1Affine) Sub(a, b *G1Affine) *G1Affine { - var bneg G1Affine - bneg.Neg(b) - p.Add(a, &bneg) - return p -} - -// Equal tests if two points in affine coordinates are equal. -func (p *G1Affine) Equal(a *G1Affine) bool { - return p.X.Equal(&a.X) && p.Y.Equal(&a.Y) -} - -// Neg sets p to the affine negative point -a = (a.X, -a.Y). -func (p *G1Affine) Neg(a *G1Affine) *G1Affine { - p.X = a.X - p.Y.Neg(&a.Y) - return p -} - -// FromJacobian converts a point p1 from Jacobian to affine coordinates. -func (p *G1Affine) FromJacobian(p1 *G1Jac) *G1Affine { - - var a, b fp.Element - - if p1.Z.IsZero() { - p.X.SetZero() - p.Y.SetZero() - return p - } - - a.Inverse(&p1.Z) - b.Square(&a) - p.X.Mul(&p1.X, &b) - p.Y.Mul(&p1.Y, &b).Mul(&p.Y, &a) - - return p -} - -// String returns the string representation E(x,y) of the affine point p or "O" if it is infinity. -func (p *G1Affine) String() string { - if p.IsInfinity() { - return "O" - } - return "E([" + p.X.String() + "," + p.Y.String() + "])" -} - -// IsInfinity checks if the affine point p is infinity, which is encoded as (0,0). -// N.B.: (0,0) is never on the curve for j=0 curves (Y²=X³+B). -func (p *G1Affine) IsInfinity() bool { - return p.X.IsZero() && p.Y.IsZero() -} - -// IsOnCurve returns true if the affine point p in on the curve. -func (p *G1Affine) IsOnCurve() bool { - var point G1Jac - point.FromAffine(p) - return point.IsOnCurve() // call this function to handle infinity point -} - -// IsInSubGroup returns true if the affine point p is in the correct subgroup, false otherwise. -func (p *G1Affine) IsInSubGroup() bool { - var _p G1Jac - _p.FromAffine(p) - return _p.IsInSubGroup() -} - -// ------------------------------------------------------------------------------------------------- -// Jacobian coordinates - -// Set sets p to a in Jacobian coordinates. -func (p *G1Jac) Set(q *G1Jac) *G1Jac { - p.X, p.Y, p.Z = q.X, q.Y, q.Z - return p -} - -// Equal tests if two points in Jacobian coordinates are equal. -func (p *G1Jac) Equal(q *G1Jac) bool { - // If one point is infinity, the other must also be infinity. - if p.Z.IsZero() { - return q.Z.IsZero() - } - // If the other point is infinity, return false since we can't - // the following checks would be incorrect. - if q.Z.IsZero() { - return false - } - - var pZSquare, aZSquare fp.Element - pZSquare.Square(&p.Z) - aZSquare.Square(&q.Z) - - var lhs, rhs fp.Element - lhs.Mul(&p.X, &aZSquare) - rhs.Mul(&q.X, &pZSquare) - if !lhs.Equal(&rhs) { - return false - } - lhs.Mul(&p.Y, &aZSquare).Mul(&lhs, &q.Z) - rhs.Mul(&q.Y, &pZSquare).Mul(&rhs, &p.Z) - - return lhs.Equal(&rhs) -} - -// Neg sets p to the Jacobian negative point -q = (q.X, -q.Y, q.Z). -func (p *G1Jac) Neg(q *G1Jac) *G1Jac { - *p = *q - p.Y.Neg(&q.Y) - return p -} - -// AddAssign sets p to p+a in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl -func (p *G1Jac) AddAssign(q *G1Jac) *G1Jac { - - // p is infinity, return q - if p.Z.IsZero() { - p.Set(q) - return p - } - - // q is infinity, return p - if q.Z.IsZero() { - return p - } - - var Z1Z1, Z2Z2, U1, U2, S1, S2, H, I, J, r, V fp.Element - Z1Z1.Square(&q.Z) - Z2Z2.Square(&p.Z) - U1.Mul(&q.X, &Z2Z2) - U2.Mul(&p.X, &Z1Z1) - S1.Mul(&q.Y, &p.Z). - Mul(&S1, &Z2Z2) - S2.Mul(&p.Y, &q.Z). - Mul(&S2, &Z1Z1) - - // if p == q, we double instead - if U1.Equal(&U2) && S1.Equal(&S2) { - return p.DoubleAssign() - } - - H.Sub(&U2, &U1) - I.Double(&H). - Square(&I) - J.Mul(&H, &I) - r.Sub(&S2, &S1).Double(&r) - V.Mul(&U1, &I) - p.X.Square(&r). - Sub(&p.X, &J). - Sub(&p.X, &V). - Sub(&p.X, &V) - p.Y.Sub(&V, &p.X). - Mul(&p.Y, &r) - S1.Mul(&S1, &J).Double(&S1) - p.Y.Sub(&p.Y, &S1) - p.Z.Add(&p.Z, &q.Z) - p.Z.Square(&p.Z). - Sub(&p.Z, &Z1Z1). - Sub(&p.Z, &Z2Z2). - Mul(&p.Z, &H) - - return p -} - -// SubAssign sets p to p-a in Jacobian coordinates. -// It uses a similar approach to AddAssign, but negates the point a before adding. -func (p *G1Jac) SubAssign(q *G1Jac) *G1Jac { - var tmp G1Jac - tmp.Set(q) - tmp.Y.Neg(&tmp.Y) - p.AddAssign(&tmp) - return p -} - -// Double sets p to [2]q in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2007-bl -func (p *G1Jac) DoubleMixed(a *G1Affine) *G1Jac { - var XX, YY, YYYY, S, M, T fp.Element - XX.Square(&a.X) - YY.Square(&a.Y) - YYYY.Square(&YY) - S.Add(&a.X, &YY). - Square(&S). - Sub(&S, &XX). - Sub(&S, &YYYY). - Double(&S) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - T.Square(&M). - Sub(&T, &S). - Sub(&T, &S) - p.X.Set(&T) - p.Y.Sub(&S, &T). - Mul(&p.Y, &M) - YYYY.Double(&YYYY). - Double(&YYYY). - Double(&YYYY) - p.Y.Sub(&p.Y, &YYYY) - p.Z.Double(&a.Y) - - return p -} - -// AddMixed sets p to p+a in Jacobian coordinates, where a.Z = 1. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl -func (p *G1Jac) AddMixed(a *G1Affine) *G1Jac { - - //if a is infinity return p - if a.IsInfinity() { - return p - } - // p is infinity, return a - if p.Z.IsZero() { - p.X = a.X - p.Y = a.Y - p.Z.SetOne() - return p - } - - var Z1Z1, U2, S2, H, HH, I, J, r, V fp.Element - Z1Z1.Square(&p.Z) - U2.Mul(&a.X, &Z1Z1) - S2.Mul(&a.Y, &p.Z). - Mul(&S2, &Z1Z1) - - // if p == a, we double instead - if U2.Equal(&p.X) && S2.Equal(&p.Y) { - return p.DoubleMixed(a) - } - - H.Sub(&U2, &p.X) - HH.Square(&H) - I.Double(&HH).Double(&I) - J.Mul(&H, &I) - r.Sub(&S2, &p.Y).Double(&r) - V.Mul(&p.X, &I) - p.X.Square(&r). - Sub(&p.X, &J). - Sub(&p.X, &V). - Sub(&p.X, &V) - J.Mul(&J, &p.Y).Double(&J) - p.Y.Sub(&V, &p.X). - Mul(&p.Y, &r) - p.Y.Sub(&p.Y, &J) - p.Z.Add(&p.Z, &H) - p.Z.Square(&p.Z). - Sub(&p.Z, &Z1Z1). - Sub(&p.Z, &HH) - - return p -} - -// Double sets p to [2]q in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2007-bl -func (p *G1Jac) Double(q *G1Jac) *G1Jac { - p.Set(q) - p.DoubleAssign() - return p -} - -// DoubleAssign doubles p in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2007-bl -func (p *G1Jac) DoubleAssign() *G1Jac { - - var XX, YY, YYYY, ZZ, S, M, T fp.Element - - XX.Square(&p.X) - YY.Square(&p.Y) - YYYY.Square(&YY) - ZZ.Square(&p.Z) - S.Add(&p.X, &YY) - S.Square(&S). - Sub(&S, &XX). - Sub(&S, &YYYY). - Double(&S) - M.Double(&XX).Add(&M, &XX) - p.Z.Add(&p.Z, &p.Y). - Square(&p.Z). - Sub(&p.Z, &YY). - Sub(&p.Z, &ZZ) - T.Square(&M) - p.X = T - T.Double(&S) - p.X.Sub(&p.X, &T) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M) - YYYY.Double(&YYYY).Double(&YYYY).Double(&YYYY) - p.Y.Sub(&p.Y, &YYYY) - - return p -} - -// ScalarMultiplication computes and returns p = [s]a -// where p and a are Jacobian points. -// using the GLV technique. -// see https://www.iacr.org/archive/crypto2001/21390189.pdf -func (p *G1Jac) ScalarMultiplication(q *G1Jac, s *big.Int) *G1Jac { - return p.mulGLV(q, s) -} - -// ScalarMultiplicationBase computes and returns p = [s]g -// where g is the prime subgroup generator. -func (p *G1Jac) ScalarMultiplicationBase(s *big.Int) *G1Jac { - return p.mulGLV(&g1Gen, s) - -} - -// String converts p to affine coordinates and returns its string representation E(x,y) or "O" if it is infinity. -func (p *G1Jac) String() string { - _p := G1Affine{} - _p.FromJacobian(p) - return _p.String() -} - -// FromAffine converts a point a from affine to Jacobian coordinates. -func (p *G1Jac) FromAffine(a *G1Affine) *G1Jac { - if a.IsInfinity() { - p.Z.SetZero() - p.X.SetOne() - p.Y.SetOne() - return p - } - p.Z.SetOne() - p.X.Set(&a.X) - p.Y.Set(&a.Y) - return p -} - -// IsOnCurve returns true if the Jacobian point p in on the curve. -func (p *G1Jac) IsOnCurve() bool { - var left, right, tmp, ZZ fp.Element - left.Square(&p.Y) - right.Square(&p.X).Mul(&right, &p.X) - ZZ.Square(&p.Z) - tmp.Square(&ZZ).Mul(&tmp, &ZZ) - // Mul tmp by bCurveCoeff=1 (nothing to do) - right.Add(&right, &tmp) - return left.Equal(&right) -} - -// IsInSubGroup returns true if p is on the r-torsion, false otherwise. - -// Z[r,0]+Z[-lambdaG1Affine, 1] is the kernel -// of (u,v)->u+lambdaG1Affinev mod r. Expressing r, lambdaG1Affine as -// polynomials in x, a short vector of this Zmodule is -// (x+1), (x³-x²+1). So we check that (x+1)p+(x³-x²+1)ϕ(p) -// is the infinity. -func (p *G1Jac) IsInSubGroup() bool { - - var res, phip G1Jac - phip.phi(p) - res.ScalarMultiplication(&phip, &xGen). - SubAssign(&phip). - ScalarMultiplication(&res, &xGen). - ScalarMultiplication(&res, &xGen). - AddAssign(&phip) - - phip.ScalarMultiplication(p, &xGen).AddAssign(p).AddAssign(&res) - - return phip.IsOnCurve() && phip.Z.IsZero() - -} - -// mulWindowed computes the 2-bits windowed double-and-add scalar -// multiplication p=[s]q in Jacobian coordinates. -func (p *G1Jac) mulWindowed(q *G1Jac, s *big.Int) *G1Jac { - - var res G1Jac - var ops [3]G1Jac - - ops[0].Set(q) - if s.Sign() == -1 { - ops[0].Neg(&ops[0]) - } - res.Set(&g1Infinity) - ops[1].Double(&ops[0]) - ops[2].Set(&ops[0]).AddAssign(&ops[1]) - - b := s.Bytes() - for i := range b { - w := b[i] - mask := byte(0xc0) - for j := 0; j < 4; j++ { - res.DoubleAssign().DoubleAssign() - c := (w & mask) >> (6 - 2*j) - if c != 0 { - res.AddAssign(&ops[c-1]) - } - mask = mask >> 2 - } - } - p.Set(&res) - - return p - -} - -// phi sets p to ϕ(a) where ϕ: (x,y) → (w x,y), -// where w is a third root of unity. -func (p *G1Jac) phi(q *G1Jac) *G1Jac { - p.Set(q) - p.X.Mul(&p.X, &thirdRootOneG1) - return p -} - -// mulGLV computes the scalar multiplication using a windowed-GLV method -// -// see https://www.iacr.org/archive/crypto2001/21390189.pdf -func (p *G1Jac) mulGLV(q *G1Jac, s *big.Int) *G1Jac { - - var table [15]G1Jac - var res G1Jac - var k1, k2 fr.Element - - res.Set(&g1Infinity) - - // table[b3b2b1b0-1] = b3b2 ⋅ ϕ(q) + b1b0*q - table[0].Set(q) - table[3].phi(q) - - // split the scalar, modifies ±q, ϕ(q) accordingly - k := ecc.SplitScalar(s, &glvBasis) - - if k[0].Sign() == -1 { - k[0].Neg(&k[0]) - table[0].Neg(&table[0]) - } - if k[1].Sign() == -1 { - k[1].Neg(&k[1]) - table[3].Neg(&table[3]) - } - - // precompute table (2 bits sliding window) - // table[b3b2b1b0-1] = b3b2 ⋅ ϕ(q) + b1b0 ⋅ q if b3b2b1b0 != 0 - table[1].Double(&table[0]) - table[2].Set(&table[1]).AddAssign(&table[0]) - table[4].Set(&table[3]).AddAssign(&table[0]) - table[5].Set(&table[3]).AddAssign(&table[1]) - table[6].Set(&table[3]).AddAssign(&table[2]) - table[7].Double(&table[3]) - table[8].Set(&table[7]).AddAssign(&table[0]) - table[9].Set(&table[7]).AddAssign(&table[1]) - table[10].Set(&table[7]).AddAssign(&table[2]) - table[11].Set(&table[7]).AddAssign(&table[3]) - table[12].Set(&table[11]).AddAssign(&table[0]) - table[13].Set(&table[11]).AddAssign(&table[1]) - table[14].Set(&table[11]).AddAssign(&table[2]) - - // bounds on the lattice base vectors guarantee that k1, k2 are len(r)/2 or len(r)/2+1 bits long max - // this is because we use a probabilistic scalar decomposition that replaces a division by a right-shift - k1 = k1.SetBigInt(&k[0]).Bits() - k2 = k2.SetBigInt(&k[1]).Bits() - - // we don't target constant-timeness so we check first if we increase the bounds or not - maxBit := k1.BitLen() - if k2.BitLen() > maxBit { - maxBit = k2.BitLen() - } - hiWordIndex := (maxBit - 1) / 64 - - // loop starts from len(k1)/2 or len(k1)/2+1 due to the bounds - for i := hiWordIndex; i >= 0; i-- { - mask := uint64(3) << 62 - for j := 0; j < 32; j++ { - res.Double(&res).Double(&res) - b1 := (k1[i] & mask) >> (62 - 2*j) - b2 := (k2[i] & mask) >> (62 - 2*j) - if b1|b2 != 0 { - s := (b2<<2 | b1) - res.AddAssign(&table[s-1]) - } - mask = mask >> 2 - } - } - - p.Set(&res) - return p -} - -// ClearCofactor maps a point in curve to r-torsion -func (p *G1Affine) ClearCofactor(a *G1Affine) *G1Affine { - var _p G1Jac - _p.FromAffine(a) - _p.ClearCofactor(&_p) - p.FromJacobian(&_p) - return p -} - -// ClearCofactor maps a point in E(Fp) to E(Fp)[r] -func (p *G1Jac) ClearCofactor(q *G1Jac) *G1Jac { - - var L0, L1, uP, u2P, u3P, tmp G1Jac - - uP.ScalarMultiplication(q, &xGen) - u2P.ScalarMultiplication(&uP, &xGen) - u3P.ScalarMultiplication(&u2P, &xGen) - - L0.Set(q).AddAssign(&u3P). - SubAssign(&u2P) - tmp.Set(q).AddAssign(&u2P). - SubAssign(&uP). - SubAssign(&uP). - Double(&tmp) - L0.SubAssign(&tmp). - SubAssign(q) - - L1.Set(q).AddAssign(&uP) - tmp.Set(&uP).SubAssign(q). - Double(&tmp). - SubAssign(&u2P) - L1.AddAssign(&tmp). - SubAssign(q) - - p.phi(&L1). - AddAssign(&L0) - - return p -} - -// JointScalarMultiplication computes [s1]a1+[s2]a2 using Strauss-Shamir technique -// where a1 and a2 are affine points. -func (p *G1Jac) JointScalarMultiplication(a1, a2 *G1Affine, s1, s2 *big.Int) *G1Jac { - - var res, p1, p2 G1Jac - res.Set(&g1Infinity) - p1.FromAffine(a1) - p2.FromAffine(a2) - - var table [15]G1Jac - - var k1, k2 big.Int - if s1.Sign() == -1 { - k1.Neg(s1) - table[0].Neg(&p1) - } else { - k1.Set(s1) - table[0].Set(&p1) - } - if s2.Sign() == -1 { - k2.Neg(s2) - table[3].Neg(&p2) - } else { - k2.Set(s2) - table[3].Set(&p2) - } - - // precompute table (2 bits sliding window) - table[1].Double(&table[0]) - table[2].Set(&table[1]).AddAssign(&table[0]) - table[4].Set(&table[3]).AddAssign(&table[0]) - table[5].Set(&table[3]).AddAssign(&table[1]) - table[6].Set(&table[3]).AddAssign(&table[2]) - table[7].Double(&table[3]) - table[8].Set(&table[7]).AddAssign(&table[0]) - table[9].Set(&table[7]).AddAssign(&table[1]) - table[10].Set(&table[7]).AddAssign(&table[2]) - table[11].Set(&table[7]).AddAssign(&table[3]) - table[12].Set(&table[11]).AddAssign(&table[0]) - table[13].Set(&table[11]).AddAssign(&table[1]) - table[14].Set(&table[11]).AddAssign(&table[2]) - - var s [2]fr.Element - s[0] = s[0].SetBigInt(&k1).Bits() - s[1] = s[1].SetBigInt(&k2).Bits() - - maxBit := k1.BitLen() - if k2.BitLen() > maxBit { - maxBit = k2.BitLen() - } - hiWordIndex := (maxBit - 1) / 64 - - for i := hiWordIndex; i >= 0; i-- { - mask := uint64(3) << 62 - for j := 0; j < 32; j++ { - res.Double(&res).Double(&res) - b1 := (s[0][i] & mask) >> (62 - 2*j) - b2 := (s[1][i] & mask) >> (62 - 2*j) - if b1|b2 != 0 { - s := (b2<<2 | b1) - res.AddAssign(&table[s-1]) - } - mask = mask >> 2 - } - } - - p.Set(&res) - return p - -} - -// JointScalarMultiplicationBase computes [s1]g+[s2]a using Straus-Shamir technique -// where g is the prime subgroup generator. -func (p *G1Jac) JointScalarMultiplicationBase(a *G1Affine, s1, s2 *big.Int) *G1Jac { - return p.JointScalarMultiplication(&g1GenAff, a, s1, s2) - -} - -// ------------------------------------------------------------------------------------------------- -// extended Jacobian coordinates - -// Set sets p to a in extended Jacobian coordinates. -func (p *g1JacExtended) Set(q *g1JacExtended) *g1JacExtended { - p.X, p.Y, p.ZZ, p.ZZZ = q.X, q.Y, q.ZZ, q.ZZZ - return p -} - -// setInfinity sets p to the infinity point (1,1,0,0). -func (p *g1JacExtended) setInfinity() *g1JacExtended { - p.X.SetOne() - p.Y.SetOne() - p.ZZ = fp.Element{} - p.ZZZ = fp.Element{} - return p -} - -// IsInfinity checks if the p is infinity, i.e. p.ZZ=0. -func (p *g1JacExtended) IsInfinity() bool { - return p.ZZ.IsZero() -} - -// fromJacExtended converts an extended Jacobian point to an affine point. -func (p *G1Affine) fromJacExtended(q *g1JacExtended) *G1Affine { - if q.ZZ.IsZero() { - p.X = fp.Element{} - p.Y = fp.Element{} - return p - } - p.X.Inverse(&q.ZZ).Mul(&p.X, &q.X) - p.Y.Inverse(&q.ZZZ).Mul(&p.Y, &q.Y) - return p -} - -// fromJacExtended converts an extended Jacobian point to a Jacobian point. -func (p *G1Jac) fromJacExtended(q *g1JacExtended) *G1Jac { - if q.ZZ.IsZero() { - p.Set(&g1Infinity) - return p - } - p.X.Mul(&q.ZZ, &q.X).Mul(&p.X, &q.ZZ) - p.Y.Mul(&q.ZZZ, &q.Y).Mul(&p.Y, &q.ZZZ) - p.Z.Set(&q.ZZZ) - return p -} - -// unsafeFromJacExtended converts an extended Jacobian point, distinct from Infinity, to a Jacobian point. -func (p *G1Jac) unsafeFromJacExtended(q *g1JacExtended) *G1Jac { - p.X.Square(&q.ZZ).Mul(&p.X, &q.X) - p.Y.Square(&q.ZZZ).Mul(&p.Y, &q.Y) - p.Z = q.ZZZ - return p -} - -// add sets p to p+q in extended Jacobian coordinates. -// -// https://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#addition-add-2008-s -func (p *g1JacExtended) add(q *g1JacExtended) *g1JacExtended { - //if q is infinity return p - if q.ZZ.IsZero() { - return p - } - // p is infinity, return q - if p.ZZ.IsZero() { - p.Set(q) - return p - } - - var A, B, U1, U2, S1, S2 fp.Element - - // p2: q, p1: p - U2.Mul(&q.X, &p.ZZ) - U1.Mul(&p.X, &q.ZZ) - A.Sub(&U2, &U1) - S2.Mul(&q.Y, &p.ZZZ) - S1.Mul(&p.Y, &q.ZZZ) - B.Sub(&S2, &S1) - - if A.IsZero() { - if B.IsZero() { - return p.double(q) - - } - p.ZZ = fp.Element{} - p.ZZZ = fp.Element{} - return p - } - - var P, R, PP, PPP, Q, V fp.Element - P.Sub(&U2, &U1) - R.Sub(&S2, &S1) - PP.Square(&P) - PPP.Mul(&P, &PP) - Q.Mul(&U1, &PP) - V.Mul(&S1, &PPP) - - p.X.Square(&R). - Sub(&p.X, &PPP). - Sub(&p.X, &Q). - Sub(&p.X, &Q) - p.Y.Sub(&Q, &p.X). - Mul(&p.Y, &R). - Sub(&p.Y, &V) - p.ZZ.Mul(&p.ZZ, &q.ZZ). - Mul(&p.ZZ, &PP) - p.ZZZ.Mul(&p.ZZZ, &q.ZZZ). - Mul(&p.ZZZ, &PPP) - - return p -} - -// double sets p to [2]q in Jacobian extended coordinates. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-dbl-2008-s-1 -// N.B.: since we consider any point on Z=0 as the point at infinity -// this doubling formula works for infinity points as well. -func (p *g1JacExtended) double(q *g1JacExtended) *g1JacExtended { - var U, V, W, S, XX, M fp.Element - - U.Double(&q.Y) - V.Square(&U) - W.Mul(&U, &V) - S.Mul(&q.X, &V) - XX.Square(&q.X) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - U.Mul(&W, &q.Y) - - p.X.Square(&M). - Sub(&p.X, &S). - Sub(&p.X, &S) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M). - Sub(&p.Y, &U) - p.ZZ.Mul(&V, &q.ZZ) - p.ZZZ.Mul(&W, &q.ZZZ) - - return p -} - -// addMixed sets p to p+q in extended Jacobian coordinates, where a.ZZ=1. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#addition-madd-2008-s -func (p *g1JacExtended) addMixed(a *G1Affine) *g1JacExtended { - - //if a is infinity return p - if a.IsInfinity() { - return p - } - // p is infinity, return a - if p.ZZ.IsZero() { - p.X = a.X - p.Y = a.Y - p.ZZ.SetOne() - p.ZZZ.SetOne() - return p - } - - var P, R fp.Element - - // p2: a, p1: p - P.Mul(&a.X, &p.ZZ) - P.Sub(&P, &p.X) - - R.Mul(&a.Y, &p.ZZZ) - R.Sub(&R, &p.Y) - - if P.IsZero() { - if R.IsZero() { - return p.doubleMixed(a) - - } - p.ZZ = fp.Element{} - p.ZZZ = fp.Element{} - return p - } - - var PP, PPP, Q, Q2, RR, X3, Y3 fp.Element - - PP.Square(&P) - PPP.Mul(&P, &PP) - Q.Mul(&p.X, &PP) - RR.Square(&R) - X3.Sub(&RR, &PPP) - Q2.Double(&Q) - p.X.Sub(&X3, &Q2) - Y3.Sub(&Q, &p.X).Mul(&Y3, &R) - R.Mul(&p.Y, &PPP) - p.Y.Sub(&Y3, &R) - p.ZZ.Mul(&p.ZZ, &PP) - p.ZZZ.Mul(&p.ZZZ, &PPP) - - return p - -} - -// subMixed works the same as addMixed, but negates a.Y. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#addition-madd-2008-s -func (p *g1JacExtended) subMixed(a *G1Affine) *g1JacExtended { - - //if a is infinity return p - if a.IsInfinity() { - return p - } - // p is infinity, return a - if p.ZZ.IsZero() { - p.X = a.X - p.Y.Neg(&a.Y) - p.ZZ.SetOne() - p.ZZZ.SetOne() - return p - } - - var P, R fp.Element - - // p2: a, p1: p - P.Mul(&a.X, &p.ZZ) - P.Sub(&P, &p.X) - - R.Mul(&a.Y, &p.ZZZ) - R.Neg(&R) - R.Sub(&R, &p.Y) - - if P.IsZero() { - if R.IsZero() { - return p.doubleNegMixed(a) - - } - p.ZZ = fp.Element{} - p.ZZZ = fp.Element{} - return p - } - - var PP, PPP, Q, Q2, RR, X3, Y3 fp.Element - - PP.Square(&P) - PPP.Mul(&P, &PP) - Q.Mul(&p.X, &PP) - RR.Square(&R) - X3.Sub(&RR, &PPP) - Q2.Double(&Q) - p.X.Sub(&X3, &Q2) - Y3.Sub(&Q, &p.X).Mul(&Y3, &R) - R.Mul(&p.Y, &PPP) - p.Y.Sub(&Y3, &R) - p.ZZ.Mul(&p.ZZ, &PP) - p.ZZZ.Mul(&p.ZZZ, &PPP) - - return p - -} - -// doubleNegMixed works the same as double, but negates q.Y. -func (p *g1JacExtended) doubleNegMixed(a *G1Affine) *g1JacExtended { - - var U, V, W, S, XX, M, S2, L fp.Element - - U.Double(&a.Y) - U.Neg(&U) - V.Square(&U) - W.Mul(&U, &V) - S.Mul(&a.X, &V) - XX.Square(&a.X) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - S2.Double(&S) - L.Mul(&W, &a.Y) - - p.X.Square(&M). - Sub(&p.X, &S2) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M). - Add(&p.Y, &L) - p.ZZ.Set(&V) - p.ZZZ.Set(&W) - - return p -} - -// doubleMixed sets p to [2]a in Jacobian extended coordinates, where a.ZZ=1. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-dbl-2008-s-1 -func (p *g1JacExtended) doubleMixed(a *G1Affine) *g1JacExtended { - - var U, V, W, S, XX, M, S2, L fp.Element - - U.Double(&a.Y) - V.Square(&U) - W.Mul(&U, &V) - S.Mul(&a.X, &V) - XX.Square(&a.X) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - S2.Double(&S) - L.Mul(&W, &a.Y) - - p.X.Square(&M). - Sub(&p.X, &S2) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M). - Sub(&p.Y, &L) - p.ZZ.Set(&V) - p.ZZZ.Set(&W) - - return p -} - -// BatchJacobianToAffineG1 converts points in Jacobian coordinates to Affine coordinates -// performing a single field inversion using the Montgomery batch inversion trick. -func BatchJacobianToAffineG1(points []G1Jac) []G1Affine { - result := make([]G1Affine, len(points)) - zeroes := make([]bool, len(points)) - accumulator := fp.One() - - // batch invert all points[].Z coordinates with Montgomery batch inversion trick - // (stores points[].Z^-1 in result[i].X to avoid allocating a slice of fr.Elements) - for i := 0; i < len(points); i++ { - if points[i].Z.IsZero() { - zeroes[i] = true - continue - } - result[i].X = accumulator - accumulator.Mul(&accumulator, &points[i].Z) - } - - var accInverse fp.Element - accInverse.Inverse(&accumulator) - - for i := len(points) - 1; i >= 0; i-- { - if zeroes[i] { - // do nothing, (X=0, Y=0) is infinity point in affine - continue - } - result[i].X.Mul(&result[i].X, &accInverse) - accInverse.Mul(&accInverse, &points[i].Z) - } - - // batch convert to affine. - parallel.Execute(len(points), func(start, end int) { - for i := start; i < end; i++ { - if zeroes[i] { - // do nothing, (X=0, Y=0) is infinity point in affine - continue - } - var a, b fp.Element - a = result[i].X - b.Square(&a) - result[i].X.Mul(&points[i].X, &b) - result[i].Y.Mul(&points[i].Y, &b). - Mul(&result[i].Y, &a) - } - }) - - return result -} - -// BatchScalarMultiplicationG1 multiplies the same base by all scalars -// and return resulting points in affine coordinates -// uses a simple windowed-NAF-like multiplication algorithm. -func BatchScalarMultiplicationG1(base *G1Affine, scalars []fr.Element) []G1Affine { - // approximate cost in group ops is - // cost = 2^{c-1} + n(scalar.nbBits+nbChunks) - - nbPoints := uint64(len(scalars)) - min := ^uint64(0) - bestC := 0 - for c := 2; c <= 16; c++ { - cost := uint64(1 << (c - 1)) // pre compute the table - nbChunks := computeNbChunks(uint64(c)) - cost += nbPoints * (uint64(c) + 1) * nbChunks // doublings + point add - if cost < min { - min = cost - bestC = c - } - } - c := uint64(bestC) // window size - nbChunks := int(computeNbChunks(c)) - - // last window may be slightly larger than c; in which case we need to compute one - // extra element in the baseTable - maxC := lastC(c) - if c > maxC { - maxC = c - } - - // precompute all powers of base for our window - // note here that if performance is critical, we can implement as in the msmX methods - // this allocation to be on the stack - baseTable := make([]G1Jac, (1 << (maxC - 1))) - baseTable[0].FromAffine(base) - for i := 1; i < len(baseTable); i++ { - baseTable[i] = baseTable[i-1] - baseTable[i].AddMixed(base) - } - // convert our base exp table into affine to use AddMixed - baseTableAff := BatchJacobianToAffineG1(baseTable) - toReturn := make([]G1Jac, len(scalars)) - - // partition the scalars into digits - digits, _ := partitionScalars(scalars, c, runtime.NumCPU()) - - // for each digit, take value in the base table, double it c time, voilà. - parallel.Execute(len(scalars), func(start, end int) { - var p G1Jac - for i := start; i < end; i++ { - p.Set(&g1Infinity) - for chunk := nbChunks - 1; chunk >= 0; chunk-- { - if chunk != nbChunks-1 { - for j := uint64(0); j < c; j++ { - p.DoubleAssign() - } - } - offset := chunk * len(scalars) - digit := digits[i+offset] - - if digit == 0 { - continue - } - - // if msbWindow bit is set, we need to subtract - if digit&1 == 0 { - // add - p.AddMixed(&baseTableAff[(digit>>1)-1]) - } else { - // sub - t := baseTableAff[digit>>1] - t.Neg(&t) - p.AddMixed(&t) - } - } - - // set our result point - toReturn[i] = p - - } - }) - toReturnAff := BatchJacobianToAffineG1(toReturn) - return toReturnAff -} - -// batchAddG1Affine adds affine points using the Montgomery batch inversion trick. -// Special cases (doubling, infinity) must be filtered out before this call. -func batchAddG1Affine[TP pG1Affine, TPP ppG1Affine, TC cG1Affine](R *TPP, P *TP, batchSize int) { - var lambda, lambdain TC - - // add part - for j := 0; j < batchSize; j++ { - lambdain[j].Sub(&(*P)[j].X, &(*R)[j].X) - } - - // invert denominator using montgomery batch invert technique - { - var accumulator fp.Element - lambda[0].SetOne() - accumulator.Set(&lambdain[0]) - - for i := 1; i < batchSize; i++ { - lambda[i] = accumulator - accumulator.Mul(&accumulator, &lambdain[i]) - } - - accumulator.Inverse(&accumulator) - - for i := batchSize - 1; i > 0; i-- { - lambda[i].Mul(&lambda[i], &accumulator) - accumulator.Mul(&accumulator, &lambdain[i]) - } - lambda[0].Set(&accumulator) - } - - var d fp.Element - var rr G1Affine - - // add part - for j := 0; j < batchSize; j++ { - // computa lambda - d.Sub(&(*P)[j].Y, &(*R)[j].Y) - lambda[j].Mul(&lambda[j], &d) - - // compute X, Y - rr.X.Square(&lambda[j]) - rr.X.Sub(&rr.X, &(*R)[j].X) - rr.X.Sub(&rr.X, &(*P)[j].X) - d.Sub(&(*R)[j].X, &rr.X) - rr.Y.Mul(&lambda[j], &d) - rr.Y.Sub(&rr.Y, &(*R)[j].Y) - (*R)[j].Set(&rr) - } -} diff --git a/ecc/bw6-756/g1_test.go b/ecc/bw6-756/g1_test.go deleted file mode 100644 index 007f6c501a..0000000000 --- a/ecc/bw6-756/g1_test.go +++ /dev/null @@ -1,885 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bw6756 - -import ( - "fmt" - "math/big" - "math/rand/v2" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -func TestG1AffineEndomorphism(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BW6-756] check that phi(P) = lambdaGLV * P", prop.ForAll( - func(a fp.Element) bool { - var p, res1, res2 G1Jac - g := MapToG1(a) - p.FromAffine(&g) - res1.phi(&p) - res2.mulWindowed(&p, &lambdaGLV) - - return p.IsInSubGroup() && res1.Equal(&res2) - }, - GenFp(), - )) - - properties.Property("[BW6-756] check that phi^2(P) + phi(P) + P = 0", prop.ForAll( - func(a fp.Element) bool { - var p, res, tmp G1Jac - g := MapToG1(a) - p.FromAffine(&g) - tmp.phi(&p) - res.phi(&tmp). - AddAssign(&tmp). - AddAssign(&p) - - return res.Z.IsZero() - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG1AffineIsOnCurve(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BW6-756] g1Gen (affine) should be on the curve", prop.ForAll( - func(a fp.Element) bool { - var op1, op2 G1Affine - op1.FromJacobian(&g1Gen) - op2.Set(&op1) - op2.Y.Mul(&op2.Y, &a) - return op1.IsOnCurve() && !op2.IsOnCurve() - }, - GenFp(), - )) - - properties.Property("[BW6-756] g1Gen (Jacobian) should be on the curve", prop.ForAll( - func(a fp.Element) bool { - var op1, op2, op3 G1Jac - op1.Set(&g1Gen) - op3.Set(&g1Gen) - - op2 = fuzzG1Jac(&g1Gen, a) - op3.Y.Mul(&op3.Y, &a) - return op1.IsOnCurve() && op2.IsOnCurve() && !op3.IsOnCurve() - }, - GenFp(), - )) - - properties.Property("[BW6-756] IsInSubGroup and MulBy subgroup order should be the same", prop.ForAll( - func(a fp.Element) bool { - var op1, op2 G1Jac - op1 = fuzzG1Jac(&g1Gen, a) - _r := fr.Modulus() - op2.ScalarMultiplication(&op1, _r) - return op1.IsInSubGroup() && op2.Z.IsZero() - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG1AffineConversions(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BW6-756] Affine representation should be independent of the Jacobian representative", prop.ForAll( - func(a fp.Element) bool { - g := fuzzG1Jac(&g1Gen, a) - var op1 G1Affine - op1.FromJacobian(&g) - return op1.X.Equal(&g1Gen.X) && op1.Y.Equal(&g1Gen.Y) - }, - GenFp(), - )) - - properties.Property("[BW6-756] Affine representation should be independent of a Extended Jacobian representative", prop.ForAll( - func(a fp.Element) bool { - var g g1JacExtended - g.X.Set(&g1Gen.X) - g.Y.Set(&g1Gen.Y) - g.ZZ.Set(&g1Gen.Z) - g.ZZZ.Set(&g1Gen.Z) - gfuzz := fuzzg1JacExtended(&g, a) - - var op1 G1Affine - op1.fromJacExtended(&gfuzz) - return op1.X.Equal(&g1Gen.X) && op1.Y.Equal(&g1Gen.Y) - }, - GenFp(), - )) - - properties.Property("[BW6-756] Jacobian representation should be the same as the affine representative", prop.ForAll( - func(a fp.Element) bool { - var g G1Jac - var op1 G1Affine - op1.X.Set(&g1Gen.X) - op1.Y.Set(&g1Gen.Y) - - var one fp.Element - one.SetOne() - - g.FromAffine(&op1) - - return g.X.Equal(&g1Gen.X) && g.Y.Equal(&g1Gen.Y) && g.Z.Equal(&one) - }, - GenFp(), - )) - - properties.Property("[BW6-756] Converting affine symbol for infinity to Jacobian should output correct infinity in Jacobian", prop.ForAll( - func() bool { - var g G1Affine - g.X.SetZero() - g.Y.SetZero() - var op1 G1Jac - op1.FromAffine(&g) - var one, zero fp.Element - one.SetOne() - return op1.X.Equal(&one) && op1.Y.Equal(&one) && op1.Z.Equal(&zero) - }, - )) - - properties.Property("[BW6-756] Converting infinity in extended Jacobian to affine should output infinity symbol in Affine", prop.ForAll( - func() bool { - var g G1Affine - var op1 g1JacExtended - var zero fp.Element - op1.X.Set(&g1Gen.X) - op1.Y.Set(&g1Gen.Y) - g.fromJacExtended(&op1) - return g.X.Equal(&zero) && g.Y.Equal(&zero) - }, - )) - - properties.Property("[BW6-756] Converting infinity in extended Jacobian to Jacobian should output infinity in Jacobian", prop.ForAll( - func() bool { - var g G1Jac - var op1 g1JacExtended - var zero, one fp.Element - one.SetOne() - op1.X.Set(&g1Gen.X) - op1.Y.Set(&g1Gen.Y) - g.fromJacExtended(&op1) - return g.X.Equal(&one) && g.Y.Equal(&one) && g.Z.Equal(&zero) - }, - )) - - properties.Property("[BW6-756] [Jacobian] Two representatives of the same class should be equal", prop.ForAll( - func(a, b fp.Element) bool { - op1 := fuzzG1Jac(&g1Gen, a) - op2 := fuzzG1Jac(&g1Gen, b) - return op1.Equal(&op2) - }, - GenFp(), - GenFp(), - )) - properties.Property("[BW6-756] BatchJacobianToAffineG1 and FromJacobian should output the same result", prop.ForAll( - func(a, b fp.Element) bool { - g1 := fuzzG1Jac(&g1Gen, a) - g2 := fuzzG1Jac(&g1Gen, b) - var op1, op2 G1Affine - op1.FromJacobian(&g1) - op2.FromJacobian(&g2) - baseTableAff := BatchJacobianToAffineG1([]G1Jac{g1, g2}) - return op1.Equal(&baseTableAff[0]) && op2.Equal(&baseTableAff[1]) - }, - GenFp(), - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG1AffineOps(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - parameters.MinSuccessfulTests = 10 - - properties := gopter.NewProperties(parameters) - - genScalar := GenFr() - - properties.Property("[BW6-756] Add(P,-P) should return the point at infinity", prop.ForAll( - func(s fr.Element) bool { - var op1, op2 G1Affine - var sInt big.Int - g := g1GenAff - s.BigInt(&sInt) - op1.ScalarMultiplication(&g, &sInt) - op2.Neg(&op1) - - op1.Add(&op1, &op2) - return op1.IsInfinity() - - }, - GenFr(), - )) - - properties.Property("[BW6-756] Add(P,0) and Add(0,P) should return P", prop.ForAll( - func(s fr.Element) bool { - var op1, op2 G1Affine - var sInt big.Int - g := g1GenAff - s.BigInt(&sInt) - op1.ScalarMultiplication(&g, &sInt) - op2.setInfinity() - - op1.Add(&op1, &op2) - op2.Add(&op2, &op1) - return op1.Equal(&op2) - - }, - GenFr(), - )) - - properties.Property("[BW6-756] Add should call double when adding the same point", prop.ForAll( - func(s fr.Element) bool { - var op1, op2 G1Affine - var sInt big.Int - g := g1GenAff - s.BigInt(&sInt) - op1.ScalarMultiplication(&g, &sInt) - - op2.Double(&op1) - op1.Add(&op1, &op1) - return op1.Equal(&op2) - - }, - GenFr(), - )) - - properties.Property("[BW6-756] [2]G = double(G) + G - G", prop.ForAll( - func(s fr.Element) bool { - var sInt big.Int - g := g1GenAff - s.BigInt(&sInt) - g.ScalarMultiplication(&g, &sInt) - var op1, op2 G1Affine - op1.ScalarMultiplication(&g, big.NewInt(2)) - op2.Double(&g) - op2.Add(&op2, &g) - op2.Sub(&op2, &g) - return op1.Equal(&op2) - }, - GenFr(), - )) - - properties.Property("[BW6-756] [-s]G = -[s]G", prop.ForAll( - func(s fr.Element) bool { - g := g1GenAff - var gj G1Jac - var nbs, bs big.Int - s.BigInt(&bs) - nbs.Neg(&bs) - - var res = true - - // mulGLV - { - var op1, op2 G1Affine - op1.ScalarMultiplication(&g, &bs).Neg(&op1) - op2.ScalarMultiplication(&g, &nbs) - res = res && op1.Equal(&op2) - } - - // mulWindowed - { - var op1, op2 G1Jac - op1.mulWindowed(&gj, &bs).Neg(&op1) - op2.mulWindowed(&gj, &nbs) - res = res && op1.Equal(&op2) - } - - return res - }, - GenFr(), - )) - - properties.Property("[BW6-756] [Jacobian] Add should call double when adding the same point", prop.ForAll( - func(a, b fp.Element) bool { - fop1 := fuzzG1Jac(&g1Gen, a) - fop2 := fuzzG1Jac(&g1Gen, b) - var op1, op2 G1Jac - op1.Set(&fop1).AddAssign(&fop2) - op2.Double(&fop2) - return op1.Equal(&op2) - }, - GenFp(), - GenFp(), - )) - - properties.Property("[BW6-756] [Jacobian] Adding the opposite of a point to itself should output inf", prop.ForAll( - func(a, b fp.Element) bool { - fop1 := fuzzG1Jac(&g1Gen, a) - fop2 := fuzzG1Jac(&g1Gen, b) - fop2.Neg(&fop2) - fop1.AddAssign(&fop2) - return fop1.Equal(&g1Infinity) - }, - GenFp(), - GenFp(), - )) - - properties.Property("[BW6-756] [Jacobian] Adding the inf to a point should not modify the point", prop.ForAll( - func(a fp.Element) bool { - fop1 := fuzzG1Jac(&g1Gen, a) - fop1.AddAssign(&g1Infinity) - var op2 G1Jac - op2.Set(&g1Infinity) - op2.AddAssign(&g1Gen) - return fop1.Equal(&g1Gen) && op2.Equal(&g1Gen) - }, - GenFp(), - )) - - properties.Property("[BW6-756] [Jacobian Extended] addMixed (-G) should equal subMixed(G)", prop.ForAll( - func(a fp.Element) bool { - fop1 := fuzzG1Jac(&g1Gen, a) - var p1, p1Neg G1Affine - p1.FromJacobian(&fop1) - p1Neg = p1 - p1Neg.Y.Neg(&p1Neg.Y) - var o1, o2 g1JacExtended - o1.addMixed(&p1Neg) - o2.subMixed(&p1) - - return o1.X.Equal(&o2.X) && - o1.Y.Equal(&o2.Y) && - o1.ZZ.Equal(&o2.ZZ) && - o1.ZZZ.Equal(&o2.ZZZ) - }, - GenFp(), - )) - - properties.Property("[BW6-756] [Jacobian Extended] doubleMixed (-G) should equal doubleNegMixed(G)", prop.ForAll( - func(a fp.Element) bool { - fop1 := fuzzG1Jac(&g1Gen, a) - var p1, p1Neg G1Affine - p1.FromJacobian(&fop1) - p1Neg = p1 - p1Neg.Y.Neg(&p1Neg.Y) - var o1, o2 g1JacExtended - o1.doubleMixed(&p1Neg) - o2.doubleNegMixed(&p1) - - return o1.X.Equal(&o2.X) && - o1.Y.Equal(&o2.Y) && - o1.ZZ.Equal(&o2.ZZ) && - o1.ZZZ.Equal(&o2.ZZZ) - }, - GenFp(), - )) - - properties.Property("[BW6-756] [Jacobian] Addmix the negation to itself should output 0", prop.ForAll( - func(a fp.Element) bool { - fop1 := fuzzG1Jac(&g1Gen, a) - fop1.Neg(&fop1) - var op2 G1Affine - op2.FromJacobian(&g1Gen) - fop1.AddMixed(&op2) - return fop1.Equal(&g1Infinity) - }, - GenFp(), - )) - - properties.Property("[BW6-756] scalar multiplication (double and add) should depend only on the scalar mod r", prop.ForAll( - func(s fr.Element) bool { - - r := fr.Modulus() - var g G1Jac - g.ScalarMultiplication(&g1Gen, r) - - var scalar, blindedScalar, rminusone big.Int - var op1, op2, op3, gneg G1Jac - rminusone.SetUint64(1).Sub(r, &rminusone) - op3.mulWindowed(&g1Gen, &rminusone) - gneg.Neg(&g1Gen) - s.BigInt(&scalar) - blindedScalar.Mul(&scalar, r).Add(&blindedScalar, &scalar) - op1.mulWindowed(&g1Gen, &scalar) - op2.mulWindowed(&g1Gen, &blindedScalar) - - return op1.Equal(&op2) && g.Equal(&g1Infinity) && !op1.Equal(&g1Infinity) && gneg.Equal(&op3) - - }, - genScalar, - )) - - properties.Property("[BW6-756] scalar multiplication (GLV) should depend only on the scalar mod r", prop.ForAll( - func(s fr.Element) bool { - - r := fr.Modulus() - var g G1Jac - g.mulGLV(&g1Gen, r) - - var scalar, blindedScalar, rminusone big.Int - var op1, op2, op3, gneg G1Jac - rminusone.SetUint64(1).Sub(r, &rminusone) - op3.ScalarMultiplication(&g1Gen, &rminusone) - gneg.Neg(&g1Gen) - s.BigInt(&scalar) - blindedScalar.Mul(&scalar, r).Add(&blindedScalar, &scalar) - op1.ScalarMultiplication(&g1Gen, &scalar) - op2.ScalarMultiplication(&g1Gen, &blindedScalar) - - return op1.Equal(&op2) && g.Equal(&g1Infinity) && !op1.Equal(&g1Infinity) && gneg.Equal(&op3) - - }, - genScalar, - )) - - properties.Property("[BW6-756] GLV and Double and Add should output the same result", prop.ForAll( - func(s fr.Element) bool { - - var r big.Int - var op1, op2 G1Jac - s.BigInt(&r) - op1.mulWindowed(&g1Gen, &r) - op2.mulGLV(&g1Gen, &r) - return op1.Equal(&op2) && !op1.Equal(&g1Infinity) - - }, - genScalar, - )) - - properties.Property("[BW6-756] JointScalarMultiplicationBase and ScalarMultiplication should output the same results", prop.ForAll( - func(s1, s2 fr.Element) bool { - - var op1, op2, temp G1Jac - - op1.JointScalarMultiplicationBase(&g1GenAff, s1.BigInt(new(big.Int)), s2.BigInt(new(big.Int))) - temp.ScalarMultiplication(&g1Gen, s2.BigInt(new(big.Int))) - op2.ScalarMultiplication(&g1Gen, s1.BigInt(new(big.Int))). - AddAssign(&temp) - - return op1.Equal(&op2) - - }, - genScalar, - genScalar, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG1AffineCofactorCleaning(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BW6-756] Clearing the cofactor of a random point should set it in the r-torsion", prop.ForAll( - func() bool { - var a, x, b fp.Element - a.SetRandom() - - x.Square(&a).Mul(&x, &a).Add(&x, &bCurveCoeff) - - for x.Legendre() != 1 { - a.SetRandom() - - x.Square(&a).Mul(&x, &a).Add(&x, &bCurveCoeff) - - } - - b.Sqrt(&x) - var point, pointCleared, infinity G1Jac - point.X.Set(&a) - point.Y.Set(&b) - point.Z.SetOne() - pointCleared.ClearCofactor(&point) - infinity.Set(&g1Infinity) - return point.IsOnCurve() && pointCleared.IsInSubGroup() && !pointCleared.Equal(&infinity) - }, - )) - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestG1AffineBatchScalarMultiplication(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzzShort - } - - properties := gopter.NewProperties(parameters) - - genScalar := GenFr() - - // size of the multiExps - const nbSamples = 10 - - properties.Property("[BW6-756] BatchScalarMultiplication should be consistent with individual scalar multiplications", prop.ForAll( - func(mixer fr.Element) bool { - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - result := BatchScalarMultiplicationG1(&g1GenAff, sampleScalars[:]) - - if len(result) != len(sampleScalars) { - return false - } - - for i := 0; i < len(result); i++ { - var expectedJac G1Jac - var expected G1Affine - var b big.Int - expectedJac.ScalarMultiplication(&g1Gen, sampleScalars[i].BigInt(&b)) - expected.FromJacobian(&expectedJac) - if !result[i].Equal(&expected) { - return false - } - } - return true - }, - genScalar, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -// ------------------------------------------------------------ -// benches - -func BenchmarkG1JacIsInSubGroup(b *testing.B) { - var a G1Jac - a.Set(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.IsInSubGroup() - } - -} - -func BenchmarkG1JacEqual(b *testing.B) { - var scalar fp.Element - if _, err := scalar.SetRandom(); err != nil { - b.Fatalf("failed to set scalar: %s", err) - } - - var a G1Jac - a.ScalarMultiplication(&g1Gen, big.NewInt(42)) - - b.Run("equal", func(b *testing.B) { - var scalarSquared fp.Element - scalarSquared.Square(&scalar) - - aZScaled := a - aZScaled.X.Mul(&aZScaled.X, &scalarSquared) - aZScaled.Y.Mul(&aZScaled.Y, &scalarSquared).Mul(&aZScaled.Y, &scalar) - aZScaled.Z.Mul(&aZScaled.Z, &scalar) - - // Check the setup. - if !a.Equal(&aZScaled) { - b.Fatalf("invalid test setup") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Equal(&aZScaled) - } - }) - - b.Run("not equal", func(b *testing.B) { - var aPlus1 G1Jac - aPlus1.AddAssign(&g1Gen) - - // Check the setup. - if a.Equal(&aPlus1) { - b.Fatalf("invalid test setup") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Equal(&aPlus1) - } - }) -} - -func BenchmarkBatchAddG1Affine(b *testing.B) { - - var P, R pG1AffineC16 - var RR ppG1AffineC16 - ridx := make([]int, len(P)) - - // TODO P == R may produce skewed benches - fillBenchBasesG1(P[:]) - fillBenchBasesG1(R[:]) - - for i := 0; i < len(ridx); i++ { - ridx[i] = i - } - - // random permute - rand.Shuffle(len(ridx), func(i, j int) { ridx[i], ridx[j] = ridx[j], ridx[i] }) - - for i, ri := range ridx { - RR[i] = &R[ri] - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - batchAddG1Affine[pG1AffineC16, ppG1AffineC16, cG1AffineC16](&RR, &P, len(P)) - } -} - -func BenchmarkG1AffineBatchScalarMultiplication(b *testing.B) { - // ensure every words of the scalars are filled - var mixer fr.Element - mixer.SetString("7716837800905789770901243404444209691916730933998574719964609384059111546487") - - const pow = 15 - const nbSamples = 1 << pow - - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - for i := 5; i <= pow; i++ { - using := 1 << i - - b.Run(fmt.Sprintf("%d points", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - _ = BatchScalarMultiplicationG1(&g1GenAff, sampleScalars[:using]) - } - }) - } -} - -func BenchmarkG1JacScalarMultiplication(b *testing.B) { - - var scalar big.Int - r := fr.Modulus() - scalar.SetString("5243587517512619047944770508185965837690552500527637822603658699938581184513", 10) - scalar.Add(&scalar, r) - - var doubleAndAdd G1Jac - - b.Run("double and add", func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - doubleAndAdd.mulWindowed(&g1Gen, &scalar) - } - }) - - var glv G1Jac - b.Run("GLV", func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - glv.mulGLV(&g1Gen, &scalar) - } - }) - -} - -func BenchmarkG1AffineCofactorClearing(b *testing.B) { - var a G1Jac - a.Set(&g1Gen) - for i := 0; i < b.N; i++ { - a.ClearCofactor(&a) - } -} - -func BenchmarkG1JacAdd(b *testing.B) { - var a G1Jac - a.Double(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.AddAssign(&g1Gen) - } -} - -func BenchmarkG1JacAddMixed(b *testing.B) { - var a G1Jac - a.Double(&g1Gen) - - var c G1Affine - c.FromJacobian(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.AddMixed(&c) - } - -} - -func BenchmarkG1JacDouble(b *testing.B) { - var a G1Jac - a.Set(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.DoubleAssign() - } - -} - -func BenchmarkG1JacExtAddMixed(b *testing.B) { - var a g1JacExtended - a.doubleMixed(&g1GenAff) - - var c G1Affine - c.FromJacobian(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.addMixed(&c) - } -} - -func BenchmarkG1JacExtSubMixed(b *testing.B) { - var a g1JacExtended - a.doubleMixed(&g1GenAff) - - var c G1Affine - c.FromJacobian(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.subMixed(&c) - } -} - -func BenchmarkG1JacExtDoubleMixed(b *testing.B) { - var a g1JacExtended - a.doubleMixed(&g1GenAff) - - var c G1Affine - c.FromJacobian(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.doubleMixed(&c) - } -} - -func BenchmarkG1JacExtDoubleNegMixed(b *testing.B) { - var a g1JacExtended - a.doubleMixed(&g1GenAff) - - var c G1Affine - c.FromJacobian(&g1Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.doubleNegMixed(&c) - } -} - -func BenchmarkG1JacExtAdd(b *testing.B) { - var a, c g1JacExtended - a.doubleMixed(&g1GenAff) - c.double(&a) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.add(&c) - } -} - -func BenchmarkG1JacExtDouble(b *testing.B) { - var a g1JacExtended - a.doubleMixed(&g1GenAff) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.double(&a) - } -} - -func BenchmarkG1AffineAdd(b *testing.B) { - var a G1Affine - a.Double(&g1GenAff) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Add(&a, &g1GenAff) - } -} - -func BenchmarkG1AffineDouble(b *testing.B) { - var a G1Affine - a.Double(&g1GenAff) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Double(&a) - } -} - -func fuzzG1Jac(p *G1Jac, f fp.Element) G1Jac { - var res G1Jac - res.X.Mul(&p.X, &f).Mul(&res.X, &f) - res.Y.Mul(&p.Y, &f).Mul(&res.Y, &f).Mul(&res.Y, &f) - res.Z.Mul(&p.Z, &f) - return res -} - -func fuzzg1JacExtended(p *g1JacExtended, f fp.Element) g1JacExtended { - var res g1JacExtended - var ff, fff fp.Element - ff.Square(&f) - fff.Mul(&ff, &f) - res.X.Mul(&p.X, &ff) - res.Y.Mul(&p.Y, &fff) - res.ZZ.Mul(&p.ZZ, &ff) - res.ZZZ.Mul(&p.ZZZ, &fff) - return res -} diff --git a/ecc/bw6-756/g2.go b/ecc/bw6-756/g2.go deleted file mode 100644 index 1c55e6dfd7..0000000000 --- a/ecc/bw6-756/g2.go +++ /dev/null @@ -1,1150 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bw6756 - -import ( - "crypto/rand" - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/internal/parallel" - "math/big" - "runtime" -) - -// G2Affine is a point in affine coordinates (x,y) -type G2Affine struct { - X, Y fp.Element -} - -// G2Jac is a point in Jacobian coordinates (x=X/Z², y=Y/Z³) -type G2Jac struct { - X, Y, Z fp.Element -} - -// g2JacExtended is a point in extended Jacobian coordinates (x=X/ZZ, y=Y/ZZZ, ZZ³=ZZZ²) -type g2JacExtended struct { - X, Y, ZZ, ZZZ fp.Element -} - -// g2Proj point in projective coordinates -type g2Proj struct { - x, y, z fp.Element -} - -// ------------------------------------------------------------------------------------------------- -// Affine coordinates - -// Set sets p to a in affine coordinates. -func (p *G2Affine) Set(a *G2Affine) *G2Affine { - p.X, p.Y = a.X, a.Y - return p -} - -// setInfinity sets p to the infinity point, which is encoded as (0,0). -// N.B.: (0,0) is never on the curve for j=0 curves (Y²=X³+B). -func (p *G2Affine) setInfinity() *G2Affine { - p.X.SetZero() - p.Y.SetZero() - return p -} - -// ScalarMultiplication computes and returns p = [s]a -// where p and a are affine points. -func (p *G2Affine) ScalarMultiplication(a *G2Affine, s *big.Int) *G2Affine { - var _p G2Jac - _p.FromAffine(a) - _p.mulGLV(&_p, s) - p.FromJacobian(&_p) - return p -} - -// ScalarMultiplicationBase computes and returns p = [s]g -// where g is the affine point generating the prime subgroup. -func (p *G2Affine) ScalarMultiplicationBase(s *big.Int) *G2Affine { - var _p G2Jac - _p.mulGLV(&g2Gen, s) - p.FromJacobian(&_p) - return p -} - -// Add adds two points in affine coordinates. -// It uses the Jacobian addition with a.Z=b.Z=1 and converts the result to affine coordinates. -// -// https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-mmadd-2007-bl -func (p *G2Affine) Add(a, b *G2Affine) *G2Affine { - var q G2Jac - // a is infinity, return b - if a.IsInfinity() { - p.Set(b) - return p - } - // b is infinity, return a - if b.IsInfinity() { - p.Set(a) - return p - } - if a.X.Equal(&b.X) { - // if b == a, we double instead - if a.Y.Equal(&b.Y) { - q.DoubleMixed(a) - return p.FromJacobian(&q) - } else { - // if b == -a, we return 0 - return p.setInfinity() - } - } - var H, HH, I, J, r, V fp.Element - H.Sub(&b.X, &a.X) - HH.Square(&H) - I.Double(&HH).Double(&I) - J.Mul(&H, &I) - r.Sub(&b.Y, &a.Y) - r.Double(&r) - V.Mul(&a.X, &I) - q.X.Square(&r). - Sub(&q.X, &J). - Sub(&q.X, &V). - Sub(&q.X, &V) - q.Y.Sub(&V, &q.X). - Mul(&q.Y, &r) - J.Mul(&a.Y, &J).Double(&J) - q.Y.Sub(&q.Y, &J) - q.Z.Double(&H) - - return p.FromJacobian(&q) -} - -// Double doubles a point in affine coordinates. -// It converts the point to Jacobian coordinates, doubles it using Jacobian -// addition with a.Z=1, and converts it back to affine coordinates. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-mdbl-2007-bl -func (p *G2Affine) Double(a *G2Affine) *G2Affine { - var q G2Jac - q.FromAffine(a) - q.DoubleMixed(a) - p.FromJacobian(&q) - return p -} - -// Sub subtracts two points in affine coordinates. -// It uses a similar approach to Add, but negates the second point before adding. -func (p *G2Affine) Sub(a, b *G2Affine) *G2Affine { - var bneg G2Affine - bneg.Neg(b) - p.Add(a, &bneg) - return p -} - -// Equal tests if two points in affine coordinates are equal. -func (p *G2Affine) Equal(a *G2Affine) bool { - return p.X.Equal(&a.X) && p.Y.Equal(&a.Y) -} - -// Neg sets p to the affine negative point -a = (a.X, -a.Y). -func (p *G2Affine) Neg(a *G2Affine) *G2Affine { - p.X = a.X - p.Y.Neg(&a.Y) - return p -} - -// FromJacobian converts a point p1 from Jacobian to affine coordinates. -func (p *G2Affine) FromJacobian(p1 *G2Jac) *G2Affine { - - var a, b fp.Element - - if p1.Z.IsZero() { - p.X.SetZero() - p.Y.SetZero() - return p - } - - a.Inverse(&p1.Z) - b.Square(&a) - p.X.Mul(&p1.X, &b) - p.Y.Mul(&p1.Y, &b).Mul(&p.Y, &a) - - return p -} - -// String returns the string representation E(x,y) of the affine point p or "O" if it is infinity. -func (p *G2Affine) String() string { - if p.IsInfinity() { - return "O" - } - return "E([" + p.X.String() + "," + p.Y.String() + "])" -} - -// IsInfinity checks if the affine point p is infinity, which is encoded as (0,0). -// N.B.: (0,0) is never on the curve for j=0 curves (Y²=X³+B). -func (p *G2Affine) IsInfinity() bool { - return p.X.IsZero() && p.Y.IsZero() -} - -// IsOnCurve returns true if the affine point p in on the curve. -func (p *G2Affine) IsOnCurve() bool { - var point G2Jac - point.FromAffine(p) - return point.IsOnCurve() // call this function to handle infinity point -} - -// IsInSubGroup returns true if the affine point p is in the correct subgroup, false otherwise. -func (p *G2Affine) IsInSubGroup() bool { - var _p G2Jac - _p.FromAffine(p) - return _p.IsInSubGroup() -} - -// ------------------------------------------------------------------------------------------------- -// Jacobian coordinates - -// Set sets p to a in Jacobian coordinates. -func (p *G2Jac) Set(q *G2Jac) *G2Jac { - p.X, p.Y, p.Z = q.X, q.Y, q.Z - return p -} - -// Equal tests if two points in Jacobian coordinates are equal. -func (p *G2Jac) Equal(q *G2Jac) bool { - // If one point is infinity, the other must also be infinity. - if p.Z.IsZero() { - return q.Z.IsZero() - } - // If the other point is infinity, return false since we can't - // the following checks would be incorrect. - if q.Z.IsZero() { - return false - } - - var pZSquare, aZSquare fp.Element - pZSquare.Square(&p.Z) - aZSquare.Square(&q.Z) - - var lhs, rhs fp.Element - lhs.Mul(&p.X, &aZSquare) - rhs.Mul(&q.X, &pZSquare) - if !lhs.Equal(&rhs) { - return false - } - lhs.Mul(&p.Y, &aZSquare).Mul(&lhs, &q.Z) - rhs.Mul(&q.Y, &pZSquare).Mul(&rhs, &p.Z) - - return lhs.Equal(&rhs) -} - -// Neg sets p to the Jacobian negative point -q = (q.X, -q.Y, q.Z). -func (p *G2Jac) Neg(q *G2Jac) *G2Jac { - *p = *q - p.Y.Neg(&q.Y) - return p -} - -// AddAssign sets p to p+a in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl -func (p *G2Jac) AddAssign(q *G2Jac) *G2Jac { - - // p is infinity, return q - if p.Z.IsZero() { - p.Set(q) - return p - } - - // q is infinity, return p - if q.Z.IsZero() { - return p - } - - var Z1Z1, Z2Z2, U1, U2, S1, S2, H, I, J, r, V fp.Element - Z1Z1.Square(&q.Z) - Z2Z2.Square(&p.Z) - U1.Mul(&q.X, &Z2Z2) - U2.Mul(&p.X, &Z1Z1) - S1.Mul(&q.Y, &p.Z). - Mul(&S1, &Z2Z2) - S2.Mul(&p.Y, &q.Z). - Mul(&S2, &Z1Z1) - - // if p == q, we double instead - if U1.Equal(&U2) && S1.Equal(&S2) { - return p.DoubleAssign() - } - - H.Sub(&U2, &U1) - I.Double(&H). - Square(&I) - J.Mul(&H, &I) - r.Sub(&S2, &S1).Double(&r) - V.Mul(&U1, &I) - p.X.Square(&r). - Sub(&p.X, &J). - Sub(&p.X, &V). - Sub(&p.X, &V) - p.Y.Sub(&V, &p.X). - Mul(&p.Y, &r) - S1.Mul(&S1, &J).Double(&S1) - p.Y.Sub(&p.Y, &S1) - p.Z.Add(&p.Z, &q.Z) - p.Z.Square(&p.Z). - Sub(&p.Z, &Z1Z1). - Sub(&p.Z, &Z2Z2). - Mul(&p.Z, &H) - - return p -} - -// SubAssign sets p to p-a in Jacobian coordinates. -// It uses a similar approach to AddAssign, but negates the point a before adding. -func (p *G2Jac) SubAssign(q *G2Jac) *G2Jac { - var tmp G2Jac - tmp.Set(q) - tmp.Y.Neg(&tmp.Y) - p.AddAssign(&tmp) - return p -} - -// Double sets p to [2]q in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2007-bl -func (p *G2Jac) DoubleMixed(a *G2Affine) *G2Jac { - var XX, YY, YYYY, S, M, T fp.Element - XX.Square(&a.X) - YY.Square(&a.Y) - YYYY.Square(&YY) - S.Add(&a.X, &YY). - Square(&S). - Sub(&S, &XX). - Sub(&S, &YYYY). - Double(&S) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - T.Square(&M). - Sub(&T, &S). - Sub(&T, &S) - p.X.Set(&T) - p.Y.Sub(&S, &T). - Mul(&p.Y, &M) - YYYY.Double(&YYYY). - Double(&YYYY). - Double(&YYYY) - p.Y.Sub(&p.Y, &YYYY) - p.Z.Double(&a.Y) - - return p -} - -// AddMixed sets p to p+a in Jacobian coordinates, where a.Z = 1. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl -func (p *G2Jac) AddMixed(a *G2Affine) *G2Jac { - - //if a is infinity return p - if a.IsInfinity() { - return p - } - // p is infinity, return a - if p.Z.IsZero() { - p.X = a.X - p.Y = a.Y - p.Z.SetOne() - return p - } - - var Z1Z1, U2, S2, H, HH, I, J, r, V fp.Element - Z1Z1.Square(&p.Z) - U2.Mul(&a.X, &Z1Z1) - S2.Mul(&a.Y, &p.Z). - Mul(&S2, &Z1Z1) - - // if p == a, we double instead - if U2.Equal(&p.X) && S2.Equal(&p.Y) { - return p.DoubleMixed(a) - } - - H.Sub(&U2, &p.X) - HH.Square(&H) - I.Double(&HH).Double(&I) - J.Mul(&H, &I) - r.Sub(&S2, &p.Y).Double(&r) - V.Mul(&p.X, &I) - p.X.Square(&r). - Sub(&p.X, &J). - Sub(&p.X, &V). - Sub(&p.X, &V) - J.Mul(&J, &p.Y).Double(&J) - p.Y.Sub(&V, &p.X). - Mul(&p.Y, &r) - p.Y.Sub(&p.Y, &J) - p.Z.Add(&p.Z, &H) - p.Z.Square(&p.Z). - Sub(&p.Z, &Z1Z1). - Sub(&p.Z, &HH) - - return p -} - -// Double sets p to [2]q in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2007-bl -func (p *G2Jac) Double(q *G2Jac) *G2Jac { - p.Set(q) - p.DoubleAssign() - return p -} - -// DoubleAssign doubles p in Jacobian coordinates. -// -// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2007-bl -func (p *G2Jac) DoubleAssign() *G2Jac { - - var XX, YY, YYYY, ZZ, S, M, T fp.Element - - XX.Square(&p.X) - YY.Square(&p.Y) - YYYY.Square(&YY) - ZZ.Square(&p.Z) - S.Add(&p.X, &YY) - S.Square(&S). - Sub(&S, &XX). - Sub(&S, &YYYY). - Double(&S) - M.Double(&XX).Add(&M, &XX) - p.Z.Add(&p.Z, &p.Y). - Square(&p.Z). - Sub(&p.Z, &YY). - Sub(&p.Z, &ZZ) - T.Square(&M) - p.X = T - T.Double(&S) - p.X.Sub(&p.X, &T) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M) - YYYY.Double(&YYYY).Double(&YYYY).Double(&YYYY) - p.Y.Sub(&p.Y, &YYYY) - - return p -} - -// ScalarMultiplication computes and returns p = [s]a -// where p and a are Jacobian points. -// using the GLV technique. -// see https://www.iacr.org/archive/crypto2001/21390189.pdf -func (p *G2Jac) ScalarMultiplication(q *G2Jac, s *big.Int) *G2Jac { - return p.mulGLV(q, s) -} - -// ScalarMultiplicationBase computes and returns p = [s]g -// where g is the prime subgroup generator. -func (p *G2Jac) ScalarMultiplicationBase(s *big.Int) *G2Jac { - return p.mulGLV(&g2Gen, s) - -} - -// String converts p to affine coordinates and returns its string representation E(x,y) or "O" if it is infinity. -func (p *G2Jac) String() string { - _p := G2Affine{} - _p.FromJacobian(p) - return _p.String() -} - -// FromAffine converts a point a from affine to Jacobian coordinates. -func (p *G2Jac) FromAffine(a *G2Affine) *G2Jac { - if a.IsInfinity() { - p.Z.SetZero() - p.X.SetOne() - p.Y.SetOne() - return p - } - p.Z.SetOne() - p.X.Set(&a.X) - p.Y.Set(&a.Y) - return p -} - -// IsOnCurve returns true if the Jacobian point p in on the curve. -func (p *G2Jac) IsOnCurve() bool { - var left, right, tmp, ZZ fp.Element - left.Square(&p.Y) - right.Square(&p.X).Mul(&right, &p.X) - ZZ.Square(&p.Z) - tmp.Square(&ZZ).Mul(&tmp, &ZZ) - tmp.Mul(&tmp, &bTwistCurveCoeff) - right.Add(&right, &tmp) - return left.Equal(&right) -} - -// IsInSubGroup returns true if p is on the r-torsion, false otherwise. - -// Z[r,0]+Z[-lambdaG2Affine, 1] is the kernel -// of (u,v)->u+lambdaG2Affinev mod r. Expressing r, lambdaG2Affine as -// polynomials in x, a short vector of this Zmodule is -// (x+1), (x³-x²+1). So we check that (x+1)p+(x³-x²+1)ϕ(p) -// is the infinity. -func (p *G2Jac) IsInSubGroup() bool { - - var res, phip G2Jac - phip.phi(p) - res.ScalarMultiplication(&phip, &xGen). - SubAssign(&phip). - ScalarMultiplication(&res, &xGen). - ScalarMultiplication(&res, &xGen). - AddAssign(&phip) - - phip.ScalarMultiplication(p, &xGen).AddAssign(p).AddAssign(&res) - - return phip.IsOnCurve() && phip.Z.IsZero() - -} - -// mulWindowed computes the 2-bits windowed double-and-add scalar -// multiplication p=[s]q in Jacobian coordinates. -func (p *G2Jac) mulWindowed(q *G2Jac, s *big.Int) *G2Jac { - - var res G2Jac - var ops [3]G2Jac - - ops[0].Set(q) - if s.Sign() == -1 { - ops[0].Neg(&ops[0]) - } - res.Set(&g2Infinity) - ops[1].Double(&ops[0]) - ops[2].Set(&ops[0]).AddAssign(&ops[1]) - - b := s.Bytes() - for i := range b { - w := b[i] - mask := byte(0xc0) - for j := 0; j < 4; j++ { - res.DoubleAssign().DoubleAssign() - c := (w & mask) >> (6 - 2*j) - if c != 0 { - res.AddAssign(&ops[c-1]) - } - mask = mask >> 2 - } - } - p.Set(&res) - - return p - -} - -// phi sets p to ϕ(a) where ϕ: (x,y) → (w x,y), -// where w is a third root of unity. -func (p *G2Jac) phi(q *G2Jac) *G2Jac { - p.Set(q) - p.X.Mul(&p.X, &thirdRootOneG2) - return p -} - -// mulGLV computes the scalar multiplication using a windowed-GLV method -// -// see https://www.iacr.org/archive/crypto2001/21390189.pdf -func (p *G2Jac) mulGLV(q *G2Jac, s *big.Int) *G2Jac { - - var table [15]G2Jac - var res G2Jac - var k1, k2 fr.Element - - res.Set(&g2Infinity) - - // table[b3b2b1b0-1] = b3b2 ⋅ ϕ(q) + b1b0*q - table[0].Set(q) - table[3].phi(q) - - // split the scalar, modifies ±q, ϕ(q) accordingly - k := ecc.SplitScalar(s, &glvBasis) - - if k[0].Sign() == -1 { - k[0].Neg(&k[0]) - table[0].Neg(&table[0]) - } - if k[1].Sign() == -1 { - k[1].Neg(&k[1]) - table[3].Neg(&table[3]) - } - - // precompute table (2 bits sliding window) - // table[b3b2b1b0-1] = b3b2 ⋅ ϕ(q) + b1b0 ⋅ q if b3b2b1b0 != 0 - table[1].Double(&table[0]) - table[2].Set(&table[1]).AddAssign(&table[0]) - table[4].Set(&table[3]).AddAssign(&table[0]) - table[5].Set(&table[3]).AddAssign(&table[1]) - table[6].Set(&table[3]).AddAssign(&table[2]) - table[7].Double(&table[3]) - table[8].Set(&table[7]).AddAssign(&table[0]) - table[9].Set(&table[7]).AddAssign(&table[1]) - table[10].Set(&table[7]).AddAssign(&table[2]) - table[11].Set(&table[7]).AddAssign(&table[3]) - table[12].Set(&table[11]).AddAssign(&table[0]) - table[13].Set(&table[11]).AddAssign(&table[1]) - table[14].Set(&table[11]).AddAssign(&table[2]) - - // bounds on the lattice base vectors guarantee that k1, k2 are len(r)/2 or len(r)/2+1 bits long max - // this is because we use a probabilistic scalar decomposition that replaces a division by a right-shift - k1 = k1.SetBigInt(&k[0]).Bits() - k2 = k2.SetBigInt(&k[1]).Bits() - - // we don't target constant-timeness so we check first if we increase the bounds or not - maxBit := k1.BitLen() - if k2.BitLen() > maxBit { - maxBit = k2.BitLen() - } - hiWordIndex := (maxBit - 1) / 64 - - // loop starts from len(k1)/2 or len(k1)/2+1 due to the bounds - for i := hiWordIndex; i >= 0; i-- { - mask := uint64(3) << 62 - for j := 0; j < 32; j++ { - res.Double(&res).Double(&res) - b1 := (k1[i] & mask) >> (62 - 2*j) - b2 := (k2[i] & mask) >> (62 - 2*j) - if b1|b2 != 0 { - s := (b2<<2 | b1) - res.AddAssign(&table[s-1]) - } - mask = mask >> 2 - } - } - - p.Set(&res) - return p -} - -// ClearCofactor maps a point in curve to r-torsion -func (p *G2Affine) ClearCofactor(a *G2Affine) *G2Affine { - var _p G2Jac - _p.FromAffine(a) - _p.ClearCofactor(&_p) - p.FromJacobian(&_p) - return p -} - -// ClearCofactor maps a point in curve to r-torsion -func (p *G2Jac) ClearCofactor(q *G2Jac) *G2Jac { - var L0, L1, uP, u2P, u3P, tmp G2Jac - - uP.ScalarMultiplication(q, &xGen) - u2P.ScalarMultiplication(&uP, &xGen) - u3P.ScalarMultiplication(&u2P, &xGen) - // ht=-2, hy=0 - // d1=1, d2=-1, d3=-1 - - L0.Set(q). - AddAssign(&u2P). - SubAssign(&uP) - tmp.Set(&u2P). - AddAssign(q). - SubAssign(&uP). - Double(&tmp) - L1.Set(&u3P). - SubAssign(&tmp) - - p.phi(&L0). - AddAssign(&L1) - - return p -} - -// ------------------------------------------------------------------------------------------------- -// extended Jacobian coordinates - -// Set sets p to a in extended Jacobian coordinates. -func (p *g2JacExtended) Set(q *g2JacExtended) *g2JacExtended { - p.X, p.Y, p.ZZ, p.ZZZ = q.X, q.Y, q.ZZ, q.ZZZ - return p -} - -// setInfinity sets p to the infinity point (1,1,0,0). -func (p *g2JacExtended) setInfinity() *g2JacExtended { - p.X.SetOne() - p.Y.SetOne() - p.ZZ = fp.Element{} - p.ZZZ = fp.Element{} - return p -} - -// IsInfinity checks if the p is infinity, i.e. p.ZZ=0. -func (p *g2JacExtended) IsInfinity() bool { - return p.ZZ.IsZero() -} - -// fromJacExtended converts an extended Jacobian point to an affine point. -func (p *G2Affine) fromJacExtended(q *g2JacExtended) *G2Affine { - if q.ZZ.IsZero() { - p.X = fp.Element{} - p.Y = fp.Element{} - return p - } - p.X.Inverse(&q.ZZ).Mul(&p.X, &q.X) - p.Y.Inverse(&q.ZZZ).Mul(&p.Y, &q.Y) - return p -} - -// fromJacExtended converts an extended Jacobian point to a Jacobian point. -func (p *G2Jac) fromJacExtended(q *g2JacExtended) *G2Jac { - if q.ZZ.IsZero() { - p.Set(&g2Infinity) - return p - } - p.X.Mul(&q.ZZ, &q.X).Mul(&p.X, &q.ZZ) - p.Y.Mul(&q.ZZZ, &q.Y).Mul(&p.Y, &q.ZZZ) - p.Z.Set(&q.ZZZ) - return p -} - -// unsafeFromJacExtended converts an extended Jacobian point, distinct from Infinity, to a Jacobian point. -func (p *G2Jac) unsafeFromJacExtended(q *g2JacExtended) *G2Jac { - p.X.Square(&q.ZZ).Mul(&p.X, &q.X) - p.Y.Square(&q.ZZZ).Mul(&p.Y, &q.Y) - p.Z = q.ZZZ - return p -} - -// add sets p to p+q in extended Jacobian coordinates. -// -// https://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#addition-add-2008-s -func (p *g2JacExtended) add(q *g2JacExtended) *g2JacExtended { - //if q is infinity return p - if q.ZZ.IsZero() { - return p - } - // p is infinity, return q - if p.ZZ.IsZero() { - p.Set(q) - return p - } - - var A, B, U1, U2, S1, S2 fp.Element - - // p2: q, p1: p - U2.Mul(&q.X, &p.ZZ) - U1.Mul(&p.X, &q.ZZ) - A.Sub(&U2, &U1) - S2.Mul(&q.Y, &p.ZZZ) - S1.Mul(&p.Y, &q.ZZZ) - B.Sub(&S2, &S1) - - if A.IsZero() { - if B.IsZero() { - return p.double(q) - - } - p.ZZ = fp.Element{} - p.ZZZ = fp.Element{} - return p - } - - var P, R, PP, PPP, Q, V fp.Element - P.Sub(&U2, &U1) - R.Sub(&S2, &S1) - PP.Square(&P) - PPP.Mul(&P, &PP) - Q.Mul(&U1, &PP) - V.Mul(&S1, &PPP) - - p.X.Square(&R). - Sub(&p.X, &PPP). - Sub(&p.X, &Q). - Sub(&p.X, &Q) - p.Y.Sub(&Q, &p.X). - Mul(&p.Y, &R). - Sub(&p.Y, &V) - p.ZZ.Mul(&p.ZZ, &q.ZZ). - Mul(&p.ZZ, &PP) - p.ZZZ.Mul(&p.ZZZ, &q.ZZZ). - Mul(&p.ZZZ, &PPP) - - return p -} - -// double sets p to [2]q in Jacobian extended coordinates. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-dbl-2008-s-1 -// N.B.: since we consider any point on Z=0 as the point at infinity -// this doubling formula works for infinity points as well. -func (p *g2JacExtended) double(q *g2JacExtended) *g2JacExtended { - var U, V, W, S, XX, M fp.Element - - U.Double(&q.Y) - V.Square(&U) - W.Mul(&U, &V) - S.Mul(&q.X, &V) - XX.Square(&q.X) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - U.Mul(&W, &q.Y) - - p.X.Square(&M). - Sub(&p.X, &S). - Sub(&p.X, &S) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M). - Sub(&p.Y, &U) - p.ZZ.Mul(&V, &q.ZZ) - p.ZZZ.Mul(&W, &q.ZZZ) - - return p -} - -// addMixed sets p to p+q in extended Jacobian coordinates, where a.ZZ=1. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#addition-madd-2008-s -func (p *g2JacExtended) addMixed(a *G2Affine) *g2JacExtended { - - //if a is infinity return p - if a.IsInfinity() { - return p - } - // p is infinity, return a - if p.ZZ.IsZero() { - p.X = a.X - p.Y = a.Y - p.ZZ.SetOne() - p.ZZZ.SetOne() - return p - } - - var P, R fp.Element - - // p2: a, p1: p - P.Mul(&a.X, &p.ZZ) - P.Sub(&P, &p.X) - - R.Mul(&a.Y, &p.ZZZ) - R.Sub(&R, &p.Y) - - if P.IsZero() { - if R.IsZero() { - return p.doubleMixed(a) - - } - p.ZZ = fp.Element{} - p.ZZZ = fp.Element{} - return p - } - - var PP, PPP, Q, Q2, RR, X3, Y3 fp.Element - - PP.Square(&P) - PPP.Mul(&P, &PP) - Q.Mul(&p.X, &PP) - RR.Square(&R) - X3.Sub(&RR, &PPP) - Q2.Double(&Q) - p.X.Sub(&X3, &Q2) - Y3.Sub(&Q, &p.X).Mul(&Y3, &R) - R.Mul(&p.Y, &PPP) - p.Y.Sub(&Y3, &R) - p.ZZ.Mul(&p.ZZ, &PP) - p.ZZZ.Mul(&p.ZZZ, &PPP) - - return p - -} - -// subMixed works the same as addMixed, but negates a.Y. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#addition-madd-2008-s -func (p *g2JacExtended) subMixed(a *G2Affine) *g2JacExtended { - - //if a is infinity return p - if a.IsInfinity() { - return p - } - // p is infinity, return a - if p.ZZ.IsZero() { - p.X = a.X - p.Y.Neg(&a.Y) - p.ZZ.SetOne() - p.ZZZ.SetOne() - return p - } - - var P, R fp.Element - - // p2: a, p1: p - P.Mul(&a.X, &p.ZZ) - P.Sub(&P, &p.X) - - R.Mul(&a.Y, &p.ZZZ) - R.Neg(&R) - R.Sub(&R, &p.Y) - - if P.IsZero() { - if R.IsZero() { - return p.doubleNegMixed(a) - - } - p.ZZ = fp.Element{} - p.ZZZ = fp.Element{} - return p - } - - var PP, PPP, Q, Q2, RR, X3, Y3 fp.Element - - PP.Square(&P) - PPP.Mul(&P, &PP) - Q.Mul(&p.X, &PP) - RR.Square(&R) - X3.Sub(&RR, &PPP) - Q2.Double(&Q) - p.X.Sub(&X3, &Q2) - Y3.Sub(&Q, &p.X).Mul(&Y3, &R) - R.Mul(&p.Y, &PPP) - p.Y.Sub(&Y3, &R) - p.ZZ.Mul(&p.ZZ, &PP) - p.ZZZ.Mul(&p.ZZZ, &PPP) - - return p - -} - -// doubleNegMixed works the same as double, but negates q.Y. -func (p *g2JacExtended) doubleNegMixed(a *G2Affine) *g2JacExtended { - - var U, V, W, S, XX, M, S2, L fp.Element - - U.Double(&a.Y) - U.Neg(&U) - V.Square(&U) - W.Mul(&U, &V) - S.Mul(&a.X, &V) - XX.Square(&a.X) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - S2.Double(&S) - L.Mul(&W, &a.Y) - - p.X.Square(&M). - Sub(&p.X, &S2) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M). - Add(&p.Y, &L) - p.ZZ.Set(&V) - p.ZZZ.Set(&W) - - return p -} - -// doubleMixed sets p to [2]a in Jacobian extended coordinates, where a.ZZ=1. -// -// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-dbl-2008-s-1 -func (p *g2JacExtended) doubleMixed(a *G2Affine) *g2JacExtended { - - var U, V, W, S, XX, M, S2, L fp.Element - - U.Double(&a.Y) - V.Square(&U) - W.Mul(&U, &V) - S.Mul(&a.X, &V) - XX.Square(&a.X) - M.Double(&XX). - Add(&M, &XX) // -> + A, but A=0 here - S2.Double(&S) - L.Mul(&W, &a.Y) - - p.X.Square(&M). - Sub(&p.X, &S2) - p.Y.Sub(&S, &p.X). - Mul(&p.Y, &M). - Sub(&p.Y, &L) - p.ZZ.Set(&V) - p.ZZZ.Set(&W) - - return p -} - -// ------------------------------------------------------------------------------------------------- -// Homogenous projective coordinates - -// Set sets p to a in projective coordinates. -func (p *g2Proj) Set(q *g2Proj) *g2Proj { - p.x, p.y, p.z = q.x, q.y, q.z - return p -} - -// Neg sets p to the projective negative point -q = (q.X, -q.Y). -func (p *g2Proj) Neg(q *g2Proj) *g2Proj { - *p = *q - p.y.Neg(&q.y) - return p -} - -// FromAffine converts q in affine to p in projective coordinates. -func (p *g2Proj) FromAffine(a *G2Affine) *g2Proj { - if a.X.IsZero() && a.Y.IsZero() { - p.z.SetZero() - p.x.SetOne() - p.y.SetOne() - return p - } - p.z.SetOne() - p.x.Set(&a.X) - p.y.Set(&a.Y) - return p -} - -// BatchScalarMultiplicationG2 multiplies the same base by all scalars -// and return resulting points in affine coordinates -// uses a simple windowed-NAF-like multiplication algorithm. -func BatchScalarMultiplicationG2(base *G2Affine, scalars []fr.Element) []G2Affine { - // approximate cost in group ops is - // cost = 2^{c-1} + n(scalar.nbBits+nbChunks) - - nbPoints := uint64(len(scalars)) - min := ^uint64(0) - bestC := 0 - for c := 2; c <= 16; c++ { - cost := uint64(1 << (c - 1)) // pre compute the table - nbChunks := computeNbChunks(uint64(c)) - cost += nbPoints * (uint64(c) + 1) * nbChunks // doublings + point add - if cost < min { - min = cost - bestC = c - } - } - c := uint64(bestC) // window size - nbChunks := int(computeNbChunks(c)) - - // last window may be slightly larger than c; in which case we need to compute one - // extra element in the baseTable - maxC := lastC(c) - if c > maxC { - maxC = c - } - - // precompute all powers of base for our window - // note here that if performance is critical, we can implement as in the msmX methods - // this allocation to be on the stack - baseTable := make([]G2Jac, (1 << (maxC - 1))) - baseTable[0].FromAffine(base) - for i := 1; i < len(baseTable); i++ { - baseTable[i] = baseTable[i-1] - baseTable[i].AddMixed(base) - } - toReturn := make([]G2Affine, len(scalars)) - - // partition the scalars into digits - digits, _ := partitionScalars(scalars, c, runtime.NumCPU()) - - // for each digit, take value in the base table, double it c time, voilà. - parallel.Execute(len(scalars), func(start, end int) { - var p G2Jac - for i := start; i < end; i++ { - p.Set(&g2Infinity) - for chunk := nbChunks - 1; chunk >= 0; chunk-- { - if chunk != nbChunks-1 { - for j := uint64(0); j < c; j++ { - p.DoubleAssign() - } - } - offset := chunk * len(scalars) - digit := digits[i+offset] - - if digit == 0 { - continue - } - - // if msbWindow bit is set, we need to subtract - if digit&1 == 0 { - // add - p.AddAssign(&baseTable[(digit>>1)-1]) - } else { - // sub - t := baseTable[digit>>1] - t.Neg(&t) - p.AddAssign(&t) - } - } - - // set our result point - toReturn[i].FromJacobian(&p) - - } - }) - return toReturn -} - -// batchAddG2Affine adds affine points using the Montgomery batch inversion trick. -// Special cases (doubling, infinity) must be filtered out before this call. -func batchAddG2Affine[TP pG2Affine, TPP ppG2Affine, TC cG2Affine](R *TPP, P *TP, batchSize int) { - var lambda, lambdain TC - - // add part - for j := 0; j < batchSize; j++ { - lambdain[j].Sub(&(*P)[j].X, &(*R)[j].X) - } - - // invert denominator using montgomery batch invert technique - { - var accumulator fp.Element - lambda[0].SetOne() - accumulator.Set(&lambdain[0]) - - for i := 1; i < batchSize; i++ { - lambda[i] = accumulator - accumulator.Mul(&accumulator, &lambdain[i]) - } - - accumulator.Inverse(&accumulator) - - for i := batchSize - 1; i > 0; i-- { - lambda[i].Mul(&lambda[i], &accumulator) - accumulator.Mul(&accumulator, &lambdain[i]) - } - lambda[0].Set(&accumulator) - } - - var d fp.Element - var rr G2Affine - - // add part - for j := 0; j < batchSize; j++ { - // computa lambda - d.Sub(&(*P)[j].Y, &(*R)[j].Y) - lambda[j].Mul(&lambda[j], &d) - - // compute X, Y - rr.X.Square(&lambda[j]) - rr.X.Sub(&rr.X, &(*R)[j].X) - rr.X.Sub(&rr.X, &(*P)[j].X) - d.Sub(&(*R)[j].X, &rr.X) - rr.Y.Mul(&lambda[j], &d) - rr.Y.Sub(&rr.Y, &(*R)[j].Y) - (*R)[j].Set(&rr) - } -} - -// RandomOnG2 produces a random point in G2 -// using standard map-to-curve methods, which means the relative discrete log -// of the generated point with respect to the canonical generator is not known. -func RandomOnG2() (G2Affine, error) { - if gBytes, err := randomFrSizedBytes(); err != nil { - return G2Affine{}, err - } else { - return HashToG2(gBytes, []byte("random on g2")) - } -} - -func randomFrSizedBytes() ([]byte, error) { - res := make([]byte, fr.Bytes) - _, err := rand.Read(res) - return res, err -} diff --git a/ecc/bw6-756/g2_test.go b/ecc/bw6-756/g2_test.go deleted file mode 100644 index 0798ac8cb0..0000000000 --- a/ecc/bw6-756/g2_test.go +++ /dev/null @@ -1,855 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bw6756 - -import ( - "fmt" - "math/big" - "math/rand/v2" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -func TestG2AffineEndomorphism(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BW6-756] check that phi(P) = lambdaGLV * P", prop.ForAll( - func(a fp.Element) bool { - var p, res1, res2 G2Jac - g := MapToG2(a) - p.FromAffine(&g) - res1.phi(&p) - res2.mulWindowed(&p, &lambdaGLV) - - return p.IsInSubGroup() && res1.Equal(&res2) - }, - GenFp(), - )) - - properties.Property("[BW6-756] check that phi^2(P) + phi(P) + P = 0", prop.ForAll( - func(a fp.Element) bool { - var p, res, tmp G2Jac - g := MapToG2(a) - p.FromAffine(&g) - tmp.phi(&p) - res.phi(&tmp). - AddAssign(&tmp). - AddAssign(&p) - - return res.Z.IsZero() - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG2AffineIsOnCurve(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BW6-756] g2Gen (affine) should be on the curve", prop.ForAll( - func(a fp.Element) bool { - var op1, op2 G2Affine - op1.FromJacobian(&g2Gen) - op2.Set(&op1) - op2.Y.Mul(&op2.Y, &a) - return op1.IsOnCurve() && !op2.IsOnCurve() - }, - GenFp(), - )) - - properties.Property("[BW6-756] g2Gen (Jacobian) should be on the curve", prop.ForAll( - func(a fp.Element) bool { - var op1, op2, op3 G2Jac - op1.Set(&g2Gen) - op3.Set(&g2Gen) - - op2 = fuzzG2Jac(&g2Gen, a) - op3.Y.Mul(&op3.Y, &a) - return op1.IsOnCurve() && op2.IsOnCurve() && !op3.IsOnCurve() - }, - GenFp(), - )) - - properties.Property("[BW6-756] IsInSubGroup and MulBy subgroup order should be the same", prop.ForAll( - func(a fp.Element) bool { - var op1, op2 G2Jac - op1 = fuzzG2Jac(&g2Gen, a) - _r := fr.Modulus() - op2.ScalarMultiplication(&op1, _r) - return op1.IsInSubGroup() && op2.Z.IsZero() - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG2AffineConversions(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BW6-756] Affine representation should be independent of the Jacobian representative", prop.ForAll( - func(a fp.Element) bool { - g := fuzzG2Jac(&g2Gen, a) - var op1 G2Affine - op1.FromJacobian(&g) - return op1.X.Equal(&g2Gen.X) && op1.Y.Equal(&g2Gen.Y) - }, - GenFp(), - )) - - properties.Property("[BW6-756] Affine representation should be independent of a Extended Jacobian representative", prop.ForAll( - func(a fp.Element) bool { - var g g2JacExtended - g.X.Set(&g2Gen.X) - g.Y.Set(&g2Gen.Y) - g.ZZ.Set(&g2Gen.Z) - g.ZZZ.Set(&g2Gen.Z) - gfuzz := fuzzg2JacExtended(&g, a) - - var op1 G2Affine - op1.fromJacExtended(&gfuzz) - return op1.X.Equal(&g2Gen.X) && op1.Y.Equal(&g2Gen.Y) - }, - GenFp(), - )) - - properties.Property("[BW6-756] Jacobian representation should be the same as the affine representative", prop.ForAll( - func(a fp.Element) bool { - var g G2Jac - var op1 G2Affine - op1.X.Set(&g2Gen.X) - op1.Y.Set(&g2Gen.Y) - - var one fp.Element - one.SetOne() - - g.FromAffine(&op1) - - return g.X.Equal(&g2Gen.X) && g.Y.Equal(&g2Gen.Y) && g.Z.Equal(&one) - }, - GenFp(), - )) - - properties.Property("[BW6-756] Converting affine symbol for infinity to Jacobian should output correct infinity in Jacobian", prop.ForAll( - func() bool { - var g G2Affine - g.X.SetZero() - g.Y.SetZero() - var op1 G2Jac - op1.FromAffine(&g) - var one, zero fp.Element - one.SetOne() - return op1.X.Equal(&one) && op1.Y.Equal(&one) && op1.Z.Equal(&zero) - }, - )) - - properties.Property("[BW6-756] Converting infinity in extended Jacobian to affine should output infinity symbol in Affine", prop.ForAll( - func() bool { - var g G2Affine - var op1 g2JacExtended - var zero fp.Element - op1.X.Set(&g2Gen.X) - op1.Y.Set(&g2Gen.Y) - g.fromJacExtended(&op1) - return g.X.Equal(&zero) && g.Y.Equal(&zero) - }, - )) - - properties.Property("[BW6-756] Converting infinity in extended Jacobian to Jacobian should output infinity in Jacobian", prop.ForAll( - func() bool { - var g G2Jac - var op1 g2JacExtended - var zero, one fp.Element - one.SetOne() - op1.X.Set(&g2Gen.X) - op1.Y.Set(&g2Gen.Y) - g.fromJacExtended(&op1) - return g.X.Equal(&one) && g.Y.Equal(&one) && g.Z.Equal(&zero) - }, - )) - - properties.Property("[BW6-756] [Jacobian] Two representatives of the same class should be equal", prop.ForAll( - func(a, b fp.Element) bool { - op1 := fuzzG2Jac(&g2Gen, a) - op2 := fuzzG2Jac(&g2Gen, b) - return op1.Equal(&op2) - }, - GenFp(), - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG2AffineOps(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - parameters.MinSuccessfulTests = 10 - - properties := gopter.NewProperties(parameters) - - genScalar := GenFr() - - properties.Property("[BW6-756] Add(P,-P) should return the point at infinity", prop.ForAll( - func(s fr.Element) bool { - var op1, op2 G2Affine - var sInt big.Int - g := g2GenAff - s.BigInt(&sInt) - op1.ScalarMultiplication(&g, &sInt) - op2.Neg(&op1) - - op1.Add(&op1, &op2) - return op1.IsInfinity() - - }, - GenFr(), - )) - - properties.Property("[BW6-756] Add(P,0) and Add(0,P) should return P", prop.ForAll( - func(s fr.Element) bool { - var op1, op2 G2Affine - var sInt big.Int - g := g2GenAff - s.BigInt(&sInt) - op1.ScalarMultiplication(&g, &sInt) - op2.setInfinity() - - op1.Add(&op1, &op2) - op2.Add(&op2, &op1) - return op1.Equal(&op2) - - }, - GenFr(), - )) - - properties.Property("[BW6-756] Add should call double when adding the same point", prop.ForAll( - func(s fr.Element) bool { - var op1, op2 G2Affine - var sInt big.Int - g := g2GenAff - s.BigInt(&sInt) - op1.ScalarMultiplication(&g, &sInt) - - op2.Double(&op1) - op1.Add(&op1, &op1) - return op1.Equal(&op2) - - }, - GenFr(), - )) - - properties.Property("[BW6-756] [2]G = double(G) + G - G", prop.ForAll( - func(s fr.Element) bool { - var sInt big.Int - g := g2GenAff - s.BigInt(&sInt) - g.ScalarMultiplication(&g, &sInt) - var op1, op2 G2Affine - op1.ScalarMultiplication(&g, big.NewInt(2)) - op2.Double(&g) - op2.Add(&op2, &g) - op2.Sub(&op2, &g) - return op1.Equal(&op2) - }, - GenFr(), - )) - - properties.Property("[BW6-756] [-s]G = -[s]G", prop.ForAll( - func(s fr.Element) bool { - g := g2GenAff - var gj G2Jac - var nbs, bs big.Int - s.BigInt(&bs) - nbs.Neg(&bs) - - var res = true - - // mulGLV - { - var op1, op2 G2Affine - op1.ScalarMultiplication(&g, &bs).Neg(&op1) - op2.ScalarMultiplication(&g, &nbs) - res = res && op1.Equal(&op2) - } - - // mulWindowed - { - var op1, op2 G2Jac - op1.mulWindowed(&gj, &bs).Neg(&op1) - op2.mulWindowed(&gj, &nbs) - res = res && op1.Equal(&op2) - } - - return res - }, - GenFr(), - )) - - properties.Property("[BW6-756] [Jacobian] Add should call double when adding the same point", prop.ForAll( - func(a, b fp.Element) bool { - fop1 := fuzzG2Jac(&g2Gen, a) - fop2 := fuzzG2Jac(&g2Gen, b) - var op1, op2 G2Jac - op1.Set(&fop1).AddAssign(&fop2) - op2.Double(&fop2) - return op1.Equal(&op2) - }, - GenFp(), - GenFp(), - )) - - properties.Property("[BW6-756] [Jacobian] Adding the opposite of a point to itself should output inf", prop.ForAll( - func(a, b fp.Element) bool { - fop1 := fuzzG2Jac(&g2Gen, a) - fop2 := fuzzG2Jac(&g2Gen, b) - fop2.Neg(&fop2) - fop1.AddAssign(&fop2) - return fop1.Equal(&g2Infinity) - }, - GenFp(), - GenFp(), - )) - - properties.Property("[BW6-756] [Jacobian] Adding the inf to a point should not modify the point", prop.ForAll( - func(a fp.Element) bool { - fop1 := fuzzG2Jac(&g2Gen, a) - fop1.AddAssign(&g2Infinity) - var op2 G2Jac - op2.Set(&g2Infinity) - op2.AddAssign(&g2Gen) - return fop1.Equal(&g2Gen) && op2.Equal(&g2Gen) - }, - GenFp(), - )) - - properties.Property("[BW6-756] [Jacobian Extended] addMixed (-G) should equal subMixed(G)", prop.ForAll( - func(a fp.Element) bool { - fop1 := fuzzG2Jac(&g2Gen, a) - var p1, p1Neg G2Affine - p1.FromJacobian(&fop1) - p1Neg = p1 - p1Neg.Y.Neg(&p1Neg.Y) - var o1, o2 g2JacExtended - o1.addMixed(&p1Neg) - o2.subMixed(&p1) - - return o1.X.Equal(&o2.X) && - o1.Y.Equal(&o2.Y) && - o1.ZZ.Equal(&o2.ZZ) && - o1.ZZZ.Equal(&o2.ZZZ) - }, - GenFp(), - )) - - properties.Property("[BW6-756] [Jacobian Extended] doubleMixed (-G) should equal doubleNegMixed(G)", prop.ForAll( - func(a fp.Element) bool { - fop1 := fuzzG2Jac(&g2Gen, a) - var p1, p1Neg G2Affine - p1.FromJacobian(&fop1) - p1Neg = p1 - p1Neg.Y.Neg(&p1Neg.Y) - var o1, o2 g2JacExtended - o1.doubleMixed(&p1Neg) - o2.doubleNegMixed(&p1) - - return o1.X.Equal(&o2.X) && - o1.Y.Equal(&o2.Y) && - o1.ZZ.Equal(&o2.ZZ) && - o1.ZZZ.Equal(&o2.ZZZ) - }, - GenFp(), - )) - - properties.Property("[BW6-756] [Jacobian] Addmix the negation to itself should output 0", prop.ForAll( - func(a fp.Element) bool { - fop1 := fuzzG2Jac(&g2Gen, a) - fop1.Neg(&fop1) - var op2 G2Affine - op2.FromJacobian(&g2Gen) - fop1.AddMixed(&op2) - return fop1.Equal(&g2Infinity) - }, - GenFp(), - )) - - properties.Property("[BW6-756] scalar multiplication (double and add) should depend only on the scalar mod r", prop.ForAll( - func(s fr.Element) bool { - - r := fr.Modulus() - var g G2Jac - g.ScalarMultiplication(&g2Gen, r) - - var scalar, blindedScalar, rminusone big.Int - var op1, op2, op3, gneg G2Jac - rminusone.SetUint64(1).Sub(r, &rminusone) - op3.mulWindowed(&g2Gen, &rminusone) - gneg.Neg(&g2Gen) - s.BigInt(&scalar) - blindedScalar.Mul(&scalar, r).Add(&blindedScalar, &scalar) - op1.mulWindowed(&g2Gen, &scalar) - op2.mulWindowed(&g2Gen, &blindedScalar) - - return op1.Equal(&op2) && g.Equal(&g2Infinity) && !op1.Equal(&g2Infinity) && gneg.Equal(&op3) - - }, - genScalar, - )) - - properties.Property("[BW6-756] scalar multiplication (GLV) should depend only on the scalar mod r", prop.ForAll( - func(s fr.Element) bool { - - r := fr.Modulus() - var g G2Jac - g.mulGLV(&g2Gen, r) - - var scalar, blindedScalar, rminusone big.Int - var op1, op2, op3, gneg G2Jac - rminusone.SetUint64(1).Sub(r, &rminusone) - op3.ScalarMultiplication(&g2Gen, &rminusone) - gneg.Neg(&g2Gen) - s.BigInt(&scalar) - blindedScalar.Mul(&scalar, r).Add(&blindedScalar, &scalar) - op1.ScalarMultiplication(&g2Gen, &scalar) - op2.ScalarMultiplication(&g2Gen, &blindedScalar) - - return op1.Equal(&op2) && g.Equal(&g2Infinity) && !op1.Equal(&g2Infinity) && gneg.Equal(&op3) - - }, - genScalar, - )) - - properties.Property("[BW6-756] GLV and Double and Add should output the same result", prop.ForAll( - func(s fr.Element) bool { - - var r big.Int - var op1, op2 G2Jac - s.BigInt(&r) - op1.mulWindowed(&g2Gen, &r) - op2.mulGLV(&g2Gen, &r) - return op1.Equal(&op2) && !op1.Equal(&g2Infinity) - - }, - genScalar, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG2AffineCofactorCleaning(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[BW6-756] Clearing the cofactor of a random point should set it in the r-torsion", prop.ForAll( - func() bool { - var a, x, b fp.Element - a.SetRandom() - - x.Square(&a).Mul(&x, &a).Add(&x, &bTwistCurveCoeff) - - for x.Legendre() != 1 { - a.SetRandom() - - x.Square(&a).Mul(&x, &a).Add(&x, &bTwistCurveCoeff) - - } - - b.Sqrt(&x) - var point, pointCleared, infinity G2Jac - point.X.Set(&a) - point.Y.Set(&b) - point.Z.SetOne() - pointCleared.ClearCofactor(&point) - infinity.Set(&g2Infinity) - return point.IsOnCurve() && pointCleared.IsInSubGroup() && !pointCleared.Equal(&infinity) - }, - )) - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestG2AffineBatchScalarMultiplication(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzzShort - } - - properties := gopter.NewProperties(parameters) - - genScalar := GenFr() - - // size of the multiExps - const nbSamples = 10 - - properties.Property("[BW6-756] BatchScalarMultiplication should be consistent with individual scalar multiplications", prop.ForAll( - func(mixer fr.Element) bool { - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - result := BatchScalarMultiplicationG2(&g2GenAff, sampleScalars[:]) - - if len(result) != len(sampleScalars) { - return false - } - - for i := 0; i < len(result); i++ { - var expectedJac G2Jac - var expected G2Affine - var b big.Int - expectedJac.ScalarMultiplication(&g2Gen, sampleScalars[i].BigInt(&b)) - expected.FromJacobian(&expectedJac) - if !result[i].Equal(&expected) { - return false - } - } - return true - }, - genScalar, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -// ------------------------------------------------------------ -// benches - -func BenchmarkG2JacIsInSubGroup(b *testing.B) { - var a G2Jac - a.Set(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.IsInSubGroup() - } - -} - -func BenchmarkG2JacEqual(b *testing.B) { - var scalar fp.Element - if _, err := scalar.SetRandom(); err != nil { - b.Fatalf("failed to set scalar: %s", err) - } - - var a G2Jac - a.ScalarMultiplication(&g2Gen, big.NewInt(42)) - - b.Run("equal", func(b *testing.B) { - var scalarSquared fp.Element - scalarSquared.Square(&scalar) - - aZScaled := a - aZScaled.X.Mul(&aZScaled.X, &scalarSquared) - aZScaled.Y.Mul(&aZScaled.Y, &scalarSquared).Mul(&aZScaled.Y, &scalar) - aZScaled.Z.Mul(&aZScaled.Z, &scalar) - - // Check the setup. - if !a.Equal(&aZScaled) { - b.Fatalf("invalid test setup") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Equal(&aZScaled) - } - }) - - b.Run("not equal", func(b *testing.B) { - var aPlus1 G2Jac - aPlus1.AddAssign(&g2Gen) - - // Check the setup. - if a.Equal(&aPlus1) { - b.Fatalf("invalid test setup") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Equal(&aPlus1) - } - }) -} - -func BenchmarkBatchAddG2Affine(b *testing.B) { - - var P, R pG2AffineC16 - var RR ppG2AffineC16 - ridx := make([]int, len(P)) - - // TODO P == R may produce skewed benches - fillBenchBasesG2(P[:]) - fillBenchBasesG2(R[:]) - - for i := 0; i < len(ridx); i++ { - ridx[i] = i - } - - // random permute - rand.Shuffle(len(ridx), func(i, j int) { ridx[i], ridx[j] = ridx[j], ridx[i] }) - - for i, ri := range ridx { - RR[i] = &R[ri] - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - batchAddG2Affine[pG2AffineC16, ppG2AffineC16, cG2AffineC16](&RR, &P, len(P)) - } -} - -func BenchmarkG2AffineBatchScalarMultiplication(b *testing.B) { - // ensure every words of the scalars are filled - var mixer fr.Element - mixer.SetString("7716837800905789770901243404444209691916730933998574719964609384059111546487") - - const pow = 15 - const nbSamples = 1 << pow - - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - for i := 5; i <= pow; i++ { - using := 1 << i - - b.Run(fmt.Sprintf("%d points", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - _ = BatchScalarMultiplicationG2(&g2GenAff, sampleScalars[:using]) - } - }) - } -} - -func BenchmarkG2JacScalarMultiplication(b *testing.B) { - - var scalar big.Int - r := fr.Modulus() - scalar.SetString("5243587517512619047944770508185965837690552500527637822603658699938581184513", 10) - scalar.Add(&scalar, r) - - var doubleAndAdd G2Jac - - b.Run("double and add", func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - doubleAndAdd.mulWindowed(&g2Gen, &scalar) - } - }) - - var glv G2Jac - b.Run("GLV", func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - glv.mulGLV(&g2Gen, &scalar) - } - }) - -} - -func BenchmarkG2AffineCofactorClearing(b *testing.B) { - var a G2Jac - a.Set(&g2Gen) - for i := 0; i < b.N; i++ { - a.ClearCofactor(&a) - } -} - -func BenchmarkG2JacAdd(b *testing.B) { - var a G2Jac - a.Double(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.AddAssign(&g2Gen) - } -} - -func BenchmarkG2JacAddMixed(b *testing.B) { - var a G2Jac - a.Double(&g2Gen) - - var c G2Affine - c.FromJacobian(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.AddMixed(&c) - } - -} - -func BenchmarkG2JacDouble(b *testing.B) { - var a G2Jac - a.Set(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.DoubleAssign() - } - -} - -func BenchmarkG2JacExtAddMixed(b *testing.B) { - var a g2JacExtended - a.doubleMixed(&g2GenAff) - - var c G2Affine - c.FromJacobian(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.addMixed(&c) - } -} - -func BenchmarkG2JacExtSubMixed(b *testing.B) { - var a g2JacExtended - a.doubleMixed(&g2GenAff) - - var c G2Affine - c.FromJacobian(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.subMixed(&c) - } -} - -func BenchmarkG2JacExtDoubleMixed(b *testing.B) { - var a g2JacExtended - a.doubleMixed(&g2GenAff) - - var c G2Affine - c.FromJacobian(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.doubleMixed(&c) - } -} - -func BenchmarkG2JacExtDoubleNegMixed(b *testing.B) { - var a g2JacExtended - a.doubleMixed(&g2GenAff) - - var c G2Affine - c.FromJacobian(&g2Gen) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.doubleNegMixed(&c) - } -} - -func BenchmarkG2JacExtAdd(b *testing.B) { - var a, c g2JacExtended - a.doubleMixed(&g2GenAff) - c.double(&a) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.add(&c) - } -} - -func BenchmarkG2JacExtDouble(b *testing.B) { - var a g2JacExtended - a.doubleMixed(&g2GenAff) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.double(&a) - } -} - -func BenchmarkG2AffineAdd(b *testing.B) { - var a G2Affine - a.Double(&g2GenAff) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Add(&a, &g2GenAff) - } -} - -func BenchmarkG2AffineDouble(b *testing.B) { - var a G2Affine - a.Double(&g2GenAff) - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Double(&a) - } -} - -func fuzzG2Jac(p *G2Jac, f fp.Element) G2Jac { - var res G2Jac - res.X.Mul(&p.X, &f).Mul(&res.X, &f) - res.Y.Mul(&p.Y, &f).Mul(&res.Y, &f).Mul(&res.Y, &f) - res.Z.Mul(&p.Z, &f) - return res -} - -func fuzzg2JacExtended(p *g2JacExtended, f fp.Element) g2JacExtended { - var res g2JacExtended - var ff, fff fp.Element - ff.Square(&f) - fff.Mul(&ff, &f) - res.X.Mul(&p.X, &ff) - res.Y.Mul(&p.Y, &fff) - res.ZZ.Mul(&p.ZZ, &ff) - res.ZZZ.Mul(&p.ZZZ, &fff) - return res -} diff --git a/ecc/bw6-756/hash_to_g1.go b/ecc/bw6-756/hash_to_g1.go deleted file mode 100644 index 60516a93d6..0000000000 --- a/ecc/bw6-756/hash_to_g1.go +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bw6756 - -import ( - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - - "math/big" -) - -//Note: This only works for simple extensions - -func g1IsogenyXNumerator(dst *fp.Element, x *fp.Element) { - g1EvalPolynomial(dst, - false, - []fp.Element{ - {15061764643505403874, 13688922241935764410, 13945096449919150208, 8436067060318510061, 4337050741185236015, 1993211805393091759, 13630591835847640353, 16993584682990519360, 8971204976640521592, 11479362202198843551, 7505613342680806802, 2623277435220162}, - {2256652953469429710, 7986151259564652590, 7173717765514014760, 5325211358772542105, 11720722258571317741, 1827380411336239380, 2945628459421726487, 15684476260997112721, 16275963394068506199, 3647627339057733797, 6840443943923060230, 2788437309956548}, - {18446744073709550557, 14704964401664098303, 11309681711681649385, 14698000069680550214, 13454959400917254747, 10801466177653926084, 15239716878514852502, 16581223703780321416, 14202840701532685566, 17948097119401515826, 5922093086188465715, 2268644466718328}, - }, - x) -} - -func g1IsogenyXDenominator(dst *fp.Element, x *fp.Element) { - g1EvalPolynomial(dst, - true, - []fp.Element{ - {9026611813877718838, 6035453010921316536, 17062904658006647585, 11643627583491349159, 14230214847810026090, 5797047097534633924, 14807366920250713864, 14779814887555727504, 10347853169840149009, 1246596934729393539, 13141619286505907834, 2448522848965627}, - }, - x) -} - -func g1IsogenyYNumerator(dst *fp.Element, x *fp.Element, y *fp.Element) { - var _dst fp.Element - g1EvalPolynomial(&_dst, - false, - []fp.Element{ - {7417, 4223785464154554368, 17883137121165483676, 8662155583981545100, 8014120713948809746, 1201662146881371066, 16399484201235277962, 16741102533822300073, 10092115902138952610, 11731609449449782212, 5432405448762957777, 1529941514692833}, - {17685820612365145165, 10133590496207434412, 4439341389815863358, 16237166311366871531, 12586675296657379027, 5752817071182370007, 18238899548945746049, 5474128448956424977, 4657871854268244383, 3092250989713984389, 13902867206193867696, 2147032802810159}, - {3384979430204144565, 11979226889346978885, 1537204611416246332, 17211189075013588966, 17581083387856976611, 11964442653859134878, 13641814725987365538, 14303342354640893273, 15190573054247983491, 5471441008586600696, 10260665915884590345, 4182655964934822}, - {18446744073709551087, 18441456226093760511, 13174518475280565460, 9763304497739979922, 14890648247077438592, 5778851725779543941, 6863645168616474272, 15668448372570953649, 6955362397092648018, 12310026665076143326, 11127771683818204033, 3310628831074305}, - }, - x) - - dst.Mul(&_dst, y) -} - -func g1IsogenyYDenominator(dst *fp.Element, x *fp.Element) { - g1EvalPolynomial(dst, - true, - []fp.Element{ - {33905, 17146720447904350208, 4439688729395715465, 17578719130422492408, 13009726419073394047, 17697253371943596573, 3126642749599797879, 15395188361529500510, 1711728968787230262, 252749604653387985, 14374699731745910598, 1397801387328302}, - {17266182735847260650, 2756724634261996626, 1058985484926618486, 5542736661445693400, 1636838704864690045, 12564472000181253073, 6593938171842885778, 4786584001734410359, 1113513889954172149, 12176975388293072748, 6351201184167411439, 1553692990780230}, - {8633091367923604897, 14375155055950078505, 17702614661430909603, 11655529750965086232, 7917563376482904218, 16634904018698739975, 9041039154614942318, 11137027547596045016, 12888931343158284942, 15514578667146961408, 4644663504360229534, 2992955351466600}, - }, - x) -} - -func g1Isogeny(p *G1Affine) { - - den := make([]fp.Element, 2) - - g1IsogenyYDenominator(&den[1], &p.X) - g1IsogenyXDenominator(&den[0], &p.X) - - g1IsogenyYNumerator(&p.Y, &p.X, &p.Y) - g1IsogenyXNumerator(&p.X, &p.X) - - den = fp.BatchInvert(den) - - p.X.Mul(&p.X, &den[0]) - p.Y.Mul(&p.Y, &den[1]) -} - -// g1SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue -// if not, we get sqrt(Z * u / v). Recall that Z is non-residue -// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. -// The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided -func g1SqrtRatio(z *fp.Element, u *fp.Element, v *fp.Element) uint64 { - - // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-sqrt_ratio-for-any-field - - tv1 := fp.Element{17302715199413996045, 15077845457253267709, 8842885729139027579, 12189878420705505575, 12380986790262239346, 585111498723936856, 4947215576903759546, 1186632482028566920, 14543050817583235372, 5644943604719368358, 9440830989708189862, 1039766423535362} //tv1 = c6 - - var tv2, tv3, tv4, tv5 fp.Element - var exp big.Int - // c4 = 4835703278458516698824703 = 2⁸² - 1 - // q is odd so c1 is at least 1. - exp.SetBytes([]byte{3, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}) - - tv2.Exp(*v, &exp) // 2. tv2 = vᶜ⁴ - tv3.Square(&tv2) // 3. tv3 = tv2² - tv3.Mul(&tv3, v) // 4. tv3 = tv3 * v - tv5.Mul(u, &tv3) // 5. tv5 = u * tv3 - - // c3 = 37877157660731232732990269576663233239936484746509109593426423261538632780449313352717366389444912082695314931794809746268936574949192324351273838279701014606648452884726586254167471840902479876056412368 - exp.SetBytes([]byte{1, 238, 213, 183, 107, 119, 49, 92, 85, 130, 79, 195, 198, 173, 25, 235, 146, 241, 154, 95, 88, 89, 209, 63, 126, 70, 68, 40, 170, 44, 116, 217, 152, 213, 206, 120, 133, 72, 219, 61, 96, 89, 2, 93, 64, 159, 85, 65, 79, 214, 57, 103, 160, 220, 200, 220, 82, 89, 162, 189, 182, 200, 212, 168, 96, 85, 71, 132, 177, 188, 251, 218, 22, 208, 189, 13, 10, 73, 216, 6, 120, 252, 199, 240, 208}) - - tv5.Exp(tv5, &exp) // 6. tv5 = tv5ᶜ³ - tv5.Mul(&tv5, &tv2) // 7. tv5 = tv5 * tv2 - tv2.Mul(&tv5, v) // 8. tv2 = tv5 * v - tv3.Mul(&tv5, u) // 9. tv3 = tv5 * u - tv4.Mul(&tv3, &tv2) // 10. tv4 = tv3 * tv2 - - // c5 = 2417851639229258349412352 - exp.SetBytes([]byte{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) - tv5.Exp(tv4, &exp) // 11. tv5 = tv4ᶜ⁵ - isQNr := g1NotOne(&tv5) // 12. isQR = tv5 == 1 - c7 := fp.Element{13990906742184113945, 15879050380504523621, 13768460034940508157, 12337541071329853620, 6296858130192020747, 9289986178217863086, 18403114759403589657, 4546259071787184045, 5504643400205978814, 13830311104669138548, 96107744534255859, 1024735223965534} - tv2.Mul(&tv3, &c7) // 13. tv2 = tv3 * c7 - tv5.Mul(&tv4, &tv1) // 14. tv5 = tv4 * tv1 - tv3.Select(int(isQNr), &tv3, &tv2) // 15. tv3 = CMOV(tv2, tv3, isQR) - tv4.Select(int(isQNr), &tv4, &tv5) // 16. tv4 = CMOV(tv5, tv4, isQR) - exp.Lsh(big.NewInt(1), 82-2) // 18, 19: tv5 = 2ⁱ⁻² for i = c1 - - for i := 82; i >= 2; i-- { // 17. for i in (c1, c1 - 1, ..., 2): - - tv5.Exp(tv4, &exp) // 20. tv5 = tv4ᵗᵛ⁵ - nE1 := g1NotOne(&tv5) // 21. e1 = tv5 == 1 - tv2.Mul(&tv3, &tv1) // 22. tv2 = tv3 * tv1 - tv1.Mul(&tv1, &tv1) // 23. tv1 = tv1 * tv1 Why not write square? - tv5.Mul(&tv4, &tv1) // 24. tv5 = tv4 * tv1 - tv3.Select(int(nE1), &tv3, &tv2) // 25. tv3 = CMOV(tv2, tv3, e1) - tv4.Select(int(nE1), &tv4, &tv5) // 26. tv4 = CMOV(tv5, tv4, e1) - - if i > 2 { - exp.Rsh(&exp, 1) // 18, 19. tv5 = 2ⁱ⁻² - } - } - - *z = tv3 - return isQNr -} - -func g1NotOne(x *fp.Element) uint64 { - - var one fp.Element - return one.SetOne().NotEqual(x) - -} - -// g1MulByZ multiplies x by [11] and stores the result in z -func g1MulByZ(z *fp.Element, x *fp.Element) { - - res := *x - - res.Double(&res) - res.Double(&res) - res.Add(&res, x) - res.Double(&res) - res.Add(&res, x) - - *z = res -} - -// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method -// MapToCurve1 implements the SSWU map -// No cofactor clearing or isogeny -func MapToCurve1(u *fp.Element) G1Affine { - - var sswuIsoCurveCoeffA = fp.Element{6087387690755251612, 7643068232434215576, 6195945763281467660, 97569654519975969, 1505434147110560758, 12342644747290341982, 14059794106692380317, 15229664573794943703, 16908793757593141664, 1949816925291208189, 9451095697369482684, 234190359239853} - var sswuIsoCurveCoeffB = fp.Element{18446744073709458379, 881299893533802495, 4886355625346099349, 6225448195760991771, 6629400315996169345, 12607886696045185322, 7201730065066775519, 1932403901886200506, 8616600553259348813, 6369175937589644082, 7499857803942196586, 3773119276850162} - - var tv1 fp.Element - tv1.Square(u) // 1. tv1 = u² - - //mul tv1 by Z - g1MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 - - var tv2 fp.Element - tv2.Square(&tv1) // 3. tv2 = tv1² - tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 - - var tv3 fp.Element - var tv4 fp.Element - tv4.SetOne() - tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 - tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 - - tv2NZero := g1NotZero(&tv2) - - // tv4 = Z - tv4 = fp.Element{18446744073709504998, 11529623972028612607, 739483395258014634, 5527028560780200701, 11477868704616895891, 15905434021829949368, 2844651761892435780, 17567410508478669002, 4162242322955979641, 15743938111024983262, 11916654042695069468, 4062866236140222} - - tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) - tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - - tv2.Square(&tv3) // 9. tv2 = tv3² - - var tv6 fp.Element - tv6.Square(&tv4) // 10. tv6 = tv4² - - var tv5 fp.Element - tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - - tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 - tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 - tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - - tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 - tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 - - var x fp.Element - x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 - - var y1 fp.Element - gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) - - var y fp.Element - y.Mul(&tv1, u) // 19. y = tv1 * u - - y.Mul(&y, &y1) // 20. y = y * y1 - - x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) - y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) - - y1.Neg(&y) - y.Select(int(g1Sgn0(u)^g1Sgn0(&y)), &y, &y1) - - // 23. e1 = sgn0(u) == sgn0(y) - // 24. y = CMOV(-y, y, e1) - - x.Div(&x, &tv4) // 25. x = x / tv4 - - return G1Affine{x, y} -} - -func g1EvalPolynomial(z *fp.Element, monic bool, coefficients []fp.Element, x *fp.Element) { - dst := coefficients[len(coefficients)-1] - - if monic { - dst.Add(&dst, x) - } - - for i := len(coefficients) - 2; i >= 0; i-- { - dst.Mul(&dst, x) - dst.Add(&dst, &coefficients[i]) - } - - z.Set(&dst) -} - -// g1Sgn0 is an algebraic substitute for the notion of sign in ordered fields -// Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function -// The sign of an element is not obviously related to that of its Montgomery form -func g1Sgn0(z *fp.Element) uint64 { - - nonMont := z.Bits() - - // m == 1 - return nonMont[0] % 2 - -} - -// MapToG1 invokes the SSWU map, and guarantees that the result is in g1 -func MapToG1(u fp.Element) G1Affine { - res := MapToCurve1(&u) - //this is in an isogenous curve - g1Isogeny(&res) - res.ClearCofactor(&res) - return res -} - -// EncodeToG1 hashes a message to a point on the G1 curve using the SSWU map. -// It is faster than HashToG1, but the result is not uniformly distributed. Unsuitable as a random oracle. -// dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap -func EncodeToG1(msg, dst []byte) (G1Affine, error) { - - var res G1Affine - u, err := fp.Hash(msg, dst, 1) - if err != nil { - return res, err - } - - res = MapToCurve1(&u[0]) - - //this is in an isogenous curve - g1Isogeny(&res) - res.ClearCofactor(&res) - return res, nil -} - -// HashToG1 hashes a message to a point on the G1 curve using the SSWU map. -// Slower than EncodeToG1, but usable as a random oracle. -// dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap -func HashToG1(msg, dst []byte) (G1Affine, error) { - u, err := fp.Hash(msg, dst, 2*1) - if err != nil { - return G1Affine{}, err - } - - Q0 := MapToCurve1(&u[0]) - Q1 := MapToCurve1(&u[1]) - - //TODO (perf): Add in E' first, then apply isogeny - g1Isogeny(&Q0) - g1Isogeny(&Q1) - - var _Q0, _Q1 G1Jac - _Q0.FromAffine(&Q0) - _Q1.FromAffine(&Q1).AddAssign(&_Q0) - - _Q1.ClearCofactor(&_Q1) - - Q1.FromJacobian(&_Q1) - return Q1, nil -} - -func g1NotZero(x *fp.Element) uint64 { - - return x[0] | x[1] | x[2] | x[3] | x[4] | x[5] | x[6] | x[7] | x[8] | x[9] | x[10] | x[11] - -} diff --git a/ecc/bw6-756/hash_to_g1_test.go b/ecc/bw6-756/hash_to_g1_test.go deleted file mode 100644 index 52fffa4a06..0000000000 --- a/ecc/bw6-756/hash_to_g1_test.go +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bw6756 - -import ( - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" - "math/rand" - "testing" -) - -func TestG1SqrtRatio(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - gen := GenFp() - - properties.Property("G1SqrtRatio must square back to the right value", prop.ForAll( - func(u fp.Element, v fp.Element) bool { - - var seen fp.Element - qr := g1SqrtRatio(&seen, &u, &v) == 0 - - seen. - Square(&seen). - Mul(&seen, &v) - - var ref fp.Element - if qr { - ref = u - } else { - g1MulByZ(&ref, &u) - } - - return seen.Equal(&ref) - }, gen, gen)) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestHashToFpG1(t *testing.T) { - for _, c := range encodeToG1Vector.cases { - elems, err := fp.Hash([]byte(c.msg), encodeToG1Vector.dst, 1) - if err != nil { - t.Error(err) - } - g1TestMatchCoord(t, "u", c.msg, c.u, g1CoordAt(elems, 0)) - } - - for _, c := range hashToG1Vector.cases { - elems, err := fp.Hash([]byte(c.msg), hashToG1Vector.dst, 2*1) - if err != nil { - t.Error(err) - } - g1TestMatchCoord(t, "u0", c.msg, c.u0, g1CoordAt(elems, 0)) - g1TestMatchCoord(t, "u1", c.msg, c.u1, g1CoordAt(elems, 1)) - } -} - -func TestMapToCurve1(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[G1] mapping output must be on curve", prop.ForAll( - func(a fp.Element) bool { - - g := MapToCurve1(&a) - - if !isOnE1Prime(g) { - t.Log("Mapping output not on E' curve") - return false - } - g1Isogeny(&g) - - if !g.IsOnCurve() { - t.Log("Isogeny∘SSWU output not on curve") - return false - } - - return true - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - - for _, c := range encodeToG1Vector.cases { - var u fp.Element - g1CoordSetString(&u, c.u) - q := MapToCurve1(&u) - g1Isogeny(&q) - g1TestMatchPoint(t, "Q", c.msg, c.Q, &q) - } - - for _, c := range hashToG1Vector.cases { - var u fp.Element - g1CoordSetString(&u, c.u0) - q := MapToCurve1(&u) - g1Isogeny(&q) - g1TestMatchPoint(t, "Q0", c.msg, c.Q0, &q) - - g1CoordSetString(&u, c.u1) - q = MapToCurve1(&u) - g1Isogeny(&q) - g1TestMatchPoint(t, "Q1", c.msg, c.Q1, &q) - } -} - -func TestMapToG1(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[G1] mapping to curve should output point on the curve", prop.ForAll( - func(a fp.Element) bool { - g := MapToG1(a) - return g.IsInSubGroup() - }, - GenFp(), - )) - - properties.Property("[G1] mapping to curve should be deterministic", prop.ForAll( - func(a fp.Element) bool { - g1 := MapToG1(a) - g2 := MapToG1(a) - return g1.Equal(&g2) - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestEncodeToG1(t *testing.T) { - t.Parallel() - for _, c := range encodeToG1Vector.cases { - p, err := EncodeToG1([]byte(c.msg), encodeToG1Vector.dst) - if err != nil { - t.Fatal(err) - } - g1TestMatchPoint(t, "P", c.msg, c.P, &p) - } -} - -func TestHashToG1(t *testing.T) { - t.Parallel() - for _, c := range hashToG1Vector.cases { - p, err := HashToG1([]byte(c.msg), hashToG1Vector.dst) - if err != nil { - t.Fatal(err) - } - g1TestMatchPoint(t, "P", c.msg, c.P, &p) - } -} - -func BenchmarkEncodeToG1(b *testing.B) { - const size = 54 - bytes := make([]byte, size) - dst := encodeToG1Vector.dst - b.ResetTimer() - - for i := 0; i < b.N; i++ { - - bytes[rand.Int()%size] = byte(rand.Int()) //#nosec G404 weak rng is fine here - - if _, err := EncodeToG1(bytes, dst); err != nil { - b.Fail() - } - } -} - -func BenchmarkHashToG1(b *testing.B) { - const size = 54 - bytes := make([]byte, size) - dst := hashToG1Vector.dst - b.ResetTimer() - - for i := 0; i < b.N; i++ { - - bytes[rand.Int()%size] = byte(rand.Int()) //#nosec G404 weak rng is fine here - - if _, err := HashToG1(bytes, dst); err != nil { - b.Fail() - } - } -} - -// TODO: Crude. Do something clever in Jacobian -func isOnE1Prime(p G1Affine) bool { - - var A, B fp.Element - - A.SetString( - "366325390957376285098262978230484264120729221881318944463135304048016771328228850721754522021764668909569771812378323538695911335197738161697826183389061747318265693466282118166204963912377131569240668748078740101254715180842994", - ) - - B.SetString( - "22", - ) - - var LHS fp.Element - LHS. - Square(&p.Y). - Sub(&LHS, &B) - - var RHS fp.Element - RHS. - Square(&p.X). - Add(&RHS, &A). - Mul(&RHS, &p.X) - - return LHS.Equal(&RHS) -} - -// Only works on simple extensions (two-story towers) -func g1CoordSetString(z *fp.Element, s string) { - z.SetString(s) -} - -func g1CoordAt(slice []fp.Element, i int) fp.Element { - return slice[i] -} - -func g1TestMatchCoord(t *testing.T, coordName string, msg string, expectedStr string, seen fp.Element) { - var expected fp.Element - - g1CoordSetString(&expected, expectedStr) - - if !expected.Equal(&seen) { - t.Errorf("mismatch on \"%s\", %s:\n\texpected %s\n\tsaw %s", msg, coordName, expected.String(), &seen) - } -} - -func g1TestMatchPoint(t *testing.T, pointName string, msg string, expected point, seen *G1Affine) { - g1TestMatchCoord(t, pointName+".x", msg, expected.x, seen.X) - g1TestMatchCoord(t, pointName+".y", msg, expected.y, seen.Y) -} - -type hashTestVector struct { - dst []byte - cases []hashTestCase -} - -type encodeTestVector struct { - dst []byte - cases []encodeTestCase -} - -type point struct { - x string - y string -} - -type encodeTestCase struct { - msg string - P point //pY a coordinate of P, the final output - u string //u hashed onto the field - Q point //Q map to curve output -} - -type hashTestCase struct { - msg string - P point //pY a coordinate of P, the final output - u0 string //u0 hashed onto the field - u1 string //u1 extra hashed onto the field - Q0 point //Q0 map to curve output - Q1 point //Q1 extra map to curve output -} - -var encodeToG1Vector encodeTestVector -var hashToG1Vector hashTestVector diff --git a/ecc/bw6-756/hash_to_g2.go b/ecc/bw6-756/hash_to_g2.go deleted file mode 100644 index 6b172aa82c..0000000000 --- a/ecc/bw6-756/hash_to_g2.go +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bw6756 - -import ( - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - - "math/big" -) - -//Note: This only works for simple extensions - -func g2IsogenyXNumerator(dst *fp.Element, x *fp.Element) { - g2EvalPolynomial(dst, - false, - []fp.Element{ - {18063257888214992592, 1393496779859630263, 8564933104033120466, 1364340956348512280, 2179459776642261738, 7246713717490156421, 11399242774977305051, 4643984571529960155, 5648608782916908274, 3095467176315578154, 3467607453741162011, 2961445036873290}, - {11658835549372914133, 12286399758052775709, 12748454159994037005, 8627322543101307557, 8488782471683565130, 3311860832147265783, 16666746629317462942, 3993283414811786302, 9484238281806419324, 9079179108706802563, 4853121176603729598, 4115220294490375}, - {9804516088925396249, 1689484976811888131, 6709027706281311158, 15148698413255486787, 4456196932928240544, 13089600824592779987, 4915748753406253244, 8824016979977565722, 5801663995596799579, 18101997615639527323, 2024314854882138752, 3982056613208256}, - {2442176974808264130, 1036024567776342463, 3553154388034084009, 5407139347352086633, 18271230407247584526, 15484355645761637943, 751188679793694145, 4984314664481512509, 610694156402706023, 4400103409344523273, 10219908803514347843, 147859773786044}, - {4103257481960472996, 3407771621173017780, 10892974841255474934, 16253724974777807377, 6376172419506182724, 15853776788744583196, 1223963103902005581, 13511207157402706386, 11263217018011554306, 4332839954383721397, 5081830234251762489, 2677604750100570}, - {16143817283423817884, 16868200401550225159, 17247373971233108250, 4471527367127116953, 3288264420010388700, 13080898939531555317, 309418061729162402, 4570902553714880875, 12501140036399581948, 16460091225458830175, 3432750371563496540, 1856425305308313}, - {6616034961392165287, 13262605314780833274, 11095673701643109564, 15659197846522771275, 6221107675939471385, 10040356795873210634, 7142688090102529240, 5217229900361698120, 107142442904534986, 4880883056892442152, 17848994686119140771, 2209292322719356}, - {8209889887433583591, 11125531176963853968, 10429304465365845536, 9698924055228808053, 11572483637041790867, 8471024052732620731, 16171107090788573972, 17389518512515618487, 2446717027925821414, 13845569984714024499, 10358707895980715431, 1981955952337795}, - {17440571817547589032, 14821760434103399038, 3467158545118505628, 1052069839390006688, 16597307299261243349, 1514696147834692288, 1502298074058409421, 9724159673512954906, 17146431833140791833, 12568150970058967052, 4051586399753035900, 3034883876673207}, - {6885431716405590573, 11285374911458481328, 8536774051778827233, 537308629644561775, 4978081861472261373, 6895244255083366290, 12771428336377158707, 18366942941316985453, 3885842168611052213, 15733883558726650552, 1898691920676539973, 1053947946004101}, - {6438046397076616827, 8748193276523668278, 12924065944264387518, 10396411676633151501, 8884810883874904938, 11581924250560173624, 7958603540887013994, 11929384227933921591, 12058158172733608199, 13011125749180986554, 8011584745771515935, 2769359570595999}, - {8485125453864892728, 9496478782187944243, 9835406788412012827, 13148749213470338156, 14346720249036467428, 11042761504554563134, 9885278452952011517, 2699400820004207727, 6861670924206724915, 10779917832009994523, 6892501690509198304, 1120589998747771}, - {17370092359602020084, 5939626380863061634, 15363278959077316883, 17700203104572168868, 6623108653336641881, 12109660681271032827, 6997648647599827246, 7989523002331856142, 2551599959583933382, 13522924635998791246, 10964309953846145036, 2038629017267546}, - {8905023910868005352, 2644351245783489261, 12712934134422018629, 2320762562258764217, 18070651629628507397, 9497759956968634620, 1196234375306649517, 11762569576125533470, 12760978227532514829, 2100402702948591945, 14717565707450064032, 3190123599240964}, - {2321122152288065791, 8559934930487477171, 2816091705874298985, 12816347129408254318, 13759444057793103650, 9245275761719025150, 4583905162646958584, 193512411034013027, 5373879090500334116, 14965705556666027214, 15938856173769808949, 1679542184021728}, - {3064863255627260738, 1322412353639848785, 18016733159894272674, 10335342398228990822, 1019043548854784599, 12367531741647476417, 16806389067180361238, 11090164111872500153, 8574474748101498214, 10816633183836176713, 6244604899848898765, 3568151292359233}, - {13882469591933839029, 3780726533369959129, 356529851879299843, 4882617127020139027, 7330450063362196924, 16839599917433694663, 15301540460601913522, 9954563827398335290, 1349170473954715280, 9235680880529941043, 1342314491728118367, 3440448802314722}, - {9829752897409242023, 15704098846998971558, 3953782753040335701, 13733604128466395046, 15940385395599933276, 11943859108918239708, 14026342461240088190, 5157027042114130164, 17730504431960750988, 5346615480493660819, 1155792137343476704, 317557884360030}, - }, - x) -} - -func g2IsogenyXDenominator(dst *fp.Element, x *fp.Element) { - g2EvalPolynomial(dst, - true, - []fp.Element{ - {7676806743992704411, 13899270453609261655, 12451810503333105973, 7586377905044480253, 4400073814052305663, 8772770626841823456, 13952615522127684289, 14011211693111766274, 8877781094782098197, 15891608636494308341, 15564328328945326253, 2909487247725599}, - {9453865609050903521, 16187066946506957518, 11903964155097034943, 8713536912762661644, 14536674352530403432, 9468882702901471210, 7150526403476766966, 5620694243891780279, 8606838083802511379, 15607157215838277797, 15500728334841342059, 2231399860031313}, - {16980338680259670981, 11521927360174526230, 3296865033313109705, 17336540695467956468, 16363282394107312970, 8395304954809411637, 4445751931845641868, 15986692066173048677, 10373032380811327027, 7515107668367104040, 1285158972258773404, 3381474465871678}, - {12590065689114661726, 5946446231937317556, 6252935342450242612, 3946482967954549718, 5477460783051802954, 9554038960651143508, 14284409639302464644, 8381919432766671976, 15064388857143239334, 11198709445699977935, 1505776422996134004, 228166822329897}, - {8718455273244450707, 18257164560137771694, 63167664726716276, 10939327297441407123, 6906792779173733504, 6910059409960120006, 15613529393540514307, 9642032594321541892, 11604953731100391742, 12576353621847180703, 16967498256620051397, 3178252700562399}, - {868685242467462437, 6117772598772167454, 180017912061117271, 6682693383856096681, 14087880244443394467, 10065045297523127645, 12651808868021479222, 253494832475484277, 13284798185615450963, 9622862694522664923, 12723192387819738881, 3881837484675755}, - {16652886415926729528, 15222425659919396075, 7883409001976488877, 108334016999777799, 2139096560884051070, 9235143524812320273, 362394594825970392, 1985075437469393064, 15285298004551295011, 4694377468798011199, 14002413277245730823, 1031059376762955}, - {11607763233042652646, 15107090951981795493, 750429153195033507, 15447410638764619883, 17517491898415918026, 6872460098882921300, 15826126419240939753, 13286003994964494101, 18044211163024483308, 996549296944201754, 658907883085288864, 3183797990537945}, - {4230116250557350613, 3657094985405752339, 7217616869566592695, 1233781281998910073, 17857880953005547064, 6893178357644011918, 14339433834014725723, 15278301383782487737, 14319056202540821048, 9161061358875487159, 1721449113640690872, 4232195864691114}, - {9776057899978497849, 18295317405102606195, 499915739732625100, 9734977242896698887, 8865331344776907994, 11499915146337835230, 2528236165698619045, 12194855496531305052, 9067458767906246251, 18260802818211288667, 2059444010111999228, 1594971276166605}, - {16948142999539174921, 7749442011298877799, 8782365616033113710, 10062902276000792382, 8578445819659278006, 18441606647876159181, 12027817196043418722, 10967829751748336848, 13807613859264788489, 14146619962209840535, 237985113988347841, 3780579450083530}, - {2994714778176611012, 15395498331776333933, 11906372358806379274, 3870528935878244793, 4010010522904287132, 9137750020304910250, 15901170280015152991, 10139251366754058601, 11005484872982035363, 8422618689683690561, 14842869397711928390, 3247505200278147}, - {101073571214872246, 18383559315189060379, 6907519649047275056, 13062498176165399437, 16983943441557859234, 18299740594093156677, 5880497128789512121, 3932944625598144568, 5690929158366038599, 7602942967413953653, 13778217666122444261, 1907343141630194}, - {2065675606533188185, 835926001041832944, 2586561621060881533, 8657377230123209903, 10089711764069753420, 3314659207458483825, 17753035059206418973, 14321321176141429822, 14378558285243871759, 12481010464609732173, 1367439684858703947, 117846213348560}, - {1753375113789138573, 5582726390451056543, 18111137772277889143, 8599937063653169974, 9539333829070716249, 10105075059414936548, 13701354987781444925, 15726824122155311967, 8194284482795973630, 17314739116151717281, 9538018392868702110, 1367705449205283}, - {9090248073906778481, 2107788472131405778, 12924810070024706928, 15005609447201957126, 961945466227986008, 8749282965734757112, 7718473509162793608, 10637582677835211604, 13790836079088289949, 4207625851985012931, 2762901280724194593, 1893895310850181}, - }, - x) -} - -func g2IsogenyYNumerator(dst *fp.Element, x *fp.Element, y *fp.Element) { - var _dst fp.Element - g2EvalPolynomial(&_dst, - false, - []fp.Element{ - {5051598811638517557, 737162936682204453, 18266277940781649426, 8297934493309658251, 14587852221828932567, 17520883813942983557, 10498215533406532450, 5832211223751606295, 3600880350437357340, 2825910535639299749, 12647163569588255163, 275061126760636}, - {12559678814741632895, 7859048468261858149, 4236488078359479360, 16841239042007152658, 5312075531742262702, 3178741308357192977, 14664365762224592012, 14895611710779307411, 17003497102161051726, 12561805960382829864, 1689612490434121511, 4339709385293787}, - {4476631763572289814, 16082167790703866320, 7394388887204004896, 3955363014434416656, 13817310402283032879, 6647511166122297131, 14720286221254837855, 4472005002784771460, 4703528211196572387, 15590281238800563442, 15593255919960024691, 2166688893510683}, - {2503229986694465765, 8594864623388797577, 11771007318796339592, 12178353071709987741, 9395567834543143117, 1623478240275898563, 10422013481936289821, 5751815158810662048, 6129775232805856798, 16015977650288458746, 11063526906033396515, 359428695871498}, - {3172204394861266886, 7178324459329989193, 12055192196751786661, 9556747627156995614, 7108346223162691745, 12196486755584093700, 13296299274967806249, 743835019303585217, 12362670244290053814, 6976826164013006385, 6645425301774832941, 3420842202901129}, - {7103471633677248769, 10501892449765124953, 7979329109825360150, 16750428365225432440, 15586582932171624974, 14076198087256240428, 2628477998626483788, 7821198768462694495, 7466756118781704838, 13948888709477971062, 7927369204535991101, 2058102271014739}, - {5288823391484243258, 13731831771514171493, 12881614458960554030, 12148729456237475052, 8848547805942475948, 12243669284994705772, 16485291633832482603, 16058960579070538785, 17547062151649259172, 6905305693038127501, 18220013145790156749, 2972569751643637}, - {13361596835977281893, 17117667659587365983, 6339637372565868085, 15717095851481368394, 16547894374671072774, 5617618166823667001, 14514184715672603774, 7631006970098262425, 6062683165562369640, 14437434371647350980, 18414922128376530006, 2265037525519879}, - {9106851493396134797, 3871350947879893854, 11419315593021252893, 5677821281985076357, 11422846285152591355, 9656976370798298175, 11940691516517696790, 8167972214220646424, 12377408730237178633, 18329224238304815706, 5868665003522831600, 4308812217155104}, - {7457550484179549606, 17785821431645724103, 8610295788728590146, 4940001953956769495, 3943521744044194208, 4168544217828160098, 5813372651329180488, 9347943620560663705, 15479905250276510060, 14862637707067876829, 17259565500085355864, 1633018135761863}, - {10835542273568905129, 8092036257982094713, 14709920468851814741, 16853250056678220902, 6812464373424184914, 5889056178203244050, 17630558818321130987, 11291602263438171858, 5363215832179755730, 1406328387408005099, 17557253559133413174, 4179578472005619}, - {14855153380663865487, 13851563109375821599, 13068709803362046654, 11885935936878191666, 5065974774005451260, 2466622550050362922, 18101639232595559187, 7571004564246793426, 11590196373886920649, 4421221247140818609, 1615676370004474465, 1368767916401153}, - {3229602409663261297, 18370132806978542041, 8156118142877923158, 10568467618403222100, 4717245830849486971, 1321623212661780313, 2402246841054709322, 1497696416423214573, 478732305369471040, 4937841867770020731, 15317670803771231546, 1916834553357246}, - {5311741649814063446, 7140916177349472547, 4629497167543615722, 3247838709755899354, 9349955712890521943, 10806931643455376261, 9797786755886426509, 8936361737713300483, 4680758664059980220, 12406007169225434383, 3533709596282820669, 2455674357456026}, - {12488981058804589629, 12457922297183532626, 451262455699675331, 2589192099576276852, 15580980055328892440, 18403052471820082137, 12189972068214284937, 13256129327810144998, 3772655204155038395, 16041014234959313151, 72181953470569885, 4281388365540118}, - {14355041116718584904, 6518908914215076528, 103136633313959269, 11248008213672993918, 6970489461061342522, 11790861983726804408, 13550185278440680395, 13812178300427159463, 16743587002127429862, 4048335839682807858, 14698087464497806541, 2419921007991860}, - {5234990458074203996, 13017287354230506146, 14528142910692005959, 3772795329349177430, 5743699393195056243, 7634427128601001554, 13604641011547791987, 135487290822320499, 3790885956722941871, 11074651945233114222, 18082432112342758574, 574821069767001}, - {52578721445132518, 4807297948059579044, 13300282182261617411, 1966048695126804313, 9698133404944599207, 8820248505019030680, 3493492344229743544, 2207289649260572501, 10632810226164047214, 4971453237616087970, 913429712705966969, 1297201987563834}, - {17825584147787228187, 9643369156770337109, 4780283242127859977, 17498229400171506032, 16108828831928294847, 2084860329621159147, 18414157538260388952, 800287692256631753, 5695704543389678503, 4041482112338895649, 11390130574267716400, 2960465040246311}, - {6300502775430373558, 8369258902650415287, 3125753630970140323, 18204543611164718383, 526661945080208710, 15161036982699244642, 17578969572176151165, 12378105092679056730, 18183133059714169258, 12446959345931710099, 15917824696898340511, 2833318602758342}, - {13591805356567102552, 7114002070737981430, 15792963314576163910, 2439908364129503908, 287177262616586422, 15991174737764895668, 6785081176756496415, 4252801190810180185, 15974406619965461794, 5898178132521952779, 16735071778182207400, 3747243900171232}, - {15813641761354694888, 5775215055800921627, 6840760130944862528, 6877362349591737745, 15249413449075939135, 4616164489636862746, 10144566619146579982, 13413064070702175791, 1351336168301780689, 13223512313899745023, 2505242198188713520, 4008932646590333}, - {12443271823023628293, 10023560508435423279, 223996864810707239, 9381771480330342249, 6071616917663934748, 8368415673838482178, 9943888360412422135, 16466687579138830466, 13092709466108236949, 9672861101133407978, 10061835049751520054, 1548449159771126}, - {682372491237998985, 11642050129783406054, 11148560572438111964, 14522736662327934823, 11397999623021472703, 1908450234014681122, 7015571940646005434, 12379468932656542568, 16774893396095877790, 6713564321785078684, 16841673995724381835, 2735911680003515}, - {13599451869524933025, 12884519722266096890, 17760075748912444940, 7988627628311688106, 14881807801342021418, 13083553921104407380, 14041750000390895271, 2472546647851334075, 3041343147406232878, 12920007657510457765, 995078962479205134, 2579040578744874}, - }, - x) - - dst.Mul(&_dst, y) -} - -func g2IsogenyYDenominator(dst *fp.Element, x *fp.Element) { - g2EvalPolynomial(dst, - true, - []fp.Element{ - {1334792841914619146, 679589714283882072, 17083068605088219648, 4813351246215290848, 18172042438030301686, 14277203889184557608, 15837808323580910883, 10306580648728757893, 11003641200468979478, 940386280777395627, 3499226008293819953, 2670356276894702}, - {9755707125415732584, 11547150959279383035, 10562244305064472457, 10066300461986435643, 7148237540886167626, 14216704987748624229, 16406871087277952232, 12806845016466455100, 2351888649504415376, 4581239853380640174, 16076162830476753346, 568274864174746}, - {893765329958948899, 6025675675869057371, 17671761618169526031, 290857361517030098, 10896841519848110098, 17285132374146627243, 6578246538169581645, 14698668347892092920, 11847506233132912482, 13196346148126584369, 8267604772654803804, 1874938974390476}, - {6735883464220501378, 12278479419325863497, 14385176350002363003, 327505777929575292, 12879051046191573898, 5244035810968697501, 3180567461135135676, 17276308001343046319, 3006667409138758213, 15967849879716326079, 16187453789268986652, 605590141583629}, - {15477429905846444566, 874980550924067157, 812459596667087749, 15729930408665072903, 14188396940358881836, 8936483967067860411, 11560783477954519230, 3137560215625292057, 9575790530359643406, 17927687113810822901, 2632829296588147564, 435712685367393}, - {17106890255742260517, 15652864607222471998, 10170811537593627639, 1453924004107446790, 11329170583151456244, 5141789719850306806, 16125025042683025570, 15076092565634391873, 8561206140914607069, 4463443131371537634, 11670432227453805701, 1689349098401896}, - {9129230268637416312, 16131424300862894054, 11599455708249086086, 13712061106571700271, 3222547574018203825, 5028201422192587329, 12665893808043057006, 7910268944325741984, 13694452156601909132, 7594944072090734412, 6879517203248637902, 1138400281829526}, - {535993526973048097, 3775829409162107138, 13717328028075297397, 931342578972386679, 9677314225136962692, 1800755985773403875, 10333191064113944512, 493249530510038265, 3476098351926766495, 1181720279204825908, 12777038111429138841, 3384692890585801}, - {6772381476956843695, 1547184579308225297, 9184478537476982368, 18151871132032417756, 11438493594152051480, 13120718087287853722, 906650696512912608, 13671423743799524436, 5153011690436366245, 3698430439766668188, 15092448703784862617, 3030866256020577}, - {8895000324450291201, 15634640715766263880, 7490240763823358372, 3817243160942000768, 6890890435018787185, 9346708229944942004, 18064456205166879593, 12575272864892718868, 15621790773858233563, 14813107721424927109, 8430498945985935887, 867090342578861}, - {12739036589867187355, 9795881745418909683, 5361817503043430525, 7798704069278097907, 18096088569918482706, 2937445070828306418, 9991593439280666445, 15110215917620419457, 921826231237854254, 5913792261041157634, 18090796787901390905, 2793222464357693}, - {3438032613460386180, 6204279332812840808, 14376358175686985834, 18381747228820135059, 14447820798834719386, 16106544860316335529, 14025545057246822816, 2539847764773437436, 4552716052497346485, 5833901919148093744, 13521223476405376994, 2797828028642414}, - {4675764524655489549, 7417596943369946677, 9595278207029085025, 5342154649589543352, 18404482716153763072, 2184348815728369995, 14066858184312939843, 16474267930709347218, 2362065103222405087, 3078344645281881403, 13469353821803478908, 304023594704714}, - {13336540334000055874, 12363933549556690779, 9868198113486195987, 17200559107951779810, 10375132447814441523, 16052049732850218957, 8448578710116104683, 271552525374008518, 8581766105752285893, 6711806820578914344, 14469754450868659033, 1966815926308711}, - {5101880310190341644, 15159071985462456973, 7149774646728826197, 12576407457185946487, 17860866655468525238, 4507443374425691963, 17618967833350108716, 7421484418298085264, 6158022389899675973, 108715159554595854, 9388611835741856695, 3474499278497195}, - {15767622754071481373, 6666535779016477106, 4339596343074584784, 4593977422804012446, 8260949087674697005, 2604766566145864187, 10520452650074955497, 9902394558193624589, 12775751068121307426, 10756048839832512283, 7768310516012640156, 1089103621897159}, - {5654788162836550163, 13749357347467323269, 13974653046468144914, 15362323059394904107, 10078076543768098232, 11650909370168368583, 12022622616989434446, 16634402826677993967, 6252490137121470998, 10291036775792765672, 13911313412251103552, 1186595065825944}, - {9121325598321594489, 4133870066251362254, 8684200896151736292, 11861689787908128953, 15931378051893636612, 12451213672520635731, 12914385543399447420, 8572569051941496555, 2726874492944369081, 10801779996113951741, 5864104807527920738, 1982990352964359}, - {11090133365261209413, 15814955091366535667, 18339652705270534313, 12470411874283941774, 7241404949791996603, 8825390212707063972, 9965333245992945637, 8672796411653931019, 1540315761125104703, 12195021924674475366, 18070568062581496335, 3519904720183927}, - {1301757369757148520, 2291752182431491104, 18031597538102297383, 1002578022922400175, 17456614351673438697, 13042781829346793120, 15050871415347786395, 14032535732147643747, 1819269103672142070, 5052963464011981037, 3935730851297570856, 3098099795194852}, - {1598327534129828645, 1100792835020160421, 14836203351660379192, 14908939120570101326, 16000114216335714301, 3466300856246268575, 3868982654067330071, 9459325155447144247, 16298126741239179846, 4600431113223808736, 16925178278642030656, 2162687411385134}, - {17788148994062165922, 7922294369928924429, 14353729795536090711, 11780816328988316738, 4791479909649638698, 4114510181199450025, 1665112860230268902, 9464476004182348196, 10513922006780843432, 7647020907782691752, 907039416571889407, 2600070448243251}, - {18279740539772034858, 13488091108525447666, 7632730442377627346, 14964872363027481484, 10761640429414937302, 12381919936976899683, 11478319527418241156, 9634723465607073757, 6359127698243469512, 2747481342146307926, 7704083516220795600, 1774160264476624}, - {13635372110860167721, 1296080719790173115, 11867537485597319624, 10870737671048455066, 11726493726432719410, 12745805811649554768, 12333923534385142391, 17801909532926800273, 11608940035451353880, 2975460672602133984, 5200998817217096522, 664536368560130}, - }, - x) -} - -func g2Isogeny(p *G2Affine) { - - den := make([]fp.Element, 2) - - g2IsogenyYDenominator(&den[1], &p.X) - g2IsogenyXDenominator(&den[0], &p.X) - - g2IsogenyYNumerator(&p.Y, &p.X, &p.Y) - g2IsogenyXNumerator(&p.X, &p.X) - - den = fp.BatchInvert(den) - - p.X.Mul(&p.X, &den[0]) - p.Y.Mul(&p.Y, &den[1]) -} - -// g2SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue -// if not, we get sqrt(Z * u / v). Recall that Z is non-residue -// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. -// The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided -func g2SqrtRatio(z *fp.Element, u *fp.Element, v *fp.Element) uint64 { - - // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-sqrt_ratio-for-any-field - - tv1 := fp.Element{17302715199413996045, 15077845457253267709, 8842885729139027579, 12189878420705505575, 12380986790262239346, 585111498723936856, 4947215576903759546, 1186632482028566920, 14543050817583235372, 5644943604719368358, 9440830989708189862, 1039766423535362} //tv1 = c6 - - var tv2, tv3, tv4, tv5 fp.Element - var exp big.Int - // c4 = 4835703278458516698824703 = 2⁸² - 1 - // q is odd so c1 is at least 1. - exp.SetBytes([]byte{3, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}) - - tv2.Exp(*v, &exp) // 2. tv2 = vᶜ⁴ - tv3.Square(&tv2) // 3. tv3 = tv2² - tv3.Mul(&tv3, v) // 4. tv3 = tv3 * v - tv5.Mul(u, &tv3) // 5. tv5 = u * tv3 - - // c3 = 37877157660731232732990269576663233239936484746509109593426423261538632780449313352717366389444912082695314931794809746268936574949192324351273838279701014606648452884726586254167471840902479876056412368 - exp.SetBytes([]byte{1, 238, 213, 183, 107, 119, 49, 92, 85, 130, 79, 195, 198, 173, 25, 235, 146, 241, 154, 95, 88, 89, 209, 63, 126, 70, 68, 40, 170, 44, 116, 217, 152, 213, 206, 120, 133, 72, 219, 61, 96, 89, 2, 93, 64, 159, 85, 65, 79, 214, 57, 103, 160, 220, 200, 220, 82, 89, 162, 189, 182, 200, 212, 168, 96, 85, 71, 132, 177, 188, 251, 218, 22, 208, 189, 13, 10, 73, 216, 6, 120, 252, 199, 240, 208}) - - tv5.Exp(tv5, &exp) // 6. tv5 = tv5ᶜ³ - tv5.Mul(&tv5, &tv2) // 7. tv5 = tv5 * tv2 - tv2.Mul(&tv5, v) // 8. tv2 = tv5 * v - tv3.Mul(&tv5, u) // 9. tv3 = tv5 * u - tv4.Mul(&tv3, &tv2) // 10. tv4 = tv3 * tv2 - - // c5 = 2417851639229258349412352 - exp.SetBytes([]byte{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) - tv5.Exp(tv4, &exp) // 11. tv5 = tv4ᶜ⁵ - isQNr := g2NotOne(&tv5) // 12. isQR = tv5 == 1 - c7 := fp.Element{13990906742184113945, 15879050380504523621, 13768460034940508157, 12337541071329853620, 6296858130192020747, 9289986178217863086, 18403114759403589657, 4546259071787184045, 5504643400205978814, 13830311104669138548, 96107744534255859, 1024735223965534} - tv2.Mul(&tv3, &c7) // 13. tv2 = tv3 * c7 - tv5.Mul(&tv4, &tv1) // 14. tv5 = tv4 * tv1 - tv3.Select(int(isQNr), &tv3, &tv2) // 15. tv3 = CMOV(tv2, tv3, isQR) - tv4.Select(int(isQNr), &tv4, &tv5) // 16. tv4 = CMOV(tv5, tv4, isQR) - exp.Lsh(big.NewInt(1), 82-2) // 18, 19: tv5 = 2ⁱ⁻² for i = c1 - - for i := 82; i >= 2; i-- { // 17. for i in (c1, c1 - 1, ..., 2): - - tv5.Exp(tv4, &exp) // 20. tv5 = tv4ᵗᵛ⁵ - nE1 := g2NotOne(&tv5) // 21. e1 = tv5 == 1 - tv2.Mul(&tv3, &tv1) // 22. tv2 = tv3 * tv1 - tv1.Mul(&tv1, &tv1) // 23. tv1 = tv1 * tv1 Why not write square? - tv5.Mul(&tv4, &tv1) // 24. tv5 = tv4 * tv1 - tv3.Select(int(nE1), &tv3, &tv2) // 25. tv3 = CMOV(tv2, tv3, e1) - tv4.Select(int(nE1), &tv4, &tv5) // 26. tv4 = CMOV(tv5, tv4, e1) - - if i > 2 { - exp.Rsh(&exp, 1) // 18, 19. tv5 = 2ⁱ⁻² - } - } - - *z = tv3 - return isQNr -} - -func g2NotOne(x *fp.Element) uint64 { - - var one fp.Element - return one.SetOne().NotEqual(x) - -} - -// g2MulByZ multiplies x by [11] and stores the result in z -func g2MulByZ(z *fp.Element, x *fp.Element) { - - res := *x - - res.Double(&res) - res.Double(&res) - res.Add(&res, x) - res.Double(&res) - res.Add(&res, x) - - *z = res -} - -// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method -// MapToCurve2 implements the SSWU map -// No cofactor clearing or isogeny -func MapToCurve2(u *fp.Element) G2Affine { - - var sswuIsoCurveCoeffA = fp.Element{11188695195863236139, 18339800635248689929, 13644954250665578253, 16122525194076552550, 1985822167495960177, 11021218035968661748, 12951199075167016614, 18080500199774882647, 3065668365127963650, 1810223365641727596, 18249180996905802984, 4351293214471385} - var sswuIsoCurveCoeffB = fp.Element{3597427888115195847, 8485485194496420669, 9451115945982544412, 10217463679676360079, 3023875305953960937, 5866766270380139867, 15059909646037855295, 1065687373540957157, 12978541562777068958, 18112033168403904062, 11632286302244735111, 1469792042332206} - - var tv1 fp.Element - tv1.Square(u) // 1. tv1 = u² - - //mul tv1 by Z - g2MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 - - var tv2 fp.Element - tv2.Square(&tv1) // 3. tv2 = tv1² - tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 - - var tv3 fp.Element - var tv4 fp.Element - tv4.SetOne() - tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 - tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 - - tv2NZero := g2NotZero(&tv2) - - // tv4 = Z - tv4 = fp.Element{18446744073709504998, 11529623972028612607, 739483395258014634, 5527028560780200701, 11477868704616895891, 15905434021829949368, 2844651761892435780, 17567410508478669002, 4162242322955979641, 15743938111024983262, 11916654042695069468, 4062866236140222} - - tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) - tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - - tv2.Square(&tv3) // 9. tv2 = tv3² - - var tv6 fp.Element - tv6.Square(&tv4) // 10. tv6 = tv4² - - var tv5 fp.Element - tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - - tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 - tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 - tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - - tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 - tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 - - var x fp.Element - x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 - - var y1 fp.Element - gx1NSquare := g2SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) - - var y fp.Element - y.Mul(&tv1, u) // 19. y = tv1 * u - - y.Mul(&y, &y1) // 20. y = y * y1 - - x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) - y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) - - y1.Neg(&y) - y.Select(int(g2Sgn0(u)^g2Sgn0(&y)), &y, &y1) - - // 23. e1 = sgn0(u) == sgn0(y) - // 24. y = CMOV(-y, y, e1) - - x.Div(&x, &tv4) // 25. x = x / tv4 - - return G2Affine{x, y} -} - -func g2EvalPolynomial(z *fp.Element, monic bool, coefficients []fp.Element, x *fp.Element) { - dst := coefficients[len(coefficients)-1] - - if monic { - dst.Add(&dst, x) - } - - for i := len(coefficients) - 2; i >= 0; i-- { - dst.Mul(&dst, x) - dst.Add(&dst, &coefficients[i]) - } - - z.Set(&dst) -} - -// g2Sgn0 is an algebraic substitute for the notion of sign in ordered fields -// Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function -// The sign of an element is not obviously related to that of its Montgomery form -func g2Sgn0(z *fp.Element) uint64 { - - nonMont := z.Bits() - - // m == 1 - return nonMont[0] % 2 - -} - -// MapToG2 invokes the SSWU map, and guarantees that the result is in g2 -func MapToG2(u fp.Element) G2Affine { - res := MapToCurve2(&u) - //this is in an isogenous curve - g2Isogeny(&res) - res.ClearCofactor(&res) - return res -} - -// EncodeToG2 hashes a message to a point on the G2 curve using the SSWU map. -// It is faster than HashToG2, but the result is not uniformly distributed. Unsuitable as a random oracle. -// dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap -func EncodeToG2(msg, dst []byte) (G2Affine, error) { - - var res G2Affine - u, err := fp.Hash(msg, dst, 1) - if err != nil { - return res, err - } - - res = MapToCurve2(&u[0]) - - //this is in an isogenous curve - g2Isogeny(&res) - res.ClearCofactor(&res) - return res, nil -} - -// HashToG2 hashes a message to a point on the G2 curve using the SSWU map. -// Slower than EncodeToG2, but usable as a random oracle. -// dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap -func HashToG2(msg, dst []byte) (G2Affine, error) { - u, err := fp.Hash(msg, dst, 2*1) - if err != nil { - return G2Affine{}, err - } - - Q0 := MapToCurve2(&u[0]) - Q1 := MapToCurve2(&u[1]) - - //TODO (perf): Add in E' first, then apply isogeny - g2Isogeny(&Q0) - g2Isogeny(&Q1) - - var _Q0, _Q1 G2Jac - _Q0.FromAffine(&Q0) - _Q1.FromAffine(&Q1).AddAssign(&_Q0) - - _Q1.ClearCofactor(&_Q1) - - Q1.FromJacobian(&_Q1) - return Q1, nil -} - -func g2NotZero(x *fp.Element) uint64 { - - return x[0] | x[1] | x[2] | x[3] | x[4] | x[5] | x[6] | x[7] | x[8] | x[9] | x[10] | x[11] - -} diff --git a/ecc/bw6-756/hash_to_g2_test.go b/ecc/bw6-756/hash_to_g2_test.go deleted file mode 100644 index 0dca72afd4..0000000000 --- a/ecc/bw6-756/hash_to_g2_test.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bw6756 - -import ( - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" - "math/rand" - "testing" -) - -func TestG2SqrtRatio(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - gen := GenFp() - - properties.Property("G2SqrtRatio must square back to the right value", prop.ForAll( - func(u fp.Element, v fp.Element) bool { - - var seen fp.Element - qr := g2SqrtRatio(&seen, &u, &v) == 0 - - seen. - Square(&seen). - Mul(&seen, &v) - - var ref fp.Element - if qr { - ref = u - } else { - g2MulByZ(&ref, &u) - } - - return seen.Equal(&ref) - }, gen, gen)) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestHashToFpG2(t *testing.T) { - for _, c := range encodeToG2Vector.cases { - elems, err := fp.Hash([]byte(c.msg), encodeToG2Vector.dst, 1) - if err != nil { - t.Error(err) - } - g2TestMatchCoord(t, "u", c.msg, c.u, g2CoordAt(elems, 0)) - } - - for _, c := range hashToG2Vector.cases { - elems, err := fp.Hash([]byte(c.msg), hashToG2Vector.dst, 2*1) - if err != nil { - t.Error(err) - } - g2TestMatchCoord(t, "u0", c.msg, c.u0, g2CoordAt(elems, 0)) - g2TestMatchCoord(t, "u1", c.msg, c.u1, g2CoordAt(elems, 1)) - } -} - -func TestMapToCurve2(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[G2] mapping output must be on curve", prop.ForAll( - func(a fp.Element) bool { - - g := MapToCurve2(&a) - - if !isOnE2Prime(g) { - t.Log("Mapping output not on E' curve") - return false - } - g2Isogeny(&g) - - if !g.IsOnCurve() { - t.Log("Isogeny∘SSWU output not on curve") - return false - } - - return true - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - - for _, c := range encodeToG2Vector.cases { - var u fp.Element - g2CoordSetString(&u, c.u) - q := MapToCurve2(&u) - g2Isogeny(&q) - g2TestMatchPoint(t, "Q", c.msg, c.Q, &q) - } - - for _, c := range hashToG2Vector.cases { - var u fp.Element - g2CoordSetString(&u, c.u0) - q := MapToCurve2(&u) - g2Isogeny(&q) - g2TestMatchPoint(t, "Q0", c.msg, c.Q0, &q) - - g2CoordSetString(&u, c.u1) - q = MapToCurve2(&u) - g2Isogeny(&q) - g2TestMatchPoint(t, "Q1", c.msg, c.Q1, &q) - } -} - -func TestMapToG2(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[G2] mapping to curve should output point on the curve", prop.ForAll( - func(a fp.Element) bool { - g := MapToG2(a) - return g.IsInSubGroup() - }, - GenFp(), - )) - - properties.Property("[G2] mapping to curve should be deterministic", prop.ForAll( - func(a fp.Element) bool { - g1 := MapToG2(a) - g2 := MapToG2(a) - return g1.Equal(&g2) - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestEncodeToG2(t *testing.T) { - t.Parallel() - for _, c := range encodeToG2Vector.cases { - p, err := EncodeToG2([]byte(c.msg), encodeToG2Vector.dst) - if err != nil { - t.Fatal(err) - } - g2TestMatchPoint(t, "P", c.msg, c.P, &p) - } -} - -func TestHashToG2(t *testing.T) { - t.Parallel() - for _, c := range hashToG2Vector.cases { - p, err := HashToG2([]byte(c.msg), hashToG2Vector.dst) - if err != nil { - t.Fatal(err) - } - g2TestMatchPoint(t, "P", c.msg, c.P, &p) - } -} - -func BenchmarkEncodeToG2(b *testing.B) { - const size = 54 - bytes := make([]byte, size) - dst := encodeToG2Vector.dst - b.ResetTimer() - - for i := 0; i < b.N; i++ { - - bytes[rand.Int()%size] = byte(rand.Int()) //#nosec G404 weak rng is fine here - - if _, err := EncodeToG2(bytes, dst); err != nil { - b.Fail() - } - } -} - -func BenchmarkHashToG2(b *testing.B) { - const size = 54 - bytes := make([]byte, size) - dst := hashToG2Vector.dst - b.ResetTimer() - - for i := 0; i < b.N; i++ { - - bytes[rand.Int()%size] = byte(rand.Int()) //#nosec G404 weak rng is fine here - - if _, err := HashToG2(bytes, dst); err != nil { - b.Fail() - } - } -} - -// TODO: Crude. Do something clever in Jacobian -func isOnE2Prime(p G2Affine) bool { - - var A, B fp.Element - - A.SetString( - "57118602307744159425297682319721487564160653101999767145324052773096108189367478774283030732039809576408712550869319179673047506447106866804266849934468622277092669812350015113558742359403374094102882495878422707102424706004276", - ) - - B.SetString( - "102855108109390644514901355293249479796903716651147068364918761583198525192685128950909159755581325881592462005806693031525796628657465213247629032577632139481530358423912698010647636802733896537485022416927428630654454903107531", - ) - - var LHS fp.Element - LHS. - Square(&p.Y). - Sub(&LHS, &B) - - var RHS fp.Element - RHS. - Square(&p.X). - Add(&RHS, &A). - Mul(&RHS, &p.X) - - return LHS.Equal(&RHS) -} - -// Only works on simple extensions (two-story towers) -func g2CoordSetString(z *fp.Element, s string) { - z.SetString(s) -} - -func g2CoordAt(slice []fp.Element, i int) fp.Element { - return slice[i] -} - -func g2TestMatchCoord(t *testing.T, coordName string, msg string, expectedStr string, seen fp.Element) { - var expected fp.Element - - g2CoordSetString(&expected, expectedStr) - - if !expected.Equal(&seen) { - t.Errorf("mismatch on \"%s\", %s:\n\texpected %s\n\tsaw %s", msg, coordName, expected.String(), &seen) - } -} - -func g2TestMatchPoint(t *testing.T, pointName string, msg string, expected point, seen *G2Affine) { - g2TestMatchCoord(t, pointName+".x", msg, expected.x, seen.X) - g2TestMatchCoord(t, pointName+".y", msg, expected.y, seen.Y) -} - -var encodeToG2Vector encodeTestVector -var hashToG2Vector hashTestVector diff --git a/ecc/bw6-756/internal/fptower/e3.go b/ecc/bw6-756/internal/fptower/e3.go deleted file mode 100644 index 089101ebdc..0000000000 --- a/ecc/bw6-756/internal/fptower/e3.go +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2020 ConsenSys AG -// -// 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 fptower - -import ( - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" -) - -// E3 is a degree-three finite field extension of fp3 -type E3 struct { - A0, A1, A2 fp.Element -} - -// Equal returns true if z equals x, false otherwise -// note this is more efficient than calling "z == x" -func (z *E3) Equal(x *E3) bool { - return z.A0.Equal(&x.A0) && z.A1.Equal(&x.A1) && z.A2.Equal(&x.A2) -} - -// SetString sets a E3 elmt from string -func (z *E3) SetString(s1, s2, s3 string) *E3 { - z.A0.SetString(s1) - z.A1.SetString(s2) - z.A2.SetString(s3) - return z -} - -// SetZero sets an E3 elmt to zero -func (z *E3) SetZero() *E3 { - *z = E3{} - return z -} - -// Clone returns a copy of self -func (z *E3) Clone() *E3 { - return &E3{ - A0: z.A0, - A1: z.A1, - A2: z.A2, - } -} - -// Set Sets a E3 elmt form another E3 elmt -func (z *E3) Set(x *E3) *E3 { - *z = *x - return z -} - -// SetOne sets z to 1 in Montgomery form and returns z -func (z *E3) SetOne() *E3 { - z.A0.SetOne() - z.A1.SetZero() - z.A2.SetZero() - return z -} - -// SetRandom sets z to a random elmt -func (z *E3) SetRandom() (*E3, error) { - if _, err := z.A0.SetRandom(); err != nil { - return nil, err - } - if _, err := z.A1.SetRandom(); err != nil { - return nil, err - } - if _, err := z.A2.SetRandom(); err != nil { - return nil, err - } - return z, nil -} - -// IsZero returns true if z is zero, false otherwise -func (z *E3) IsZero() bool { - return z.A0.IsZero() && z.A1.IsZero() && z.A2.IsZero() -} - -// IsOne returns true if z is one, false otherwise -func (z *E3) IsOne() bool { - return z.A0.IsOne() && z.A1.IsZero() && z.A2.IsZero() -} - -// Neg negates the E3 number -func (z *E3) Neg(x *E3) *E3 { - z.A0.Neg(&x.A0) - z.A1.Neg(&x.A1) - z.A2.Neg(&x.A2) - return z -} - -// Add adds two elements of E3 -func (z *E3) Add(x, y *E3) *E3 { - z.A0.Add(&x.A0, &y.A0) - z.A1.Add(&x.A1, &y.A1) - z.A2.Add(&x.A2, &y.A2) - return z -} - -// Sub subtracts two elements of E3 -func (z *E3) Sub(x, y *E3) *E3 { - z.A0.Sub(&x.A0, &y.A0) - z.A1.Sub(&x.A1, &y.A1) - z.A2.Sub(&x.A2, &y.A2) - return z -} - -// Double doubles an element in E3 -func (z *E3) Double(x *E3) *E3 { - z.A0.Double(&x.A0) - z.A1.Double(&x.A1) - z.A2.Double(&x.A2) - return z -} - -// String puts E3 elmt in string form -func (z *E3) String() string { - return (z.A0.String() + "+(" + z.A1.String() + ")*u+(" + z.A2.String() + ")*u**2") -} - -// MulByElement multiplies an element in E3 by an element in fp -func (z *E3) MulByElement(x *E3, y *fp.Element) *E3 { - _y := *y - z.A0.Mul(&x.A0, &_y) - z.A1.Mul(&x.A1, &_y) - z.A2.Mul(&x.A2, &_y) - return z -} - -// MulBy12 multiplication by sparse element (0,b1,b2) -func (x *E3) MulBy12(b1, b2 *fp.Element) *E3 { - var t1, t2, c0, tmp, c1, c2 fp.Element - t1.Mul(&x.A1, b1) - t2.Mul(&x.A2, b2) - c0.Add(&x.A1, &x.A2) - tmp.Add(b1, b2) - c0.Mul(&c0, &tmp) - c0.Sub(&c0, &t1) - c0.Sub(&c0, &t2) - c0.MulByNonResidue(&c0) - c1.Add(&x.A0, &x.A1) - c1.Mul(&c1, b1) - c1.Sub(&c1, &t1) - tmp.MulByNonResidue(&t2) - c1.Add(&c1, &tmp) - tmp.Add(&x.A0, &x.A2) - c2.Mul(b2, &tmp) - c2.Sub(&c2, &t2) - c2.Add(&c2, &t1) - - x.A0 = c0 - x.A1 = c1 - x.A2 = c2 - - return x -} - -// MulBy01 multiplication by sparse element (c0,c1,0) -func (z *E3) MulBy01(c0, c1 *fp.Element) *E3 { - - var a, b, tmp, t0, t1, t2 fp.Element - - a.Mul(&z.A0, c0) - b.Mul(&z.A1, c1) - - tmp.Add(&z.A1, &z.A2) - t0.Mul(c1, &tmp) - t0.Sub(&t0, &b) - t0.MulByNonResidue(&t0) - t0.Add(&t0, &a) - - tmp.Add(&z.A0, &z.A2) - t2.Mul(c0, &tmp) - t2.Sub(&t2, &a) - t2.Add(&t2, &b) - - t1.Add(c0, c1) - tmp.Add(&z.A0, &z.A1) - t1.Mul(&t1, &tmp) - t1.Sub(&t1, &a) - t1.Sub(&t1, &b) - - z.A0.Set(&t0) - z.A1.Set(&t1) - z.A2.Set(&t2) - - return z -} - -// MulBy1 multiplication of E6 by sparse element (0, c1, 0) -func (z *E3) MulBy1(c1 *fp.Element) *E3 { - - var b, tmp, t0, t1 fp.Element - b.Mul(&z.A1, c1) - - tmp.Add(&z.A1, &z.A2) - t0.Mul(c1, &tmp) - t0.Sub(&t0, &b) - t0.MulByNonResidue(&t0) - - tmp.Add(&z.A0, &z.A1) - t1.Mul(c1, &tmp) - t1.Sub(&t1, &b) - - z.A0.Set(&t0) - z.A1.Set(&t1) - z.A2.Set(&b) - - return z -} - -// Mul sets z to the E3-product of x,y, returns z -func (z *E3) Mul(x, y *E3) *E3 { - // Karatsuba method for cubic extensions - // https://eprint.iacr.org/2006/471.pdf (section 4) - var t0, t1, t2, c0, c1, c2, tmp fp.Element - t0.Mul(&x.A0, &y.A0) - t1.Mul(&x.A1, &y.A1) - t2.Mul(&x.A2, &y.A2) - - c0.Add(&x.A1, &x.A2) - tmp.Add(&y.A1, &y.A2) - c0.Mul(&c0, &tmp).Sub(&c0, &t1).Sub(&c0, &t2).MulByNonResidue(&c0) - - tmp.Add(&x.A0, &x.A2) - c2.Add(&y.A0, &y.A2).Mul(&c2, &tmp).Sub(&c2, &t0).Sub(&c2, &t2) - - c1.Add(&x.A0, &x.A1) - tmp.Add(&y.A0, &y.A1) - c1.Mul(&c1, &tmp).Sub(&c1, &t0).Sub(&c1, &t1) - t2.MulByNonResidue(&t2) - - z.A0.Add(&c0, &t0) - z.A1.Add(&c1, &t2) - z.A2.Add(&c2, &t1) - - return z -} - -// MulAssign sets z to the E3-product of z,y, returns z -func (z *E3) MulAssign(x *E3) *E3 { - return z.Mul(z, x) -} - -// Square sets z to the E3-product of x,x, returns z -func (z *E3) Square(x *E3) *E3 { - - // Algorithm 16 from https://eprint.iacr.org/2010/354.pdf - var c4, c5, c1, c2, c3, c0, c6 fp.Element - - c6.Double(&x.A1) - c4.Mul(&x.A0, &c6) // x.A0 * xA1 * 2 - c5.Square(&x.A2) - c1.MulByNonResidue(&c5).Add(&c1, &c4) - c2.Sub(&c4, &c5) - - c3.Square(&x.A0) - c4.Sub(&x.A0, &x.A1).Add(&c4, &x.A2) - c5.Mul(&c6, &x.A2) // x.A1 * xA2 * 2 - c4.Square(&c4) - c0.MulByNonResidue(&c5) - c4.Add(&c4, &c5).Sub(&c4, &c3) - - z.A0.Add(&c0, &c3) - z.A1 = c1 - z.A2.Add(&c2, &c4) - - return z -} - -// MulByNonResidue mul x by (0,1,0) -func (z *E3) MulByNonResidue(x *E3) *E3 { - z.A2, z.A1, z.A0 = x.A1, x.A0, x.A2 - z.A0.MulByNonResidue(&z.A0) - return z -} - -// Inverse an element in E3 -// -// if x == 0, sets and returns z = x -func (z *E3) Inverse(x *E3) *E3 { - // Algorithm 17 from https://eprint.iacr.org/2010/354.pdf - // step 9 is wrong in the paper it's t1-t4 - var t0, t1, t2, t3, t4, t5, t6, c0, c1, c2, d1, d2 fp.Element - t0.Square(&x.A0) - t1.Square(&x.A1) - t2.Square(&x.A2) - t3.Mul(&x.A0, &x.A1) - t4.Mul(&x.A0, &x.A2) - t5.Mul(&x.A1, &x.A2) - c0.MulByNonResidue(&t5).Neg(&c0).Add(&c0, &t0) - c1.MulByNonResidue(&t2).Sub(&c1, &t3) - c2.Sub(&t1, &t4) - t6.Mul(&x.A0, &c0) - d1.Mul(&x.A2, &c1) - d2.Mul(&x.A1, &c2) - d1.Add(&d1, &d2).MulByNonResidue(&d1) - t6.Add(&t6, &d1) - t6.Inverse(&t6) - z.A0.Mul(&c0, &t6) - z.A1.Mul(&c1, &t6) - z.A2.Mul(&c2, &t6) - - return z -} - -// BatchInvertE3 returns a new slice with every element in a inverted. -// It uses Montgomery batch inversion trick. -// -// if a[i] == 0, returns result[i] = a[i] -func BatchInvertE3(a []E3) []E3 { - res := make([]E3, len(a)) - if len(a) == 0 { - return res - } - - zeroes := make([]bool, len(a)) - var accumulator E3 - accumulator.SetOne() - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - zeroes[i] = true - continue - } - res[i].Set(&accumulator) - accumulator.Mul(&accumulator, &a[i]) - } - - accumulator.Inverse(&accumulator) - - for i := len(a) - 1; i >= 0; i-- { - if zeroes[i] { - continue - } - res[i].Mul(&res[i], &accumulator) - accumulator.Mul(&accumulator, &a[i]) - } - - return res -} diff --git a/ecc/bw6-756/internal/fptower/e3_test.go b/ecc/bw6-756/internal/fptower/e3_test.go deleted file mode 100644 index 09cc04640f..0000000000 --- a/ecc/bw6-756/internal/fptower/e3_test.go +++ /dev/null @@ -1,313 +0,0 @@ -package fptower - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -// ------------------------------------------------------------ -// tests - -func TestE3ReceiverIsOperand(t *testing.T) { - t.Parallel() - - parameters := gopter.DefaultTestParameters() - parameters.MinSuccessfulTests = 100 - - properties := gopter.NewProperties(parameters) - - genA := GenE3() - genB := GenE3() - genfp := GenFp() - - properties.Property("[BW756] Having the receiver as operand (addition) should output the same result", prop.ForAll( - func(a, b *E3) bool { - var c, d E3 - d.Set(a) - c.Add(a, b) - a.Add(a, b) - b.Add(&d, b) - return a.Equal(b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("[BW756] Having the receiver as operand (sub) should output the same result", prop.ForAll( - func(a, b *E3) bool { - var c, d E3 - d.Set(a) - c.Sub(a, b) - a.Sub(a, b) - b.Sub(&d, b) - return a.Equal(b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("[BW756] Having the receiver as operand (mul) should output the same result", prop.ForAll( - func(a, b *E3) bool { - var c, d E3 - d.Set(a) - c.Mul(a, b) - a.Mul(a, b) - b.Mul(&d, b) - return a.Equal(b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("[BW756] Having the receiver as operand (square) should output the same result", prop.ForAll( - func(a *E3) bool { - var b E3 - b.Square(a) - a.Square(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BW756] Having the receiver as operand (neg) should output the same result", prop.ForAll( - func(a *E3) bool { - var b E3 - b.Neg(a) - a.Neg(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BW756] Having the receiver as operand (double) should output the same result", prop.ForAll( - func(a *E3) bool { - var b E3 - b.Double(a) - a.Double(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BW756] Having the receiver as operand (mul by non residue) should output the same result", prop.ForAll( - func(a *E3) bool { - var b E3 - b.MulByNonResidue(a) - a.MulByNonResidue(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BW756] Having the receiver as operand (Inverse) should output the same result", prop.ForAll( - func(a *E3) bool { - var b E3 - b.Inverse(a) - a.Inverse(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BW756] Having the receiver as operand (mul by element) should output the same result", prop.ForAll( - func(a *E3, b fp.Element) bool { - var c E3 - c.MulByElement(a, &b) - a.MulByElement(a, &b) - return a.Equal(&c) - }, - genA, - genfp, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestE3Ops(t *testing.T) { - t.Parallel() - - parameters := gopter.DefaultTestParameters() - parameters.MinSuccessfulTests = 100 - - properties := gopter.NewProperties(parameters) - - genA := GenE3() - genB := GenE3() - genfp := GenFp() - - properties.Property("[BW756] sub & add should leave an element invariant", prop.ForAll( - func(a, b *E3) bool { - var c E3 - c.Set(a) - c.Add(&c, b).Sub(&c, b) - return c.Equal(a) - }, - genA, - genB, - )) - - properties.Property("[BW756] mul & inverse should leave an element invariant", prop.ForAll( - func(a, b *E3) bool { - var c, d E3 - d.Inverse(b) - c.Set(a) - c.Mul(&c, b).Mul(&c, &d) - return c.Equal(a) - }, - genA, - genB, - )) - - properties.Property("[BW756] inverse twice should leave an element invariant", prop.ForAll( - func(a *E3) bool { - var b E3 - b.Inverse(a).Inverse(&b) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BW756] BatchInvertE3 should output the same result as Inverse", prop.ForAll( - func(a, b, c *E3) bool { - - batch := BatchInvertE3([]E3{*a, *b, *c}) - a.Inverse(a) - b.Inverse(b) - c.Inverse(c) - return a.Equal(&batch[0]) && b.Equal(&batch[1]) && c.Equal(&batch[2]) - }, - genA, - genA, - genA, - )) - - properties.Property("[BW756] neg twice should leave an element invariant", prop.ForAll( - func(a *E3) bool { - var b E3 - b.Neg(a).Neg(&b) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BW756] square and mul should output the same result", prop.ForAll( - func(a *E3) bool { - var b, c E3 - b.Mul(a, a) - c.Square(a) - return b.Equal(&c) - }, - genA, - )) - - properties.Property("[BW756] MulByElement MulByElement inverse should leave an element invariant", prop.ForAll( - func(a *E3, b fp.Element) bool { - var c E3 - var d fp.Element - d.Inverse(&b) - c.MulByElement(a, &b).MulByElement(&c, &d) - return c.Equal(a) - }, - genA, - genfp, - )) - - properties.Property("[BW756] Double and mul by 2 should output the same result", prop.ForAll( - func(a *E3) bool { - var b E3 - var c fp.Element - c.SetUint64(2) - b.Double(a) - a.MulByElement(a, &c) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BW756] Mulbynonres should be the same as multiplying by (0,1)", prop.ForAll( - func(a *E3) bool { - var b, c, d E3 - b.A1.SetOne() - c.MulByNonResidue(a) - d.Mul(a, &b) - return c.Equal(&d) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -// ------------------------------------------------------------ -// benches - -func BenchmarkE3Add(b *testing.B) { - var a, c E3 - _, _ = a.SetRandom() - _, _ = c.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Add(&a, &c) - } -} - -func BenchmarkE3Sub(b *testing.B) { - var a, c E3 - _, _ = a.SetRandom() - _, _ = c.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Sub(&a, &c) - } -} - -func BenchmarkE3Mul(b *testing.B) { - var a, c E3 - _, _ = a.SetRandom() - _, _ = c.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Mul(&a, &c) - } -} - -func BenchmarkE3MulByElement(b *testing.B) { - var a E3 - var c fp.Element - _, _ = c.SetRandom() - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.MulByElement(&a, &c) - } -} - -func BenchmarkE3Square(b *testing.B) { - var a E3 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Square(&a) - } -} - -func BenchmarkE3Inverse(b *testing.B) { - var a E3 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Inverse(&a) - } -} - -func BenchmarkE3MulNonRes(b *testing.B) { - var a E3 - _, _ = a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.MulByNonResidue(&a) - } -} diff --git a/ecc/bw6-756/internal/fptower/e6.go b/ecc/bw6-756/internal/fptower/e6.go deleted file mode 100644 index afb5ecc59b..0000000000 --- a/ecc/bw6-756/internal/fptower/e6.go +++ /dev/null @@ -1,773 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 fptower - -import ( - "errors" - "math/big" - "sync" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) - -var bigIntPool = sync.Pool{ - New: func() interface{} { - return new(big.Int) - }, -} - -// E6 is a degree two finite field extension of fp3 -type E6 struct { - B0, B1 E3 -} - -// Equal returns true if z equals x, false otherwise -func (z *E6) Equal(x *E6) bool { - return z.B0.Equal(&x.B0) && z.B1.Equal(&x.B1) -} - -// String puts E6 in string form -func (z *E6) String() string { - return (z.B0.String() + "+(" + z.B1.String() + ")*v") -} - -// SetString sets a E6 from string -func (z *E6) SetString(s0, s1, s2, s3, s4, s5 string) *E6 { - z.B0.SetString(s0, s1, s2) - z.B1.SetString(s3, s4, s5) - return z -} - -// Set copies x into z and returns z -func (z *E6) Set(x *E6) *E6 { - *z = *x - return z -} - -// SetOne sets z to 1 in Montgomery form and returns z -func (z *E6) SetOne() *E6 { - *z = E6{} - z.B0.A0.SetOne() - return z -} - -// Add sets z=x+y in E6 and returns z -func (z *E6) Add(x, y *E6) *E6 { - z.B0.Add(&x.B0, &y.B0) - z.B1.Add(&x.B1, &y.B1) - return z -} - -// Sub sets z to x-y and returns z -func (z *E6) Sub(x, y *E6) *E6 { - z.B0.Sub(&x.B0, &y.B0) - z.B1.Sub(&x.B1, &y.B1) - return z -} - -// Double sets z=2*x and returns z -func (z *E6) Double(x *E6) *E6 { - z.B0.Double(&x.B0) - z.B1.Double(&x.B1) - return z -} - -// SetRandom used only in tests -func (z *E6) SetRandom() (*E6, error) { - if _, err := z.B0.SetRandom(); err != nil { - return nil, err - } - if _, err := z.B1.SetRandom(); err != nil { - return nil, err - } - return z, nil -} - -// IsZero returns true if z is zero, false otherwise -func (z *E6) IsZero() bool { - return z.B0.IsZero() && z.B1.IsZero() -} - -// IsOne returns true if z is one, false otherwise -func (z *E6) IsOne() bool { - return z.B0.IsOne() && z.B1.IsZero() -} - -// Mul sets z=x*y in E6 and returns z -func (z *E6) Mul(x, y *E6) *E6 { - var a, b, c E3 - a.Add(&x.B0, &x.B1) - b.Add(&y.B0, &y.B1) - a.Mul(&a, &b) - b.Mul(&x.B0, &y.B0) - c.Mul(&x.B1, &y.B1) - z.B1.Sub(&a, &b).Sub(&z.B1, &c) - z.B0.MulByNonResidue(&c).Add(&z.B0, &b) - return z -} - -// Square sets z=x*x in E6 and returns z -func (z *E6) Square(x *E6) *E6 { - - //Algorithm 22 from https://eprint.iacr.org/2010/354.pdf - var c0, c2, c3 E3 - c0.Sub(&x.B0, &x.B1) - c3.MulByNonResidue(&x.B1).Neg(&c3).Add(&x.B0, &c3) - c2.Mul(&x.B0, &x.B1) - c0.Mul(&c0, &c3).Add(&c0, &c2) - z.B1.Double(&c2) - c2.MulByNonResidue(&c2) - z.B0.Add(&c0, &c2) - - return z -} - -// Karabina's compressed cyclotomic square -// https://eprint.iacr.org/2010/542.pdf -// Th. 3.2 with minor modifications to fit our tower -func (z *E6) CyclotomicSquareCompressed(x *E6) *E6 { - - var t [7]fp.Element - - // t0 = g1² - t[0].Square(&x.B0.A1) - // t1 = g5² - t[1].Square(&x.B1.A2) - // t5 = g1 + g5 - t[5].Add(&x.B0.A1, &x.B1.A2) - // t2 = (g1 + g5)² - t[2].Square(&t[5]) - - // t3 = g1² + g5² - t[3].Add(&t[0], &t[1]) - // t5 = 2 * g1 * g5 - t[5].Sub(&t[2], &t[3]) - - // t6 = g3 + g2 - t[6].Add(&x.B1.A0, &x.B0.A2) - // t3 = (g3 + g2)² - t[3].Square(&t[6]) - // t2 = g3² - t[2].Square(&x.B1.A0) - - // t6 = 2 * nr * g1 * g5 - t[6].MulByNonResidue(&t[5]) - // t5 = 4 * nr * g1 * g5 + 2 * g3 - t[5].Add(&t[6], &x.B1.A0). - Double(&t[5]) - // z3 = 6 * nr * g1 * g5 + 2 * g3 - z.B1.A0.Add(&t[5], &t[6]) - - // t4 = nr * g5² - t[4].MulByNonResidue(&t[1]) - // t5 = nr * g5² + g1² - t[5].Add(&t[0], &t[4]) - // t6 = nr * g5² + g1² - g2 - t[6].Sub(&t[5], &x.B0.A2) - - // t1 = g2² - t[1].Square(&x.B0.A2) - - // t6 = 2 * nr * g5² + 2 * g1² - 2*g2 - t[6].Double(&t[6]) - // z2 = 3 * nr * g5² + 3 * g1² - 2*g2 - z.B0.A2.Add(&t[6], &t[5]) - - // t4 = nr * g2² - t[4].MulByNonResidue(&t[1]) - // t5 = g3² + nr * g2² - t[5].Add(&t[2], &t[4]) - // t6 = g3² + nr * g2² - g1 - t[6].Sub(&t[5], &x.B0.A1) - // t6 = 2 * g3² + 2 * nr * g2² - 2 * g1 - t[6].Double(&t[6]) - // z1 = 3 * g3² + 3 * nr * g2² - 2 * g1 - z.B0.A1.Add(&t[6], &t[5]) - - // t0 = g2² + g3² - t[0].Add(&t[2], &t[1]) - // t5 = 2 * g3 * g2 - t[5].Sub(&t[3], &t[0]) - // t6 = 2 * g3 * g2 + g5 - t[6].Add(&t[5], &x.B1.A2) - // t6 = 4 * g3 * g2 + 2 * g5 - t[6].Double(&t[6]) - // z5 = 6 * g3 * g2 + 2 * g5 - z.B1.A2.Add(&t[5], &t[6]) - - return z -} - -// DecompressKarabina Karabina's cyclotomic square result -// if g3 != 0 -// -// g4 = (E * g5² + 3 * g1² - 2 * g2)/4g3 -// -// if g3 == 0 -// -// g4 = 2g1g5/g2 -// -// if g3=g2=0 then g4=g5=g1=0 and g0=1 (x=1) -// Theorem 3.1 is well-defined for all x in Gϕₙ\{1} -func (z *E6) DecompressKarabina(x *E6) *E6 { - - var t [3]fp.Element - var one fp.Element - one.SetOne() - - if x.B1.A0.IsZero() /* g3 == 0 */ { - t[0].Mul(&x.B0.A1, &x.B1.A2). - Double(&t[0]) - // t1 = g2 - t[1].Set(&x.B0.A2) - - // g3 != 0 - - if t[1].IsZero() /* g2 == g3 == 0 */ { - return z.SetOne() - } - } else /* g3 != 0 */ { - // t0 = g1² - t[0].Square(&x.B0.A1) - // t1 = 3 * g1² - 2 * g2 - t[1].Sub(&t[0], &x.B0.A2). - Double(&t[1]). - Add(&t[1], &t[0]) - // t0 = E * g5² + t1 - t[2].Square(&x.B1.A2) - t[0].MulByNonResidue(&t[2]). - Add(&t[0], &t[1]) - // t1 = 1/(4 * g3) - t[1].Double(&x.B1.A0). - Double(&t[1]) - } - - // z4 = g4 - z.B1.A1.Div(&t[0], &t[1]) // costly - - // t1 = g2 * g1 - t[1].Mul(&x.B0.A2, &x.B0.A1) - // t2 = 2 * g4² - 3 * g2 * g1 - t[2].Square(&x.B1.A1). - Sub(&t[2], &t[1]). - Double(&t[2]). - Sub(&t[2], &t[1]) - // t1 = g3 * g5 (g3 can be 0) - t[1].Mul(&x.B1.A0, &x.B1.A2) - // c₀ = E * (2 * g4² + g3 * g5 - 3 * g2 * g1) + 1 - t[2].Add(&t[2], &t[1]) - z.B0.A0.MulByNonResidue(&t[2]). - Add(&z.B0.A0, &one) - - z.B0.A1.Set(&x.B0.A1) - z.B0.A2.Set(&x.B0.A2) - z.B1.A0.Set(&x.B1.A0) - z.B1.A2.Set(&x.B1.A2) - - return z -} - -// Granger-Scott's cyclotomic square -// https://eprint.iacr.org/2009/565.pdf, 3.2 -func (z *E6) CyclotomicSquare(x *E6) *E6 { - // x=(x0,x1,x2,x3,x4,x5,x6,x7) in E3⁶ - // cyclosquare(x)=(3*x4²*u + 3*x0² - 2*x0, - // 3*x2²*u + 3*x3² - 2*x1, - // 3*x5²*u + 3*x1² - 2*x2, - // 6*x1*x5*u + 2*x3, - // 6*x0*x4 + 2*x4, - // 6*x2*x3 + 2*x5) - - var t [9]fp.Element - - t[0].Square(&x.B1.A1) - t[1].Square(&x.B0.A0) - t[6].Add(&x.B1.A1, &x.B0.A0).Square(&t[6]).Sub(&t[6], &t[0]).Sub(&t[6], &t[1]) // 2*x4*x0 - t[2].Square(&x.B0.A2) - t[3].Square(&x.B1.A0) - t[7].Add(&x.B0.A2, &x.B1.A0).Square(&t[7]).Sub(&t[7], &t[2]).Sub(&t[7], &t[3]) // 2*x2*x3 - t[4].Square(&x.B1.A2) - t[5].Square(&x.B0.A1) - t[8].Add(&x.B1.A2, &x.B0.A1).Square(&t[8]).Sub(&t[8], &t[4]).Sub(&t[8], &t[5]).MulByNonResidue(&t[8]) // 2*x5*x1*u - - t[0].MulByNonResidue(&t[0]).Add(&t[0], &t[1]) // x4²*u + x0² - t[2].MulByNonResidue(&t[2]).Add(&t[2], &t[3]) // x2²*u + x3² - t[4].MulByNonResidue(&t[4]).Add(&t[4], &t[5]) // x5²*u + x1² - - z.B0.A0.Sub(&t[0], &x.B0.A0).Double(&z.B0.A0).Add(&z.B0.A0, &t[0]) - z.B0.A1.Sub(&t[2], &x.B0.A1).Double(&z.B0.A1).Add(&z.B0.A1, &t[2]) - z.B0.A2.Sub(&t[4], &x.B0.A2).Double(&z.B0.A2).Add(&z.B0.A2, &t[4]) - - z.B1.A0.Add(&t[8], &x.B1.A0).Double(&z.B1.A0).Add(&z.B1.A0, &t[8]) - z.B1.A1.Add(&t[6], &x.B1.A1).Double(&z.B1.A1).Add(&z.B1.A1, &t[6]) - z.B1.A2.Add(&t[7], &x.B1.A2).Double(&z.B1.A2).Add(&z.B1.A2, &t[7]) - - return z -} - -// Inverse sets z to the inverse of x in E6 and returns z -// -// if x == 0, sets and returns z = x -func (z *E6) Inverse(x *E6) *E6 { - // Algorithm 23 from https://eprint.iacr.org/2010/354.pdf - - var t0, t1, tmp E3 - t0.Square(&x.B0) - t1.Square(&x.B1) - tmp.MulByNonResidue(&t1) - t0.Sub(&t0, &tmp) - t1.Inverse(&t0) - z.B0.Mul(&x.B0, &t1) - z.B1.Mul(&x.B1, &t1).Neg(&z.B1) - - return z -} - -// BatchInvertE6 returns a new slice with every element in a inverted. -// It uses Montgomery batch inversion trick. -// -// if a[i] == 0, returns result[i] = a[i] -func BatchInvertE6(a []E6) []E6 { - res := make([]E6, len(a)) - if len(a) == 0 { - return res - } - - zeroes := make([]bool, len(a)) - var accumulator E6 - accumulator.SetOne() - - for i := 0; i < len(a); i++ { - if a[i].IsZero() { - zeroes[i] = true - continue - } - res[i].Set(&accumulator) - accumulator.Mul(&accumulator, &a[i]) - } - - accumulator.Inverse(&accumulator) - - for i := len(a) - 1; i >= 0; i-- { - if zeroes[i] { - continue - } - res[i].Mul(&res[i], &accumulator) - accumulator.Mul(&accumulator, &a[i]) - } - - return res -} - -// Exp sets z=xᵏ (mod q⁶) and returns it -// uses 2-bits windowed method -func (z *E6) Exp(x E6, k *big.Int) *E6 { - if k.IsUint64() && k.Uint64() == 0 { - return z.SetOne() - } - - e := k - if k.Sign() == -1 { - // negative k, we invert - // if k < 0: xᵏ (mod q⁶) == (x⁻¹)ᵏ (mod q⁶) - x.Inverse(&x) - - // we negate k in a temp big.Int since - // Int.Bit(_) of k and -k is different - e = bigIntPool.Get().(*big.Int) - defer bigIntPool.Put(e) - e.Neg(k) - } - - var res E6 - var ops [3]E6 - - res.SetOne() - ops[0].Set(&x) - ops[1].Square(&ops[0]) - ops[2].Set(&ops[0]).Mul(&ops[2], &ops[1]) - - b := e.Bytes() - for i := range b { - w := b[i] - mask := byte(0xc0) - for j := 0; j < 4; j++ { - res.Square(&res).Square(&res) - c := (w & mask) >> (6 - 2*j) - if c != 0 { - res.Mul(&res, &ops[c-1]) - } - mask = mask >> 2 - } - } - z.Set(&res) - - return z -} - -// CyclotomicExp sets z=xᵏ (mod q⁶) and returns it -// uses 2-NAF decomposition -// x must be in the cyclotomic subgroup -// TODO: use a windowed method -func (z *E6) CyclotomicExp(x E6, k *big.Int) *E6 { - if k.IsUint64() && k.Uint64() == 0 { - return z.SetOne() - } - - e := k - if k.Sign() == -1 { - // negative k, we invert (=conjugate) - // if k < 0: xᵏ (mod q⁶) == (x⁻¹)ᵏ (mod q⁶) - x.Conjugate(&x) - - // we negate k in a temp big.Int since - // Int.Bit(_) of k and -k is different - e = bigIntPool.Get().(*big.Int) - defer bigIntPool.Put(e) - e.Neg(k) - } - - var res, xInv E6 - xInv.InverseUnitary(&x) - res.SetOne() - eNAF := make([]int8, e.BitLen()+3) - n := ecc.NafDecomposition(e, eNAF[:]) - for i := n - 1; i >= 0; i-- { - res.CyclotomicSquare(&res) - if eNAF[i] == 1 { - res.Mul(&res, &x) - } else if eNAF[i] == -1 { - res.Mul(&res, &xInv) - } - } - z.Set(&res) - return z -} - -// ExpGLV sets z=xᵏ (q⁶) and returns it -// uses 2-dimensional GLV with 2-bits windowed method -// x must be in GT -// TODO: use 2-NAF -// TODO: use higher dimensional decomposition -func (z *E6) ExpGLV(x E6, k *big.Int) *E6 { - if k.IsUint64() && k.Uint64() == 0 { - return z.SetOne() - } - - e := k - if k.Sign() == -1 { - // negative k, we invert - // if k < 0: xᵏ (mod q⁶) == (x⁻¹)ᵏ (mod q⁶) - x.Conjugate(&x) - - // we negate k in a temp big.Int since - // Int.Bit(_) of k and -k is different - e = bigIntPool.Get().(*big.Int) - defer bigIntPool.Put(e) - e.Neg(k) - } - - var table [15]E6 - var res E6 - var s1, s2 fr.Element - - res.SetOne() - - // table[b3b2b1b0-1] = b3b2*Frobinius(x) + b1b0*x - table[0].Set(&x) - table[3].Frobenius(&x) - - // split the scalar, modifies ±x, Frob(x) accordingly - s := ecc.SplitScalar(e, &glvBasis) - - if s[0].Sign() == -1 { - s[0].Neg(&s[0]) - table[0].InverseUnitary(&table[0]) - } - if s[1].Sign() == -1 { - s[1].Neg(&s[1]) - table[3].InverseUnitary(&table[3]) - } - - // precompute table (2 bits sliding window) - // table[b3b2b1b0-1] = b3b2*Frobenius(x) + b1b0*x if b3b2b1b0 != 0 - table[1].CyclotomicSquare(&table[0]) - table[2].Mul(&table[1], &table[0]) - table[4].Mul(&table[3], &table[0]) - table[5].Mul(&table[3], &table[1]) - table[6].Mul(&table[3], &table[2]) - table[7].CyclotomicSquare(&table[3]) - table[8].Mul(&table[7], &table[0]) - table[9].Mul(&table[7], &table[1]) - table[10].Mul(&table[7], &table[2]) - table[11].Mul(&table[7], &table[3]) - table[12].Mul(&table[11], &table[0]) - table[13].Mul(&table[11], &table[1]) - table[14].Mul(&table[11], &table[2]) - - // bounds on the lattice base vectors guarantee that s1, s2 are len(r)/2 bits long max - s1 = s1.SetBigInt(&s[0]).Bits() - s2 = s2.SetBigInt(&s[1]).Bits() - - maxBit := s1.BitLen() - if s2.BitLen() > maxBit { - maxBit = s2.BitLen() - } - hiWordIndex := (maxBit - 1) / 64 - - // loop starts from len(s1)/2 due to the bounds - for i := hiWordIndex; i >= 0; i-- { - mask := uint64(3) << 62 - for j := 0; j < 32; j++ { - res.CyclotomicSquare(&res).CyclotomicSquare(&res) - b1 := (s1[i] & mask) >> (62 - 2*j) - b2 := (s2[i] & mask) >> (62 - 2*j) - if b1|b2 != 0 { - s := (b2<<2 | b1) - res.Mul(&res, &table[s-1]) - } - mask = mask >> 2 - } - } - - z.Set(&res) - return z -} - -// InverseUnitary inverses a unitary element -func (z *E6) InverseUnitary(x *E6) *E6 { - return z.Conjugate(x) -} - -// Conjugate sets z to x conjugated and returns z -func (z *E6) Conjugate(x *E6) *E6 { - *z = *x - z.B1.Neg(&z.B1) - return z -} - -// SizeOfGT represents the size in bytes that a GT element need in binary form -const SizeOfGT = fp.Bytes * 6 - -// Bytes returns the regular (non montgomery) value -// of z as a big-endian byte array. -// z.C1.B2.A1 | z.C1.B2.A0 | z.C1.B1.A1 | ... -func (z *E6) Bytes() (r [SizeOfGT]byte) { - - offset := 0 - var buf [fp.Bytes]byte - - buf = z.B1.A2.Bytes() - copy(r[offset:offset+fp.Bytes], buf[:]) - offset += fp.Bytes - - buf = z.B1.A1.Bytes() - copy(r[offset:offset+fp.Bytes], buf[:]) - offset += fp.Bytes - - buf = z.B1.A0.Bytes() - copy(r[offset:offset+fp.Bytes], buf[:]) - offset += fp.Bytes - - buf = z.B0.A2.Bytes() - copy(r[offset:offset+fp.Bytes], buf[:]) - offset += fp.Bytes - - buf = z.B0.A1.Bytes() - copy(r[offset:offset+fp.Bytes], buf[:]) - offset += fp.Bytes - - buf = z.B0.A0.Bytes() - copy(r[offset:offset+fp.Bytes], buf[:]) - - return -} - -// SetBytes interprets e as the bytes of a big-endian GT -// sets z to that value (in Montgomery form), and returns z. -// z.C1.B2.A1 | z.C1.B2.A0 | z.C1.B1.A1 | ... -func (z *E6) SetBytes(e []byte) error { - if len(e) != SizeOfGT { - return errors.New("invalid buffer size") - } - offset := 0 - z.B1.A2.SetBytes(e[offset : offset+fp.Bytes]) - offset += fp.Bytes - z.B1.A1.SetBytes(e[offset : offset+fp.Bytes]) - offset += fp.Bytes - z.B1.A0.SetBytes(e[offset : offset+fp.Bytes]) - offset += fp.Bytes - z.B0.A2.SetBytes(e[offset : offset+fp.Bytes]) - offset += fp.Bytes - z.B0.A1.SetBytes(e[offset : offset+fp.Bytes]) - offset += fp.Bytes - z.B0.A0.SetBytes(e[offset : offset+fp.Bytes]) - - return nil -} - -// IsInSubGroup ensures GT/E6 is in correct subgroup -func (z *E6) IsInSubGroup() bool { - var tmp, a, _a, b E6 - var t [6]E6 - - // check z^(phi_k(p)) == 1 - a.Frobenius(z) - b.Frobenius(&a).Mul(&b, z) - - if !a.Equal(&b) { - return false - } - - // check z^(p+1-t) == 1 - _a.Frobenius(z) - a.CyclotomicSquare(&_a).Mul(&a, &_a) // z^(3p) - - // t(x)-1 = (-x⁶ + 5x⁵ - 9x⁴ + 7x³ - 4x + 5)/3 - t[0].CyclotomicSquare(z). - CyclotomicSquare(&t[0]) // z^4 - t[1].Mul(&t[0], z) //z^5* - t[2].Expt(&t[0]). - Conjugate(&t[2]) // z^(-4u)* - tmp.CyclotomicSquare(&t[2]). - Expt(&tmp). - Expt(&tmp) // z^(-8u^3) - t[4].Expt(z). - Expt(&t[4]). - Expt(&t[4]) // z^(u^3) - t[3].Mul(&t[4], &tmp). - Conjugate(&t[3]) // z^(7u^3)* - t[4].Conjugate(&t[4]). - Mul(&t[4], &tmp). - Expt(&t[4]) // z^(-9u^4)* - t[5].Expt(&tmp). - Conjugate(&t[5]). - Mul(&t[5], &t[4]). - Expt(&t[5]) // z^(-u^5) - t[0].Expt(&t[5]) // z^(-u^6)* - tmp.Expt(&t[4]) // z^(-9u^5) - t[5].CyclotomicSquare(&t[5]). - CyclotomicSquare(&t[5]). - Conjugate(&t[5]). - Mul(&t[5], &tmp). - Conjugate(&t[5]) // z^(5u^5)* - - b.Mul(&t[1], &t[2]). - Mul(&b, &t[3]). - Mul(&b, &t[4]). - Mul(&b, &t[5]). - Mul(&b, &t[0]) // z^(3(t-1)) - - return a.Equal(&b) -} - -// CompressTorus GT/E6 element to half its size -// z must be in the cyclotomic subgroup -// i.e. z^(p⁴-p²+1)=1 -// e.g. GT -// "COMPRESSION IN FINITE FIELDS AND TORUS-BASED CRYPTOGRAPHY", K. RUBIN AND A. SILVERBERG -// z.B1 == 0 only when z ∈ {-1,1} -func (z *E6) CompressTorus() (E3, error) { - - if z.B1.IsZero() { - return E3{}, errors.New("invalid input") - } - - var res, tmp, one E3 - one.SetOne() - tmp.Inverse(&z.B1) - res.Add(&z.B0, &one). - Mul(&res, &tmp) - - return res, nil -} - -// BatchCompressTorus GT/E6 elements to half their size -// using a batch inversion -func BatchCompressTorus(x []E6) ([]E3, error) { - - n := len(x) - if n == 0 { - return []E3{}, errors.New("invalid input size") - } - - var one E3 - one.SetOne() - res := make([]E3, n) - - for i := 0; i < n; i++ { - res[i].Set(&x[i].B1) - // throw an error if any of the x[i].C1 is 0 - if res[i].IsZero() { - return []E3{}, errors.New("invalid input") - } - } - - t := BatchInvertE3(res) // costs 1 inverse - - for i := 0; i < n; i++ { - res[i].Add(&x[i].B0, &one). - Mul(&res[i], &t[i]) - } - - return res, nil -} - -// DecompressTorus GT/E6 a compressed element -// element must be in the cyclotomic subgroup -// "COMPRESSION IN FINITE FIELDS AND TORUS-BASED CRYPTOGRAPHY", K. RUBIN AND A. SILVERBERG -func (z *E3) DecompressTorus() E6 { - - var res, num, denum E6 - num.B0.Set(z) - num.B1.SetOne() - denum.B0.Set(z) - denum.B1.SetOne().Neg(&denum.B1) - res.Inverse(&denum). - Mul(&res, &num) - - return res -} - -// BatchDecompressTorus GT/E6 compressed elements -// using a batch inversion -func BatchDecompressTorus(x []E3) ([]E6, error) { - - n := len(x) - if n == 0 { - return []E6{}, errors.New("invalid input size") - } - - res := make([]E6, n) - num := make([]E6, n) - denum := make([]E6, n) - - for i := 0; i < n; i++ { - num[i].B0.Set(&x[i]) - num[i].B1.SetOne() - denum[i].B0.Set(&x[i]) - denum[i].B1.SetOne().Neg(&denum[i].B1) - } - - denum = BatchInvertE6(denum) // costs 1 inverse - - for i := 0; i < n; i++ { - res[i].Mul(&num[i], &denum[i]) - } - - return res, nil -} diff --git a/ecc/bw6-756/internal/fptower/e6_pairing.go b/ecc/bw6-756/internal/fptower/e6_pairing.go deleted file mode 100644 index 1312e518ce..0000000000 --- a/ecc/bw6-756/internal/fptower/e6_pairing.go +++ /dev/null @@ -1,242 +0,0 @@ -package fptower - -import "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - -func (z *E6) nSquare(n int) { - for i := 0; i < n; i++ { - z.CyclotomicSquare(z) - } -} - -func (z *E6) nSquareCompressed(n int) { - for i := 0; i < n; i++ { - z.CyclotomicSquareCompressed(z) - } -} - -// ExptMinus1 set z to x^t in E6 and return z -// t-1 = 11045256207009841152 -func (z *E6) ExptMinus1(x *E6) *E6 { - - var result, t0, t1 E6 - - result.CyclotomicSquare(x) - result.nSquare(2) - t0.Mul(x, &result) - t1.CyclotomicSquare(&t0) - t1.nSquare(2) - result.Mul(&t0, &t1) - t1.Mul(&t1, &result) - t1.nSquare(5) - t0.Mul(&t0, &t1) - t0.nSquare(10) - result.Mul(&result, &t0) - result.nSquareCompressed(41) - result.DecompressKarabina(&result) - z.Set(&result) - - return z -} - -// ExptMinus1Square set z to x^t in E6 and return z -// (t-1)^2 = 121997684678489422939424157776272687104 -func (z *E6) ExptMinus1Square(x *E6) *E6 { - var result, t0, t1, t2, t3 E6 - result.CyclotomicSquare(x) - t0.Mul(x, &result) - result.CyclotomicSquare(&t0) - t0.Mul(&t0, &result) - result.Mul(&result, &t0) - t1.CyclotomicSquare(&result) - t1.nSquare(2) - t0.Mul(&t0, &t1) - result.Mul(&result, &t0) - t0.Mul(&t0, &result) - result.Mul(&result, &t0) - t1.CyclotomicSquare(&result) - t1.Mul(&result, &t1) - t1.CyclotomicSquare(&t1) - t2.CyclotomicSquare(&t1) - t1.Mul(&t1, &t2) - t2.Mul(&t2, &t1) - t1.Mul(&t1, &t2) - t3.CyclotomicSquare(&t1) - t2.Mul(&t2, &t3) - t1.Mul(&t1, &t2) - t3.CyclotomicSquare(&t1) - t3.CyclotomicSquare(&t3) - t3.Mul(&t1, &t3) - t3.nSquare(2) - t2.Mul(&t2, &t3) - t2.nSquare(11) - t1.Mul(&t1, &t2) - t0.Mul(&t0, &t1) - t0.nSquare(13) - result.Mul(&result, &t0) - result.nSquareCompressed(82) - result.DecompressKarabina(&result) - z.Set(&result) - - return z -} - -// Expt set z to x^t in E6 and return z -// t = 11045256207009841153 -func (z *E6) Expt(x *E6) *E6 { - var result E6 - - result.ExptMinus1(x) - result.Mul(&result, x) - z.Set(&result) - - return z -} - -// ExptPlus1 set z to x^(t+1) in E6 and return z -// t+1 = 11045256207009841154 -func (z *E6) ExptPlus1(x *E6) *E6 { - var result, t E6 - - result.ExptMinus1(x) - t.CyclotomicSquare(x) - result.Mul(&result, &t) - z.Set(&result) - - return z -} - -// ExptMinus1Div3 set z to x^(t-1)/3 in E6 and return z -// (t-1)/3 = 3681752069003280384 -func (z *E6) ExptMinus1Div3(x *E6) *E6 { - var result, t0, t1 E6 - result.CyclotomicSquare(x) - t0.Mul(x, &result) - t1.CyclotomicSquare(&t0) - t1.nSquare(2) - result.Mul(&t0, &t1) - t1.Mul(&t1, &result) - t1.nSquare(5) - t0.Mul(&t0, &t1) - t0.nSquare(10) - result.Mul(&result, &t0) - result.nSquareCompressed(41) - result.DecompressKarabina(&result) - z.Set(&result) - - return z -} - -// MulBy014 multiplication by sparse element (c0,c1,0,0,c4,0) -func (z *E6) MulBy014(c0, c1, c4 *fp.Element) *E6 { - - var a, b E3 - var d fp.Element - - a.Set(&z.B0) - a.MulBy01(c0, c1) - - b.Set(&z.B1) - b.MulBy1(c4) - d.Add(c1, c4) - - z.B1.Add(&z.B1, &z.B0) - z.B1.MulBy01(c0, &d) - z.B1.Sub(&z.B1, &a) - z.B1.Sub(&z.B1, &b) - z.B0.MulByNonResidue(&b) - z.B0.Add(&z.B0, &a) - - return z -} - -// MulBy01 multiplication by sparse element (c0, c1, 0, 0, 1) -func (z *E6) MulBy01(c0, c1 *fp.Element) *E6 { - - var a, b E3 - var d fp.Element - - a.Set(&z.B0) - a.MulBy01(c0, c1) - - b.MulByNonResidue(&z.B1) - d.SetOne().Add(c1, &d) - - z.B1.Add(&z.B1, &z.B0) - z.B1.MulBy01(c0, &d) - z.B1.Sub(&z.B1, &a) - z.B1.Sub(&z.B1, &b) - z.B0.MulByNonResidue(&b) - z.B0.Add(&z.B0, &a) - - return z -} - -// Mul014By014 multiplication of sparse element (c0,c1,0,0,c4,0) by sparse element (d0,d1,0,0,d4,0) -func Mul014By014(d0, d1, d4, c0, c1, c4 *fp.Element) [5]fp.Element { - var z00, tmp, x0, x1, x4, x04, x01, x14 fp.Element - x0.Mul(c0, d0) - x1.Mul(c1, d1) - x4.Mul(c4, d4) - tmp.Add(c0, c4) - x04.Add(d0, d4). - Mul(&x04, &tmp). - Sub(&x04, &x0). - Sub(&x04, &x4) - tmp.Add(c0, c1) - x01.Add(d0, d1). - Mul(&x01, &tmp). - Sub(&x01, &x0). - Sub(&x01, &x1) - tmp.Add(c1, c4) - x14.Add(d1, d4). - Mul(&x14, &tmp). - Sub(&x14, &x1). - Sub(&x14, &x4) - - z00.MulByNonResidue(&x4). - Add(&z00, &x0) - - return [5]fp.Element{z00, x01, x1, x04, x14} -} - -// Mul01By01 multiplication of sparse element (c0,c1,0,0,1,0) by sparse element (d0,d1,0,0,1,0) -func Mul01By01(d0, d1, c0, c1 *fp.Element) [5]fp.Element { - var z00, tmp, x0, x1, x4, x04, x01, x14 fp.Element - x0.Mul(c0, d0) - x1.Mul(c1, d1) - x4.SetOne() - x04.Add(d0, c0) - tmp.Add(c0, c1) - x01.Add(d0, d1). - Mul(&x01, &tmp). - Sub(&x01, &x0). - Sub(&x01, &x1) - x14.Add(d1, c1) - - z00.MulByNonResidue(&x4). - Add(&z00, &x0) - - return [5]fp.Element{z00, x01, x1, x04, x14} -} - -// MulBy01245 multiplies z by an E12 sparse element of the form (x0, x1, x2, 0, x4, x5) -func (z *E6) MulBy01245(x *[5]fp.Element) *E6 { - var c1, a, b, c, z0, z1 E3 - c0 := &E3{A0: x[0], A1: x[1], A2: x[2]} - c1.A1 = x[3] - c1.A2 = x[4] - a.Add(&z.B0, &z.B1) - b.Add(c0, &c1) - a.Mul(&a, &b) - b.Mul(&z.B0, c0) - c.Set(&z.B1).MulBy12(&x[3], &x[4]) - z1.Sub(&a, &b) - z1.Sub(&z1, &c) - z0.MulByNonResidue(&c) - z0.Add(&z0, &b) - - z.B0 = z0 - z.B1 = z1 - - return z -} diff --git a/ecc/bw6-756/internal/fptower/e6_test.go b/ecc/bw6-756/internal/fptower/e6_test.go deleted file mode 100644 index cd69cbff59..0000000000 --- a/ecc/bw6-756/internal/fptower/e6_test.go +++ /dev/null @@ -1,436 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// 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 fptower - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -// ------------------------------------------------------------ -// tests - -func TestE6Serialization(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - parameters.MinSuccessfulTests = 100 - - properties := gopter.NewProperties(parameters) - - genA := GenE6() - - properties.Property("[BW6-756] SetBytes(Bytes()) should stay constant", prop.ForAll( - func(a *E6) bool { - var b E6 - buf := a.Bytes() - if err := b.SetBytes(buf[:]); err != nil { - return false - } - return a.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestE6ReceiverIsOperand(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - parameters.MinSuccessfulTests = 100 - - properties := gopter.NewProperties(parameters) - - genA := GenE6() - genB := GenE6() - - properties.Property("[BW6-756] Having the receiver as operand (addition) should output the same result", prop.ForAll( - func(a, b *E6) bool { - var c, d E6 - d.Set(a) - c.Add(a, b) - a.Add(a, b) - b.Add(&d, b) - return a.Equal(b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("[BW6-756] Having the receiver as operand (sub) should output the same result", prop.ForAll( - func(a, b *E6) bool { - var c, d E6 - d.Set(a) - c.Sub(a, b) - a.Sub(a, b) - b.Sub(&d, b) - return a.Equal(b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("[BW6-756] Having the receiver as operand (mul) should output the same result", prop.ForAll( - func(a, b *E6) bool { - var c, d E6 - d.Set(a) - c.Mul(a, b) - a.Mul(a, b) - b.Mul(&d, b) - return a.Equal(b) && a.Equal(&c) && b.Equal(&c) - }, - genA, - genB, - )) - - properties.Property("[BW6-756] Having the receiver as operand (square) should output the same result", prop.ForAll( - func(a *E6) bool { - var b E6 - b.Square(a) - a.Square(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BW6-756] Having the receiver as operand (double) should output the same result", prop.ForAll( - func(a *E6) bool { - var b E6 - b.Double(a) - a.Double(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BW6-756] Having the receiver as operand (Inverse) should output the same result", prop.ForAll( - func(a *E6) bool { - var b E6 - b.Inverse(a) - a.Inverse(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BW6-756] Having the receiver as operand (Cyclotomic square) should output the same result", prop.ForAll( - func(a *E6) bool { - var b E6 - b.CyclotomicSquare(a) - a.CyclotomicSquare(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BW6-756] Having the receiver as operand (Conjugate) should output the same result", prop.ForAll( - func(a *E6) bool { - var b E6 - b.Conjugate(a) - a.Conjugate(a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BW6-756] Having the receiver as operand (Frobenius) should output the same result", prop.ForAll( - func(a *E6) bool { - var b E6 - b.Frobenius(a) - a.Frobenius(a) - return a.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestE6Ops(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - parameters.MinSuccessfulTests = 100 - - properties := gopter.NewProperties(parameters) - - genA := GenE6() - genB := GenE6() - - properties.Property("[BW6-756] sub & add should leave an element invariant", prop.ForAll( - func(a, b *E6) bool { - var c E6 - c.Set(a) - c.Add(&c, b).Sub(&c, b) - return c.Equal(a) - }, - genA, - genB, - )) - - properties.Property("[BW6-756] mul & inverse should leave an element invariant", prop.ForAll( - func(a, b *E6) bool { - var c, d E6 - d.Inverse(b) - c.Set(a) - c.Mul(&c, b).Mul(&c, &d) - return c.Equal(a) - }, - genA, - genB, - )) - - properties.Property("[BW6-756] inverse twice should leave an element invariant", prop.ForAll( - func(a *E6) bool { - var b E6 - b.Inverse(a).Inverse(&b) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BW6-756] square and mul should output the same result", prop.ForAll( - func(a *E6) bool { - var b, c E6 - b.Mul(a, a) - c.Square(a) - return b.Equal(&c) - }, - genA, - )) - - properties.Property("[BW6-756] a + pi(a), a-pi(a) should be real", prop.ForAll( - func(a *E6) bool { - var b, c, d E6 - var e, f, g E3 - b.Conjugate(a) - c.Add(a, &b) - d.Sub(a, &b) - e.Double(&a.B0) - f.Double(&a.B1) - return c.B1.Equal(&g) && d.B0.Equal(&g) && e.Equal(&c.B0) && f.Equal(&d.B1) - }, - genA, - )) - - properties.Property("[BW6-756] Torus-based Compress/decompress E6 elements in the cyclotomic subgroup", prop.ForAll( - func(a *E6) bool { - var b E6 - b.Conjugate(a) - a.Inverse(a) - b.Mul(&b, a) - a.Frobenius(&b).Mul(a, &b) - - c, _ := a.CompressTorus() - d := c.DecompressTorus() - return a.Equal(&d) - }, - genA, - )) - - properties.Property("[BW6-756] Torus-based batch Compress/decompress E6 elements in the cyclotomic subgroup", prop.ForAll( - func(a, e, f *E6) bool { - var b E6 - b.Conjugate(a) - a.Inverse(a) - b.Mul(&b, a) - a.Frobenius(&b).Mul(a, &b) - - e.CyclotomicSquare(a) - f.CyclotomicSquare(e) - - c, _ := BatchCompressTorus([]E6{*a, *e, *f}) - d, _ := BatchDecompressTorus(c) - return a.Equal(&d[0]) && e.Equal(&d[1]) && f.Equal(&d[2]) - }, - genA, - genA, - genA, - )) - - properties.Property("[BW6-756] pi**12=id", prop.ForAll( - func(a *E6) bool { - var b E6 - b.Frobenius(a). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b). - Frobenius(&b) - return b.Equal(a) - }, - genA, - )) - - properties.Property("[BW6-756] cyclotomic square (Granger-Scott) and square should be the same in the cyclotomic subgroup", prop.ForAll( - func(a *E6) bool { - var b, c, d E6 - b.Conjugate(a) - a.Inverse(a) - b.Mul(&b, a) - a.Frobenius(&b).Mul(a, &b) - c.Square(a) - d.CyclotomicSquare(a) - return c.Equal(&d) - }, - genA, - )) - - properties.Property("[BW6-756] compressed cyclotomic square (Karabina) and square should be the same in the cyclotomic subgroup", prop.ForAll( - func(a *E6) bool { - var _a, b, c, d, _c, _d E6 - _a.SetOne().Double(&_a) - - // put a and _a in the cyclotomic subgroup - // a (g3 != 0 probably) - b.Conjugate(a) - a.Inverse(a) - b.Mul(&b, a) - a.Frobenius(&b).Mul(a, &b) - // _a (g3 == 0) - b.Conjugate(&_a) - _a.Inverse(&_a) - b.Mul(&b, &_a) - _a.Frobenius(&b).Mul(&_a, &b) - - // case g3 != 0 - c.Square(a) - d.CyclotomicSquareCompressed(a).DecompressKarabina(&d) - - // case g3 == 0 - _c.Square(&_a) - _d.CyclotomicSquareCompressed(&_a).DecompressKarabina(&_d) - - return c.Equal(&d) - }, - genA, - )) - - properties.Property("[BW6-756] Frobenius of x in E6 should be equal to x^q", prop.ForAll( - func(a *E6) bool { - var b, c E6 - q := fp.Modulus() - b.Frobenius(a) - c.Exp(*a, q) - return c.Equal(&b) - }, - genA, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -// ------------------------------------------------------------ -// benches - -func BenchmarkE6Add(b *testing.B) { - var a, c E6 - a.SetRandom() - c.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Add(&a, &c) - } -} - -func BenchmarkE6Sub(b *testing.B) { - var a, c E6 - a.SetRandom() - c.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Sub(&a, &c) - } -} - -func BenchmarkE6Mul(b *testing.B) { - var a, c E6 - a.SetRandom() - c.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Mul(&a, &c) - } -} - -func BenchmarkE6Cyclosquare(b *testing.B) { - var a E6 - a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.CyclotomicSquare(&a) - } -} - -func BenchmarkE6Square(b *testing.B) { - var a E6 - a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Square(&a) - } -} - -func BenchmarkE6Inverse(b *testing.B) { - var a E6 - a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Inverse(&a) - } -} - -func BenchmarkE6Conjugate(b *testing.B) { - var a E6 - a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Conjugate(&a) - } -} - -func BenchmarkE6Frobenius(b *testing.B) { - var a E6 - a.SetRandom() - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Frobenius(&a) - } -} - -func BenchmarkE6Expt(b *testing.B) { - var a, c E6 - a.SetRandom() - b.ResetTimer() - c.Conjugate(&a) - a.Inverse(&a) - c.Mul(&c, &a) - - a.Frobenius(&c). - Mul(&a, &c) - - for i := 0; i < b.N; i++ { - a.Expt(&a) - } -} diff --git a/ecc/bw6-756/internal/fptower/frobenius.go b/ecc/bw6-756/internal/fptower/frobenius.go deleted file mode 100644 index 73a7602c65..0000000000 --- a/ecc/bw6-756/internal/fptower/frobenius.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2020 ConsenSys AG -// -// 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 fptower - -import "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - -var _frobA = fp.Element{ - 4513305906938863657, - 16223881110415437916, - 2594807996890465129, - 12027263585750947831, - 4394688080420790544, - 16545365607090591069, - 17206939158340345469, - 16693218895653628888, - 12341936222077983834, - 15961798706098381578, - 6325965824540199947, - 854909948470066, -} -var _frobB = fp.Element{ - 13933438166770692198, - 9936849508207988643, - 15731274946730933551, - 17453539207763286666, - 9211229669332609391, - 16304457798847396452, - 9530634072302290725, - 16589137634438497937, - 3757329544587311773, - 6048657743386074056, - 539268601340212626, - 3128351770947469, -} -var _frobC = fp.Element{ - 4513305906938859419, - 12241098542315434076, - 17754824365858099600, - 5821813791745674579, - 7115107423905013045, - 2898523548767316962, - 7403683460125356932, - 16613279480632639560, - 14397298621774850312, - 623298467364696769, - 15794181680107729725, - 1224261424482813, -} -var _frobAC = fp.Element{ - 4239, - 7713986544913874944, - 18326082943621398681, - 11034058719804682881, - 13605917749753399936, - 14403079332228435905, - 8290829156933084579, - 14835612456382575210, - 16099265766665295608, - 3563712375774904018, - 6865234425880412574, - 3983261719417535, -} -var _frobBC = fp.Element{ - 13933438166770687960, - 5954066940107984803, - 12444547241989016406, - 11248089413758013415, - 11931649012816831892, - 2657615740524122345, - 18174122447796853804, - 16509198219417508608, - 5812691944284178251, - 9156901578361940863, - 10007484456907742403, - 3497703246960216, -} - -// Frobenius set z in E6 to Frobenius(x), return z -func (z *E6) Frobenius(x *E6) *E6 { - - z.B0.A0 = x.B0.A0 - z.B0.A1.Mul(&x.B0.A1, &_frobA) - z.B0.A2.Mul(&x.B0.A2, &_frobB) - - z.B1.A0.Mul(&x.B1.A0, &_frobC) - z.B1.A1.Mul(&x.B1.A1, &_frobAC) - z.B1.A2.Mul(&x.B1.A2, &_frobBC) - - return z -} diff --git a/ecc/bw6-756/internal/fptower/generators_test.go b/ecc/bw6-756/internal/fptower/generators_test.go deleted file mode 100644 index 6d2e175a8c..0000000000 --- a/ecc/bw6-756/internal/fptower/generators_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package fptower - -import ( - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - "github.com/leanovate/gopter" -) - -// TODO all gopter.Gen are incorrect, use same model as goff - -// GenFp generates an Fp element -func GenFp() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var elmt fp.Element - - if _, err := elmt.SetRandom(); err != nil { - panic(err) - } - genResult := gopter.NewGenResult(elmt, gopter.NoShrinker) - return genResult - } -} - -// GenE3 generates an E3 elmt -func GenE3() gopter.Gen { - return gopter.CombineGens( - GenFp(), - GenFp(), - GenFp(), - ).Map(func(values []interface{}) *E3 { - return &E3{A0: values[0].(fp.Element), A1: values[1].(fp.Element), A2: values[2].(fp.Element)} - }) -} - -// E6 generates an E6 elmt -func GenE6() gopter.Gen { - return gopter.CombineGens( - GenE3(), - GenE3(), - ).Map(func(values []interface{}) *E6 { - return &E6{B0: *values[0].(*E3), B1: *values[1].(*E3)} - }) -} diff --git a/ecc/bw6-756/internal/fptower/parameters.go b/ecc/bw6-756/internal/fptower/parameters.go deleted file mode 100644 index 8a8ce6f783..0000000000 --- a/ecc/bw6-756/internal/fptower/parameters.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2020 ConsenSys AG -// -// 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 fptower - -import ( - "math/big" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) - -// t-1 -var xGen big.Int - -var glvBasis ecc.Lattice - -func init() { - xGen.SetString("164391353554439166353793911729193406645071739502673898176639736370075683438438023898983435337730", 10) - _r := fr.Modulus() - ecc.PrecomputeLattice(_r, &xGen, &glvBasis) -} diff --git a/ecc/bw6-756/kzg/doc.go b/ecc/bw6-756/kzg/doc.go deleted file mode 100644 index d1045726f2..0000000000 --- a/ecc/bw6-756/kzg/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package kzg provides a KZG commitment scheme. -package kzg diff --git a/ecc/bw6-756/kzg/kzg.go b/ecc/bw6-756/kzg/kzg.go deleted file mode 100644 index 9002613d45..0000000000 --- a/ecc/bw6-756/kzg/kzg.go +++ /dev/null @@ -1,593 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package kzg - -import ( - "errors" - "hash" - "math/big" - "sync" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bw6-756" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/fiat-shamir" - - "github.com/consensys/gnark-crypto/internal/parallel" -) - -var ( - ErrInvalidNbDigests = errors.New("number of digests is not the same as the number of polynomials") - ErrZeroNbDigests = errors.New("number of digests is zero") - ErrInvalidPolynomialSize = errors.New("invalid polynomial size (larger than SRS or == 0)") - ErrVerifyOpeningProof = errors.New("can't verify opening proof") - ErrVerifyBatchOpeningSinglePoint = errors.New("can't verify batch opening proof at single point") - ErrMinSRSSize = errors.New("minimum srs size is 2") -) - -// Digest commitment of a polynomial. -type Digest = bw6756.G1Affine - -// ProvingKey used to create or open commitments -type ProvingKey struct { - G1 []bw6756.G1Affine // [G₁ [α]G₁ , [α²]G₁, ... ] -} - -// VerifyingKey used to verify opening proofs -type VerifyingKey struct { - G2 [2]bw6756.G2Affine // [G₂, [α]G₂ ] - G1 bw6756.G1Affine - Lines [2][2][len(bw6756.LoopCounter) - 1]bw6756.LineEvaluationAff // precomputed pairing lines corresponding to G₂, [α]G₂ -} - -// SRS must be computed through MPC and comprises the ProvingKey and the VerifyingKey -type SRS struct { - Pk ProvingKey - Vk VerifyingKey -} - -// TODO @Tabaie get rid of this and use the polynomial package -// eval returns p(point) where p is interpreted as a polynomial -// ∑_{i= 0; i-- { - res.Mul(&res, &point).Add(&res, &p[i]) - } - return res -} - -// NewSRS returns a new SRS using alpha as randomness source -// -// In production, a SRS generated through MPC should be used. -// -// Set Alpha = -1 to generate quickly a balanced, valid SRS (useful for benchmarking). -// -// implements io.ReaderFrom and io.WriterTo -func NewSRS(size uint64, bAlpha *big.Int) (*SRS, error) { - - if size < 2 { - return nil, ErrMinSRSSize - } - var srs SRS - srs.Pk.G1 = make([]bw6756.G1Affine, size) - - var alpha fr.Element - alpha.SetBigInt(bAlpha) - - var bMOne big.Int - bMOne.SetInt64(-1) - - _, _, gen1Aff, gen2Aff := bw6756.Generators() - - // in this case, the SRS is <αⁱ[G₁]> whera α is of order 4 - // so no need to run the batch scalar multiplication. We cannot use alpha=1 - // because it tampers the benchmarks (the SRS is not balanced). - if bAlpha.Cmp(&bMOne) == 0 { - - t, err := fr.Generator(4) - if err != nil { - return &srs, nil - } - var bt big.Int - t.BigInt(&bt) - - var g [4]bw6756.G1Affine - g[0] = gen1Aff - for i := 1; i < 4; i++ { - g[i].ScalarMultiplication(&g[i-1], &bt) - } - parallel.Execute(int(size), func(start, end int) { - for i := start; i < int(end); i++ { - srs.Pk.G1[i] = g[i%4] - } - }) - srs.Vk.G1 = gen1Aff - srs.Vk.G2[0] = gen2Aff - srs.Vk.G2[1].ScalarMultiplication(&srs.Vk.G2[0], &bt) - srs.Vk.Lines[0] = bw6756.PrecomputeLines(srs.Vk.G2[0]) - srs.Vk.Lines[1] = bw6756.PrecomputeLines(srs.Vk.G2[1]) - return &srs, nil - } - srs.Pk.G1[0] = gen1Aff - srs.Vk.G1 = gen1Aff - srs.Vk.G2[0] = gen2Aff - srs.Vk.G2[1].ScalarMultiplication(&gen2Aff, bAlpha) - srs.Vk.Lines[0] = bw6756.PrecomputeLines(srs.Vk.G2[0]) - srs.Vk.Lines[1] = bw6756.PrecomputeLines(srs.Vk.G2[1]) - - alphas := make([]fr.Element, size-1) - alphas[0] = alpha - for i := 1; i < len(alphas); i++ { - alphas[i].Mul(&alphas[i-1], &alpha) - } - g1s := bw6756.BatchScalarMultiplicationG1(&gen1Aff, alphas) - copy(srs.Pk.G1[1:], g1s) - - return &srs, nil -} - -// OpeningProof KZG proof for opening at a single point. -// -// implements io.ReaderFrom and io.WriterTo -type OpeningProof struct { - // H quotient polynomial (f - f(z))/(x-z) - H bw6756.G1Affine - - // ClaimedValue purported value - ClaimedValue fr.Element -} - -// BatchOpeningProof opening proof for many polynomials at the same point -// -// implements io.ReaderFrom and io.WriterTo -type BatchOpeningProof struct { - // H quotient polynomial Sum_i gamma**i*(f - f(z))/(x-z) - H bw6756.G1Affine - - // ClaimedValues purported values - ClaimedValues []fr.Element -} - -// Commit commits to a polynomial using a multi exponentiation with the SRS. -// It is assumed that the polynomial is in canonical form, in Montgomery form. -func Commit(p []fr.Element, pk ProvingKey, nbTasks ...int) (Digest, error) { - - if len(p) == 0 || len(p) > len(pk.G1) { - return Digest{}, ErrInvalidPolynomialSize - } - - var res bw6756.G1Affine - - config := ecc.MultiExpConfig{} - if len(nbTasks) > 0 { - config.NbTasks = nbTasks[0] - } - if _, err := res.MultiExp(pk.G1[:len(p)], p, config); err != nil { - return Digest{}, err - } - - return res, nil -} - -// Open computes an opening proof of polynomial p at given point. -// fft.Domain Cardinality must be larger than p.Degree() -func Open(p []fr.Element, point fr.Element, pk ProvingKey) (OpeningProof, error) { - if len(p) == 0 || len(p) > len(pk.G1) { - return OpeningProof{}, ErrInvalidPolynomialSize - } - - // build the proof - res := OpeningProof{ - ClaimedValue: eval(p, point), - } - - // compute H - // h reuses memory from _p - _p := make([]fr.Element, len(p)) - copy(_p, p) - h := dividePolyByXminusA(_p, res.ClaimedValue, point) - - // commit to H - hCommit, err := Commit(h, pk) - if err != nil { - return OpeningProof{}, err - } - res.H.Set(&hCommit) - - return res, nil -} - -// Verify verifies a KZG opening proof at a single point -func Verify(commitment *Digest, proof *OpeningProof, point fr.Element, vk VerifyingKey) error { - - // [f(a)]G₁ + [-a]([H(α)]G₁) = [f(a) - a*H(α)]G₁ - var totalG1 bw6756.G1Jac - var pointNeg fr.Element - var cmInt, pointInt big.Int - proof.ClaimedValue.BigInt(&cmInt) - pointNeg.Neg(&point).BigInt(&pointInt) - totalG1.JointScalarMultiplication(&vk.G1, &proof.H, &cmInt, &pointInt) - - // [f(a) - a*H(α)]G₁ + [-f(α)]G₁ = [f(a) - f(α) - a*H(α)]G₁ - var commitmentJac bw6756.G1Jac - commitmentJac.FromAffine(commitment) - totalG1.SubAssign(&commitmentJac) - - // e([f(α)-f(a)+aH(α)]G₁], G₂).e([-H(α)]G₁, [α]G₂) == 1 - var totalG1Aff bw6756.G1Affine - totalG1Aff.FromJacobian(&totalG1) - check, err := bw6756.PairingCheckFixedQ( - []bw6756.G1Affine{totalG1Aff, proof.H}, - vk.Lines[:], - ) - - if err != nil { - return err - } - if !check { - return ErrVerifyOpeningProof - } - return nil -} - -// BatchOpenSinglePoint creates a batch opening proof at point of a list of polynomials. -// It's an interactive protocol, made non-interactive using Fiat Shamir. -// -// * point is the point at which the polynomials are opened. -// * digests is the list of committed polynomials to open, need to derive the challenge using Fiat Shamir. -// * polynomials is the list of polynomials to open, they are supposed to be of the same size. -// * dataTranscript extra data that might be needed to derive the challenge used for folding -func BatchOpenSinglePoint(polynomials [][]fr.Element, digests []Digest, point fr.Element, hf hash.Hash, pk ProvingKey, dataTranscript ...[]byte) (BatchOpeningProof, error) { - - // check for invalid sizes - nbDigests := len(digests) - if nbDigests != len(polynomials) { - return BatchOpeningProof{}, ErrInvalidNbDigests - } - - // TODO ensure the polynomials are of the same size - largestPoly := -1 - for _, p := range polynomials { - if len(p) == 0 || len(p) > len(pk.G1) { - return BatchOpeningProof{}, ErrInvalidPolynomialSize - } - if len(p) > largestPoly { - largestPoly = len(p) - } - } - - var res BatchOpeningProof - - // compute the purported values - res.ClaimedValues = make([]fr.Element, len(polynomials)) - var wg sync.WaitGroup - wg.Add(len(polynomials)) - for i := 0; i < len(polynomials); i++ { - go func(_i int) { - res.ClaimedValues[_i] = eval(polynomials[_i], point) - wg.Done() - }(i) - } - - // wait for polynomial evaluations to be completed (res.ClaimedValues) - wg.Wait() - - // derive the challenge γ, binded to the point and the commitments - gamma, err := deriveGamma(point, digests, res.ClaimedValues, hf, dataTranscript...) - if err != nil { - return BatchOpeningProof{}, err - } - - // ∑ᵢγⁱf(a) - var foldedEvaluations fr.Element - chSumGammai := make(chan struct{}, 1) - go func() { - foldedEvaluations = res.ClaimedValues[nbDigests-1] - for i := nbDigests - 2; i >= 0; i-- { - foldedEvaluations.Mul(&foldedEvaluations, &gamma). - Add(&foldedEvaluations, &res.ClaimedValues[i]) - } - close(chSumGammai) - }() - - // compute ∑ᵢγⁱfᵢ - // note: if we are willing to parallelize that, we could clone the poly and scale them by - // gamma n in parallel, before reducing into foldedPolynomials - foldedPolynomials := make([]fr.Element, largestPoly) - copy(foldedPolynomials, polynomials[0]) - gammas := make([]fr.Element, len(polynomials)) - gammas[0] = gamma - for i := 1; i < len(polynomials); i++ { - gammas[i].Mul(&gammas[i-1], &gamma) - } - - for i := 1; i < len(polynomials); i++ { - i := i - parallel.Execute(len(polynomials[i]), func(start, end int) { - var pj fr.Element - for j := start; j < end; j++ { - pj.Mul(&polynomials[i][j], &gammas[i-1]) - foldedPolynomials[j].Add(&foldedPolynomials[j], &pj) - } - }) - } - - // compute H - <-chSumGammai - h := dividePolyByXminusA(foldedPolynomials, foldedEvaluations, point) - foldedPolynomials = nil // same memory as h - - res.H, err = Commit(h, pk) - if err != nil { - return BatchOpeningProof{}, err - } - - return res, nil -} - -// FoldProof fold the digests and the proofs in batchOpeningProof using Fiat Shamir -// to obtain an opening proof at a single point. -// -// * digests list of digests on which batchOpeningProof is based -// * batchOpeningProof opening proof of digests -// * transcript extra data needed to derive the challenge used for folding. -// * returns the folded version of batchOpeningProof, Digest, the folded version of digests -func FoldProof(digests []Digest, batchOpeningProof *BatchOpeningProof, point fr.Element, hf hash.Hash, dataTranscript ...[]byte) (OpeningProof, Digest, error) { - - nbDigests := len(digests) - - // check consistency between numbers of claims vs number of digests - if nbDigests != len(batchOpeningProof.ClaimedValues) { - return OpeningProof{}, Digest{}, ErrInvalidNbDigests - } - - // derive the challenge γ, binded to the point and the commitments - gamma, err := deriveGamma(point, digests, batchOpeningProof.ClaimedValues, hf, dataTranscript...) - if err != nil { - return OpeningProof{}, Digest{}, ErrInvalidNbDigests - } - - // fold the claimed values and digests - // gammai = [1,γ,γ²,..,γⁿ⁻¹] - gammai := make([]fr.Element, nbDigests) - gammai[0].SetOne() - if nbDigests > 1 { - gammai[1] = gamma - } - for i := 2; i < nbDigests; i++ { - gammai[i].Mul(&gammai[i-1], &gamma) - } - - foldedDigests, foldedEvaluations, err := fold(digests, batchOpeningProof.ClaimedValues, gammai) - if err != nil { - return OpeningProof{}, Digest{}, err - } - - // create the folded opening proof - var res OpeningProof - res.ClaimedValue.Set(&foldedEvaluations) - res.H.Set(&batchOpeningProof.H) - - return res, foldedDigests, nil -} - -// BatchVerifySinglePoint verifies a batched opening proof at a single point of a list of polynomials. -// -// * digests list of digests on which opening proof is done -// * batchOpeningProof proof of correct opening on the digests -// * dataTranscript extra data that might be needed to derive the challenge used for the folding -func BatchVerifySinglePoint(digests []Digest, batchOpeningProof *BatchOpeningProof, point fr.Element, hf hash.Hash, vk VerifyingKey, dataTranscript ...[]byte) error { - - // fold the proof - foldedProof, foldedDigest, err := FoldProof(digests, batchOpeningProof, point, hf, dataTranscript...) - if err != nil { - return err - } - - // verify the foldedProof against the foldedDigest - err = Verify(&foldedDigest, &foldedProof, point, vk) - return err - -} - -// BatchVerifyMultiPoints batch verifies a list of opening proofs at different points. -// The purpose of the batching is to have only one pairing for verifying several proofs. -// -// * digests list of committed polynomials -// * proofs list of opening proofs, one for each digest -// * points the list of points at which the opening are done -func BatchVerifyMultiPoints(digests []Digest, proofs []OpeningProof, points []fr.Element, vk VerifyingKey) error { - - // check consistency nb proogs vs nb digests - if len(digests) != len(proofs) || len(digests) != len(points) { - return ErrInvalidNbDigests - } - - // len(digests) should be nonzero because of randomNumbers - if len(digests) == 0 { - return ErrZeroNbDigests - } - - // if only one digest, call Verify - if len(digests) == 1 { - return Verify(&digests[0], &proofs[0], points[0], vk) - } - - // sample random numbers λᵢ for sampling - randomNumbers := make([]fr.Element, len(digests)) - randomNumbers[0].SetOne() - for i := 1; i < len(randomNumbers); i++ { - _, err := randomNumbers[i].SetRandom() - if err != nil { - return err - } - } - - // fold the committed quotients compute ∑ᵢλᵢ[Hᵢ(α)]G₁ - var foldedQuotients bw6756.G1Affine - quotients := make([]bw6756.G1Affine, len(proofs)) - for i := 0; i < len(randomNumbers); i++ { - quotients[i].Set(&proofs[i].H) - } - config := ecc.MultiExpConfig{} - if _, err := foldedQuotients.MultiExp(quotients, randomNumbers, config); err != nil { - return err - } - - // fold digests and evals - evals := make([]fr.Element, len(digests)) - for i := 0; i < len(randomNumbers); i++ { - evals[i].Set(&proofs[i].ClaimedValue) - } - - // fold the digests: ∑ᵢλᵢ[f_i(α)]G₁ - // fold the evals : ∑ᵢλᵢfᵢ(aᵢ) - foldedDigests, foldedEvals, err := fold(digests, evals, randomNumbers) - if err != nil { - return err - } - - // compute commitment to folded Eval [∑ᵢλᵢfᵢ(aᵢ)]G₁ - var foldedEvalsCommit bw6756.G1Affine - var foldedEvalsBigInt big.Int - foldedEvals.BigInt(&foldedEvalsBigInt) - foldedEvalsCommit.ScalarMultiplication(&vk.G1, &foldedEvalsBigInt) - - // compute foldedDigests = ∑ᵢλᵢ[fᵢ(α)]G₁ - [∑ᵢλᵢfᵢ(aᵢ)]G₁ - foldedDigests.Sub(&foldedDigests, &foldedEvalsCommit) - - // combien the points and the quotients using γᵢ - // ∑ᵢλᵢ[p_i]([Hᵢ(α)]G₁) - var foldedPointsQuotients bw6756.G1Affine - for i := 0; i < len(randomNumbers); i++ { - randomNumbers[i].Mul(&randomNumbers[i], &points[i]) - } - _, err = foldedPointsQuotients.MultiExp(quotients, randomNumbers, config) - if err != nil { - return err - } - - // ∑ᵢλᵢ[f_i(α)]G₁ - [∑ᵢλᵢfᵢ(aᵢ)]G₁ + ∑ᵢλᵢ[p_i]([Hᵢ(α)]G₁) - // = [∑ᵢλᵢf_i(α) - ∑ᵢλᵢfᵢ(aᵢ) + ∑ᵢλᵢpᵢHᵢ(α)]G₁ - foldedDigests.Add(&foldedDigests, &foldedPointsQuotients) - - // -∑ᵢλᵢ[Qᵢ(α)]G₁ - foldedQuotients.Neg(&foldedQuotients) - - // pairing check - // e([∑ᵢλᵢ(fᵢ(α) - fᵢ(pᵢ) + pᵢHᵢ(α))]G₁, G₂).e([-∑ᵢλᵢ[Hᵢ(α)]G₁), [α]G₂) - check, err := bw6756.PairingCheckFixedQ( - []bw6756.G1Affine{foldedDigests, foldedQuotients}, - vk.Lines[:], - ) - if err != nil { - return err - } - if !check { - return ErrVerifyOpeningProof - } - return nil - -} - -// fold folds digests and evaluations using the list of factors as random numbers. -// -// * digests list of digests to fold -// * evaluations list of evaluations to fold -// * factors list of multiplicative factors used for the folding (in Montgomery form) -// -// * Returns ∑ᵢcᵢdᵢ, ∑ᵢcᵢf(aᵢ) -func fold(di []Digest, fai []fr.Element, ci []fr.Element) (Digest, fr.Element, error) { - - // length inconsistency between digests and evaluations should have been done before calling this function - nbDigests := len(di) - - // fold the claimed values ∑ᵢcᵢf(aᵢ) - var foldedEvaluations, tmp fr.Element - for i := 0; i < nbDigests; i++ { - tmp.Mul(&fai[i], &ci[i]) - foldedEvaluations.Add(&foldedEvaluations, &tmp) - } - - // fold the digests ∑ᵢ[cᵢ]([fᵢ(α)]G₁) - var foldedDigests Digest - _, err := foldedDigests.MultiExp(di, ci, ecc.MultiExpConfig{}) - if err != nil { - return foldedDigests, foldedEvaluations, err - } - - // folding done - return foldedDigests, foldedEvaluations, nil - -} - -// deriveGamma derives a challenge using Fiat Shamir to fold proofs. -func deriveGamma(point fr.Element, digests []Digest, claimedValues []fr.Element, hf hash.Hash, dataTranscript ...[]byte) (fr.Element, error) { - - // derive the challenge gamma, binded to the point and the commitments - fs := fiatshamir.NewTranscript(hf, "gamma") - if err := fs.Bind("gamma", point.Marshal()); err != nil { - return fr.Element{}, err - } - for i := range digests { - if err := fs.Bind("gamma", digests[i].Marshal()); err != nil { - return fr.Element{}, err - } - } - for i := range claimedValues { - if err := fs.Bind("gamma", claimedValues[i].Marshal()); err != nil { - return fr.Element{}, err - } - } - - for i := 0; i < len(dataTranscript); i++ { - if err := fs.Bind("gamma", dataTranscript[i]); err != nil { - return fr.Element{}, err - } - } - - gammaByte, err := fs.ComputeChallenge("gamma") - if err != nil { - return fr.Element{}, err - } - var gamma fr.Element - gamma.SetBytes(gammaByte) - - return gamma, nil -} - -// dividePolyByXminusA computes (f-f(a))/(x-a), in canonical basis, in regular form -// f memory is re-used for the result -func dividePolyByXminusA(f []fr.Element, fa, a fr.Element) []fr.Element { - - // first we compute f-f(a) - f[0].Sub(&f[0], &fa) - - // now we use synthetic division to divide by x-a - var t fr.Element - for i := len(f) - 2; i >= 0; i-- { - t.Mul(&f[i+1], &a) - - f[i].Add(&f[i], &t) - } - - // the result is of degree deg(f)-1 - return f[1:] -} diff --git a/ecc/bw6-756/kzg/kzg_test.go b/ecc/bw6-756/kzg/kzg_test.go deleted file mode 100644 index 1d40d4c950..0000000000 --- a/ecc/bw6-756/kzg/kzg_test.go +++ /dev/null @@ -1,758 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package kzg - -import ( - "bytes" - "crypto/sha256" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "math/big" - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bw6-756" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/fft" - - "github.com/consensys/gnark-crypto/utils/testutils" -) - -// Test SRS re-used across tests of the KZG scheme -var testSrs *SRS -var bAlpha *big.Int - -func init() { - const srsSize = 230 - bAlpha = new(big.Int).SetInt64(42) // randomise ? - testSrs, _ = NewSRS(ecc.NextPowerOfTwo(srsSize), bAlpha) -} - -func TestToLagrangeG1(t *testing.T) { - assert := require.New(t) - - const size = 32 - - // convert the test SRS to Lagrange form - lagrange, err := ToLagrangeG1(testSrs.Pk.G1[:size]) - assert.NoError(err) - - // generate the Lagrange SRS manually and compare - w, err := fr.Generator(uint64(size)) - assert.NoError(err) - - var li, n, d, one, acc, alpha fr.Element - alpha.SetBigInt(bAlpha) - li.SetUint64(uint64(size)).Inverse(&li) - one.SetOne() - n.Exp(alpha, big.NewInt(int64(size))).Sub(&n, &one) - d.Sub(&alpha, &one) - li.Mul(&li, &n).Div(&li, &d) - expectedSrsLagrange := make([]bw6756.G1Affine, size) - _, _, g1Gen, _ := bw6756.Generators() - var s big.Int - acc.SetOne() - for i := 0; i < size; i++ { - li.BigInt(&s) - expectedSrsLagrange[i].ScalarMultiplication(&g1Gen, &s) - - li.Mul(&li, &w).Mul(&li, &d) - acc.Mul(&acc, &w) - d.Sub(&alpha, &acc) - li.Div(&li, &d) - } - - for i := 0; i < size; i++ { - assert.True(expectedSrsLagrange[i].Equal(&lagrange[i]), "error lagrange conversion") - } -} - -func TestCommitLagrange(t *testing.T) { - - assert := require.New(t) - - // sample a sparse polynomial (here in Lagrange form) - size := 64 - pol := make([]fr.Element, size) - pol[0].SetRandom() - for i := 0; i < size; i = i + 8 { - pol[i].SetRandom() - } - - // commitment using Lagrange SRS - lagrange, err := ToLagrangeG1(testSrs.Pk.G1[:size]) - assert.NoError(err) - var pkLagrange ProvingKey - pkLagrange.G1 = lagrange - - digestLagrange, err := Commit(pol, pkLagrange) - assert.NoError(err) - - // commitment using canonical SRS - d := fft.NewDomain(uint64(size)) - d.FFTInverse(pol, fft.DIF) - fft.BitReverse(pol) - digestCanonical, err := Commit(pol, testSrs.Pk) - assert.NoError(err) - - // compare the results - assert.True(digestCanonical.Equal(&digestLagrange), "error CommitLagrange") -} - -func TestDividePolyByXminusA(t *testing.T) { - - const pSize = 230 - - // build random polynomial - pol := make([]fr.Element, pSize) - pol[0].SetRandom() - for i := 1; i < pSize; i++ { - pol[i] = pol[i-1] - } - - // evaluate the polynomial at a random point - var point fr.Element - point.SetRandom() - evaluation := eval(pol, point) - - // probabilistic test (using Schwartz Zippel lemma, evaluation at one point is enough) - var randPoint, xminusa fr.Element - randPoint.SetRandom() - polRandpoint := eval(pol, randPoint) - polRandpoint.Sub(&polRandpoint, &evaluation) // f(rand)-f(point) - - // compute f-f(a)/x-a - // h re-uses the memory of pol - h := dividePolyByXminusA(pol, evaluation, point) - - if len(h) != 229 { - t.Fatal("inconsistent size of quotient") - } - - hRandPoint := eval(h, randPoint) - xminusa.Sub(&randPoint, &point) // rand-point - - // f(rand)-f(point) ==? h(rand)*(rand-point) - hRandPoint.Mul(&hRandPoint, &xminusa) - - if !hRandPoint.Equal(&polRandpoint) { - t.Fatal("Error f-f(a)/x-a") - } -} - -func TestSerializationSRS(t *testing.T) { - // create a SRS - srs, err := NewSRS(64, new(big.Int).SetInt64(42)) - assert.NoError(t, err) - t.Run("proving key round-trip", testutils.SerializationRoundTrip(&srs.Pk)) - t.Run("proving key raw round-trip", testutils.SerializationRoundTripRaw(&srs.Pk)) - t.Run("verifying key round-trip", testutils.SerializationRoundTrip(&srs.Vk)) - t.Run("whole SRS round-trip", testutils.SerializationRoundTrip(srs)) - t.Run("unsafe whole SRS round-trip", testutils.UnsafeBinaryMarshalerRoundTrip(srs)) -} - -func TestCommit(t *testing.T) { - - // create a polynomial - f := make([]fr.Element, 60) - for i := 0; i < 60; i++ { - f[i].SetRandom() - } - - // commit using the method from KZG - _kzgCommit, err := Commit(f, testSrs.Pk) - if err != nil { - t.Fatal(err) - } - var kzgCommit bw6756.G1Affine - kzgCommit.Unmarshal(_kzgCommit.Marshal()) - - // check commitment using manual commit - var x fr.Element - x.SetString("42") - fx := eval(f, x) - var fxbi big.Int - fx.BigInt(&fxbi) - var manualCommit bw6756.G1Affine - manualCommit.Set(&testSrs.Vk.G1) - manualCommit.ScalarMultiplication(&manualCommit, &fxbi) - - // compare both results - if !kzgCommit.Equal(&manualCommit) { - t.Fatal("error KZG commitment") - } - -} - -func TestVerifySinglePoint(t *testing.T) { - - // create a polynomial - f := randomPolynomial(60) - - // commit the polynomial - digest, err := Commit(f, testSrs.Pk) - if err != nil { - t.Fatal(err) - } - - // compute opening proof at a random point - var point fr.Element - point.SetString("4321") - proof, err := Open(f, point, testSrs.Pk) - if err != nil { - t.Fatal(err) - } - - // verify the claimed valued - expected := eval(f, point) - if !proof.ClaimedValue.Equal(&expected) { - t.Fatal("inconsistent claimed value") - } - - // verify correct proof - err = Verify(&digest, &proof, point, testSrs.Vk) - if err != nil { - t.Fatal(err) - } - - { - // verify wrong proof - proof.ClaimedValue.Double(&proof.ClaimedValue) - err = Verify(&digest, &proof, point, testSrs.Vk) - if err == nil { - t.Fatal("verifying wrong proof should have failed") - } - } - { - // verify wrong proof with quotient set to zero - // see https://cryptosubtlety.medium.com/00-8d4adcf4d255 - proof.H.X.SetZero() - proof.H.Y.SetZero() - err = Verify(&digest, &proof, point, testSrs.Vk) - if err == nil { - t.Fatal("verifying wrong proof should have failed") - } - } -} - -func TestVerifySinglePointQuickSRS(t *testing.T) { - - size := 64 - srs, err := NewSRS(64, big.NewInt(-1)) - if err != nil { - t.Fatal(err) - } - - // random polynomial - p := make([]fr.Element, size) - for i := 0; i < size; i++ { - p[i].SetRandom() - } - - // random value - var x fr.Element - x.SetRandom() - - // verify valid proof - d, err := Commit(p, srs.Pk) - if err != nil { - t.Fatal(err) - } - proof, err := Open(p, x, srs.Pk) - if err != nil { - t.Fatal(err) - } - err = Verify(&d, &proof, x, srs.Vk) - if err != nil { - t.Fatal(err) - } - - // verify wrong proof - proof.ClaimedValue.SetRandom() - err = Verify(&d, &proof, x, srs.Vk) - if err == nil { - t.Fatal(err) - } - -} - -func TestBatchVerifySinglePoint(t *testing.T) { - - size := 40 - - // create polynomials - f := make([][]fr.Element, 10) - for i := range f { - f[i] = randomPolynomial(size) - } - - // commit the polynomials - digests := make([]Digest, len(f)) - for i := range f { - digests[i], _ = Commit(f[i], testSrs.Pk) - - } - - // pick a hash function - hf := sha256.New() - - // compute opening proof at a random point - var point fr.Element - point.SetString("4321") - proof, err := BatchOpenSinglePoint(f, digests, point, hf, testSrs.Pk) - if err != nil { - t.Fatal(err) - } - - var salt fr.Element - salt.SetRandom() - proofExtendedTranscript, err := BatchOpenSinglePoint(f, digests, point, hf, testSrs.Pk, salt.Marshal()) - if err != nil { - t.Fatal(err) - } - - // verify the claimed values - for i := range f { - expectedClaim := eval(f[i], point) - if !expectedClaim.Equal(&proof.ClaimedValues[i]) { - t.Fatal("inconsistent claimed values") - } - } - - // verify correct proof - err = BatchVerifySinglePoint(digests, &proof, point, hf, testSrs.Vk) - if err != nil { - t.Fatal(err) - } - - // verify correct proof with extended transcript - err = BatchVerifySinglePoint(digests, &proofExtendedTranscript, point, hf, testSrs.Vk, salt.Marshal()) - if err != nil { - t.Fatal(err) - } - - { - // verify wrong proof - proof.ClaimedValues[0].Double(&proof.ClaimedValues[0]) - err = BatchVerifySinglePoint(digests, &proof, point, hf, testSrs.Vk) - if err == nil { - t.Fatal("verifying wrong proof should have failed") - } - } - { - // verify wrong proof with quotient set to zero - // see https://cryptosubtlety.medium.com/00-8d4adcf4d255 - proof.H.X.SetZero() - proof.H.Y.SetZero() - err = BatchVerifySinglePoint(digests, &proof, point, hf, testSrs.Vk) - if err == nil { - t.Fatal("verifying wrong proof should have failed") - } - } -} - -func TestBatchVerifyMultiPoints(t *testing.T) { - - // create polynomials - f := make([][]fr.Element, 10) - for i := 0; i < 10; i++ { - f[i] = randomPolynomial(40) - } - - // commit the polynomials - digests := make([]Digest, 10) - for i := 0; i < 10; i++ { - digests[i], _ = Commit(f[i], testSrs.Pk) - } - - // pick a hash function - hf := sha256.New() - - // compute 2 batch opening proofs at 2 random points - points := make([]fr.Element, 2) - batchProofs := make([]BatchOpeningProof, 2) - points[0].SetRandom() - batchProofs[0], _ = BatchOpenSinglePoint(f[:5], digests[:5], points[0], hf, testSrs.Pk) - points[1].SetRandom() - batchProofs[1], _ = BatchOpenSinglePoint(f[5:], digests[5:], points[1], hf, testSrs.Pk) - - // fold the 2 batch opening proofs - proofs := make([]OpeningProof, 2) - foldedDigests := make([]Digest, 2) - proofs[0], foldedDigests[0], _ = FoldProof(digests[:5], &batchProofs[0], points[0], hf) - proofs[1], foldedDigests[1], _ = FoldProof(digests[5:], &batchProofs[1], points[1], hf) - - // check that the individual batch proofs are correct - err := Verify(&foldedDigests[0], &proofs[0], points[0], testSrs.Vk) - if err != nil { - t.Fatal(err) - } - err = Verify(&foldedDigests[1], &proofs[1], points[1], testSrs.Vk) - if err != nil { - t.Fatal(err) - } - - // batch verify correct folded proofs - err = BatchVerifyMultiPoints(foldedDigests, proofs, points, testSrs.Vk) - if err != nil { - t.Fatal(err) - } - - { - // batch verify tampered folded proofs - proofs[0].ClaimedValue.Double(&proofs[0].ClaimedValue) - - err = BatchVerifyMultiPoints(foldedDigests, proofs, points, testSrs.Vk) - if err == nil { - t.Fatal(err) - } - } - { - // batch verify tampered folded proofs with quotients set to infinity - // see https://cryptosubtlety.medium.com/00-8d4adcf4d255 - proofs[0].H.X.SetZero() - proofs[0].H.Y.SetZero() - proofs[1].H.X.SetZero() - proofs[1].H.Y.SetZero() - err = BatchVerifyMultiPoints(foldedDigests, proofs, points, testSrs.Vk) - if err == nil { - t.Fatal(err) - } - } -} - -func TestUnsafeToBytesTruncating(t *testing.T) { - assert := require.New(t) - srs, err := NewSRS(ecc.NextPowerOfTwo(1<<10), big.NewInt(-1)) - assert.NoError(err) - - // marshal the SRS, but explicitly with less points. - var buf bytes.Buffer - err = srs.WriteDump(&buf, 1<<9) - assert.NoError(err) - - r := bytes.NewReader(buf.Bytes()) - - // unmarshal the SRS - var newSRS SRS - err = newSRS.ReadDump(r) - assert.NoError(err) - - // check that the SRS proving key has only 1 << 9 points - assert.Equal(1<<9, len(newSRS.Pk.G1)) - - // ensure they are equal to the original SRS - assert.Equal(srs.Pk.G1[:1<<9], newSRS.Pk.G1) - - // read even less points. - var newSRSPartial SRS - r = bytes.NewReader(buf.Bytes()) - err = newSRSPartial.ReadDump(r, 1<<8) - assert.NoError(err) - - // check that the SRS proving key has only 1 << 8 points - assert.Equal(1<<8, len(newSRSPartial.Pk.G1)) - - // ensure they are equal to the original SRS - assert.Equal(srs.Pk.G1[:1<<8], newSRSPartial.Pk.G1) -} - -const benchSize = 1 << 16 - -func BenchmarkSRSGen(b *testing.B) { - - b.Run("real SRS", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) - } - }) - b.Run("quick SRS", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - NewSRS(ecc.NextPowerOfTwo(benchSize), big.NewInt(-1)) - } - }) -} - -func BenchmarkKZGCommit(b *testing.B) { - - b.Run("real SRS", func(b *testing.B) { - srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) - assert.NoError(b, err) - // random polynomial - p := randomPolynomial(benchSize / 2) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = Commit(p, srs.Pk) - } - }) - b.Run("quick SRS", func(b *testing.B) { - srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), big.NewInt(-1)) - assert.NoError(b, err) - // random polynomial - p := randomPolynomial(benchSize / 2) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = Commit(p, srs.Pk) - } - }) -} - -func BenchmarkDivideByXMinusA(b *testing.B) { - const pSize = 1 << 22 - - // build random polynomial - pol := make([]fr.Element, pSize) - pol[0].SetRandom() - for i := 1; i < pSize; i++ { - pol[i] = pol[i-1] - } - var a, fa fr.Element - a.SetRandom() - fa.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - dividePolyByXminusA(pol, fa, a) - pol = pol[:pSize] - pol[pSize-1] = pol[0] - } -} - -func BenchmarkKZGOpen(b *testing.B) { - srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) - assert.NoError(b, err) - - // random polynomial - p := randomPolynomial(benchSize / 2) - var r fr.Element - r.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = Open(p, r, srs.Pk) - } -} - -func BenchmarkKZGVerify(b *testing.B) { - srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) - assert.NoError(b, err) - - // random polynomial - p := randomPolynomial(benchSize / 2) - var r fr.Element - r.SetRandom() - - // commit - comm, err := Commit(p, srs.Pk) - assert.NoError(b, err) - - // open - openingProof, err := Open(p, r, srs.Pk) - assert.NoError(b, err) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - Verify(&comm, &openingProof, r, srs.Vk) - } -} - -func BenchmarkKZGBatchOpen10(b *testing.B) { - srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) - assert.NoError(b, err) - - // 10 random polynomials - var ps [10][]fr.Element - for i := 0; i < 10; i++ { - ps[i] = randomPolynomial(benchSize / 2) - } - - // commitments - var commitments [10]Digest - for i := 0; i < 10; i++ { - commitments[i], _ = Commit(ps[i], srs.Pk) - } - - // pick a hash function - hf := sha256.New() - - var r fr.Element - r.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - BatchOpenSinglePoint(ps[:], commitments[:], r, hf, srs.Pk) - } -} - -func BenchmarkKZGBatchVerify10(b *testing.B) { - srs, err := NewSRS(ecc.NextPowerOfTwo(benchSize), new(big.Int).SetInt64(42)) - if err != nil { - b.Fatal(err) - } - - // 10 random polynomials - var ps [10][]fr.Element - for i := 0; i < 10; i++ { - ps[i] = randomPolynomial(benchSize / 2) - } - - // commitments - var commitments [10]Digest - for i := 0; i < 10; i++ { - commitments[i], _ = Commit(ps[i], srs.Pk) - } - - // pick a hash function - hf := sha256.New() - - var r fr.Element - r.SetRandom() - - proof, err := BatchOpenSinglePoint(ps[:], commitments[:], r, hf, srs.Pk) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - BatchVerifySinglePoint(commitments[:], &proof, r, hf, testSrs.Vk) - } -} - -func randomPolynomial(size int) []fr.Element { - f := make([]fr.Element, size) - for i := 0; i < size; i++ { - f[i].SetRandom() - } - return f -} - -func BenchmarkToLagrangeG1(b *testing.B) { - const size = 1 << 14 - - var samplePoints [size]bw6756.G1Affine - fillBenchBasesG1(samplePoints[:]) - b.ResetTimer() - - for i := 0; i < b.N; i++ { - if _, err := ToLagrangeG1(samplePoints[:]); err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkSerializeSRS(b *testing.B) { - // let's create a quick SRS - srs, err := NewSRS(ecc.NextPowerOfTwo(1<<24), big.NewInt(-1)) - if err != nil { - b.Fatal(err) - } - - // now we can benchmark the WriteTo, WriteRawTo and WriteDump methods - b.Run("WriteTo", func(b *testing.B) { - b.ResetTimer() - var buf bytes.Buffer - for i := 0; i < b.N; i++ { - buf.Reset() - _, err := srs.WriteTo(&buf) - if err != nil { - b.Fatal(err) - } - } - }) - - b.Run("WriteRawTo", func(b *testing.B) { - b.ResetTimer() - var buf bytes.Buffer - for i := 0; i < b.N; i++ { - buf.Reset() - _, err := srs.WriteRawTo(&buf) - if err != nil { - b.Fatal(err) - } - } - }) - - b.Run("WriteDump", func(b *testing.B) { - b.ResetTimer() - var buf bytes.Buffer - for i := 0; i < b.N; i++ { - buf.Reset() - if err := srs.WriteDump(&buf); err != nil { - b.Fatal(err) - } - } - }) - -} - -func BenchmarkDeserializeSRS(b *testing.B) { - // let's create a quick SRS - srs, err := NewSRS(ecc.NextPowerOfTwo(1<<24), big.NewInt(-1)) - if err != nil { - b.Fatal(err) - } - - b.Run("UnsafeReadFrom", func(b *testing.B) { - var buf bytes.Buffer - if _, err := srs.WriteRawTo(&buf); err != nil { - b.Fatal(err) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - var newSRS SRS - _, err := newSRS.UnsafeReadFrom(bytes.NewReader(buf.Bytes())) - if err != nil { - b.Fatal(err) - } - } - }) - - b.Run("ReadDump", func(b *testing.B) { - var buf bytes.Buffer - err := srs.WriteDump(&buf) - if err != nil { - b.Fatal(err) - } - data := buf.Bytes() - b.ResetTimer() - for i := 0; i < b.N; i++ { - var newSRS SRS - if err := newSRS.ReadDump(bytes.NewReader(data)); err != nil { - b.Fatal(err) - } - } - }) -} - -func fillBenchBasesG1(samplePoints []bw6756.G1Affine) { - var r big.Int - r.SetString("340444420969191673093399857471996460938405", 10) - samplePoints[0].ScalarMultiplication(&samplePoints[0], &r) - - one := samplePoints[0].X - one.SetOne() - - for i := 1; i < len(samplePoints); i++ { - samplePoints[i].X.Add(&samplePoints[i-1].X, &one) - samplePoints[i].Y.Sub(&samplePoints[i-1].Y, &one) - } -} diff --git a/ecc/bw6-756/kzg/marshal.go b/ecc/bw6-756/kzg/marshal.go deleted file mode 100644 index b74a955e20..0000000000 --- a/ecc/bw6-756/kzg/marshal.go +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package kzg - -import ( - "github.com/consensys/gnark-crypto/ecc/bw6-756" - "io" - - "github.com/consensys/gnark-crypto/utils/unsafe" -) - -// WriteTo writes binary encoding of the ProvingKey -func (pk *ProvingKey) WriteTo(w io.Writer) (int64, error) { - return pk.writeTo(w) -} - -// WriteRawTo writes binary encoding of ProvingKey to w without point compression -func (pk *ProvingKey) WriteRawTo(w io.Writer) (int64, error) { - return pk.writeTo(w, bw6756.RawEncoding()) -} - -func (pk *ProvingKey) writeTo(w io.Writer, options ...func(*bw6756.Encoder)) (int64, error) { - // encode the ProvingKey - enc := bw6756.NewEncoder(w, options...) - if err := enc.Encode(pk.G1); err != nil { - return enc.BytesWritten(), err - } - return enc.BytesWritten(), nil -} - -// WriteRawTo writes binary encoding of VerifyingKey to w without point compression -func (vk *VerifyingKey) WriteRawTo(w io.Writer) (int64, error) { - return vk.writeTo(w, bw6756.RawEncoding()) -} - -// WriteTo writes binary encoding of the VerifyingKey -func (vk *VerifyingKey) WriteTo(w io.Writer) (int64, error) { - return vk.writeTo(w) -} - -func (vk *VerifyingKey) writeTo(w io.Writer, options ...func(*bw6756.Encoder)) (int64, error) { - // encode the VerifyingKey - enc := bw6756.NewEncoder(w, options...) - nLines := 190 - toEncode := make([]interface{}, 0, 4*nLines+3) - toEncode = append(toEncode, &vk.G2[0]) - toEncode = append(toEncode, &vk.G2[1]) - toEncode = append(toEncode, &vk.G1) - for k := 0; k < 2; k++ { - for j := 0; j < 2; j++ { - for i := nLines - 1; i >= 0; i-- { - toEncode = append(toEncode, &vk.Lines[k][j][i].R0) - toEncode = append(toEncode, &vk.Lines[k][j][i].R1) - } - } - } - - for _, v := range toEncode { - if err := enc.Encode(v); err != nil { - return enc.BytesWritten(), err - } - } - - return enc.BytesWritten(), nil -} - -// WriteDump writes the binary encoding of the entire SRS memory representation -// It is meant to be use to achieve fast serialization/deserialization and -// is not compatible with WriteTo / ReadFrom. It does not do any validation -// and doesn't encode points in a canonical form. -// @unsafe: this is platform dependent and may not be compatible with other platforms -// @unstable: the format may change in the future -// If maxPkPoints is provided, the number of points in the ProvingKey will be limited to maxPkPoints -func (srs *SRS) WriteDump(w io.Writer, maxPkPoints ...int) error { - maxG1 := len(srs.Pk.G1) - if len(maxPkPoints) > 0 && maxPkPoints[0] < maxG1 && maxPkPoints[0] > 0 { - maxG1 = maxPkPoints[0] - } - // first we write the VerifyingKey; it is small so we re-use WriteTo - - if _, err := srs.Vk.writeTo(w, bw6756.RawEncoding()); err != nil { - return err - } - - // write the marker - if err := unsafe.WriteMarker(w); err != nil { - return err - } - - // write the slice - return unsafe.WriteSlice(w, srs.Pk.G1[:maxG1]) -} - -// ReadDump deserializes the SRS from a reader, as written by WriteDump -func (srs *SRS) ReadDump(r io.Reader, maxPkPoints ...int) error { - // first we read the VerifyingKey; it is small so we re-use ReadFrom - _, err := srs.Vk.ReadFrom(r) - if err != nil { - return err - } - - // read the marker - if err := unsafe.ReadMarker(r); err != nil { - return err - } - - // read the slice - srs.Pk.G1, _, err = unsafe.ReadSlice[[]bw6756.G1Affine](r, maxPkPoints...) - return err -} - -// WriteTo writes binary encoding of the entire SRS -func (srs *SRS) WriteTo(w io.Writer) (int64, error) { - // encode the SRS - var pn, vn int64 - var err error - if pn, err = srs.Pk.WriteTo(w); err != nil { - return pn, err - } - vn, err = srs.Vk.WriteTo(w) - return pn + vn, err -} - -// WriteRawTo writes binary encoding of the entire SRS without point compression -func (srs *SRS) WriteRawTo(w io.Writer) (int64, error) { - // encode the SRS - var pn, vn int64 - var err error - if pn, err = srs.Pk.WriteRawTo(w); err != nil { - return pn, err - } - vn, err = srs.Vk.WriteRawTo(w) - return pn + vn, err -} - -// ReadFrom decodes ProvingKey data from reader. -func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { - // decode the ProvingKey - dec := bw6756.NewDecoder(r) - if err := dec.Decode(&pk.G1); err != nil { - return dec.BytesRead(), err - } - return dec.BytesRead(), nil -} - -// UnsafeReadFrom decodes ProvingKey data from reader without checking -// that point are in the correct subgroup. -func (pk *ProvingKey) UnsafeReadFrom(r io.Reader) (int64, error) { - // decode the ProvingKey - dec := bw6756.NewDecoder(r, bw6756.NoSubgroupChecks()) - if err := dec.Decode(&pk.G1); err != nil { - return dec.BytesRead(), err - } - return dec.BytesRead(), nil -} - -// ReadFrom decodes VerifyingKey data from reader. -func (vk *VerifyingKey) ReadFrom(r io.Reader) (int64, error) { - // decode the VerifyingKey - dec := bw6756.NewDecoder(r) - nLines := 190 - toDecode := make([]interface{}, 0, 4*nLines+3) - toDecode = append(toDecode, &vk.G2[0]) - toDecode = append(toDecode, &vk.G2[1]) - toDecode = append(toDecode, &vk.G1) - for k := 0; k < 2; k++ { - for j := 0; j < 2; j++ { - for i := nLines - 1; i >= 0; i-- { - toDecode = append(toDecode, &vk.Lines[k][j][i].R0) - toDecode = append(toDecode, &vk.Lines[k][j][i].R1) - } - } - } - - for _, v := range toDecode { - if err := dec.Decode(v); err != nil { - return dec.BytesRead(), err - } - } - - return dec.BytesRead(), nil -} - -// ReadFrom decodes SRS data from reader. -func (srs *SRS) ReadFrom(r io.Reader) (int64, error) { - // decode the VerifyingKey - var pn, vn int64 - var err error - if pn, err = srs.Pk.ReadFrom(r); err != nil { - return pn, err - } - vn, err = srs.Vk.ReadFrom(r) - return pn + vn, err -} - -// UnsafeReadFrom decodes SRS data from reader without sub group checks -func (srs *SRS) UnsafeReadFrom(r io.Reader) (int64, error) { - // decode the VerifyingKey - var pn, vn int64 - var err error - if pn, err = srs.Pk.UnsafeReadFrom(r); err != nil { - return pn, err - } - vn, err = srs.Vk.ReadFrom(r) - return pn + vn, err -} - -// WriteTo writes binary encoding of a OpeningProof -func (proof *OpeningProof) WriteTo(w io.Writer) (int64, error) { - enc := bw6756.NewEncoder(w) - - toEncode := []interface{}{ - &proof.H, - &proof.ClaimedValue, - } - - for _, v := range toEncode { - if err := enc.Encode(v); err != nil { - return enc.BytesWritten(), err - } - } - - return enc.BytesWritten(), nil -} - -// ReadFrom decodes OpeningProof data from reader. -func (proof *OpeningProof) ReadFrom(r io.Reader) (int64, error) { - dec := bw6756.NewDecoder(r) - - toDecode := []interface{}{ - &proof.H, - &proof.ClaimedValue, - } - - for _, v := range toDecode { - if err := dec.Decode(v); err != nil { - return dec.BytesRead(), err - } - } - - return dec.BytesRead(), nil -} - -// WriteTo writes binary encoding of a BatchOpeningProof -func (proof *BatchOpeningProof) WriteTo(w io.Writer) (int64, error) { - enc := bw6756.NewEncoder(w) - - toEncode := []interface{}{ - &proof.H, - proof.ClaimedValues, - } - - for _, v := range toEncode { - if err := enc.Encode(v); err != nil { - return enc.BytesWritten(), err - } - } - - return enc.BytesWritten(), nil -} - -// ReadFrom decodes BatchOpeningProof data from reader. -func (proof *BatchOpeningProof) ReadFrom(r io.Reader) (int64, error) { - dec := bw6756.NewDecoder(r) - toDecode := []interface{}{ - &proof.H, - &proof.ClaimedValues, - } - - for _, v := range toDecode { - if err := dec.Decode(v); err != nil { - return dec.BytesRead(), err - } - } - - return dec.BytesRead(), nil -} diff --git a/ecc/bw6-756/kzg/utils.go b/ecc/bw6-756/kzg/utils.go deleted file mode 100644 index 6d7947a057..0000000000 --- a/ecc/bw6-756/kzg/utils.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package kzg - -import ( - "fmt" - "math/big" - "math/bits" - "runtime" - - "github.com/consensys/gnark-crypto/ecc" - curve "github.com/consensys/gnark-crypto/ecc/bw6-756" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/internal/parallel" -) - -// ToLagrangeG1 in place transform of coeffs canonical form into Lagrange form. -// From the formula Lᵢ(τ) = 1/n∑_{j> nn - if irev > i { - a[i], a[irev] = a[irev], a[i] - } - } -} - -func butterflyG1(a *curve.G1Jac, b *curve.G1Jac) { - t := *a - a.AddAssign(b) - t.SubAssign(b) - b.Set(&t) -} - -func difFFTG1(a []curve.G1Jac, twiddles []*big.Int, stage, maxSplits int, chDone chan struct{}) { - if chDone != nil { - defer close(chDone) - } - - n := len(a) - if n == 1 { - return - } - m := n >> 1 - - butterflyG1(&a[0], &a[m]) - // stage determines the stride - // if stage == 0, then we use 1, w, w**2, w**3, w**4, w**5, w**6, ... - // if stage == 1, then we use 1, w**2, w**4, w**6, ... that is, indexes 0, 2, 4, 6, ... of stage 0 - // if stage == 2, then we use 1, w**4, w**8, w**12, ... that is indexes 0, 4, 8, 12, ... of stage 0 - stride := 1 << stage - - const butterflyThreshold = 8 - if m >= butterflyThreshold { - // 1 << stage == estimated used CPUs - numCPU := runtime.NumCPU() / (1 << (stage)) - parallel.Execute(m, func(start, end int) { - if start == 0 { - start = 1 - } - j := start * stride - for i := start; i < end; i++ { - butterflyG1(&a[i], &a[i+m]) - a[i+m].ScalarMultiplication(&a[i+m], twiddles[j]) - j += stride - } - }, numCPU) - } else { - j := stride - for i := 1; i < m; i++ { - butterflyG1(&a[i], &a[i+m]) - a[i+m].ScalarMultiplication(&a[i+m], twiddles[j]) - j += stride - } - } - - if m == 1 { - return - } - - nextStage := stage + 1 - if stage < maxSplits { - chDone := make(chan struct{}, 1) - go difFFTG1(a[m:n], twiddles, nextStage, maxSplits, chDone) - difFFTG1(a[0:m], twiddles, nextStage, maxSplits, nil) - <-chDone - } else { - difFFTG1(a[0:m], twiddles, nextStage, maxSplits, nil) - difFFTG1(a[m:n], twiddles, nextStage, maxSplits, nil) - } -} diff --git a/ecc/bw6-756/marshal.go b/ecc/bw6-756/marshal.go deleted file mode 100644 index 08083e42b8..0000000000 --- a/ecc/bw6-756/marshal.go +++ /dev/null @@ -1,1272 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bw6756 - -import ( - "encoding/binary" - "errors" - "io" - "reflect" - "sync/atomic" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/internal/fptower" - "github.com/consensys/gnark-crypto/internal/parallel" -) - -// To encode G1Affine and G2Affine points, we mask the most significant bits with these bits to specify without ambiguity -// metadata needed for point (de)compression -// we follow the BLS12-381 style encoding as specified in ZCash and now IETF -// see https://datatracker.ietf.org/doc/draft-irtf-cfrg-pairing-friendly-curves/11/ -// Appendix C. ZCash serialization format for BLS12_381 -const ( - mMask byte = 0b111 << 5 - mUncompressed byte = 0b000 << 5 - _ byte = 0b001 << 5 // invalid - mUncompressedInfinity byte = 0b010 << 5 - _ byte = 0b011 << 5 // invalid - mCompressedSmallest byte = 0b100 << 5 - mCompressedLargest byte = 0b101 << 5 - mCompressedInfinity byte = 0b110 << 5 - _ byte = 0b111 << 5 // invalid -) - -// SizeOfGT represents the size in bytes that a GT element need in binary form -const SizeOfGT = fptower.SizeOfGT - -var ( - ErrInvalidInfinityEncoding = errors.New("invalid infinity point encoding") - ErrInvalidEncoding = errors.New("invalid point encoding") -) - -// Encoder writes bw6-756 object values to an output stream -type Encoder struct { - w io.Writer - n int64 // written bytes - raw bool // raw vs compressed encoding -} - -// Decoder reads bw6-756 object values from an inbound stream -type Decoder struct { - r io.Reader - n int64 // read bytes - subGroupCheck bool // default to true -} - -// NewDecoder returns a binary decoder supporting curve bw6-756 objects in both -// compressed and uncompressed (raw) forms -func NewDecoder(r io.Reader, options ...func(*Decoder)) *Decoder { - d := &Decoder{r: r, subGroupCheck: true} - - for _, o := range options { - o(d) - } - - return d -} - -// Decode reads the binary encoding of v from the stream -// type must be *uint64, *fr.Element, *fp.Element, *G1Affine, *G2Affine, *[]G1Affine or *[]G2Affine -func (dec *Decoder) Decode(v interface{}) (err error) { - rv := reflect.ValueOf(v) - if v == nil || rv.Kind() != reflect.Ptr || rv.IsNil() || !rv.Elem().CanSet() { - return errors.New("bw6-756 decoder: unsupported type, need pointer") - } - - // implementation note: code is a bit verbose (abusing code generation), but minimize allocations on the heap - // in particular, careful attention must be given to usage of Bytes() method on Elements and Points - // that return an array (not a slice) of bytes. Using this is beneficial to minimize memory allocations - // in very large (de)serialization upstream in gnark. - // (but detrimental to code readability here) - - var read64 int64 - if vf, ok := v.(io.ReaderFrom); ok { - read64, err = vf.ReadFrom(dec.r) - dec.n += read64 - return - } - - var buf [SizeOfG2AffineUncompressed]byte - var read int - var sliceLen uint32 - - switch t := v.(type) { - case *[][]uint64: - if sliceLen, err = dec.readUint32(); err != nil { - return - } - *t = make([][]uint64, sliceLen) - - for i := range *t { - if sliceLen, err = dec.readUint32(); err != nil { - return - } - (*t)[i] = make([]uint64, sliceLen) - for j := range (*t)[i] { - if (*t)[i][j], err = dec.readUint64(); err != nil { - return - } - } - } - return - case *[]uint64: - if sliceLen, err = dec.readUint32(); err != nil { - return - } - *t = make([]uint64, sliceLen) - for i := range *t { - if (*t)[i], err = dec.readUint64(); err != nil { - return - } - } - return - case *fr.Element: - read, err = io.ReadFull(dec.r, buf[:fr.Bytes]) - dec.n += int64(read) - if err != nil { - return - } - err = t.SetBytesCanonical(buf[:fr.Bytes]) - return - case *fp.Element: - read, err = io.ReadFull(dec.r, buf[:fp.Bytes]) - dec.n += int64(read) - if err != nil { - return - } - err = t.SetBytesCanonical(buf[:fp.Bytes]) - return - case *[]fr.Element: - read64, err = (*fr.Vector)(t).ReadFrom(dec.r) - dec.n += read64 - return - case *[]fp.Element: - read64, err = (*fp.Vector)(t).ReadFrom(dec.r) - dec.n += read64 - return - case *[][]fr.Element: - if sliceLen, err = dec.readUint32(); err != nil { - return - } - if len(*t) != int(sliceLen) { - *t = make([][]fr.Element, sliceLen) - } - for i := range *t { - read64, err = (*fr.Vector)(&(*t)[i]).ReadFrom(dec.r) - dec.n += read64 - } - return - case *G1Affine: - // we start by reading compressed point size, if metadata tells us it is uncompressed, we read more. - read, err = io.ReadFull(dec.r, buf[:SizeOfG1AffineCompressed]) - dec.n += int64(read) - if err != nil { - return - } - nbBytes := SizeOfG1AffineCompressed - - // 111, 011, 001 --> invalid mask - if isMaskInvalid(buf[0]) { - err = ErrInvalidEncoding - return - } - - // most significant byte contains metadata - if !isCompressed(buf[0]) { - nbBytes = SizeOfG1AffineUncompressed - // we read more. - read, err = io.ReadFull(dec.r, buf[SizeOfG1AffineCompressed:SizeOfG1AffineUncompressed]) - dec.n += int64(read) - if err != nil { - return - } - } - _, err = t.setBytes(buf[:nbBytes], dec.subGroupCheck) - return - case *G2Affine: - // we start by reading compressed point size, if metadata tells us it is uncompressed, we read more. - read, err = io.ReadFull(dec.r, buf[:SizeOfG2AffineCompressed]) - dec.n += int64(read) - if err != nil { - return - } - nbBytes := SizeOfG2AffineCompressed - - // 111, 011, 001 --> invalid mask - if isMaskInvalid(buf[0]) { - err = ErrInvalidEncoding - return - } - - // most significant byte contains metadata - if !isCompressed(buf[0]) { - nbBytes = SizeOfG2AffineUncompressed - // we read more. - read, err = io.ReadFull(dec.r, buf[SizeOfG2AffineCompressed:SizeOfG2AffineUncompressed]) - dec.n += int64(read) - if err != nil { - return - } - } - _, err = t.setBytes(buf[:nbBytes], dec.subGroupCheck) - return - case *[]G1Affine: - sliceLen, err = dec.readUint32() - if err != nil { - return - } - if len(*t) != int(sliceLen) || *t == nil { - *t = make([]G1Affine, sliceLen) - } - compressed := make([]bool, sliceLen) - for i := 0; i < len(*t); i++ { - - // we start by reading compressed point size, if metadata tells us it is uncompressed, we read more. - read, err = io.ReadFull(dec.r, buf[:SizeOfG1AffineCompressed]) - dec.n += int64(read) - if err != nil { - return - } - nbBytes := SizeOfG1AffineCompressed - - // 111, 011, 001 --> invalid mask - if isMaskInvalid(buf[0]) { - err = ErrInvalidEncoding - return - } - - // most significant byte contains metadata - if !isCompressed(buf[0]) { - nbBytes = SizeOfG1AffineUncompressed - // we read more. - read, err = io.ReadFull(dec.r, buf[SizeOfG1AffineCompressed:SizeOfG1AffineUncompressed]) - dec.n += int64(read) - if err != nil { - return - } - _, err = (*t)[i].setBytes(buf[:nbBytes], false) - if err != nil { - return - } - } else { - var r bool - if r, err = (*t)[i].unsafeSetCompressedBytes(buf[:nbBytes]); err != nil { - return - } - compressed[i] = !r - } - } - var nbErrs uint64 - parallel.Execute(len(compressed), func(start, end int) { - for i := start; i < end; i++ { - if compressed[i] { - if err := (*t)[i].unsafeComputeY(dec.subGroupCheck); err != nil { - atomic.AddUint64(&nbErrs, 1) - } - } else if dec.subGroupCheck { - if !(*t)[i].IsInSubGroup() { - atomic.AddUint64(&nbErrs, 1) - } - } - } - }) - if nbErrs != 0 { - return errors.New("point decompression failed") - } - - return nil - case *[]G2Affine: - sliceLen, err = dec.readUint32() - if err != nil { - return - } - if len(*t) != int(sliceLen) { - *t = make([]G2Affine, sliceLen) - } - compressed := make([]bool, sliceLen) - for i := 0; i < len(*t); i++ { - - // we start by reading compressed point size, if metadata tells us it is uncompressed, we read more. - read, err = io.ReadFull(dec.r, buf[:SizeOfG2AffineCompressed]) - dec.n += int64(read) - if err != nil { - return - } - nbBytes := SizeOfG2AffineCompressed - - // 111, 011, 001 --> invalid mask - if isMaskInvalid(buf[0]) { - err = ErrInvalidEncoding - return - } - - // most significant byte contains metadata - if !isCompressed(buf[0]) { - nbBytes = SizeOfG2AffineUncompressed - // we read more. - read, err = io.ReadFull(dec.r, buf[SizeOfG2AffineCompressed:SizeOfG2AffineUncompressed]) - dec.n += int64(read) - if err != nil { - return - } - _, err = (*t)[i].setBytes(buf[:nbBytes], false) - if err != nil { - return - } - } else { - var r bool - if r, err = (*t)[i].unsafeSetCompressedBytes(buf[:nbBytes]); err != nil { - return - } - compressed[i] = !r - } - } - var nbErrs uint64 - parallel.Execute(len(compressed), func(start, end int) { - for i := start; i < end; i++ { - if compressed[i] { - if err := (*t)[i].unsafeComputeY(dec.subGroupCheck); err != nil { - atomic.AddUint64(&nbErrs, 1) - } - } else if dec.subGroupCheck { - if !(*t)[i].IsInSubGroup() { - atomic.AddUint64(&nbErrs, 1) - } - } - } - }) - if nbErrs != 0 { - return errors.New("point decompression failed") - } - - return nil - default: - n := binary.Size(t) - if n == -1 { - return errors.New("bw6-756 encoder: unsupported type") - } - err = binary.Read(dec.r, binary.BigEndian, t) - if err == nil { - dec.n += int64(n) - } - return - } -} - -// BytesRead return total bytes read from reader -func (dec *Decoder) BytesRead() int64 { - return dec.n -} - -func (dec *Decoder) readUint32() (r uint32, err error) { - var read int - var buf [4]byte - read, err = io.ReadFull(dec.r, buf[:4]) - dec.n += int64(read) - if err != nil { - return - } - r = binary.BigEndian.Uint32(buf[:4]) - return -} - -func (dec *Decoder) readUint64() (r uint64, err error) { - var read int - var buf [8]byte - read, err = io.ReadFull(dec.r, buf[:]) - dec.n += int64(read) - if err != nil { - return - } - r = binary.BigEndian.Uint64(buf[:]) - return -} - -// isMaskInvalid returns true if the mask is invalid -func isMaskInvalid(msb byte) bool { - mData := msb & mMask - return ((mData == (0b111 << 5)) || (mData == (0b011 << 5)) || (mData == (0b001 << 5))) -} - -func isCompressed(msb byte) bool { - mData := msb & mMask - return !((mData == mUncompressed) || (mData == mUncompressedInfinity)) -} - -// NewEncoder returns a binary encoder supporting curve bw6-756 objects -func NewEncoder(w io.Writer, options ...func(*Encoder)) *Encoder { - // default settings - enc := &Encoder{ - w: w, - n: 0, - raw: false, - } - - // handle options - for _, option := range options { - option(enc) - } - - return enc -} - -// Encode writes the binary encoding of v to the stream -// type must be uint64, *fr.Element, *fp.Element, *G1Affine, *G2Affine, []G1Affine or []G2Affine -func (enc *Encoder) Encode(v interface{}) (err error) { - if enc.raw { - return enc.encodeRaw(v) - } - return enc.encode(v) -} - -// BytesWritten return total bytes written on writer -func (enc *Encoder) BytesWritten() int64 { - return enc.n -} - -// RawEncoding returns an option to use in NewEncoder(...) which sets raw encoding mode to true -// points will not be compressed using this option -func RawEncoding() func(*Encoder) { - return func(enc *Encoder) { - enc.raw = true - } -} - -// NoSubgroupChecks returns an option to use in NewDecoder(...) which disable subgroup checks on the points -// the decoder will read. Use with caution, as crafted points from an untrusted source can lead to crypto-attacks. -func NoSubgroupChecks() func(*Decoder) { - return func(dec *Decoder) { - dec.subGroupCheck = false - } -} - -// isZeroed checks that the provided bytes are at 0 -func isZeroed(firstByte byte, buf []byte) bool { - if firstByte != 0 { - return false - } - for _, b := range buf { - if b != 0 { - return false - } - } - return true -} - -func (enc *Encoder) encode(v interface{}) (err error) { - rv := reflect.ValueOf(v) - if v == nil || (rv.Kind() == reflect.Ptr && rv.IsNil()) { - return errors.New(" encoder: can't encode ") - } - - // implementation note: code is a bit verbose (abusing code generation), but minimize allocations on the heap - - var written64 int64 - if vw, ok := v.(io.WriterTo); ok { - written64, err = vw.WriteTo(enc.w) - enc.n += written64 - return - } - - var written int - - switch t := v.(type) { - case []uint64: - return enc.writeUint64Slice(t) - case [][]uint64: - return enc.writeUint64SliceSlice(t) - case *fr.Element: - buf := t.Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case *fp.Element: - buf := t.Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case *G1Affine: - buf := t.Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case *G2Affine: - buf := t.Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case fr.Vector: - written64, err = t.WriteTo(enc.w) - enc.n += written64 - return - case fp.Vector: - written64, err = t.WriteTo(enc.w) - enc.n += written64 - return - case []fr.Element: - written64, err = (*fr.Vector)(&t).WriteTo(enc.w) - enc.n += written64 - return - case []fp.Element: - written64, err = (*fp.Vector)(&t).WriteTo(enc.w) - enc.n += written64 - return - case [][]fr.Element: - // write slice length - if err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))); err != nil { - return - } - enc.n += 4 - for i := range t { - written64, err = (*fr.Vector)(&t[i]).WriteTo(enc.w) - enc.n += written64 - } - return - case []G1Affine: - // write slice length - err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))) - if err != nil { - return - } - enc.n += 4 - - var buf [SizeOfG1AffineCompressed]byte - - for i := 0; i < len(t); i++ { - buf = t[i].Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - if err != nil { - return - } - } - return nil - case []G2Affine: - // write slice length - err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))) - if err != nil { - return - } - enc.n += 4 - - var buf [SizeOfG2AffineCompressed]byte - - for i := 0; i < len(t); i++ { - buf = t[i].Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - if err != nil { - return - } - } - return nil - default: - n := binary.Size(t) - if n == -1 { - return errors.New(" encoder: unsupported type") - } - err = binary.Write(enc.w, binary.BigEndian, t) - enc.n += int64(n) - return - } -} - -func (enc *Encoder) encodeRaw(v interface{}) (err error) { - rv := reflect.ValueOf(v) - if v == nil || (rv.Kind() == reflect.Ptr && rv.IsNil()) { - return errors.New(" encoder: can't encode ") - } - - // implementation note: code is a bit verbose (abusing code generation), but minimize allocations on the heap - - var written64 int64 - if vw, ok := v.(io.WriterTo); ok { - written64, err = vw.WriteTo(enc.w) - enc.n += written64 - return - } - - var written int - - switch t := v.(type) { - case []uint64: - return enc.writeUint64Slice(t) - case [][]uint64: - return enc.writeUint64SliceSlice(t) - case *fr.Element: - buf := t.Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case *fp.Element: - buf := t.Bytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case *G1Affine: - buf := t.RawBytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case *G2Affine: - buf := t.RawBytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - return - case fr.Vector: - written64, err = t.WriteTo(enc.w) - enc.n += written64 - return - case fp.Vector: - written64, err = t.WriteTo(enc.w) - enc.n += written64 - return - case []fr.Element: - written64, err = (*fr.Vector)(&t).WriteTo(enc.w) - enc.n += written64 - return - case []fp.Element: - written64, err = (*fp.Vector)(&t).WriteTo(enc.w) - enc.n += written64 - return - case [][]fr.Element: - // write slice length - if err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))); err != nil { - return - } - enc.n += 4 - for i := range t { - written64, err = (*fr.Vector)(&t[i]).WriteTo(enc.w) - enc.n += written64 - } - return - case []G1Affine: - // write slice length - err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))) - if err != nil { - return - } - enc.n += 4 - - var buf [SizeOfG1AffineUncompressed]byte - - for i := 0; i < len(t); i++ { - buf = t[i].RawBytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - if err != nil { - return - } - } - return nil - case []G2Affine: - // write slice length - err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))) - if err != nil { - return - } - enc.n += 4 - - var buf [SizeOfG2AffineUncompressed]byte - - for i := 0; i < len(t); i++ { - buf = t[i].RawBytes() - written, err = enc.w.Write(buf[:]) - enc.n += int64(written) - if err != nil { - return - } - } - return nil - default: - n := binary.Size(t) - if n == -1 { - return errors.New(" encoder: unsupported type") - } - err = binary.Write(enc.w, binary.BigEndian, t) - enc.n += int64(n) - return - } -} - -func (enc *Encoder) writeUint64Slice(t []uint64) (err error) { - if err = enc.writeUint32(uint32(len(t))); err != nil { - return - } - for i := range t { - if err = enc.writeUint64(t[i]); err != nil { - return - } - } - return nil -} - -func (enc *Encoder) writeUint64SliceSlice(t [][]uint64) (err error) { - if err = enc.writeUint32(uint32(len(t))); err != nil { - return - } - for i := range t { - if err = enc.writeUint32(uint32(len(t[i]))); err != nil { - return - } - for j := range t[i] { - if err = enc.writeUint64(t[i][j]); err != nil { - return - } - } - } - return nil -} - -func (enc *Encoder) writeUint64(a uint64) error { - var buff [64 / 8]byte - binary.BigEndian.PutUint64(buff[:], a) - written, err := enc.w.Write(buff[:]) - enc.n += int64(written) - return err -} - -func (enc *Encoder) writeUint32(a uint32) error { - var buff [32 / 8]byte - binary.BigEndian.PutUint32(buff[:], a) - written, err := enc.w.Write(buff[:]) - enc.n += int64(written) - return err -} - -// SizeOfG1AffineCompressed represents the size in bytes that a G1Affine need in binary form, compressed -const SizeOfG1AffineCompressed = 96 - -// SizeOfG1AffineUncompressed represents the size in bytes that a G1Affine need in binary form, uncompressed -const SizeOfG1AffineUncompressed = SizeOfG1AffineCompressed * 2 - -// Marshal converts p to a byte slice (without point compression) -func (p *G1Affine) Marshal() []byte { - b := p.RawBytes() - return b[:] -} - -// Unmarshal is an alias to SetBytes() -func (p *G1Affine) Unmarshal(buf []byte) error { - _, err := p.SetBytes(buf) - return err -} - -// Bytes returns binary representation of p -// will store X coordinate in regular form and a parity bit -// we follow the BLS12-381 style encoding as specified in ZCash and now IETF -// -// The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form. -// -// The second-most significant bit indicates that the point is at infinity. If this bit is set, the remaining bits of the group element's encoding should be set to zero. -// -// The third-most significant bit is set if (and only if) this point is in compressed form and it is not the point at infinity and its y-coordinate is the lexicographically largest of the two associated with the encoded x-coordinate. -func (p *G1Affine) Bytes() (res [SizeOfG1AffineCompressed]byte) { - - // check if p is infinity point - if p.X.IsZero() && p.Y.IsZero() { - res[0] = mCompressedInfinity - return - } - - msbMask := mCompressedSmallest - // compressed, we need to know if Y is lexicographically bigger than -Y - // if p.Y ">" -p.Y - if p.Y.LexicographicallyLargest() { - msbMask = mCompressedLargest - } - - // we store X and mask the most significant word with our metadata mask - fp.BigEndian.PutElement((*[fp.Bytes]byte)(res[0:0+fp.Bytes]), p.X) - - res[0] |= msbMask - - return -} - -// RawBytes returns binary representation of p (stores X and Y coordinate) -// see Bytes() for a compressed representation -func (p *G1Affine) RawBytes() (res [SizeOfG1AffineUncompressed]byte) { - - // check if p is infinity point - if p.X.IsZero() && p.Y.IsZero() { - - res[0] = mUncompressedInfinity - - return - } - - // not compressed - // we store the Y coordinate - fp.BigEndian.PutElement((*[fp.Bytes]byte)(res[96:96+fp.Bytes]), p.Y) - - // we store X and mask the most significant word with our metadata mask - fp.BigEndian.PutElement((*[fp.Bytes]byte)(res[0:0+fp.Bytes]), p.X) - - res[0] |= mUncompressed - - return -} - -// SetBytes sets p from binary representation in buf and returns number of consumed bytes -// -// bytes in buf must match either RawBytes() or Bytes() output -// -// if buf is too short io.ErrShortBuffer is returned -// -// if buf contains compressed representation (output from Bytes()) and we're unable to compute -// the Y coordinate (i.e the square root doesn't exist) this function returns an error -// -// this check if the resulting point is on the curve and in the correct subgroup -func (p *G1Affine) SetBytes(buf []byte) (int, error) { - return p.setBytes(buf, true) -} - -func (p *G1Affine) setBytes(buf []byte, subGroupCheck bool) (int, error) { - if len(buf) < SizeOfG1AffineCompressed { - return 0, io.ErrShortBuffer - } - - // most significant byte - mData := buf[0] & mMask - - // 111, 011, 001 --> invalid mask - if isMaskInvalid(mData) { - return 0, ErrInvalidEncoding - } - - // check buffer size - if (mData == mUncompressed) || (mData == mUncompressedInfinity) { - if len(buf) < SizeOfG1AffineUncompressed { - return 0, io.ErrShortBuffer - } - } - - // infinity encoded, we still check that the buffer is full of zeroes. - if mData == mCompressedInfinity { - if !isZeroed(buf[0] & ^mMask, buf[1:SizeOfG1AffineCompressed]) { - return 0, ErrInvalidInfinityEncoding - } - p.X.SetZero() - p.Y.SetZero() - return SizeOfG1AffineCompressed, nil - } - if mData == mUncompressedInfinity { - if !isZeroed(buf[0] & ^mMask, buf[1:SizeOfG1AffineUncompressed]) { - return 0, ErrInvalidInfinityEncoding - } - p.X.SetZero() - p.Y.SetZero() - return SizeOfG1AffineUncompressed, nil - } - - // uncompressed point - if mData == mUncompressed { - // read X and Y coordinates - if err := p.X.SetBytesCanonical(buf[:fp.Bytes]); err != nil { - return 0, err - } - if err := p.Y.SetBytesCanonical(buf[fp.Bytes : fp.Bytes*2]); err != nil { - return 0, err - } - - // subgroup check - if subGroupCheck && !p.IsInSubGroup() { - return 0, errors.New("invalid point: subgroup check failed") - } - - return SizeOfG1AffineUncompressed, nil - } - - // we have a compressed coordinate - // we need to - // 1. copy the buffer (to keep this method thread safe) - // 2. we need to solve the curve equation to compute Y - - var bufX [fp.Bytes]byte - copy(bufX[:fp.Bytes], buf[:fp.Bytes]) - bufX[0] &= ^mMask - - // read X coordinate - if err := p.X.SetBytesCanonical(bufX[:fp.Bytes]); err != nil { - return 0, err - } - - var YSquared, Y fp.Element - - YSquared.Square(&p.X).Mul(&YSquared, &p.X) - YSquared.Add(&YSquared, &bCurveCoeff) - if Y.Sqrt(&YSquared) == nil { - return 0, errors.New("invalid compressed coordinate: square root doesn't exist") - } - - if Y.LexicographicallyLargest() { - // Y ">" -Y - if mData == mCompressedSmallest { - Y.Neg(&Y) - } - } else { - // Y "<=" -Y - if mData == mCompressedLargest { - Y.Neg(&Y) - } - } - - p.Y = Y - - // subgroup check - if subGroupCheck && !p.IsInSubGroup() { - return 0, errors.New("invalid point: subgroup check failed") - } - - return SizeOfG1AffineCompressed, nil -} - -// unsafeComputeY called by Decoder when processing slices of compressed point in parallel (step 2) -// it computes the Y coordinate from the already set X coordinate and is compute intensive -func (p *G1Affine) unsafeComputeY(subGroupCheck bool) error { - // stored in unsafeSetCompressedBytes - - mData := byte(p.Y[0]) - - // we have a compressed coordinate, we need to solve the curve equation to compute Y - var YSquared, Y fp.Element - - YSquared.Square(&p.X).Mul(&YSquared, &p.X) - YSquared.Add(&YSquared, &bCurveCoeff) - if Y.Sqrt(&YSquared) == nil { - return errors.New("invalid compressed coordinate: square root doesn't exist") - } - - if Y.LexicographicallyLargest() { - // Y ">" -Y - if mData == mCompressedSmallest { - Y.Neg(&Y) - } - } else { - // Y "<=" -Y - if mData == mCompressedLargest { - Y.Neg(&Y) - } - } - - p.Y = Y - - // subgroup check - if subGroupCheck && !p.IsInSubGroup() { - return errors.New("invalid point: subgroup check failed") - } - - return nil -} - -// unsafeSetCompressedBytes is called by Decoder when processing slices of compressed point in parallel (step 1) -// assumes buf[:8] mask is set to compressed -// returns true if point is infinity and need no further processing -// it sets X coordinate and uses Y for scratch space to store decompression metadata -func (p *G1Affine) unsafeSetCompressedBytes(buf []byte) (isInfinity bool, err error) { - - // read the most significant byte - mData := buf[0] & mMask - - if mData == mCompressedInfinity { - isInfinity = true - if !isZeroed(buf[0] & ^mMask, buf[1:SizeOfG1AffineCompressed]) { - return isInfinity, ErrInvalidInfinityEncoding - } - p.X.SetZero() - p.Y.SetZero() - return isInfinity, nil - } - - // we need to copy the input buffer (to keep this method thread safe) - var bufX [fp.Bytes]byte - copy(bufX[:fp.Bytes], buf[:fp.Bytes]) - bufX[0] &= ^mMask - - // read X coordinate - if err := p.X.SetBytesCanonical(bufX[:fp.Bytes]); err != nil { - return false, err - } - // store mData in p.Y[0] - p.Y[0] = uint64(mData) - - // recomputing Y will be done asynchronously - return isInfinity, nil -} - -// SizeOfG2AffineCompressed represents the size in bytes that a G2Affine need in binary form, compressed -const SizeOfG2AffineCompressed = 96 - -// SizeOfG2AffineUncompressed represents the size in bytes that a G2Affine need in binary form, uncompressed -const SizeOfG2AffineUncompressed = SizeOfG2AffineCompressed * 2 - -// Marshal converts p to a byte slice (without point compression) -func (p *G2Affine) Marshal() []byte { - b := p.RawBytes() - return b[:] -} - -// Unmarshal is an alias to SetBytes() -func (p *G2Affine) Unmarshal(buf []byte) error { - _, err := p.SetBytes(buf) - return err -} - -// Bytes returns binary representation of p -// will store X coordinate in regular form and a parity bit -// we follow the BLS12-381 style encoding as specified in ZCash and now IETF -// -// The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form. -// -// The second-most significant bit indicates that the point is at infinity. If this bit is set, the remaining bits of the group element's encoding should be set to zero. -// -// The third-most significant bit is set if (and only if) this point is in compressed form and it is not the point at infinity and its y-coordinate is the lexicographically largest of the two associated with the encoded x-coordinate. -func (p *G2Affine) Bytes() (res [SizeOfG2AffineCompressed]byte) { - - // check if p is infinity point - if p.X.IsZero() && p.Y.IsZero() { - res[0] = mCompressedInfinity - return - } - - msbMask := mCompressedSmallest - // compressed, we need to know if Y is lexicographically bigger than -Y - // if p.Y ">" -p.Y - if p.Y.LexicographicallyLargest() { - msbMask = mCompressedLargest - } - - // we store X and mask the most significant word with our metadata mask - fp.BigEndian.PutElement((*[fp.Bytes]byte)(res[0:0+fp.Bytes]), p.X) - - res[0] |= msbMask - - return -} - -// RawBytes returns binary representation of p (stores X and Y coordinate) -// see Bytes() for a compressed representation -func (p *G2Affine) RawBytes() (res [SizeOfG2AffineUncompressed]byte) { - - // check if p is infinity point - if p.X.IsZero() && p.Y.IsZero() { - - res[0] = mUncompressedInfinity - - return - } - - // not compressed - // we store the Y coordinate - fp.BigEndian.PutElement((*[fp.Bytes]byte)(res[96:96+fp.Bytes]), p.Y) - - // we store X and mask the most significant word with our metadata mask - fp.BigEndian.PutElement((*[fp.Bytes]byte)(res[0:0+fp.Bytes]), p.X) - - res[0] |= mUncompressed - - return -} - -// SetBytes sets p from binary representation in buf and returns number of consumed bytes -// -// bytes in buf must match either RawBytes() or Bytes() output -// -// if buf is too short io.ErrShortBuffer is returned -// -// if buf contains compressed representation (output from Bytes()) and we're unable to compute -// the Y coordinate (i.e the square root doesn't exist) this function returns an error -// -// this check if the resulting point is on the curve and in the correct subgroup -func (p *G2Affine) SetBytes(buf []byte) (int, error) { - return p.setBytes(buf, true) -} - -func (p *G2Affine) setBytes(buf []byte, subGroupCheck bool) (int, error) { - if len(buf) < SizeOfG2AffineCompressed { - return 0, io.ErrShortBuffer - } - - // most significant byte - mData := buf[0] & mMask - - // 111, 011, 001 --> invalid mask - if isMaskInvalid(mData) { - return 0, ErrInvalidEncoding - } - - // check buffer size - if (mData == mUncompressed) || (mData == mUncompressedInfinity) { - if len(buf) < SizeOfG2AffineUncompressed { - return 0, io.ErrShortBuffer - } - } - - // infinity encoded, we still check that the buffer is full of zeroes. - if mData == mCompressedInfinity { - if !isZeroed(buf[0] & ^mMask, buf[1:SizeOfG2AffineCompressed]) { - return 0, ErrInvalidInfinityEncoding - } - p.X.SetZero() - p.Y.SetZero() - return SizeOfG2AffineCompressed, nil - } - if mData == mUncompressedInfinity { - if !isZeroed(buf[0] & ^mMask, buf[1:SizeOfG2AffineUncompressed]) { - return 0, ErrInvalidInfinityEncoding - } - p.X.SetZero() - p.Y.SetZero() - return SizeOfG2AffineUncompressed, nil - } - - // uncompressed point - if mData == mUncompressed { - // read X and Y coordinates - if err := p.X.SetBytesCanonical(buf[:fp.Bytes]); err != nil { - return 0, err - } - if err := p.Y.SetBytesCanonical(buf[fp.Bytes : fp.Bytes*2]); err != nil { - return 0, err - } - - // subgroup check - if subGroupCheck && !p.IsInSubGroup() { - return 0, errors.New("invalid point: subgroup check failed") - } - - return SizeOfG2AffineUncompressed, nil - } - - // we have a compressed coordinate - // we need to - // 1. copy the buffer (to keep this method thread safe) - // 2. we need to solve the curve equation to compute Y - - var bufX [fp.Bytes]byte - copy(bufX[:fp.Bytes], buf[:fp.Bytes]) - bufX[0] &= ^mMask - - // read X coordinate - if err := p.X.SetBytesCanonical(bufX[:fp.Bytes]); err != nil { - return 0, err - } - - var YSquared, Y fp.Element - - YSquared.Square(&p.X).Mul(&YSquared, &p.X) - YSquared.Add(&YSquared, &bTwistCurveCoeff) - if Y.Sqrt(&YSquared) == nil { - return 0, errors.New("invalid compressed coordinate: square root doesn't exist") - } - - if Y.LexicographicallyLargest() { - // Y ">" -Y - if mData == mCompressedSmallest { - Y.Neg(&Y) - } - } else { - // Y "<=" -Y - if mData == mCompressedLargest { - Y.Neg(&Y) - } - } - - p.Y = Y - - // subgroup check - if subGroupCheck && !p.IsInSubGroup() { - return 0, errors.New("invalid point: subgroup check failed") - } - - return SizeOfG2AffineCompressed, nil -} - -// unsafeComputeY called by Decoder when processing slices of compressed point in parallel (step 2) -// it computes the Y coordinate from the already set X coordinate and is compute intensive -func (p *G2Affine) unsafeComputeY(subGroupCheck bool) error { - // stored in unsafeSetCompressedBytes - - mData := byte(p.Y[0]) - - // we have a compressed coordinate, we need to solve the curve equation to compute Y - var YSquared, Y fp.Element - - YSquared.Square(&p.X).Mul(&YSquared, &p.X) - YSquared.Add(&YSquared, &bTwistCurveCoeff) - if Y.Sqrt(&YSquared) == nil { - return errors.New("invalid compressed coordinate: square root doesn't exist") - } - - if Y.LexicographicallyLargest() { - // Y ">" -Y - if mData == mCompressedSmallest { - Y.Neg(&Y) - } - } else { - // Y "<=" -Y - if mData == mCompressedLargest { - Y.Neg(&Y) - } - } - - p.Y = Y - - // subgroup check - if subGroupCheck && !p.IsInSubGroup() { - return errors.New("invalid point: subgroup check failed") - } - - return nil -} - -// unsafeSetCompressedBytes is called by Decoder when processing slices of compressed point in parallel (step 1) -// assumes buf[:8] mask is set to compressed -// returns true if point is infinity and need no further processing -// it sets X coordinate and uses Y for scratch space to store decompression metadata -func (p *G2Affine) unsafeSetCompressedBytes(buf []byte) (isInfinity bool, err error) { - - // read the most significant byte - mData := buf[0] & mMask - - if mData == mCompressedInfinity { - isInfinity = true - if !isZeroed(buf[0] & ^mMask, buf[1:SizeOfG2AffineCompressed]) { - return isInfinity, ErrInvalidInfinityEncoding - } - p.X.SetZero() - p.Y.SetZero() - return isInfinity, nil - } - - // we need to copy the input buffer (to keep this method thread safe) - var bufX [fp.Bytes]byte - copy(bufX[:fp.Bytes], buf[:fp.Bytes]) - bufX[0] &= ^mMask - - // read X coordinate - if err := p.X.SetBytesCanonical(bufX[:fp.Bytes]); err != nil { - return false, err - } - // store mData in p.Y[0] - p.Y[0] = uint64(mData) - - // recomputing Y will be done asynchronously - return isInfinity, nil -} diff --git a/ecc/bw6-756/marshal_test.go b/ecc/bw6-756/marshal_test.go deleted file mode 100644 index 7478bddc4f..0000000000 --- a/ecc/bw6-756/marshal_test.go +++ /dev/null @@ -1,520 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bw6756 - -import ( - "bytes" - crand "crypto/rand" - "io" - "math/big" - "math/rand/v2" - "reflect" - "testing" - - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/internal/fptower" -) - -const ( - nbFuzzShort = 10 - nbFuzz = 100 -) - -func TestEncoder(t *testing.T) { - t.Parallel() - // TODO need proper fuzz testing here - - var inA uint64 - var inB fr.Element - var inC fp.Element - var inD G1Affine - var inE G1Affine - var inF G2Affine - var inG []G1Affine - var inH []G2Affine - var inI []fp.Element - var inJ []fr.Element - var inK fr.Vector - var inL [][]fr.Element - var inM [][]uint64 - - // set values of inputs - inA = rand.Uint64() //#nosec G404 weak rng is fine here - inB.SetRandom() - inC.SetRandom() - inD.ScalarMultiplication(&g1GenAff, new(big.Int).SetUint64(rand.Uint64())) //#nosec G404 weak rng is fine here - // inE --> infinity - inF.ScalarMultiplication(&g2GenAff, new(big.Int).SetUint64(rand.Uint64())) //#nosec G404 weak rng is fine here - inG = make([]G1Affine, 2) - inH = make([]G2Affine, 0) - inG[1] = inD - inI = make([]fp.Element, 3) - inI[2] = inD.X - inJ = make([]fr.Element, 0) - inK = make(fr.Vector, 42) - inK[41].SetUint64(42) - inL = [][]fr.Element{inJ, inK} - inM = [][]uint64{{1, 2}, {4}, {}} - - // encode them, compressed and raw - var buf, bufRaw bytes.Buffer - enc := NewEncoder(&buf) - encRaw := NewEncoder(&bufRaw, RawEncoding()) - toEncode := []interface{}{inA, &inB, &inC, &inD, &inE, &inF, inG, inH, inI, inJ, inK, inL, inM} - for _, v := range toEncode { - if err := enc.Encode(v); err != nil { - t.Fatal(err) - } - if err := encRaw.Encode(v); err != nil { - t.Fatal(err) - } - } - - testDecode := func(t *testing.T, r io.Reader, n int64) { - dec := NewDecoder(r) - var outA uint64 - var outB fr.Element - var outC fp.Element - var outD G1Affine - var outE G1Affine - outE.X.SetOne() - outE.Y.SetUint64(42) - var outF G2Affine - var outG []G1Affine - var outH []G2Affine - var outI []fp.Element - var outJ []fr.Element - var outK fr.Vector - var outL [][]fr.Element - var outM [][]uint64 - - toDecode := []interface{}{&outA, &outB, &outC, &outD, &outE, &outF, &outG, &outH, &outI, &outJ, &outK, &outL, &outM} - for _, v := range toDecode { - if err := dec.Decode(v); err != nil { - t.Fatal(err) - } - } - - // compare values - if inA != outA { - t.Fatal("didn't encode/decode uint64 value properly") - } - - if !inB.Equal(&outB) || !inC.Equal(&outC) { - t.Fatal("decode(encode(Element) failed") - } - if !inD.Equal(&outD) || !inE.Equal(&outE) { - t.Fatal("decode(encode(G1Affine) failed") - } - if !inF.Equal(&outF) { - t.Fatal("decode(encode(G2Affine) failed") - } - if (len(inG) != len(outG)) || (len(inH) != len(outH)) { - t.Fatal("decode(encode(slice(points))) failed") - } - for i := 0; i < len(inG); i++ { - if !inG[i].Equal(&outG[i]) { - t.Fatal("decode(encode(slice(points))) failed") - } - } - if (len(inI) != len(outI)) || (len(inJ) != len(outJ)) { - t.Fatal("decode(encode(slice(elements))) failed") - } - for i := 0; i < len(inI); i++ { - if !inI[i].Equal(&outI[i]) { - t.Fatal("decode(encode(slice(elements))) failed") - } - } - if !reflect.DeepEqual(inK, outK) { - t.Fatal("decode(encode(vector)) failed") - } - if !reflect.DeepEqual(inL, outL) { - t.Fatal("decode(encode(slice²(elements))) failed") - } - if !reflect.DeepEqual(inM, outM) { - t.Fatal("decode(encode(slice²(uint64))) failed") - } - if n != dec.BytesRead() { - t.Fatal("bytes read don't match bytes written") - } - } - - // decode them - testDecode(t, &buf, enc.BytesWritten()) - testDecode(t, &bufRaw, encRaw.BytesWritten()) - -} - -func TestIsCompressed(t *testing.T) { - t.Parallel() - var g1Inf, g1 G1Affine - var g2Inf, g2 G2Affine - - g1 = g1GenAff - g2 = g2GenAff - - { - b := g1Inf.Bytes() - if !isCompressed(b[0]) { - t.Fatal("g1Inf.Bytes() should be compressed") - } - } - - { - b := g1Inf.RawBytes() - if isCompressed(b[0]) { - t.Fatal("g1Inf.RawBytes() should be uncompressed") - } - } - - { - b := g1.Bytes() - if !isCompressed(b[0]) { - t.Fatal("g1.Bytes() should be compressed") - } - } - - { - b := g1.RawBytes() - if isCompressed(b[0]) { - t.Fatal("g1.RawBytes() should be uncompressed") - } - } - - { - b := g2Inf.Bytes() - if !isCompressed(b[0]) { - t.Fatal("g2Inf.Bytes() should be compressed") - } - } - - { - b := g2Inf.RawBytes() - if isCompressed(b[0]) { - t.Fatal("g2Inf.RawBytes() should be uncompressed") - } - } - - { - b := g2.Bytes() - if !isCompressed(b[0]) { - t.Fatal("g2.Bytes() should be compressed") - } - } - - { - b := g2.RawBytes() - if isCompressed(b[0]) { - t.Fatal("g2.RawBytes() should be uncompressed") - } - } - -} - -func TestG1AffineInvalidBitMask(t *testing.T) { - t.Parallel() - var buf [SizeOfG1AffineCompressed]byte - crand.Read(buf[:]) - - var p G1Affine - buf[0] = 0b111 << 5 - if _, err := p.SetBytes(buf[:]); err != ErrInvalidEncoding { - t.Fatal("should error on invalid bit mask") - } - buf[0] = 0b011 << 5 - if _, err := p.SetBytes(buf[:]); err != ErrInvalidEncoding { - t.Fatal("should error on invalid bit mask") - } - buf[0] = 0b001 << 5 - if _, err := p.SetBytes(buf[:]); err != ErrInvalidEncoding { - t.Fatal("should error on invalid bit mask") - } -} - -func TestG1AffineSerialization(t *testing.T) { - t.Parallel() - // test round trip serialization of infinity - { - // compressed - { - var p1, p2 G1Affine - p2.X.SetRandom() - p2.Y.SetRandom() - buf := p1.Bytes() - n, err := p2.SetBytes(buf[:]) - if err != nil { - t.Fatal(err) - } - if n != SizeOfG1AffineCompressed { - t.Fatal("invalid number of bytes consumed in buffer") - } - if !(p2.X.IsZero() && p2.Y.IsZero()) { - t.Fatal("deserialization of uncompressed infinity point is not infinity") - } - } - - // uncompressed - { - var p1, p2 G1Affine - p2.X.SetRandom() - p2.Y.SetRandom() - buf := p1.RawBytes() - n, err := p2.SetBytes(buf[:]) - if err != nil { - t.Fatal(err) - } - if n != SizeOfG1AffineUncompressed { - t.Fatal("invalid number of bytes consumed in buffer") - } - if !(p2.X.IsZero() && p2.Y.IsZero()) { - t.Fatal("deserialization of uncompressed infinity point is not infinity") - } - } - } - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[G1] Affine SetBytes(RawBytes) should stay the same", prop.ForAll( - func(a fp.Element) bool { - var start, end G1Affine - var ab big.Int - a.BigInt(&ab) - start.ScalarMultiplication(&g1GenAff, &ab) - - buf := start.RawBytes() - n, err := end.SetBytes(buf[:]) - if err != nil { - return false - } - if n != SizeOfG1AffineUncompressed { - return false - } - return start.X.Equal(&end.X) && start.Y.Equal(&end.Y) - }, - GenFp(), - )) - - properties.Property("[G1] Affine SetBytes(Bytes()) should stay the same", prop.ForAll( - func(a fp.Element) bool { - var start, end G1Affine - var ab big.Int - a.BigInt(&ab) - start.ScalarMultiplication(&g1GenAff, &ab) - - buf := start.Bytes() - n, err := end.SetBytes(buf[:]) - if err != nil { - return false - } - if n != SizeOfG1AffineCompressed { - return false - } - return start.X.Equal(&end.X) && start.Y.Equal(&end.Y) - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestG2AffineInvalidBitMask(t *testing.T) { - t.Parallel() - var buf [SizeOfG2AffineCompressed]byte - crand.Read(buf[:]) - - var p G2Affine - buf[0] = 0b111 << 5 - if _, err := p.SetBytes(buf[:]); err != ErrInvalidEncoding { - t.Fatal("should error on invalid bit mask") - } - buf[0] = 0b011 << 5 - if _, err := p.SetBytes(buf[:]); err != ErrInvalidEncoding { - t.Fatal("should error on invalid bit mask") - } - buf[0] = 0b001 << 5 - if _, err := p.SetBytes(buf[:]); err != ErrInvalidEncoding { - t.Fatal("should error on invalid bit mask") - } -} - -func TestG2AffineSerialization(t *testing.T) { - t.Parallel() - // test round trip serialization of infinity - { - // compressed - { - var p1, p2 G2Affine - p2.X.SetRandom() - p2.Y.SetRandom() - buf := p1.Bytes() - n, err := p2.SetBytes(buf[:]) - if err != nil { - t.Fatal(err) - } - if n != SizeOfG2AffineCompressed { - t.Fatal("invalid number of bytes consumed in buffer") - } - if !(p2.X.IsZero() && p2.Y.IsZero()) { - t.Fatal("deserialization of uncompressed infinity point is not infinity") - } - } - - // uncompressed - { - var p1, p2 G2Affine - p2.X.SetRandom() - p2.Y.SetRandom() - buf := p1.RawBytes() - n, err := p2.SetBytes(buf[:]) - if err != nil { - t.Fatal(err) - } - if n != SizeOfG2AffineUncompressed { - t.Fatal("invalid number of bytes consumed in buffer") - } - if !(p2.X.IsZero() && p2.Y.IsZero()) { - t.Fatal("deserialization of uncompressed infinity point is not infinity") - } - } - } - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - properties.Property("[G2] Affine SetBytes(RawBytes) should stay the same", prop.ForAll( - func(a fp.Element) bool { - var start, end G2Affine - var ab big.Int - a.BigInt(&ab) - start.ScalarMultiplication(&g2GenAff, &ab) - - buf := start.RawBytes() - n, err := end.SetBytes(buf[:]) - if err != nil { - return false - } - if n != SizeOfG2AffineUncompressed { - return false - } - return start.X.Equal(&end.X) && start.Y.Equal(&end.Y) - }, - GenFp(), - )) - - properties.Property("[G2] Affine SetBytes(Bytes()) should stay the same", prop.ForAll( - func(a fp.Element) bool { - var start, end G2Affine - var ab big.Int - a.BigInt(&ab) - start.ScalarMultiplication(&g2GenAff, &ab) - - buf := start.Bytes() - n, err := end.SetBytes(buf[:]) - if err != nil { - return false - } - if n != SizeOfG2AffineCompressed { - return false - } - return start.X.Equal(&end.X) && start.Y.Equal(&end.Y) - }, - GenFp(), - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -// define Gopters generators - -// GenFr generates an Fr element -func GenFr() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var elmt fr.Element - - if _, err := elmt.SetRandom(); err != nil { - panic(err) - } - - return gopter.NewGenResult(elmt, gopter.NoShrinker) - } -} - -// GenFp generates an Fp element -func GenFp() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var elmt fp.Element - - if _, err := elmt.SetRandom(); err != nil { - panic(err) - } - - return gopter.NewGenResult(elmt, gopter.NoShrinker) - } -} - -// GenE3 generates an E3 elmt -func GenE3() gopter.Gen { - return gopter.CombineGens( - GenFp(), - GenFp(), - GenFp(), - ).Map(func(values []interface{}) fptower.E3 { - return fptower.E3{A0: values[0].(fp.Element), A1: values[1].(fp.Element), A2: values[2].(fp.Element)} - }) -} - -// E6 generates an E6 elmt -func GenE6() gopter.Gen { - return gopter.CombineGens( - GenE3(), - GenE3(), - ).Map(func(values []interface{}) fptower.E6 { - return fptower.E6{B0: values[0].(fptower.E3), B1: values[1].(fptower.E3)} - }) -} - -// GenBigInt generates a big.Int -func GenBigInt() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var s big.Int - var b [fp.Bytes]byte - _, err := crand.Read(b[:]) - if err != nil { - panic(err) - } - s.SetBytes(b[:]) - genResult := gopter.NewGenResult(s, gopter.NoShrinker) - return genResult - } -} diff --git a/ecc/bw6-756/multiexp.go b/ecc/bw6-756/multiexp.go deleted file mode 100644 index 1e44037056..0000000000 --- a/ecc/bw6-756/multiexp.go +++ /dev/null @@ -1,761 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bw6756 - -import ( - "errors" - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/internal/parallel" - "math" - "runtime" -) - -// MultiExp implements section 4 of https://eprint.iacr.org/2012/549.pdf -// -// This call return an error if len(scalars) != len(points) or if provided config is invalid. -func (p *G1Affine) MultiExp(points []G1Affine, scalars []fr.Element, config ecc.MultiExpConfig) (*G1Affine, error) { - var _p G1Jac - if _, err := _p.MultiExp(points, scalars, config); err != nil { - return nil, err - } - p.FromJacobian(&_p) - return p, nil -} - -// MultiExp implements section 4 of https://eprint.iacr.org/2012/549.pdf -// -// This call return an error if len(scalars) != len(points) or if provided config is invalid. -func (p *G1Jac) MultiExp(points []G1Affine, scalars []fr.Element, config ecc.MultiExpConfig) (*G1Jac, error) { - // TODO @gbotrel replace the ecc.MultiExpConfig by a Option pattern for maintainability. - // note: - // each of the msmCX method is the same, except for the c constant it declares - // duplicating (through template generation) these methods allows to declare the buckets on the stack - // the choice of c needs to be improved: - // there is a theoretical value that gives optimal asymptotics - // but in practice, other factors come into play, including: - // * if c doesn't divide 64, the word size, then we're bound to select bits over 2 words of our scalars, instead of 1 - // * number of CPUs - // * cache friendliness (which depends on the host, G1 or G2... ) - // --> for example, on BN254, a G1 point fits into one cache line of 64bytes, but a G2 point don't. - - // for each msmCX - // step 1 - // we compute, for each scalars over c-bit wide windows, nbChunk digits - // if the digit is larger than 2^{c-1}, then, we borrow 2^c from the next window and subtract - // 2^{c} to the current digit, making it negative. - // negative digits will be processed in the next step as adding -G into the bucket instead of G - // (computing -G is cheap, and this saves us half of the buckets) - // step 2 - // buckets are declared on the stack - // notice that we have 2^{c-1} buckets instead of 2^{c} (see step1) - // we use jacobian extended formulas here as they are faster than mixed addition - // msmProcessChunk places points into buckets base on their selector and return the weighted bucket sum in given channel - // step 3 - // reduce the buckets weighed sums into our result (msmReduceChunk) - - // ensure len(points) == len(scalars) - nbPoints := len(points) - if nbPoints != len(scalars) { - return nil, errors.New("len(points) != len(scalars)") - } - - // if nbTasks is not set, use all available CPUs - if config.NbTasks <= 0 { - config.NbTasks = runtime.NumCPU() * 2 - } else if config.NbTasks > 1024 { - return nil, errors.New("invalid config: config.NbTasks > 1024") - } - - // here, we compute the best C for nbPoints - // we split recursively until nbChunks(c) >= nbTasks, - bestC := func(nbPoints int) uint64 { - // implemented msmC methods (the c we use must be in this slice) - implementedCs := []uint64{4, 5, 8, 11, 16} - var C uint64 - // approximate cost (in group operations) - // cost = bits/c * (nbPoints + 2^{c}) - // this needs to be verified empirically. - // for example, on a MBP 2016, for G2 MultiExp > 8M points, hand picking c gives better results - min := math.MaxFloat64 - for _, c := range implementedCs { - cc := (fr.Bits + 1) * (nbPoints + (1 << c)) - cost := float64(cc) / float64(c) - if cost < min { - min = cost - C = c - } - } - return C - } - - C := bestC(nbPoints) - nbChunks := int(computeNbChunks(C)) - - // should we recursively split the msm in half? (see below) - // we want to minimize the execution time of the algorithm; - // splitting the msm will **add** operations, but if it allows to use more CPU, it might be worth it. - - // costFunction returns a metric that represent the "wall time" of the algorithm - costFunction := func(nbTasks, nbCpus, costPerTask int) int { - // cost for the reduction of all tasks (msmReduceChunk) - totalCost := nbTasks - - // cost for the computation of each task (msmProcessChunk) - for nbTasks >= nbCpus { - nbTasks -= nbCpus - totalCost += costPerTask - } - if nbTasks > 0 { - totalCost += costPerTask - } - return totalCost - } - - // costPerTask is the approximate number of group ops per task - costPerTask := func(c uint64, nbPoints int) int { return (nbPoints + int((1 << c))) } - - costPreSplit := costFunction(nbChunks, config.NbTasks, costPerTask(C, nbPoints)) - - cPostSplit := bestC(nbPoints / 2) - nbChunksPostSplit := int(computeNbChunks(cPostSplit)) - costPostSplit := costFunction(nbChunksPostSplit*2, config.NbTasks, costPerTask(cPostSplit, nbPoints/2)) - - // if the cost of the split msm is lower than the cost of the non split msm, we split - if costPostSplit < costPreSplit { - config.NbTasks = int(math.Ceil(float64(config.NbTasks) / 2.0)) - var _p G1Jac - chDone := make(chan struct{}, 1) - go func() { - _p.MultiExp(points[:nbPoints/2], scalars[:nbPoints/2], config) - close(chDone) - }() - p.MultiExp(points[nbPoints/2:], scalars[nbPoints/2:], config) - <-chDone - p.AddAssign(&_p) - return p, nil - } - - // if we don't split, we use the best C we found - _innerMsmG1(p, C, points, scalars, config) - - return p, nil -} - -func _innerMsmG1(p *G1Jac, c uint64, points []G1Affine, scalars []fr.Element, config ecc.MultiExpConfig) *G1Jac { - // partition the scalars - digits, chunkStats := partitionScalars(scalars, c, config.NbTasks) - - nbChunks := computeNbChunks(c) - - // for each chunk, spawn one go routine that'll loop through all the scalars in the - // corresponding bit-window - // note that buckets is an array allocated on the stack and this is critical for performance - - // each go routine sends its result in chChunks[i] channel - chChunks := make([]chan g1JacExtended, nbChunks) - for i := 0; i < len(chChunks); i++ { - chChunks[i] = make(chan g1JacExtended, 1) - } - - // we use a semaphore to limit the number of go routines running concurrently - // (only if nbTasks < nbCPU) - var sem chan struct{} - if config.NbTasks < runtime.NumCPU() { - // we add nbChunks because if chunk is overweight we split it in two - sem = make(chan struct{}, config.NbTasks+int(nbChunks)) - for i := 0; i < config.NbTasks; i++ { - sem <- struct{}{} - } - defer func() { - close(sem) - }() - } - - // the last chunk may be processed with a different method than the rest, as it could be smaller. - n := len(points) - for j := int(nbChunks - 1); j >= 0; j-- { - processChunk := getChunkProcessorG1(c, chunkStats[j]) - if j == int(nbChunks-1) { - processChunk = getChunkProcessorG1(lastC(c), chunkStats[j]) - } - if chunkStats[j].weight >= 115 { - // we split this in more go routines since this chunk has more work to do than the others. - // else what would happen is this go routine would finish much later than the others. - chSplit := make(chan g1JacExtended, 2) - split := n / 2 - - if sem != nil { - sem <- struct{}{} // add another token to the semaphore, since we split in two. - } - go processChunk(uint64(j), chSplit, c, points[:split], digits[j*n:(j*n)+split], sem) - go processChunk(uint64(j), chSplit, c, points[split:], digits[(j*n)+split:(j+1)*n], sem) - go func(chunkID int) { - s1 := <-chSplit - s2 := <-chSplit - close(chSplit) - s1.add(&s2) - chChunks[chunkID] <- s1 - }(j) - continue - } - go processChunk(uint64(j), chChunks[j], c, points, digits[j*n:(j+1)*n], sem) - } - - return msmReduceChunkG1Affine(p, int(c), chChunks[:]) -} - -// getChunkProcessorG1 decides, depending on c window size and statistics for the chunk -// to return the best algorithm to process the chunk. -func getChunkProcessorG1(c uint64, stat chunkStat) func(chunkID uint64, chRes chan<- g1JacExtended, c uint64, points []G1Affine, digits []uint16, sem chan struct{}) { - switch c { - - case 3: - return processChunkG1Jacobian[bucketg1JacExtendedC3] - case 4: - return processChunkG1Jacobian[bucketg1JacExtendedC4] - case 5: - return processChunkG1Jacobian[bucketg1JacExtendedC5] - case 8: - return processChunkG1Jacobian[bucketg1JacExtendedC8] - case 11: - const batchSize = 150 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG1Jacobian[bucketg1JacExtendedC11] - } - return processChunkG1BatchAffine[bucketg1JacExtendedC11, bucketG1AffineC11, bitSetC11, pG1AffineC11, ppG1AffineC11, qG1AffineC11, cG1AffineC11] - case 16: - const batchSize = 640 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG1Jacobian[bucketg1JacExtendedC16] - } - return processChunkG1BatchAffine[bucketg1JacExtendedC16, bucketG1AffineC16, bitSetC16, pG1AffineC16, ppG1AffineC16, qG1AffineC16, cG1AffineC16] - default: - // panic("will not happen c != previous values is not generated by templates") - return processChunkG1Jacobian[bucketg1JacExtendedC16] - } -} - -// msmReduceChunkG1Affine reduces the weighted sum of the buckets into the result of the multiExp -func msmReduceChunkG1Affine(p *G1Jac, c int, chChunks []chan g1JacExtended) *G1Jac { - var _p g1JacExtended - totalj := <-chChunks[len(chChunks)-1] - _p.Set(&totalj) - for j := len(chChunks) - 2; j >= 0; j-- { - for l := 0; l < c; l++ { - _p.double(&_p) - } - totalj := <-chChunks[j] - _p.add(&totalj) - } - - return p.unsafeFromJacExtended(&_p) -} - -// Fold computes the multi-exponentiation \sum_{i=0}^{len(points)-1} points[i] * -// combinationCoeff^i and stores the result in p. It returns error in case -// configuration is invalid. -func (p *G1Affine) Fold(points []G1Affine, combinationCoeff fr.Element, config ecc.MultiExpConfig) (*G1Affine, error) { - var _p G1Jac - if _, err := _p.Fold(points, combinationCoeff, config); err != nil { - return nil, err - } - p.FromJacobian(&_p) - return p, nil -} - -// Fold computes the multi-exponentiation \sum_{i=0}^{len(points)-1} points[i] * -// combinationCoeff^i and stores the result in p. It returns error in case -// configuration is invalid. -func (p *G1Jac) Fold(points []G1Affine, combinationCoeff fr.Element, config ecc.MultiExpConfig) (*G1Jac, error) { - scalars := make([]fr.Element, len(points)) - scalar := fr.NewElement(1) - for i := 0; i < len(points); i++ { - scalars[i].Set(&scalar) - scalar.Mul(&scalar, &combinationCoeff) - } - return p.MultiExp(points, scalars, config) -} - -// MultiExp implements section 4 of https://eprint.iacr.org/2012/549.pdf -// -// This call return an error if len(scalars) != len(points) or if provided config is invalid. -func (p *G2Affine) MultiExp(points []G2Affine, scalars []fr.Element, config ecc.MultiExpConfig) (*G2Affine, error) { - var _p G2Jac - if _, err := _p.MultiExp(points, scalars, config); err != nil { - return nil, err - } - p.FromJacobian(&_p) - return p, nil -} - -// MultiExp implements section 4 of https://eprint.iacr.org/2012/549.pdf -// -// This call return an error if len(scalars) != len(points) or if provided config is invalid. -func (p *G2Jac) MultiExp(points []G2Affine, scalars []fr.Element, config ecc.MultiExpConfig) (*G2Jac, error) { - // TODO @gbotrel replace the ecc.MultiExpConfig by a Option pattern for maintainability. - // note: - // each of the msmCX method is the same, except for the c constant it declares - // duplicating (through template generation) these methods allows to declare the buckets on the stack - // the choice of c needs to be improved: - // there is a theoretical value that gives optimal asymptotics - // but in practice, other factors come into play, including: - // * if c doesn't divide 64, the word size, then we're bound to select bits over 2 words of our scalars, instead of 1 - // * number of CPUs - // * cache friendliness (which depends on the host, G1 or G2... ) - // --> for example, on BN254, a G1 point fits into one cache line of 64bytes, but a G2 point don't. - - // for each msmCX - // step 1 - // we compute, for each scalars over c-bit wide windows, nbChunk digits - // if the digit is larger than 2^{c-1}, then, we borrow 2^c from the next window and subtract - // 2^{c} to the current digit, making it negative. - // negative digits will be processed in the next step as adding -G into the bucket instead of G - // (computing -G is cheap, and this saves us half of the buckets) - // step 2 - // buckets are declared on the stack - // notice that we have 2^{c-1} buckets instead of 2^{c} (see step1) - // we use jacobian extended formulas here as they are faster than mixed addition - // msmProcessChunk places points into buckets base on their selector and return the weighted bucket sum in given channel - // step 3 - // reduce the buckets weighed sums into our result (msmReduceChunk) - - // ensure len(points) == len(scalars) - nbPoints := len(points) - if nbPoints != len(scalars) { - return nil, errors.New("len(points) != len(scalars)") - } - - // if nbTasks is not set, use all available CPUs - if config.NbTasks <= 0 { - config.NbTasks = runtime.NumCPU() * 2 - } else if config.NbTasks > 1024 { - return nil, errors.New("invalid config: config.NbTasks > 1024") - } - - // here, we compute the best C for nbPoints - // we split recursively until nbChunks(c) >= nbTasks, - bestC := func(nbPoints int) uint64 { - // implemented msmC methods (the c we use must be in this slice) - implementedCs := []uint64{4, 5, 8, 11, 16} - var C uint64 - // approximate cost (in group operations) - // cost = bits/c * (nbPoints + 2^{c}) - // this needs to be verified empirically. - // for example, on a MBP 2016, for G2 MultiExp > 8M points, hand picking c gives better results - min := math.MaxFloat64 - for _, c := range implementedCs { - cc := (fr.Bits + 1) * (nbPoints + (1 << c)) - cost := float64(cc) / float64(c) - if cost < min { - min = cost - C = c - } - } - return C - } - - C := bestC(nbPoints) - nbChunks := int(computeNbChunks(C)) - - // should we recursively split the msm in half? (see below) - // we want to minimize the execution time of the algorithm; - // splitting the msm will **add** operations, but if it allows to use more CPU, it might be worth it. - - // costFunction returns a metric that represent the "wall time" of the algorithm - costFunction := func(nbTasks, nbCpus, costPerTask int) int { - // cost for the reduction of all tasks (msmReduceChunk) - totalCost := nbTasks - - // cost for the computation of each task (msmProcessChunk) - for nbTasks >= nbCpus { - nbTasks -= nbCpus - totalCost += costPerTask - } - if nbTasks > 0 { - totalCost += costPerTask - } - return totalCost - } - - // costPerTask is the approximate number of group ops per task - costPerTask := func(c uint64, nbPoints int) int { return (nbPoints + int((1 << c))) } - - costPreSplit := costFunction(nbChunks, config.NbTasks, costPerTask(C, nbPoints)) - - cPostSplit := bestC(nbPoints / 2) - nbChunksPostSplit := int(computeNbChunks(cPostSplit)) - costPostSplit := costFunction(nbChunksPostSplit*2, config.NbTasks, costPerTask(cPostSplit, nbPoints/2)) - - // if the cost of the split msm is lower than the cost of the non split msm, we split - if costPostSplit < costPreSplit { - config.NbTasks = int(math.Ceil(float64(config.NbTasks) / 2.0)) - var _p G2Jac - chDone := make(chan struct{}, 1) - go func() { - _p.MultiExp(points[:nbPoints/2], scalars[:nbPoints/2], config) - close(chDone) - }() - p.MultiExp(points[nbPoints/2:], scalars[nbPoints/2:], config) - <-chDone - p.AddAssign(&_p) - return p, nil - } - - // if we don't split, we use the best C we found - _innerMsmG2(p, C, points, scalars, config) - - return p, nil -} - -func _innerMsmG2(p *G2Jac, c uint64, points []G2Affine, scalars []fr.Element, config ecc.MultiExpConfig) *G2Jac { - // partition the scalars - digits, chunkStats := partitionScalars(scalars, c, config.NbTasks) - - nbChunks := computeNbChunks(c) - - // for each chunk, spawn one go routine that'll loop through all the scalars in the - // corresponding bit-window - // note that buckets is an array allocated on the stack and this is critical for performance - - // each go routine sends its result in chChunks[i] channel - chChunks := make([]chan g2JacExtended, nbChunks) - for i := 0; i < len(chChunks); i++ { - chChunks[i] = make(chan g2JacExtended, 1) - } - - // we use a semaphore to limit the number of go routines running concurrently - // (only if nbTasks < nbCPU) - var sem chan struct{} - if config.NbTasks < runtime.NumCPU() { - // we add nbChunks because if chunk is overweight we split it in two - sem = make(chan struct{}, config.NbTasks+int(nbChunks)) - for i := 0; i < config.NbTasks; i++ { - sem <- struct{}{} - } - defer func() { - close(sem) - }() - } - - // the last chunk may be processed with a different method than the rest, as it could be smaller. - n := len(points) - for j := int(nbChunks - 1); j >= 0; j-- { - processChunk := getChunkProcessorG2(c, chunkStats[j]) - if j == int(nbChunks-1) { - processChunk = getChunkProcessorG2(lastC(c), chunkStats[j]) - } - if chunkStats[j].weight >= 115 { - // we split this in more go routines since this chunk has more work to do than the others. - // else what would happen is this go routine would finish much later than the others. - chSplit := make(chan g2JacExtended, 2) - split := n / 2 - - if sem != nil { - sem <- struct{}{} // add another token to the semaphore, since we split in two. - } - go processChunk(uint64(j), chSplit, c, points[:split], digits[j*n:(j*n)+split], sem) - go processChunk(uint64(j), chSplit, c, points[split:], digits[(j*n)+split:(j+1)*n], sem) - go func(chunkID int) { - s1 := <-chSplit - s2 := <-chSplit - close(chSplit) - s1.add(&s2) - chChunks[chunkID] <- s1 - }(j) - continue - } - go processChunk(uint64(j), chChunks[j], c, points, digits[j*n:(j+1)*n], sem) - } - - return msmReduceChunkG2Affine(p, int(c), chChunks[:]) -} - -// getChunkProcessorG2 decides, depending on c window size and statistics for the chunk -// to return the best algorithm to process the chunk. -func getChunkProcessorG2(c uint64, stat chunkStat) func(chunkID uint64, chRes chan<- g2JacExtended, c uint64, points []G2Affine, digits []uint16, sem chan struct{}) { - switch c { - - case 3: - return processChunkG2Jacobian[bucketg2JacExtendedC3] - case 4: - return processChunkG2Jacobian[bucketg2JacExtendedC4] - case 5: - return processChunkG2Jacobian[bucketg2JacExtendedC5] - case 8: - return processChunkG2Jacobian[bucketg2JacExtendedC8] - case 11: - const batchSize = 150 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG2Jacobian[bucketg2JacExtendedC11] - } - return processChunkG2BatchAffine[bucketg2JacExtendedC11, bucketG2AffineC11, bitSetC11, pG2AffineC11, ppG2AffineC11, qG2AffineC11, cG2AffineC11] - case 16: - const batchSize = 640 - // here we could check some chunk statistic (deviation, ...) to determine if calling - // the batch affine version is worth it. - if stat.nbBucketFilled < batchSize { - // clear indicator that batch affine method is not appropriate here. - return processChunkG2Jacobian[bucketg2JacExtendedC16] - } - return processChunkG2BatchAffine[bucketg2JacExtendedC16, bucketG2AffineC16, bitSetC16, pG2AffineC16, ppG2AffineC16, qG2AffineC16, cG2AffineC16] - default: - // panic("will not happen c != previous values is not generated by templates") - return processChunkG2Jacobian[bucketg2JacExtendedC16] - } -} - -// msmReduceChunkG2Affine reduces the weighted sum of the buckets into the result of the multiExp -func msmReduceChunkG2Affine(p *G2Jac, c int, chChunks []chan g2JacExtended) *G2Jac { - var _p g2JacExtended - totalj := <-chChunks[len(chChunks)-1] - _p.Set(&totalj) - for j := len(chChunks) - 2; j >= 0; j-- { - for l := 0; l < c; l++ { - _p.double(&_p) - } - totalj := <-chChunks[j] - _p.add(&totalj) - } - - return p.unsafeFromJacExtended(&_p) -} - -// Fold computes the multi-exponentiation \sum_{i=0}^{len(points)-1} points[i] * -// combinationCoeff^i and stores the result in p. It returns error in case -// configuration is invalid. -func (p *G2Affine) Fold(points []G2Affine, combinationCoeff fr.Element, config ecc.MultiExpConfig) (*G2Affine, error) { - var _p G2Jac - if _, err := _p.Fold(points, combinationCoeff, config); err != nil { - return nil, err - } - p.FromJacobian(&_p) - return p, nil -} - -// Fold computes the multi-exponentiation \sum_{i=0}^{len(points)-1} points[i] * -// combinationCoeff^i and stores the result in p. It returns error in case -// configuration is invalid. -func (p *G2Jac) Fold(points []G2Affine, combinationCoeff fr.Element, config ecc.MultiExpConfig) (*G2Jac, error) { - scalars := make([]fr.Element, len(points)) - scalar := fr.NewElement(1) - for i := 0; i < len(points); i++ { - scalars[i].Set(&scalar) - scalar.Mul(&scalar, &combinationCoeff) - } - return p.MultiExp(points, scalars, config) -} - -// selector stores the index, mask and shifts needed to select bits from a scalar -// it is used during the multiExp algorithm or the batch scalar multiplication -type selector struct { - index uint64 // index in the multi-word scalar to select bits from - mask uint64 // mask (c-bit wide) - shift uint64 // shift needed to get our bits on low positions - - multiWordSelect bool // set to true if we need to select bits from 2 words (case where c doesn't divide 64) - maskHigh uint64 // same than mask, for index+1 - shiftHigh uint64 // same than shift, for index+1 -} - -// return number of chunks for a given window size c -// the last chunk may be bigger to accommodate a potential carry from the NAF decomposition -func computeNbChunks(c uint64) uint64 { - return (fr.Bits + c - 1) / c -} - -// return the last window size for a scalar; -// this last window should accommodate a carry (from the NAF decomposition) -// it can be == c if we have 1 available bit -// it can be > c if we have 0 available bit -// it can be < c if we have 2+ available bits -func lastC(c uint64) uint64 { - nbAvailableBits := (computeNbChunks(c) * c) - fr.Bits - return c + 1 - nbAvailableBits -} - -type chunkStat struct { - // relative weight of work compared to other chunks. 100.0 -> nominal weight. - weight float32 - - // percentage of bucket filled in the window; - ppBucketFilled float32 - nbBucketFilled int -} - -// partitionScalars compute, for each scalars over c-bit wide windows, nbChunk digits -// if the digit is larger than 2^{c-1}, then, we borrow 2^c from the next window and subtract -// 2^{c} to the current digit, making it negative. -// negative digits can be processed in a later step as adding -G into the bucket instead of G -// (computing -G is cheap, and this saves us half of the buckets in the MultiExp or BatchScalarMultiplication) -func partitionScalars(scalars []fr.Element, c uint64, nbTasks int) ([]uint16, []chunkStat) { - // no benefit here to have more tasks than CPUs - if nbTasks > runtime.NumCPU() { - nbTasks = runtime.NumCPU() - } - - // number of c-bit radixes in a scalar - nbChunks := computeNbChunks(c) - - digits := make([]uint16, len(scalars)*int(nbChunks)) - - mask := uint64((1 << c) - 1) // low c bits are 1 - max := int(1<<(c-1)) - 1 // max value (inclusive) we want for our digits - cDivides64 := (64 % c) == 0 // if c doesn't divide 64, we may need to select over multiple words - - // compute offset and word selector / shift to select the right bits of our windows - selectors := make([]selector, nbChunks) - for chunk := uint64(0); chunk < nbChunks; chunk++ { - jc := uint64(chunk * c) - d := selector{} - d.index = jc / 64 - d.shift = jc - (d.index * 64) - d.mask = mask << d.shift - d.multiWordSelect = !cDivides64 && d.shift > (64-c) && d.index < (fr.Limbs-1) - if d.multiWordSelect { - nbBitsHigh := d.shift - uint64(64-c) - d.maskHigh = (1 << nbBitsHigh) - 1 - d.shiftHigh = (c - nbBitsHigh) - } - selectors[chunk] = d - } - - parallel.Execute(len(scalars), func(start, end int) { - for i := start; i < end; i++ { - if scalars[i].IsZero() { - // everything is 0, no need to process this scalar - continue - } - scalar := scalars[i].Bits() - - var carry int - - // for each chunk in the scalar, compute the current digit, and an eventual carry - for chunk := uint64(0); chunk < nbChunks-1; chunk++ { - s := selectors[chunk] - - // init with carry if any - digit := carry - carry = 0 - - // digit = value of the c-bit window - digit += int((scalar[s.index] & s.mask) >> s.shift) - - if s.multiWordSelect { - // we are selecting bits over 2 words - digit += int(scalar[s.index+1]&s.maskHigh) << s.shiftHigh - } - - // if the digit is larger than 2^{c-1}, then, we borrow 2^c from the next window and subtract - // 2^{c} to the current digit, making it negative. - if digit > max { - digit -= (1 << c) - carry = 1 - } - - // if digit is zero, no impact on result - if digit == 0 { - continue - } - - var bits uint16 - if digit > 0 { - bits = uint16(digit) << 1 - } else { - bits = (uint16(-digit-1) << 1) + 1 - } - digits[int(chunk)*len(scalars)+i] = bits - } - - // for the last chunk, we don't want to borrow from a next window - // (but may have a larger max value) - chunk := nbChunks - 1 - s := selectors[chunk] - // init with carry if any - digit := carry - // digit = value of the c-bit window - digit += int((scalar[s.index] & s.mask) >> s.shift) - if s.multiWordSelect { - // we are selecting bits over 2 words - digit += int(scalar[s.index+1]&s.maskHigh) << s.shiftHigh - } - digits[int(chunk)*len(scalars)+i] = uint16(digit) << 1 - } - - }, nbTasks) - - // aggregate chunk stats - chunkStats := make([]chunkStat, nbChunks) - if c <= 9 { - // no need to compute stats for small window sizes - return digits, chunkStats - } - parallel.Execute(len(chunkStats), func(start, end int) { - // for each chunk compute the statistics - for chunkID := start; chunkID < end; chunkID++ { - // indicates if a bucket is hit. - var b bitSetC16 - - // digits for the chunk - chunkDigits := digits[chunkID*len(scalars) : (chunkID+1)*len(scalars)] - - totalOps := 0 - nz := 0 // non zero buckets count - for _, digit := range chunkDigits { - if digit == 0 { - continue - } - totalOps++ - bucketID := digit >> 1 - if digit&1 == 0 { - bucketID -= 1 - } - if !b[bucketID] { - nz++ - b[bucketID] = true - } - } - chunkStats[chunkID].weight = float32(totalOps) // count number of ops for now, we will compute the weight after - chunkStats[chunkID].ppBucketFilled = (float32(nz) * 100.0) / float32(int(1<<(c-1))) - chunkStats[chunkID].nbBucketFilled = nz - } - }, nbTasks) - - totalOps := float32(0.0) - for _, stat := range chunkStats { - totalOps += stat.weight - } - - target := totalOps / float32(nbChunks) - if target != 0.0 { - // if target == 0, it means all the scalars are 0 everywhere, there is no work to be done. - for i := 0; i < len(chunkStats); i++ { - chunkStats[i].weight = (chunkStats[i].weight * 100.0) / target - } - } - - return digits, chunkStats -} diff --git a/ecc/bw6-756/multiexp_affine.go b/ecc/bw6-756/multiexp_affine.go deleted file mode 100644 index dd1b9723ac..0000000000 --- a/ecc/bw6-756/multiexp_affine.go +++ /dev/null @@ -1,571 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bw6756 - -import ( - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" -) - -type batchOpG1Affine struct { - bucketID uint16 - point G1Affine -} - -// processChunkG1BatchAffine process a chunk of the scalars during the msm -// using affine coordinates for the buckets. To amortize the cost of the inverse in the affine addition -// we use a batch affine addition. -// -// this is derived from a PR by 0x0ece : https://github.com/ConsenSys/gnark-crypto/pull/249 -// See Section 5.3: ia.cr/2022/1396 -func processChunkG1BatchAffine[BJE ibg1JacExtended, B ibG1Affine, BS bitSet, TP pG1Affine, TPP ppG1Affine, TQ qOpsG1Affine, TC cG1Affine]( - chunk uint64, - chRes chan<- g1JacExtended, - c uint64, - points []G1Affine, - digits []uint16, - sem chan struct{}) { - - if sem != nil { - // if we are limited, wait for a token in the semaphore - <-sem - } - - // the batch affine addition needs independent points; in other words, for a window of batchSize - // we want to hit independent bucketIDs when processing the digit. if there is a conflict (we're trying - // to add 2 different points to the same bucket), then we push the conflicted point to a queue. - // each time the batch is full, we execute it, and tentatively put the points (if not conflict) - // from the top of the queue into the next batch. - // if the queue is full, we "flush it"; we sequentially add the points to the buckets in - // g1JacExtended coordinates. - // The reasoning behind this is the following; batchSize is chosen such as, for a uniformly random - // input, the number of conflicts is going to be low, and the element added to the queue should be immediately - // processed in the next batch. If it's not the case, then our inputs are not random; and we fallback to - // non-batch-affine version. - - // note that we have 2 sets of buckets - // 1 in G1Affine used with the batch affine additions - // 1 in g1JacExtended used in case the queue of conflicting points - var buckets B // in G1Affine coordinates, infinity point is represented as (0,0), no need to init - var bucketsJE BJE - for i := 0; i < len(buckets); i++ { - bucketsJE[i].setInfinity() - } - - // setup for the batch affine; - var ( - bucketIds BS // bitSet to signify presence of a bucket in current batch - cptAdd int // count the number of bucket + point added to current batch - R TPP // bucket references - P TP // points to be added to R (buckets); it is beneficial to store them on the stack (ie copy) - queue TQ // queue of points that conflict the current batch - qID int // current position in queue - ) - - batchSize := len(P) - - isFull := func() bool { return cptAdd == batchSize } - - executeAndReset := func() { - batchAddG1Affine[TP, TPP, TC](&R, &P, cptAdd) - var tmp BS - bucketIds = tmp - cptAdd = 0 - } - - addFromQueue := func(op batchOpG1Affine) { - // @precondition: must ensures bucket is not "used" in current batch - // note that there is a bit of duplicate logic between add and addFromQueue - // the reason is that as of Go 1.19.3, if we pass a pointer to the queue item (see add signature) - // the compiler will put the queue on the heap. - BK := &buckets[op.bucketID] - - // handle special cases with inf or -P / P - if BK.IsInfinity() { - BK.Set(&op.point) - return - } - if BK.X.Equal(&op.point.X) { - if BK.Y.Equal(&op.point.Y) { - // P + P: doubling, which should be quite rare -- - // we use the other set of buckets - bucketsJE[op.bucketID].addMixed(&op.point) - return - } - BK.setInfinity() - return - } - - bucketIds[op.bucketID] = true - R[cptAdd] = BK - P[cptAdd] = op.point - cptAdd++ - } - - add := func(bucketID uint16, PP *G1Affine, isAdd bool) { - // @precondition: ensures bucket is not "used" in current batch - BK := &buckets[bucketID] - // handle special cases with inf or -P / P - if BK.IsInfinity() { - if isAdd { - BK.Set(PP) - } else { - BK.Neg(PP) - } - return - } - if BK.X.Equal(&PP.X) { - if BK.Y.Equal(&PP.Y) { - // P + P: doubling, which should be quite rare -- - if isAdd { - bucketsJE[bucketID].addMixed(PP) - } else { - BK.setInfinity() - } - return - } - if isAdd { - BK.setInfinity() - } else { - bucketsJE[bucketID].subMixed(PP) - } - return - } - - bucketIds[bucketID] = true - R[cptAdd] = BK - if isAdd { - P[cptAdd].Set(PP) - } else { - P[cptAdd].Neg(PP) - } - cptAdd++ - } - - flushQueue := func() { - for i := 0; i < qID; i++ { - bucketsJE[queue[i].bucketID].addMixed(&queue[i].point) - } - qID = 0 - } - - processTopQueue := func() { - for i := qID - 1; i >= 0; i-- { - if bucketIds[queue[i].bucketID] { - return - } - addFromQueue(queue[i]) - // len(queue) < batchSize so no need to check for full batch. - qID-- - } - } - - for i, digit := range digits { - - if digit == 0 || points[i].IsInfinity() { - continue - } - - bucketID := uint16((digit >> 1)) - isAdd := digit&1 == 0 - if isAdd { - // add - bucketID -= 1 - } - - if bucketIds[bucketID] { - // put it in queue - queue[qID].bucketID = bucketID - if isAdd { - queue[qID].point.Set(&points[i]) - } else { - queue[qID].point.Neg(&points[i]) - } - qID++ - - // queue is full, flush it. - if qID == len(queue)-1 { - flushQueue() - } - continue - } - - // we add the point to the batch. - add(bucketID, &points[i], isAdd) - if isFull() { - executeAndReset() - processTopQueue() - } - } - - // flush items in batch. - executeAndReset() - - // empty the queue - flushQueue() - - // reduce buckets into total - // total = bucket[0] + 2*bucket[1] + 3*bucket[2] ... + n*bucket[n-1] - var runningSum, total g1JacExtended - runningSum.setInfinity() - total.setInfinity() - for k := len(buckets) - 1; k >= 0; k-- { - runningSum.addMixed(&buckets[k]) - if !bucketsJE[k].IsInfinity() { - runningSum.add(&bucketsJE[k]) - } - total.add(&runningSum) - } - - if sem != nil { - // release a token to the semaphore - // before sending to chRes - sem <- struct{}{} - } - - chRes <- total - -} - -// we declare the buckets as fixed-size array types -// this allow us to allocate the buckets on the stack -type bucketG1AffineC11 [1024]G1Affine -type bucketG1AffineC16 [32768]G1Affine - -// buckets: array of G1Affine points of size 1 << (c-1) -type ibG1Affine interface { - bucketG1AffineC11 | - bucketG1AffineC16 -} - -// array of coordinates fp.Element -type cG1Affine interface { - cG1AffineC11 | - cG1AffineC16 -} - -// buckets: array of G1Affine points (for the batch addition) -type pG1Affine interface { - pG1AffineC11 | - pG1AffineC16 -} - -// buckets: array of *G1Affine points (for the batch addition) -type ppG1Affine interface { - ppG1AffineC11 | - ppG1AffineC16 -} - -// buckets: array of G1Affine queue operations (for the batch addition) -type qOpsG1Affine interface { - qG1AffineC11 | - qG1AffineC16 -} - -// batch size 150 when c = 11 -type cG1AffineC11 [150]fp.Element -type pG1AffineC11 [150]G1Affine -type ppG1AffineC11 [150]*G1Affine -type qG1AffineC11 [150]batchOpG1Affine - -// batch size 640 when c = 16 -type cG1AffineC16 [640]fp.Element -type pG1AffineC16 [640]G1Affine -type ppG1AffineC16 [640]*G1Affine -type qG1AffineC16 [640]batchOpG1Affine - -type batchOpG2Affine struct { - bucketID uint16 - point G2Affine -} - -// processChunkG2BatchAffine process a chunk of the scalars during the msm -// using affine coordinates for the buckets. To amortize the cost of the inverse in the affine addition -// we use a batch affine addition. -// -// this is derived from a PR by 0x0ece : https://github.com/ConsenSys/gnark-crypto/pull/249 -// See Section 5.3: ia.cr/2022/1396 -func processChunkG2BatchAffine[BJE ibg2JacExtended, B ibG2Affine, BS bitSet, TP pG2Affine, TPP ppG2Affine, TQ qOpsG2Affine, TC cG2Affine]( - chunk uint64, - chRes chan<- g2JacExtended, - c uint64, - points []G2Affine, - digits []uint16, - sem chan struct{}) { - - if sem != nil { - // if we are limited, wait for a token in the semaphore - <-sem - } - - // the batch affine addition needs independent points; in other words, for a window of batchSize - // we want to hit independent bucketIDs when processing the digit. if there is a conflict (we're trying - // to add 2 different points to the same bucket), then we push the conflicted point to a queue. - // each time the batch is full, we execute it, and tentatively put the points (if not conflict) - // from the top of the queue into the next batch. - // if the queue is full, we "flush it"; we sequentially add the points to the buckets in - // g2JacExtended coordinates. - // The reasoning behind this is the following; batchSize is chosen such as, for a uniformly random - // input, the number of conflicts is going to be low, and the element added to the queue should be immediately - // processed in the next batch. If it's not the case, then our inputs are not random; and we fallback to - // non-batch-affine version. - - // note that we have 2 sets of buckets - // 1 in G2Affine used with the batch affine additions - // 1 in g2JacExtended used in case the queue of conflicting points - var buckets B // in G2Affine coordinates, infinity point is represented as (0,0), no need to init - var bucketsJE BJE - for i := 0; i < len(buckets); i++ { - bucketsJE[i].setInfinity() - } - - // setup for the batch affine; - var ( - bucketIds BS // bitSet to signify presence of a bucket in current batch - cptAdd int // count the number of bucket + point added to current batch - R TPP // bucket references - P TP // points to be added to R (buckets); it is beneficial to store them on the stack (ie copy) - queue TQ // queue of points that conflict the current batch - qID int // current position in queue - ) - - batchSize := len(P) - - isFull := func() bool { return cptAdd == batchSize } - - executeAndReset := func() { - batchAddG2Affine[TP, TPP, TC](&R, &P, cptAdd) - var tmp BS - bucketIds = tmp - cptAdd = 0 - } - - addFromQueue := func(op batchOpG2Affine) { - // @precondition: must ensures bucket is not "used" in current batch - // note that there is a bit of duplicate logic between add and addFromQueue - // the reason is that as of Go 1.19.3, if we pass a pointer to the queue item (see add signature) - // the compiler will put the queue on the heap. - BK := &buckets[op.bucketID] - - // handle special cases with inf or -P / P - if BK.IsInfinity() { - BK.Set(&op.point) - return - } - if BK.X.Equal(&op.point.X) { - if BK.Y.Equal(&op.point.Y) { - // P + P: doubling, which should be quite rare -- - // we use the other set of buckets - bucketsJE[op.bucketID].addMixed(&op.point) - return - } - BK.setInfinity() - return - } - - bucketIds[op.bucketID] = true - R[cptAdd] = BK - P[cptAdd] = op.point - cptAdd++ - } - - add := func(bucketID uint16, PP *G2Affine, isAdd bool) { - // @precondition: ensures bucket is not "used" in current batch - BK := &buckets[bucketID] - // handle special cases with inf or -P / P - if BK.IsInfinity() { - if isAdd { - BK.Set(PP) - } else { - BK.Neg(PP) - } - return - } - if BK.X.Equal(&PP.X) { - if BK.Y.Equal(&PP.Y) { - // P + P: doubling, which should be quite rare -- - if isAdd { - bucketsJE[bucketID].addMixed(PP) - } else { - BK.setInfinity() - } - return - } - if isAdd { - BK.setInfinity() - } else { - bucketsJE[bucketID].subMixed(PP) - } - return - } - - bucketIds[bucketID] = true - R[cptAdd] = BK - if isAdd { - P[cptAdd].Set(PP) - } else { - P[cptAdd].Neg(PP) - } - cptAdd++ - } - - flushQueue := func() { - for i := 0; i < qID; i++ { - bucketsJE[queue[i].bucketID].addMixed(&queue[i].point) - } - qID = 0 - } - - processTopQueue := func() { - for i := qID - 1; i >= 0; i-- { - if bucketIds[queue[i].bucketID] { - return - } - addFromQueue(queue[i]) - // len(queue) < batchSize so no need to check for full batch. - qID-- - } - } - - for i, digit := range digits { - - if digit == 0 || points[i].IsInfinity() { - continue - } - - bucketID := uint16((digit >> 1)) - isAdd := digit&1 == 0 - if isAdd { - // add - bucketID -= 1 - } - - if bucketIds[bucketID] { - // put it in queue - queue[qID].bucketID = bucketID - if isAdd { - queue[qID].point.Set(&points[i]) - } else { - queue[qID].point.Neg(&points[i]) - } - qID++ - - // queue is full, flush it. - if qID == len(queue)-1 { - flushQueue() - } - continue - } - - // we add the point to the batch. - add(bucketID, &points[i], isAdd) - if isFull() { - executeAndReset() - processTopQueue() - } - } - - // flush items in batch. - executeAndReset() - - // empty the queue - flushQueue() - - // reduce buckets into total - // total = bucket[0] + 2*bucket[1] + 3*bucket[2] ... + n*bucket[n-1] - var runningSum, total g2JacExtended - runningSum.setInfinity() - total.setInfinity() - for k := len(buckets) - 1; k >= 0; k-- { - runningSum.addMixed(&buckets[k]) - if !bucketsJE[k].IsInfinity() { - runningSum.add(&bucketsJE[k]) - } - total.add(&runningSum) - } - - if sem != nil { - // release a token to the semaphore - // before sending to chRes - sem <- struct{}{} - } - - chRes <- total - -} - -// we declare the buckets as fixed-size array types -// this allow us to allocate the buckets on the stack -type bucketG2AffineC11 [1024]G2Affine -type bucketG2AffineC16 [32768]G2Affine - -// buckets: array of G2Affine points of size 1 << (c-1) -type ibG2Affine interface { - bucketG2AffineC11 | - bucketG2AffineC16 -} - -// array of coordinates fp.Element -type cG2Affine interface { - cG2AffineC11 | - cG2AffineC16 -} - -// buckets: array of G2Affine points (for the batch addition) -type pG2Affine interface { - pG2AffineC11 | - pG2AffineC16 -} - -// buckets: array of *G2Affine points (for the batch addition) -type ppG2Affine interface { - ppG2AffineC11 | - ppG2AffineC16 -} - -// buckets: array of G2Affine queue operations (for the batch addition) -type qOpsG2Affine interface { - qG2AffineC11 | - qG2AffineC16 -} - -// batch size 150 when c = 11 -type cG2AffineC11 [150]fp.Element -type pG2AffineC11 [150]G2Affine -type ppG2AffineC11 [150]*G2Affine -type qG2AffineC11 [150]batchOpG2Affine - -// batch size 640 when c = 16 -type cG2AffineC16 [640]fp.Element -type pG2AffineC16 [640]G2Affine -type ppG2AffineC16 [640]*G2Affine -type qG2AffineC16 [640]batchOpG2Affine - -type bitSetC3 [4]bool -type bitSetC4 [8]bool -type bitSetC5 [16]bool -type bitSetC8 [128]bool -type bitSetC11 [1024]bool -type bitSetC16 [32768]bool - -type bitSet interface { - bitSetC3 | - bitSetC4 | - bitSetC5 | - bitSetC8 | - bitSetC11 | - bitSetC16 -} diff --git a/ecc/bw6-756/multiexp_jacobian.go b/ecc/bw6-756/multiexp_jacobian.go deleted file mode 100644 index 5dc50140fc..0000000000 --- a/ecc/bw6-756/multiexp_jacobian.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bw6756 - -func processChunkG1Jacobian[B ibg1JacExtended](chunk uint64, - chRes chan<- g1JacExtended, - c uint64, - points []G1Affine, - digits []uint16, - sem chan struct{}) { - - if sem != nil { - // if we are limited, wait for a token in the semaphore - <-sem - } - - var buckets B - for i := 0; i < len(buckets); i++ { - buckets[i].setInfinity() - } - - // for each scalars, get the digit corresponding to the chunk we're processing. - for i, digit := range digits { - if digit == 0 { - continue - } - - // if msbWindow bit is set, we need to subtract - if digit&1 == 0 { - // add - buckets[(digit>>1)-1].addMixed(&points[i]) - } else { - // sub - buckets[(digit >> 1)].subMixed(&points[i]) - } - } - - // reduce buckets into total - // total = bucket[0] + 2*bucket[1] + 3*bucket[2] ... + n*bucket[n-1] - - var runningSum, total g1JacExtended - runningSum.setInfinity() - total.setInfinity() - for k := len(buckets) - 1; k >= 0; k-- { - if !buckets[k].IsInfinity() { - runningSum.add(&buckets[k]) - } - total.add(&runningSum) - } - - if sem != nil { - // release a token to the semaphore - // before sending to chRes - sem <- struct{}{} - } - - chRes <- total -} - -// we declare the buckets as fixed-size array types -// this allow us to allocate the buckets on the stack -type bucketg1JacExtendedC3 [4]g1JacExtended -type bucketg1JacExtendedC4 [8]g1JacExtended -type bucketg1JacExtendedC5 [16]g1JacExtended -type bucketg1JacExtendedC8 [128]g1JacExtended -type bucketg1JacExtendedC11 [1024]g1JacExtended -type bucketg1JacExtendedC16 [32768]g1JacExtended - -type ibg1JacExtended interface { - bucketg1JacExtendedC3 | - bucketg1JacExtendedC4 | - bucketg1JacExtendedC5 | - bucketg1JacExtendedC8 | - bucketg1JacExtendedC11 | - bucketg1JacExtendedC16 -} - -func processChunkG2Jacobian[B ibg2JacExtended](chunk uint64, - chRes chan<- g2JacExtended, - c uint64, - points []G2Affine, - digits []uint16, - sem chan struct{}) { - - if sem != nil { - // if we are limited, wait for a token in the semaphore - <-sem - } - - var buckets B - for i := 0; i < len(buckets); i++ { - buckets[i].setInfinity() - } - - // for each scalars, get the digit corresponding to the chunk we're processing. - for i, digit := range digits { - if digit == 0 { - continue - } - - // if msbWindow bit is set, we need to subtract - if digit&1 == 0 { - // add - buckets[(digit>>1)-1].addMixed(&points[i]) - } else { - // sub - buckets[(digit >> 1)].subMixed(&points[i]) - } - } - - // reduce buckets into total - // total = bucket[0] + 2*bucket[1] + 3*bucket[2] ... + n*bucket[n-1] - - var runningSum, total g2JacExtended - runningSum.setInfinity() - total.setInfinity() - for k := len(buckets) - 1; k >= 0; k-- { - if !buckets[k].IsInfinity() { - runningSum.add(&buckets[k]) - } - total.add(&runningSum) - } - - if sem != nil { - // release a token to the semaphore - // before sending to chRes - sem <- struct{}{} - } - - chRes <- total -} - -// we declare the buckets as fixed-size array types -// this allow us to allocate the buckets on the stack -type bucketg2JacExtendedC3 [4]g2JacExtended -type bucketg2JacExtendedC4 [8]g2JacExtended -type bucketg2JacExtendedC5 [16]g2JacExtended -type bucketg2JacExtendedC8 [128]g2JacExtended -type bucketg2JacExtendedC11 [1024]g2JacExtended -type bucketg2JacExtendedC16 [32768]g2JacExtended - -type ibg2JacExtended interface { - bucketg2JacExtendedC3 | - bucketg2JacExtendedC4 | - bucketg2JacExtendedC5 | - bucketg2JacExtendedC8 | - bucketg2JacExtendedC11 | - bucketg2JacExtendedC16 -} diff --git a/ecc/bw6-756/multiexp_test.go b/ecc/bw6-756/multiexp_test.go deleted file mode 100644 index 78b610a9f4..0000000000 --- a/ecc/bw6-756/multiexp_test.go +++ /dev/null @@ -1,863 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bw6756 - -import ( - "fmt" - "math/big" - "math/bits" - "math/rand/v2" - "runtime" - "sync" - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -func TestMultiExpG1(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = 3 - } else { - parameters.MinSuccessfulTests = nbFuzzShort * 2 - } - - properties := gopter.NewProperties(parameters) - - genScalar := GenFr() - - // size of the multiExps - const nbSamples = 73 - - // multi exp points - var samplePoints [nbSamples]G1Affine - var g G1Jac - g.Set(&g1Gen) - for i := 1; i <= nbSamples; i++ { - samplePoints[i-1].FromJacobian(&g) - g.AddAssign(&g1Gen) - } - - // sprinkle some points at infinity - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - - // final scalar to use in double and add method (without mixer factor) - // n(n+1)(2n+1)/6 (sum of the squares from 1 to n) - var scalar big.Int - scalar.SetInt64(nbSamples) - scalar.Mul(&scalar, new(big.Int).SetInt64(nbSamples+1)) - scalar.Mul(&scalar, new(big.Int).SetInt64(2*nbSamples+1)) - scalar.Div(&scalar, new(big.Int).SetInt64(6)) - - // ensure a multiexp that's splitted has the same result as a non-splitted one.. - properties.Property("[G1] Multi exponentiation (cmax) should be consistent with splitted multiexp", prop.ForAll( - func(mixer fr.Element) bool { - var samplePointsLarge [nbSamples * 13]G1Affine - for i := 0; i < 13; i++ { - copy(samplePointsLarge[i*nbSamples:], samplePoints[:]) - } - - var rmax, splitted1, splitted2 G1Jac - - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples * 13]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - rmax.MultiExp(samplePointsLarge[:], sampleScalars[:], ecc.MultiExpConfig{}) - splitted1.MultiExp(samplePointsLarge[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: 128}) - splitted2.MultiExp(samplePointsLarge[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: 51}) - return rmax.Equal(&splitted1) && rmax.Equal(&splitted2) - }, - genScalar, - )) - - // cRange is generated from template and contains the available parameters for the multiexp window size - cRange := []uint64{3, 4, 5, 8, 11, 16} - if testing.Short() { - // test only "odd" and "even" (ie windows size divide word size vs not) - cRange = []uint64{5, 14} - } - - properties.Property(fmt.Sprintf("[G1] Multi exponentiation (c in %v) should be consistent with sum of square", cRange), prop.ForAll( - func(mixer fr.Element) bool { - - var expected G1Jac - - // compute expected result with double and add - var finalScalar, mixerBigInt big.Int - finalScalar.Mul(&scalar, mixer.BigInt(&mixerBigInt)) - expected.ScalarMultiplication(&g1Gen, &finalScalar) - - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - results := make([]G1Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG1(&results[i], c, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - for i := 1; i < len(results); i++ { - if !results[i].Equal(&results[i-1]) { - t.Logf("result for c=%d != c=%d", cRange[i-1], cRange[i]) - return false - } - } - return true - }, - genScalar, - )) - - properties.Property(fmt.Sprintf("[G1] Multi exponentiation (c in %v) of points at infinity should output a point at infinity", cRange), prop.ForAll( - func(mixer fr.Element) bool { - - var samplePointsZero [nbSamples]G1Affine - - var expected G1Jac - - // compute expected result with double and add - var finalScalar, mixerBigInt big.Int - finalScalar.Mul(&scalar, mixer.BigInt(&mixerBigInt)) - expected.ScalarMultiplication(&g1Gen, &finalScalar) - - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - samplePointsZero[i-1].setInfinity() - } - - results := make([]G1Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG1(&results[i], c, samplePointsZero[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - for i := 0; i < len(results); i++ { - if !results[i].Z.IsZero() { - t.Logf("result for c=%d is not infinity", cRange[i]) - return false - } - } - return true - }, - genScalar, - )) - - properties.Property(fmt.Sprintf("[G1] Multi exponentiation (c in %v) with a vector of 0s as input should output a point at infinity", cRange), prop.ForAll( - func(mixer fr.Element) bool { - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - results := make([]G1Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG1(&results[i], c, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - for i := 0; i < len(results); i++ { - if !results[i].Z.IsZero() { - t.Logf("result for c=%d is not infinity", cRange[i]) - return false - } - } - return true - }, - genScalar, - )) - - // note : this test is here as we expect to have a different multiExp than the above bucket method - // for small number of points - properties.Property("[G1] Multi exponentiation (<50points) should be consistent with sum of square", prop.ForAll( - func(mixer fr.Element) bool { - - var g G1Jac - g.Set(&g1Gen) - - // mixer ensures that all the words of a fpElement are set - samplePoints := make([]G1Affine, 30) - sampleScalars := make([]fr.Element, 30) - - for i := 1; i <= 30; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - samplePoints[i-1].FromJacobian(&g) - g.AddAssign(&g1Gen) - } - - var op1MultiExp G1Affine - op1MultiExp.MultiExp(samplePoints, sampleScalars, ecc.MultiExpConfig{}) - - var finalBigScalar fr.Element - var finalBigScalarBi big.Int - var op1ScalarMul G1Affine - finalBigScalar.SetUint64(9455).Mul(&finalBigScalar, &mixer) - finalBigScalar.BigInt(&finalBigScalarBi) - op1ScalarMul.ScalarMultiplication(&g1GenAff, &finalBigScalarBi) - - return op1ScalarMul.Equal(&op1MultiExp) - }, - genScalar, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestCrossMultiExpG1(t *testing.T) { - const nbSamples = 1 << 14 - // multi exp points - var samplePoints [nbSamples]G1Affine - var g G1Jac - g.Set(&g1Gen) - for i := 1; i <= nbSamples; i++ { - samplePoints[i-1].FromJacobian(&g) - g.AddAssign(&g1Gen) - } - - // sprinkle some points at infinity - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - - var sampleScalars [nbSamples]fr.Element - fillBenchScalars(sampleScalars[:]) - - // sprinkle some doublings - for i := 10; i < 100; i++ { - samplePoints[i] = samplePoints[0] - sampleScalars[i] = sampleScalars[0] - } - - // cRange is generated from template and contains the available parameters for the multiexp window size - cRange := []uint64{3, 4, 5, 8, 11, 16} - if testing.Short() { - // test only "odd" and "even" (ie windows size divide word size vs not) - cRange = []uint64{5, 14} - } - - results := make([]G1Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG1(&results[i], c, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - - var r G1Jac - _innerMsmG1Reference(&r, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - - var expected, got G1Affine - expected.FromJacobian(&r) - - for i := 0; i < len(results); i++ { - got.FromJacobian(&results[i]) - if !expected.Equal(&got) { - t.Fatalf("cross msm failed with c=%d", cRange[i]) - } - } - -} - -// _innerMsmG1Reference always do ext jacobian with c == 16 -func _innerMsmG1Reference(p *G1Jac, points []G1Affine, scalars []fr.Element, config ecc.MultiExpConfig) *G1Jac { - // partition the scalars - digits, _ := partitionScalars(scalars, 16, config.NbTasks) - - nbChunks := computeNbChunks(16) - - // for each chunk, spawn one go routine that'll loop through all the scalars in the - // corresponding bit-window - // note that buckets is an array allocated on the stack and this is critical for performance - - // each go routine sends its result in chChunks[i] channel - chChunks := make([]chan g1JacExtended, nbChunks) - for i := 0; i < len(chChunks); i++ { - chChunks[i] = make(chan g1JacExtended, 1) - } - - // the last chunk may be processed with a different method than the rest, as it could be smaller. - n := len(points) - for j := int(nbChunks - 1); j >= 0; j-- { - processChunk := processChunkG1Jacobian[bucketg1JacExtendedC16] - go processChunk(uint64(j), chChunks[j], 16, points, digits[j*n:(j+1)*n], nil) - } - - return msmReduceChunkG1Affine(p, int(16), chChunks[:]) -} - -func BenchmarkMultiExpG1(b *testing.B) { - - const ( - pow = (bits.UintSize / 2) - (bits.UintSize / 8) // 24 on 64 bits arch, 12 on 32 bits - nbSamples = 1 << pow - ) - - var ( - samplePoints [nbSamples]G1Affine - sampleScalars [nbSamples]fr.Element - sampleScalarsSmallValues [nbSamples]fr.Element - sampleScalarsRedundant [nbSamples]fr.Element - ) - - fillBenchScalars(sampleScalars[:]) - copy(sampleScalarsSmallValues[:], sampleScalars[:]) - copy(sampleScalarsRedundant[:], sampleScalars[:]) - - // this means first chunk is going to have more work to do and should be split into several go routines - for i := 0; i < len(sampleScalarsSmallValues); i++ { - if i%5 == 0 { - sampleScalarsSmallValues[i].SetZero() - sampleScalarsSmallValues[i][0] = 1 - } - } - - // bad case for batch affine because scalar distribution might look uniform - // but over batchSize windows, we may hit a lot of conflicts and force the msm-affine - // to process small batches of additions to flush its queue of conflicted points. - for i := 0; i < len(sampleScalarsRedundant); i += 100 { - for j := i + 1; j < i+100 && j < len(sampleScalarsRedundant); j++ { - sampleScalarsRedundant[j] = sampleScalarsRedundant[i] - } - } - - fillBenchBasesG1(samplePoints[:]) - - var testPoint G1Affine - - for i := 5; i <= pow; i++ { - using := 1 << i - - b.Run(fmt.Sprintf("%d points", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:using], sampleScalars[:using], ecc.MultiExpConfig{}) - } - }) - - b.Run(fmt.Sprintf("%d points-smallvalues", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:using], sampleScalarsSmallValues[:using], ecc.MultiExpConfig{}) - } - }) - - b.Run(fmt.Sprintf("%d points-redundancy", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:using], sampleScalarsRedundant[:using], ecc.MultiExpConfig{}) - } - }) - } -} - -func BenchmarkMultiExpG1Reference(b *testing.B) { - const nbSamples = 1 << 20 - - var ( - samplePoints [nbSamples]G1Affine - sampleScalars [nbSamples]fr.Element - ) - - fillBenchScalars(sampleScalars[:]) - fillBenchBasesG1(samplePoints[:]) - - var testPoint G1Affine - - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - } -} - -func BenchmarkManyMultiExpG1Reference(b *testing.B) { - const nbSamples = 1 << 20 - - var ( - samplePoints [nbSamples]G1Affine - sampleScalars [nbSamples]fr.Element - ) - - fillBenchScalars(sampleScalars[:]) - fillBenchBasesG1(samplePoints[:]) - - var t1, t2, t3 G1Affine - b.ResetTimer() - for j := 0; j < b.N; j++ { - var wg sync.WaitGroup - wg.Add(3) - go func() { - t1.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - wg.Done() - }() - go func() { - t2.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - wg.Done() - }() - go func() { - t3.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - wg.Done() - }() - wg.Wait() - } -} - -// WARNING: this return points that are NOT on the curve and is meant to be use for benchmarking -// purposes only. We don't check that the result is valid but just measure "computational complexity". -// -// Rationale for generating points that are not on the curve is that for large benchmarks, generating -// a vector of different points can take minutes. Using the same point or subset will bias the benchmark result -// since bucket additions in extended jacobian coordinates will hit doubling algorithm instead of add. -func fillBenchBasesG1(samplePoints []G1Affine) { - var r big.Int - r.SetString("340444420969191673093399857471996460938405", 10) - samplePoints[0].ScalarMultiplication(&samplePoints[0], &r) - - one := samplePoints[0].X - one.SetOne() - - for i := 1; i < len(samplePoints); i++ { - samplePoints[i].X.Add(&samplePoints[i-1].X, &one) - samplePoints[i].Y.Sub(&samplePoints[i-1].Y, &one) - } -} - -func TestMultiExpG2(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = 3 - } else { - parameters.MinSuccessfulTests = nbFuzzShort * 2 - } - - properties := gopter.NewProperties(parameters) - - genScalar := GenFr() - - // size of the multiExps - const nbSamples = 73 - - // multi exp points - var samplePoints [nbSamples]G2Affine - var g G2Jac - g.Set(&g2Gen) - for i := 1; i <= nbSamples; i++ { - samplePoints[i-1].FromJacobian(&g) - g.AddAssign(&g2Gen) - } - - // sprinkle some points at infinity - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - - // final scalar to use in double and add method (without mixer factor) - // n(n+1)(2n+1)/6 (sum of the squares from 1 to n) - var scalar big.Int - scalar.SetInt64(nbSamples) - scalar.Mul(&scalar, new(big.Int).SetInt64(nbSamples+1)) - scalar.Mul(&scalar, new(big.Int).SetInt64(2*nbSamples+1)) - scalar.Div(&scalar, new(big.Int).SetInt64(6)) - - // ensure a multiexp that's splitted has the same result as a non-splitted one.. - properties.Property("[G2] Multi exponentiation (cmax) should be consistent with splitted multiexp", prop.ForAll( - func(mixer fr.Element) bool { - var samplePointsLarge [nbSamples * 13]G2Affine - for i := 0; i < 13; i++ { - copy(samplePointsLarge[i*nbSamples:], samplePoints[:]) - } - - var rmax, splitted1, splitted2 G2Jac - - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples * 13]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - rmax.MultiExp(samplePointsLarge[:], sampleScalars[:], ecc.MultiExpConfig{}) - splitted1.MultiExp(samplePointsLarge[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: 128}) - splitted2.MultiExp(samplePointsLarge[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: 51}) - return rmax.Equal(&splitted1) && rmax.Equal(&splitted2) - }, - genScalar, - )) - - // cRange is generated from template and contains the available parameters for the multiexp window size - // for g2, CI suffers with large c size since it needs to allocate a lot of memory for the buckets. - // test only "odd" and "even" (ie windows size divide word size vs not) - cRange := []uint64{5, 14} - - properties.Property(fmt.Sprintf("[G2] Multi exponentiation (c in %v) should be consistent with sum of square", cRange), prop.ForAll( - func(mixer fr.Element) bool { - - var expected G2Jac - - // compute expected result with double and add - var finalScalar, mixerBigInt big.Int - finalScalar.Mul(&scalar, mixer.BigInt(&mixerBigInt)) - expected.ScalarMultiplication(&g2Gen, &finalScalar) - - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - } - - results := make([]G2Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG2(&results[i], c, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - for i := 1; i < len(results); i++ { - if !results[i].Equal(&results[i-1]) { - t.Logf("result for c=%d != c=%d", cRange[i-1], cRange[i]) - return false - } - } - return true - }, - genScalar, - )) - - properties.Property(fmt.Sprintf("[G2] Multi exponentiation (c in %v) of points at infinity should output a point at infinity", cRange), prop.ForAll( - func(mixer fr.Element) bool { - - var samplePointsZero [nbSamples]G2Affine - - var expected G2Jac - - // compute expected result with double and add - var finalScalar, mixerBigInt big.Int - finalScalar.Mul(&scalar, mixer.BigInt(&mixerBigInt)) - expected.ScalarMultiplication(&g2Gen, &finalScalar) - - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - for i := 1; i <= nbSamples; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - samplePointsZero[i-1].setInfinity() - } - - results := make([]G2Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG2(&results[i], c, samplePointsZero[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - for i := 0; i < len(results); i++ { - if !results[i].Z.IsZero() { - t.Logf("result for c=%d is not infinity", cRange[i]) - return false - } - } - return true - }, - genScalar, - )) - - properties.Property(fmt.Sprintf("[G2] Multi exponentiation (c in %v) with a vector of 0s as input should output a point at infinity", cRange), prop.ForAll( - func(mixer fr.Element) bool { - // mixer ensures that all the words of a fpElement are set - var sampleScalars [nbSamples]fr.Element - - results := make([]G2Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG2(&results[i], c, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - for i := 0; i < len(results); i++ { - if !results[i].Z.IsZero() { - t.Logf("result for c=%d is not infinity", cRange[i]) - return false - } - } - return true - }, - genScalar, - )) - - // note : this test is here as we expect to have a different multiExp than the above bucket method - // for small number of points - properties.Property("[G2] Multi exponentiation (<50points) should be consistent with sum of square", prop.ForAll( - func(mixer fr.Element) bool { - - var g G2Jac - g.Set(&g2Gen) - - // mixer ensures that all the words of a fpElement are set - samplePoints := make([]G2Affine, 30) - sampleScalars := make([]fr.Element, 30) - - for i := 1; i <= 30; i++ { - sampleScalars[i-1].SetUint64(uint64(i)). - Mul(&sampleScalars[i-1], &mixer) - samplePoints[i-1].FromJacobian(&g) - g.AddAssign(&g2Gen) - } - - var op1MultiExp G2Affine - op1MultiExp.MultiExp(samplePoints, sampleScalars, ecc.MultiExpConfig{}) - - var finalBigScalar fr.Element - var finalBigScalarBi big.Int - var op1ScalarMul G2Affine - finalBigScalar.SetUint64(9455).Mul(&finalBigScalar, &mixer) - finalBigScalar.BigInt(&finalBigScalarBi) - op1ScalarMul.ScalarMultiplication(&g2GenAff, &finalBigScalarBi) - - return op1ScalarMul.Equal(&op1MultiExp) - }, - genScalar, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestCrossMultiExpG2(t *testing.T) { - const nbSamples = 1 << 14 - // multi exp points - var samplePoints [nbSamples]G2Affine - var g G2Jac - g.Set(&g2Gen) - for i := 1; i <= nbSamples; i++ { - samplePoints[i-1].FromJacobian(&g) - g.AddAssign(&g2Gen) - } - - // sprinkle some points at infinity - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - samplePoints[rand.N(nbSamples)].setInfinity() //#nosec G404 weak rng is fine here - - var sampleScalars [nbSamples]fr.Element - fillBenchScalars(sampleScalars[:]) - - // sprinkle some doublings - for i := 10; i < 100; i++ { - samplePoints[i] = samplePoints[0] - sampleScalars[i] = sampleScalars[0] - } - - // cRange is generated from template and contains the available parameters for the multiexp window size - // for g2, CI suffers with large c size since it needs to allocate a lot of memory for the buckets. - // test only "odd" and "even" (ie windows size divide word size vs not) - cRange := []uint64{5, 14} - - results := make([]G2Jac, len(cRange)) - for i, c := range cRange { - _innerMsmG2(&results[i], c, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - } - - var r G2Jac - _innerMsmG2Reference(&r, samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{NbTasks: runtime.NumCPU()}) - - var expected, got G2Affine - expected.FromJacobian(&r) - - for i := 0; i < len(results); i++ { - got.FromJacobian(&results[i]) - if !expected.Equal(&got) { - t.Fatalf("cross msm failed with c=%d", cRange[i]) - } - } - -} - -// _innerMsmG2Reference always do ext jacobian with c == 16 -func _innerMsmG2Reference(p *G2Jac, points []G2Affine, scalars []fr.Element, config ecc.MultiExpConfig) *G2Jac { - // partition the scalars - digits, _ := partitionScalars(scalars, 16, config.NbTasks) - - nbChunks := computeNbChunks(16) - - // for each chunk, spawn one go routine that'll loop through all the scalars in the - // corresponding bit-window - // note that buckets is an array allocated on the stack and this is critical for performance - - // each go routine sends its result in chChunks[i] channel - chChunks := make([]chan g2JacExtended, nbChunks) - for i := 0; i < len(chChunks); i++ { - chChunks[i] = make(chan g2JacExtended, 1) - } - - // the last chunk may be processed with a different method than the rest, as it could be smaller. - n := len(points) - for j := int(nbChunks - 1); j >= 0; j-- { - processChunk := processChunkG2Jacobian[bucketg2JacExtendedC16] - go processChunk(uint64(j), chChunks[j], 16, points, digits[j*n:(j+1)*n], nil) - } - - return msmReduceChunkG2Affine(p, int(16), chChunks[:]) -} - -func BenchmarkMultiExpG2(b *testing.B) { - - const ( - pow = (bits.UintSize / 2) - (bits.UintSize / 8) // 24 on 64 bits arch, 12 on 32 bits - nbSamples = 1 << pow - ) - - var ( - samplePoints [nbSamples]G2Affine - sampleScalars [nbSamples]fr.Element - sampleScalarsSmallValues [nbSamples]fr.Element - sampleScalarsRedundant [nbSamples]fr.Element - ) - - fillBenchScalars(sampleScalars[:]) - copy(sampleScalarsSmallValues[:], sampleScalars[:]) - copy(sampleScalarsRedundant[:], sampleScalars[:]) - - // this means first chunk is going to have more work to do and should be split into several go routines - for i := 0; i < len(sampleScalarsSmallValues); i++ { - if i%5 == 0 { - sampleScalarsSmallValues[i].SetZero() - sampleScalarsSmallValues[i][0] = 1 - } - } - - // bad case for batch affine because scalar distribution might look uniform - // but over batchSize windows, we may hit a lot of conflicts and force the msm-affine - // to process small batches of additions to flush its queue of conflicted points. - for i := 0; i < len(sampleScalarsRedundant); i += 100 { - for j := i + 1; j < i+100 && j < len(sampleScalarsRedundant); j++ { - sampleScalarsRedundant[j] = sampleScalarsRedundant[i] - } - } - - fillBenchBasesG2(samplePoints[:]) - - var testPoint G2Affine - - for i := 5; i <= pow; i++ { - using := 1 << i - - b.Run(fmt.Sprintf("%d points", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:using], sampleScalars[:using], ecc.MultiExpConfig{}) - } - }) - - b.Run(fmt.Sprintf("%d points-smallvalues", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:using], sampleScalarsSmallValues[:using], ecc.MultiExpConfig{}) - } - }) - - b.Run(fmt.Sprintf("%d points-redundancy", using), func(b *testing.B) { - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:using], sampleScalarsRedundant[:using], ecc.MultiExpConfig{}) - } - }) - } -} - -func BenchmarkMultiExpG2Reference(b *testing.B) { - const nbSamples = 1 << 20 - - var ( - samplePoints [nbSamples]G2Affine - sampleScalars [nbSamples]fr.Element - ) - - fillBenchScalars(sampleScalars[:]) - fillBenchBasesG2(samplePoints[:]) - - var testPoint G2Affine - - b.ResetTimer() - for j := 0; j < b.N; j++ { - testPoint.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - } -} - -func BenchmarkManyMultiExpG2Reference(b *testing.B) { - const nbSamples = 1 << 20 - - var ( - samplePoints [nbSamples]G2Affine - sampleScalars [nbSamples]fr.Element - ) - - fillBenchScalars(sampleScalars[:]) - fillBenchBasesG2(samplePoints[:]) - - var t1, t2, t3 G2Affine - b.ResetTimer() - for j := 0; j < b.N; j++ { - var wg sync.WaitGroup - wg.Add(3) - go func() { - t1.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - wg.Done() - }() - go func() { - t2.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - wg.Done() - }() - go func() { - t3.MultiExp(samplePoints[:], sampleScalars[:], ecc.MultiExpConfig{}) - wg.Done() - }() - wg.Wait() - } -} - -// WARNING: this return points that are NOT on the curve and is meant to be use for benchmarking -// purposes only. We don't check that the result is valid but just measure "computational complexity". -// -// Rationale for generating points that are not on the curve is that for large benchmarks, generating -// a vector of different points can take minutes. Using the same point or subset will bias the benchmark result -// since bucket additions in extended jacobian coordinates will hit doubling algorithm instead of add. -func fillBenchBasesG2(samplePoints []G2Affine) { - var r big.Int - r.SetString("340444420969191673093399857471996460938405", 10) - samplePoints[0].ScalarMultiplication(&samplePoints[0], &r) - - one := samplePoints[0].X - one.SetOne() - - for i := 1; i < len(samplePoints); i++ { - samplePoints[i].X.Add(&samplePoints[i-1].X, &one) - samplePoints[i].Y.Sub(&samplePoints[i-1].Y, &one) - } -} - -func fillBenchScalars(sampleScalars []fr.Element) { - // ensure every words of the scalars are filled - for i := 0; i < len(sampleScalars); i++ { - sampleScalars[i].SetRandom() - } -} diff --git a/ecc/bw6-756/pairing.go b/ecc/bw6-756/pairing.go deleted file mode 100644 index 5efcc25a6b..0000000000 --- a/ecc/bw6-756/pairing.go +++ /dev/null @@ -1,583 +0,0 @@ -// Copyright 2020 ConsenSys AG -// -// 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 bw6756 - -import ( - "errors" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - "github.com/consensys/gnark-crypto/ecc/bw6-756/internal/fptower" -) - -// GT target group of the pairing -type GT = fptower.E6 - -type lineEvaluation struct { - r0 fp.Element - r1 fp.Element - r2 fp.Element -} - -// Pair calculates the reduced pairing for a set of points -// ∏ᵢ e(Pᵢ, Qᵢ). -// -// This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. -func Pair(P []G1Affine, Q []G2Affine) (GT, error) { - f, err := MillerLoop(P, Q) - if err != nil { - return GT{}, err - } - return FinalExponentiation(&f), nil -} - -// PairingCheck calculates the reduced pairing for a set of points and returns True if the result is One -// ∏ᵢ e(Pᵢ, Qᵢ) =? 1 -// -// This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. -func PairingCheck(P []G1Affine, Q []G2Affine) (bool, error) { - f, err := Pair(P, Q) - if err != nil { - return false, err - } - var one GT - one.SetOne() - return f.Equal(&one), nil -} - -// FinalExponentiation computes the exponentiation (∏ᵢ zᵢ)ᵈ -// where d = (p^6-1)/r = (p^6-1)/Φ_6(p) ⋅ Φ_6(p)/r = (p^3-1)(p+1)(p^2 - p +1)/r -// we use instead d=s ⋅ (p^3-1)(p+1)(p^2 - p +1)/r -// where s is the cofactor (x_0+1) (El Housni and Guillevic) -func FinalExponentiation(z *GT, _z ...*GT) GT { - - var result GT - result.Set(z) - - for _, e := range _z { - result.Mul(&result, e) - } - - var buf GT - - // Easy part - // (p^3-1)(p+1) - buf.Conjugate(&result) - result.Inverse(&result) - buf.Mul(&buf, &result) - result.Frobenius(&buf). - Mul(&result, &buf) - - var one GT - one.SetOne() - if result.Equal(&one) { - return result - } - - // 2. Hard part (up to permutation) - // (x₀+1)(p²-p+1)/r - // Algorithm 4.4 from https://yelhousni.github.io/phd.pdf - var a, b, c, d, e, f, g, h, i, j, k, t GT - a.ExptMinus1Square(&result) - t.Frobenius(&result) - a.Mul(&a, &t) - b.ExptPlus1(&a) - t.Conjugate(&result) - b.Mul(&b, &t) - t.CyclotomicSquare(&a) - a.Mul(&a, &t) - c.ExptMinus1Div3(&b) - d.ExptMinus1(&c) - e.ExptMinus1Square(&d) - e.Mul(&e, &d) - d.Conjugate(&d) - f.Mul(&d, &b) - g.ExptPlus1(&e) - g.Mul(&g, &f) - h.Mul(&g, &c) - i.Mul(&g, &d) - i.ExptPlus1(&i) - t.Conjugate(&f) - i.Mul(&i, &t) - // ht, hy = -1, -1 - // c1 = (ht+hy)/2 = -1 - j.Conjugate(&h) - j.Mul(&j, &e) - k.CyclotomicSquare(&j) - k.Mul(&k, &j) - k.Mul(&k, &b) - // c2 = (ht**2+3*hy**2)/4 = 1 - k.Mul(&k, &i) - result.Mul(&a, &k) - - return result -} - -// MillerLoop computes the multi-Miller loop -// ∏ᵢ MillerLoop(Pᵢ, Qᵢ) = -// ∏ᵢ { fᵢ_{x₀+1+λ(x₀³-x₀²-x₀),Qᵢ}(Pᵢ) } -// -// Alg.2 in https://eprint.iacr.org/2021/1359.pdf -// Eq. (6') in https://hackmd.io/@gnark/BW6-761-changes -func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { - // check input size match - n := len(P) - if n == 0 || n != len(Q) { - return GT{}, errors.New("invalid inputs sizes") - } - - // filter infinity points - p := make([]G1Affine, 0, n) - q0 := make([]G2Affine, 0, n) - - for k := 0; k < n; k++ { - if P[k].IsInfinity() || Q[k].IsInfinity() { - continue - } - p = append(p, P[k]) - q0 = append(q0, Q[k]) - } - - n = len(p) - - // precomputations - qProj1 := make([]g2Proj, n) - q1 := make([]G2Affine, n) - q1Neg := make([]G2Affine, n) - q0Neg := make([]G2Affine, n) - for k := 0; k < n; k++ { - q1[k].Y.Neg(&q0[k].Y) - q0Neg[k].X.Set(&q0[k].X) - q0Neg[k].Y.Set(&q1[k].Y) - q1[k].X.Mul(&q0[k].X, &thirdRootOneG1) - qProj1[k].FromAffine(&q1[k]) - q1Neg[k].Neg(&q1[k]) - } - - // f_{a0+λ*a1,Q}(P) - var result GT - result.SetOne() - var l, l0 lineEvaluation - var prodLines [5]fp.Element - - var j int8 - - if n >= 1 { - // i = 189, separately to avoid an E12 Square - // (Square(res) = 1² = 1) - // j = 0 - // k = 0, separately to avoid MulBy014 (res × ℓ) - // (assign line to res) - // qProj1[0] ← 2qProj1[0] and l0 the tangent ℓ passing 2qProj1[0] - qProj1[0].doubleStep(&l0) - // line evaluation at Q[0] (assign) - result.B0.A0.Set(&l0.r0) - result.B0.A1.Mul(&l0.r1, &p[0].X) - result.B1.A1.Mul(&l0.r2, &p[0].Y) - } - - // k = 1 - if n >= 2 { - // qProj1[1] ← 2qProj1[1] and l0 the tangent ℓ passing 2qProj1[1] - qProj1[1].doubleStep(&l0) - // line evaluation at Q[1] - l0.r1.Mul(&l0.r1, &p[1].X) - l0.r2.Mul(&l0.r2, &p[1].Y) - prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &result.B0.A0, &result.B0.A1, &result.B1.A1) - result.B0.A0 = prodLines[0] - result.B0.A1 = prodLines[1] - result.B0.A2 = prodLines[2] - result.B1.A1 = prodLines[3] - result.B1.A2 = prodLines[4] - } - - // k >= 2 - for k := 2; k < n; k++ { - // qProj1[k] ← 2qProj1[k] and l0 the tangent ℓ passing 2qProj1[k] - qProj1[k].doubleStep(&l0) - // line evaluation at Q[k] - l0.r1.Mul(&l0.r1, &p[k].X) - l0.r2.Mul(&l0.r2, &p[k].Y) - // ℓ × res - result.MulBy014(&l0.r0, &l0.r1, &l0.r2) - } - - for i := 188; i >= 1; i-- { - result.Square(&result) - - j = LoopCounter1[i]*3 + LoopCounter[i] - - for k := 0; k < n; k++ { - qProj1[k].doubleStep(&l0) - l0.r1.Mul(&l0.r1, &p[k].X) - l0.r2.Mul(&l0.r2, &p[k].Y) - - switch j { - // cases -4, -2, 2, 4 do not occur, given the static LoopCounters - case -3: - qProj1[k].addMixedStep(&l, &q1Neg[k]) - l.r1.Mul(&l.r1, &p[k].X) - l.r2.Mul(&l.r2, &p[k].Y) - prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) - result.MulBy01245(&prodLines) - case -1: - qProj1[k].addMixedStep(&l, &q0Neg[k]) - l.r1.Mul(&l.r1, &p[k].X) - l.r2.Mul(&l.r2, &p[k].Y) - prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) - result.MulBy01245(&prodLines) - case 0: - result.MulBy014(&l0.r0, &l0.r1, &l0.r2) - case 1: - qProj1[k].addMixedStep(&l, &q0[k]) - l.r1.Mul(&l.r1, &p[k].X) - l.r2.Mul(&l.r2, &p[k].Y) - prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) - result.MulBy01245(&prodLines) - case 3: - qProj1[k].addMixedStep(&l, &q1[k]) - l.r1.Mul(&l.r1, &p[k].X) - l.r2.Mul(&l.r2, &p[k].Y) - prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) - result.MulBy01245(&prodLines) - default: - return GT{}, errors.New("invalid LoopCounter") - } - } - } - - // i = 0, separately to avoid a point addition - // j = -3 - result.Square(&result) - for k := 0; k < n; k++ { - qProj1[k].doubleStep(&l0) - l0.r1.Mul(&l0.r1, &p[k].X) - l0.r2.Mul(&l0.r2, &p[k].Y) - qProj1[k].lineCompute(&l, &q1Neg[k]) - l.r1.Mul(&l.r1, &p[k].X) - l.r2.Mul(&l.r2, &p[k].Y) - prodLines = fptower.Mul014By014(&l0.r0, &l0.r1, &l0.r2, &l.r0, &l.r1, &l.r2) - result.MulBy01245(&prodLines) - } - - return result, nil - -} - -// doubleStep doubles a point in Homogenous projective coordinates, and evaluates the line in Miller loop -// https://eprint.iacr.org/2013/722.pdf (Section 4.3) -func (p *g2Proj) doubleStep(evaluations *lineEvaluation) { - - // get some Element from our pool - var t1, A, B, C, D, E, EE, F, G, H, I, J, K fp.Element - A.Mul(&p.x, &p.y) - A.Halve() - B.Square(&p.y) - C.Square(&p.z) - D.Double(&C). - Add(&D, &C) - E.Mul(&D, &bTwistCurveCoeff) - F.Double(&E). - Add(&F, &E) - G.Add(&B, &F) - G.Halve() - H.Add(&p.y, &p.z). - Square(&H) - t1.Add(&B, &C) - H.Sub(&H, &t1) - I.Sub(&E, &B) - J.Square(&p.x) - EE.Square(&E) - K.Double(&EE). - Add(&K, &EE) - - // X, Y, Z - p.x.Sub(&B, &F). - Mul(&p.x, &A) - p.y.Square(&G). - Sub(&p.y, &K) - p.z.Mul(&B, &H) - - // Line evaluation - evaluations.r0.Set(&I) - evaluations.r1.Double(&J). - Add(&evaluations.r1, &J) - evaluations.r2.Neg(&H) -} - -// addMixedStep point addition in Mixed Homogenous projective and Affine coordinates -// https://eprint.iacr.org/2013/722.pdf (Section 4.3) -func (p *g2Proj) addMixedStep(evaluations *lineEvaluation, a *G2Affine) { - - // get some Element from our pool - var Y2Z1, X2Z1, O, L, C, D, E, F, G, H, t0, t1, t2, J fp.Element - Y2Z1.Mul(&a.Y, &p.z) - O.Sub(&p.y, &Y2Z1) - X2Z1.Mul(&a.X, &p.z) - L.Sub(&p.x, &X2Z1) - C.Square(&O) - D.Square(&L) - E.Mul(&L, &D) - F.Mul(&p.z, &C) - G.Mul(&p.x, &D) - t0.Double(&G) - H.Add(&E, &F). - Sub(&H, &t0) - t1.Mul(&p.y, &E) - - // X, Y, Z - p.x.Mul(&L, &H) - p.y.Sub(&G, &H). - Mul(&p.y, &O). - Sub(&p.y, &t1) - p.z.Mul(&E, &p.z) - - t2.Mul(&L, &a.Y) - J.Mul(&a.X, &O). - Sub(&J, &t2) - - // Line evaluation - evaluations.r0.Set(&J) - evaluations.r1.Neg(&O) - evaluations.r2.Set(&L) -} - -// lineCompute computes the line through p in Homogenous projective coordinates -// and a in affine coordinates. It does not compute the resulting point p+a. -func (p *g2Proj) lineCompute(evaluations *lineEvaluation, a *G2Affine) { - - // get some Element from our pool - var Y2Z1, X2Z1, O, L, t2, J fp.Element - Y2Z1.Mul(&a.Y, &p.z) - O.Sub(&p.y, &Y2Z1) - X2Z1.Mul(&a.X, &p.z) - L.Sub(&p.x, &X2Z1) - t2.Mul(&L, &a.Y) - J.Mul(&a.X, &O). - Sub(&J, &t2) - - // Line evaluation - evaluations.r0.Set(&J) - evaluations.r1.Neg(&O) - evaluations.r2.Set(&L) -} - -// ---------------------- -// Fixed-argument pairing -// ---------------------- - -type LineEvaluationAff struct { - R0 fp.Element - R1 fp.Element -} - -// PairFixedQ calculates the reduced pairing for a set of points -// ∏ᵢ e(Pᵢ, Qᵢ) where Q are fixed points in G2. -// -// This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. -func PairFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) { - f, err := MillerLoopFixedQ(P, lines) - if err != nil { - return GT{}, err - } - return FinalExponentiation(&f), nil -} - -// PairingCheckFixedQ calculates the reduced pairing for a set of points and returns True if the result is One -// ∏ᵢ e(Pᵢ, Qᵢ) =? 1 where Q are fixed points in G2. -// -// This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. -func PairingCheckFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (bool, error) { - f, err := PairFixedQ(P, lines) - if err != nil { - return false, err - } - var one GT - one.SetOne() - return f.Equal(&one), nil -} - -// PrecomputeLines precomputes the lines for the fixed-argument Miller loop -func PrecomputeLines(Q G2Affine) (PrecomputedLines [2][len(LoopCounter) - 1]LineEvaluationAff) { - - // precomputations - var accQ, imQ, imQneg, negQ G2Affine - imQ.Y.Neg(&Q.Y) - negQ.X.Set(&Q.X) - negQ.Y.Set(&imQ.Y) - imQ.X.Mul(&Q.X, &thirdRootOneG1) - accQ.Set(&imQ) - imQneg.Neg(&imQ) - - for i := len(LoopCounter) - 2; i >= 0; i-- { - accQ.doubleStep(&PrecomputedLines[0][i]) - - switch LoopCounter1[i]*3 + LoopCounter[i] { - // cases -4, -2, 2, 4 do not occur, given the static LoopCounters - case -3: - accQ.addStep(&PrecomputedLines[1][i], &imQneg) - case -1: - accQ.addStep(&PrecomputedLines[1][i], &negQ) - case 0: - continue - case 1: - accQ.addStep(&PrecomputedLines[1][i], &Q) - case 3: - accQ.addStep(&PrecomputedLines[1][i], &imQ) - default: - return [2][len(LoopCounter) - 1]LineEvaluationAff{} - } - } - - return PrecomputedLines -} - -// MillerLoopFixedQ computes the multi-Miller loop as in MillerLoop -// but Qᵢ are fixed points in G2 known in advance. -func MillerLoopFixedQ(P []G1Affine, lines [][2][len(LoopCounter) - 1]LineEvaluationAff) (GT, error) { - // check input size match - n := len(P) - if n == 0 || n != len(lines) { - return GT{}, errors.New("invalid inputs sizes") - } - - // no need to filter infinity points: - // 1. if Pᵢ=(0,0) then -x/y=1/y=0 by gnark-crypto convention and so - // lines R0 and R1 are 0. It happens that result will stay, through - // the Miller loop, in 𝔽p⁶ because MulBy01(0,0,1), - // Mul01By01(0,0,1,0,0,1) and MulBy01245 set result.C0 to 0. At the - // end result will be in a proper subgroup of Fp¹² so it be reduced to - // 1 in FinalExponentiation. - // - // and/or - // - // 2. if Qᵢ=(0,0) then PrecomputeLines(Qᵢ) will return lines R0 and R1 - // that are 0 because of gnark-convention (*/0==0) in doubleStep and - // addStep. Similarly to Pᵢ=(0,0) it happens that result be 1 - // after the FinalExponentiation. - - // precomputations - yInv := make([]fp.Element, n) - xNegOverY := make([]fp.Element, n) - for k := 0; k < n; k++ { - yInv[k].Set(&P[k].Y) - } - yInv = fp.BatchInvert(yInv) - for k := 0; k < n; k++ { - xNegOverY[k].Mul(&P[k].X, &yInv[k]). - Neg(&xNegOverY[k]) - } - - // f_{a0+λ*a1,Q}(P) - var result GT - result.SetOne() - var prodLines [5]fp.Element - - for i := len(LoopCounter) - 2; i >= 0; i-- { - result.Square(&result) - - j := LoopCounter1[i]*3 + LoopCounter[i] - for k := 0; k < n; k++ { - lines[k][0][i].R1. - Mul( - &lines[k][0][i].R1, - &yInv[k], - ) - lines[k][0][i].R0. - Mul(&lines[k][0][i].R0, - &xNegOverY[k], - ) - if j == 0 { - result.MulBy01( - &lines[k][0][i].R1, - &lines[k][0][i].R0, - ) - - } else { - lines[k][1][i].R1. - Mul( - &lines[k][1][i].R1, - &yInv[k], - ) - lines[k][1][i].R0. - Mul( - &lines[k][1][i].R0, - &xNegOverY[k], - ) - prodLines = fptower.Mul01By01( - &lines[k][0][i].R1, &lines[k][0][i].R0, - &lines[k][1][i].R1, &lines[k][1][i].R0, - ) - result.MulBy01245(&prodLines) - } - } - } - - return result, nil - -} - -func (p *G2Affine) doubleStep(evaluations *LineEvaluationAff) { - - var n, d, λ, xr, yr fp.Element - // λ = 3x²/2y - n.Square(&p.X) - λ.Double(&n). - Add(&λ, &n) - d.Double(&p.Y) - λ.Div(&λ, &d) - - // xr = λ²-2x - xr.Square(&λ). - Sub(&xr, &p.X). - Sub(&xr, &p.X) - - // yr = λ(x-xr)-y - yr.Sub(&p.X, &xr). - Mul(&yr, &λ). - Sub(&yr, &p.Y) - - evaluations.R0.Set(&λ) - evaluations.R1.Mul(&λ, &p.X). - Sub(&evaluations.R1, &p.Y) - - p.X.Set(&xr) - p.Y.Set(&yr) -} - -func (p *G2Affine) addStep(evaluations *LineEvaluationAff, a *G2Affine) { - var n, d, λ, λλ, xr, yr fp.Element - - // compute λ = (y2-y1)/(x2-x1) - n.Sub(&a.Y, &p.Y) - d.Sub(&a.X, &p.X) - λ.Div(&n, &d) - - // xr = λ²-x1-x2 - λλ.Square(&λ) - n.Add(&p.X, &a.X) - xr.Sub(&λλ, &n) - - // yr = λ(x1-xr) - y1 - yr.Sub(&p.X, &xr). - Mul(&yr, &λ). - Sub(&yr, &p.Y) - - evaluations.R0.Set(&λ) - evaluations.R1.Mul(&λ, &p.X). - Sub(&evaluations.R1, &p.Y) - - p.X.Set(&xr) - p.Y.Set(&yr) -} diff --git a/ecc/bw6-756/pairing_test.go b/ecc/bw6-756/pairing_test.go deleted file mode 100644 index ee3774d96d..0000000000 --- a/ecc/bw6-756/pairing_test.go +++ /dev/null @@ -1,505 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package bw6756 - -import ( - "fmt" - "math/big" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -// ------------------------------------------------------------ -// tests - -func TestPairing(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genA := GenE6() - - genR1 := GenFr() - genR2 := GenFr() - - properties.Property("[BW6-756] Having the receiver as operand (final expo) should output the same result", prop.ForAll( - func(a GT) bool { - b := FinalExponentiation(&a) - a = FinalExponentiation(&a) - return a.Equal(&b) - }, - genA, - )) - - properties.Property("[BW6-756] Exponentiating FinalExpo(a) to r should output 1", prop.ForAll( - func(a GT) bool { - b := FinalExponentiation(&a) - return !a.IsInSubGroup() && b.IsInSubGroup() - }, - genA, - )) - - properties.Property("[BW6-756] Exp, CyclotomicExp and ExpGLV results must be the same in GT (small and big exponents)", prop.ForAll( - func(a GT, e fr.Element) bool { - - var res bool - - // exponent > r - { - a = FinalExponentiation(&a) - var _e big.Int - _e.SetString("169893631828481842931290008859743243489098146141979830311893424751855271950692001433356165550548410610101138388623573573742608490725625288296502860183437011025036209791574001140592327223981416956942076610555083128655330944007957223952510233203018053264066056080064687038560794652180979019775788172491868553073169893631828481842931290008859743243489098146141979830311893424751855271950692001433356165550548410610101138388623573573742608490725625288296502860183437011025036209791574001140592327223981416956942076610555083128655330944007957223952510233203018053264066056080064687038560794652180979019775788172491868553073", 10) - var b, c, d GT - b.Exp(a, &_e) - c.ExpGLV(a, &_e) - d.CyclotomicExp(a, &_e) - res = b.Equal(&c) && c.Equal(&d) - } - - // exponent < r - { - a = FinalExponentiation(&a) - var _e big.Int - e.BigInt(&_e) - var b, c, d GT - b.Exp(a, &_e) - c.ExpGLV(a, &_e) - d.CyclotomicExp(a, &_e) - res = res && b.Equal(&c) && c.Equal(&d) - } - - return res - }, - genA, - genR1, - )) - - properties.Property("[BW6-756] Expt(Expt) and Exp(t^2) should output the same result in the cyclotomic subgroup", prop.ForAll( - func(a GT) bool { - var b, c, d GT - b.Conjugate(&a) - a.Inverse(&a) - b.Mul(&b, &a) - - a.Frobenius(&b). - Mul(&a, &b) - - c.Expt(&a).Expt(&c) - d.Exp(a, &xGen).Exp(d, &xGen) - return c.Equal(&d) - }, - genA, - )) - - properties.Property("[BW6-756] bilinearity", prop.ForAll( - func(a, b fr.Element) bool { - - var res, resa, resb, resab, zero GT - - var ag1 G1Affine - var bg2 G2Affine - - var abigint, bbigint, ab big.Int - - a.BigInt(&abigint) - b.BigInt(&bbigint) - ab.Mul(&abigint, &bbigint) - - ag1.ScalarMultiplication(&g1GenAff, &abigint) - bg2.ScalarMultiplication(&g2GenAff, &bbigint) - - res, _ = Pair([]G1Affine{g1GenAff}, []G2Affine{g2GenAff}) - resa, _ = Pair([]G1Affine{ag1}, []G2Affine{g2GenAff}) - resb, _ = Pair([]G1Affine{g1GenAff}, []G2Affine{bg2}) - - resab.Exp(res, &ab) - resa.Exp(resa, &bbigint) - resb.Exp(resb, &abigint) - - return resab.Equal(&resa) && resab.Equal(&resb) && !res.Equal(&zero) - - }, - genR1, - genR2, - )) - - properties.Property("[BW6-756] PairingCheck", prop.ForAll( - func(a, b fr.Element) bool { - - var g1GenAffNeg G1Affine - g1GenAffNeg.Neg(&g1GenAff) - tabP := []G1Affine{g1GenAff, g1GenAffNeg} - tabQ := []G2Affine{g2GenAff, g2GenAff} - - res, _ := PairingCheck(tabP, tabQ) - - return res - }, - genR1, - genR2, - )) - - properties.Property("[BW6-756] Pair should output the same result with MillerLoop or MillerLoopFixedQ", prop.ForAll( - func(a, b fr.Element) bool { - - var ag1 G1Affine - var bg2 G2Affine - - var abigint, bbigint big.Int - - a.BigInt(&abigint) - b.BigInt(&bbigint) - - ag1.ScalarMultiplication(&g1GenAff, &abigint) - bg2.ScalarMultiplication(&g2GenAff, &bbigint) - - P := []G1Affine{g1GenAff, ag1} - Q := []G2Affine{g2GenAff, bg2} - - ml1, _ := MillerLoop(P, Q) - ml2, _ := MillerLoopFixedQ( - P, - [][2][len(LoopCounter) - 1]LineEvaluationAff{ - PrecomputeLines(Q[0]), - PrecomputeLines(Q[1]), - }) - - res1 := FinalExponentiation(&ml1) - res2 := FinalExponentiation(&ml2) - - return res1.Equal(&res2) - }, - genR1, - genR2, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestMillerLoop(t *testing.T) { - - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - genR1 := GenFr() - genR2 := GenFr() - - properties.Property("[BW6-756] MillerLoop of pairs should be equal to the product of MillerLoops", prop.ForAll( - func(a, b fr.Element) bool { - - var simpleProd, factorizedProd GT - - var ag1 G1Affine - var bg2 G2Affine - - var abigint, bbigint big.Int - - a.BigInt(&abigint) - b.BigInt(&bbigint) - - ag1.ScalarMultiplication(&g1GenAff, &abigint) - bg2.ScalarMultiplication(&g2GenAff, &bbigint) - - P0 := []G1Affine{g1GenAff} - P1 := []G1Affine{ag1} - Q0 := []G2Affine{g2GenAff} - Q1 := []G2Affine{bg2} - - // FE( ML(a,b) * ML(c,d) * ML(e,f) * ML(g,h) ) - M1, _ := MillerLoop(P0, Q0) - M2, _ := MillerLoop(P1, Q0) - M3, _ := MillerLoop(P0, Q1) - M4, _ := MillerLoop(P1, Q1) - simpleProd.Mul(&M1, &M2).Mul(&simpleProd, &M3).Mul(&simpleProd, &M4) - simpleProd = FinalExponentiation(&simpleProd) - - tabP := []G1Affine{g1GenAff, ag1, g1GenAff, ag1} - tabQ := []G2Affine{g2GenAff, g2GenAff, bg2, bg2} - - // FE( ML([a,c,e,g] ; [b,d,f,h]) ) -> saves 3 squares in Fqk - factorizedProd, _ = Pair(tabP, tabQ) - - return simpleProd.Equal(&factorizedProd) - }, - genR1, - genR2, - )) - - properties.Property("[BW6-756] MillerLoop and MillerLoopFixedQ should skip pairs with a point at infinity", prop.ForAll( - func(a, b fr.Element) bool { - - var one GT - - var ag1, g1Inf G1Affine - var bg2, g2Inf G2Affine - - var abigint, bbigint big.Int - - one.SetOne() - - a.BigInt(&abigint) - b.BigInt(&bbigint) - - ag1.ScalarMultiplication(&g1GenAff, &abigint) - bg2.ScalarMultiplication(&g2GenAff, &bbigint) - - g1Inf.FromJacobian(&g1Infinity) - g2Inf.FromJacobian(&g2Infinity) - - // e([0,c] ; [b,d]) - // -> should be equal to e(c,d) - tabP := []G1Affine{g1Inf, ag1} - tabQ := []G2Affine{g2GenAff, bg2} - res1, _ := Pair(tabP, tabQ) - - // e([a,c] ; [0,d]) - // -> should be equal to e(c,d) - tabP = []G1Affine{g1GenAff, ag1} - tabQ = []G2Affine{g2Inf, bg2} - res2, _ := Pair(tabP, tabQ) - - // e([0,c] ; [b,d]) with fixed points b and d - // -> should be equal to e(c,d) - tabP = []G1Affine{g1Inf, ag1} - linesQ := [][2][len(LoopCounter) - 1]LineEvaluationAff{ - PrecomputeLines(g2GenAff), - PrecomputeLines(bg2), - } - res3, _ := PairFixedQ(tabP, linesQ) - - // e([a,c] ; [0,d]) with fixed points 0 and d - // -> should be equal to e(c,d) - tabP = []G1Affine{g1GenAff, ag1} - linesQ = [][2][len(LoopCounter) - 1]LineEvaluationAff{ - PrecomputeLines(g2Inf), - PrecomputeLines(bg2), - } - res4, _ := PairFixedQ(tabP, linesQ) - - // e([0,c] ; [d,0]) - // -> should be equal to 1 - tabP = []G1Affine{g1Inf, ag1} - tabQ = []G2Affine{bg2, g2Inf} - res5, _ := Pair(tabP, tabQ) - - // e([0,c] ; [d,0]) with fixed points d and 0 - // -> should be equal to 1 - tabP = []G1Affine{g1Inf, ag1} - linesQ = [][2][len(LoopCounter) - 1]LineEvaluationAff{ - PrecomputeLines(bg2), - PrecomputeLines(g2Inf), - } - res6, _ := PairFixedQ(tabP, linesQ) - - // e([0,0]) - // -> should be equal to 1 - tabP = []G1Affine{g1Inf} - tabQ = []G2Affine{g2Inf} - res7, _ := Pair(tabP, tabQ) - - // e([0,0]) with fixed point 0 - // -> should be equal to 1 - tabP = []G1Affine{g1Inf} - linesQ = [][2][len(LoopCounter) - 1]LineEvaluationAff{ - PrecomputeLines(g2Inf), - } - res8, _ := PairFixedQ(tabP, linesQ) - - return res1.Equal(&res2) && res2.Equal(&res3) && res3.Equal(&res4) && - res5.Equal(&one) && res6.Equal(&one) && res7.Equal(&one) && res8.Equal(&one) - }, - genR1, - genR2, - )) - - properties.Property("[BW6-756] compressed pairing", prop.ForAll( - func(a, b fr.Element) bool { - - var ag1 G1Affine - var bg2 G2Affine - - var abigint, bbigint big.Int - - a.BigInt(&abigint) - b.BigInt(&bbigint) - - ag1.ScalarMultiplication(&g1GenAff, &abigint) - bg2.ScalarMultiplication(&g2GenAff, &bbigint) - - res, _ := Pair([]G1Affine{ag1}, []G2Affine{bg2}) - - compressed, _ := res.CompressTorus() - decompressed := compressed.DecompressTorus() - - return decompressed.Equal(&res) - - }, - genR1, - genR2, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -// ------------------------------------------------------------ -// benches - -func BenchmarkPairing(b *testing.B) { - - var g1GenAff G1Affine - var g2GenAff G2Affine - - g1GenAff.FromJacobian(&g1Gen) - g2GenAff.FromJacobian(&g2Gen) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - Pair([]G1Affine{g1GenAff}, []G2Affine{g2GenAff}) - } -} - -func BenchmarkMillerLoop(b *testing.B) { - - var g1GenAff G1Affine - var g2GenAff G2Affine - - g1GenAff.FromJacobian(&g1Gen) - g2GenAff.FromJacobian(&g2Gen) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - MillerLoop([]G1Affine{g1GenAff}, []G2Affine{g2GenAff}) - } -} - -func BenchmarkFinalExponentiation(b *testing.B) { - - var a GT - a.SetRandom() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - FinalExponentiation(&a) - } - -} - -func BenchmarkMultiMiller(b *testing.B) { - - var g1GenAff G1Affine - var g2GenAff G2Affine - - g1GenAff.FromJacobian(&g1Gen) - g2GenAff.FromJacobian(&g2Gen) - - n := 10 - P := make([]G1Affine, n) - Q := make([]G2Affine, n) - - for i := 2; i <= n; i++ { - for j := 0; j < i; j++ { - P[j].Set(&g1GenAff) - Q[j].Set(&g2GenAff) - } - b.Run(fmt.Sprintf("%d pairs", i), func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - MillerLoop(P, Q) - } - }) - } -} - -func BenchmarkMultiPair(b *testing.B) { - - var g1GenAff G1Affine - var g2GenAff G2Affine - - g1GenAff.FromJacobian(&g1Gen) - g2GenAff.FromJacobian(&g2Gen) - - n := 10 - P := make([]G1Affine, n) - Q := make([]G2Affine, n) - - for i := 2; i <= n; i++ { - for j := 0; j < i; j++ { - P[j].Set(&g1GenAff) - Q[j].Set(&g2GenAff) - } - b.Run(fmt.Sprintf("%d pairs", i), func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - Pair(P, Q) - } - }) - } -} - -func BenchmarkExpGT(b *testing.B) { - - var a GT - a.SetRandom() - a = FinalExponentiation(&a) - - var e fp.Element - e.SetRandom() - - k := new(big.Int).SetUint64(12) - e.Exp(e, k) - var _e big.Int - e.BigInt(&_e) - - b.Run("Naive windowed Exp", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Exp(a, &_e) - } - }) - - b.Run("2-NAF cyclotomic Exp", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.CyclotomicExp(a, &_e) - } - }) - - b.Run("windowed 2-dim GLV Exp", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.ExpGLV(a, &_e) - } - }) -} diff --git a/ecc/bw6-756/twistededwards/curve.go b/ecc/bw6-756/twistededwards/curve.go deleted file mode 100644 index a18dbd35c9..0000000000 --- a/ecc/bw6-756/twistededwards/curve.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package twistededwards - -import ( - "math/big" - "sync" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) - -// CurveParams curve parameters: ax^2 + y^2 = 1 + d*x^2*y^2 -type CurveParams struct { - A, D fr.Element - Cofactor fr.Element - Order big.Int - Base PointAffine -} - -// GetEdwardsCurve returns the twisted Edwards curve on bw6-756/Fr -func GetEdwardsCurve() CurveParams { - initOnce.Do(initCurveParams) - // copy to keep Order private - var res CurveParams - - res.A.Set(&curveParams.A) - res.D.Set(&curveParams.D) - res.Cofactor.Set(&curveParams.Cofactor) - res.Order.Set(&curveParams.Order) - res.Base.Set(&curveParams.Base) - - return res -} - -var ( - initOnce sync.Once - curveParams CurveParams -) - -func initCurveParams() { - curveParams.A.SetString("35895") - curveParams.D.SetString("35894") - curveParams.Cofactor.SetString("8") - curveParams.Order.SetString("75656025759413271466656060197725120092480961471365614219134998880569790930794516726065877484428941069706901665493", 10) - - curveParams.Base.X.SetString("357240753431396842603421262238241571158569743053156052278371293545344505472364896271378029423975465332156840775830") - curveParams.Base.Y.SetString("279345325880910540799960837653138904956852780817349960193932651092957355032339063742900216468694143617372745972501") -} - -// mulByA multiplies fr.Element by curveParams.A -func mulByA(x *fr.Element) { - x.Mul(x, &curveParams.A) -} diff --git a/ecc/bw6-756/twistededwards/doc.go b/ecc/bw6-756/twistededwards/doc.go deleted file mode 100644 index f9f92b78d4..0000000000 --- a/ecc/bw6-756/twistededwards/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package twistededwards provides bw6-756's twisted edwards "companion curve" defined on fr. -package twistededwards diff --git a/ecc/bw6-756/twistededwards/eddsa/doc.go b/ecc/bw6-756/twistededwards/eddsa/doc.go deleted file mode 100644 index 70657706d8..0000000000 --- a/ecc/bw6-756/twistededwards/eddsa/doc.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -// Package eddsa provides EdDSA signature scheme on bw6-756's twisted edwards curve. -// -// # See also -// -// https://en.wikipedia.org/wiki/EdDSA -package eddsa diff --git a/ecc/bw6-756/twistededwards/eddsa/eddsa.go b/ecc/bw6-756/twistededwards/eddsa/eddsa.go deleted file mode 100644 index d639a95067..0000000000 --- a/ecc/bw6-756/twistededwards/eddsa/eddsa.go +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package eddsa - -import ( - "crypto/subtle" - "errors" - "hash" - "io" - "math/big" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/twistededwards" - "github.com/consensys/gnark-crypto/signature" - "golang.org/x/crypto/blake2b" -) - -var errNotOnCurve = errors.New("point not on curve") -var errHashNeeded = errors.New("hFunc cannot be nil. We need a hash for Fiat-Shamir") - -const ( - sizeFr = fr.Bytes - sizePublicKey = sizeFr - sizeSignature = 2 * sizeFr - sizePrivateKey = 2*sizeFr + 32 -) - -// PublicKey eddsa signature object -// cf https://en.wikipedia.org/wiki/EdDSA for notation -type PublicKey struct { - A twistededwards.PointAffine -} - -// PrivateKey private key of an eddsa instance -type PrivateKey struct { - PublicKey PublicKey // copy of the associated public key - scalar [sizeFr]byte // secret scalar, in big Endian - randSrc [32]byte // source -} - -// Signature represents an eddsa signature -// cf https://en.wikipedia.org/wiki/EdDSA for notation -type Signature struct { - R twistededwards.PointAffine - S [sizeFr]byte -} - -// GenerateKey generates a public and private key pair. -func GenerateKey(r io.Reader) (*PrivateKey, error) { - c := twistededwards.GetEdwardsCurve() - - var pub PublicKey - var priv PrivateKey - // The source of randomness and the secret scalar must come - // from 2 distinct sources. Since the scalar is the size of the - // field of definition (48 bytes), the scalar must come from a - // different digest so there is no overlap between the source of - // randomness and the scalar. - - // used for random scalar (aka private key) - seed := make([]byte, 32) - _, err := r.Read(seed) - if err != nil { - return nil, err - } - h1 := blake2b.Sum512(seed[:]) - - // used for the source of randomness when hashing the message - h2 := blake2b.Sum512(h1[:]) - for i := 0; i < 32; i++ { - priv.randSrc[i] = h2[i] - } - - // prune the key - // https://tools.ietf.org/html/rfc8032#section-5.1.5, key generation - h1[0] &= 0xF8 - h1[sizeFr-1] &= 0x7F - h1[sizeFr-1] |= 0x40 - - // reverse first bytes because setBytes interpret stream as big endian - // but in eddsa specs s is the first 32 bytes in little endian - for i, j := 0, sizeFr-1; i < sizeFr; i, j = i+1, j-1 { - priv.scalar[i] = h1[j] - } - - var bScalar big.Int - bScalar.SetBytes(priv.scalar[:]) - pub.A.ScalarMultiplication(&c.Base, &bScalar) - - priv.PublicKey = pub - - return &priv, nil -} - -// Equal compares 2 public keys -func (pub *PublicKey) Equal(x signature.PublicKey) bool { - xx, ok := x.(*PublicKey) - if !ok { - return false - } - bpk := pub.Bytes() - bxx := xx.Bytes() - return subtle.ConstantTimeCompare(bpk, bxx) == 1 -} - -// Public returns the public key associated to the private key. -func (privKey *PrivateKey) Public() signature.PublicKey { - var pub PublicKey - pub.A.Set(&privKey.PublicKey.A) - return &pub -} - -// Sign sign a sequence of field elements -// For arbitrary strings use fr.Hash first -// Pure Eddsa version (see https://tools.ietf.org/html/rfc8032#page-8) -func (privKey *PrivateKey) Sign(message []byte, hFunc hash.Hash) ([]byte, error) { - - // hFunc cannot be nil. - // We need a hash function for the Fiat-Shamir. - if hFunc == nil { - return nil, errHashNeeded - } - - curveParams := twistededwards.GetEdwardsCurve() - - var res Signature - - // blinding factor for the private key - // blindingFactorBigInt must be the same size as the private key, - // blindingFactorBigInt = h(randomness_source||message)[:sizeFr] - var blindingFactorBigInt big.Int - - // randSrc = privKey.randSrc || msg (-> message = MSB message .. LSB message) - randSrc := make([]byte, 32+len(message)) - copy(randSrc, privKey.randSrc[:]) - copy(randSrc[32:], message) - - // randBytes = H(randSrc) - blindingFactorBytes := blake2b.Sum512(randSrc[:]) // TODO ensures that the hash used to build the key and the one used here is the same - blindingFactorBigInt.SetBytes(blindingFactorBytes[:sizeFr]) - - // compute R = randScalar*Base - res.R.ScalarMultiplication(&curveParams.Base, &blindingFactorBigInt) - if !res.R.IsOnCurve() { - return nil, errNotOnCurve - } - - // compute H(R, A, M), all parameters in data are in Montgomery form - hFunc.Reset() - - resRX := res.R.X.Bytes() - resRY := res.R.Y.Bytes() - resAX := privKey.PublicKey.A.X.Bytes() - resAY := privKey.PublicKey.A.Y.Bytes() - toWrite := [][]byte{resRX[:], resRY[:], resAX[:], resAY[:], message} - for _, bytes := range toWrite { - if _, err := hFunc.Write(bytes); err != nil { - return nil, err - } - } - - var hramInt big.Int - hramBin := hFunc.Sum(nil) - hramInt.SetBytes(hramBin) - - // Compute s = randScalarInt + H(R,A,M)*S - // going with big int to do ops mod curve order - var bscalar, bs big.Int - bscalar.SetBytes(privKey.scalar[:]) - bs.Mul(&hramInt, &bscalar). - Add(&bs, &blindingFactorBigInt). - Mod(&bs, &curveParams.Order) - sb := bs.Bytes() - if len(sb) < sizeFr { - offset := make([]byte, sizeFr-len(sb)) - sb = append(offset, sb...) - } - copy(res.S[:], sb[:]) - - return res.Bytes(), nil -} - -// Verify verifies an eddsa signature -func (pub *PublicKey) Verify(sigBin, message []byte, hFunc hash.Hash) (bool, error) { - - // hFunc cannot be nil. - // We need a hash function for the Fiat-Shamir. - if hFunc == nil { - return false, errHashNeeded - } - - curveParams := twistededwards.GetEdwardsCurve() - - // verify that pubKey and R are on the curve - if !pub.A.IsOnCurve() { - return false, errNotOnCurve - } - - // Deserialize the signature - var sig Signature - if _, err := sig.SetBytes(sigBin); err != nil { - return false, err - } - - // compute H(R, A, M), all parameters in data are in Montgomery form - - hFunc.Reset() - - sigRX := sig.R.X.Bytes() - sigRY := sig.R.Y.Bytes() - sigAX := pub.A.X.Bytes() - sigAY := pub.A.Y.Bytes() - - toWrite := [][]byte{sigRX[:], sigRY[:], sigAX[:], sigAY[:], message} - for _, bytes := range toWrite { - if _, err := hFunc.Write(bytes); err != nil { - return false, err - } - } - - var hramInt big.Int - hramBin := hFunc.Sum(nil) - hramInt.SetBytes(hramBin) - - // lhs = cofactor*S*Base - var lhs twistededwards.PointAffine - var bCofactor, bs big.Int - curveParams.Cofactor.BigInt(&bCofactor) - bs.SetBytes(sig.S[:]) - lhs.ScalarMultiplication(&curveParams.Base, &bs). - ScalarMultiplication(&lhs, &bCofactor) - - if !lhs.IsOnCurve() { - return false, errNotOnCurve - } - - // rhs = cofactor*(R + H(R,A,M)*A) - var rhs twistededwards.PointAffine - rhs.ScalarMultiplication(&pub.A, &hramInt). - Add(&rhs, &sig.R). - ScalarMultiplication(&rhs, &bCofactor) - if !rhs.IsOnCurve() { - return false, errNotOnCurve - } - - // verifies that cofactor*S*Base=cofactor*(R + H(R,A,M)*A) - if !lhs.X.Equal(&rhs.X) || !lhs.Y.Equal(&rhs.Y) { - return false, nil - } - - return true, nil -} diff --git a/ecc/bw6-756/twistededwards/eddsa/eddsa_test.go b/ecc/bw6-756/twistededwards/eddsa/eddsa_test.go deleted file mode 100644 index 2c55f8000a..0000000000 --- a/ecc/bw6-756/twistededwards/eddsa/eddsa_test.go +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package eddsa - -import ( - "crypto/sha256" - "math/big" - "math/rand" - "testing" - - crand "crypto/rand" - - "fmt" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/twistededwards" - "github.com/consensys/gnark-crypto/hash" -) - -func Example() { - // instantiate hash function - hFunc := hash.MIMC_BW6_756.New() - - // create a eddsa key pair - privateKey, _ := GenerateKey(crand.Reader) - publicKey := privateKey.PublicKey - - // generate a message (the size must be a multiple of the size of Fr) - var _msg fr.Element - _msg.SetRandom() - msg := _msg.Marshal() - - // sign the message - signature, _ := privateKey.Sign(msg, hFunc) - - // verifies signature - isValid, _ := publicKey.Verify(signature, msg, hFunc) - if !isValid { - fmt.Println("1. invalid signature") - } else { - fmt.Println("1. valid signature") - } - - // Output: 1. valid signature -} - -func TestNonMalleability(t *testing.T) { - - // buffer too big - t.Run("buffer_overflow", func(t *testing.T) { - bsig := make([]byte, 2*sizeFr+1) - var sig Signature - _, err := sig.SetBytes(bsig) - if err != errWrongSize { - t.Fatal("should raise wrong size error") - } - }) - - // R overflows p_mod - t.Run("R_overflow", func(t *testing.T) { - bsig := make([]byte, 2*sizeFr) - frMod := fr.Modulus() - r := big.NewInt(1) - r.Add(frMod, r) - buf := r.Bytes() - for i := 0; i < sizeFr; i++ { - bsig[sizeFr-1-i] = buf[i] - } - - var sig Signature - _, err := sig.SetBytes(bsig) - if err != errRBiggerThanPMod { - t.Fatal("should raise error r >= p_mod") - } - }) - - // S overflows r_mod - t.Run("S_overflow", func(t *testing.T) { - bsig := make([]byte, 2*sizeFr) - o := big.NewInt(1) - cp := twistededwards.GetEdwardsCurve() - o.Add(&cp.Order, o) - buf := o.Bytes() - copy(bsig[sizeFr:], buf[:]) - big.NewInt(1).FillBytes(bsig[:sizeFr]) - - var sig Signature - _, err := sig.SetBytes(bsig) - if err != errSBiggerThanRMod { - t.Fatal("should raise error s >= r_mod") - } - }) - -} - -func TestNoZeros(t *testing.T) { - t.Run("R.Y=0", func(t *testing.T) { - // R points are 0 - var sig Signature - sig.R.X.SetInt64(1) - sig.R.Y.SetInt64(0) - s := big.NewInt(1) - s.FillBytes(sig.S[:]) - bts := sig.Bytes() - var newSig Signature - _, err := newSig.SetBytes(bts) - if err != errZero { - t.Fatal("expected error for zero R.Y") - } - }) - t.Run("S=0", func(t *testing.T) { - // S is 0 - var R twistededwards.PointAffine - cp := twistededwards.GetEdwardsCurve() - R.ScalarMultiplication(&cp.Base, big.NewInt(1)) - var sig Signature - sig.R.Set(&R) - bts := sig.Bytes() - var newSig Signature - _, err := newSig.SetBytes(bts) - if err != errZero { - t.Fatal("expected error for zero S") - } - }) -} - -func TestSerialization(t *testing.T) { - - src := rand.NewSource(0) - r := rand.New(src) //#nosec G404 weak rng is fine here - - privKey1, err := GenerateKey(r) - if err != nil { - t.Fatal(err) - } - pubKey1 := privKey1.PublicKey - - privKey2, err := GenerateKey(r) - if err != nil { - t.Fatal(err) - } - pubKey2 := privKey2.PublicKey - - pubKeyBin1 := pubKey1.Bytes() - pubKey2.SetBytes(pubKeyBin1) - pubKeyBin2 := pubKey2.Bytes() - if len(pubKeyBin1) != len(pubKeyBin2) { - t.Fatal("Inconsistent size") - } - for i := 0; i < len(pubKeyBin1); i++ { - if pubKeyBin1[i] != pubKeyBin2[i] { - t.Fatal("Error serialize(deserialize(.))") - } - } - - privKeyBin1 := privKey1.Bytes() - privKey2.SetBytes(privKeyBin1) - privKeyBin2 := privKey2.Bytes() - if len(privKeyBin1) != len(privKeyBin2) { - t.Fatal("Inconsistent size") - } - for i := 0; i < len(privKeyBin1); i++ { - if privKeyBin1[i] != privKeyBin2[i] { - t.Fatal("Error serialize(deserialize(.))") - } - } -} - -func TestEddsaMIMC(t *testing.T) { - - src := rand.NewSource(0) - r := rand.New(src) //#nosec G404 weak rng is fine here - - // create eddsa obj and sign a message - privKey, err := GenerateKey(r) - if err != nil { - t.Fatal(nil) - } - pubKey := privKey.PublicKey - hFunc := hash.MIMC_BW6_756.New() - - var frMsg fr.Element - frMsg.SetString("44717650746155748460101257525078853138837311576962212923649547644148297035978") - msgBin := frMsg.Bytes() - signature, err := privKey.Sign(msgBin[:], hFunc) - if err != nil { - t.Fatal(err) - } - - // verifies correct msg - res, err := pubKey.Verify(signature, msgBin[:], hFunc) - if err != nil { - t.Fatal(err) - } - if !res { - t.Fatal("Verify correct signature should return true") - } - - // verifies wrong msg - frMsg.SetString("44717650746155748460101257525078853138837311576962212923649547644148297035979") - msgBin = frMsg.Bytes() - res, err = pubKey.Verify(signature, msgBin[:], hFunc) - if err != nil { - t.Fatal(err) - } - if res { - t.Fatal("Verify wrong signature should be false") - } - -} - -func TestEddsaSHA256(t *testing.T) { - - src := rand.NewSource(0) - r := rand.New(src) //#nosec G404 weak rng is fine here - - hFunc := sha256.New() - - // create eddsa obj and sign a message - // create eddsa obj and sign a message - - privKey, err := GenerateKey(r) - pubKey := privKey.PublicKey - if err != nil { - t.Fatal(err) - } - - signature, err := privKey.Sign([]byte("message"), hFunc) - if err != nil { - t.Fatal(err) - } - - // verifies correct msg - res, err := pubKey.Verify(signature, []byte("message"), hFunc) - if err != nil { - t.Fatal(err) - } - if !res { - t.Fatal("Verify correct signature should return true") - } - - // verifies wrong msg - res, err = pubKey.Verify(signature, []byte("wrong_message"), hFunc) - if err != nil { - t.Fatal(err) - } - if res { - t.Fatal("Verify wrong signature should be false") - } - -} - -// benchmarks - -func BenchmarkVerify(b *testing.B) { - - src := rand.NewSource(0) - r := rand.New(src) //#nosec G404 weak rng is fine here - - hFunc := hash.MIMC_BW6_756.New() - - // create eddsa obj and sign a message - privKey, err := GenerateKey(r) - pubKey := privKey.PublicKey - if err != nil { - b.Fatal(err) - } - var frMsg fr.Element - frMsg.SetString("44717650746155748460101257525078853138837311576962212923649547644148297035978") - msgBin := frMsg.Bytes() - signature, _ := privKey.Sign(msgBin[:], hFunc) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - pubKey.Verify(signature, msgBin[:], hFunc) - } -} diff --git a/ecc/bw6-756/twistededwards/eddsa/marshal.go b/ecc/bw6-756/twistededwards/eddsa/marshal.go deleted file mode 100644 index c5a731b480..0000000000 --- a/ecc/bw6-756/twistededwards/eddsa/marshal.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package eddsa - -import ( - "crypto/subtle" - "errors" - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/consensys/gnark-crypto/ecc/bw6-756/twistededwards" - "io" - "math/big" -) - -// cf point.go (ugly copy) -const mUnmask = 0x7f - -var errWrongSize = errors.New("wrong size buffer") -var errSBiggerThanRMod = errors.New("s >= r_mod") -var errRBiggerThanPMod = errors.New("r >= p_mod") -var errZero = errors.New("zero value") - -// Bytes returns the binary representation of the public key -// follows https://tools.ietf.org/html/rfc8032#section-3.1 -// and returns a compressed representation of the point (x,y) -// -// x, y are the coordinates of the point -// on the twisted Edwards as big endian integers. -// compressed representation store x with a parity bit to recompute y -func (pk *PublicKey) Bytes() []byte { - var res [sizePublicKey]byte - pkBin := pk.A.Bytes() - subtle.ConstantTimeCopy(1, res[:sizeFr], pkBin[:]) - return res[:] -} - -// SetBytes sets p from binary representation in buf. -// buf represents a public key as x||y where x, y are -// interpreted as big endian binary numbers corresponding -// to the coordinates of a point on the twisted Edwards. -// It returns the number of bytes read from the buffer. -func (pk *PublicKey) SetBytes(buf []byte) (int, error) { - n := 0 - if len(buf) < sizePublicKey { - return n, io.ErrShortBuffer - } - if _, err := pk.A.SetBytes(buf[:sizeFr]); err != nil { - return 0, err - } - n += sizeFr - if !pk.A.IsOnCurve() { - return n, errNotOnCurve - } - return n, nil -} - -// Bytes returns the binary representation of pk, -// as byte array publicKey||scalar||randSrc -// where publicKey is as publicKey.Bytes(), and -// scalar is in big endian, of size sizeFr. -func (privKey *PrivateKey) Bytes() []byte { - var res [sizePrivateKey]byte - pubkBin := privKey.PublicKey.A.Bytes() - subtle.ConstantTimeCopy(1, res[:sizeFr], pubkBin[:]) - subtle.ConstantTimeCopy(1, res[sizeFr:2*sizeFr], privKey.scalar[:]) - subtle.ConstantTimeCopy(1, res[2*sizeFr:], privKey.randSrc[:]) - return res[:] -} - -// SetBytes sets pk from buf, where buf is interpreted -// as publicKey||scalar||randSrc -// where publicKey is as publicKey.Bytes(), and -// scalar is in big endian, of size sizeFr. -// It returns the number byte read. -func (privKey *PrivateKey) SetBytes(buf []byte) (int, error) { - n := 0 - if len(buf) < sizePrivateKey { - return n, io.ErrShortBuffer - } - if _, err := privKey.PublicKey.A.SetBytes(buf[:sizeFr]); err != nil { - return 0, err - } - n += sizeFr - if !privKey.PublicKey.A.IsOnCurve() { - return n, errNotOnCurve - } - subtle.ConstantTimeCopy(1, privKey.scalar[:], buf[sizeFr:2*sizeFr]) - n += sizeFr - subtle.ConstantTimeCopy(1, privKey.randSrc[:], buf[2*sizeFr:]) - n += sizeFr - return n, nil -} - -// Bytes returns the binary representation of sig -// as a byte array of size 3*sizeFr x||y||s where -// - x, y are the coordinates of a point on the twisted -// Edwards represented in big endian -// - s=r+h(r,a,m) mod l, the Hasse bound guarantees that -// s is smaller than sizeFr (in particular it is supposed -// s is NOT blinded) -func (sig *Signature) Bytes() []byte { - var res [sizeSignature]byte - sigRBin := sig.R.Bytes() - subtle.ConstantTimeCopy(1, res[:sizeFr], sigRBin[:]) - subtle.ConstantTimeCopy(1, res[sizeFr:], sig.S[:]) - return res[:] -} - -// SetBytes sets sig from a buffer in binary. -// buf is read interpreted as x||y||s where -// - x,y are the coordinates of a point on the twisted -// Edwards represented in big endian -// - s=r+h(r,a,m) mod l, the Hasse bound guarantees that -// s is smaller than sizeFr (in particular it is supposed -// s is NOT blinded) -// -// It returns the number of bytes read from buf. -func (sig *Signature) SetBytes(buf []byte) (int, error) { - n := 0 - if len(buf) != sizeSignature { - return n, errWrongSize - } - - // R < P_mod (to avoid malleability) - // P_mod = field of def of the twisted Edwards = Fr snark field - fpMod := fr.Modulus() - zero := big.NewInt(0) - bufBigInt := new(big.Int) - bufCopy := make([]byte, fr.Bytes) - for i := 0; i < sizeFr; i++ { - bufCopy[sizeFr-1-i] = buf[i] - } - bufCopy[0] &= mUnmask - bufBigInt.SetBytes(bufCopy) - if bufBigInt.Cmp(zero) == 0 { - return 0, errZero - } - if bufBigInt.Cmp(fpMod) != -1 { - return 0, errRBiggerThanPMod - } - - // S < R_mod (to avoid malleability) - // R_mod is the relevant group size of the twisted Edwards NOT the fr snark field so it's supposedly smaller - bufBigInt.SetBytes(buf[sizeFr : 2*sizeFr]) - if bufBigInt.Cmp(zero) == 0 { - return 0, errZero - } - cp := twistededwards.GetEdwardsCurve() - if bufBigInt.Cmp(&cp.Order) != -1 { - return 0, errSBiggerThanRMod - } - - // deserialisation - if _, err := sig.R.SetBytes(buf[:sizeFr]); err != nil { - return 0, err - } - n += sizeFr - if !sig.R.IsOnCurve() { - return n, errNotOnCurve - } - subtle.ConstantTimeCopy(1, sig.S[:], buf[sizeFr:2*sizeFr]) - n += sizeFr - return n, nil -} diff --git a/ecc/bw6-756/twistededwards/point.go b/ecc/bw6-756/twistededwards/point.go deleted file mode 100644 index edb6d19f0e..0000000000 --- a/ecc/bw6-756/twistededwards/point.go +++ /dev/null @@ -1,673 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package twistededwards - -import ( - "crypto/subtle" - "io" - "math/big" - "math/bits" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" -) - -// PointAffine point on a twisted Edwards curve -type PointAffine struct { - X, Y fr.Element -} - -// PointProj point in projective coordinates -type PointProj struct { - X, Y, Z fr.Element -} - -// PointExtended point in extended coordinates -type PointExtended struct { - X, Y, Z, T fr.Element -} - -const ( - //following https://tools.ietf.org/html/rfc8032#section-3.1, - // an fr element x is negative if its binary encoding is - // lexicographically larger than -x. - mCompressedNegative = 0x80 - mCompressedPositive = 0x00 - mUnmask = 0x7f - - // size in byte of a compressed point (point.Y --> fr.Element) - sizePointCompressed = fr.Bytes -) - -// Bytes returns the compressed point as a byte array -// Follows https://tools.ietf.org/html/rfc8032#section-3.1, -// as the twisted Edwards implementation is primarily used -// for eddsa. -func (p *PointAffine) Bytes() [sizePointCompressed]byte { - - var res [sizePointCompressed]byte - var mask uint - - y := p.Y.Bytes() - - if p.X.LexicographicallyLargest() { - mask = mCompressedNegative - } else { - mask = mCompressedPositive - } - // p.Y must be in little endian - y[0] |= byte(mask) // msb of y - for i, j := 0, sizePointCompressed-1; i < j; i, j = i+1, j-1 { - y[i], y[j] = y[j], y[i] - } - subtle.ConstantTimeCopy(1, res[:], y[:]) - return res -} - -// Marshal converts p to a byte slice -func (p *PointAffine) Marshal() []byte { - b := p.Bytes() - return b[:] -} - -func computeX(y *fr.Element) (x fr.Element) { - initOnce.Do(initCurveParams) - - var one, num, den fr.Element - one.SetOne() - num.Square(y) - den.Mul(&num, &curveParams.D) - num.Sub(&one, &num) - den.Sub(&curveParams.A, &den) - x.Div(&num, &den) - x.Sqrt(&x) - return -} - -// SetBytes sets p from buf -// len(buf) >= sizePointCompressed -// buf contains the Y coordinate masked with a parity bit to recompute the X coordinate -// from the curve equation. See Bytes() and https://tools.ietf.org/html/rfc8032#section-3.1 -// Returns the number of read bytes and an error if the buffer is too short. -func (p *PointAffine) SetBytes(buf []byte) (int, error) { - - if len(buf) < sizePointCompressed { - return 0, io.ErrShortBuffer - } - bufCopy := make([]byte, sizePointCompressed) - subtle.ConstantTimeCopy(1, bufCopy, buf[:sizePointCompressed]) - for i, j := 0, sizePointCompressed-1; i < j; i, j = i+1, j-1 { - bufCopy[i], bufCopy[j] = bufCopy[j], bufCopy[i] - } - isLexicographicallyLargest := (mCompressedNegative&bufCopy[0])>>7 == 1 - bufCopy[0] &= mUnmask - p.Y.SetBytes(bufCopy) - p.X = computeX(&p.Y) - if isLexicographicallyLargest { - if !p.X.LexicographicallyLargest() { - p.X.Neg(&p.X) - } - } else { - if p.X.LexicographicallyLargest() { - p.X.Neg(&p.X) - } - } - - return sizePointCompressed, nil -} - -// Unmarshal alias to SetBytes() -func (p *PointAffine) Unmarshal(b []byte) error { - _, err := p.SetBytes(b) - return err -} - -// Set sets p to p1 and return it -func (p *PointAffine) Set(p1 *PointAffine) *PointAffine { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - return p -} - -// Equal returns true if p=p1 false otherwise -func (p *PointAffine) Equal(p1 *PointAffine) bool { - return p.X.Equal(&p1.X) && p.Y.Equal(&p1.Y) -} - -// IsZero returns true if p=0 false otherwise -func (p *PointAffine) IsZero() bool { - var one fr.Element - one.SetOne() - return p.X.IsZero() && p.Y.Equal(&one) -} - -// NewPointAffine creates a new instance of PointAffine -func NewPointAffine(x, y fr.Element) PointAffine { - return PointAffine{x, y} -} - -// IsOnCurve checks if a point is on the twisted Edwards curve -func (p *PointAffine) IsOnCurve() bool { - initOnce.Do(initCurveParams) - - var lhs, rhs, tmp fr.Element - - tmp.Mul(&p.Y, &p.Y) - lhs.Mul(&p.X, &p.X) - mulByA(&lhs) - lhs.Add(&lhs, &tmp) - - tmp.Mul(&p.X, &p.X). - Mul(&tmp, &p.Y). - Mul(&tmp, &p.Y). - Mul(&tmp, &curveParams.D) - rhs.SetOne().Add(&rhs, &tmp) - - return lhs.Equal(&rhs) -} - -// Neg sets p to -p1 and returns it -func (p *PointAffine) Neg(p1 *PointAffine) *PointAffine { - p.X.Neg(&p1.X) - p.Y = p1.Y - return p -} - -// Add adds two points (x,y), (u,v) on a twisted Edwards curve with parameters a, d -// modifies p -func (p *PointAffine) Add(p1, p2 *PointAffine) *PointAffine { - initOnce.Do(initCurveParams) - - var xu, yv, xv, yu, dxyuv, one, denx, deny fr.Element - pRes := new(PointAffine) - xv.Mul(&p1.X, &p2.Y) - yu.Mul(&p1.Y, &p2.X) - pRes.X.Add(&xv, &yu) - - xu.Mul(&p1.X, &p2.X) - mulByA(&xu) - yv.Mul(&p1.Y, &p2.Y) - pRes.Y.Sub(&yv, &xu) - - dxyuv.Mul(&xv, &yu).Mul(&dxyuv, &curveParams.D) - one.SetOne() - denx.Add(&one, &dxyuv) - deny.Sub(&one, &dxyuv) - - p.X.Div(&pRes.X, &denx) - p.Y.Div(&pRes.Y, &deny) - - return p -} - -// Double doubles point (x,y) on a twisted Edwards curve with parameters a, d -// modifies p -func (p *PointAffine) Double(p1 *PointAffine) *PointAffine { - - p.Set(p1) - var xx, yy, xy, denum, two fr.Element - - xx.Square(&p.X) - yy.Square(&p.Y) - xy.Mul(&p.X, &p.Y) - mulByA(&xx) - denum.Add(&xx, &yy) - - p.X.Double(&xy).Div(&p.X, &denum) - - two.SetOne().Double(&two) - denum.Neg(&denum).Add(&denum, &two) - - p.Y.Sub(&yy, &xx).Div(&p.Y, &denum) - - return p -} - -// FromProj sets p in affine from p in projective -func (p *PointAffine) FromProj(p1 *PointProj) *PointAffine { - var I fr.Element - I.Inverse(&p1.Z) - p.X.Mul(&p1.X, &I) - p.Y.Mul(&p1.Y, &I) - return p -} - -// FromExtended sets p in affine from p in extended coordinates -func (p *PointAffine) FromExtended(p1 *PointExtended) *PointAffine { - var I fr.Element - I.Inverse(&p1.Z) - p.X.Mul(&p1.X, &I) - p.Y.Mul(&p1.Y, &I) - return p -} - -// ScalarMultiplication scalar multiplication of a point -// p1 in affine coordinates with a scalar in big.Int -func (p *PointAffine) ScalarMultiplication(p1 *PointAffine, scalar *big.Int) *PointAffine { - - var p1Extended, resExtended PointExtended - p1Extended.FromAffine(p1) - resExtended.ScalarMultiplication(&p1Extended, scalar) - p.FromExtended(&resExtended) - - return p -} - -// setInfinity sets p to O (0:1) -func (p *PointAffine) setInfinity() *PointAffine { - p.X.SetZero() - p.Y.SetOne() - return p -} - -//-------- Projective coordinates - -// Set sets p to p1 and return it -func (p *PointProj) Set(p1 *PointProj) *PointProj { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - p.Z.Set(&p1.Z) - return p -} - -// setInfinity sets p to O (0:1:1) -func (p *PointProj) setInfinity() *PointProj { - p.X.SetZero() - p.Y.SetOne() - p.Z.SetOne() - return p -} - -// Equal returns true if p=p1 false otherwise -// If one point is on the affine chart Z=0 it returns false -func (p *PointProj) Equal(p1 *PointProj) bool { - // If one point is infinity, the other must also be infinity. - if p.Z.IsZero() { - return p1.Z.IsZero() - } - // If the other point is infinity, return false since we can't - // the following checks would be incorrect. - if p1.Z.IsZero() { - return false - } - - var lhs, rhs fr.Element - lhs.Mul(&p.X, &p1.Z) - rhs.Mul(&p1.X, &p.Z) - if !lhs.Equal(&rhs) { - return false - } - lhs.Mul(&p.Y, &p1.Z) - rhs.Mul(&p1.Y, &p.Z) - - return lhs.Equal(&rhs) -} - -// IsZero returns true if p=0 false otherwise -func (p *PointProj) IsZero() bool { - return p.X.IsZero() && p.Y.Equal(&p.Z) -} - -// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d -// modifies p -func (p *PointProj) Neg(p1 *PointProj) *PointProj { - p.X.Neg(&p1.X) - p.Y = p1.Y - p.Z = p1.Z - return p -} - -// FromAffine sets p in projective from p in affine -func (p *PointProj) FromAffine(p1 *PointAffine) *PointProj { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - p.Z.SetOne() - return p -} - -// MixedAdd adds a point in projective to a point in affine coordinates -// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-madd-2008-bbjlp -func (p *PointProj) MixedAdd(p1 *PointProj, p2 *PointAffine) *PointProj { - initOnce.Do(initCurveParams) - - var B, C, D, E, F, G, H, I fr.Element - B.Square(&p1.Z) - C.Mul(&p1.X, &p2.X) - D.Mul(&p1.Y, &p2.Y) - E.Mul(&curveParams.D, &C).Mul(&E, &D) - F.Sub(&B, &E) - G.Add(&B, &E) - H.Add(&p1.X, &p1.Y) - I.Add(&p2.X, &p2.Y) - p.X.Mul(&H, &I). - Sub(&p.X, &C). - Sub(&p.X, &D). - Mul(&p.X, &p1.Z). - Mul(&p.X, &F) - mulByA(&C) - p.Y.Sub(&D, &C). - Mul(&p.Y, &p1.Z). - Mul(&p.Y, &G) - p.Z.Mul(&F, &G) - - return p -} - -// Double adds points in projective coordinates -// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#doubling-dbl-2008-bbjlp -func (p *PointProj) Double(p1 *PointProj) *PointProj { - - var B, C, D, E, F, H, J fr.Element - - B.Add(&p1.X, &p1.Y).Square(&B) - C.Square(&p1.X) - D.Square(&p1.Y) - E.Set(&C) - mulByA(&E) - F.Add(&E, &D) - H.Square(&p1.Z) - J.Sub(&F, &H).Sub(&J, &H) - p.X.Sub(&B, &C). - Sub(&p.X, &D). - Mul(&p.X, &J) - p.Y.Sub(&E, &D).Mul(&p.Y, &F) - p.Z.Mul(&F, &J) - - return p -} - -// Add adds points in projective coordinates -// cf https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp -func (p *PointProj) Add(p1, p2 *PointProj) *PointProj { - initOnce.Do(initCurveParams) - - var A, B, C, D, E, F, G, H, I fr.Element - A.Mul(&p1.Z, &p2.Z) - B.Square(&A) - C.Mul(&p1.X, &p2.X) - D.Mul(&p1.Y, &p2.Y) - E.Mul(&curveParams.D, &C).Mul(&E, &D) - F.Sub(&B, &E) - G.Add(&B, &E) - H.Add(&p1.X, &p1.Y) - I.Add(&p2.X, &p2.Y) - p.X.Mul(&H, &I). - Sub(&p.X, &C). - Sub(&p.X, &D). - Mul(&p.X, &A). - Mul(&p.X, &F) - mulByA(&C) - C.Neg(&C) - p.Y.Add(&D, &C). - Mul(&p.Y, &A). - Mul(&p.Y, &G) - p.Z.Mul(&F, &G) - - return p -} - -// scalarMulWindowed scalar multiplication of a point -// p1 in projective coordinates with a scalar in big.Int -// using the windowed double-and-add method. -func (p *PointProj) scalarMulWindowed(p1 *PointProj, scalar *big.Int) *PointProj { - var _scalar big.Int - _scalar.Set(scalar) - p.Set(p1) - if _scalar.Sign() == -1 { - _scalar.Neg(&_scalar) - p.Neg(p) - } - var resProj PointProj - resProj.setInfinity() - const wordSize = bits.UintSize - sWords := _scalar.Bits() - - for i := len(sWords) - 1; i >= 0; i-- { - ithWord := sWords[i] - for k := 0; k < wordSize; k++ { - resProj.Double(&resProj) - kthBit := (ithWord >> (wordSize - 1 - k)) & 1 - if kthBit == 1 { - resProj.Add(&resProj, p) - } - } - } - - p.Set(&resProj) - return p -} - -// ScalarMultiplication scalar multiplication of a point -// p1 in projective coordinates with a scalar in big.Int -func (p *PointProj) ScalarMultiplication(p1 *PointProj, scalar *big.Int) *PointProj { - return p.scalarMulWindowed(p1, scalar) -} - -// ------- Extended coordinates - -// Set sets p to p1 and return it -func (p *PointExtended) Set(p1 *PointExtended) *PointExtended { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - p.T.Set(&p1.T) - p.Z.Set(&p1.Z) - return p -} - -// IsZero returns true if p=0 false otherwise -func (p *PointExtended) IsZero() bool { - return p.X.IsZero() && p.Y.Equal(&p.Z) && p.T.IsZero() -} - -// Equal returns true if p=p1 false otherwise -// If one point is on the affine chart Z=0 it returns false -func (p *PointExtended) Equal(p1 *PointExtended) bool { - if p.Z.IsZero() || p1.Z.IsZero() { - return false - } - var pAffine, p1Affine PointAffine - pAffine.FromExtended(p) - p1Affine.FromExtended(p1) - return pAffine.Equal(&p1Affine) -} - -// Neg negates point (x,y) on a twisted Edwards curve with parameters a, d -// modifies p -func (p *PointExtended) Neg(p1 *PointExtended) *PointExtended { - p.X.Neg(&p1.X) - p.Y = p1.Y - p.Z = p1.Z - p.T.Neg(&p1.T) - return p -} - -// FromAffine sets p in projective from p in affine -func (p *PointExtended) FromAffine(p1 *PointAffine) *PointExtended { - p.X.Set(&p1.X) - p.Y.Set(&p1.Y) - p.Z.SetOne() - p.T.Mul(&p1.X, &p1.Y) - return p -} - -// Add adds points in extended coordinates -// See https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd -func (p *PointExtended) Add(p1, p2 *PointExtended) *PointExtended { - var A, B, C, D, E, F, G, H, tmp fr.Element - A.Mul(&p1.X, &p2.X) - B.Mul(&p1.Y, &p2.Y) - C.Mul(&p1.T, &p2.T).Mul(&C, &curveParams.D) - D.Mul(&p1.Z, &p2.Z) - tmp.Add(&p1.X, &p1.Y) - E.Add(&p2.X, &p2.Y). - Mul(&E, &tmp). - Sub(&E, &A). - Sub(&E, &B) - F.Sub(&D, &C) - G.Add(&D, &C) - H.Set(&A) - mulByA(&H) - H.Sub(&B, &H) - - p.X.Mul(&E, &F) - p.Y.Mul(&G, &H) - p.T.Mul(&E, &H) - p.Z.Mul(&F, &G) - - return p -} - -// MixedAdd adds a point in extended coordinates to a point in affine coordinates -// See https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-madd-2008-hwcd-2 -func (p *PointExtended) MixedAdd(p1 *PointExtended, p2 *PointAffine) *PointExtended { - var A, B, C, D, E, F, G, H, tmp fr.Element - - A.Mul(&p2.X, &p1.Z) - B.Mul(&p2.Y, &p1.Z) - - if p1.X.Equal(&A) && p1.Y.Equal(&B) { - p.MixedDouble(p1) - return p - } - - A.Mul(&p1.X, &p2.X) - B.Mul(&p1.Y, &p2.Y) - C.Mul(&p1.Z, &p2.X). - Mul(&C, &p2.Y) - D.Set(&p1.T) - E.Add(&D, &C) - tmp.Sub(&p1.X, &p1.Y) - F.Add(&p2.X, &p2.Y). - Mul(&F, &tmp). - Add(&F, &B). - Sub(&F, &A) - G.Set(&A) - mulByA(&G) - G.Add(&G, &B) - H.Sub(&D, &C) - - p.X.Mul(&E, &F) - p.Y.Mul(&G, &H) - p.T.Mul(&E, &H) - p.Z.Mul(&F, &G) - - return p -} - -// Double adds points in extended coordinates -// Dedicated doubling -// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd -func (p *PointExtended) Double(p1 *PointExtended) *PointExtended { - - var A, B, C, D, E, F, G, H fr.Element - - A.Square(&p1.X) - B.Square(&p1.Y) - C.Square(&p1.Z). - Double(&C) - D.Set(&A) - mulByA(&D) - E.Add(&p1.X, &p1.Y). - Square(&E). - Sub(&E, &A). - Sub(&E, &B) - G.Add(&D, &B) - F.Sub(&G, &C) - H.Sub(&D, &B) - - p.X.Mul(&E, &F) - p.Y.Mul(&G, &H) - p.T.Mul(&H, &E) - p.Z.Mul(&F, &G) - - return p -} - -// MixedDouble adds points in extended coordinates -// Dedicated mixed doubling -// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-mdbl-2008-hwcd -func (p *PointExtended) MixedDouble(p1 *PointExtended) *PointExtended { - - var A, B, D, E, G, H, two fr.Element - two.SetUint64(2) - - A.Square(&p1.X) - B.Square(&p1.Y) - D.Set(&A) - mulByA(&D) - E.Add(&p1.X, &p1.Y). - Square(&E). - Sub(&E, &A). - Sub(&E, &B) - G.Add(&D, &B) - H.Sub(&D, &B) - - p.X.Sub(&G, &two). - Mul(&p.X, &E) - p.Y.Mul(&G, &H) - p.T.Mul(&H, &E) - p.Z.Square(&G). - Sub(&p.Z, &G). - Sub(&p.Z, &G) - - return p -} - -// setInfinity sets p to O (0:1:1:0) -func (p *PointExtended) setInfinity() *PointExtended { - p.X.SetZero() - p.Y.SetOne() - p.Z.SetOne() - p.T.SetZero() - return p -} - -// scalarMulWindowed scalar multiplication of a point -// p1 in extended coordinates with a scalar in big.Int -// using the windowed double-and-add method. -func (p *PointExtended) scalarMulWindowed(p1 *PointExtended, scalar *big.Int) *PointExtended { - var _scalar big.Int - _scalar.Set(scalar) - p.Set(p1) - if _scalar.Sign() == -1 { - _scalar.Neg(&_scalar) - p.Neg(p) - } - var resExtended PointExtended - resExtended.setInfinity() - const wordSize = bits.UintSize - sWords := _scalar.Bits() - - for i := len(sWords) - 1; i >= 0; i-- { - ithWord := sWords[i] - for k := 0; k < wordSize; k++ { - resExtended.Double(&resExtended) - kthBit := (ithWord >> (wordSize - 1 - k)) & 1 - if kthBit == 1 { - resExtended.Add(&resExtended, p) - } - } - } - - p.Set(&resExtended) - return p -} - -// ScalarMultiplication scalar multiplication of a point -// p1 in extended coordinates with a scalar in big.Int -func (p *PointExtended) ScalarMultiplication(p1 *PointExtended, scalar *big.Int) *PointExtended { - return p.scalarMulWindowed(p1, scalar) -} diff --git a/ecc/bw6-756/twistededwards/point_test.go b/ecc/bw6-756/twistededwards/point_test.go deleted file mode 100644 index 14163e08c2..0000000000 --- a/ecc/bw6-756/twistededwards/point_test.go +++ /dev/null @@ -1,969 +0,0 @@ -// Copyright 2020 Consensys Software Inc. -// -// 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 consensys/gnark-crypto DO NOT EDIT - -package twistededwards - -import ( - "crypto/rand" - "math/big" - "testing" - - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/prop" -) - -// ------------------------------------------------------------ -// tests - -const ( - nbFuzzShort = 10 - nbFuzz = 100 -) - -func TestReceiverIsOperand(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - - // affine - properties.Property("Equal affine: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - params := GetEdwardsCurve() - var p1 PointAffine - p1.Set(¶ms.Base) - - return p1.Equal(&p1) && p1.Equal(¶ms.Base) - }, - )) - - properties.Property("Add affine: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2, p3 PointAffine - p1.Set(¶ms.Base) - p2.Set(¶ms.Base) - p3.Set(¶ms.Base) - - res := true - - p3.Add(&p1, &p2) - p1.Add(&p1, &p2) - res = res && p3.Equal(&p1) - - p1.Set(¶ms.Base) - p2.Add(&p1, &p2) - res = res && p2.Equal(&p3) - - return res - }, - )) - - properties.Property("Double affine: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2 PointAffine - p1.Set(¶ms.Base) - p2.Set(¶ms.Base) - - p2.Double(&p1) - p1.Double(&p1) - - return p2.Equal(&p1) - }, - )) - - properties.Property("Neg affine: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2 PointAffine - p1.Set(¶ms.Base) - p2.Set(¶ms.Base) - - p2.Neg(&p1) - p1.Neg(&p1) - - return p2.Equal(&p1) - }, - )) - - properties.Property("Neg affine: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2 PointAffine - p1.Set(¶ms.Base) - p2.Set(¶ms.Base) - - var s big.Int - s.SetUint64(10) - - p2.ScalarMultiplication(&p1, &s) - p1.ScalarMultiplication(&p1, &s) - - return p2.Equal(&p1) - }, - )) - properties.TestingRun(t, gopter.ConsoleReporter(false)) - - // projective - properties.Property("Equal projective: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - params := GetEdwardsCurve() - var p1, baseProj PointProj - p1.FromAffine(¶ms.Base) - baseProj.FromAffine(¶ms.Base) - - return p1.Equal(&p1) && p1.Equal(&baseProj) - }, - )) - - properties.Property("Add projective: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2, p3 PointProj - p1.FromAffine(¶ms.Base) - p2.FromAffine(¶ms.Base) - p3.FromAffine(¶ms.Base) - - res := true - - p3.Add(&p1, &p2) - p1.Add(&p1, &p2) - res = res && p3.Equal(&p1) - - p1.FromAffine(¶ms.Base) - p2.Add(&p1, &p2) - res = res && p2.Equal(&p3) - - return res - }, - )) - - properties.Property("Double projective: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2 PointProj - p1.FromAffine(¶ms.Base) - p2.FromAffine(¶ms.Base) - - p2.Double(&p1) - p1.Double(&p1) - - return p2.Equal(&p1) - }, - )) - - properties.Property("Neg projective: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2 PointProj - p1.FromAffine(¶ms.Base) - p2.FromAffine(¶ms.Base) - - p2.Neg(&p1) - p1.Neg(&p1) - - return p2.Equal(&p1) - }, - )) - - // extended - properties.Property("Equal extended: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - params := GetEdwardsCurve() - var p1, baseProj PointProj - p1.FromAffine(¶ms.Base) - baseProj.FromAffine(¶ms.Base) - - return p1.Equal(&p1) && p1.Equal(&baseProj) - }, - )) - - properties.Property("Add extended: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2, p3 PointProj - p1.FromAffine(¶ms.Base) - p2.FromAffine(¶ms.Base) - p3.FromAffine(¶ms.Base) - - res := true - - p3.Add(&p1, &p2) - p1.Add(&p1, &p2) - res = res && p3.Equal(&p1) - - p1.FromAffine(¶ms.Base) - p2.Add(&p1, &p2) - res = res && p2.Equal(&p3) - - return res - }, - )) - - properties.Property("Double extended: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2 PointProj - p1.FromAffine(¶ms.Base) - p2.FromAffine(¶ms.Base) - - p2.Double(&p1) - p1.Double(&p1) - - return p2.Equal(&p1) - }, - )) - - properties.Property("Neg extended: having the receiver as operand should output the same result", prop.ForAll( - func() bool { - - params := GetEdwardsCurve() - - var p1, p2 PointProj - p1.FromAffine(¶ms.Base) - p2.FromAffine(¶ms.Base) - - p2.Neg(&p1) - p1.Neg(&p1) - - return p2.Equal(&p1) - }, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestField(t *testing.T) { - t.Parallel() - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - genS := GenBigInt() - - properties.Property("MulByA(x) should match Mul(x, curve.A)", prop.ForAll( - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var z1, z2 fr.Element - z1.SetBigInt(&s) - z2.Mul(&z1, ¶ms.A) - mulByA(&z1) - - return z1.Equal(&z2) - }, - genS, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) -} - -func TestOps(t *testing.T) { - - parameters := gopter.DefaultTestParameters() - if testing.Short() { - parameters.MinSuccessfulTests = nbFuzzShort - } else { - parameters.MinSuccessfulTests = nbFuzz - } - - properties := gopter.NewProperties(parameters) - genS1 := GenBigInt() - genS2 := GenBigInt() - - // affine - properties.Property("(affine) 0+0=2*0=0", prop.ForAll( - func(s1 big.Int) bool { - - var p1, p2, zero PointAffine - zero.setInfinity() - - p1.Add(&zero, &zero) - p2.Double(&zero) - - return p1.IsOnCurve() && p1.Equal(&zero) && p1.Equal(&p2) - }, - genS1, - )) - - properties.Property("(affine) P+0=P", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var p1, p2, zero PointAffine - p1.ScalarMultiplication(¶ms.Base, &s1) - zero.setInfinity() - - p2.Add(&p1, &zero) - - return p2.IsOnCurve() && p2.Equal(&p1) - }, - genS1, - )) - - properties.Property("(affine) P+(-P)=O", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var p1, p2 PointAffine - p1.ScalarMultiplication(¶ms.Base, &s1) - p2.Neg(&p1) - - p1.Add(&p1, &p2) - - var one fr.Element - one.SetOne() - - return p1.IsOnCurve() && p1.IsZero() - }, - genS1, - )) - - properties.Property("(affine) P+P=2*P", prop.ForAll( - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var p1, p2, inf PointAffine - p1.ScalarMultiplication(¶ms.Base, &s) - p2.ScalarMultiplication(¶ms.Base, &s) - - p1.Add(&p1, &p2) - p2.Double(&p2) - - return p1.IsOnCurve() && p1.Equal(&p2) && !p1.Equal(&inf) - }, - genS1, - )) - - properties.Property("(affine) [a]P+[b]P = [a+b]P", prop.ForAll( - func(s1, s2 big.Int) bool { - - params := GetEdwardsCurve() - - var p1, p2, p3, inf PointAffine - inf.X.SetZero() - inf.Y.SetZero() - p1.ScalarMultiplication(¶ms.Base, &s1) - p2.ScalarMultiplication(¶ms.Base, &s2) - p3.Set(¶ms.Base) - - p2.Add(&p1, &p2) - - s1.Add(&s1, &s2) - p3.ScalarMultiplication(¶ms.Base, &s1) - - return p2.IsOnCurve() && p3.Equal(&p2) && !p3.Equal(&inf) - }, - genS1, - genS2, - )) - - properties.Property("(affine) [a]P+[-a]P = O", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var p1, p2, inf PointAffine - inf.X.SetZero() - inf.Y.SetOne() - p1.ScalarMultiplication(¶ms.Base, &s1) - s1.Neg(&s1) - p2.ScalarMultiplication(¶ms.Base, &s1) - - p2.Add(&p1, &p2) - - return p2.IsOnCurve() && p2.Equal(&inf) - }, - genS1, - )) - - properties.Property("(affine) [5]P=[2][2]P+P", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var p1, p2 PointAffine - p1.ScalarMultiplication(¶ms.Base, &s1) - - five := big.NewInt(5) - p2.Double(&p1).Double(&p2).Add(&p2, &p1) - p1.ScalarMultiplication(&p1, five) - - return p2.IsOnCurve() && p2.Equal(&p1) - }, - genS1, - )) - - // projective - properties.Property("(projective) 0+0=2*0=0", prop.ForAll( - func(s1 big.Int) bool { - - var p1, p2, zero PointProj - zero.setInfinity() - - p1.Add(&zero, &zero) - p2.Double(&zero) - - return p1.Equal(&zero) && p1.Equal(&p2) - }, - genS1, - )) - - properties.Property("(projective) P+0=P", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var baseProj, p1, p2, zero PointProj - baseProj.FromAffine(¶ms.Base) - p1.ScalarMultiplication(&baseProj, &s1) - zero.setInfinity() - - p2.Add(&p1, &zero) - - return p2.Equal(&p1) - }, - genS1, - )) - - properties.Property("(projective) P+(-P)=O", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var baseProj, p1, p2, p PointProj - baseProj.FromAffine(¶ms.Base) - p1.ScalarMultiplication(&baseProj, &s1) - p2.Neg(&p1) - - p.Add(&p1, &p2) - - return p.IsZero() - }, - genS1, - )) - - properties.Property("(projective) P+P=2*P", prop.ForAll( - - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var baseProj, p1, p2, p PointProj - baseProj.FromAffine(¶ms.Base) - p.ScalarMultiplication(&baseProj, &s) - - p1.Add(&p, &p) - p2.Double(&p) - - return p1.Equal(&p2) - }, - genS1, - )) - - properties.Property("(projective) [5]P=[2][2]P+P", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var baseProj, p1, p2 PointProj - baseProj.FromAffine(¶ms.Base) - p1.ScalarMultiplication(&baseProj, &s1) - - five := big.NewInt(5) - p2.Double(&p1).Double(&p2).Add(&p2, &p1) - p1.ScalarMultiplication(&p1, five) - - return p2.Equal(&p1) - }, - genS1, - )) - - // extended - properties.Property("(extended) 0+0=0", prop.ForAll( - func(s1 big.Int) bool { - - var p1, zero PointExtended - zero.setInfinity() - - p1.Add(&zero, &zero) - - return p1.Equal(&zero) - }, - genS1, - )) - - properties.Property("(extended) P+0=P", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var baseExtended, p1, p2, zero PointExtended - baseExtended.FromAffine(¶ms.Base) - p1.ScalarMultiplication(&baseExtended, &s1) - zero.setInfinity() - - p2.Add(&p1, &zero) - - return p2.Equal(&p1) - }, - genS1, - )) - - properties.Property("(extended) P+(-P)=O", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var baseExtended, p1, p2, p PointExtended - baseExtended.FromAffine(¶ms.Base) - p1.ScalarMultiplication(&baseExtended, &s1) - p2.Neg(&p1) - - p.Add(&p1, &p2) - - return p.IsZero() - }, - genS1, - )) - - properties.Property("(extended) P+P=2*P", prop.ForAll( - - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var baseExtended, p1, p2, p PointExtended - baseExtended.FromAffine(¶ms.Base) - p.ScalarMultiplication(&baseExtended, &s) - - p1.Add(&p, &p) - p2.Double(&p) - - return p1.Equal(&p2) - }, - genS1, - )) - - properties.Property("(extended) [5]P=[2][2]P+P", prop.ForAll( - func(s1 big.Int) bool { - - params := GetEdwardsCurve() - - var baseExtended, p1, p2 PointExtended - baseExtended.FromAffine(¶ms.Base) - p1.ScalarMultiplication(&baseExtended, &s1) - - five := big.NewInt(5) - p2.Double(&p1).Double(&p2).Add(&p2, &p1) - p1.ScalarMultiplication(&p1, five) - - return p2.Equal(&p1) - }, - genS1, - )) - - // mixed affine+extended - properties.Property("(mixed affine+extended) P+(-P)=O", prop.ForAll( - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var baseExtended, pExtended, p PointExtended - var pAffine PointAffine - baseExtended.FromAffine(¶ms.Base) - pExtended.ScalarMultiplication(&baseExtended, &s) - pAffine.ScalarMultiplication(¶ms.Base, &s) - pAffine.Neg(&pAffine) - - p.MixedAdd(&pExtended, &pAffine) - - return p.IsZero() - }, - genS1, - )) - - properties.Property("(mixed affine+extended) P+P=2*P", prop.ForAll( - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var baseExtended, pExtended, p, p2 PointExtended - var pAffine PointAffine - baseExtended.FromAffine(¶ms.Base) - pExtended.ScalarMultiplication(&baseExtended, &s) - pAffine.ScalarMultiplication(¶ms.Base, &s) - - p.MixedAdd(&pExtended, &pAffine) - p2.MixedDouble(&pExtended) - - return p.Equal(&p2) - }, - genS1, - )) - - // mixed affine+projective - properties.Property("(mixed affine+proj) P+(-P)=O", prop.ForAll( - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var baseProj, pProj, p PointProj - var pAffine PointAffine - baseProj.FromAffine(¶ms.Base) - pProj.ScalarMultiplication(&baseProj, &s) - pAffine.ScalarMultiplication(¶ms.Base, &s) - pAffine.Neg(&pAffine) - - p.MixedAdd(&pProj, &pAffine) - - return p.IsZero() - }, - genS1, - )) - - properties.Property("(mixed affine+proj) P+P=2*P", prop.ForAll( - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var baseProj, pProj, p, p2 PointProj - var pAffine PointAffine - baseProj.FromAffine(¶ms.Base) - pProj.ScalarMultiplication(&baseProj, &s) - pAffine.ScalarMultiplication(¶ms.Base, &s) - - p.MixedAdd(&pProj, &pAffine) - p2.Double(&pProj) - - return p.Equal(&p2) - }, - genS1, - )) - - properties.Property("scalar multiplication in Proj vs Ext should be consistent", prop.ForAll( - func(s big.Int) bool { - - params := GetEdwardsCurve() - - var baseProj PointProj - var baseExt PointExtended - var p1, p2 PointAffine - baseProj.FromAffine(¶ms.Base) - baseProj.ScalarMultiplication(&baseProj, &s) - baseExt.FromAffine(¶ms.Base) - baseExt.ScalarMultiplication(&baseExt, &s) - - p1.FromProj(&baseProj) - p2.FromExtended(&baseExt) - - return p1.Equal(&p2) - }, - genS1, - )) - - properties.TestingRun(t, gopter.ConsoleReporter(false)) - -} - -func TestMarshal(t *testing.T) { - t.Parallel() - initOnce.Do(initCurveParams) - - var point, unmarshalPoint PointAffine - point.Set(&curveParams.Base) - for i := 0; i < 20; i++ { - b := point.Marshal() - unmarshalPoint.Unmarshal(b) - if !point.Equal(&unmarshalPoint) { - t.Fatal("error unmarshal(marshal(point))") - } - point.Add(&point, &curveParams.Base) - } -} - -// GenBigInt generates a big.Int -// TODO @thomas we use fr size as max bound here -func GenBigInt() gopter.Gen { - return func(genParams *gopter.GenParameters) *gopter.GenResult { - var s big.Int - var b [fr.Bytes]byte - _, err := rand.Read(b[:]) //#nosec G404 weak rng is fine here - if err != nil { - panic(err) - } - s.SetBytes(b[:]) - genResult := gopter.NewGenResult(s, gopter.NoShrinker) - return genResult - } -} - -// ------------------------------------------------------------ -// benches - -func BenchmarkProjEqual(b *testing.B) { - params := GetEdwardsCurve() - - var scalar fr.Element - if _, err := scalar.SetRandom(); err != nil { - b.Fatalf("error generating random scalar: %v", err) - } - - var baseProj PointProj - baseProj.FromAffine(¶ms.Base) - var a PointProj - a.ScalarMultiplication(&baseProj, big.NewInt(42)) - - b.Run("equal", func(b *testing.B) { - aZScaled := a - aZScaled.X.Mul(&aZScaled.X, &scalar) - aZScaled.Y.Mul(&aZScaled.Y, &scalar) - aZScaled.Z.Mul(&aZScaled.Z, &scalar) - - // Check the setup. - if !a.Equal(&aZScaled) { - b.Fatalf("invalid test setup") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Equal(&aZScaled) - } - }) - - b.Run("not equal", func(b *testing.B) { - var aPlus1 PointProj - aPlus1.Add(&a, &baseProj) - - // Check the setup. - if a.Equal(&aPlus1) { - b.Fatalf("invalid test setup") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - a.Equal(&aPlus1) - } - }) -} - -func BenchmarkScalarMulExtended(b *testing.B) { - params := GetEdwardsCurve() - var a PointExtended - var s big.Int - a.FromAffine(¶ms.Base) - s.SetString("52435875175126190479447705081859658376581184513", 10) - s.Add(&s, ¶ms.Order) - - var doubleAndAdd PointExtended - - b.ResetTimer() - for j := 0; j < b.N; j++ { - doubleAndAdd.ScalarMultiplication(&a, &s) - } -} - -func BenchmarkScalarMulProjective(b *testing.B) { - params := GetEdwardsCurve() - var a PointProj - var s big.Int - a.FromAffine(¶ms.Base) - s.SetString("52435875175126190479447705081859658376581184513", 10) - s.Add(&s, ¶ms.Order) - - var doubleAndAdd PointProj - - b.ResetTimer() - for j := 0; j < b.N; j++ { - doubleAndAdd.ScalarMultiplication(&a, &s) - } -} - -func BenchmarkNeg(b *testing.B) { - params := GetEdwardsCurve() - var s big.Int - s.SetString("52435875175126190479447705081859658376581184513", 10) - - b.Run("Affine", func(b *testing.B) { - var point PointAffine - point.ScalarMultiplication(¶ms.Base, &s) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - point.Neg(&point) - } - }) - b.Run("Projective", func(b *testing.B) { - var baseProj PointProj - baseProj.FromAffine(¶ms.Base) - var point PointProj - point.ScalarMultiplication(&baseProj, &s) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - point.Neg(&point) - } - }) - b.Run("Extended", func(b *testing.B) { - var baseProj PointExtended - baseProj.FromAffine(¶ms.Base) - var point PointExtended - point.ScalarMultiplication(&baseProj, &s) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - point.Neg(&point) - } - }) -} - -func BenchmarkMixedAdd(b *testing.B) { - params := GetEdwardsCurve() - var s big.Int - s.SetString("52435875175126190479447705081859658376581184513", 10) - var point PointAffine - point.ScalarMultiplication(¶ms.Base, &s) - - b.Run("Projective", func(b *testing.B) { - var accum PointProj - accum.setInfinity() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - accum.MixedAdd(&accum, &point) - } - }) - b.Run("Extended", func(b *testing.B) { - var accum PointExtended - accum.setInfinity() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - accum.MixedAdd(&accum, &point) - } - }) -} - -func BenchmarkAdd(b *testing.B) { - params := GetEdwardsCurve() - var s big.Int - s.SetString("52435875175126190479447705081859658376581184513", 10) - - b.Run("Affine", func(b *testing.B) { - var point PointAffine - point.ScalarMultiplication(¶ms.Base, &s) - var accum PointAffine - accum.setInfinity() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - accum.Add(&accum, &point) - } - }) - b.Run("Projective", func(b *testing.B) { - var pointAff PointAffine - pointAff.ScalarMultiplication(¶ms.Base, &s) - var accum, point PointProj - point.FromAffine(&pointAff) - accum.setInfinity() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - accum.Add(&accum, &point) - } - }) - b.Run("Extended", func(b *testing.B) { - var pointAff PointAffine - pointAff.ScalarMultiplication(¶ms.Base, &s) - var accum, point PointExtended - point.FromAffine(&pointAff) - accum.setInfinity() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - accum.Add(&accum, &point) - } - }) -} - -func BenchmarkIsOnCurve(b *testing.B) { - params := GetEdwardsCurve() - var s big.Int - s.SetString("52435875175126190479447705081859658376581184513", 10) - - b.Run("positive", func(b *testing.B) { - var point PointAffine - point.ScalarMultiplication(¶ms.Base, &s) - - if !point.IsOnCurve() { - b.Fatal("point should must be on curve") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = point.IsOnCurve() - } - }) - - b.Run("negative", func(b *testing.B) { - var point PointAffine - point.ScalarMultiplication(¶ms.Base, &s) - point.X.Add(&point.X, &point.X) - - if point.IsOnCurve() { - b.Fatal("point should not be on curve") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = point.IsOnCurve() - } - }) -} diff --git a/internal/generator/config/bls12-378.go b/internal/generator/config/bls12-378.go deleted file mode 100644 index b30d9b0594..0000000000 --- a/internal/generator/config/bls12-378.go +++ /dev/null @@ -1,74 +0,0 @@ -package config - -var BLS12_378 = Curve{ - Name: "bls12-378", - CurvePackage: "bls12378", - EnumID: "BLS12_378", - FrModulus: "14883435066912132899950318861128167269793560281114003360875131245101026639873", - FpModulus: "605248206075306171733248481581800960739847691770924913753520744034740935903401304776283802348837311170974282940417", - G1: Point{ - CoordType: "fp.Element", - CoordExtDegree: 1, - PointName: "g1", - GLV: true, - CofactorCleaning: true, - CRange: defaultCRange(), - }, - G2: Point{ - CoordType: "fptower.E2", - CoordExtDegree: 2, - PointName: "g2", - GLV: true, - CofactorCleaning: true, - CRange: defaultCRange(), - Projective: true, - }, - // 2-isogeny - HashE1: &HashSuiteSswu{ - A: []string{"0x3eeb0416684d18f2c41f0ac56b4172c97877b1f2170ca6f42387dd67a2cc5c175e179b1a06ffff79e0723fffffffff2"}, - B: []string{"0x16"}, - Z: []int{11}, - Isogeny: &Isogeny{ - XMap: RationalPolynomial{ - Num: [][]string{ - {"0x2f304310ce39d2c3011a6d50eb4ece730cab541269dbc53c7594241b1c244eff01c0ce03cbe00000000000000000000"}, - {"0x9d9ea03fd9a908c76d1012fb4743eb0c720b5849c7b761ff1e3f31fc34200004ca4510000000001"}, - {"0x2f304310ce39d2c3ed885db0b1cc5b9e3043708b54c1a5cf20a52889c7b761fdaf1f98fe1a1000072f6798000000001"}, - }, - Den: [][]string{ - {"0x2767a80ff66a4231db4404bed1d0fac31c82d61271edd87fc78fcc7f0d0800013291440000000004"}, - }, - }, - YMap: RationalPolynomial{ - Num: [][]string{ - {"0x2f304310ce39d2c3ed885db0b1cc5b9e3043708b54c1a5cf20a52889c7b761fdaf1f98fe1a1000072f6797fffffffff"}, - {"0x7dd6082cd09a322f6a993378ddbf030e107849ad95ef7bbdbc611d64e38ea7c4e9cea46c7d00013291440000000002"}, - {"0x1f75820b34268c838ac8d9803d05ca3f43c5122b2366f9c76b7f1f7530b7fefd221e864e5f90000bf9aca8000000002"}, - {"0x370da3939b4375e4951f17f8cf6e6ae3384eadf7e2e1ec1c50c0af4b69009cfd4c4f87d31e68000861f8dc000000001"}, - }, - Den: [][]string{ - {"0x3eeb0416684d19053cb5d240ed107a284059eb647102326980dc360d0a49d7fce97f76a822c00009948a1fffffffff9"}, - {"0xec6df05fc67d8d2b23981c78eae5e092ab11046eab9312fead5ecafa4e3000072f6798000000000c"}, - {"0x7636f82fe33ec69591cc0e3c7572f0495588823755c9897f56af657d2718000397b3cc000000000c"}, - }, - }, - }, - }, -} - -var tBLS12_78 = TwistedEdwardsCurve{ - Name: BLS12_378.Name, - Package: "twistededwards", - EnumID: BLS12_378.EnumID, - A: "16249", - D: "826857503717340716663906603396009292766308904506333520048618402505612607353", - Cofactor: "8", - Order: "1860429383364016612493789857641020908721690454530426945748883177201355593303", - BaseX: "6772953896463446981848394912418300623023000177913479948380771331313783560843", - BaseY: "9922290044608088599966879240752111513195706854076002240583420830067351093249", -} - -func init() { - addCurve(&BLS12_378) - addTwistedEdwardCurve(&tBLS12_78) -} diff --git a/internal/generator/config/bw6-756.go b/internal/generator/config/bw6-756.go deleted file mode 100644 index 21ab364ccf..0000000000 --- a/internal/generator/config/bw6-756.go +++ /dev/null @@ -1,177 +0,0 @@ -package config - -var BW6_756 = Curve{ - Name: "bw6-756", - CurvePackage: "bw6756", - EnumID: "BW6_756", - FrModulus: "605248206075306171733248481581800960739847691770924913753520744034740935903401304776283802348837311170974282940417", - FpModulus: "366325390957376286590726555727219947825377821289246188278797409783441745356050456327989347160777465284190855125642086860525706497928518803244008749360363712553766506755227344593404398783886857865261088226271336335268413437902849", - G1: Point{ - CoordType: "fp.Element", - CoordExtDegree: 1, - PointName: "g1", - GLV: true, - CofactorCleaning: true, - CRange: []int{4, 5, 8, 16}, - }, - G2: Point{ - CoordType: "fp.Element", - CoordExtDegree: 1, - PointName: "g2", - GLV: true, - CofactorCleaning: true, - CRange: []int{4, 5, 8, 16}, - Projective: true, - }, - // 2-isogeny - HashE1: &HashSuiteSswu{ - A: []string{"0xf76adbb5bb98ade21e8fbe4eef81b6e9756798f2e64bff3cb65781179d6b076b17683f3cd5042655e16802b1a5b1b5b5e386e23e2731d24c843595d5c79ca4b8b9179cf10cb86e782d614a1f78930c25aeacdc30c0008fb417dfffffffff2"}, - B: []string{"0x16"}, - Z: []int{11}, - Isogeny: &Isogeny{ - XMap: RationalPolynomial{ - Num: [][]string{ - {"0xb99024c84cb2829c6f231ad6488f8ecbe73b35d177c03c95a1b5d4aff107d954726713be7dc02432d0d2d3f919a6b98b6ff46f95026f313a3f953d1776abdcc48d809cb0a92e2b7464ed6eab9312f612a13505b1d000072f6798000000000"}, - {"0x26bd1df0d7ae6c65ccce9c94a71497cd125c27c5fea6d502de92ee64cfe6161e0e05eb4aa16bb7f4c55960f84e03d6b986c05315e0dc296ea61a6ce0bfe7584381b90f97adb14083c7e63f8683ffffb35baf0000000001"}, - {"0xb99024c84cb282a010dde96a80e9b8571a99e3c121ae77cf5a598f3fd0abd199502d6d31fb5237042160e2f83bbff87df05586dc52cb529ee19d07248b4fbf241ffad1c2a6de71c88e46e4e3dbb1026d5ecafa4e300000000000000000001"}, - }, - Den: [][]string{ - {"0x9af477c35eb9b197333a72529c525f3449709f17fa9b540b7a4bb9933f9858783817ad2a85aedfd3156583e1380f5ae61b014c578370a5ba9869b382ff9d610e06e43e5eb6c5020f1f98fe1a0ffffecd6ebc0000000004"}, - }, - }, - YMap: RationalPolynomial{ - Num: [][]string{ - {"0xb99024c84cb282a010dde96a80e9b8571a99e3c121ae77cf5a598f3fd0abd199502d6d31fb5237042160e2f83bbff87df05586dc52cb529ee19d07248b4fbf241ffad1c2a6de71c88e46e4e3dbb1026d5ecafa4e2ffffffffffffffffffff"}, - {"0x1eed5b76b77315ce6c7800aef7b306952f8658ccae70a80831fd94f251e13a45b7ccc7290e7ae2e14ef34b51df3471733e5650ac56b2e140baada4fc20270074f385fcf816086d73d46b785d5a289f4a69c36293f7ffee097d04000000002"}, - {"0x7bb56ddaddcc5719024ebf85e3a0a46fefc545c5c0628b194a34c4ba6ac12eab1339f794cfc8e22966cea64f49ee8f4675ef712f878e58793870797ac6d90c77a7cc163e6cef3cd9dd88b97adb140df8fcc7f0d07ffff8d09868000000002"}, - {"0xd87d803f042598656902e5a6ebbb571049b389b6a74b8bc73ebdd1ca73731f32dd8a54ba4fdfeada26f108cc45b54c92edb91d566097e064073732fff7dd09aa254f4a0dc2ae2f69fb52b5b4804e82d4ee97795b380000000000000000001"}, - }, - Den: [][]string{ - {"0xf76adbb5bb98ae2ac127e1e3568cf5c978cd2fac2ce89fbf23221455163a6ccc6ae73c42a46d9eb02c812ea04faaa0a7eb1cb3d06e646e292cd15edb646a54302aa3c258de7ded0b685e868524ec033c7e63f8683fffffffffffffffffff9"}, - {"0x3a1bace94385a298b335eadefa9ee3b39b8a3ba8fdfa3f844ddc659737d9212d1508e0eff22193ef280611747505c2164a207ca0d14a3e25f927a3511fdb0465429597638489e0c5abd95f49c5ffff8d09868000000000c"}, - {"0x1d0dd674a1c2d14c599af56f7d4f71d9cdc51dd47efd1fc226ee32cb9bec90968a847077f910c9f7940308ba3a82e10b25103e5068a51f12fc93d1a88fed8232a14acbb1c244f062d5ecafa4e2ffffc684c34000000000c"}, - }, - }, - }, - }, - // 17-isogeny - HashE2: &HashSuiteSswu{ - A: []string{"0x2693fe3f93094f745f2f924da1a4260675024d398700e46db4678d6c77e732927310a52e43b73de56540c9dde1f7896d115a7661f0e25e7c54754324cb2ab353f985c8bdaccf0b3ae0e6ee49e40dd2b5d8aee87356b60e17f9de343033534"}, - B: []string{"0x4577fc0edab719863d99d99fa7737b795dd19e05021cf50e2bfaadf8d3cf9670ba09ab81773af807f37f714475a819c9dd47d9d9b93ec37c70f3b857252cd99cac4151e0b42eb00271779d66c0b0c79e7f450a1fd85b66a23256679146fcb"}, - Z: []int{11}, - Isogeny: &Isogeny{ - XMap: RationalPolynomial{ - Num: [][]string{ - {"0x57b49e22a638e7ce6fd84adb98fef2ee37a078dad5e6db136e00515b7db476b3092fe24c634db1c19b32784bd06db0e1bdbcc194bd2ea84b5485485423516adaf53426ca54ef27fe07de0370f3473b05fab20bba48a8ceeb8f39138734a79"}, - {"0x4eb16246adf77c244d7c96da07c828728ec8cf4f73b36f9d3b8638cd7d5f00932de546ce6d666b6c1c78a99b824336da2aa7cf8017843619aba11c4e20eed632ffbd4ed04676972f223e842b7d9f408585f879993e979cefce07f6b320f98"}, - {"0xb0fe2b7d5cc6757f09eb475d4ec4eb2892eec65ebb4d0b73dffe388dc8b625ae1094586e7d5f12d55eae19f405323e54346d6e75fe5e30ed86a66f3bd956f2e0cf3b68afc427260f3782828672bc312081de7f76b025cb18780f79ccbb931"}, - {"0x63ae87b32561b93fb20fe607e846b376b2db30eb8774a0c741bc2c2525053672f753b6ea1b420fc0817c91332c754ee91bbdf25a5e5fb852314dc28a26aef1a6fc6bae25a0285b8355a97b597283baeb152f6a0ff281a7a6029551336d19a"}, - {"0x8e95e735b9b8a53ec343d0960e4ee6f8d38aaadd78fb413533fb771a3be16d1dfb91d669d0fdf927aa93c60a147c23c6b26b66cab157b3b1381e64646aae66dfc53e2698852470de8fb92e17b4504bc43249a9a2e7330e3b5e25da416f46f"}, - {"0x5f0f6d5f0ff7ce8777840d1c4d25d85f9a63f04df2479c5b73f1a95a3caf20143e0bf4e9412a9d9d63de7b03be54cfe59522fa2405cd2b02340479f9c8776d6ea9fca2dea8d6d71f455162a6e9f40b76311257170313a8682db61095c7bb5"}, - {"0xce64c0bb237310b05120e54a6ce7ca932dc2d6411b9761463cf0c31e07d4caf5bac19db735631219b68c1dd00e79f19e2653d050137a01218f5b1371d9a276a8938dfd36a0565662a1c60e970b0ba6ca1e330ca446d4863f5c44cf57dfe70"}, - {"0x5c79bf1abe91cef7276af031994044074bd33da0fdde5d1356095b3d02fa2d46345e935838131a84e7408ef41afb492c5a5cf8256daa4be842a0c4e9056f1f3eae94b3d47a78956718cec82b21f3e51338e5e2dea165bb52ed3c3fef55e2 "}, - {"0x528d835a2e6a30781df335a708c54aeb2b71dea9a27564944950b4eb0d8298a44b86f039ac8815883395a340deb72664ce9ef1d01799fe56b8141ffa816ff7ce020b37944db6c438b78a9e78e96ccbc63cff0f7f7e9366ee17fcce48ded23"}, - {"0x778a6ab417356eb65d79c0a0a1cf6d67f3de72168b1ac6c13f3f619fbb33e1da96b6b4b7f07ab0d82d1702006321158b231b405cfbffe44d033ba590dd218224ea7c800dfa1f00dfc41bc8d4f7d6ae966107bf5589f8ba66cfa90c23216c3"}, - {"0x4147349d36fc97c1ff5e168c56d665d0b6f1a8a048eb903e134490eac3b6d62e9c4741b77e0290a585bfa7a1c0dc29fe0b8000728ae4d7f01a849fde08e5c268914f0dee8ed42612e1531f958cf84126358cd261871bded7381b133541604"}, - {"0xc471c9e9bbb09990d5e6c9f5017fe1382bcb0835464ce01b69391ec611babbeca074b376ad1d5ee9c49a2214a89824f408b8eb1dbf80b9851997a2d7f4fc288fb2ee7c3c4fa9d52283e977c924af4c360c5332fce61c340d6af7edef6cfff"}, - {"0x44d18369bb41e08dea957a700307d9929a58b5565bb4fc9b985b6e98047cf473b636f6afa8ddf5cbf053e02477a616339eaef3bdf402e782850a2dc76555e82e8e803f2aa39f76249929725c6ca5d292d4b10aee3535e3c9d9368f01216da"}, - {"0xd46505c4491681e80a79fca2f3899ddefc5c765972f1ff4f4d20c6b8b89b0be5decd3e700f31ca2a0a24759dab83fd694a850902b3e14d656e4864b6fc2bf1f4aac08ed4fd147884840cb0cea7f94b0f923fb94ce65f77b5992c71e1e5038"}, - {"0x6f9632f9f15470710c2004132ef3ad5f5600087a3195f06fa2a1e69c0aea80a94e43c427cd681cd23040668e3aef649d09455fa213ab6364c58327073c8c798963672d092a60943e08d17f05771a42e6abc0d30ef3f1ec16195ff2e6d455d"}, - {"0x9a51e637dd926867d26d4fea699ddbf56041fb4767c71b5cef365a2a944cb79879168c5a93b9fde6be4d414a4016b01b70c039253548cc08b8309388a1a511ebf1dc8f8b9c436cb21aa944c9240a0c1834fc199be1153717ea4ad6f5cf86 "}, - {"0x52852129f19b369553182b651e1a006b282bec349935ab9fd2b392c7e43de42da08efb80da828e5bdb508c3a396e6e22a4f596da57126b2c654700cca80014fab35dd74dabc755ea253556e3d6a77d3d93f85b80b56fa4d4011970a59dcc4"}, - {"0x1c4076fbf2dfd41be9c3e3c24375f6dc8ab18fa163e8f502566471ef2432f6472cfb668c7b4d2e7753eb5181083607b70cac77be553df58c3b26f757f1c70c45474e0c72ac735c9e176ee3f4a10142ce372df6465e1e1e1e1e1e1e1e1e1e2"}, - }, - Den: [][]string{ - {"0x67ca0a05ba18be2717328e9da3d366da18fe119dbf0c150dd6e49daa6ed4f8de92e85a04bc69f045366c80877116759af4bc0c2a6f49ec158f0a2ba5e118ba6d26ac9f2821aa917978d4a606d940397818481adb5ab3f2c9670d2faac21bf"}, - {"0x461d07431e8397832b6ae980dc75548271ce6a87465b5ea5292316b8b1eb078b69e551472f41df43806e24ad887fcb1f551938ec0efc00499f7c3ba5bf7d8ec6d63a4ddd274aad64d716307d11a0ddb9d1c0f40296317c29455075bffb2ba"}, - {"0x43635d242d3fb85cfd3a36bfa1e3628afb5e1e14ae9f67f5ab735f8f6723bc48ba9c507c50e703220244876a482f00f4d7f2d022eccac8301995542df93cc151c10b4a622565832b8793de295ab227054118d673efecd6c50fea7b111606e"}, - {"0x86095d084f72a9be4c483ccc2c746e6ef6e26720fe2700df898dd5a5b20d6a832e2025d2d00a8447279691db55bea69a97e63e4acff6b5bc3254c3fa00017cb0adf4980a85af177485637c02ccc568a182ae232f54eada9391e3d98ed5ffe"}, - {"0xb017fcf26b80fc4af09015d106339becd5762ccb32eae4554d8eac08772a9335ccb7a8a916bd84caf4bcd4d5e53ad1ddf931deda6d5bc04c2df1682d31c696bf2321a2f005e77db814de79b98353ea4625962b9e30e78311ec0d9825e1f61"}, - {"0x45fb599a0f0a1dc49bb19803f84cb2e31e0c7c36dbdd187ab5bd8f024061a702d3e9dea95dc499b77fc32f4824fbaf91d82433dafbec16e1c977b20efd359e1334357a6a5b2d4b5c9452e665fc212908db0d18cfc0b5a5f3706b47c8868d1"}, - {"0x6b33147c7484eee118a499d769e8cd5067e77bae02abf17f8a47b040d39891089ffc19052ed287404aa3e3d2eb7787cd08bb0bfcc1c990a73d649cbfeadd6c4cf9ae2580101c1a1f45a90d32b0ff13e02b5836642634c7e952b6486d29f8 "}, - {"0x387e570f75f5fa085d262c1cb47c27f872beec94798787b8ea11debe75533ea5b0031eb544968899daacb97792dc67039eabcb6e64559a202f2529af243b2d7f993cf4896d9cd1af66b9cdf2753e483999115c0caa96403d360aa9841aaae"}, - {"0x1257e076f478ad94029dfb12ae9d928ce4550151c5a02aac92329f2ecfbdbd0a41567dc38e5fe5a0d5d3adcee3d1d84639145de0de9644244c34c031bd5d564a483ca617084e2829bd8e898733dfb99cc3cb655161d5854f687d9592c67de"}, - {"0xcb293e9849238ea68db183154dd3318b7a97d2c8b5911164a377e42468284ac8f489f97435e2ae5587efc9390ec8fe2dd55a466388491e21b477cf483333fa4f481bade0f0a80dc35c4d967dad4a5aaa1864323a8ab11f376eb6ee6440cd5"}, - {"0xecfd07101cf66ec654369b1dab1bb19588285083358b3f41fbcfa9a8667bee9f2e009a65b4588590ba71ba35bfbf3f11e0e38cfc02bf8e01475be7ce2fce4a6fcc8aaa3aae1d4696fcf86deff15d543c992e08aa4ef0822fa2da7d3dd82b9"}, - {"0x505e4b45c2fd0610b6de37cee0cb9a6e5434fe87b45190ff65418b316e4852a031583c3b5c28b4f78e5cdebbd7ac1675f1bbabb6bbd07ffad253349ed13c6a1e605856fc83288739254e877b0cca8c895a2119d0c9371fc59deded45cb1b4"}, - {"0x4356cbf741137297e9995a239f126c8e69898333c8a0891ee9ae4acb9f66cd1fb8b5c550d54b3d3516d27fc5ebb31b938d50d18a5d1fd92c4fbc246ae3a12886bab0e3b38692d26b5cc13372389c7c65df91ba04dcb040f2c8356bfb78783"}, - {"0x2d30af585d5961e587be65e1b61869b980d0a9a66510b0013800455621c06ab17bbf7cdb49df67795dba802f4a5dc215300a3a409408de7801f2f0a8e5b3587d478f94c523087fda1b7d9fce00d99ad124ae782dd5a6033e14141ae7af925"}, - {"0x89ce51dbfa7cf438f4174452a0159618713a55916e86698c00023059f53812346d2e996cd2be2ce603938b6e28add4df16fac31411400aaab469b668d9fc0efe45b04c071a2e09243a6f1c24fd2927823ba1b82803796b4c5ee8d14b6eca "}, - {"0x60380c3366f74e8a5f5447e6867e4d6b0ca3c6d01e5ad7bfaff113c053f5cada2ab35379044733a1e57cd19cf5ae16240e7bde52e621adad86a454c400381cf47f872d5b7ccf190cddc4a5477a932bd4a5e22534d30913553db82af7238e4"}, - }, - }, - YMap: RationalPolynomial{ - Num: [][]string{ - {"0x9f93a11a37f333a5052a497f2ff8959bc7b3e69bd5f470445f01d6071d144dbe992d4ea3783f0fe7d017f5dcc1e6d2980590726773e6b4bd7bc10cd999e2c38c2aabb90c484462c378b64918c1a48e51921fb837ac5feba926c3f623c4160"}, - {"0x577ef753befab81d08fb0ce7402dc357e7dcd98cd569530135f3ad339845cfcceb84f041cab594864b56060469cc8e0cefa322613061cf50bb7f8b3adba1caa0a522a5df2481f64cc0fbde989a13c70a8168d6cd40a30a342c186f229597 "}, - {"0xa68f14dfeb538700dd4d74482406ffae6be6396e04519ff32a9d0962b7250cd4a6a14a17fbc36032b9a79cb3f79b06d9918b4a34b8302120baf4757568bd3987d5bad8222ea01116c54237a31599acdc600517d3f733f5150930c07a9b8ac"}, - {"0x62f40704a8cef5860a7c1a0e953d8ab65ae5e0cb6e49d58da6e2501eabf797a77b043818a3bfa545a51060e24c3b7c2773412e527e1b72acf2044d95548b72836851a635e50c4c24b35c5a769b985fe464d857d16cecf2f8060374067589b"}, - {"0x1930bec080bcf38ef774f04e675193fc123f266b80e62e1b294b0978abeb31b3afcb02e33f65309399d12e3803fa052098b473eea25be9b9064c8821c0bbb90d9eba711b1dfddf00c2ae407d79382ccad4c6df7d43c160ed257138fdbd403"}, - {"0x65e5a193d3016af59101e156a7a886a3d15bfebab72190e437dcd4ee1a42a17f739c140aead0cf987e922daaf716fb7b36b61e0996fe6abaa4656b8a7f5b87f41ab28135a032b51957e955ce8efb83242cc62fe08a71dd8065c66720c000d"}, - {"0x7efada35b7158ff27d88e0ebb8775bb5e3c7f4224e20a223bb2acc78e0176802b998265a7d6473b22171bc9a57bf3943ebf43f71fd0f9006e5a7af6e6e76f11b949e7adb6b7d252bb4c36338ad539a9a7c77cea84280e7cd2964e5591d226"}, - {"0x29eb281af9b44d21986ba03590afd087e477f3cad3234825f131a31eab2adb59ed830aeab5e5156f8c5de90f81c12d8231604c9e25c4b358501edef95ed2926a79bf118f22d9c4f6455c8c7e37d618c9bc16c46c974437daf821bbd839df9"}, - {"0x47e261e740726acbe699eac8c486186ec6492f0adc2b3dc99615e874b4b7e093bcdafe1075392c49bca1ed7d4d043e2798c971e73535be10e8f4d6aa23b3bed69de7e75a8d3a8373e8ab0621582fb7cc43d2643095843d47003dccbecfc7d"}, - {"0xbd7d4e63943690a235389a6bb5d448042414a1fd9dcf42116515f6f7e5dbaf168a9830e70255aefd2bc14ca35eeb922039ab85ba718d3e6d23d86b08381e413ce18ab5c378c43675af0695ee62eb42790c5d5f89c2d0db6460678d5d7d2ed"}, - {"0x41c2f299e34019cb40e2af5a6bded90f03c9e6e0cbdd853e783f4f3d95935d39ee31fbb61d17610387b84ba9519c16881183a72c59fe7383aa5ae217b42b7480f8f058ad146edf6b4a7549cccea967ded9fd28d150274cee458208907ced2"}, - {"0x405da38064d76408c3adde67a954882f3c8fd36eabe3201a9dfd75629c8f4036c1d63831453109f54cfbc4cc8b9e5f663d3db51eefcefbce9b533b82e005bd9ee7af344ca3dcc40775a3fd24231ad6aa8ec93782ca5005c54856f0d85c04a"}, - {"0x5bafa78c01429499eed6e25e9349a6ca3d2e0ac1267e137174d00a6c433c6095e3d1f312fef70a5703698371ddd1c06e302261b4f6069ec4b78ad4e3dcb37b5253c0787566ba225638b446092283e42cbe85907b5ebff3b33c270a868f0c "}, - {"0x39c152b20238115dd4e6df5508cab1e2c8a7ee9eefdc292f058b6f89586654c8b3586b6f76e713ba51824ca6e2e3ac7a381a8f1746d7d5e500312a7c8513c46a5736425da606f7b9d34fb95df8e54ba0e7cdaef0079df1eced56133412cd "}, - {"0xfeac73088d841e8e555bfc8117068298c2aa3b88c0172c8339c4c507dc212472eccc77267a6946c0c4c260135b4a5f0665194fdf129bc15fc9623987abc4b4d251feb6696737b390dd48da1ee8eae6123b350c4356ab64e534709643ac84 "}, - {"0x6a9a250e9bafa906b1ee1517ad74a816a62c5706d13b5a198fbefb1d2d3dbda735841e67a0adc0ad30495670bd2b83953c65b19aa017ee928b6d5d195b3d16590edf554767224c79b07e803db87acc6a54b60d71b15eb75702f9168c5181e"}, - {"0xe1aeadc4bbd21621ff33dcea2e0ec99a6ea667741cf62af0494afde045b4ccb713836169de252f3018df60a9b42972f3ce650e5fd93a8e06e4fc281fead12a631e834d5753f175a5d76c2c3582caf46cdce6228ce2e767d59573c748ffe55"}, - {"0x7ed20aa5207c66a812970c473f270e99e5974d86d49be1e1976f669388cd568cf799f5c57d9a251960bad6c97dd3fc335eb51b4852cca361a95fc3eff218be4b9b46bfebfc552521659092156b85da783cc46a24ba5d1c1af9ddf017443b0"}, - {"0x7235fb0eb41c06b940ccf7b11788831523a2347562d30c2b147b7381f37a4a63dbae4794c8bd7d0f681bf17aa86221277496fd257af5814f73c8303e1ba09fb80ecf6627b00d453dc712e67aec504f7ece5f39d54f49f6212e792ec1b67dc"}, - {"0xba8193a4d3130aa424d39453c76c8ed262b2e2e0d07f453061b253ce779e1ba0a03f90d00a58f8d08c681204aa771512b68cd12d2dd4e7ee320514a2d5f56a1b8dd2a33c1d4d60a503cade7e0eafd341d8ac667b1c8ccee77ee263808a2c0"}, - {"0xf58592db221a5b17116ecd2bf86c7cb449dd623e49bf58ccb63baaf1cfbaae68778caa8b2f5d9b9211636f79b7ef12b9aa2155c4ef99276190af85a5121b9beccd7c747e153187c668b1846a179312b7fdccb9fd8273ffd3b058c2a97e99f"}, - {"0x2fb957ed355d384286b2f6153caad9da4cd75da2f960d2b0162badab4e6bb653f08a4ba1c93813fd4bdc0ed7ff19073696c16685bc73c7924f3f094e19db49cdce7365a991e22911fe27b1ddf631c5f8775210eee3de31057c27f397c5db2"}, - {"0x9a597c03b1977869ca1a58b04c6c7b5a8136d673428d8077c01fef3fcf1b7661dd2c9a0a9f258d60ac5cddbaddb91dc17e171cb4a48d32d3eaf5be484430496052e4e835781bbdb2f52aa838da2b488143dc2266adbcc6dad98b7bc422c65"}, - {"0xe19735baa44196f6aa3be94274abd8e8669acdf6e9d985820bf1adf34d66305e0a9c697ae0996b67513553a1a5b6e2c465895925873497d56cb7796a3a3fb58bea27d51180a658bcd7d81c7a86577ed8ed84b61484b70703a5be7ae16fce5"}, - {"0x3be0b32a85b89ede867e519b45827587ac1c9b3201628e5a499574e5da2edccadf907d9f793c735da5ff0fc3e60d3550653e133c3d1b467b5951ac65f8ac41b50e3a2e76024288847449f0d35d73d783c6a1c167f67a3e01c5894d10d4986"}, - }, - Den: [][]string{ - {"0x6fce5ce514cee5debaed37973056b6a3edfe727211c6e6a1fab570eed4f2a508e96d6b081ff4a1b1a8acdf79fd0bd33c663045955b8cc064b8b92b7bbfd15fec96bb04be80bae195d8d9d51c240630f776a199d13ecad6bc53f75189d614d"}, - {"0xe0962cd218121ad29e5d7490bf192c86380cb1f4aa91a0e3a86aa73be92a1c3e8a440a35f0248ee807f47a4239a421000433a638d113dca21313ab532984ee2abf3b38d1170fc51c7ae131c57a7449c65cfdd78de9c937528723d0712be77"}, - {"0x1ccc8c1978c03d019911928c1fafaafbfd603ad5590c6854d54c3ea722f18a19020e256e2d2f66418fd103b42765a5195c910f10097fe452c4ba4b5d5ce5a67ee3adde1a6b2cdf3b7721da38f676e97788cc4c64e2e7962fb81bbdc0f28ba"}, - {"0xe22f7cbf189dff65136339d090ea28fc60f120cfa26dd8433c6960b418362be2336be5e929dcc61beb4412dcb232db75be87f17b02742c2206620c090b9799e3ed9d988b69316875970f19cf14388a4fde13f176355fd16b702a250a65024"}, - {"0xe8f52882ce0a0e52ca01be901b93d21fdffae3d31b385750e422cc08cf926c7958a67728e2d26aae7ebf673a840d9d1f8aeb9e58a379f690461f8100817b8185a3e87401b650d473bea9733277bd2166d9f1d7f6fbe6711012bb354d2e636"}, - {"0x875fe99541a3b4273eb0e06ca68021e7b484844fe1276a3dd6c1ca17fff7bfa81a7fe367de671cbfe1498baea048a898dfd77b2d7ffbd4bbf204f862d8771bd35197f0575f6ddaf2dd8915747726e2aae3ddd49c58e47d732e7dc74aaccfa"}, - {"0x4dfd80356811a35a65acb00c46d07a392658904a4fc0cbda29046c44a833a31ed9850230c5f4ed0c1e1e76576ad104d193292821897138fdb5700b90c70041f6f5570cba9b4f4d7def07d469b4c892e87d2ec709ba01068510b911e9e8ad3"}, - {"0xa7f230aa84f2547cbe501721f030ed5bc4376f897cc22d1df86da38d73b27c3903e3e44109d24de87f66a029b5cfd5f68fa440f57de9533515f9912fc130ee39f65d7fb8da98d707cbcb268a3f32cb3b40ab7dcac9128d916a20625e28985"}, - {"0x7f35f1b0caeeffdd8711317415c54cfc5a1a04ab3cd08e6b4415b78b5760ce5dc99acd7c5a4a4c1238c478461e7b79c4f09bc4b7c9c42468fabcfdf142db1403d5b01a4cd818594dfaf729a18514e62aeb7a3733765836d2a9d893325c7a9"}, - {"0x9bce845eb9ff707815ed7ed3fb74d199d492671d29a3bf2072594b9154f530a311514634ef4fb471e883c854dde519b85f679d07aea459f7c3870ba31b1bd2c3548152cc0ce70241c24572d7eb65069b1a97f2bd155a4185ef91ea5659b13"}, - {"0xf0e88310ee09722be8558b6ee541afb4441bc1e77ae82ebcd5d792f6330980c3d36914d73138f0da01be4580c0c2188c0e2a010746b596d80755e71d07028f78bc29af82e7e5be87fb3430b1696810ea95758f3924d846cc0192a1e4f6e10"}, - {"0x8237fb8a7d102cfa45e7020363d892aa7380d1b5ee84b792c65fc2b167552c67623578c416e9922772f3ec99fddaef25907d91bbf0c72b8f0a0070a4d635a1ccdc78a8c6d4a175ed00a68a1eb0e7b90a6a5f33c6555486c77d4aed026ebaa"}, - {"0xacc8c17e87078f38e1056204eea4bd9343903c88966d0ddc2ca5166a97a99db4b66f74480eaf6030d42f8f23f00f1e7a1a6d13f086a76f04f14e98f12917de8d7998089c48bd860301813a95f708c6bd0417acadf80cb9831ee34286cfea8"}, - {"0xa9c3c892d49a5c3e506bfa242431f2b9f3934c51c40045e40478e1f4120da439ba6f0cc154e80bf1ea7ced993b7310545b6af11a6b3bbab611e648afe77297a776ef9468f51706bfbf60a100b53571a0a7361cb592a4f3000a45c013c950f"}, - {"0xeeade4edfeeeef2bc9f34296787829c99b544f11722d55db8789550c5e005ae27d651137cbdf9cb95ca030e3d84544dd21d7dca37bcab9f0e1ddc4cf633b5986746b0371648bac79367a9512396e9789031acc705cbff8c6d15d828c6bdd8"}, - {"0x6b6c82e8a5d0faec7af39bbaa40d86ea47acaac61910aaf01c238d8ac0f2587e2f993835b77e0312a3d23a2da61cfe7d68c91af4d0e24dfa29d5ce2c470c29c5ff6d89f060a0c0546daa1287fa3ab18a20cc1666ad84e3343d25997269773"}, - {"0xdb9102798323f938aa773da2a56ebce58aee6da13e2b10b8a56bea0398ad2d5fa0585d35f7b8b0e9076577c6e36ccf6f9d12e9018bbc8424d38620b874e24d4265bbf27fe0940ddd713e2bf225f0cac7a82c92516c75fc6d88cdbbdd58ec0"}, - {"0x3ad0edad38f3ac9e11e7c078c6e80cf48879547ea9efe567d0fcf53151b71697643a4940c375e6593b96a3370c56964ea3fc16bb574197886c0d9da0064eeb7e4121296069a4509b10f97a4b4a4943d083dbe0a221465711128d1778f332 "}, - {"0xc23a66e6ae7cebc4e55e5a301aa0bb92932e809fc326fc2a9e5ca5711241c8c77313b93e8065e8ecfeb3765a53e2b48bbbd6e27b960e76d00dd5f212f51c2381f6571a0a2d1bdb232ae1fea083b5573ebc76b53c5e8c53434a3e0ba80c3e4"}, - {"0x9dea557e9d9328f93a7a014265d3e7bb51164ef0a4eee3dd1219b918c457f7f0818521d498b13552933f9ead5b0b48d6b3db282c72ec06d7ca1721d3732f3fb59c6c655f3526db0cc833984a0f4957f312abf88133767cad4485c2f9b9af2"}, - {"0x9497a4421814871b03a96cb5d6b294880db80435d857c2fe9f4e97d75b37c795a1728121d66ce742793f98090d0c894f715a7e81dce84f594369b8bb2b9be0d482436c1ceffdc50efae8d85be760beecec788b5f1b08180dfe69567a0c102"}, - {"0x9ca415b1068cafcb60219e17f1350a1ba427a276689247978356bc51e19fbc7bde4a260c33e175dfc83ab230d17ed24c88d8de8c145213227d443192eeb2e1e4c61b7be341bd88638d5b44cedf93d445b344552c004f4b02bcc04b4c235e5"}, - {"0x2a27787477dddb1679fedf03a1e406b8df8cee954e12fd31e027d2d20f5dd6ffe6dc92a5c9ab16be8f21439232809c6187cdae5a5c6fb2f48617d22f6f2a719c5fa6125df02e551fc5c88bf21ff67de5c3cae2f8553bea742fdb7cc29c9cd"}, - {"0x9054124d1a72f5cf8efe6bd9c9bd742092f5aa382d88439f87e99da07df0b047400cfd35866acd72d83b3a6b7085213615b9cd7c5932848449f67f2600542b6ebf4ac4093b36a5934ca6f7eb37dcc1bef8d337cf3c8d9cffdc944072b5556"}, - }, - }, - }, - }, -} - -var tBW6_756 = TwistedEdwardsCurve{ - Name: BW6_756.Name, - Package: "twistededwards", - EnumID: BW6_756.EnumID, - A: "35895", - D: "35894", - Cofactor: "8", - Order: "75656025759413271466656060197725120092480961471365614219134998880569790930794516726065877484428941069706901665493", - BaseX: "357240753431396842603421262238241571158569743053156052278371293545344505472364896271378029423975465332156840775830", - BaseY: "279345325880910540799960837653138904956852780817349960193932651092957355032339063742900216468694143617372745972501", -} - -func init() { - addCurve(&BW6_756) - addTwistedEdwardCurve(&tBW6_756) -} diff --git a/internal/generator/crypto/hash/mimc/template/mimc.go.tmpl b/internal/generator/crypto/hash/mimc/template/mimc.go.tmpl index ad1f77bdb3..69d0434980 100644 --- a/internal/generator/crypto/hash/mimc/template/mimc.go.tmpl +++ b/internal/generator/crypto/hash/mimc/template/mimc.go.tmpl @@ -15,13 +15,13 @@ const ( mimcNbRounds = 111 {{- else if eq .Name "bls12-377"}} mimcNbRounds = 62 -{{- else if or (eq .Name "bls12-378") (eq .Name "bls24-315")}} +{{- else if eq .Name "bls24-315"}} mimcNbRounds = 109 {{- else if eq .Name "bls24-317"}} mimcNbRounds = 91 {{- else if eq .Name "bw6-633"}} mimcNbRounds = 136 -{{- else if or (eq .Name "bw6-761") (eq .Name "bw6-756")}} +{{- else if eq .Name "bw6-761"}} mimcNbRounds = 163 {{- end}} seed = "seed" // seed to derive the constants diff --git a/internal/generator/ecc/template/point.go.tmpl b/internal/generator/ecc/template/point.go.tmpl index 94371457aa..4a06f1a5ac 100644 --- a/internal/generator/ecc/template/point.go.tmpl +++ b/internal/generator/ecc/template/point.go.tmpl @@ -519,7 +519,7 @@ func (p *{{ $TJacobian }}) IsOnCurve() bool { {{- else if eq .Name "bw6-761"}} // Mul tmp by bCurveCoeff=-1 tmp.Neg(&tmp) - {{- else if or (eq .Name "bls12-377") (eq .Name "bls12-378") (eq .Name "bls24-315") (eq .Name "bw6-756")}} + {{- else if or (eq .Name "bls12-377") (eq .Name "bls24-315")}} // Mul tmp by bCurveCoeff=1 (nothing to do) {{- else}} tmp.Mul(&tmp, &bCurveCoeff) @@ -573,7 +573,7 @@ func (p *{{ $TJacobian }}) IsOnCurve() bool { return res.IsOnCurve() && res.Z.IsZero() } {{- end}} -{{else if or (eq .Name "bw6-761") (eq .Name "bw6-756")}} +{{else if eq .Name "bw6-761"}} // IsInSubGroup returns true if p is on the r-torsion, false otherwise. {{ if .GLV}} // Z[r,0]+Z[-lambda{{ $TAffine }}, 1] is the kernel @@ -713,7 +713,7 @@ func (p *{{ $TJacobian }}) IsOnCurve() bool { return res.IsOnCurve() && res.Z.IsZero() } - {{else if or (eq .Name "bls12-377") (eq .Name "bls12-378")}} + {{else if eq .Name "bls12-377"}} // https://eprint.iacr.org/2021/1130.pdf, sec.4 // and https://eprint.iacr.org/2022/352.pdf, sec. 4.2 // ψ(p) = [x₀]P @@ -896,7 +896,7 @@ func (p *{{$TJacobian}}) ClearCofactor(q *{{$TJacobian}}) *{{$TJacobian}} { res.ScalarMultiplication(q, &xGen).AddAssign(q) p.Set(&res) return p -{{else if or (eq .Name "bls12-377") (eq .Name "bls12-378") (eq .Name "bls24-317")}} +{{else if or (eq .Name "bls12-377") (eq .Name "bls24-317")}} // cf https://eprint.iacr.org/2019/403.pdf, 5 var res {{$TJacobian}} res.ScalarMultiplication(q, &xGen).Neg(&res).AddAssign(q) @@ -981,39 +981,6 @@ func (p *{{$TJacobian}}) ClearCofactor(q *{{$TJacobian}}) *{{$TJacobian}} { {{ end}} return p -{{else if eq .Name "bw6-756"}} -{{ if .GLV}} - var L0, L1, uP, u2P, u3P, tmp G1Jac - - uP.ScalarMultiplication(q, &xGen) - u2P.ScalarMultiplication(&uP, &xGen) - u3P.ScalarMultiplication(&u2P, &xGen) - - L0.Set(q).AddAssign(&u3P). - SubAssign(&u2P) - tmp.Set(q).AddAssign(&u2P). - SubAssign(&uP). - SubAssign(&uP). - Double(&tmp) - L0.SubAssign(&tmp). - SubAssign(q) - - L1.Set(q).AddAssign(&uP) - tmp.Set(&uP).SubAssign(q). - Double(&tmp). - SubAssign(&u2P) - L1.AddAssign(&tmp). - SubAssign(q) - - p.phi(&L1). - AddAssign(&L0) -{{ else}} - var c1 big.Int - c1.SetString("605248206075306171568857128027361794400937215108643640003009340657451546212610770151705515081537938829431808196608", 10) - p.ScalarMultiplication(q, &c1) -{{ end}} - - return p {{- end}} } {{ else }} @@ -1067,7 +1034,7 @@ func (p *{{$TJacobian}}) ClearCofactor(q *{{$TJacobian}}) *{{$TJacobian}} { return p -{{else if or (eq .Name "bls12-377") (eq .Name "bls12-378")}} +{{else if eq .Name "bls12-377"}} // https://eprint.iacr.org/2017/419.pdf, 4.1 var xg, xxg, res, t G2Jac xg.ScalarMultiplication(q, &xGen) @@ -1219,37 +1186,6 @@ func (p *{{$TJacobian}}) ClearCofactor(q *{{$TJacobian}}) *{{$TJacobian}} { {{end}} return p -{{else if eq .Name "bw6-756"}} - -{{- if .GLV}} - var L0, L1, uP, u2P, u3P, tmp G2Jac - - uP.ScalarMultiplication(q, &xGen) - u2P.ScalarMultiplication(&uP, &xGen) - u3P.ScalarMultiplication(&u2P, &xGen) - // ht=-2, hy=0 - // d1=1, d2=-1, d3=-1 - - L0.Set(q). - AddAssign(&u2P). - SubAssign(&uP) - tmp.Set(&u2P). - AddAssign(q). - SubAssign(&uP). - Double(&tmp) - L1.Set(&u3P). - SubAssign(&tmp) - - p.phi(&L0). - AddAssign(&L1) -{{else}} - var c2 big.Int - c2.SetString("605248206075306171568857128027361794400937215108643640003009340657451546212610770151705515081537938829431808196609", 10) - p.ScalarMultiplication(q, &c2) -{{end}} - - return p - {{- end}} } {{- end}} @@ -1866,4 +1802,4 @@ func randomFrSizedBytes() ([]byte, error) { _, err := rand.Read(res) return res, err } -{{- end}} \ No newline at end of file +{{- end}} diff --git a/internal/generator/ecc/template/tests/marshal.go.tmpl b/internal/generator/ecc/template/tests/marshal.go.tmpl index 6a37ac64cc..eb214a00dc 100644 --- a/internal/generator/ecc/template/tests/marshal.go.tmpl +++ b/internal/generator/ecc/template/tests/marshal.go.tmpl @@ -390,7 +390,7 @@ func GenFp() gopter.Gen { // e2 e4 e12 e24 for bls24 // e2 e6 e12 else */}} -{{if or (eq .Name "bw6-633") (eq .Name "bw6-761") (eq .Name "bw6-756")}} +{{if or (eq .Name "bw6-633") (eq .Name "bw6-761")}} // GenE3 generates an E3 elmt func GenE3() gopter.Gen { return gopter.CombineGens( diff --git a/internal/generator/ecc/template/tests/point.go.tmpl b/internal/generator/ecc/template/tests/point.go.tmpl index 275cc15d6f..ee4fdb615a 100644 --- a/internal/generator/ecc/template/tests/point.go.tmpl +++ b/internal/generator/ecc/template/tests/point.go.tmpl @@ -76,7 +76,7 @@ import ( )) {{if eq .PointName "g2" }} - {{- if and (eq .PointName "g2") (ne .Name "bw6-761") (ne .Name "bw6-633") (ne .Name "bw6-756") }} + {{- if and (eq .PointName "g2") (ne .Name "bw6-761") (ne .Name "bw6-633")}} properties.Property("[{{ toUpper .Name }}] check that psi^2(P) = -phi(P)", prop.ForAll( func(a {{ .CoordType}}) bool { var p, res1, res2 {{ $TJacobian }} diff --git a/internal/generator/edwards/eddsa/template/eddsa.go.tmpl b/internal/generator/edwards/eddsa/template/eddsa.go.tmpl index 938edd7028..fb77e694a2 100644 --- a/internal/generator/edwards/eddsa/template/eddsa.go.tmpl +++ b/internal/generator/edwards/eddsa/template/eddsa.go.tmpl @@ -49,7 +49,7 @@ func GenerateKey(r io.Reader) (*PrivateKey, error) { var pub PublicKey var priv PrivateKey - {{- if or (eq .Name "bw6-761") (eq .Name "bw6-633") (eq .Name "bw6-756")}} + {{- if or (eq .Name "bw6-761") (eq .Name "bw6-633")}} // The source of randomness and the secret scalar must come // from 2 distinct sources. Since the scalar is the size of the // field of definition (48 bytes), the scalar must come from a @@ -84,7 +84,7 @@ func GenerateKey(r io.Reader) (*PrivateKey, error) { // prune the key // https://tools.ietf.org/html/rfc8032#section-5.1.5, key generation - {{- if or (eq .Name "bw6-761") (eq .Name "bw6-633") (eq .Name "bw6-756")}} + {{- if or (eq .Name "bw6-761") (eq .Name "bw6-633")}} h1[0] &= 0xF8 h1[sizeFr-1] &= 0x7F h1[sizeFr-1] |= 0x40 @@ -97,7 +97,7 @@ func GenerateKey(r io.Reader) (*PrivateKey, error) { // reverse first bytes because setBytes interpret stream as big endian // but in eddsa specs s is the first 32 bytes in little endian {{- $h := "h"}} - {{- if or (eq .Name "bw6-761") (eq .Name "bw6-633") (eq .Name "bw6-756")}} + {{- if or (eq .Name "bw6-761") (eq .Name "bw6-633")}} {{- $h = "h1"}} {{- end}} for i, j := 0, sizeFr - 1; i < sizeFr; i, j = i+1, j-1 { diff --git a/internal/generator/edwards/template/point.go.tmpl b/internal/generator/edwards/template/point.go.tmpl index 558633ca8c..cfc4436422 100644 --- a/internal/generator/edwards/template/point.go.tmpl +++ b/internal/generator/edwards/template/point.go.tmpl @@ -555,11 +555,7 @@ func (p *PointExtended) MixedAdd(p1 *PointExtended, p2 *PointAffine) *PointExten // Double adds points in extended coordinates // Dedicated doubling -{{- if or (eq .Name "bls12-378") (eq .Name "bw6-756")}} -// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd -{{- else}} // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd -{{- end}} func (p *PointExtended) Double(p1 *PointExtended) *PointExtended { var A, B, C, D, E, F, G, H fr.Element @@ -588,11 +584,7 @@ func (p *PointExtended) Double(p1 *PointExtended) *PointExtended { // MixedDouble adds points in extended coordinates // Dedicated mixed doubling -{{- if or (eq .Name "bls12-378") (eq .Name "bw6-756")}} -// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-mdbl-2008-hwcd -{{- else}} // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-mdbl-2008-hwcd -{{- end}} func (p *PointExtended) MixedDouble(p1 *PointExtended) *PointExtended { var A, B, D, E, G, H, two fr.Element diff --git a/internal/generator/fft/template/domain.go.tmpl b/internal/generator/fft/template/domain.go.tmpl index 0ac209d4db..a6953a29f0 100644 --- a/internal/generator/fft/template/domain.go.tmpl +++ b/internal/generator/fft/template/domain.go.tmpl @@ -26,7 +26,7 @@ type Domain struct { // this is set with the WithoutPrecompute option; // if true, the domain does some pre-computation and stores it. // if false, the FFT will compute the twiddles on the fly (this is less CPU efficient, but uses less memory) - withPrecompute bool + withPrecompute bool // the following slices are not serialized and are (re)computed through domain.preComputeTwiddles() @@ -48,9 +48,7 @@ type Domain struct { // GeneratorFullMultiplicativeGroup returns a generator of 𝔽ᵣˣ func GeneratorFullMultiplicativeGroup() fr.Element { var res fr.Element - {{if eq .Name "bls12-378"}} - res.SetUint64(22) - {{else if eq .Name "bls12-377"}} + {{if eq .Name "bls12-377"}} res.SetUint64(22) {{else if eq .Name "bls12-381"}} res.SetUint64(7) @@ -58,8 +56,6 @@ func GeneratorFullMultiplicativeGroup() fr.Element { res.SetUint64(5) {{else if eq .Name "bw6-761"}} res.SetUint64(15) - {{else if eq .Name "bw6-756"}} - res.SetUint64(5) {{else if eq .Name "bw6-633"}} res.SetUint64(13) {{else if eq .Name "bls24-315"}} @@ -85,7 +81,7 @@ func NewDomain(m uint64, opts ...DomainOption) *Domain { } domain.FrMultiplicativeGenInv.Inverse(&domain.FrMultiplicativeGen) - var err error + var err error domain.Generator, err = Generator(m) if err != nil { panic(err) @@ -285,7 +281,7 @@ func (d *Domain) ReadFrom(r io.Reader) (int64, error) { if d.withPrecompute { d.preComputeTwiddles() - } + } return dec.BytesRead(), nil } diff --git a/internal/generator/fft/template/fr.generator.go.tmpl b/internal/generator/fft/template/fr.generator.go.tmpl index 05a988d211..3cfaef1026 100644 --- a/internal/generator/fft/template/fr.generator.go.tmpl +++ b/internal/generator/fft/template/fr.generator.go.tmpl @@ -12,10 +12,7 @@ func Generator(m uint64) (Element, error) { x := ecc.NextPowerOfTwo(m) var rootOfUnity Element - {{if eq .Name "bls12-378"}} - rootOfUnity.SetString("4045585818372166415418670827807793147093034396422209590578257013290761627990") - const maxOrderRoot uint64 = 42 - {{else if eq .Name "bls12-377"}} + {{if eq .Name "bls12-377"}} rootOfUnity.SetString("8065159656716812877374967518403273466521432693661810619979959746626482506078") const maxOrderRoot uint64 = 47 {{else if eq .Name "bls12-381"}} @@ -27,9 +24,6 @@ func Generator(m uint64) (Element, error) { {{else if eq .Name "bw6-761"}} rootOfUnity.SetString("32863578547254505029601261939868325669770508939375122462904745766352256812585773382134936404344547323199885654433") const maxOrderRoot uint64 = 46 - {{else if eq .Name "bw6-756"}} - rootOfUnity.SetString("199251335866470442271346949249090720992237796757894062992204115206570647302191425225605716521843542790404563904580") - const maxOrderRoot uint64 = 41 {{else if eq .Name "bw6-633"}} rootOfUnity.SetString("4991787701895089137426454739366935169846548798279261157172811661565882460884369603588700158257") const maxOrderRoot uint64 = 20 @@ -51,4 +45,4 @@ func Generator(m uint64) (Element, error) { var generator Element generator.Exp(rootOfUnity, big.NewInt(int64(expo))) // order x return generator, nil -} \ No newline at end of file +} diff --git a/internal/generator/fft/template/imports.go.tmpl b/internal/generator/fft/template/imports.go.tmpl index 4f6e4ee1e5..c8d1a96222 100644 --- a/internal/generator/fft/template/imports.go.tmpl +++ b/internal/generator/fft/template/imports.go.tmpl @@ -1,8 +1,6 @@ {{ define "import_fr" }} -{{ if eq .Name "bls12-378"}} - "github.com/consensys/gnark-crypto/ecc/bls12-378/fr" -{{ else if eq .Name "bls12-377"}} +{{ if eq .Name "bls12-377"}} "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" {{ else if eq .Name "bls12-381"}} "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" @@ -10,8 +8,6 @@ "github.com/consensys/gnark-crypto/ecc/bn254/fr" {{ else if eq .Name "bw6-761"}} "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" -{{ else if eq .Name "bw6-756"}} - "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" {{ else if eq .Name "bw6-633"}} "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" {{ else if eq .Name "bls24-315"}} @@ -25,9 +21,7 @@ {{end}} {{ define "import_curve" }} -{{if eq .Name "bls12-378"}} - curve "github.com/consensys/gnark-crypto/ecc/bls12-378" -{{else if eq .Name "bls12-377"}} +{{if eq .Name "bls12-377"}} curve "github.com/consensys/gnark-crypto/ecc/bls12-377" {{else if eq .Name "bls12-381"}} curve "github.com/consensys/gnark-crypto/ecc/bls12-381" @@ -35,8 +29,6 @@ curve "github.com/consensys/gnark-crypto/ecc/bn254" {{else if eq .Name "bw6-761"}} curve "github.com/consensys/gnark-crypto/ecc/bw6-761" -{{else if eq .Name "bw6-756"}} - curve "github.com/consensys/gnark-crypto/ecc/bw6-756" {{else if eq .Name "bw6-633"}} curve "github.com/consensys/gnark-crypto/ecc/bw6-633" {{ else if eq .Name "bls24-315"}} diff --git a/internal/generator/kzg/template/marshal.go.tmpl b/internal/generator/kzg/template/marshal.go.tmpl index 8798bbf329..106285303c 100644 --- a/internal/generator/kzg/template/marshal.go.tmpl +++ b/internal/generator/kzg/template/marshal.go.tmpl @@ -39,15 +39,13 @@ func (vk *VerifyingKey) writeTo(w io.Writer, options ...func(*{{.CurvePackage}}. // encode the VerifyingKey enc := {{ .CurvePackage }}.NewEncoder(w, options...) - {{- if eq .Name "bw6-756"}} - nLines := 190 - {{- else if eq .Name "bw6-761"}} + {{- if eq .Name "bw6-761"}} nLines := 189 {{- else if eq .Name "bw6-633"}} nLines := 158 {{- else if eq .Name "bn254"}} nLines := 66 - {{- else if or (eq .Name "bls12-381") (eq .Name "bls12-377") (eq .Name "bls12-378")}} + {{- else if or (eq .Name "bls12-381") (eq .Name "bls12-377")}} nLines := 63 {{- else }} nLines := 32 @@ -117,7 +115,7 @@ func (srs *SRS) ReadDump(r io.Reader, maxPkPoints ...int) error { // read the slice srs.Pk.G1, _, err = unsafe.ReadSlice[[]{{.CurvePackage}}.G1Affine](r, maxPkPoints...) - return err + return err } // WriteTo writes binary encoding of the entire SRS @@ -170,15 +168,13 @@ func (vk *VerifyingKey) ReadFrom(r io.Reader) (int64, error) { // decode the VerifyingKey dec := {{ .CurvePackage }}.NewDecoder(r) - {{- if eq .Name "bw6-756"}} - nLines := 190 - {{- else if eq .Name "bw6-761"}} + {{- if eq .Name "bw6-761"}} nLines := 189 {{- else if eq .Name "bw6-633"}} nLines := 158 {{- else if eq .Name "bn254"}} nLines := 66 - {{- else if or (eq .Name "bls12-381") (eq .Name "bls12-377") (eq .Name "bls12-378")}} + {{- else if or (eq .Name "bls12-381") (eq .Name "bls12-377")}} nLines := 63 {{- else }} nLines := 32 diff --git a/internal/generator/pairing/template/tests/pairing.go.tmpl b/internal/generator/pairing/template/tests/pairing.go.tmpl index a05c5e4ea8..c41f4618ca 100644 --- a/internal/generator/pairing/template/tests/pairing.go.tmpl +++ b/internal/generator/pairing/template/tests/pairing.go.tmpl @@ -24,7 +24,7 @@ func TestPairing(t *testing.T) { properties := gopter.NewProperties(parameters) - {{if or (eq .Name "bw6-761") (eq .Name "bw6-633") (eq .Name "bw6-756")}} + {{if or (eq .Name "bw6-761") (eq .Name "bw6-633")}} genA := GenE6() {{else if or (eq .Name "bls24-315") (eq .Name "bls24-317")}} genA := GenE24() @@ -92,7 +92,7 @@ func TestPairing(t *testing.T) { b.Conjugate(&a) a.Inverse(&a) b.Mul(&b, &a) - {{if or (eq .Name "bw6-761") (eq .Name "bw6-633") (eq .Name "bw6-756")}} + {{if or (eq .Name "bw6-761") (eq .Name "bw6-633")}} a.Frobenius(&b). {{else if or (eq .Name "bls24-315") (eq .Name "bls24-317")}} a.FrobeniusQuad(&b). diff --git a/internal/generator/tower/generate.go b/internal/generator/tower/generate.go index 251032d3e7..734e86aa72 100644 --- a/internal/generator/tower/generate.go +++ b/internal/generator/tower/generate.go @@ -12,7 +12,7 @@ import ( // Generate generates a tower 2->6->12 over fp func Generate(conf config.Curve, baseDir string, bgen *bavard.BatchGenerator) error { - if conf.Equal(config.BW6_756) || conf.Equal(config.BW6_761) || conf.Equal(config.BW6_633) || conf.Equal(config.BLS24_315) || conf.Equal(config.BLS24_317) { + if conf.Equal(config.BW6_761) || conf.Equal(config.BW6_633) || conf.Equal(config.BLS24_315) || conf.Equal(config.BLS24_317) { return nil } From 8c8f1560a1c9224513d8af7e5650510e16b7d105 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 5 Sep 2024 15:32:37 -0400 Subject: [PATCH 2/2] refactor: up doc --- README.md | 3 --- ecc/ecc.go | 10 ++-------- ecc/ecc.md | 7 +++---- hash/hashes.go | 16 ---------------- internal/apicheck_test.go | 5 ----- kzg/kzg.go | 6 ------ signature/ecdsa/ecdsa.go | 6 ------ signature/eddsa/eddsa.go | 6 ------ 8 files changed, 5 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 946c2678ba..0cab35d6b5 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ * [`bls24-317`] * [`bls12-377`] / [`bw6-761`] * [`bls24-315`] / [`bw6-633`] - * [`bls12-378`] / [`bw6-756`] * Each of these curves has a [`twistededwards`] sub-package with its companion curve which allow efficient elliptic curve cryptography inside zkSNARK circuits. * [`field/goff`] - Finite field arithmetic code generator (blazingly fast big.Int) * [`fft`] - Fast Fourier Transform @@ -106,10 +105,8 @@ This project is licensed under the Apache 2 License - see the [LICENSE](LICENSE) [`bls24-317`]: https://pkg.go.dev/github.com/consensys/gnark-crypto/ecc/bls24-317 [`bls12-377`]: https://pkg.go.dev/github.com/consensys/gnark-crypto/ecc/bls12-377 [`bls24-315`]: https://pkg.go.dev/github.com/consensys/gnark-crypto/ecc/bls24-315 -[`bls12-378`]: https://pkg.go.dev/github.com/consensys/gnark-crypto/ecc/bls12-378 [`bw6-761`]: https://pkg.go.dev/github.com/consensys/gnark-crypto/ecc/bw6-761 [`bw6-633`]: https://pkg.go.dev/github.com/consensys/gnark-crypto/ecc/bw6-633 -[`bw6-756`]: https://pkg.go.dev/github.com/consensys/gnark-crypto/ecc/bw6-756 [`twistededwards`]: https://pkg.go.dev/github.com/consensys/gnark-crypto/ecc/bn254/twistededwards [`eddsa`]: https://pkg.go.dev/github.com/consensys/gnark-crypto/ecc/bn254/twistededwards/eddsa [`fft`]: https://pkg.go.dev/github.com/consensys/gnark-crypto/ecc/bn254/fr/fft diff --git a/ecc/ecc.go b/ecc/ecc.go index a9c4b6dfb8..9f927d5678 100644 --- a/ecc/ecc.go +++ b/ecc/ecc.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package ecc provides bls12-381, bls12-377, bls12-378, bn254, bw6-761, bls24-315, bls24-317, bw6-633, bls12-378, bw6-756, secp256k1 and stark-curve elliptic curves implementation (+pairing). +// Package ecc provides bls12-381, bls12-377, bn254, bw6-761, bls24-315, bls24-317, bw6-633, secp256k1 and stark-curve elliptic curves implementation (+pairing). // // Also // @@ -42,20 +42,18 @@ const ( UNKNOWN ID = iota BN254 BLS12_377 - BLS12_378 BLS12_381 BLS24_315 BLS24_317 BW6_761 BW6_633 - BW6_756 STARK_CURVE SECP256K1 ) // Implemented return the list of curves fully implemented in gnark-crypto func Implemented() []ID { - return []ID{BN254, BLS12_377, BLS12_381, BW6_761, BLS24_315, BW6_633, BLS12_378, BW6_756, BLS24_317, STARK_CURVE, SECP256K1} + return []ID{BN254, BLS12_377, BLS12_381, BW6_761, BLS24_315, BW6_633, BLS24_317, STARK_CURVE, SECP256K1} } func IDFromString(s string) (ID, error) { @@ -90,8 +88,6 @@ func (id ID) config() *config.Curve { switch id { case BLS12_377: return &config.BLS12_377 - case BLS12_378: - return &config.BLS12_378 case BLS12_381: return &config.BLS12_381 case BN254: @@ -104,8 +100,6 @@ func (id ID) config() *config.Curve { return &config.BLS24_315 case BLS24_317: return &config.BLS24_317 - case BW6_756: - return &config.BW6_756 case STARK_CURVE: return &config.STARK_CURVE case SECP256K1: diff --git a/ecc/ecc.md b/ecc/ecc.md index f4508d7610..a0048b7fd0 100644 --- a/ecc/ecc.md +++ b/ecc/ecc.md @@ -3,11 +3,10 @@ * BLS12-381 (Zcash) * BN254 (Ethereum) * BLS12-377 (ZEXE) -* BW6-761 (EC supporting pairing on BLS12-377 field of definition) +* BW6-761 (2-chain with BLS12-377) * BLS24-315 -* BW6-633 (EC supporting pairing on BLS24-315 field of definition) -* BLS12-378 (GT-strong SNARK-friendly) -* BW6-756 (EC supporting pairing on BLS12-378 field of definition) +* BW6-633 (2-chain with BLS24-315) +* BLS24-317 * STARK (STARK curve for ECDSA) ### Twisted edwards curves diff --git a/hash/hashes.go b/hash/hashes.go index 8f668c7a4e..56cc5d1b41 100644 --- a/hash/hashes.go +++ b/hash/hashes.go @@ -18,13 +18,11 @@ import ( "hash" bls377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/mimc" - bls378 "github.com/consensys/gnark-crypto/ecc/bls12-378/fr/mimc" bls381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/mimc" bls315 "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/mimc" bls317 "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/mimc" bn254 "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" bw633 "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/mimc" - bw756 "github.com/consensys/gnark-crypto/ecc/bw6-756/fr/mimc" bw761 "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/mimc" ) @@ -38,8 +36,6 @@ const ( MIMC_BLS12_381 // MIMC_BLS12_377 is the MiMC hash function for the BLS12-377 curve. MIMC_BLS12_377 - // MIMC_BLS12_378 is the MiMC hash function for the BLS12-378 curve. - MIMC_BLS12_378 // MIMC_BW6_761 is the MiMC hash function for the BW6-761 curve. MIMC_BW6_761 // MIMC_BLS24_315 is the MiMC hash function for the BLS24-315 curve. @@ -48,8 +44,6 @@ const ( MIMC_BLS24_317 // MIMC_BW6_633 is the MiMC hash function for the BW6-633 curve. MIMC_BW6_633 - // MIMC_BW6_756 is the MiMC hash function for the BW6-756 curve. - MIMC_BW6_756 ) // size of digests in bytes @@ -57,12 +51,10 @@ var digestSize = []uint8{ MIMC_BN254: 32, MIMC_BLS12_381: 48, MIMC_BLS12_377: 48, - MIMC_BLS12_378: 48, MIMC_BW6_761: 96, MIMC_BLS24_315: 48, MIMC_BLS24_317: 48, MIMC_BW6_633: 80, - MIMC_BW6_756: 96, } // New initializes the hash function. @@ -74,8 +66,6 @@ func (m Hash) New() hash.Hash { return bls381.NewMiMC() case MIMC_BLS12_377: return bls377.NewMiMC() - case MIMC_BLS12_378: - return bls378.NewMiMC() case MIMC_BW6_761: return bw761.NewMiMC() case MIMC_BLS24_315: @@ -84,8 +74,6 @@ func (m Hash) New() hash.Hash { return bls317.NewMiMC() case MIMC_BW6_633: return bw633.NewMiMC() - case MIMC_BW6_756: - return bw756.NewMiMC() default: panic("Unknown mimc ID") } @@ -100,8 +88,6 @@ func (m Hash) String() string { return "MIMC_BLS381" case MIMC_BLS12_377: return "MIMC_BLS377" - case MIMC_BLS12_378: - return "MIMC_BLS378" case MIMC_BW6_761: return "MIMC_BW761" case MIMC_BLS24_315: @@ -110,8 +96,6 @@ func (m Hash) String() string { return "MIMC_BLS317" case MIMC_BW6_633: return "MIMC_BW633" - case MIMC_BW6_756: - return "MIMC_BW756" default: panic("Unknown mimc ID") } diff --git a/internal/apicheck_test.go b/internal/apicheck_test.go index 384c138d1a..eebf5e3f6b 100644 --- a/internal/apicheck_test.go +++ b/internal/apicheck_test.go @@ -2,7 +2,6 @@ package main import ( bls377 "github.com/consensys/gnark-crypto/ecc/bls12-377" - bls378 "github.com/consensys/gnark-crypto/ecc/bls12-378" bls381 "github.com/consensys/gnark-crypto/ecc/bls12-381" "github.com/consensys/gnark-crypto/ecc/bn254" bw761 "github.com/consensys/gnark-crypto/ecc/bw6-761" @@ -15,7 +14,6 @@ var err error var ( gtbls377 bls377.GT - gtbls378 bls378.GT gtbls381 bls381.GT gtbn254 bn254.GT gtbw761 bw761.GT @@ -24,21 +22,18 @@ var ( func init() { // Pair gtbls377, err = bls377.Pair([]bls377.G1Affine{}, []bls377.G2Affine{}) - gtbls378, err = bls378.Pair([]bls378.G1Affine{}, []bls378.G2Affine{}) gtbls381, err = bls381.Pair([]bls381.G1Affine{}, []bls381.G2Affine{}) gtbn254, err = bn254.Pair([]bn254.G1Affine{}, []bn254.G2Affine{}) gtbw761, err = bw761.Pair([]bw761.G1Affine{}, []bw761.G2Affine{}) // MillerLoop gtbls377, err = bls377.MillerLoop([]bls377.G1Affine{}, []bls377.G2Affine{}) - gtbls378, err = bls378.MillerLoop([]bls378.G1Affine{}, []bls378.G2Affine{}) gtbls381, err = bls381.MillerLoop([]bls381.G1Affine{}, []bls381.G2Affine{}) gtbn254, err = bn254.MillerLoop([]bn254.G1Affine{}, []bn254.G2Affine{}) gtbw761, err = bw761.MillerLoop([]bw761.G1Affine{}, []bw761.G2Affine{}) // FinalExp gtbls377 = bls377.FinalExponentiation(>bls377) - gtbls378 = bls378.FinalExponentiation(>bls378) gtbls381 = bls381.FinalExponentiation(>bls381) gtbn254 = bn254.FinalExponentiation(>bn254) gtbw761 = bw761.FinalExponentiation(>bw761) diff --git a/kzg/kzg.go b/kzg/kzg.go index 134b11576d..1ebb7e3080 100644 --- a/kzg/kzg.go +++ b/kzg/kzg.go @@ -9,13 +9,11 @@ import ( "github.com/consensys/gnark-crypto/ecc" kzg_bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/kzg" - kzg_bls12378 "github.com/consensys/gnark-crypto/ecc/bls12-378/kzg" kzg_bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/kzg" kzg_bls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/kzg" kzg_bls24317 "github.com/consensys/gnark-crypto/ecc/bls24-317/kzg" kzg_bn254 "github.com/consensys/gnark-crypto/ecc/bn254/kzg" kzg_bw6633 "github.com/consensys/gnark-crypto/ecc/bw6-633/kzg" - kzg_bw6756 "github.com/consensys/gnark-crypto/ecc/bw6-756/kzg" kzg_bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/kzg" ) @@ -43,8 +41,6 @@ func NewSRS(curveID ecc.ID) SRS { return &kzg_bn254.SRS{} case ecc.BLS12_377: return &kzg_bls12377.SRS{} - case ecc.BLS12_378: - return &kzg_bls12378.SRS{} case ecc.BLS12_381: return &kzg_bls12381.SRS{} case ecc.BLS24_315: @@ -55,8 +51,6 @@ func NewSRS(curveID ecc.ID) SRS { return &kzg_bw6761.SRS{} case ecc.BW6_633: return &kzg_bw6633.SRS{} - case ecc.BW6_756: - return &kzg_bw6756.SRS{} default: panic("not implemented") } diff --git a/signature/ecdsa/ecdsa.go b/signature/ecdsa/ecdsa.go index 68daac09d5..91bf8284b5 100644 --- a/signature/ecdsa/ecdsa.go +++ b/signature/ecdsa/ecdsa.go @@ -21,13 +21,11 @@ import ( "github.com/consensys/gnark-crypto/ecc" ecdsa_bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/ecdsa" - ecdsa_bls12378 "github.com/consensys/gnark-crypto/ecc/bls12-378/ecdsa" ecdsa_bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/ecdsa" ecdsa_bls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/ecdsa" ecdsa_bls24317 "github.com/consensys/gnark-crypto/ecc/bls24-317/ecdsa" ecdsa_bn254 "github.com/consensys/gnark-crypto/ecc/bn254/ecdsa" ecdsa_bw6633 "github.com/consensys/gnark-crypto/ecc/bw6-633/ecdsa" - ecdsa_bw6756 "github.com/consensys/gnark-crypto/ecc/bw6-756/ecdsa" ecdsa_bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/ecdsa" ecdsa_secp256k1 "github.com/consensys/gnark-crypto/ecc/secp256k1/ecdsa" ecdsa_starkcurve "github.com/consensys/gnark-crypto/ecc/stark-curve/ecdsa" @@ -43,12 +41,8 @@ func New(ss ecc.ID, r io.Reader) (signature.Signer, error) { return ecdsa_bls12381.GenerateKey(r) case ecc.BLS12_377: return ecdsa_bls12377.GenerateKey(r) - case ecc.BLS12_378: - return ecdsa_bls12378.GenerateKey(r) case ecc.BW6_761: return ecdsa_bw6761.GenerateKey(r) - case ecc.BW6_756: - return ecdsa_bw6756.GenerateKey(r) case ecc.BLS24_315: return ecdsa_bls24315.GenerateKey(r) case ecc.BLS24_317: diff --git a/signature/eddsa/eddsa.go b/signature/eddsa/eddsa.go index 33901b4a4d..9df765f867 100644 --- a/signature/eddsa/eddsa.go +++ b/signature/eddsa/eddsa.go @@ -20,14 +20,12 @@ import ( "io" eddsa_bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/twistededwards/eddsa" - eddsa_bls12378 "github.com/consensys/gnark-crypto/ecc/bls12-378/twistededwards/eddsa" eddsa_bls12381_bandersnatch "github.com/consensys/gnark-crypto/ecc/bls12-381/bandersnatch/eddsa" eddsa_bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/twistededwards/eddsa" eddsa_bls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/twistededwards/eddsa" eddsa_bls24317 "github.com/consensys/gnark-crypto/ecc/bls24-317/twistededwards/eddsa" eddsa_bn254 "github.com/consensys/gnark-crypto/ecc/bn254/twistededwards/eddsa" eddsa_bw6633 "github.com/consensys/gnark-crypto/ecc/bw6-633/twistededwards/eddsa" - eddsa_bw6756 "github.com/consensys/gnark-crypto/ecc/bw6-756/twistededwards/eddsa" eddsa_bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/twistededwards/eddsa" "github.com/consensys/gnark-crypto/ecc/twistededwards" "github.com/consensys/gnark-crypto/signature" @@ -44,12 +42,8 @@ func New(ss twistededwards.ID, r io.Reader) (signature.Signer, error) { return eddsa_bls12381_bandersnatch.GenerateKey(r) case twistededwards.BLS12_377: return eddsa_bls12377.GenerateKey(r) - case twistededwards.BLS12_378: - return eddsa_bls12378.GenerateKey(r) case twistededwards.BW6_761: return eddsa_bw6761.GenerateKey(r) - case twistededwards.BW6_756: - return eddsa_bw6756.GenerateKey(r) case twistededwards.BLS24_315: return eddsa_bls24315.GenerateKey(r) case twistededwards.BLS24_317: