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