Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
field.test.cpp
Go to the documentation of this file.
1#include "field.hpp"
2#include "../bool/bool.hpp"
8#include <gtest/gtest.h>
9#include <utility>
10
11using namespace bb;
12
13namespace {
15}
16
17template <class T> void ignore_unused(T&) {} // use to ignore unused variables in lambdas
18
19using namespace bb;
20
21template <typename Builder> class stdlib_field : public testing::Test {
26
27 static uint64_t fidget(Builder& builder)
28 {
29 field_ct a(public_witness_ct(&builder, fr::one())); // a is a legit wire value in our circuit
31 (fr::one())); // b is just a constant, and should not turn up as a wire value in our circuit
32 const size_t num_gates = builder.get_num_finalized_gates_inefficient();
33
34 // This shouldn't create a constraint - we just need to scale the addition/multiplication gates that `a` is
35 // involved in, `c` should have the same witness index as `a`, i.e. point to the same wire value
36 field_ct c = a + b;
37 EXPECT_TRUE(field_ct::witness_indices_match(c, a));
38 EXPECT_TRUE(builder.get_num_finalized_gates_inefficient() == num_gates);
39 field_ct d(&builder, fr::coset_generator<0>()); // like b, d is just a constant and not a wire value
40
41 // by this point, we shouldn't have added any constraints in our circuit
42 for (size_t i = 0; i < 17; ++i) {
43 c = c * d; // shouldn't create a constraint - just scales up c (which points to same wire value as a)
44 c = c - d; // shouldn't create a constraint - just adds a constant term into c's gates
45 c = c * a; // will create a constraint - both c and a are wires in our circuit (the same wire actually, so
46 // this is a square-ish gate)
47 }
48
49 // run the same computation using normal types so we can compare the output
50 uint64_t aa = 1;
51 uint64_t bb = 1;
52 uint64_t cc = aa + bb;
53 uint64_t dd = 5;
54 for (size_t i = 0; i < 17; ++i) {
55 cc = cc * dd;
56 cc = cc - dd;
57 cc = cc * aa;
58 }
59 return cc;
60 }
61
62 static void build_test_circuit(Builder& builder, size_t num_gates)
63 {
66
67 field_ct c(&builder);
68 for (size_t i = 0; i < (num_gates / 4) - 4; ++i) {
69 c = a + b;
70 c = a * c;
71 a = b * b;
72 b = c * c;
73 }
74 }
75
76 public:
78 {
81 field_ct elt(witness_ct(&builder, val));
82 // Ensure the value is correct
83 EXPECT_EQ(elt.get_value(), val);
84 // Ensure that the context is not missing
85 EXPECT_FALSE(elt.is_constant());
86 }
87 static void test_add()
88 {
90 // Case 1: both summands are witnesses
93 field_ct sum = a + b;
94 EXPECT_TRUE(sum.get_value() == a.get_value() + b.get_value());
95 EXPECT_FALSE(sum.is_constant());
96
97 // Case 2: second summand is a constant
99 field_ct sum_with_constant = sum + c;
100 EXPECT_TRUE(sum_with_constant.get_value() == sum.get_value() + c.get_value());
101 EXPECT_TRUE(field_ct::witness_indices_match(sum, sum_with_constant));
102
103 // Case 3: first summand is a constant
104 sum_with_constant = c + sum;
105 EXPECT_TRUE(sum_with_constant.get_value() == sum.get_value() + c.get_value());
106 EXPECT_TRUE(field_ct::witness_indices_match(sum, sum_with_constant));
107
108 // Case 4: both summands are witnesses with matching indices
109 field_ct sum_with_same_witness_index = sum_with_constant + sum;
110 EXPECT_TRUE(sum_with_same_witness_index.get_value() == sum.get_value() + sum_with_constant.get_value());
111 EXPECT_TRUE(field_ct::witness_indices_match(sum_with_same_witness_index, sum_with_constant) &&
112 field_ct::witness_indices_match(sum_with_same_witness_index, sum));
113
114 // Case 5: both summands are constant
116 field_ct constant_sum = c + d;
117 EXPECT_TRUE(constant_sum.is_constant());
118 EXPECT_TRUE(constant_sum.get_value() == d.get_value() + c.get_value());
119 }
121 {
122 auto run_test = [&](fr elt, size_t num_bits, bool expect_verified) {
125 a.create_range_constraint(num_bits, "field_tests: range_constraint on a fails");
126
127 bool verified = CircuitChecker::check(builder);
128 EXPECT_EQ(verified, expect_verified);
129 if (verified != expect_verified) {
130 info("Range constraint malfunction on ", elt, " with num_bits ", num_bits);
131 }
132 };
133
134 run_test(2, 1, false);
135 run_test(2, 2, true);
136 run_test(3, 2, true);
137 // 130 = 0b10000010, 8 bits
138 for (size_t num_bits = 1; num_bits < 17; num_bits++) {
139 run_test(130, num_bits, num_bits >= 8);
140 }
141
142 // -1 has maximum bit length
143 run_test(-1, fr::modulus.get_msb(), false);
144 run_test(-1, 128, false);
145 run_test(-1, fr::modulus.get_msb() + 1, true);
146 }
147
149 {
150 // Test the conversion from field_t to bool_t.
151
152 std::array<bb::fr, 5> input_array{ 0, 1, 0, 1, bb::fr::random_element() };
153 // Cases 0,1: Constant 0, 1
154 // Cases 2,3: Witnesses 0, 1
155 for (size_t idx = 0; idx < 4; idx++) {
156 bool expected_to_be_constant = (idx < 2);
158 field_ct field_elt = (expected_to_be_constant) ? field_ct(input_array[idx])
159 : field_ct(witness_ct(&builder, input_array[idx]));
160 bool_ct converted(field_elt);
161 EXPECT_TRUE(converted.is_constant() == expected_to_be_constant);
162 EXPECT_TRUE(field_elt.get_value() == converted.get_value());
163
164 if (!expected_to_be_constant) {
165 EXPECT_TRUE(CircuitChecker::check(builder));
166 EXPECT_TRUE(field_ct::witness_indices_match(converted, field_elt));
167 }
168 }
169 // Check that the conversion aborts in the case of random field elements.
170 bool_ct invalid_bool;
171 // Case 4: Invalid constant conversion
173 invalid_bool = bool_ct(field_ct(input_array.back())),
174 "Assertion failed: (additive_constant == bb::fr::one() || additive_constant == bb::fr::zero())");
175 // Case 5: Invalid witness conversion
177 EXPECT_THROW_OR_ABORT(invalid_bool = bool_ct(field_ct(witness_ct(&builder, input_array.back()))),
178 "Assertion failed: ((witness == bb::fr::zero()) || (witness == bb::fr::one()) == true)");
179 }
185 {
188 bool_ct b_false = bool_ct(one * field_ct(0));
189 EXPECT_FALSE(b_false.get_value());
190 }
192 {
194 // Populate test inputs
200
202 engine.get_random_uint256(), // lhs, rhs = const
203 witness_ct(&builder, engine.get_random_uint256()), // one side is a witness
204 witness_ct(&builder, engine.get_random_uint256()), // both witnesses
205 lhs_in[3], // equal constants
206 lhs_in[4] // equal witnesses
207 };
208
209 auto check_conditional_assign =
210 [](auto& builder, bool_ct& predicate, field_ct& lhs, field_ct& rhs, bool same_elt) {
211 size_t num_gates_before = builder.get_num_finalized_gates_inefficient();
212 field_ct result = field_ct::conditional_assign_internal(predicate, lhs, rhs);
213 EXPECT_TRUE(result.get_value() == (predicate.get_value() ? lhs.get_value() : rhs.get_value()));
214
215 size_t expected_num_gates = 0;
216 // If predicate is constant, no need to constrain the result of the operation
217 if (!predicate.is_constant()) {
218 // If the witness index and constants of lhs and lhs do coincide, no gates are added
219 if (!same_elt) {
220 int num_witnesses = static_cast<int>(!rhs.is_constant()) + static_cast<int>(!lhs.is_constant());
221 // If lhs or rhs is a constant field element, `lhs - rhs` does not create an extra gate
222 expected_num_gates += static_cast<size_t>(num_witnesses);
223 }
224 }
225
226 EXPECT_TRUE(builder.get_num_finalized_gates_inefficient() - num_gates_before == expected_num_gates);
227 };
228 // Populate predicate array, ensure that both constant and witness predicates are present
229 std::array<bool_ct, 4> predicates{
230 bool_ct(true), bool_ct(false), bool_ct(witness_ct(&builder, true)), bool_ct(witness_ct(&builder, false))
231 };
232
233 for (auto& predicate : predicates) {
234 for (size_t i = 0; i < 4; i++) {
235 check_conditional_assign(builder, predicate, lhs_in[i], rhs_in[i], i > 2);
236 }
237 }
238 EXPECT_TRUE(CircuitChecker::check(builder));
239 }
245 {
246
247 auto check_that_conditional_assign_result_is_constant = [](bool_ct& predicate) {
248 field_ct x(2);
249 field_ct y(2);
250 field_ct z(1);
251 field_ct alpha = x.madd(y, -z);
252 field_ct beta(3);
253 field_ct zeta = field_ct::conditional_assign_internal(predicate, alpha, beta);
254
255 EXPECT_TRUE(zeta.is_constant());
256 };
257
259 // Populate predicate array, ensure that both constant and witness predicates are present
260 std::array<bool_ct, 4> predicates{
261 bool_ct(true), bool_ct(false), bool_ct(witness_ct(&builder, true)), bool_ct(witness_ct(&builder, false))
262 };
263
264 for (auto& predicate : predicates) {
265 check_that_conditional_assign_result_is_constant(predicate);
266 }
267 }
268
275 {
277
278 field_ct a(1);
279 field_ct b(1);
280 EXPECT_TRUE(a.multiplicative_constant == bb::fr::one());
281 EXPECT_TRUE(b.multiplicative_constant == bb::fr::one());
282 auto c = a + b;
283 EXPECT_TRUE(c.multiplicative_constant == bb::fr::one());
284 c = a - b;
285 EXPECT_TRUE(c.multiplicative_constant == bb::fr::one());
286 c = -c;
287 EXPECT_TRUE(c.multiplicative_constant == bb::fr::one());
288 }
289
293 static void test_assert_equal()
294 {
295 auto run_test = [](bool constrain, bool true_when_y_val_zero = true) {
297 field_ct x = witness_ct(&builder, 1);
298 field_ct y = witness_ct(&builder, 0);
299
300 // With no constraints, the proof verification will pass even though
301 // we assert x and y are equal.
302 bool expected_result = true;
303
304 if (constrain) {
305 /* The fact that we have a passing test in both cases that follow tells us
306 * that the failure in the first case comes from the additive constraint,
307 * not from a copy constraint. That failure is because the assert_equal
308 * below says that 'the value of y was always x'--the value 1 is substituted
309 * for x when evaluating the gate identity.
310 */
311 if (true_when_y_val_zero) {
312 // constraint: 0*x + 1*y + 0*0 + 0 == 0
313
314 builder.create_add_gate({ .a = x.get_witness_index(),
315 .b = y.get_witness_index(),
316 .c = builder.zero_idx(),
317 .a_scaling = 0,
318 .b_scaling = 1,
319 .c_scaling = 0,
320 .const_scaling = 0 });
321 expected_result = false;
322 } else {
323 // constraint: 0*x + 1*y + 0*0 - 1 == 0
324
325 builder.create_add_gate({ .a = x.get_witness_index(),
326 .b = y.get_witness_index(),
327 .c = builder.zero_idx(),
328 .a_scaling = 0,
329 .b_scaling = 1,
330 .c_scaling = 0,
331 .const_scaling = -1 });
332 expected_result = true;
333 }
334 }
335
336 x.assert_equal(y);
337
338 // both field elements have real value 1 now
339 EXPECT_EQ(x.get_value(), 1);
340 EXPECT_EQ(y.get_value(), 1);
341
342 bool result = CircuitChecker::check(builder);
343
344 EXPECT_EQ(result, expected_result);
345 };
346
347 run_test(false);
348 run_test(true, true);
349 run_test(true, false);
350 }
351
353 {
355
356 // Constant == constant
357 {
358 field_ct a(&builder, 5);
359 field_ct b(&builder, 5);
360 EXPECT_NO_THROW(a.assert_equal(b));
361 }
362
363 // Constant != constant
364 {
365 field_ct a(&builder, 3);
366 field_ct b(&builder, 7);
367 EXPECT_THROW_OR_ABORT(a.assert_equal(b), "field_t::assert_equal: constants are not equal");
368 }
369
370 // Constant == witness
371 {
373 size_t num_gates_start = builder.get_num_finalized_gates_inefficient();
374 field_ct a(&builder, 9);
376 a.assert_equal(b);
377 EXPECT_TRUE(CircuitChecker::check(builder));
378 // 1 gate is needed to fix the constant
379 EXPECT_EQ(builder.get_num_finalized_gates_inefficient() - num_gates_start, 1);
380 }
381
382 // Witness == constant
383 {
385 size_t num_gates_start = builder.get_num_finalized_gates_inefficient();
387 field_ct b(&builder, 42);
388 a.assert_equal(b);
389 EXPECT_TRUE(CircuitChecker::check(builder));
390 // 1 gate is needed to fix the constant
391 EXPECT_EQ(builder.get_num_finalized_gates_inefficient() - num_gates_start, 1);
392 }
393
394 // Witness == witness (equal values)
395 {
397 size_t num_gates_start = builder.get_num_finalized_gates_inefficient();
398
401 a.assert_equal(b);
402 EXPECT_TRUE(CircuitChecker::check(builder));
403 // Both witnesses are normalized, no gates are created, only a copy constraint
404 EXPECT_EQ(builder.get_num_finalized_gates_inefficient() - num_gates_start, 0);
405 }
406
407 // Witness != witness (both are not normalized)
408 {
410 size_t num_gates_start = builder.get_num_finalized_gates_inefficient();
412 a += 13;
414 b += 1;
415 a.assert_equal(b);
416 EXPECT_FALSE(CircuitChecker::check(builder));
417 // Both witnesses are not normalized, we use a single `add_gate` to ensure they are equal
418 EXPECT_EQ(builder.get_num_finalized_gates_inefficient() - num_gates_start, 1);
419 EXPECT_EQ(builder.err(), "field_t::assert_equal");
420 }
421 }
422
424 {
426 auto gates_before = builder.get_num_finalized_gates_inefficient();
427 uint64_t expected = fidget(builder);
428 auto gates_after = builder.get_num_finalized_gates_inefficient();
429 auto& block = builder.blocks.arithmetic;
430 EXPECT_EQ(builder.get_variable(block.w_o()[block.size() - 1]), fr(expected));
431 info("Number of gates added", gates_after - gates_before);
432 bool result = CircuitChecker::check(builder);
433 EXPECT_EQ(result, true);
434 }
435
436 static void test_div()
437 {
439
443
447
448 // Case 0: Numerator = const, denominator != const
449 field_ct out = field_ct(&builder, b.get_value()) / a;
450 EXPECT_EQ(out.get_value(), b.get_value() / a.get_value());
451 EXPECT_FALSE(out.is_constant());
452 // Check that the result is normalized in this case
453 EXPECT_TRUE(out.multiplicative_constant == 1 && out.additive_constant == 0);
454
455 // Case 1: Numerator and denominator != const
456 out = b / a;
457 EXPECT_EQ(out.get_value(), b.get_value() / a.get_value());
458
459 // Case 2: Numerator != const, denominator = const,
460 out = a / b.get_value();
461 EXPECT_EQ(out.get_value(), a.get_value() / b.get_value());
462 EXPECT_TRUE(field_ct::witness_indices_match(out, a));
463
464 // Case 3: Numerator = const 0.
465 out = field_ct(0) / b;
466 EXPECT_EQ(out.get_value(), 0);
467 EXPECT_EQ(out.is_constant(), true);
468
469 bool result = CircuitChecker::check(builder);
470 EXPECT_EQ(result, true);
471 }
472
474 {
475 // Case 0. Numerator = const, denominator = const. Check the correctness of the value and that the result is
476 // constant.
479 field_ct q = a / b;
480 EXPECT_TRUE(q.is_constant());
481 EXPECT_EQ(a.get_value() / b.get_value(), q.get_value());
482
483 {
484 // Case 1. Numerator = const, denominator = const 0. Check that the division is aborted
485 b = 0;
486 EXPECT_THROW_OR_ABORT(a / b, ".*");
487 }
488 { // Case 2. Numerator != const, denominator = const 0. Check that the division is aborted
491 b = 0;
492 EXPECT_THROW_OR_ABORT(a / b, ".*");
493 }
494 {
495 // Case 3. Numerator != const, denominator = witness 0 . Check that the circuit fails.
499 q = a / b;
500 EXPECT_FALSE(CircuitChecker::check(builder));
501 }
502 {
503 // Case 4. Numerator = const, denominator = witness 0 . Check that the circuit fails.
507 q = a / b;
508 EXPECT_FALSE(CircuitChecker::check(builder));
509 }
510 }
511 static void test_invert()
512 {
513 // Test constant case
515 field_ct b = a.invert();
516 // Check that the result is constant and correct
517 EXPECT_TRUE(a.is_constant() && (b.get_value() * a.get_value() == 1));
518
519 // Test non-constant case
521 a = witness_ct(&builder, a.get_value());
522 b = a.invert();
523 // Check that the result is normalized
524 EXPECT_TRUE((b.multiplicative_constant == 1) && (b.additive_constant == 0));
525 // Check that the result is correct
526 EXPECT_TRUE(a.get_value() * b.get_value() == 1);
527 }
528
529 static void test_invert_zero()
530 {
532
534 {
535 a.invert();
536 // Check that the result is constant and correct
537 EXPECT_FALSE(CircuitChecker::check(builder));
538 EXPECT_EQ(builder.err(), "field_t::invert denominator is 0");
539 }
540
541 a = 0;
542 EXPECT_THROW_OR_ABORT(a.invert(), "field_t::invert denominator is constant 0");
543 }
545 {
547
549
550 field_ct b = a++;
551
552 EXPECT_EQ(b.get_value(), 10);
553 EXPECT_EQ(a.get_value(), 11);
554 EXPECT_TRUE(!b.is_constant());
555
556 EXPECT_TRUE(CircuitChecker::check(builder));
557 }
558
560 {
562
564
565 field_ct b = ++a;
566
567 EXPECT_EQ(b.get_value(), 11);
568 EXPECT_EQ(a.get_value(), 11);
569
570 bool result = CircuitChecker::check(builder);
571 EXPECT_EQ(result, true);
572 }
573
575 {
579
580 field_ct c = a + b;
581
582 for (size_t i = 0; i < 16; ++i) {
583 b = a;
584 a = c;
585 c = a + b;
586 }
587
588 EXPECT_EQ(c.get_value(), fr(4181));
589
590 bool result = CircuitChecker::check(builder);
591 EXPECT_EQ(result, true);
592 }
593
595 {
597
601
602 field_ct a_sqr = a * a;
603 field_ct b_sqr = b * b;
604 field_ct c_sqr = c * c;
605 c_sqr.set_public();
606 field_ct sum_sqrs = a_sqr + b_sqr;
607
608 // builder.assert_equal(sum_sqrs.get_witness_index(), c_sqr.get_witness_index(), "triple is not pythagorean");
609 c_sqr.assert_equal(sum_sqrs);
610
611 bool verified = CircuitChecker::check(builder);
612
613 ASSERT_TRUE(verified);
614 }
615
616 static void test_equality()
617 {
619 auto gates_before = builder.get_num_finalized_gates_inefficient();
622 bool_ct r = a == b;
623
624 auto gates_after = builder.get_num_finalized_gates_inefficient();
625 EXPECT_EQ(r.get_value(), true);
626
627 fr x = r.get_value();
628 EXPECT_EQ(x, fr(1));
629 // Using a == b, when both a and b are witnesses, adds 4 constraints:
630 // 1) compute a - b;
631 // 2) ensure r is bool;
632 // 3) (a - b) * I + r - 1 = 0;
633 // 4) -I * r + r = 0.
634 EXPECT_EQ(gates_after - gates_before, 4UL);
635
636 bool result = CircuitChecker::check(builder);
637 EXPECT_EQ(result, true);
638 }
639
641 {
643
644 auto gates_before = builder.get_num_finalized_gates_inefficient();
647 bool_ct r = a == b;
648 auto gates_after = builder.get_num_finalized_gates_inefficient();
649
650 EXPECT_FALSE(r.get_value());
651
652 // Using a == b, when both a and b are witnesses, adds 4 constraints:
653 // 1) compute a - b;
654 // 2) ensure r is bool;
655 // 3) (a - b) * I + r - 1 = 0;
656 // 4) -I * r + r = 0
657 EXPECT_EQ(gates_after - gates_before, 4UL);
658 EXPECT_TRUE(CircuitChecker::check(builder));
659 }
660
662 {
665
666 auto gates_before = builder.get_num_finalized_gates_inefficient();
667 field_ct b = 3;
668 field_ct c = 7;
669 // Note that the lhs is constant, hence (rhs - lhs) can be computed without adding new gates, using == in
670 // this case requires 3 constraints 1) ensure r is bool; 2) (a - b) * I + r - 1 = 0; 3) -I * r + r = 0
671 bool_ct r = (a * c) == (b * c + c);
672 auto gates_after = builder.get_num_finalized_gates_inefficient();
673 EXPECT_EQ(gates_after - gates_before, 3UL);
674 r = r && (b + 1 == a);
675 EXPECT_EQ(r.get_value(), true);
676 // The situation is as above, but we also applied && to bool_t witnesses, which adds an extra gate.
677 EXPECT_EQ(builder.get_num_finalized_gates_inefficient() - gates_after, 4UL);
678 EXPECT_TRUE(CircuitChecker::check(builder));
679 }
680
682 {
683 size_t n = 16384;
685
687
688 bool result = CircuitChecker::check(builder);
689 EXPECT_EQ(result, true);
690 }
691
692 static void test_is_zero()
693 {
695 // Create constant elements
697 field_ct e(&builder, fr::one());
698 // Validate that `is_zero()` check does not add any gates in this case
699 const size_t old_n = builder.get_num_finalized_gates_inefficient();
700 bool_ct d_zero = d.is_zero();
701 bool_ct e_zero = e.is_zero();
702 const size_t new_n = builder.get_num_finalized_gates_inefficient();
703 EXPECT_EQ(old_n, new_n);
704
705 // Create witnesses
708 // Create constants
712 field_ct c_4 = c_1 + c_2;
713
714 // Ensure that `a` and `b` are not normalized
715 a = a * c_4 + c_4;
716 b = b * c_4 + c_4; // = -c_4 + c_4
717 b = (b - c_1 - c_2) / c_4; // = (-c_1 - c_2 )/c_4 = -1
718 b = b + c_3; // = -1 + 1 = 0
719 EXPECT_TRUE(a.additive_constant != 0 || a.multiplicative_constant != 1);
720 EXPECT_TRUE(b.additive_constant != 0 || b.multiplicative_constant != 1);
721
722 bool_ct a_zero = a.is_zero();
723 bool_ct b_zero = b.is_zero();
724
725 bool_ct a_normalized_zero = a.normalize().is_zero();
726 bool_ct b_normalized_zero = b.normalize().is_zero();
727
728 EXPECT_EQ(a_zero.get_value(), false);
729 EXPECT_EQ(b_zero.get_value(), true);
730 EXPECT_EQ(a_normalized_zero.get_value(), false);
731 EXPECT_EQ(b_normalized_zero.get_value(), true);
732 EXPECT_EQ(d_zero.get_value(), true);
733 EXPECT_EQ(e_zero.get_value(), false);
734
735 bool result = CircuitChecker::check(builder);
736 EXPECT_EQ(result, true);
737 }
738
740 {
742 size_t num_gates_before = builder.get_num_finalized_gates_inefficient();
744 if (a.get_value() == 0) {
745 a += 1;
746 }
747 a.assert_is_not_zero();
748 // a is a constant, so no gates should be added
749 EXPECT_TRUE(builder.get_num_finalized_gates_inefficient() - num_gates_before == 0);
750 a = witness_ct(&builder, 17);
751 a.assert_is_not_zero();
752 EXPECT_TRUE(builder.get_num_finalized_gates_inefficient() - num_gates_before == 1);
753 // Ensure a is not normalized anymore
754 a *= 2;
755 a += 4;
756 a.assert_is_not_zero();
757 EXPECT_TRUE(CircuitChecker::check(builder));
758 { // a is a non-normalized witness with value 0
759 a -= field_ct(a.get_value());
760 a.assert_is_not_zero();
761 EXPECT_FALSE(CircuitChecker::check(builder));
762 }
763 { // a is a normalized witness with value 0
764 a = witness_ct(&builder, 0);
765 a.assert_is_not_zero();
766 EXPECT_FALSE(CircuitChecker::check(builder));
767 }
768 { // a is a const 0
769 a = field_ct(0);
770 EXPECT_THROW_OR_ABORT(a.assert_is_not_zero(), "assert_is_not_zero");
771 }
772 }
773
774 static void test_madd()
775 {
777
787
788 // test madd when all operands are witnesses
789 field_ct d = a * ma + ca;
790 field_ct e = b * mb + cb;
791 field_ct f = c * mc + cc;
792 field_ct g = d.madd(e, f);
793 field_ct h = d * e + f;
794 h = h.normalize();
795 g = g.normalize();
796 EXPECT_EQ(g.get_value(), h.get_value());
797
798 // test madd when to_add = constant
799 field_ct i = a.madd(b, ma);
800 field_ct j = a * b + ma;
801 i = i.normalize();
802 j = j.normalize();
803 EXPECT_EQ(i.get_value(), j.get_value());
804
805 // test madd when to_mul = constant
806 field_ct k = a.madd(mb, c);
807 field_ct l = a * mb + c;
808 k = k.normalize();
809 l = l.normalize();
810 EXPECT_EQ(k.get_value(), l.get_value());
811
812 // test madd when lhs is constant
813 field_ct m = ma.madd(b, c);
814 field_ct n = ma * b + c;
815 m = m.normalize();
816 n = n.normalize();
817 EXPECT_EQ(m.get_value(), n.get_value());
818
819 bool result = CircuitChecker::check(builder);
820 EXPECT_EQ(result, true);
821 }
823 {
824
825 auto make_constant = [](Builder& builder, int val) { return field_ct(&builder, bb::fr(val)); };
826 auto make_witness = [](Builder& builder, int val) { return field_ct(witness_ct(&builder, bb::fr(val))); };
827
828 struct Case {
829 bool a_const;
830 bool b_const;
831 bool c_const;
832 bool expect_gate;
833 };
834
835 std::vector<Case> cases = {
836 { true, true, true, false }, { true, true, false, false }, { true, false, true, false },
837 { false, true, true, false }, { true, false, false, true }, { false, true, false, true },
838 { false, false, true, true }, { false, false, false, true },
839 };
840
841 for (const auto& [a_const, b_const, c_const, expect_gate] : cases) {
843
844 auto a = a_const ? make_constant(builder, 1) : make_witness(builder, 1);
845 auto b = b_const ? make_constant(builder, 2) : make_witness(builder, 2);
846 auto c = c_const ? make_constant(builder, 3) : make_witness(builder, 3);
847
848 size_t before = builder.get_num_finalized_gates_inefficient();
849 a.madd(b, c);
850 size_t after = builder.get_num_finalized_gates_inefficient();
851 bool gate_added = (after - before == 1);
852 EXPECT_EQ(gate_added, expect_gate);
853
854 before = builder.get_num_finalized_gates_inefficient();
855 a.add_two(b, c);
856 after = builder.get_num_finalized_gates_inefficient();
857
858 gate_added = (after - before == 1);
859 EXPECT_EQ(gate_added, expect_gate);
860 }
861 }
863 {
865 std::array<bool_ct, 4> predicates{
866 bool_ct(true), bool_ct(false), bool_ct(witness_ct(&builder, true)), bool_ct(witness_ct(&builder, false))
867 };
868 field_ct constant_summand(bb::fr::random_element());
870 for (auto& predicate : predicates) {
871
872 const bool predicate_is_witness = !predicate.is_constant();
873
874 // Conditionally negate a constant
875 size_t num_gates_before = builder.get_num_finalized_gates_inefficient();
876 auto result = constant_summand.conditional_negate(predicate);
877 auto expected_result = predicate.get_value() ? -constant_summand.get_value() : constant_summand.get_value();
878 EXPECT_TRUE(result.get_value() == expected_result);
879 // Check that `result` is constant if and only if both the predicate and (*this) are constant.
880 EXPECT_TRUE(result.is_constant() == predicate.is_constant());
881 // A gate is only added if the predicate is a witness
882 EXPECT_TRUE(builder.get_num_finalized_gates_inefficient() - num_gates_before == 0);
883
884 // Conditionally negate a witness
885 num_gates_before = builder.get_num_finalized_gates_inefficient();
886 result = witness_summand.conditional_negate(predicate);
887 expected_result = predicate.get_value() ? -witness_summand.get_value() : witness_summand.get_value();
888 EXPECT_TRUE(result.get_value() == expected_result);
889 // The result must be a witness
890 EXPECT_FALSE(result.is_constant());
891 // A gate is only added if the predicate is a witness
892 EXPECT_TRUE(builder.get_num_finalized_gates_inefficient() - num_gates_before == predicate_is_witness);
893 }
894 }
895 static void test_two_bit_table()
896 {
902
904
905 bool_ct zero(witness_ct(&builder, false));
906 bool_ct one(witness_ct(&builder, true));
907
908 field_ct result_a = field_ct::select_from_two_bit_table(table, zero, zero).normalize();
909 field_ct result_b = field_ct::select_from_two_bit_table(table, zero, one).normalize();
910 field_ct result_c = field_ct::select_from_two_bit_table(table, one, zero).normalize();
911 field_ct result_d = field_ct::select_from_two_bit_table(table, one, one).normalize();
912
913 EXPECT_EQ(result_a.get_value(), a.get_value());
914 EXPECT_EQ(result_b.get_value(), b.get_value());
915 EXPECT_EQ(result_c.get_value(), c.get_value());
916 EXPECT_EQ(result_d.get_value(), d.get_value());
917
918 bool result = CircuitChecker::check(builder);
919 EXPECT_EQ(result, true);
920 }
921
922 static void test_split_at()
923 {
925
926 // Test different bit sizes
927 std::vector<size_t> test_bit_sizes = { 8, 16, 32, 100, 252 };
928
929 // Lambda to check split_at functionality
930 auto check_split_at = [&](const field_ct& a, size_t start, size_t num_bits) {
931 const uint256_t a_native = a.get_value();
932 auto split_data = a.no_wrap_split_at(start, num_bits);
933 EXPECT_EQ(split_data.first.get_value(), a_native & ((uint256_t(1) << start) - 1));
934 EXPECT_EQ(split_data.second.get_value(), (a_native >> start) & ((uint256_t(1) << num_bits) - 1));
935
936 if (a.is_constant()) {
937 EXPECT_TRUE(split_data.first.is_constant());
938 EXPECT_TRUE(split_data.second.is_constant());
939 }
940
941 if (start == 0) {
942 EXPECT_TRUE(split_data.first.is_constant());
943 EXPECT_TRUE(split_data.first.get_value() == 0);
944 EXPECT_EQ(split_data.second.get_value(), a.get_value());
945 }
946 };
947
948 for (size_t num_bits : test_bit_sizes) {
949 uint256_t a_native = engine.get_random_uint256() & ((uint256_t(1) << num_bits) - 1);
950
951 // check split_at for a constant
952 field_ct a_constant(a_native);
953 check_split_at(a_constant, 0, num_bits);
954 check_split_at(a_constant, num_bits / 4, num_bits);
955 check_split_at(a_constant, num_bits / 3, num_bits);
956 check_split_at(a_constant, num_bits / 2, num_bits);
957 check_split_at(a_constant, num_bits - 1, num_bits);
958
959 // check split_at for a witness
960 field_ct a_witness(witness_ct(&builder, a_native));
961 check_split_at(a_witness, 0, num_bits);
962 check_split_at(a_witness, num_bits / 4, num_bits);
963 check_split_at(a_witness, num_bits / 3, num_bits);
964 check_split_at(a_witness, num_bits / 2, num_bits);
965 check_split_at(a_witness, num_bits - 1, num_bits);
966 }
967
968 bool result = CircuitChecker::check(builder);
969 EXPECT_EQ(result, true);
970 }
971
973 {
983
985
986 bool_ct zero(witness_ct(&builder, false));
987 bool_ct one(witness_ct(&builder, true));
988
989 field_ct result_a = field_ct::select_from_three_bit_table(table, zero, zero, zero).normalize();
990 field_ct result_b = field_ct::select_from_three_bit_table(table, zero, zero, one).normalize();
991 field_ct result_c = field_ct::select_from_three_bit_table(table, zero, one, zero).normalize();
992 field_ct result_d = field_ct::select_from_three_bit_table(table, zero, one, one).normalize();
993 field_ct result_e = field_ct::select_from_three_bit_table(table, one, zero, zero).normalize();
994 field_ct result_f = field_ct::select_from_three_bit_table(table, one, zero, one).normalize();
995 field_ct result_g = field_ct::select_from_three_bit_table(table, one, one, zero).normalize();
996 field_ct result_h = field_ct::select_from_three_bit_table(table, one, one, one).normalize();
997
998 EXPECT_EQ(result_a.get_value(), a.get_value());
999 EXPECT_EQ(result_b.get_value(), b.get_value());
1000 EXPECT_EQ(result_c.get_value(), c.get_value());
1001 EXPECT_EQ(result_d.get_value(), d.get_value());
1002 EXPECT_EQ(result_e.get_value(), e.get_value());
1003 EXPECT_EQ(result_f.get_value(), f.get_value());
1004 EXPECT_EQ(result_g.get_value(), g.get_value());
1005 EXPECT_EQ(result_h.get_value(), h.get_value());
1006
1007 bool result = CircuitChecker::check(builder);
1008 EXPECT_EQ(result, true);
1009 }
1010
1012 {
1014
1017 field_ct c(witness_ct(&builder, fr(3)));
1018 field_ct d(witness_ct(&builder, fr(4)));
1019 field_ct e(witness_ct(&builder, fr(5)));
1020 std::vector<field_ct> set = { a, b, c, d, e };
1021
1022 a.assert_is_in_set(set);
1023 info("num gates = ", builder.get_num_finalized_gates_inefficient());
1024
1025 bool result = CircuitChecker::check(builder);
1026 EXPECT_EQ(result, true);
1027 }
1028
1030 {
1032
1035 field_ct c(witness_ct(&builder, fr(3)));
1036 field_ct d(witness_ct(&builder, fr(4)));
1037 field_ct e(witness_ct(&builder, fr(5)));
1038 std::vector<field_ct> set = { a, b, c, d, e };
1039
1040 field_ct f(witness_ct(&builder, fr(6)));
1041 f.assert_is_in_set(set);
1042
1043 info("num gates = ", builder.get_num_finalized_gates_inefficient());
1044 bool result = CircuitChecker::check(builder);
1045 EXPECT_EQ(result, false);
1046 }
1047
1048 static void test_pow()
1049 {
1051
1052 std::array<uint32_t, 3> const_exponent_values{ 0, 1, engine.get_random_uint32() };
1053 std::array<field_ct, 3> witness_exponent_values{ witness_ct(&builder, 0),
1054 witness_ct(&builder, 1),
1056
1057 std::array<uint256_t, 3> base_values{ 0, 1, engine.get_random_uint256() };
1058 for (auto& base : base_values) {
1059 for (auto& exponent : const_exponent_values) {
1060 // Test constant base && integer exponent cases
1061 field_ct result = field_ct(base).pow(exponent);
1062 EXPECT_TRUE(result.is_constant());
1063 EXPECT_EQ(result.get_value(), bb::fr(base).pow(exponent));
1064 // Test witness base && integer exponent cases
1065 field_ct witness_base(witness_ct(&builder, base));
1066 result = witness_base.pow(exponent);
1067
1068 if (exponent != 0) {
1069 EXPECT_TRUE(!result.is_constant());
1070 } else {
1071 EXPECT_TRUE(result.is_constant());
1072 }
1073
1074 EXPECT_EQ(result.get_value(), bb::fr(base).pow(exponent));
1075
1076 EXPECT_TRUE(CircuitChecker::check(builder));
1077 }
1078 for (auto& exponent : witness_exponent_values) {
1079
1080 // Test constant base && witness exponent cases
1081 field_ct result = field_ct(base).pow(exponent);
1082 // Normalized witness == 1 leads to constant results in `conditional_assign(predicate, 1, 1)`
1083 EXPECT_EQ(result.is_constant(), base == 1);
1084
1085 EXPECT_EQ(result.get_value(), bb::fr(base).pow(exponent.get_value()));
1086 // Test witness base && witness exponent cases
1087 field_ct witness_base(witness_ct(&builder, base));
1088 result = witness_base.pow(exponent);
1089
1090 EXPECT_TRUE(!result.is_constant());
1091 EXPECT_EQ(result.get_value(), bb::fr(base).pow(exponent.get_value()));
1092
1093 EXPECT_TRUE(CircuitChecker::check(builder));
1094 }
1095 }
1096 }
1097
1099 {
1101
1102 fr base_val(engine.get_random_uint256());
1103 uint64_t exponent_val = engine.get_random_uint32();
1104 exponent_val += (uint64_t(1) << 32);
1105
1106 [[maybe_unused]] field_ct base = witness_ct(&builder, base_val);
1107 field_ct exponent = witness_ct(&builder, exponent_val);
1108 EXPECT_THROW_OR_ABORT(base.pow(exponent), "Assertion failed: \\(exponent_value.get_msb\\(\\) < 32\\)");
1109
1110 exponent = field_ct(exponent_val);
1111 EXPECT_THROW_OR_ABORT(base.pow(exponent), "Assertion failed: \\(exponent_value.get_msb\\(\\) < 32\\)");
1112 };
1113
1115 {
1117
1119 field_ct value_ct = witness_ct(&builder, value);
1120
1121 field_ct first_copy = witness_ct(&builder, value_ct.get_value());
1122 field_ct second_copy = field_ct::copy_as_new_witness(builder, value_ct);
1123
1124 EXPECT_EQ(value_ct.get_value(), value);
1125 EXPECT_EQ(first_copy.get_value(), value);
1126 EXPECT_EQ(second_copy.get_value(), value);
1127
1128 EXPECT_EQ(value_ct.get_witness_index() + 1, first_copy.get_witness_index());
1129 EXPECT_EQ(value_ct.get_witness_index() + 2, second_copy.get_witness_index());
1130 info("num gates = ", builder.get_num_finalized_gates_inefficient());
1131
1132 bool result = CircuitChecker::check(builder);
1133 EXPECT_EQ(result, true);
1134 }
1135
1137 {
1138 // Create a constant 0, `assert_is_zero()` does nothing in-circuit in this case
1139 field_ct elt = bb::fr::zero();
1140 elt.assert_is_zero();
1141 // If we apply `assert_is_zero()` to a non-zero constant, we hit an ASSERT failure
1142 elt = bb::fr::random_element();
1143
1144 if (elt.get_value() != 0) {
1145 EXPECT_THROW_OR_ABORT(elt.assert_is_zero(), "field_t::assert_is_zero");
1146 }
1147 // Create a witness 0
1149 elt = witness_ct(&builder, bb::fr::zero());
1150 elt.assert_is_zero();
1151 // The circuit must be correct
1152 EXPECT_TRUE(CircuitChecker::check(builder));
1153
1154 // If we apply `assert_is_zero()` to a non-zero witness, an unsatisfiable `poly_gate` constraint is created
1156 if (non_zero_elt.get_value() != 0) {
1157 non_zero_elt.assert_is_zero();
1158 EXPECT_FALSE(CircuitChecker::check(builder));
1159 }
1160 }
1161
1162 static void test_accumulate()
1163 {
1164 for (size_t max_vector_length = 1; max_vector_length < 100; max_vector_length++) {
1166 std::vector<bb::fr> native_input(max_vector_length, 0);
1167 for (auto& entry : native_input) {
1168 entry = bb::fr::random_element();
1169 }
1170
1171 // Compute the native sum
1172 bb::fr native_sum = std::accumulate(native_input.begin(), native_input.end(), bb::fr::zero());
1173 std::vector<field_ct> input(max_vector_length);
1174 size_t idx = 0;
1175 // Convert native vector to a vector of field_t elements. Every 5th element is set to be constant.
1176 for (auto& native_entry : native_input) {
1177 field_ct entry = ((idx % 5) == 0) ? field_ct(native_entry) : witness_ct(&builder, native_entry);
1178 input.emplace_back(entry);
1179 idx++;
1180 }
1181 // Compute the accumulation result
1183 EXPECT_EQ(native_sum, sum.get_value());
1184
1185 // Check that the result is normalized
1186 if (!sum.is_constant()) {
1187 EXPECT_TRUE(sum.multiplicative_constant == 1 && sum.additive_constant == 0);
1188 }
1189
1190 EXPECT_TRUE(CircuitChecker::check(builder));
1191
1192 // Check that the number of gates is as expected
1193 size_t num_witnesses = max_vector_length - (max_vector_length + 4) / 5;
1194 size_t padding = (3 - (num_witnesses % 3)) % 3;
1195 size_t expected_num_gates = (num_witnesses + padding) / 3;
1196
1197 EXPECT_EQ(builder.get_num_finalized_gates_inefficient(/*ensure_nonzero=*/false) - 1, expected_num_gates);
1198
1199 // Check that the accumulation of constant entries does not create a witness
1200 std::vector<field_ct> constant_input;
1201 for (auto& entry : input) {
1202 if (entry.is_constant()) {
1203 constant_input.emplace_back(entry);
1204 }
1205 }
1206 field_ct constant_sum = field_ct::accumulate(constant_input);
1207 EXPECT_TRUE(constant_sum.is_constant());
1208 }
1209 // Test edge cases
1210 // 1. Accumulating an empty vector should lead to constant zero.
1211 std::vector<field_ct> empty_input;
1212 field_ct result(field_ct::accumulate(empty_input));
1213 EXPECT_TRUE(result.is_constant() && result.get_value() == bb::fr::zero());
1214 // 2. Check that the result of accumulating a single witness summand is correct and normalized.
1216 field_ct single_summand = witness_ct(&builder, bb::fr::random_element());
1217 single_summand += field_ct(bb::fr(3));
1218 single_summand *= field_ct(bb::fr(2));
1219 // `single_summand` isn't normalized anymore
1220 EXPECT_TRUE(single_summand.additive_constant != 0 && single_summand.multiplicative_constant != 1);
1221 std::vector<field_ct> single_element_input{ single_summand };
1222 // The accumulation result is expected to be normalized
1223 result = field_ct::accumulate(single_element_input);
1224 EXPECT_TRUE(result.get_value() == single_summand.get_value() && result.additive_constant == 0 &&
1225 result.multiplicative_constant == 1);
1226 }
1227
1228 static void test_fix_witness()
1229 {
1231
1233 witness.fix_witness();
1234 // Validate that the negated value of the witness is recorded in q_c.
1235 EXPECT_TRUE(builder.blocks.arithmetic.q_c().back() == -witness.get_value());
1236 }
1237
1239 {
1241
1242 for (size_t i = 0; i < 10; ++i) {
1243 int a_val = static_cast<int>(engine.get_random_uint8());
1244 int b_val = 0;
1245 switch (i) {
1246 case 0: {
1247 b_val = a_val;
1248 break;
1249 }
1250 case 1: {
1251 b_val = a_val + 1;
1252 break;
1253 }
1254 case 2: {
1255 b_val = a_val - 1;
1256 break;
1257 }
1258 default: {
1259 b_val = static_cast<int>(engine.get_random_uint8());
1260 break;
1261 }
1262 }
1263 if (b_val < 0) {
1264 b_val = 255;
1265 }
1266 if (b_val > 255) {
1267 b_val = 0;
1268 }
1269 field_ct a = witness_ct(&builder, static_cast<uint64_t>(a_val));
1270 field_ct b = witness_ct(&builder, static_cast<uint64_t>(b_val));
1271 a.create_range_constraint(8);
1272 b.create_range_constraint(8);
1273 bool_ct result = a.template ranged_less_than<8>(b);
1274 bool expected = a_val < b_val;
1275
1276 EXPECT_EQ(result.get_value(), expected);
1277 }
1278 bool check_result = CircuitChecker::check(builder);
1279 EXPECT_EQ(check_result, true);
1280 }
1281
1283 {
1285
1288
1289 constexpr uint256_t modulus = bb::fr::modulus;
1290 constexpr size_t max_valid_num_bits = modulus.get_msb() - 1;
1291
1292 // ---------- VALID CASE ----------
1293 {
1294 constexpr size_t num_bits = max_valid_num_bits;
1295 EXPECT_NO_THROW({
1296 auto result = a.template ranged_less_than<num_bits>(b);
1297 EXPECT_EQ(result.get_value(), true);
1298 });
1299 }
1300 }
1301
1302 static void test_add_two()
1303 {
1305 auto x_1 = bb::fr::random_element();
1306 auto x_2 = bb::fr::random_element();
1307 auto x_3 = bb::fr::random_element();
1308
1309 field_ct x_1_ct = witness_ct(&builder, x_1);
1310 field_ct x_2_ct = witness_ct(&builder, x_2);
1311 field_ct x_3_ct = witness_ct(&builder, x_3);
1312
1313 auto sum_ct = x_1_ct.add_two(x_2_ct, x_3_ct);
1314
1315 EXPECT_EQ(sum_ct.get_value(), x_1 + x_2 + x_3);
1316
1317 bool circuit_checks = CircuitChecker::check(builder);
1318 EXPECT_TRUE(circuit_checks);
1319 }
1321 {
1323 // Randomly generate a and b (a must ≤ 252 bits)
1324 uint256_t a_val =
1326 auto a = field_ct(witness_ct(&builder, a_val));
1328 EXPECT_TRUE(a.get_origin_tag().is_free_witness());
1329 EXPECT_TRUE(b.get_origin_tag().is_free_witness());
1330 const size_t parent_id = 0;
1331
1332 const auto submitted_value_origin_tag = OriginTag(parent_id, /*round_id=*/0, /*is_submitted=*/true);
1333 const auto challenge_origin_tag = OriginTag(parent_id, /*round_id=*/0, /*is_submitted=*/false);
1334 const auto next_challenge_tag = OriginTag(parent_id, /*round_id=*/1, /*submitted=*/false);
1335
1336 const auto first_two_merged_tag = OriginTag(submitted_value_origin_tag, challenge_origin_tag);
1337 const auto first_and_third_merged_tag = OriginTag(submitted_value_origin_tag, next_challenge_tag);
1338 const auto first_second_third_merged_tag = OriginTag(first_two_merged_tag, next_challenge_tag);
1339
1340 a.set_origin_tag(submitted_value_origin_tag);
1341 b.set_origin_tag(challenge_origin_tag);
1342
1343 EXPECT_EQ(a.get_origin_tag(), submitted_value_origin_tag);
1344 EXPECT_EQ(b.get_origin_tag(), challenge_origin_tag);
1345
1346 // Basic additon merges tags
1347 auto c = a + b;
1348 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
1349
1350 // Basic multiplication merges tags
1351 auto d = a * b;
1352 EXPECT_EQ(d.get_origin_tag(), first_two_merged_tag);
1353
1354 // Basic subtraction merges tags
1355 auto e = a - b;
1356 EXPECT_EQ(e.get_origin_tag(), first_two_merged_tag);
1357
1358 // Division merges tags
1359
1360 auto f = a / b;
1361 EXPECT_EQ(f.get_origin_tag(), first_two_merged_tag);
1362
1363 // Exponentiation merges tags
1364
1365 auto exponent = field_ct(witness_ct(&builder, 10));
1366 exponent.set_origin_tag(challenge_origin_tag);
1367 auto g = a.pow(exponent);
1368 EXPECT_EQ(g.get_origin_tag(), first_two_merged_tag);
1369
1370 // Madd merges tags
1372 h.set_origin_tag(next_challenge_tag);
1373 auto i = a.madd(b, h);
1374 EXPECT_EQ(i.get_origin_tag(), first_second_third_merged_tag);
1375
1376 // add_two merges tags
1377 auto j = a.add_two(b, h);
1378 EXPECT_EQ(j.get_origin_tag(), first_second_third_merged_tag);
1379
1380 // Normalize preserves tag
1381
1382 EXPECT_EQ(j.normalize().get_origin_tag(), j.get_origin_tag());
1383
1384 // is_zero preserves tag
1385
1386 EXPECT_EQ(a.is_zero().get_origin_tag(), a.get_origin_tag());
1387
1388 // equals/not equals operator merges tags
1389
1390 EXPECT_EQ((a == b).get_origin_tag(), first_two_merged_tag);
1391 EXPECT_EQ((a != b).get_origin_tag(), first_two_merged_tag);
1392
1393 // Conditionals merge tags
1394
1395 auto k = bool_ct(witness_ct(&builder, 1));
1396 k.set_origin_tag(next_challenge_tag);
1397 auto l = a.conditional_negate(k);
1398 EXPECT_EQ(l.get_origin_tag(), first_and_third_merged_tag);
1399
1401 EXPECT_EQ(m.get_origin_tag(), first_second_third_merged_tag);
1402
1403 // Accumulate merges tags
1404 const size_t MAX_ACCUMULATED_ELEMENTS = 16;
1405 std::vector<field_ct> elements;
1406 std::vector<OriginTag> accumulated_tags;
1407 for (size_t index = 0; index < MAX_ACCUMULATED_ELEMENTS; index++) {
1408 const auto current_tag = OriginTag(parent_id, index >> 1, !(index & 1));
1409 if (index == 0) {
1410 accumulated_tags.push_back(current_tag);
1411 } else {
1412 accumulated_tags.emplace_back(accumulated_tags[index - 1], current_tag);
1413 }
1415 element.set_origin_tag(current_tag);
1416 elements.emplace_back(element);
1417 }
1418
1419 for (size_t index = MAX_ACCUMULATED_ELEMENTS - 1; index > 0; index--) {
1420 EXPECT_EQ(field_ct::accumulate(elements).get_origin_tag(), accumulated_tags[index]);
1421 elements.pop_back();
1422 }
1423
1424 // Split preserves tags
1425 const size_t num_bits = uint256_t(a.get_value()).get_msb() + 1;
1426 auto split_data = a.no_wrap_split_at(num_bits / 2, num_bits);
1427 EXPECT_EQ(split_data.first.get_origin_tag(), submitted_value_origin_tag);
1428 EXPECT_EQ(split_data.second.get_origin_tag(), submitted_value_origin_tag);
1429
1430 // Conversions
1431
1432 auto o = field_ct(witness_ct(&builder, 1));
1433 o.set_origin_tag(submitted_value_origin_tag);
1434 auto p = bool_ct(o);
1435 EXPECT_EQ(p.get_origin_tag(), submitted_value_origin_tag);
1436
1437 o.set_origin_tag(challenge_origin_tag);
1438 o = field_ct(p);
1439
1440 EXPECT_EQ(o.get_origin_tag(), submitted_value_origin_tag);
1441
1443 auto poisoned_tag = challenge_origin_tag;
1444 poisoned_tag.poison();
1445 q.set_origin_tag(poisoned_tag);
1446#ifndef NDEBUG
1447 EXPECT_THROW(q + q, std::runtime_error);
1448#endif
1449 }
1450
1452 {
1454
1455 Builder builder1;
1456 Builder builder2;
1457
1458 auto null = static_cast<Builder*>(nullptr);
1459
1460 // Case 1: All nullptr
1461 {
1462 Builder* result = validate_context(null, null, null);
1463 EXPECT_EQ(result, nullptr);
1464 }
1465
1466 // Case 2: One non-nullptr
1467 {
1468 Builder* result = validate_context(&builder1);
1469 EXPECT_EQ(result, &builder1);
1470 }
1471
1472 // Case 3: Leading nullptrs
1473 {
1474 Builder* result = validate_context(null, null, &builder1);
1475 EXPECT_EQ(result, &builder1);
1476 }
1477
1478 // Case 4: One non-null followed by nullptrs
1479 {
1480 Builder* result = validate_context(&builder1, null, null);
1481 EXPECT_EQ(result, &builder1);
1482 }
1483
1484 // Case 5: All same non-nullptr
1485 {
1486 Builder* result = validate_context(&builder1, &builder1, &builder1);
1487 EXPECT_EQ(result, &builder1);
1488 }
1489
1490 // Case 6: Conflict between two different non-nullptrs
1491 {
1492 EXPECT_THROW_OR_ABORT(validate_context(&builder1, &builder2),
1493 "Pointers refer to different builder objects!");
1494 }
1495
1496 // Case 7: Conflict between first and last non-null
1497 {
1498 EXPECT_THROW_OR_ABORT(validate_context(&builder1, null, null, &builder2),
1499 "Pointers refer to different builder objects!");
1500 }
1501
1502 // Case 8: First null, two same non-null later
1503 {
1504 Builder* result = validate_context(null, &builder1, &builder1);
1505 EXPECT_EQ(result, &builder1);
1506 }
1507
1508 // Case 9: Interleaved nulls and same pointer
1509 {
1510 Builder* result = validate_context(&builder1, null, &builder1, null);
1511 EXPECT_EQ(result, &builder1);
1512 }
1513 }
1514
1516 {
1517 // Case 1: Empty container returns nullptr
1518 {
1520 Builder* ctx = validate_context<Builder>(empty);
1521 EXPECT_EQ(ctx, nullptr);
1522 }
1523
1524 // Case 2: Same context
1525 {
1527 std::vector<field_ct> fields = {
1528 field_ct(&builder, 1),
1529 field_ct(&builder, 2),
1530 field_ct(&builder, 3),
1531 };
1532 Builder* ctx = validate_context<Builder>(fields);
1533 EXPECT_EQ(ctx, &builder);
1534 }
1535
1536 // Case 3: Some nullptr contexts
1537 {
1539 field_ct null_field; // context is nullptr
1540 field_ct a(&builder, 1);
1541 field_ct b(&builder, 2);
1542 std::vector<field_ct> fields = { null_field, a, b };
1543 Builder* ctx = validate_context<Builder>(fields);
1544 EXPECT_EQ(ctx, &builder);
1545 }
1546
1547 // Case 4: Mismatched contexts should throw/abort
1548 {
1549 Builder builder1;
1550 Builder builder2;
1551 std::vector<field_ct> fields = {
1552 field_ct(&builder1, 1),
1553 field_ct(&builder1, 1),
1554 field_ct(1),
1555 field_ct(&builder2, 2),
1556 };
1557
1558 EXPECT_THROW_OR_ABORT(validate_context<Builder>(fields), "Pointers refer to different builder objects!");
1559 }
1560 }
1561};
1562using CircuitTypes = testing::Types<bb::UltraCircuitBuilder>;
1563
1565
1566TYPED_TEST(stdlib_field, test_accumulate)
1567{
1568 TestFixture::test_accumulate();
1569}
1571{
1572 TestFixture::test_add();
1573}
1574TYPED_TEST(stdlib_field, test_add_mul_with_constants)
1575{
1576 TestFixture::test_add_mul_with_constants();
1577}
1579{
1580 TestFixture::test_add_two();
1581}
1582TYPED_TEST(stdlib_field, test_assert_equal)
1583{
1584 TestFixture::test_assert_equal();
1585}
1586TYPED_TEST(stdlib_field, test_assert_equal_gate_count)
1587{
1588 TestFixture::test_assert_equal_with_gate_count();
1589}
1590TYPED_TEST(stdlib_field, test_assert_is_in_set)
1591{
1592 TestFixture::test_assert_is_in_set();
1593}
1594TYPED_TEST(stdlib_field, test_assert_is_in_set_fails)
1595{
1596 TestFixture::test_assert_is_in_set_fails();
1597}
1598TYPED_TEST(stdlib_field, test_assert_is_zero)
1599{
1600 TestFixture::test_assert_is_zero();
1601}
1602TYPED_TEST(stdlib_field, test_assert_is_not_zero)
1603{
1604 TestFixture::test_assert_is_not_zero();
1605}
1606TYPED_TEST(stdlib_field, test_bool_conversion)
1607{
1608 TestFixture::test_bool_conversion();
1609}
1610TYPED_TEST(stdlib_field, test_bool_conversion_regression)
1611{
1612 TestFixture::test_bool_conversion_regression();
1613}
1614TYPED_TEST(stdlib_field, test_conditional_assign)
1615{
1616 TestFixture::test_conditional_assign();
1617}
1618TYPED_TEST(stdlib_field, test_conditional_assign_regression)
1619{
1620 TestFixture::test_conditional_assign_regression();
1621}
1622TYPED_TEST(stdlib_field, test_conditional_negate)
1623{
1624 TestFixture::test_conditional_negate();
1625}
1626TYPED_TEST(stdlib_field, test_constructor_from_witness)
1627{
1628 TestFixture::test_constructor_from_witness();
1629}
1630TYPED_TEST(stdlib_field, test_copy_as_new_witness)
1631{
1632 TestFixture::test_copy_as_new_witness();
1633}
1634TYPED_TEST(stdlib_field, test_create_range_constraint)
1635{
1636 TestFixture::create_range_constraint();
1637}
1639{
1640 TestFixture::test_div();
1641}
1642TYPED_TEST(stdlib_field, test_div_edge_cases)
1643{
1644 TestFixture::test_div_edge_cases();
1645}
1647{
1648 TestFixture::test_equality();
1649}
1650TYPED_TEST(stdlib_field, test_equality_false)
1651{
1652 TestFixture::test_equality_false();
1653}
1654TYPED_TEST(stdlib_field, test_equality_with_constants)
1655{
1656 TestFixture::test_equality_with_constants();
1657}
1658TYPED_TEST(stdlib_field, test_field_fibbonaci)
1659{
1660 TestFixture::test_field_fibbonaci();
1661}
1662TYPED_TEST(stdlib_field, test_field_pythagorean)
1663{
1664 TestFixture::test_field_pythagorean();
1665}
1666TYPED_TEST(stdlib_field, test_fix_witness)
1667{
1668 TestFixture::test_fix_witness();
1669}
1671{
1672 TestFixture::test_invert();
1673}
1674TYPED_TEST(stdlib_field, test_invert_zero)
1675{
1676 TestFixture::test_invert_zero();
1677}
1679{
1680 TestFixture::test_is_zero();
1681}
1682TYPED_TEST(stdlib_field, test_larger_circuit)
1683{
1684 TestFixture::test_larger_circuit();
1685}
1687{
1688 TestFixture::test_madd();
1689}
1690TYPED_TEST(stdlib_field, test_madd_add_two_gate_count)
1691{
1692 TestFixture::test_madd_add_two_gate_count();
1693}
1694TYPED_TEST(stdlib_field, test_multiplicative_constant_regression)
1695{
1696 TestFixture::test_multiplicative_constant_regression();
1697}
1698TYPED_TEST(stdlib_field, test_origin_tag_consistency)
1699{
1700 TestFixture::test_origin_tag_consistency();
1701}
1702TYPED_TEST(stdlib_field, test_postfix_increment)
1703{
1704 TestFixture::test_postfix_increment();
1705}
1707{
1708 TestFixture::test_pow();
1709}
1710TYPED_TEST(stdlib_field, test_pow_exponent_out_of_range)
1711{
1712 TestFixture::test_pow_exponent_out_of_range();
1713}
1714TYPED_TEST(stdlib_field, test_prefix_increment)
1715{
1716 TestFixture::test_prefix_increment();
1717}
1718TYPED_TEST(stdlib_field, test_ranged_less_than)
1719{
1720 TestFixture::test_ranged_less_than();
1721}
1722TYPED_TEST(stdlib_field, test_ranged_less_than_max_num_bits)
1723{
1724 TestFixture::test_ranged_less_than_max_num_bits();
1725}
1727{
1728 TestFixture::test_split_at();
1729}
1730TYPED_TEST(stdlib_field, test_three_bit_table)
1731{
1732 TestFixture::test_three_bit_table();
1733}
1734TYPED_TEST(stdlib_field, test_two_bit_table)
1735{
1736 TestFixture::test_two_bit_table();
1737}
1738TYPED_TEST(stdlib_field, test_validate_context)
1739{
1740 TestFixture::test_validate_context();
1741}
1742TYPED_TEST(stdlib_field, test_validate_container_context)
1743{
1744 TestFixture::test_validate_container_context();
1745}
#define EXPECT_THROW_OR_ABORT(statement, matcher)
Definition assert.hpp:174
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
virtual uint8_t get_random_uint8()=0
virtual uint32_t get_random_uint32()=0
virtual uint256_t get_random_uint256()=0
constexpr uint64_t get_msb() const
Implements boolean logic in-circuit.
Definition bool.hpp:59
bool get_value() const
Definition bool.hpp:124
bool is_constant() const
Definition bool.hpp:126
void assert_is_zero(std::string const &msg="field_t::assert_is_zero") const
Enforce a copy constraint between *this and 0 stored at zero_idx of the Builder.
Definition field.cpp:679
field_t conditional_negate(const bool_t< Builder > &predicate) const
If predicate's value == true, negate the value, else keep it unchanged.
Definition field.cpp:859
void assert_is_in_set(const std::vector< field_t > &set, std::string const &msg="field_t::assert_not_in_set") const
Constrain *this \in set by enforcing that P(X) = \prod_{s \in set} (X - s) is 0 at X = *this.
Definition field.cpp:985
uint32_t set_public() const
Definition field.hpp:434
void assert_equal(const field_t &rhs, std::string const &msg="field_t::assert_equal") const
Copy constraint: constrain that *this field is equal to rhs element.
Definition field.cpp:930
field_t madd(const field_t &to_mul, const field_t &to_add) const
Definition field.cpp:510
bb::fr additive_constant
Definition field.hpp:93
static field_t select_from_three_bit_table(const std::array< field_t, 8 > &table, const bool_t< Builder > &t2, const bool_t< Builder > &t1, const bool_t< Builder > &t0)
Given a multilinear polynomial in 3 variables, which is represented by a table of monomial coefficien...
Definition field.cpp:1069
static field_t accumulate(const std::vector< field_t > &input)
Efficiently compute the sum of vector entries. Using big_add_gate we reduce the number of gates neede...
Definition field.cpp:1167
static bool witness_indices_match(const field_t &a, const field_t &b)
Check if two field elements have the same witness index (for identity checks).
Definition field.hpp:519
static std::array< field_t, 8 > preprocess_three_bit_table(const field_t &T0, const field_t &T1, const field_t &T2, const field_t &T3, const field_t &T4, const field_t &T5, const field_t &T6, const field_t &T7)
Given a table T of size 8, outputs the monomial coefficients of the multilinear polynomial in t0,...
Definition field.cpp:1021
bb::fr multiplicative_constant
Definition field.hpp:94
static field_t copy_as_new_witness(Builder &context, field_t const &other)
Definition field.hpp:254
static field_t conditional_assign_internal(const bool_t< Builder > &predicate, const field_t &lhs, const field_t &rhs)
If predicate == true then return lhs, else return rhs.
Definition field.cpp:885
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:828
field_t normalize() const
Return a new element, where the in-circuit witness contains the actual represented value (multiplicat...
Definition field.cpp:638
static field_t select_from_two_bit_table(const std::array< field_t, 4 > &table, const bool_t< Builder > &t1, const bool_t< Builder > &t0)
Given a multilinear polynomial in 2 variables, which is represented by a table of monomial coefficien...
Definition field.cpp:1047
static field_t from_witness(Builder *ctx, const bb::fr &input)
Definition field.hpp:454
bool_t< Builder > is_zero() const
Validate whether a field_t element is zero.
Definition field.cpp:775
field_t pow(const uint32_t &exponent) const
Raise this field element to the power of the provided uint32_t exponent.
Definition field.cpp:422
bool is_constant() const
Definition field.hpp:429
static std::array< field_t, 4 > preprocess_two_bit_table(const field_t &T0, const field_t &T1, const field_t &T2, const field_t &T3)
Given a table T of size 4, outputs the monomial coefficients of the multilinear polynomial in t0,...
Definition field.cpp:1003
field_t add_two(const field_t &add_b, const field_t &add_c) const
Efficiently compute (this + a + b) using big_mul gate.
Definition field.cpp:575
uint32_t get_witness_index() const
Get the witness index of the current field element.
Definition field.hpp:506
static void test_bool_conversion()
static void test_conditional_assign_regression()
Test that conditional assign doesn't produce a new witness if lhs and rhs are constant.
void test_validate_container_context()
static void test_fix_witness()
static void test_div()
static void test_assert_is_zero()
static void test_conditional_assign()
static uint64_t fidget(Builder &builder)
static void test_div_edge_cases()
static void create_range_constraint()
static void test_prefix_increment()
static void test_equality()
static void test_invert()
stdlib::public_witness_t< Builder > public_witness_ct
static void test_split_at()
static void test_equality_false()
static void build_test_circuit(Builder &builder, size_t num_gates)
static void test_ranged_less_than_max_num_bits()
static void test_larger_circuit()
static void test_accumulate()
static void test_field_fibbonaci()
void test_validate_context()
static void test_copy_as_new_witness()
static void test_is_zero()
stdlib::witness_t< Builder > witness_ct
static void test_assert_equal()
Demonstrate current behavior of assert_equal.
stdlib::bool_t< Builder > bool_ct
static void test_invert_zero()
void test_assert_equal_with_gate_count()
static void test_constructor_from_witness()
static void test_assert_is_in_set()
static void test_pow()
static void test_conditional_negate()
static void test_origin_tag_consistency()
static void test_add()
static void test_add_two()
static void test_add_mul_with_constants()
static void test_multiplicative_constant_regression()
Test that multiplicative_constant of constants is no longer affected by any arithimetic operation.
static void test_assert_is_in_set_fails()
static void test_equality_with_constants()
static void test_three_bit_table()
static void test_madd()
static void test_two_bit_table()
static void test_field_pythagorean()
static void test_ranged_less_than()
static void test_postfix_increment()
static void test_assert_is_not_zero()
static void test_bool_conversion_regression()
Test that bool is converted correctly.
stdlib::field_t< Builder > field_ct
static void test_madd_add_two_gate_count()
static void test_pow_exponent_out_of_range()
void info(Args... args)
Definition log.hpp:75
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
bool expected_result
ECCVMCircuitBuilder Builder
numeric::RNG & engine
void ignore_unused(T &)
constexpr size_t MAX_NO_WRAP_INTEGER_BIT_LENGTH
Definition grumpkin.hpp:15
constexpr T get_msb(const T in)
Definition get_msb.hpp:47
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:190
T * validate_context(T *ptr)
Definition field.hpp:16
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TYPED_TEST_SUITE(ShpleminiTest, TestSettings)
field< Bn254FrParams > fr
Definition fr.hpp:174
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::MegaCircuitBuilder, bb::UltraCircuitBuilder > CircuitTypes
static constexpr field neg_one()
static constexpr field one()
static constexpr uint256_t modulus
BB_INLINE constexpr field pow(const uint256_t &exponent) const noexcept
constexpr field invert() const noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr bool is_zero() const noexcept
static constexpr field zero()