barretenberg
Loading...
Searching...
No Matches
biggroup.hpp
1#pragma once
2
3#include "../bigfield/bigfield.hpp"
4#include "../byte_array/byte_array.hpp"
5#include "../circuit_builders/circuit_builders_fwd.hpp"
6#include "../field/field.hpp"
7#include "../memory/rom_table.hpp"
8#include "../memory/twin_rom_table.hpp"
9#include "barretenberg/ecc/curves/bn254/g1.hpp"
10#include "barretenberg/ecc/curves/secp256k1/secp256k1.hpp"
11#include "barretenberg/ecc/curves/secp256r1/secp256r1.hpp"
12
13// TODO(https://github.com/AztecProtocol/barretenberg/issues/707) If using a a circuit builder with Goblin, which is
14// designed to have efficient barretenberg::g1 operations, a developer might accidentally write inefficient circuits
15// using biggroup functions that do not use the OpQueue. We use this concept to prevent compilation of such functions.
16template <typename Builder, typename NativeGroup>
17concept IsNotGoblinInefficiencyTrap = !(IsGoblinBuilder<Builder> && std::same_as<NativeGroup, barretenberg::g1>);
18
19namespace proof_system::plonk::stdlib {
20
21// ( ͡° ͜ʖ ͡°)
22template <class Builder, class Fq, class Fr, class NativeGroup> class element {
23 public:
25 std::vector<field_t<Builder>> wnaf;
26 field_t<Builder> positive_skew;
27 field_t<Builder> negative_skew;
28 field_t<Builder> least_significant_wnaf_fragment;
29 bool has_wnaf_fragment = false;
30 };
34 };
35
36 element();
37 element(const typename NativeGroup::affine_element& input);
38 element(const Fq& x, const Fq& y);
39
40 element(const element& other);
41 element(element&& other);
42
43 static element from_witness(Builder* ctx, const typename NativeGroup::affine_element& input)
44 {
45 Fq x = Fq::from_witness(ctx, input.x);
46 Fq y = Fq::from_witness(ctx, input.y);
47 element out(x, y);
48 out.validate_on_curve();
49 return out;
50 }
51
52 void validate_on_curve() const
53 {
54 Fq b(get_context(), uint256_t(NativeGroup::curve_b));
55 if constexpr (!NativeGroup::has_a) {
56 // we validate y^2 = x^3 + b by setting "fix_remainder_zero = true" when calling mult_madd
57 Fq::mult_madd({ x.sqr(), y }, { x, -y }, { b }, true);
58 } else {
59 Fq a(get_context(), uint256_t(NativeGroup::curve_a));
60 // we validate y^2 = x^3 + ax + b by setting "fix_remainder_zero = true" when calling mult_madd
61 Fq::mult_madd({ x.sqr(), x, y }, { x, a, -y }, { b }, true);
62 }
63 }
64
65 static element one(Builder* ctx)
66 {
67 uint256_t x = uint256_t(NativeGroup::one.x);
68 uint256_t y = uint256_t(NativeGroup::one.y);
69 Fq x_fq(ctx, x);
70 Fq y_fq(ctx, y);
71 return element(x_fq, y_fq);
72 }
73
74 element& operator=(const element& other);
75 element& operator=(element&& other);
76
77 byte_array<Builder> to_byte_array() const
78 {
79 byte_array<Builder> result(get_context());
80 result.write(y.to_byte_array());
81 result.write(x.to_byte_array());
82 return result;
83 }
84
85 element operator+(const element& other) const;
86 element operator-(const element& other) const;
87 element operator-() const
88 {
89 element result(*this);
90 result.y = -result.y;
91 return result;
92 }
93 element operator+=(const element& other)
94 {
95 *this = *this + other;
96 return *this;
97 }
98 element operator-=(const element& other)
99 {
100 *this = *this - other;
101 return *this;
102 }
103 std::array<element, 2> add_sub(const element& other) const;
104
105 element operator*(const Fr& other) const;
106
107 element conditional_negate(const bool_t<Builder>& predicate) const
108 {
109 element result(*this);
110 result.y = result.y.conditional_negate(predicate);
111 return result;
112 }
113
114 element normalize() const
115 {
116 element result(*this);
117 result.x.assert_is_in_field();
118 result.y.assert_is_in_field();
119 return result;
120 }
121
122 element reduce() const
123 {
124 element result(*this);
125 result.x.self_reduce();
126 result.y.self_reduce();
127 return result;
128 }
129
130 element dbl() const;
131
132 // we use this data structure to add together a sequence of points.
133 // By tracking the previous values of x_1, y_1, \lambda, we can avoid
134 // computing the output y-coordinate of intermediate additions
136 Fq x1_prev;
137 Fq y1_prev;
138 Fq lambda_prev;
139 Fq x3_prev;
140 Fq y3_prev;
141 bool is_element = false;
142
144 explicit chain_add_accumulator(const element& input)
145 {
146 x3_prev = input.x;
147 y3_prev = input.y;
148 is_element = true;
149 }
150 chain_add_accumulator(const chain_add_accumulator& other) = default;
152 chain_add_accumulator& operator=(const chain_add_accumulator& other) = default;
153 chain_add_accumulator& operator=(chain_add_accumulator&& other) = default;
154 };
155
160 static chain_add_accumulator chain_add_start(const element& p1, const element& p2)
162 static chain_add_accumulator chain_add(const element& p1, const chain_add_accumulator& accumulator)
164 static element chain_add_end(const chain_add_accumulator& accumulator)
166
167 element montgomery_ladder(const element& other) const
171 element multiple_montgomery_ladder(const std::vector<chain_add_accumulator>& to_add) const
173
174 element quadruple_and_add(const std::vector<element>& to_add) const
176
177 typename NativeGroup::affine_element get_value() const
178 {
179 uint512_t x_val = x.get_value();
180 uint512_t y_val = y.get_value();
181 return typename NativeGroup::affine_element(x_val.lo, y_val.lo);
182 }
183
184 // compute a multi-scalar-multiplication by creating a precomputed lookup table for each point,
185 // splitting each scalar multiplier up into a 4-bit sliding window wNAF.
186 // more efficient than batch_mul if num_points < 4
187 // only works with Plookup!
188 template <size_t max_num_bits = 0>
189 static element wnaf_batch_mul(const std::vector<element>& points, const std::vector<Fr>& scalars);
190 static element batch_mul(const std::vector<element>& points,
191 const std::vector<Fr>& scalars,
192 const size_t max_num_bits = 0);
193
194 // TODO(https://github.com/AztecProtocol/barretenberg/issues/707) max_num_bits is unused; could implement and use
195 // this to optimize other operations.
196 static element goblin_batch_mul(const std::vector<element>& points,
197 const std::vector<Fr>& scalars,
198 const size_t max_num_bits = 0);
199
200 // we want to conditionally compile this method iff our curve params are the BN254 curve.
201 // This is a bit tricky to do with `std::enable_if`, because `bn254_endo_batch_mul` is a member function of a class
202 // template
203 // && the compiler can't perform partial template specialization on member functions of class templates
204 // => our template parameter cannot be a value but must instead by a type
205 // Our input to `std::enable_if` is a comparison between two types (NativeGroup and barretenberg::g1), which
206 // resolves to either `true/false`.
207 // If `std::enable_if` resolves to `true`, it resolves to a `typedef` that equals `void`
208 // If `std::enable_if` resolves to `false`, there is no member typedef
209 // We want to take the *type* of the output typedef of `std::enable_if`
210 // i.e. for the bn254 curve, the template param is `typename = void`
211 // for any other curve, there is no template param
212 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, barretenberg::g1>::value>>
213 requires(IsNotGoblinBuilder<Builder>) // TODO(https://github.com/AztecProtocol/barretenberg/issues/707)
214 static element bn254_endo_batch_mul(const std::vector<element>& big_points,
215 const std::vector<Fr>& big_scalars,
216 const std::vector<element>& small_points,
217 const std::vector<Fr>& small_scalars,
218 const size_t max_num_small_bits);
219
220 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, barretenberg::g1>::value>>
221 requires(IsNotGoblinBuilder<Builder>) // TODO(https://github.com/AztecProtocol/barretenberg/issues/707)
222 static element bn254_endo_batch_mul_with_generator(const std::vector<element>& big_points,
223 const std::vector<Fr>& big_scalars,
224 const std::vector<element>& small_points,
225 const std::vector<Fr>& small_scalars,
226 const Fr& generator_scalar,
227 const size_t max_num_small_bits);
228
229 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, secp256k1::g1>::value>>
230 static element secp256k1_ecdsa_mul(const element& pubkey, const Fr& u1, const Fr& u2);
231
232 static std::vector<bool_t<Builder>> compute_naf(const Fr& scalar, const size_t max_num_bits = 0);
233
234 template <size_t max_num_bits = 0, size_t WNAF_SIZE = 4>
235 static std::vector<field_t<Builder>> compute_wnaf(const Fr& scalar);
236
237 template <size_t wnaf_size, size_t staggered_lo_offset = 0, size_t staggered_hi_offset = 0>
238 static secp256k1_wnaf_pair compute_secp256k1_endo_wnaf(const Fr& scalar);
239
240 Builder* get_context() const
241 {
242 if (x.context != nullptr) {
243 return x.context;
244 }
245 if (y.context != nullptr) {
246 return y.context;
247 }
248 return nullptr;
249 }
250
251 Builder* get_context(const element& other) const
252 {
253 if (x.context != nullptr) {
254 return x.context;
255 }
256 if (y.context != nullptr) {
257 return y.context;
258 }
259 if (other.x.context != nullptr) {
260 return other.x.context;
261 }
262 if (other.y.context != nullptr) {
263 return other.y.context;
264 }
265 return nullptr;
266 }
267
268 Fq x;
269 Fq y;
270
271 private:
272 template <size_t num_elements, typename = typename std::enable_if<HasPlookup<Builder>>>
273 static std::array<twin_rom_table<Builder>, 5> create_group_element_rom_tables(
274 const std::array<element, num_elements>& elements, std::array<uint256_t, 8>& limb_max);
275
276 template <size_t num_elements, typename = typename std::enable_if<HasPlookup<Builder>>>
277 static element read_group_element_rom_tables(const std::array<twin_rom_table<Builder>, 5>& tables,
278 const field_t<Builder>& index,
279 const std::array<uint256_t, 8>& limb_max);
280
281 static std::pair<element, element> compute_offset_generators(const size_t num_rounds);
282
283 template <typename = typename std::enable_if<HasPlookup<Builder>>> struct four_bit_table_plookup {
284 four_bit_table_plookup(){};
285 four_bit_table_plookup(const element& input);
286
287 four_bit_table_plookup(const four_bit_table_plookup& other) = default;
288 four_bit_table_plookup& operator=(const four_bit_table_plookup& other) = default;
289
290 element operator[](const field_t<Builder>& index) const;
291 element operator[](const size_t idx) const { return element_table[idx]; }
292 std::array<element, 16> element_table;
293 std::array<twin_rom_table<Builder>, 5> coordinates;
294 std::array<uint256_t, 8> limb_max; // tracks the maximum limb size represented in each element_table entry
295 };
296
297 template <typename = typename std::enable_if<HasPlookup<Builder>>> struct eight_bit_fixed_base_table {
298 enum CurveType { BN254, SECP256K1, SECP256R1 };
299 eight_bit_fixed_base_table(const CurveType input_curve_type, bool use_endo)
300 : curve_type(input_curve_type)
301 , use_endomorphism(use_endo){};
302
303 eight_bit_fixed_base_table(const eight_bit_fixed_base_table& other) = default;
304 eight_bit_fixed_base_table& operator=(const eight_bit_fixed_base_table& other) = default;
305
306 element operator[](const field_t<Builder>& index) const;
307
308 element operator[](const size_t idx) const;
309
310 CurveType curve_type;
311 bool use_endomorphism;
312 };
313
314 template <typename = typename std::enable_if<HasPlookup<Builder>>>
315 static std::pair<four_bit_table_plookup<>, four_bit_table_plookup<>> create_endo_pair_four_bit_table_plookup(
316 const element& input)
317 {
318 four_bit_table_plookup<> P1;
319 four_bit_table_plookup<> endoP1;
320 element d2 = input.dbl();
321
322 P1.element_table[8] = input;
323 for (size_t i = 9; i < 16; ++i) {
324 P1.element_table[i] = P1.element_table[i - 1] + d2;
325 }
326 for (size_t i = 0; i < 8; ++i) {
327 P1.element_table[i] = (-P1.element_table[15 - i]);
328 }
329 for (size_t i = 0; i < 16; ++i) {
330 endoP1.element_table[i].y = P1.element_table[15 - i].y;
331 }
333 Fq beta(barretenberg::fr(beta_val.slice(0, 136)), barretenberg::fr(beta_val.slice(136, 256)), false);
334 for (size_t i = 0; i < 8; ++i) {
335 endoP1.element_table[i].x = P1.element_table[i].x * beta;
336 endoP1.element_table[15 - i].x = endoP1.element_table[i].x;
337 }
338 P1.coordinates = create_group_element_rom_tables<16>(P1.element_table, P1.limb_max);
339 endoP1.coordinates = create_group_element_rom_tables<16>(endoP1.element_table, endoP1.limb_max);
340 auto result = std::make_pair<four_bit_table_plookup<>, four_bit_table_plookup<>>(
341 (four_bit_table_plookup<>)P1, (four_bit_table_plookup<>)endoP1);
342 return result;
343 }
344
364 template <size_t length> struct lookup_table_base {
365 static constexpr size_t table_size = (1ULL << (length - 1));
366 lookup_table_base(const std::array<element, length>& inputs);
367 lookup_table_base(const lookup_table_base& other) = default;
368 lookup_table_base& operator=(const lookup_table_base& other) = default;
369
370 element get(const std::array<bool_t<Builder>, length>& bits) const;
371
372 element operator[](const size_t idx) const { return element_table[idx]; }
373
374 std::array<field_t<Builder>, table_size> x_b0_table;
375 std::array<field_t<Builder>, table_size> x_b1_table;
376 std::array<field_t<Builder>, table_size> x_b2_table;
377 std::array<field_t<Builder>, table_size> x_b3_table;
378
379 std::array<field_t<Builder>, table_size> y_b0_table;
380 std::array<field_t<Builder>, table_size> y_b1_table;
381 std::array<field_t<Builder>, table_size> y_b2_table;
382 std::array<field_t<Builder>, table_size> y_b3_table;
383 element twin0;
384 element twin1;
385 std::array<element, table_size> element_table;
386 };
387
393 template <size_t length, typename = typename std::enable_if<HasPlookup<Builder>>> struct lookup_table_plookup {
394 static constexpr size_t table_size = (1ULL << (length));
395 lookup_table_plookup() {}
396 lookup_table_plookup(const std::array<element, length>& inputs);
397 lookup_table_plookup(const lookup_table_plookup& other) = default;
398 lookup_table_plookup& operator=(const lookup_table_plookup& other) = default;
399
400 element get(const std::array<bool_t<Builder>, length>& bits) const;
401
402 element operator[](const size_t idx) const { return element_table[idx]; }
403
404 std::array<element, table_size> element_table;
405 std::array<twin_rom_table<Builder>, 5> coordinates;
406 std::array<uint256_t, 8> limb_max;
407 };
408
409 using twin_lookup_table =
410 typename std::conditional<HasPlookup<Builder>, lookup_table_plookup<2, void>, lookup_table_base<2>>::type;
411
412 using triple_lookup_table =
413 typename std::conditional<HasPlookup<Builder>, lookup_table_plookup<3, void>, lookup_table_base<3>>::type;
414
415 using quad_lookup_table =
416 typename std::conditional<HasPlookup<Builder>, lookup_table_plookup<4, void>, lookup_table_base<4>>::type;
417
422 static std::pair<quad_lookup_table, quad_lookup_table> create_endo_pair_quad_lookup_table(
423 const std::array<element, 4>& inputs)
424 {
425 quad_lookup_table base_table(inputs);
426 quad_lookup_table endo_table;
428 Fq beta(barretenberg::fr(beta_val.slice(0, 136)), barretenberg::fr(beta_val.slice(136, 256)), false);
429 if constexpr (HasPlookup<Builder>) {
430 for (size_t i = 0; i < 8; ++i) {
431 endo_table.element_table[i + 8].x = base_table[7 - i].x * beta;
432 endo_table.element_table[i + 8].y = base_table[7 - i].y;
433
434 endo_table.element_table[7 - i] = (-endo_table.element_table[i + 8]);
435 }
436
437 endo_table.coordinates = create_group_element_rom_tables<16>(endo_table.element_table, endo_table.limb_max);
438 } else {
439 std::array<element, 4> endo_inputs(inputs);
440 for (auto& input : endo_inputs) {
441 input.x *= beta;
442 input.y = -input.y;
443 }
444 endo_table = quad_lookup_table(endo_inputs);
445 }
446 return std::make_pair<quad_lookup_table, quad_lookup_table>((quad_lookup_table)base_table,
447 (quad_lookup_table)endo_table);
448 }
449
454 template <typename X = typename std::enable_if<HasPlookup<Builder>>>
455 static std::pair<lookup_table_plookup<5, X>, lookup_table_plookup<5, X>> create_endo_pair_five_lookup_table(
456 const std::array<element, 5>& inputs)
457 {
458 lookup_table_plookup<5> base_table(inputs);
459 lookup_table_plookup<5> endo_table;
461 Fq beta(barretenberg::fr(beta_val.slice(0, 136)), barretenberg::fr(beta_val.slice(136, 256)), false);
462 if constexpr (HasPlookup<Builder>) {
463 for (size_t i = 0; i < 16; ++i) {
464 endo_table.element_table[i + 16].x = base_table[15 - i].x * beta;
465 endo_table.element_table[i + 16].y = base_table[15 - i].y;
466
467 endo_table.element_table[15 - i] = (-endo_table.element_table[i + 16]);
468 }
469
470 endo_table.coordinates = create_group_element_rom_tables<32>(endo_table.element_table, endo_table.limb_max);
471 }
472 return std::make_pair<lookup_table_plookup<5>, lookup_table_plookup<5>>((lookup_table_plookup<5>)base_table,
473 (lookup_table_plookup<5>)endo_table);
474 }
475
481 template <typename X = typename std::enable_if<HasPlookup<Builder>>> struct batch_lookup_table_plookup {
482 batch_lookup_table_plookup(const std::vector<element>& points)
483 {
484 num_points = points.size();
485 num_fives = num_points / 5;
486 num_sixes = 0;
487 // size-6 table is expensive and only benefits us if creating them reduces the number of total tables
488 if (num_fives * 5 == (num_points - 1)) {
489 num_fives -= 1;
490 num_sixes = 1;
491 } else if (num_fives * 5 == (num_points - 2) && num_fives >= 2) {
492 num_fives -= 2;
493 num_sixes = 2;
494 } else if (num_fives * 5 == (num_points - 3) && num_fives >= 3) {
495 num_fives -= 3;
496 num_sixes = 3;
497 }
498
499 has_quad = ((num_fives * 5 + num_sixes * 6) < num_points - 3) && (num_points >= 4);
500
501 has_triple = ((num_fives * 5 + num_sixes * 6 + (size_t)has_quad * 4) < num_points - 2) && (num_points >= 3);
502
503 has_twin =
504 ((num_fives * 5 + num_sixes * 6 + (size_t)has_quad * 4 + (size_t)has_triple * 3) < num_points - 1) &&
505 (num_points >= 2);
506
507 has_singleton = num_points != ((num_fives * 5 + num_sixes * 6) + ((size_t)has_quad * 4) +
508 ((size_t)has_triple * 3) + ((size_t)has_twin * 2));
509
510 size_t offset = 0;
511 for (size_t i = 0; i < num_sixes; ++i) {
512 six_tables.push_back(lookup_table_plookup<6>({
513 points[offset + 6 * i],
514 points[offset + 6 * i + 1],
515 points[offset + 6 * i + 2],
516 points[offset + 6 * i + 3],
517 points[offset + 6 * i + 4],
518 points[offset + 6 * i + 5],
519 }));
520 }
521 offset += 6 * num_sixes;
522 for (size_t i = 0; i < num_fives; ++i) {
523 five_tables.push_back(lookup_table_plookup<5>({
524 points[offset + 5 * i],
525 points[offset + 5 * i + 1],
526 points[offset + 5 * i + 2],
527 points[offset + 5 * i + 3],
528 points[offset + 5 * i + 4],
529 }));
530 }
531 offset += 5 * num_fives;
532
533 if (has_quad) {
534 quad_tables.push_back(
535 quad_lookup_table({ points[offset], points[offset + 1], points[offset + 2], points[offset + 3] }));
536 }
537
538 if (has_triple) {
539 triple_tables.push_back(
540 triple_lookup_table({ points[offset], points[offset + 1], points[offset + 2] }));
541 }
542 if (has_twin) {
543 twin_tables.push_back(twin_lookup_table({ points[offset], points[offset + 1] }));
544 }
545
546 if (has_singleton) {
547 singletons.push_back(points[points.size() - 1]);
548 }
549 }
550
551 element get_initial_entry() const
552 {
553 std::vector<element> add_accumulator;
554 for (size_t i = 0; i < num_sixes; ++i) {
555 add_accumulator.push_back(six_tables[i][0]);
556 }
557 for (size_t i = 0; i < num_fives; ++i) {
558 add_accumulator.push_back(five_tables[i][0]);
559 }
560 if (has_quad) {
561 add_accumulator.push_back(quad_tables[0][0]);
562 }
563 if (has_twin) {
564 add_accumulator.push_back(twin_tables[0][0]);
565 }
566 if (has_triple) {
567 add_accumulator.push_back(triple_tables[0][0]);
568 }
569 if (has_singleton) {
570 add_accumulator.push_back(singletons[0]);
571 }
572
573 element accumulator = add_accumulator[0];
574 for (size_t i = 1; i < add_accumulator.size(); ++i) {
575 accumulator = accumulator + add_accumulator[i];
576 }
577 return accumulator;
578 }
579
580 chain_add_accumulator get_chain_initial_entry() const
581 {
582 std::vector<element> add_accumulator;
583 for (size_t i = 0; i < num_sixes; ++i) {
584 add_accumulator.push_back(six_tables[i][0]);
585 }
586 for (size_t i = 0; i < num_fives; ++i) {
587 add_accumulator.push_back(five_tables[i][0]);
588 }
589 if (has_quad) {
590 add_accumulator.push_back(quad_tables[0][0]);
591 }
592 if (has_twin) {
593 add_accumulator.push_back(twin_tables[0][0]);
594 }
595 if (has_triple) {
596 add_accumulator.push_back(triple_tables[0][0]);
597 }
598 if (has_singleton) {
599 add_accumulator.push_back(singletons[0]);
600 }
601 if (add_accumulator.size() >= 2) {
602 chain_add_accumulator output = element::chain_add_start(add_accumulator[0], add_accumulator[1]);
603 for (size_t i = 2; i < add_accumulator.size(); ++i) {
604 output = element::chain_add(add_accumulator[i], output);
605 }
606 return output;
607 }
608 return chain_add_accumulator(add_accumulator[0]);
609 }
610
611 element::chain_add_accumulator get_chain_add_accumulator(std::vector<bool_t<Builder>>& naf_entries) const
612 {
613 std::vector<element> round_accumulator;
614 for (size_t j = 0; j < num_sixes; ++j) {
615 round_accumulator.push_back(six_tables[j].get({ naf_entries[6 * j],
616 naf_entries[6 * j + 1],
617 naf_entries[6 * j + 2],
618 naf_entries[6 * j + 3],
619 naf_entries[6 * j + 4],
620 naf_entries[6 * j + 5] }));
621 }
622 size_t offset = num_sixes * 6;
623 for (size_t j = 0; j < num_fives; ++j) {
624 round_accumulator.push_back(five_tables[j].get({ naf_entries[offset + j * 5],
625 naf_entries[offset + j * 5 + 1],
626 naf_entries[offset + j * 5 + 2],
627 naf_entries[offset + j * 5 + 3],
628 naf_entries[offset + j * 5 + 4] }));
629 }
630 offset += num_fives * 5;
631 if (has_quad) {
632 round_accumulator.push_back(quad_tables[0].get({ naf_entries[offset],
633 naf_entries[offset + 1],
634 naf_entries[offset + 2],
635 naf_entries[offset + 3] }));
636 }
637
638 if (has_triple) {
639 round_accumulator.push_back(
640 triple_tables[0].get({ naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2] }));
641 }
642 if (has_twin) {
643 round_accumulator.push_back(twin_tables[0].get({ naf_entries[offset], naf_entries[offset + 1] }));
644 }
645 if (has_singleton) {
646 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
647 }
648
649 element::chain_add_accumulator accumulator;
650 if (round_accumulator.size() == 1) {
651 return element::chain_add_accumulator(round_accumulator[0]);
652 } else if (round_accumulator.size() == 2) {
653 return element::chain_add_start(round_accumulator[0], round_accumulator[1]);
654 } else {
655 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
656 for (size_t j = 2; j < round_accumulator.size(); ++j) {
657 accumulator = element::chain_add(round_accumulator[j], accumulator);
658 }
659 }
660 return (accumulator);
661 }
662
663 element get(std::vector<bool_t<Builder>>& naf_entries) const
664 {
665 std::vector<element> round_accumulator;
666 for (size_t j = 0; j < num_sixes; ++j) {
667 round_accumulator.push_back(six_tables[j].get({ naf_entries[6 * j],
668 naf_entries[6 * j + 1],
669 naf_entries[6 * j + 2],
670 naf_entries[6 * j + 3],
671 naf_entries[6 * j + 4],
672 naf_entries[6 * j + 5] }));
673 }
674 size_t offset = num_sixes * 6;
675
676 for (size_t j = 0; j < num_fives; ++j) {
677 round_accumulator.push_back(five_tables[j].get({ naf_entries[offset + 5 * j],
678 naf_entries[offset + 5 * j + 1],
679 naf_entries[offset + 5 * j + 2],
680 naf_entries[offset + 5 * j + 3],
681 naf_entries[offset + 5 * j + 4] }));
682 }
683
684 offset += num_fives * 5;
685
686 if (has_quad) {
687 round_accumulator.push_back(quad_tables[0].get(
688 naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2], naf_entries[offset + 3]));
689 }
690
691 if (has_triple) {
692 round_accumulator.push_back(
693 triple_tables[0].get(naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2]));
694 }
695 if (has_twin) {
696 round_accumulator.push_back(twin_tables[0].get(naf_entries[offset], naf_entries[offset + 1]));
697 }
698 if (has_singleton) {
699 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
700 }
701
702 element result = round_accumulator[0];
703 element::chain_add_accumulator accumulator;
704 if (round_accumulator.size() == 1) {
705 return result;
706 } else if (round_accumulator.size() == 2) {
707 return result + round_accumulator[1];
708 } else {
709 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
710 for (size_t j = 2; j < round_accumulator.size(); ++j) {
711 accumulator = element::chain_add(round_accumulator[j], accumulator);
712 }
713 }
714 return element::chain_add_end(accumulator);
715 }
716
717 std::vector<lookup_table_plookup<6>> six_tables;
718 std::vector<lookup_table_plookup<5>> five_tables;
719 std::vector<quad_lookup_table> quad_tables;
720 std::vector<triple_lookup_table> triple_tables;
721 std::vector<twin_lookup_table> twin_tables;
722 std::vector<element> singletons;
723 size_t num_points;
724
725 size_t num_sixes;
726 size_t num_fives;
727 bool has_quad;
728 bool has_triple;
729 bool has_twin;
730 bool has_singleton;
731 };
732
737 struct batch_lookup_table_base {
738 batch_lookup_table_base(const std::vector<element>& points)
739 {
740 num_points = points.size();
741 num_quads = num_points / 4;
742
743 has_triple = ((num_quads * 4) < num_points - 2) && (num_points >= 3);
744
745 has_twin = ((num_quads * 4 + (size_t)has_triple * 3) < num_points - 1) && (num_points >= 2);
746
747 has_singleton = num_points != (num_quads * 4 + ((size_t)has_triple * 3) + ((size_t)has_twin * 2));
748
749 for (size_t i = 0; i < num_quads; ++i) {
750 quad_tables.push_back(
751 quad_lookup_table({ points[4 * i], points[4 * i + 1], points[4 * i + 2], points[4 * i + 3] }));
752 }
753
754 if (has_triple) {
755 triple_tables.push_back(triple_lookup_table(
756 { points[4 * num_quads], points[4 * num_quads + 1], points[4 * num_quads + 2] }));
757 }
758 if (has_twin) {
759 twin_tables.push_back(twin_lookup_table({ points[4 * num_quads], points[4 * num_quads + 1] }));
760 }
761
762 if (has_singleton) {
763 singletons.push_back(points[points.size() - 1]);
764 }
765 }
766
767 element get_initial_entry() const
768 {
769 std::vector<element> add_accumulator;
770 for (size_t i = 0; i < num_quads; ++i) {
771 add_accumulator.push_back(quad_tables[i][0]);
772 }
773 if (has_twin) {
774 add_accumulator.push_back(twin_tables[0][0]);
775 }
776 if (has_triple) {
777 add_accumulator.push_back(triple_tables[0][0]);
778 }
779 if (has_singleton) {
780 add_accumulator.push_back(singletons[0]);
781 }
782
783 element accumulator = add_accumulator[0];
784 for (size_t i = 1; i < add_accumulator.size(); ++i) {
785 accumulator = accumulator + add_accumulator[i];
786 }
787 return accumulator;
788 }
789
790 chain_add_accumulator get_chain_initial_entry() const
791 {
792 std::vector<element> add_accumulator;
793 for (size_t i = 0; i < num_quads; ++i) {
794 add_accumulator.push_back(quad_tables[i][0]);
795 }
796 if (has_twin) {
797 add_accumulator.push_back(twin_tables[0][0]);
798 }
799 if (has_triple) {
800 add_accumulator.push_back(triple_tables[0][0]);
801 }
802 if (has_singleton) {
803 add_accumulator.push_back(singletons[0]);
804 }
805 if (add_accumulator.size() >= 2) {
806 chain_add_accumulator output = element::chain_add_start(add_accumulator[0], add_accumulator[1]);
807 for (size_t i = 2; i < add_accumulator.size(); ++i) {
808 output = element::chain_add(add_accumulator[i], output);
809 }
810 return output;
811 }
812 return chain_add_accumulator(add_accumulator[0]);
813 }
814
815 element::chain_add_accumulator get_chain_add_accumulator(std::vector<bool_t<Builder>>& naf_entries) const
816 {
817 std::vector<element> round_accumulator;
818 for (size_t j = 0; j < num_quads; ++j) {
819 round_accumulator.push_back(quad_tables[j].get(std::array<bool_t<Builder>, 4>{
820 naf_entries[4 * j], naf_entries[4 * j + 1], naf_entries[4 * j + 2], naf_entries[4 * j + 3] }));
821 }
822
823 if (has_triple) {
824 round_accumulator.push_back(triple_tables[0].get(std::array<bool_t<Builder>, 3>{
825 naf_entries[num_quads * 4], naf_entries[num_quads * 4 + 1], naf_entries[num_quads * 4 + 2] }));
826 }
827 if (has_twin) {
828 round_accumulator.push_back(twin_tables[0].get(
829 std::array<bool_t<Builder>, 2>{ naf_entries[num_quads * 4], naf_entries[num_quads * 4 + 1] }));
830 }
831 if (has_singleton) {
832 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
833 }
834
835 element::chain_add_accumulator accumulator;
836 if (round_accumulator.size() == 1) {
837 accumulator.x3_prev = round_accumulator[0].x;
838 accumulator.y3_prev = round_accumulator[0].y;
839 accumulator.is_element = true;
840 return accumulator;
841 } else if (round_accumulator.size() == 2) {
842 return element::chain_add_start(round_accumulator[0], round_accumulator[1]);
843 } else {
844 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
845 for (size_t j = 2; j < round_accumulator.size(); ++j) {
846 accumulator = element::chain_add(round_accumulator[j], accumulator);
847 }
848 }
849 return (accumulator);
850 }
851
852 element get(std::vector<bool_t<Builder>>& naf_entries) const
853 {
854 std::vector<element> round_accumulator;
855 for (size_t j = 0; j < num_quads; ++j) {
856 round_accumulator.push_back(quad_tables[j].get(
857 { naf_entries[4 * j], naf_entries[4 * j + 1], naf_entries[4 * j + 2], naf_entries[4 * j + 3] }));
858 }
859
860 if (has_triple) {
861 round_accumulator.push_back(triple_tables[0].get(std::array<bool_t<Builder>, 3>{
862 naf_entries[num_quads * 4], naf_entries[num_quads * 4 + 1], naf_entries[num_quads * 4 + 2] }));
863 }
864 if (has_twin) {
865 round_accumulator.push_back(
866 twin_tables[0].get({ naf_entries[num_quads * 4], naf_entries[num_quads * 4 + 1] }));
867 }
868 if (has_singleton) {
869 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
870 }
871
872 element result = round_accumulator[0];
873 element::chain_add_accumulator accumulator;
874 if (round_accumulator.size() == 1) {
875 return result;
876 } else if (round_accumulator.size() == 2) {
877 return result + round_accumulator[1];
878 } else {
879 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
880 for (size_t j = 2; j < round_accumulator.size(); ++j) {
881 accumulator = element::chain_add(round_accumulator[j], accumulator);
882 }
883 }
884 return element::chain_add_end(accumulator);
885 }
886
887 std::vector<quad_lookup_table> quad_tables;
888 std::vector<triple_lookup_table> triple_tables;
889 std::vector<twin_lookup_table> twin_tables;
890 std::vector<element> singletons;
891 size_t num_points;
892
893 size_t num_quads;
894 bool has_triple;
895 bool has_twin;
896 bool has_singleton;
897 };
898
899 using batch_lookup_table =
900 typename std::conditional<HasPlookup<Builder>, batch_lookup_table_plookup<>, batch_lookup_table_base>::type;
901};
902
903template <typename C, typename Fq, typename Fr, typename G>
904inline std::ostream& operator<<(std::ostream& os, element<C, Fq, Fr, G> const& v)
905{
906 return os << "{ " << v.x << " , " << v.y << " }";
907}
908} // namespace proof_system::plonk::stdlib
909
910#include "biggroup_batch_mul.hpp"
911#include "biggroup_bn254.hpp"
912#include "biggroup_goblin.hpp"
913#include "biggroup_impl.hpp"
914#include "biggroup_nafs.hpp"
915#include "biggroup_secp256k1.hpp"
916#include "biggroup_tables.hpp"
Definition: uint256.hpp:25
constexpr uint256_t slice(uint64_t start, uint64_t end) const
Definition: uint256_impl.hpp:157
Definition: standard_circuit_builder.hpp:12
Definition: biggroup.hpp:22
static secp256k1_wnaf_pair compute_secp256k1_endo_wnaf(const Fr &scalar)
Definition: biggroup_nafs.hpp:96
element multiple_montgomery_ladder(const std::vector< chain_add_accumulator > &to_add) const
Perform repeated iterations of the montgomery ladder algorithm.
Definition: biggroup_impl.hpp:458
element quadruple_and_add(const std::vector< element > &to_add) const
Compute 4.P + to_add[0] + ... + to_add[to_add.size() - 1].
Definition: biggroup_impl.hpp:363
static element batch_mul(const std::vector< element > &points, const std::vector< Fr > &scalars, const size_t max_num_bits=0)
Definition: biggroup_impl.hpp:620
element montgomery_ladder(const element &other) const
Definition: biggroup_impl.hpp:282
static element goblin_batch_mul(const std::vector< element > &points, const std::vector< Fr > &scalars, const size_t max_num_bits=0)
Goblin style batch multiplication.
Definition: biggroup_goblin.hpp:30
static chain_add_accumulator chain_add(const element &p1, const chain_add_accumulator &accumulator)
Definition: biggroup_impl.hpp:183
std::array< element, 2 > add_sub(const element &other) const
Compute (*this) + other AND (*this) - other as a size-2 array.
Definition: biggroup_impl.hpp:108
static chain_add_accumulator chain_add_start(const element &p1, const element &p2)
Definition: biggroup_impl.hpp:165
element operator*(const Fr &other) const
Definition: biggroup_impl.hpp:679
static element chain_add_end(const chain_add_accumulator &accumulator)
Definition: biggroup_impl.hpp:228
Definition: field.hpp:10
Contains all the headers required to adequately compile the types defined in circuit_builders_fwd....
Definition: circuit_builders.hpp:11
Definition: circuit_builders.hpp:15
Definition: circuit_builders.hpp:17
Definition: biggroup.hpp:17
BBERG_INLINE constexpr field sqr() const noexcept
Definition: field_impl.hpp:61