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()); // 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 // -1/2 has "second maximal" bit length
146 run_test(-1 / 2, fr::modulus.get_msb(), true);
147 }
148
150 {
151 // Test the conversion from field_t to bool_t.
152
153 std::array<bb::fr, 5> input_array{ 0, 1, 0, 1, bb::fr::random_element() };
154 // Cases 0,1: Constant 0, 1
155 // Cases 2,3: Witnesses 0, 1
156 for (size_t idx = 0; idx < 4; idx++) {
157 bool expected_to_be_constant = (idx < 2);
159 field_ct field_elt = (expected_to_be_constant) ? field_ct(input_array[idx])
160 : field_ct(witness_ct(&builder, input_array[idx]));
161 bool_ct converted(field_elt);
162 EXPECT_TRUE(converted.is_constant() == expected_to_be_constant);
163 EXPECT_TRUE(field_elt.get_value() == converted.get_value());
164
165 if (!expected_to_be_constant) {
166 EXPECT_TRUE(CircuitChecker::check(builder));
167 EXPECT_TRUE(field_ct::witness_indices_match(converted, field_elt));
168 }
169 }
170 // Check that the conversion aborts in the case of random field elements.
171 bool_ct invalid_bool;
172 // Case 4: Invalid constant conversion
173 EXPECT_THROW_WITH_MESSAGE(invalid_bool = bool_ct(field_ct(input_array.back())),
174 "Attempting to create a bool_t from a witness_t not satisfying x\\^2 - x = 0");
175 // Case 5: Invalid witness conversion
177 EXPECT_THROW_WITH_MESSAGE(invalid_bool = bool_ct(field_ct(witness_ct(&builder, input_array.back()))),
178 "Attempting to create a bool_t from a witness_t not satisfying x\\^2 - x = 0");
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_WITH_MESSAGE(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(a / b, std::runtime_error);
487 }
488 { // Case 2. Numerator != const, denominator = const 0. Check that the division is aborted
491 b = 0;
492 EXPECT_THROW(a / b, std::runtime_error);
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_WITH_MESSAGE(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_WITH_MESSAGE(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(uint32_t max_exponent_bits)
1049 {
1051
1052 std::array<uint32_t, 3> const_exponent_values{ 0, 1, engine.get_random_uint32() % (1 << max_exponent_bits) };
1053 std::array<field_ct, 3> witness_exponent_values{
1054 witness_ct(&builder, 0),
1055 witness_ct(&builder, 1),
1056 witness_ct(&builder, engine.get_random_uint32() % (1 << max_exponent_bits))
1057 };
1058
1059 std::array<uint256_t, 3> base_values{ 0, 1, engine.get_random_uint256() };
1060 for (auto& base : base_values) {
1061 for (auto& exponent : const_exponent_values) {
1062 // Test constant base && integer exponent cases
1063 field_ct result = field_ct(base).pow(exponent);
1064 EXPECT_TRUE(result.is_constant());
1065 EXPECT_EQ(result.get_value(), bb::fr(base).pow(exponent));
1066 // Test witness base && integer exponent cases
1067 field_ct witness_base(witness_ct(&builder, base));
1068 if (max_exponent_bits == 32) {
1069 result = witness_base.pow(exponent);
1070 } else if (max_exponent_bits == CONST_OP_QUEUE_LOG_SIZE + 1) {
1071 result = witness_base.template pow<CONST_OP_QUEUE_LOG_SIZE + 1>(exponent);
1072 } else {
1073 bb::assert_failure("Invalid max_exponent_bits value in test_pow");
1074 }
1075
1076 if (exponent != 0) {
1077 EXPECT_TRUE(!result.is_constant());
1078 } else {
1079 EXPECT_TRUE(result.is_constant());
1080 }
1081
1082 EXPECT_EQ(result.get_value(), bb::fr(base).pow(exponent));
1083
1084 EXPECT_TRUE(CircuitChecker::check(builder));
1085 }
1086 for (auto& exponent : witness_exponent_values) {
1087
1088 // Test constant base && witness exponent cases
1089 field_ct result = field_ct(base).pow(exponent);
1090 // Normalized witness == 1 leads to constant results in `conditional_assign(predicate, 1, 1)`
1091 EXPECT_EQ(result.is_constant(), base == 1);
1092
1093 EXPECT_EQ(result.get_value(), bb::fr(base).pow(exponent.get_value()));
1094 // Test witness base && witness exponent cases
1095 field_ct witness_base(witness_ct(&builder, base));
1096 if (max_exponent_bits == 32) {
1097 result = witness_base.pow(exponent);
1098 } else if (max_exponent_bits == CONST_OP_QUEUE_LOG_SIZE + 1) {
1099 result = witness_base.template pow<CONST_OP_QUEUE_LOG_SIZE + 1>(exponent);
1100 } else {
1101 bb::assert_failure("Invalid max_exponent_bits value in test_pow");
1102 }
1103
1104 EXPECT_TRUE(!result.is_constant());
1105 EXPECT_EQ(result.get_value(), bb::fr(base).pow(exponent.get_value()));
1106
1107 EXPECT_TRUE(CircuitChecker::check(builder));
1108 }
1109 }
1110 }
1111
1112 static void test_pow_witness_exponent_out_of_range(uint32_t max_exponent_bits)
1113 {
1114
1115 fr base_val(engine.get_random_uint256());
1116 uint64_t exponent_val = engine.get_random_uint32() % (uint64_t(1) << max_exponent_bits);
1117 exponent_val += (uint64_t(1) << max_exponent_bits);
1118
1120
1121 [[maybe_unused]] field_ct base = witness_ct(&builder, base_val);
1122 field_ct exponent = witness_ct(&builder, exponent_val);
1123 field_ct result;
1124 if (max_exponent_bits == 32) {
1125 result = base.pow(exponent);
1126 } else if (max_exponent_bits == CONST_OP_QUEUE_LOG_SIZE + 1) {
1127 result = base.template pow<CONST_OP_QUEUE_LOG_SIZE + 1>(exponent);
1128 } else {
1129 bb::assert_failure("Invalid max_exponent_bits value in test_pow");
1130 }
1131
1132 EXPECT_FALSE(CircuitChecker::check(builder));
1133 EXPECT_TRUE(builder.failed());
1134 EXPECT_EQ(builder.err(), "field_t::pow exponent accumulator incorrect");
1135 }
1136
1137 static void test_pow_constant_exponent_out_of_range(uint32_t max_exponent_bits)
1138
1139 {
1140 fr base_val(engine.get_random_uint256());
1141 uint64_t exponent_val = engine.get_random_uint32() % (uint64_t(1) << max_exponent_bits);
1142 exponent_val += (uint64_t(1) << max_exponent_bits);
1143
1145
1146 [[maybe_unused]] field_ct base = witness_ct(&builder, base_val);
1147 field_ct exponent = field_ct(exponent_val);
1148 if (max_exponent_bits == 32) {
1149 EXPECT_THROW_WITH_MESSAGE(base.pow(exponent), "Exponent too large in field_t::pow");
1150 } else if (max_exponent_bits == CONST_OP_QUEUE_LOG_SIZE + 1) {
1151 EXPECT_THROW_WITH_MESSAGE(base.template pow<CONST_OP_QUEUE_LOG_SIZE + 1>(exponent),
1152 "Exponent too large in field_t::pow");
1153 } else {
1154 bb::assert_failure("Invalid max_exponent_bits value in test_pow");
1155 }
1156 }
1157
1159 {
1161
1163 field_ct value_ct = witness_ct(&builder, value);
1164
1165 field_ct first_copy = witness_ct(&builder, value_ct.get_value());
1166 field_ct second_copy = field_ct::copy_as_new_witness(builder, value_ct);
1167
1168 EXPECT_EQ(value_ct.get_value(), value);
1169 EXPECT_EQ(first_copy.get_value(), value);
1170 EXPECT_EQ(second_copy.get_value(), value);
1171
1172 EXPECT_EQ(value_ct.get_witness_index() + 1, first_copy.get_witness_index());
1173 EXPECT_EQ(value_ct.get_witness_index() + 2, second_copy.get_witness_index());
1174 info("num gates = ", builder.get_num_finalized_gates_inefficient());
1175
1176 bool result = CircuitChecker::check(builder);
1177 EXPECT_EQ(result, true);
1178 }
1179
1181 {
1182 // Create a constant 0, `assert_is_zero()` does nothing in-circuit in this case
1183 field_ct elt = bb::fr::zero();
1184 elt.assert_is_zero();
1185 // If we apply `assert_is_zero()` to a non-zero constant, we hit an ASSERT failure
1186 elt = bb::fr::random_element();
1187
1188 if (elt.get_value() != 0) {
1189 EXPECT_THROW_WITH_MESSAGE(elt.assert_is_zero(), "field_t::assert_is_zero");
1190 }
1191 // Create a witness 0
1193 elt = witness_ct(&builder, bb::fr::zero());
1194 elt.assert_is_zero();
1195 // The circuit must be correct
1196 EXPECT_TRUE(CircuitChecker::check(builder));
1197
1198 // If we apply `assert_is_zero()` to a non-zero witness, an unsatisfiable `poly_gate` constraint is created
1200 if (non_zero_elt.get_value() != 0) {
1201 non_zero_elt.assert_is_zero();
1202 EXPECT_FALSE(CircuitChecker::check(builder));
1203 }
1204 }
1205
1206 static void test_accumulate()
1207 {
1208 for (size_t max_vector_length = 1; max_vector_length < 100; max_vector_length++) {
1210 std::vector<bb::fr> native_input(max_vector_length, 0);
1211 for (auto& entry : native_input) {
1212 entry = bb::fr::random_element();
1213 }
1214
1215 // Compute the native sum
1216 bb::fr native_sum = std::accumulate(native_input.begin(), native_input.end(), bb::fr::zero());
1217 std::vector<field_ct> input(max_vector_length);
1218 size_t idx = 0;
1219 // Convert native vector to a vector of field_t elements. Every 5th element is set to be constant.
1220 for (auto& native_entry : native_input) {
1221 field_ct entry = ((idx % 5) == 0) ? field_ct(native_entry) : witness_ct(&builder, native_entry);
1222 input.emplace_back(entry);
1223 idx++;
1224 }
1225 // Compute the accumulation result
1227 EXPECT_EQ(native_sum, sum.get_value());
1228
1229 // Check that the result is normalized
1230 if (!sum.is_constant()) {
1231 EXPECT_TRUE(sum.multiplicative_constant == 1 && sum.additive_constant == 0);
1232 }
1233
1234 EXPECT_TRUE(CircuitChecker::check(builder));
1235
1236 // Check that the number of gates is as expected
1237 size_t num_witnesses = max_vector_length - (max_vector_length + 4) / 5;
1238 size_t padding = (3 - (num_witnesses % 3)) % 3;
1239 size_t expected_num_gates = (num_witnesses + padding) / 3;
1240
1241 EXPECT_EQ(builder.get_num_finalized_gates_inefficient() - 1, expected_num_gates);
1242
1243 // Check that the accumulation of constant entries does not create a witness
1244 std::vector<field_ct> constant_input;
1245 for (auto& entry : input) {
1246 if (entry.is_constant()) {
1247 constant_input.emplace_back(entry);
1248 }
1249 }
1250 field_ct constant_sum = field_ct::accumulate(constant_input);
1251 EXPECT_TRUE(constant_sum.is_constant());
1252 }
1253 // Test edge cases
1254 // 1. Accumulating an empty vector should lead to constant zero.
1255 std::vector<field_ct> empty_input;
1256 field_ct result(field_ct::accumulate(empty_input));
1257 EXPECT_TRUE(result.is_constant() && result.get_value() == bb::fr::zero());
1258 // 2. Check that the result of accumulating a single witness summand is correct and normalized.
1260 field_ct single_summand = witness_ct(&builder, bb::fr::random_element());
1261 single_summand += field_ct(bb::fr(3));
1262 single_summand *= field_ct(bb::fr(2));
1263 // `single_summand` isn't normalized anymore
1264 EXPECT_TRUE(single_summand.additive_constant != 0 && single_summand.multiplicative_constant != 1);
1265 std::vector<field_ct> single_element_input{ single_summand };
1266 // The accumulation result is expected to be normalized
1267 result = field_ct::accumulate(single_element_input);
1268 EXPECT_TRUE(result.get_value() == single_summand.get_value() && result.additive_constant == 0 &&
1269 result.multiplicative_constant == 1);
1270 }
1271
1272 static void test_fix_witness()
1273 {
1275
1277 witness.fix_witness();
1278 // Validate that the negated value of the witness is recorded in q_c.
1279 EXPECT_TRUE(builder.blocks.arithmetic.q_c().back() == -witness.get_value());
1280 }
1281
1283 {
1285
1286 for (size_t i = 0; i < 10; ++i) {
1287 int a_val = static_cast<int>(engine.get_random_uint8());
1288 int b_val = 0;
1289 switch (i) {
1290 case 0: {
1291 b_val = a_val;
1292 break;
1293 }
1294 case 1: {
1295 b_val = a_val + 1;
1296 break;
1297 }
1298 case 2: {
1299 b_val = a_val - 1;
1300 break;
1301 }
1302 default: {
1303 b_val = static_cast<int>(engine.get_random_uint8());
1304 break;
1305 }
1306 }
1307 if (b_val < 0) {
1308 b_val = 255;
1309 }
1310 if (b_val > 255) {
1311 b_val = 0;
1312 }
1313 field_ct a = witness_ct(&builder, static_cast<uint64_t>(a_val));
1314 field_ct b = witness_ct(&builder, static_cast<uint64_t>(b_val));
1315 a.create_range_constraint(8);
1316 b.create_range_constraint(8);
1317 bool_ct result = a.template ranged_less_than<8>(b);
1318 bool expected = a_val < b_val;
1319
1320 EXPECT_EQ(result.get_value(), expected);
1321 }
1322 bool check_result = CircuitChecker::check(builder);
1323 EXPECT_EQ(check_result, true);
1324 }
1325
1327 {
1329
1332
1333 constexpr uint256_t modulus = bb::fr::modulus;
1334 constexpr size_t max_valid_num_bits = modulus.get_msb() - 1;
1335
1336 // ---------- VALID CASE ----------
1337 {
1338 constexpr size_t num_bits = max_valid_num_bits;
1339 EXPECT_NO_THROW({
1340 auto result = a.template ranged_less_than<num_bits>(b);
1341 EXPECT_EQ(result.get_value(), true);
1342 });
1343 }
1344 }
1345
1346 static void test_add_two()
1347 {
1349 auto x_1 = bb::fr::random_element();
1350 auto x_2 = bb::fr::random_element();
1351 auto x_3 = bb::fr::random_element();
1352
1353 field_ct x_1_ct = witness_ct(&builder, x_1);
1354 field_ct x_2_ct = witness_ct(&builder, x_2);
1355 field_ct x_3_ct = witness_ct(&builder, x_3);
1356
1357 auto sum_ct = x_1_ct.add_two(x_2_ct, x_3_ct);
1358
1359 EXPECT_EQ(sum_ct.get_value(), x_1 + x_2 + x_3);
1360
1361 bool circuit_checks = CircuitChecker::check(builder);
1362 EXPECT_TRUE(circuit_checks);
1363 }
1365 {
1367 // Randomly generate a and b (a must ≤ 252 bits)
1368 uint256_t a_val =
1370 auto a = field_ct(witness_ct(&builder, a_val));
1372 EXPECT_TRUE(a.get_origin_tag().is_free_witness());
1373 EXPECT_TRUE(b.get_origin_tag().is_free_witness());
1374 const size_t parent_id = 0;
1375
1376 const auto submitted_value_origin_tag = OriginTag(parent_id, /*round_id=*/0, /*is_submitted=*/true);
1377 const auto challenge_origin_tag = OriginTag(parent_id, /*round_id=*/0, /*is_submitted=*/false);
1378 const auto next_challenge_tag = OriginTag(parent_id, /*round_id=*/1, /*submitted=*/false);
1379
1380 const auto first_two_merged_tag = OriginTag(submitted_value_origin_tag, challenge_origin_tag);
1381 const auto first_and_third_merged_tag = OriginTag(submitted_value_origin_tag, next_challenge_tag);
1382 const auto first_second_third_merged_tag = OriginTag(first_two_merged_tag, next_challenge_tag);
1383
1384 a.set_origin_tag(submitted_value_origin_tag);
1385 b.set_origin_tag(challenge_origin_tag);
1386
1387 EXPECT_EQ(a.get_origin_tag(), submitted_value_origin_tag);
1388 EXPECT_EQ(b.get_origin_tag(), challenge_origin_tag);
1389
1390 // Basic additon merges tags
1391 auto c = a + b;
1392 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
1393
1394 // Basic multiplication merges tags
1395 auto d = a * b;
1396 EXPECT_EQ(d.get_origin_tag(), first_two_merged_tag);
1397
1398 // Basic subtraction merges tags
1399 auto e = a - b;
1400 EXPECT_EQ(e.get_origin_tag(), first_two_merged_tag);
1401
1402 // Division merges tags
1403
1404 auto f = a / b;
1405 EXPECT_EQ(f.get_origin_tag(), first_two_merged_tag);
1406
1407 // Exponentiation merges tags
1408
1409 auto exponent = field_ct(witness_ct(&builder, 10));
1410 exponent.set_origin_tag(challenge_origin_tag);
1411 auto g = a.pow(exponent);
1412 EXPECT_EQ(g.get_origin_tag(), first_two_merged_tag);
1413
1414 // Madd merges tags
1416 h.set_origin_tag(next_challenge_tag);
1417 auto i = a.madd(b, h);
1418 EXPECT_EQ(i.get_origin_tag(), first_second_third_merged_tag);
1419
1420 // add_two merges tags
1421 auto j = a.add_two(b, h);
1422 EXPECT_EQ(j.get_origin_tag(), first_second_third_merged_tag);
1423
1424 // Normalize preserves tag
1425
1426 EXPECT_EQ(j.normalize().get_origin_tag(), j.get_origin_tag());
1427
1428 // is_zero preserves tag
1429
1430 EXPECT_EQ(a.is_zero().get_origin_tag(), a.get_origin_tag());
1431
1432 // equals/not equals operator merges tags
1433
1434 EXPECT_EQ((a == b).get_origin_tag(), first_two_merged_tag);
1435 EXPECT_EQ((a != b).get_origin_tag(), first_two_merged_tag);
1436
1437 // Conditionals merge tags
1438
1439 auto k = bool_ct(witness_ct(&builder, 1));
1440 k.set_origin_tag(next_challenge_tag);
1441 auto l = a.conditional_negate(k);
1442 EXPECT_EQ(l.get_origin_tag(), first_and_third_merged_tag);
1443
1445 EXPECT_EQ(m.get_origin_tag(), first_second_third_merged_tag);
1446
1447 // Accumulate merges tags (smoke test - detailed tag logic tested in origin_tag tests)
1448 std::vector<field_ct> acc_elements;
1451 acc_a.set_origin_tag(submitted_value_origin_tag);
1452 acc_b.set_origin_tag(challenge_origin_tag);
1453 acc_elements.push_back(acc_a);
1454 acc_elements.push_back(acc_b);
1455 EXPECT_EQ(field_ct::accumulate(acc_elements).get_origin_tag(), first_two_merged_tag);
1456
1457 // Split preserves tags
1458 const size_t num_bits = uint256_t(a.get_value()).get_msb() + 1;
1459 auto split_data = a.no_wrap_split_at(num_bits / 2, num_bits);
1460 EXPECT_EQ(split_data.first.get_origin_tag(), submitted_value_origin_tag);
1461 EXPECT_EQ(split_data.second.get_origin_tag(), submitted_value_origin_tag);
1462
1463 // Conversions
1464
1465 auto o = field_ct(witness_ct(&builder, 1));
1466 o.set_origin_tag(submitted_value_origin_tag);
1467 auto p = bool_ct(o);
1468 EXPECT_EQ(p.get_origin_tag(), submitted_value_origin_tag);
1469
1470 o.set_origin_tag(challenge_origin_tag);
1471 o = field_ct(p);
1472
1473 EXPECT_EQ(o.get_origin_tag(), submitted_value_origin_tag);
1474
1476 auto poisoned_tag = challenge_origin_tag;
1477 poisoned_tag.poison();
1478 q.set_origin_tag(poisoned_tag);
1479#ifndef NDEBUG
1480 EXPECT_THROW(q + q, std::runtime_error);
1481#endif
1482
1483 // ranged_less_than: check tag behavior
1484 auto rlt_a = field_ct(witness_ct(&builder, uint256_t(50)));
1485 auto rlt_b = field_ct(witness_ct(&builder, uint256_t(100)));
1486 rlt_a.set_origin_tag(submitted_value_origin_tag);
1487 rlt_b.set_origin_tag(challenge_origin_tag);
1488 auto rlt_result = rlt_a.template ranged_less_than<8>(rlt_b);
1489 EXPECT_EQ(rlt_result.get_origin_tag(), first_two_merged_tag);
1490 }
1491
1493 {
1495
1496 Builder builder1;
1497 Builder builder2;
1498
1499 auto null = static_cast<Builder*>(nullptr);
1500
1501 // Case 1: All nullptr
1502 {
1503 Builder* result = validate_context(null, null, null);
1504 EXPECT_EQ(result, nullptr);
1505 }
1506
1507 // Case 2: One non-nullptr
1508 {
1509 Builder* result = validate_context(&builder1);
1510 EXPECT_EQ(result, &builder1);
1511 }
1512
1513 // Case 3: Leading nullptrs
1514 {
1515 Builder* result = validate_context(null, null, &builder1);
1516 EXPECT_EQ(result, &builder1);
1517 }
1518
1519 // Case 4: One non-null followed by nullptrs
1520 {
1521 Builder* result = validate_context(&builder1, null, null);
1522 EXPECT_EQ(result, &builder1);
1523 }
1524
1525 // Case 5: All same non-nullptr
1526 {
1527 Builder* result = validate_context(&builder1, &builder1, &builder1);
1528 EXPECT_EQ(result, &builder1);
1529 }
1530
1531 // Case 6: Conflict between two different non-nullptrs
1532 {
1533 EXPECT_THROW_WITH_MESSAGE(validate_context(&builder1, &builder2),
1534 "Pointers refer to different builder objects!");
1535 }
1536
1537 // Case 7: Conflict between first and last non-null
1538 {
1539 EXPECT_THROW_WITH_MESSAGE(validate_context(&builder1, null, null, &builder2),
1540 "Pointers refer to different builder objects!");
1541 }
1542
1543 // Case 8: First null, two same non-null later
1544 {
1545 Builder* result = validate_context(null, &builder1, &builder1);
1546 EXPECT_EQ(result, &builder1);
1547 }
1548
1549 // Case 9: Interleaved nulls and same pointer
1550 {
1551 Builder* result = validate_context(&builder1, null, &builder1, null);
1552 EXPECT_EQ(result, &builder1);
1553 }
1554 }
1555
1557 {
1558 // Case 1: Empty container returns nullptr
1559 {
1561 Builder* ctx = validate_context<Builder>(empty);
1562 EXPECT_EQ(ctx, nullptr);
1563 }
1564
1565 // Case 2: Same context
1566 {
1568 std::vector<field_ct> fields = {
1569 field_ct(&builder, 1),
1570 field_ct(&builder, 2),
1571 field_ct(&builder, 3),
1572 };
1573 Builder* ctx = validate_context<Builder>(fields);
1574 EXPECT_EQ(ctx, &builder);
1575 }
1576
1577 // Case 3: Some nullptr contexts
1578 {
1580 field_ct null_field; // context is nullptr
1581 field_ct a(&builder, 1);
1582 field_ct b(&builder, 2);
1583 std::vector<field_ct> fields = { null_field, a, b };
1584 Builder* ctx = validate_context<Builder>(fields);
1585 EXPECT_EQ(ctx, &builder);
1586 }
1587
1588 // Case 4: Mismatched contexts should throw/abort
1589 {
1590 Builder builder1;
1591 Builder builder2;
1592 std::vector<field_ct> fields = {
1593 field_ct(&builder1, 1),
1594 field_ct(&builder1, 1),
1595 field_ct(1),
1596 field_ct(&builder2, 2),
1597 };
1598
1599 EXPECT_THROW_WITH_MESSAGE(validate_context<Builder>(fields),
1600 "Pointers refer to different builder objects!");
1601 }
1602 }
1603};
1604using CircuitTypes = testing::Types<bb::UltraCircuitBuilder>;
1605
1607
1608TYPED_TEST(stdlib_field, test_accumulate)
1609{
1610 TestFixture::test_accumulate();
1611}
1613{
1614 TestFixture::test_add();
1615}
1616TYPED_TEST(stdlib_field, test_add_mul_with_constants)
1617{
1618 TestFixture::test_add_mul_with_constants();
1619}
1621{
1622 TestFixture::test_add_two();
1623}
1624TYPED_TEST(stdlib_field, test_assert_equal)
1625{
1626 TestFixture::test_assert_equal();
1627}
1628TYPED_TEST(stdlib_field, test_assert_equal_gate_count)
1629{
1630 TestFixture::test_assert_equal_with_gate_count();
1631}
1632TYPED_TEST(stdlib_field, test_assert_is_in_set)
1633{
1634 TestFixture::test_assert_is_in_set();
1635}
1636TYPED_TEST(stdlib_field, test_assert_is_in_set_fails)
1637{
1638 TestFixture::test_assert_is_in_set_fails();
1639}
1640TYPED_TEST(stdlib_field, test_assert_is_zero)
1641{
1642 TestFixture::test_assert_is_zero();
1643}
1644TYPED_TEST(stdlib_field, test_assert_is_not_zero)
1645{
1646 TestFixture::test_assert_is_not_zero();
1647}
1648TYPED_TEST(stdlib_field, test_bool_conversion)
1649{
1650 TestFixture::test_bool_conversion();
1651}
1652TYPED_TEST(stdlib_field, test_bool_conversion_regression)
1653{
1654 TestFixture::test_bool_conversion_regression();
1655}
1656TYPED_TEST(stdlib_field, test_conditional_assign)
1657{
1658 TestFixture::test_conditional_assign();
1659}
1660TYPED_TEST(stdlib_field, test_conditional_assign_regression)
1661{
1662 TestFixture::test_conditional_assign_regression();
1663}
1664TYPED_TEST(stdlib_field, test_conditional_negate)
1665{
1666 TestFixture::test_conditional_negate();
1667}
1668TYPED_TEST(stdlib_field, test_constructor_from_witness)
1669{
1670 TestFixture::test_constructor_from_witness();
1671}
1672TYPED_TEST(stdlib_field, test_copy_as_new_witness)
1673{
1674 TestFixture::test_copy_as_new_witness();
1675}
1676TYPED_TEST(stdlib_field, test_create_range_constraint)
1677{
1678 TestFixture::create_range_constraint();
1679}
1681{
1682 TestFixture::test_div();
1683}
1684TYPED_TEST(stdlib_field, test_div_edge_cases)
1685{
1686 TestFixture::test_div_edge_cases();
1687}
1689{
1690 TestFixture::test_equality();
1691}
1692TYPED_TEST(stdlib_field, test_equality_false)
1693{
1694 TestFixture::test_equality_false();
1695}
1696TYPED_TEST(stdlib_field, test_equality_with_constants)
1697{
1698 TestFixture::test_equality_with_constants();
1699}
1700TYPED_TEST(stdlib_field, test_field_fibbonaci)
1701{
1702 TestFixture::test_field_fibbonaci();
1703}
1704TYPED_TEST(stdlib_field, test_field_pythagorean)
1705{
1706 TestFixture::test_field_pythagorean();
1707}
1708TYPED_TEST(stdlib_field, test_fix_witness)
1709{
1710 TestFixture::test_fix_witness();
1711}
1713{
1714 TestFixture::test_invert();
1715}
1716TYPED_TEST(stdlib_field, test_invert_zero)
1717{
1718 TestFixture::test_invert_zero();
1719}
1721{
1722 TestFixture::test_is_zero();
1723}
1724TYPED_TEST(stdlib_field, test_larger_circuit)
1725{
1726 TestFixture::test_larger_circuit();
1727}
1729{
1730 TestFixture::test_madd();
1731}
1732TYPED_TEST(stdlib_field, test_madd_add_two_gate_count)
1733{
1734 TestFixture::test_madd_add_two_gate_count();
1735}
1736TYPED_TEST(stdlib_field, test_multiplicative_constant_regression)
1737{
1738 TestFixture::test_multiplicative_constant_regression();
1739}
1740TYPED_TEST(stdlib_field, test_origin_tag_consistency)
1741{
1742 TestFixture::test_origin_tag_consistency();
1743}
1744TYPED_TEST(stdlib_field, test_postfix_increment)
1745{
1746 TestFixture::test_postfix_increment();
1747}
1748TYPED_TEST(stdlib_field, test_pow_op_queue)
1749{
1750 TestFixture::test_pow(/*max_exponent_bits*/ CONST_OP_QUEUE_LOG_SIZE + 1);
1751}
1753{
1754 TestFixture::test_pow(/*max_exponent_bits*/ 32);
1755}
1756TYPED_TEST(stdlib_field, test_pow_witness_exponent_out_of_range_op_queue)
1757{
1759 TestFixture::test_pow_witness_exponent_out_of_range(/*max_exponent_bits*/ CONST_OP_QUEUE_LOG_SIZE + 1);
1760}
1761TYPED_TEST(stdlib_field, test_pow_witness_exponent_out_of_range_32)
1762{
1764 TestFixture::test_pow_witness_exponent_out_of_range(/*max_exponent_bits*/ 32);
1765}
1766TYPED_TEST(stdlib_field, test_pow_constant_exponent_out_of_range_op_queue)
1767{
1768 TestFixture::test_pow_constant_exponent_out_of_range(/*max_exponent_bits*/ CONST_OP_QUEUE_LOG_SIZE + 1);
1769}
1770TYPED_TEST(stdlib_field, test_pow_constant_exponent_out_of_range_32)
1771{
1772 TestFixture::test_pow_constant_exponent_out_of_range(/*max_exponent_bits*/ 32);
1773}
1774TYPED_TEST(stdlib_field, test_prefix_increment)
1775{
1776 TestFixture::test_prefix_increment();
1777}
1778TYPED_TEST(stdlib_field, test_ranged_less_than)
1779{
1780 TestFixture::test_ranged_less_than();
1781}
1782TYPED_TEST(stdlib_field, test_ranged_less_than_max_num_bits)
1783{
1784 TestFixture::test_ranged_less_than_max_num_bits();
1785}
1787{
1788 TestFixture::test_split_at();
1789}
1790TYPED_TEST(stdlib_field, test_three_bit_table)
1791{
1792 TestFixture::test_three_bit_table();
1793}
1794TYPED_TEST(stdlib_field, test_two_bit_table)
1795{
1796 TestFixture::test_two_bit_table();
1797}
1798TYPED_TEST(stdlib_field, test_validate_context)
1799{
1800 TestFixture::test_validate_context();
1801}
1802TYPED_TEST(stdlib_field, test_validate_container_context)
1803{
1804 TestFixture::test_validate_container_context();
1805}
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:193
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:33
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:60
bool get_value() const
Definition bool.hpp:125
bool is_constant() const
Definition bool.hpp:127
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:689
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:869
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:998
uint32_t set_public() const
Definition field.hpp:447
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:942
field_t madd(const field_t &to_mul, const field_t &to_add) const
Definition field.cpp:520
bb::fr additive_constant
Definition field.hpp:94
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:1082
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:1180
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:532
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:1034
bb::fr multiplicative_constant
Definition field.hpp:95
static field_t copy_as_new_witness(Builder &context, field_t const &other)
Definition field.hpp:266
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:895
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:838
field_t normalize() const
Return a new element, where the in-circuit witness contains the actual represented value (multiplicat...
Definition field.cpp:648
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:1060
static field_t from_witness(Builder *ctx, const bb::fr &input)
Definition field.hpp:467
bool_t< Builder > is_zero() const
Validate whether a field_t element is zero.
Definition field.cpp:785
field_t pow(const uint32_t &exponent) const
Raise this field element to the power of the provided uint32_t exponent.
Definition field.cpp:430
bool is_constant() const
Definition field.hpp:442
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:1016
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:585
uint32_t get_witness_index() const
Get the witness index of the current field element.
Definition field.hpp:519
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_pow(uint32_t max_exponent_bits)
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_pow_witness_exponent_out_of_range(uint32_t max_exponent_bits)
static void test_pow_constant_exponent_out_of_range(uint32_t max_exponent_bits)
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_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()
#define info(...)
Definition log.hpp:93
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:16
constexpr T get_msb(const T in)
Definition get_msb.hpp:50
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:245
T * validate_context(T *ptr)
Definition field.hpp:17
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
field< Bn254FrParams > fr
Definition fr.hpp:155
Inner sum(Cont< Inner, Args... > const &in)
Definition container.hpp:70
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)
void assert_failure(std::string const &err)
Definition assert.cpp:11
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
static constexpr field coset_generator()
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()