Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
general_field.test.cpp
Go to the documentation of this file.
1
18#include <gtest/gtest.h>
19
20using namespace bb;
21
22template <typename F> class FieldTest : public ::testing::Test {};
23
24template <typename F>
25concept HasPow = IsAnyOf<F, bb::fq, bb::fr, secp256k1::fq, secp256k1::fr, secp256r1::fq, secp256r1::fr>;
26
27template <typename F>
28concept HasSqrt = IsAnyOf<F, bb::fq, bb::fr>;
29
30template <typename F>
31concept IsExtensionField = IsAnyOf<F, bb::fq2, bb::fq6, bb::fq12>;
32
33using AllFieldTypes = ::testing::
34 Types<bb::fq, bb::fr, secp256k1::fq, secp256k1::fr, secp256r1::fq, secp256r1::fr, bb::fq2, bb::fq6, bb::fq12>;
35
37
38// ================================
39// Zero and One
40// ================================
41
42TYPED_TEST(FieldTest, ZeroIsAdditiveIdentity)
43{
44 using FF = TypeParam;
45
47 FF zero = FF::zero();
48
49 EXPECT_EQ(a + zero, a);
50 EXPECT_EQ(zero + a, a);
51}
52
53TYPED_TEST(FieldTest, OneIsMultiplicativeIdentity)
54{
55 using FF = TypeParam;
56
58 FF one = FF::one();
59
60 EXPECT_EQ(a * one, a);
61 EXPECT_EQ(one * a, a);
62}
63
65{
66 using FF = TypeParam;
67
68 FF zero = FF::zero();
69 FF one = FF::one();
70 FF random = FF::random_element();
71
72 EXPECT_TRUE(zero.is_zero());
73 EXPECT_FALSE(one.is_zero());
74 EXPECT_FALSE(random.is_zero());
75}
76
77// ================================
78// Addition
79// ================================
80
81TYPED_TEST(FieldTest, AdditionCommutative)
82{
83 using FF = TypeParam;
84
87
88 EXPECT_EQ(a + b, b + a);
89}
90
91TYPED_TEST(FieldTest, AdditionAssociative)
92{
93 using FF = TypeParam;
94
98 FF a_plus_b = a + b;
99 FF b_plus_c = b + c;
100
101 EXPECT_EQ(a_plus_b + c, a + b_plus_c);
102}
103
104// ================================
105// Subtraction and Negation
106// ================================
107
108TYPED_TEST(FieldTest, SubtractionIsAdditionOfNegation)
109{
110 using FF = TypeParam;
111
114 FF neg_b = -b;
115
116 EXPECT_EQ(a - b, a + neg_b);
117}
118
119TYPED_TEST(FieldTest, NegationCancels)
120{
121 using FF = TypeParam;
122
124 FF neg_a = -a;
125 FF result = a + neg_a;
126
127 EXPECT_EQ(result, FF::zero());
128}
129
130TYPED_TEST(FieldTest, NegationOfZero)
131{
132 using FF = TypeParam;
133
134 FF zero = FF::zero();
135 FF neg_zero = -zero;
136
137 EXPECT_EQ(zero, neg_zero);
138}
139
140TYPED_TEST(FieldTest, DoubleNegation)
141{
142 using FF = TypeParam;
143
145 EXPECT_EQ(-(-a), a);
146}
147
148// ================================
149// Multiplication
150// ================================
151
152TYPED_TEST(FieldTest, MultiplicationCommutative)
153{
154 using FF = TypeParam;
155
158
159 EXPECT_EQ(a * b, b * a);
160}
161
162TYPED_TEST(FieldTest, MultiplicationAssociative)
163{
164 using FF = TypeParam;
165
169
170 EXPECT_EQ((a * b) * c, a * (b * c));
171}
172
173TYPED_TEST(FieldTest, MultiplicationDistributive)
174{
175 using FF = TypeParam;
176
180
181 EXPECT_EQ(a * (b + c), (a * b) + (a * c));
182}
183
185{
186 using FF = TypeParam;
187
189 FF zero = FF::zero();
190
191 EXPECT_EQ(a * zero, FF::zero());
192 EXPECT_EQ(zero * a, FF::zero());
193}
194
195// ================================
196// Squaring
197// ================================
198
199TYPED_TEST(FieldTest, SquaringMatchesMultiplication)
200{
201 using FF = TypeParam;
202
204 FF sqr_result = a.sqr();
205 FF mul_result = a * a;
206
207 EXPECT_EQ(sqr_result, mul_result);
208}
209
210TYPED_TEST(FieldTest, DifferenceOfSquares)
211{
212 using FF = TypeParam;
213
214 // (a - b)(a + b) = a² - b²
217
218 FF lhs = (a - b) * (a + b);
219 FF rhs = a.sqr() - b.sqr();
220
221 EXPECT_EQ(lhs, rhs);
222}
223
224// ================================
225// Inversion
226// ================================
227
228TYPED_TEST(FieldTest, InverseProperty)
229{
230 using FF = TypeParam;
231
233 FF a_inv = a.invert();
234 FF result = a * a_inv;
235
236 EXPECT_EQ(result, FF::one());
237}
238
239TYPED_TEST(FieldTest, InvertOneIsOne)
240{
241 using FF = TypeParam;
242
243 FF one = FF::one();
244 FF result = one.invert();
245 EXPECT_EQ(result, FF::one());
246}
247
248TYPED_TEST(FieldTest, DoubleInverse)
249{
250 using FF = TypeParam;
251
253 FF a_inv_inv = a.invert().invert();
254
255 EXPECT_EQ(a_inv_inv, a);
256}
257
258// ================================
259// Exponentation
260// ================================
261
262TYPED_TEST(FieldTest, PowRegressionCheck)
263{
264 if constexpr (HasPow<TypeParam>) {
265 using FF = TypeParam;
266
267 FF zero = FF::zero();
268 FF one = FF::one();
269
270 EXPECT_EQ(zero.pow(uint256_t(0)), one);
271 }
272}
273
275{
276 if constexpr (HasSqrt<TypeParam>) {
277 using FF = TypeParam;
278
279 FF input = FF::random_element();
280 auto [is_sqr, root] = input.sqrt();
281 FF result = root.sqr();
282
283 if (is_sqr) {
284 EXPECT_EQ(result, input);
285 } else {
286 EXPECT_EQ(result, FF::zero());
287 }
288 }
289}
290
291// ================================
292// Self-Modifying Operations
293// ================================
294
296{
297 using FF = TypeParam;
298
300 FF a_copy = a;
301
302 a_copy.self_neg();
303 EXPECT_EQ(a_copy, -a);
304}
305
306TYPED_TEST(FieldTest, OperatorPlusEquals)
307{
308 using FF = TypeParam;
309
312 FF expected = a + b;
313
314 a += b;
315 EXPECT_EQ(a, expected);
316}
317
318TYPED_TEST(FieldTest, OperatorMinusEquals)
319{
320 using FF = TypeParam;
321
324 FF expected = a - b;
325
326 a -= b;
327 EXPECT_EQ(a, expected);
328}
329
330TYPED_TEST(FieldTest, OperatorTimesEquals)
331{
332 using FF = TypeParam;
333
336 FF expected = a * b;
337
338 a *= b;
339 EXPECT_EQ(a, expected);
340}
341
343{
344 using FF = TypeParam;
345
347 FF expected = a.sqr();
348
349 a.self_sqr();
350 EXPECT_EQ(a, expected);
351}
352
353// ================================
354// Algebraic Identities
355// ================================
356
357TYPED_TEST(FieldTest, AddMulConsistency)
358{
359 using FF = TypeParam;
360
361 // a + a + a should equal 3a (verified via repeated addition on both sides)
363 FF sum = a + a + a;
364
365 // Build "3" as one + one + one to avoid integer constructors, which do not exist in our implementation of extension
366 // fields.
367 FF three = FF::one() + FF::one() + FF::one();
368 FF product = a * three;
369
370 EXPECT_EQ(sum, product);
371}
372
373TYPED_TEST(FieldTest, SubMulConsistency)
374{
375 using FF = TypeParam;
376
377 // 4a - a = 3a
379 FF four_a = a + a + a + a;
380 FF result = four_a - a;
381
382 FF three = FF::one() + FF::one() + FF::one();
383 FF expected = a * three;
384
385 EXPECT_EQ(result, expected);
386}
387
388TYPED_TEST(FieldTest, MulSqrConsistency)
389{
390 using FF = TypeParam;
391
392 // Check that (a - b) * (a + b) = a^2 - b^2
395 FF t1;
396 FF t2;
397 FF mul_result;
398 FF sqr_result;
399
400 t1 = a - b;
401 t2 = a + b;
402 mul_result = t1 * t2;
403
404 t1 = a.sqr();
405 t2 = b.sqr();
406 sqr_result = t1 - t2;
407
408 EXPECT_EQ(mul_result, sqr_result);
409}
410
411// ================================
412// Montgomery Form and Reduction
413// ================================
414
415TYPED_TEST(FieldTest, FromMontgomeryForm)
416{
417 using FF = TypeParam;
418
419 constexpr FF t0 = FF::one();
420 // Use from_montgomery_form_reduced() for base fields to ensure full reduction to [0, p).
421 // The WASM Montgomery multiplication returns coarse results in [0, 2p), so without
422 // reduce_once() the raw limbs may hold p+1 instead of 1.
423 // Extension fields already call from_montgomery_form_reduced() on their components internally.
424 constexpr FF result = [&]() constexpr {
425 if constexpr (!IsExtensionField<FF>) {
427 } else {
428 return t0.from_montgomery_form();
429 }
430 }();
431 constexpr uint256_t expected = 0x01;
432 uint256_t to_be_compared;
433
434 if constexpr (!IsExtensionField<FF>) {
435 to_be_compared = { result.data[0], result.data[1], result.data[2], result.data[3] };
436 } else if constexpr (std::is_same_v<FF, bb::fq2>) {
437 EXPECT_EQ(result.c1, bb::fq::zero());
438 to_be_compared = { result.c0.data[0], result.c0.data[1], result.c0.data[2], result.c0.data[3] };
439 } else if constexpr (std::is_same_v<FF, bb::fq6>) {
440 EXPECT_EQ(result.c0.c1, bb::fq::zero());
441 EXPECT_EQ(result.c1, bb::fq2::zero());
442 EXPECT_EQ(result.c2, bb::fq2::zero());
443 to_be_compared = { result.c0.c0.data[0], result.c0.c0.data[1], result.c0.c0.data[2], result.c0.c0.data[3] };
444 } else {
445 EXPECT_EQ(result.c0.c0.c1, bb::fq::zero());
446 EXPECT_EQ(result.c0.c1, bb::fq2::zero());
447 EXPECT_EQ(result.c0.c2, bb::fq2::zero());
448 EXPECT_EQ(result.c1, bb::fq6::zero());
449 to_be_compared = {
450 result.c0.c0.c0.data[0], result.c0.c0.c0.data[1], result.c0.c0.c0.data[2], result.c0.c0.c0.data[3]
451 };
452 }
453
454 EXPECT_EQ(to_be_compared, expected);
455}
456
457TYPED_TEST(FieldTest, MontgomeryConsistencyCheck)
458{
459 using FF = TypeParam;
460
463 FF aR;
464 FF bR;
465 FF aRR;
466 FF bRR;
467 FF bRRR;
468 FF result_a;
469 FF result_b;
470 FF result_c;
471 FF result_d;
472
473 aR = a.to_montgomery_form();
474 aRR = aR.to_montgomery_form();
475 bR = b.to_montgomery_form();
476 bRR = bR.to_montgomery_form();
477 bRRR = bRR.to_montgomery_form();
478 result_a = aRR * bRR; // abRRR
479 result_b = aR * bRRR; // abRRR
480 result_c = aR * bR; // abR
481 result_d = a * b; // abR^-1
482
483 EXPECT_EQ((result_a == result_b), true);
484
486 result_a.self_from_montgomery_form(); // abRR
487 result_a.self_from_montgomery_form(); // abR
488 result_a.self_from_montgomery_form(); // ab
489 result_c.self_from_montgomery_form(); // ab
490 result_d.self_to_montgomery_form(); // ab
491
492 EXPECT_EQ((result_a == result_c), true);
493 EXPECT_EQ((result_a == result_d), true);
494 }
495}
496
497// ================================
498// Other tests
499// ================================
500
502{
503 if constexpr (!IsExtensionField<TypeParam>) {
504 using FF = TypeParam;
505
506 FF result = FF::random_element();
507 FF expected;
508 FF::__copy(result, expected);
509
510 EXPECT_EQ((result == expected), true);
511 }
512}
513
514TYPED_TEST(FieldTest, SerializeToBuffer)
515{
516 if constexpr (!IsExtensionField<TypeParam>) {
517 using FF = TypeParam;
518
519 std::array<uint8_t, 32> buffer;
520 FF a{ 0x1234567876543210, 0x2345678987654321, 0x3456789a98765432, 0x006789abcba98765 };
522
524
525 EXPECT_EQ(buffer[31], 0x10);
526 EXPECT_EQ(buffer[30], 0x32);
527 EXPECT_EQ(buffer[29], 0x54);
528 EXPECT_EQ(buffer[28], 0x76);
529 EXPECT_EQ(buffer[27], 0x78);
530 EXPECT_EQ(buffer[26], 0x56);
531 EXPECT_EQ(buffer[25], 0x34);
532 EXPECT_EQ(buffer[24], 0x12);
533
534 EXPECT_EQ(buffer[23], 0x21);
535 EXPECT_EQ(buffer[22], 0x43);
536 EXPECT_EQ(buffer[21], 0x65);
537 EXPECT_EQ(buffer[20], 0x87);
538 EXPECT_EQ(buffer[19], 0x89);
539 EXPECT_EQ(buffer[18], 0x67);
540 EXPECT_EQ(buffer[17], 0x45);
541 EXPECT_EQ(buffer[16], 0x23);
542
543 EXPECT_EQ(buffer[15], 0x32);
544 EXPECT_EQ(buffer[14], 0x54);
545 EXPECT_EQ(buffer[13], 0x76);
546 EXPECT_EQ(buffer[12], 0x98);
547 EXPECT_EQ(buffer[11], 0x9a);
548 EXPECT_EQ(buffer[10], 0x78);
549 EXPECT_EQ(buffer[9], 0x56);
550 EXPECT_EQ(buffer[8], 0x34);
551
552 EXPECT_EQ(buffer[7], 0x65);
553 EXPECT_EQ(buffer[6], 0x87);
554 EXPECT_EQ(buffer[5], 0xa9);
555 EXPECT_EQ(buffer[4], 0xcb);
556 EXPECT_EQ(buffer[3], 0xab);
557 EXPECT_EQ(buffer[2], 0x89);
558 EXPECT_EQ(buffer[1], 0x67);
559 EXPECT_EQ(buffer[0], 0x00);
560 }
561}
562
563TYPED_TEST(FieldTest, SerializeFromBuffer)
564{
565 if constexpr (!IsExtensionField<TypeParam>) {
566 using FF = TypeParam;
567
568 std::array<uint8_t, 32> buffer;
569 FF expected{ 0x1234567876543210, 0x2345678987654321, 0x3456789a98765432, 0x006789abcba98765 };
570
571 FF::serialize_to_buffer(expected, &buffer[0]);
572 FF result = FF::serialize_from_buffer(&buffer[0]);
573
574 EXPECT_EQ(result, expected);
575 } else if constexpr (std::is_same_v<TypeParam, bb::fq2>) {
576 std::array<uint8_t, 64> buffer;
577 fq expected_c0 = { 0x1234567876543210, 0x2345678987654321, 0x3456789a98765432, 0x006789abcba98765 };
578 fq expected_c1 = { 0x12a4e67f76b43210, 0x23e56f898a65cc21, 0x005678add98e5432, 0x1f6789a2cba98700 };
579 fq2 expected{ expected_c0, expected_c1 };
580
581 fq2::serialize_to_buffer(expected, &buffer[0]);
582
584
585 EXPECT_EQ(result, expected);
586 }
587}
static constexpr field6 zero()
Definition field6.hpp:58
FF a
FF b
std::unique_ptr< uint8_t[]> buffer
Definition engine.cpp:50
::testing::Types< bb::fq, bb::fr, secp256k1::fq, secp256k1::fr, secp256r1::fq, secp256r1::fr, bb::fq2, bb::fq6, bb::fq12 > AllFieldTypes
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
Inner sum(Cont< Inner, Args... > const &in)
Definition container.hpp:70
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static field2 serialize_from_buffer(uint8_t *buffer)
static void serialize_to_buffer(const field2 &value, uint8_t *buffer)
BB_INLINE constexpr field from_montgomery_form_reduced() const noexcept
BB_INLINE constexpr field to_montgomery_form() const noexcept
BB_INLINE constexpr field pow(const uint256_t &exponent) const noexcept
BB_INLINE constexpr void self_sqr() &noexcept
constexpr field invert() const noexcept
BB_INLINE constexpr void self_neg() &noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr field sqr() const noexcept
static field serialize_from_buffer(const uint8_t *buffer)
static void serialize_to_buffer(const field &value, uint8_t *buffer)
constexpr std::pair< bool, field > sqrt() const noexcept
Compute square root of the field element.
static BB_INLINE void __copy(const field &a, field &r) noexcept
BB_INLINE constexpr void self_from_montgomery_form() &noexcept
BB_INLINE constexpr bool is_zero() const noexcept
BB_INLINE constexpr void self_to_montgomery_form() &noexcept
BB_INLINE constexpr field from_montgomery_form() const noexcept