Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ultra_circuit_builder.test.cpp
Go to the documentation of this file.
6
7#include <gtest/gtest.h>
8
9using namespace bb;
10
11namespace {
13}
14namespace bb {
15
16TEST(UltraCircuitBuilder, CopyConstructor)
17{
19
20 for (size_t i = 0; i < 16; ++i) {
21 for (size_t j = 0; j < 16; ++j) {
22 uint64_t left = static_cast<uint64_t>(j);
23 uint64_t right = static_cast<uint64_t>(i);
24 uint32_t left_idx = builder.add_variable(fr(left));
25 uint32_t right_idx = builder.add_variable(fr(right));
26 uint32_t result_idx = builder.add_variable(fr(left ^ right));
27
28 uint32_t add_idx = builder.add_variable(fr(left) + fr(right) + builder.get_variable(result_idx));
29 builder.create_big_add_gate(
30 { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) });
31 }
32 }
33
34 bool result = CircuitChecker::check(builder);
35 EXPECT_EQ(result, true);
36
37 UltraCircuitBuilder duplicate_builder{ builder };
38
39 EXPECT_EQ(duplicate_builder.get_estimated_num_finalized_gates(), builder.get_estimated_num_finalized_gates());
40 EXPECT_TRUE(CircuitChecker::check(duplicate_builder));
41}
42
43TEST(UltraCircuitBuilder, CreateGatesFromPlookupAccumulators)
44{
45
46 UltraCircuitBuilder circuit_builder = UltraCircuitBuilder();
47
48 fr input_value = fr::random_element();
49 const fr input_lo = static_cast<uint256_t>(input_value).slice(0, plookup::fixed_base::table::BITS_PER_LO_SCALAR);
50 const auto input_lo_index = circuit_builder.add_variable(input_lo);
51
53
54 const auto lookup_witnesses = circuit_builder.create_gates_from_plookup_accumulators(
55 plookup::MultiTableId::FIXED_BASE_LEFT_LO, sequence_data_lo, input_lo_index);
56
58
59 EXPECT_EQ(num_lookups, lookup_witnesses[plookup::ColumnIdx::C1].size());
60
61 {
63
65 std::vector<uint8_t> input_buf;
66 write(input_buf, base_point);
67 const auto offset_generators =
69
70 grumpkin::g1::element accumulator = base_point;
71 uint256_t expected_scalar(input_lo);
72 const auto table_bits = plookup::fixed_base::table::BITS_PER_TABLE;
74 for (size_t i = 0; i < num_tables; ++i) {
75
76 auto round_scalar = circuit_builder.get_variable(lookup_witnesses[plookup::ColumnIdx::C1][i]);
77 auto round_x = circuit_builder.get_variable(lookup_witnesses[plookup::ColumnIdx::C2][i]);
78 auto round_y = circuit_builder.get_variable(lookup_witnesses[plookup::ColumnIdx::C3][i]);
79
80 EXPECT_EQ(uint256_t(round_scalar), expected_scalar);
81
82 auto next_scalar = static_cast<uint256_t>(
83 (i == num_tables - 1) ? fr(0)
84 : circuit_builder.get_variable(lookup_witnesses[plookup::ColumnIdx::C1][i + 1]));
85
86 uint256_t slice = static_cast<uint256_t>(round_scalar) - (next_scalar << table_bits);
87 EXPECT_EQ(slice, (uint256_t(input_lo) >> (i * table_bits)) & mask);
88
89 grumpkin::g1::affine_element expected_point(accumulator * static_cast<uint256_t>(slice) +
90 offset_generators[i]);
91
92 EXPECT_EQ(round_x, expected_point.x);
93 EXPECT_EQ(round_y, expected_point.y);
94 for (size_t j = 0; j < table_bits; ++j) {
95 accumulator = accumulator.dbl();
96 }
97 expected_scalar >>= table_bits;
98 }
99 }
100
101 bool result = CircuitChecker::check(circuit_builder);
102 EXPECT_EQ(result, true);
103}
104
105TEST(UltraCircuitBuilder, BadLookupFailure)
106{
109
110 // Erroneously set a non-zero wire value to zero in one of the lookup gates
111 for (auto& wire_3_witness_idx : builder.blocks.lookup.w_o()) {
112 if (wire_3_witness_idx != builder.zero_idx) {
113 wire_3_witness_idx = builder.zero_idx;
114 break;
115 }
116 }
117
118 EXPECT_FALSE(CircuitChecker::check(builder));
119}
120
122{
124 fr a = fr::one();
125 builder.add_public_variable(a);
126 bool result = CircuitChecker::check(builder);
127 EXPECT_EQ(result, true);
128}
129TEST(UltraCircuitBuilder, TestNoLookupProof)
130{
132
133 for (size_t i = 0; i < 16; ++i) {
134 for (size_t j = 0; j < 16; ++j) {
135 uint64_t left = static_cast<uint64_t>(j);
136 uint64_t right = static_cast<uint64_t>(i);
137 uint32_t left_idx = builder.add_variable(fr(left));
138 uint32_t right_idx = builder.add_variable(fr(right));
139 uint32_t result_idx = builder.add_variable(fr(left ^ right));
140
141 uint32_t add_idx = builder.add_variable(fr(left) + fr(right) + builder.get_variable(result_idx));
142 builder.create_big_add_gate(
143 { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) });
144 }
145 }
146
147 bool result = CircuitChecker::check(builder);
148 EXPECT_EQ(result, true);
149}
150
151TEST(UltraCircuitBuilder, TestEllipticGate)
152{
154 typedef grumpkin::g1::element element;
156
158
160 affine_element p3(element(p1) + element(p2));
161
162 uint32_t x1 = builder.add_variable(p1.x);
163 uint32_t y1 = builder.add_variable(p1.y);
164 uint32_t x2 = builder.add_variable(p2.x);
165 uint32_t y2 = builder.add_variable(p2.y);
166 uint32_t x3 = builder.add_variable(p3.x);
167 uint32_t y3 = builder.add_variable(p3.y);
168
169 builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, 1 });
170
171 bool result = CircuitChecker::check(builder);
172 EXPECT_EQ(result, true);
173
174 builder.create_ecc_add_gate({ x1 + 1, y1, x2, y2, x3, y3, 1 });
175
176 EXPECT_EQ(CircuitChecker::check(builder), false);
177}
178
179TEST(UltraCircuitBuilder, TestEllipticDoubleGate)
180{
182 typedef grumpkin::g1::element element;
184
186 affine_element p3(element(p1).dbl());
187
188 uint32_t x1 = builder.add_variable(p1.x);
189 uint32_t y1 = builder.add_variable(p1.y);
190 uint32_t x3 = builder.add_variable(p3.x);
191 uint32_t y3 = builder.add_variable(p3.y);
192
193 builder.create_ecc_dbl_gate({ x1, y1, x3, y3 });
194
195 bool result = CircuitChecker::check(builder);
196 EXPECT_EQ(result, true);
197}
198
199TEST(UltraCircuitBuilder, NonTrivialTagPermutation)
200{
203 fr b = -a;
204
205 auto a_idx = builder.add_variable(a);
206 auto b_idx = builder.add_variable(b);
207 auto c_idx = builder.add_variable(b);
208 auto d_idx = builder.add_variable(a);
209
210 builder.create_add_gate({ a_idx, b_idx, builder.zero_idx, fr::one(), fr::one(), fr::zero(), fr::zero() });
211 builder.create_add_gate({ c_idx, d_idx, builder.zero_idx, fr::one(), fr::one(), fr::zero(), fr::zero() });
212
213 builder.create_tag(1, 2);
214 builder.create_tag(2, 1);
215
216 builder.assign_tag(a_idx, 1);
217 builder.assign_tag(b_idx, 1);
218 builder.assign_tag(c_idx, 2);
219 builder.assign_tag(d_idx, 2);
220
221 bool result = CircuitChecker::check(builder);
222 EXPECT_EQ(result, true);
223
224 // Break the tag
225 builder.real_variable_tags[builder.real_variable_index[a_idx]] = 2;
226 EXPECT_EQ(CircuitChecker::check(builder), false);
227}
228
229TEST(UltraCircuitBuilder, NonTrivialTagPermutationAndCycles)
230{
233 fr c = -a;
234
235 auto a_idx = builder.add_variable(a);
236 auto b_idx = builder.add_variable(a);
237 builder.assert_equal(a_idx, b_idx);
238 auto c_idx = builder.add_variable(c);
239 auto d_idx = builder.add_variable(c);
240 builder.assert_equal(c_idx, d_idx);
241 auto e_idx = builder.add_variable(a);
242 auto f_idx = builder.add_variable(a);
243 builder.assert_equal(e_idx, f_idx);
244 auto g_idx = builder.add_variable(c);
245 auto h_idx = builder.add_variable(c);
246 builder.assert_equal(g_idx, h_idx);
247
248 builder.create_tag(1, 2);
249 builder.create_tag(2, 1);
250
251 builder.assign_tag(a_idx, 1);
252 builder.assign_tag(c_idx, 1);
253 builder.assign_tag(e_idx, 2);
254 builder.assign_tag(g_idx, 2);
255
256 builder.create_add_gate({ b_idx, a_idx, builder.zero_idx, fr::one(), fr::neg_one(), fr::zero(), fr::zero() });
257 builder.create_add_gate({ c_idx, g_idx, builder.zero_idx, fr::one(), -fr::one(), fr::zero(), fr::zero() });
258 builder.create_add_gate({ e_idx, f_idx, builder.zero_idx, fr::one(), -fr::one(), fr::zero(), fr::zero() });
259
260 bool result = CircuitChecker::check(builder);
261 EXPECT_EQ(result, true);
262
263 // Break the tag
264 builder.real_variable_tags[builder.real_variable_index[a_idx]] = 2;
265 EXPECT_EQ(CircuitChecker::check(builder), false);
266}
267TEST(UltraCircuitBuilder, BadTagPermutation)
268{
271 fr b = -a;
272
273 auto a_idx = builder.add_variable(a);
274 auto b_idx = builder.add_variable(b);
275 auto c_idx = builder.add_variable(b);
276 auto d_idx = builder.add_variable(a + 1);
277
278 builder.create_add_gate({ a_idx, b_idx, builder.zero_idx, 1, 1, 0, 0 });
279 builder.create_add_gate({ c_idx, d_idx, builder.zero_idx, 1, 1, 0, -1 });
280
281 bool result = CircuitChecker::check(builder);
282 EXPECT_EQ(result, true);
283
284 builder.create_tag(1, 2);
285 builder.create_tag(2, 1);
286
287 builder.assign_tag(a_idx, 1);
288 builder.assign_tag(b_idx, 1);
289 builder.assign_tag(c_idx, 2);
290 builder.assign_tag(d_idx, 2);
291
293 EXPECT_EQ(result, false);
294}
295
297{
299 fr a = fr::one();
300 fr b = fr(2);
301 fr c = fr(3);
302 fr d = fr(4);
303
304 auto a_idx = builder.add_variable(a);
305 auto b_idx = builder.add_variable(b);
306 auto c_idx = builder.add_variable(c);
307 auto d_idx = builder.add_variable(d);
308 builder.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx });
309
310 bool result = CircuitChecker::check(builder);
311 EXPECT_EQ(result, true);
312}
313
314std::vector<uint32_t> add_variables(UltraCircuitBuilder& builder, std::vector<fr> variables)
315{
316 std::vector<uint32_t> res;
317 for (size_t i = 0; i < variables.size(); i++) {
318 res.emplace_back(builder.add_variable(variables[i]));
319 }
320 return res;
321}
322TEST(UltraCircuitBuilder, SortWithEdgesGate)
323{
324 fr a = fr::one();
325 fr b = fr(2);
326 fr c = fr(3);
327 fr d = fr(4);
328 fr e = fr(5);
329 fr f = fr(6);
330 fr g = fr(7);
331 fr h = fr(8);
332
333 {
335 auto a_idx = builder.add_variable(a);
336 auto b_idx = builder.add_variable(b);
337 auto c_idx = builder.add_variable(c);
338 auto d_idx = builder.add_variable(d);
339 auto e_idx = builder.add_variable(e);
340 auto f_idx = builder.add_variable(f);
341 auto g_idx = builder.add_variable(g);
342 auto h_idx = builder.add_variable(h);
343 builder.create_sort_constraint_with_edges({ a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, h);
344 bool result = CircuitChecker::check(builder);
345 EXPECT_EQ(result, true);
346 }
347
348 {
350 auto a_idx = builder.add_variable(a);
351 auto b_idx = builder.add_variable(b);
352 auto c_idx = builder.add_variable(c);
353 auto d_idx = builder.add_variable(d);
354 auto e_idx = builder.add_variable(e);
355 auto f_idx = builder.add_variable(f);
356 auto g_idx = builder.add_variable(g);
357 auto h_idx = builder.add_variable(h);
358 builder.create_sort_constraint_with_edges({ a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, g);
359
360 bool result = CircuitChecker::check(builder);
361 EXPECT_EQ(result, false);
362 }
363 {
365 auto a_idx = builder.add_variable(a);
366 auto b_idx = builder.add_variable(b);
367 auto c_idx = builder.add_variable(c);
368 auto d_idx = builder.add_variable(d);
369 auto e_idx = builder.add_variable(e);
370 auto f_idx = builder.add_variable(f);
371 auto g_idx = builder.add_variable(g);
372 auto h_idx = builder.add_variable(h);
373 builder.create_sort_constraint_with_edges({ a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, b, h);
374
375 bool result = CircuitChecker::check(builder);
376 EXPECT_EQ(result, false);
377 }
378 {
380 auto a_idx = builder.add_variable(a);
381 auto c_idx = builder.add_variable(c);
382 auto d_idx = builder.add_variable(d);
383 auto e_idx = builder.add_variable(e);
384 auto f_idx = builder.add_variable(f);
385 auto g_idx = builder.add_variable(g);
386 auto h_idx = builder.add_variable(h);
387 auto b2_idx = builder.add_variable(fr(15));
388 builder.create_sort_constraint_with_edges({ a_idx, b2_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, b, h);
389 bool result = CircuitChecker::check(builder);
390 EXPECT_EQ(result, false);
391 }
392 {
394 auto idx = add_variables(builder, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25,
395 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 });
396 builder.create_sort_constraint_with_edges(idx, 1, 45);
397 bool result = CircuitChecker::check(builder);
398 EXPECT_EQ(result, true);
399 }
400 {
402 auto idx = add_variables(builder, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25,
403 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 });
404
405 builder.create_sort_constraint_with_edges(idx, 1, 29);
406 bool result = CircuitChecker::check(builder);
407 EXPECT_EQ(result, false);
408 }
409}
410
411TEST(UltraCircuitBuilder, RangeConstraint)
412{
413 {
415 auto indices = add_variables(builder, { 1, 2, 3, 4, 5, 6, 7, 8 });
416 for (size_t i = 0; i < indices.size(); i++) {
417 builder.create_new_range_constraint(indices[i], 8);
418 }
419 // auto ind = {a_idx,b_idx,c_idx,d_idx,e_idx,f_idx,g_idx,h_idx};
420 builder.create_sort_constraint(indices);
421 bool result = CircuitChecker::check(builder);
422 EXPECT_EQ(result, true);
423 }
424 {
426 auto indices = add_variables(builder, { 3 });
427 for (size_t i = 0; i < indices.size(); i++) {
428 builder.create_new_range_constraint(indices[i], 3);
429 }
430 // auto ind = {a_idx,b_idx,c_idx,d_idx,e_idx,f_idx,g_idx,h_idx};
431 builder.create_dummy_constraints(indices);
432 bool result = CircuitChecker::check(builder);
433 EXPECT_EQ(result, true);
434 }
435 {
437 auto indices = add_variables(builder, { 1, 2, 3, 4, 5, 6, 8, 25 });
438 for (size_t i = 0; i < indices.size(); i++) {
439 builder.create_new_range_constraint(indices[i], 8);
440 }
441 builder.create_sort_constraint(indices);
442 bool result = CircuitChecker::check(builder);
443 EXPECT_EQ(result, false);
444 }
445 {
447 auto indices =
448 add_variables(builder, { 1, 2, 3, 4, 5, 6, 10, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 19, 51 });
449 for (size_t i = 0; i < indices.size(); i++) {
450 builder.create_new_range_constraint(indices[i], 128);
451 }
452 builder.create_dummy_constraints(indices);
453 bool result = CircuitChecker::check(builder);
454 EXPECT_EQ(result, true);
455 }
456 {
458 auto indices =
459 add_variables(builder, { 1, 2, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 });
460 for (size_t i = 0; i < indices.size(); i++) {
461 builder.create_new_range_constraint(indices[i], 79);
462 }
463 builder.create_dummy_constraints(indices);
464 bool result = CircuitChecker::check(builder);
465 EXPECT_EQ(result, false);
466 }
467 {
469 auto indices =
470 add_variables(builder, { 1, 0, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 });
471 for (size_t i = 0; i < indices.size(); i++) {
472 builder.create_new_range_constraint(indices[i], 79);
473 }
474 builder.create_dummy_constraints(indices);
475 bool result = CircuitChecker::check(builder);
476 EXPECT_EQ(result, false);
477 }
478}
479
480TEST(UltraCircuitBuilder, RangeWithGates)
481{
483 auto idx = add_variables(builder, { 1, 2, 3, 4, 5, 6, 7, 8 });
484 for (size_t i = 0; i < idx.size(); i++) {
485 builder.create_new_range_constraint(idx[i], 8);
486 }
487
488 builder.create_add_gate({ idx[0], idx[1], builder.zero_idx, fr::one(), fr::one(), fr::zero(), -3 });
489 builder.create_add_gate({ idx[2], idx[3], builder.zero_idx, fr::one(), fr::one(), fr::zero(), -7 });
490 builder.create_add_gate({ idx[4], idx[5], builder.zero_idx, fr::one(), fr::one(), fr::zero(), -11 });
491 builder.create_add_gate({ idx[6], idx[7], builder.zero_idx, fr::one(), fr::one(), fr::zero(), -15 });
492 bool result = CircuitChecker::check(builder);
493 EXPECT_EQ(result, true);
494}
495
496TEST(UltraCircuitBuilder, RangeWithGatesWhereRangeIsNotAPowerOfTwo)
497{
499 auto idx = add_variables(builder, { 1, 2, 3, 4, 5, 6, 7, 8 });
500 for (size_t i = 0; i < idx.size(); i++) {
501 builder.create_new_range_constraint(idx[i], 12);
502 }
503
504 builder.create_add_gate({ idx[0], idx[1], builder.zero_idx, fr::one(), fr::one(), fr::zero(), -3 });
505 builder.create_add_gate({ idx[2], idx[3], builder.zero_idx, fr::one(), fr::one(), fr::zero(), -7 });
506 builder.create_add_gate({ idx[4], idx[5], builder.zero_idx, fr::one(), fr::one(), fr::zero(), -11 });
507 builder.create_add_gate({ idx[6], idx[7], builder.zero_idx, fr::one(), fr::one(), fr::zero(), -15 });
508 bool result = CircuitChecker::check(builder);
509 EXPECT_EQ(result, true);
510}
511
512TEST(UltraCircuitBuilder, SortWidgetComplex)
513{
514 {
515
517 std::vector<fr> a = { 1, 3, 4, 7, 7, 8, 11, 14, 15, 15, 18, 19, 21, 21, 24, 25, 26, 27, 30, 32 };
518 std::vector<uint32_t> ind;
519 for (size_t i = 0; i < a.size(); i++)
520 ind.emplace_back(builder.add_variable(a[i]));
521 builder.create_sort_constraint(ind);
522
523 bool result = CircuitChecker::check(builder);
524 EXPECT_EQ(result, true);
525 }
526 {
527
529 std::vector<fr> a = { 1, 3, 4, 7, 7, 8, 16, 14, 15, 15, 18, 19, 21, 21, 24, 25, 26, 27, 30, 32 };
530 std::vector<uint32_t> ind;
531 for (size_t i = 0; i < a.size(); i++)
532 ind.emplace_back(builder.add_variable(a[i]));
533 builder.create_sort_constraint(ind);
534
535 bool result = CircuitChecker::check(builder);
536 EXPECT_EQ(result, false);
537 }
538}
540{
542 fr a = fr::one();
543 fr b = fr(2);
544 fr c = fr(3);
545 fr d = fr(8);
546
547 auto a_idx = builder.add_variable(a);
548 auto b_idx = builder.add_variable(b);
549 auto c_idx = builder.add_variable(c);
550 auto d_idx = builder.add_variable(d);
551 builder.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx });
552
553 bool result = CircuitChecker::check(builder);
554 EXPECT_EQ(result, false);
555}
556
557TEST(UltraCircuitBuilder, ComposedRangeConstraint)
558{
559 // even num bits - not divisible by 3
561 auto c = fr::random_element();
562 auto d = uint256_t(c).slice(0, 133);
563 auto e = fr(d);
564 auto a_idx = builder.add_variable(fr(e));
565 builder.create_add_gate({ a_idx, builder.zero_idx, builder.zero_idx, 1, 0, 0, -fr(e) });
566 builder.decompose_into_default_range(a_idx, 134);
567
568 // odd num bits - divisible by 3
569 auto c_1 = fr::random_element();
570 auto d_1 = uint256_t(c_1).slice(0, 126);
571 auto e_1 = fr(d_1);
572 auto a_idx_1 = builder.add_variable(fr(e_1));
573 builder.create_add_gate({ a_idx_1, builder.zero_idx, builder.zero_idx, 1, 0, 0, -fr(e_1) });
574 builder.decompose_into_default_range(a_idx_1, 127);
575
576 bool result = CircuitChecker::check(builder);
577 EXPECT_EQ(result, true);
578}
579
580TEST(UltraCircuitBuilder, NonNativeFieldMultiplication)
581{
583
586 uint256_t modulus = fq::modulus;
587
590 uint1024_t p_big = uint512_t(uint256_t(modulus));
591
592 uint1024_t q_big = (a_big * b_big) / p_big;
593 uint1024_t r_big = (a_big * b_big) % p_big;
594
595 uint256_t q(q_big.lo.lo);
596 uint256_t r(r_big.lo.lo);
597
598 const auto split_into_limbs = [&](const uint512_t& input) {
599 constexpr size_t NUM_BITS = 68;
600 std::array<fr, 4> limbs;
601 limbs[0] = input.slice(0, NUM_BITS).lo;
602 limbs[1] = input.slice(NUM_BITS * 1, NUM_BITS * 2).lo;
603 limbs[2] = input.slice(NUM_BITS * 2, NUM_BITS * 3).lo;
604 limbs[3] = input.slice(NUM_BITS * 3, NUM_BITS * 4).lo;
605 return limbs;
606 };
607
608 const auto get_limb_witness_indices = [&](const std::array<fr, 4>& limbs) {
609 std::array<uint32_t, 4> limb_indices;
610 limb_indices[0] = builder.add_variable(limbs[0]);
611 limb_indices[1] = builder.add_variable(limbs[1]);
612 limb_indices[2] = builder.add_variable(limbs[2]);
613 limb_indices[3] = builder.add_variable(limbs[3]);
614 return limb_indices;
615 };
616 const uint512_t BINARY_BASIS_MODULUS = uint512_t(1) << (68 * 4);
617 auto modulus_limbs = split_into_limbs(BINARY_BASIS_MODULUS - uint512_t(modulus));
618
619 const auto a_indices = get_limb_witness_indices(split_into_limbs(uint256_t(a)));
620 const auto b_indices = get_limb_witness_indices(split_into_limbs(uint256_t(b)));
621 const auto q_indices = get_limb_witness_indices(split_into_limbs(uint256_t(q)));
622 const auto r_indices = get_limb_witness_indices(split_into_limbs(uint256_t(r)));
623
625 a_indices, b_indices, q_indices, r_indices, modulus_limbs,
626 };
627 const auto [lo_1_idx, hi_1_idx] = builder.evaluate_non_native_field_multiplication(inputs);
628 builder.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70);
629
630 bool result = CircuitChecker::check(builder);
631 EXPECT_EQ(result, true);
632}
633
638TEST(UltraCircuitBuilder, NonNativeFieldMultiplicationSortCheck)
639{
641
644 uint256_t modulus = fq::modulus;
645
648 uint1024_t p_big = uint512_t(uint256_t(modulus));
649
650 uint1024_t q_big = (a_big * b_big) / p_big;
651 uint1024_t r_big = (a_big * b_big) % p_big;
652
653 uint256_t q(q_big.lo.lo);
654 uint256_t r(r_big.lo.lo);
655
656 const auto split_into_limbs = [&](const uint512_t& input) {
657 constexpr size_t NUM_BITS = 68;
658 std::array<fr, 4> limbs;
659 limbs[0] = input.slice(0, NUM_BITS).lo;
660 limbs[1] = input.slice(NUM_BITS * 1, NUM_BITS * 2).lo;
661 limbs[2] = input.slice(NUM_BITS * 2, NUM_BITS * 3).lo;
662 limbs[3] = input.slice(NUM_BITS * 3, NUM_BITS * 4).lo;
663 return limbs;
664 };
665
666 const auto get_limb_witness_indices = [&](const std::array<fr, 4>& limbs) {
667 std::array<uint32_t, 4> limb_indices;
668 limb_indices[0] = builder.add_variable(limbs[0]);
669 limb_indices[1] = builder.add_variable(limbs[1]);
670 limb_indices[2] = builder.add_variable(limbs[2]);
671 limb_indices[3] = builder.add_variable(limbs[3]);
672 return limb_indices;
673 };
674 const uint512_t BINARY_BASIS_MODULUS = uint512_t(1) << (68 * 4);
675 auto modulus_limbs = split_into_limbs(BINARY_BASIS_MODULUS - uint512_t(modulus));
676
677 const auto a_indices = get_limb_witness_indices(split_into_limbs(uint256_t(a)));
678 const auto b_indices = get_limb_witness_indices(split_into_limbs(uint256_t(b)));
679 const auto q_indices = get_limb_witness_indices(split_into_limbs(uint256_t(q)));
680 const auto r_indices = get_limb_witness_indices(split_into_limbs(uint256_t(r)));
681
683 a_indices, b_indices, q_indices, r_indices, modulus_limbs,
684 };
685 const auto [lo_1_idx, hi_1_idx] = builder.evaluate_non_native_field_multiplication(inputs);
686 builder.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70);
687
688 bool result = CircuitChecker::check(builder);
689 EXPECT_EQ(result, true);
690
691 // Everything above was copied from the previous test.
692 // Check that in the nnf blocks, the other selectors besides the nnf selector are zero
693 for (size_t i = 0; i < builder.blocks.nnf.size(); ++i) {
694 EXPECT_EQ(builder.blocks.nnf.q_arith()[i], 0);
695 EXPECT_EQ(builder.blocks.nnf.q_delta_range()[i], 0);
696 EXPECT_EQ(builder.blocks.nnf.q_elliptic()[i], 0);
697 EXPECT_EQ(builder.blocks.nnf.q_lookup_type()[i], 0);
698 EXPECT_EQ(builder.blocks.nnf.q_poseidon2_external()[i], 0);
699 EXPECT_EQ(builder.blocks.nnf.q_poseidon2_internal()[i], 0);
700 }
701}
702
704{
706
707 uint32_t rom_values[8]{
708 builder.add_variable(fr::random_element()), builder.add_variable(fr::random_element()),
709 builder.add_variable(fr::random_element()), builder.add_variable(fr::random_element()),
710 builder.add_variable(fr::random_element()), builder.add_variable(fr::random_element()),
711 builder.add_variable(fr::random_element()), builder.add_variable(fr::random_element()),
712 };
713
714 size_t rom_id = builder.create_ROM_array(8);
715
716 for (size_t i = 0; i < 8; ++i) {
717 builder.set_ROM_element(rom_id, i, rom_values[i]);
718 }
719
720 uint32_t a_idx = builder.read_ROM_array(rom_id, builder.add_variable(5));
721 EXPECT_EQ(a_idx != rom_values[5], true);
722 uint32_t b_idx = builder.read_ROM_array(rom_id, builder.add_variable(4));
723 uint32_t c_idx = builder.read_ROM_array(rom_id, builder.add_variable(1));
724
725 const auto d_value = builder.get_variable(a_idx) + builder.get_variable(b_idx) + builder.get_variable(c_idx);
726 uint32_t d_idx = builder.add_variable(d_value);
727
728 builder.create_big_add_gate({
729 a_idx,
730 b_idx,
731 c_idx,
732 d_idx,
733 1,
734 1,
735 1,
736 -1,
737 0,
738 });
739
740 bool result = CircuitChecker::check(builder);
741 EXPECT_EQ(result, true);
742}
743
749{
751
752 // Initialize a length 1 RAM array with a single value
753 fr ram_value = 5;
754 uint32_t ram_value_idx = builder.add_variable(ram_value);
755 size_t ram_id = builder.create_RAM_array(/*array_size=*/1);
756 builder.init_RAM_element(ram_id, /*index_value=*/0, ram_value_idx);
757
758 // Read from the RAM array we just created (at the 0th index)
759 uint32_t read_idx = builder.add_variable(0);
760 uint32_t a_idx = builder.read_RAM_array(ram_id, read_idx);
761
762 // Use the result in a simple arithmetic gate
763 builder.create_big_add_gate({
764 a_idx,
765 builder.zero_idx,
766 builder.zero_idx,
767 builder.zero_idx,
768 -1,
769 0,
770 0,
771 0,
772 builder.get_variable(ram_value_idx),
773 });
774
775 EXPECT_TRUE(CircuitChecker::check(builder));
776}
777
779{
781
782 uint32_t ram_values[8]{
783 builder.add_variable(fr::random_element()), builder.add_variable(fr::random_element()),
784 builder.add_variable(fr::random_element()), builder.add_variable(fr::random_element()),
785 builder.add_variable(fr::random_element()), builder.add_variable(fr::random_element()),
786 builder.add_variable(fr::random_element()), builder.add_variable(fr::random_element()),
787 };
788
789 size_t ram_id = builder.create_RAM_array(8);
790
791 for (size_t i = 0; i < 8; ++i) {
792 builder.init_RAM_element(ram_id, i, ram_values[i]);
793 }
794
795 uint32_t a_idx = builder.read_RAM_array(ram_id, builder.add_variable(5));
796 EXPECT_EQ(a_idx != ram_values[5], true);
797
798 uint32_t b_idx = builder.read_RAM_array(ram_id, builder.add_variable(4));
799 uint32_t c_idx = builder.read_RAM_array(ram_id, builder.add_variable(1));
800
801 builder.write_RAM_array(ram_id, builder.add_variable(4), builder.add_variable(500));
802 uint32_t d_idx = builder.read_RAM_array(ram_id, builder.add_variable(4));
803
804 EXPECT_EQ(builder.get_variable(d_idx), 500);
805
806 // ensure these vars get used in another arithmetic gate
807 const auto e_value = builder.get_variable(a_idx) + builder.get_variable(b_idx) + builder.get_variable(c_idx) +
808 builder.get_variable(d_idx);
809 uint32_t e_idx = builder.add_variable(e_value);
810
811 builder.create_big_add_gate(
812 {
813 a_idx,
814 b_idx,
815 c_idx,
816 d_idx,
817 -1,
818 -1,
819 -1,
820 -1,
821 0,
822 },
823 true);
824 builder.create_big_add_gate(
825 {
826 builder.zero_idx,
827 builder.zero_idx,
828 builder.zero_idx,
829 e_idx,
830 0,
831 0,
832 0,
833 0,
834 0,
835 },
836 false);
837
838 bool result = CircuitChecker::check(builder);
839 EXPECT_EQ(result, true);
840
841 // Test the builder copy constructor for a circuit with RAM gates
842 UltraCircuitBuilder duplicate_builder{ builder };
843
844 EXPECT_EQ(duplicate_builder.get_estimated_num_finalized_gates(), builder.get_estimated_num_finalized_gates());
845 EXPECT_TRUE(CircuitChecker::check(duplicate_builder));
846}
847
848TEST(UltraCircuitBuilder, RangeChecksOnDuplicates)
849{
851
852 uint32_t a = builder.add_variable(100);
853 uint32_t b = builder.add_variable(100);
854 uint32_t c = builder.add_variable(100);
855 uint32_t d = builder.add_variable(100);
856
857 builder.assert_equal(a, b);
858 builder.assert_equal(a, c);
859 builder.assert_equal(a, d);
860
861 builder.create_new_range_constraint(a, 1000);
862 builder.create_new_range_constraint(b, 1001);
863 builder.create_new_range_constraint(c, 999);
864 builder.create_new_range_constraint(d, 1000);
865
866 builder.create_big_add_gate(
867 {
868 a,
869 b,
870 c,
871 d,
872 0,
873 0,
874 0,
875 0,
876 0,
877 },
878 false);
879 bool result = CircuitChecker::check(builder);
880 EXPECT_EQ(result, true);
881}
882
883TEST(UltraCircuitBuilder, CheckCircuitShowcase)
884{
886 // check_circuit allows us to check correctness on the go
887
888 uint32_t a = builder.add_variable(0xdead);
889 uint32_t b = builder.add_variable(0xbeef);
890 // Let's create 2 gates that will bind these 2 variables to be one these two values
891 builder.create_poly_gate(
892 { a, a, builder.zero_idx, fr(1), -fr(0xdead) - fr(0xbeef), 0, 0, fr(0xdead) * fr(0xbeef) });
893 builder.create_poly_gate(
894 { b, b, builder.zero_idx, fr(1), -fr(0xdead) - fr(0xbeef), 0, 0, fr(0xdead) * fr(0xbeef) });
895
896 // We can check if this works
897 EXPECT_EQ(CircuitChecker::check(builder), true);
898
899 // Now let's create a range constraint for b
900 builder.create_new_range_constraint(b, 0xbeef);
901
902 // We can check if this works
903 EXPECT_EQ(CircuitChecker::check(builder), true);
904
905 // But what if we now assert b to be equal to a?
906 builder.assert_equal(a, b, "Oh no");
907
908 // It fails, because a is 0xdead and it can't fit in the range constraint
909 EXPECT_EQ(CircuitChecker::check(builder), false);
910
911 // But if we force them both back to be 0xbeef...
912 uint32_t c = builder.add_variable(0xbeef);
913 builder.assert_equal(c, b);
914
915 // The circuit will magically pass again
916 EXPECT_EQ(CircuitChecker::check(builder), true);
917}
918
919} // namespace bb
virtual uint32_t add_variable(const FF &in)
FF get_variable(const uint32_t index) const
Get the value of the variable v_{index}.
static void add_lookup_gates(Builder &builder, size_t num_iterations=1)
Add lookup gates using the uint32 XOR lookup table (table size 4096)
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
plookup::ReadData< uint32_t > create_gates_from_plookup_accumulators(const plookup::MultiTableId &id, const plookup::ReadData< FF > &read_values, const uint32_t key_a_index, std::optional< uint32_t > key_b_index=std::nullopt)
Perform a series of lookups, one for each 'row' in read_values.
static AffineElement commit_native(const std::vector< Fq > &inputs, GeneratorContext context={})
Given a vector of fields, generate a pedersen commitment using the indexed generators.
Definition pedersen.cpp:24
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
Definition element.hpp:33
constexpr element dbl() const noexcept
static std::vector< affine_element > derive_generators(const std::vector< uint8_t > &domain_separator_bytes, const size_t num_generators, const size_t starting_index=0)
Derives generator points via hash-to-curve.
Definition group.hpp:87
constexpr uint256_t slice(uint64_t start, uint64_t end) const
static constexpr affine_element lhs_generator_point()
AluTraceBuilder builder
Definition alu.test.cpp:123
FF a
FF b
grumpkin::g1::affine_element affine_element
Definition c_bind.hpp:15
numeric::RNG & engine
uintx< uint256_t > uint512_t
Definition uintx.hpp:307
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:190
ReadData< bb::fr > get_lookup_accumulators(const MultiTableId id, const fr &key_a, const fr &key_b, const bool is_2_to_1_lookup)
Given a table ID and the key(s) for a key-value lookup, return the lookup accumulators.
@ FIXED_BASE_LEFT_LO
Definition types.hpp:100
Entry point for Barretenberg command-line interface.
std::vector< uint32_t > add_variables(UltraCircuitBuilder &builder, std::vector< fr > variables)
TEST(MegaCircuitBuilder, CopyConstructor)
field< Bn254FrParams > fr
Definition fr.hpp:174
void write(B &buf, field2< base_field, Params > const &value)
C slice(C const &container, size_t start)
Definition container.hpp:9
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr field neg_one()
static constexpr field one()
static constexpr uint256_t modulus
static field random_element(numeric::RNG *engine=nullptr) noexcept
static constexpr field zero()
static constexpr size_t MAX_TABLE_SIZE
static constexpr size_t BITS_PER_TABLE
static constexpr size_t BITS_PER_LO_SCALAR
static constexpr size_t NUM_TABLES_PER_LO_MULTITABLE