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
11#include "fq.hpp"
14#include <gtest/gtest.h>
15
16using namespace bb;
17
18namespace {
20} // namespace
21
22// ================================
23// Fixed Compile-Time Tests (field-specific expected values)
24// These tests use hardcoded expected values that are only valid for native builds (R = 2^256).
25// WASM uses R = 2^261.
26// ================================
27
28#if defined(__SIZEOF_INT128__) && !defined(__wasm__)
29TEST(BN254Fq, CompileTimeMultiplication)
30{
31 constexpr fq a = uint256_t{ 0xa9b879029c49e60eUL, 0x2517b72250caa7b3UL, 0x6b86c81105dae2d1UL, 0x3a81735d5aec0c3UL };
32 constexpr fq b = uint256_t{ 0x744fc10aec23e56aUL, 0x5dea4788a3b936a6UL, 0xa0a89f4a8af01df1UL, 0x72ae28836807df3UL };
33 constexpr fq expected =
34 uint256_t{ 0x6c0a789c0028fd09UL, 0xca9520d84c684efaUL, 0xcbf3f7b023a852b4UL, 0x1b2e4dac41400621UL };
35
36 constexpr fq result = a * b;
37 static_assert(result == expected);
38}
39
40TEST(BN254Fq, CompileTimeSquaring)
41{
42 constexpr fq a = uint256_t{ 0xa9b879029c49e60eUL, 0x2517b72250caa7b3UL, 0x6b86c81105dae2d1UL, 0x3a81735d5aec0c3UL };
43 constexpr fq expected =
44 uint256_t{ 0x41081a42fdaa7e23UL, 0x44d1140f756ed419UL, 0x53716b0a6f253e63UL, 0xb1a0b04044d75fUL };
45
46 constexpr fq result = a.sqr();
47 static_assert(result == expected);
48}
49
50TEST(BN254Fq, CompileTimeAddition)
51{
52 constexpr fq a{ 0x7d2e20e82f73d3e8, 0x8e50616a7a9d419d, 0xcdc833531508914b, 0xd510253a2ce62c };
53 constexpr fq b{ 0x2829438b071fd14e, 0xb03ef3f9ff9274e, 0x605b671f6dc7b209, 0x8701f9d971fbc9 };
54 constexpr fq expected{ 0xa55764733693a536, 0x995450aa1a9668eb, 0x2e239a7282d04354, 0x15c121f139ee1f6 };
55
56 constexpr fq result = a + b;
57 static_assert(result == expected);
58}
59
60TEST(BN254Fq, CompileTimeSubtraction)
61{
62 constexpr fq a{ 0xd68d01812313fb7c, 0x2965d7ae7c6070a5, 0x08ef9af6d6ba9a48, 0x0cb8fe2108914f53 };
63 constexpr fq b{ 0x2cd2a2a37e9bf14a, 0xebc86ef589c530f6, 0x75124885b362b8fe, 0x1394324205c7a41d };
64 constexpr fq expected{ 0xe5daeaf47cf50779, 0xd51ed34a5b0d0a3c, 0x4c2d9827a4d939a6, 0x29891a51e3fb4b5f };
65
66 constexpr fq result = a - b;
67 static_assert(result == expected);
68}
69#endif
70
71TEST(BN254Fq, CompileTimeInversion)
72{
73 constexpr fq a = uint256_t{ 0xa9b879029c49e60eUL, 0x2517b72250caa7b3UL, 0x6b86c81105dae2d1UL, 0x3a81735d5aec0c3UL };
74 constexpr fq inv = a.invert();
75 // Verify a * a^-1 = 1
76 static_assert(a * inv == fq::one());
77}
78
79// ================================
80// Montgomery Form
81// ================================
82
83TEST(BN254Fq, FromMontgomeryForm)
84{
85 constexpr fq t0 = fq::one();
86 constexpr fq result = t0.from_montgomery_form();
87 constexpr fq expected{ 0x01, 0x00, 0x00, 0x00 };
88 EXPECT_EQ(result, expected);
89}
90
91TEST(BN254Fq, MontgomeryConsistencyCheck)
92{
95 fq aR;
96 fq bR;
97 fq aRR;
98 fq bRR;
99 fq bRRR;
100 fq result_a;
101 fq result_b;
102 fq result_c;
103 fq result_d;
104 aR = a.to_montgomery_form();
105 aRR = aR.to_montgomery_form();
106 bR = b.to_montgomery_form();
107 bRR = bR.to_montgomery_form();
108 bRRR = bRR.to_montgomery_form();
109 result_a = aRR * bRR; // abRRR
110 result_b = aR * bRRR; // abRRR
111 result_c = aR * bR; // abR
112 result_d = a * b; // abR^-1
113 EXPECT_EQ((result_a == result_b), true);
114 result_a.self_from_montgomery_form(); // abRR
115 result_a.self_from_montgomery_form(); // abR
116 result_a.self_from_montgomery_form(); // ab
117 result_c.self_from_montgomery_form(); // ab
118 result_d.self_to_montgomery_form(); // ab
119 EXPECT_EQ((result_a == result_c), true);
120 EXPECT_EQ((result_a == result_d), true);
121}
122
123// ================================
124// Arithmetic Consistency
125// ================================
126
127TEST(BN254Fq, AddMulConsistency)
128{
129 fq multiplicand = { 0x09, 0, 0, 0 };
130 multiplicand.self_to_montgomery_form();
131
133 fq result;
134 result = a + a; // 2
135 result += result; // 4
136 result += result; // 8
137 result += a; // 9
138
139 fq expected;
140 expected = a * multiplicand;
141
142 EXPECT_EQ((result == expected), true);
143}
144
145TEST(BN254Fq, SubMulConsistency)
146{
147 fq multiplicand = { 0x05, 0, 0, 0 };
148 multiplicand.self_to_montgomery_form();
149
151 fq result;
152 result = a + a; // 2
153 result += result; // 4
154 result += result; // 8
155 result -= a; // 7
156 result -= a; // 6
157 result -= a; // 5
158
159 fq expected;
160 expected = a * multiplicand;
161
162 EXPECT_EQ((result == expected), true);
163}
164
165TEST(BN254Fq, Invert)
166{
167 fq input = fq::random_element();
168 fq inverse = input.invert();
169 fq result = input * inverse;
170 result = result.reduce_once();
171 result = result.reduce_once();
172 EXPECT_EQ(result, fq::one());
173}
174
175TEST(BN254Fq, InvertOneIsOne)
176{
177 fq result = fq::one();
178 result = result.invert();
179 EXPECT_EQ((result == fq::one()), true);
180}
181
182TEST(BN254Fq, Sqrt)
183{
184 fq input = fq::one();
185 auto [is_sqr, root] = input.sqrt();
186 fq result = root.sqr();
187 EXPECT_EQ(result, input);
188}
189
190TEST(BN254Fq, SqrtRandom)
191{
192 for (size_t i = 0; i < 1; ++i) {
193 fq input = fq::random_element().sqr();
194 auto [is_sqr, root] = input.sqrt();
195 fq root_test = root.sqr();
196 EXPECT_EQ(root_test, input);
197 }
198}
199
200TEST(BN254Fq, OneAndZero)
201{
202 fq result;
203 result = fq::one() - fq::one();
204 EXPECT_EQ((result == fq::zero()), true);
205}
206
207TEST(BN254Fq, Copy)
208{
209 fq result = fq::random_element();
210 fq expected;
211 fq::__copy(result, expected);
212 EXPECT_EQ((result == expected), true);
213}
214
215TEST(BN254Fq, Neg)
216{
218 fq b;
219 b = -a;
220 fq result;
221 result = a + b;
222 EXPECT_EQ((result == fq::zero()), true);
223}
224
225// ================================
226// Endomorphism
227// ================================
228
229TEST(BN254Fq, SplitIntoEndomorphismScalars)
230{
232 fq k1 = 0;
233 fq k2 = 0;
234
236
237 fq result = 0;
238
241
242 EXPECT_LT(uint256_t(k1).get_msb(), 128);
243 EXPECT_LT(uint256_t(k2).get_msb(), 128);
244
245 result = k2 * fq::cube_root_of_unity();
246 result = k1 - result;
247
249 EXPECT_EQ(result, k);
250}
251
252TEST(BN254Fq, SplitIntoEndomorphismScalarsSimple)
253{
254 fq input = { 1, 0, 0, 0 };
255 fq k = { 0, 0, 0, 0 };
256 fq k1 = { 0, 0, 0, 0 };
257 fq k2 = { 0, 0, 0, 0 };
258 fq::__copy(input, k);
259
261
262 fq result{ 0, 0, 0, 0 };
265
266 EXPECT_LT(uint256_t(k1).get_msb(), 128);
267 EXPECT_LT(uint256_t(k2).get_msb(), 128);
268
269 fq beta = fq::cube_root_of_unity();
270 result = k2 * beta;
271 result = k1 - result;
272
274 for (size_t i = 0; i < 4; ++i) {
275 EXPECT_EQ(result.data[i], k.data[i]);
276 }
277}
278
279// Regression: k = ceil(m * 2^256 / endo_g2), for m an integer, previously produced negative k2 in the GLV
280// splitting, causing 128-bit truncation to extract wrong values. See endomorphism_scalars.py.
281TEST(BN254Fq, SplitEndomorphismNegativeK2)
282{
283 // clang-format off
284 struct test_case { std::array<uint64_t, 4> limbs; const char* tag; };
285 const std::array<test_case, 3> cases = {{
286 {{ 0x71922da036dca5f4, 0xd970a56127fb8227, 0x59e26bcea0d48bac, 0x0 }, "m=1"},
287 {{ 0xe3245b406db94be8, 0xb2e14ac24ff7044e, 0xb3c4d79d41a91759, 0x0 }, "m=2"},
288 {{ 0x54b688e0a495f1dc, 0x8c51f02377f28676, 0x0da7436be27da306, 0x1 }, "m=3"},
289 }};
290 // clang-format on
291
292 fq lambda = fq::cube_root_of_unity();
293
294 for (const auto& tc : cases) {
295 fq k{ tc.limbs[0], tc.limbs[1], tc.limbs[2], tc.limbs[3] };
296 fq k1{ 0, 0, 0, 0 };
297 fq k2{ 0, 0, 0, 0 };
298
300
301 k1.self_to_montgomery_form();
302 k2.self_to_montgomery_form();
303 fq result = k1 - k2 * lambda;
305
306 EXPECT_EQ(result, k) << tc.tag;
307 }
308}
309
310TEST(BN254Fq, SplitIntoEndomorphismEdgeCase)
311{
312 fq input = { 0, 0, 1, 0 }; // 2^128
313 fq k = { 0, 0, 0, 0 };
314 fq k1 = { 0, 0, 0, 0 };
315 fq k2 = { 0, 0, 0, 0 };
316 fq::__copy(input, k);
317
319
320 fq result{ 0, 0, 0, 0 };
323
324 EXPECT_LT(uint256_t(k1).get_msb(), 128);
325 EXPECT_LT(uint256_t(k2).get_msb(), 128);
326
327 fq beta = fq::cube_root_of_unity();
328 result = k2 * beta;
329 result = k1 - result;
330
332 for (size_t i = 0; i < 4; ++i) {
333 EXPECT_EQ(result.data[i], k.data[i]);
334 }
335}
336
337// ================================
338// Buffer Serialization
339// ================================
340
341TEST(BN254Fq, SerializeToBuffer)
342{
343 std::array<uint8_t, 32> buffer;
344 fq a = { 0x1234567876543210, 0x2345678987654321, 0x3456789a98765432, 0x006789abcba98765 };
346
348
349 EXPECT_EQ(buffer[31], 0x10);
350 EXPECT_EQ(buffer[30], 0x32);
351 EXPECT_EQ(buffer[29], 0x54);
352 EXPECT_EQ(buffer[28], 0x76);
353 EXPECT_EQ(buffer[27], 0x78);
354 EXPECT_EQ(buffer[26], 0x56);
355 EXPECT_EQ(buffer[25], 0x34);
356 EXPECT_EQ(buffer[24], 0x12);
357
358 EXPECT_EQ(buffer[23], 0x21);
359 EXPECT_EQ(buffer[22], 0x43);
360 EXPECT_EQ(buffer[21], 0x65);
361 EXPECT_EQ(buffer[20], 0x87);
362 EXPECT_EQ(buffer[19], 0x89);
363 EXPECT_EQ(buffer[18], 0x67);
364 EXPECT_EQ(buffer[17], 0x45);
365 EXPECT_EQ(buffer[16], 0x23);
366
367 EXPECT_EQ(buffer[15], 0x32);
368 EXPECT_EQ(buffer[14], 0x54);
369 EXPECT_EQ(buffer[13], 0x76);
370 EXPECT_EQ(buffer[12], 0x98);
371 EXPECT_EQ(buffer[11], 0x9a);
372 EXPECT_EQ(buffer[10], 0x78);
373 EXPECT_EQ(buffer[9], 0x56);
374 EXPECT_EQ(buffer[8], 0x34);
375
376 EXPECT_EQ(buffer[7], 0x65);
377 EXPECT_EQ(buffer[6], 0x87);
378 EXPECT_EQ(buffer[5], 0xa9);
379 EXPECT_EQ(buffer[4], 0xcb);
380 EXPECT_EQ(buffer[3], 0xab);
381 EXPECT_EQ(buffer[2], 0x89);
382 EXPECT_EQ(buffer[1], 0x67);
383 EXPECT_EQ(buffer[0], 0x00);
384}
385
386TEST(BN254Fq, SerializeFromBuffer)
387{
388 std::array<uint8_t, 32> buffer;
389 fq expected = { 0x1234567876543210, 0x2345678987654321, 0x3456789a98765432, 0x006789abcba98765 };
390
391 fq::serialize_to_buffer(expected, &buffer[0]);
392 fq result = fq::serialize_from_buffer(&buffer[0]);
393
394 EXPECT_EQ(result, expected);
395}
396
397// ================================
398// Regression Tests
399// ================================
400// TEST to check we don't have 0^0=0
401TEST(BN254Fq, PowRegressionCheck)
402{
403 fq zero = fq::zero();
404 fq one = fq::one();
405 EXPECT_EQ(zero.pow(uint256_t(0)), one);
406}
407
408// AUDITTODO: should we remove this test?
409TEST(BN254Fq, SqrRegression)
410{
411 std::array<uint256_t, 7> values = {
412 uint256_t(0xbdf876654b0ade1b, 0x2c3a66c64569f338, 0x2cd8bf2ec1fe55a3, 0x11c0ea9ee5693ede),
413 uint256_t(0x551b14ec34f2151c, 0x62e472ed83a2891e, 0xf208d5e5c9b5b3fb, 0x14315aeaf6027d8c),
414 uint256_t(0xad39959ae8013750, 0x7f1d2c709ab84cbb, 0x408028b80a60c2f1, 0x1dcd116fc26f856e),
415 uint256_t(0x95e967d30dcce9ce, 0x56139274241d2ea1, 0x85b19c1c616ec456, 0x1f1780cf9bf045b4),
416 uint256_t(0xbe841c861d8eb80e, 0xc5980d67a21386c0, 0x5fd1f1afecddeeb5, 0x24dbb8c1baea0250),
417 uint256_t(0x3ae4b3a27f05d6e3, 0xc5f6785b12df8d29, 0xc3a6c5f095103046, 0xd6b94cb2cc1fd4b),
418 uint256_t(0xc003c71932a6ced5, 0x6302a413f68e26e9, 0x2ed4a9b64d69fad, 0xfe61ffab1ae227d)
419 };
420 for (auto& value : values) {
421 fq element(value);
422 EXPECT_EQ(element.sqr(), element * element);
423 }
424}
425// ==============================
426// Reduction equivalence
427// ==============================
428
429// A 512-bit value big_num can be reduced mod p in two ways:
430// 1. Direct: (big_num % p)
431// 2. Split: fq(lo) + fq(2^256) * fq(hi), where big_num = lo + 2^256 * hi
432// This test verifies both methods produce the same result.
433TEST(BN254Fq, Uint512ReductionEquivalence)
434{
435 uint512_t random_uint512 = engine.get_random_uint512();
436 auto random_lo = fq(random_uint512.lo);
437 auto random_hi = fq(random_uint512.hi);
439 constexpr auto pow_2_256 = fq(uint256_t(1) << 128).sqr();
440 EXPECT_EQ(random_lo + pow_2_256 * random_hi, fq((random_uint512 % q).lo));
441}
uint512_t get_random_uint512()
Definition engine.hpp:38
FF a
FF b
numeric::RNG & engine
std::unique_ptr< uint8_t[]> buffer
Definition engine.cpp:50
constexpr T get_msb(const T in)
Definition get_msb.hpp:49
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:217
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FqParams > fq
Definition fq.hpp:170
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
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 void self_from_montgomery_form_reduced() &noexcept
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
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 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()