Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
affine_element.test.cpp
Go to the documentation of this file.
2
13
14#include "gmock/gmock.h"
15#include <algorithm>
16#include <fstream>
17#include <gtest/gtest.h>
18#include <iterator>
19#include <tuple>
20
21using ::testing::Each;
22using ::testing::ElementsAreArray;
23using ::testing::Eq;
24using ::testing::Property;
25
26using namespace bb;
27
28namespace {
29template <typename G1> class TestAffineElement : public testing::Test {
30 using element = typename G1::element;
31 using affine_element = typename G1::affine_element;
32
33 public:
34 static void test_read_write_buffer()
35 {
36 // a generic point
37 {
38 affine_element P = affine_element(element::random_element());
39 affine_element Q;
40 affine_element R;
41
42 std::vector<uint8_t> v(65); // extra byte to allow a bad read
43 uint8_t* ptr = v.data();
44 affine_element::serialize_to_buffer(P, ptr);
45
46 // bad read
47 Q = affine_element::serialize_from_buffer(ptr + 1);
48 ASSERT_FALSE(Q.on_curve() && !Q.is_point_at_infinity());
49 ASSERT_FALSE(P == Q);
50
51 // good read
52 R = affine_element::serialize_from_buffer(ptr);
53 ASSERT_TRUE(R.on_curve());
54 ASSERT_TRUE(P == R);
55 }
56
57 // point at infinity
58 {
59 affine_element P = affine_element(element::random_element());
60 P.self_set_infinity();
61 affine_element R;
62
63 std::vector<uint8_t> v(64);
64 uint8_t* ptr = v.data();
65 affine_element::serialize_to_buffer(P, ptr);
66
67 R = affine_element::serialize_from_buffer(ptr);
68 ASSERT_TRUE(R.is_point_at_infinity());
69 ASSERT_TRUE(P == R);
70 }
71 }
72
73 static void test_read_and_write()
74 {
75 // a generic point
76 {
77 affine_element P = affine_element(element::random_element());
78 [[maybe_unused]] affine_element R;
79
80 std::vector<uint8_t> v(sizeof(R));
81 uint8_t* ptr = v.data();
82 write(ptr, P);
83 ASSERT_TRUE(P.on_curve());
84
85 // // Reset to start?
86 // ptr = v.data();
87
88 const uint8_t* read_ptr = v.data();
89 // good read
90 read(read_ptr, R);
91 ASSERT_TRUE(R.on_curve());
92 ASSERT_TRUE(P == R);
93 }
94 }
95
96 static void test_msgpack_serialization()
97 {
98 // a generic point
99 {
100 affine_element P = affine_element(element::random_element());
101
102 // Serialize using msgpack
103 msgpack::sbuffer sbuf;
104 msgpack::pack(sbuf, P);
105
106 // Deserialize using msgpack
107 msgpack::object_handle oh = msgpack::unpack(sbuf.data(), sbuf.size());
108 msgpack::object deserialized = oh.get();
109
110 affine_element R;
111 deserialized.convert(R);
112
113 ASSERT_TRUE(R.on_curve() && !R.is_point_at_infinity());
114 ASSERT_TRUE(P == R);
115 }
116
117 // point at infinity
118 {
119 affine_element P = affine_element(element::random_element());
120 P.self_set_infinity();
121
122 // Serialize using msgpack
123 msgpack::sbuffer sbuf;
124 msgpack::pack(sbuf, P);
125
126 // Deserialize using msgpack
127 msgpack::object_handle oh = msgpack::unpack(sbuf.data(), sbuf.size());
128 msgpack::object deserialized = oh.get();
129
130 affine_element R;
131 deserialized.convert(R);
132
133 ASSERT_TRUE(R.is_point_at_infinity());
134 ASSERT_TRUE(P == R);
135 }
136 }
137
138 static void test_point_compression()
139 {
140 for (size_t i = 0; i < 10; i++) {
141 affine_element P = affine_element(element::random_element());
142 uint256_t compressed = uint256_t(P.x);
143 if (uint256_t(P.y).get_bit(0)) {
144 compressed.data[3] |= group_elements::UINT256_TOP_LIMB_MSB;
145 }
146 affine_element Q = affine_element::from_compressed(compressed);
147 EXPECT_EQ(P, Q);
148 }
149 }
150
151 static void test_point_compression_unsafe()
152 {
153 for (size_t i = 0; i < 100; i++) {
154 affine_element P = affine_element(element::random_element());
155 uint256_t compressed = uint256_t(P.x);
156
157 // Note that we do not check the point Q_points[1] because its highly unlikely to hit a point P on the curve
158 // such that r < P.x < q.
159 std::array<affine_element, 2> Q_points = affine_element::from_compressed_unsafe(compressed);
160 EXPECT_EQ(P, Q_points[0]);
161 }
162 }
163
164 // Regression test to ensure that the point at infinity is not equal to its coordinate-wise reduction, which may lie
165 // on the curve, depending on the y-coordinate.
166 // TODO(@Rumata888): add corresponding typed test class
167 static void test_infinity_regression()
168 {
169 affine_element P;
170 P.self_set_infinity();
171 affine_element R(0, P.y);
172 ASSERT_FALSE(P == R);
173 }
174 static void test_infinity_ordering_regression()
175 {
176 affine_element P(0, 1);
177 affine_element Q(0, 1);
178
179 P.self_set_infinity();
180 EXPECT_NE(P < Q, Q < P);
181 }
182
187 static void test_batch_endomorphism_by_minus_one()
188 {
189 constexpr size_t num_points = 2;
190 std::vector<affine_element> affine_points(num_points, affine_element::one());
191
193 element::batch_mul_with_endomorphism(affine_points, -affine_element::Fr::one());
194
195 for (size_t i = 0; i < num_points; i++) {
196 EXPECT_EQ(affine_points[i], -result[i]);
197 }
198 }
199
204 static void test_fixed_point_at_infinity()
205 {
206 using Fq = affine_element::Fq;
207 affine_element P = affine_element::infinity();
208 affine_element Q(Fq::zero(), Fq::zero());
209 Q.x.self_set_msb();
210 affine_element R = affine_element(element::random_element());
211 EXPECT_EQ(P, Q);
212 EXPECT_NE(P, R);
213 }
214};
215
216// using TestTypes = testing::Types<bb::g1>;
217using TestTypes = testing::Types<bb::g1, grumpkin::g1, secp256k1::g1, secp256r1::g1>;
218} // namespace
219
220TYPED_TEST_SUITE(TestAffineElement, TestTypes);
221
222TYPED_TEST(TestAffineElement, ReadWrite)
223{
224 TestFixture::test_read_and_write();
225}
226
227TYPED_TEST(TestAffineElement, ReadWriteBuffer)
228{
229 TestFixture::test_read_write_buffer();
230 TestFixture::test_msgpack_serialization();
231}
232
233TYPED_TEST(TestAffineElement, PointCompression)
234{
235 if constexpr (TypeParam::Fq::modulus.data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) {
236 GTEST_SKIP();
237 } else {
238 TestFixture::test_point_compression();
239 }
240}
241
242TYPED_TEST(TestAffineElement, FixedInfinityPoint)
243{
244 if constexpr (TypeParam::Fq::modulus.data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) {
245 GTEST_SKIP();
246 } else {
247 TestFixture::test_fixed_point_at_infinity();
248 }
249}
250
251TYPED_TEST(TestAffineElement, PointCompressionUnsafe)
252{
253 if constexpr (TypeParam::Fq::modulus.data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) {
254 TestFixture::test_point_compression_unsafe();
255 } else {
256 GTEST_SKIP();
257 }
258}
259
260TYPED_TEST(TestAffineElement, InfinityOrderingRegression)
261{
262 TestFixture::test_infinity_ordering_regression();
263}
264
265namespace bb::group_elements {
266// mul_with_endomorphism and mul_without_endomorphism are private in affine_element.
267// We could make those public to test or create other public utilities, but to keep the API intact we
268// instead mark TestElementPrivate as a friend class so that our test functions can have access.
270 public:
271 template <typename Element, typename Scalar>
272 static Element mul_without_endomorphism(const Element& element, const Scalar& scalar)
273 {
274 return element.mul_without_endomorphism(scalar);
275 }
276 template <typename Element, typename Scalar>
277 static Element mul_with_endomorphism(const Element& element, const Scalar& scalar)
278 {
279 return element.mul_with_endomorphism(scalar);
280 }
281};
282} // namespace bb::group_elements
283
284// Our endomorphism-specialized multiplication should match our generic multiplication
285TYPED_TEST(TestAffineElement, MulWithEndomorphismMatchesMulWithoutEndomorphism)
286{
287 for (int i = 0; i < 100; i++) {
288 auto x1 = bb::group_elements::element(grumpkin::g1::affine_element::random_element());
292 EXPECT_EQ(r1, r2);
293 }
294}
295
296TEST(AffineElementFromPublicInputs, Bn254FromPublicInputs)
297{
298 using Curve = curve::BN254;
299 using Fr = Curve::ScalarField;
300 using AffineElement = Curve::AffineElement;
301
302 AffineElement point = AffineElement::random_element();
303
304 // Construct public inputs using FrCodec format (2 limbs of 136 bits per coordinate)
305 std::vector<Fr> public_inputs = FrCodec::serialize_to_fields(point);
306
307 std::span<Fr, AffineElement::PUBLIC_INPUTS_SIZE> limbs(public_inputs.data(), AffineElement::PUBLIC_INPUTS_SIZE);
308
309 auto reconstructed = FrCodec::deserialize_from_fields<AffineElement>(limbs);
310
311 EXPECT_EQ(reconstructed, point);
312}
313
314TEST(AffineElementFromPublicInputs, GrumpkinFromPublicInputs)
315{
316 using Curve = curve::Grumpkin;
317 using AffineElement = Curve::AffineElement;
318 using Fr = bb::fr;
319
320 AffineElement point = AffineElement::random_element();
321
322 // Construct public inputs using FrCodec format
323 std::vector<Fr> public_inputs = FrCodec::serialize_to_fields(point);
324
325 std::span<Fr, AffineElement::PUBLIC_INPUTS_SIZE> limbs(public_inputs.data(), AffineElement::PUBLIC_INPUTS_SIZE);
326
327 auto reconstructed = FrCodec::deserialize_from_fields<AffineElement>(limbs);
328
329 EXPECT_EQ(reconstructed, point);
330}
331
332// TODO(https://github.com/AztecProtocol/barretenberg/issues/909): These tests are not typed for no reason
333// Multiplication of a point at infinity by a scalar should be a point at infinity
334TEST(AffineElement, InfinityMulByScalarIsInfinity)
335{
336 auto result = grumpkin::g1::affine_element::infinity() * grumpkin::fr::random_element();
337 EXPECT_TRUE(result.is_point_at_infinity());
338}
339
340// Batched multiplication of points should match
341TEST(AffineElement, BatchMulMatchesNonBatchMul)
342{
343 constexpr size_t num_points = 512;
344 std::vector<grumpkin::g1::affine_element> affine_points(num_points - 1, grumpkin::g1::affine_element::infinity());
345 // Include a point at infinity to test the mixed infinity + non-infinity case
346 affine_points.push_back(grumpkin::g1::affine_element::infinity());
349 std::transform(affine_points.begin(),
350 affine_points.end(),
351 std::back_inserter(expected),
352 [exponent](const auto& el) { return el * exponent; });
353
355 grumpkin::g1::element::batch_mul_with_endomorphism(affine_points, exponent);
356
357 EXPECT_THAT(result, ElementsAreArray(expected));
358}
359
360// Batched multiplication of a point at infinity by a scalar should result in points at infinity
361TEST(AffineElement, InfinityBatchMulByScalarIsInfinity)
362{
363 constexpr size_t num_points = 1024;
364 std::vector<grumpkin::g1::affine_element> affine_points(num_points, grumpkin::g1::affine_element::infinity());
365
367 grumpkin::g1::element::batch_mul_with_endomorphism(affine_points, grumpkin::fr::random_element());
368
369 EXPECT_THAT(result, Each(Property(&grumpkin::g1::affine_element::is_point_at_infinity, Eq(true))));
370}
371
372TYPED_TEST(TestAffineElement, BatchEndomoprhismByMinusOne)
373{
374 if constexpr (TypeParam::USE_ENDOMORPHISM) {
375 TestFixture::test_batch_endomorphism_by_minus_one();
376 } else {
377 GTEST_SKIP();
378 }
379}
380
381TEST(AffineElement, HashToCurve)
382{
384 test_vectors.emplace_back(std::vector<uint8_t>(),
386 fr(uint256_t("24c4cb9c1206ab5470592f237f1698abe684dadf0ab4d7a132c32b2134e2c12e")),
387 fr(uint256_t("0668b8d61a317fb34ccad55c930b3554f1828a0e5530479ecab4defe6bbc0b2e"))));
388
389 test_vectors.emplace_back(std::vector<uint8_t>{ 1 },
391 fr(uint256_t("107f1b633c6113f3222f39f6256f0546b41a4880918c86864b06471afb410454")),
392 fr(uint256_t("050cd3823d0c01590b6a50adcc85d2ee4098668fd28805578aa05a423ea938c6"))));
393
394 // "hello world"
395 test_vectors.emplace_back(std::vector<uint8_t>{ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64 },
397 fr(uint256_t("037c5c229ae495f6e8d1b4bf7723fafb2b198b51e27602feb8a4d1053d685093")),
398 fr(uint256_t("10cf9596c5b2515692d930efa2cf3817607e4796856a79f6af40c949b066969f"))));
399
400 for (std::tuple<std::vector<uint8_t>, grumpkin::g1::affine_element> test_case : test_vectors) {
401 auto result = grumpkin::g1::affine_element::hash_to_curve(std::get<0>(test_case), 0);
402 auto expected_result = std::get<1>(test_case);
403 std::cout << result << std::endl;
404 EXPECT_TRUE(result == expected_result);
405 }
406}
static std::vector< fr > serialize_to_fields(const T &val)
Conversion from transcript values to bb::frs.
typename Group::affine_element AffineElement
Definition grumpkin.hpp:65
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....
Definition element.hpp:33
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
Definition group.hpp:42
constexpr bool get_bit(uint64_t bit_index) const
bool expected_result
test_vector test_vectors[]
bb::curve::BN254::Element Element
const size_t num_points
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.
Definition api.hpp:5
void read(B &it, field2< base_field, Params > &value)
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
field< Bn254FrParams > fr
Definition fr.hpp:155
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
Definition tuple.hpp:13
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
static field random_element(numeric::RNG *engine=nullptr) noexcept
static constexpr field zero()