Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ultra_circuit_builder_arithmetic.test.cpp
Go to the documentation of this file.
3
4#include <gtest/gtest.h>
5
6using namespace bb;
7
24class UltraCircuitBuilderArithmetic : public ::testing::Test {
25 protected:
26 // Helper structs to set up gate data
31
36
41
43 fr a, b, c;
45 };
46
47 // Create gate that enforces: a + b = c
48 static AddTripleData create_add_triple_data(uint64_t a_val = 5, uint64_t b_val = 7)
49 {
50 fr a(a_val);
51 fr b(b_val);
52 fr c = a + b;
53 return { a, b, c, fr(1), fr(1), fr(-1), fr(0) };
54 }
55
56 // Create gate that enforces: d = a + b + c
57 static AddQuadData create_add_quad_data(uint64_t a_val = 3, uint64_t b_val = 5, uint64_t c_val = 7)
58 {
59 fr a(a_val);
60 fr b(b_val);
61 fr c(c_val);
62 fr d = a + b + c;
63 return { a, b, c, d, fr(1), fr(1), fr(1), fr(-1), fr(0) };
64 }
65
66 // Create gate that enforces: d = a * b + c
67 static MulQuadData create_mul_quad_data(uint64_t a_val = 5, uint64_t b_val = 7, uint64_t c_val = 3)
68 {
69 fr a(a_val);
70 fr b(b_val);
71 fr c(c_val);
72 fr d = a * b + c;
73 return { a, b, c, d, fr(1), fr(0), fr(0), fr(1), fr(-1), fr(0) };
74 }
75
76 // Create gate that enforces: c = a * b + 2a + 3b
77 static ArithTripleData create_arithmetic_triple_data(uint64_t a_val = 5, uint64_t b_val = 7)
78 {
79 fr a(a_val);
80 fr b(b_val);
81 fr c = a * b + fr(2) * a + fr(3) * b;
82 return { a, b, c, fr(1), fr(2), fr(3), fr(-1), fr(0) };
83 }
84};
85
86// Verifies that a valid 3-wire addition gate passes the circuit checker
88{
90 auto data = create_add_triple_data(5, 7);
91 builder.create_add_gate({ builder.add_variable(data.a),
92 builder.add_variable(data.b),
93 builder.add_variable(data.c),
94 data.a_scaling,
95 data.b_scaling,
96 data.c_scaling,
97 data.const_scaling });
98 EXPECT_TRUE(CircuitChecker::check(builder));
99}
100
101// Verifies that invalidating any variable or scaling coefficient in an add gate causes failure
103{
104 auto test_invalid = [](auto modify_data) {
106 auto data = create_add_triple_data(5, 7);
107 modify_data(data);
108 builder.create_add_gate({ builder.add_variable(data.a),
109 builder.add_variable(data.b),
110 builder.add_variable(data.c),
111 data.a_scaling,
112 data.b_scaling,
113 data.c_scaling,
114 data.const_scaling });
115 EXPECT_FALSE(CircuitChecker::check(builder));
116 };
117
118 // Test witness failures
119 test_invalid([](AddTripleData& d) { d.a += fr(1); });
120 test_invalid([](AddTripleData& d) { d.b += fr(1); });
121 test_invalid([](AddTripleData& d) { d.c += fr(1); });
122
123 // Test scaling coefficient failures
124 test_invalid([](AddTripleData& d) { d.a_scaling += fr(1); });
125 test_invalid([](AddTripleData& d) { d.b_scaling += fr(1); });
126 test_invalid([](AddTripleData& d) { d.c_scaling += fr(1); });
127 test_invalid([](AddTripleData& d) { d.const_scaling += fr(1); });
128}
129
130// Verifies that a valid 4-wire addition gate passes the circuit checker
132{
134 auto data = create_add_quad_data(3, 5, 7);
135 builder.create_big_add_gate({ builder.add_variable(data.a),
136 builder.add_variable(data.b),
137 builder.add_variable(data.c),
138 builder.add_variable(data.d),
139 data.a_scaling,
140 data.b_scaling,
141 data.c_scaling,
142 data.d_scaling,
143 data.const_scaling });
144 EXPECT_TRUE(CircuitChecker::check(builder));
145}
146
147// Verifies that invalidating any variable or scaling coefficient in a big add gate causes failure
149{
150 auto test_invalid = [](auto modify_data) {
152 auto data = create_add_quad_data(3, 5, 7);
153 modify_data(data);
154 builder.create_big_add_gate({ builder.add_variable(data.a),
155 builder.add_variable(data.b),
156 builder.add_variable(data.c),
157 builder.add_variable(data.d),
158 data.a_scaling,
159 data.b_scaling,
160 data.c_scaling,
161 data.d_scaling,
162 data.const_scaling });
163 EXPECT_FALSE(CircuitChecker::check(builder));
164 };
165
166 // Test witness failures
167 test_invalid([](AddQuadData& d) { d.a += fr(1); });
168 test_invalid([](AddQuadData& d) { d.b += fr(1); });
169 test_invalid([](AddQuadData& d) { d.c += fr(1); });
170 test_invalid([](AddQuadData& d) { d.d += fr(1); });
171
172 // Test scaling coefficient failures
173 test_invalid([](AddQuadData& d) { d.a_scaling += fr(1); });
174 test_invalid([](AddQuadData& d) { d.b_scaling += fr(1); });
175 test_invalid([](AddQuadData& d) { d.c_scaling += fr(1); });
176 test_invalid([](AddQuadData& d) { d.d_scaling += fr(1); });
177 test_invalid([](AddQuadData& d) { d.const_scaling += fr(1); });
178}
179
180// Verifies that a valid arithmetic gate passes the circuit checker
182{
184 auto data = create_arithmetic_triple_data(5, 7);
185 builder.create_arithmetic_gate({ builder.add_variable(data.a),
186 builder.add_variable(data.b),
187 builder.add_variable(data.c),
188 data.q_m,
189 data.q_l,
190 data.q_r,
191 data.q_o,
192 data.q_c });
193 EXPECT_TRUE(CircuitChecker::check(builder));
194}
195
196// Verifies that invalidating any variable or selector coefficient in a arithmetic gate causes failure
198{
199 auto test_invalid = [](auto modify_data) {
201 auto data = create_arithmetic_triple_data(5, 7);
202 modify_data(data);
203 builder.create_arithmetic_gate({ builder.add_variable(data.a),
204 builder.add_variable(data.b),
205 builder.add_variable(data.c),
206 data.q_m,
207 data.q_l,
208 data.q_r,
209 data.q_o,
210 data.q_c });
211 EXPECT_FALSE(CircuitChecker::check(builder));
212 };
213
214 // Test witness failures
215 test_invalid([](ArithTripleData& d) { d.a += fr(1); });
216 test_invalid([](ArithTripleData& d) { d.b += fr(1); });
217 test_invalid([](ArithTripleData& d) { d.c += fr(1); });
218
219 // Test selector coefficient failures
220 test_invalid([](ArithTripleData& d) { d.q_m += fr(1); });
221 test_invalid([](ArithTripleData& d) { d.q_l += fr(1); });
222 test_invalid([](ArithTripleData& d) { d.q_r += fr(1); });
223 test_invalid([](ArithTripleData& d) { d.q_o += fr(1); });
224 test_invalid([](ArithTripleData& d) { d.q_c += fr(1); });
225}
226
227// Verifies that multiple independent gates can coexist in a circuit
229{
231
232 // Create three independent operations
233 auto add_data = create_add_triple_data(5, 7);
234 auto big_mul_data = create_mul_quad_data(3, 4);
235 auto arith_data = create_arithmetic_triple_data(2, 6);
236
237 // Add gate
238 uint32_t add_a = builder.add_variable(add_data.a);
239 uint32_t add_b = builder.add_variable(add_data.b);
240 uint32_t add_c = builder.add_variable(add_data.c);
241 builder.create_add_gate(
242 { add_a, add_b, add_c, add_data.a_scaling, add_data.b_scaling, add_data.c_scaling, add_data.const_scaling });
243
244 // Big mul gate
245 uint32_t a_idx = builder.add_variable(big_mul_data.a);
246 uint32_t b_idx = builder.add_variable(big_mul_data.b);
247 uint32_t c_idx = builder.add_variable(big_mul_data.c);
248 uint32_t d_idx = builder.add_variable(big_mul_data.d);
249
250 builder.create_big_mul_add_gate({ a_idx,
251 b_idx,
252 c_idx,
253 d_idx,
254 big_mul_data.mul_scaling,
255 big_mul_data.a_scaling,
256 big_mul_data.b_scaling,
257 big_mul_data.c_scaling,
258 big_mul_data.d_scaling,
259 big_mul_data.const_scaling },
260 /* use_next_gate_w_4 */ false);
261
262 // Arithmetic gate
263 uint32_t arith_a = builder.add_variable(arith_data.a);
264 uint32_t arith_b = builder.add_variable(arith_data.b);
265 uint32_t arith_c = builder.add_variable(arith_data.c);
266 builder.create_arithmetic_gate(
267 { arith_a, arith_b, arith_c, arith_data.q_m, arith_data.q_l, arith_data.q_r, arith_data.q_o, arith_data.q_c });
268
269 EXPECT_TRUE(CircuitChecker::check(builder));
270}
271
272// Verifies that arithmetic_gate can handle complex multi-term expressions
273TEST_F(UltraCircuitBuilderArithmetic, ArithmeticGateComplexExpression)
274{
276
277 // Polynomial: 3*a*b + 5*a - 2*b = c
278 fr a(7);
279 fr b(11);
280 fr c = fr(3) * a * b + fr(5) * a - fr(2) * b;
281
282 builder.create_arithmetic_gate({ builder.add_variable(a),
283 builder.add_variable(b),
284 builder.add_variable(c),
285 fr(3),
286 fr(5),
287 fr(-2),
288 fr(-1),
289 fr(0) });
290 EXPECT_TRUE(CircuitChecker::check(builder));
291}
292
293// Verifies that q_arith = 2 mode (with w_4_shift) works correctly
294// In this mode, the constraint includes the w_4 value from the NEXT row
295// Constraint: 2 * [q_m * w_1 * w_2 + \sum_{i=1..4} q_i * w_i + q_c + w_4_shift] = 0
296TEST_F(UltraCircuitBuilderArithmetic, BigAddGateWithNextRowW4)
297{
299
300 // First gate: a + b + c + d + next_w_4 = 0
301 // where next_w_4 comes from the w_4 wire of the following gate
302 fr a(3);
303 fr b(5);
304 fr c(7);
305 fr next_w_4(11); // This will be the w_4 of the next gate
306 fr d = -(a + b + c + next_w_4);
307
308 uint32_t a_idx = builder.add_variable(a);
309 uint32_t b_idx = builder.add_variable(b);
310 uint32_t c_idx = builder.add_variable(c);
311 uint32_t d_idx = builder.add_variable(d);
312 uint32_t next_w_4_idx = builder.add_variable(next_w_4);
313 uint32_t dummy_idx = builder.add_variable(fr(13));
314
315 // First gate with use_next_gate_w_4 = true (sets q_arith = 2)
316 builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, fr(1), fr(1), fr(1), fr(1), fr(0) },
317 /* use_next_gate_w_4 */ true);
318
319 // Second gate to provide the w_4 value
320 builder.create_big_add_gate({ dummy_idx, dummy_idx, dummy_idx, next_w_4_idx, fr(0), fr(0), fr(0), fr(0), fr(0) });
321
322 EXPECT_TRUE(CircuitChecker::check(builder));
323}
324
325// Verifies that q_arith = 2 mode fails when w_4_shift value is incorrect
326TEST_F(UltraCircuitBuilderArithmetic, BigAddGateWithNextRowW4Failure)
327{
329
330 // Set up the same as above but with WRONG d value
331 fr a(3);
332 fr b(5);
333 fr c(7);
334 fr next_w_4(11);
335 fr d = -(a + b + c + next_w_4) + fr(1); // INCORRECT: off by 1
336
337 uint32_t a_idx = builder.add_variable(a);
338 uint32_t b_idx = builder.add_variable(b);
339 uint32_t c_idx = builder.add_variable(c);
340 uint32_t d_idx = builder.add_variable(d);
341 uint32_t next_w_4_idx = builder.add_variable(next_w_4);
342 uint32_t dummy_idx = builder.add_variable(fr(13));
343
344 builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, fr(1), fr(1), fr(1), fr(1), fr(0) },
345 /* use_next_gate_w_4 */ true);
346
347 builder.create_big_add_gate({ dummy_idx, dummy_idx, dummy_idx, next_w_4_idx, fr(0), fr(0), fr(0), fr(0), fr(0) });
348
349 EXPECT_FALSE(CircuitChecker::check(builder));
350}
351
352// Verifies that a valid big_mul_add_gate without w_4_shift passes (q_arith = 1)
354{
356
357 // Constraint: a * b + c + d = 0, or equivalently d = -(a*b + c)
358 fr a(3);
359 fr b(5);
360 fr c(7);
361 fr d = -(a * b + c);
362
363 // create_big_mul_add_gate with include_next_gate_w_4=false uses q_arith=1
364 builder.create_big_mul_add_gate({ builder.add_variable(a),
365 builder.add_variable(b),
366 builder.add_variable(c),
367 builder.add_variable(d),
368 fr(1),
369 fr(0),
370 fr(0),
371 fr(1),
372 fr(1),
373 fr(0) },
374 /* use_next_gate_w_4 */ false);
375 EXPECT_TRUE(CircuitChecker::check(builder));
376}
377
378// Verifies that invalidating any variable or scaling coefficient in a big_mul_add_gate causes failure
380{
381 auto test_invalid = [](auto modify_data) {
383 auto data = create_mul_quad_data(5, 7, 3);
384 modify_data(data);
385 builder.create_big_mul_add_gate({ builder.add_variable(data.a),
386 builder.add_variable(data.b),
387 builder.add_variable(data.c),
388 builder.add_variable(data.d),
389 data.mul_scaling,
390 data.a_scaling,
391 data.b_scaling,
392 data.c_scaling,
393 data.d_scaling,
394 data.const_scaling },
395 /* use_next_gate_w_4 */ false);
396 EXPECT_FALSE(CircuitChecker::check(builder));
397 };
398
399 // Test witness failures
400 test_invalid([](MulQuadData& d) { d.a += fr(1); });
401 test_invalid([](MulQuadData& d) { d.b += fr(1); });
402 test_invalid([](MulQuadData& d) { d.c += fr(1); });
403 test_invalid([](MulQuadData& d) { d.d += fr(1); });
404
405 // Test scaling coefficient failures
406 test_invalid([](MulQuadData& d) { d.mul_scaling += fr(1); });
407 test_invalid([](MulQuadData& d) { d.a_scaling += fr(1); });
408 test_invalid([](MulQuadData& d) { d.b_scaling += fr(1); });
409 test_invalid([](MulQuadData& d) { d.c_scaling += fr(1); });
410 test_invalid([](MulQuadData& d) { d.d_scaling += fr(1); });
411 test_invalid([](MulQuadData& d) { d.const_scaling += fr(1); });
412}
413
414// Verifies that q_arith = 2 mode works with big_mul_add_gate
415TEST_F(UltraCircuitBuilderArithmetic, BigMulAddGateWithNextRowW4)
416{
418
419 // Constraint: a * b + c + d + next_w_4 = 0
420 fr a(3);
421 fr b(5);
422 fr c(7);
423 fr next_w_4(11);
424 fr d = -(a * b + c + next_w_4);
425
426 uint32_t a_idx = builder.add_variable(a);
427 uint32_t b_idx = builder.add_variable(b);
428 uint32_t c_idx = builder.add_variable(c);
429 uint32_t d_idx = builder.add_variable(d);
430 uint32_t next_w_4_idx = builder.add_variable(next_w_4);
431 uint32_t dummy_idx = builder.add_variable(fr(13));
432
433 // Note: mul_scaling is also adjusted for q_arith = 2 mode
434 builder.create_big_mul_add_gate({ a_idx, b_idx, c_idx, d_idx, fr(1), fr(0), fr(0), fr(1), fr(1), fr(0) },
435 /* use_next_gate_w_4 */ true);
436
437 builder.create_big_add_gate({ dummy_idx, dummy_idx, dummy_idx, next_w_4_idx, fr(0), fr(0), fr(0), fr(0), fr(0) });
438
439 EXPECT_TRUE(CircuitChecker::check(builder));
440}
441
442// Verifies that q_arith = 2 mode fails when w_4_shift value is incorrect for big_mul_add_gate
443TEST_F(UltraCircuitBuilderArithmetic, BigMulAddGateWithNextRowW4Failure)
444{
446
447 // Set up the same as above but with WRONG d value
448 fr a(3);
449 fr b(5);
450 fr c(7);
451 fr next_w_4(11);
452 fr d = -(a * b + c + next_w_4) + fr(1); // INCORRECT: off by 1
453
454 uint32_t a_idx = builder.add_variable(a);
455 uint32_t b_idx = builder.add_variable(b);
456 uint32_t c_idx = builder.add_variable(c);
457 uint32_t d_idx = builder.add_variable(d);
458 uint32_t next_w_4_idx = builder.add_variable(next_w_4);
459 uint32_t dummy_idx = builder.add_variable(fr(13));
460
461 builder.create_big_mul_add_gate({ a_idx, b_idx, c_idx, d_idx, fr(1), fr(0), fr(0), fr(1), fr(1), fr(0) },
462 /* use_next_gate_w_4 */ true);
463
464 builder.create_big_add_gate({ dummy_idx, dummy_idx, dummy_idx, next_w_4_idx, fr(0), fr(0), fr(0), fr(0), fr(0) });
465
466 EXPECT_FALSE(CircuitChecker::check(builder));
467}
468
469// Verifies that create_bool_gate works for boolean values (0 and 1)
471{
472 // Test that 0 passes
473 {
475 uint32_t zero_idx = builder.add_variable(fr(0));
476 builder.create_bool_gate(zero_idx);
477 EXPECT_TRUE(CircuitChecker::check(builder));
478 }
479
480 // Test that 1 passes
481 {
483 uint32_t one_idx = builder.add_variable(fr(1));
484 builder.create_bool_gate(one_idx);
485 EXPECT_TRUE(CircuitChecker::check(builder));
486 }
487}
488
489// Verifies that create_bool_gate fails for non-boolean values
491{
492 // Test that 2 fails
493 {
495 uint32_t two_idx = builder.add_variable(fr(2));
496 builder.create_bool_gate(two_idx);
497 EXPECT_FALSE(CircuitChecker::check(builder));
498 }
499
500 // Test that -1 fails
501 {
503 uint32_t neg_one_idx = builder.add_variable(fr(-1));
504 builder.create_bool_gate(neg_one_idx);
505 EXPECT_FALSE(CircuitChecker::check(builder));
506 }
507}
508
509// Verifies q_arith = 3 mode with both subrelations satisfied, and failures when tampered
510// When q_arith=3 mode, multiplication is disabled and two subrelations are active:
511// Subrelation 1 (primary): [q_1*w_1 + q_2*w_2 + q_3*w_3 + q_4*w_4 + q_c + 2*w_4_shift] * 3 = 0
512// Subrelation 2 (secondary): [w_1 + w_4 - w_1_shift + q_m] * 6 = 0
514{
515 auto build_and_check = [](auto modify_fn, bool expect_valid) {
517
518 // Baseline wire values
519 fr w_1(10);
520 fr w_2(5);
521 fr w_3(7);
522 fr w_4(20);
523 fr w_1_next(30);
524 fr w_4_next(3);
525
526 // Compute selectors to satisfy both subrelations
527 fr q_m = w_1_next - w_1 - w_4;
528 const fr scale = fr(2);
529 fr q_1 = scale;
530 fr q_2 = scale;
531 fr q_3 = scale;
532 fr q_4 = scale;
533 fr q_c = -(q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + fr(2) * w_4_next);
534
535 // Apply modification (if any)
536 modify_fn(w_1, w_2, w_3, w_4, w_1_next, w_4_next, q_m, q_1, q_2, q_3, q_4, q_c);
537
538 uint32_t w1_idx = builder.add_variable(w_1);
539 uint32_t w2_idx = builder.add_variable(w_2);
540 uint32_t w3_idx = builder.add_variable(w_3);
541 uint32_t w4_idx = builder.add_variable(w_4);
542 uint32_t w1_next_idx = builder.add_variable(w_1_next);
543 uint32_t w4_next_idx = builder.add_variable(w_4_next);
544
545 // Gate 1: q_arith = 3
546 builder.blocks.arithmetic.populate_wires(w1_idx, w2_idx, w3_idx, w4_idx);
547 builder.blocks.arithmetic.q_m().emplace_back(q_m);
548 builder.blocks.arithmetic.q_1().emplace_back(q_1);
549 builder.blocks.arithmetic.q_2().emplace_back(q_2);
550 builder.blocks.arithmetic.q_3().emplace_back(q_3);
551 builder.blocks.arithmetic.q_4().emplace_back(q_4);
552 builder.blocks.arithmetic.q_5().emplace_back(0);
553 builder.blocks.arithmetic.q_c().emplace_back(q_c);
554 builder.blocks.arithmetic.set_gate_selector(3);
555 builder.check_selector_length_consistency();
556 builder.increment_num_gates();
557
558 // Gate 2: provides w_1_shift and w_4_shift
559 builder.blocks.arithmetic.populate_wires(w1_next_idx, builder.zero_idx(), builder.zero_idx(), w4_next_idx);
560 builder.blocks.arithmetic.q_m().emplace_back(0);
561 builder.blocks.arithmetic.q_1().emplace_back(0);
562 builder.blocks.arithmetic.q_2().emplace_back(0);
563 builder.blocks.arithmetic.q_3().emplace_back(0);
564 builder.blocks.arithmetic.q_4().emplace_back(0);
565 builder.blocks.arithmetic.q_5().emplace_back(0);
566 builder.blocks.arithmetic.q_c().emplace_back(0);
567 builder.blocks.arithmetic.set_gate_selector(1);
568 builder.check_selector_length_consistency();
569 builder.increment_num_gates();
570
571 if (expect_valid) {
572 EXPECT_TRUE(CircuitChecker::check(builder));
573 } else {
574 EXPECT_FALSE(CircuitChecker::check(builder));
575 }
576 };
577
578 // Test baseline: no modifications, should pass
579 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&) {}, true);
580
581 // Test witness failures (affect primary subrelation)
582 build_and_check([](fr& w_1, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&) { w_1 += fr(1); }, false);
583 build_and_check([](fr&, fr& w_2, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&) { w_2 += fr(1); }, false);
584 build_and_check([](fr&, fr&, fr& w_3, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&) { w_3 += fr(1); }, false);
585 build_and_check([](fr&, fr&, fr&, fr& w_4, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&) { w_4 += fr(1); }, false);
586
587 // Test shift wire failures
588 build_and_check([](fr&, fr&, fr&, fr&, fr& w_1_next, fr&, fr&, fr&, fr&, fr&, fr&, fr&) { w_1_next += fr(1); },
589 false);
590 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr& w_4_next, fr&, fr&, fr&, fr&, fr&, fr&) { w_4_next += fr(1); },
591 false);
592
593 // Test selector failures
594 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr&, fr& q_m, fr&, fr&, fr&, fr&, fr&) { q_m += fr(1); }, false);
595 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr& q_1, fr&, fr&, fr&, fr&) { q_1 += fr(1); }, false);
596 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr& q_2, fr&, fr&, fr&) { q_2 += fr(1); }, false);
597 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr& q_3, fr&, fr&) { q_3 += fr(1); }, false);
598 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr& q_4, fr&) { q_4 += fr(1); }, false);
599 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr& q_c) { q_c += fr(1); }, false);
600}
601
602// Verifies that multiplication by zero works correctly
604{
606
607 // Test: 0 * 5 = 0
608 fr zero = fr(0);
609 fr five = fr(5);
610 fr result = fr(0);
611
612 // q_m * w_1 * w_2 + q_o * w_3 = 0, where w_1=0, w_2=5, w_3=0
613 builder.create_arithmetic_gate({ builder.add_variable(zero),
614 builder.add_variable(five),
615 builder.add_variable(result),
616 fr(1),
617 fr(0),
618 fr(0),
619 fr(-1),
620 fr(0) });
621 EXPECT_TRUE(CircuitChecker::check(builder));
622}
623
624// Verifies that using fixed witnesses in arithmetic gates works
626{
628
629 // Create fixed witnesses in two different ways
630 uint32_t const_5 = builder.put_constant_variable(fr(5));
631 uint32_t const_7 = builder.add_variable(fr(7));
632 builder.fix_witness(const_7, fr(7)); // Fix it to ensure it stays 7
633
634 // Use them in an arithmetic gate: 5 + 7 = 12
635 fr result = fr(12);
636 uint32_t result_idx = builder.add_variable(result);
637
638 builder.create_add_gate({ const_5, const_7, result_idx, fr(1), fr(1), fr(-1), fr(0) });
639
640 EXPECT_TRUE(CircuitChecker::check(builder));
641}
642
643// Verifies behavior with field boundary values (values near modulus)
645{
647
648 // Test with -1 (modulus - 1 in field)
649 fr minus_one = fr(-1);
650 fr one = fr(1);
651 fr zero = fr(0);
652
653 // -1 + 1 = 0
654 builder.create_add_gate({ builder.add_variable(minus_one),
655 builder.add_variable(one),
656 builder.add_variable(zero),
657 fr(1),
658 fr(1),
659 fr(-1),
660 fr(0) });
661 EXPECT_TRUE(CircuitChecker::check(builder));
662}
663
664// Verifies that all-zero gates pass (trivial constraint)
666{
668
669 uint32_t zero = builder.zero_idx();
670
671 // All wires zero, all scalings zero: 0*0 + 0*0 + 0*0 + 0*0 + 0 = 0
672 builder.create_arithmetic_gate({ zero, zero, zero, fr(0), fr(0), fr(0), fr(0), fr(0) });
673
674 EXPECT_TRUE(CircuitChecker::check(builder));
675}
676
677// Verifies that builder.zero_idx() works as expected in gates
679{
681
682 // Even though a=5 and b=7, if their scalings are 0, only c matters
683 fr a(5);
684 fr b(7);
685
686 // 1*5 + 1*7 + 1*c = 12, so c must be 0
687 builder.create_add_gate(
688 { builder.add_variable(a), builder.add_variable(b), builder.zero_idx(), fr(1), fr(1), fr(1), fr(-12) });
689 EXPECT_TRUE(CircuitChecker::check(builder));
690}
691
692// Verifies that zero scaling factors effectively disable wires
694{
696
697 // Even though a=5 and b=7, if their scalings are 0, only c matters
698 fr a(5);
699 fr b(7);
700 fr c(0); // Only this needs to be correct
701
702 // 0*a + 0*b + (-1)*c = 0, so c must be 0
703 builder.create_add_gate(
704 { builder.add_variable(a), builder.add_variable(b), builder.add_variable(c), fr(0), fr(0), fr(-1), fr(0) });
705 EXPECT_TRUE(CircuitChecker::check(builder));
706}
707
708// Verifies complex big_mul_add_gate with all parameters non-zero
709TEST_F(UltraCircuitBuilderArithmetic, BigMulAddAllParametersNonZero)
710{
712
713 // mul_scaling * a * b + a_scaling * a + b_scaling * b + c_scaling * c + d_scaling * d + const = 0
714 fr mul_scaling(2);
715 fr a_scaling(3);
716 fr b_scaling(5);
717 fr c_scaling(7);
718 fr d_scaling(11);
719 fr const_scaling(13);
720
721 fr a(2);
722 fr b(3);
723 fr c(4);
724
725 // Solve for d: d = -(mul*a*b + a_s*a + b_s*b + c_s*c + const) / d_s
726 fr d = -(mul_scaling * a * b + a_scaling * a + b_scaling * b + c_scaling * c + const_scaling) / d_scaling;
727
728 builder.create_big_mul_add_gate({ builder.add_variable(a),
729 builder.add_variable(b),
730 builder.add_variable(c),
731 builder.add_variable(d),
732 mul_scaling,
733 a_scaling,
734 b_scaling,
735 c_scaling,
736 d_scaling,
737 const_scaling },
738 /* use_next_gate_w_4 */ false);
739 EXPECT_TRUE(CircuitChecker::check(builder));
740}
741
742// Verifies public input variables work in arithmetic gates
743TEST_F(UltraCircuitBuilderArithmetic, PublicInputInArithmetic)
744{
746
747 // Add a public input
748 fr public_value(100);
749 uint32_t public_idx = builder.add_public_variable(public_value);
750
751 // Use it in an arithmetic constraint
752 fr private_value(50);
753 fr result = public_value + private_value;
754
755 // public + private = result
756 builder.create_add_gate(
757 { public_idx, builder.add_variable(private_value), builder.add_variable(result), fr(1), fr(1), fr(-1), fr(0) });
758 EXPECT_TRUE(CircuitChecker::check(builder));
759}
Test suite for UltraCircuitBuilder arithmetic gate methods.
static MulQuadData create_mul_quad_data(uint64_t a_val=5, uint64_t b_val=7, uint64_t c_val=3)
static AddTripleData create_add_triple_data(uint64_t a_val=5, uint64_t b_val=7)
static AddQuadData create_add_quad_data(uint64_t a_val=3, uint64_t b_val=5, uint64_t c_val=7)
static ArithTripleData create_arithmetic_triple_data(uint64_t a_val=5, uint64_t b_val=7)
virtual uint32_t add_variable(const FF &in)
Add a variable to variables.
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
uint32_t put_constant_variable(const FF &variable)
AluTraceBuilder builder
Definition alu.test.cpp:124
const std::vector< MemoryValue > data
FF a
FF b
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FrParams > fr
Definition fr.hpp:155
TEST_F(UltraCircuitBuilderArithmetic, AddGate)