Zeth - Zerocash on Ethereum  0.8
Reference implementation of the Zeth protocol by Clearmatics
mpc_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_MPC_UTILS_TCC__
6 #define __ZETH_MPC_GROTH16_MPC_UTILS_TCC__
7 
8 #include "libzeth/core/evaluator_from_lagrange.hpp"
9 #include "libzeth/core/utils.hpp"
10 #include "libzeth/mpc/groth16/mpc_utils.hpp"
11 #include "libzeth/mpc/groth16/phase2.hpp"
12 
13 #include <algorithm>
14 #include <exception>
15 #include <libfqfft/evaluation_domain/domains/basic_radix2_domain_aux.tcc>
16 
17 namespace libzeth
18 {
19 
20 template<typename ppT>
21 srs_mpc_layer_L1<ppT>::srs_mpc_layer_L1(
22  libff::G1_vector<ppT> &&T_tau_powers_g1,
23  libff::G1_vector<ppT> &&A_g1,
24  libff::G1_vector<ppT> &&B_g1,
25  libff::G2_vector<ppT> &&B_g2,
26  libff::G1_vector<ppT> &&ABC_g1)
27  : T_tau_powers_g1(std::move(T_tau_powers_g1))
28  , A_g1(std::move(A_g1))
29  , B_g1(std::move(B_g1))
30  , B_g2(std::move(B_g2))
31  , ABC_g1(std::move(ABC_g1))
32 {
33 }
34 
35 template<typename ppT> size_t srs_mpc_layer_L1<ppT>::degree() const
36 {
37  return T_tau_powers_g1.size() + 1;
38 }
39 
40 template<typename ppT> bool srs_mpc_layer_L1<ppT>::is_well_formed() const
41 {
42  return libzeth::container_is_well_formed(T_tau_powers_g1) &&
43  libzeth::container_is_well_formed(A_g1) &&
44  libzeth::container_is_well_formed(B_g1) &&
45  libzeth::container_is_well_formed(B_g2) &&
46  libzeth::container_is_well_formed(ABC_g1);
47 }
48 
49 template<typename ppT>
50 void srs_mpc_layer_L1<ppT>::write(std::ostream &out) const
51 {
52  using G1 = libff::G1<ppT>;
53  using G2 = libff::G2<ppT>;
54  check_well_formed(*this, "mpc_layer1 (write)");
55 
56  // Write the sizes first, then stream out the values.
57  const size_t num_T_tau_powers = T_tau_powers_g1.size();
58  const size_t num_polynomials = A_g1.size();
59  out.write((const char *)&num_T_tau_powers, sizeof(num_T_tau_powers));
60  out.write((const char *)&num_polynomials, sizeof(num_polynomials));
61 
62  for (const G1 &v : T_tau_powers_g1) {
63  out << v;
64  }
65 
66  for (const G1 &v : A_g1) {
67  out << v;
68  }
69 
70  for (const G1 &v : B_g1) {
71  out << v;
72  }
73 
74  for (const G2 &v : B_g2) {
75  out << v;
76  }
77 
78  for (const G1 &v : ABC_g1) {
79  out << v;
80  }
81 }
82 
83 template<typename ppT>
84 srs_mpc_layer_L1<ppT> srs_mpc_layer_L1<ppT>::read(std::istream &in)
85 {
86  using G1 = libff::G1<ppT>;
87  using G2 = libff::G2<ppT>;
88 
89  size_t num_T_tau_powers;
90  size_t num_polynomials;
91 
92  in.read((char *)&num_T_tau_powers, sizeof(num_T_tau_powers));
93  in.read((char *)&num_polynomials, sizeof(num_polynomials));
94 
95  libff::G1_vector<ppT> T_tau_powers_g1(num_T_tau_powers);
96  libff::G1_vector<ppT> A_g1(num_polynomials);
97  libff::G1_vector<ppT> B_g1(num_polynomials);
98  libff::G2_vector<ppT> B_g2(num_polynomials);
99  libff::G1_vector<ppT> ABC_g1(num_polynomials);
100 
101  for (G1 &v : T_tau_powers_g1) {
102  in >> v;
103  }
104 
105  for (G1 &v : A_g1) {
106  in >> v;
107  }
108 
109  for (G1 &v : B_g1) {
110  in >> v;
111  }
112 
113  for (G2 &v : B_g2) {
114  in >> v;
115  }
116 
117  for (G1 &v : ABC_g1) {
118  in >> v;
119  }
120 
121  srs_mpc_layer_L1<ppT> l1(
122  std::move(T_tau_powers_g1),
123  std::move(A_g1),
124  std::move(B_g1),
125  std::move(B_g2),
126  std::move(ABC_g1));
127  check_well_formed(l1, "mpc_layer1 (read)");
128  return l1;
129 }
130 
131 template<typename ppT>
132 srs_mpc_layer_L1<ppT> mpc_compute_linearcombination(
133  const srs_powersoftau<ppT> &pot,
134  const srs_lagrange_evaluations<ppT> &lagrange,
135  const libsnark::qap_instance<libff::Fr<ppT>> &qap)
136 {
137  using Fr = libff::Fr<ppT>;
138  using G1 = libff::G1<ppT>;
139  using G2 = libff::G2<ppT>;
140  libff::enter_block("Call to mpc_compute_linearcombination");
141 
142  // n = number of constraints in r1cs, or equivalently, n = deg(t(x))
143  // t(x) being the target polynomial of the QAP
144  // Note: In the code-base the target polynomial is also denoted Z
145  // as referred to as "the vanishing polynomial", and t is also used
146  // to represent the query point (aka "tau").
147  const size_t n = qap.degree();
148  const size_t num_variables = qap.num_variables();
149 
150  if (n != 1ull << libff::log2(n)) {
151  throw std::invalid_argument("non-pow-2 domain");
152  }
153  if (n != lagrange.degree) {
154  throw std::invalid_argument(
155  "domain size differs from Lagrange evaluation");
156  }
157 
158  libff::print_indent();
159  printf("n=%zu\n", n);
160 
161  // The QAP polynomials A, B, C are of degree (n-1) as we know they
162  // are created by interpolation of an r1cs of n constraints.
163  // As a consequence, the polynomial (A.B - C) is of degree 2n-2,
164  // while the target polynomial t is of degree n.
165  // Thus, we need to have access (in the SRS) to powers up to 2n-2.
166  // To represent such polynomials we need {x^i} for in {0, ... n-2}
167  // hence we check below that we have at least n-1 elements
168  // in the set of powers of tau
169  assert(pot.tau_powers_g1.size() >= 2 * n - 1);
170 
171  // Domain uses n-roots of unity, so
172  // t(x) = x^n - 1
173  // => t(x) . x^i = x^(n+i) - x^i
174  libff::G1_vector<ppT> t_x_pow_i(n - 1, G1::zero());
175  libff::enter_block("computing [t(x) . x^i]_1");
176  for (size_t i = 0; i < n - 1; ++i) {
177  t_x_pow_i[i] = pot.tau_powers_g1[n + i] - pot.tau_powers_g1[i];
178  }
179  libff::leave_block("computing [t(x) . x^i]_1");
180 
181  libff::enter_block("computing A_i, B_i, C_i, ABC_i at x");
182  libff::G1_vector<ppT> As_g1(num_variables + 1);
183  libff::G1_vector<ppT> Bs_g1(num_variables + 1);
184  libff::G2_vector<ppT> Bs_g2(num_variables + 1);
185  libff::G1_vector<ppT> Cs_g1(num_variables + 1);
186  libff::G1_vector<ppT> ABCs_g1(num_variables + 1);
187 #ifdef MULTICORE
188 #pragma omp parallel for
189 #endif
190  for (size_t j = 0; j < num_variables + 1; ++j) {
191  G1 ABC_j_at_x = G1::zero();
192 
193  {
194  G1 A_j_at_x = G1::zero();
195  const std::map<size_t, Fr> &A_j_lagrange =
196  qap.A_in_Lagrange_basis[j];
197  for (const auto &entry : A_j_lagrange) {
198  A_j_at_x = A_j_at_x +
199  (entry.second * lagrange.lagrange_g1[entry.first]);
200  ABC_j_at_x =
201  ABC_j_at_x +
202  (entry.second * lagrange.beta_lagrange_g1[entry.first]);
203  }
204 
205  As_g1[j] = A_j_at_x;
206  }
207 
208  {
209  G1 B_j_at_x_g1 = G1::zero();
210  G2 B_j_at_x_g2 = G2::zero();
211 
212  const std::map<size_t, Fr> &B_j_lagrange =
213  qap.B_in_Lagrange_basis[j];
214  for (const auto &entry : B_j_lagrange) {
215  B_j_at_x_g1 = B_j_at_x_g1 + (entry.second *
216  lagrange.lagrange_g1[entry.first]);
217  B_j_at_x_g2 = B_j_at_x_g2 + (entry.second *
218  lagrange.lagrange_g2[entry.first]);
219  ABC_j_at_x =
220  ABC_j_at_x +
221  (entry.second * lagrange.alpha_lagrange_g1[entry.first]);
222  }
223 
224  Bs_g1[j] = B_j_at_x_g1;
225  Bs_g2[j] = B_j_at_x_g2;
226  }
227 
228  {
229  G1 C_j_at_x = G1::zero();
230  const std::map<size_t, Fr> &C_j_lagrange =
231  qap.C_in_Lagrange_basis[j];
232  for (const auto &entry : C_j_lagrange) {
233  C_j_at_x =
234  C_j_at_x + entry.second * lagrange.lagrange_g1[entry.first];
235  }
236 
237  Cs_g1[j] = C_j_at_x;
238  ABC_j_at_x = ABC_j_at_x + C_j_at_x;
239  }
240 
241  ABCs_g1[j] = ABC_j_at_x;
242  }
243  libff::leave_block("computing A_i, B_i, C_i, ABC_i at x");
244 
245  // TODO: Consider dropping those entries we know will not be used
246  // by this circuit and using sparse vectors where it makes sense
247  // (as is done for B_i's in r1cs_gg_ppzksnark_proving_key).
248  libff::leave_block("Call to mpc_compute_linearcombination");
249 
250  return srs_mpc_layer_L1<ppT>(
251  std::move(t_x_pow_i),
252  std::move(As_g1),
253  std::move(Bs_g1),
254  std::move(Bs_g2),
255  std::move(ABCs_g1));
256 }
257 
258 } // namespace libzeth
259 
260 #endif // __ZETH_MPC_GROTH16_MPC_UTILS_TCC__