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: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
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 "../memory/rom_table.hpp"
15#include "../memory/twin_rom_table.hpp"
20#include <cstddef>
21
23
24// ( ͡° ͜ʖ ͡°)
25template <class Builder_, class Fq, class Fr, class NativeGroup> class element {
26 public:
27 using Builder = Builder_;
29 using biggroup_tag = element; // Facilitates a constexpr check IsBigGroup
30 using BaseField = Fq;
31
32 // Number of bb::fr field elements used to represent a goblin element in the public inputs
33 static constexpr size_t PUBLIC_INPUTS_SIZE = BIGGROUP_PUBLIC_INPUTS_SIZE;
45
46 element();
47 element(const typename NativeGroup::affine_element& input);
48 element(const Fq& x, const Fq& y);
49 element(const Fq& x, const Fq& y, const bool_ct& is_infinity);
50
51 element(const element& other);
52 element(element&& other) noexcept;
53
54 ~element() = default;
55
62 {
63 const typename NativeGroup::affine_element& native_val = NativeGroup::affine_element::one();
64 element val(native_val);
65 size_t idx = 0;
67 for (auto& limb : val.x.binary_basis_limbs) {
68 limb_vals[idx++] = limb.element.get_value();
69 }
70 for (auto& limb : val.y.binary_basis_limbs) {
71 limb_vals[idx++] = limb.element.get_value();
72 }
74 return limb_vals;
75 }
76
82 uint32_t set_public() const
83 {
84 const uint32_t start_idx = x.set_public();
85 y.set_public();
86
87 return start_idx;
88 }
89
97 {
98 const size_t FRS_PER_FQ = Fq::PUBLIC_INPUTS_SIZE;
99 std::span<const Fr, FRS_PER_FQ> x_limbs{ limbs.data(), FRS_PER_FQ };
100 std::span<const Fr, FRS_PER_FQ> y_limbs{ limbs.data() + FRS_PER_FQ, FRS_PER_FQ };
101
102 return { Fq::reconstruct_from_public(x_limbs), Fq::reconstruct_from_public(y_limbs) };
103 }
104
113 static element from_witness(Builder* ctx, const typename NativeGroup::affine_element& input)
114 {
115 element out;
116 if (input.is_point_at_infinity()) {
117 Fq x = Fq::from_witness(ctx, NativeGroup::affine_one.x);
118 Fq y = Fq::from_witness(ctx, NativeGroup::affine_one.y);
119 out.x = x;
120 out.y = y;
121 } else {
122 Fq x = Fq::from_witness(ctx, input.x);
123 Fq y = Fq::from_witness(ctx, input.y);
124 out.x = x;
125 out.y = y;
126 }
127 out.set_point_at_infinity(witness_t<Builder>(ctx, input.is_point_at_infinity()));
128
129 // Mark the element as coming out of nowhere
131 out.validate_on_curve();
132 return out;
133 }
134
138 void validate_on_curve(std::string const& msg = "biggroup::validate_on_curve") const
139 {
140 bool has_circuit_failed = get_context()->failed();
141
142 Fq b(get_context(), uint256_t(NativeGroup::curve_b));
143 Fq _b = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), b);
144 Fq _x = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), x);
145 Fq _y = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), y);
146 if constexpr (!NativeGroup::has_a) {
147 // we validate y^2 = x^3 + b by setting "fix_remainder_zero = true" when calling mult_madd
148 Fq::mult_madd({ _x.sqr(), _y }, { _x, -_y }, { _b }, true);
149 } else {
150 Fq a(get_context(), uint256_t(NativeGroup::curve_a));
151 Fq _a = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), a);
152 // we validate y^2 = x^3 + ax + b by setting "fix_remainder_zero = true" when calling mult_madd
153 Fq::mult_madd({ _x.sqr(), _x, _y }, { _x, _a, -_y }, { _b }, true);
154 }
155
156 if ((!has_circuit_failed) && (get_context()->failed())) {
157 vinfo("Original bigfield error generated by biggroup::validate_on_curve: ", get_context()->err());
158 get_context()->failure(msg);
159 }
160 }
161
166 {
167 this->x.convert_constant_to_fixed_witness(builder);
168 this->y.convert_constant_to_fixed_witness(builder);
169 // Origin tags should be unset after fixing the witness
171 }
172
177 {
178 // Origin tags should be updated within
179 this->x.fix_witness();
180 this->y.fix_witness();
181
182 // This is now effectively a constant
184 }
185
189 static element one(Builder* ctx)
190 {
191 uint256_t x = uint256_t(NativeGroup::one.x);
192 uint256_t y = uint256_t(NativeGroup::one.y);
193 Fq x_fq(ctx, x);
194 Fq y_fq(ctx, y);
195 return element(x_fq, y_fq);
196 }
197
199 {
200 Fr zero = Fr::from_witness_index(ctx, ctx->zero_idx());
201 zero.unset_free_witness_tag();
202 Fq x_fq(zero, zero);
203 Fq y_fq(zero, zero);
204 element result(x_fq, y_fq);
205 result.set_point_at_infinity(true);
206 return result;
207 }
208
209 element& operator=(const element& other);
210 element& operator=(element&& other) noexcept;
211
218 {
220 result.write(y.to_byte_array());
221 result.write(x.to_byte_array());
222 return result;
223 }
224
225 element checked_unconditional_add(const element& other) const;
227
228 element operator+(const element& other) const;
229 element operator-(const element& other) const;
231 {
232 element result(*this);
233 result.y = -result.y;
234 return result;
235 }
237 {
238 *this = *this + other;
239 return *this;
240 }
242 {
243 *this = *this - other;
244 return *this;
245 }
247
248 element operator*(const Fr& scalar) const;
249
250 element conditional_negate(const bool_ct& predicate) const
251 {
252 element result(*this);
253 result.y = result.y.conditional_negate(predicate);
254 return result;
255 }
256
264 element conditional_select(const element& other, const bool_ct& predicate) const
265 {
266 // If predicate is constant, we can select out of circuit
267 if (predicate.is_constant()) {
268 auto result = predicate.get_value() ? other : *this;
269 result.set_origin_tag(
270 OriginTag(predicate.get_origin_tag(), other.get_origin_tag(), this->get_origin_tag()));
271 return result;
272 }
273
274 // Get the builder context
275 Builder* ctx = validate_context<Builder>(get_context(), other.get_context(), predicate.get_context());
276 BB_ASSERT_NEQ(ctx, nullptr, "biggroup::conditional_select must have a context");
277
278 element result(*this);
279 result.x = result.x.conditional_select(other.x, predicate);
280 result.y = result.y.conditional_select(other.y, predicate);
281 result._is_infinity =
283 return result;
284 }
285
297 const std::string msg = "biggroup::incomplete_assert_equal") const
298 {
299 is_point_at_infinity().assert_equal(other.is_point_at_infinity(), msg + " (infinity flag)");
300 x.assert_equal(other.x, msg + " (x coordinate)");
301 y.assert_equal(other.y, msg + " (y coordinate)");
302 }
303
305 {
306 element result(*this);
307 result.x.reduce_mod_target_modulus();
308 result.y.reduce_mod_target_modulus();
309 return result;
310 }
311 element scalar_mul(const Fr& scalar, const size_t max_num_bits = 0) const;
312
314 {
315 element result(*this);
316 result.x.self_reduce();
317 result.y.self_reduce();
318 return result;
319 }
320
321 element dbl() const;
322
323 // we use this data structure to add together a sequence of points.
324 // By tracking the previous values of x_1, y_1, \lambda, we can avoid
325 // computing the output y-coordinate of intermediate additions
332 bool is_element = false;
333
335 explicit chain_add_accumulator(const element& input)
336 : x3_prev(input.x)
337 , y3_prev(input.y)
338 , is_element(true)
339 {}
341 chain_add_accumulator(chain_add_accumulator&& other) noexcept = default;
345 };
346
351 static chain_add_accumulator chain_add_start(const element& p1, const element& p2);
352 static chain_add_accumulator chain_add(const element& p1, const chain_add_accumulator& accumulator);
353 static element chain_add_end(const chain_add_accumulator& accumulator);
354 element montgomery_ladder(const element& other) const;
358
359 typename NativeGroup::affine_element get_value() const
360 {
361 uint512_t x_val = x.get_value() % Fq::modulus_u512;
362 uint512_t y_val = y.get_value() % Fq::modulus_u512;
363 auto result = typename NativeGroup::affine_element(x_val.lo, y_val.lo);
365 result.self_set_infinity();
366 }
367 return result;
368 }
369
370 static std::pair<std::vector<element>, std::vector<Fr>> mask_points(const std::vector<element>& _points,
371 const std::vector<Fr>& _scalars);
372
374 const std::vector<element>& _points, const std::vector<Fr>& _scalars);
375
376 // compute a multi-scalar-multiplication by creating a precomputed lookup table for each point,
377 // splitting each scalar multiplier up into a 4-bit sliding window wNAF.
378 // more efficient than batch_mul if num_points < 4
379 // only works with Plookup!
380 template <size_t max_num_bits = 0>
381 static element wnaf_batch_mul(const std::vector<element>& points, const std::vector<Fr>& scalars);
382 static element batch_mul(const std::vector<element>& points,
383 const std::vector<Fr>& scalars,
384 const size_t max_num_bits = 0,
385 const bool with_edgecases = false);
386
387 // we want to conditionally compile this method iff our curve params are the BN254 curve.
388 // This is a bit tricky to do with `std::enable_if`, because `bn254_endo_batch_mul` is a member function of a class
389 // template
390 // && the compiler can't perform partial template specialization on member functions of class templates
391 // => our template parameter cannot be a value but must instead by a type
392 // Our input to `std::enable_if` is a comparison between two types (NativeGroup and bb::g1), which
393 // resolves to either `true/false`.
394 // If `std::enable_if` resolves to `true`, it resolves to a `typedef` that equals `void`
395 // If `std::enable_if` resolves to `false`, there is no member typedef
396 // We want to take the *type* of the output typedef of `std::enable_if`
397 // i.e. for the bn254 curve, the template param is `typename = void`
398 // for any other curve, there is no template param
399 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, bb::g1>::value>>
402 const std::vector<Fr>& big_scalars,
403 const std::vector<element>& small_points,
404 const std::vector<Fr>& small_scalars,
405 const size_t max_num_small_bits);
406
407 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, bb::g1>::value>>
410 const std::vector<Fr>& big_scalars,
411 const std::vector<element>& small_points,
412 const std::vector<Fr>& small_scalars,
413 const Fr& generator_scalar,
414 const size_t max_num_small_bits);
415
416 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, secp256k1::g1>::value>>
417 static element secp256k1_ecdsa_mul(const element& pubkey, const Fr& u1, const Fr& u2);
418
419 static std::vector<bool_ct> compute_naf(const Fr& scalar, const size_t max_num_bits = 0);
420
421 template <size_t max_num_bits = 0, size_t WNAF_SIZE = 4>
423
424 template <size_t wnaf_size, size_t staggered_lo_offset = 0, size_t staggered_hi_offset = 0>
425 static secp256k1_wnaf_pair compute_secp256k1_endo_wnaf(const Fr& scalar, const bool range_constrain_wnaf = true);
426
428 {
429 if (x.context != nullptr) {
430 return x.context;
431 }
432 if (y.context != nullptr) {
433 return y.context;
434 }
435 return nullptr;
436 }
437
438 Builder* get_context(const element& other) const
439 {
440 if (x.context != nullptr) {
441 return x.context;
442 }
443 if (y.context != nullptr) {
444 return y.context;
445 }
446 if (other.x.context != nullptr) {
447 return other.x.context;
448 }
449 if (other.y.context != nullptr) {
450 return other.y.context;
451 }
452 return nullptr;
453 }
454
456 void set_point_at_infinity(const bool_ct& is_infinity, const bool& add_to_used_witnesses = false)
457 {
458 _is_infinity = is_infinity.normalize();
459 if (add_to_used_witnesses) {
461 };
462 }
464
465 void set_origin_tag(OriginTag tag) const
466 {
467 x.set_origin_tag(tag);
468 y.set_origin_tag(tag);
470 }
471
473 {
474 return OriginTag(x.get_origin_tag(), y.get_origin_tag(), _is_infinity.get_origin_tag());
475 }
476
481 {
482 x.unset_free_witness_tag();
483 y.unset_free_witness_tag();
485 }
486
491 {
492 x.set_free_witness_tag();
493 y.set_free_witness_tag();
495 }
496
499
500 // For testing purposes only
502
503 private:
505
520 template <size_t num_bits, size_t wnaf_size, size_t lo_stagger, size_t hi_stagger>
522 const secp256k1::fr& scalar,
523 size_t stagger,
524 bool is_negative,
525 const bool range_constrain_wnaf = true,
526 bool is_lo = false);
527
537 template <size_t wnaf_size>
538 static std::pair<uint64_t, bool> get_staggered_wnaf_fragment_value(const uint64_t fragment_u64,
539 const uint64_t stagger,
540 bool is_negative,
541 bool wnaf_skew);
542
556 template <size_t wnaf_size>
558 const uint64_t* wnaf_values,
559 bool is_negative,
560 size_t rounds,
561 const bool range_constrain_wnaf = true);
562
574 template <size_t wnaf_size>
576 const std::vector<field_t<Builder>>& wnaf,
577 const bool_ct& positive_skew,
578 const bool_ct& negative_skew,
579 const field_t<Builder>& stagger_fragment,
580 const size_t stagger,
581 const size_t rounds);
582
583 template <size_t num_elements>
586
587 template <size_t num_elements>
588 static element read_group_element_rom_tables(const std::array<twin_rom_table<Builder>, Fq::NUM_LIMBS + 1>& tables,
589 const field_t<Builder>& index,
591
592 static std::pair<element, element> compute_offset_generators(const size_t num_rounds);
593 static typename NativeGroup::affine_element compute_table_offset_generator();
594
602 four_bit_table_plookup(const element& input);
603
609
611 element operator[](const size_t idx) const { return element_table[idx]; }
613
614 // Each coordinate is an Fq element, which has 4 binary basis limbs and 1 prime basis limb
616 std::array<uint256_t, Fq::NUM_LIMBS * 2> limb_max; // tracks the maximum size of each binary basis limb
617 };
618
627 eight_bit_fixed_base_table(const CurveType input_curve_type, bool use_endo)
628 : curve_type(input_curve_type)
629 , use_endomorphism(use_endo)
630 {}
631
637
639
640 element operator[](const size_t index) const;
641
644 };
645
647 const element& input);
648
653 template <size_t length> struct lookup_table_plookup {
654 static constexpr size_t table_size = (1ULL << (length));
659 lookup_table_plookup(lookup_table_plookup&& other) noexcept = default;
662
663 element get(const std::array<bool_ct, length>& bits) const;
664
665 element operator[](const size_t idx) const { return element_table[idx]; }
666
668
669 // Each coordinate is an Fq element, which has 4 binary basis limbs and 1 prime basis limb
670 // ROM tables: (idx, x0, x1), (idx, x2, x3), (idx, y0, y1), (idx, y2, y3), (idx, xp, yp)
673 };
674
676
678
680
686 const std::array<element, 4>& inputs)
687 {
688 quad_lookup_table base_table(inputs);
689 quad_lookup_table endo_table;
691 Fq beta(bb::fr(beta_val.slice(0, 136)), bb::fr(beta_val.slice(136, 256)), false);
692 for (size_t i = 0; i < 8; ++i) {
693 endo_table.element_table[i + 8].x = base_table[7 - i].x * beta;
694 endo_table.element_table[i + 8].y = base_table[7 - i].y;
695
696 endo_table.element_table[7 - i] = (-endo_table.element_table[i + 8]);
697 }
698
699 endo_table.coordinates = create_group_element_rom_tables<16>(endo_table.element_table, endo_table.limb_max);
700 return std::make_pair<quad_lookup_table, quad_lookup_table>(base_table, endo_table);
701 }
702
709 : num_points(points.size())
710 , num_fives(num_points / 5)
711 {
712 // size-6 table is expensive and only benefits us if creating them reduces the number of total tables
713 if (num_points == 1) {
714 num_fives = 0;
715 num_sixes = 0;
716 } else if (num_fives * 5 == (num_points - 1)) {
717 // last 6 points to be added as one 6-table
718 num_fives -= 1;
719 num_sixes = 1;
720 } else if (num_fives * 5 == (num_points - 2) && num_fives >= 2) {
721 // last 12 points to be added as two 6-tables
722 num_fives -= 2;
723 num_sixes = 2;
724 } else if (num_fives * 5 == (num_points - 3) && num_fives >= 3) {
725 // last 18 points to be added as three 6-tables
726 num_fives -= 3;
727 num_sixes = 3;
728 }
729
730 // Calculate remaining points after allocating fives and sixes tables
731 size_t remaining_points = num_points - (num_fives * 5 + num_sixes * 6);
732
733 // Allocate one quad table if required (and update remaining points)
734 has_quad = (remaining_points >= 4) && (num_points >= 4);
735 if (has_quad) {
736 remaining_points -= 4;
737 }
738
739 // Allocate one triple table if required (and update remaining points)
740 has_triple = (remaining_points >= 3) && (num_points >= 3);
741 if (has_triple) {
742 remaining_points -= 3;
743 }
744
745 // Allocate one twin table if required (and update remaining points)
746 has_twin = (remaining_points >= 2) && (num_points >= 2);
747 if (has_twin) {
748 remaining_points -= 2;
749 }
750
751 // If there is anything remaining, allocate a singleton
752 has_singleton = (remaining_points != 0) && (num_points >= 1);
753
754 // Sanity check
756 num_sixes * 6 + num_fives * 5 + static_cast<size_t>(has_quad) * 4 +
757 static_cast<size_t>(has_triple) * 3 + static_cast<size_t>(has_twin) * 2 +
758 static_cast<size_t>(has_singleton) * 1,
759 "point allocation mismatch");
760
761 size_t offset = 0;
762 for (size_t i = 0; i < num_sixes; ++i) {
764 points[offset + (6 * i)],
765 points[offset + (6 * i) + 1],
766 points[offset + (6 * i) + 2],
767 points[offset + (6 * i) + 3],
768 points[offset + (6 * i) + 4],
769 points[offset + (6 * i) + 5],
770 }));
771 }
772 offset += 6 * num_sixes;
773 for (size_t i = 0; i < num_fives; ++i) {
775 points[offset + (5 * i)],
776 points[offset + (5 * i) + 1],
777 points[offset + (5 * i) + 2],
778 points[offset + (5 * i) + 3],
779 points[offset + (5 * i) + 4],
780 }));
781 }
782 offset += 5 * num_fives;
783
784 if (has_quad) {
785 quad_tables.push_back(
786 quad_lookup_table({ points[offset], points[offset + 1], points[offset + 2], points[offset + 3] }));
787 }
788 if (has_triple) {
789 triple_tables.push_back(
790 triple_lookup_table({ points[offset], points[offset + 1], points[offset + 2] }));
791 }
792 if (has_twin) {
793 twin_tables.push_back(twin_lookup_table({ points[offset], points[offset + 1] }));
794 }
795 if (has_singleton) {
796 singletons.push_back(points[points.size() - 1]);
797 }
798 }
799
801 {
802 std::vector<element> add_accumulator;
803 for (size_t i = 0; i < num_sixes; ++i) {
804 add_accumulator.push_back(six_tables[i][0]);
805 }
806 for (size_t i = 0; i < num_fives; ++i) {
807 add_accumulator.push_back(five_tables[i][0]);
808 }
809 if (has_quad) {
810 add_accumulator.push_back(quad_tables[0][0]);
811 }
812 if (has_twin) {
813 add_accumulator.push_back(twin_tables[0][0]);
814 }
815 if (has_triple) {
816 add_accumulator.push_back(triple_tables[0][0]);
817 }
818 if (has_singleton) {
819 add_accumulator.push_back(singletons[0]);
820 }
821
822 element accumulator = add_accumulator[0];
823 for (size_t i = 1; i < add_accumulator.size(); ++i) {
824 accumulator = accumulator + add_accumulator[i];
825 }
826 return accumulator;
827 }
828
830 {
831 std::vector<element> add_accumulator;
832 for (size_t i = 0; i < num_sixes; ++i) {
833 add_accumulator.push_back(six_tables[i][0]);
834 }
835 for (size_t i = 0; i < num_fives; ++i) {
836 add_accumulator.push_back(five_tables[i][0]);
837 }
838 if (has_quad) {
839 add_accumulator.push_back(quad_tables[0][0]);
840 }
841 if (has_twin) {
842 add_accumulator.push_back(twin_tables[0][0]);
843 }
844 if (has_triple) {
845 add_accumulator.push_back(triple_tables[0][0]);
846 }
847 if (has_singleton) {
848 add_accumulator.push_back(singletons[0]);
849 }
850 if (add_accumulator.size() >= 2) {
851 chain_add_accumulator output = element::chain_add_start(add_accumulator[0], add_accumulator[1]);
852 for (size_t i = 2; i < add_accumulator.size(); ++i) {
853 output = element::chain_add(add_accumulator[i], output);
854 }
855 return output;
856 }
857 return chain_add_accumulator(add_accumulator[0]);
858 }
859
860 element::chain_add_accumulator get_chain_add_accumulator(std::vector<bool_ct>& naf_entries) const
861 {
862 std::vector<element> round_accumulator;
863 for (size_t j = 0; j < num_sixes; ++j) {
864 round_accumulator.push_back(six_tables[j].get({ naf_entries[6 * j],
865 naf_entries[(6 * j) + 1],
866 naf_entries[(6 * j) + 2],
867 naf_entries[(6 * j) + 3],
868 naf_entries[(6 * j) + 4],
869 naf_entries[(6 * j) + 5] }));
870 }
871 size_t offset = num_sixes * 6;
872 for (size_t j = 0; j < num_fives; ++j) {
873 round_accumulator.push_back(five_tables[j].get({ naf_entries[offset + (j * 5)],
874 naf_entries[offset + (j * 5) + 1],
875 naf_entries[offset + (j * 5) + 2],
876 naf_entries[offset + (j * 5) + 3],
877 naf_entries[offset + (j * 5) + 4] }));
878 }
879 offset += num_fives * 5;
880 if (has_quad) {
881 round_accumulator.push_back(quad_tables[0].get({ naf_entries[offset],
882 naf_entries[offset + 1],
883 naf_entries[offset + 2],
884 naf_entries[offset + 3] }));
885 }
886
887 if (has_triple) {
888 round_accumulator.push_back(
889 triple_tables[0].get({ naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2] }));
890 }
891 if (has_twin) {
892 round_accumulator.push_back(twin_tables[0].get({ naf_entries[offset], naf_entries[offset + 1] }));
893 }
894 if (has_singleton) {
895 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
896 }
897
898 element::chain_add_accumulator accumulator;
899 if (round_accumulator.size() == 1) {
900 return element::chain_add_accumulator(round_accumulator[0]);
901 }
902
903 if (round_accumulator.size() == 2) {
904 return element::chain_add_start(round_accumulator[0], round_accumulator[1]);
905 }
906
907 // Use chain add for at least 3 elements
908 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
909 for (size_t j = 2; j < round_accumulator.size(); ++j) {
910 accumulator = element::chain_add(round_accumulator[j], accumulator);
911 }
912
913 return (accumulator);
914 }
915
916 element get(std::vector<bool_ct>& naf_entries) const
917 {
918 std::vector<element> round_accumulator;
919 for (size_t j = 0; j < num_sixes; ++j) {
920 round_accumulator.push_back(six_tables[j].get({ naf_entries[(6 * j)],
921 naf_entries[(6 * j) + 1],
922 naf_entries[(6 * j) + 2],
923 naf_entries[(6 * j) + 3],
924 naf_entries[(6 * j) + 4],
925 naf_entries[(6 * j) + 5] }));
926 }
927 size_t offset = num_sixes * 6;
928
929 for (size_t j = 0; j < num_fives; ++j) {
930 round_accumulator.push_back(five_tables[j].get({ naf_entries[offset + (5 * j)],
931 naf_entries[offset + (5 * j) + 1],
932 naf_entries[offset + (5 * j) + 2],
933 naf_entries[offset + (5 * j) + 3],
934 naf_entries[offset + (5 * j) + 4] }));
935 }
936
937 offset += num_fives * 5;
938
939 if (has_quad) {
940 round_accumulator.push_back(quad_tables[0].get(
941 naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2], naf_entries[offset + 3]));
942 }
943 if (has_triple) {
944 round_accumulator.push_back(
945 triple_tables[0].get(naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2]));
946 }
947 if (has_twin) {
948 round_accumulator.push_back(twin_tables[0].get(naf_entries[offset], naf_entries[offset + 1]));
949 }
950 if (has_singleton) {
951 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
952 }
953
954 element result = round_accumulator[0];
955 element::chain_add_accumulator accumulator;
956 if (round_accumulator.size() == 1) {
957 return result;
958 }
959
960 if (round_accumulator.size() == 2) {
961 return result + round_accumulator[1];
962 }
963
964 // For 3 or more elements, use chain addition
965 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
966 for (size_t j = 2; j < round_accumulator.size(); ++j) {
967 accumulator = element::chain_add(round_accumulator[j], accumulator);
968 }
969
970 return element::chain_add_end(accumulator);
971 }
972
980
981 size_t num_sixes = 0;
982 size_t num_fives;
987 };
988
990};
991
992// For testing purposes only
994 public:
995 template <typename C, typename Fq, typename Fr, typename G, size_t wnaf_size>
996 static auto get_staggered_wnaf_fragment_value(uint64_t fragment_u64,
997 uint64_t stagger,
998 bool is_negative,
999 bool wnaf_skew)
1000 {
1001 return element<C, Fq, Fr, G>::template get_staggered_wnaf_fragment_value<wnaf_size>(
1002 fragment_u64, stagger, is_negative, wnaf_skew);
1003 }
1004};
1005
1006template <typename C, typename Fq, typename Fr, typename G>
1007inline std::ostream& operator<<(std::ostream& os, element<C, Fq, Fr, G> const& v)
1008{
1009 return os << "{ " << v.x << " , " << v.y << " }";
1010}
1011} // namespace bb::stdlib::element_default
1012
1013namespace bb::stdlib {
1014template <typename T>
1016
1017template <typename Builder, class Fq, class Fr, class NativeGroup>
1021
1027template <typename C, typename Fq, typename Fr, typename G>
1031} // namespace bb::stdlib
1032#include "biggroup_batch_mul.hpp"
1033#include "biggroup_bn254.hpp"
1034#include "biggroup_goblin.hpp"
1035#include "biggroup_impl.hpp"
1036#include "biggroup_nafs.hpp"
1037#include "biggroup_secp256k1.hpp"
1038#include "biggroup_tables.hpp"
#define BB_ASSERT_NEQ(actual, expected,...)
Definition assert.hpp:103
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:88
constexpr uint256_t slice(uint64_t start, uint64_t end) const
Implements boolean logic in-circuit.
Definition bool.hpp:59
bool get_value() const
Definition bool.hpp:111
bool is_constant() const
Definition bool.hpp:113
void set_origin_tag(const OriginTag &new_tag) const
Definition bool.hpp:128
bool_t normalize() const
A bool_t element is normalized if witness_inverted == false. For a given *this, output its normalized...
Definition bool.cpp:505
void set_free_witness_tag()
Definition bool.hpp:130
uint32_t get_normalized_witness_index() const
Definition bool.hpp:124
static bool_t conditional_assign(const bool_t< Builder > &predicate, const bool_t &lhs, const bool_t &rhs)
Implements the ternary operator - if predicate == true then return lhs, else return rhs.
Definition bool.cpp:456
void unset_free_witness_tag()
Definition bool.hpp:131
Builder * get_context() const
Definition bool.hpp:126
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:423
OriginTag get_origin_tag() const
Definition bool.hpp:129
Represents a dynamic array of bytes in-circuit.
byte_array & write(byte_array const &other)
Appends the contents of another byte_array (other) to the end of this one.
static auto get_staggered_wnaf_fragment_value(uint64_t fragment_u64, uint64_t stagger, bool is_negative, bool wnaf_skew)
Definition biggroup.hpp:996
element checked_unconditional_subtract(const element &other) const
element & operator=(const element &other)
byte_array< Builder > to_byte_array() const
Serialize the element to a byte array in form: (yhi || ylo || xhi || xlo).
Definition biggroup.hpp:217
element operator-=(const element &other)
Definition biggroup.hpp:241
NativeGroup::affine_element get_value() const
Definition biggroup.hpp:359
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.
Builder * get_context(const element &other) const
Definition biggroup.hpp:438
element multiple_montgomery_ladder(const std::vector< chain_add_accumulator > &to_add) const
Perform repeated iterations of the montgomery ladder algorithm.
void validate_on_curve(std::string const &msg="biggroup::validate_on_curve") const
Check that the point is on the curve.
Definition biggroup.hpp:138
static element one(Builder *ctx)
Creates a constant group generator.
Definition biggroup.hpp:189
static chain_add_accumulator chain_add_start(const element &p1, const element &p2)
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:113
void fix_witness()
Fix a witness. The value of the witness is constrained with a selector.
Definition biggroup.hpp:176
element montgomery_ladder(const element &other) const
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)
void unset_free_witness_tag()
Unset the free witness flag for the element's tags.
Definition biggroup.hpp:480
static element point_at_infinity(Builder *ctx)
Definition biggroup.hpp:198
static std::vector< field_t< Builder > > compute_wnaf(const Fr &scalar)
static NativeGroup::affine_element compute_table_offset_generator()
Compute an offset generator for use in biggroup tables.
void set_origin_tag(OriginTag tag) const
Definition biggroup.hpp:465
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:296
void set_point_at_infinity(const bool_ct &is_infinity, const bool &add_to_used_witnesses=false)
Definition biggroup.hpp:456
static element bn254_endo_batch_mul_with_generator(const std::vector< element > &big_points, const std::vector< Fr > &big_scalars, const std::vector< element > &small_points, const std::vector< Fr > &small_scalars, const Fr &generator_scalar, const size_t max_num_small_bits)
static element read_group_element_rom_tables(const std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > &tables, const field_t< Builder > &index, const std::array< uint256_t, Fq::NUM_LIMBS *2 > &limb_max)
static std::pair< quad_lookup_table, quad_lookup_table > create_endo_pair_quad_lookup_table(const std::array< element, 4 > &inputs)
Definition biggroup.hpp:685
element(const typename NativeGroup::affine_element &input)
element quadruple_and_add(const std::vector< element > &to_add) const
Compute 4.P + to_add[0] + ... + to_add[to_add.size() - 1].
static element bn254_endo_batch_mul(const std::vector< element > &big_points, const std::vector< Fr > &big_scalars, const std::vector< element > &small_points, const std::vector< Fr > &small_scalars, const size_t max_num_small_bits)
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:165
static std::array< fr, PUBLIC_INPUTS_SIZE > construct_dummy()
Construct a dummy element (the group generator) and return its limbs as fr constants.
Definition biggroup.hpp:61
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:236
static secp256k1_wnaf_pair compute_secp256k1_endo_wnaf(const Fr &scalar, const bool range_constrain_wnaf=true)
static element wnaf_batch_mul(const std::vector< element > &points, const std::vector< Fr > &scalars)
element checked_unconditional_add(const element &other) const
static std::vector< bool_ct > compute_naf(const Fr &scalar, const size_t max_num_bits=0)
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:82
static Fr reconstruct_bigfield_from_wnaf(Builder *builder, const std::vector< field_t< Builder > > &wnaf, const bool_ct &positive_skew, const bool_ct &negative_skew, const field_t< Builder > &stagger_fragment, const size_t stagger, const size_t rounds)
Reconstruct a scalar from its wNAF representation in circuit.
element operator*(const Fr &scalar) const
element conditional_negate(const bool_ct &predicate) const
Definition biggroup.hpp:250
static std::pair< std::vector< element >, std::vector< Fr > > handle_points_at_infinity(const std::vector< element > &_points, const std::vector< Fr > &_scalars)
Replace all pairs (∞, scalar) by the pair (one, 0) where one is a fixed generator of the curve.
void set_free_witness_tag()
Set the free witness flag for the element's tags.
Definition biggroup.hpp:490
static std::vector< field_t< Builder > > 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.
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:264
element get_standard_form() const
Enforce x and y coordinates of a point to be (0,0) in the case of point at infinity.
static element reconstruct_from_public(const std::span< const Fr, PUBLIC_INPUTS_SIZE > &limbs)
Reconstruct a biggroup element from limbs of its coordinates (generally stored in the public inputs)
Definition biggroup.hpp:96
std::array< element, 2 > checked_unconditional_add_sub(const element &other) const
Compute (*this) + other AND (*this) - other as a size-2 array.
static constexpr size_t PUBLIC_INPUTS_SIZE
Definition biggroup.hpp:33
element operator+(const element &other) const
static chain_add_accumulator chain_add(const element &p1, const chain_add_accumulator &accumulator)
static std::pair< std::vector< element >, std::vector< Fr > > mask_points(const std::vector< element > &_points, const std::vector< Fr > &_scalars)
Given two lists of points that need to be multiplied by scalars, create a new list of length +1 with ...
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=false)
Generic batch multiplication that works for all elliptic curve types.
static std::pair< element, element > compute_offset_generators(const size_t num_rounds)
static element secp256k1_ecdsa_mul(const element &pubkey, const Fr &u1, const Fr &u2)
Custom element class for when using goblin.
#define vinfo(...)
Definition log.hpp:79
AluTraceBuilder builder
Definition alu.test.cpp:123
FF a
FF b
#define G(r, i, a, b, c, d)
Definition blake2s.cpp:116
uint8_t const size_t length
Definition data_store.hpp:9
ssize_t offset
Definition engine.cpp:36
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
static constexpr field cube_root_of_unity()
static constexpr size_t PUBLIC_INPUTS_SIZE
BB_INLINE constexpr field sqr() const noexcept
static field reconstruct_from_public(const std::span< const field< V >, PUBLIC_INPUTS_SIZE > &limbs)
static constexpr field zero()
element get(std::vector< bool_ct > &naf_entries) const
Definition biggroup.hpp:916
std::vector< lookup_table_plookup< 6 > > six_tables
Definition biggroup.hpp:973
std::vector< lookup_table_plookup< 5 > > five_tables
Definition biggroup.hpp:974
element::chain_add_accumulator get_chain_add_accumulator(std::vector< bool_ct > &naf_entries) const
Definition biggroup.hpp:860
batch_lookup_table_plookup(const std::vector< element > &points)
Definition biggroup.hpp:708
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:625
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
element operator[](const field_t< Builder > &index) const
eight_bit_fixed_base_table(const CurveType input_curve_type, bool use_endo)
Definition biggroup.hpp:627
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:600
std::array< uint256_t, Fq::NUM_LIMBS *2 > limb_max
Definition biggroup.hpp:616
four_bit_table_plookup & operator=(four_bit_table_plookup &&other) noexcept=default
element operator[](const field_t< Builder > &index) const
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:615
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:653
std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > coordinates
Definition biggroup.hpp:671
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:672
std::vector< field_t< Builder > > wnaf
Definition biggroup.hpp:35
bb::fq Fq