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_, typename ScalarField_, bool use_bigfield> struct TestType {
40 public:
41 using Curve = Curve_;
42 // The base field is always a bigfield, so we only have to select the scalar field type
43 using bigfield_element = bb::stdlib::
44 element<typename Curve::Builder, typename Curve::BaseField, ScalarField_, typename Curve::GroupNative>;
46 // the field of scalars acting on element_ct
47 using scalar_ct = ScalarField_;
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 EXPECT_EQ(builder.failed(), !expected_result);
72 };
73
74 // Helper to check the infinity status of a circuit element.
75 // Ultra: reads the in-circuit is_point_at_infinity flag.
76 // Goblin/Mega: derives infinity from native (0,0) coordinates (no circuit flag exists).
77 static bool is_infinity(const element_ct& e)
78 {
79 if constexpr (HasGoblinBuilder<TestType>) {
80 return e.get_value().is_point_at_infinity();
81 } else {
82 return e.is_point_at_infinity().get_value();
83 }
84 }
85
86 // Create a random point as a witness
88 {
89 affine_element point_native(element::random_element());
90 element_ct point_ct = element_ct::from_witness(builder, point_native);
91 return std::make_pair(point_native, point_ct);
92 }
93
94 // Create a random point as a constant
96 {
97 affine_element point_native(element::random_element());
98 // Create constant coordinates with builder context
99 using Fq = typename element_ct::BaseField;
100 Fq x_const(builder, uint256_t(point_native.x));
101 Fq y_const(builder, uint256_t(point_native.y));
102 element_ct point_ct(x_const, y_const);
103 return std::make_pair(point_native, point_ct);
104 }
105
106 // Create a random point based on InputType
114
115 // Create a random scalar as a witness
117 {
118 fr scalar_native = fr::random_element();
119 if (even && uint256_t(scalar_native).get_bit(0)) {
120 scalar_native -= fr(1); // make it even if it's odd
121 }
122 scalar_ct scalar_ct_val = scalar_ct::from_witness(builder, scalar_native);
123 return std::make_pair(scalar_native, scalar_ct_val);
124 }
125
126 // Create a random scalar as a constant
128 {
129 fr scalar_native = fr::random_element();
130 if (even && uint256_t(scalar_native).get_bit(0)) {
131 scalar_native -= fr(1); // make it even if it's odd
132 }
133 scalar_ct scalar_ct_val = scalar_ct(builder, scalar_native);
134 return std::make_pair(scalar_native, scalar_ct_val);
135 }
136
137 // Create a random scalar based on InputType
139 {
140 if (type == InputType::WITNESS) {
142 }
144 }
145
147 {
148 uint256_t scalar_u256 = engine.get_random_uint256();
149 scalar_u256 = scalar_u256 >> (256 - num_bits); // keep only the lower num_bits bits
150
151 fr scalar_native(scalar_u256);
152 scalar_ct scalar_ct_val;
153 if (type == InputType::WITNESS) {
154 scalar_ct_val = scalar_ct::from_witness(builder, scalar_native);
155 } else {
156 scalar_ct_val = scalar_ct(builder, scalar_native);
157 }
158 return std::make_pair(scalar_native, scalar_ct_val);
159 }
160
161 public:
162 // Smoke tests for origin tag propagation across all basic operations
164 {
167
168 // Setup: two points with different tags
169 auto [input_a, a] = get_random_point(&builder, InputType::WITNESS);
170 auto [input_b, b] = get_random_point(&builder, InputType::WITNESS);
171 a.set_origin_tag(submitted_value_origin_tag);
172 b.set_origin_tag(challenge_origin_tag);
173
174 // Tag is preserved after being set
175 EXPECT_EQ(a.get_origin_tag(), submitted_value_origin_tag);
176 EXPECT_EQ(b.get_origin_tag(), challenge_origin_tag);
177
178 // Binary operations merge tags
179 EXPECT_EQ((a + b).get_origin_tag(), first_two_merged_tag);
180 EXPECT_EQ((a - b).get_origin_tag(), first_two_merged_tag);
181
182 // Unary operations preserve tags
183 EXPECT_EQ(a.dbl().get_origin_tag(), submitted_value_origin_tag);
184 EXPECT_EQ((-a).get_origin_tag(), submitted_value_origin_tag);
185
186 // Scalar multiplication merges tags
188 scalar.set_origin_tag(challenge_origin_tag);
189 EXPECT_EQ((a * scalar).get_origin_tag(), first_two_merged_tag);
190
191 // Conditional operations merge tags
192 auto predicate = bool_ct(witness_ct(&builder, true));
193 predicate.set_origin_tag(challenge_origin_tag);
194 EXPECT_EQ(a.conditional_negate(predicate).get_origin_tag(), first_two_merged_tag);
195
196 // conditional_select merges all three input tags
197 predicate.set_origin_tag(next_challenge_tag);
198 EXPECT_EQ(a.conditional_select(b, predicate).get_origin_tag(), first_second_third_merged_tag);
199
200 // Construction from tagged field elements merges member tags
201 affine_element input_c(element::random_element());
202 auto x = element_ct::BaseField::from_witness(&builder, input_c.x);
203 auto y = element_ct::BaseField::from_witness(&builder, input_c.y);
204
205 // Set tags on the individual field elements
206 x.set_origin_tag(submitted_value_origin_tag);
207 y.set_origin_tag(challenge_origin_tag);
208
209 // Construct biggroup element from pre-tagged field elements
210 // The is_infinity flag is auto-detected from coordinates and won't have a user-set tag
211 element_ct c(x, y);
212
213 // The tag of the biggroup element should be the union of x and y member tags
214 EXPECT_EQ(c.get_origin_tag(), first_two_merged_tag);
215
216 // compute_naf propagates tag to output bits (not available on goblin elements)
217 if constexpr (!HasGoblinBuilder<TestType>) {
218 auto naf_scalar = scalar_ct::from_witness(&builder, fr(12345));
219 naf_scalar.set_origin_tag(submitted_value_origin_tag);
220 auto naf = element_ct::compute_naf(naf_scalar, 16);
221 for (const auto& bit : naf) {
222 EXPECT_EQ(bit.get_origin_tag(), submitted_value_origin_tag);
223 }
224 }
225
226#ifndef NDEBUG
227 // Instant death tag causes exception on use.
228 // NOTE: We construct the element BEFORE poisoning its x coordinate.
229 // The 2-argument element_ct constructor sums the x limbs to detect the point at infinity,
230 // which would trigger the instant_death check if the tag were already set.
231 affine_element input_death(element::random_element());
232 auto x_death = element_ct::BaseField::from_witness(&builder, input_death.x);
233 auto y_normal = element_ct::BaseField::from_witness(&builder, input_death.y);
234 y_normal.set_origin_tag(constant_tag);
235 element_ct death_point(x_death, y_normal, /*assert_on_curve=*/false);
236 // Poison the x coordinate after construction so the throw happens inside operator+
237 death_point.x().set_origin_tag(instant_death_tag);
238 EXPECT_THROW(death_point + death_point, std::runtime_error);
239
240 // AUDITTODO: incomplete_assert_equal has inconsistent instant_death behavior between builders. (this was simply
241 // untested before).
242 //
243 // Design intent: assert_equal methods explicitly disable tag checking to allow comparing
244 // values from different transcript sources. So instant_death should NOT be triggered.
245 //
246 // Current behavior:
247 // - bigfield: instant_death IS triggered because bigfield::get_origin_tag()
248 // merges 5 limb tags, which invokes the OriginTag merge constructor that checks for
249 // instant_death. This happens BEFORE tags are cleared.
250 // - goblin_field: instant_death is NOT triggered because goblin_field::assert_equal
251 // delegates to field_t::assert_equal on each limb, which saves tags individually without
252 // merging.
253 //
254 // Potential fix: In bigfield::assert_equal, save/restore tags at the limb level instead of
255 // calling get_origin_tag() which merges tags.
256#endif
257 }
258
260 {
261 // Only test for non-goblin builders (goblin elements don't have assert_coordinates_in_field
262 // because coordinate checks are done in the ECCVM circuit)
263 if constexpr (!HasGoblinBuilder<TestType>) {
264 // Test 1: Valid coordinates should pass
265 {
267
268 // Test multiple random points to ensure assert_coordinates_in_field works correctly
269 for (size_t i = 0; i < 3; ++i) {
270 affine_element valid_point(element::random_element());
271 element_ct point = element_ct::from_witness(&builder, valid_point);
272
273 // This should not fail - coordinates are in field
274 point.assert_coordinates_in_field();
275 }
276
277 // Verify the circuit is correct
279 }
280
281 // Test 2: Invalid x coordinate should cause circuit to fail
282 {
284 affine_element valid_point(element::random_element());
285
286 // Create a bigfield element with x coordinate that will be out of range
287 // We do this by creating a valid witness but then manipulating the limb values
288 // to make them represent a value >= the modulus
289 auto x_coord = element_ct::BaseField::from_witness(&builder, valid_point.x);
290 auto y_coord = element_ct::BaseField::from_witness(&builder, valid_point.y);
291
292 // Manipulate the limbs to create an invalid value
293 // Set the highest limb to a very large value that would make the total >= modulus
295 x_coord, 3, field_ct::from_witness(&builder, bb::fr(uint256_t(1) << 68)));
296 x_coord.set_limb_max(3, uint256_t(1) << 68);
297
298 // Skip curve check since we're intentionally creating an invalid point
299 // Note: is_infinity is auto-detected as false since coords are non-zero
300 element_ct point(x_coord, y_coord, /*assert_on_curve=*/false);
301 point.assert_coordinates_in_field();
302
303 // Circuit should fail because x coordinate is out of field
305 }
306
307 // Test 3: Invalid y coordinate should cause circuit to fail
308 {
310 affine_element valid_point(element::random_element());
311
312 auto x_coord = element_ct::BaseField::from_witness(&builder, valid_point.x);
313 auto y_coord = element_ct::BaseField::from_witness(&builder, valid_point.y);
314
315 // Manipulate the limbs to create an invalid value
316 // Set the highest limb to a very large value that would make the total >= modulus
318 y_coord, 3, field_ct::from_witness(&builder, bb::fr(uint256_t(1) << 68)));
319 y_coord.set_limb_max(3, uint256_t(1) << 68);
320
321 // Skip curve check since we're intentionally creating an invalid point
322 // Note: is_infinity is auto-detected as false since coords are non-zero
323 element_ct point(x_coord, y_coord, /*assert_on_curve=*/false);
324 point.assert_coordinates_in_field();
325
326 // Circuit should fail because y coordinate is out of field
328 }
329 }
330 }
331
333 {
335 size_t num_repetitions = 10;
336 for (size_t i = 0; i < num_repetitions; ++i) {
337 auto [input_a, a] = get_random_point(&builder, a_type);
338 auto [input_b, b] = get_random_point(&builder, b_type);
339
340 uint64_t before = builder.get_num_finalized_gates_inefficient();
341 element_ct c = a + b;
342 uint64_t after = builder.get_num_finalized_gates_inefficient();
343
344 if (i == num_repetitions - 1) {
345 benchmark_info(Builder::NAME_STRING, "Biggroup", "ADD", "Gate Count", after - before);
346 }
347
348 affine_element c_expected(element(input_a) + element(input_b));
349
350 uint256_t c_x_u256 = c.x().get_value().lo;
351 uint256_t c_y_u256 = c.y().get_value().lo;
352
353 fq c_x_result(c_x_u256);
354 fq c_y_result(c_y_u256);
355
356 EXPECT_EQ(c_x_result, c_expected.x);
357 EXPECT_EQ(c_y_result, c_expected.y);
358 }
359
361 }
362
364 {
366 size_t num_repetitions = 10;
367 for (size_t i = 0; i < num_repetitions; ++i) {
368 auto [input_a, a] = get_random_point(&builder, a_type);
369 auto [input_b, b] = get_random_point(&builder, b_type);
370
371 element_ct original_a = a;
372 a += b;
373
374 affine_element expected(element(input_a) + element(input_b));
375 uint256_t result_x = a.x().get_value().lo;
376 uint256_t result_y = a.y().get_value().lo;
377
378 EXPECT_EQ(fq(result_x), expected.x);
379 EXPECT_EQ(fq(result_y), expected.y);
380 }
382 }
383
385 {
387 size_t num_repetitions = 1;
388 for (size_t i = 0; i < num_repetitions; ++i) {
389 affine_element input_a(element::random_element());
390 affine_element input_b(element::random_element());
391 input_b.self_set_infinity();
392 element_ct a = element_ct::from_witness(&builder, input_a);
393 element_ct a_alternate = element_ct::from_witness(&builder, input_a);
394 element_ct a_negated = element_ct::from_witness(&builder, -input_a);
395 element_ct b = element_ct::from_witness(&builder, input_b);
396
397 element_ct c = a + b;
398 element_ct d = b + a;
399 element_ct e = b + b;
400 element_ct f = a + a;
401 element_ct g = a + a_alternate;
402 element_ct h = a + a_negated;
403
404 affine_element c_expected = affine_element(element(input_a) + element(input_b));
405 affine_element d_expected = affine_element(element(input_b) + element(input_a));
406 affine_element e_expected = affine_element(element(input_b) + element(input_b));
407 affine_element f_expected = affine_element(element(input_a) + element(input_a));
408 affine_element g_expected = affine_element(element(input_a) + element(input_a));
409 affine_element h_expected = affine_element(element(input_a) + element(-input_a));
410
411 EXPECT_EQ(c.get_value(), c_expected);
412 EXPECT_EQ(d.get_value(), d_expected);
413 EXPECT_EQ(e.get_value(), e_expected);
414 EXPECT_EQ(f.get_value(), f_expected);
415 EXPECT_EQ(g.get_value(), g_expected);
416 EXPECT_EQ(h.get_value(), h_expected);
417 }
418
420 }
426 {
428 size_t num_repetitions = 5;
429 for (size_t i = 0; i < num_repetitions; ++i) {
430 // Create canonical point at infinity (constant and witness cases)
431 element_ct input_a = element_ct::constant_infinity(&builder);
432 element_ct input_b = element_ct::from_witness(&builder, affine_element::infinity());
433
434 auto standard_a = input_a.get_standard_form();
435 auto standard_b = input_b.get_standard_form();
436
437 EXPECT_EQ(is_infinity(standard_a), true);
438 EXPECT_EQ(is_infinity(standard_b), true);
439
440 fq standard_a_x = standard_a.x().get_value().lo;
441 fq standard_a_y = standard_a.y().get_value().lo;
442
443 fq standard_b_x = standard_b.x().get_value().lo;
444 fq standard_b_y = standard_b.y().get_value().lo;
445
446 // Canonical infinity points should maintain (0, 0) coordinates
447 EXPECT_EQ(standard_a_x, 0);
448 EXPECT_EQ(standard_a_y, 0);
449 EXPECT_EQ(standard_b_x, 0);
450 EXPECT_EQ(standard_b_y, 0);
451 }
452
454 }
455
457 {
459 size_t num_repetitions = 10;
460 for (size_t i = 0; i < num_repetitions; ++i) {
461 auto [input_a, a] = get_random_point(&builder, a_type);
462 auto [input_b, b] = get_random_point(&builder, b_type);
463
464 element_ct c = a - b;
465
466 affine_element c_expected(element(input_a) - element(input_b));
467
468 uint256_t c_x_u256 = c.x().get_value().lo;
469 uint256_t c_y_u256 = c.y().get_value().lo;
470
471 fq c_x_result(c_x_u256);
472 fq c_y_result(c_y_u256);
473
474 EXPECT_EQ(c_x_result, c_expected.x);
475 EXPECT_EQ(c_y_result, c_expected.y);
476 }
477
479 }
480
482 {
484 size_t num_repetitions = 10;
485 for (size_t i = 0; i < num_repetitions; ++i) {
486 auto [input_a, a] = get_random_point(&builder, a_type);
487 auto [input_b, b] = get_random_point(&builder, b_type);
488
489 a -= b;
490
491 affine_element expected(element(input_a) - element(input_b));
492 uint256_t result_x = a.x().get_value().lo;
493 uint256_t result_y = a.y().get_value().lo;
494
495 EXPECT_EQ(fq(result_x), expected.x);
496 EXPECT_EQ(fq(result_y), expected.y);
497 }
499 }
500
502 {
504 size_t num_repetitions = 1;
505 for (size_t i = 0; i < num_repetitions; ++i) {
506 affine_element input_a(element::random_element());
507 affine_element input_b(element::random_element());
508 input_b.self_set_infinity();
509 element_ct a = element_ct::from_witness(&builder, input_a);
510 element_ct a_alternate = element_ct::from_witness(&builder, input_a);
511 element_ct a_negated = element_ct::from_witness(&builder, -input_a);
512 element_ct b = element_ct::from_witness(&builder, input_b);
513
514 element_ct c = a - b;
515 element_ct d = b - a;
516 element_ct e = b - b;
517 element_ct f = a - a;
518 element_ct g = a - a_alternate;
519 element_ct h = a - a_negated;
520
521 affine_element c_expected = affine_element(element(input_a) - element(input_b));
522 affine_element d_expected = affine_element(element(input_b) - element(input_a));
523 affine_element e_expected = affine_element(element(input_b) - element(input_b));
524 affine_element f_expected = affine_element(element(input_a) - element(input_a));
525 affine_element g_expected = affine_element(element(input_a) - element(input_a));
526 affine_element h_expected = affine_element(element(input_a) - element(-input_a));
527
528 EXPECT_EQ(c.get_value(), c_expected);
529 EXPECT_EQ(d.get_value(), d_expected);
530 EXPECT_EQ(e.get_value(), e_expected);
531 EXPECT_EQ(f.get_value(), f_expected);
532 EXPECT_EQ(g.get_value(), g_expected);
533 EXPECT_EQ(h.get_value(), h_expected);
534 }
535
537 }
538
541 {
543 size_t num_repetitions = 10;
544 for (size_t i = 0; i < num_repetitions; ++i) {
545 auto [input_a, a] = get_random_point(&builder, a_type);
546 auto [input_b, b] = get_random_point(&builder, b_type);
547
548 element_ct result = a.checked_unconditional_add(b);
549
550 affine_element expected(element(input_a) + element(input_b));
551 uint256_t result_x = result.x().get_value().lo;
552 uint256_t result_y = result.y().get_value().lo;
553
554 EXPECT_EQ(fq(result_x), expected.x);
555 EXPECT_EQ(fq(result_y), expected.y);
556 }
558 }
559
562 {
564 size_t num_repetitions = 10;
565 for (size_t i = 0; i < num_repetitions; ++i) {
566 auto [input_a, a] = get_random_point(&builder, a_type);
567 auto [input_b, b] = get_random_point(&builder, b_type);
568
569 element_ct result = a.checked_unconditional_subtract(b);
570
571 affine_element expected(element(input_a) - element(input_b));
572 uint256_t result_x = result.x().get_value().lo;
573 uint256_t result_y = result.y().get_value().lo;
574
575 EXPECT_EQ(fq(result_x), expected.x);
576 EXPECT_EQ(fq(result_y), expected.y);
577 }
579 }
580
583 {
585 size_t num_repetitions = 10;
586 for (size_t i = 0; i < num_repetitions; ++i) {
587 const auto [input_a, a] = get_random_point(&builder, a_type);
588 const auto [input_b, b] = get_random_point(&builder, b_type);
589
590 // Since unchecked_unconditional_add_sub is private in biggroup, we test it via the element_test_accessor
592
593 affine_element expected_sum(element(input_a) + element(input_b));
594 affine_element expected_diff(element(input_a) - element(input_b));
595
596 uint256_t sum_x = sum.x().get_value().lo;
597 uint256_t sum_y = sum.y().get_value().lo;
598 uint256_t diff_x = diff.x().get_value().lo;
599 uint256_t diff_y = diff.y().get_value().lo;
600
601 EXPECT_EQ(fq(sum_x), expected_sum.x);
602 EXPECT_EQ(fq(sum_y), expected_sum.y);
603 EXPECT_EQ(fq(diff_x), expected_diff.x);
604 EXPECT_EQ(fq(diff_y), expected_diff.y);
605 }
607 }
608
610 {
612 size_t num_repetitions = 10;
613 for (size_t i = 0; i < num_repetitions; ++i) {
614 auto [input_a, a] = get_random_point(&builder, a_type);
615
616 element_ct c = a.dbl();
617
618 affine_element c_expected(element(input_a).dbl());
619
620 uint256_t c_x_u256 = c.x().get_value().lo;
621 uint256_t c_y_u256 = c.y().get_value().lo;
622
623 fq c_x_result(c_x_u256);
624 fq c_y_result(c_y_u256);
625
626 EXPECT_EQ(c_x_result, c_expected.x);
627 EXPECT_EQ(c_y_result, c_expected.y);
628 }
630 }
631
633 {
635 {
636 // Case 1: Doubling point at infinity should return point at infinity
637 affine_element input_infinity(element::random_element());
638 input_infinity.self_set_infinity();
639 element_ct a_infinity = element_ct::from_witness(&builder, input_infinity);
640
641 element_ct result_infinity = a_infinity.dbl();
642
643 // Result should be point at infinity
644 EXPECT_TRUE(is_infinity(result_infinity));
645 }
646 {
647 // Case 2: Doubling a normal point should not result in infinity
648 affine_element input_normal(element::random_element());
649 element_ct a_normal = element_ct::from_witness(&builder, input_normal);
650
651 element_ct result_normal = a_normal.dbl();
652
653 // Result should not be point at infinity (with overwhelming probability)
654 EXPECT_FALSE(is_infinity(result_normal));
655
656 // Verify correctness
657 affine_element expected_normal(element(input_normal).dbl());
658 uint256_t result_x = result_normal.x().get_value().lo;
659 uint256_t result_y = result_normal.y().get_value().lo;
660 fq expected_x(result_x);
661 fq expected_y(result_y);
662 EXPECT_EQ(expected_x, expected_normal.x);
663 EXPECT_EQ(expected_y, expected_normal.y);
664 }
666 }
667
669 {
671
672 // For bn254 curve: y^2 = x^3 + 3
673 // We need a point where y = 0, which means x^3 = -3
674 // For most curves, there may not be a rational point with y = 0
675 // So we test the logic by creating a witness point with y = 0 explicitly
676 // Even if it's not on the curve, we can test the doubling logic
677 affine_element test_point(element::random_element());
678
679 // Create a point with y = 0 (may not be on curve, but tests the edge case)
680 auto x_coord = element_ct::BaseField::from_witness(&builder, test_point.x);
681 auto y_coord = element_ct::BaseField::from_witness(&builder, fq(0));
682 // Skip curve check since we're intentionally creating an invalid point to test edge case
683 // Note: is_infinity is auto-detected as false since x coordinate is non-zero
684 element_ct a(x_coord, y_coord, /*assert_on_curve=*/false);
685
686 // With the new assertion, attempting to double a point with y = 0 should throw
687 // because for valid curves like bn254, y = 0 cannot occur on the curve
688 EXPECT_THROW_WITH_MESSAGE(a.dbl(), "Attempting to dbl a point with y = 0, not allowed.");
689 }
690
692 {
693 // Test that P + P equals P.dbl()
695 size_t num_repetitions = 5;
696 for (size_t i = 0; i < num_repetitions; ++i) {
697 auto [input_a, a] = get_random_point(&builder, InputType::WITNESS);
698
699 element_ct sum = a + a;
700 element_ct doubled = a.dbl();
701
702 // Results should match
703 uint256_t sum_x = sum.x().get_value().lo;
704 uint256_t sum_y = sum.y().get_value().lo;
705 uint256_t dbl_x = doubled.x().get_value().lo;
706 uint256_t dbl_y = doubled.y().get_value().lo;
707
708 EXPECT_EQ(fq(sum_x), fq(dbl_x));
709 EXPECT_EQ(fq(sum_y), fq(dbl_y));
710 EXPECT_EQ(is_infinity(sum), is_infinity(doubled));
711 }
713 }
714
716 {
717 // Test that P - (-P) equals 2P
719 size_t num_repetitions = 5;
720 for (size_t i = 0; i < num_repetitions; ++i) {
721 auto [input_a, a] = get_random_point(&builder, InputType::WITNESS);
722
723 element_ct neg_a = -a;
724 element_ct result = a - neg_a;
725 element_ct expected = a.dbl();
726
727 // P - (-P) = P + P = 2P
728 uint256_t result_x = result.x().get_value().lo;
729 uint256_t result_y = result.y().get_value().lo;
730 uint256_t expected_x = expected.x().get_value().lo;
731 uint256_t expected_y = expected.y().get_value().lo;
732
733 EXPECT_EQ(fq(result_x), fq(expected_x));
734 EXPECT_EQ(fq(result_y), fq(expected_y));
735 }
737 }
738
742 {
744 size_t num_repetitions = 10;
745 for (size_t i = 0; i < num_repetitions; ++i) {
746
747 auto [input_a, a] = get_random_point(&builder, a_type);
748 auto [input_b, b] = get_random_point(&builder, b_type);
749 auto [input_c, c] = get_random_point(&builder, c_type);
750
751 auto acc = element_ct::chain_add_start(a, b);
752 auto acc_out = element_ct::chain_add(c, acc);
753 element_ct result = element_ct::chain_add_end(acc_out);
754
755 // Verify result
756 affine_element expected(element(input_a) + element(input_b) + element(input_c));
757 uint256_t result_x = result.x().get_value().lo;
758 uint256_t result_y = result.y().get_value().lo;
759 EXPECT_EQ(fq(result_x), expected.x);
760 EXPECT_EQ(fq(result_y), expected.y);
761
762 // Check intermediate values
763 auto lambda_prev = (input_b.y - input_a.y) / (input_b.x - input_a.x);
764 auto x3_prev = lambda_prev * lambda_prev - input_b.x - input_a.x;
765 auto y3_prev = lambda_prev * (input_a.x - x3_prev) - input_a.y;
766 auto lambda = (y3_prev - input_c.y) / (x3_prev - input_c.x);
767 auto x3 = lambda * lambda - x3_prev - input_c.x;
768
769 uint256_t x3_u256 = acc_out.x3_prev.get_value().lo;
770 uint256_t lambda_u256 = acc_out.lambda_prev.get_value().lo;
771
772 fq x3_result(x3_u256);
773 fq lambda_result(lambda_u256);
774
775 EXPECT_EQ(x3_result, x3);
776 EXPECT_EQ(lambda_result, lambda);
777 }
778
780 }
781
783 {
785 size_t num_repetitions = 10;
786 for (size_t i = 0; i < num_repetitions; ++i) {
787 affine_element acc_small(element::random_element());
788 element_ct acc_big = element_ct::from_witness(&builder, acc_small);
789
791 for (size_t j = 0; j < i; ++j) {
792 affine_element add_1_small_0(element::random_element());
793 element_ct add_1_big_0 = element_ct::from_witness(&builder, add_1_small_0);
794 affine_element add_2_small_0(element::random_element());
795 element_ct add_2_big_0 = element_ct::from_witness(&builder, add_2_small_0);
796 typename element_ct::chain_add_accumulator add_1 =
797 element_ct::chain_add_start(add_1_big_0, add_2_big_0);
798 to_add.emplace_back(add_1);
799 }
800 acc_big.multiple_montgomery_ladder(to_add);
801 }
802
804 }
805
807 {
809 size_t num_repetitions = 10;
810 for (size_t i = 0; i < num_repetitions; ++i) {
811 auto [input_a, a] = get_random_point(&builder, point_type);
812
813 element_ct normalized = a.normalize();
814
815 // Normalized should equal the original
816 uint256_t x_before = a.x().get_value().lo;
817 uint256_t y_before = a.y().get_value().lo;
818 uint256_t x_after = normalized.x().get_value().lo;
819 uint256_t y_after = normalized.y().get_value().lo;
820
821 EXPECT_EQ(fq(x_before), fq(x_after));
822 EXPECT_EQ(fq(y_before), fq(y_after));
823 }
825 }
826
827 static void test_reduce(InputType point_type = InputType::WITNESS)
828 {
830 size_t num_repetitions = 10;
831 for (size_t i = 0; i < num_repetitions; ++i) {
832 auto [input_a, a] = get_random_point(&builder, point_type);
833
834 element_ct reduced = a.reduce();
835
836 // Reduced should equal the original
837 uint256_t x_before = a.x().get_value().lo;
838 uint256_t y_before = a.y().get_value().lo;
839 uint256_t x_after = reduced.x().get_value().lo;
840 uint256_t y_after = reduced.y().get_value().lo;
841
842 EXPECT_EQ(fq(x_before), fq(x_after));
843 EXPECT_EQ(fq(y_before), fq(y_after));
844 }
846 }
847
849 {
851 auto [input_a, a] = get_random_point(&builder, a_type);
852
853 element_ct neg_a = -a;
854
855 affine_element expected = affine_element(-element(input_a));
856 uint512_t neg_x_u512 = uint512_t(neg_a.x().get_value()) % uint512_t(fq::modulus);
857 uint512_t neg_y_u512 = uint512_t(neg_a.y().get_value()) % uint512_t(fq::modulus);
858 uint256_t neg_x = neg_x_u512.lo;
859 uint256_t neg_y = neg_y_u512.lo;
860
861 EXPECT_EQ(fq(neg_x), expected.x);
862 EXPECT_EQ(fq(neg_y), expected.y);
863
865 }
866
868 InputType predicate_type = InputType::WITNESS)
869 {
871 size_t num_repetitions = 10;
872 for (size_t i = 0; i < num_repetitions; ++i) {
873 // Get random point
874 auto [input_a, a] = get_random_point(&builder, point_type);
875
876 // Get random predicate
877 bool predicate_value = (engine.get_random_uint8() % 2) != 0;
878 bool_ct predicate = (predicate_type == InputType::WITNESS) ? bool_ct(witness_ct(&builder, predicate_value))
879 : bool_ct(predicate_value);
880
881 element_ct c = a.conditional_negate(predicate);
882
883 affine_element c_expected = predicate_value ? affine_element(-element(input_a)) : input_a;
884 EXPECT_EQ(c.get_value(), c_expected);
885 }
887 }
888
891 InputType predicate_type = InputType::WITNESS)
892 {
894 size_t num_repetitions = 10;
895 for (size_t i = 0; i < num_repetitions; ++i) {
896 auto [input_a, a] = get_random_point(&builder, a_type);
897 auto [input_b, b] = get_random_point(&builder, b_type);
898
899 bool predicate_value = (engine.get_random_uint8() % 2) != 0;
900 bool_ct predicate = (predicate_type == InputType::WITNESS) ? bool_ct(witness_ct(&builder, predicate_value))
901 : bool_ct(predicate_value);
902
903 element_ct c = a.conditional_select(b, predicate);
904
905 affine_element c_expected = predicate_value ? input_b : input_a;
906 EXPECT_EQ(c.get_value(), c_expected);
907 }
909 }
910
912 {
913 // Case 1: Should pass because the points are identical
914 {
916 size_t num_repetitions = 10;
917 for (size_t i = 0; i < num_repetitions; ++i) {
918 affine_element input_a(element::random_element());
919 element_ct a = element_ct::from_witness(&builder, input_a);
920 element_ct b = element_ct::from_witness(&builder, input_a);
921
922 a.incomplete_assert_equal(b, "elements don't match");
923 }
925 }
926 // Case 2: Should pass because the points are identical and at infinity (canonical representation)
927 {
929 size_t num_repetitions = 10;
930 for (size_t i = 0; i < num_repetitions; ++i) {
931 affine_element input_a(element::random_element());
932 input_a.self_set_infinity();
933 element_ct a = element_ct::from_witness(&builder, input_a);
934 element_ct b = element_ct::from_witness(&builder, input_a);
935
936 a.incomplete_assert_equal(b, "elements don't match");
937 }
939 }
940 // Case 3: Self-assertion (point equals itself)
941 {
943 affine_element input(element::random_element());
944 element_ct a = element_ct::from_witness(&builder, input);
945
946 a.incomplete_assert_equal(a, "self assertion test");
947
949 }
950 }
951
953 {
954 // Case 1: Should fail because the points are different
955 {
957 affine_element input_a(element::random_element());
958 affine_element input_b(element::random_element());
959 // Ensure inputs are different
960 while (input_a == input_b) {
961 input_b = element::random_element();
962 }
963 element_ct a = element_ct::from_witness(&builder, input_a);
964 element_ct b = element_ct::from_witness(&builder, input_b);
965
966 a.incomplete_assert_equal(b, "elements don't match");
967
968 // Circuit should fail (Circuit checker doesn't fail because it doesn't actually check copy constraints,
969 // it only checks gate constraints)
970 EXPECT_EQ(builder.failed(), true);
971 EXPECT_EQ(builder.err(), "elements don't match (x coordinate)");
972 }
973 // Case 2: Should fail because the points have same x but different y
974 {
976 affine_element input_a(element::random_element());
977
978 // Create a point with the same x coordinate but different y
979 // For an elliptic curve y^2 = x^3 + ax + b, if (x, y) is on the curve, then (x, -y) is also on the
980 // curve
981 affine_element input_b = input_a;
982 input_b.y = -input_a.y; // Negate y to get a different point with same x
983
984 // Construct the circuit elements with same x but different y
985 auto x_coord = element_ct::BaseField::from_witness(&builder, input_a.x);
986 auto y_coord_a = element_ct::BaseField::from_witness(&builder, input_a.y);
987 auto y_coord_b = element_ct::BaseField::from_witness(&builder, input_b.y);
988
989 // Note: is_infinity is auto-detected as false since coordinates are non-zero
990 element_ct a(x_coord, y_coord_a);
991 element_ct b(x_coord, y_coord_b);
992
993 a.incomplete_assert_equal(b, "elements don't match");
994
995 // Circuit should fail with y coordinate error
996 EXPECT_EQ(builder.failed(), true);
997 EXPECT_EQ(builder.err(), "elements don't match (y coordinate)");
998 }
999 // Case 3: Infinity flag mismatch (one point at infinity, one not)
1000 {
1002 affine_element input_a(element::random_element());
1003 affine_element input_b(element::random_element());
1004
1005 input_a.self_set_infinity();
1006 element_ct a = element_ct::from_witness(&builder, input_a); // at infinity
1007 element_ct b = element_ct::from_witness(&builder, input_b); // not at infinity
1008
1009 a.incomplete_assert_equal(b, "infinity flag mismatch test");
1010
1011 EXPECT_EQ(builder.failed(), true);
1012 if constexpr (HasGoblinBuilder<TestType>) {
1013 // Goblin has no infinity flag; (0,0) coords differ from b's coords
1014 EXPECT_EQ(builder.err(), "infinity flag mismatch test (x coordinate)");
1015 } else {
1016 EXPECT_EQ(builder.err(), "infinity flag mismatch test (infinity flag)");
1017 }
1018 }
1019 }
1020
1021 static void test_compute_naf()
1022 {
1024 size_t max_num_bits = 254;
1025 for (size_t length = 2; length < max_num_bits; length += 1) {
1026
1027 fr scalar_val;
1028
1029 uint256_t scalar_raw = engine.get_random_uint256();
1030 scalar_raw = scalar_raw >> (256 - length);
1031
1032 scalar_val = fr(scalar_raw);
1033
1034 // We test non-zero scalars here
1035 if (scalar_val == fr(0)) {
1036 scalar_val += 1;
1037 };
1038 scalar_ct scalar = scalar_ct::from_witness(&builder, scalar_val);
1039 auto naf = element_ct::compute_naf(scalar, length);
1040
1041 // scalar = -naf[L] + \sum_{i=0}^{L-1}(1-2*naf[i]) 2^{L-1-i}
1042 fr reconstructed_val(0);
1043 for (size_t i = 0; i < length; i++) {
1044 reconstructed_val += (fr(1) - fr(2) * fr(naf[i].get_value())) * fr(uint256_t(1) << (length - 1 - i));
1045 };
1046 reconstructed_val -= fr(naf[length].get_value());
1047 EXPECT_EQ(scalar_val, reconstructed_val);
1048 }
1049
1051 }
1052
1054 {
1055 for (size_t max_num_bits : { 0UL, 1UL, 2UL, 64UL, 128UL }) {
1057
1059 auto naf = element_ct::compute_naf(scalar, max_num_bits);
1060 ASSERT_FALSE(naf.empty());
1061
1062 // For scalar = 0, the canonical NAF encoding is [MSB=0, 1, ..., 1, skew=1] (bool semantics: 0 ⟶ +1, 1 ⟶ −1)
1063 const size_t length = naf.size() - 1;
1064 EXPECT_FALSE(naf[0].get_value()); // msm 0
1065 EXPECT_TRUE(naf[length].get_value()); // lsb 1
1066 for (size_t k = 1; k < length; ++k) { //
1067 EXPECT_TRUE(naf[k].get_value()); // rest all bits 1
1068 }
1069
1070 // Field reconstruction: scalar = -naf[L] + Σ_{i=0..L-1} (1 - 2·naf[i]) · 2^{L-1-i}.
1071 fr reconstructed(0);
1072 for (size_t i = 0; i < length; ++i) {
1073 reconstructed += (fr(1) - fr(2) * fr(naf[i].get_value())) * fr(uint256_t(1) << (length - 1 - i));
1074 }
1075 reconstructed -= fr(naf[length].get_value());
1076 EXPECT_EQ(reconstructed, fr(0));
1077
1079 }
1080 }
1081
1083 {
1085
1086 // Create a scalar that is even (skew=1) and has least-significant 2L bits all 0 (L=68, 2L=136)
1087 // This causes overflow in negative_lo = skew + sum_{i=0}^{135} a'_{i+1} * 2^i = 1 + (2^136 - 1) = 2^136
1088 //
1089 // Scalar chosen such that least significant 136 bits are zero:
1090 fr scalar_native = fr::random_element();
1091 uint256_t scalar_raw = uint256_t(scalar_native);
1092 scalar_raw = (scalar_raw >> 136) << 136;
1093 fr scalar_val = fr(scalar_raw);
1094 scalar_ct scalar = scalar_ct::from_witness(&builder, scalar_val);
1095 scalar.set_origin_tag(submitted_value_origin_tag);
1096
1097 // Compute NAF with full field size
1098 const size_t length = fr::modulus.get_msb() + 1;
1099
1100 // This should not overflow with the fix in place
1101 auto naf = element_ct::compute_naf(scalar, length);
1102
1103 // Verify NAF correctness
1104 for (const auto& bit : naf) {
1105 EXPECT_EQ(bit.get_origin_tag(), submitted_value_origin_tag);
1106 }
1107
1108 // Reconstruct scalar from NAF: scalar = -naf[L] + \sum_{i=0}^{L-1}(1-2*naf[i]) 2^{L-1-i}
1109 fr reconstructed_val(0);
1110 for (size_t i = 0; i < length; i++) {
1111 reconstructed_val += (fr(1) - fr(2) * fr(naf[i].get_value())) * fr(uint256_t(1) << (length - 1 - i));
1112 }
1113 reconstructed_val -= fr(naf[length].get_value());
1114
1115 EXPECT_EQ(scalar_val, reconstructed_val);
1117 }
1118
1119 // Regression test: overwrite the naf witnesses with the malicious top-bit-flipped
1120 // assignment (plus the `field_t::accumulate` intermediates that keep the big_add_gate chain
1121 // satisfied) and check the circuit is rejected.
1123 {
1124 if constexpr (scalar_ct::is_composite) {
1125 GTEST_SKIP() << "composite-Fr reconstruction uses a different witness layout";
1126 } else {
1127 // `is_write_vk_mode = true` permits witness mutation via `set_variable`.
1128 Builder builder(/*is_write_vk_mode=*/true);
1129 const size_t num_rounds = fr::modulus.get_msb() + 1;
1130 const uint256_t top_pow = uint256_t(1) << (num_rounds - 1);
1131 const uint256_t range_size = uint256_t(1) << num_rounds;
1132
1133 fr scalar_val = fr::random_element();
1134 while (scalar_val == fr::zero()) {
1135 scalar_val = fr::random_element();
1136 }
1137
1138 // Create a scalar witness
1139 scalar_ct scalar = scalar_ct::from_witness(&builder, scalar_val);
1140 auto naf = element_ct::compute_naf(scalar, num_rounds);
1141 EXPECT_TRUE(CircuitChecker::check(builder)) << "honest circuit must verify before mutation";
1142
1143 // Rebalance: with `naf[0] = 1`, the integer reconstruction needs
1144 // `lower_bits + skew ≡ scalar (mod r)`. Let `shifted_scalar ≡ (scalar + top_pow) (mod r)`
1145 // reduced into `[0, range_size - 1]`; pick `skew` for parity, then
1146 // `lower_bits_int = (range_size - 1 - shifted_scalar - skew) / 2` gives
1147 // `naf[k] = bit_{num_rounds-1-k}(lower_bits_int)` for k ∈ [1, num_rounds - 1].
1148 const uint256_t target_int = static_cast<uint256_t>(scalar_val + fr(top_pow));
1149 const uint256_t shifted_scalar =
1150 (target_int < top_pow) ? target_int + top_pow : target_int + top_pow - fr::modulus;
1151 const uint64_t skew = shifted_scalar.get_bit(0) ? 0 : 1;
1152 const uint256_t lower_bits_int = (range_size - 1 - shifted_scalar - skew) / 2;
1153
1154 // Overwrite naf witnesses with the malicious assignment.
1155 builder.set_variable(naf[0].get_witness_index(), fr(1));
1156 builder.set_variable(naf[num_rounds].get_witness_index(), fr(skew));
1157 for (size_t k = 1; k < num_rounds; ++k) {
1158 builder.set_variable(naf[k].get_witness_index(),
1159 fr(lower_bits_int.get_bit(num_rounds - 1 - k) ? 1 : 0));
1160 }
1161
1162 // Recompute the intermediate accumulator witnesses from the malicious naf bits.
1163 const size_t num_inputs = num_rounds + 1;
1164 const size_t num_gates = (num_inputs + 2) / 3;
1165 const size_t padded_size = num_gates * 3;
1166 const uint32_t accumulate_start_idx = naf[0].get_witness_index() + 1;
1167
1168 std::vector<fr> summands(padded_size, fr(0));
1169 for (size_t i = 0; i < num_rounds; ++i) {
1170 const fr naf_bit = builder.get_variable(naf[num_rounds - 1 - i].get_witness_index());
1171 summands[i] = (fr(1) - fr(2) * naf_bit) * fr(uint256_t(1) << i);
1172 }
1173 summands[num_rounds] = -builder.get_variable(naf[num_rounds].get_witness_index());
1174
1175 fr accumulator = std::accumulate(summands.begin(), summands.end(), fr(0));
1176 EXPECT_EQ(accumulator, scalar_val) << "rebalance arithmetic should give a field-equivalent reconstruction";
1177 builder.set_variable(accumulate_start_idx, accumulator);
1178 for (size_t gate_idx = 0; gate_idx + 1 < num_gates; ++gate_idx) {
1179 accumulator -= summands[3 * gate_idx] + summands[(3 * gate_idx) + 1] + summands[(3 * gate_idx) + 2];
1180 builder.set_variable(accumulate_start_idx + 1 + static_cast<uint32_t>(gate_idx), accumulator);
1181 }
1182
1183 EXPECT_FALSE(CircuitChecker::check(builder))
1184 << "compute_naf must reject the malicious top-bit-flipped NAF assignment";
1185 }
1186 }
1187
1188 // Regression test: compute_naf must not depend on witness values for its circuit shape.
1189 // Previously it did — with `max_num_bits < field_size`, passing a zero-valued scalar witness
1190 // forced `num_rounds = fr::modulus.get_msb() + 1`, producing more NAF entries
1191 // (and a larger circuit) than a non-zero scalar with the same `max_num_bits`.
1193 {
1194 constexpr size_t max_num_bits = 128;
1195 const std::array<fr, 2> scalar_values{ fr(42), fr::zero() };
1196
1197 std::array<Builder, 2> builders;
1198 for (size_t k = 0; k < 2; ++k) {
1199 Builder& builder = builders[k];
1200 scalar_ct scalar = scalar_ct::from_witness(&builder, scalar_values[k]);
1201 (void)element_ct::compute_naf(scalar, max_num_bits);
1202 }
1203
1204 EXPECT_EQ(builders[0].blocks, builders[1].blocks);
1205 }
1206
1207 static void test_mul(InputType scalar_type = InputType::WITNESS, InputType point_type = InputType::WITNESS)
1208 {
1210 size_t num_repetitions = 1;
1211 for (size_t i = 0; i < num_repetitions; ++i) {
1212 auto [input, P] = get_random_point(&builder, point_type);
1213 auto [scalar, x] = get_random_scalar(&builder, scalar_type, /*even*/ true);
1214
1215 std::cerr << "gates before mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1216 element_ct c = P * x;
1217 std::cerr << "builder after mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1218 affine_element c_expected(element(input) * scalar);
1219
1220 fq c_x_result(c.x().get_value().lo);
1221 fq c_y_result(c.y().get_value().lo);
1222
1223 EXPECT_EQ(c_x_result, c_expected.x);
1224 EXPECT_EQ(c_y_result, c_expected.y);
1225 }
1226
1228 }
1229
1231 InputType point_type = InputType::WITNESS)
1232 {
1234
1235 const auto run_mul_and_check = [&](element_ct& P, scalar_ct& x, const affine_element& expected) {
1236 // Perform multiplication
1237 element_ct result = P * x;
1238
1239 // Check if result is infinity
1240 bool result_is_inf = is_infinity(result);
1241 bool expected_is_inf = expected.is_point_at_infinity();
1242
1243 EXPECT_EQ(result_is_inf, expected_is_inf);
1244
1245 // If not infinity, check if the coordinates match
1246 if (!expected_is_inf) {
1247 uint256_t result_x = result.x().get_value().lo;
1248 uint256_t result_y = result.y().get_value().lo;
1249
1250 EXPECT_EQ(fq(result_x), expected.x);
1251 EXPECT_EQ(fq(result_y), expected.y);
1252 }
1253 };
1254
1255 // Case 1: P * 0 = ∞
1256 {
1257 auto [input, P] = get_random_point(&builder, point_type);
1259 : scalar_ct(&builder, fr(0));
1260 affine_element expected_infinity = affine_element(element::infinity());
1261 run_mul_and_check(P, x, expected_infinity);
1262 }
1263 // Case 2: (∞) * k = ∞
1264 {
1265 auto [input, P] = get_random_point(&builder, point_type);
1266 if (point_type == InputType::CONSTANT) {
1267 P = element_ct::constant_infinity(&builder);
1268 } else {
1269 input.self_set_infinity();
1270 P = element_ct::from_witness(&builder, input);
1271 }
1272
1273 auto [scalar, x] = get_random_scalar(&builder, scalar_type, /*even*/ true);
1274 affine_element expected_infinity = affine_element(element::infinity());
1275 run_mul_and_check(P, x, expected_infinity);
1276 }
1277 // Case 3: P * 1 = P
1278 {
1279 auto [input, P] = get_random_point(&builder, point_type);
1280 scalar_ct one = (scalar_type == InputType::WITNESS) ? scalar_ct::from_witness(&builder, fr(1))
1281 : scalar_ct(&builder, fr(1));
1282 run_mul_and_check(P, one, input);
1283 }
1284 // Case 4: P * (-1) = -P
1285 {
1286 auto [input, P] = get_random_point(&builder, point_type);
1287 fr neg_one = -fr(1);
1288 scalar_ct neg_one_ct = (scalar_type == InputType::WITNESS) ? scalar_ct::from_witness(&builder, neg_one)
1289 : scalar_ct(&builder, neg_one);
1290 affine_element expected = affine_element(-element(input));
1291 run_mul_and_check(P, neg_one_ct, expected);
1292 }
1294 }
1295
1296 // Test short scalar mul with variable bit lengths.
1298 {
1300
1301 std::vector<size_t> test_lengths = { 2, 3, 10, 11, 31, 32, 63, 64, 127, 128, 252, 253 };
1302
1303 for (size_t i : test_lengths) {
1304 affine_element input(element::random_element());
1305 // Get a random 256 integer
1306 uint256_t scalar_raw = engine.get_random_uint256();
1307 // Produce a length =< i scalar.
1308 scalar_raw = scalar_raw >> (256 - i);
1309 fr scalar = fr(scalar_raw);
1310
1311 // Avoid multiplication by 0 that may occur when `i` is small
1312 if (scalar == fr(0)) {
1313 scalar += 1;
1314 };
1315
1316 element_ct P = element_ct::from_witness(&builder, input);
1318
1319 std::cerr << "gates before mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1320 // Multiply using specified scalar length
1321 element_ct c = P.scalar_mul(x, i);
1322 std::cerr << "builder after mul " << builder.get_num_finalized_gates_inefficient() << std::endl;
1323 affine_element c_expected(element(input) * scalar);
1324
1325 fq c_x_result(c.x().get_value().lo);
1326 fq c_y_result(c.y().get_value().lo);
1327
1328 EXPECT_EQ(c_x_result, c_expected.x);
1329
1330 EXPECT_EQ(c_y_result, c_expected.y);
1331 }
1332
1334 }
1335
1337 {
1338 // A point at infinity must preserve `is_point_at_infinity()` after multiplication by a
1339 // short scalar. The gate count must also be identical to the finite-point case:
1340 // `handle_points_at_infinity` rewrites (∞, s) as (G, 0) in-circuit, the small path's
1341 // `compute_naf` handles the zero scalar at `max_num_bits` width, and `batch_mul_internal`
1342 // routes by compile-time facts only, so the circuit shape does not depend on whether
1343 // the input point is infinity.
1344
1345 std::vector<element> points(2);
1346 points[0] = element::infinity();
1347 points[1] = element::random_element();
1348 std::vector<size_t> gates(2);
1349
1350 bool expect_infinity = true;
1351 for (auto [point, num_gates] : zip_view(points, gates)) {
1353
1354 const size_t max_num_bits = 128;
1355 uint256_t scalar_raw = engine.get_random_uint256() >> (256 - max_num_bits);
1356 fr scalar = fr(scalar_raw);
1357
1358 element_ct P = element_ct::from_witness(&builder, point);
1360
1361 element_ct c = P.scalar_mul(x, max_num_bits);
1362 num_gates = builder.get_num_finalized_gates_inefficient();
1363
1364 EXPECT_EQ(is_infinity(c), expect_infinity);
1366 expect_infinity = false;
1367 }
1368 EXPECT_EQ(gates[0], gates[1]);
1369 }
1370
1371 static void test_twin_mul()
1372 {
1374 size_t num_repetitions = 1;
1375 for (size_t i = 0; i < num_repetitions; ++i) {
1376 affine_element input_a(element::random_element());
1377 affine_element input_b(element::random_element());
1378 fr scalar_a(fr::random_element());
1379 fr scalar_b(fr::random_element());
1380 if ((uint256_t(scalar_a).get_bit(0) & 1) == 1) {
1381 scalar_a -= fr(1); // skew bit is 1
1382 }
1383 if ((uint256_t(scalar_b).get_bit(0) & 1) == 0) {
1384 scalar_b += fr(1); // skew bit is 0
1385 }
1386 element_ct P_a = element_ct::from_witness(&builder, input_a);
1387 scalar_ct x_a = scalar_ct::from_witness(&builder, scalar_a);
1388 element_ct P_b = element_ct::from_witness(&builder, input_b);
1389 scalar_ct x_b = scalar_ct::from_witness(&builder, scalar_b);
1390
1391 element_ct c = element_ct::batch_mul({ P_a, P_b }, { x_a, x_b });
1392
1393 element input_c = (element(input_a) * scalar_a);
1394 element input_d = (element(input_b) * scalar_b);
1395 affine_element expected(input_c + input_d);
1396 fq c_x_result(c.x().get_value().lo);
1397 fq c_y_result(c.y().get_value().lo);
1398
1399 EXPECT_EQ(c_x_result, expected.x);
1400 EXPECT_EQ(c_y_result, expected.y);
1401 }
1403 }
1404
1406 {
1408 size_t num_repetitions = 1;
1409 for (size_t i = 0; i < num_repetitions; ++i) {
1410 affine_element input_a(element::random_element());
1411 affine_element input_b(element::random_element());
1412 input_b.self_set_infinity();
1413
1414 // Get two 128-bit scalars
1415 const size_t max_num_bits = 128;
1416 uint256_t scalar_raw_a = engine.get_random_uint256();
1417 scalar_raw_a = scalar_raw_a >> (256 - max_num_bits);
1418 fr scalar_a = fr(scalar_raw_a);
1419
1420 uint256_t scalar_raw_b = engine.get_random_uint256();
1421 scalar_raw_b = scalar_raw_b >> (256 - max_num_bits);
1422 fr scalar_b = fr(scalar_raw_b);
1423
1424 element_ct P_a = element_ct::from_witness(&builder, input_a); // A
1425 scalar_ct x_a = scalar_ct::from_witness(&builder, scalar_a); // s_1 (128 bits)
1426 element_ct P_b = element_ct::from_witness(&builder, input_b); // ∞
1427 scalar_ct x_b = scalar_ct::from_witness(&builder, scalar_b); // s_2 (128 bits)
1428
1429 element_ct c = element_ct::batch_mul({ P_a, P_b }, { x_a, x_b }, 128);
1430
1431 element input_c = (element(input_a) * scalar_a);
1432 element input_d = (element(input_b) * scalar_b);
1433 affine_element expected(input_c + input_d);
1434 fq c_x_result(c.x().get_value().lo);
1435 fq c_y_result(c.y().get_value().lo);
1436
1437 EXPECT_EQ(c_x_result, expected.x);
1438 EXPECT_EQ(c_y_result, expected.y);
1439 }
1441 }
1442
1444 {
1446 affine_element input_P(element::random_element());
1447
1448 affine_element input_P_a = affine_element(element(input_P) + element(input_P)); // 2P
1449 affine_element input_P_b = affine_element(element(input_P_a) + element(input_P)); // 3P
1450 affine_element input_P_c = affine_element(element(input_P_a) + element(input_P_b)); // 5P
1451 std::vector<affine_element> input_points = { input_P_a, input_P_b, input_P_c };
1452
1453 // Choose scalars such that their NAF representations are:
1454 // skew msd lsd
1455 // a: 0 [+1, +1, -1, +1] = -0 + 2^3 + 2^2 - 2^1 + 2^0 = 11
1456 // b: 1 [+1, +1, +1, +1] = -1 + 2^3 + 2^2 + 2^1 + 2^0 = 14
1457 // c: 1 [+1, -1, +1, +1] = -1 + 2^3 - 2^2 + 2^1 + 2^0 = 6
1458 fr scalar_a(11);
1459 fr scalar_b(14);
1460 fr scalar_c(6);
1461 std::vector<fr> input_scalars = { scalar_a, scalar_b, scalar_c };
1462
1463 std::vector<scalar_ct> scalars;
1465 for (size_t i = 0; i < 3; ++i) {
1466 const element_ct point = element_ct::from_witness(&builder, input_points[i]);
1467 const scalar_ct scalar = scalar_ct::from_witness(&builder, input_scalars[i]);
1468 scalars.emplace_back(scalar);
1469 points.emplace_back(point);
1470 }
1471
1472 // Since with_edgecases = true by default, should handle linearly dependent points correctly
1473 // (offset generator is now a free witness sampled inside batch_mul)
1474 element_ct c = element_ct::batch_mul(points,
1475 scalars,
1476 /*max_num_bits*/ 128);
1477 element input_e = (element(input_P_a) * scalar_a);
1478 element input_f = (element(input_P_b) * scalar_b);
1479 element input_g = (element(input_P_c) * scalar_c);
1480
1481 affine_element expected(input_e + input_f + input_g);
1482 fq c_x_result(c.x().get_value().lo);
1483 fq c_y_result(c.y().get_value().lo);
1484
1485 EXPECT_EQ(c_x_result, expected.x);
1486 EXPECT_EQ(c_y_result, expected.y);
1487
1489 }
1490
1492 {
1494 affine_element input_P(element::random_element());
1495
1496 affine_element input_P_a = affine_element(element(input_P) + element(input_P)); // 2P
1497 affine_element input_P_b = affine_element(element(input_P_a) + element(input_P)); // 3P
1498 affine_element input_P_c = affine_element(element(input_P_a) + element(input_P_b)); // 5P
1499 std::vector<affine_element> input_points = { input_P_a, input_P_b, input_P_c };
1500
1501 // Choose scalars similar to the previous test
1502 fr scalar_a(11);
1503 fr scalar_b(14);
1504 fr scalar_c(6);
1505 std::vector<fr> input_scalars = { scalar_a, scalar_b, scalar_c };
1506
1507 std::vector<scalar_ct> scalars;
1509 for (size_t i = 0; i < 3; ++i) {
1510 const element_ct point = element_ct::from_witness(&builder, input_points[i]);
1511 points.emplace_back(point);
1512
1513 const scalar_ct scalar = scalar_ct::from_witness(&builder, input_scalars[i]);
1514 scalars.emplace_back(scalar);
1515 }
1516
1517 // with_edgecases = false should fail due to linearly dependent points
1518 // This will fail only while using ultra builder
1519 element_ct::batch_mul(points, scalars, /*max_num_bits*/ 4, /*with_edgecases*/ false);
1520
1522 EXPECT_EQ(builder.err(), "bigfield: prime limb diff is zero, but expected non-zero");
1523 }
1524
1525 // Regression test for the offset-generator as point at infinity in `mask_points`.
1527 {
1528 // is_write_vk_mode=true so `set_variable` (used below to poison the offset generator's witness) is
1529 // permitted; outside VK mode, the builder asserts to prevent accidental witness overwrites.
1530 Builder builder(/*is_write_vk_mode=*/true);
1531 using BaseField = typename element_ct::BaseField;
1532
1533 std::vector<element_ct> circuit_points;
1534 std::vector<scalar_ct> circuit_scalars;
1535 for (size_t i = 0; i < 2; ++i) {
1536 circuit_points.push_back(get_random_point(&builder, InputType::WITNESS).second);
1537 circuit_scalars.push_back(get_random_scalar(&builder, InputType::WITNESS).second);
1538 }
1539
1540 // Call mask_points via the test accessor (it is a private member of element).
1541 auto [_masked_points, _masked_scalars, offset_G] = stdlib::element_default::element_test_accessor::
1542 mask_points<Builder, typename element_ct::BaseField, scalar_ct, typename Curve::GroupNative>(
1543 circuit_points, circuit_scalars);
1544
1545 // Sanity: with the honest random offset generator, mask_points's non-infinity constraint is satisfied.
1546 EXPECT_TRUE(CircuitChecker::check(builder));
1547
1548 // Set offset generator to be a point at infinity: (0, 0) with is_infinity = 1.
1549 for (size_t i = 0; i < BaseField::NUM_LIMBS; ++i) {
1550 builder.set_variable(offset_G.x().get_limb(i).element.get_witness_index(), 0);
1551 builder.set_variable(offset_G.y().get_limb(i).element.get_witness_index(), 0);
1552 }
1553 builder.set_variable(offset_G.is_point_at_infinity().get_witness_index(), 1);
1554
1555 // The non-infinity assertion inside mask_points must catch the malicious witness substitution.
1556 EXPECT_FALSE(CircuitChecker::check(builder));
1557 }
1558
1559 // Regression test for a masked point p_i + 2ⁱ·G being the point at infinity in `mask_points`.
1561 {
1562 Builder builder(/*is_write_vk_mode=*/true);
1563 using BaseField = typename element_ct::BaseField;
1564
1565 std::vector<element_ct> circuit_points;
1566 std::vector<scalar_ct> circuit_scalars;
1567 for (size_t i = 0; i < 2; ++i) {
1568 circuit_points.push_back(get_random_point(&builder, InputType::WITNESS).second);
1569 circuit_scalars.push_back(get_random_scalar(&builder, InputType::WITNESS).second);
1570 }
1571
1572 auto [masked_points, _masked_scalars, _offset_G] = stdlib::element_default::element_test_accessor::
1573 mask_points<Builder, typename element_ct::BaseField, scalar_ct, typename Curve::GroupNative>(
1574 circuit_points, circuit_scalars);
1575
1576 EXPECT_TRUE(CircuitChecker::check(builder));
1577
1578 // Set the first masked point to be a point at infinity: (0, 0) with is_infinity = 1.
1579 for (size_t i = 0; i < BaseField::NUM_LIMBS; ++i) {
1580 builder.set_variable(masked_points[0].x().get_limb(i).element.get_witness_index(), 0);
1581 builder.set_variable(masked_points[0].y().get_limb(i).element.get_witness_index(), 0);
1582 }
1583 builder.set_variable(masked_points[0].is_point_at_infinity().get_witness_index(), 1);
1584
1585 EXPECT_FALSE(CircuitChecker::check(builder));
1586 }
1587
1588 static void test_one()
1589 {
1591 size_t num_repetitions = 1;
1592 for (size_t i = 0; i < num_repetitions; ++i) {
1593 fr scalar_a(fr::random_element());
1594 if ((uint256_t(scalar_a).get_bit(0) & 1) == 1) {
1595 scalar_a -= fr(1); // skew bit is 1
1596 }
1597 element_ct P_a = element_ct::one(&builder);
1598 scalar_ct x_a = scalar_ct::from_witness(&builder, scalar_a);
1599 element_ct c = P_a * x_a;
1600
1601 affine_element expected(g1::one * scalar_a);
1602 fq c_x_result(c.x().get_value().lo);
1603 fq c_y_result(c.y().get_value().lo);
1604
1605 EXPECT_EQ(c_x_result, expected.x);
1606 EXPECT_EQ(c_y_result, expected.y);
1607 }
1608
1610 }
1611
1612 // Overload: defaults to all WITNESS types for given num_points
1613 static void test_helper_batch_mul(size_t num_points,
1614 const bool short_scalars = false,
1615 const bool with_edgecases = false)
1616 {
1617 std::vector<InputType> point_types(num_points, InputType::WITNESS);
1618 std::vector<InputType> scalar_types(num_points, InputType::WITNESS);
1619 test_helper_batch_mul(point_types, scalar_types, short_scalars, with_edgecases);
1620 }
1621
1623 std::vector<InputType> scalar_types,
1624 const bool short_scalars = false,
1625 const bool with_edgecases = false)
1626 {
1628
1629 const size_t num_points = point_types.size();
1631 std::vector<fr> scalars;
1632 std::vector<element_ct> circuit_points;
1633 std::vector<scalar_ct> circuit_scalars;
1634
1635 for (size_t i = 0; i < num_points; ++i) {
1636 // Generate scalars
1637 if (short_scalars) {
1638 auto [input_scalar, x] = get_random_short_scalar(&builder, scalar_types[i], /*num_bits*/ 128);
1639 scalars.push_back(input_scalar);
1640 circuit_scalars.push_back(x);
1641 } else {
1642 auto [input_scalar, x] = get_random_scalar(&builder, scalar_types[i], /*even*/ true);
1643 scalars.push_back(input_scalar);
1644 circuit_scalars.push_back(x);
1645 }
1646
1647 // Generate points
1648 auto [input_point, P] = get_random_point(&builder, point_types[i]);
1649 points.push_back(input_point);
1650 circuit_points.push_back(P);
1651 }
1652
1653 element_ct result_point =
1654 element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, with_edgecases);
1655
1656 element expected_point = g1::one;
1657 expected_point.self_set_infinity();
1658 for (size_t i = 0; i < num_points; ++i) {
1659 expected_point += (element(points[i]) * scalars[i]);
1660 }
1661
1662 expected_point = expected_point.normalize();
1663 fq result_x(result_point.x().get_value().lo);
1664 fq result_y(result_point.y().get_value().lo);
1665
1666 EXPECT_EQ(result_x, expected_point.x);
1667 EXPECT_EQ(result_y, expected_point.y);
1668
1670 }
1671
1672 static void test_batch_mul()
1673 {
1674 const size_t num_points = 5;
1677 std::vector<fr> scalars;
1678 for (size_t i = 0; i < num_points; ++i) {
1679 points.push_back(affine_element(element::random_element()));
1680 scalars.push_back(fr::random_element());
1681 }
1682
1683 std::vector<element_ct> circuit_points;
1684 std::vector<scalar_ct> circuit_scalars;
1685 for (size_t i = 0; i < num_points; ++i) {
1686 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1687 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1688 }
1689
1690 element_ct result_point = element_ct::batch_mul(circuit_points, circuit_scalars);
1691
1692 element expected_point = g1::one;
1693 expected_point.self_set_infinity();
1694 for (size_t i = 0; i < num_points; ++i) {
1695 expected_point += (element(points[i]) * scalars[i]);
1696 }
1697
1698 expected_point = expected_point.normalize();
1699 fq result_x(result_point.x().get_value().lo);
1700 fq result_y(result_point.y().get_value().lo);
1701
1702 EXPECT_EQ(result_x, expected_point.x);
1703 EXPECT_EQ(result_y, expected_point.y);
1704
1706 }
1707
1709 {
1710 const size_t num_points = 5;
1713 std::vector<fr> scalars;
1714 for (size_t i = 0; i < num_points; ++i) {
1715 points.push_back(affine_element(element::random_element()));
1716 scalars.push_back(fr::random_element());
1717 }
1718
1719 std::vector<element_ct> circuit_points;
1720 std::vector<scalar_ct> circuit_scalars;
1721 for (size_t i = 0; i < num_points; ++i) {
1722 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1723 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1724 }
1725
1726 element_ct result_point2 =
1727 element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true);
1728
1729 element expected_point = g1::one;
1730 expected_point.self_set_infinity();
1731 for (size_t i = 0; i < num_points; ++i) {
1732 expected_point += (element(points[i]) * scalars[i]);
1733 }
1734
1735 expected_point = expected_point.normalize();
1736
1737 fq result2_x(result_point2.x().get_value().lo);
1738 fq result2_y(result_point2.y().get_value().lo);
1739
1740 EXPECT_EQ(result2_x, expected_point.x);
1741 EXPECT_EQ(result2_y, expected_point.y);
1742
1744 }
1745
1747 {
1748 const auto test_repeated_points = [](const uint32_t num_points) {
1749 // batch P + ... + P = m*P
1750 info("num points: ", num_points);
1752 std::vector<fr> scalars;
1753 for (size_t idx = 0; idx < num_points; idx++) {
1754 points.push_back(affine_element::one());
1755 scalars.push_back(1);
1756 }
1757
1759 ASSERT_EQ(points.size(), scalars.size());
1760
1761 std::vector<element_ct> circuit_points;
1762 std::vector<scalar_ct> circuit_scalars;
1763 for (size_t i = 0; i < num_points; ++i) {
1764 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1765 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1766 }
1767 element_ct result_point =
1768 element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true);
1769
1770 auto expected_point = element::infinity();
1771 for (const auto& point : points) {
1772 expected_point += point;
1773 }
1774 expected_point = expected_point.normalize();
1775
1776 fq result_x(result_point.x().get_value().lo);
1777 fq result_y(result_point.y().get_value().lo);
1778
1779 EXPECT_EQ(result_x, expected_point.x);
1780 EXPECT_EQ(result_y, expected_point.y);
1781
1783 };
1784 test_repeated_points(2);
1785 test_repeated_points(3);
1786 test_repeated_points(4);
1787 test_repeated_points(5);
1788 test_repeated_points(6);
1789 test_repeated_points(7);
1790 }
1792 {
1793 {
1794 // batch oo + P = P
1796 points.push_back(affine_element::infinity());
1797 points.push_back(affine_element(element::random_element()));
1798 std::vector<fr> scalars;
1799 scalars.push_back(1);
1800 scalars.push_back(1);
1801
1803 ASSERT_EQ(points.size(), scalars.size());
1804 const size_t num_points = points.size();
1805
1806 std::vector<element_ct> circuit_points;
1807 std::vector<scalar_ct> circuit_scalars;
1808 for (size_t i = 0; i < num_points; ++i) {
1809 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1810 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1811 }
1812
1813 element_ct result_point =
1814 element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true);
1815
1816 element expected_point = points[1];
1817 expected_point = expected_point.normalize();
1818
1819 fq result_x(result_point.x().get_value().lo);
1820 fq result_y(result_point.y().get_value().lo);
1821
1822 EXPECT_EQ(result_x, expected_point.x);
1823 EXPECT_EQ(result_y, expected_point.y);
1824
1826 }
1827 {
1828 // batch 0 * P1 + P2 = P2
1830 points.push_back(affine_element(element::random_element()));
1831 points.push_back(affine_element(element::random_element()));
1832 std::vector<fr> scalars;
1833 scalars.push_back(0);
1834 scalars.push_back(1);
1835
1837 ASSERT_EQ(points.size(), scalars.size());
1838 const size_t num_points = points.size();
1839
1840 std::vector<element_ct> circuit_points;
1841 std::vector<scalar_ct> circuit_scalars;
1842 for (size_t i = 0; i < num_points; ++i) {
1843 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1844 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1845 }
1846
1847 element_ct result_point =
1848 element_ct::batch_mul(circuit_points, circuit_scalars, /*max_num_bits=*/0, /*with_edgecases=*/true);
1849
1850 element expected_point = points[1];
1851 expected_point = expected_point.normalize();
1852
1853 fq result_x(result_point.x().get_value().lo);
1854 fq result_y(result_point.y().get_value().lo);
1855
1856 EXPECT_EQ(result_x, expected_point.x);
1857 EXPECT_EQ(result_y, expected_point.y);
1858
1860 }
1861 }
1862
1863 // Test batch_mul with all points at infinity
1865 {
1868 std::vector<fr> scalars;
1869
1870 for (size_t i = 0; i < 5; ++i) {
1871 points.push_back(affine_element::infinity());
1872 scalars.push_back(fr::random_element());
1873 }
1874
1875 std::vector<element_ct> circuit_points;
1876 std::vector<scalar_ct> circuit_scalars;
1877
1878 for (size_t i = 0; i < points.size(); ++i) {
1879 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1880 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1881 }
1882
1883 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars, 0, true);
1884
1885 // Result should be point at infinity
1886 EXPECT_TRUE(is_infinity(result));
1888 }
1889
1890 // Test batch_mul with all zero scalars
1892 {
1895 std::vector<fr> scalars;
1896
1897 for (size_t i = 0; i < 5; ++i) {
1898 points.push_back(affine_element(element::random_element()));
1899 scalars.push_back(fr::zero());
1900 }
1901
1902 std::vector<element_ct> circuit_points;
1903 std::vector<scalar_ct> circuit_scalars;
1904
1905 for (size_t i = 0; i < points.size(); ++i) {
1906 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1907 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1908 }
1909
1910 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars, 0, true);
1911
1912 // Result should be point at infinity
1913 EXPECT_TRUE(is_infinity(result));
1915 }
1916
1917 // Test batch_mul with mixed zero and non-zero scalars
1919 {
1922 std::vector<fr> scalars;
1923
1924 for (size_t i = 0; i < 6; ++i) {
1925 points.push_back(affine_element(element::random_element()));
1926 // Alternate between zero and non-zero scalars
1927 scalars.push_back((i % 2 == 0) ? fr::zero() : fr::random_element());
1928 }
1929
1930 std::vector<element_ct> circuit_points;
1931 std::vector<scalar_ct> circuit_scalars;
1932
1933 for (size_t i = 0; i < points.size(); ++i) {
1934 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
1935 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
1936 }
1937
1938 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars, 0, true);
1939
1940 // Compute expected result
1941 element expected = element::infinity();
1942 for (size_t i = 0; i < points.size(); ++i) {
1943 expected += (element(points[i]) * scalars[i]);
1944 }
1945 affine_element expected_affine = affine_element(expected);
1946
1947 uint256_t result_x = result.x().get_value().lo;
1948 uint256_t result_y = result.y().get_value().lo;
1949
1950 EXPECT_EQ(fq(result_x), expected_affine.x);
1951 EXPECT_EQ(fq(result_y), expected_affine.y);
1952
1954 }
1955
1956 // Regression test: the batch_mul partition must not depend on witness values. Build the
1957 // same source program with two scalar assignments (all non-zero vs. alternating zeros) and
1958 // assert the resulting execution-trace blocks (selectors + wire indices) are identical.
1960 {
1961 constexpr size_t max_num_bits = 128;
1962 constexpr size_t num_points = 4;
1963
1964 std::vector<affine_element> input_points;
1965 std::array<std::vector<fr>, 2> scalar_assignments;
1966 for (size_t i = 0; i < num_points; ++i) {
1967 input_points.push_back(affine_element(element::random_element()));
1968 const uint256_t s_raw = engine.get_random_uint256() >> (256 - max_num_bits);
1969 scalar_assignments[0].push_back(fr(s_raw));
1970 scalar_assignments[1].push_back((i % 2 == 0) ? fr::zero() : fr(s_raw));
1971 }
1972
1973 std::array<Builder, 2> builders;
1974 for (size_t k = 0; k < 2; ++k) {
1975 Builder& builder = builders[k];
1976 std::vector<element_ct> circuit_points;
1977 std::vector<scalar_ct> circuit_scalars;
1978 for (size_t i = 0; i < num_points; ++i) {
1979 circuit_points.push_back(element_ct::from_witness(&builder, input_points[i]));
1980 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalar_assignments[k][i]));
1981 }
1982
1983 element_ct circuit_result =
1984 element_ct::batch_mul(circuit_points, circuit_scalars, max_num_bits, /*with_edgecases=*/false);
1985
1986 element expected = element::infinity();
1987 for (size_t i = 0; i < num_points; ++i) {
1988 expected += (element(input_points[i]) * scalar_assignments[k][i]);
1989 }
1990 affine_element expected_affine = affine_element(expected);
1991 if (expected_affine.is_point_at_infinity()) {
1992 EXPECT_TRUE(is_infinity(circuit_result));
1993 } else {
1994 const uint256_t result_x = circuit_result.x().get_value().lo;
1995 const uint256_t result_y = circuit_result.y().get_value().lo;
1996 EXPECT_EQ(fq(result_x), expected_affine.x);
1997 EXPECT_EQ(fq(result_y), expected_affine.y);
1998 }
2000 }
2001
2002 // Ensure the blocks are equal for both builders
2003 EXPECT_EQ(builders[0].blocks, builders[1].blocks);
2004 }
2005
2006 // Test batch_mul with mixed infinity and valid points
2008 {
2011 std::vector<fr> scalars;
2012
2013 for (size_t i = 0; i < 6; ++i) {
2014 // Alternate between infinity and valid points
2015 points.push_back((i % 2 == 0) ? affine_element::infinity() : affine_element(element::random_element()));
2016 scalars.push_back(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 if (!points[i].is_point_at_infinity()) {
2033 expected += (element(points[i]) * scalars[i]);
2034 }
2035 }
2036 affine_element expected_affine = affine_element(expected);
2037
2038 uint256_t result_x = result.x().get_value().lo;
2039 uint256_t result_y = result.y().get_value().lo;
2040
2041 EXPECT_EQ(fq(result_x), expected_affine.x);
2042 EXPECT_EQ(fq(result_y), expected_affine.y);
2043
2045 }
2046
2047 // Test batch_mul with points that cancel out
2049 {
2052 std::vector<fr> scalars;
2053
2054 // Add P and -P with same scalar
2055 affine_element P(element::random_element());
2057 fr scalar = fr::random_element();
2058
2059 points.push_back(P);
2060 scalars.push_back(scalar);
2061 points.push_back(neg_P);
2062 scalars.push_back(scalar);
2063
2064 // Add some other points to make it non-trivial
2065 for (size_t i = 0; i < 3; ++i) {
2066 points.push_back(affine_element(element::random_element()));
2067 scalars.push_back(fr::random_element());
2068 }
2069
2070 std::vector<element_ct> circuit_points;
2071 std::vector<scalar_ct> circuit_scalars;
2072
2073 for (size_t i = 0; i < points.size(); ++i) {
2074 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
2075 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
2076 }
2077
2078 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars, 0, true);
2079
2080 // Compute expected result
2081 element expected = element::infinity();
2082 for (size_t i = 0; i < points.size(); ++i) {
2083 expected += (element(points[i]) * scalars[i]);
2084 }
2085 affine_element expected_affine = affine_element(expected);
2086
2087 uint256_t result_x = result.x().get_value().lo;
2088 uint256_t result_y = result.y().get_value().lo;
2089
2090 EXPECT_EQ(fq(result_x), expected_affine.x);
2091 EXPECT_EQ(fq(result_y), expected_affine.y);
2092
2094 }
2095
2096 // Test batch_mul with constant and witness points mixed
2098 {
2100 std::vector<affine_element> points_native;
2101 std::vector<fr> scalars_native;
2102 std::vector<element_ct> circuit_points;
2103 std::vector<scalar_ct> circuit_scalars;
2104
2105 // Add constant-constant points
2106 for (size_t i = 0; i < 3; ++i) {
2107 const auto [point, point_ct] = get_random_point(&builder, InputType::CONSTANT);
2108 const auto [scalar, scalar_ct] = get_random_scalar(&builder, InputType::CONSTANT);
2109 points_native.push_back(point);
2110 scalars_native.push_back(scalar);
2111 circuit_points.push_back(point_ct); // Constant
2112 circuit_scalars.push_back(scalar_ct); // Constant
2113 }
2114
2115 // Add witness-witness points
2116 for (size_t i = 0; i < 3; ++i) {
2117 const auto [point, point_ct] = get_random_point(&builder, InputType::WITNESS);
2118 const auto [scalar, scalar_ct] = get_random_scalar(&builder, InputType::WITNESS);
2119 points_native.push_back(point);
2120 scalars_native.push_back(scalar);
2121 circuit_points.push_back(point_ct); // Witness
2122 circuit_scalars.push_back(scalar_ct); // Witness
2123 }
2124
2125 // Add constant-witness points
2126 for (size_t i = 0; i < 4; ++i) {
2127 const auto [point, point_ct] = get_random_point(&builder, InputType::CONSTANT);
2128 const auto [scalar, scalar_ct] = get_random_scalar(&builder, InputType::WITNESS);
2129 points_native.push_back(point);
2130 scalars_native.push_back(scalar);
2131 circuit_points.push_back(element_ct(point.x, point.y)); // Constant
2132 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalar)); // Witness
2133 }
2134
2135 // Add witness-constant points
2136 for (size_t i = 0; i < 4; ++i) {
2137 const auto [point, point_ct] = get_random_point(&builder, InputType::WITNESS);
2138 const auto [scalar, scalar_ct] = get_random_scalar(&builder, InputType::CONSTANT);
2139 points_native.push_back(point);
2140 scalars_native.push_back(scalar);
2141 circuit_points.push_back(point_ct); // Witness
2142 circuit_scalars.push_back(scalar_ct); // Constant
2143 }
2144
2145 element_ct result = element_ct::batch_mul(circuit_points, circuit_scalars);
2146
2147 // Compute expected result
2148 element expected = element::infinity();
2149 for (size_t i = 0; i < points_native.size(); ++i) {
2150 expected += (element(points_native[i]) * scalars_native[i]);
2151 }
2152 affine_element expected_affine = affine_element(expected);
2153
2154 uint256_t result_x = result.x().get_value().lo;
2155 uint256_t result_y = result.y().get_value().lo;
2156
2157 EXPECT_EQ(fq(result_x), expected_affine.x);
2158 EXPECT_EQ(fq(result_y), expected_affine.y);
2159
2161 }
2162
2163 // Test batch_mul with large number of points (stress test)
2165 {
2168 std::vector<fr> scalars;
2169 constexpr size_t num_points = 20;
2170
2171 for (size_t i = 0; i < num_points; ++i) {
2172 points.push_back(affine_element(element::random_element()));
2173 scalars.push_back(fr::random_element());
2174 }
2175
2176 std::vector<element_ct> circuit_points;
2177 std::vector<scalar_ct> circuit_scalars;
2178
2179 for (size_t i = 0; i < points.size(); ++i) {
2180 circuit_points.push_back(element_ct::from_witness(&builder, points[i]));
2181 circuit_scalars.push_back(scalar_ct::from_witness(&builder, scalars[i]));
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.size(); ++i) {
2189 expected += (element(points[i]) * scalars[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 that infinity representation is canonical (x=0, y=0) after all operations
2204 {
2206
2207 // Case 1: constant_infinity() returns canonical form
2208 {
2209 element_ct inf = element_ct::constant_infinity(&builder);
2210 EXPECT_TRUE(is_infinity(inf));
2211 // Verify coordinates are (0, 0)
2212 EXPECT_EQ(fq(inf.x().get_value().lo), fq(0));
2213 EXPECT_EQ(fq(inf.y().get_value().lo), fq(0));
2214 }
2215
2216 // Case 2: P + (-P) = infinity with canonical coords
2217 {
2218 affine_element input(element::random_element());
2219 element_ct P = element_ct::from_witness(&builder, input);
2220 element_ct neg_P = -P;
2221 element_ct result = P + neg_P;
2222
2223 EXPECT_TRUE(is_infinity(result));
2224 // After standardization, coordinates should be (0, 0)
2225 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2226 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2227 }
2228
2229 // Case 3: P - P = infinity with canonical coords
2230 {
2231 affine_element input(element::random_element());
2232 element_ct P = element_ct::from_witness(&builder, input);
2233 element_ct result = P - P;
2234
2235 EXPECT_TRUE(is_infinity(result));
2236 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2237 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2238 }
2239
2240 // Case 4: infinity + infinity = infinity with canonical coords
2241 {
2242 element_ct inf1 = element_ct::constant_infinity(&builder);
2243 element_ct inf2 = element_ct::constant_infinity(&builder);
2244 element_ct result = inf1 + inf2;
2245
2246 EXPECT_TRUE(is_infinity(result));
2247 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2248 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2249 }
2250
2251 // Case 5: 2 * infinity = infinity with canonical coords
2252 {
2253 element_ct inf = element_ct::constant_infinity(&builder);
2254 element_ct result = inf.dbl();
2255
2256 EXPECT_TRUE(is_infinity(result));
2257 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2258 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2259 }
2260
2262 }
2263
2264 // Test chained operations involving infinity
2266 {
2268
2269 // (a + infinity) - a = infinity
2270 {
2271 affine_element input(element::random_element());
2272 element_ct a = element_ct::from_witness(&builder, input);
2273 element_ct inf = element_ct::constant_infinity(&builder);
2274
2275 element_ct temp = a + inf;
2276 element_ct result = temp - a;
2277
2278 EXPECT_TRUE(is_infinity(result));
2279 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2280 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2281 }
2282
2283 // a + (b - b) = a
2284 {
2285 affine_element input_a(element::random_element());
2286 affine_element input_b(element::random_element());
2287 element_ct a = element_ct::from_witness(&builder, input_a);
2288 element_ct b = element_ct::from_witness(&builder, input_b);
2289
2290 element_ct zero = b - b; // Should be infinity
2291 element_ct result = a + zero;
2292
2293 // Result should equal a
2294 EXPECT_EQ(fq(result.x().get_value().lo), input_a.x);
2295 EXPECT_EQ(fq(result.y().get_value().lo), input_a.y);
2296 EXPECT_FALSE(is_infinity(result));
2297 }
2298
2299 // (infinity - infinity) + a = a
2300 {
2301 affine_element input(element::random_element());
2302 element_ct a = element_ct::from_witness(&builder, input);
2303 element_ct inf1 = element_ct::constant_infinity(&builder);
2304 element_ct inf2 = element_ct::constant_infinity(&builder);
2305
2306 element_ct zero = inf1 - inf2;
2307 element_ct result = zero + a;
2308
2309 EXPECT_EQ(fq(result.x().get_value().lo), input.x);
2310 EXPECT_EQ(fq(result.y().get_value().lo), input.y);
2311 }
2312
2314 }
2315
2316 // Test conditional_select with infinity points
2318 {
2320
2321 affine_element input_a(element::random_element());
2322 element_ct a = element_ct::from_witness(&builder, input_a);
2323 element_ct inf = element_ct::constant_infinity(&builder);
2324
2325 // Case 1: Select finite point when predicate is false
2326 {
2327 bool_ct pred(witness_ct(&builder, false));
2328 element_ct result = a.conditional_select(inf, pred);
2329
2330 EXPECT_FALSE(is_infinity(result));
2331 EXPECT_EQ(fq(result.x().get_value().lo), input_a.x);
2332 EXPECT_EQ(fq(result.y().get_value().lo), input_a.y);
2333 }
2334
2335 // Case 2: Select infinity when predicate is true
2336 {
2337 bool_ct pred(witness_ct(&builder, true));
2338 element_ct result = a.conditional_select(inf, pred);
2339
2340 EXPECT_TRUE(is_infinity(result));
2341 }
2342
2343 // Case 3: Select between two infinity points
2344 {
2345 element_ct inf2 = element_ct::constant_infinity(&builder);
2346 bool_ct pred(witness_ct(&builder, true));
2347 element_ct result = inf.conditional_select(inf2, pred);
2348
2349 EXPECT_TRUE(is_infinity(result));
2350 }
2351
2353 }
2354
2355 // Test conditional_negate with infinity
2357 {
2359
2360 element_ct inf = element_ct::constant_infinity(&builder);
2361
2362 // Negating infinity should still be infinity
2363 {
2364 bool_ct pred(witness_ct(&builder, true));
2365 element_ct result = inf.conditional_negate(pred);
2366
2367 EXPECT_TRUE(is_infinity(result));
2368 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2369 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2370 }
2371
2372 // Not negating infinity should still be infinity
2373 {
2374 bool_ct pred(witness_ct(&builder, false));
2375 element_ct result = inf.conditional_negate(pred);
2376
2377 EXPECT_TRUE(is_infinity(result));
2378 }
2379
2381 }
2382
2383 // Test get_standard_form preserves canonical infinity representation
2385 {
2387
2388 // Use constant_infinity() factory to create canonical infinity with (0, 0) coordinates
2389 // Note: We no longer support non-canonical infinity representations (points with
2390 // random coords but is_infinity=true) through the public API
2391 element_ct P = element_ct::constant_infinity(&builder);
2392
2393 // Canonical infinity has (0, 0) coordinates
2394 EXPECT_EQ(fq(P.x().get_value().lo), fq(0));
2395 EXPECT_EQ(fq(P.y().get_value().lo), fq(0));
2396 EXPECT_TRUE(is_infinity(P));
2397
2398 // After standardization, coords should still be (0, 0)
2399 element_ct standardized = P.get_standard_form();
2400 EXPECT_TRUE(is_infinity(standardized));
2401 EXPECT_EQ(fq(standardized.x().get_value().lo), fq(0));
2402 EXPECT_EQ(fq(standardized.y().get_value().lo), fq(0));
2403
2405 }
2406
2407 // Test auto-detection of infinity in 2-argument constructor
2409 {
2411
2412 // Create element with (0, 0) coordinates - should auto-detect as infinity
2413 auto x_zero = element_ct::BaseField::from_witness(&builder, fq(0));
2414 auto y_zero = element_ct::BaseField::from_witness(&builder, fq(0));
2415
2416 element_ct point(x_zero, y_zero);
2417
2418 EXPECT_TRUE(is_infinity(point));
2419
2421 }
2422
2423 // Test scalar multiplication edge cases with infinity
2425 {
2427
2428 // Case 1: 0 * P = infinity
2429 {
2430 affine_element input(element::random_element());
2431 element_ct P = element_ct::from_witness(&builder, input);
2433
2434 element_ct result = P * zero;
2435 EXPECT_TRUE(is_infinity(result));
2436 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2437 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2438 }
2439
2440 // Case 2: k * infinity = infinity
2441 {
2442 element_ct inf = element_ct::constant_infinity(&builder);
2443 fr scalar_val = fr::random_element();
2444 scalar_ct k = scalar_ct::from_witness(&builder, scalar_val);
2445
2446 element_ct result = inf * k;
2447 EXPECT_TRUE(is_infinity(result));
2448 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2449 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2450 }
2451
2452 // Case 3: 0 * infinity = infinity
2453 {
2454 element_ct inf = element_ct::constant_infinity(&builder);
2456
2457 element_ct result = inf * zero;
2458 EXPECT_TRUE(is_infinity(result));
2459 }
2460
2462 }
2463
2464 // Test batch_mul where result cancels to infinity
2466 {
2468
2469 // P*a + Q*b + P*(-a) + Q*(-b) = infinity
2470 affine_element P(element::random_element());
2471 affine_element Q(element::random_element());
2474
2475 std::vector<element_ct> points = {
2476 element_ct::from_witness(&builder, P),
2477 element_ct::from_witness(&builder, Q),
2478 element_ct::from_witness(&builder, P),
2479 element_ct::from_witness(&builder, Q),
2480 };
2481
2486
2487 element_ct result = element_ct::batch_mul(points, scalars, 0, true);
2488
2489 EXPECT_TRUE(is_infinity(result));
2490 EXPECT_EQ(fq(result.x().get_value().lo), fq(0));
2491 EXPECT_EQ(fq(result.y().get_value().lo), fq(0));
2492
2494 }
2495
2496 // Test addition with constant infinity
2498 {
2500
2501 // P + constant_infinity = P
2502 affine_element input(element::random_element());
2503 element_ct P = element_ct::from_witness(&builder, input);
2504 element_ct const_inf = element_ct::constant_infinity(&builder); // This is a constant
2505
2506 element_ct result = P + const_inf;
2507
2508 EXPECT_FALSE(is_infinity(result));
2509 EXPECT_EQ(fq(result.x().get_value().lo), input.x);
2510 EXPECT_EQ(fq(result.y().get_value().lo), input.y);
2511
2512 // constant_infinity + P = P
2513 element_ct result2 = const_inf + P;
2514 EXPECT_FALSE(is_infinity(result2));
2515 EXPECT_EQ(fq(result2.x().get_value().lo), input.x);
2516 EXPECT_EQ(fq(result2.y().get_value().lo), input.y);
2517
2519 }
2520
2521 // Test that witness infinity points (created via operations) work correctly
2523 {
2525
2526 // Create infinity as P - P (witness-based infinity)
2527 affine_element input(element::random_element());
2528 element_ct P = element_ct::from_witness(&builder, input);
2529 element_ct witness_inf = P - P;
2530
2531 // Use this witness infinity in operations
2532 affine_element input2(element::random_element());
2533 element_ct Q = element_ct::from_witness(&builder, input2);
2534
2535 // Q + witness_inf = Q
2536 element_ct result = Q + witness_inf;
2537 EXPECT_EQ(fq(result.x().get_value().lo), input2.x);
2538 EXPECT_EQ(fq(result.y().get_value().lo), input2.y);
2539
2540 // witness_inf + Q = Q
2541 element_ct result2 = witness_inf + Q;
2542 EXPECT_EQ(fq(result2.x().get_value().lo), input2.x);
2543 EXPECT_EQ(fq(result2.y().get_value().lo), input2.y);
2544
2546 }
2547};
2548
2549// bn254 with ultra arithmetisation where scalar field is native field, base field is non-native field (bigfield)
2552
2553// bn254 with ultra arithmetisation where both scalar and base fields are non-native fields
2556 true>;
2557
2558// bn254 with mega arithmetisation where scalar field is native field, base field is non-native field
2561
2562// secp256r1 with ultra arithmetisation where both scalar and base fields are (naturally) non-native fields
2565 false>;
2566
2567// secp256k1 with ultra arithmetisation where both scalar and base fields are (naturally) non-native fields
2570 false>;
2571
2572using TestTypes = testing::Types<bn254_with_ultra,
2577
2579
2580TYPED_TEST(stdlib_biggroup, validate_on_curve)
2581{
2583 // Goblin points do not implement validate on curve
2584 if constexpr (!HasGoblinBuilder<TypeParam>) {
2585 using Builder = TestFixture::Builder;
2586 using element_ct = TestFixture::element_ct;
2587 using Fq = TestFixture::Curve::BaseField;
2588 using FqNative = TestFixture::Curve::BaseFieldNative;
2589 using GroupNative = TestFixture::Curve::GroupNative;
2590
2592 auto [native_point, witness_point] = TestFixture::get_random_witness_point(&builder);
2593
2594 // Valid point
2595 Fq expected_zero = witness_point.validate_on_curve("biggroup::validate_on_curve", false);
2596 expected_zero.assert_equal(Fq::zero());
2597 EXPECT_EQ(expected_zero.get_value(), static_cast<uint512_t>(FqNative::zero()));
2598
2599 // Invalid point
2600 Fq random_x = Fq::from_witness(&builder, FqNative::random_element());
2601 Fq random_y = Fq::from_witness(&builder, FqNative::random_element());
2602 element_ct invalid_point(random_x, random_y, /*assert_on_curve*/ false);
2603 Fq expected_non_zero = invalid_point.validate_on_curve("biggroup::validate_on_curve", false);
2604 Fq expected_value = -random_y.sqr() + random_x.pow(3) + Fq(uint256_t(GroupNative::curve_b));
2605 if constexpr (GroupNative::has_a) {
2606 expected_value += random_x * Fq(uint256_t(GroupNative::curve_a));
2607 }
2608 expected_non_zero.assert_equal(expected_value);
2609
2610 // Reduce the value to remove constants
2611 expected_non_zero.self_reduce();
2612 expected_value.self_reduce();
2613 EXPECT_EQ(expected_non_zero.get_value(), expected_value.get_value());
2614
2615 TestFixture::EXPECT_CIRCUIT_CORRECTNESS(builder);
2616
2617 // Check that the circuit fails if validate_on_curve is called with default parameters
2618 [[maybe_unused]] Fq _ = invalid_point.validate_on_curve();
2619 TestFixture::EXPECT_CIRCUIT_CORRECTNESS(builder, false);
2620 }
2621}
2622
2624{
2625 TestFixture::test_basic_tag_logic();
2626}
2627
2628TYPED_TEST(stdlib_biggroup, assert_coordinates_in_field)
2629{
2630 TestFixture::test_assert_coordinates_in_field();
2631}
2632
2633// Addition tests
2635{
2636 TestFixture::test_add();
2637}
2638TYPED_TEST(stdlib_biggroup, add_with_constants)
2639{
2640 TestFixture::test_add(InputType::WITNESS, InputType::CONSTANT); // w + c
2641 TestFixture::test_add(InputType::CONSTANT, InputType::WITNESS); // c + w
2642 TestFixture::test_add(InputType::CONSTANT, InputType::CONSTANT); // c + c
2643}
2644TYPED_TEST(stdlib_biggroup, add_points_at_infinity)
2645{
2646 TestFixture::test_add_points_at_infinity();
2647}
2648TYPED_TEST(stdlib_biggroup, standard_form_of_point_at_infinity)
2649{
2650 TestFixture::test_standard_form_of_point_at_infinity();
2651}
2652
2653// Subtraction tests
2655{
2656 TestFixture::test_sub();
2657}
2658TYPED_TEST(stdlib_biggroup, sub_with_constants)
2659{
2660 TestFixture::test_sub(InputType::WITNESS, InputType::CONSTANT); // w - c
2661 TestFixture::test_sub(InputType::CONSTANT, InputType::WITNESS); // c - w
2662 TestFixture::test_sub(InputType::CONSTANT, InputType::CONSTANT); // c - c
2663}
2664TYPED_TEST(stdlib_biggroup, sub_points_at_infinity)
2665{
2666 TestFixture::test_sub_points_at_infinity();
2667}
2669{
2670 TestFixture::test_dbl();
2671}
2672TYPED_TEST(stdlib_biggroup, dbl_with_constant)
2673{
2674 TestFixture::test_dbl(InputType::CONSTANT); // dbl(c)
2675}
2676TYPED_TEST(stdlib_biggroup, dbl_with_infinity)
2677{
2678 TestFixture::test_dbl_with_infinity();
2679}
2681{
2682 if constexpr (HasGoblinBuilder<TypeParam>) {
2683 GTEST_SKIP() << "mega builder does not support this edge case";
2684 } else {
2685 TestFixture::test_dbl_with_y_zero();
2686 }
2687}
2689{
2690 TestFixture::test_add_equals_dbl();
2691}
2692TYPED_TEST(stdlib_biggroup, sub_neg_equals_double)
2693{
2694 TestFixture::test_sub_neg_equals_double();
2695}
2696
2697// Test chain_add
2699{
2700 if constexpr (HasGoblinBuilder<TypeParam>) {
2701 GTEST_SKIP() << "mega builder does not implement chain_add function";
2702 } else {
2703 TestFixture::test_chain_add();
2704 };
2705}
2706HEAVY_TYPED_TEST(stdlib_biggroup, chain_add_with_constants)
2707{
2708 if constexpr (HasGoblinBuilder<TypeParam>) {
2709 GTEST_SKIP() << "mega builder does not implement chain_add function";
2710 } else {
2711 TestFixture::test_chain_add(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w, w, c
2712 TestFixture::test_chain_add(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // w, c, w
2713 TestFixture::test_chain_add(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // w, c, c
2714 TestFixture::test_chain_add(InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS); // c, w, w
2715 TestFixture::test_chain_add(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // c, w, c
2716 TestFixture::test_chain_add(InputType::CONSTANT, InputType::CONSTANT, InputType::WITNESS); // c, c, w
2717 TestFixture::test_chain_add(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT); // c, c, c
2718 }
2719}
2720
2721// Test multiple_montgomery_ladder
2722HEAVY_TYPED_TEST(stdlib_biggroup, multiple_montgomery_ladder)
2723{
2724
2725 if constexpr (HasGoblinBuilder<TypeParam>) {
2726 GTEST_SKIP() << "mega builder does not implement multiple_montgomery_ladder function";
2727 } else {
2728 TestFixture::test_multiple_montgomery_ladder();
2729 };
2730}
2731
2732// Test normalize
2734{
2735 TestFixture::test_normalize();
2736}
2737TYPED_TEST(stdlib_biggroup, normalize_constant)
2738{
2739 TestFixture::test_normalize(InputType::CONSTANT);
2740}
2741
2742// Test reduce
2744{
2745 TestFixture::test_reduce();
2746}
2748{
2749 TestFixture::test_reduce(InputType::CONSTANT);
2750}
2751
2752// Test unary negation
2754{
2755 TestFixture::test_unary_negate(InputType::WITNESS);
2756}
2757
2758TYPED_TEST(stdlib_biggroup, unary_negate_with_constants)
2759{
2760 TestFixture::test_unary_negate(InputType::CONSTANT);
2761}
2762
2763// Test operator+=
2765{
2766 TestFixture::test_add_assign(InputType::WITNESS, InputType::WITNESS);
2767}
2768
2769TYPED_TEST(stdlib_biggroup, add_assign_with_constants)
2770{
2771 TestFixture::test_add_assign(InputType::WITNESS, InputType::CONSTANT); // w += c
2772 TestFixture::test_add_assign(InputType::CONSTANT, InputType::WITNESS); // c += w
2773}
2774
2775// Test operator-=
2777{
2778 TestFixture::test_sub_assign(InputType::WITNESS, InputType::WITNESS);
2779}
2780TYPED_TEST(stdlib_biggroup, sub_assign_with_constants)
2781{
2782 TestFixture::test_sub_assign(InputType::WITNESS, InputType::CONSTANT); // w -= c
2783 TestFixture::test_sub_assign(InputType::CONSTANT, InputType::WITNESS); // c -= w
2784}
2785// Test checked_unconditional_add
2786TYPED_TEST(stdlib_biggroup, checked_unconditional_add)
2787{
2788 TestFixture::test_checked_unconditional_add(InputType::WITNESS, InputType::WITNESS);
2789}
2790TYPED_TEST(stdlib_biggroup, checked_unconditional_add_with_constants)
2791{
2792 TestFixture::test_checked_unconditional_add(InputType::WITNESS, InputType::CONSTANT); // w + c
2793 TestFixture::test_checked_unconditional_add(InputType::CONSTANT, InputType::WITNESS); // c + w
2794 TestFixture::test_checked_unconditional_add(InputType::CONSTANT, InputType::CONSTANT); // c + c
2795}
2796// Test checked_unconditional_subtract
2797TYPED_TEST(stdlib_biggroup, checked_unconditional_subtract)
2798{
2799 TestFixture::test_checked_unconditional_subtract(InputType::WITNESS, InputType::WITNESS);
2800}
2801TYPED_TEST(stdlib_biggroup, checked_unconditional_subtract_with_constants)
2802{
2803 TestFixture::test_checked_unconditional_subtract(InputType::WITNESS, InputType::CONSTANT); // w - c
2804 TestFixture::test_checked_unconditional_subtract(InputType::CONSTANT, InputType::WITNESS); // c - w
2805 TestFixture::test_checked_unconditional_subtract(InputType::CONSTANT, InputType::CONSTANT); // c - c
2806}
2807// Test checked_unconditional_add_sub
2808TYPED_TEST(stdlib_biggroup, checked_unconditional_add_sub)
2809{
2810 TestFixture::test_checked_unconditional_add_sub();
2811}
2812TYPED_TEST(stdlib_biggroup, checked_unconditional_add_sub_with_constants)
2813{
2814 TestFixture::test_checked_unconditional_add_sub(InputType::WITNESS, InputType::CONSTANT); // w, c
2815 TestFixture::test_checked_unconditional_add_sub(InputType::CONSTANT, InputType::WITNESS); // c, w
2816 TestFixture::test_checked_unconditional_add_sub(InputType::CONSTANT, InputType::CONSTANT); // c, c
2817}
2818// Test conditional_negate
2819TYPED_TEST(stdlib_biggroup, conditional_negate)
2820{
2821 TestFixture::test_conditional_negate();
2822}
2823TYPED_TEST(stdlib_biggroup, conditional_negate_with_constants)
2824{
2825 TestFixture::test_conditional_negate(InputType::WITNESS, InputType::CONSTANT); // w, c
2826 TestFixture::test_conditional_negate(InputType::CONSTANT, InputType::WITNESS); // c, w
2827 TestFixture::test_conditional_negate(InputType::CONSTANT, InputType::CONSTANT); // c, c
2828}
2829// Test conditional_select
2830TYPED_TEST(stdlib_biggroup, conditional_select)
2831{
2832 TestFixture::test_conditional_select();
2833}
2834TYPED_TEST(stdlib_biggroup, conditional_select_with_constants)
2835{
2836 TestFixture::test_conditional_select(InputType::WITNESS, InputType::WITNESS, InputType::CONSTANT); // w, w, c
2837 TestFixture::test_conditional_select(InputType::WITNESS, InputType::CONSTANT, InputType::WITNESS); // w, c, w
2838 TestFixture::test_conditional_select(InputType::WITNESS, InputType::CONSTANT, InputType::CONSTANT); // w, c, c
2839 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::WITNESS, InputType::WITNESS); // c, w, w
2840 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::CONSTANT, InputType::WITNESS); // c, c, w
2841 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::WITNESS, InputType::CONSTANT); // c, w, c
2842 TestFixture::test_conditional_select(InputType::CONSTANT, InputType::CONSTANT, InputType::CONSTANT); // c, c, c
2843}
2844TYPED_TEST(stdlib_biggroup, incomplete_assert_equal)
2845{
2846 TestFixture::test_incomplete_assert_equal();
2847}
2848TYPED_TEST(stdlib_biggroup, incomplete_assert_equal_fails)
2849{
2850 TestFixture::test_incomplete_assert_equal_failure();
2851}
2852
2854{
2855 if constexpr (!HasGoblinBuilder<TypeParam>) {
2856 size_t num_repetitions = 1;
2857 for (size_t i = 0; i < num_repetitions; i++) {
2858 TestFixture::test_compute_naf();
2859 }
2860 } else {
2861 GTEST_SKIP() << "mega builder does not implement compute_naf function";
2862 }
2863}
2864
2866{
2867 if constexpr (!HasGoblinBuilder<TypeParam>) {
2868 TestFixture::test_compute_naf_zero();
2869 } else {
2870 GTEST_SKIP() << "mega builder does not implement compute_naf function";
2871 }
2872}
2873
2874HEAVY_TYPED_TEST(stdlib_biggroup, compute_naf_overflow_lower_half)
2875{
2876 if constexpr (!HasGoblinBuilder<TypeParam>) {
2877 TestFixture::test_compute_naf_overflow_lower_half();
2878 } else {
2879 GTEST_SKIP() << "mega builder does not implement compute_naf function";
2880 }
2881}
2882
2883HEAVY_TYPED_TEST(stdlib_biggroup, compute_naf_top_bit_rejects_malicious_witness)
2884{
2885 if constexpr (!HasGoblinBuilder<TypeParam>) {
2886 TestFixture::test_compute_naf_top_bit_rejects_malicious_witness();
2887 } else {
2888 GTEST_SKIP() << "mega builder does not implement compute_naf function";
2889 }
2890}
2891
2892HEAVY_TYPED_TEST(stdlib_biggroup, compute_naf_witness_value_independence)
2893{
2894 if constexpr (!HasGoblinBuilder<TypeParam>) {
2895 TestFixture::test_compute_naf_witness_value_independence();
2896 } else {
2897 GTEST_SKIP() << "mega builder does not implement compute_naf function";
2898 }
2899}
2900
2902{
2903 TestFixture::test_mul();
2904}
2906{
2907 TestFixture::test_mul(InputType::WITNESS, InputType::CONSTANT); // w * c
2908 TestFixture::test_mul(InputType::CONSTANT, InputType::WITNESS); // c * w
2909 TestFixture::test_mul(InputType::CONSTANT, InputType::CONSTANT); // c * c
2910}
2912{
2913 TestFixture::test_mul_edge_cases();
2914}
2915HEAVY_TYPED_TEST(stdlib_biggroup, mul_edge_cases_with_constants)
2916{
2917 TestFixture::test_mul_edge_cases(InputType::WITNESS, InputType::CONSTANT); // w * c
2918 TestFixture::test_mul_edge_cases(InputType::CONSTANT, InputType::WITNESS); // c * w
2919 TestFixture::test_mul_edge_cases(InputType::CONSTANT, InputType::CONSTANT); // c * c
2920}
2921
2922HEAVY_TYPED_TEST(stdlib_biggroup, short_scalar_mul_with_bit_lengths)
2923{
2924 if constexpr (HasGoblinBuilder<TypeParam>) {
2925 GTEST_SKIP() << "mega builder does not implement scalar_mul function";
2926 } else {
2927 TestFixture::test_short_scalar_mul_with_bit_lengths();
2928 }
2929}
2930
2931HEAVY_TYPED_TEST(stdlib_biggroup, short_scalar_mul_infinity)
2932{
2933 if constexpr (HasGoblinBuilder<TypeParam>) {
2934 GTEST_SKIP() << "mega builder does not implement scalar_mul function";
2935 } else {
2936 TestFixture::test_short_scalar_mul_infinity();
2937 }
2938}
2939
2940// Batch multiplication tests
2941// 1 point - Base case only
2943{
2944 TestFixture::test_helper_batch_mul(1);
2945}
2946
2947// 2 points - Base case + flag variations + one constant mix
2949{
2950 TestFixture::test_helper_batch_mul(2);
2951}
2952HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_twin_short_scalars)
2953{
2954 TestFixture::test_helper_batch_mul(2, true); // short_scalars
2955}
2956HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_twin_with_edgecases)
2957{
2958 TestFixture::test_helper_batch_mul(2, false, true); // short_scalars, with_edgecases
2959}
2960HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_twin_short_scalars_with_edgecases)
2961{
2962 TestFixture::test_helper_batch_mul(2, true, true); // short_scalars, with_edgecases
2963}
2964HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_twin_mixed_constants)
2965{
2966 TestFixture::test_helper_batch_mul({ InputType::WITNESS, InputType::CONSTANT },
2968}
2969
2970// 3 points - Base case only
2972{
2973 TestFixture::test_helper_batch_mul(3);
2974}
2975
2976// 4 points - Base case only
2978{
2979 TestFixture::test_helper_batch_mul(4);
2980}
2981
2982// 5 points - Base case + edge case + short scalar + mixed constant
2984{
2985 TestFixture::test_helper_batch_mul(5);
2986}
2987HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_five_with_edgecases)
2988{
2989 TestFixture::test_helper_batch_mul(5, false, true); // short_scalars, with_edgecases
2990}
2991HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_five_short_scalars)
2992{
2993 TestFixture::test_helper_batch_mul(5, true); // short_scalars
2994}
2995HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_five_short_scalars_with_edgecases)
2996{
2997 TestFixture::test_helper_batch_mul(5, true, true); // short_scalars, with_edgecases
2998}
3005
3006// 6 points - Base case only
3008{
3009 TestFixture::test_helper_batch_mul(6);
3010}
3011
3013{
3014 TestFixture::test_twin_mul();
3015}
3016
3017HEAVY_TYPED_TEST(stdlib_biggroup, twin_mul_with_infinity)
3018{
3019 TestFixture::test_twin_mul_with_infinity();
3020}
3021
3022HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_linearly_dependent_generators)
3023{
3024 TestFixture::test_batch_mul_linearly_dependent_generators();
3025}
3026
3027HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_linearly_dependent_generators_failure)
3028{
3029 if constexpr (HasGoblinBuilder<TypeParam>) {
3030 GTEST_SKIP() << "this failure test is designed for ultra builder only";
3031 } else {
3032 TestFixture::test_batch_mul_linearly_dependent_generators_failure();
3033 }
3034}
3035
3036HEAVY_TYPED_TEST(stdlib_biggroup, offset_generator_infinity_is_rejected)
3037{
3038 if constexpr (HasGoblinBuilder<TypeParam>) {
3039 GTEST_SKIP() << "mask_points is only used on the ultra path";
3040 } else {
3041 TestFixture::test_offset_generator_infinity_is_rejected();
3042 }
3043}
3044
3045HEAVY_TYPED_TEST(stdlib_biggroup, masked_point_infinity_is_rejected)
3046{
3047 if constexpr (HasGoblinBuilder<TypeParam>) {
3048 GTEST_SKIP() << "mask_points is only used on the ultra path";
3049 } else {
3050 TestFixture::test_masked_point_infinity_is_rejected();
3051 }
3052}
3053
3055{
3056 TestFixture::test_one();
3057}
3058
3060{
3061 TestFixture::test_batch_mul();
3062}
3063
3064HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_edgecase_equivalence)
3065{
3066 TestFixture::test_batch_mul_edgecase_equivalence();
3067}
3068HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_edge_case_set1)
3069{
3070 TestFixture::test_batch_mul_edge_case_set1();
3071}
3072
3073HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_edge_case_set2)
3074{
3075 TestFixture::test_batch_mul_edge_case_set2();
3076}
3077
3078// Batch mul edge case tests
3079HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_all_infinity)
3080{
3081 TestFixture::test_batch_mul_all_infinity();
3082}
3083
3084HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_all_zero_scalars)
3085{
3086 TestFixture::test_batch_mul_all_zero_scalars();
3087}
3088
3089HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_mixed_zero_scalars)
3090{
3091 TestFixture::test_batch_mul_mixed_zero_scalars();
3092}
3093
3094HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_short_scalars_witness_value_independence)
3095{
3096 if constexpr (HasGoblinBuilder<TypeParam>) {
3097 GTEST_SKIP() << "mega builder uses goblin_element batch_mul; the witness-value partition is in the ultra path";
3098 } else {
3099 TestFixture::test_batch_mul_short_scalars_witness_value_independence();
3100 }
3101}
3102
3103HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_mixed_infinity)
3104{
3105 TestFixture::test_batch_mul_mixed_infinity();
3106}
3107
3108HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_cancellation)
3109{
3110 TestFixture::test_batch_mul_cancellation();
3111}
3112
3113HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_mixed_constant_witness)
3114{
3115 TestFixture::test_batch_mul_mixed_constant_witness();
3116}
3117
3118HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_large_number_of_points)
3119{
3120 TestFixture::test_batch_mul_large_number_of_points();
3121}
3122
3123// Point at Infinity Edge Case Tests
3124TYPED_TEST(stdlib_biggroup, infinity_canonical_representation)
3125{
3126 TestFixture::test_infinity_canonical_representation();
3127}
3128
3129TYPED_TEST(stdlib_biggroup, infinity_chained_operations)
3130{
3131 TestFixture::test_infinity_chained_operations();
3132}
3133
3134TYPED_TEST(stdlib_biggroup, conditional_select_with_infinity)
3135{
3136 TestFixture::test_conditional_select_with_infinity();
3137}
3138
3139TYPED_TEST(stdlib_biggroup, conditional_negate_with_infinity)
3140{
3141 TestFixture::test_conditional_negate_with_infinity();
3142}
3143
3144TYPED_TEST(stdlib_biggroup, get_standard_form_normalizes_infinity)
3145{
3146 TestFixture::test_get_standard_form_normalizes_infinity();
3147}
3148
3149TYPED_TEST(stdlib_biggroup, infinity_auto_detection_in_constructor)
3150{
3151 TestFixture::test_infinity_auto_detection_in_constructor();
3152}
3153
3154HEAVY_TYPED_TEST(stdlib_biggroup, scalar_mul_infinity_edge_cases)
3155{
3156 TestFixture::test_scalar_mul_infinity_edge_cases();
3157}
3158
3159HEAVY_TYPED_TEST(stdlib_biggroup, batch_mul_complete_cancellation)
3160{
3161 TestFixture::test_batch_mul_complete_cancellation();
3162}
3163
3164TYPED_TEST(stdlib_biggroup, add_constant_infinity)
3165{
3166 TestFixture::test_add_constant_infinity();
3167}
3168
3169TYPED_TEST(stdlib_biggroup, witness_infinity_from_operations)
3170{
3171 TestFixture::test_witness_infinity_from_operations();
3172}
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:224
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:33
InputType
TestType< stdlib::secp256r1< bb::UltraCircuitBuilder >, stdlib::secp256r1< bb::UltraCircuitBuilder >::ScalarField, false > secp256r1_with_ultra
TestType< stdlib::bn254< bb::UltraCircuitBuilder >, stdlib::bn254< bb::UltraCircuitBuilder >::ScalarField, false > bn254_with_ultra
TestType< stdlib::bn254< bb::UltraCircuitBuilder >, bb::stdlib::bigfield< bb::UltraCircuitBuilder, bb::Bn254FrParams >, true > bn254_with_ultra_scalar_bigfield
InputType
TestType< stdlib::bn254< bb::MegaCircuitBuilder >, stdlib::bn254< bb::MegaCircuitBuilder >::ScalarField, false > bn254_with_mega
constexpr InputType operator!(InputType type)
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
BB_INLINE constexpr void self_set_infinity() noexcept
group_elements::affine_element< Fq, Fr, Params > affine_element
Definition group.hpp:44
static constexpr element one
Definition group.hpp:48
group_elements::element< Fq, Fr, Params > element
Definition group.hpp:43
virtual uint8_t get_random_uint8()=0
virtual uint256_t get_random_uint256()=0
constexpr bool get_bit(uint64_t bit_index) const
constexpr uint64_t get_msb() const
static void set_limb_element(bigfield &bf, size_t i, const typename bigfield::field_ct &v)
void set_origin_tag(const bb::OriginTag &tag) const
Definition bigfield.hpp:716
static bigfield from_witness(Builder *ctx, const bb::field< T > &input)
Definition bigfield.hpp:322
static constexpr bool is_composite
Definition bigfield.hpp:355
Implements boolean logic in-circuit.
Definition bool.hpp:60
static auto checked_unconditional_add_sub(const element< C, Fq, Fr, G > &elem1, const element< C, Fq, Fr, G > &elem2)
Definition biggroup.hpp:994
static field_t from_witness(Builder *ctx, const bb::fr &input)
Definition field.hpp:480
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_masked_point_infinity_is_rejected()
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_compute_naf_top_bit_rejects_malicious_witness()
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_offset_generator_infinity_is_rejected()
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_batch_mul_short_scalars_witness_value_independence()
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()
static void test_compute_naf_witness_value_independence()
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
numeric::RNG & engine
secp256r1_ct::Group element_ct
uintx< uint256_t > uint512_t
Definition uintx.hpp:309
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:245
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
bb::stdlib::element< typename Curve::Builder, typename Curve::BaseField, ScalarField_, typename Curve::GroupNative > bigfield_element
std::conditional_t< use_bigfield, bigfield_element, typename Curve::Group > element_ct
ScalarField_ scalar_ct
static constexpr uint256_t modulus
BB_INLINE constexpr field pow(const uint256_t &exponent) const noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr field sqr() const noexcept
BB_INLINE constexpr field reduce() const noexcept
reduce once, i.e., if the value is bigger than the modulus, subtract off the modulus once.
static constexpr field zero()
#define HEAVY_TYPED_TEST(x, y)
Definition test.hpp:11
VectorField result