Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
fq.test.cpp
Go to the documentation of this file.
1#include "fq.hpp"
5#include <gtest/gtest.h>
6
7using namespace bb;
8
9// Used to ensure variables are evaluated at runtime and not compile time.
10// If EXPECT_EQ macro params are evaluated at compile-time, compiler can optimize them away.
11// This triggers compiler errors due to the gtest suite expecting at least one test statement in a TEST macro
12void shallow_copy(const fq& in, fq& out)
13{
14 out.data[0] = in.data[0];
15 out.data[1] = in.data[1];
16 out.data[2] = in.data[2];
17 out.data[3] = in.data[3];
18};
19TEST(fq, Msgpack)
20{
21 auto [actual, expected] = msgpack_roundtrip(bb::fq{ 1ULL, 2ULL, 3ULL, 4ULL });
22 EXPECT_EQ(actual, expected);
23}
24
25TEST(fq, Eq)
26{
27 constexpr fq a{ 0x01, 0x02, 0x03, 0x04 };
28 constexpr fq b{ 0x01, 0x02, 0x03, 0x04 };
29 constexpr fq c{ 0x01, 0x02, 0x03, 0x05 };
30 constexpr fq d{ 0x01, 0x02, 0x04, 0x04 };
31 constexpr fq e{ 0x01, 0x03, 0x03, 0x04 };
32 constexpr fq f{ 0x02, 0x02, 0x03, 0x04 };
33 static_assert(a == b);
34 static_assert(!(a == c));
35 static_assert(!(a == d));
36 static_assert(!(a == e));
37 static_assert(!(a == f));
38
39 fq a_var;
40 fq b_var;
41 fq c_var;
42 fq d_var;
43 fq e_var;
44 fq f_var;
45
46 shallow_copy(a, a_var);
47 shallow_copy(b, b_var);
48 shallow_copy(c, c_var);
49 shallow_copy(d, d_var);
50 shallow_copy(e, e_var);
51 shallow_copy(f, f_var);
52
53 EXPECT_EQ(a_var == a_var, true);
54 EXPECT_EQ(a_var == b_var, true);
55 EXPECT_EQ(a_var == c_var, false);
56 EXPECT_EQ(a_var == d_var, false);
57 EXPECT_EQ(a_var == e_var, false);
58 EXPECT_EQ(a_var == f_var, false);
59}
60
61TEST(fq, IsZero)
62{
63 fq a = fq::zero();
64 fq b = fq::zero();
65 fq c = fq::zero();
66 fq d = fq::zero();
67 fq e = fq::zero();
68
69 b.data[0] = 1;
70 c.data[1] = 1;
71 d.data[2] = 1;
72 e.data[3] = 1;
73 EXPECT_EQ(a.is_zero(), true);
74 EXPECT_EQ(b.is_zero(), false);
75 EXPECT_EQ(c.is_zero(), false);
76 EXPECT_EQ(d.is_zero(), false);
77 EXPECT_EQ(e.is_zero(), false);
78}
79
80TEST(fq, RandomElement)
81{
84
85 EXPECT_EQ(a == b, false);
86 EXPECT_EQ(a.is_zero(), false);
87 EXPECT_EQ(a.is_zero(), false);
88}
89
90TEST(fq, MulCheckAgainstConstants)
91{
92 // test against some randomly generated test data
93 constexpr fq a = uint256_t{ 0xa9b879029c49e60eUL, 0x2517b72250caa7b3UL, 0x6b86c81105dae2d1UL, 0x3a81735d5aec0c3UL };
94 constexpr fq a_copy =
95 uint256_t{ 0xa9b879029c49e60eUL, 0x2517b72250caa7b3UL, 0x6b86c81105dae2d1UL, 0x3a81735d5aec0c3UL };
96 constexpr fq b = uint256_t{ 0x744fc10aec23e56aUL, 0x5dea4788a3b936a6UL, 0xa0a89f4a8af01df1UL, 0x72ae28836807df3UL };
97 constexpr fq b_copy =
98 uint256_t{ 0x744fc10aec23e56aUL, 0x5dea4788a3b936a6UL, 0xa0a89f4a8af01df1UL, 0x72ae28836807df3UL };
99
100 constexpr fq const_expected =
101 uint256_t{ 0x6c0a789c0028fd09UL, 0xca9520d84c684efaUL, 0xcbf3f7b023a852b4UL, 0x1b2e4dac41400621UL };
102 constexpr fq const_result = a * b;
103 static_assert(const_result == const_expected);
104 static_assert(a == a_copy);
105 static_assert(b == b_copy);
106
107 fq c;
108 fq d;
109 shallow_copy(a, c);
110 shallow_copy(b, d);
111 EXPECT_EQ(c * d, const_expected);
112}
113
114// validate that zero-value limbs don't cause any problems
115TEST(fq, MulShortIntegers)
116{
117 constexpr fq a{ 0xa, 0, 0, 0 };
118 constexpr fq b{ 0xb, 0, 0, 0 };
119 constexpr uint256_t a_original(a);
120 constexpr uint256_t b_original(b);
121 uint256_t prod_expected = (uint512_t(a_original) * uint512_t(b_original) % uint512_t(fq::modulus)).lo;
122 fq const_expected = prod_expected;
123 constexpr fq const_result = a * b;
124 ASSERT_EQ(const_result, const_expected);
125
126 fq c;
127 fq d;
128 shallow_copy(a, c);
129 shallow_copy(b, d);
130 EXPECT_EQ(c * d, const_expected);
131}
132
133TEST(fq, MulSqrConsistency)
134{
137 fq t1;
138 fq t2;
139 fq mul_result;
140 fq sqr_result;
141 t1 = a - b;
142 t2 = a + b;
143 mul_result = t1 * t2;
144 t1 = a.sqr();
145 t2 = b.sqr();
146 sqr_result = t1 - t2;
147 EXPECT_EQ(mul_result, sqr_result);
148}
149
150TEST(fq, SqrCheckAgainstConstants)
151{
152 constexpr fq a = uint256_t{ 0xa9b879029c49e60eUL, 0x2517b72250caa7b3UL, 0x6b86c81105dae2d1UL, 0x3a81735d5aec0c3UL };
153
154 constexpr fq expected =
155 uint256_t{ 0x41081a42fdaa7e23UL, 0x44d1140f756ed419UL, 0x53716b0a6f253e63UL, 0xb1a0b04044d75fUL };
156 constexpr fq result = a.sqr();
157 static_assert(result == expected);
158
159 fq b;
160 shallow_copy(a, b);
161
162 fq c = b.sqr();
163 EXPECT_EQ(result, c);
164}
165
166TEST(fq, AddCheckAgainstConstants)
167{
168 constexpr fq a{ 0x7d2e20e82f73d3e8, 0x8e50616a7a9d419d, 0xcdc833531508914b, 0xd510253a2ce62c };
169 constexpr fq b{ 0x2829438b071fd14e, 0xb03ef3f9ff9274e, 0x605b671f6dc7b209, 0x8701f9d971fbc9 };
170 constexpr fq const_expected{ 0xa55764733693a536, 0x995450aa1a9668eb, 0x2e239a7282d04354, 0x15c121f139ee1f6 };
171 constexpr fq const_result = a + b;
172 static_assert(const_result == const_expected);
173
174 fq c;
175 fq d;
176 shallow_copy(a, c);
177 shallow_copy(b, d);
178 EXPECT_EQ(c + d, const_expected);
179}
180
181TEST(fq, SubCheckAgainstConstants)
182{
183 constexpr fq a{ 0xd68d01812313fb7c, 0x2965d7ae7c6070a5, 0x08ef9af6d6ba9a48, 0x0cb8fe2108914f53 };
184 constexpr fq b{ 0x2cd2a2a37e9bf14a, 0xebc86ef589c530f6, 0x75124885b362b8fe, 0x1394324205c7a41d };
185 constexpr fq const_expected{ 0xe5daeaf47cf50779, 0xd51ed34a5b0d0a3c, 0x4c2d9827a4d939a6, 0x29891a51e3fb4b5f };
186 constexpr fq const_result = a - b;
187 static_assert(const_result == const_expected);
188
189 fq c;
190 fq d;
191 shallow_copy(a, c);
192 shallow_copy(b, d);
193 EXPECT_EQ(c - d, const_expected);
194}
195
196TEST(fq, CoarseEquivalenceChecks)
197{
200
201 fq c = (a * b) + a - b;
202
203 fq d = a * b + a - b;
204
205 EXPECT_EQ(c, d);
206}
207
208TEST(fq, toMontgomeryForm)
209{
210 fq result = fq{ 0x01, 0x00, 0x00, 0x00 }.to_montgomery_form();
211 fq expected = fq::one();
212 EXPECT_EQ(result, expected);
213}
214
215TEST(fq, FromMontgomeryForm)
216{
217 constexpr fq t0 = fq::one();
218 constexpr fq result = t0.from_montgomery_form();
219 constexpr fq expected{ 0x01, 0x00, 0x00, 0x00 };
220 EXPECT_EQ(result, expected);
221}
222
223TEST(fq, MontgomeryConsistencyCheck)
224{
227 fq aR;
228 fq bR;
229 fq aRR;
230 fq bRR;
231 fq bRRR;
232 fq result_a;
233 fq result_b;
234 fq result_c;
235 fq result_d;
236 aR = a.to_montgomery_form();
237 aRR = aR.to_montgomery_form();
238 bR = b.to_montgomery_form();
239 bRR = bR.to_montgomery_form();
240 bRRR = bRR.to_montgomery_form();
241 result_a = aRR * bRR; // abRRR
242 result_b = aR * bRRR; // abRRR
243 result_c = aR * bR; // abR
244 result_d = a * b; // abR^-1
245 EXPECT_EQ((result_a == result_b), true);
246 result_a.self_from_montgomery_form(); // abRR
247 result_a.self_from_montgomery_form(); // abR
248 result_a.self_from_montgomery_form(); // ab
249 result_c.self_from_montgomery_form(); // ab
250 result_d.self_to_montgomery_form(); // ab
251 EXPECT_EQ((result_a == result_c), true);
252 EXPECT_EQ((result_a == result_d), true);
253}
254
255TEST(fq, AddMulConsistency)
256{
257 fq multiplicand = { 0x09, 0, 0, 0 };
258 multiplicand.self_to_montgomery_form();
259
261 fq result;
262 result = a + a; // 2
263 result += result; // 4
264 result += result; // 8
265 result += a; // 9
266
267 fq expected;
268 expected = a * multiplicand;
269
270 EXPECT_EQ((result == expected), true);
271}
272
273TEST(fq, SubMulConsistency)
274{
275 fq multiplicand = { 0x05, 0, 0, 0 };
276 multiplicand.self_to_montgomery_form();
277
279 fq result;
280 result = a + a; // 2
281 result += result; // 4
282 result += result; // 8
283 result -= a; // 7
284 result -= a; // 6
285 result -= a; // 5
286
287 fq expected;
288 expected = a * multiplicand;
289
290 EXPECT_EQ((result == expected), true);
291}
292
293TEST(fq, beta)
294{
296
297 fq beta_x = { x.data[0], x.data[1], x.data[2], x.data[3] };
298 fq beta = fq::cube_root_of_unity();
299 beta_x = beta_x * beta;
300
301 // compute x^3
302 fq x_cubed;
303 x_cubed = x * x;
304 x_cubed *= x;
305
306 // compute beta_x^3
307 fq beta_x_cubed;
308 beta_x_cubed = beta_x * beta_x;
309 beta_x_cubed *= beta_x;
310
311 EXPECT_EQ((x_cubed == beta_x_cubed), true);
312}
313
314TEST(fq, Invert)
315{
316 fq input = fq::random_element();
317 fq inverse = input.invert();
318 fq result = input * inverse;
319 result = result.reduce_once();
320 result = result.reduce_once();
321 EXPECT_EQ(result, fq::one());
322}
323
324TEST(fq, InvertOneIsOne)
325{
326 fq result = fq::one();
327 result = result.invert();
328 EXPECT_EQ((result == fq::one()), true);
329}
330
331TEST(fq, Sqrt)
332{
333 fq input = fq::one();
334 auto [is_sqr, root] = input.sqrt();
335 fq result = root.sqr();
336 EXPECT_EQ(result, input);
337}
338
339TEST(fq, SqrtRandom)
340{
341 for (size_t i = 0; i < 1; ++i) {
342 fq input = fq::random_element().sqr();
343 auto [is_sqr, root] = input.sqrt();
344 fq root_test = root.sqr();
345 EXPECT_EQ(root_test, input);
346 }
347}
348
349TEST(fq, OneAndZero)
350{
351 fq result;
352 result = fq::one() - fq::one();
353 EXPECT_EQ((result == fq::zero()), true);
354}
355
356TEST(fq, Copy)
357{
358 fq result = fq::random_element();
359 fq expected;
360 fq::__copy(result, expected);
361 EXPECT_EQ((result == expected), true);
362}
363
364TEST(fq, Neg)
365{
367 fq b;
368 b = -a;
369 fq result;
370 result = a + b;
371 EXPECT_EQ((result == fq::zero()), true);
372}
373
374TEST(fq, SplitIntoEndomorphismScalars)
375{
377 fq k1 = 0;
378 fq k2 = 0;
379
381
382 // std::cout << "endo scalars = " << k1 << k2 << std::endl;
383 fq result = 0;
384
387
388 result = k2 * fq::cube_root_of_unity();
389 result = k1 - result;
390
392 EXPECT_EQ(result, k);
393}
394
395TEST(fq, SplitIntoEndomorphismScalarsSimple)
396{
397
398 fq input = { 1, 0, 0, 0 };
399 fq k = { 0, 0, 0, 0 };
400 fq k1 = { 0, 0, 0, 0 };
401 fq k2 = { 0, 0, 0, 0 };
402 fq::__copy(input, k);
403
405
406 fq result{ 0, 0, 0, 0 };
409
410 fq beta = fq::cube_root_of_unity();
411 result = k2 * beta;
412 result = k1 - result;
413
415 for (size_t i = 0; i < 4; ++i) {
416 EXPECT_EQ(result.data[i], k.data[i]);
417 }
418}
419
420TEST(fq, SerializeToBuffer)
421{
422 std::array<uint8_t, 32> buffer;
423 fq a = { 0x1234567876543210, 0x2345678987654321, 0x3456789a98765432, 0x006789abcba98765 };
425
427
428 EXPECT_EQ(buffer[31], 0x10);
429 EXPECT_EQ(buffer[30], 0x32);
430 EXPECT_EQ(buffer[29], 0x54);
431 EXPECT_EQ(buffer[28], 0x76);
432 EXPECT_EQ(buffer[27], 0x78);
433 EXPECT_EQ(buffer[26], 0x56);
434 EXPECT_EQ(buffer[25], 0x34);
435 EXPECT_EQ(buffer[24], 0x12);
436
437 EXPECT_EQ(buffer[23], 0x21);
438 EXPECT_EQ(buffer[22], 0x43);
439 EXPECT_EQ(buffer[21], 0x65);
440 EXPECT_EQ(buffer[20], 0x87);
441 EXPECT_EQ(buffer[19], 0x89);
442 EXPECT_EQ(buffer[18], 0x67);
443 EXPECT_EQ(buffer[17], 0x45);
444 EXPECT_EQ(buffer[16], 0x23);
445
446 EXPECT_EQ(buffer[15], 0x32);
447 EXPECT_EQ(buffer[14], 0x54);
448 EXPECT_EQ(buffer[13], 0x76);
449 EXPECT_EQ(buffer[12], 0x98);
450 EXPECT_EQ(buffer[11], 0x9a);
451 EXPECT_EQ(buffer[10], 0x78);
452 EXPECT_EQ(buffer[9], 0x56);
453 EXPECT_EQ(buffer[8], 0x34);
454
455 EXPECT_EQ(buffer[7], 0x65);
456 EXPECT_EQ(buffer[6], 0x87);
457 EXPECT_EQ(buffer[5], 0xa9);
458 EXPECT_EQ(buffer[4], 0xcb);
459 EXPECT_EQ(buffer[3], 0xab);
460 EXPECT_EQ(buffer[2], 0x89);
461 EXPECT_EQ(buffer[1], 0x67);
462 EXPECT_EQ(buffer[0], 0x00);
463}
464
465TEST(fq, SerializeFromBuffer)
466{
467 std::array<uint8_t, 32> buffer;
468 fq expected = { 0x1234567876543210, 0x2345678987654321, 0x3456789a98765432, 0x006789abcba98765 };
469
470 fq::serialize_to_buffer(expected, &buffer[0]);
471
472 fq result = fq::serialize_from_buffer(&buffer[0]);
473
474 EXPECT_EQ((result == expected), true);
475}
476
477TEST(fq, MultiplicativeGenerator)
478{
479 EXPECT_EQ(fq::multiplicative_generator(), fq(3));
480}
481
482TEST(fq, RInv)
483{
484 uint256_t prime_256{
486 };
487 uint512_t r{ 0, 1 };
488 // -(1/q) mod r
489 uint512_t q{ -prime_256, 0 };
490 uint256_t q_inv = q.invmod(r).lo;
491 uint64_t expected = (q_inv).data[0];
492 uint64_t result = Bn254FqParams::r_inv;
493 EXPECT_EQ(result, expected);
494}
495
496// TEST to check we don't have 0^0=0
497TEST(fq, PowRegressionCheck)
498{
499 fq zero = fq::zero();
500 fq one = fq::one();
501 EXPECT_EQ(zero.pow(uint256_t(0)), one);
502}
503// 438268ca91d42ad f1e7025a7b654e1f f8d9d72e0438b995 8c422ec208ac8a6e
504
505TEST(fq, SqrRegression)
506{
507 std::array<uint256_t, 7> values = {
508 uint256_t(0xbdf876654b0ade1b, 0x2c3a66c64569f338, 0x2cd8bf2ec1fe55a3, 0x11c0ea9ee5693ede),
509 uint256_t(0x551b14ec34f2151c, 0x62e472ed83a2891e, 0xf208d5e5c9b5b3fb, 0x14315aeaf6027d8c),
510 uint256_t(0xad39959ae8013750, 0x7f1d2c709ab84cbb, 0x408028b80a60c2f1, 0x1dcd116fc26f856e),
511 uint256_t(0x95e967d30dcce9ce, 0x56139274241d2ea1, 0x85b19c1c616ec456, 0x1f1780cf9bf045b4),
512 uint256_t(0xbe841c861d8eb80e, 0xc5980d67a21386c0, 0x5fd1f1afecddeeb5, 0x24dbb8c1baea0250),
513 uint256_t(0x3ae4b3a27f05d6e3, 0xc5f6785b12df8d29, 0xc3a6c5f095103046, 0xd6b94cb2cc1fd4b),
514 uint256_t(0xc003c71932a6ced5, 0x6302a413f68e26e9, 0x2ed4a9b64d69fad, 0xfe61ffab1ae227d)
515 };
516 for (auto& value : values) {
517 fq element(value);
518 EXPECT_EQ(element.sqr(), element * element);
519 }
520}
521
522TEST(fq, NegAndSelfNeg0CmpRegression)
523{
524 fq a = 0;
525 fq a_neg = -a;
526 EXPECT_EQ((a == a_neg), true);
527 a = 0;
528 a_neg = 0;
529 a_neg.self_neg();
530 EXPECT_EQ((a == a_neg), true);
531}
532
533// This test shows that ((lo|hi)% modulus) in uint512_t is equivalent to (lo + 2^256 * hi) in field elements so we
534// don't have to use the slow API (uint512_t' modulo operation)
535TEST(fq, EquivalentRandomness)
536{
538 uint512_t random_uint512 = engine.get_random_uint512();
539 auto random_lo = fq(random_uint512.lo);
540 auto random_hi = fq(random_uint512.hi);
542 constexpr auto pow_2_256 = fq(uint256_t(1) << 128).sqr();
543 EXPECT_EQ(random_lo + pow_2_256 * random_hi, fq((random_uint512 % q).lo));
544}
static constexpr uint64_t modulus_0
Definition fq.hpp:24
static constexpr uint64_t modulus_3
Definition fq.hpp:27
static constexpr uint64_t modulus_1
Definition fq.hpp:25
static constexpr uint64_t modulus_2
Definition fq.hpp:26
static constexpr uint64_t r_inv
Definition fq.hpp:96
uint512_t get_random_uint512()
Definition engine.hpp:38
const std::vector< FF > data
FF a
FF b
numeric::RNG & engine
uint8_t buffer[RANDOM_BUFFER_SIZE]
Definition engine.cpp:34
void shallow_copy(const fq &in, fq &out)
Definition fq.test.cpp:12
uintx< uint256_t > uint512_t
Definition uintx.hpp:307
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:190
Entry point for Barretenberg command-line interface.
field< Bn254FqParams > fq
Definition fq.hpp:169
TEST(MegaCircuitBuilder, CopyConstructor)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr field cube_root_of_unity()
static constexpr field one()
static constexpr uint256_t modulus
BB_INLINE constexpr field to_montgomery_form() const noexcept
BB_INLINE constexpr field pow(const uint256_t &exponent) const noexcept
static void split_into_endomorphism_scalars(const field &k, field &k1, field &k2)
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
static constexpr field multiplicative_generator() 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
BB_INLINE constexpr field reduce_once() const noexcept
static constexpr field zero()
std::pair< T, T > msgpack_roundtrip(const T &object)