Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
biggroup.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Suyash], commit: 553c5eb82901955c638b943065acd3e47fc918c0}
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
8
9#include "../bigfield/bigfield.hpp"
10#include "../bigfield/goblin_field.hpp"
11#include "../byte_array/byte_array.hpp"
12#include "../circuit_builders/circuit_builders_fwd.hpp"
13#include "../field/field.hpp"
14#include "../field/field_utils.hpp"
15#include "../memory/rom_table.hpp"
16#include "../memory/twin_rom_table.hpp"
21#include <cstddef>
22
24
25// ( ͡° ͜ʖ ͡°)
26template <class Builder_, class Fq, class Fr, class NativeGroup> class element {
27 public:
28 using Builder = Builder_;
32 using biggroup_tag = element; // Facilitates a constexpr check IsBigGroup
33 using BaseField = Fq;
34
35 // Number of bb::fr field elements used to represent a goblin element in the public inputs
36 static constexpr size_t PUBLIC_INPUTS_SIZE = BIGGROUP_PUBLIC_INPUTS_SIZE;
37
38 element();
39
40 // Construct a biggroup element from its coordinates
41 // The infinity flag is automatically set based on whether both coordinates are zero.
42 // By default, we validate that the point is on the curve
43 element(const Fq& x, const Fq& y, const bool assert_on_curve = true);
44
45 element(const element& other);
46 element(element&& other) noexcept;
47
48 ~element() = default;
49
57 uint32_t set_public() const
58 {
59 auto standard = get_standard_form(); // if point is at infinity, ensure coordinates are (0,0).
60 const uint32_t start_idx = standard._x.set_public();
61 standard._y.set_public();
62
63 return start_idx;
64 }
65
74 static element from_witness(Builder* ctx, const typename NativeGroup::affine_element& input)
75 {
76 // By convention we set the coordinates of the point at infinity to (0,0).
77 Fq x;
78 Fq y;
79 if (input.is_point_at_infinity()) {
80 x = Fq::from_witness(ctx, typename NativeGroup::Fq(0));
81 y = Fq::from_witness(ctx, typename NativeGroup::Fq(0));
82 } else {
83 x = Fq::from_witness(ctx, input.x);
84 y = Fq::from_witness(ctx, input.y);
85 }
86
87 // Create _is_infinity as a witness with the actual infinity status.
88 // Security: Since assert_on_curve is true, validate_on_curve() enforces that if infinity=true, then x = y = 0.
89 bool_ct is_infinity = bool_ct(witness_ct(ctx, input.is_point_at_infinity()));
90
91 // Construct the biggroup element with private constructor to avoid redundant on-curve check
92 element out = element(x, y, is_infinity, /*assert_on_curve=*/true);
93
94 // Mark the element as coming out of nowhere
95 out.set_free_witness_tag();
96 return out;
97 }
98
102 Fq validate_on_curve(std::string const& msg = "biggroup::validate_on_curve", bool assert_is_on_curve = true) const
103 {
104 // Return early for constant inputs
105 if (this->is_constant()) {
106 BB_ASSERT(this->get_value().on_curve(), "biggroup::validate_on_curve: constant point not on curve");
107 return typename Fq::native(this->get_value().on_curve() ? 0 : 1);
108 }
109
110 // If this is a point at infinity, it must have (x, y) = (0, 0)
111 _x.assert_zero_if(is_point_at_infinity(), "biggroup::validate_on_curve: infinity point must have x = 0");
112 _y.assert_zero_if(is_point_at_infinity(), "biggroup::validate_on_curve: infinity point must have y = 0");
113
114 bool has_circuit_failed = get_context()->failed();
115 Fq result;
116
117 Fq b(get_context(), uint256_t(NativeGroup::curve_b));
118 Fq adjusted_b = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), b);
119 // If `assert_is_on_curve` is true, we validate y^2 = x^3 + b by setting "fix_remainder_zero = true" when
120 // calling mult_madd. Otherwise, we return the difference x^3 + ax + b - y^2
121 if constexpr (!NativeGroup::has_a) {
122 result = Fq::mult_madd({ _x.sqr(), _y }, { _x, -_y }, { adjusted_b }, assert_is_on_curve);
123 ;
124 } else {
125 Fq a(get_context(), uint256_t(NativeGroup::curve_a));
126 Fq adjusted_a = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), a);
127 result = Fq::mult_madd({ _x.sqr(), _x, _y }, { _x, adjusted_a, -_y }, { adjusted_b }, assert_is_on_curve);
128 }
129
130 if ((!has_circuit_failed) && (get_context()->failed())) {
131 vinfo("Original bigfield error generated by biggroup::validate_on_curve: ", get_context()->err());
132 get_context()->failure(msg);
133 }
134
135 return result;
136 }
137
138 [[nodiscard]] bool is_constant() const
139 {
140 const bool x_is_const = _x.is_constant();
141 const bool y_is_const = _y.is_constant();
142 BB_ASSERT_EQ(x_is_const, y_is_const, "biggroup: x and y coordinate constant status mismatch");
143 return x_is_const;
144 }
145
150 {
151 this->_x.convert_constant_to_fixed_witness(builder);
152 this->_y.convert_constant_to_fixed_witness(builder);
153 // Origin tags should be unset after fixing the witness
155 }
156
161 {
162 // Origin tags should be updated within
163 this->_x.fix_witness();
164 this->_y.fix_witness();
166
167 // This is now effectively a constant
169 }
170
174 static element one(Builder* ctx)
175 {
176 uint256_t x = uint256_t(NativeGroup::one.x);
177 uint256_t y = uint256_t(NativeGroup::one.y);
178 Fq x_fq(ctx, x);
179 Fq y_fq(ctx, y);
180 // Use private 4-arg constructor with explicit is_infinity=false (the generator is never infinity).
181 // This avoids the expensive bigfield equality checks in the 2-arg constructor's infinity auto-detection.
182 return element(x_fq, y_fq, bool_ct(ctx, false), /*assert_on_curve=*/false);
183 }
184
190 {
191 Fq x_fq(ctx, uint256_t(0));
192 Fq y_fq(ctx, uint256_t(0));
193 return element(x_fq, y_fq, /*is_infinity=*/bool_ct(ctx, true), /*assert_on_curve=*/false);
194 }
195
196 element& operator=(const element& other);
197 element& operator=(element&& other) noexcept;
198
199 element checked_unconditional_add(const element& other) const;
201
202 element operator+(const element& other) const;
203 element operator-(const element& other) const;
205 {
206 element result(*this);
207 result._y = -result._y;
208 return result;
209 }
211 {
212 *this = *this + other;
213 return *this;
214 }
216 {
217 *this = *this - other;
218 return *this;
219 }
220
221 element operator*(const Fr& scalar) const;
222
223 element conditional_negate(const bool_ct& predicate) const
224 {
225 element result(*this);
226 result._y = result._y.conditional_negate(predicate);
227 return result;
228 }
229
237 element conditional_select(const element& other, const bool_ct& predicate) const
238 {
239 // If predicate is constant, we can select out of circuit
240 if (predicate.is_constant()) {
241 auto result = predicate.get_value() ? other : *this;
243 OriginTag(predicate.get_origin_tag(), other.get_origin_tag(), this->get_origin_tag()));
244 return result;
245 }
246
247 // Get the builder context
248 Builder* ctx = validate_context<Builder>(get_context(), other.get_context(), predicate.get_context());
249 BB_ASSERT_NEQ(ctx, nullptr, "biggroup::conditional_select must have a context");
250
251 element result(*this);
252 result._x = result._x.conditional_select(other._x, predicate);
253 result._y = result._y.conditional_select(other._y, predicate);
254 result._is_infinity =
255 bool_ct::conditional_assign(predicate, other.is_point_at_infinity(), result.is_point_at_infinity());
256 return result;
257 }
258
259 static element conditional_assign(const bool_t<Builder>& predicate, const element& lhs, const element& rhs)
260 {
261 return rhs.conditional_select(lhs, predicate);
262 }
263
275 const std::string msg = "biggroup::incomplete_assert_equal") const
276 {
277 is_point_at_infinity().assert_equal(other.is_point_at_infinity(), msg + " (infinity flag)");
278 _x.assert_equal(other._x, msg + " (x coordinate)");
279 _y.assert_equal(other._y, msg + " (y coordinate)");
280 }
281
283 {
284 element result(*this);
285 result._x.reduce_mod_target_modulus();
286 result._y.reduce_mod_target_modulus();
287 return result;
288 }
289 element scalar_mul(const Fr& scalar, const size_t max_num_bits = 0) const;
290
292 {
293 element result(*this);
294 result._x.self_reduce();
295 result._y.self_reduce();
296 return result;
297 }
298
299 void assert_coordinates_in_field(const std::string& msg = "biggroup::assert_coordinates_in_field") const
300 {
301 _x.assert_is_in_field(msg + " (x coordinate)");
302 _y.assert_is_in_field(msg + " (y coordinate)");
303 }
304
305 element dbl() const;
306
307 // we use this data structure to add together a sequence of points.
308 // By tracking the previous values of x_1, y_1, \lambda, we can avoid
309 // computing the output y-coordinate of intermediate additions
316 bool is_full_element = false;
317
319 explicit chain_add_accumulator(const element& input)
320 : x3_prev(input._x)
321 , y3_prev(input._y)
322 , is_full_element(true)
323 {}
325 chain_add_accumulator(chain_add_accumulator&& other) noexcept = default;
329 };
330
342 static chain_add_accumulator chain_add_start(const element& p1, const element& p2);
343 static chain_add_accumulator chain_add(const element& p1, const chain_add_accumulator& accumulator);
344 static element chain_add_end(const chain_add_accumulator& accumulator);
346
347 typename NativeGroup::affine_element get_value() const
348 {
349 uint512_t x_val = _x.get_value() % Fq::modulus_u512;
350 uint512_t y_val = _y.get_value() % Fq::modulus_u512;
351 auto result = typename NativeGroup::affine_element(x_val.lo, y_val.lo);
353 result.self_set_infinity();
354 }
355 return result;
356 }
357
358 static element batch_mul(const std::vector<element>& points,
359 const std::vector<Fr>& scalars,
360 const size_t max_num_bits = 0,
361 const bool with_edgecases = true);
362
363 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, secp256k1::g1>::value>>
364 static element secp256k1_ecdsa_mul(const element& pubkey, const Fr& u1, const Fr& u2);
365
375
376 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, secp256r1::g1>::value>>
377 static Secp256r1EcdsaMulResult secp256r1_ecdsa_mul(const element& pubkey, const Fr& u1, const Fr& u2);
378
386 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, secp256r1::g1>::value>>
388
393 static std::vector<bool_ct> compute_naf(const Fr& scalar, const size_t max_num_bits = 0);
394
395 // Internal struct to represent wNAF for a secp256k1 scalar
402
403 // Internal struct to represent a pair of secp256k1 wNAFs
408
413 template <size_t wnaf_size, size_t staggered_lo_offset = 0, size_t staggered_hi_offset = 0>
414 static secp256k1_wnaf_pair compute_secp256k1_endo_wnaf(const Fr& scalar, const bool range_constrain_wnaf = true);
415
416 Builder* get_context() const { return validate_context<Builder>(_x.get_context(), _y.get_context()); }
417
418 Builder* get_context(const element& other) const
419 {
420 return validate_context<Builder>(get_context(), other.get_context());
421 }
422
423 // Coordinate accessors (non-owning, const reference)
424 const Fq& x() const { return _x; }
425 const Fq& y() const { return _y; }
426
429
431 {
432 _x.set_origin_tag(tag);
433 _y.set_origin_tag(tag);
435 }
436
438 {
439 _x.clear_round_provenance();
440 _y.clear_round_provenance();
442 }
443
445 {
446 return OriginTag(_x.get_origin_tag(), _y.get_origin_tag(), _is_infinity.get_origin_tag());
447 }
448
453 {
454 _x.unset_free_witness_tag();
455 _y.unset_free_witness_tag();
457 }
458
463 {
464 _x.set_free_witness_tag();
465 _y.set_free_witness_tag();
467 }
468
469 // For testing purposes only
471
472 private:
473 // Private constructor with explicit infinity flag control.
474 // Use public constructors or factory methods instead - they auto-detect infinity from coordinates.
475 element(const Fq& x, const Fq& y, const bool_ct& is_infinity, bool assert_on_curve);
476
480
481 // Internal implementations - may produce non-canonical infinity representation (efficient for chaining)
482 element add_internal(const element& other) const;
483 element subtract_internal(const element& other) const;
484 element dbl_internal() const;
486 const std::vector<Fr>& scalars,
487 const size_t max_num_bits = 0,
488 const bool with_edgecases = false);
489
495
505 static std::tuple<std::vector<element>, std::vector<Fr>, element> mask_points(const std::vector<element>& _points,
506 const std::vector<Fr>& _scalars);
507
517 const std::vector<element>& _points, const std::vector<Fr>& _scalars);
518
533 template <size_t num_bits, size_t wnaf_size, size_t lo_stagger, size_t hi_stagger>
535 const secp256k1::fr& scalar,
536 size_t stagger,
537 bool is_negative,
538 const bool range_constrain_wnaf = true,
539 bool is_lo = false);
540
550 template <size_t wnaf_size>
551 static std::pair<uint64_t, bool> get_staggered_wnaf_fragment_value(const uint64_t fragment_u64,
552 const uint64_t stagger,
553 bool is_negative,
554 bool wnaf_skew);
555
569 template <size_t wnaf_size>
571 const uint64_t* wnaf_values,
572 bool is_negative,
573 size_t rounds,
574 const bool range_constrain_wnaf = true);
575
587 template <size_t wnaf_size>
589 const std::vector<field_ct>& wnaf,
590 const bool_ct& positive_skew,
591 const bool_ct& negative_skew,
592 const field_ct& stagger_fragment,
593 const size_t stagger,
594 const size_t rounds);
595
596 template <size_t num_elements>
599
600 template <size_t num_elements>
601 static element read_group_element_rom_tables(const std::array<twin_rom_table<Builder>, Fq::NUM_LIMBS + 1>& tables,
602 const field_ct& index,
604
605 static std::pair<element, element> compute_offset_generators(const size_t num_rounds);
606 static typename NativeGroup::affine_element compute_table_offset_generator();
607
615 four_bit_table_plookup(const element& input);
616
622
623 element operator[](const field_ct& index) const;
624 element operator[](const size_t idx) const { return element_table[idx]; }
626
627 // Each coordinate is an Fq element, which has 4 binary basis limbs and 1 prime basis limb
629 std::array<uint256_t, Fq::NUM_LIMBS * 2> limb_max; // tracks the maximum size of each binary basis limb
630 };
631
640 eight_bit_fixed_base_table(const CurveType input_curve_type, bool use_endo)
641 : curve_type(input_curve_type)
642 , use_endomorphism(use_endo)
643 {}
644
650
651 element operator[](const field_ct& index) const;
652
653 element operator[](const size_t index) const;
654
657 };
658
660 const element& input);
661
666 template <size_t length> struct lookup_table_plookup {
667 static constexpr size_t table_size = (1ULL << (length));
672 lookup_table_plookup(lookup_table_plookup&& other) noexcept = default;
675
676 element get(const std::array<bool_ct, length>& bits) const;
677
678 element operator[](const size_t idx) const { return element_table[idx]; }
679
681
682 // Each coordinate is an Fq element, which has 4 binary basis limbs and 1 prime basis limb
683 // ROM tables: (idx, x0, x1), (idx, x2, x3), (idx, y0, y1), (idx, y2, y3), (idx, xp, yp)
686 };
687
689
691
693
700 {
701 quad_lookup_table base_table(inputs);
702 quad_lookup_table endo_table;
704 Fq beta(bb::fr(beta_val.slice(0, 136)), bb::fr(beta_val.slice(136, 256)), false);
705 for (size_t i = 0; i < 8; ++i) {
706 endo_table.element_table[i + 8].x = base_table[7 - i].x * beta;
707 endo_table.element_table[i + 8].y = base_table[7 - i].y;
708
709 endo_table.element_table[7 - i] = (-endo_table.element_table[i + 8]);
710 }
711
712 endo_table.coordinates = create_group_element_rom_tables<16>(endo_table.element_table, endo_table.limb_max);
713 return std::make_pair<quad_lookup_table, quad_lookup_table>(base_table, endo_table);
714 }
715
722 : num_points(points.size())
723 , num_fives(num_points / 5)
724 {
725 // size-6 table is expensive and only benefits us if creating them reduces the number of total tables
726 if (num_points == 1) {
727 num_fives = 0;
728 num_sixes = 0;
729 } else if (num_fives * 5 == (num_points - 1)) {
730 // last 6 points to be added as one 6-table
731 num_fives -= 1;
732 num_sixes = 1;
733 } else if (num_fives * 5 == (num_points - 2) && num_fives >= 2) {
734 // last 12 points to be added as two 6-tables
735 num_fives -= 2;
736 num_sixes = 2;
737 } else if (num_fives * 5 == (num_points - 3) && num_fives >= 3) {
738 // last 18 points to be added as three 6-tables
739 num_fives -= 3;
740 num_sixes = 3;
741 }
742
743 // Calculate remaining points after allocating fives and sixes tables
744 size_t remaining_points = num_points - (num_fives * 5 + num_sixes * 6);
745
746 // Allocate one quad table if required (and update remaining points)
747 has_quad = (remaining_points >= 4) && (num_points >= 4);
748 if (has_quad) {
749 remaining_points -= 4;
750 }
751
752 // Allocate one triple table if required (and update remaining points)
753 has_triple = (remaining_points >= 3) && (num_points >= 3);
754 if (has_triple) {
755 remaining_points -= 3;
756 }
757
758 // Allocate one twin table if required (and update remaining points)
759 has_twin = (remaining_points >= 2) && (num_points >= 2);
760 if (has_twin) {
761 remaining_points -= 2;
762 }
763
764 // If there is anything remaining, allocate a singleton
765 has_singleton = (remaining_points != 0) && (num_points >= 1);
766
767 // Sanity check
769 num_sixes * 6 + num_fives * 5 + static_cast<size_t>(has_quad) * 4 +
770 static_cast<size_t>(has_triple) * 3 + static_cast<size_t>(has_twin) * 2 +
771 static_cast<size_t>(has_singleton) * 1,
772 "point allocation mismatch");
773
774 size_t offset = 0;
775 for (size_t i = 0; i < num_sixes; ++i) {
777 points[offset + (6 * i)],
778 points[offset + (6 * i) + 1],
779 points[offset + (6 * i) + 2],
780 points[offset + (6 * i) + 3],
781 points[offset + (6 * i) + 4],
782 points[offset + (6 * i) + 5],
783 }));
784 }
785 offset += 6 * num_sixes;
786 for (size_t i = 0; i < num_fives; ++i) {
788 points[offset + (5 * i)],
789 points[offset + (5 * i) + 1],
790 points[offset + (5 * i) + 2],
791 points[offset + (5 * i) + 3],
792 points[offset + (5 * i) + 4],
793 }));
794 }
795 offset += 5 * num_fives;
796
797 if (has_quad) {
798 quad_tables.push_back(
799 quad_lookup_table({ points[offset], points[offset + 1], points[offset + 2], points[offset + 3] }));
800 }
801 if (has_triple) {
802 triple_tables.push_back(
803 triple_lookup_table({ points[offset], points[offset + 1], points[offset + 2] }));
804 }
805 if (has_twin) {
806 twin_tables.push_back(twin_lookup_table({ points[offset], points[offset + 1] }));
807 }
808 if (has_singleton) {
809 singletons.push_back(points[points.size() - 1]);
810 }
811 }
812
814 {
815 std::vector<element> add_accumulator;
816 for (size_t i = 0; i < num_sixes; ++i) {
817 add_accumulator.push_back(six_tables[i][0]);
818 }
819 for (size_t i = 0; i < num_fives; ++i) {
820 add_accumulator.push_back(five_tables[i][0]);
821 }
822 if (has_quad) {
823 add_accumulator.push_back(quad_tables[0][0]);
824 }
825 if (has_twin) {
826 add_accumulator.push_back(twin_tables[0][0]);
827 }
828 if (has_triple) {
829 add_accumulator.push_back(triple_tables[0][0]);
830 }
831 if (has_singleton) {
832 add_accumulator.push_back(singletons[0]);
833 }
834 if (add_accumulator.size() >= 2) {
835 chain_add_accumulator output = element::chain_add_start(add_accumulator[0], add_accumulator[1]);
836 for (size_t i = 2; i < add_accumulator.size(); ++i) {
837 output = element::chain_add(add_accumulator[i], output);
838 }
839 return output;
840 }
841 return chain_add_accumulator(add_accumulator[0]);
842 }
843
844 element::chain_add_accumulator get_chain_add_accumulator(std::vector<bool_ct>& naf_entries) const
845 {
846 std::vector<element> round_accumulator;
847 for (size_t j = 0; j < num_sixes; ++j) {
848 round_accumulator.push_back(six_tables[j].get({ naf_entries[6 * j],
849 naf_entries[(6 * j) + 1],
850 naf_entries[(6 * j) + 2],
851 naf_entries[(6 * j) + 3],
852 naf_entries[(6 * j) + 4],
853 naf_entries[(6 * j) + 5] }));
854 }
855 size_t offset = num_sixes * 6;
856 for (size_t j = 0; j < num_fives; ++j) {
857 round_accumulator.push_back(five_tables[j].get({ naf_entries[offset + (j * 5)],
858 naf_entries[offset + (j * 5) + 1],
859 naf_entries[offset + (j * 5) + 2],
860 naf_entries[offset + (j * 5) + 3],
861 naf_entries[offset + (j * 5) + 4] }));
862 }
863 offset += num_fives * 5;
864 if (has_quad) {
865 round_accumulator.push_back(quad_tables[0].get({ naf_entries[offset],
866 naf_entries[offset + 1],
867 naf_entries[offset + 2],
868 naf_entries[offset + 3] }));
869 }
870
871 if (has_triple) {
872 round_accumulator.push_back(
873 triple_tables[0].get({ naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2] }));
874 }
875 if (has_twin) {
876 round_accumulator.push_back(twin_tables[0].get({ naf_entries[offset], naf_entries[offset + 1] }));
877 }
878 if (has_singleton) {
879 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
880 }
881
882 if (round_accumulator.size() == 1) {
883 return element::chain_add_accumulator(round_accumulator[0]);
884 }
885
886 if (round_accumulator.size() == 2) {
887 return element::chain_add_start(round_accumulator[0], round_accumulator[1]);
888 }
889
890 // Use chain add for at least 3 elements
891 element::chain_add_accumulator accumulator =
892 element::chain_add_start(round_accumulator[0], round_accumulator[1]);
893 for (size_t j = 2; j < round_accumulator.size(); ++j) {
894 accumulator = element::chain_add(round_accumulator[j], accumulator);
895 }
896
897 return (accumulator);
898 }
899
900 element get(std::vector<bool_ct>& naf_entries) const
901 {
902 std::vector<element> round_accumulator;
903 for (size_t j = 0; j < num_sixes; ++j) {
904 round_accumulator.push_back(six_tables[j].get({ naf_entries[(6 * j)],
905 naf_entries[(6 * j) + 1],
906 naf_entries[(6 * j) + 2],
907 naf_entries[(6 * j) + 3],
908 naf_entries[(6 * j) + 4],
909 naf_entries[(6 * j) + 5] }));
910 }
911 size_t offset = num_sixes * 6;
912
913 for (size_t j = 0; j < num_fives; ++j) {
914 round_accumulator.push_back(five_tables[j].get({ naf_entries[offset + (5 * j)],
915 naf_entries[offset + (5 * j) + 1],
916 naf_entries[offset + (5 * j) + 2],
917 naf_entries[offset + (5 * j) + 3],
918 naf_entries[offset + (5 * j) + 4] }));
919 }
920
921 offset += num_fives * 5;
922
923 if (has_quad) {
924 round_accumulator.push_back(quad_tables[0].get(
925 naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2], naf_entries[offset + 3]));
926 }
927 if (has_triple) {
928 round_accumulator.push_back(
929 triple_tables[0].get(naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2]));
930 }
931 if (has_twin) {
932 round_accumulator.push_back(twin_tables[0].get(naf_entries[offset], naf_entries[offset + 1]));
933 }
934 if (has_singleton) {
935 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
936 }
937
938 element result = round_accumulator[0];
939 element::chain_add_accumulator accumulator;
940 if (round_accumulator.size() == 1) {
941 return result;
942 }
943
944 if (round_accumulator.size() == 2) {
945 return result + round_accumulator[1];
946 }
947
948 // For 3 or more elements, use chain addition
949 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
950 for (size_t j = 2; j < round_accumulator.size(); ++j) {
951 accumulator = element::chain_add(round_accumulator[j], accumulator);
952 }
953
954 return element::chain_add_end(accumulator);
955 }
956
964
965 size_t num_sixes = 0;
966 size_t num_fives;
971 };
972
974
976 const std::vector<Fr>& scalars,
977 const size_t max_num_bits);
978};
979
980// For testing purposes only
982 public:
983 template <typename C, typename Fq, typename Fr, typename G, size_t wnaf_size>
984 static auto get_staggered_wnaf_fragment_value(uint64_t fragment_u64,
985 uint64_t stagger,
986 bool is_negative,
987 bool wnaf_skew)
988 {
989 return element<C, Fq, Fr, G>::template get_staggered_wnaf_fragment_value<wnaf_size>(
990 fragment_u64, stagger, is_negative, wnaf_skew);
991 }
992
993 template <typename C, typename Fq, typename Fr, typename G>
995 {
996 return elem1.checked_unconditional_add_sub(elem2);
997 }
998
999 template <typename C, typename Fq, typename Fr, typename G>
1000 static auto mask_points(const std::vector<element<C, Fq, Fr, G>>& points, const std::vector<Fr>& scalars)
1001 {
1002 return element<C, Fq, Fr, G>::mask_points(points, scalars);
1003 }
1004
1005 // Overload for goblin_element
1006 template <typename C, typename Fq, typename Fr, typename G>
1012
1018 template <typename C, typename Fq, typename Fr, typename G>
1020 const Fq& y,
1021 const stdlib::bool_t<C>& is_infinity,
1022 bool assert_on_curve = false)
1023 {
1024 return element<C, Fq, Fr, G>(x, y, is_infinity, assert_on_curve);
1025 }
1026};
1027
1028template <typename C, typename Fq, typename Fr, typename G>
1029inline std::ostream& operator<<(std::ostream& os, element<C, Fq, Fr, G> const& v)
1030{
1031 return os << "{ " << v.x() << " , " << v.y() << " }";
1032}
1033} // namespace bb::stdlib::element_default
1034
1035namespace bb::stdlib {
1036template <typename T>
1038
1039template <typename Builder, class Fq, class Fr, class NativeGroup>
1043
1049template <typename C, typename Fq, typename Fr, typename G>
1053} // namespace bb::stdlib
1055#include "biggroup_goblin.hpp"
1056#include "biggroup_impl.hpp"
1057#include "biggroup_nafs.hpp"
1058#include "biggroup_secp256k1.hpp"
1059#include "biggroup_secp256r1.hpp"
1060#include "biggroup_tables.hpp"
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_NEQ(actual, expected,...)
Definition assert.hpp:98
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
constexpr uint256_t slice(uint64_t start, uint64_t end) const
Implements boolean logic in-circuit.
Definition bool.hpp:60
bool get_value() const
Definition bool.hpp:125
void fix_witness()
Definition bool.hpp:159
bool is_constant() const
Definition bool.hpp:127
void set_origin_tag(const OriginTag &new_tag) const
Definition bool.hpp:154
void set_free_witness_tag()
Definition bool.hpp:156
static bool_t conditional_assign(const bool_t< Builder > &predicate, const bool_t &lhs, const bool_t &rhs)
Conditionally assign lhs or rhs based on predicate, always returns normalized result.
Definition bool.cpp:478
void unset_free_witness_tag()
Definition bool.hpp:157
Builder * get_context() const
Definition bool.hpp:152
void clear_round_provenance() const
Definition bool.hpp:158
void assert_equal(const bool_t &rhs, std::string const &msg="bool_t::assert_equal") const
Implements copy constraint for bool_t elements.
Definition bool.cpp:433
OriginTag get_origin_tag() const
Definition bool.hpp:155
static auto checked_unconditional_add_sub(const element_goblin::goblin_element< C, Fq, Fr, G > &elem1, const element_goblin::goblin_element< C, Fq, Fr, G > &elem2)
static auto mask_points(const std::vector< element< C, Fq, Fr, G > > &points, const std::vector< Fr > &scalars)
static auto get_staggered_wnaf_fragment_value(uint64_t fragment_u64, uint64_t stagger, bool is_negative, bool wnaf_skew)
Definition biggroup.hpp:984
static element< C, Fq, Fr, G > create_element_with_explicit_infinity(const Fq &x, const Fq &y, const stdlib::bool_t< C > &is_infinity, bool assert_on_curve=false)
Create an element with explicit infinity flag (for testing only).
static auto checked_unconditional_add_sub(const element< C, Fq, Fr, G > &elem1, const element< C, Fq, Fr, G > &elem2)
Definition biggroup.hpp:994
element checked_unconditional_subtract(const element &other) const
element & operator=(const element &other)
element add_internal(const element &other) const
Internal implementation of ECC point addition.
void assert_coordinates_in_field(const std::string &msg="biggroup::assert_coordinates_in_field") const
Definition biggroup.hpp:299
element operator-=(const element &other)
Definition biggroup.hpp:215
stdlib::bool_t< Builder > bool_ct
Definition biggroup.hpp:29
NativeGroup::affine_element get_value() const
Definition biggroup.hpp:347
static std::pair< Fr, secp256k1_wnaf > compute_secp256k1_single_wnaf(Builder *builder, const secp256k1::fr &scalar, size_t stagger, bool is_negative, const bool range_constrain_wnaf=true, bool is_lo=false)
Compute the wNAF representation (in circuit) of a scalar for secp256k1.
element(const Fq &x, const Fq &y, const bool_ct &is_infinity, bool assert_on_curve)
Builder * get_context(const element &other) const
Definition biggroup.hpp:418
element multiple_montgomery_ladder(const std::vector< chain_add_accumulator > &to_add) const
Perform repeated iterations of the montgomery ladder algorithm.
static element one(Builder *ctx)
Creates a constant group generator.
Definition biggroup.hpp:174
static chain_add_accumulator chain_add_start(const element &p1, const element &p2)
Optimized chained addition for non-infinity points.
static element from_witness(Builder *ctx, const typename NativeGroup::affine_element &input)
Create a biggroup witness from a native group element, allocating new witnesses as necessary.
Definition biggroup.hpp:74
void fix_witness()
Fix a witness. The value of the witness is constrained with a selector.
Definition biggroup.hpp:160
static std::pair< four_bit_table_plookup, four_bit_table_plookup > create_endo_pair_four_bit_table_plookup(const element &input)
Create a endo pair four bit table for the given group element.
static element chain_add_end(const chain_add_accumulator &accumulator)
End an addition chain and compute the final y-coordinate.
Fq validate_on_curve(std::string const &msg="biggroup::validate_on_curve", bool assert_is_on_curve=true) const
Check that the point is on the curve.
Definition biggroup.hpp:102
void unset_free_witness_tag()
Unset the free witness flag for the element's tags.
Definition biggroup.hpp:452
static element secp256r1_fixed_base_mul(const Fr &u)
Fixed-base scalar multiplication for the secp256r1 generator using Pedersen-style ROM tables.
static NativeGroup::affine_element compute_table_offset_generator()
Compute an offset generator for use in biggroup tables.
static std::vector< field_ct > convert_wnaf_values_to_witnesses(Builder *builder, const uint64_t *wnaf_values, bool is_negative, size_t rounds, const bool range_constrain_wnaf=true)
Convert wNAF values to witness values.
void set_origin_tag(OriginTag tag) const
Definition biggroup.hpp:430
void incomplete_assert_equal(const element &other, const std::string msg="biggroup::incomplete_assert_equal") const
Asserts that two group elements are equal (i.e., x, y coordinates and infinity flag are all equal).
Definition biggroup.hpp:274
stdlib::witness_t< Builder > witness_ct
Definition biggroup.hpp:31
static std::pair< quad_lookup_table, quad_lookup_table > create_endo_pair_quad_lookup_table(const std::array< element, 4 > &inputs)
Definition biggroup.hpp:698
static element process_strauss_msm_rounds(const std::vector< element > &points, const std::vector< Fr > &scalars, const size_t max_num_bits)
static Fr reconstruct_bigfield_from_wnaf(Builder *builder, const std::vector< field_ct > &wnaf, const bool_ct &positive_skew, const bool_ct &negative_skew, const field_ct &stagger_fragment, const size_t stagger, const size_t rounds)
Reconstruct a scalar from its wNAF representation in circuit.
static std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > create_group_element_rom_tables(const std::array< element, num_elements > &rom_data, std::array< uint256_t, Fq::NUM_LIMBS *2 > &limb_max)
void convert_constant_to_fixed_witness(Builder *builder)
Creates fixed witnesses from a constant element.
Definition biggroup.hpp:149
element dbl_internal() const
Internal implementation of point doubling.
element scalar_mul(const Fr &scalar, const size_t max_num_bits=0) const
Implements scalar multiplication that supports short scalars. For multiple scalar multiplication use ...
element operator+=(const element &other)
Definition biggroup.hpp:210
static element read_group_element_rom_tables(const std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > &tables, const field_ct &index, const std::array< uint256_t, Fq::NUM_LIMBS *2 > &limb_max)
static secp256k1_wnaf_pair compute_secp256k1_endo_wnaf(const Fr &scalar, const bool range_constrain_wnaf=true)
Compute endomorphism for a secp256k1 scalar, and then compute the wNAF representation of both halves.
element checked_unconditional_add(const element &other) const
static element batch_mul(const std::vector< element > &points, const std::vector< Fr > &scalars, const size_t max_num_bits=0, const bool with_edgecases=true)
Generic batch multiplication that works for all elliptic curve types.
static std::vector< bool_ct > compute_naf(const Fr &scalar, const size_t max_num_bits=0)
Compute Non-Adjacent Form (NAF) representation of a scalar.
static std::pair< uint64_t, bool > get_staggered_wnaf_fragment_value(const uint64_t fragment_u64, const uint64_t stagger, bool is_negative, bool wnaf_skew)
Compute the stagger-related part of wNAF and the final skew.
uint32_t set_public() const
Set the witness indices for the x and y coordinates to public.
Definition biggroup.hpp:57
static element conditional_assign(const bool_t< Builder > &predicate, const element &lhs, const element &rhs)
Definition biggroup.hpp:259
element operator*(const Fr &scalar) const
element conditional_negate(const bool_ct &predicate) const
Definition biggroup.hpp:223
static std::pair< std::vector< element >, std::vector< Fr > > handle_points_at_infinity(const std::vector< element > &_points, const std::vector< Fr > &_scalars)
Handle points at infinity in batch operations, replaces (∞, scalar) pairs by (G, 0)
static element constant_infinity(Builder *ctx)
Creates a constant point at infinity with canonical (0, 0) coordinates.
Definition biggroup.hpp:189
void set_free_witness_tag()
Set the free witness flag for the element's tags.
Definition biggroup.hpp:462
static Secp256r1EcdsaMulResult secp256r1_ecdsa_mul(const element &pubkey, const Fr &u1, const Fr &u2)
element conditional_select(const element &other, const bool_ct &predicate) const
Selects this if predicate is false, other if predicate is true.
Definition biggroup.hpp:237
element subtract_internal(const element &other) const
Internal implementation of ECC point subtraction.
element get_standard_form() const
Enforce x and y coordinates of a point to be (0, 0) in the case of point at infinity.
std::array< element, 2 > checked_unconditional_add_sub(const element &other) const
Compute both add and subtract (a + b, a - b) results simultaneously.
static constexpr size_t PUBLIC_INPUTS_SIZE
Definition biggroup.hpp:36
element operator+(const element &other) const
Evaluate ECC point addition over *this and other.
static chain_add_accumulator chain_add(const element &p1, const chain_add_accumulator &accumulator)
Evaluate a chain addition using incomplete addition formulae.
static std::pair< element, element > compute_offset_generators(const size_t num_rounds)
static element batch_mul_internal(const std::vector< element > &points, const std::vector< Fr > &scalars, const size_t max_num_bits=0, const bool with_edgecases=false)
Internal implementation of generic batch multiplication that works for all elliptic curve types.
static element secp256k1_ecdsa_mul(const element &pubkey, const Fr &u1, const Fr &u2)
static std::tuple< std::vector< element >, std::vector< Fr >, element > mask_points(const std::vector< element > &_points, const std::vector< Fr > &_scalars)
Mask points for batch multiplication to handle edge cases.
element dbl() const
Evaluates a point doubling.
Custom element class for when using goblin.
std::array< goblin_element, 2 > checked_unconditional_add_sub(const goblin_element &other) const
#define vinfo(...)
Definition log.hpp:94
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
#define G(r, i, a, b, c, d)
Definition blake2s.cpp:116
ssize_t offset
Definition engine.cpp:62
AvmProvingInputs inputs
std::ostream & operator<<(std::ostream &os, element< C, Fq, Fr, G > const &v)
std::conditional_t< IsGoblinBigGroup< C, Fq, Fr, G >, element_goblin::goblin_element< C, goblin_field< C >, Fr, G >, element_default::element< C, Fq, Fr, G > > element
element wraps either element_default::element or element_goblin::goblin_element depending on parametr...
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Curve::ScalarField Fr
bb::VectorAffineElementPushSpan< BaseParams > lhs
bb::VectorAffineElementPushSpan< BaseParams > out
bb::VectorAffineElementPushSpan< BaseParams > rhs
grumpkin::fq Fq
static constexpr field cube_root_of_unity()
BB_INLINE constexpr field sqr() const noexcept
static constexpr field zero()
Output of secp256r1_ecdsa_mul: the multiplication result plus a soundness flag.
Definition biggroup.hpp:371
element get(std::vector< bool_ct > &naf_entries) const
Definition biggroup.hpp:900
std::vector< lookup_table_plookup< 6 > > six_tables
Definition biggroup.hpp:957
std::vector< lookup_table_plookup< 5 > > five_tables
Definition biggroup.hpp:958
element::chain_add_accumulator get_chain_add_accumulator(std::vector< bool_ct > &naf_entries) const
Definition biggroup.hpp:844
batch_lookup_table_plookup(const std::vector< element > &points)
Definition biggroup.hpp:721
chain_add_accumulator(chain_add_accumulator &&other) noexcept=default
chain_add_accumulator(const chain_add_accumulator &other)=default
chain_add_accumulator & operator=(const chain_add_accumulator &other)=default
chain_add_accumulator & operator=(chain_add_accumulator &&other) noexcept=default
Eight-bit fixed base table for scalar multiplication.
Definition biggroup.hpp:638
eight_bit_fixed_base_table & operator=(eight_bit_fixed_base_table &&other) noexcept=default
eight_bit_fixed_base_table & operator=(const eight_bit_fixed_base_table &other)=default
eight_bit_fixed_base_table(const CurveType input_curve_type, bool use_endo)
Definition biggroup.hpp:640
eight_bit_fixed_base_table(eight_bit_fixed_base_table &&other) noexcept=default
eight_bit_fixed_base_table(const eight_bit_fixed_base_table &other)=default
Four-bit variable-base table for scalar multiplication.
Definition biggroup.hpp:613
std::array< uint256_t, Fq::NUM_LIMBS *2 > limb_max
Definition biggroup.hpp:629
four_bit_table_plookup & operator=(four_bit_table_plookup &&other) noexcept=default
four_bit_table_plookup(const four_bit_table_plookup &other)=default
four_bit_table_plookup(four_bit_table_plookup &&other) noexcept=default
std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > coordinates
Definition biggroup.hpp:628
four_bit_table_plookup & operator=(const four_bit_table_plookup &other)=default
Generic lookup table that uses ROM tables internally to access group elements.
Definition biggroup.hpp:666
std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > coordinates
Definition biggroup.hpp:684
lookup_table_plookup(const lookup_table_plookup &other)=default
lookup_table_plookup(lookup_table_plookup &&other) noexcept=default
lookup_table_plookup & operator=(lookup_table_plookup &&other) noexcept=default
lookup_table_plookup & operator=(const lookup_table_plookup &other)=default
element get(const std::array< bool_ct, length > &bits) const
std::array< uint256_t, Fq::NUM_LIMBS *2 > limb_max
Definition biggroup.hpp:685
BB_VF_LOAD_LIMBS * this
VectorField result