Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
prime_field.test.cpp
Go to the documentation of this file.
1
25#include <gtest/gtest.h>
26
27using namespace bb;
28
29namespace {
31} // namespace
32
33// Type-parameterized test fixture for prime fields
34template <typename F> class PrimeFieldTest : public ::testing::Test {
35 public:
36 // Helper to get a random field element as uint256_t (for reference calculations)
38 {
40 while (res >= F::modulus) {
41 res -= F::modulus;
42 }
43 return res;
44 }
45};
46
47// Register all prime field types
48using PrimeFieldTypes = ::testing::Types<bb::fq, bb::fr, secp256k1::fq, secp256k1::fr, secp256r1::fq, secp256r1::fr>;
49
50// Fields where sqrt() works correctly
51using SqrtFieldTypes = ::testing::Types<bb::fq, bb::fr, secp256k1::fq, secp256r1::fq>;
52
53// Fields that have cube_root_of_unity() defined
54using CubeRootFieldTypes = ::testing::Types<bb::fq, bb::fr, secp256k1::fq, secp256k1::fr>;
55
57
58template <typename> class PrimeFieldSqrtTest : public ::testing::Test {};
60
61template <typename> class PrimeFieldCubeRootTest : public ::testing::Test {};
63
64// ================================
65// Compile-time Tests (Prime Field Specific)
66// ================================
67
68TYPED_TEST(PrimeFieldTest, CompileTimeEquality)
69{
70 using F = TypeParam;
71
72 constexpr F a{ 0x01, 0x02, 0x03, 0x04 };
73 constexpr F b{ 0x01, 0x02, 0x03, 0x04 };
74
75 constexpr F c{ 0x01, 0x02, 0x03, 0x05 };
76 constexpr F d{ 0x01, 0x02, 0x04, 0x04 };
77 constexpr F e{ 0x01, 0x03, 0x03, 0x04 };
78 constexpr F f{ 0x02, 0x02, 0x03, 0x04 };
79 static_assert(a == b);
80 static_assert(!(a == c));
81 static_assert(!(a == d));
82 static_assert(!(a == e));
83 static_assert(!(a == f));
84}
85
86TYPED_TEST(PrimeFieldTest, CompileTimeSmallAddSubMul)
87{
88 using F = TypeParam;
89
90 constexpr F a{ 0x01, 0x02, 0x03, 0x04 };
91 constexpr F b{ 0x05, 0x06, 0x07, 0x08 };
92
93 // Just verify these operations are constexpr and produce consistent results
94 constexpr F sum = a + b;
95 constexpr F diff = a - b;
96 constexpr F prod = a * b;
97 constexpr F sq = a.sqr();
98
99 // Verify at runtime that constexpr results match runtime results
100 EXPECT_EQ(sum, a + b);
101 EXPECT_EQ(diff, a - b);
102 EXPECT_EQ(prod, a * b);
103 EXPECT_EQ(sq, a.sqr());
104}
105
106TYPED_TEST(PrimeFieldTest, CompileTimeUint256Conversion)
107{
108 using F = TypeParam;
109
110 constexpr uint256_t a{ 0x1111, 0x2222, 0x3333, 0x4444 };
111 constexpr F b(a);
112 constexpr uint256_t c = b;
113
114 static_assert(a == c);
115}
116
117// ================================
118// uint256_t Arithmetic Verification
119// ================================
120
121TYPED_TEST(PrimeFieldTest, AdditionModular)
122{
123 using F = TypeParam;
124
125 uint256_t a_raw = TestFixture::get_random_element_raw();
126 uint256_t b_raw = TestFixture::get_random_element_raw();
127
128 F a(a_raw);
129 F b(b_raw);
130 F c = a + b;
131
132 uint512_t expected_512 = uint512_t(a_raw) + uint512_t(b_raw);
133 uint256_t expected = (expected_512 % uint512_t(F::modulus)).lo;
134
135 EXPECT_EQ(uint256_t(c), expected);
136}
137
138TYPED_TEST(PrimeFieldTest, SubtractionModular)
139{
140 using F = TypeParam;
141
142 uint256_t a_raw = TestFixture::get_random_element_raw();
143 uint256_t b_raw = TestFixture::get_random_element_raw();
144
145 F a(a_raw);
146 F b(b_raw);
147 F c = a - b;
148
149 uint512_t expected_512 = uint512_t(a_raw) + uint512_t(F::modulus) - uint512_t(b_raw);
150 uint256_t expected = (expected_512 % uint512_t(F::modulus)).lo;
151
152 EXPECT_EQ(uint256_t(c), expected);
153}
154
155TYPED_TEST(PrimeFieldTest, MultiplicationModular)
156{
157 using F = TypeParam;
158
159 uint256_t a_raw = TestFixture::get_random_element_raw();
160 uint256_t b_raw = TestFixture::get_random_element_raw();
161
162 F a(a_raw);
163 F b(b_raw);
164 F c = a * b;
165
166 uint512_t c_512 = uint512_t(a_raw) * uint512_t(b_raw);
167 uint256_t expected = (c_512 % uint512_t(F::modulus)).lo;
168
169 EXPECT_EQ(uint256_t(c), expected);
170}
171
172TYPED_TEST(PrimeFieldTest, SquaringModular)
173{
174 using F = TypeParam;
175
176 uint256_t a_raw = TestFixture::get_random_element_raw();
177
178 F a(a_raw);
179 F c = a.sqr();
180
181 uint512_t c_512 = uint512_t(a_raw) * uint512_t(a_raw);
182 uint256_t expected = (c_512 % uint512_t(F::modulus)).lo;
183
184 EXPECT_EQ(uint256_t(c), expected);
185}
186
187TYPED_TEST(PrimeFieldTest, Uint256Roundtrip)
188{
189 using F = TypeParam;
190
191 uint256_t original = TestFixture::get_random_element_raw();
192 F field_element(original);
193 uint256_t recovered(field_element);
194
195 EXPECT_EQ(original, recovered);
196}
197
198// ================================
199// Montgomery Form (Prime Field Specific)
200// ================================
201
202TYPED_TEST(PrimeFieldTest, MontgomeryRoundtrip)
203{
204 using F = TypeParam;
205
206 F a = F::random_element();
208 EXPECT_EQ(a, b);
209}
210
211// ================================
212// Square Root
213// ================================
214
216{
217 using F = TypeParam;
218
219 F one = F::one();
220 auto [is_sqr, root] = one.sqrt();
221
222 EXPECT_TRUE(is_sqr);
223 EXPECT_EQ(root.sqr(), one);
224}
225
227{
228 using F = TypeParam;
229
230 F a = F::random_element();
231 F a_sqr = a.sqr();
232 auto [is_sqr, root] = a_sqr.sqrt();
233
234 EXPECT_TRUE(is_sqr);
235 EXPECT_EQ(root.sqr(), a_sqr);
236 EXPECT_TRUE((root == a) || (root == -a));
237}
238
239// ================================
240// Cube Root of Unity
241// ================================
242
244{
245 using F = TypeParam;
246
247 // lambda^3 = 1, so (lambda * x)^3 = x^3
248 F x = F::random_element();
249 F lambda = F::cube_root_of_unity();
250 F lambda_x = x * lambda;
251
252 F x_cubed = x * x * x;
253 F lambda_x_cubed = lambda_x * lambda_x * lambda_x;
254
255 EXPECT_EQ(x_cubed, lambda_x_cubed);
256}
257
258// ================================
259// Multiplicative Generator (Quadratic Non-Residue). AUDITTODO: kill this?
260// ================================
261
262TYPED_TEST(PrimeFieldTest, MultiplicativeGenerator)
263{
264 using F = TypeParam;
265
266 // The multiplicative generator g is defined such that g^((p-1)/2) = -1
267 // This means g is a quadratic non-residue (not a perfect square in the field)
268 F g = F::multiplicative_generator();
269 uint256_t p_minus_one_over_two = (F::modulus - 1) >> 1;
270 EXPECT_EQ(g.pow(p_minus_one_over_two), -F::one());
271}
272
273// ================================
274// Exponentiation
275// ================================
276
277TYPED_TEST(PrimeFieldTest, PowZeroExponent)
278{
279 using F = TypeParam;
280
281 // a^0 = 1 for any non-zero a
282 F a = F::random_element();
283 EXPECT_EQ(a.pow(uint256_t(0)), F::one());
284}
285
287{
288 using F = TypeParam;
289
290 F a = F::random_element();
291 EXPECT_EQ(a.pow(uint256_t(1)), a);
292}
293
295{
296 using F = TypeParam;
297
298 F a = F::random_element();
299 EXPECT_EQ(a.pow(uint256_t(2)), a * a);
300}
301
303{
304 using F = TypeParam;
305
306 F a = F::random_element();
307 EXPECT_EQ(a.pow(uint256_t(3)), a * a * a);
308}
309
310// ================================
311// Batch Invert (only implemented for prime fields)
312// ================================
313
315{
316 using F = TypeParam;
317 constexpr size_t batch_size = 10;
318
319 std::vector<F> elements(batch_size);
320 std::vector<F> inverses(batch_size);
321
322 for (size_t i = 0; i < batch_size; ++i) {
323 elements[i] = F::random_element();
324 inverses[i] = elements[i];
325 }
326
327 F::batch_invert(&inverses[0], batch_size);
328
329 for (size_t i = 0; i < batch_size; ++i) {
330 F product = elements[i] * inverses[i];
331 product = product.reduce_once().reduce_once();
332 EXPECT_EQ(product, F::one());
333 }
334}
335
336// ================================
337// Increment Operators
338// ================================
339
340TYPED_TEST(PrimeFieldTest, PrefixIncrement)
341{
342 using F = TypeParam;
343
344 F a = F::random_element();
345 F a_before = a;
346 F b = ++a;
347
348 // Prefix increment returns the new value
349 EXPECT_EQ(b, a);
350 EXPECT_EQ(a, a_before + F(1));
351}
352
353TYPED_TEST(PrimeFieldTest, PostfixIncrement)
354{
355 using F = TypeParam;
356
357 F a = F::random_element();
358 F a_old = a;
359 F b = a++;
360
361 // Postfix increment returns the old value
362 EXPECT_EQ(b, a_old);
363 EXPECT_EQ(a, a_old + F(1));
364}
365
366// ================================
367// Serialization
368// ================================
369
371{
372 using F = TypeParam;
373
374 F a = F::random_element();
375 auto [actual, expected] = msgpack_roundtrip(a);
376 EXPECT_EQ(actual, expected);
377}
static uint256_t get_random_element_raw()
virtual uint256_t get_random_uint256()=0
FF a
FF b
numeric::RNG & engine
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.
Definition api.hpp:5
TYPED_TEST_SUITE(ShpleminiTest, TestSettings)
Inner sum(Cont< Inner, Args... > const &in)
Definition container.hpp:70
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
::testing::Types< bb::fq, bb::fr, secp256k1::fq, secp256r1::fq > SqrtFieldTypes
::testing::Types< bb::fq, bb::fr, secp256k1::fq, secp256k1::fr, secp256r1::fq, secp256r1::fr > PrimeFieldTypes
::testing::Types< bb::fq, bb::fr, secp256k1::fq, secp256k1::fr > CubeRootFieldTypes
BB_INLINE constexpr field to_montgomery_form() const noexcept
BB_INLINE constexpr field pow(const uint256_t &exponent) const noexcept
BB_INLINE constexpr field sqr() const noexcept
constexpr std::pair< bool, field > sqrt() const noexcept
Compute square root of the field element.
BB_INLINE constexpr field from_montgomery_form() const noexcept
std::pair< T, T > msgpack_roundtrip(const T &object)