Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
biggroup.test.cpp
Go to the documentation of this file.
1#include "../biggroup/biggroup.hpp"
2#include "../bigfield/bigfield.hpp"
3#include "../bool/bool.hpp"
4#include "../field/field.hpp"
15#include <vector>
16
17using namespace bb;
18
19namespace {
21}
22
23enum struct InputType {
24 WITNESS,
26};
27
32
33template <typename T>
35
36// One can only define a TYPED_TEST with a single template paramter.
37// Our workaround is to pass parameters of the following type.
38template <typename _Curve, bool _use_bigfield = false> struct TestType {
39 public:
40 using Curve = _Curve;
41 static const bool use_bigfield = _use_bigfield;
42 using element_ct =
43 typename std::conditional<_use_bigfield, typename Curve::g1_bigfr_ct, typename Curve::Group>::type;
44 // the field of scalars acting on element_ct
45 using scalar_ct =
46 typename std::conditional<_use_bigfield, typename Curve::bigfr_ct, typename Curve::ScalarField>::type;
47};
48
50template <typename TestType> class stdlib_biggroup : public testing::Test {
51 public:
52 using Curve = typename TestType::Curve;
55
56 using fq = typename Curve::BaseFieldNative;
57 using fr = typename Curve::ScalarFieldNative;
58 using g1 = typename Curve::GroupNative;
60 using element = typename g1::element;
61
62 using Builder = typename Curve::Builder;
66
67 static constexpr auto EXPECT_CIRCUIT_CORRECTNESS = [](Builder& builder, bool expected_result = true) {
68 info("num gates = ", builder.get_num_finalized_gates_inefficient());
70 };
71
72 // Create a random point as a witness
74 {
75 affine_element point_native(element::random_element());
76 element_ct point_ct = element_ct::from_witness(builder, point_native);
77 return std::make_pair(point_native, point_ct);
78 }
79
80 // Create a random point as a constant
82 {
83 affine_element point_native(element::random_element());
84 // Create constant coordinates with builder context
85 using Fq = typename element_ct::BaseField;
86 Fq x_const(builder, uint256_t(point_native.x));
87 Fq y_const(builder, uint256_t(point_native.y));
88 element_ct point_ct(x_const, y_const);
89 return std::make_pair(point_native, point_ct);
90 }
91
92 // Create a random point based on InputType
100
101 // Create a random scalar as a witness
103 {
104 fr scalar_native = fr::random_element();
105 if (even && uint256_t(scalar_native).get_bit(0)) {
106 scalar_native -= fr(1); // make it even if it's odd
107 }
108 scalar_ct scalar_ct_val = scalar_ct::from_witness(builder, scalar_native);
109 return std::make_pair(scalar_native, scalar_ct_val);
110 }
111
112 // Create a random scalar as a constant
114 {
115 fr scalar_native = fr::random_element();
116 if (even && uint256_t(scalar_native).get_bit(0)) {
117 scalar_native -= fr(1); // make it even if it's odd
118 }
119 scalar_ct scalar_ct_val = scalar_ct(builder, scalar_native);
120 return std::make_pair(scalar_native, scalar_ct_val);
121 }
122
123 // Create a random scalar based on InputType
125 {
126 if (type == InputType::WITNESS) {
128 }
130 }
131
133 {
134 uint256_t scalar_u256 = engine.get_random_uint256();
135 scalar_u256 = scalar_u256 >> (256 - num_bits); // keep only the lower num_bits bits
136
137 fr scalar_native(scalar_u256);
138 scalar_ct scalar_ct_val;
139 if (type == InputType::WITNESS) {
140 scalar_ct_val = scalar_ct::from_witness(builder, scalar_native);
141 } else {
142 scalar_ct_val = scalar_ct(builder, scalar_native);
143 }
144 return std::make_pair(scalar_native, scalar_ct_val);
145 }
146
147 public:
149 {
151 affine_element input_a(element::random_element());
152
153 element_ct a = element_ct::from_witness(&builder, input_a);
154 a.set_origin_tag(next_submitted_value_origin_tag);
155 // Tag is preserved after being set
156 EXPECT_EQ(a.get_origin_tag(), next_submitted_value_origin_tag);
157
158 // Tags from members are merged
159 // Create field elements with specific tags before constructing the biggroup element
160 affine_element input_c(element::random_element());
161 auto x = element_ct::BaseField::from_witness(&builder, input_c.x);
162 auto y = element_ct::BaseField::from_witness(&builder, input_c.y);
163 auto pif = bool_ct(witness_ct(&builder, false));
164
165 // Set tags on the individual field elements
166 x.set_origin_tag(submitted_value_origin_tag);
167 y.set_origin_tag(challenge_origin_tag);
168 pif.set_origin_tag(next_challenge_tag);
169
170 // Construct biggroup element from pre-tagged field elements
171 element_ct c(x, y, pif);
172
173 // The tag of the biggroup element should be the union of all 3 member tags
174 EXPECT_EQ(c.get_origin_tag(), first_second_third_merged_tag);
175
176#ifndef NDEBUG
177 // Test that instant_death_tag on x coordinate propagates correctly
178 affine_element input_b(element::random_element());
179 auto x_death = element_ct::BaseField::from_witness(&builder, input_b.x);
180 auto y_normal = element_ct::BaseField::from_witness(&builder, input_b.y);
181 auto pif_normal = bool_ct(witness_ct(&builder, false));
182
183 x_death.set_origin_tag(instant_death_tag);
184
185 element_ct b(x_death, y_normal, pif_normal);
186 // Working with instant death tagged element causes an exception
187 EXPECT_THROW(b + b, std::runtime_error);
188#endif
189 }
190
192 {
193 // Only test for non-goblin builders (goblin elements don't have assert_coordinates_in_field
194 // because coordinate checks are done in the ECCVM circuit)
195 if constexpr (!HasGoblinBuilder<TestType>) {
196 // Test 1: Valid coordinates should pass
197 {
199
200 // Test multiple random points to ensure assert_coordinates_in_field works correctly
201 for (size_t i = 0; i < 3; ++i) {
202 affine_element valid_point(element::random_element());
203 element_ct point = element_ct::from_witness(&builder, valid_point);
204
205 // This should not fail - coordinates are in field
206 point.assert_coordinates_in_field();
207 }
208
209 // Verify the circuit is correct
211 }
212
213 // Test 2: Invalid x coordinate should cause circuit to fail
214 {
216 affine_element valid_point(element::random_element());
217
218 // Create a bigfield element with x coordinate that will be out of range
219 // We do this by creating a valid witness but then manipulating the limb values
220 // to make them represent a value >= the modulus
221 auto x_coord = element_ct::BaseField::from_witness(&builder, valid_point.x);
222 auto y_coord = element_ct::BaseField::from_witness(&builder, valid_point.y);
223
224 // Manipulate the limbs to create an invalid value
225 // Set the highest limb to a very large value that would make the total >= modulus
226 x_coord.binary_basis_limbs[3].element = field_ct::from_witness(&builder, bb::fr(uint256_t(1) << 68));
227 x_coord.binary_basis_limbs[3].maximum_value = uint256_t(1) << 68;
228
229 // Skip curve check since we're intentionally creating an invalid point
230 element_ct point(x_coord, y_coord, bool_ct(witness_ct(&builder, false)), /*assert_on_curve=*/false);
231 point.assert_coordinates_in_field();
232
233 // Circuit should fail because x coordinate is out of field
235 }
236
237 // Test 3: Invalid y coordinate should cause circuit to fail
238 {
240 affine_element valid_point(element::random_element());
241
242 auto x_coord = element_ct::BaseField::from_witness(&builder, valid_point.x);
243 auto y_coord = element_ct::BaseField::from_witness(&builder, valid_point.y);
244
245 // Manipulate the limbs to create an invalid value
246 // Set the highest limb to a very large value that would make the total >= modulus
247 y_coord.binary_basis_limbs[3].element = field_ct::from_witness(&builder, bb::fr(uint256_t(1) << 68));
248 y_coord.binary_basis_limbs[3].maximum_value = uint256_t(1) << 68;
249
250 // Skip curve check since we're intentionally creating an invalid point
251 element_ct point(x_coord, y_coord, bool_ct(witness_ct(&builder, false)), /*assert_on_curve=*/false);
252 point.assert_coordinates_in_field();
253
254 // Circuit should fail because y coordinate is out of field
256 }
257 }
258 }
259
261 {
263 size_t num_repetitions = 10;
264 for (size_t i = 0; i < num_repetitions; ++i) {
265 auto [input_a, a] = get_random_point(&builder, a_type);
266 auto [input_b, b] = get_random_point(&builder, b_type);
267
268 // Set different tags in a and b
269 a.set_origin_tag(submitted_value_origin_tag);
270 b.set_origin_tag(challenge_origin_tag);
271
272 uint64_t before = builder.get_num_finalized_gates_inefficient();
273 element_ct c = a + b;
274 uint64_t after = builder.get_num_finalized_gates_inefficient();
275
276 // Check that the resulting tag is the union of inputs' tgs
277 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
278 if (i == num_repetitions - 1) {
279 benchmark_info(Builder::NAME_STRING, "Biggroup", "ADD", "Gate Count", after - before);
280 }
281
282 affine_element c_expected(element(input_a) + element(input_b));
283
284 uint256_t c_x_u256 = c.x().get_value().lo;
285 uint256_t c_y_u256 = c.y().get_value().lo;
286
287 fq c_x_result(c_x_u256);
288 fq c_y_result(c_y_u256);
289
290 EXPECT_EQ(c_x_result, c_expected.x);
291 EXPECT_EQ(c_y_result, c_expected.y);
292 }
293
295 }
296
298 {
300 size_t num_repetitions = 10;
301 for (size_t i = 0; i < num_repetitions; ++i) {
302 auto [input_a, a] = get_random_point(&builder, a_type);
303 auto [input_b, b] = get_random_point(&builder, b_type);
304
305 element_ct original_a = a;
306 a += b;
307
308 affine_element expected(element(input_a) + element(input_b));
309 uint256_t result_x = a.x().get_value().lo;
310 uint256_t result_y = a.y().get_value().lo;
311
312 EXPECT_EQ(fq(result_x), expected.x);
313 EXPECT_EQ(fq(result_y), expected.y);
314 }
316 }
317
319 {
321 size_t num_repetitions = 1;
322 for (size_t i = 0; i < num_repetitions; ++i) {
323 affine_element input_a(element::random_element());
324 affine_element input_b(element::random_element());
325 input_b.self_set_infinity();
326 element_ct a = element_ct::from_witness(&builder, input_a);
327
328 // create copy of a with different witness
329 element_ct a_alternate = element_ct::from_witness(&builder, input_a);
330 element_ct a_negated = element_ct::from_witness(&builder, -input_a);
331 element_ct b = element_ct::from_witness(&builder, input_b);
332
333 // Set different tags on all elements
334 a.set_origin_tag(submitted_value_origin_tag);
335 b.set_origin_tag(challenge_origin_tag);
336 a_alternate.set_origin_tag(next_challenge_tag);
337 // We can't use next_submitted_value tag here or it will break, so construct a tag manually
338 const auto second_round_challenge_tag =
339 OriginTag(/*parent_index=*/0, /*child_index=*/2, /*is_submitted=*/false);
340 a_negated.set_origin_tag(second_round_challenge_tag);
341
342 element_ct c = a + b;
343 element_ct d = b + a;
344 element_ct e = b + b;
345 element_ct f = a + a;
346 element_ct g = a + a_alternate;
347 element_ct h = a + a_negated;
348
349 // Check the resulting tags are correct unions of input tags
350 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
351 EXPECT_EQ(d.get_origin_tag(), first_two_merged_tag);
352 EXPECT_EQ(e.get_origin_tag(), challenge_origin_tag);
353 EXPECT_EQ(f.get_origin_tag(), submitted_value_origin_tag);
354 EXPECT_EQ(g.get_origin_tag(), first_and_third_merged_tag);
355 EXPECT_EQ(h.get_origin_tag(), OriginTag(submitted_value_origin_tag, second_round_challenge_tag));
356
357 affine_element c_expected = affine_element(element(input_a) + element(input_b));
358 affine_element d_expected = affine_element(element(input_b) + element(input_a));
359 affine_element e_expected = affine_element(element(input_b) + element(input_b));
360 affine_element f_expected = affine_element(element(input_a) + element(input_a));
361 affine_element g_expected = affine_element(element(input_a) + element(input_a));
362 affine_element h_expected = affine_element(element(input_a) + element(-input_a));
363
364 EXPECT_EQ(c.get_value(), c_expected);
365 EXPECT_EQ(d.get_value(), d_expected);
366 EXPECT_EQ(e.get_value(), e_expected);
367 EXPECT_EQ(f.get_value(), f_expected);
368 EXPECT_EQ(g.get_value(), g_expected);
369 EXPECT_EQ(h.get_value(), h_expected);
370 }
371
373 }
379 {
381 size_t num_repetitions = 5;
382 for (size_t i = 0; i < num_repetitions; ++i) {
383 // Check both constant and witness case
384 element_ct input_a(element::random_element());
385 element_ct input_b = element_ct::from_witness(&builder, element::random_element());
386 input_a.set_point_at_infinity(true);
387 input_b.set_point_at_infinity(true);
388
389 // Set tags
390 input_a.set_origin_tag(submitted_value_origin_tag);
391 input_b.set_origin_tag(challenge_origin_tag);
392
393 auto standard_a = input_a.get_standard_form();
394 auto standard_b = input_b.get_standard_form();
395
396 // Check that tags are preserved
397 EXPECT_EQ(standard_a.get_origin_tag(), submitted_value_origin_tag);
398 EXPECT_EQ(standard_b.get_origin_tag(), challenge_origin_tag);
399
400 EXPECT_EQ(standard_a.is_point_at_infinity().get_value(), true);
401 EXPECT_EQ(standard_b.is_point_at_infinity().get_value(), true);
402
403 fq standard_a_x = standard_a.x().get_value().lo;
404 fq standard_a_y = standard_a.y().get_value().lo;
405
406 fq standard_b_x = standard_b.x().get_value().lo;
407 fq standard_b_y = standard_b.y().get_value().lo;
408
409 EXPECT_EQ(standard_a_x, 0);
410 EXPECT_EQ(standard_a_y, 0);
411 EXPECT_EQ(standard_b_x, 0);
412 EXPECT_EQ(standard_b_y, 0);
413 }
414
416 }
417
419 {
421 size_t num_repetitions = 10;
422 for (size_t i = 0; i < num_repetitions; ++i) {
423 auto [input_a, a] = get_random_point(&builder, a_type);
424 auto [input_b, b] = get_random_point(&builder, b_type);
425
426 // Set tags
427 a.set_origin_tag(submitted_value_origin_tag);
428 b.set_origin_tag(challenge_origin_tag);
429
430 element_ct c = a - b;
431
432 // Check tags have merged
433 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
434
435 affine_element c_expected(element(input_a) - element(input_b));
436
437 uint256_t c_x_u256 = c.x().get_value().lo;
438 uint256_t c_y_u256 = c.y().get_value().lo;
439
440 fq c_x_result(c_x_u256);
441 fq c_y_result(c_y_u256);
442
443 EXPECT_EQ(c_x_result, c_expected.x);
444 EXPECT_EQ(c_y_result, c_expected.y);
445 }
446
448 }
449
451 {
453 size_t num_repetitions = 10;
454 for (size_t i = 0; i < num_repetitions; ++i) {
455 auto [input_a, a] = get_random_point(&builder, a_type);
456 auto [input_b, b] = get_random_point(&builder, b_type);
457
458 a -= b;
459
460 affine_element expected(element(input_a) - element(input_b));
461 uint256_t result_x = a.x().get_value().lo;
462 uint256_t result_y = a.y().get_value().lo;
463
464 EXPECT_EQ(fq(result_x), expected.x);
465 EXPECT_EQ(fq(result_y), expected.y);
466 }
468 }
469
471 {
473 size_t num_repetitions = 1;
474 for (size_t i = 0; i < num_repetitions; ++i) {
475 affine_element input_a(element::random_element());
476 affine_element input_b(element::random_element());
477 input_b.self_set_infinity();
478 element_ct a = element_ct::from_witness(&builder, input_a);
479
480 // create copy of a with different witness
481 element_ct a_alternate = element_ct::from_witness(&builder, input_a);
482 element_ct a_negated = element_ct::from_witness(&builder, -input_a);
483 element_ct b = element_ct::from_witness(&builder, input_b);
484
485 // Set different tags on all elements
486 a.set_origin_tag(submitted_value_origin_tag);
487 b.set_origin_tag(challenge_origin_tag);
488 a_alternate.set_origin_tag(next_challenge_tag);
489 // We can't use next_submitted_value tag here or it will break, so construct a tag manually
490 const auto second_round_challenge_tag =
491 OriginTag(/*parent_index=*/0, /*child_index=*/2, /*is_submitted=*/false);
492 a_negated.set_origin_tag(second_round_challenge_tag);
493
494 element_ct c = a - b;
495 element_ct d = b - a;
496 element_ct e = b - b;
497 element_ct f = a - a;
498 element_ct g = a - a_alternate;
499 element_ct h = a - a_negated;
500
501 // Check the resulting tags are correct unions of input tags
502 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
503 EXPECT_EQ(d.get_origin_tag(), first_two_merged_tag);
504 EXPECT_EQ(e.get_origin_tag(), challenge_origin_tag);
505 EXPECT_EQ(f.get_origin_tag(), submitted_value_origin_tag);
506 EXPECT_EQ(g.get_origin_tag(), first_and_third_merged_tag);
507 EXPECT_EQ(h.get_origin_tag(), OriginTag(submitted_value_origin_tag, second_round_challenge_tag));
508
509 affine_element c_expected = affine_element(element(input_a) - element(input_b));
510 affine_element d_expected = affine_element(element(input_b) - element(input_a));
511 affine_element e_expected = affine_element(element(input_b) - element(input_b));
512 affine_element f_expected = affine_element(element(input_a) - element(input_a));
513 affine_element g_expected = affine_element(element(input_a) - element(input_a));
514 affine_element h_expected = affine_element(element(input_a) - element(-input_a));
515
516 EXPECT_EQ(c.get_value(), c_expected);
517 EXPECT_EQ(d.get_value(), d_expected);
518 EXPECT_EQ(e.get_value(), e_expected);
519 EXPECT_EQ(f.get_value(), f_expected);
520 EXPECT_EQ(g.get_value(), g_expected);
521 EXPECT_EQ(h.get_value(), h_expected);
522 }
523
525 }
526
529 {
531 size_t num_repetitions = 10;
532 for (size_t i = 0; i < num_repetitions; ++i) {
533 auto [input_a, a] = get_random_point(&builder, a_type);
534 auto [input_b, b] = get_random_point(&builder, b_type);
535
536 element_ct result = a.checked_unconditional_add(b);
537
538 affine_element expected(element(input_a) + element(input_b));
539 uint256_t result_x = result.x().get_value().lo;
540 uint256_t result_y = result.y().get_value().lo;
541
542 EXPECT_EQ(fq(result_x), expected.x);
543 EXPECT_EQ(fq(result_y), expected.y);
544 }
546 }
547
550 {
552 size_t num_repetitions = 10;
553 for (size_t i = 0; i < num_repetitions; ++i) {
554 auto [input_a, a] = get_random_point(&builder, a_type);
555 auto [input_b, b] = get_random_point(&builder, b_type);
556
557 element_ct result = a.checked_unconditional_subtract(b);
558
559 affine_element expected(element(input_a) - element(input_b));
560 uint256_t result_x = result.x().get_value().lo;
561 uint256_t result_y = result.y().get_value().lo;
562
563 EXPECT_EQ(fq(result_x), expected.x);
564 EXPECT_EQ(fq(result_y), expected.y);
565 }
567 }
568
571 {
573 size_t num_repetitions = 10;
574 for (size_t i = 0; i < num_repetitions; ++i) {
575 const auto [input_a, a] = get_random_point(&builder, a_type);
576 const auto [input_b, b] = get_random_point(&builder, b_type);
577
578 // Since unchecked_unconditional_add_sub is private in biggroup, we test it via the element_test_accessor
580
581 affine_element expected_sum(element(input_a) + element(input_b));
582 affine_element expected_diff(element(input_a) - element(input_b));
583
584 uint256_t sum_x = sum.x().get_value().lo;
585 uint256_t sum_y = sum.y().get_value().lo;
586 uint256_t diff_x = diff.x().get_value().lo;
587 uint256_t diff_y = diff.y().get_value().lo;
588
589 EXPECT_EQ(fq(sum_x), expected_sum.x);
590 EXPECT_EQ(fq(sum_y), expected_sum.y);
591 EXPECT_EQ(fq(diff_x), expected_diff.x);
592 EXPECT_EQ(fq(diff_y), expected_diff.y);
593 }
595 }
596
598 {
600 size_t num_repetitions = 10;
601 for (size_t i = 0; i < num_repetitions; ++i) {
602 auto [input_a, a] = get_random_point(&builder, a_type);
603
604 a.set_origin_tag(submitted_value_origin_tag);
605
606 element_ct c = a.dbl();
607
608 // Check that the tag is preserved
609 EXPECT_EQ(c.get_origin_tag(), submitted_value_origin_tag);
610
611 affine_element c_expected(element(input_a).dbl());
612
613 uint256_t c_x_u256 = c.x().get_value().lo;
614 uint256_t c_y_u256 = c.y().get_value().lo;
615
616 fq c_x_result(c_x_u256);
617 fq c_y_result(c_y_u256);
618
619 EXPECT_EQ(c_x_result, c_expected.x);
620 EXPECT_EQ(c_y_result, c_expected.y);
621 }
623 }
624
626 {
628 {
629 // Case 1: Doubling point at infinity should return point at infinity
630 affine_element input_infinity(element::random_element());
631 input_infinity.self_set_infinity();
632 element_ct a_infinity = element_ct::from_witness(&builder, input_infinity);
633 a_infinity.set_origin_tag(submitted_value_origin_tag);
634
635 element_ct result_infinity = a_infinity.dbl();
636
637 // Check that the tag is preserved
638 EXPECT_EQ(result_infinity.get_origin_tag(), submitted_value_origin_tag);
639
640 // Result should be point at infinity
641 EXPECT_TRUE(result_infinity.is_point_at_infinity().get_value());
642 }
643 {
644 // Case 2: Doubling a normal point should not result in infinity
645 affine_element input_normal(element::random_element());
646 element_ct a_normal = element_ct::from_witness(&builder, input_normal);
647 a_normal.set_origin_tag(submitted_value_origin_tag);
648
649 element_ct result_normal = a_normal.dbl();
650
651 // Check that the tag is preserved
652 EXPECT_EQ(result_normal.get_origin_tag(), submitted_value_origin_tag);
653
654 // Result should not be point at infinity (with overwhelming probability)
655 EXPECT_FALSE(result_normal.is_point_at_infinity().get_value());
656
657 // Verify correctness
658 affine_element expected_normal(element(input_normal).dbl());
659 uint256_t result_x = result_normal.x().get_value().lo;
660 uint256_t result_y = result_normal.y().get_value().lo;
661 fq expected_x(result_x);
662 fq expected_y(result_y);
663 EXPECT_EQ(expected_x, expected_normal.x);
664 EXPECT_EQ(expected_y, expected_normal.y);
665 }
667 }
668
670 {
672
673 // For bn254 curve: y^2 = x^3 + 3
674 // We need a point where y = 0, which means x^3 = -3
675 // For most curves, there may not be a rational point with y = 0
676 // So we test the logic by creating a witness point with y = 0 explicitly
677 // Even if it's not on the curve, we can test the doubling logic
678 affine_element test_point(element::random_element());
679
680 // Create a point with y = 0 (may not be on curve, but tests the edge case)
681 auto x_coord = element_ct::BaseField::from_witness(&builder, test_point.x);
682 auto y_coord = element_ct::BaseField::from_witness(&builder, fq(0));
683 // Skip curve check since we're intentionally creating an invalid point to test edge case
684 element_ct a(x_coord, y_coord, bool_ct(witness_ct(&builder, false)), /*assert_on_curve=*/false);
685
686 a.set_origin_tag(submitted_value_origin_tag);
687
688 // With the new assertion, attempting to double a point with y = 0 should throw
689 // because for valid curves like bn254, y = 0 cannot occur on the curve
690 EXPECT_THROW_OR_ABORT(a.dbl(), "Attempting to dbl a point with y = 0, not allowed.");
691 }
692
694 {
695 // Test that P + P equals P.dbl()
697 size_t num_repetitions = 5;
698 for (size_t i = 0; i < num_repetitions; ++i) {
699 auto [input_a, a] = get_random_point(&builder, InputType::WITNESS);
700
701 element_ct sum = a + a;
702 element_ct doubled = a.dbl();
703
704 // Results should match
705 uint256_t sum_x = sum.x().get_value().lo;
706 uint256_t sum_y = sum.y().get_value().lo;
707 uint256_t dbl_x = doubled.x().get_value().lo;
708 uint256_t dbl_y = doubled.y().get_value().lo;
709
710 EXPECT_EQ(fq(sum_x), fq(dbl_x));
711 EXPECT_EQ(fq(sum_y), fq(dbl_y));
712 EXPECT_EQ(sum.is_point_at_infinity().get_value(), doubled.is_point_at_infinity().get_value());
713 }
715 }
716
718 {
719 // Test that P - (-P) equals 2P
721 size_t num_repetitions = 5;
722 for (size_t i = 0; i < num_repetitions; ++i) {
723 auto [input_a, a] = get_random_point(&builder, InputType::WITNESS);
724
725 element_ct neg_a = -a;
726 element_ct result = a - neg_a;
727 element_ct expected = a.dbl();
728
729 // P - (-P) = P + P = 2P
730 uint256_t result_x = result.x().get_value().lo;
731 uint256_t result_y = result.y().get_value().lo;
732 uint256_t expected_x = expected.x().get_value().lo;
733 uint256_t expected_y = expected.y().get_value().lo;
734
735 EXPECT_EQ(fq(result_x), fq(expected_x));
736 EXPECT_EQ(fq(result_y), fq(expected_y));
737 }
739 }
740
744 {
746 size_t num_repetitions = 10;
747 for (size_t i = 0; i < num_repetitions; ++i) {
748
749 auto [input_a, a] = get_random_point(&builder, a_type);
750 auto [input_b, b] = get_random_point(&builder, b_type);
751 auto [input_c, c] = get_random_point(&builder, c_type);
752
753 auto acc = element_ct::chain_add_start(a, b);
754 auto acc_out = element_ct::chain_add(c, acc);
755 element_ct result = element_ct::chain_add_end(acc_out);
756
757 // Verify result
758 affine_element expected(element(input_a) + element(input_b) + element(input_c));
759 uint256_t result_x = result.x().get_value().lo;
760 uint256_t result_y = result.y().get_value().lo;
761 EXPECT_EQ(fq(result_x), expected.x);
762 EXPECT_EQ(fq(result_y), expected.y);
763
764 // Check intermediate values
765 auto lambda_prev = (input_b.y - input_a.y) / (input_b.x - input_a.x);
766 auto x3_prev = lambda_prev * lambda_prev - input_b.x - input_a.x;
767 auto y3_prev = lambda_prev * (input_a.x - x3_prev) - input_a.y;
768 auto lambda = (y3_prev - input_c.y) / (x3_prev - input_c.x);
769 auto x3 = lambda * lambda - x3_prev - input_c.x;
770
771 uint256_t x3_u256 = acc_out.x3_prev.get_value().lo;
772 uint256_t lambda_u256 = acc_out.lambda_prev.get_value().lo;
773
774 fq x3_result(x3_u256);
775 fq lambda_result(lambda_u256);
776
777 EXPECT_EQ(x3_result, x3);
778 EXPECT_EQ(lambda_result, lambda);
779 }
780
782 }
783
785 {
787 size_t num_repetitions = 10;
788 for (size_t i = 0; i < num_repetitions; ++i) {
789 affine_element acc_small(element::random_element());
790 element_ct acc_big = element_ct::from_witness(&builder, acc_small);
791
793 for (size_t j = 0; j < i; ++j) {
794 affine_element add_1_small_0(element::random_element());
795 element_ct add_1_big_0 = element_ct::from_witness(&builder, add_1_small_0);
796 affine_element add_2_small_0(element::random_element());
797 element_ct add_2_big_0 = element_ct::from_witness(&builder, add_2_small_0);
798 typename element_ct::chain_add_accumulator add_1 =
799 element_ct::chain_add_start(add_1_big_0, add_2_big_0);
800 to_add.emplace_back(add_1);
801 }
802 acc_big.multiple_montgomery_ladder(to_add);
803 }
804
806 }
807
809 {
811 size_t num_repetitions = 10;
812 for (size_t i = 0; i < num_repetitions; ++i) {
813 auto [input_a, a] = get_random_point(&builder, point_type);
814
815 element_ct normalized = a.normalize();
816
817 // Normalized should equal the original
818 uint256_t x_before = a.x().get_value().lo;
819 uint256_t y_before = a.y().get_value().lo;
820 uint256_t x_after = normalized.x().get_value().lo;
821 uint256_t y_after = normalized.y().get_value().lo;
822
823 EXPECT_EQ(fq(x_before), fq(x_after));
824 EXPECT_EQ(fq(y_before), fq(y_after));
825 }
827 }
828
829 static void test_reduce(InputType point_type = InputType::WITNESS)
830 {
832 size_t num_repetitions = 10;
833 for (size_t i = 0; i < num_repetitions; ++i) {
834 auto [input_a, a] = get_random_point(&builder, point_type);
835
836 element_ct reduced = a.reduce();
837
838 // Reduced should equal the original
839 uint256_t x_before = a.x().get_value().lo;
840 uint256_t y_before = a.y().get_value().lo;
841 uint256_t x_after = reduced.x().get_value().lo;
842 uint256_t y_after = reduced.y().get_value().lo;
843
844 EXPECT_EQ(fq(x_before), fq(x_after));
845 EXPECT_EQ(fq(y_before), fq(y_after));
846 }
848 }
849
851 {
853 auto [input_a, a] = get_random_point(&builder, a_type);
854
855 element_ct neg_a = -a;
856
857 affine_element expected = affine_element(-element(input_a));
858 uint512_t neg_x_u512 = uint512_t(neg_a.x().get_value()) % uint512_t(fq::modulus);
859 uint512_t neg_y_u512 = uint512_t(neg_a.y().get_value()) % uint512_t(fq::modulus);
860 uint256_t neg_x = neg_x_u512.lo;
861 uint256_t neg_y = neg_y_u512.lo;
862
863 EXPECT_EQ(fq(neg_x), expected.x);
864 EXPECT_EQ(fq(neg_y), expected.y);
865
867 }
868
870 InputType predicate_type = InputType::WITNESS)
871 {
873 size_t num_repetitions = 10;
874 for (size_t i = 0; i < num_repetitions; ++i) {
875 // Get random point
876 auto [input_a, a] = get_random_point(&builder, point_type);
877 a.set_origin_tag(submitted_value_origin_tag);
878
879 // Get random predicate
880 bool predicate_value = (engine.get_random_uint8() % 2) != 0;
881 bool_ct predicate = (predicate_type == InputType::WITNESS) ? bool_ct(witness_ct(&builder, predicate_value))
882 : bool_ct(predicate_value);
883 predicate.set_origin_tag(challenge_origin_tag);
884
885 element_ct c = a.conditional_negate(predicate);
886
887 // Check the resulting tag is preserved
888 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
889
890 affine_element c_expected = predicate_value ? affine_element(-element(input_a)) : input_a;
891 EXPECT_EQ(c.get_value(), c_expected);
892 }
894 }
895
898 InputType predicate_type = InputType::WITNESS)
899 {
901 size_t num_repetitions = 10;
902 for (size_t i = 0; i < num_repetitions; ++i) {
903 auto [input_a, a] = get_random_point(&builder, a_type);
904 auto [input_b, b] = get_random_point(&builder, b_type);
905
906 bool predicate_value = (engine.get_random_uint8() % 2) != 0;
907 bool_ct predicate = (predicate_type == InputType::WITNESS) ? bool_ct(witness_ct(&builder, predicate_value))
908 : bool_ct(predicate_value);
909
910 // Set different tags in a and b and the predicate
911 a.set_origin_tag(submitted_value_origin_tag);
912 b.set_origin_tag(challenge_origin_tag);
913 predicate.set_origin_tag(next_challenge_tag);
914
915 element_ct c = a.conditional_select(b, predicate);
916
917 // Check that the resulting tag is the union of inputs' tags
918 EXPECT_EQ(c.get_origin_tag(), first_second_third_merged_tag);
919
920 affine_element c_expected = predicate_value ? input_b : input_a;
921 EXPECT_EQ(c.get_value(), c_expected);
922 }
924 }
925
927 {
928 // Case 1: Should pass because the points are identical
929 {
931 size_t num_repetitions = 10;
932 for (size_t i = 0; i < num_repetitions; ++i) {
933 affine_element input_a(element::random_element());
934 element_ct a = element_ct::from_witness(&builder, input_a);
935 element_ct b = element_ct::from_witness(&builder, input_a);
936
937 // Set different tags in a and b
938 a.set_origin_tag(submitted_value_origin_tag);
939 b.set_origin_tag(challenge_origin_tag);
940
941 a.incomplete_assert_equal(b, "elements don't match");
942 }
944 }
945 // Case 2: Should pass because the points are identical and at infinity
946 {
948 size_t num_repetitions = 10;
949 for (size_t i = 0; i < num_repetitions; ++i) {
950 affine_element input_a(element::random_element());
951 element_ct a = element_ct::from_witness(&builder, input_a);
952 element_ct b = element_ct::from_witness(&builder, input_a);
953
954 // Set different tags in a and b
955 a.set_origin_tag(submitted_value_origin_tag);
956 b.set_origin_tag(challenge_origin_tag);
957
958 a.set_point_at_infinity(bool_ct(witness_ct(&builder, true)));
959 b.set_point_at_infinity(bool_ct(witness_ct(&builder, true)));
960
961 a.incomplete_assert_equal(b, "elements don't match");
962 }
964 }
965 // Case 3: Self-assertion (point equals itself)
966 {
968 affine_element input(element::random_element());
969 element_ct a = element_ct::from_witness(&builder, input);
970
971 a.incomplete_assert_equal(a, "self assertion test");
972
974 }
975 }
976
978 {
979 // Case 1: Should fail because the points are different
980 {
982 affine_element input_a(element::random_element());
983 affine_element input_b(element::random_element());
984 // Ensure inputs are different
985 while (input_a == input_b) {
986 input_b = element::random_element();
987 }
988 element_ct a = element_ct::from_witness(&builder, input_a);
989 element_ct b = element_ct::from_witness(&builder, input_b);
990
991 // Set different tags in a and b
992 a.set_origin_tag(submitted_value_origin_tag);
993 b.set_origin_tag(challenge_origin_tag);
994
995 a.incomplete_assert_equal(b, "elements don't match");
996
997 // Circuit should fail (Circuit checker doesn't fail because it doesn't actually check copy constraints,
998 // it only checks gate constraints)
999 EXPECT_EQ(builder.failed(), true);
1000 EXPECT_EQ(builder.err(), "elements don't match (x coordinate)");
1001 }
1002 // Case 2: Should fail because the points have same x but different y
1003 {
1005 affine_element input_a(element::random_element());
1006
1007 // Create a point with the same x coordinate but different y
1008 // For an elliptic curve y^2 = x^3 + ax + b, if (x, y) is on the curve, then (x, -y) is also on the
1009 // curve
1010 affine_element input_b = input_a;
1011 input_b.y = -input_a.y; // Negate y to get a different point with same x
1012
1013 // Construct the circuit elements with same x but different y
1014 auto x_coord = element_ct::BaseField::from_witness(&builder, input_a.x);
1015 auto y_coord_a = element_ct::BaseField::from_witness(&builder, input_a.y);
1016 auto y_coord_b = element_ct::BaseField::from_witness(&builder, input_b.y);
1017
1018 element_ct a(x_coord, y_coord_a, bool_ct(witness_ct(&builder, false)));
1019 element_ct b(x_coord, y_coord_b, bool_ct(witness_ct(&builder, false)));
1020
1021 // Set different tags in a and b
1022 a.set_origin_tag(submitted_value_origin_tag);
1023 b.set_origin_tag(challenge_origin_tag);
1024
1025 a.incomplete_assert_equal(b, "elements don't match");
1026
1027 // Circuit should fail with y coordinate error
1028 EXPECT_EQ(builder.failed(), true);
1029 EXPECT_EQ(builder.err(), "elements don't match (y coordinate)");
1030 }
1031 // Case 3: Infinity flag mismatch (one point at infinity, one not)
1032 {
1034 affine_element input_a(element::random_element());
1035 affine_element input_b(element::random_element());
1036
1037 element_ct a = element_ct::from_witness(&builder, input_a);
1038 element_ct b = element_ct::from_witness(&builder, input_b);
1039
1040 // Set only one point at infinity
1041 a.set_point_at_infinity(bool_ct(witness_ct(&builder, true))); // at infinity
1042 b.set_point_at_infinity(bool_ct(witness_ct(&builder, false))); // not at infinity
1043
1044 a.incomplete_assert_equal(b, "infinity flag mismatch test");
1045
1046 EXPECT_EQ(builder.failed(), true);
1047 EXPECT_EQ(builder.err(), "infinity flag mismatch test (infinity flag)");
1048 }
1049 }
1050
1052 {
1054 // Check that two points at infinity with different x,y coords fail the equality check
1055 affine_element input_a(element::random_element());
1056 affine_element input_b(element::random_element());
1057
1058 // Ensure inputs are different
1059 while (input_a == input_b) {
1060 input_b = element::random_element();
1061 }
1062 element_ct a = element_ct::from_witness(&builder, input_a);
1063 element_ct b = element_ct::from_witness(&builder, input_b);
1064
1065 const bool_ct is_infinity = bool_ct(witness_ct(&builder, 1));
1066 a.set_point_at_infinity(is_infinity);
1067 b.set_point_at_infinity(is_infinity);
1068
1069 // Set different tags in a and b
1070 a.set_origin_tag(submitted_value_origin_tag);
1071 b.set_origin_tag(challenge_origin_tag);
1072
1073 a.incomplete_assert_equal(b, "points at infinity with different x,y should not be equal");
1074
1075 // Circuit should fail
1076 EXPECT_EQ(builder.failed(), true);
1077 EXPECT_EQ(builder.err(), "points at infinity with different x,y should not be equal (x coordinate)");
1078 }
1079
1080 static void test_compute_naf()
1081 {
1083 size_t max_num_bits = 254;
1084 for (size_t length = 2; length < max_num_bits; length += 1) {
1085
1086 fr scalar_val;
1087
1088 uint256_t scalar_raw = engine.get_random_uint256();
1089 scalar_raw = scalar_raw >> (256 - length);
1090
1091 scalar_val = fr(scalar_raw);
1092
1093 // We test non-zero scalars here
1094 if (scalar_val == fr(0)) {
1095 scalar_val += 1;
1096 };
1097 scalar_ct scalar = scalar_ct::from_witness(&builder, scalar_val);
1098 // Set tag for scalar
1099 scalar.set_origin_tag(submitted_value_origin_tag);
1100 auto naf = element_ct::compute_naf(scalar, length);
1101
1102 for (const auto& bit : naf) {
1103 // Check that the tag is propagated to bits
1104 EXPECT_EQ(bit.get_origin_tag(), submitted_value_origin_tag);
1105 }
1106 // scalar = -naf[L] + \sum_{i=0}^{L-1}(1-2*naf[i]) 2^{L-1-i}
1107 fr reconstructed_val(0);
1108 for (size_t i = 0; i < length; i++) {
1109 reconstructed_val += (fr(1) - fr(2) * fr(naf[i].get_value())) * fr(uint256_t(1) << (length - 1 - i));
1110 };
1111 reconstructed_val -= fr(naf[length].get_value());
1112 EXPECT_EQ(scalar_val, reconstructed_val);
1113 }
1114
1116 }
1117
1119 {
1121 size_t length = fr::modulus.get_msb() + 1;
1122
1123 // Our algorithm for input 0 outputs the NAF representation of r (the field modulus)
1124 fr scalar_val(0);
1125
1126 scalar_ct scalar = scalar_ct::from_witness(&builder, scalar_val);
1127
1128 // Set tag for scalar
1129 scalar.set_origin_tag(submitted_value_origin_tag);
1130 auto naf = element_ct::compute_naf(scalar, length);
1131
1132 for (const auto& bit : naf) {
1133 // Check that the tag is propagated to bits
1134 EXPECT_EQ(bit.get_origin_tag(), submitted_value_origin_tag);
1135 }
1136
1137 // scalar = -naf[L] + \sum_{i=0}^{L-1}(1-2*naf[i]) 2^{L-1-i}
1138 fr reconstructed_val(0);
1139 uint256_t reconstructed_u256(0);
1140 for (size_t i = 0; i < length; i++) {
1141 reconstructed_val += (fr(1) - fr(2) * fr(naf[i].get_value())) * fr(uint256_t(1) << (length - 1 - i));
1142 reconstructed_u256 +=
1143 (uint256_t(1) - uint256_t(2) * uint256_t(naf[i].get_value())) * (uint256_t(1) << (length - 1 - i));
1144 };
1145 reconstructed_val -= fr(naf[length].get_value());
1146 EXPECT_EQ(scalar_val, reconstructed_val);
1147 EXPECT_EQ(reconstructed_u256, uint256_t(fr::modulus));
1148
1150 }
1151
1152 static void test_mul(InputType scalar_type = InputType::WITNESS, InputType point_type = InputType::WITNESS)
1153 {
1155 size_t num_repetitions = 1;
1156 for (size_t i = 0; i < num_repetitions; ++i) {
1157 auto [input, P] = get_random_point(&builder, point_type);
1158 auto [scalar, x] = get_random_scalar(&builder, scalar_type, /*even*/ true);
1159
1160 // Set input tags
1161 x.set_origin_tag(challenge_origin_tag);
1162 P.set_origin_tag(submitted_value_origin_tag);
1163
1164 std::cerr << "gates before mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1165 element_ct c = P * x;
1166 std::cerr << "builder aftr mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1167 affine_element c_expected(element(input) * scalar);
1168
1169 // Check the result of the multiplication has a tag that's the union of inputs' tags
1170 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
1171 fq c_x_result(c.x().get_value().lo);
1172 fq c_y_result(c.y().get_value().lo);
1173
1174 EXPECT_EQ(c_x_result, c_expected.x);
1175 EXPECT_EQ(c_y_result, c_expected.y);
1176 }
1177
1179 }
1180
1182 InputType point_type = InputType::WITNESS)
1183 {
1185
1186 const auto run_mul_and_check = [&](element_ct& P, scalar_ct& x, const affine_element& expected) {
1187 // Set input tags
1188 x.set_origin_tag(challenge_origin_tag);
1189 P.set_origin_tag(submitted_value_origin_tag);
1190
1191 // Perform multiplication
1192 element_ct result = P * x;
1193
1194 // Check the result tag
1195 EXPECT_EQ(result.get_origin_tag(), first_two_merged_tag);
1196
1197 // Check if result is infinity
1198 bool result_is_inf = result.is_point_at_infinity().get_value();
1199 bool expected_is_inf = expected.is_point_at_infinity();
1200
1201 EXPECT_EQ(result_is_inf, expected_is_inf);
1202
1203 // If not infinity, check if the coordinates match
1204 if (!expected_is_inf) {
1205 uint256_t result_x = result.x().get_value().lo;
1206 uint256_t result_y = result.y().get_value().lo;
1207
1208 EXPECT_EQ(fq(result_x), expected.x);
1209 EXPECT_EQ(fq(result_y), expected.y);
1210 }
1211 };
1212
1213 // Case 1: P * 0 = ∞
1214 {
1215 auto [input, P] = get_random_point(&builder, point_type);
1216 scalar_ct x = (scalar_type == InputType::WITNESS) ? scalar_ct::from_witness(&builder, fr(0))
1217 : scalar_ct(&builder, fr(0));
1218 affine_element expected_infinity = affine_element(element::infinity());
1219 run_mul_and_check(P, x, expected_infinity);
1220 }
1221 // Case 2: (∞) * k = ∞
1222 {
1223 auto [input, P] = get_random_point(&builder, point_type);
1224 if (point_type == InputType::CONSTANT) {
1225 P.set_point_at_infinity(bool_ct(true));
1226 } else {
1227 P.set_point_at_infinity(bool_ct(witness_ct(&builder, true)));
1228 }
1229
1230 // For mega circuit builder, we need to ensure the point at infinity is (0, 0)
1231 if constexpr (IsMegaBuilder<Builder>) {
1232 P = P.get_standard_form();
1233 }
1234
1235 auto [scalar, x] = get_random_scalar(&builder, scalar_type, /*even*/ true);
1236 affine_element expected_infinity = affine_element(element::infinity());
1237 run_mul_and_check(P, x, expected_infinity);
1238 }
1239 // Case 3: P * 1 = P
1240 {
1241 auto [input, P] = get_random_point(&builder, point_type);
1242 scalar_ct one = (scalar_type == InputType::WITNESS) ? scalar_ct::from_witness(&builder, fr(1))
1243 : scalar_ct(&builder, fr(1));
1244 run_mul_and_check(P, one, input);
1245 }
1246 // Case 4: P * (-1) = -P
1247 {
1248 auto [input, P] = get_random_point(&builder, point_type);
1249 fr neg_one = -fr(1);
1250 scalar_ct neg_one_ct = (scalar_type == InputType::WITNESS) ? scalar_ct::from_witness(&builder, neg_one)
1251 : scalar_ct(&builder, neg_one);
1252 affine_element expected = affine_element(-element(input));
1253 run_mul_and_check(P, neg_one_ct, expected);
1254 }
1256 }
1257
1258 // Test short scalar mul with variable bit lengths.
1260 {
1262
1263 std::vector<size_t> test_lengths = { 2, 3, 10, 11, 31, 32, 63, 64, 127, 128, 252, 253 };
1264
1265 for (size_t i : test_lengths) {
1266 affine_element input(element::random_element());
1267 // Get a random 256 integer
1268 uint256_t scalar_raw = engine.get_random_uint256();
1269 // Produce a length =< i scalar.
1270 scalar_raw = scalar_raw >> (256 - i);
1271 fr scalar = fr(scalar_raw);
1272
1273 // Avoid multiplication by 0 that may occur when `i` is small
1274 if (scalar == fr(0)) {
1275 scalar += 1;
1276 };
1277
1278 element_ct P = element_ct::from_witness(&builder, input);
1279 scalar_ct x = scalar_ct::from_witness(&builder, scalar);
1280
1281 // Set input tags
1282 x.set_origin_tag(challenge_origin_tag);
1283 P.set_origin_tag(submitted_value_origin_tag);
1284
1285 std::cerr << "gates before mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1286 // Multiply using specified scalar length
1287 element_ct c = P.scalar_mul(x, i);
1288 std::cerr << "builder aftr mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1289 affine_element c_expected(element(input) * scalar);
1290
1291 // Check the result of the multiplication has a tag that's the union of inputs' tags
1292 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
1293 fq c_x_result(c.x().get_value().lo);
1294 fq c_y_result(c.y().get_value().lo);
1295
1296 EXPECT_EQ(c_x_result, c_expected.x);
1297
1298 EXPECT_EQ(c_y_result, c_expected.y);
1299 }
1300
1302 }
1303
1305 {
1306 // We check that a point at infinity preserves `is_point_at_infinity()` flag after being multiplied against
1307 // a short scalar and also check that the number of gates in this case is more than the number of gates
1308 // spent on a finite point.
1309
1310 // Populate test points.
1311 std::vector<element> points(2);
1312
1313 points[0] = element::infinity();
1314 points[1] = element::random_element();
1315 // Containter for gate counts.
1316 std::vector<size_t> gates(2);
1317
1318 // We initialize this flag as `true`, because the first result is expected to be the point at infinity.
1319 bool expect_infinity = true;
1320
1321 for (auto [point, num_gates] : zip_view(points, gates)) {
1323
1324 const size_t max_num_bits = 128;
1325 // Get a random 256-bit integer
1326 uint256_t scalar_raw = engine.get_random_uint256();
1327 // Produce a length =< max_num_bits scalar.
1328 scalar_raw = scalar_raw >> (256 - max_num_bits);
1329 fr scalar = fr(scalar_raw);
1330
1331 element_ct P = element_ct::from_witness(&builder, point);
1332 scalar_ct x = scalar_ct::from_witness(&builder, scalar);
1333
1334 // Set input tags
1335 x.set_origin_tag(challenge_origin_tag);
1336 P.set_origin_tag(submitted_value_origin_tag);
1337
1338 std::cerr << "gates before mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1339 element_ct c = P.scalar_mul(x, max_num_bits);
1340 std::cerr << "builder aftr mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1341 num_gates = builder.get_num_finalized_gates_inefficient();
1342 // Check the result of the multiplication has a tag that's the union of inputs' tags
1343 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
1344
1345 EXPECT_EQ(c.is_point_at_infinity().get_value(), expect_infinity);
1347 // The second point is finite, hence we flip the flag
1348 expect_infinity = false;
1349 }
1350 // Check that the numbers of gates are greater when multiplying by point at infinity,
1351 // because we transform (s * ∞) into (0 * G), and NAF representation of 0 ≡ NAF(r) which is 254 bits long.
1352 EXPECT_GT(gates[0], gates[1]);
1353 }
1354
1355 static void test_twin_mul()
1356 {
1358 size_t num_repetitions = 1;
1359 for (size_t i = 0; i < num_repetitions; ++i) {
1360 affine_element input_a(element::random_element());
1361 affine_element input_b(element::random_element());
1362 fr scalar_a(fr::random_element());
1363 fr scalar_b(fr::random_element());
1364 if ((uint256_t(scalar_a).get_bit(0) & 1) == 1) {
1365 scalar_a -= fr(1); // skew bit is 1
1366 }
1367 if ((uint256_t(scalar_b).get_bit(0) & 1) == 0) {
1368 scalar_b += fr(1); // skew bit is 0
1369 }
1370 element_ct P_a = element_ct::from_witness(&builder, input_a);
1371 scalar_ct x_a = scalar_ct::from_witness(&builder, scalar_a);
1372 element_ct P_b = element_ct::from_witness(&builder, input_b);
1373 scalar_ct x_b = scalar_ct::from_witness(&builder, scalar_b);
1374
1375 // Set tags
1376 P_a.set_origin_tag(submitted_value_origin_tag);
1377 x_a.set_origin_tag(challenge_origin_tag);
1378 P_b.set_origin_tag(next_submitted_value_origin_tag);
1379 x_b.set_origin_tag(next_challenge_tag);
1380
1381 element_ct c = element_ct::batch_mul({ P_a, P_b }, { x_a, x_b });
1382
1383 // Check that the resulting tag is a union of all tags
1384 EXPECT_EQ(c.get_origin_tag(), first_to_fourth_merged_tag);
1385 element input_c = (element(input_a) * scalar_a);
1386 element input_d = (element(input_b) * scalar_b);
1387 affine_element expected(input_c + input_d);
1388 fq c_x_result(c.x().get_value().lo);
1389 fq c_y_result(c.y().get_value().lo);
1390
1391 EXPECT_EQ(c_x_result, expected.x);
1392 EXPECT_EQ(c_y_result, expected.y);
1393 }
1395 }
1396
1398 {
1400 size_t num_repetitions = 1;
1401 for (size_t i = 0; i < num_repetitions; ++i) {
1402 affine_element input_a(element::random_element());
1403 affine_element input_b(element::random_element());
1404 input_b.self_set_infinity();
1405
1406 // Get two 128-bit scalars
1407 const size_t max_num_bits = 128;
1408 uint256_t scalar_raw_a = engine.get_random_uint256();
1409 scalar_raw_a = scalar_raw_a >> (256 - max_num_bits);
1410 fr scalar_a = fr(scalar_raw_a);
1411
1412 uint256_t scalar_raw_b = engine.get_random_uint256();
1413 scalar_raw_b = scalar_raw_b >> (256 - max_num_bits);
1414 fr scalar_b = fr(scalar_raw_b);
1415
1416 element_ct P_a = element_ct::from_witness(&builder, input_a); // A
1417 scalar_ct x_a = scalar_ct::from_witness(&builder, scalar_a); // s_1 (128 bits)
1418 element_ct P_b = element_ct::from_witness(&builder, input_b); // ∞
1419 scalar_ct x_b = scalar_ct::from_witness(&builder, scalar_b); // s_2 (128 bits)
1420
1421 // Set tags
1422 P_a.set_origin_tag(submitted_value_origin_tag);
1423 x_a.set_origin_tag(challenge_origin_tag);
1424 P_b.set_origin_tag(next_submitted_value_origin_tag);
1425 x_b.set_origin_tag(next_challenge_tag);
1426
1427 element_ct c = element_ct::batch_mul({ P_a, P_b }, { x_a, x_b }, 128);
1428
1429 // Check that the resulting tag is a union of all tags
1430 EXPECT_EQ(c.get_origin_tag(), first_to_fourth_merged_tag);
1431 element input_c = (element(input_a) * scalar_a);
1432 element input_d = (element(input_b) * scalar_b);
1433 affine_element expected(input_c + input_d);
1434 fq c_x_result(c.x().get_value().lo);
1435 fq c_y_result(c.y().get_value().lo);
1436
1437 EXPECT_EQ(c_x_result, expected.x);
1438 EXPECT_EQ(c_y_result, expected.y);
1439 }
1441 }
1442
1444 {
1446 affine_element input_P(element::random_element());
1447
1448 affine_element input_P_a = affine_element(element(input_P) + element(input_P)); // 2P
1449 affine_element input_P_b = affine_element(element(input_P_a) + element(input_P)); // 3P
1450 affine_element input_P_c = affine_element(element(input_P_a) + element(input_P_b)); // 5P
1451 std::vector<affine_element> input_points = { input_P_a, input_P_b, input_P_c };
1452
1453 // Choose scalars such that their NAF representations are:
1454 // skew msd lsd
1455 // a: 0 [+1, +1, -1, +1] = -0 + 2^3 + 2^2 - 2^1 + 2^0 = 11
1456 // b: 1 [+1, +1, +1, +1] = -1 + 2^3 + 2^2 + 2^1 + 2^0 = 14
1457 // c: 1 [+1, -1, +1, +1] = -1 + 2^3 - 2^2 + 2^1 + 2^0 = 6
1458 fr scalar_a(11);
1459 fr scalar_b(14);
1460 fr scalar_c(6);
1461 std::vector<fr> input_scalars = { scalar_a, scalar_b, scalar_c };
1462
1463 OriginTag tag_union{};
1464 std::vector<scalar_ct> scalars;
1466 for (size_t i = 0; i < 3; ++i) {
1467 const element_ct point = element_ct::from_witness(&builder, input_points[i]);
1468 point.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true));
1469 tag_union = OriginTag(tag_union, point.get_origin_tag());
1470
1471 const scalar_ct scalar = scalar_ct::from_witness(&builder, input_scalars[i]);
1472 scalar.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false));
1473 tag_union = OriginTag(tag_union, scalar.get_origin_tag());
1474
1475 scalars.emplace_back(scalar);
1476 points.emplace_back(point);
1477 }
1478
1479 // If with_edgecases = true, should handle linearly dependent points correctly
1480 // Define masking scalar (128 bits)
1481 const auto get_128_bit_scalar = []() {
1482 uint256_t scalar_u256(0, 0, 0, 0);
1483 scalar_u256.data[0] = engine.get_random_uint64();
1484 scalar_u256.data[1] = engine.get_random_uint64();
1485 fr scalar(scalar_u256);
1486 return scalar;
1487 };
1488 fr masking_scalar = get_128_bit_scalar();
1489 scalar_ct masking_scalar_ct = scalar_ct::from_witness(&builder, masking_scalar);
1490 element_ct c = element_ct::batch_mul(points,
1491 scalars,
1492 /*max_num_bits*/ 128,
1493 /*with_edgecases*/ true,
1494 /*masking_scalar*/ masking_scalar_ct);
1495
1496 // Check that the result tag is a union of inputs' tags
1497 EXPECT_EQ(c.get_origin_tag(), tag_union);
1498 element input_e = (element(input_P_a) * scalar_a);
1499 element input_f = (element(input_P_b) * scalar_b);
1500 element input_g = (element(input_P_c) * scalar_c);
1501
1502 affine_element expected(input_e + input_f + input_g);
1503 fq c_x_result(c.x().get_value().lo);
1504 fq c_y_result(c.y().get_value().lo);
1505
1506 EXPECT_EQ(c_x_result, expected.x);
1507 EXPECT_EQ(c_y_result, expected.y);
1508
1510 }
1511
1513 {
1515 affine_element input_P(element::random_element());
1516
1517 affine_element input_P_a = affine_element(element(input_P) + element(input_P)); // 2P
1518 affine_element input_P_b = affine_element(element(input_P_a) + element(input_P)); // 3P
1519 affine_element input_P_c = affine_element(element(input_P_a) + element(input_P_b)); // 5P
1520 std::vector<affine_element> input_points = { input_P_a, input_P_b, input_P_c };
1521
1522 // Choose scalars similar to the previous test
1523 fr scalar_a(11);
1524 fr scalar_b(14);
1525 fr scalar_c(6);
1526 std::vector<fr> input_scalars = { scalar_a, scalar_b, scalar_c };
1527
1528 std::vector<scalar_ct> scalars;
1530 for (size_t i = 0; i < 3; ++i) {
1531 const element_ct point = element_ct::from_witness(&builder, input_points[i]);
1532 points.emplace_back(point);
1533
1534 const scalar_ct scalar = scalar_ct::from_witness(&builder, input_scalars[i]);
1535 scalars.emplace_back(scalar);
1536 }
1537
1538 // with_edgecases = false should fail due to linearly dependent points
1539 // This will fail only while using ultra builder
1540 element_ct::batch_mul(points, scalars, /*max_num_bits*/ 4, /*with_edgecases*/ false);
1541
1543 EXPECT_EQ(builder.err(), "bigfield: prime limb diff is zero, but expected non-zero");
1544 }
1545
1546 static void test_one()
1547 {
1549 size_t num_repetitions = 1;
1550 for (size_t i = 0; i < num_repetitions; ++i) {
1551 fr scalar_a(fr::random_element());
1552 if ((uint256_t(scalar_a).get_bit(0) & 1) == 1) {
1553 scalar_a -= fr(1); // skew bit is 1
1554 }
1555 element_ct P_a = element_ct::one(&builder);
1556
1557 // Set origin tag for element to submitted value in round 0
1558 P_a.set_origin_tag(submitted_value_origin_tag);
1559 scalar_ct x_a = scalar_ct::from_witness(&builder, scalar_a);
1560
1561 // Set origin tag for scalar to challenge in round 0
1562 x_a.set_origin_tag(challenge_origin_tag);
1563 element_ct c = P_a * x_a;
1564
1565 // Check that the resulting tag is a union
1566 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
1567 affine_element expected(g1::one * scalar_a);
1568 fq c_x_result(c.x().get_value().lo);
1569 fq c_y_result(c.y().get_value().lo);
1570
1571 EXPECT_EQ(c_x_result, expected.x);
1572 EXPECT_EQ(c_y_result, expected.y);
1573 }
1574
1576 }
1577
1578 // Overload: defaults to all WITNESS types for given num_points
1579 static void test_helper_batch_mul(size_t num_points,
1580 const bool short_scalars = false,
1581 const bool with_edgecases = false)
1582 {
1583 std::vector<InputType> point_types(num_points, InputType::WITNESS);
1584 std::vector<InputType> scalar_types(num_points, InputType::WITNESS);
1585 test_helper_batch_mul(point_types, scalar_types, short_scalars, with_edgecases);
1586 }
1587
1589 std::vector<InputType> scalar_types,
1590 const bool short_scalars = false,
1591 const bool with_edgecases = false)
1592 {
1594
1595 const size_t num_points = point_types.size();
1597 std::vector<fr> scalars;
1598 std::vector<element_ct> circuit_points;
1599 std::vector<scalar_ct> circuit_scalars;
1600
1601 for (size_t i = 0; i < num_points; ++i) {
1602 // Generate scalars
1603 if (short_scalars) {
1604 auto [input_scalar, x] = get_random_short_scalar(&builder, scalar_types[i], /*num_bits*/ 128);
1605 scalars.push_back(input_scalar);
1606 circuit_scalars.push_back(x);
1607 } else {
1608 auto [input_scalar, x] = get_random_scalar(&builder, scalar_types[i], /*even*/ true);
1609 scalars.push_back(input_scalar);
1610 circuit_scalars.push_back(x);
1611 }
1612
1613 // Generate points
1614 auto [input_point, P] = get_random_point(&builder, point_types[i]);
1615 points.push_back(input_point);
1616 circuit_points.push_back(P);
1617 }
1618
1619 OriginTag tag_union{};
1620 for (size_t i = 0; i < num_points; ++i) {
1621 // Set tag to submitted value tag at round i
1622 circuit_points[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true));
1623 tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag());
1624
1625 // Set tag to challenge tag at round i
1626 circuit_scalars[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false));
1627 tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag());
1628 }
1629
1630 // Define masking scalar (128 bits) if with_edgecases is true
1631 const auto get_128_bit_scalar = []() {
1632 uint256_t scalar_u256(0, 0, 0, 0);
1633 scalar_u256.data[0] = engine.get_random_uint64();
1634 scalar_u256.data[1] = engine.get_random_uint64();
1635 fr scalar(scalar_u256);
1636 return scalar;
1637 };
1638 fr masking_scalar = with_edgecases ? get_128_bit_scalar() : fr(1);
1639 scalar_ct masking_scalar_ct =
1640 with_edgecases ? scalar_ct::from_witness(&builder, masking_scalar) : scalar_ct(&builder, fr(1));
1641
1642 element_ct result_point = element_ct::batch_mul(
1643 circuit_points, circuit_scalars, /*max_num_bits=*/0, with_edgecases, masking_scalar_ct);
1644
1645 // Check the resulting tag is a union of inputs' tags
1646 EXPECT_EQ(result_point.get_origin_tag(), tag_union);
1647
1648 element expected_point = g1::one;
1649 expected_point.self_set_infinity();
1650 for (size_t i = 0; i < num_points; ++i) {
1651 expected_point += (element(points[i]) * scalars[i]);
1652 }
1653
1654 expected_point = expected_point.normalize();
1655 fq result_x(result_point.x().get_value().lo);
1656 fq result_y(result_point.y().get_value().lo);
1657
1658 EXPECT_EQ(result_x, expected_point.x);
1659 EXPECT_EQ(result_y, expected_point.y);
1660
1662 }
1663
1664 static void test_batch_mul()
1665 {
1666 const size_t num_points = 5;
1669 std::vector<fr> scalars;
1670 for (size_t i = 0; i < num_points; ++i) {
1671 points.push_back(affine_element(element::random_element()));
1672 scalars.push_back(fr::random_element());
1673 }
1674
1675 std::vector<element_ct> circuit_points;
1676 std::vector<scalar_ct> circuit_scalars;
1677 OriginTag tag_union{};
1678 for (size_t i = 0; i < num_points; ++i) {
1679 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1680
1681 // Set tag to submitted value tag at round i
1682 circuit_points[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true));
1683 tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag());
1684 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1685
1686 // Set tag to challenge tag at round i
1687 circuit_scalars[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false));
1688 tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag());
1689 }
1690
1691 element_ct result_point = element_ct::batch_mul(circuit_points, circuit_scalars);
1692
1693 // Check the resulting tag is a union of inputs' tags
1694 EXPECT_EQ(result_point.get_origin_tag(), tag_union);
1695
1696 element expected_point = g1::one;
1697 expected_point.self_set_infinity();
1698 for (size_t i = 0; i < num_points; ++i) {
1699 expected_point += (element(points[i]) * scalars[i]);
1700 }
1701
1702 expected_point = expected_point.normalize();
1703 fq result_x(result_point.x().get_value().lo);
1704 fq result_y(result_point.y().get_value().lo);
1705
1706 EXPECT_EQ(result_x, expected_point.x);
1707 EXPECT_EQ(result_y, expected_point.y);
1708
1710 }
1711
1713 {
1714 const size_t num_points = 5;
1717 std::vector<fr> scalars;
1718 for (size_t i = 0; i < num_points; ++i) {
1719 points.push_back(affine_element(element::random_element()));
1720 scalars.push_back(fr::random_element());
1721 }
1722
1723 std::vector<element_ct> circuit_points;
1724 std::vector<scalar_ct> circuit_scalars;
1725
1726 OriginTag tag_union{};
1727 for (size_t i = 0; i < num_points; ++i) {
1728 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1729
1730 // Set tag to submitted value tag at round i
1731 circuit_points[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true));
1732 tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag());
1733 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1734
1735 // Set tag to challenge tag at round i
1736 circuit_scalars[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false));
1737 tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag());
1738 }
1739
1740 element_ct result_point2 =
1741 element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true);
1742
1743 // Check that the result tag is a union of inputs' tags
1744 EXPECT_EQ(result_point2.get_origin_tag(), tag_union);
1745 element expected_point = g1::one;
1746 expected_point.self_set_infinity();
1747 for (size_t i = 0; i < num_points; ++i) {
1748 expected_point += (element(points[i]) * scalars[i]);
1749 }
1750
1751 expected_point = expected_point.normalize();
1752
1753 fq result2_x(result_point2.x().get_value().lo);
1754 fq result2_y(result_point2.y().get_value().lo);
1755
1756 EXPECT_EQ(result2_x, expected_point.x);
1757 EXPECT_EQ(result2_y, expected_point.y);
1758
1760 }
1761
1763 {
1764 const auto test_repeated_points = [](const uint32_t num_points) {
1765 // batch P + ... + P = m*P
1766 info("num points: ", num_points);
1768 std::vector<fr> scalars;
1769 for (size_t idx = 0; idx < num_points; idx++) {
1770 points.push_back(affine_element::one());
1771 scalars.push_back(1);
1772 }
1773
1775 ASSERT_EQ(points.size(), scalars.size());
1776
1777 std::vector<element_ct> circuit_points;
1778 std::vector<scalar_ct> circuit_scalars;
1779
1780 OriginTag tag_union{};
1781 for (size_t i = 0; i < num_points; ++i) {
1782 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1783
1784 // Set tag to submitted value tag at round i
1785 circuit_points[i].set_origin_tag(
1786 OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true));
1787 tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag());
1788 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1789
1790 // Set tag to challenge tag at round i
1791 circuit_scalars[i].set_origin_tag(
1792 OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false));
1793 tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag());
1794 }
1795 element_ct result_point =
1796 element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true);
1797
1798 // Check that the result tag is a union of inputs' tags
1799 EXPECT_EQ(result_point.get_origin_tag(), tag_union);
1800
1801 auto expected_point = element::infinity();
1802 for (const auto& point : points) {
1803 expected_point += point;
1804 }
1805 expected_point = expected_point.normalize();
1806
1807 fq result_x(result_point.x().get_value().lo);
1808 fq result_y(result_point.y().get_value().lo);
1809
1810 EXPECT_EQ(result_x, expected_point.x);
1811 EXPECT_EQ(result_y, expected_point.y);
1812
1814 };
1815 test_repeated_points(2);
1816 test_repeated_points(3);
1817 test_repeated_points(4);
1818 test_repeated_points(5);
1819 test_repeated_points(6);
1820 test_repeated_points(7);
1821 }
1823 {
1824 {
1825 // batch oo + P = P
1827 points.push_back(affine_element::infinity());
1828 points.push_back(affine_element(element::random_element()));
1829 std::vector<fr> scalars;
1830 scalars.push_back(1);
1831 scalars.push_back(1);
1832
1834 ASSERT_EQ(points.size(), scalars.size());
1835 const size_t num_points = points.size();
1836
1837 std::vector<element_ct> circuit_points;
1838 std::vector<scalar_ct> circuit_scalars;
1839
1840 OriginTag tag_union{};
1841 for (size_t i = 0; i < num_points; ++i) {
1842 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1843
1844 // Set tag to submitted value tag at round i
1845 circuit_points[i].set_origin_tag(
1846 OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true));
1847 tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag());
1848 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1849
1850 // Set tag to challenge tag at round i
1851 circuit_scalars[i].set_origin_tag(
1852 OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false));
1853 tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag());
1854 }
1855
1856 element_ct result_point =
1857 element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true);
1858
1859 // Check that the result tag is a union of inputs' tags
1860 EXPECT_EQ(result_point.get_origin_tag(), tag_union);
1861
1862 element expected_point = points[1];
1863 expected_point = expected_point.normalize();
1864
1865 fq result_x(result_point.x().get_value().lo);
1866 fq result_y(result_point.y().get_value().lo);
1867
1868 EXPECT_EQ(result_x, expected_point.x);
1869 EXPECT_EQ(result_y, expected_point.y);
1870
1872 }
1873 {
1874 // batch 0 * P1 + P2 = P2
1876 points.push_back(affine_element(element::random_element()));
1877 points.push_back(affine_element(element::random_element()));
1878 std::vector<fr> scalars;
1879 scalars.push_back(0);
1880 scalars.push_back(1);
1881
1883 ASSERT_EQ(points.size(), scalars.size());
1884 const size_t num_points = points.size();
1885
1886 std::vector<element_ct> circuit_points;
1887 std::vector<scalar_ct> circuit_scalars;
1888 OriginTag tag_union{};
1889 for (size_t i = 0; i < num_points; ++i) {
1890 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1891
1892 // Set tag to submitted value tag at round i
1893 circuit_points[i].set_origin_tag(
1894 OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true));
1895 tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag());
1896 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1897
1898 // Set tag to challenge tag at round i
1899 circuit_scalars[i].set_origin_tag(
1900 OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false));
1901 tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag());
1902 }
1903
1904 element_ct result_point =
1905 element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true);
1906
1907 // Check that the result tag is a union of inputs' tags
1908 EXPECT_EQ(result_point.get_origin_tag(), tag_union);
1909
1910 element expected_point = points[1];
1911 expected_point = expected_point.normalize();
1912
1913 fq result_x(result_point.x().get_value().lo);
1914 fq result_y(result_point.y().get_value().lo);
1915
1916 EXPECT_EQ(result_x, expected_point.x);
1917 EXPECT_EQ(result_y, expected_point.y);
1918
1920 }
1921 }
1922
1923 // Test batch_mul with all points at infinity
1925 {
1928 std::vector<fr> scalars;
1929
1930 for (size_t i = 0; i < 5; ++i) {
1931 points.push_back(affine_element::infinity());
1932 scalars.push_back(fr::random_element());
1933 }
1934
1935 std::vector<element_ct> circuit_points;
1936 std::vector<scalar_ct> circuit_scalars;
1937
1938 for (size_t i = 0; i < points.size(); ++i) {
1939 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1940 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1941 }
1942
1943 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars, 0, true);
1944
1945 // Result should be point at infinity
1946 EXPECT_TRUE(result.is_point_at_infinity().get_value());
1948 }
1949
1950 // Test batch_mul with all zero scalars
1952 {
1955 std::vector<fr> scalars;
1956
1957 for (size_t i = 0; i < 5; ++i) {
1958 points.push_back(affine_element(element::random_element()));
1959 scalars.push_back(fr::zero());
1960 }
1961
1962 std::vector<element_ct> circuit_points;
1963 std::vector<scalar_ct> circuit_scalars;
1964
1965 for (size_t i = 0; i < points.size(); ++i) {
1966 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1967 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1968 }
1969
1970 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars, 0, true);
1971
1972 // Result should be point at infinity
1973 EXPECT_TRUE(result.is_point_at_infinity().get_value());
1975 }
1976
1977 // Test batch_mul with mixed zero and non-zero scalars
1979 {
1982 std::vector<fr> scalars;
1983
1984 for (size_t i = 0; i < 6; ++i) {
1985 points.push_back(affine_element(element::random_element()));
1986 // Alternate between zero and non-zero scalars
1987 scalars.push_back((i % 2 == 0) ? fr::zero() : fr::random_element());
1988 }
1989
1990 std::vector<element_ct> circuit_points;
1991 std::vector<scalar_ct> circuit_scalars;
1992
1993 for (size_t i = 0; i < points.size(); ++i) {
1994 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1995 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1996 }
1997
1998 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars, 0, true);
1999
2000 // Compute expected result
2001 element expected = element::infinity();
2002 for (size_t i = 0; i < points.size(); ++i) {
2003 expected += (element(points[i]) * scalars[i]);
2004 }
2005 affine_element expected_affine = affine_element(expected);
2006
2007 uint256_t result_x = result.x().get_value().lo;
2008 uint256_t result_y = result.y().get_value().lo;
2009
2010 EXPECT_EQ(fq(result_x), expected_affine.x);
2011 EXPECT_EQ(fq(result_y), expected_affine.y);
2012
2014 }
2015
2016 // Test batch_mul with mixed infinity and valid points
2018 {
2021 std::vector<fr> scalars;
2022
2023 for (size_t i = 0; i < 6; ++i) {
2024 // Alternate between infinity and valid points
2025 points.push_back((i % 2 == 0) ? affine_element::infinity() : affine_element(element::random_element()));
2026 scalars.push_back(fr::random_element());
2027 }
2028
2029 std::vector<element_ct> circuit_points;
2030 std::vector<scalar_ct> circuit_scalars;
2031
2032 for (size_t i = 0; i < points.size(); ++i) {
2033 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
2034 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
2035 }
2036
2037 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars, 0, true);
2038
2039 // Compute expected result
2040 element expected = element::infinity();
2041 for (size_t i = 0; i < points.size(); ++i) {
2042 if (!points[i].is_point_at_infinity()) {
2043 expected += (element(points[i]) * scalars[i]);
2044 }
2045 }
2046 affine_element expected_affine = affine_element(expected);
2047
2048 uint256_t result_x = result.x().get_value().lo;
2049 uint256_t result_y = result.y().get_value().lo;
2050
2051 EXPECT_EQ(fq(result_x), expected_affine.x);
2052 EXPECT_EQ(fq(result_y), expected_affine.y);
2053
2055 }
2056
2057 // Test batch_mul with points that cancel out
2059 {
2062 std::vector<fr> scalars;
2063
2064 // Add P and -P with same scalar
2065 affine_element P(element::random_element());
2067 fr scalar = fr::random_element();
2068
2069 points.push_back(P);
2070 scalars.push_back(scalar);
2071 points.push_back(neg_P);
2072 scalars.push_back(scalar);
2073
2074 // Add some other points to make it non-trivial
2075 for (size_t i = 0; i < 3; ++i) {
2076 points.push_back(affine_element(element::random_element()));
2077 scalars.push_back(fr::random_element());
2078 }
2079
2080 std::vector<element_ct> circuit_points;
2081 std::vector<scalar_ct> circuit_scalars;
2082
2083 for (size_t i = 0; i < points.size(); ++i) {
2084 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
2085 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
2086 }
2087
2088 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars, 0, true);
2089
2090 // Compute expected result
2091 element expected = element::infinity();
2092 for (size_t i = 0; i < points.size(); ++i) {
2093 expected += (element(points[i]) * scalars[i]);
2094 }
2095 affine_element expected_affine = affine_element(expected);
2096
2097 uint256_t result_x = result.x().get_value().lo;
2098 uint256_t result_y = result.y().get_value().lo;
2099
2100 EXPECT_EQ(fq(result_x), expected_affine.x);
2101 EXPECT_EQ(fq(result_y), expected_affine.y);
2102
2104 }
2105
2106 // Test batch_mul with constant and witness points mixed
2108 {
2110 std::vector<affine_element> points_native;
2111 std::vector<fr> scalars_native;
2112 std::vector<element_ct> circuit_points;
2113 std::vector<scalar_ct> circuit_scalars;
2114
2115 // Add constant-constant points
2116 for (size_t i = 0; i < 3; ++i) {
2117 const auto [point, point_ct] = get_random_point(&builder, InputType::CONSTANT);
2118 const auto [scalar, scalar_ct] = get_random_scalar(&builder, InputType::CONSTANT);
2119 points_native.push_back(point);
2120 scalars_native.push_back(scalar);
2121 circuit_points.push_back(point_ct); // Constant
2122 circuit_scalars.push_back(scalar_ct); // Constant
2123 }
2124
2125 // Add witness-witness points
2126 for (size_t i = 0; i < 3; ++i) {
2127 const auto [point, point_ct] = get_random_point(&builder, InputType::WITNESS);
2128 const auto [scalar, scalar_ct] = get_random_scalar(&builder, InputType::WITNESS);
2129 points_native.push_back(point);
2130 scalars_native.push_back(scalar);
2131 circuit_points.push_back(point_ct); // Witness
2132 circuit_scalars.push_back(scalar_ct); // Witness
2133 }
2134
2135 // Add constant-witness points
2136 for (size_t i = 0; i < 4; ++i) {
2137 const auto [point, point_ct] = get_random_point(&builder, InputType::CONSTANT);
2138 const auto [scalar, scalar_ct] = get_random_scalar(&builder, InputType::WITNESS);
2139 points_native.push_back(point);
2140 scalars_native.push_back(scalar);
2141 circuit_points.push_back(element_ct(point)); // Constant
2142 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalar)); // Witness
2143 }
2144
2145 // Add witness-constant points
2146 for (size_t i = 0; i < 4; ++i) {
2147 const auto [point, point_ct] = get_random_point(&builder, InputType::WITNESS);
2148 const auto [scalar, scalar_ct] = get_random_scalar(&builder, InputType::CONSTANT);
2149 points_native.push_back(point);
2150 scalars_native.push_back(scalar);
2151 circuit_points.push_back(point_ct); // Witness
2152 circuit_scalars.push_back(scalar_ct); // Constant
2153 }
2154
2155 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars);
2156
2157 // Compute expected result
2158 element expected = element::infinity();
2159 for (size_t i = 0; i < points_native.size(); ++i) {
2160 expected += (element(points_native[i]) * scalars_native[i]);
2161 }
2162 affine_element expected_affine = affine_element(expected);
2163
2164 uint256_t result_x = result.x().get_value().lo;
2165 uint256_t result_y = result.y().get_value().lo;
2166
2167 EXPECT_EQ(fq(result_x), expected_affine.x);
2168 EXPECT_EQ(fq(result_y), expected_affine.y);
2169
2171 }
2172
2173 // Test batch_mul with large number of points (stress test)
2175 {
2178 std::vector<fr> scalars;
2179 constexpr size_t num_points = 20;
2180
2181 for (size_t i = 0; i < num_points; ++i) {
2182 points.push_back(affine_element(element::random_element()));
2183 scalars.push_back(fr::random_element());
2184 }
2185
2186 std::vector<element_ct> circuit_points;
2187 std::vector<scalar_ct> circuit_scalars;
2188
2189 for (size_t i = 0; i < points.size(); ++i) {
2190 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
2191 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
2192 }
2193
2194 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars);
2195
2196 // Compute expected result
2197 element expected = element::infinity();
2198 for (size_t i = 0; i < points.size(); ++i) {
2199 expected += (element(points[i]) * scalars[i]);
2200 }
2201 affine_element expected_affine = affine_element(expected);
2202
2203 uint256_t result_x = result.x().get_value().lo;
2204 uint256_t result_y = result.y().get_value().lo;
2205
2206 EXPECT_EQ(fq(result_x), expected_affine.x);
2207 EXPECT_EQ(fq(result_y), expected_affine.y);
2208
2210 }
2211};
2212
2213// bn254 with ultra arithmetisation where scalar field is native field, base field is non-native field (bigfield)
2215
2216// bn254 with ultra arithmetisation where both scalar and base fields are non-native fields
2219
2220// bn254 with mega arithmetisation where scalar field is native field, base field is non-native field
2222
2223// secp256r1 with ultra arithmetisation where both scalar and base fields are (naturally) non-native fields
2226
2227// secp256k1 with ultra arithmetisation where both scalar and base fields are (naturally) non-native fields
2230
2231using TestTypes = testing::Types<bn254_with_ultra,
2236
2238
2240{
2241 TestFixture::test_basic_tag_logic();
2242}
2243
2244TYPED_TEST(stdlib_biggroup, assert_coordinates_in_field)
2245{
2246 TestFixture::test_assert_coordinates_in_field();
2247}
2248
2249// Addition tests
2251{
2252 TestFixture::test_add();
2253}
2254TYPED_TEST(stdlib_biggroup, add_with_constants)
2255{
2256 TestFixture::test_add(InputType::WITNESS, InputType::CONSTANT); // w + c
2257 TestFixture::test_add(InputType::CONSTANT, InputType::WITNESS); // c + w
2258 TestFixture::test_add(InputType::CONSTANT, InputType::CONSTANT); // c + c
2259}
2260TYPED_TEST(stdlib_biggroup, add_points_at_infinity)
2261{
2262 TestFixture::test_add_points_at_infinity();
2263}
2264TYPED_TEST(stdlib_biggroup, standard_form_of_point_at_infinity)
2265{
2266 TestFixture::test_standard_form_of_point_at_infinity();
2267}
2268
2269// Subtraction tests
2271{
2272 TestFixture::test_sub();
2273}
2274TYPED_TEST(stdlib_biggroup, sub_with_constants)
2275{
2276 TestFixture::test_sub(InputType::WITNESS, InputType::CONSTANT); // w - c
2277 TestFixture::test_sub(InputType::CONSTANT, InputType::WITNESS); // c - w
2278 TestFixture::test_sub(InputType::CONSTANT, InputType::CONSTANT); // c - c
2279}
2280TYPED_TEST(stdlib_biggroup, sub_points_at_infinity)
2281{
2282 TestFixture::test_sub_points_at_infinity();
2283}
2285{
2286 TestFixture::test_dbl();
2287}
2288TYPED_TEST(stdlib_biggroup, dbl_with_constant)
2289{
2290 TestFixture::test_dbl(InputType::CONSTANT); // dbl(c)
2291}
2292TYPED_TEST(stdlib_biggroup, dbl_with_infinity)
2293{
2294 TestFixture::test_dbl_with_infinity();
2295}
2297{
2298 if constexpr (HasGoblinBuilder<TypeParam>) {
2299 GTEST_SKIP() << "mega builder does not support this edge case";
2300 } else {
2301 TestFixture::test_dbl_with_y_zero();
2302 }
2303}
2305{
2306 TestFixture::test_add_equals_dbl();
2307}
2308TYPED_TEST(stdlib_biggroup, sub_neg_equals_double)
2309{
2310 TestFixture::test_sub_neg_equals_double();
2311}
2312
2313// Test chain_add
2315{
2316 if constexpr (HasGoblinBuilder<TypeParam>) {
2317 GTEST_SKIP() << "mega builder does not implement chain_add function";
2318 } else {
2319 TestFixture::test_chain_add();
2320 };
2321}
2322HEAVY_TYPED_TEST(stdlib_biggroup, chain_add_with_constants)
2323{
2324 if constexpr (HasGoblinBuilder<TypeParam>) {
2325 GTEST_SKIP() << "mega builder does not implement chain_add function";
2326 } else {
2327 TestFixture::test_chain_add(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w, w, c
2328 TestFixture::test_chain_add(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // w, c, w
2329 TestFixture::test_chain_add(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // w, c, c
2330 TestFixture::test_chain_add(InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS); // c, w, w
2331 TestFixture::test_chain_add(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // c, w, c
2332 TestFixture::test_chain_add(InputType::CONSTANT, InputType::CONSTANT, InputType::WITNESS); // c, c, w
2333 TestFixture::test_chain_add(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT); // c, c, c
2334 }
2335}
2336
2337// Test multiple_montgomery_ladder
2338HEAVY_TYPED_TEST(stdlib_biggroup, multiple_montgomery_ladder)
2339{
2340
2341 if constexpr (HasGoblinBuilder<TypeParam>) {
2342 GTEST_SKIP() << "mega builder does not implement multiple_montgomery_ladder function";
2343 } else {
2344 TestFixture::test_multiple_montgomery_ladder();
2345 };
2346}
2347
2348// Test normalize
2350{
2351 TestFixture::test_normalize();
2352}
2353TYPED_TEST(stdlib_biggroup, normalize_constant)
2354{
2355 TestFixture::test_normalize(InputType::CONSTANT);
2356}
2357
2358// Test reduce
2360{
2361 TestFixture::test_reduce();
2362}
2364{
2365 TestFixture::test_reduce(InputType::CONSTANT);
2366}
2367
2368// Test unary negation
2370{
2371 TestFixture::test_unary_negate(InputType::WITNESS);
2372}
2373
2374TYPED_TEST(stdlib_biggroup, unary_negate_with_constants)
2375{
2376 TestFixture::test_unary_negate(InputType::CONSTANT);
2377}
2378
2379// Test operator+=
2381{
2382 TestFixture::test_add_assign(InputType::WITNESS, InputType::WITNESS);
2383}
2384
2385TYPED_TEST(stdlib_biggroup, add_assign_with_constants)
2386{
2387 TestFixture::test_add_assign(InputType::WITNESS, InputType::CONSTANT); // w += c
2388 TestFixture::test_add_assign(InputType::CONSTANT, InputType::WITNESS); // c += w
2389}
2390
2391// Test operator-=
2393{
2394 TestFixture::test_sub_assign(InputType::WITNESS, InputType::WITNESS);
2395}
2396TYPED_TEST(stdlib_biggroup, sub_assign_with_constants)
2397{
2398 TestFixture::test_sub_assign(InputType::WITNESS, InputType::CONSTANT); // w -= c
2399 TestFixture::test_sub_assign(InputType::CONSTANT, InputType::WITNESS); // c -= w
2400}
2401// Test checked_unconditional_add
2402TYPED_TEST(stdlib_biggroup, checked_unconditional_add)
2403{
2404 TestFixture::test_checked_unconditional_add(InputType::WITNESS, InputType::WITNESS);
2405}
2406TYPED_TEST(stdlib_biggroup, checked_unconditional_add_with_constants)
2407{
2408 TestFixture::test_checked_unconditional_add(InputType::WITNESS, InputType::CONSTANT); // w + c
2409 TestFixture::test_checked_unconditional_add(InputType::CONSTANT, InputType::WITNESS); // c + w
2410 TestFixture::test_checked_unconditional_add(InputType::CONSTANT, InputType::CONSTANT); // c + c
2411}
2412// Test checked_unconditional_subtract
2413TYPED_TEST(stdlib_biggroup, checked_unconditional_subtract)
2414{
2415 TestFixture::test_checked_unconditional_subtract(InputType::WITNESS, InputType::WITNESS);
2416}
2417TYPED_TEST(stdlib_biggroup, checked_unconditional_subtract_with_constants)
2418{
2419 TestFixture::test_checked_unconditional_subtract(InputType::WITNESS, InputType::CONSTANT); // w - c
2420 TestFixture::test_checked_unconditional_subtract(InputType::CONSTANT, InputType::WITNESS); // c - w
2421 TestFixture::test_checked_unconditional_subtract(InputType::CONSTANT, InputType::CONSTANT); // c - c
2422}
2423// Test checked_unconditional_add_sub
2424TYPED_TEST(stdlib_biggroup, checked_unconditional_add_sub)
2425{
2426 TestFixture::test_checked_unconditional_add_sub();
2427}
2428TYPED_TEST(stdlib_biggroup, checked_unconditional_add_sub_with_constants)
2429{
2430 TestFixture::test_checked_unconditional_add_sub(InputType::WITNESS, InputType::CONSTANT); // w, c
2431 TestFixture::test_checked_unconditional_add_sub(InputType::CONSTANT, InputType::WITNESS); // c, w
2432 TestFixture::test_checked_unconditional_add_sub(InputType::CONSTANT, InputType::CONSTANT); // c, c
2433}
2434// Test conditional_negate
2435TYPED_TEST(stdlib_biggroup, conditional_negate)
2436{
2437 TestFixture::test_conditional_negate();
2438}
2439TYPED_TEST(stdlib_biggroup, conditional_negate_with_constants)
2440{
2441 TestFixture::test_conditional_negate(InputType::WITNESS, InputType::CONSTANT); // w, c
2442 TestFixture::test_conditional_negate(InputType::CONSTANT, InputType::WITNESS); // c, w
2443 TestFixture::test_conditional_negate(InputType::CONSTANT, InputType::CONSTANT); // c, c
2444}
2445// Test conditional_select
2446TYPED_TEST(stdlib_biggroup, conditional_select)
2447{
2448 TestFixture::test_conditional_select();
2449}
2450TYPED_TEST(stdlib_biggroup, conditional_select_with_constants)
2451{
2452 TestFixture::test_conditional_select(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w, w, c
2453 TestFixture::test_conditional_select(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // w, c, w
2454 TestFixture::test_conditional_select(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // w, c, c
2455 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS); // c, w, w
2456 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::CONSTANT, InputType::WITNESS); // c, c, w
2457 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // c, w, c
2458 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT); // c, c, c
2459}
2460TYPED_TEST(stdlib_biggroup, incomplete_assert_equal)
2461{
2462 TestFixture::test_incomplete_assert_equal();
2463}
2464TYPED_TEST(stdlib_biggroup, incomplete_assert_equal_fails)
2465{
2466 TestFixture::test_incomplete_assert_equal_failure();
2467}
2468TYPED_TEST(stdlib_biggroup, incomplete_assert_equal_edge_cases)
2469{
2470 TestFixture::test_incomplete_assert_equal_edge_cases();
2471}
2472
2474{
2475 if constexpr (!HasGoblinBuilder<TypeParam>) {
2476 size_t num_repetitions = 1;
2477 for (size_t i = 0; i < num_repetitions; i++) {
2478 TestFixture::test_compute_naf();
2479 }
2480 } else {
2481 GTEST_SKIP() << "mega builder does not implement compute_naf function";
2482 }
2483}
2484
2486{
2487 if constexpr (!HasGoblinBuilder<TypeParam>) {
2488 TestFixture::test_compute_naf_zero();
2489 } else {
2490 GTEST_SKIP() << "mega builder does not implement compute_naf function";
2491 }
2492}
2493
2495{
2496 TestFixture::test_mul();
2497}
2499{
2500 TestFixture::test_mul(InputType::WITNESS, InputType::CONSTANT); // w * c
2501 TestFixture::test_mul(InputType::CONSTANT, InputType::WITNESS); // c * w
2502 TestFixture::test_mul(InputType::CONSTANT, InputType::CONSTANT); // c * c
2503}
2505{
2506 TestFixture::test_mul_edge_cases();
2507}
2508HEAVY_TYPED_TEST(stdlib_biggroup, mul_edge_cases_with_constants)
2509{
2510 TestFixture::test_mul_edge_cases(InputType::WITNESS, InputType::CONSTANT); // w * c
2511 TestFixture::test_mul_edge_cases(InputType::CONSTANT, InputType::WITNESS); // c * w
2512 TestFixture::test_mul_edge_cases(InputType::CONSTANT, InputType::CONSTANT); // c * c
2513}
2514
2515HEAVY_TYPED_TEST(stdlib_biggroup, short_scalar_mul_with_bit_lengths)
2516{
2517 if constexpr (HasGoblinBuilder<TypeParam>) {
2518 GTEST_SKIP() << "mega builder does not implement scalar_mul function";
2519 } else {
2520 TestFixture::test_short_scalar_mul_with_bit_lengths();
2521 }
2522}
2523
2524HEAVY_TYPED_TEST(stdlib_biggroup, short_scalar_mul_infinity)
2525{
2526 if constexpr (HasGoblinBuilder<TypeParam>) {
2527 GTEST_SKIP() << "mega builder does not implement scalar_mul function";
2528 } else {
2529 TestFixture::test_short_scalar_mul_infinity();
2530 }
2531}
2532
2533// Batch multiplication tests
2534// 1 point - Base case only
2536{
2537 TestFixture::test_helper_batch_mul(1);
2538}
2539
2540// 2 points - Base case + flag variations + one constant mix
2542{
2543 TestFixture::test_helper_batch_mul(2);
2544}
2545HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_twin_short_scalars)
2546{
2547 TestFixture::test_helper_batch_mul(2, true); // short_scalars
2548}
2549HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_twin_with_edgecases)
2550{
2551 TestFixture::test_helper_batch_mul(2, false, true); // short_scalars, with_edgecases
2552}
2553HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_twin_short_scalars_with_edgecases)
2554{
2555 TestFixture::test_helper_batch_mul(2, true, true); // short_scalars, with_edgecases
2556}
2557HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_twin_mixed_constants)
2558{
2559 TestFixture::test_helper_batch_mul({ InputType::WITNESS, InputType::CONSTANT },
2561}
2562
2563// 3 points - Base case only
2565{
2566 TestFixture::test_helper_batch_mul(3);
2567}
2568
2569// 4 points - Base case only
2571{
2572 TestFixture::test_helper_batch_mul(4);
2573}
2574
2575// 5 points - Base case + edge case + short scalar + mixed constant
2577{
2578 TestFixture::test_helper_batch_mul(5);
2579}
2580HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_five_with_edgecases)
2581{
2582 TestFixture::test_helper_batch_mul(5, false, true); // short_scalars, with_edgecases
2583}
2584HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_five_short_scalars)
2585{
2586 TestFixture::test_helper_batch_mul(5, true); // short_scalars
2587}
2588HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_five_short_scalars_with_edgecases)
2589{
2590 TestFixture::test_helper_batch_mul(5, true, true); // short_scalars, with_edgecases
2591}
2598
2599// 6 points - Base case only
2601{
2602 TestFixture::test_helper_batch_mul(6);
2603}
2604
2606{
2607 TestFixture::test_twin_mul();
2608}
2609
2610HEAVY_TYPED_TEST(stdlib_biggroup, twin_mul_with_infinity)
2611{
2612 TestFixture::test_twin_mul_with_infinity();
2613}
2614
2615HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_linearly_dependent_generators)
2616{
2617 TestFixture::test_batch_mul_linearly_dependent_generators();
2618}
2619
2620HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_linearly_dependent_generators_failure)
2621{
2622 if constexpr (HasGoblinBuilder<TypeParam>) {
2623 GTEST_SKIP() << "this failure test is designed for ultra builder only";
2624 } else {
2625 TestFixture::test_batch_mul_linearly_dependent_generators_failure();
2626 }
2627}
2628
2630{
2631 TestFixture::test_one();
2632}
2633
2635{
2636 TestFixture::test_batch_mul();
2637}
2638
2639HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_edgecase_equivalence)
2640{
2641 TestFixture::test_batch_mul_edgecase_equivalence();
2642}
2643HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_edge_case_set1)
2644{
2645 TestFixture::test_batch_mul_edge_case_set1();
2646}
2647
2648HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_edge_case_set2)
2649{
2650 TestFixture::test_batch_mul_edge_case_set2();
2651}
2652
2653// Batch mul edge case tests
2654HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_all_infinity)
2655{
2656 TestFixture::test_batch_mul_all_infinity();
2657}
2658
2659HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_all_zero_scalars)
2660{
2661 TestFixture::test_batch_mul_all_zero_scalars();
2662}
2663
2664HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_mixed_zero_scalars)
2665{
2666 TestFixture::test_batch_mul_mixed_zero_scalars();
2667}
2668
2669HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_mixed_infinity)
2670{
2671 TestFixture::test_batch_mul_mixed_infinity();
2672}
2673
2674HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_cancellation)
2675{
2676 TestFixture::test_batch_mul_cancellation();
2677}
2678
2679HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_mixed_constant_witness)
2680{
2681 TestFixture::test_batch_mul_mixed_constant_witness();
2682}
2683
2684HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_large_number_of_points)
2685{
2686 TestFixture::test_batch_mul_large_number_of_points();
2687}
#define EXPECT_THROW_OR_ABORT(statement, matcher)
Definition assert.hpp:174
InputType
stdlib_biggroup< TestType< stdlib::bn254< bb::UltraCircuitBuilder >, false > > bn254_with_ultra
testing::Types< bn254_with_ultra, bn254_with_ultra_scalar_bigfield, bn254_with_mega, secp256r1_with_ultra, secp256k1_with_ultra > TestTypes
InputType
stdlib_biggroup< TestType< stdlib::bn254< bb::UltraCircuitBuilder >, true > > bn254_with_ultra_scalar_bigfield
stdlib_biggroup< TestType< stdlib::secp256r1< bb::UltraCircuitBuilder >, true > > secp256r1_with_ultra
constexpr InputType operator!(InputType type)
stdlib_biggroup< TestType< stdlib::bn254< bb::MegaCircuitBuilder >, false > > bn254_with_mega
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
constexpr void self_set_infinity() noexcept
static constexpr affine_element one() noexcept
BB_INLINE constexpr void self_set_infinity() noexcept
group_elements::affine_element< Fq, Fr, Params > affine_element
Definition group.hpp:42
static constexpr element one
Definition group.hpp:46
group_elements::element< Fq, Fr, Params > element
Definition group.hpp:41
virtual uint64_t get_random_uint64()=0
virtual uint8_t get_random_uint8()=0
virtual uint256_t get_random_uint256()=0
constexpr uint64_t get_msb() const
Implements boolean logic in-circuit.
Definition bool.hpp:59
void set_origin_tag(const OriginTag &new_tag) const
Definition bool.hpp:153
static auto checked_unconditional_add_sub(const element< C, Fq, Fr, G > &elem1, const element< C, Fq, Fr, G > &elem2)
Definition biggroup.hpp:957
static field_t from_witness(Builder *ctx, const bb::fr &input)
Definition field.hpp:454
static void test_checked_unconditional_add_sub(InputType a_type=InputType::WITNESS, InputType b_type=InputType::WITNESS)
static void test_sub_points_at_infinity()
static void test_sub_neg_equals_double()
static void test_helper_batch_mul(std::vector< InputType > point_types, std::vector< InputType > scalar_types, const bool short_scalars=false, const bool with_edgecases=false)
static void test_conditional_negate(InputType point_type=InputType::WITNESS, InputType predicate_type=InputType::WITNESS)
static void test_batch_mul_edgecase_equivalence()
static void test_one()
static void test_reduce(InputType point_type=InputType::WITNESS)
static void test_twin_mul()
static void test_add_points_at_infinity()
static void test_chain_add(InputType a_type=InputType::WITNESS, InputType b_type=InputType::WITNESS, InputType c_type=InputType::WITNESS)
static void test_compute_naf()
typename g1::element element
static void test_multiple_montgomery_ladder()
static void test_batch_mul_cancellation()
static void test_dbl_with_infinity()
static std::pair< affine_element, element_ct > get_random_constant_point(Builder *builder)
static void test_compute_naf_zero()
static void test_mul(InputType scalar_type=InputType::WITNESS, InputType point_type=InputType::WITNESS)
static void test_batch_mul_mixed_infinity()
typename Curve::ScalarFieldNative fr
static void test_batch_mul_edge_case_set2()
static std::pair< fr, scalar_ct > get_random_constant_scalar(Builder *builder, bool even=false)
typename TestType::element_ct element_ct
static void test_assert_coordinates_in_field()
static std::pair< affine_element, element_ct > get_random_witness_point(Builder *builder)
static void test_mul_edge_cases(InputType scalar_type=InputType::WITNESS, InputType point_type=InputType::WITNESS)
typename g1::affine_element affine_element
typename TestType::Curve Curve
static void test_incomplete_assert_equal_edge_cases()
static std::pair< fr, scalar_ct > get_random_witness_scalar(Builder *builder, bool even=false)
static void test_batch_mul_linearly_dependent_generators()
static void test_conditional_select(InputType a_type=InputType::WITNESS, InputType b_type=InputType::WITNESS, InputType predicate_type=InputType::WITNESS)
static void test_basic_tag_logic()
static void test_add(InputType a_type=InputType::WITNESS, InputType b_type=InputType::WITNESS)
typename Curve::Builder Builder
static void test_incomplete_assert_equal()
static void test_batch_mul_mixed_constant_witness()
static void test_twin_mul_with_infinity()
static void test_unary_negate(InputType a_type=InputType::WITNESS)
typename TestType::scalar_ct scalar_ct
stdlib::bool_t< Builder > bool_ct
static std::pair< fr, scalar_ct > get_random_scalar(Builder *builder, InputType type, bool even=false)
static void test_batch_mul_edge_case_set1()
static void test_checked_unconditional_subtract(InputType a_type=InputType::WITNESS, InputType b_type=InputType::WITNESS)
static void test_short_scalar_mul_with_bit_lengths()
static void test_short_scalar_mul_infinity()
static void test_dbl(InputType a_type=InputType::WITNESS)
static void test_normalize(InputType point_type=InputType::WITNESS)
static void test_incomplete_assert_equal_failure()
static std::pair< fr, scalar_ct > get_random_short_scalar(Builder *builder, InputType type, size_t num_bits)
stdlib::witness_t< Builder > witness_ct
static void test_standard_form_of_point_at_infinity()
Check that converting a point at infinity into standard form ensures the coordinates are zeroes.
typename Curve::GroupNative g1
typename Curve::BaseFieldNative fq
static void test_batch_mul_mixed_zero_scalars()
static void test_add_assign(InputType a_type=InputType::WITNESS, InputType b_type=InputType::WITNESS)
static std::pair< affine_element, element_ct > get_random_point(Builder *builder, InputType type)
static void test_batch_mul_large_number_of_points()
static void test_dbl_with_y_zero()
static void test_sub_assign(InputType a_type=InputType::WITNESS, InputType b_type=InputType::WITNESS)
static void test_batch_mul()
static void test_batch_mul_all_zero_scalars()
static void test_add_equals_dbl()
static void test_helper_batch_mul(size_t num_points, const bool short_scalars=false, const bool with_edgecases=false)
static void test_sub(InputType a_type=InputType::WITNESS, InputType b_type=InputType::WITNESS)
static void test_batch_mul_linearly_dependent_generators_failure()
static constexpr auto EXPECT_CIRCUIT_CORRECTNESS
static void test_batch_mul_all_infinity()
static void test_checked_unconditional_add(InputType a_type=InputType::WITNESS, InputType b_type=InputType::WITNESS)
void benchmark_info(Args...)
Info used to store circuit statistics during CI/CD with concrete structure. Writes straight to log.
Definition log.hpp:110
void info(Args... args)
Definition log.hpp:75
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
bool expected_result
uint8_t const size_t length
Definition data_store.hpp:9
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
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TYPED_TEST_SUITE(ShpleminiTest, TestSettings)
Inner sum(Cont< Inner, Args... > const &in)
Definition container.hpp:70
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
This file contains part of the logic for the Origin Tag mechanism that tracks the use of in-circuit p...
#define STANDARD_TESTING_TAGS
typename std::conditional< _use_bigfield, typename Curve::g1_bigfr_ct, typename Curve::Group >::type element_ct
typename std::conditional< _use_bigfield, typename Curve::bigfr_ct, typename Curve::ScalarField >::type scalar_ct
static const bool use_bigfield
static constexpr uint256_t modulus
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr field reduce() const noexcept
static constexpr field zero()
#define HEAVY_TYPED_TEST(x, y)
Definition test.hpp:11