Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bigfield_edge_cases.test.cpp
Go to the documentation of this file.
4
7
8#include "../bool/bool.hpp"
9#include "../byte_array/byte_array.hpp"
10#include "../field/field.hpp"
11#include "./bigfield.hpp"
22#include <array>
23#include <cstdint>
24#include <gtest/gtest.h>
25#include <memory>
26#include <utility>
27
28using namespace bb;
29
30namespace {
32}
33
34enum struct InputType {
35 WITNESS,
37};
38
43
44// Helper to extract Builder and Params from bigfield<Builder, Params>
45template <typename T> struct extract_builder;
46template <typename T> struct extract_fq_params;
47
48template <template <typename, typename> class BigField, typename Builder, typename Params>
49struct extract_builder<BigField<Builder, Params>> {
50 using type = Builder;
51};
52
53template <template <typename, typename> class BigField, typename Builder, typename Params>
54struct extract_fq_params<BigField<Builder, Params>> {
55 using type = Params;
56};
57
58template <typename BigField> using builder_t = typename extract_builder<BigField>::type;
59template <typename BigField> using params_t = typename extract_fq_params<BigField>::type;
60
62template <typename BigField> class stdlib_bigfield_edge_cases : public testing::Test {
63
64 using Builder = builder_t<BigField>; // extract builder from BigField
65 using fr_ct = typename bb::stdlib::bn254<Builder>::ScalarField; // native circuit field
66 using fq_native = bb::field<params_t<BigField>>; // native bigfield type
67 using fq_ct = BigField; // non-native field (circuit type)
68 using witness_ct = stdlib::witness_t<Builder>; // circuit witness type
69 using bool_ct = stdlib::bool_t<Builder>; // circuit boolean type
70 using byte_array_ct = stdlib::byte_array<Builder>; // circuit byte array type
71
72 public:
73 // Declare edge case values
75 uint512_t(uint256_t(0)), // 0
76 uint512_t(uint256_t(1)), // 1
77 uint512_t(fr::modulus) - 1, // n - 1
79 uint512_t(fq_ct::modulus) - uint512_t(1), // p - 1
80 };
81
83 uint512_t(fq_ct::modulus), // p
84 uint512_t(fq_ct::modulus) + uint512_t(1), // p + 1
85 uint512_t(fq_ct::modulus) + uint512_t(fr::modulus), // p + n
86 (uint512_t(1) << 256) - 1, // 2^256 - 1
87 (uint512_t(1) << 256), // 2^256
88 (uint512_t(1) << 256) + 1, // 2^256 + 1
89 uint512_t(fq_ct::get_maximum_unreduced_value()) - 1, // max unreduced - 1
90 uint512_t(fq_ct::get_maximum_unreduced_value()), // max unreduced
91 uint512_t(fq_ct::get_maximum_unreduced_value()) + 1, // max unreduced + 1
92 (uint512_t(1) << (stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION * 4)) - 1, // 2^272 - 1
93 };
94
95 constexpr static uint512_t reduction_upper_bound = uint512_t(1) << (fq_ct::modulus.get_msb() + 1); // p < 2^s
96
98 {
99 auto builder = Builder();
100 for (const auto& value : values_larger_than_bigfield) {
101 fq_ct val = fq_ct::create_from_u512_as_witness(&builder, value, true);
102 EXPECT_EQ(val.get_value() >= fq_ct::modulus, true);
103 }
104 auto result_check = CircuitChecker::check(builder);
105 EXPECT_EQ(result_check, true);
106 }
107
109 {
110 // Create four limbs, first three with 68 bits each and the last with 61 bits
111 auto builder = Builder();
112 const uint256_t valid_mask = (uint256_t(1) << fq_ct::NUM_LIMB_BITS) - 1;
113 const uint256_t limb_0_native = uint256_t(fr::random_element()) & valid_mask;
114 fr_ct limb_0 = witness_ct(&builder, limb_0_native);
115 const uint256_t limb_1_native = uint256_t(fr::random_element()) & valid_mask;
116 fr_ct limb_1 = witness_ct(&builder, limb_1_native);
117 const uint256_t limb_2_native = uint256_t(fr::random_element()) & valid_mask;
118 fr_ct limb_2 = witness_ct(&builder, limb_2_native);
119
120 // last limb has only 61 bits
121 const uint256_t other_mask = (uint256_t(1) << 61) - 1;
122 uint256_t limb_3_native = uint256_t(fr::random_element()) & other_mask;
123 limb_3_native |= (uint256_t(1) << 60); // set 61st bit to 1 to ensure the combined value is > 2^265
124 fr_ct limb_3 = witness_ct(&builder, limb_3_native);
125
126 // Create a bigfield from them (without range constraints on limbs)
127 fq_ct combined_a = fq_ct::unsafe_construct_from_limbs(limb_0, limb_1, limb_2, limb_3, true);
128 combined_a.binary_basis_limbs[3].maximum_value = other_mask;
129
130 // Check that individual limbs are ≤ than maximum unreduced limb value
131 const bool limbs_less_than_max = (limb_0_native <= fq_ct::get_maximum_unreduced_limb_value()) &&
132 (limb_1_native <= fq_ct::get_maximum_unreduced_limb_value()) &&
133 (limb_2_native <= fq_ct::get_maximum_unreduced_limb_value()) &&
134 (limb_3_native <= fq_ct::get_maximum_unreduced_limb_value());
135 EXPECT_EQ(limbs_less_than_max, true);
136
137 // Check that the combined max value is greater than the maximum unreduced bigfield value
138 EXPECT_GT(combined_a.get_maximum_value(), // 2^68 * 2^68 * 2^68 * 2^61 = 2^265
139 fq_ct::get_maximum_unreduced_value() // sqrt(2^272 * |Fr|) ≈ (2^(263) - 1) or (2^(264) - 1)
140 );
141
142 // Squaring op must perform self-reduction
143 EXPECT_EQ(combined_a.get_value() > fq_ct::modulus, true); // unreduced value > modulus
144 combined_a.sqr();
145
146 // Check that original combined value is now reduced
147 EXPECT_EQ(combined_a.get_value() < reduction_upper_bound, true); // reduced value < 2^s
148 EXPECT_EQ(combined_a.get_maximum_value() <= fq_ct::get_maximum_unreduced_value(), true);
149
150 bool result = CircuitChecker::check(builder);
151 EXPECT_EQ(result, true);
152 }
153
155 {
156 // Create four limbs, first three limbs with 68 bits and last one with 50 bits
157 auto builder = Builder();
158 const uint256_t valid_mask = (uint256_t(1) << fq_ct::NUM_LIMB_BITS) - 1;
159 const uint256_t limb_0_native = uint256_t(fr::random_element()) & valid_mask;
160 fr_ct limb_0 = witness_ct(&builder, limb_0_native);
161 const uint256_t limb_1_native = uint256_t(fr::random_element()) & valid_mask;
162 fr_ct limb_1 = witness_ct(&builder, limb_1_native);
163 const uint256_t limb_2_native = uint256_t(fr::random_element()) & valid_mask;
164 fr_ct limb_2 = witness_ct(&builder, limb_2_native);
165
166 // last limb has 50 bits
167 const uint256_t other_mask = (uint256_t(1) << 50) - 1;
168 const uint256_t limb_3_native = uint256_t(fr::random_element()) & other_mask;
169 fr_ct limb_3 = witness_ct(&builder, limb_3_native);
170
171 // Create a bigfield from them (without range constraints on limbs)
172 // The max values are set to defaults max values.
173 fq_ct combined_a = fq_ct::unsafe_construct_from_limbs(limb_0, limb_1, limb_2, limb_3);
174
175 // Increase the max value of the first limb to cause overflow, this should trigger reduction
176 combined_a.binary_basis_limbs[0].maximum_value =
177 (uint256_t(1) << fq_ct::MAX_UNREDUCED_LIMB_BITS) + 1000; // > 2^78
178
179 // Check that the combined max value is less than the maximum unreduced bigfield value
180 EXPECT_EQ(combined_a.get_maximum_value() <= fq_ct::get_maximum_unreduced_value(), true);
181
182 // Perform a squaring which should trigger reduction
183 combined_a.sqr();
184
185 // Check that the original combined value is now reduced
186 EXPECT_EQ(combined_a.get_value() < reduction_upper_bound, true);
187 EXPECT_EQ(combined_a.get_maximum_value() <= fq_ct::get_maximum_unreduced_value(), true);
188
189 // Check the circuit is valid
190 bool result = CircuitChecker::check(builder);
191 EXPECT_EQ(result, true);
192 }
193
195 {
196 // Create four limbs, first three limbs with 68 bits and last one with 40 bits
197 auto builder = Builder();
198
199 const uint256_t valid_mask = (uint256_t(1) << fq_ct::NUM_LIMB_BITS) - 1;
200 const uint256_t limb_0_native = uint256_t(fr::random_element()) & valid_mask;
201 fr_ct limb_0 = witness_ct(&builder, limb_0_native);
202 const uint256_t limb_1_native = uint256_t(fr::random_element()) & valid_mask;
203 fr_ct limb_1 = witness_ct(&builder, limb_1_native);
204 const uint256_t limb_2_native = uint256_t(fr::random_element()) & valid_mask;
205 fr_ct limb_2 = witness_ct(&builder, limb_2_native);
206
207 // Last limb has 40 bits.
208 // We want the last limb to be small enough such that even after 10 doublings
209 // the overall max value is still less than the max allowed unreduced value.
210 const uint256_t other_mask = (uint256_t(1) << 40) - 1;
211 const uint256_t limb_3_native = uint256_t(fr::random_element()) & other_mask;
212 fr_ct limb_3 = witness_ct(&builder, limb_3_native);
213
214 // Create a bigfield from them (without range constraints on limbs)
215 // The max values are set to defaults values of (2^68 - 1) for first three limbs and (2^40 - 1) for last limb
216 fq_ct combined_a = fq_ct::unsafe_construct_from_limbs(limb_0, limb_1, limb_2, limb_3);
217 combined_a.binary_basis_limbs[3].maximum_value = other_mask;
218
219 // Add this value to itself before any reduction is triggered
220 // 11 doublings should be possible before exceeding the max unreduced value
221 for (size_t i = 0; i < 11; ++i) {
222 const uint64_t msb_index_before = combined_a.binary_basis_limbs[0].maximum_value.get_msb();
223 combined_a = combined_a + combined_a;
224 const uint64_t msb_index_after = combined_a.binary_basis_limbs[0].maximum_value.get_msb();
225
226 // should increase max value by 1 bit for each doubling
227 EXPECT_EQ(msb_index_after, msb_index_before + 1ULL);
228 }
229 EXPECT_EQ(combined_a.binary_basis_limbs[0].maximum_value.get_msb(), fq_ct::MAX_UNREDUCED_LIMB_BITS);
230 EXPECT_EQ(combined_a.binary_basis_limbs[0].maximum_value > fq_ct::get_maximum_unreduced_limb_value(), true);
231
232 // If we perform one more doubling, reduction should be triggered
233 combined_a = combined_a + combined_a;
234 EXPECT_EQ(combined_a.binary_basis_limbs[0].maximum_value.get_msb(), fq_ct::NUM_LIMB_BITS);
235
236 // Check the circuit
237 bool result = CircuitChecker::check(builder);
238 EXPECT_EQ(result, true);
239 }
240
241 // Gets a random bigfield element that is a circuit-witness
243 {
245 if (reduce_input) {
246 elt_native = elt_native.reduce_once().reduce_once();
247 }
248 fr elt_native_lo = fr(uint256_t(elt_native).slice(0, fq_ct::NUM_LIMB_BITS * 2));
249 fr elt_native_hi = fr(uint256_t(elt_native).slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 4));
250 fq_ct elt_ct(witness_ct(builder, elt_native_lo), witness_ct(builder, elt_native_hi));
251 return std::make_pair(elt_native, elt_ct);
252 }
253
254 static void check_invariants(const fq_ct& field_element, const std::string& operation_name)
255 {
256 // Invariant 1: Limb maximum values should be >= actual witness values
257 for (size_t i = 0; i < 4; ++i) {
258 uint256_t witness_value = uint256_t(field_element.binary_basis_limbs[i].element.get_value());
259 uint256_t max_value = field_element.binary_basis_limbs[i].maximum_value;
260
261 EXPECT_GE(max_value, witness_value)
262 << "invariant violation in " << operation_name << ":\n limb[" << i << "] max_value:\n " << max_value
263 << " < \n witness_value\n " << witness_value;
264 }
265
266 // Invariant 2: Prime limb should be consistent with binary limbs
267 uint256_t computed_prime = field_element.binary_basis_limbs[0].element.get_value();
268 computed_prime += field_element.binary_basis_limbs[1].element.get_value() * fq_ct::shift_1;
269 computed_prime += field_element.binary_basis_limbs[2].element.get_value() * fq_ct::shift_2;
270 computed_prime += field_element.binary_basis_limbs[3].element.get_value() * fq_ct::shift_3;
271 uint256_t actual_prime = field_element.prime_basis_limb.get_value();
272
273 EXPECT_EQ(fr(computed_prime), fr(actual_prime))
274 << "invariant violation in " << operation_name << ":\n computed prime:\n " << fr(computed_prime)
275 << " != \n actual prime:\n " << fr(actual_prime);
276
277 // Invariant 3: Maximum values should have fewer bits than PROHIBITED_LIMB_BITS
278 for (size_t i = 0; i < 4; ++i) {
279 uint64_t max_bits = field_element.binary_basis_limbs[i].maximum_value.get_msb() + 1;
280
281 EXPECT_LT(max_bits, fq_ct::PROHIBITED_LIMB_BITS)
282 << "invariant violation in " << operation_name << ":\n limb[" << i << "] has " << max_bits
283 << " bits, which exceeds PROHIBITED_LIMB_BITS (" << fq_ct::PROHIBITED_LIMB_BITS << ")";
284 }
285 }
286
287 // Generic test for binary operations (add, sub, mul) with native checks and invariant checks
288 template <typename BinaryOp, typename NativeOp>
289 static void test_invariants_during_binary_operation(BinaryOp binary_op,
290 NativeOp native_op,
291 const std::string& operation_name,
292 const bool skip_zero = false)
293 {
294 // Create two random bigfield elements with their native counterparts
295 auto builder = Builder();
296 auto [a_native, a_ct] = get_random_witness(&builder, false);
297 auto [b_native, b_ct] = get_random_witness(&builder, false);
298
299 // Perform the binary operation on both circuit and native values
300 fq_ct c_ct = binary_op(a_ct, b_ct);
301 fq_native c_native = native_op(a_native, b_native);
302
303 check_invariants(c_ct, operation_name + " (initial)");
304
305 // Test operation with edge case values
306 for (const auto& edge_value : edge_case_values) {
307 const bool is_fq_value_zero = (fq_native(edge_value) == fq_native(0));
308 const bool is_fr_value_zero = (fr(uint256_t(fq_native(edge_value))) == fr(0));
309 if (skip_zero && (is_fq_value_zero || is_fr_value_zero)) {
310 // if denominator is p, native division will fail (because we check a mod p != 0 for denominator a)
311 // if denominator is n (mod p), circuit division will fail (because we check a mod n != 0 for
312 // denominator a)
313 continue;
314 }
315 fq_ct edge_case = fq_ct::create_from_u512_as_witness(&builder, edge_value, true);
316 fq_native edge_native = fq_native(edge_value);
317
318 fq_ct result_ct = binary_op(c_ct, edge_case);
319 fq_native result_native = native_op(c_native, edge_native);
320
321 check_invariants(result_ct, operation_name + " with edge case");
322 c_ct = result_ct; // Chain operations
323 c_native = result_native;
324 }
325
326 // Test operation with values larger than bigfield
327 for (const auto& large_value : values_larger_than_bigfield) {
328 const bool is_fq_value_zero = (fq_native(large_value) == fq_native(0));
329 const bool is_fr_value_zero = (fr(uint256_t(fq_native(large_value))) == fr(0));
330 if (skip_zero && (is_fq_value_zero || is_fr_value_zero)) {
331 // if denominator is p, native division will fail (because we check a mod p != 0 for denominator a)
332 // if denominator is n (mod p), circuit division will fail (because we check a mod n != 0 for
333 // denominator a)
334 continue;
335 }
336 fq_ct large_case = fq_ct::create_from_u512_as_witness(&builder, large_value, true);
337 fq_native large_native = fq_native(large_value);
338
339 fq_ct result_ct = binary_op(c_ct, large_case);
340 fq_native result_native = native_op(c_native, large_native);
341
342 check_invariants(result_ct, operation_name + " with large value");
343 c_ct = result_ct; // Chain operations
344 c_native = result_native;
345 }
346
347 // Check invariants on the final result
348 check_invariants(c_ct, operation_name + " (final)");
349
350 // Final native check
351 c_ct.self_reduce();
352 EXPECT_EQ(c_ct.get_value(), uint512_t(c_native)) << "native check failed for " << operation_name << " (final)";
353
354 // Check the circuit is valid
355 bool result = CircuitChecker::check(builder);
356 EXPECT_EQ(result, true);
357 }
358
359#define INVARIANT_BINARY_OP_TEST(op_name, op_symbol, skip_zero) \
360 static void test_invariants_during_##op_name() \
361 { \
362 test_invariants_during_binary_operation([](const fq_ct& a, const fq_ct& b) { return a op_symbol b; }, \
363 [](const fq_native& a, const fq_native& b) { return a op_symbol b; }, \
364 #op_name, \
365 skip_zero); \
366 }
367
368 // Generate invariant test functions for all binary operations
369 INVARIANT_BINARY_OP_TEST(addition, +, false)
370 INVARIANT_BINARY_OP_TEST(subtraction, -, false)
371 INVARIANT_BINARY_OP_TEST(multiplication, *, false)
372 INVARIANT_BINARY_OP_TEST(division, /, true) // skip zero for division
373
375 {
376 test_invariants_during_binary_operation([](const fq_ct& a, const fq_ct&) { return a.sqr(); },
377 [](const fq_native& a, const fq_native&) { return a.sqr(); },
378 "squaring",
379 false);
380 }
381
383 {
384 test_invariants_during_binary_operation([](const fq_ct& a, const fq_ct&) { return -a; },
385 [](const fq_native& a, const fq_native&) { return -a; },
386 "negation",
387 false);
388 }
389
391 {
393 for (const auto& value : edge_case_values) {
394 fq_ct edge_case = fq_ct::create_from_u512_as_witness(&builder, value, /*can_overflow*/ false);
395
396 // This should pass for values strictly less than modulus
397 edge_case.assert_is_in_field();
398 }
399
400 bool result = CircuitChecker::check(builder);
401 EXPECT_EQ(result, true);
402 }
403
405 {
406 for (const auto& large_value : values_larger_than_bigfield) {
407 // Checks each large value individually in its own circuit
409
410 // For values larger than the field modulus, it should trigger a circuit error.
411 fq_ct large_case = fq_ct::create_from_u512_as_witness(&builder, large_value, true);
412 large_case.assert_is_in_field();
413
414 bool result = CircuitChecker::check(builder);
415 EXPECT_EQ(result, false);
416
417 if (large_value < reduction_upper_bound) {
418 // If the value is less than 2^s (i.e., it is reduced), then the error should appear in the borrow
419 // checks (during subtraction).
420 EXPECT_EQ(builder.err(), "bigfield::unsafe_assert_less_than: r2 or r3 too large: hi limb.");
421 } else {
422 // If the value is greater than 2^s, the error appears earlier while performing the range constraint
423 // checks on the limbs.
424 EXPECT_EQ(builder.err(), "bigfield::assert_less_than: limb 2 or 3 too large: hi limb.");
425 }
426 }
427 }
428
430 {
432
433 for (size_t i = 0; i < edge_case_values.size() - 1; ++i) {
434 // Check against all larger edge case values
435 for (size_t j = i + 1; j < edge_case_values.size(); ++j) {
436 fq_ct edge_case_small = fq_ct::create_from_u512_as_witness(&builder, edge_case_values[i], true);
437
438 // This should always pass since edge_case_values is sorted in ascending order
439 edge_case_small.assert_less_than(edge_case_values[j].lo);
440 }
441 }
442
443 bool result = CircuitChecker::check(builder);
444 EXPECT_EQ(result, true);
445 }
446
448 {
449 for (size_t i = 2; i < edge_case_values.size(); ++i) {
450 // Checks each large value individually in its own circuit
452
453 // This should fail since values in edge_case_values are sorted in ascending order
454 fq_ct larger_value = fq_ct::create_from_u512_as_witness(&builder, edge_case_values[i], true);
455 uint256_t smaller_value = edge_case_values[i - 1].lo;
456 larger_value.assert_less_than(smaller_value);
457
458 bool result = CircuitChecker::check(builder);
459 EXPECT_EQ(result, false);
460 EXPECT_EQ(builder.err(), "bigfield::unsafe_assert_less_than: r2 or r3 too large: hi limb.");
461 }
462 }
463
465 {
467
468 // Check that both edge case values as well as values larger than bigfield are reduced correctly
469 for (const auto& value : edge_case_values) {
470 fq_ct edge_case = fq_ct::create_from_u512_as_witness(&builder, value, false);
471 fq_native edge_case_native = fq_native(value);
472
473 edge_case.reduce_mod_target_modulus();
474 edge_case_native = edge_case_native.reduce_once().reduce_once();
475
476 EXPECT_EQ(edge_case.get_value() < fq_ct::modulus, true);
477 EXPECT_EQ(edge_case.get_value(), uint512_t(edge_case_native));
478 }
479
480 for (const auto& large_value : values_larger_than_bigfield) {
481 fq_ct large_case = fq_ct::create_from_u512_as_witness(&builder, large_value, true);
482 fq_native large_case_native = fq_native(large_value);
483
484 large_case.reduce_mod_target_modulus();
485 large_case_native = large_case_native.reduce_once().reduce_once();
486
487 EXPECT_EQ(large_case.get_value() < fq_ct::modulus, true);
488 EXPECT_EQ(large_case.get_value(), uint512_t(large_case_native));
489 }
490
491 bool result = CircuitChecker::check(builder);
492 EXPECT_EQ(result, true);
493 }
494
496 {
498
499 // One is n, other is (p + n)
500 fq_ct value_n = fq_ct::create_from_u512_as_witness(&builder, edge_case_values[3], true);
501 fq_ct value_p_plus_n = fq_ct::create_from_u512_as_witness(&builder, values_larger_than_bigfield[2], true);
502
503 // Both should be equal to n mod p
504 value_p_plus_n.assert_equal(value_n);
505
506 // Create random bigfield element and add p to it
507 auto [random_native, random_ct] = get_random_witness(&builder, false);
508 fq_ct random_plus_p =
509 fq_ct::create_from_u512_as_witness(&builder, uint512_t(random_native) + uint512_t(fq_ct::modulus), true);
510 random_plus_p.assert_equal(random_ct);
511
512 bool result = CircuitChecker::check(builder);
513 EXPECT_EQ(result, true);
514 }
515
517 {
518 {
520
521 // numerator and denominator both are witnesses
522 auto [a_native, a_ct] = get_random_witness(&builder, false);
523 fq_ct zero = fq_ct::create_from_u512_as_witness(&builder, uint512_t(0), true);
524 fq_ct zero_modulus = fq_ct::create_from_u512_as_witness(&builder, uint512_t(fq_ct::modulus), true);
525
526 // Division by zero should trigger an assertion failure
527 fq_ct output = a_ct / zero;
528 fq_ct output_modulus = a_ct / zero_modulus;
529
530 // outputs are irrelevant
531 EXPECT_EQ(output.get_value(), 0);
532 EXPECT_EQ(output_modulus.get_value(), 0);
533
534 bool result = CircuitChecker::check(builder);
535 EXPECT_EQ(result, false);
536 EXPECT_EQ(builder.err(), "bigfield: prime limb diff is zero, but expected non-zero");
537 }
538 {
540
541 // numerator is constant, denominator is witness
543 fq_ct a_ct(&builder, uint256_t(a_native));
544 fq_ct zero = fq_ct::create_from_u512_as_witness(&builder, uint512_t(0), true);
545
546 // Division by zero should trigger an assertion failure
547 fq_ct output = a_ct / zero;
548
549 // outputs are irrelevant
550 EXPECT_EQ(output.get_value(), 0);
551
552 bool result = CircuitChecker::check(builder);
553 EXPECT_EQ(result, false);
554 EXPECT_EQ(builder.err(), "bigfield: prime limb diff is zero, but expected non-zero");
555 }
556 {
558
559 // numerator is empty, denominator is witness
560 fq_ct zero = fq_ct::create_from_u512_as_witness(&builder, uint512_t(0), true);
561
562 // Division by zero should trigger an assertion failure
563 fq_ct output = fq_ct::div_check_denominator_nonzero({}, zero);
564
565 // outputs are irrelevant
566 EXPECT_EQ(output.get_value(), 0);
567
568 bool result = CircuitChecker::check(builder);
569 EXPECT_EQ(result, false);
570 EXPECT_EQ(builder.err(), "bigfield: prime limb diff is zero, but expected non-zero");
571 }
572 {
574
575 // numerator is witness, denominator is constant
576 [[maybe_unused]] auto [a_native, a_ct] = get_random_witness(&builder, false);
577 fq_ct constant_zero = fq_ct(&builder, uint256_t(0));
578
579#ifndef NDEBUG
580 // In debug mode, we should hit an assertion failure
581 EXPECT_THROW_OR_ABORT(a_ct / constant_zero, "bigfield: prime limb diff is zero, but expected non-zero");
582#endif
583 }
584 {
586
587 // numerator and denominator both are constant
589 fq_ct a_ct(&builder, uint256_t(a_native));
590
591#ifndef NDEBUG
592 fq_ct constant_zero = fq_ct(&builder, uint256_t(0));
593 EXPECT_THROW_OR_ABORT(a_ct / constant_zero, "bigfield: division by zero in constant division");
594#endif
595 }
596 {
598
599 // numerator is empty, denominator is constant
600#ifndef NDEBUG
601 fq_ct constant_zero = fq_ct(&builder, uint256_t(0));
602 EXPECT_THROW_OR_ABORT(fq_ct::div_check_denominator_nonzero({}, constant_zero),
603 "bigfield: prime limb diff is zero, but expected non-zero");
604#endif
605 }
606 }
607};
608
609// Define types for which the above tests will be constructed.
610using CircuitTypes = testing::Types<typename bb::stdlib::bn254<UltraCircuitBuilder>::BaseField,
615
616// Define the suite of tests.
618
619TYPED_TEST(stdlib_bigfield_edge_cases, larger_than_bigfield_allowed)
620{
621 TestFixture::test_larger_than_bigfield_allowed();
622}
624{
625 TestFixture::test_reduction_check_works();
626}
627TYPED_TEST(stdlib_bigfield_edge_cases, reduction_works_on_limb_overflow)
628{
629 TestFixture::test_reduction_works_on_limb_overflow();
630}
631TYPED_TEST(stdlib_bigfield_edge_cases, max_value_tracking_during_addition)
632{
633 TestFixture::test_maximum_value_tracking_during_addition();
634}
635
636// invariant checks during operations
637TYPED_TEST(stdlib_bigfield_edge_cases, invariants_during_addition)
638{
639 TestFixture::test_invariants_during_addition();
640}
641TYPED_TEST(stdlib_bigfield_edge_cases, invariants_during_subtraction)
642{
643 TestFixture::test_invariants_during_subtraction();
644}
645TYPED_TEST(stdlib_bigfield_edge_cases, invariants_during_multiplication)
646{
647 TestFixture::test_invariants_during_multiplication();
648}
649TYPED_TEST(stdlib_bigfield_edge_cases, invariants_during_division)
650{
651 TestFixture::test_invariants_during_division();
652}
653TYPED_TEST(stdlib_bigfield_edge_cases, invariants_during_squaring)
654{
655 TestFixture::test_invariants_during_squaring();
656}
657TYPED_TEST(stdlib_bigfield_edge_cases, invariants_during_negation)
658{
659 TestFixture::test_invariants_during_negation();
660}
661
662// assert related checks
664{
665 TestFixture::test_assert_is_in_field();
666}
667TYPED_TEST(stdlib_bigfield_edge_cases, assert_is_in_field_fails)
668{
669 TestFixture::test_assert_is_in_field_fails();
670}
672{
673 TestFixture::test_assert_less_than();
674}
676{
677 TestFixture::test_assert_less_than_fails();
678}
679TYPED_TEST(stdlib_bigfield_edge_cases, reduce_mod_target_modulus)
680{
681 TestFixture::test_reduce_mod_target_modulus();
682}
684{
685 TestFixture::test_assert_equal_edge_case();
686}
687
689{
690 TestFixture::test_divide_by_zero_fails();
691}
#define EXPECT_THROW_OR_ABORT(statement, matcher)
Definition assert.hpp:185
typename extract_builder< BigField >::type builder_t
typename extract_fq_params< BigField >::type params_t
InputType
typename extract_builder< BigField >::type builder_t
constexpr InputType operator!(InputType type)
#define INVARIANT_BINARY_OP_TEST(op_name, op_symbol, skip_zero)
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
constexpr uint64_t get_msb() const
Implements boolean logic in-circuit.
Definition bool.hpp:59
Represents a dynamic array of bytes in-circuit.
stdlib::witness_t< Builder > witness_ct
static std::pair< fq_native, fq_ct > get_random_witness(Builder *builder, bool reduce_input=false)
static void test_invariants_during_binary_operation(BinaryOp binary_op, NativeOp native_op, const std::string &operation_name, const bool skip_zero=false)
typename bb::stdlib::bn254< Builder >::ScalarField fr_ct
static const std::array< uint512_t, 10 > values_larger_than_bigfield
static constexpr std::array< uint512_t, 5 > edge_case_values
static constexpr uint512_t reduction_upper_bound
static void check_invariants(const fq_ct &field_element, const std::string &operation_name)
bb::field< params_t< BigField > > fq_native
AluTraceBuilder builder
Definition alu.test.cpp:123
FF a
ECCVMCircuitBuilder Builder
testing::Types< bb::UltraCircuitBuilder, bb::MegaCircuitBuilder > CircuitTypes
crypto::Poseidon2Bn254ScalarFieldParams Params
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.
TYPED_TEST_SUITE(ShpleminiTest, TestSettings)
field< Bn254FrParams > fr
Definition fr.hpp:174
C slice(C const &container, size_t start)
Definition container.hpp:9
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
This file contains part of the logic for the Origin Tag mechanism that tracks the use of in-circuit p...
#define STANDARD_TESTING_TAGS
General class for prime fields see Prime field documentation["field documentation"] for general imple...
static constexpr uint256_t modulus
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr field reduce_once() const noexcept