Zeth - Zerocash on Ethereum  0.8
Reference implementation of the Zeth protocol by Clearmatics
powersoftau_utils.tcc
Go to the documentation of this file.
1 // Copyright (c) 2015-2022 Clearmatics Technologies Ltd
2 //
3 // SPDX-License-Identifier: LGPL-3.0+
4 
5 #ifndef __ZETH_MPC_GROTH16_POWERSOFTAU_UTILS_TCC__
6 #define __ZETH_MPC_GROTH16_POWERSOFTAU_UTILS_TCC__
7 
8 #include "libzeth/core/utils.hpp"
9 #include "libzeth/mpc/groth16/powersoftau_utils.hpp"
10 
11 #include <libff/algebra/curves/alt_bn128/alt_bn128_pp.hpp>
12 #include <libff/algebra/fields/fp.hpp>
13 #include <thread>
14 
15 namespace libzeth
16 {
17 
18 namespace
19 {
20 
21 template<mp_size_t n, const libff::bigint<n> &modulus>
22 void to_montgomery_repr(libff::Fp_model<n, modulus> &m)
23 {
24  m.mul_reduce(libff::Fp_model<n, modulus>::Rsquared);
25 }
26 
27 template<mp_size_t n, const libff::bigint<n> &modulus>
28 libff::Fp_model<n, modulus> from_montgomery_repr(libff::Fp_model<n, modulus> m)
29 {
30  libff::Fp_model<n, modulus> tmp;
31  tmp.mont_repr.data[0] = 1;
32  tmp.mul_reduce(m.mont_repr);
33  return tmp;
34 }
35 
36 template<mp_size_t n, const libff::bigint<n> &modulus>
37 std::istream &read_powersoftau_fp(
38  std::istream &in, libff::Fp_model<n, modulus> &out)
39 {
40  const size_t data_size = sizeof(libff::bigint<n>);
41  char *bytes = (char *)&out;
42  in.read(bytes, data_size);
43 
44  std::reverse(&bytes[0], &bytes[data_size]);
45  to_montgomery_repr(out);
46 
47  return in;
48 }
49 
50 template<mp_size_t n, const libff::bigint<n> &modulus>
51 void write_powersoftau_fp(
52  std::ostream &out, const libff::Fp_model<n, modulus> &fp)
53 {
54  libff::Fp_model<n, modulus> copy = from_montgomery_repr(fp);
55  std::reverse((char *)&copy, (char *)(&copy + 1));
56  out.write((const char *)&copy, sizeof(mp_limb_t) * n);
57 }
58 
59 template<mp_size_t n, const libff::bigint<n> &modulus>
60 std::istream &read_powersoftau_fp2(
61  std::istream &in, libff::Fp2_model<n, modulus> &el)
62 {
63  // Fq2 data is packed into a single 512 bit integer as:
64  //
65  // c1 * modulus + c0
66  libff::bigint<2 * n> packed;
67  in >> packed;
68  std::reverse((uint8_t *)&packed, (uint8_t *)((&packed) + 1));
69 
70  libff::bigint<n + 1> c1;
71 
72  mpn_tdiv_qr(
73  c1.data, // quotient
74  el.coeffs[0].mont_repr.data, // remainder
75  0,
76  packed.data,
77  n * 2,
78  modulus.data,
79  n);
80 
81  for (size_t i = 0; i < n; ++i) {
82  el.coeffs[1].mont_repr.data[i] = c1.data[i];
83  }
84 
85  to_montgomery_repr(el.coeffs[0]);
86  to_montgomery_repr(el.coeffs[1]);
87 
88  return in;
89 }
90 
91 template<mp_size_t n, const libff::bigint<n> &modulus>
92 void write_powersoftau_fp2(
93  std::ostream &out, const libff::Fp2_model<n, modulus> &fp2)
94 {
95  // Fq2 data is packed into a single 512 bit integer as:
96  //
97  // c1 * modulus + c0
98  libff::Fp_model<n, modulus> c0 = from_montgomery_repr(fp2.coeffs[0]);
99  libff::Fp_model<n, modulus> c1 = from_montgomery_repr(fp2.coeffs[1]);
100 
101  libff::bigint<2 * n> packed;
102  packed.clear();
103  for (size_t i = 0; i < n; ++i) {
104  packed.data[i] = c0.mont_repr.data[i];
105  }
106 
107  for (size_t i = 0; i < n; ++i) {
108  mp_limb_t carryout = mpn_addmul_1(
109  &packed.data[i], modulus.data, n, c1.mont_repr.data[i]);
110  assert(packed.data[n + i] == 0);
111  carryout = mpn_add_1(
112  &packed.data[n + i], &packed.data[n + i], n - i, carryout);
113  assert(carryout == 0);
114  }
115 
116  std::reverse((uint8_t *)&packed, (uint8_t *)((&packed) + 1));
117  out.write((const char *)&packed, sizeof(packed));
118 }
119 
120 // Use the technique described in Section 3 of "A multi-party protocol
121 // for constructing the public parameters of the Pinocchio zk-SNARK"
122 // (https://eprint.iacr.org/2017/602.pdf)
123 // to efficiently evaluate Lagrange polynomials ${L_i(x)}_i$ for the
124 // $d=2^n$-roots of unity, given powers ${x^i}_i$ for $i=0..d-1$.
125 template<typename Fr, typename Gr>
126 static void compute_lagrange_from_powers(
127  std::vector<Gr> &powers, const Fr &omega_inv)
128 {
129  libfqfft::_basic_radix2_FFT<Fr, Gr>(powers, omega_inv);
130  const Fr n_inv = Fr(powers.size()).inverse();
131  for (auto &a : powers) {
132  a = n_inv * a;
133  }
134 }
135 
136 // Given two sequences `as` and `bs` of group elements, compute
137 // a_accum = as[0] * r_0 + ... + as[n] * r_n
138 // b_accum = bs[0] * r_0 + ... + bs[n] * r_n
139 // for random scalars r_0 ... r_n.
140 template<typename ppT, typename G>
141 void random_linear_combination(
142  const std::vector<G> &as, const std::vector<G> &bs, G &a_accum, G &b_accum)
143 {
144  if (as.size() != bs.size()) {
145  throw std::invalid_argument(
146  "vector size mismatch (random_linear_comb)");
147  }
148 
149  a_accum = G::zero();
150  b_accum = G::zero();
151 
152  // Split across threads, each one accumulating into its own thread-local
153  // variable, and then (atomically) adding that to the global a1_accum and
154  // b1_accum values. These final sums are then used in the final pairing
155  // check.
156 #ifdef MULTICORE
157 #pragma omp parallel shared(a_accum, b_accum)
158 #endif
159  {
160  G a_thread_accum = G::zero();
161  G b_thread_accum = G::zero();
162 
163  const size_t scalar_bits = libff::Fr<ppT>::num_bits;
164  const size_t window_size = libff::wnaf_opt_window_size<G>(scalar_bits);
165  std::vector<long> wnaf;
166 
167 #ifdef MULTICORE
168 #pragma omp for
169 #endif
170  for (size_t i = 0; i < as.size(); ++i) {
171  const libff::Fr<ppT> r = libff::Fr<ppT>::random_element();
172  update_wnaf(wnaf, window_size, r.as_bigint());
173  G r_ai = fixed_window_wnaf_exp(window_size, as[i], wnaf);
174  G r_bi = fixed_window_wnaf_exp(window_size, bs[i], wnaf);
175 
176  a_thread_accum = a_thread_accum + r_ai;
177  b_thread_accum = b_thread_accum + r_bi;
178  }
179 
180 #ifdef MULTICORE
181 #pragma omp critical
182 #endif
183  {
184  a_accum = a_accum + a_thread_accum;
185  b_accum = b_accum + b_thread_accum;
186  }
187  }
188 }
189 
190 // Similar to random_linear_combination, but compute:
191 // a_accum = as[0] * r_0 + ... + as[n-1] * r_{n-1}
192 // b_accum = bs[1] * r_0 + ... + bs[n ] * r_{n-1}
193 // for checking consistent ratio of consecutive entries.
194 template<typename ppT, typename G>
195 void random_linear_combination_consecutive(
196  const std::vector<G> &as, G &a_accum, G &b_accum)
197 {
198  a_accum = G::zero();
199  b_accum = G::zero();
200 
201  const size_t num_entries = as.size() - 1;
202 
203 #ifdef MULTICORE
204 #pragma omp parallel shared(a_accum, b_accum)
205 #endif
206  {
207  G a_thread_accum = G::zero();
208  G b_thread_accum = G::zero();
209 
210  const size_t scalar_bits = libff::Fr<ppT>::num_bits;
211  const size_t window_size = libff::wnaf_opt_window_size<G>(scalar_bits);
212  std::vector<long> wnaf;
213 
214 #ifdef MULTICORE
215 #pragma omp for
216 #endif
217  for (size_t i = 0; i < num_entries; ++i) {
218  const libff::Fr<ppT> r = libff::Fr<ppT>::random_element();
219  update_wnaf(wnaf, window_size, r.as_bigint());
220  G r_ai = fixed_window_wnaf_exp(window_size, as[i], wnaf);
221  G r_bi = fixed_window_wnaf_exp(window_size, as[i + 1], wnaf);
222 
223  a_thread_accum = a_thread_accum + r_ai;
224  b_thread_accum = b_thread_accum + r_bi;
225  }
226 
227 #ifdef MULTICORE
228 #pragma omp critical
229 #endif
230  {
231  a_accum = a_accum + a_thread_accum;
232  b_accum = b_accum + b_thread_accum;
233  }
234  }
235 }
236 
237 } // namespace
238 
239 // -----------------------------------------------------------------------------
240 // powersoftau
241 // -----------------------------------------------------------------------------
242 
243 template<typename ppT>
244 srs_powersoftau<ppT>::srs_powersoftau(
245  libff::G1_vector<ppT> &&tau_powers_g1,
246  libff::G2_vector<ppT> &&tau_powers_g2,
247  libff::G1_vector<ppT> &&alpha_tau_powers_g1,
248  libff::G1_vector<ppT> &&beta_tau_powers_g1,
249  const libff::G2<ppT> &beta_g2)
250  : tau_powers_g1(std::move(tau_powers_g1))
251  , tau_powers_g2(std::move(tau_powers_g2))
252  , alpha_tau_powers_g1(std::move(alpha_tau_powers_g1))
253  , beta_tau_powers_g1(std::move(beta_tau_powers_g1))
254  , beta_g2(beta_g2)
255 {
256 }
257 
258 template<typename ppT> bool srs_powersoftau<ppT>::is_well_formed() const
259 {
260  return libzeth::container_is_well_formed(tau_powers_g1) &&
261  libzeth::container_is_well_formed(tau_powers_g2) &&
262  libzeth::container_is_well_formed(alpha_tau_powers_g1) &&
263  libzeth::container_is_well_formed(beta_tau_powers_g1) &&
264  beta_g2.is_well_formed();
265 }
266 
267 // -----------------------------------------------------------------------------
268 // powersoftau_lagrange_evaluations
269 // -----------------------------------------------------------------------------
270 
271 template<typename ppT>
272 srs_lagrange_evaluations<ppT>::srs_lagrange_evaluations(
273  size_t degree,
274  std::vector<libff::G1<ppT>> &&lagrange_g1,
275  std::vector<libff::G2<ppT>> &&lagrange_g2,
276  std::vector<libff::G1<ppT>> &&alpha_lagrange_g1,
277  std::vector<libff::G1<ppT>> &&beta_lagrange_g1)
278  : degree(degree)
279  , lagrange_g1(std::move(lagrange_g1))
280  , lagrange_g2(std::move(lagrange_g2))
281  , alpha_lagrange_g1(std::move(alpha_lagrange_g1))
282  , beta_lagrange_g1(std::move(beta_lagrange_g1))
283 {
284 }
285 
286 template<typename ppT>
287 bool srs_lagrange_evaluations<ppT>::is_well_formed() const
288 {
289  return container_is_well_formed(lagrange_g1) &&
290  container_is_well_formed(lagrange_g2) &&
291  container_is_well_formed(alpha_lagrange_g1) &&
292  container_is_well_formed(beta_lagrange_g1);
293 }
294 
295 template<typename ppT>
296 void srs_lagrange_evaluations<ppT>::write(std::ostream &out) const
297 {
298  check_well_formed(*this, "powersoftau (write)");
299 
300  out.write((const char *)&degree, sizeof(degree));
301  for (const libff::G1<ppT> &l_g1 : lagrange_g1) {
302  out << l_g1;
303  }
304  for (const libff::G2<ppT> &l_g2 : lagrange_g2) {
305  out << l_g2;
306  }
307  for (const libff::G1<ppT> &alpha_l_g1 : alpha_lagrange_g1) {
308  out << alpha_l_g1;
309  }
310  for (const libff::G1<ppT> &beta_l_g1 : beta_lagrange_g1) {
311  out << beta_l_g1;
312  }
313 }
314 
315 template<typename ppT>
316 srs_lagrange_evaluations<ppT> srs_lagrange_evaluations<ppT>::read(
317  std::istream &in)
318 {
319  size_t degree;
320  in.read((char *)&degree, sizeof(degree));
321 
322  std::vector<libff::G1<ppT>> lagrange_g1(degree);
323  std::vector<libff::G2<ppT>> lagrange_g2(degree);
324  std::vector<libff::G1<ppT>> alpha_lagrange_g1(degree);
325  std::vector<libff::G1<ppT>> beta_lagrange_g1(degree);
326 
327  for (libff::G1<ppT> &l_g1 : lagrange_g1) {
328  in >> l_g1;
329  }
330  for (libff::G2<ppT> &l_g2 : lagrange_g2) {
331  in >> l_g2;
332  }
333  for (libff::G1<ppT> &alpha_l_g1 : alpha_lagrange_g1) {
334  in >> alpha_l_g1;
335  }
336  for (libff::G1<ppT> &beta_l_g1 : beta_lagrange_g1) {
337  in >> beta_l_g1;
338  }
339 
340  srs_lagrange_evaluations<ppT> lagrange(
341  degree,
342  std::move(lagrange_g1),
343  std::move(lagrange_g2),
344  std::move(alpha_lagrange_g1),
345  std::move(beta_lagrange_g1));
346  check_well_formed(lagrange, "lagrange (read)");
347  return lagrange;
348 }
349 
350 template<typename ppT>
351 srs_powersoftau<ppT> dummy_powersoftau_from_secrets(
352  const libff::Fr<ppT> &tau,
353  const libff::Fr<ppT> &alpha,
354  const libff::Fr<ppT> &beta,
355  size_t n)
356 {
357  libff::enter_block("dummy_phase1_from_secrets");
358 
359  // Compute powers. Note zero-th power is included (alpha_g1 etc
360  // are provided in this way), so to support order N polynomials,
361  // N+1 entries are required.
362  const size_t num_tau_powers_g1 = 2 * n - 2 + 1;
363  libff::G1_vector<ppT> tau_powers_g1;
364  libff::G2_vector<ppT> tau_powers_g2;
365  libff::G1_vector<ppT> alpha_tau_powers_g1;
366  libff::G1_vector<ppT> beta_tau_powers_g1;
367 
368  libff::enter_block("tau powers");
369  std::vector<libff::Fr<ppT>> tau_powers(num_tau_powers_g1);
370  tau_powers[0] = libff::Fr<ppT>::one();
371  for (size_t i = 1; i < num_tau_powers_g1; ++i) {
372  tau_powers[i] = tau * tau_powers[i - 1];
373  }
374  libff::leave_block("tau powers");
375 
376  libff::enter_block("window tables");
377  const size_t window_size = libff::get_exp_window_size<libff::G1<ppT>>(n);
378  const size_t window_size_tau_g1 =
379  libff::get_exp_window_size<libff::G1<ppT>>(2 * n - 1);
380 
381  libff::window_table<libff::G1<ppT>> tau_g1_table;
382  libff::window_table<libff::G2<ppT>> tau_g2_table;
383  libff::window_table<libff::G1<ppT>> alpha_tau_g1_table;
384  libff::window_table<libff::G1<ppT>> beta_tau_g1_table;
385  {
386  std::thread tau_g1_table_thread([&tau_g1_table, window_size_tau_g1]() {
387  tau_g1_table = libff::get_window_table(
388  libff::G1<ppT>::size_in_bits(),
389  window_size_tau_g1,
390  libff::G1<ppT>::one());
391  });
392 
393  std::thread tau_g2_table_thread([&tau_g2_table, window_size]() {
394  tau_g2_table = libff::get_window_table(
395  libff::G2<ppT>::size_in_bits(),
396  window_size,
397  libff::G2<ppT>::one());
398  });
399 
400  std::thread alpha_tau_g1_table_thread(
401  [&alpha_tau_g1_table, window_size, alpha]() {
402  alpha_tau_g1_table = libff::get_window_table(
403  libff::G1<ppT>::size_in_bits(),
404  window_size,
405  alpha * libff::G1<ppT>::one());
406  });
407 
408  std::thread beta_tau_g1_table_thread(
409  [&beta_tau_g1_table, window_size, beta]() {
410  beta_tau_g1_table = libff::get_window_table(
411  libff::G1<ppT>::size_in_bits(),
412  window_size,
413  beta * libff::G1<ppT>::one());
414  });
415 
416  tau_g1_table_thread.join();
417  tau_g2_table_thread.join();
418  alpha_tau_g1_table_thread.join();
419  beta_tau_g1_table_thread.join();
420  }
421  libff::leave_block("window tables");
422 
423  libff::enter_block("tau_g1 powers");
424  tau_powers_g1 = libff::batch_exp(
425  libff::G1<ppT>::size_in_bits(),
426  window_size_tau_g1,
427  tau_g1_table,
428  tau_powers);
429  libff::leave_block("tau_g1 powers");
430 
431  libff::enter_block("tau_g2 powers");
432  tau_powers_g2 = libff::batch_exp(
433  libff::G2<ppT>::size_in_bits(),
434  window_size,
435  tau_g2_table,
436  tau_powers,
437  n);
438  libff::leave_block("tau_g2 powers");
439 
440  libff::enter_block("alpha_tau_g1 powers");
441  alpha_tau_powers_g1 = libff::batch_exp(
442  libff::G1<ppT>::size_in_bits(),
443  window_size,
444  alpha_tau_g1_table,
445  tau_powers,
446  n);
447  libff::leave_block("alpha_tau_g1 powers");
448 
449  libff::enter_block("beta_tau_g1 powers");
450  beta_tau_powers_g1 = libff::batch_exp(
451  libff::G1<ppT>::size_in_bits(),
452  window_size,
453  beta_tau_g1_table,
454  tau_powers,
455  n);
456  libff::leave_block("beta_tau_g1 powers");
457 
458  libff::leave_block("dummy_phase1_from_secrets");
459  return srs_powersoftau<ppT>(
460  std::move(tau_powers_g1),
461  std::move(tau_powers_g2),
462  std::move(alpha_tau_powers_g1),
463  std::move(beta_tau_powers_g1),
464  beta * libff::G2<ppT>::one());
465 }
466 
467 template<typename ppT> srs_powersoftau<ppT> dummy_powersoftau(size_t n)
468 {
469  libff::Fr<ppT> tau = libff::Fr<ppT>::random_element();
470  libff::Fr<ppT> alpha = libff::Fr<ppT>::random_element();
471  libff::Fr<ppT> beta = libff::Fr<ppT>::random_element();
472 
473  return dummy_powersoftau_from_secrets<ppT>(tau, alpha, beta, n);
474 }
475 
476 template<typename ppT>
477 void read_powersoftau_fr(std::istream &in, libff::Fr<ppT> &out)
478 {
479  read_powersoftau_fp(in, out);
480 }
481 
482 template<typename ppT>
483 void read_powersoftau_g1(std::istream &in, libff::G1<ppT> &out)
484 {
485  uint8_t marker;
486  in.read((char *)&marker, 1);
487 
488  switch (marker) {
489  case 0x00:
490  // zero
491  out = libff::G1<ppT>::zero();
492  break;
493  case 0x04: {
494  // Uncompressed
495  libff::Fq<ppT> x;
496  libff::Fq<ppT> y;
497  read_powersoftau_fp(in, x);
498  read_powersoftau_fp(in, y);
499  out = libff::G1<ppT>(x, y, libff::Fq<ppT>::one());
500  break;
501  }
502  default:
503  assert(false);
504  break;
505  }
506 }
507 
508 /// Structure of G2 varies between pairings, so difficult to implement
509 /// generically. A specialized version for alt_bn128_pp is provided for
510 /// compatibility with https://github.com/clearmatics/powersoftau.
511 template<>
512 void read_powersoftau_g2<libff::alt_bn128_pp>(
513  std::istream &, libff::alt_bn128_G2 &);
514 
515 template<typename ppT>
516 void read_powersoftau_g2(std::istream &in, libff::G2<ppT> &out)
517 {
518  in >> out;
519 }
520 
521 template<typename ppT>
522 void write_powersoftau_fr(std::ostream &out, const libff::Fr<ppT> &fr)
523 {
524  write_powersoftau_fp(out, fr);
525 }
526 
527 template<typename ppT>
528 void write_powersoftau_g1(std::ostream &out, const libff::G1<ppT> &g1)
529 {
530  if (g1.is_zero()) {
531  const uint8_t zero = 0;
532  out.write((const char *)&zero, 1);
533  return;
534  }
535 
536  libff::G1<ppT> copy(g1);
537  copy.to_affine_coordinates();
538 
539  const uint8_t marker = 0x04;
540  out.write((const char *)&marker, 1);
541  write_powersoftau_fp(out, copy.X);
542  write_powersoftau_fp(out, copy.Y);
543 }
544 
545 /// Structure of G2 varies between pairings, so difficult to implement
546 /// generically. A specialized version for alt_bn128_pp is provided for
547 /// compatibility with https://github.com/clearmatics/powersoftau.
548 template<>
549 void write_powersoftau_g2<libff::alt_bn128_pp>(
550  std::ostream &, const libff::alt_bn128_G2 &);
551 
552 template<typename ppT>
553 void write_powersoftau_g2(std::ostream &out, const libff::G2<ppT> &g2)
554 {
555  out << g2;
556 }
557 
558 template<typename ppT>
559 srs_powersoftau<ppT> powersoftau_load(std::istream &in, size_t n)
560 {
561  using G1 = libff::G1<ppT>;
562  using G2 = libff::G2<ppT>;
563 
564  // From:
565  //
566  // https://github.com/clearmatics/powersoftau
567  //
568  // Assume the stream is the final challenge file from the
569  // powersoftau protocol. Load the Accumulator object.
570  //
571  // File is structured:
572  //
573  // [prev_resp_hash : uint8_t[64]]
574  // [accumulator : Accumulator]
575  //
576  // From src/lib.rs:
577  //
578  // pub struct Accumulator {
579  // /// tau^0, tau^1, tau^2, ..., tau^{TAU_POWERS_G1_LENGTH - 1}
580  // pub tau_powers_g1: Vec<G1>,
581  // /// tau^0, tau^1, tau^2, ..., tau^{TAU_POWERS_LENGTH - 1}
582  // pub tau_powers_g2: Vec<G2>,
583  // /// alpha * tau^0, alpha * tau^1, alpha * tau^2, ..., alpha *
584  // tau^{TAU_POWERS_LENGTH - 1} pub alpha_tau_powers_g1: Vec<G1>,
585  // /// beta * tau^0, beta * tau^1, beta * tau^2, ..., beta *
586  // tau^{TAU_POWERS_LENGTH - 1} pub beta_tau_powers_g1: Vec<G1>,
587  // /// beta
588  // pub beta_g2: G2
589  // }
590  uint8_t hash[64];
591  in.read((char *)(&hash[0]), sizeof(hash));
592 
593  const size_t num_powers_of_tau = 2 * n - 1;
594 
595  std::vector<G1> tau_powers_g1(num_powers_of_tau);
596  for (size_t i = 0; i < num_powers_of_tau; ++i) {
597  read_powersoftau_g1<ppT>(in, tau_powers_g1[i]);
598  }
599  if (tau_powers_g1[0] != G1::one()) {
600  throw std::invalid_argument("invalid powersoftau file?");
601  }
602 
603  std::vector<G2> tau_powers_g2(n);
604  for (size_t i = 0; i < n; ++i) {
605  read_powersoftau_g2<ppT>(in, tau_powers_g2[i]);
606  }
607  if (tau_powers_g2[0] != G2::one()) {
608  throw std::invalid_argument("invalid powersoftau file?");
609  }
610 
611  std::vector<G1> alpha_tau_powers_g1(n);
612  for (size_t i = 0; i < n; ++i) {
613  read_powersoftau_g1<ppT>(in, alpha_tau_powers_g1[i]);
614  }
615 
616  std::vector<G1> beta_tau_powers_g1(n);
617  for (size_t i = 0; i < n; ++i) {
618  read_powersoftau_g1<ppT>(in, beta_tau_powers_g1[i]);
619  }
620 
621  G2 beta_g2;
622  read_powersoftau_g2<ppT>(in, beta_g2);
623 
624  srs_powersoftau<ppT> pot(
625  std::move(tau_powers_g1),
626  std::move(tau_powers_g2),
627  std::move(alpha_tau_powers_g1),
628  std::move(beta_tau_powers_g1),
629  beta_g2);
630  check_well_formed(pot, "powersoftau (load)");
631  return pot;
632 }
633 
634 template<typename ppT>
635 void powersoftau_write(std::ostream &out, const srs_powersoftau<ppT> &pot)
636 {
637  check_well_formed(pot, "powersoftau (write)");
638 
639  // Fake the hash
640  uint8_t hash[64];
641  memset(hash, 0, sizeof(hash));
642  out.write((char *)(&hash[0]), sizeof(hash));
643 
644  const size_t n = pot.tau_powers_g2.size();
645  const size_t num_powers_of_tau = 2 * n - 1;
646  for (size_t i = 0; i < num_powers_of_tau; ++i) {
647  write_powersoftau_g1<ppT>(out, pot.tau_powers_g1[i]);
648  }
649  for (size_t i = 0; i < n; ++i) {
650  write_powersoftau_g2<ppT>(out, pot.tau_powers_g2[i]);
651  }
652  for (size_t i = 0; i < n; ++i) {
653  write_powersoftau_g1<ppT>(out, pot.alpha_tau_powers_g1[i]);
654  }
655  for (size_t i = 0; i < n; ++i) {
656  write_powersoftau_g1<ppT>(out, pot.beta_tau_powers_g1[i]);
657  }
658  write_powersoftau_g2<ppT>(out, pot.beta_g2);
659 }
660 
661 template<typename ppT>
662 bool same_ratio(
663  const libff::G1<ppT> &a1,
664  const libff::G1<ppT> &b1,
665  const libff::G2<ppT> &a2,
666  const libff::G2<ppT> &b2)
667 {
668  const libff::G1_precomp<ppT> &a1_precomp = ppT::precompute_G1(a1);
669  const libff::G1_precomp<ppT> &b1_precomp = ppT::precompute_G1(b1);
670  const libff::G2_precomp<ppT> &a2_precomp = ppT::precompute_G2(a2);
671  const libff::G2_precomp<ppT> &b2_precomp = ppT::precompute_G2(b2);
672 
673  const libff::Fqk<ppT> a1b2 = ppT::miller_loop(a1_precomp, b2_precomp);
674  const libff::Fqk<ppT> b1a2 = ppT::miller_loop(b1_precomp, a2_precomp);
675 
676  const libff::GT<ppT> a1b2_gt = ppT::final_exponentiation(a1b2);
677  const libff::GT<ppT> b1a2_gt = ppT::final_exponentiation(b1a2);
678 
679  // Decide whether ratio a1:b1 in G1 equals a2:b2 in G2 by checking:
680  // e(a1, b2) =?= e(b1, a2)
681  return a1b2_gt == b1a2_gt;
682 }
683 
684 template<typename ppT>
685 bool same_ratio_vectors(
686  const std::vector<libff::G1<ppT>> &a1s,
687  const std::vector<libff::G1<ppT>> &b1s,
688  const libff::G2<ppT> &a2,
689  const libff::G2<ppT> &b2)
690 {
691  using G1 = libff::G1<ppT>;
692 
693  libff::enter_block("call to same_ratio_vectors (G1)");
694  if (a1s.size() != b1s.size()) {
695  throw std::invalid_argument("vector size mismatch in same_ratio_batch");
696  }
697 
698  libff::enter_block("accumulating random combination");
699  G1 a1_accum;
700  G1 b1_accum;
701  random_linear_combination<ppT>(a1s, b1s, a1_accum, b1_accum);
702  libff::leave_block("accumulating random combination");
703 
704  const bool same = same_ratio<ppT>(a1_accum, b1_accum, a2, b2);
705  libff::leave_block("call to same_ratio_vectors (G1)");
706  return same;
707 }
708 
709 template<typename ppT>
710 bool same_ratio_vectors(
711  const libff::G1<ppT> &a1,
712  const libff::G1<ppT> &b1,
713  const std::vector<libff::G2<ppT>> &a2s,
714  const std::vector<libff::G2<ppT>> &b2s)
715 {
716  using G2 = libff::G2<ppT>;
717 
718  libff::enter_block("call to same_ratio_vectors (G2)");
719  if (a2s.size() != b2s.size()) {
720  throw std::invalid_argument("vector size mismatch in same_ratio_batch");
721  }
722 
723  libff::enter_block("accumulating random combination");
724  G2 a2_accum;
725  G2 b2_accum;
726  random_linear_combination<ppT>(a2s, b2s, a2_accum, b2_accum);
727  libff::leave_block("accumulating random combination");
728 
729  const bool same = same_ratio<ppT>(a1, b1, a2_accum, b2_accum);
730  libff::leave_block("call to same_ratio_vectors (G2)");
731  return same;
732 }
733 
734 template<typename ppT>
735 bool same_ratio_consecutive(
736  const std::vector<libff::G1<ppT>> &a1s,
737  const libff::G2<ppT> &a2,
738  const libff::G2<ppT> &b2)
739 {
740  using G1 = libff::G1<ppT>;
741 
742  libff::enter_block("call to same_ratio_consecutive (G1)");
743 
744  libff::enter_block("accumulating random combination");
745  G1 a1_accum;
746  G1 b1_accum;
747  random_linear_combination_consecutive<ppT>(a1s, a1_accum, b1_accum);
748  libff::leave_block("accumulating random combination");
749 
750  const bool same = same_ratio<ppT>(a1_accum, b1_accum, a2, b2);
751  libff::leave_block("call to same_ratio_consecutive (G1)");
752  return same;
753 }
754 
755 template<typename ppT>
756 bool same_ratio_consecutive(
757  const libff::G1<ppT> &a1,
758  const libff::G1<ppT> &b1,
759  const std::vector<libff::G2<ppT>> &a2s)
760 {
761  using G2 = libff::G2<ppT>;
762 
763  libff::enter_block("call to same_ratio_consecutive (G2)");
764 
765  libff::enter_block("accumulating random combination");
766  G2 a2_accum;
767  G2 b2_accum;
768  random_linear_combination_consecutive<ppT>(a2s, a2_accum, b2_accum);
769  libff::leave_block("accumulating random combination");
770 
771  const bool same = same_ratio<ppT>(a1, b1, a2_accum, b2_accum);
772  libff::leave_block("call to same_ratio_consecutive (G2)");
773  return same;
774 }
775 
776 template<typename ppT>
777 bool powersoftau_is_well_formed(const srs_powersoftau<ppT> &pot)
778 {
779  // TODO: Cache precomputed g1, tau_g1, g2, tau_g2
780  // TODO: Parallelize
781 
782  // Check sizes are valid. tau_powers_g1 should have 2n-1 elements, and
783  // other vectors should have n entries.
784  const size_t n = (pot.tau_powers_g1.size() + 1) / 2;
785  if (n != 1ull << libff::log2(n) || n != pot.tau_powers_g2.size() ||
786  n != pot.alpha_tau_powers_g1.size() ||
787  n != pot.beta_tau_powers_g1.size()) {
788  return false;
789  }
790 
791  // Make sure that the identity of each group is at index 0
792  if (pot.tau_powers_g1[0] != libff::G1<ppT>::one() ||
793  pot.tau_powers_g2[0] != libff::G2<ppT>::one()) {
794  return false;
795  }
796 
797  const libff::G1<ppT> g1 = libff::G1<ppT>::one();
798  const libff::G2<ppT> g2 = libff::G2<ppT>::one();
799  const libff::G1<ppT> tau_g1 = pot.tau_powers_g1[1];
800  const libff::G2<ppT> tau_g2 = pot.tau_powers_g2[1];
801 
802  // SameRatio((g1, tau_g1), (g2, tau_g2))
803  const bool tau_g1_g2_consistent =
804  same_ratio<ppT>(g1, pot.tau_powers_g1[1], g2, pot.tau_powers_g2[1]);
805  if (!tau_g1_g2_consistent) {
806  return false;
807  }
808 
809  // SameRatio((tau_powers_g1[i-1], tau_powers_g1[i]), (g2, tau_g2))
810  // SameRatio((tau_powers_g2[i-1], tau_powers_g2[i]), (g1, tau_g1))
811  // SameRatio(
812  // (alpha_tau_powers_g1[i-1], alpha_tau_powers_g1[i]), (g2, tau_g2))
813  // SameRatio(
814  // (beta_tau_powers_g1[i-1], beta_tau_powers_g1[i]), (g2, tau_g2))
815  if (!same_ratio_consecutive<ppT>(pot.tau_powers_g1, g2, tau_g2) ||
816  !same_ratio_consecutive<ppT>(g1, tau_g1, pot.tau_powers_g2) ||
817  !same_ratio_consecutive<ppT>(pot.alpha_tau_powers_g1, g2, tau_g2) ||
818  !same_ratio_consecutive<ppT>(pot.beta_tau_powers_g1, g2, tau_g2)) {
819  return false;
820  }
821 
822  // SameRatio((g1, beta_tau_powers_g1), (g2, beta_g2))
823  if (!same_ratio<ppT>(g1, pot.beta_tau_powers_g1[0], g2, pot.beta_g2)) {
824  return false;
825  }
826 
827  return true;
828 }
829 
830 template<typename ppT>
831 srs_lagrange_evaluations<ppT> powersoftau_compute_lagrange_evaluations(
832  const srs_powersoftau<ppT> &pot, const size_t n)
833 {
834  using Fr = libff::Fr<ppT>;
835  using G1 = libff::G1<ppT>;
836  using G2 = libff::G2<ppT>;
837 
838  if (n != 1ull << libff::log2(n)) {
839  throw std::invalid_argument("non-pow-2 domain");
840  }
841  if (pot.tau_powers_g1.size() < n) {
842  throw std::invalid_argument("insufficient powers of tau");
843  }
844 
845  libff::enter_block("r1cs_gg_ppzksnark_compute_lagrange_evaluations");
846  libff::print_indent();
847  printf("n=%zu\n", n);
848 
849  libfqfft::basic_radix2_domain<Fr> domain(n);
850  const Fr omega = domain.get_domain_element(1);
851  const Fr omega_inv = omega.inverse();
852 
853  // Compute [ L_j(t) ]_1 from { [x^i] } i=0..n-1
854  libff::enter_block("computing [Lagrange_i(x)]_1");
855  std::vector<G1> lagrange_g1(
856  pot.tau_powers_g1.begin(), pot.tau_powers_g1.begin() + n);
857  if (lagrange_g1[0] != G1::one() || lagrange_g1.size() != n) {
858  throw std::invalid_argument("unexpected powersoftau data (g1). Invalid "
859  "file or degree mismatch");
860  }
861  compute_lagrange_from_powers(lagrange_g1, omega_inv);
862  libff::leave_block("computing [Lagrange_i(x)]_1");
863 
864  libff::enter_block("computing [Lagrange_i(x)]_2");
865  std::vector<G2> lagrange_g2(
866  pot.tau_powers_g2.begin(), pot.tau_powers_g2.begin() + n);
867  if (lagrange_g2[0] != G2::one() || lagrange_g2.size() != n) {
868  throw std::invalid_argument("unexpected powersoftau data (g2). invalid "
869  "file or degree mismatch");
870  }
871  compute_lagrange_from_powers(lagrange_g2, omega_inv);
872  libff::leave_block("computing [Lagrange_i(x)]_2");
873 
874  libff::enter_block("computing [alpha . Lagrange_i(x)]_1");
875  std::vector<G1> alpha_lagrange_g1(
876  pot.alpha_tau_powers_g1.begin(), pot.alpha_tau_powers_g1.begin() + n);
877  if (alpha_lagrange_g1.size() != n) {
878  throw std::invalid_argument("unexpected powersoftau data (alpha). "
879  "invalid file or degree mismatch");
880  }
881  compute_lagrange_from_powers(alpha_lagrange_g1, omega_inv);
882  libff::leave_block("computing [alpha . Lagrange_i(x)]_1");
883 
884  libff::enter_block("computing [beta . Lagrange_i(x)]_1");
885  std::vector<G1> beta_lagrange_g1(
886  pot.beta_tau_powers_g1.begin(), pot.beta_tau_powers_g1.begin() + n);
887  if (beta_lagrange_g1.size() != n) {
888  throw std::invalid_argument("unexpected powersoftau data (alpha). "
889  "invalid file or degree mismatch");
890  }
891  compute_lagrange_from_powers(beta_lagrange_g1, omega_inv);
892  libff::leave_block("computing [beta . Lagrange_i(x)]_1");
893 
894  libff::leave_block("r1cs_gg_ppzksnark_compute_lagrange_evaluations");
895 
896  return srs_lagrange_evaluations<ppT>(
897  n,
898  std::move(lagrange_g1),
899  std::move(lagrange_g2),
900  std::move(alpha_lagrange_g1),
901  std::move(beta_lagrange_g1));
902 }
903 
904 } // namespace libzeth
905 
906 #endif // __ZETH_MPC_GROTH16_POWERSOFTAU_UTILS_TCC__