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_c().emplace_back(q_c);
553 builder.blocks.arithmetic.set_gate_selector(3);
554 builder.check_selector_length_consistency();
555 builder.increment_num_gates();
556
557 // Gate 2: provides w_1_shift and w_4_shift
558 builder.blocks.arithmetic.populate_wires(w1_next_idx, builder.zero_idx(), builder.zero_idx(), w4_next_idx);
559 builder.blocks.arithmetic.q_m().emplace_back(0);
560 builder.blocks.arithmetic.q_1().emplace_back(0);
561 builder.blocks.arithmetic.q_2().emplace_back(0);
562 builder.blocks.arithmetic.q_3().emplace_back(0);
563 builder.blocks.arithmetic.q_4().emplace_back(0);
564 builder.blocks.arithmetic.q_c().emplace_back(0);
565 builder.blocks.arithmetic.set_gate_selector(1);
566 builder.check_selector_length_consistency();
567 builder.increment_num_gates();
568
569 if (expect_valid) {
570 EXPECT_TRUE(CircuitChecker::check(builder));
571 } else {
572 EXPECT_FALSE(CircuitChecker::check(builder));
573 }
574 };
575
576 // Test baseline: no modifications, should pass
577 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&) {}, true);
578
579 // Test witness failures (affect primary subrelation)
580 build_and_check([](fr& w_1, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&) { w_1 += fr(1); }, false);
581 build_and_check([](fr&, fr& w_2, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&) { w_2 += fr(1); }, false);
582 build_and_check([](fr&, fr&, fr& w_3, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&) { w_3 += fr(1); }, false);
583 build_and_check([](fr&, fr&, fr&, fr& w_4, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&) { w_4 += fr(1); }, false);
584
585 // Test shift wire failures
586 build_and_check([](fr&, fr&, fr&, fr&, fr& w_1_next, fr&, fr&, fr&, fr&, fr&, fr&, fr&) { w_1_next += fr(1); },
587 false);
588 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr& w_4_next, fr&, fr&, fr&, fr&, fr&, fr&) { w_4_next += fr(1); },
589 false);
590
591 // Test selector failures
592 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr&, fr& q_m, fr&, fr&, fr&, fr&, fr&) { q_m += fr(1); }, false);
593 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr& q_1, fr&, fr&, fr&, fr&) { q_1 += fr(1); }, false);
594 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr& q_2, fr&, fr&, fr&) { q_2 += fr(1); }, false);
595 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr& q_3, fr&, fr&) { q_3 += fr(1); }, false);
596 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr& q_4, fr&) { q_4 += fr(1); }, false);
597 build_and_check([](fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr&, fr& q_c) { q_c += fr(1); }, false);
598}
599
600// Verifies that multiplication by zero works correctly
602{
604
605 // Test: 0 * 5 = 0
606 fr zero = fr(0);
607 fr five = fr(5);
608 fr result = fr(0);
609
610 // q_m * w_1 * w_2 + q_o * w_3 = 0, where w_1=0, w_2=5, w_3=0
611 builder.create_arithmetic_gate({ builder.add_variable(zero),
612 builder.add_variable(five),
613 builder.add_variable(result),
614 fr(1),
615 fr(0),
616 fr(0),
617 fr(-1),
618 fr(0) });
619 EXPECT_TRUE(CircuitChecker::check(builder));
620}
621
622// Verifies that using fixed witnesses in arithmetic gates works
624{
626
627 // Create fixed witnesses in two different ways
628 uint32_t const_5 = builder.put_constant_variable(fr(5));
629 uint32_t const_7 = builder.add_variable(fr(7));
630 builder.fix_witness(const_7, fr(7)); // Fix it to ensure it stays 7
631
632 // Use them in an arithmetic gate: 5 + 7 = 12
633 fr result = fr(12);
634 uint32_t result_idx = builder.add_variable(result);
635
636 builder.create_add_gate({ const_5, const_7, result_idx, fr(1), fr(1), fr(-1), fr(0) });
637
638 EXPECT_TRUE(CircuitChecker::check(builder));
639}
640
641// Verifies behavior with field boundary values (values near modulus)
643{
645
646 // Test with -1 (modulus - 1 in field)
647 fr minus_one = fr(-1);
648 fr one = fr(1);
649 fr zero = fr(0);
650
651 // -1 + 1 = 0
652 builder.create_add_gate({ builder.add_variable(minus_one),
653 builder.add_variable(one),
654 builder.add_variable(zero),
655 fr(1),
656 fr(1),
657 fr(-1),
658 fr(0) });
659 EXPECT_TRUE(CircuitChecker::check(builder));
660}
661
662// Verifies that all-zero gates pass (trivial constraint)
664{
666
667 uint32_t zero = builder.zero_idx();
668
669 // All wires zero, all scalings zero: 0*0 + 0*0 + 0*0 + 0*0 + 0 = 0
670 builder.create_arithmetic_gate({ zero, zero, zero, fr(0), fr(0), fr(0), fr(0), fr(0) });
671
672 EXPECT_TRUE(CircuitChecker::check(builder));
673}
674
675// Verifies that builder.zero_idx() works as expected in gates
677{
679
680 // Even though a=5 and b=7, if their scalings are 0, only c matters
681 fr a(5);
682 fr b(7);
683
684 // 1*5 + 1*7 + 1*c = 12, so c must be 0
685 builder.create_add_gate(
686 { builder.add_variable(a), builder.add_variable(b), builder.zero_idx(), fr(1), fr(1), fr(1), fr(-12) });
687 EXPECT_TRUE(CircuitChecker::check(builder));
688}
689
690// Verifies that zero scaling factors effectively disable wires
692{
694
695 // Even though a=5 and b=7, if their scalings are 0, only c matters
696 fr a(5);
697 fr b(7);
698 fr c(0); // Only this needs to be correct
699
700 // 0*a + 0*b + (-1)*c = 0, so c must be 0
701 builder.create_add_gate(
702 { builder.add_variable(a), builder.add_variable(b), builder.add_variable(c), fr(0), fr(0), fr(-1), fr(0) });
703 EXPECT_TRUE(CircuitChecker::check(builder));
704}
705
706// Verifies complex big_mul_add_gate with all parameters non-zero
707TEST_F(UltraCircuitBuilderArithmetic, BigMulAddAllParametersNonZero)
708{
710
711 // mul_scaling * a * b + a_scaling * a + b_scaling * b + c_scaling * c + d_scaling * d + const = 0
712 fr mul_scaling(2);
713 fr a_scaling(3);
714 fr b_scaling(5);
715 fr c_scaling(7);
716 fr d_scaling(11);
717 fr const_scaling(13);
718
719 fr a(2);
720 fr b(3);
721 fr c(4);
722
723 // Solve for d: d = -(mul*a*b + a_s*a + b_s*b + c_s*c + const) / d_s
724 fr d = -(mul_scaling * a * b + a_scaling * a + b_scaling * b + c_scaling * c + const_scaling) / d_scaling;
725
726 builder.create_big_mul_add_gate({ builder.add_variable(a),
727 builder.add_variable(b),
728 builder.add_variable(c),
729 builder.add_variable(d),
730 mul_scaling,
731 a_scaling,
732 b_scaling,
733 c_scaling,
734 d_scaling,
735 const_scaling },
736 /* use_next_gate_w_4 */ false);
737 EXPECT_TRUE(CircuitChecker::check(builder));
738}
739
740// Verifies public input variables work in arithmetic gates
741TEST_F(UltraCircuitBuilderArithmetic, PublicInputInArithmetic)
742{
744
745 // Add a public input
746 fr public_value(100);
747 uint32_t public_idx = builder.add_public_variable(public_value);
748
749 // Use it in an arithmetic constraint
750 fr private_value(50);
751 fr result = public_value + private_value;
752
753 // public + private = result
754 builder.create_add_gate(
755 { public_idx, builder.add_variable(private_value), builder.add_variable(result), fr(1), fr(1), fr(-1), fr(0) });
756 EXPECT_TRUE(CircuitChecker::check(builder));
757}
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:174
TEST_F(UltraCircuitBuilderArithmetic, AddGate)