38 const std::vector<Fr>& big_scalars,
40 const std::vector<Fr>& small_scalars,
41 const Fr& generator_scalar,
42 const size_t max_num_small_bits)
45 for (
auto element : big_points) {
52 constexpr size_t NUM_BIG_POINTS = 4;
56 create_endo_pair_quad_lookup_table({ big_points[0], big_points[1], big_points[2], big_points[3] });
57 auto& big_table = big_table_pair.first;
58 auto& endo_table = big_table_pair.second;
64 const auto split_into_endomorphism_scalars = [ctx](
const Fr& scalar) {
65 bb::fr k = scalar.get_value();
72 scalar.assert_equal(scalar_k1 - scalar_k2 * beta);
76 for (
size_t i = 0; i < NUM_BIG_POINTS; ++i) {
77 const auto [scalar_k1, scalar_k2] = split_into_endomorphism_scalars(big_scalars[i]);
78 big_naf_entries.emplace_back(compute_naf(scalar_k1, max_num_small_bits));
79 endo_naf_entries.emplace_back(compute_naf(scalar_k2, max_num_small_bits));
82 const auto [generator_k1, generator_k2] = split_into_endomorphism_scalars(generator_scalar);
85 const auto generator_table =
86 element::eight_bit_fixed_base_table(element::eight_bit_fixed_base_table::CurveType::BN254,
false);
87 const auto generator_endo_table =
88 element::eight_bit_fixed_base_table(element::eight_bit_fixed_base_table::CurveType::BN254,
true);
90 for (
size_t i = 0; i < small_points.size(); ++i) {
91 small_naf_entries.emplace_back(compute_naf(small_scalars[i], max_num_small_bits));
94 const size_t num_rounds = max_num_small_bits;
96 const auto offset_generators = compute_offset_generators(num_rounds);
99 init_point = element::chain_add(endo_table[0], init_point);
100 init_point = element::chain_add(big_table[0], init_point);
102 element accumulator = element::chain_add_end(init_point);
104 const auto get_point_to_add = [&](
size_t naf_index) {
108 for (
size_t i = 0; i < small_points.size(); ++i) {
109 small_nafs.emplace_back(small_naf_entries[i][naf_index]);
111 for (
size_t i = 0; i < NUM_BIG_POINTS; ++i) {
112 big_nafs.emplace_back(big_naf_entries[i][naf_index]);
113 endo_nafs.emplace_back(endo_naf_entries[i][naf_index]);
117 to_add = element::chain_add(big_table.get({ big_nafs[0], big_nafs[1], big_nafs[2], big_nafs[3] }), to_add);
118 to_add = element::chain_add(endo_table.get({ endo_nafs[0], endo_nafs[1], endo_nafs[2], endo_nafs[3] }), to_add);
124 constexpr size_t num_rounds_per_iteration = 4;
127 static_assert(num_rounds_per_iteration < 8);
129 size_t num_iterations = num_rounds / num_rounds_per_iteration;
130 num_iterations += ((num_iterations * num_rounds_per_iteration) == num_rounds) ? 0 : 1;
131 const size_t num_rounds_per_final_iteration = (num_rounds - 1) - ((num_iterations - 1) * num_rounds_per_iteration);
133 size_t generator_idx = 0;
134 for (
size_t i = 0; i < num_iterations; ++i) {
136 const size_t inner_num_rounds =
137 (i != num_iterations - 1) ? num_rounds_per_iteration : num_rounds_per_final_iteration;
140 for (
size_t j = 0; j < inner_num_rounds; ++j) {
141 to_add.emplace_back(get_point_to_add(i * num_rounds_per_iteration + j + 1));
144 bool add_generator_this_round =
false;
146 for (
size_t j = 0; j < inner_num_rounds; ++j) {
147 add_generator_this_round = ((i * num_rounds_per_iteration + j) % 8) == 6;
148 if (add_generator_this_round) {
153 if (add_generator_this_round) {
154 to_add[add_idx] = element::chain_add(generator_table[generator_wnaf[generator_idx]], to_add[add_idx]);
156 element::chain_add(generator_endo_table[generator_endo_wnaf[generator_idx]], to_add[add_idx]);
162 for (
size_t i = 0; i < small_points.size(); ++i) {
163 element skew = accumulator - small_points[i];
164 Fq out_x = accumulator.
x.conditional_select(skew.
x, small_naf_entries[i][num_rounds]);
165 Fq out_y = accumulator.
y.conditional_select(skew.
y, small_naf_entries[i][num_rounds]);
166 accumulator =
element(out_x, out_y);
172 for (
size_t i = 0; i < NUM_BIG_POINTS; ++i) {
173 element skew_point = big_points[i];
174 skew_point.
x *= beta;
175 element skew = accumulator + skew_point;
176 Fq out_x = accumulator.
x.conditional_select(skew.
x, endo_naf_entries[i][num_rounds]);
177 Fq out_y = accumulator.
y.conditional_select(skew.
y, endo_naf_entries[i][num_rounds]);
178 accumulator =
element(out_x, out_y);
181 element skew = accumulator - generator_table[128];
182 Fq out_x = accumulator.
x.conditional_select(skew.
x,
bool_ct(generator_wnaf[generator_wnaf.size() - 1]));
183 Fq out_y = accumulator.
y.conditional_select(skew.
y,
bool_ct(generator_wnaf[generator_wnaf.size() - 1]));
184 accumulator =
element(out_x, out_y);
187 element skew = accumulator - generator_endo_table[128];
188 Fq out_x = accumulator.
x.conditional_select(skew.
x,
bool_ct(generator_endo_wnaf[generator_wnaf.size() - 1]));
189 Fq out_y = accumulator.
y.conditional_select(skew.
y,
bool_ct(generator_endo_wnaf[generator_wnaf.size() - 1]));
190 accumulator =
element(out_x, out_y);
193 for (
size_t i = 0; i < NUM_BIG_POINTS; ++i) {
194 element skew = accumulator - big_points[i];
195 Fq out_x = accumulator.
x.conditional_select(skew.
x, big_naf_entries[i][num_rounds]);
196 Fq out_y = accumulator.
y.conditional_select(skew.
y, big_naf_entries[i][num_rounds]);
197 accumulator =
element(out_x, out_y);
199 accumulator = accumulator - offset_generators.second;
219 const std::vector<Fr>& big_scalars,
221 const std::vector<Fr>& small_scalars,
222 const size_t max_num_small_bits)
227 const size_t num_big_points = big_points.size();
228 const size_t num_small_points = small_points.size();
230 for (
auto element : big_points) {
238 std::vector<Fr> scalars;
240 std::vector<Fr> endo_scalars;
255 for (
size_t i = 0; i < num_big_points; ++i) {
256 Fr scalar = big_scalars[i];
269 scalar_k1.set_origin_tag(scalar.get_origin_tag());
270 scalar_k2.set_origin_tag(scalar.get_origin_tag());
273 scalar.assert_equal(scalar_k1 - scalar_k2 * lambda);
274 scalars.push_back(scalar_k1);
275 endo_scalars.push_back(scalar_k2);
277 points.push_back(point);
283 point.
y.self_reduce();
284 endo_points.push_back(point);
286 for (
size_t i = 0; i < num_small_points; ++i) {
287 points.push_back(small_points[i]);
288 scalars.push_back(small_scalars[i]);
295 for (
size_t i = 0; i < points.size(); i++) {
296 union_tag =
OriginTag(union_tag,
OriginTag(points[i].get_origin_tag(), scalars[i].get_origin_tag()));
334 const size_t num_rounds = max_num_small_bits;
335 const size_t num_points = points.size();
337 for (
size_t i = 0; i < num_points; ++i) {
338 naf_entries.emplace_back(compute_naf(scalars[i], max_num_small_bits));
345 const auto offset_generators = compute_offset_generators(num_rounds);
369 for (
size_t i = 1; i < num_rounds / 2; ++i) {
372 for (
size_t j = 0; j < points.size(); ++j) {
373 nafs.emplace_back(naf_entries[j][i * 2 - 1]);
389 for (
size_t j = 0; j < points.size(); ++j) {
390 nafs[j] = (naf_entries[j][i * 2]);
399 if ((num_rounds & 0x01ULL) == 0x00ULL) {
401 for (
size_t j = 0; j < points.size(); ++j) {
402 nafs.emplace_back(naf_entries[j][num_rounds - 1]);
426 for (
size_t i = 0; i < num_points; ++i) {
427 element skew = accumulator - points[i];
428 Fq out_x = accumulator.
x.conditional_select(skew.
x, naf_entries[i][num_rounds]);
429 Fq out_y = accumulator.
y.conditional_select(skew.
y, naf_entries[i][num_rounds]);
430 accumulator =
element(out_x, out_y);
434 accumulator = accumulator - offset_generators.second;