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"
16#include <vector>
17
18using namespace bb;
19
20namespace {
22}
23
24enum struct InputType {
25 WITNESS,
27};
28
33
34template <typename T>
36
37// One can only define a TYPED_TEST with a single template paramter.
38// Our workaround is to pass parameters of the following type.
39template <typename _Curve, bool _use_bigfield = false> struct TestType {
40 public:
41 using Curve = _Curve;
42 static const bool use_bigfield = _use_bigfield;
43 using element_ct =
44 typename std::conditional<_use_bigfield, typename Curve::g1_bigfr_ct, typename Curve::Group>::type;
45 // the field of scalars acting on element_ct
46 using scalar_ct =
47 typename std::conditional<_use_bigfield, typename Curve::bigfr_ct, typename Curve::ScalarField>::type;
48};
49
51template <typename TestType> class stdlib_biggroup : public testing::Test {
52 public:
53 using Curve = typename TestType::Curve;
56
57 using fq = typename Curve::BaseFieldNative;
58 using fr = typename Curve::ScalarFieldNative;
59 using g1 = typename Curve::GroupNative;
61 using element = typename g1::element;
62
63 using Builder = typename Curve::Builder;
67
68 static constexpr auto EXPECT_CIRCUIT_CORRECTNESS = [](Builder& builder, bool expected_result = true) {
69 info("num gates = ", builder.get_num_finalized_gates_inefficient());
71 };
72
73 // Helper to check the infinity status of a circuit element.
74 // Ultra: reads the in-circuit is_point_at_infinity flag.
75 // Goblin/Mega: derives infinity from native (0,0) coordinates (no circuit flag exists).
76 static bool is_infinity(const element_ct& e)
77 {
78 if constexpr (HasGoblinBuilder<TestType>) {
79 return e.get_value().is_point_at_infinity();
80 } else {
81 return e.is_point_at_infinity().get_value();
82 }
83 }
84
85 // Create a random point as a witness
87 {
88 affine_element point_native(element::random_element());
89 element_ct point_ct = element_ct::from_witness(builder, point_native);
90 return std::make_pair(point_native, point_ct);
91 }
92
93 // Create a random point as a constant
95 {
96 affine_element point_native(element::random_element());
97 // Create constant coordinates with builder context
98 using Fq = typename element_ct::BaseField;
99 Fq x_const(builder, uint256_t(point_native.x));
100 Fq y_const(builder, uint256_t(point_native.y));
101 element_ct point_ct(x_const, y_const);
102 return std::make_pair(point_native, point_ct);
103 }
104
105 // Create a random point based on InputType
113
114 // Create a random scalar as a witness
116 {
117 fr scalar_native = fr::random_element();
118 if (even && uint256_t(scalar_native).get_bit(0)) {
119 scalar_native -= fr(1); // make it even if it's odd
120 }
121 scalar_ct scalar_ct_val = scalar_ct::from_witness(builder, scalar_native);
122 return std::make_pair(scalar_native, scalar_ct_val);
123 }
124
125 // Create a random scalar as a constant
127 {
128 fr scalar_native = fr::random_element();
129 if (even && uint256_t(scalar_native).get_bit(0)) {
130 scalar_native -= fr(1); // make it even if it's odd
131 }
132 scalar_ct scalar_ct_val = scalar_ct(builder, scalar_native);
133 return std::make_pair(scalar_native, scalar_ct_val);
134 }
135
136 // Create a random scalar based on InputType
138 {
139 if (type == InputType::WITNESS) {
141 }
143 }
144
146 {
147 uint256_t scalar_u256 = engine.get_random_uint256();
148 scalar_u256 = scalar_u256 >> (256 - num_bits); // keep only the lower num_bits bits
149
150 fr scalar_native(scalar_u256);
151 scalar_ct scalar_ct_val;
152 if (type == InputType::WITNESS) {
153 scalar_ct_val = scalar_ct::from_witness(builder, scalar_native);
154 } else {
155 scalar_ct_val = scalar_ct(builder, scalar_native);
156 }
157 return std::make_pair(scalar_native, scalar_ct_val);
158 }
159
160 public:
162 {
164 affine_element input_a(element::random_element());
165
166 element_ct a = element_ct::from_witness(&builder, input_a);
167 a.set_origin_tag(next_submitted_value_origin_tag);
168 // Tag is preserved after being set
169 EXPECT_EQ(a.get_origin_tag(), next_submitted_value_origin_tag);
170
171 // Tags from members are merged
172 // Create field elements with specific tags before constructing the biggroup element
173 affine_element input_c(element::random_element());
174 auto x = element_ct::BaseField::from_witness(&builder, input_c.x);
175 auto y = element_ct::BaseField::from_witness(&builder, input_c.y);
176
177 // Set tags on the individual field elements
178 x.set_origin_tag(submitted_value_origin_tag);
179 y.set_origin_tag(challenge_origin_tag);
180
181 // Construct biggroup element from pre-tagged field elements
182 // The is_infinity flag is auto-detected from coordinates and won't have a user-set tag
183 element_ct c(x, y);
184
185 // The tag of the biggroup element should be the union of x and y member tags
186 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
187
188#ifndef NDEBUG
189 // Test that instant_death_tag on x coordinate propagates correctly
190 affine_element input_b(element::random_element());
191 auto x_death = element_ct::BaseField::from_witness(&builder, input_b.x);
192 auto y_normal = element_ct::BaseField::from_witness(&builder, input_b.y);
193
194 x_death.set_origin_tag(instant_death_tag);
195 // Set constant tags on the other elements so they can be merged with instant_death_tag
196 y_normal.set_origin_tag(constant_tag);
197 pif_normal.set_origin_tag(constant_tag);
198
199 // Use assert_on_curve=false to avoid triggering instant_death during validate_on_curve()
200 element_ct b(x_death, y_normal, pif_normal, /*assert_on_curve=*/false);
201 // Working with instant death tagged element causes an exception
202 EXPECT_THROW(b + b, std::runtime_error);
203#endif
204 }
205
207 {
208 // Only test for non-goblin builders (goblin elements don't have assert_coordinates_in_field
209 // because coordinate checks are done in the ECCVM circuit)
210 if constexpr (!HasGoblinBuilder<TestType>) {
211 // Test 1: Valid coordinates should pass
212 {
214
215 // Test multiple random points to ensure assert_coordinates_in_field works correctly
216 for (size_t i = 0; i < 3; ++i) {
217 affine_element valid_point(element::random_element());
218 element_ct point = element_ct::from_witness(&builder, valid_point);
219
220 // This should not fail - coordinates are in field
221 point.assert_coordinates_in_field();
222 }
223
224 // Verify the circuit is correct
226 }
227
228 // Test 2: Invalid x coordinate should cause circuit to fail
229 {
231 affine_element valid_point(element::random_element());
232
233 // Create a bigfield element with x coordinate that will be out of range
234 // We do this by creating a valid witness but then manipulating the limb values
235 // to make them represent a value >= the modulus
236 auto x_coord = element_ct::BaseField::from_witness(&builder, valid_point.x);
237 auto y_coord = element_ct::BaseField::from_witness(&builder, valid_point.y);
238
239 // Manipulate the limbs to create an invalid value
240 // Set the highest limb to a very large value that would make the total >= modulus
241 x_coord.binary_basis_limbs[3].element = field_ct::from_witness(&builder, bb::fr(uint256_t(1) << 68));
242 x_coord.binary_basis_limbs[3].maximum_value = uint256_t(1) << 68;
243
244 // Skip curve check since we're intentionally creating an invalid point
245 // Note: is_infinity is auto-detected as false since coords are non-zero
246 element_ct point(x_coord, y_coord, /*assert_on_curve=*/false);
247 point.assert_coordinates_in_field();
248
249 // Circuit should fail because x coordinate is out of field
251 }
252
253 // Test 3: Invalid y coordinate should cause circuit to fail
254 {
256 affine_element valid_point(element::random_element());
257
258 auto x_coord = element_ct::BaseField::from_witness(&builder, valid_point.x);
259 auto y_coord = element_ct::BaseField::from_witness(&builder, valid_point.y);
260
261 // Manipulate the limbs to create an invalid value
262 // Set the highest limb to a very large value that would make the total >= modulus
263 y_coord.binary_basis_limbs[3].element = field_ct::from_witness(&builder, bb::fr(uint256_t(1) << 68));
264 y_coord.binary_basis_limbs[3].maximum_value = uint256_t(1) << 68;
265
266 // Skip curve check since we're intentionally creating an invalid point
267 // Note: is_infinity is auto-detected as false since coords are non-zero
268 element_ct point(x_coord, y_coord, /*assert_on_curve=*/false);
269 point.assert_coordinates_in_field();
270
271 // Circuit should fail because y coordinate is out of field
273 }
274 }
275 }
276
278 {
280 size_t num_repetitions = 10;
281 for (size_t i = 0; i < num_repetitions; ++i) {
282 auto [input_a, a] = get_random_point(&builder, a_type);
283 auto [input_b, b] = get_random_point(&builder, b_type);
284
285 // Set different tags in a and b
286 a.set_origin_tag(submitted_value_origin_tag);
287 b.set_origin_tag(challenge_origin_tag);
288
289 uint64_t before = builder.get_num_finalized_gates_inefficient();
290 element_ct c = a + b;
291 uint64_t after = builder.get_num_finalized_gates_inefficient();
292
293 // Check that the resulting tag is the union of inputs' tgs
294 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
295 if (i == num_repetitions - 1) {
296 benchmark_info(Builder::NAME_STRING, "Biggroup", "ADD", "Gate Count", after - before);
297 }
298
299 affine_element c_expected(element(input_a) + element(input_b));
300
301 uint256_t c_x_u256 = c.x().get_value().lo;
302 uint256_t c_y_u256 = c.y().get_value().lo;
303
304 fq c_x_result(c_x_u256);
305 fq c_y_result(c_y_u256);
306
307 EXPECT_EQ(c_x_result, c_expected.x);
308 EXPECT_EQ(c_y_result, c_expected.y);
309 }
310
312 }
313
315 {
317 size_t num_repetitions = 10;
318 for (size_t i = 0; i < num_repetitions; ++i) {
319 auto [input_a, a] = get_random_point(&builder, a_type);
320 auto [input_b, b] = get_random_point(&builder, b_type);
321
322 element_ct original_a = a;
323 a += b;
324
325 affine_element expected(element(input_a) + element(input_b));
326 uint256_t result_x = a.x().get_value().lo;
327 uint256_t result_y = a.y().get_value().lo;
328
329 EXPECT_EQ(fq(result_x), expected.x);
330 EXPECT_EQ(fq(result_y), expected.y);
331 }
333 }
334
336 {
338 size_t num_repetitions = 1;
339 for (size_t i = 0; i < num_repetitions; ++i) {
340 affine_element input_a(element::random_element());
341 affine_element input_b(element::random_element());
342 input_b.self_set_infinity();
343 element_ct a = element_ct::from_witness(&builder, input_a);
344
345 // create copy of a with different witness
346 element_ct a_alternate = element_ct::from_witness(&builder, input_a);
347 element_ct a_negated = element_ct::from_witness(&builder, -input_a);
348 element_ct b = element_ct::from_witness(&builder, input_b);
349
350 // Set different tags on all elements
351 a.set_origin_tag(submitted_value_origin_tag);
352 b.set_origin_tag(challenge_origin_tag);
353 a_alternate.set_origin_tag(next_challenge_tag);
354 // We can't use next_submitted_value tag here or it will break, so construct a tag manually
355 const auto second_round_challenge_tag =
356 OriginTag(/*parent_index=*/0, /*child_index=*/2, /*is_submitted=*/false);
357 a_negated.set_origin_tag(second_round_challenge_tag);
358
359 element_ct c = a + b;
360 element_ct d = b + a;
361 element_ct e = b + b;
362 element_ct f = a + a;
363 element_ct g = a + a_alternate;
364 element_ct h = a + a_negated;
365
366 // Check the resulting tags are correct unions of input tags
367 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
368 EXPECT_EQ(d.get_origin_tag(), first_two_merged_tag);
369 EXPECT_EQ(e.get_origin_tag(), challenge_origin_tag);
370 EXPECT_EQ(f.get_origin_tag(), submitted_value_origin_tag);
371 EXPECT_EQ(g.get_origin_tag(), first_and_third_merged_tag);
372 EXPECT_EQ(h.get_origin_tag(), OriginTag(submitted_value_origin_tag, second_round_challenge_tag));
373
374 affine_element c_expected = affine_element(element(input_a) + element(input_b));
375 affine_element d_expected = affine_element(element(input_b) + element(input_a));
376 affine_element e_expected = affine_element(element(input_b) + element(input_b));
377 affine_element f_expected = affine_element(element(input_a) + element(input_a));
378 affine_element g_expected = affine_element(element(input_a) + element(input_a));
379 affine_element h_expected = affine_element(element(input_a) + element(-input_a));
380
381 EXPECT_EQ(c.get_value(), c_expected);
382 EXPECT_EQ(d.get_value(), d_expected);
383 EXPECT_EQ(e.get_value(), e_expected);
384 EXPECT_EQ(f.get_value(), f_expected);
385 EXPECT_EQ(g.get_value(), g_expected);
386 EXPECT_EQ(h.get_value(), h_expected);
387 }
388
390 }
396 {
398 size_t num_repetitions = 5;
399 for (size_t i = 0; i < num_repetitions; ++i) {
400 // Create canonical point at infinity (constant and witness cases)
401 element_ct input_a = element_ct::constant_infinity(&builder);
402 element_ct input_b = element_ct::from_witness(&builder, affine_element::infinity());
403
404 // Set tags
405 input_a.set_origin_tag(submitted_value_origin_tag);
406 input_b.set_origin_tag(challenge_origin_tag);
407
408 auto standard_a = input_a.get_standard_form();
409 auto standard_b = input_b.get_standard_form();
410
411 // Check that tags are preserved
412 EXPECT_EQ(standard_a.get_origin_tag(), submitted_value_origin_tag);
413 EXPECT_EQ(standard_b.get_origin_tag(), challenge_origin_tag);
414
415 EXPECT_EQ(is_infinity(standard_a), true);
416 EXPECT_EQ(is_infinity(standard_b), true);
417
418 fq standard_a_x = standard_a.x().get_value().lo;
419 fq standard_a_y = standard_a.y().get_value().lo;
420
421 fq standard_b_x = standard_b.x().get_value().lo;
422 fq standard_b_y = standard_b.y().get_value().lo;
423
424 // Canonical infinity points should maintain (0, 0) coordinates
425 EXPECT_EQ(standard_a_x, 0);
426 EXPECT_EQ(standard_a_y, 0);
427 EXPECT_EQ(standard_b_x, 0);
428 EXPECT_EQ(standard_b_y, 0);
429 }
430
432 }
433
435 {
437 size_t num_repetitions = 10;
438 for (size_t i = 0; i < num_repetitions; ++i) {
439 auto [input_a, a] = get_random_point(&builder, a_type);
440 auto [input_b, b] = get_random_point(&builder, b_type);
441
442 // Set tags
443 a.set_origin_tag(submitted_value_origin_tag);
444 b.set_origin_tag(challenge_origin_tag);
445
446 element_ct c = a - b;
447
448 // Check tags have merged
449 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
450
451 affine_element c_expected(element(input_a) - element(input_b));
452
453 uint256_t c_x_u256 = c.x().get_value().lo;
454 uint256_t c_y_u256 = c.y().get_value().lo;
455
456 fq c_x_result(c_x_u256);
457 fq c_y_result(c_y_u256);
458
459 EXPECT_EQ(c_x_result, c_expected.x);
460 EXPECT_EQ(c_y_result, c_expected.y);
461 }
462
464 }
465
467 {
469 size_t num_repetitions = 10;
470 for (size_t i = 0; i < num_repetitions; ++i) {
471 auto [input_a, a] = get_random_point(&builder, a_type);
472 auto [input_b, b] = get_random_point(&builder, b_type);
473
474 a -= b;
475
476 affine_element expected(element(input_a) - element(input_b));
477 uint256_t result_x = a.x().get_value().lo;
478 uint256_t result_y = a.y().get_value().lo;
479
480 EXPECT_EQ(fq(result_x), expected.x);
481 EXPECT_EQ(fq(result_y), expected.y);
482 }
484 }
485
487 {
489 size_t num_repetitions = 1;
490 for (size_t i = 0; i < num_repetitions; ++i) {
491 affine_element input_a(element::random_element());
492 affine_element input_b(element::random_element());
493 input_b.self_set_infinity();
494 element_ct a = element_ct::from_witness(&builder, input_a);
495
496 // create copy of a with different witness
497 element_ct a_alternate = element_ct::from_witness(&builder, input_a);
498 element_ct a_negated = element_ct::from_witness(&builder, -input_a);
499 element_ct b = element_ct::from_witness(&builder, input_b);
500
501 // Set different tags on all elements
502 a.set_origin_tag(submitted_value_origin_tag);
503 b.set_origin_tag(challenge_origin_tag);
504 a_alternate.set_origin_tag(next_challenge_tag);
505 // We can't use next_submitted_value tag here or it will break, so construct a tag manually
506 const auto second_round_challenge_tag =
507 OriginTag(/*parent_index=*/0, /*child_index=*/2, /*is_submitted=*/false);
508 a_negated.set_origin_tag(second_round_challenge_tag);
509
510 element_ct c = a - b;
511 element_ct d = b - a;
512 element_ct e = b - b;
513 element_ct f = a - a;
514 element_ct g = a - a_alternate;
515 element_ct h = a - a_negated;
516
517 // Check the resulting tags are correct unions of input tags
518 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
519 EXPECT_EQ(d.get_origin_tag(), first_two_merged_tag);
520 EXPECT_EQ(e.get_origin_tag(), challenge_origin_tag);
521 EXPECT_EQ(f.get_origin_tag(), submitted_value_origin_tag);
522 EXPECT_EQ(g.get_origin_tag(), first_and_third_merged_tag);
523 EXPECT_EQ(h.get_origin_tag(), OriginTag(submitted_value_origin_tag, second_round_challenge_tag));
524
525 affine_element c_expected = affine_element(element(input_a) - element(input_b));
526 affine_element d_expected = affine_element(element(input_b) - element(input_a));
527 affine_element e_expected = affine_element(element(input_b) - element(input_b));
528 affine_element f_expected = affine_element(element(input_a) - element(input_a));
529 affine_element g_expected = affine_element(element(input_a) - element(input_a));
530 affine_element h_expected = affine_element(element(input_a) - element(-input_a));
531
532 EXPECT_EQ(c.get_value(), c_expected);
533 EXPECT_EQ(d.get_value(), d_expected);
534 EXPECT_EQ(e.get_value(), e_expected);
535 EXPECT_EQ(f.get_value(), f_expected);
536 EXPECT_EQ(g.get_value(), g_expected);
537 EXPECT_EQ(h.get_value(), h_expected);
538 }
539
541 }
542
545 {
547 size_t num_repetitions = 10;
548 for (size_t i = 0; i < num_repetitions; ++i) {
549 auto [input_a, a] = get_random_point(&builder, a_type);
550 auto [input_b, b] = get_random_point(&builder, b_type);
551
552 element_ct result = a.checked_unconditional_add(b);
553
554 affine_element expected(element(input_a) + element(input_b));
555 uint256_t result_x = result.x().get_value().lo;
556 uint256_t result_y = result.y().get_value().lo;
557
558 EXPECT_EQ(fq(result_x), expected.x);
559 EXPECT_EQ(fq(result_y), expected.y);
560 }
562 }
563
566 {
568 size_t num_repetitions = 10;
569 for (size_t i = 0; i < num_repetitions; ++i) {
570 auto [input_a, a] = get_random_point(&builder, a_type);
571 auto [input_b, b] = get_random_point(&builder, b_type);
572
573 element_ct result = a.checked_unconditional_subtract(b);
574
575 affine_element expected(element(input_a) - element(input_b));
576 uint256_t result_x = result.x().get_value().lo;
577 uint256_t result_y = result.y().get_value().lo;
578
579 EXPECT_EQ(fq(result_x), expected.x);
580 EXPECT_EQ(fq(result_y), expected.y);
581 }
583 }
584
587 {
589 size_t num_repetitions = 10;
590 for (size_t i = 0; i < num_repetitions; ++i) {
591 const auto [input_a, a] = get_random_point(&builder, a_type);
592 const auto [input_b, b] = get_random_point(&builder, b_type);
593
594 // Since unchecked_unconditional_add_sub is private in biggroup, we test it via the element_test_accessor
596
597 affine_element expected_sum(element(input_a) + element(input_b));
598 affine_element expected_diff(element(input_a) - element(input_b));
599
600 uint256_t sum_x = sum.x().get_value().lo;
601 uint256_t sum_y = sum.y().get_value().lo;
602 uint256_t diff_x = diff.x().get_value().lo;
603 uint256_t diff_y = diff.y().get_value().lo;
604
605 EXPECT_EQ(fq(sum_x), expected_sum.x);
606 EXPECT_EQ(fq(sum_y), expected_sum.y);
607 EXPECT_EQ(fq(diff_x), expected_diff.x);
608 EXPECT_EQ(fq(diff_y), expected_diff.y);
609 }
611 }
612
614 {
616 size_t num_repetitions = 10;
617 for (size_t i = 0; i < num_repetitions; ++i) {
618 auto [input_a, a] = get_random_point(&builder, a_type);
619
620 a.set_origin_tag(submitted_value_origin_tag);
621
622 element_ct c = a.dbl();
623
624 // Check that the tag is preserved
625 EXPECT_EQ(c.get_origin_tag(), submitted_value_origin_tag);
626
627 affine_element c_expected(element(input_a).dbl());
628
629 uint256_t c_x_u256 = c.x().get_value().lo;
630 uint256_t c_y_u256 = c.y().get_value().lo;
631
632 fq c_x_result(c_x_u256);
633 fq c_y_result(c_y_u256);
634
635 EXPECT_EQ(c_x_result, c_expected.x);
636 EXPECT_EQ(c_y_result, c_expected.y);
637 }
639 }
640
642 {
644 {
645 // Case 1: Doubling point at infinity should return point at infinity
646 affine_element input_infinity(element::random_element());
647 input_infinity.self_set_infinity();
648 element_ct a_infinity = element_ct::from_witness(&builder, input_infinity);
649 a_infinity.set_origin_tag(submitted_value_origin_tag);
650
651 element_ct result_infinity = a_infinity.dbl();
652
653 // Check that the tag is preserved
654 EXPECT_EQ(result_infinity.get_origin_tag(), submitted_value_origin_tag);
655
656 // Result should be point at infinity
657 EXPECT_TRUE(is_infinity(result_infinity));
658 }
659 {
660 // Case 2: Doubling a normal point should not result in infinity
661 affine_element input_normal(element::random_element());
662 element_ct a_normal = element_ct::from_witness(&builder, input_normal);
663 a_normal.set_origin_tag(submitted_value_origin_tag);
664
665 element_ct result_normal = a_normal.dbl();
666
667 // Check that the tag is preserved
668 EXPECT_EQ(result_normal.get_origin_tag(), submitted_value_origin_tag);
669
670 // Result should not be point at infinity (with overwhelming probability)
671 EXPECT_FALSE(is_infinity(result_normal));
672
673 // Verify correctness
674 affine_element expected_normal(element(input_normal).dbl());
675 uint256_t result_x = result_normal.x().get_value().lo;
676 uint256_t result_y = result_normal.y().get_value().lo;
677 fq expected_x(result_x);
678 fq expected_y(result_y);
679 EXPECT_EQ(expected_x, expected_normal.x);
680 EXPECT_EQ(expected_y, expected_normal.y);
681 }
683 }
684
686 {
688
689 // For bn254 curve: y^2 = x^3 + 3
690 // We need a point where y = 0, which means x^3 = -3
691 // For most curves, there may not be a rational point with y = 0
692 // So we test the logic by creating a witness point with y = 0 explicitly
693 // Even if it's not on the curve, we can test the doubling logic
694 affine_element test_point(element::random_element());
695
696 // Create a point with y = 0 (may not be on curve, but tests the edge case)
697 auto x_coord = element_ct::BaseField::from_witness(&builder, test_point.x);
698 auto y_coord = element_ct::BaseField::from_witness(&builder, fq(0));
699 // Skip curve check since we're intentionally creating an invalid point to test edge case
700 // Note: is_infinity is auto-detected as false since x coordinate is non-zero
701 element_ct a(x_coord, y_coord, /*assert_on_curve=*/false);
702
703 a.set_origin_tag(submitted_value_origin_tag);
704
705 // With the new assertion, attempting to double a point with y = 0 should throw
706 // because for valid curves like bn254, y = 0 cannot occur on the curve
707 EXPECT_THROW_WITH_MESSAGE(a.dbl(), "Attempting to dbl a point with y = 0, not allowed.");
708 }
709
711 {
712 // Test that P + P equals P.dbl()
714 size_t num_repetitions = 5;
715 for (size_t i = 0; i < num_repetitions; ++i) {
716 auto [input_a, a] = get_random_point(&builder, InputType::WITNESS);
717
718 element_ct sum = a + a;
719 element_ct doubled = a.dbl();
720
721 // Results should match
722 uint256_t sum_x = sum.x().get_value().lo;
723 uint256_t sum_y = sum.y().get_value().lo;
724 uint256_t dbl_x = doubled.x().get_value().lo;
725 uint256_t dbl_y = doubled.y().get_value().lo;
726
727 EXPECT_EQ(fq(sum_x), fq(dbl_x));
728 EXPECT_EQ(fq(sum_y), fq(dbl_y));
729 EXPECT_EQ(is_infinity(sum), is_infinity(doubled));
730 }
732 }
733
735 {
736 // Test that P - (-P) equals 2P
738 size_t num_repetitions = 5;
739 for (size_t i = 0; i < num_repetitions; ++i) {
740 auto [input_a, a] = get_random_point(&builder, InputType::WITNESS);
741
742 element_ct neg_a = -a;
743 element_ct result = a - neg_a;
744 element_ct expected = a.dbl();
745
746 // P - (-P) = P + P = 2P
747 uint256_t result_x = result.x().get_value().lo;
748 uint256_t result_y = result.y().get_value().lo;
749 uint256_t expected_x = expected.x().get_value().lo;
750 uint256_t expected_y = expected.y().get_value().lo;
751
752 EXPECT_EQ(fq(result_x), fq(expected_x));
753 EXPECT_EQ(fq(result_y), fq(expected_y));
754 }
756 }
757
761 {
763 size_t num_repetitions = 10;
764 for (size_t i = 0; i < num_repetitions; ++i) {
765
766 auto [input_a, a] = get_random_point(&builder, a_type);
767 auto [input_b, b] = get_random_point(&builder, b_type);
768 auto [input_c, c] = get_random_point(&builder, c_type);
769
770 auto acc = element_ct::chain_add_start(a, b);
771 auto acc_out = element_ct::chain_add(c, acc);
772 element_ct result = element_ct::chain_add_end(acc_out);
773
774 // Verify result
775 affine_element expected(element(input_a) + element(input_b) + element(input_c));
776 uint256_t result_x = result.x().get_value().lo;
777 uint256_t result_y = result.y().get_value().lo;
778 EXPECT_EQ(fq(result_x), expected.x);
779 EXPECT_EQ(fq(result_y), expected.y);
780
781 // Check intermediate values
782 auto lambda_prev = (input_b.y - input_a.y) / (input_b.x - input_a.x);
783 auto x3_prev = lambda_prev * lambda_prev - input_b.x - input_a.x;
784 auto y3_prev = lambda_prev * (input_a.x - x3_prev) - input_a.y;
785 auto lambda = (y3_prev - input_c.y) / (x3_prev - input_c.x);
786 auto x3 = lambda * lambda - x3_prev - input_c.x;
787
788 uint256_t x3_u256 = acc_out.x3_prev.get_value().lo;
789 uint256_t lambda_u256 = acc_out.lambda_prev.get_value().lo;
790
791 fq x3_result(x3_u256);
792 fq lambda_result(lambda_u256);
793
794 EXPECT_EQ(x3_result, x3);
795 EXPECT_EQ(lambda_result, lambda);
796 }
797
799 }
800
802 {
804 size_t num_repetitions = 10;
805 for (size_t i = 0; i < num_repetitions; ++i) {
806 affine_element acc_small(element::random_element());
807 element_ct acc_big = element_ct::from_witness(&builder, acc_small);
808
810 for (size_t j = 0; j < i; ++j) {
811 affine_element add_1_small_0(element::random_element());
812 element_ct add_1_big_0 = element_ct::from_witness(&builder, add_1_small_0);
813 affine_element add_2_small_0(element::random_element());
814 element_ct add_2_big_0 = element_ct::from_witness(&builder, add_2_small_0);
815 typename element_ct::chain_add_accumulator add_1 =
816 element_ct::chain_add_start(add_1_big_0, add_2_big_0);
817 to_add.emplace_back(add_1);
818 }
819 acc_big.multiple_montgomery_ladder(to_add);
820 }
821
823 }
824
826 {
828 size_t num_repetitions = 10;
829 for (size_t i = 0; i < num_repetitions; ++i) {
830 auto [input_a, a] = get_random_point(&builder, point_type);
831
832 element_ct normalized = a.normalize();
833
834 // Normalized should equal the original
835 uint256_t x_before = a.x().get_value().lo;
836 uint256_t y_before = a.y().get_value().lo;
837 uint256_t x_after = normalized.x().get_value().lo;
838 uint256_t y_after = normalized.y().get_value().lo;
839
840 EXPECT_EQ(fq(x_before), fq(x_after));
841 EXPECT_EQ(fq(y_before), fq(y_after));
842 }
844 }
845
846 static void test_reduce(InputType point_type = InputType::WITNESS)
847 {
849 size_t num_repetitions = 10;
850 for (size_t i = 0; i < num_repetitions; ++i) {
851 auto [input_a, a] = get_random_point(&builder, point_type);
852
853 element_ct reduced = a.reduce();
854
855 // Reduced should equal the original
856 uint256_t x_before = a.x().get_value().lo;
857 uint256_t y_before = a.y().get_value().lo;
858 uint256_t x_after = reduced.x().get_value().lo;
859 uint256_t y_after = reduced.y().get_value().lo;
860
861 EXPECT_EQ(fq(x_before), fq(x_after));
862 EXPECT_EQ(fq(y_before), fq(y_after));
863 }
865 }
866
868 {
870 auto [input_a, a] = get_random_point(&builder, a_type);
871
872 element_ct neg_a = -a;
873
874 affine_element expected = affine_element(-element(input_a));
875 uint512_t neg_x_u512 = uint512_t(neg_a.x().get_value()) % uint512_t(fq::modulus);
876 uint512_t neg_y_u512 = uint512_t(neg_a.y().get_value()) % uint512_t(fq::modulus);
877 uint256_t neg_x = neg_x_u512.lo;
878 uint256_t neg_y = neg_y_u512.lo;
879
880 EXPECT_EQ(fq(neg_x), expected.x);
881 EXPECT_EQ(fq(neg_y), expected.y);
882
884 }
885
887 InputType predicate_type = InputType::WITNESS)
888 {
890 size_t num_repetitions = 10;
891 for (size_t i = 0; i < num_repetitions; ++i) {
892 // Get random point
893 auto [input_a, a] = get_random_point(&builder, point_type);
894 a.set_origin_tag(submitted_value_origin_tag);
895
896 // Get random predicate
897 bool predicate_value = (engine.get_random_uint8() % 2) != 0;
898 bool_ct predicate = (predicate_type == InputType::WITNESS) ? bool_ct(witness_ct(&builder, predicate_value))
899 : bool_ct(predicate_value);
900 predicate.set_origin_tag(challenge_origin_tag);
901
902 element_ct c = a.conditional_negate(predicate);
903
904 // Check the resulting tag is preserved
905 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
906
907 affine_element c_expected = predicate_value ? affine_element(-element(input_a)) : input_a;
908 EXPECT_EQ(c.get_value(), c_expected);
909 }
911 }
912
915 InputType predicate_type = InputType::WITNESS)
916 {
918 size_t num_repetitions = 10;
919 for (size_t i = 0; i < num_repetitions; ++i) {
920 auto [input_a, a] = get_random_point(&builder, a_type);
921 auto [input_b, b] = get_random_point(&builder, b_type);
922
923 bool predicate_value = (engine.get_random_uint8() % 2) != 0;
924 bool_ct predicate = (predicate_type == InputType::WITNESS) ? bool_ct(witness_ct(&builder, predicate_value))
925 : bool_ct(predicate_value);
926
927 // Set different tags in a and b and the predicate
928 a.set_origin_tag(submitted_value_origin_tag);
929 b.set_origin_tag(challenge_origin_tag);
930 predicate.set_origin_tag(next_challenge_tag);
931
932 element_ct c = a.conditional_select(b, predicate);
933
934 // Check that the resulting tag is the union of inputs' tags
935 EXPECT_EQ(c.get_origin_tag(), first_second_third_merged_tag);
936
937 affine_element c_expected = predicate_value ? input_b : input_a;
938 EXPECT_EQ(c.get_value(), c_expected);
939 }
941 }
942
944 {
945 // Case 1: Should pass because the points are identical
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.incomplete_assert_equal(b, "elements don't match");
959 }
961 }
962 // Case 2: Should pass because the points are identical and at infinity (canonical representation)
963 {
965 size_t num_repetitions = 10;
966 for (size_t i = 0; i < num_repetitions; ++i) {
967 affine_element input_a(element::random_element());
968 input_a.self_set_infinity();
969 element_ct a = element_ct::from_witness(&builder, input_a);
970 element_ct b = element_ct::from_witness(&builder, input_a);
971
972 // Set different tags in a and b
973 a.set_origin_tag(submitted_value_origin_tag);
974 b.set_origin_tag(challenge_origin_tag);
975
976 a.incomplete_assert_equal(b, "elements don't match");
977 }
979 }
980 // Case 3: Self-assertion (point equals itself)
981 {
983 affine_element input(element::random_element());
984 element_ct a = element_ct::from_witness(&builder, input);
985
986 a.incomplete_assert_equal(a, "self assertion test");
987
989 }
990 }
991
993 {
994 // Case 1: Should fail because the points are different
995 {
997 affine_element input_a(element::random_element());
998 affine_element input_b(element::random_element());
999 // Ensure inputs are different
1000 while (input_a == input_b) {
1001 input_b = element::random_element();
1002 }
1003 element_ct a = element_ct::from_witness(&builder, input_a);
1004 element_ct b = element_ct::from_witness(&builder, input_b);
1005
1006 // Set different tags in a and b
1007 a.set_origin_tag(submitted_value_origin_tag);
1008 b.set_origin_tag(challenge_origin_tag);
1009
1010 a.incomplete_assert_equal(b, "elements don't match");
1011
1012 // Circuit should fail (Circuit checker doesn't fail because it doesn't actually check copy constraints,
1013 // it only checks gate constraints)
1014 EXPECT_EQ(builder.failed(), true);
1015 EXPECT_EQ(builder.err(), "elements don't match (x coordinate)");
1016 }
1017 // Case 2: Should fail because the points have same x but different y
1018 {
1020 affine_element input_a(element::random_element());
1021
1022 // Create a point with the same x coordinate but different y
1023 // For an elliptic curve y^2 = x^3 + ax + b, if (x, y) is on the curve, then (x, -y) is also on the
1024 // curve
1025 affine_element input_b = input_a;
1026 input_b.y = -input_a.y; // Negate y to get a different point with same x
1027
1028 // Construct the circuit elements with same x but different y
1029 auto x_coord = element_ct::BaseField::from_witness(&builder, input_a.x);
1030 auto y_coord_a = element_ct::BaseField::from_witness(&builder, input_a.y);
1031 auto y_coord_b = element_ct::BaseField::from_witness(&builder, input_b.y);
1032
1033 // Note: is_infinity is auto-detected as false since coordinates are non-zero
1034 element_ct a(x_coord, y_coord_a);
1035 element_ct b(x_coord, y_coord_b);
1036
1037 // Set different tags in a and b
1038 a.set_origin_tag(submitted_value_origin_tag);
1039 b.set_origin_tag(challenge_origin_tag);
1040
1041 a.incomplete_assert_equal(b, "elements don't match");
1042
1043 // Circuit should fail with y coordinate error
1044 EXPECT_EQ(builder.failed(), true);
1045 EXPECT_EQ(builder.err(), "elements don't match (y coordinate)");
1046 }
1047 // Case 3: Infinity flag mismatch (one point at infinity, one not)
1048 {
1050 affine_element input_a(element::random_element());
1051 affine_element input_b(element::random_element());
1052
1053 input_a.self_set_infinity();
1054 element_ct a = element_ct::from_witness(&builder, input_a); // at infinity
1055 element_ct b = element_ct::from_witness(&builder, input_b); // not at infinity
1056
1057 a.incomplete_assert_equal(b, "infinity flag mismatch test");
1058
1059 EXPECT_EQ(builder.failed(), true);
1060 if constexpr (HasGoblinBuilder<TestType>) {
1061 // Goblin has no infinity flag; (0,0) coords differ from b's coords
1062 EXPECT_EQ(builder.err(), "infinity flag mismatch test (x coordinate)");
1063 } else {
1064 EXPECT_EQ(builder.err(), "infinity flag mismatch test (infinity flag)");
1065 }
1066 }
1067 }
1068
1069 static void test_compute_naf()
1070 {
1072 size_t max_num_bits = 254;
1073 for (size_t length = 2; length < max_num_bits; length += 1) {
1074
1075 fr scalar_val;
1076
1077 uint256_t scalar_raw = engine.get_random_uint256();
1078 scalar_raw = scalar_raw >> (256 - length);
1079
1080 scalar_val = fr(scalar_raw);
1081
1082 // We test non-zero scalars here
1083 if (scalar_val == fr(0)) {
1084 scalar_val += 1;
1085 };
1086 scalar_ct scalar = scalar_ct::from_witness(&builder, scalar_val);
1087 // Set tag for scalar
1088 scalar.set_origin_tag(submitted_value_origin_tag);
1089 auto naf = element_ct::compute_naf(scalar, length);
1090
1091 for (const auto& bit : naf) {
1092 // Check that the tag is propagated to bits
1093 EXPECT_EQ(bit.get_origin_tag(), submitted_value_origin_tag);
1094 }
1095 // scalar = -naf[L] + \sum_{i=0}^{L-1}(1-2*naf[i]) 2^{L-1-i}
1096 fr reconstructed_val(0);
1097 for (size_t i = 0; i < length; i++) {
1098 reconstructed_val += (fr(1) - fr(2) * fr(naf[i].get_value())) * fr(uint256_t(1) << (length - 1 - i));
1099 };
1100 reconstructed_val -= fr(naf[length].get_value());
1101 EXPECT_EQ(scalar_val, reconstructed_val);
1102 }
1103
1105 }
1106
1108 {
1110 size_t length = fr::modulus.get_msb() + 1;
1111
1112 // Our algorithm for input 0 outputs the NAF representation of r (the field modulus)
1113 fr scalar_val(0);
1114
1115 scalar_ct scalar = scalar_ct::from_witness(&builder, scalar_val);
1116
1117 // Set tag for scalar
1118 scalar.set_origin_tag(submitted_value_origin_tag);
1119 auto naf = element_ct::compute_naf(scalar, length);
1120
1121 for (const auto& bit : naf) {
1122 // Check that the tag is propagated to bits
1123 EXPECT_EQ(bit.get_origin_tag(), submitted_value_origin_tag);
1124 }
1125
1126 // scalar = -naf[L] + \sum_{i=0}^{L-1}(1-2*naf[i]) 2^{L-1-i}
1127 fr reconstructed_val(0);
1128 uint256_t reconstructed_u256(0);
1129 for (size_t i = 0; i < length; i++) {
1130 reconstructed_val += (fr(1) - fr(2) * fr(naf[i].get_value())) * fr(uint256_t(1) << (length - 1 - i));
1131 reconstructed_u256 +=
1132 (uint256_t(1) - uint256_t(2) * uint256_t(naf[i].get_value())) * (uint256_t(1) << (length - 1 - i));
1133 };
1134 reconstructed_val -= fr(naf[length].get_value());
1135 EXPECT_EQ(scalar_val, reconstructed_val);
1136 EXPECT_EQ(reconstructed_u256, uint256_t(fr::modulus));
1137
1139 }
1140
1142 {
1144
1145 // Create a scalar that is even (skew=1) and has least-significant 2L bits all 0 (L=68, 2L=136)
1146 // This causes overflow in negative_lo = skew + sum_{i=0}^{135} a'_{i+1} * 2^i = 1 + (2^136 - 1) = 2^136
1147 //
1148 // Scalar chosen such that least significant 136 bits are zero:
1149 fr scalar_native = fr::random_element();
1150 uint256_t scalar_raw = uint256_t(scalar_native);
1151 scalar_raw = (scalar_raw >> 136) << 136;
1152 fr scalar_val = fr(scalar_raw);
1153 scalar_ct scalar = scalar_ct::from_witness(&builder, scalar_val);
1154 scalar.set_origin_tag(submitted_value_origin_tag);
1155
1156 // Compute NAF with full field size
1157 const size_t length = fr::modulus.get_msb() + 1;
1158
1159 // This should not overflow with the fix in place
1160 auto naf = element_ct::compute_naf(scalar, length);
1161
1162 // Verify NAF correctness
1163 for (const auto& bit : naf) {
1164 EXPECT_EQ(bit.get_origin_tag(), submitted_value_origin_tag);
1165 }
1166
1167 // Reconstruct scalar from NAF: scalar = -naf[L] + \sum_{i=0}^{L-1}(1-2*naf[i]) 2^{L-1-i}
1168 fr reconstructed_val(0);
1169 for (size_t i = 0; i < length; i++) {
1170 reconstructed_val += (fr(1) - fr(2) * fr(naf[i].get_value())) * fr(uint256_t(1) << (length - 1 - i));
1171 }
1172 reconstructed_val -= fr(naf[length].get_value());
1173
1174 EXPECT_EQ(scalar_val, reconstructed_val);
1176 }
1177
1178 static void test_mul(InputType scalar_type = InputType::WITNESS, InputType point_type = InputType::WITNESS)
1179 {
1181 size_t num_repetitions = 1;
1182 for (size_t i = 0; i < num_repetitions; ++i) {
1183 auto [input, P] = get_random_point(&builder, point_type);
1184 auto [scalar, x] = get_random_scalar(&builder, scalar_type, /*even*/ true);
1185
1186 // Set input tags
1187 x.set_origin_tag(challenge_origin_tag);
1188 P.set_origin_tag(submitted_value_origin_tag);
1189
1190 std::cerr << "gates before mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1191 element_ct c = P * x;
1192 std::cerr << "builder aftr mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1193 affine_element c_expected(element(input) * scalar);
1194
1195 // Check the result of the multiplication has a tag that's the union of inputs' tags
1196 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
1197 fq c_x_result(c.x().get_value().lo);
1198 fq c_y_result(c.y().get_value().lo);
1199
1200 EXPECT_EQ(c_x_result, c_expected.x);
1201 EXPECT_EQ(c_y_result, c_expected.y);
1202 }
1203
1205 }
1206
1208 InputType point_type = InputType::WITNESS)
1209 {
1211
1212 const auto run_mul_and_check = [&](element_ct& P, scalar_ct& x, const affine_element& expected) {
1213 // Set input tags
1214 x.set_origin_tag(challenge_origin_tag);
1215 P.set_origin_tag(submitted_value_origin_tag);
1216
1217 // Perform multiplication
1218 element_ct result = P * x;
1219
1220 // Check the result tag
1221 EXPECT_EQ(result.get_origin_tag(), first_two_merged_tag);
1222
1223 // Check if result is infinity
1224 bool result_is_inf = is_infinity(result);
1225 bool expected_is_inf = expected.is_point_at_infinity();
1226
1227 EXPECT_EQ(result_is_inf, expected_is_inf);
1228
1229 // If not infinity, check if the coordinates match
1230 if (!expected_is_inf) {
1231 uint256_t result_x = result.x().get_value().lo;
1232 uint256_t result_y = result.y().get_value().lo;
1233
1234 EXPECT_EQ(fq(result_x), expected.x);
1235 EXPECT_EQ(fq(result_y), expected.y);
1236 }
1237 };
1238
1239 // Case 1: P * 0 = ∞
1240 {
1241 auto [input, P] = get_random_point(&builder, point_type);
1242 scalar_ct x = (scalar_type == InputType::WITNESS) ? scalar_ct::from_witness(&builder, fr(0))
1243 : scalar_ct(&builder, fr(0));
1244 affine_element expected_infinity = affine_element(element::infinity());
1245 run_mul_and_check(P, x, expected_infinity);
1246 }
1247 // Case 2: (∞) * k = ∞
1248 {
1249 auto [input, P] = get_random_point(&builder, point_type);
1250 if (point_type == InputType::CONSTANT) {
1251 P = element_ct::constant_infinity(&builder);
1252 } else {
1253 input.self_set_infinity();
1254 P = element_ct::from_witness(&builder, input);
1255 }
1256
1257 auto [scalar, x] = get_random_scalar(&builder, scalar_type, /*even*/ true);
1258 affine_element expected_infinity = affine_element(element::infinity());
1259 run_mul_and_check(P, x, expected_infinity);
1260 }
1261 // Case 3: P * 1 = P
1262 {
1263 auto [input, P] = get_random_point(&builder, point_type);
1264 scalar_ct one = (scalar_type == InputType::WITNESS) ? scalar_ct::from_witness(&builder, fr(1))
1265 : scalar_ct(&builder, fr(1));
1266 run_mul_and_check(P, one, input);
1267 }
1268 // Case 4: P * (-1) = -P
1269 {
1270 auto [input, P] = get_random_point(&builder, point_type);
1271 fr neg_one = -fr(1);
1272 scalar_ct neg_one_ct = (scalar_type == InputType::WITNESS) ? scalar_ct::from_witness(&builder, neg_one)
1273 : scalar_ct(&builder, neg_one);
1274 affine_element expected = affine_element(-element(input));
1275 run_mul_and_check(P, neg_one_ct, expected);
1276 }
1278 }
1279
1280 // Test short scalar mul with variable bit lengths.
1282 {
1284
1285 std::vector<size_t> test_lengths = { 2, 3, 10, 11, 31, 32, 63, 64, 127, 128, 252, 253 };
1286
1287 for (size_t i : test_lengths) {
1288 affine_element input(element::random_element());
1289 // Get a random 256 integer
1290 uint256_t scalar_raw = engine.get_random_uint256();
1291 // Produce a length =< i scalar.
1292 scalar_raw = scalar_raw >> (256 - i);
1293 fr scalar = fr(scalar_raw);
1294
1295 // Avoid multiplication by 0 that may occur when `i` is small
1296 if (scalar == fr(0)) {
1297 scalar += 1;
1298 };
1299
1300 element_ct P = element_ct::from_witness(&builder, input);
1301 scalar_ct x = scalar_ct::from_witness(&builder, scalar);
1302
1303 // Set input tags
1304 x.set_origin_tag(challenge_origin_tag);
1305 P.set_origin_tag(submitted_value_origin_tag);
1306
1307 std::cerr << "gates before mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1308 // Multiply using specified scalar length
1309 element_ct c = P.scalar_mul(x, i);
1310 std::cerr << "builder aftr mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1311 affine_element c_expected(element(input) * scalar);
1312
1313 // Check the result of the multiplication has a tag that's the union of inputs' tags
1314 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
1315 fq c_x_result(c.x().get_value().lo);
1316 fq c_y_result(c.y().get_value().lo);
1317
1318 EXPECT_EQ(c_x_result, c_expected.x);
1319
1320 EXPECT_EQ(c_y_result, c_expected.y);
1321 }
1322
1324 }
1325
1327 {
1328 // We check that a point at infinity preserves `is_point_at_infinity()` flag after being multiplied against
1329 // a short scalar and also check that the number of gates in this case is more than the number of gates
1330 // spent on a finite point.
1331
1332 // Populate test points.
1333 std::vector<element> points(2);
1334
1335 points[0] = element::infinity();
1336 points[1] = element::random_element();
1337 // Containter for gate counts.
1338 std::vector<size_t> gates(2);
1339
1340 // We initialize this flag as `true`, because the first result is expected to be the point at infinity.
1341 bool expect_infinity = true;
1342
1343 for (auto [point, num_gates] : zip_view(points, gates)) {
1345
1346 const size_t max_num_bits = 128;
1347 // Get a random 256-bit integer
1348 uint256_t scalar_raw = engine.get_random_uint256();
1349 // Produce a length =< max_num_bits scalar.
1350 scalar_raw = scalar_raw >> (256 - max_num_bits);
1351 fr scalar = fr(scalar_raw);
1352
1353 element_ct P = element_ct::from_witness(&builder, point);
1354 scalar_ct x = scalar_ct::from_witness(&builder, scalar);
1355
1356 // Set input tags
1357 x.set_origin_tag(challenge_origin_tag);
1358 P.set_origin_tag(submitted_value_origin_tag);
1359
1360 std::cerr << "gates before mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1361 element_ct c = P.scalar_mul(x, max_num_bits);
1362 std::cerr << "builder aftr mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1363 num_gates = builder.get_num_finalized_gates_inefficient();
1364 // Check the result of the multiplication has a tag that's the union of inputs' tags
1365 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
1366
1367 EXPECT_EQ(is_infinity(c), expect_infinity);
1369 // The second point is finite, hence we flip the flag
1370 expect_infinity = false;
1371 }
1372 // Check that the numbers of gates are greater when multiplying by point at infinity,
1373 // because we transform (s * ∞) into (0 * G), and NAF representation of 0 ≡ NAF(r) which is 254 bits long.
1374 EXPECT_GT(gates[0], gates[1]);
1375 }
1376
1377 static void test_twin_mul()
1378 {
1380 size_t num_repetitions = 1;
1381 for (size_t i = 0; i < num_repetitions; ++i) {
1382 affine_element input_a(element::random_element());
1383 affine_element input_b(element::random_element());
1384 fr scalar_a(fr::random_element());
1385 fr scalar_b(fr::random_element());
1386 if ((uint256_t(scalar_a).get_bit(0) & 1) == 1) {
1387 scalar_a -= fr(1); // skew bit is 1
1388 }
1389 if ((uint256_t(scalar_b).get_bit(0) & 1) == 0) {
1390 scalar_b += fr(1); // skew bit is 0
1391 }
1392 element_ct P_a = element_ct::from_witness(&builder, input_a);
1393 scalar_ct x_a = scalar_ct::from_witness(&builder, scalar_a);
1394 element_ct P_b = element_ct::from_witness(&builder, input_b);
1395 scalar_ct x_b = scalar_ct::from_witness(&builder, scalar_b);
1396
1397 // Set tags
1398 P_a.set_origin_tag(submitted_value_origin_tag);
1399 x_a.set_origin_tag(challenge_origin_tag);
1400 P_b.set_origin_tag(next_submitted_value_origin_tag);
1401 x_b.set_origin_tag(next_challenge_tag);
1402
1403 element_ct c = element_ct::batch_mul({ P_a, P_b }, { x_a, x_b });
1404
1405 // Check that the resulting tag is a union of all tags
1406 EXPECT_EQ(c.get_origin_tag(), first_to_fourth_merged_tag);
1407 element input_c = (element(input_a) * scalar_a);
1408 element input_d = (element(input_b) * scalar_b);
1409 affine_element expected(input_c + input_d);
1410 fq c_x_result(c.x().get_value().lo);
1411 fq c_y_result(c.y().get_value().lo);
1412
1413 EXPECT_EQ(c_x_result, expected.x);
1414 EXPECT_EQ(c_y_result, expected.y);
1415 }
1417 }
1418
1420 {
1422 size_t num_repetitions = 1;
1423 for (size_t i = 0; i < num_repetitions; ++i) {
1424 affine_element input_a(element::random_element());
1425 affine_element input_b(element::random_element());
1426 input_b.self_set_infinity();
1427
1428 // Get two 128-bit scalars
1429 const size_t max_num_bits = 128;
1430 uint256_t scalar_raw_a = engine.get_random_uint256();
1431 scalar_raw_a = scalar_raw_a >> (256 - max_num_bits);
1432 fr scalar_a = fr(scalar_raw_a);
1433
1434 uint256_t scalar_raw_b = engine.get_random_uint256();
1435 scalar_raw_b = scalar_raw_b >> (256 - max_num_bits);
1436 fr scalar_b = fr(scalar_raw_b);
1437
1438 element_ct P_a = element_ct::from_witness(&builder, input_a); // A
1439 scalar_ct x_a = scalar_ct::from_witness(&builder, scalar_a); // s_1 (128 bits)
1440 element_ct P_b = element_ct::from_witness(&builder, input_b); // ∞
1441 scalar_ct x_b = scalar_ct::from_witness(&builder, scalar_b); // s_2 (128 bits)
1442
1443 // Set tags
1444 P_a.set_origin_tag(submitted_value_origin_tag);
1445 x_a.set_origin_tag(challenge_origin_tag);
1446 P_b.set_origin_tag(next_submitted_value_origin_tag);
1447 x_b.set_origin_tag(next_challenge_tag);
1448
1449 element_ct c = element_ct::batch_mul({ P_a, P_b }, { x_a, x_b }, 128);
1450
1451 // Check that the resulting tag is a union of all tags
1452 EXPECT_EQ(c.get_origin_tag(), first_to_fourth_merged_tag);
1453 element input_c = (element(input_a) * scalar_a);
1454 element input_d = (element(input_b) * scalar_b);
1455 affine_element expected(input_c + input_d);
1456 fq c_x_result(c.x().get_value().lo);
1457 fq c_y_result(c.y().get_value().lo);
1458
1459 EXPECT_EQ(c_x_result, expected.x);
1460 EXPECT_EQ(c_y_result, expected.y);
1461 }
1463 }
1464
1466 {
1468 affine_element input_P(element::random_element());
1469
1470 affine_element input_P_a = affine_element(element(input_P) + element(input_P)); // 2P
1471 affine_element input_P_b = affine_element(element(input_P_a) + element(input_P)); // 3P
1472 affine_element input_P_c = affine_element(element(input_P_a) + element(input_P_b)); // 5P
1473 std::vector<affine_element> input_points = { input_P_a, input_P_b, input_P_c };
1474
1475 // Choose scalars such that their NAF representations are:
1476 // skew msd lsd
1477 // a: 0 [+1, +1, -1, +1] = -0 + 2^3 + 2^2 - 2^1 + 2^0 = 11
1478 // b: 1 [+1, +1, +1, +1] = -1 + 2^3 + 2^2 + 2^1 + 2^0 = 14
1479 // c: 1 [+1, -1, +1, +1] = -1 + 2^3 - 2^2 + 2^1 + 2^0 = 6
1480 fr scalar_a(11);
1481 fr scalar_b(14);
1482 fr scalar_c(6);
1483 std::vector<fr> input_scalars = { scalar_a, scalar_b, scalar_c };
1484
1485 OriginTag tag_union =
1486 OriginTag::constant(); // Initialize as CONSTANT so merging with input tags works correctly
1487 std::vector<scalar_ct> scalars;
1489 for (size_t i = 0; i < 3; ++i) {
1490 const element_ct point = element_ct::from_witness(&builder, input_points[i]);
1491 point.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true));
1492 tag_union = OriginTag(tag_union, point.get_origin_tag());
1493
1494 const scalar_ct scalar = scalar_ct::from_witness(&builder, input_scalars[i]);
1495 scalar.set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false));
1496 tag_union = OriginTag(tag_union, scalar.get_origin_tag());
1497
1498 scalars.emplace_back(scalar);
1499 points.emplace_back(point);
1500 }
1501
1502 // If with_edgecases = true, should handle linearly dependent points correctly
1503 // Define masking scalar (128 bits)
1504 const auto get_128_bit_scalar = []() {
1505 uint256_t scalar_u256(0, 0, 0, 0);
1506 scalar_u256.data[0] = engine.get_random_uint64();
1507 scalar_u256.data[1] = engine.get_random_uint64();
1508 fr scalar(scalar_u256);
1509 return scalar;
1510 };
1511 fr masking_scalar = get_128_bit_scalar();
1512 scalar_ct masking_scalar_ct = scalar_ct::from_witness(&builder, masking_scalar);
1513 element_ct c = element_ct::batch_mul(points,
1514 scalars,
1515 /*max_num_bits*/ 128,
1516 /*with_edgecases*/ true,
1517 /*masking_scalar*/ masking_scalar_ct);
1518
1519 // Check that the result tag is a union of inputs' tags
1520 EXPECT_EQ(c.get_origin_tag(), tag_union);
1521 element input_e = (element(input_P_a) * scalar_a);
1522 element input_f = (element(input_P_b) * scalar_b);
1523 element input_g = (element(input_P_c) * scalar_c);
1524
1525 affine_element expected(input_e + input_f + input_g);
1526 fq c_x_result(c.x().get_value().lo);
1527 fq c_y_result(c.y().get_value().lo);
1528
1529 EXPECT_EQ(c_x_result, expected.x);
1530 EXPECT_EQ(c_y_result, expected.y);
1531
1533 }
1534
1536 {
1538 affine_element input_P(element::random_element());
1539
1540 affine_element input_P_a = affine_element(element(input_P) + element(input_P)); // 2P
1541 affine_element input_P_b = affine_element(element(input_P_a) + element(input_P)); // 3P
1542 affine_element input_P_c = affine_element(element(input_P_a) + element(input_P_b)); // 5P
1543 std::vector<affine_element> input_points = { input_P_a, input_P_b, input_P_c };
1544
1545 // Choose scalars similar to the previous test
1546 fr scalar_a(11);
1547 fr scalar_b(14);
1548 fr scalar_c(6);
1549 std::vector<fr> input_scalars = { scalar_a, scalar_b, scalar_c };
1550
1551 std::vector<scalar_ct> scalars;
1553 for (size_t i = 0; i < 3; ++i) {
1554 const element_ct point = element_ct::from_witness(&builder, input_points[i]);
1555 points.emplace_back(point);
1556
1557 const scalar_ct scalar = scalar_ct::from_witness(&builder, input_scalars[i]);
1558 scalars.emplace_back(scalar);
1559 }
1560
1561 // with_edgecases = false should fail due to linearly dependent points
1562 // This will fail only while using ultra builder
1563 element_ct::batch_mul(points, scalars, /*max_num_bits*/ 4, /*with_edgecases*/ false);
1564
1566 EXPECT_EQ(builder.err(), "bigfield: prime limb diff is zero, but expected non-zero");
1567 }
1568
1569 static void test_one()
1570 {
1572 size_t num_repetitions = 1;
1573 for (size_t i = 0; i < num_repetitions; ++i) {
1574 fr scalar_a(fr::random_element());
1575 if ((uint256_t(scalar_a).get_bit(0) & 1) == 1) {
1576 scalar_a -= fr(1); // skew bit is 1
1577 }
1578 element_ct P_a = element_ct::one(&builder);
1579
1580 // Set origin tag for element to submitted value in round 0
1581 P_a.set_origin_tag(submitted_value_origin_tag);
1582 scalar_ct x_a = scalar_ct::from_witness(&builder, scalar_a);
1583
1584 // Set origin tag for scalar to challenge in round 0
1585 x_a.set_origin_tag(challenge_origin_tag);
1586 element_ct c = P_a * x_a;
1587
1588 // Check that the resulting tag is a union
1589 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
1590 affine_element expected(g1::one * scalar_a);
1591 fq c_x_result(c.x().get_value().lo);
1592 fq c_y_result(c.y().get_value().lo);
1593
1594 EXPECT_EQ(c_x_result, expected.x);
1595 EXPECT_EQ(c_y_result, expected.y);
1596 }
1597
1599 }
1600
1601 // Overload: defaults to all WITNESS types for given num_points
1602 static void test_helper_batch_mul(size_t num_points,
1603 const bool short_scalars = false,
1604 const bool with_edgecases = false)
1605 {
1606 std::vector<InputType> point_types(num_points, InputType::WITNESS);
1607 std::vector<InputType> scalar_types(num_points, InputType::WITNESS);
1608 test_helper_batch_mul(point_types, scalar_types, short_scalars, with_edgecases);
1609 }
1610
1612 std::vector<InputType> scalar_types,
1613 const bool short_scalars = false,
1614 const bool with_edgecases = false)
1615 {
1617
1618 const size_t num_points = point_types.size();
1620 std::vector<fr> scalars;
1621 std::vector<element_ct> circuit_points;
1622 std::vector<scalar_ct> circuit_scalars;
1623
1624 for (size_t i = 0; i < num_points; ++i) {
1625 // Generate scalars
1626 if (short_scalars) {
1627 auto [input_scalar, x] = get_random_short_scalar(&builder, scalar_types[i], /*num_bits*/ 128);
1628 scalars.push_back(input_scalar);
1629 circuit_scalars.push_back(x);
1630 } else {
1631 auto [input_scalar, x] = get_random_scalar(&builder, scalar_types[i], /*even*/ true);
1632 scalars.push_back(input_scalar);
1633 circuit_scalars.push_back(x);
1634 }
1635
1636 // Generate points
1637 auto [input_point, P] = get_random_point(&builder, point_types[i]);
1638 points.push_back(input_point);
1639 circuit_points.push_back(P);
1640 }
1641
1642 OriginTag tag_union =
1643 OriginTag::constant(); // Initialize as CONSTANT so merging with input tags works correctly
1644 for (size_t i = 0; i < num_points; ++i) {
1645 // Set tag to submitted value tag at round i
1646 circuit_points[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true));
1647 tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag());
1648
1649 // Set tag to challenge tag at round i
1650 circuit_scalars[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false));
1651 tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag());
1652 }
1653
1654 // Define masking scalar (128 bits) if with_edgecases is true
1655 const auto get_128_bit_scalar = []() {
1656 uint256_t scalar_u256(0, 0, 0, 0);
1657 scalar_u256.data[0] = engine.get_random_uint64();
1658 scalar_u256.data[1] = engine.get_random_uint64();
1659 fr scalar(scalar_u256);
1660 return scalar;
1661 };
1662 fr masking_scalar = with_edgecases ? get_128_bit_scalar() : fr(1);
1663 scalar_ct masking_scalar_ct =
1664 with_edgecases ? scalar_ct::from_witness(&builder, masking_scalar) : scalar_ct(&builder, fr(1));
1665
1666 element_ct result_point = element_ct::batch_mul(
1667 circuit_points, circuit_scalars, /*max_num_bits=*/0, with_edgecases, masking_scalar_ct);
1668
1669 // Check the resulting tag is a union of inputs' tags
1670 EXPECT_EQ(result_point.get_origin_tag(), tag_union);
1671
1672 element expected_point = g1::one;
1673 expected_point.self_set_infinity();
1674 for (size_t i = 0; i < num_points; ++i) {
1675 expected_point += (element(points[i]) * scalars[i]);
1676 }
1677
1678 expected_point = expected_point.normalize();
1679 fq result_x(result_point.x().get_value().lo);
1680 fq result_y(result_point.y().get_value().lo);
1681
1682 EXPECT_EQ(result_x, expected_point.x);
1683 EXPECT_EQ(result_y, expected_point.y);
1684
1686 }
1687
1688 static void test_batch_mul()
1689 {
1690 const size_t num_points = 5;
1693 std::vector<fr> scalars;
1694 for (size_t i = 0; i < num_points; ++i) {
1695 points.push_back(affine_element(element::random_element()));
1696 scalars.push_back(fr::random_element());
1697 }
1698
1699 std::vector<element_ct> circuit_points;
1700 std::vector<scalar_ct> circuit_scalars;
1701 OriginTag tag_union =
1702 OriginTag::constant(); // Initialize as CONSTANT so merging with input tags works correctly
1703 for (size_t i = 0; i < num_points; ++i) {
1704 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1705
1706 // Set tag to submitted value tag at round i
1707 circuit_points[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true));
1708 tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag());
1709 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1710
1711 // Set tag to challenge tag at round i
1712 circuit_scalars[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false));
1713 tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag());
1714 }
1715
1716 element_ct result_point = element_ct::batch_mul(circuit_points, circuit_scalars);
1717
1718 // Check the resulting tag is a union of inputs' tags
1719 EXPECT_EQ(result_point.get_origin_tag(), tag_union);
1720
1721 element expected_point = g1::one;
1722 expected_point.self_set_infinity();
1723 for (size_t i = 0; i < num_points; ++i) {
1724 expected_point += (element(points[i]) * scalars[i]);
1725 }
1726
1727 expected_point = expected_point.normalize();
1728 fq result_x(result_point.x().get_value().lo);
1729 fq result_y(result_point.y().get_value().lo);
1730
1731 EXPECT_EQ(result_x, expected_point.x);
1732 EXPECT_EQ(result_y, expected_point.y);
1733
1735 }
1736
1738 {
1739 const size_t num_points = 5;
1742 std::vector<fr> scalars;
1743 for (size_t i = 0; i < num_points; ++i) {
1744 points.push_back(affine_element(element::random_element()));
1745 scalars.push_back(fr::random_element());
1746 }
1747
1748 std::vector<element_ct> circuit_points;
1749 std::vector<scalar_ct> circuit_scalars;
1750
1751 OriginTag tag_union =
1752 OriginTag::constant(); // Initialize as CONSTANT so merging with input tags works correctly
1753 for (size_t i = 0; i < num_points; ++i) {
1754 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1755
1756 // Set tag to submitted value tag at round i
1757 circuit_points[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true));
1758 tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag());
1759 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1760
1761 // Set tag to challenge tag at round i
1762 circuit_scalars[i].set_origin_tag(OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false));
1763 tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag());
1764 }
1765
1766 element_ct result_point2 =
1767 element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true);
1768
1769 // Check that the result tag is a union of inputs' tags
1770 EXPECT_EQ(result_point2.get_origin_tag(), tag_union);
1771 element expected_point = g1::one;
1772 expected_point.self_set_infinity();
1773 for (size_t i = 0; i < num_points; ++i) {
1774 expected_point += (element(points[i]) * scalars[i]);
1775 }
1776
1777 expected_point = expected_point.normalize();
1778
1779 fq result2_x(result_point2.x().get_value().lo);
1780 fq result2_y(result_point2.y().get_value().lo);
1781
1782 EXPECT_EQ(result2_x, expected_point.x);
1783 EXPECT_EQ(result2_y, expected_point.y);
1784
1786 }
1787
1789 {
1790 const auto test_repeated_points = [](const uint32_t num_points) {
1791 // batch P + ... + P = m*P
1792 info("num points: ", num_points);
1794 std::vector<fr> scalars;
1795 for (size_t idx = 0; idx < num_points; idx++) {
1796 points.push_back(affine_element::one());
1797 scalars.push_back(1);
1798 }
1799
1801 ASSERT_EQ(points.size(), scalars.size());
1802
1803 std::vector<element_ct> circuit_points;
1804 std::vector<scalar_ct> circuit_scalars;
1805
1806 OriginTag tag_union =
1807 OriginTag::constant(); // Initialize as CONSTANT so merging with input tags works correctly
1808 for (size_t i = 0; i < num_points; ++i) {
1809 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1810
1811 // Set tag to submitted value tag at round i
1812 circuit_points[i].set_origin_tag(
1813 OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true));
1814 tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag());
1815 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1816
1817 // Set tag to challenge tag at round i
1818 circuit_scalars[i].set_origin_tag(
1819 OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false));
1820 tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag());
1821 }
1822 element_ct result_point =
1823 element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true);
1824
1825 // Check that the result tag is a union of inputs' tags
1826 EXPECT_EQ(result_point.get_origin_tag(), tag_union);
1827
1828 auto expected_point = element::infinity();
1829 for (const auto& point : points) {
1830 expected_point += point;
1831 }
1832 expected_point = expected_point.normalize();
1833
1834 fq result_x(result_point.x().get_value().lo);
1835 fq result_y(result_point.y().get_value().lo);
1836
1837 EXPECT_EQ(result_x, expected_point.x);
1838 EXPECT_EQ(result_y, expected_point.y);
1839
1841 };
1842 test_repeated_points(2);
1843 test_repeated_points(3);
1844 test_repeated_points(4);
1845 test_repeated_points(5);
1846 test_repeated_points(6);
1847 test_repeated_points(7);
1848 }
1850 {
1851 {
1852 // batch oo + P = P
1854 points.push_back(affine_element::infinity());
1855 points.push_back(affine_element(element::random_element()));
1856 std::vector<fr> scalars;
1857 scalars.push_back(1);
1858 scalars.push_back(1);
1859
1861 ASSERT_EQ(points.size(), scalars.size());
1862 const size_t num_points = points.size();
1863
1864 std::vector<element_ct> circuit_points;
1865 std::vector<scalar_ct> circuit_scalars;
1866
1867 OriginTag tag_union =
1868 OriginTag::constant(); // Initialize as CONSTANT so merging with input tags works correctly
1869 for (size_t i = 0; i < num_points; ++i) {
1870 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1871
1872 // Set tag to submitted value tag at round i
1873 circuit_points[i].set_origin_tag(
1874 OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true));
1875 tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag());
1876 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1877
1878 // Set tag to challenge tag at round i
1879 circuit_scalars[i].set_origin_tag(
1880 OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false));
1881 tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag());
1882 }
1883
1884 element_ct result_point =
1885 element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true);
1886
1887 // Check that the result tag is a union of inputs' tags
1888 EXPECT_EQ(result_point.get_origin_tag(), tag_union);
1889
1890 element expected_point = points[1];
1891 expected_point = expected_point.normalize();
1892
1893 fq result_x(result_point.x().get_value().lo);
1894 fq result_y(result_point.y().get_value().lo);
1895
1896 EXPECT_EQ(result_x, expected_point.x);
1897 EXPECT_EQ(result_y, expected_point.y);
1898
1900 }
1901 {
1902 // batch 0 * P1 + P2 = P2
1904 points.push_back(affine_element(element::random_element()));
1905 points.push_back(affine_element(element::random_element()));
1906 std::vector<fr> scalars;
1907 scalars.push_back(0);
1908 scalars.push_back(1);
1909
1911 ASSERT_EQ(points.size(), scalars.size());
1912 const size_t num_points = points.size();
1913
1914 std::vector<element_ct> circuit_points;
1915 std::vector<scalar_ct> circuit_scalars;
1916 OriginTag tag_union =
1917 OriginTag::constant(); // Initialize as CONSTANT so merging with input tags works correctly
1918 for (size_t i = 0; i < num_points; ++i) {
1919 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1920
1921 // Set tag to submitted value tag at round i
1922 circuit_points[i].set_origin_tag(
1923 OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/true));
1924 tag_union = OriginTag(tag_union, circuit_points[i].get_origin_tag());
1925 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1926
1927 // Set tag to challenge tag at round i
1928 circuit_scalars[i].set_origin_tag(
1929 OriginTag(/*parent_index=*/0, /*child_index=*/i, /*is_submitted=*/false));
1930 tag_union = OriginTag(tag_union, circuit_scalars[i].get_origin_tag());
1931 }
1932
1933 element_ct result_point =
1934 element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true);
1935
1936 // Check that the result tag is a union of inputs' tags
1937 EXPECT_EQ(result_point.get_origin_tag(), tag_union);
1938
1939 element expected_point = points[1];
1940 expected_point = expected_point.normalize();
1941
1942 fq result_x(result_point.x().get_value().lo);
1943 fq result_y(result_point.y().get_value().lo);
1944
1945 EXPECT_EQ(result_x, expected_point.x);
1946 EXPECT_EQ(result_y, expected_point.y);
1947
1949 }
1950 }
1951
1952 // Test batch_mul with all points at infinity
1954 {
1957 std::vector<fr> scalars;
1958
1959 for (size_t i = 0; i < 5; ++i) {
1960 points.push_back(affine_element::infinity());
1961 scalars.push_back(fr::random_element());
1962 }
1963
1964 std::vector<element_ct> circuit_points;
1965 std::vector<scalar_ct> circuit_scalars;
1966
1967 for (size_t i = 0; i < points.size(); ++i) {
1968 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1969 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1970 }
1971
1972 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars, 0, true);
1973
1974 // Result should be point at infinity
1975 EXPECT_TRUE(is_infinity(result));
1977 }
1978
1979 // Test batch_mul with all zero scalars
1981 {
1984 std::vector<fr> scalars;
1985
1986 for (size_t i = 0; i < 5; ++i) {
1987 points.push_back(affine_element(element::random_element()));
1988 scalars.push_back(fr::zero());
1989 }
1990
1991 std::vector<element_ct> circuit_points;
1992 std::vector<scalar_ct> circuit_scalars;
1993
1994 for (size_t i = 0; i < points.size(); ++i) {
1995 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1996 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1997 }
1998
1999 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars, 0, true);
2000
2001 // Result should be point at infinity
2002 EXPECT_TRUE(is_infinity(result));
2004 }
2005
2006 // Test batch_mul with mixed zero and non-zero scalars
2008 {
2011 std::vector<fr> scalars;
2012
2013 for (size_t i = 0; i < 6; ++i) {
2014 points.push_back(affine_element(element::random_element()));
2015 // Alternate between zero and non-zero scalars
2016 scalars.push_back((i % 2 == 0) ? fr::zero() : fr::random_element());
2017 }
2018
2019 std::vector<element_ct> circuit_points;
2020 std::vector<scalar_ct> circuit_scalars;
2021
2022 for (size_t i = 0; i < points.size(); ++i) {
2023 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
2024 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
2025 }
2026
2027 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars, 0, true);
2028
2029 // Compute expected result
2030 element expected = element::infinity();
2031 for (size_t i = 0; i < points.size(); ++i) {
2032 expected += (element(points[i]) * scalars[i]);
2033 }
2034 affine_element expected_affine = affine_element(expected);
2035
2036 uint256_t result_x = result.x().get_value().lo;
2037 uint256_t result_y = result.y().get_value().lo;
2038
2039 EXPECT_EQ(fq(result_x), expected_affine.x);
2040 EXPECT_EQ(fq(result_y), expected_affine.y);
2041
2043 }
2044
2045 // Test batch_mul with mixed infinity and valid points
2047 {
2050 std::vector<fr> scalars;
2051
2052 for (size_t i = 0; i < 6; ++i) {
2053 // Alternate between infinity and valid points
2054 points.push_back((i % 2 == 0) ? affine_element::infinity() : affine_element(element::random_element()));
2055 scalars.push_back(fr::random_element());
2056 }
2057
2058 std::vector<element_ct> circuit_points;
2059 std::vector<scalar_ct> circuit_scalars;
2060
2061 for (size_t i = 0; i < points.size(); ++i) {
2062 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
2063 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
2064 }
2065
2066 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars, 0, true);
2067
2068 // Compute expected result
2069 element expected = element::infinity();
2070 for (size_t i = 0; i < points.size(); ++i) {
2071 if (!points[i].is_point_at_infinity()) {
2072 expected += (element(points[i]) * scalars[i]);
2073 }
2074 }
2075 affine_element expected_affine = affine_element(expected);
2076
2077 uint256_t result_x = result.x().get_value().lo;
2078 uint256_t result_y = result.y().get_value().lo;
2079
2080 EXPECT_EQ(fq(result_x), expected_affine.x);
2081 EXPECT_EQ(fq(result_y), expected_affine.y);
2082
2084 }
2085
2086 // Test batch_mul with points that cancel out
2088 {
2091 std::vector<fr> scalars;
2092
2093 // Add P and -P with same scalar
2094 affine_element P(element::random_element());
2096 fr scalar = fr::random_element();
2097
2098 points.push_back(P);
2099 scalars.push_back(scalar);
2100 points.push_back(neg_P);
2101 scalars.push_back(scalar);
2102
2103 // Add some other points to make it non-trivial
2104 for (size_t i = 0; i < 3; ++i) {
2105 points.push_back(affine_element(element::random_element()));
2106 scalars.push_back(fr::random_element());
2107 }
2108
2109 std::vector<element_ct> circuit_points;
2110 std::vector<scalar_ct> circuit_scalars;
2111
2112 for (size_t i = 0; i < points.size(); ++i) {
2113 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
2114 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
2115 }
2116
2117 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars, 0, true);
2118
2119 // Compute expected result
2120 element expected = element::infinity();
2121 for (size_t i = 0; i < points.size(); ++i) {
2122 expected += (element(points[i]) * scalars[i]);
2123 }
2124 affine_element expected_affine = affine_element(expected);
2125
2126 uint256_t result_x = result.x().get_value().lo;
2127 uint256_t result_y = result.y().get_value().lo;
2128
2129 EXPECT_EQ(fq(result_x), expected_affine.x);
2130 EXPECT_EQ(fq(result_y), expected_affine.y);
2131
2133 }
2134
2135 // Test batch_mul with constant and witness points mixed
2137 {
2139 std::vector<affine_element> points_native;
2140 std::vector<fr> scalars_native;
2141 std::vector<element_ct> circuit_points;
2142 std::vector<scalar_ct> circuit_scalars;
2143
2144 // Add constant-constant points
2145 for (size_t i = 0; i < 3; ++i) {
2146 const auto [point, point_ct] = get_random_point(&builder, InputType::CONSTANT);
2147 const auto [scalar, scalar_ct] = get_random_scalar(&builder, InputType::CONSTANT);
2148 points_native.push_back(point);
2149 scalars_native.push_back(scalar);
2150 circuit_points.push_back(point_ct); // Constant
2151 circuit_scalars.push_back(scalar_ct); // Constant
2152 }
2153
2154 // Add witness-witness points
2155 for (size_t i = 0; i < 3; ++i) {
2156 const auto [point, point_ct] = get_random_point(&builder, InputType::WITNESS);
2157 const auto [scalar, scalar_ct] = get_random_scalar(&builder, InputType::WITNESS);
2158 points_native.push_back(point);
2159 scalars_native.push_back(scalar);
2160 circuit_points.push_back(point_ct); // Witness
2161 circuit_scalars.push_back(scalar_ct); // Witness
2162 }
2163
2164 // Add constant-witness points
2165 for (size_t i = 0; i < 4; ++i) {
2166 const auto [point, point_ct] = get_random_point(&builder, InputType::CONSTANT);
2167 const auto [scalar, scalar_ct] = get_random_scalar(&builder, InputType::WITNESS);
2168 points_native.push_back(point);
2169 scalars_native.push_back(scalar);
2170 circuit_points.push_back(element_ct(point.x, point.y)); // Constant
2171 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalar)); // Witness
2172 }
2173
2174 // Add witness-constant points
2175 for (size_t i = 0; i < 4; ++i) {
2176 const auto [point, point_ct] = get_random_point(&builder, InputType::WITNESS);
2177 const auto [scalar, scalar_ct] = get_random_scalar(&builder, InputType::CONSTANT);
2178 points_native.push_back(point);
2179 scalars_native.push_back(scalar);
2180 circuit_points.push_back(point_ct); // Witness
2181 circuit_scalars.push_back(scalar_ct); // Constant
2182 }
2183
2184 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars);
2185
2186 // Compute expected result
2187 element expected = element::infinity();
2188 for (size_t i = 0; i < points_native.size(); ++i) {
2189 expected += (element(points_native[i]) * scalars_native[i]);
2190 }
2191 affine_element expected_affine = affine_element(expected);
2192
2193 uint256_t result_x = result.x().get_value().lo;
2194 uint256_t result_y = result.y().get_value().lo;
2195
2196 EXPECT_EQ(fq(result_x), expected_affine.x);
2197 EXPECT_EQ(fq(result_y), expected_affine.y);
2198
2200 }
2201
2202 // Test batch_mul with large number of points (stress test)
2204 {
2207 std::vector<fr> scalars;
2208 constexpr size_t num_points = 20;
2209
2210 for (size_t i = 0; i < num_points; ++i) {
2211 points.push_back(affine_element(element::random_element()));
2212 scalars.push_back(fr::random_element());
2213 }
2214
2215 std::vector<element_ct> circuit_points;
2216 std::vector<scalar_ct> circuit_scalars;
2217
2218 for (size_t i = 0; i < points.size(); ++i) {
2219 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
2220 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
2221 }
2222
2223 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars);
2224
2225 // Compute expected result
2226 element expected = element::infinity();
2227 for (size_t i = 0; i < points.size(); ++i) {
2228 expected += (element(points[i]) * scalars[i]);
2229 }
2230 affine_element expected_affine = affine_element(expected);
2231
2232 uint256_t result_x = result.x().get_value().lo;
2233 uint256_t result_y = result.y().get_value().lo;
2234
2235 EXPECT_EQ(fq(result_x), expected_affine.x);
2236 EXPECT_EQ(fq(result_y), expected_affine.y);
2237
2239 }
2240
2241 // Test that infinity representation is canonical (x=0, y=0) after all operations
2243 {
2245
2246 // Case 1: constant_infinity() returns canonical form
2247 {
2248 element_ct inf = element_ct::constant_infinity(&builder);
2249 EXPECT_TRUE(is_infinity(inf));
2250 // Verify coordinates are (0, 0)
2251 EXPECT_EQ(fq(inf.x().get_value().lo), fq(0));
2252 EXPECT_EQ(fq(inf.y().get_value().lo), fq(0));
2253 }
2254
2255 // Case 2: P + (-P) = infinity with canonical coords
2256 {
2257 affine_element input(element::random_element());
2258 element_ct P = element_ct::from_witness(&builder, input);
2259 element_ct neg_P = -P;
2260 element_ct result = P + neg_P;
2261
2262 EXPECT_TRUE(is_infinity(result));
2263 // After standardization, coordinates should be (0, 0)
2264 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2265 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2266 }
2267
2268 // Case 3: P - P = infinity with canonical coords
2269 {
2270 affine_element input(element::random_element());
2271 element_ct P = element_ct::from_witness(&builder, input);
2272 element_ct result = P - P;
2273
2274 EXPECT_TRUE(is_infinity(result));
2275 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2276 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2277 }
2278
2279 // Case 4: infinity + infinity = infinity with canonical coords
2280 {
2281 element_ct inf1 = element_ct::constant_infinity(&builder);
2282 element_ct inf2 = element_ct::constant_infinity(&builder);
2283 element_ct result = inf1 + inf2;
2284
2285 EXPECT_TRUE(is_infinity(result));
2286 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2287 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2288 }
2289
2290 // Case 5: 2 * infinity = infinity with canonical coords
2291 {
2292 element_ct inf = element_ct::constant_infinity(&builder);
2293 element_ct result = inf.dbl();
2294
2295 EXPECT_TRUE(is_infinity(result));
2296 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2297 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2298 }
2299
2301 }
2302
2303 // Test chained operations involving infinity
2305 {
2307
2308 // (a + infinity) - a = infinity
2309 {
2310 affine_element input(element::random_element());
2311 element_ct a = element_ct::from_witness(&builder, input);
2312 element_ct inf = element_ct::constant_infinity(&builder);
2313
2314 element_ct temp = a + inf;
2315 element_ct result = temp - a;
2316
2317 EXPECT_TRUE(is_infinity(result));
2318 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2319 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2320 }
2321
2322 // a + (b - b) = a
2323 {
2324 affine_element input_a(element::random_element());
2325 affine_element input_b(element::random_element());
2326 element_ct a = element_ct::from_witness(&builder, input_a);
2327 element_ct b = element_ct::from_witness(&builder, input_b);
2328
2329 element_ct zero = b - b; // Should be infinity
2330 element_ct result = a + zero;
2331
2332 // Result should equal a
2333 EXPECT_EQ(fq(result.x().get_value().lo), input_a.x);
2334 EXPECT_EQ(fq(result.y().get_value().lo), input_a.y);
2335 EXPECT_FALSE(is_infinity(result));
2336 }
2337
2338 // (infinity - infinity) + a = a
2339 {
2340 affine_element input(element::random_element());
2341 element_ct a = element_ct::from_witness(&builder, input);
2342 element_ct inf1 = element_ct::constant_infinity(&builder);
2343 element_ct inf2 = element_ct::constant_infinity(&builder);
2344
2345 element_ct zero = inf1 - inf2;
2346 element_ct result = zero + a;
2347
2348 EXPECT_EQ(fq(result.x().get_value().lo), input.x);
2349 EXPECT_EQ(fq(result.y().get_value().lo), input.y);
2350 }
2351
2353 }
2354
2355 // Test conditional_select with infinity points
2357 {
2359
2360 affine_element input_a(element::random_element());
2361 element_ct a = element_ct::from_witness(&builder, input_a);
2362 element_ct inf = element_ct::constant_infinity(&builder);
2363
2364 // Case 1: Select finite point when predicate is false
2365 {
2366 bool_ct pred(witness_ct(&builder, false));
2367 element_ct result = a.conditional_select(inf, pred);
2368
2369 EXPECT_FALSE(is_infinity(result));
2370 EXPECT_EQ(fq(result.x().get_value().lo), input_a.x);
2371 EXPECT_EQ(fq(result.y().get_value().lo), input_a.y);
2372 }
2373
2374 // Case 2: Select infinity when predicate is true
2375 {
2376 bool_ct pred(witness_ct(&builder, true));
2377 element_ct result = a.conditional_select(inf, pred);
2378
2379 EXPECT_TRUE(is_infinity(result));
2380 }
2381
2382 // Case 3: Select between two infinity points
2383 {
2384 element_ct inf2 = element_ct::constant_infinity(&builder);
2385 bool_ct pred(witness_ct(&builder, true));
2386 element_ct result = inf.conditional_select(inf2, pred);
2387
2388 EXPECT_TRUE(is_infinity(result));
2389 }
2390
2392 }
2393
2394 // Test conditional_negate with infinity
2396 {
2398
2399 element_ct inf = element_ct::constant_infinity(&builder);
2400
2401 // Negating infinity should still be infinity
2402 {
2403 bool_ct pred(witness_ct(&builder, true));
2404 element_ct result = inf.conditional_negate(pred);
2405
2406 EXPECT_TRUE(is_infinity(result));
2407 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2408 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2409 }
2410
2411 // Not negating infinity should still be infinity
2412 {
2413 bool_ct pred(witness_ct(&builder, false));
2414 element_ct result = inf.conditional_negate(pred);
2415
2416 EXPECT_TRUE(is_infinity(result));
2417 }
2418
2420 }
2421
2422 // Test get_standard_form preserves canonical infinity representation
2424 {
2426
2427 // Use constant_infinity() factory to create canonical infinity with (0, 0) coordinates
2428 // Note: We no longer support non-canonical infinity representations (points with
2429 // random coords but is_infinity=true) through the public API
2430 element_ct P = element_ct::constant_infinity(&builder);
2431
2432 // Canonical infinity has (0, 0) coordinates
2433 EXPECT_EQ(fq(P.x().get_value().lo), fq(0));
2434 EXPECT_EQ(fq(P.y().get_value().lo), fq(0));
2435 EXPECT_TRUE(is_infinity(P));
2436
2437 // After standardization, coords should still be (0, 0)
2438 element_ct standardized = P.get_standard_form();
2439 EXPECT_TRUE(is_infinity(standardized));
2440 EXPECT_EQ(fq(standardized.x().get_value().lo), fq(0));
2441 EXPECT_EQ(fq(standardized.y().get_value().lo), fq(0));
2442
2444 }
2445
2446 // Test auto-detection of infinity in 2-argument constructor
2448 {
2450
2451 // Create element with (0, 0) coordinates - should auto-detect as infinity
2452 auto x_zero = element_ct::BaseField::from_witness(&builder, fq(0));
2453 auto y_zero = element_ct::BaseField::from_witness(&builder, fq(0));
2454
2455 element_ct point(x_zero, y_zero);
2456
2457 EXPECT_TRUE(is_infinity(point));
2458
2460 }
2461
2462 // Test scalar multiplication edge cases with infinity
2464 {
2466
2467 // Case 1: 0 * P = infinity
2468 {
2469 affine_element input(element::random_element());
2470 element_ct P = element_ct::from_witness(&builder, input);
2471 scalar_ct zero = scalar_ct::from_witness(&builder, fr(0));
2472
2473 element_ct result = P * zero;
2474 EXPECT_TRUE(is_infinity(result));
2475 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2476 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2477 }
2478
2479 // Case 2: k * infinity = infinity
2480 {
2481 element_ct inf = element_ct::constant_infinity(&builder);
2482 fr scalar_val = fr::random_element();
2483 scalar_ct k = scalar_ct::from_witness(&builder, scalar_val);
2484
2485 element_ct result = inf * k;
2486 EXPECT_TRUE(is_infinity(result));
2487 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2488 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2489 }
2490
2491 // Case 3: 0 * infinity = infinity
2492 {
2493 element_ct inf = element_ct::constant_infinity(&builder);
2494 scalar_ct zero = scalar_ct::from_witness(&builder, fr(0));
2495
2496 element_ct result = inf * zero;
2497 EXPECT_TRUE(is_infinity(result));
2498 }
2499
2501 }
2502
2503 // Test batch_mul where result cancels to infinity
2505 {
2507
2508 // P*a + Q*b + P*(-a) + Q*(-b) = infinity
2509 affine_element P(element::random_element());
2510 affine_element Q(element::random_element());
2513
2514 std::vector<element_ct> points = {
2515 element_ct::from_witness(&builder, P),
2516 element_ct::from_witness(&builder, Q),
2517 element_ct::from_witness(&builder, P),
2518 element_ct::from_witness(&builder, Q),
2519 };
2520
2521 std::vector<scalar_ct> scalars = { scalar_ct::from_witness(&builder, a),
2522 scalar_ct::from_witness(&builder, b),
2523 scalar_ct::from_witness(&builder, -a),
2524 scalar_ct::from_witness(&builder, -b) };
2525
2526 element_ct result = element_ct::batch_mul(points, scalars, 0, true);
2527
2528 EXPECT_TRUE(is_infinity(result));
2529 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2530 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2531
2533 }
2534
2535 // Test addition with constant infinity
2537 {
2539
2540 // P + constant_infinity = P
2541 affine_element input(element::random_element());
2542 element_ct P = element_ct::from_witness(&builder, input);
2543 element_ct const_inf = element_ct::constant_infinity(&builder); // This is a constant
2544
2545 element_ct result = P + const_inf;
2546
2547 EXPECT_FALSE(is_infinity(result));
2548 EXPECT_EQ(fq(result.x().get_value().lo), input.x);
2549 EXPECT_EQ(fq(result.y().get_value().lo), input.y);
2550
2551 // constant_infinity + P = P
2552 element_ct result2 = const_inf + P;
2553 EXPECT_FALSE(is_infinity(result2));
2554 EXPECT_EQ(fq(result2.x().get_value().lo), input.x);
2555 EXPECT_EQ(fq(result2.y().get_value().lo), input.y);
2556
2558 }
2559
2560 // Test that witness infinity points (created via operations) work correctly
2562 {
2564
2565 // Create infinity as P - P (witness-based infinity)
2566 affine_element input(element::random_element());
2567 element_ct P = element_ct::from_witness(&builder, input);
2568 element_ct witness_inf = P - P;
2569
2570 // Use this witness infinity in operations
2571 affine_element input2(element::random_element());
2572 element_ct Q = element_ct::from_witness(&builder, input2);
2573
2574 // Q + witness_inf = Q
2575 element_ct result = Q + witness_inf;
2576 EXPECT_EQ(fq(result.x().get_value().lo), input2.x);
2577 EXPECT_EQ(fq(result.y().get_value().lo), input2.y);
2578
2579 // witness_inf + Q = Q
2580 element_ct result2 = witness_inf + Q;
2581 EXPECT_EQ(fq(result2.x().get_value().lo), input2.x);
2582 EXPECT_EQ(fq(result2.y().get_value().lo), input2.y);
2583
2585 }
2586};
2587
2588// bn254 with ultra arithmetisation where scalar field is native field, base field is non-native field (bigfield)
2590
2591// bn254 with ultra arithmetisation where both scalar and base fields are non-native fields
2594
2595// bn254 with mega arithmetisation where scalar field is native field, base field is non-native field
2597
2598// secp256r1 with ultra arithmetisation where both scalar and base fields are (naturally) non-native fields
2601
2602// secp256k1 with ultra arithmetisation where both scalar and base fields are (naturally) non-native fields
2605
2606using TestTypes = testing::Types<bn254_with_ultra,
2611
2613
2615{
2616 TestFixture::test_basic_tag_logic();
2617}
2618
2619TYPED_TEST(stdlib_biggroup, assert_coordinates_in_field)
2620{
2621 TestFixture::test_assert_coordinates_in_field();
2622}
2623
2624// Addition tests
2626{
2627 TestFixture::test_add();
2628}
2629TYPED_TEST(stdlib_biggroup, add_with_constants)
2630{
2631 TestFixture::test_add(InputType::WITNESS, InputType::CONSTANT); // w + c
2632 TestFixture::test_add(InputType::CONSTANT, InputType::WITNESS); // c + w
2633 TestFixture::test_add(InputType::CONSTANT, InputType::CONSTANT); // c + c
2634}
2635TYPED_TEST(stdlib_biggroup, add_points_at_infinity)
2636{
2637 TestFixture::test_add_points_at_infinity();
2638}
2639TYPED_TEST(stdlib_biggroup, standard_form_of_point_at_infinity)
2640{
2641 TestFixture::test_standard_form_of_point_at_infinity();
2642}
2643
2644// Subtraction tests
2646{
2647 TestFixture::test_sub();
2648}
2649TYPED_TEST(stdlib_biggroup, sub_with_constants)
2650{
2651 TestFixture::test_sub(InputType::WITNESS, InputType::CONSTANT); // w - c
2652 TestFixture::test_sub(InputType::CONSTANT, InputType::WITNESS); // c - w
2653 TestFixture::test_sub(InputType::CONSTANT, InputType::CONSTANT); // c - c
2654}
2655TYPED_TEST(stdlib_biggroup, sub_points_at_infinity)
2656{
2657 TestFixture::test_sub_points_at_infinity();
2658}
2660{
2661 TestFixture::test_dbl();
2662}
2663TYPED_TEST(stdlib_biggroup, dbl_with_constant)
2664{
2665 TestFixture::test_dbl(InputType::CONSTANT); // dbl(c)
2666}
2667TYPED_TEST(stdlib_biggroup, dbl_with_infinity)
2668{
2669 TestFixture::test_dbl_with_infinity();
2670}
2672{
2673 if constexpr (HasGoblinBuilder<TypeParam>) {
2674 GTEST_SKIP() << "mega builder does not support this edge case";
2675 } else {
2676 TestFixture::test_dbl_with_y_zero();
2677 }
2678}
2680{
2681 TestFixture::test_add_equals_dbl();
2682}
2683TYPED_TEST(stdlib_biggroup, sub_neg_equals_double)
2684{
2685 TestFixture::test_sub_neg_equals_double();
2686}
2687
2688// Test chain_add
2690{
2691 if constexpr (HasGoblinBuilder<TypeParam>) {
2692 GTEST_SKIP() << "mega builder does not implement chain_add function";
2693 } else {
2694 TestFixture::test_chain_add();
2695 };
2696}
2697HEAVY_TYPED_TEST(stdlib_biggroup, chain_add_with_constants)
2698{
2699 if constexpr (HasGoblinBuilder<TypeParam>) {
2700 GTEST_SKIP() << "mega builder does not implement chain_add function";
2701 } else {
2702 TestFixture::test_chain_add(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w, w, c
2703 TestFixture::test_chain_add(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // w, c, w
2704 TestFixture::test_chain_add(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // w, c, c
2705 TestFixture::test_chain_add(InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS); // c, w, w
2706 TestFixture::test_chain_add(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // c, w, c
2707 TestFixture::test_chain_add(InputType::CONSTANT, InputType::CONSTANT, InputType::WITNESS); // c, c, w
2708 TestFixture::test_chain_add(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT); // c, c, c
2709 }
2710}
2711
2712// Test multiple_montgomery_ladder
2713HEAVY_TYPED_TEST(stdlib_biggroup, multiple_montgomery_ladder)
2714{
2715
2716 if constexpr (HasGoblinBuilder<TypeParam>) {
2717 GTEST_SKIP() << "mega builder does not implement multiple_montgomery_ladder function";
2718 } else {
2719 TestFixture::test_multiple_montgomery_ladder();
2720 };
2721}
2722
2723// Test normalize
2725{
2726 TestFixture::test_normalize();
2727}
2728TYPED_TEST(stdlib_biggroup, normalize_constant)
2729{
2730 TestFixture::test_normalize(InputType::CONSTANT);
2731}
2732
2733// Test reduce
2735{
2736 TestFixture::test_reduce();
2737}
2739{
2740 TestFixture::test_reduce(InputType::CONSTANT);
2741}
2742
2743// Test unary negation
2745{
2746 TestFixture::test_unary_negate(InputType::WITNESS);
2747}
2748
2749TYPED_TEST(stdlib_biggroup, unary_negate_with_constants)
2750{
2751 TestFixture::test_unary_negate(InputType::CONSTANT);
2752}
2753
2754// Test operator+=
2756{
2757 TestFixture::test_add_assign(InputType::WITNESS, InputType::WITNESS);
2758}
2759
2760TYPED_TEST(stdlib_biggroup, add_assign_with_constants)
2761{
2762 TestFixture::test_add_assign(InputType::WITNESS, InputType::CONSTANT); // w += c
2763 TestFixture::test_add_assign(InputType::CONSTANT, InputType::WITNESS); // c += w
2764}
2765
2766// Test operator-=
2768{
2769 TestFixture::test_sub_assign(InputType::WITNESS, InputType::WITNESS);
2770}
2771TYPED_TEST(stdlib_biggroup, sub_assign_with_constants)
2772{
2773 TestFixture::test_sub_assign(InputType::WITNESS, InputType::CONSTANT); // w -= c
2774 TestFixture::test_sub_assign(InputType::CONSTANT, InputType::WITNESS); // c -= w
2775}
2776// Test checked_unconditional_add
2777TYPED_TEST(stdlib_biggroup, checked_unconditional_add)
2778{
2779 TestFixture::test_checked_unconditional_add(InputType::WITNESS, InputType::WITNESS);
2780}
2781TYPED_TEST(stdlib_biggroup, checked_unconditional_add_with_constants)
2782{
2783 TestFixture::test_checked_unconditional_add(InputType::WITNESS, InputType::CONSTANT); // w + c
2784 TestFixture::test_checked_unconditional_add(InputType::CONSTANT, InputType::WITNESS); // c + w
2785 TestFixture::test_checked_unconditional_add(InputType::CONSTANT, InputType::CONSTANT); // c + c
2786}
2787// Test checked_unconditional_subtract
2788TYPED_TEST(stdlib_biggroup, checked_unconditional_subtract)
2789{
2790 TestFixture::test_checked_unconditional_subtract(InputType::WITNESS, InputType::WITNESS);
2791}
2792TYPED_TEST(stdlib_biggroup, checked_unconditional_subtract_with_constants)
2793{
2794 TestFixture::test_checked_unconditional_subtract(InputType::WITNESS, InputType::CONSTANT); // w - c
2795 TestFixture::test_checked_unconditional_subtract(InputType::CONSTANT, InputType::WITNESS); // c - w
2796 TestFixture::test_checked_unconditional_subtract(InputType::CONSTANT, InputType::CONSTANT); // c - c
2797}
2798// Test checked_unconditional_add_sub
2799TYPED_TEST(stdlib_biggroup, checked_unconditional_add_sub)
2800{
2801 TestFixture::test_checked_unconditional_add_sub();
2802}
2803TYPED_TEST(stdlib_biggroup, checked_unconditional_add_sub_with_constants)
2804{
2805 TestFixture::test_checked_unconditional_add_sub(InputType::WITNESS, InputType::CONSTANT); // w, c
2806 TestFixture::test_checked_unconditional_add_sub(InputType::CONSTANT, InputType::WITNESS); // c, w
2807 TestFixture::test_checked_unconditional_add_sub(InputType::CONSTANT, InputType::CONSTANT); // c, c
2808}
2809// Test conditional_negate
2810TYPED_TEST(stdlib_biggroup, conditional_negate)
2811{
2812 TestFixture::test_conditional_negate();
2813}
2814TYPED_TEST(stdlib_biggroup, conditional_negate_with_constants)
2815{
2816 TestFixture::test_conditional_negate(InputType::WITNESS, InputType::CONSTANT); // w, c
2817 TestFixture::test_conditional_negate(InputType::CONSTANT, InputType::WITNESS); // c, w
2818 TestFixture::test_conditional_negate(InputType::CONSTANT, InputType::CONSTANT); // c, c
2819}
2820// Test conditional_select
2821TYPED_TEST(stdlib_biggroup, conditional_select)
2822{
2823 TestFixture::test_conditional_select();
2824}
2825TYPED_TEST(stdlib_biggroup, conditional_select_with_constants)
2826{
2827 TestFixture::test_conditional_select(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w, w, c
2828 TestFixture::test_conditional_select(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // w, c, w
2829 TestFixture::test_conditional_select(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // w, c, c
2830 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS); // c, w, w
2831 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::CONSTANT, InputType::WITNESS); // c, c, w
2832 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // c, w, c
2833 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT); // c, c, c
2834}
2835TYPED_TEST(stdlib_biggroup, incomplete_assert_equal)
2836{
2837 TestFixture::test_incomplete_assert_equal();
2838}
2839TYPED_TEST(stdlib_biggroup, incomplete_assert_equal_fails)
2840{
2841 TestFixture::test_incomplete_assert_equal_failure();
2842}
2843
2845{
2846 if constexpr (!HasGoblinBuilder<TypeParam>) {
2847 size_t num_repetitions = 1;
2848 for (size_t i = 0; i < num_repetitions; i++) {
2849 TestFixture::test_compute_naf();
2850 }
2851 } else {
2852 GTEST_SKIP() << "mega builder does not implement compute_naf function";
2853 }
2854}
2855
2857{
2858 if constexpr (!HasGoblinBuilder<TypeParam>) {
2859 TestFixture::test_compute_naf_zero();
2860 } else {
2861 GTEST_SKIP() << "mega builder does not implement compute_naf function";
2862 }
2863}
2864
2865HEAVY_TYPED_TEST(stdlib_biggroup, compute_naf_overflow_lower_half)
2866{
2867 if constexpr (!HasGoblinBuilder<TypeParam>) {
2868 TestFixture::test_compute_naf_overflow_lower_half();
2869 } else {
2870 GTEST_SKIP() << "mega builder does not implement compute_naf function";
2871 }
2872}
2873
2875{
2876 TestFixture::test_mul();
2877}
2879{
2880 TestFixture::test_mul(InputType::WITNESS, InputType::CONSTANT); // w * c
2881 TestFixture::test_mul(InputType::CONSTANT, InputType::WITNESS); // c * w
2882 TestFixture::test_mul(InputType::CONSTANT, InputType::CONSTANT); // c * c
2883}
2885{
2886 TestFixture::test_mul_edge_cases();
2887}
2888HEAVY_TYPED_TEST(stdlib_biggroup, mul_edge_cases_with_constants)
2889{
2890 TestFixture::test_mul_edge_cases(InputType::WITNESS, InputType::CONSTANT); // w * c
2891 TestFixture::test_mul_edge_cases(InputType::CONSTANT, InputType::WITNESS); // c * w
2892 TestFixture::test_mul_edge_cases(InputType::CONSTANT, InputType::CONSTANT); // c * c
2893}
2894
2895HEAVY_TYPED_TEST(stdlib_biggroup, short_scalar_mul_with_bit_lengths)
2896{
2897 if constexpr (HasGoblinBuilder<TypeParam>) {
2898 GTEST_SKIP() << "mega builder does not implement scalar_mul function";
2899 } else {
2900 TestFixture::test_short_scalar_mul_with_bit_lengths();
2901 }
2902}
2903
2904HEAVY_TYPED_TEST(stdlib_biggroup, short_scalar_mul_infinity)
2905{
2906 if constexpr (HasGoblinBuilder<TypeParam>) {
2907 GTEST_SKIP() << "mega builder does not implement scalar_mul function";
2908 } else {
2909 TestFixture::test_short_scalar_mul_infinity();
2910 }
2911}
2912
2913// Batch multiplication tests
2914// 1 point - Base case only
2916{
2917 TestFixture::test_helper_batch_mul(1);
2918}
2919
2920// 2 points - Base case + flag variations + one constant mix
2922{
2923 TestFixture::test_helper_batch_mul(2);
2924}
2925HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_twin_short_scalars)
2926{
2927 TestFixture::test_helper_batch_mul(2, true); // short_scalars
2928}
2929HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_twin_with_edgecases)
2930{
2931 TestFixture::test_helper_batch_mul(2, false, true); // short_scalars, with_edgecases
2932}
2933HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_twin_short_scalars_with_edgecases)
2934{
2935 TestFixture::test_helper_batch_mul(2, true, true); // short_scalars, with_edgecases
2936}
2937HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_twin_mixed_constants)
2938{
2939 TestFixture::test_helper_batch_mul({ InputType::WITNESS, InputType::CONSTANT },
2941}
2942
2943// 3 points - Base case only
2945{
2946 TestFixture::test_helper_batch_mul(3);
2947}
2948
2949// 4 points - Base case only
2951{
2952 TestFixture::test_helper_batch_mul(4);
2953}
2954
2955// 5 points - Base case + edge case + short scalar + mixed constant
2957{
2958 TestFixture::test_helper_batch_mul(5);
2959}
2960HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_five_with_edgecases)
2961{
2962 TestFixture::test_helper_batch_mul(5, false, true); // short_scalars, with_edgecases
2963}
2964HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_five_short_scalars)
2965{
2966 TestFixture::test_helper_batch_mul(5, true); // short_scalars
2967}
2968HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_five_short_scalars_with_edgecases)
2969{
2970 TestFixture::test_helper_batch_mul(5, true, true); // short_scalars, with_edgecases
2971}
2978
2979// 6 points - Base case only
2981{
2982 TestFixture::test_helper_batch_mul(6);
2983}
2984
2986{
2987 TestFixture::test_twin_mul();
2988}
2989
2990HEAVY_TYPED_TEST(stdlib_biggroup, twin_mul_with_infinity)
2991{
2992 TestFixture::test_twin_mul_with_infinity();
2993}
2994
2995HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_linearly_dependent_generators)
2996{
2997 TestFixture::test_batch_mul_linearly_dependent_generators();
2998}
2999
3000HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_linearly_dependent_generators_failure)
3001{
3002 if constexpr (HasGoblinBuilder<TypeParam>) {
3003 GTEST_SKIP() << "this failure test is designed for ultra builder only";
3004 } else {
3005 TestFixture::test_batch_mul_linearly_dependent_generators_failure();
3006 }
3007}
3008
3010{
3011 TestFixture::test_one();
3012}
3013
3015{
3016 TestFixture::test_batch_mul();
3017}
3018
3019HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_edgecase_equivalence)
3020{
3021 TestFixture::test_batch_mul_edgecase_equivalence();
3022}
3023HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_edge_case_set1)
3024{
3025 TestFixture::test_batch_mul_edge_case_set1();
3026}
3027
3028HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_edge_case_set2)
3029{
3030 TestFixture::test_batch_mul_edge_case_set2();
3031}
3032
3033// Batch mul edge case tests
3034HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_all_infinity)
3035{
3036 TestFixture::test_batch_mul_all_infinity();
3037}
3038
3039HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_all_zero_scalars)
3040{
3041 TestFixture::test_batch_mul_all_zero_scalars();
3042}
3043
3044HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_mixed_zero_scalars)
3045{
3046 TestFixture::test_batch_mul_mixed_zero_scalars();
3047}
3048
3049HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_mixed_infinity)
3050{
3051 TestFixture::test_batch_mul_mixed_infinity();
3052}
3053
3054HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_cancellation)
3055{
3056 TestFixture::test_batch_mul_cancellation();
3057}
3058
3059HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_mixed_constant_witness)
3060{
3061 TestFixture::test_batch_mul_mixed_constant_witness();
3062}
3063
3064HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_large_number_of_points)
3065{
3066 TestFixture::test_batch_mul_large_number_of_points();
3067}
3068
3069// Point at Infinity Edge Case Tests
3070TYPED_TEST(stdlib_biggroup, infinity_canonical_representation)
3071{
3072 TestFixture::test_infinity_canonical_representation();
3073}
3074
3075TYPED_TEST(stdlib_biggroup, infinity_chained_operations)
3076{
3077 TestFixture::test_infinity_chained_operations();
3078}
3079
3080TYPED_TEST(stdlib_biggroup, conditional_select_with_infinity)
3081{
3082 TestFixture::test_conditional_select_with_infinity();
3083}
3084
3085TYPED_TEST(stdlib_biggroup, conditional_negate_with_infinity)
3086{
3087 TestFixture::test_conditional_negate_with_infinity();
3088}
3089
3090TYPED_TEST(stdlib_biggroup, get_standard_form_normalizes_infinity)
3091{
3092 TestFixture::test_get_standard_form_normalizes_infinity();
3093}
3094
3095TYPED_TEST(stdlib_biggroup, infinity_auto_detection_in_constructor)
3096{
3097 TestFixture::test_infinity_auto_detection_in_constructor();
3098}
3099
3100HEAVY_TYPED_TEST(stdlib_biggroup, scalar_mul_infinity_edge_cases)
3101{
3102 TestFixture::test_scalar_mul_infinity_edge_cases();
3103}
3104
3105HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_complete_cancellation)
3106{
3107 TestFixture::test_batch_mul_complete_cancellation();
3108}
3109
3110TYPED_TEST(stdlib_biggroup, add_constant_infinity)
3111{
3112 TestFixture::test_add_constant_infinity();
3113}
3114
3115TYPED_TEST(stdlib_biggroup, witness_infinity_from_operations)
3116{
3117 TestFixture::test_witness_infinity_from_operations();
3118}
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:193
InputType
stdlib_biggroup< TestType< stdlib::bn254< bb::UltraCircuitBuilder >, false > > bn254_with_ultra
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 infinity()
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:60
void set_origin_tag(const OriginTag &new_tag) const
Definition bool.hpp:154
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:466
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_witness_infinity_from_operations()
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_conditional_negate_with_infinity()
static void test_compute_naf()
typename g1::element element
static void test_multiple_montgomery_ladder()
static void test_batch_mul_cancellation()
static void test_add_constant_infinity()
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)
static void test_get_standard_form_normalizes_infinity()
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_infinity_auto_detection_in_constructor()
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 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_conditional_select_with_infinity()
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_infinity_chained_operations()
static void test_incomplete_assert_equal_failure()
static bool is_infinity(const element_ct &e)
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
static void test_scalar_mul_infinity_edge_cases()
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_compute_naf_overflow_lower_half()
static void test_batch_mul_complete_cancellation()
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_infinity_canonical_representation()
static void test_batch_mul_all_infinity()
static void test_checked_unconditional_add(InputType a_type=InputType::WITNESS, InputType b_type=InputType::WITNESS)
#define info(...)
Definition log.hpp:93
void benchmark_info(Args...)
Info used to store circuit statistics during CI/CD with concrete structure. Writes straight to log.
Definition log.hpp:121
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:212
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
Inner sum(Cont< Inner, Args... > const &in)
Definition container.hpp:70
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
testing::Types< VKTestParams< UltraFlavor, stdlib::recursion::honk::DefaultIO< UltraCircuitBuilder > >, VKTestParams< UltraFlavor, stdlib::recursion::honk::RollupIO >, VKTestParams< UltraKeccakFlavor, stdlib::recursion::honk::DefaultIO< UltraCircuitBuilder > >, VKTestParams< MegaFlavor, stdlib::recursion::honk::DefaultIO< MegaCircuitBuilder > > > TestTypes
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 OriginTag constant()
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