14#include "gmock/gmock.h"
17#include <gtest/gtest.h>
22using ::testing::ElementsAreArray;
24using ::testing::Property;
29template <
typename G1>
class TestAffineElement :
public testing::Test {
30 using element =
typename G1::element;
31 using affine_element =
typename G1::affine_element;
32 using Fr =
typename G1::Fr;
35 static void test_read_write_buffer()
39 affine_element P = affine_element(element::random_element());
42 std::vector<uint8_t> v(64);
43 uint8_t* ptr = v.data();
44 affine_element::serialize_to_buffer(P, ptr);
46 R = affine_element::serialize_from_buffer(ptr);
47 ASSERT_TRUE(R.on_curve());
53 affine_element P = affine_element(element::random_element());
54 P.self_set_infinity();
57 std::vector<uint8_t> v(64);
58 uint8_t* ptr = v.data();
59 affine_element::serialize_to_buffer(P, ptr);
61 R = affine_element::serialize_from_buffer(ptr);
62 ASSERT_TRUE(R.is_point_at_infinity());
68 static void test_deserialize_off_curve_throws()
70 using Fq =
typename G1::Fq;
73 affine_element P = affine_element(element::random_element());
74 affine_element off_curve;
78 std::vector<uint8_t> v(
sizeof(affine_element));
79 uint8_t* ptr = v.data();
80 affine_element::serialize_to_buffer(off_curve, ptr);
82 if (!off_curve.on_curve()) {
89 static void test_read_and_write()
93 affine_element P = affine_element(element::random_element());
94 [[maybe_unused]] affine_element R;
96 std::vector<uint8_t> v(
sizeof(R));
97 uint8_t* ptr = v.data();
99 ASSERT_TRUE(P.on_curve());
104 const uint8_t* read_ptr = v.data();
107 ASSERT_TRUE(R.on_curve());
112 static void test_msgpack_serialization()
116 affine_element P = affine_element(element::random_element());
119 msgpack::sbuffer sbuf;
120 msgpack::pack(sbuf, P);
123 msgpack::object_handle oh = msgpack::unpack(sbuf.data(), sbuf.size());
124 msgpack::object deserialized = oh.get();
127 deserialized.convert(R);
129 ASSERT_TRUE(R.on_curve() && !R.is_point_at_infinity());
135 affine_element P = affine_element(element::random_element());
136 P.self_set_infinity();
139 msgpack::sbuffer sbuf;
140 msgpack::pack(sbuf, P);
143 msgpack::object_handle oh = msgpack::unpack(sbuf.data(), sbuf.size());
144 msgpack::object deserialized = oh.get();
147 deserialized.convert(R);
149 ASSERT_TRUE(R.is_point_at_infinity());
154 static void test_point_compression()
156 for (
size_t i = 0; i < 10; i++) {
157 affine_element P = affine_element(element::random_element());
160 compressed.
data[3] |= group_elements::UINT256_TOP_LIMB_MSB;
162 affine_element Q = affine_element::from_compressed(compressed);
167 static void test_point_compression_unsafe()
169 for (
size_t i = 0; i < 100; i++) {
170 affine_element P = affine_element(element::random_element());
176 EXPECT_EQ(P, Q_points[0]);
180 static void test_add_affine()
183 affine_element lhs_affine(
lhs);
186 affine_element rhs_affine(
rhs);
189 affine_element
result = lhs_affine + rhs_affine;
198 static void test_mixed_add_infinity_regression()
200 const element P = element::random_element();
201 const affine_element Q_inf = affine_element::infinity();
204 EXPECT_EQ(P + Q_inf, P);
205 EXPECT_EQ(P - Q_inf, P);
218 EXPECT_EQ(Q_inf + P, P);
219 EXPECT_EQ(Q_inf - P, -P);
222 element inf_elem = element::zero();
223 ASSERT_TRUE(inf_elem.is_point_at_infinity());
224 EXPECT_TRUE((inf_elem + Q_inf).is_point_at_infinity());
225 EXPECT_TRUE((inf_elem - Q_inf).is_point_at_infinity());
228 EXPECT_TRUE((P + Q_inf).on_curve());
229 EXPECT_TRUE((P - Q_inf).on_curve());
234 static void test_infinity_regression()
237 P.self_set_infinity();
238 affine_element R(0, P.y);
239 ASSERT_FALSE(P == R);
241 static void test_infinity_ordering_regression()
243 affine_element P(0, 1);
244 affine_element Q(0, 1);
246 P.self_set_infinity();
247 EXPECT_NE(P < Q, Q < P);
253 static void test_point_compression_non_canonical_x()
255 using Fq =
typename G1::Fq;
263 affine_element pt1 = affine_element::from_compressed(x1);
264 affine_element pt2 = affine_element::from_compressed(x2);
267 EXPECT_TRUE(pt1.on_curve());
275 static void test_point_compression_invalid_x()
277 using Fq =
typename G1::Fq;
278 size_t invalid_count = 0;
279 for (
size_t i = 0; i < 20; ++i) {
289 EXPECT_GT(invalid_count, 0U);
296 static void test_batch_endomorphism_by_minus_one()
302 element::batch_mul_with_endomorphism(affine_points, -affine_element::Fr::one());
305 EXPECT_EQ(affine_points[i], -
result[i]);
313 static void test_fixed_point_at_infinity()
315 using Fq = affine_element::Fq;
316 affine_element P = affine_element::infinity();
319 affine_element R = affine_element(element::random_element());
324 static void test_infinity_mul_by_scalar_is_infinity()
327 EXPECT_TRUE(
result.is_point_at_infinity());
330 static void test_batch_mul_matches_non_batch_mul()
334 affine_points.push_back(affine_element::infinity());
337 std::transform(affine_points.begin(),
340 [exponent](
const auto& el) { return el * exponent; });
342 EXPECT_THAT(
result, ElementsAreArray(expected));
345 static void test_infinity_batch_mul_by_scalar_is_infinity()
350 EXPECT_THAT(
result, Each(Property(&affine_element::is_point_at_infinity, Eq(
true))));
353 static void test_batch_mul_endomorphism_even_scalars()
355 const affine_element P = affine_element::one();
357 for (
const Fr scalar : {
Fr(0),
Fr(2),
Fr(4),
Fr(6),
Fr(8) }) {
358 const auto result = element::batch_mul_with_endomorphism(points, scalar);
359 const affine_element expected(
element(P) * scalar);
360 for (
size_t i = 0; i < points.size(); ++i) {
361 EXPECT_EQ(
result[i], expected);
369 static size_t k2_bit_length(
const Fr& scalar)
376 const auto& k2 = endo.second;
378 return 128 -
static_cast<size_t>(__builtin_clzll(k2[1]));
381 return 64 -
static_cast<size_t>(__builtin_clzll(k2[0]));
389 static Fr find_scalar_with_k2_bits(
size_t target_bits,
size_t max_attempts = 2000)
391 for (
size_t i = 0; i < max_attempts; ++i) {
393 if (k2_bit_length(s) == target_bits) {
397 throw_or_abort(
"could not find scalar with desired K2 bit-width");
402 static void check_batch_mul_against_naive(
size_t num_points,
const Fr& scalar)
405 points.reserve(num_points);
407 points.push_back(affine_element(element::random_element()));
410 expected.reserve(num_points);
411 for (
const auto& p : points) {
412 expected.push_back(affine_element(
element(p) * scalar));
415 ASSERT_EQ(
result.size(), expected.size());
416 EXPECT_THAT(
result, ElementsAreArray(expected));
422 static void test_batch_mul_zero_scalar()
426 points.reserve(num_points);
428 points.push_back(affine_element(element::random_element()));
431 ASSERT_EQ(
result.size(), num_points);
432 for (
const auto& r :
result) {
433 EXPECT_TRUE(r.is_point_at_infinity());
438 static void test_batch_mul_num_points_not_multiple_of_threads()
444 static void test_batch_mul_scalar_under_127_bits()
447 const Fr scalar(
uint256_t{ 0xdeadbeefcafef00dULL, 0x3edcba98765432f1ULL, 0, 0 });
448 ASSERT_EQ(k2_bit_length(scalar), 0U);
449 check_batch_mul_against_naive(64, scalar);
453 static void test_batch_mul_scalar_low_127_bits_zero()
456 check_batch_mul_against_naive(64, scalar);
460 static void test_batch_mul_k2_128_bits_never_occurs()
462 for (
size_t i = 0; i < 10000; ++i) {
464 const size_t bits = k2_bit_length(s);
465 ASSERT_LE(bits, 127U) <<
"GLV split must produce K2 ≤ 127 bits; got " << bits <<
" bits on sample " << i;
470 static void test_batch_mul_k2_127_bits()
472 const Fr scalar = find_scalar_with_k2_bits(127);
473 ASSERT_EQ(k2_bit_length(scalar), 127U);
474 check_batch_mul_against_naive(64, scalar);
478 static void test_batch_mul_k2_126_bits()
480 const Fr scalar = find_scalar_with_k2_bits(126);
481 ASSERT_EQ(k2_bit_length(scalar), 126U);
482 check_batch_mul_against_naive(64, scalar);
486 static void test_batch_mul_k2_125_bits()
488 const Fr scalar = find_scalar_with_k2_bits(125);
489 ASSERT_EQ(k2_bit_length(scalar), 125U);
490 check_batch_mul_against_naive(64, scalar);
494 static void test_batch_mul_empty_input()
498 EXPECT_TRUE(
result.empty());
502 static void test_batch_mul_size_less_than_num_threads()
504 for (
size_t sz : {
size_t{ 1 },
size_t{ 2 },
size_t{ 3 } }) {
515 static void test_batch_mul_small_scalars_edge_predicate()
519 points.reserve(num_points);
521 points.push_back(affine_element(element::random_element()));
525 for (int64_t s = -32; s <= 32; ++s) {
526 const Fr scalar = (s >= 0) ?
Fr(
static_cast<uint64_t
>(s)) : -
Fr(static_cast<uint64_t>(-s));
528 expected.reserve(num_points);
529 for (
const auto& p : points) {
530 expected.push_back(affine_element(
element(p) * scalar));
533 ASSERT_EQ(
result.size(), expected.size());
535 EXPECT_EQ(
result[i], expected[i]) <<
"scalar = " << s <<
", point index = " << i;
540 static void test_batch_mul_randomized_matches_naive()
542 for (
size_t trial = 0; trial < 24; ++trial) {
545 if ((trial % 8) == 0) {
546 scalar =
Fr(
static_cast<uint64_t
>(trial + 1));
548 check_batch_mul_against_naive(num_points, scalar);
555 static Fr random_short_scalar()
564 static void check_two_round_fold_against_naive(
size_t t,
const Fr& u1,
const Fr& u2)
567 points.reserve(4 * t);
568 for (
size_t i = 0; i < 4 * t; ++i) {
569 points.push_back(affine_element(element::random_element()));
571 const Fr u12 = u1 * u2;
574 for (
size_t i = 0; i < t; ++i) {
576 acc +=
element(points[i + t]) * u1;
577 acc +=
element(points[i + 2 * t]) * u2;
578 acc += points[i + 3 * t];
579 expected.push_back(affine_element(acc));
582 ASSERT_EQ(
result.size(), expected.size());
583 for (
size_t i = 0; i < t; ++i) {
584 EXPECT_EQ(
result[i], expected[i]) <<
"index " << i;
588 static void test_two_round_fold_random_challenges()
590 check_two_round_fold_against_naive(64, random_short_scalar(), random_short_scalar());
591 check_two_round_fold_against_naive(17, random_short_scalar(), random_short_scalar());
592 for (
size_t t : {
size_t{ 1 },
size_t{ 2 },
size_t{ 3 } }) {
593 check_two_round_fold_against_naive(t, random_short_scalar(), random_short_scalar());
602 static void test_two_round_fold_small_challenges()
604 for (uint64_t s1 : { 1ULL, 2ULL, 3ULL, 8ULL, 15ULL, 16ULL }) {
605 for (uint64_t s2 : { 1ULL, 2ULL, 4ULL, 7ULL, 16ULL }) {
606 check_two_round_fold_against_naive(4,
Fr(s1),
Fr(s2));
613 static void test_two_round_fold_matches_sequential_folds()
615 constexpr size_t t = 32;
617 points.reserve(4 * t);
618 for (
size_t i = 0; i < 4 * t; ++i) {
619 points.push_back(affine_element(element::random_element()));
621 const Fr u1 = random_short_scalar();
622 const Fr u2 = random_short_scalar();
636 ASSERT_EQ(fused.size(), round2.size());
637 for (
size_t i = 0; i < t; ++i) {
638 EXPECT_EQ(fused[i], round2[i]) <<
"index " << i;
642 static void test_frc_codec_round_trip()
645 affine_element point = affine_element::random_element();
648 affine_element::PUBLIC_INPUTS_SIZE);
649 auto reconstructed = FrCodec::deserialize_from_fields<affine_element>(limbs);
650 EXPECT_EQ(reconstructed, point);
655 static void test_is_in_prime_subgroup_accepts_subgroup_points()
657 EXPECT_TRUE(affine_element::infinity().is_in_prime_subgroup());
658 EXPECT_TRUE(affine_element::one().is_in_prime_subgroup());
660 for (
size_t i = 0; i < 8; ++i) {
661 affine_element P = affine_element(element::random_element());
662 EXPECT_TRUE(P.is_in_prime_subgroup());
668using TestTypes = testing::Types<bb::g1, grumpkin::g1, secp256k1::g1, secp256r1::g1>;
675 TestFixture::test_add_affine();
682 TestFixture::test_mixed_add_infinity_regression();
687 TestFixture::test_read_and_write();
692 TestFixture::test_read_write_buffer();
693 TestFixture::test_msgpack_serialization();
698 if constexpr (TypeParam::Fq::modulus.data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) {
701 TestFixture::test_point_compression();
707 if constexpr (TypeParam::Fq::modulus.data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) {
710 TestFixture::test_fixed_point_at_infinity();
716 if constexpr (TypeParam::Fq::modulus.data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) {
717 TestFixture::test_point_compression_unsafe();
725 TestFixture::test_infinity_ordering_regression();
734 template <
typename Element,
typename Scalar>
739 template <
typename Element,
typename Scalar>
748TYPED_TEST(TestAffineElement, MulWithEndomorphismMatchesMulWithoutEndomorphism)
750 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
753 using element_t =
typename TypeParam::element;
754 using Fr =
typename TypeParam::Fr;
755 for (
int i = 0; i < 100; i++) {
756 element_t x1(element_t::random_element());
765TYPED_TEST(TestAffineElement, MulWithEndomorphismEdgeCasesMatchMulWithoutEndomorphism)
767 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
770 using element_t =
typename TypeParam::element;
771 using Fr =
typename TypeParam::Fr;
773 const element_t point(element_t::random_element());
774 std::vector<Fr> scalars;
776 for (uint64_t i = 0; i <= 64; ++i) {
777 scalars.emplace_back(i);
780 for (
const size_t bit : { 125UL, 126UL, 127UL }) {
782 for (
const uint64_t delta : { 0UL, 1UL, 2UL, 7UL, 8UL, 15UL, 16UL }) {
783 scalars.emplace_back(power + delta);
785 scalars.emplace_back(power - delta);
790 for (
const Fr& scalar : scalars) {
793 EXPECT_EQ(point * scalar, expected);
794 EXPECT_EQ(point.mul_const_time(scalar), expected);
803 using element_t =
typename TypeParam::element;
804 using Fr =
typename TypeParam::Fr;
805 element_t
G(element_t::random_element());
809 EXPECT_EQ(
G.mul_const_time(s),
G * s);
812 for (
int i = 0; i < 50; ++i) {
814 EXPECT_EQ(
G.mul_const_time(s),
G * s);
822 TestFixture::test_frc_codec_round_trip();
830TYPED_TEST(TestAffineElement, BatchMulEndomorphismEvenScalars)
832 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
835 TestFixture::test_batch_mul_endomorphism_even_scalars();
842 TestFixture::test_infinity_mul_by_scalar_is_infinity();
848 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
851 TestFixture::test_batch_mul_matches_non_batch_mul();
856TYPED_TEST(TestAffineElement, InfinityBatchMulByScalarIsInfinity)
858 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
861 TestFixture::test_infinity_batch_mul_by_scalar_is_infinity();
867 if constexpr (TypeParam::USE_ENDOMORPHISM) {
868 TestFixture::test_batch_endomorphism_by_minus_one();
879 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
882 TestFixture::test_batch_mul_zero_scalar();
886TYPED_TEST(TestAffineElement, BatchMulNumPointsNotMultipleOfThreads)
888 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
891 TestFixture::test_batch_mul_num_points_not_multiple_of_threads();
898 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
901 TestFixture::test_two_round_fold_random_challenges();
907 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
910 TestFixture::test_two_round_fold_small_challenges();
914TYPED_TEST(TestAffineElement, TwoRoundFoldMatchesSequentialFolds)
916 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
919 TestFixture::test_two_round_fold_matches_sequential_folds();
925 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
928 TestFixture::test_batch_mul_scalar_under_127_bits();
934 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
937 TestFixture::test_batch_mul_scalar_low_127_bits_zero();
943 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
946 TestFixture::test_batch_mul_k2_128_bits_never_occurs();
952 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
955 TestFixture::test_batch_mul_k2_127_bits();
961 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
964 TestFixture::test_batch_mul_k2_126_bits();
970 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
973 TestFixture::test_batch_mul_k2_125_bits();
979 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
982 TestFixture::test_batch_mul_empty_input();
988 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
991 TestFixture::test_batch_mul_size_less_than_num_threads();
997 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
1000 TestFixture::test_batch_mul_randomized_matches_naive();
1006 if constexpr (!TypeParam::USE_ENDOMORPHISM) {
1009 TestFixture::test_batch_mul_small_scalars_edge_predicate();
1016 TestFixture::test_deserialize_off_curve_throws();
1020TYPED_TEST(TestAffineElement, IsInPrimeSubgroupAcceptsSubgroupPoints)
1022 TestFixture::test_is_in_prime_subgroup_accepts_subgroup_points();
1028 if constexpr (TypeParam::Fq::modulus.data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) {
1031 TestFixture::test_point_compression_invalid_x();
1038 if constexpr (TypeParam::Fq::modulus.data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) {
1041 TestFixture::test_point_compression_non_canonical_x();
1050 fr(
uint256_t(
"24c4cb9c1206ab5470592f237f1698abe684dadf0ab4d7a132c32b2134e2c12e")),
1051 fr(
uint256_t(
"0668b8d61a317fb34ccad55c930b3554f1828a0e5530479ecab4defe6bbc0b2e"))));
1055 fr(
uint256_t(
"107f1b633c6113f3222f39f6256f0546b41a4880918c86864b06471afb410454")),
1056 fr(
uint256_t(
"050cd3823d0c01590b6a50adcc85d2ee4098668fd28805578aa05a423ea938c6"))));
1059 test_vectors.emplace_back(std::vector<uint8_t>{ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64 },
1061 fr(
uint256_t(
"037c5c229ae495f6e8d1b4bf7723fafb2b198b51e27602feb8a4d1053d685093")),
1062 fr(
uint256_t(
"10cf9596c5b2515692d930efa2cf3817607e4796856a79f6af40c949b066969f"))));
1065 auto result = grumpkin::g1::affine_element::hash_to_curve(
std::get<0>(test_case), 0);
#define EXPECT_THROW_OR_ABORT(statement, matcher)
static std::vector< fr > serialize_to_fields(const T &val)
Conversion from transcript values to bb::frs.
static Element mul_without_endomorphism(const Element &element, const Scalar &scalar)
static Element mul_with_endomorphism(const Element &element, const Scalar &scalar)
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
element mul_with_endomorphism(const Fr &scalar) const noexcept
element mul_without_endomorphism(const Fr &scalar) const noexcept
group_elements::affine_element< Fq, Fr, Params > affine_element
constexpr bool get_bit(uint64_t bit_index) const
#define G(r, i, a, b, c, d)
test_vector test_vectors[]
std::conditional_t< IsGoblinBigGroup< C, Fq, Fr, G >, element_goblin::goblin_element< C, goblin_field< C >, Fr, G >, element_default::element< C, Fq, Fr, G > > element
element wraps either element_default::element or element_goblin::goblin_element depending on parametr...
Entry point for Barretenberg command-line interface.
void read(B &it, field2< base_field, Params > &value)
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
field< Bn254FrParams > fr
void write(B &buf, field2< base_field, Params > const &value)
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
testing::Types< VKTestParams< UltraFlavor, stdlib::recursion::honk::DefaultIO< UltraCircuitBuilder > >, VKTestParams< UltraFlavor, stdlib::recursion::honk::RollupIO >, VKTestParams< UltraKeccakFlavor, stdlib::recursion::honk::DefaultIO< UltraCircuitBuilder > >, VKTestParams< MegaFlavor, stdlib::recursion::honk::DefaultIO< MegaCircuitBuilder > > > TestTypes
bb::VectorAffineElementPushSpan< BaseParams > lhs
bb::VectorAffineElementPushSpan< BaseParams > rhs
static constexpr field one()
static constexpr uint256_t modulus
static void split_into_endomorphism_scalars(const field &k, field &k1, field &k2)
Full-width endomorphism decomposition: k ≡ k1 - k2·λ (mod r). Modifies the field elements k1 and k2.
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr bool is_zero() const noexcept
BB_INLINE constexpr field from_montgomery_form() const noexcept
static constexpr field zero()
void throw_or_abort(std::string const &err)