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{ 0x83aa80986c4f06f8, 0xbd01cce5e3b3afc3, 0x1cba208cb70aa13b, 0x2a582eb35a932e0d };
32 constexpr fq b{ 0x348ea47f1840a528, 0x5e6eb8e57e1b246d, 0x10852d3d36002e53, 0x280130d2f6a97aba };
33 constexpr fq expected{ 0x67eaddc2ba233427, 0x3c4f7dfe46ef24a9, 0x8fecb77e2ff74d64, 0x275537b321138ee7 };
34
35 constexpr fq result = a * b;
36 static_assert(result == expected);
37}
38
39TEST(BN254Fq, CompileTimeSquaring)
40{
41 constexpr fq a{ 0x83aa80986c4f06f8, 0xbd01cce5e3b3afc3, 0x1cba208cb70aa13b, 0x2a582eb35a932e0d };
42 constexpr fq expected{ 0xe441c0408a6fab60, 0xb94616ade6ed8752, 0x36cb53ba8e85397f, 0x17698305ec38b773 };
43
44 constexpr fq result = a.sqr();
45 static_assert(result == expected);
46}
47
48TEST(BN254Fq, CompileTimeAddition)
49{
50 constexpr fq a{ 0x7d2e20e82f73d3e8, 0x8e50616a7a9d419d, 0xcdc833531508914b, 0xd510253a2ce62c };
51 constexpr fq b{ 0x2829438b071fd14e, 0xb03ef3f9ff9274e, 0x605b671f6dc7b209, 0x8701f9d971fbc9 };
52 constexpr fq expected{ 0xa55764733693a536, 0x995450aa1a9668eb, 0x2e239a7282d04354, 0x15c121f139ee1f6 };
53
54 constexpr fq result = a + b;
55 static_assert(result == expected);
56}
57
58TEST(BN254Fq, CompileTimeSubtraction)
59{
60 constexpr fq a{ 0xd68d01812313fb7c, 0x2965d7ae7c6070a5, 0x08ef9af6d6ba9a48, 0x0cb8fe2108914f53 };
61 constexpr fq b{ 0x2cd2a2a37e9bf14a, 0xebc86ef589c530f6, 0x75124885b362b8fe, 0x1394324205c7a41d };
62 constexpr fq expected{ 0xe5daeaf47cf50779, 0xd51ed34a5b0d0a3c, 0x4c2d9827a4d939a6, 0x29891a51e3fb4b5f };
63
64 constexpr fq result = a - b;
65 static_assert(result == expected);
66}
67#endif
68
69TEST(BN254Fq, CompileTimeInversion)
70{
71 constexpr fq a{ 0x83aa80986c4f06f8, 0xbd01cce5e3b3afc3, 0x1cba208cb70aa13b, 0x2a582eb35a932e0d };
72 constexpr fq inv = a.invert();
73 // Verify a * a^-1 = 1
74 static_assert(a * inv == fq::one());
75}
76
77// ================================
78// Endomorphism
79// ================================
80
81TEST(BN254Fq, SplitIntoEndomorphismScalars)
82{
84 fq k1 = 0;
85 fq k2 = 0;
86
88
89 fq result = 0;
90
93
94 EXPECT_LT(uint256_t(k1).get_msb(), 128);
95 EXPECT_LT(uint256_t(k2).get_msb(), 128);
96
97 result = k2 * fq::cube_root_of_unity();
98 result = k1 - result;
99
101 EXPECT_EQ(result, k);
102}
103
104TEST(BN254Fq, SplitIntoEndomorphismScalarsSimple)
105{
106 fq input = { 1, 0, 0, 0 };
107 fq k = { 0, 0, 0, 0 };
108 fq k1 = { 0, 0, 0, 0 };
109 fq k2 = { 0, 0, 0, 0 };
110 fq::__copy(input, k);
111
113
114 fq result{ 0, 0, 0, 0 };
117
118 EXPECT_LT(uint256_t(k1).get_msb(), 128);
119 EXPECT_LT(uint256_t(k2).get_msb(), 128);
120
121 fq beta = fq::cube_root_of_unity();
122 result = k2 * beta;
123 result = k1 - result;
124
126 for (size_t i = 0; i < 4; ++i) {
127 EXPECT_EQ(result.data[i], k.data[i]);
128 }
129}
130
131// Regression: k = ceil(m * 2^256 / endo_g2), for m an integer, previously produced negative k2 in the GLV
132// splitting, causing 128-bit truncation to extract wrong values. See endomorphism_scalars.py.
133TEST(BN254Fq, SplitEndomorphismNegativeK2)
134{
135 // clang-format off
136 struct test_case { std::array<uint64_t, 4> limbs; const char* tag; };
137 const std::array<test_case, 3> cases = {{
138 {{ 0x71922da036dca5f4, 0xd970a56127fb8227, 0x59e26bcea0d48bac, 0x0 }, "m=1"},
139 {{ 0xe3245b406db94be8, 0xb2e14ac24ff7044e, 0xb3c4d79d41a91759, 0x0 }, "m=2"},
140 {{ 0x54b688e0a495f1dc, 0x8c51f02377f28676, 0x0da7436be27da306, 0x1 }, "m=3"},
141 }};
142 // clang-format on
143
144 fq lambda = fq::cube_root_of_unity();
145
146 for (const auto& tc : cases) {
147 fq k{ tc.limbs[0], tc.limbs[1], tc.limbs[2], tc.limbs[3] };
148 fq k1{ 0, 0, 0, 0 };
149 fq k2{ 0, 0, 0, 0 };
150
152
153 k1.self_to_montgomery_form();
154 k2.self_to_montgomery_form();
155 fq result = k1 - k2 * lambda;
157
158 EXPECT_EQ(result, k) << tc.tag;
159 }
160}
161
162TEST(BN254Fq, SplitIntoEndomorphismEdgeCase)
163{
164 fq input = { 0, 0, 1, 0 }; // 2^128
165 fq k = { 0, 0, 0, 0 };
166 fq k1 = { 0, 0, 0, 0 };
167 fq k2 = { 0, 0, 0, 0 };
168 fq::__copy(input, k);
169
171
172 fq result{ 0, 0, 0, 0 };
175
176 EXPECT_LT(uint256_t(k1).get_msb(), 128);
177 EXPECT_LT(uint256_t(k2).get_msb(), 128);
178
179 fq beta = fq::cube_root_of_unity();
180 result = k2 * beta;
181 result = k1 - result;
182
184 for (size_t i = 0; i < 4; ++i) {
185 EXPECT_EQ(result.data[i], k.data[i]);
186 }
187}
188
189// ================================
190// Regression Tests
191// ================================
192
193TEST(BN254Fq, SqrRegression)
194{
195 std::array<uint256_t, 7> values = {
196 uint256_t(0xbdf876654b0ade1b, 0x2c3a66c64569f338, 0x2cd8bf2ec1fe55a3, 0x11c0ea9ee5693ede),
197 uint256_t(0x551b14ec34f2151c, 0x62e472ed83a2891e, 0xf208d5e5c9b5b3fb, 0x14315aeaf6027d8c),
198 uint256_t(0xad39959ae8013750, 0x7f1d2c709ab84cbb, 0x408028b80a60c2f1, 0x1dcd116fc26f856e),
199 uint256_t(0x95e967d30dcce9ce, 0x56139274241d2ea1, 0x85b19c1c616ec456, 0x1f1780cf9bf045b4),
200 uint256_t(0xbe841c861d8eb80e, 0xc5980d67a21386c0, 0x5fd1f1afecddeeb5, 0x24dbb8c1baea0250),
201 uint256_t(0x3ae4b3a27f05d6e3, 0xc5f6785b12df8d29, 0xc3a6c5f095103046, 0xd6b94cb2cc1fd4b),
202 uint256_t(0xc003c71932a6ced5, 0x6302a413f68e26e9, 0x2ed4a9b64d69fad, 0xfe61ffab1ae227d)
203 };
204 for (auto& value : values) {
205 fq element(value);
206 EXPECT_EQ(element.sqr(), element * element);
207 }
208}
209// ==============================
210// Reduction equivalence
211// ==============================
212
213// A 512-bit value big_num can be reduced mod p in two ways:
214// 1. Direct: (big_num % p)
215// 2. Split: fq(lo) + fq(2^256) * fq(hi), where big_num = lo + 2^256 * hi
216// This test verifies both methods produce the same result.
217TEST(BN254Fq, Uint512ReductionEquivalence)
218{
219 uint512_t random_uint512 = engine.get_random_uint512();
220 auto random_lo = fq(random_uint512.lo);
221 auto random_hi = fq(random_uint512.hi);
223 constexpr auto pow_2_256 = fq(uint256_t(1) << 128).sqr();
224 EXPECT_EQ(random_lo + pow_2_256 * random_hi, fq((random_uint512 % q).lo));
225}
uint512_t get_random_uint512()
Definition engine.hpp:38
FF a
FF b
numeric::RNG & engine
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:153
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
static void split_into_endomorphism_scalars(const field &k, field &k1, field &k2)
Full-width endomorphism decomposition: k ≡ k1 - k2·λ (mod r). Modifies the field elements k1 and k2.
constexpr field invert() const noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr field sqr() const noexcept
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