Clearmatics Libsnark  0.1
C++ library for zkSNARK proofs
bdfg21.tcc
Go to the documentation of this file.
1 /** @file
2  *****************************************************************************
3  * @author This file is part of libff, developed by Clearmatics Ltd
4  * (originally developed by SCIPR Lab) and contributors
5  * (see AUTHORS).
6  * @copyright MIT license (see LICENSE file)
7  *****************************************************************************/
8 
9 #ifndef __LIBSNARK_POLYNOMIAL_COMMITMENTS_BDFG21_TCC__
10 #define __LIBSNARK_POLYNOMIAL_COMMITMENTS_BDFG21_TCC__
11 
12 #include "libsnark/polynomial_commitments/bdfg21.hpp"
13 #include "libsnark/polynomial_commitments/kzg10_batched.hpp"
14 
15 namespace libsnark
16 {
17 
18 namespace internal
19 {
20 
21 /// Convenience function to compute the polynomial:
22 ///
23 /// start_factor * \sum_i factor^{i-1} polys[i]
24 template<typename FieldT>
25 static polynomial<FieldT> polynomial_accumulate_with_power_factors(
26  const std::vector<polynomial<FieldT>> &polys,
27  const FieldT &start_factor,
28  const FieldT &factor)
29 {
30  const size_t t = polys.size();
31  if (t == 1) {
32  if (start_factor != FieldT::one()) {
33  return polynomial_scalar_mul(polys[0], start_factor);
34  }
35  return polys[0];
36  }
37 
38  // Leverage the fact that t > 1 to avoid a copy:
39  //
40  // f_accum <- factor * polys[t-1] + polys[t-2]
41  // for i = t-3, ... 0:
42  // f_accum <- factor * f_accum + polys[i]
43  // f_accum <- start_factor * f_accum
44 
45  size_t i = t - 1;
46  polynomial<FieldT> f_accum = polynomial_scalar_mul(polys[i], factor);
47  polynomial_add_inplace(f_accum, polys[--i]);
48 
49  while (i > 0) {
50  polynomial_scalar_mul_inplace(f_accum, factor);
51  polynomial_add_inplace(f_accum, polys[--i]);
52  }
53 
54  if (start_factor != FieldT::one()) {
55  polynomial_scalar_mul_inplace(f_accum, start_factor);
56  }
57 
58  return f_accum;
59 }
60 
61 /// Let $T = \{z_0, ...., z_{n-1}\}$ be a sequence of field elements, and $z$
62 /// another field element. Return a vector in which the i-th entry is:
63 ///
64 /// (z - z_0) * ... * (z - z_{i-1}) * (z - z_{i+1}) * ... * (z - z_{n-1})
65 ///
66 /// i.e. the evaluation at $z$ of the polynomial of degree $n-1$ which is zero
67 /// at all $z_j$ for $j \neq i$.
68 template<typename FieldT>
69 std::vector<FieldT> compute_Z_T_minus_z_j_values(
70  const std::vector<FieldT> &T, const FieldT &z)
71 {
72  const size_t num_entries = T.size();
73  std::vector<FieldT> Y;
74  Y.reserve(num_entries);
75 
76  // Compute:
77  //
78  // Y = [1, (z-z_0), (z-z_0)(z-z_1), ..., (z-z_0)..(z-z_{n-2})]
79  //
80  // Y[0] = 1
81  // for i = 1 to n-1:
82  // Y[i] = Y[i-1] * (z - T[i-1])
83 
84  Y.push_back(FieldT::one());
85  for (size_t i = 1; i < num_entries; ++i) {
86  Y.push_back(Y.back() * (z - T[i - 1]));
87  }
88 
89  // Then, iterate through the vector in reverse:
90  //
91  // zz <- 1
92  // for i = n-2, ... 0:
93  // zz <- zz * (z-z_{i+1})
94  // Y[i] <- Y[i] * zz;
95 
96  FieldT zz = FieldT::one();
97  for (size_t i = num_entries - 2; i < num_entries; --i) {
98  zz *= (z - T[i + 1]);
99  Y[i] *= zz;
100  }
101 
102  return Y;
103 }
104 
105 /// Compute polynomials of the form:
106 ///
107 /// H_J = start_factor * \sum_{i \in F_j} factor^{i-1} (f_i(X) - f_i(z_j))
108 ///
109 /// where evals contains the precomputed $f_i(z_j)$ values. This also
110 /// corresponds to terms of the form:
111 ///
112 /// start_factor * \sum_{i \in F_j} factor^{i-1} (f_i(X) - r_i(X))
113 ///
114 /// since $r_i(X)$ (the polynomial which attains $f_i(z_j)$ at $z_j$ is a
115 /// constant.
116 template<typename FieldT>
117 std::vector<FieldT> compute_bdfg21_f_minus_r_polynomial(
118  const std::vector<polynomial<FieldT>> &f_set,
119  const std::vector<FieldT> &evals,
120  const FieldT &start_factor,
121  const FieldT &factor)
122 {
123  const size_t num_polys = f_set.size();
124  assert(evals.size() == num_polys);
125 
126  // Compute
127  //
128  // A(X) = start_factor * \sum_{i \in F_j} factor^{i-1} f_i(X)
129 
130  polynomial<FieldT> A = internal::polynomial_accumulate_with_power_factors(
131  f_set, start_factor, factor);
132 
133  // Compute
134  //
135  // B = start_factor * \sum_{i \in F_j} factor^{i-1} f_i(z_j)
136 
137  FieldT alpha = start_factor;
138  FieldT B = alpha * evals[0];
139 
140  for (size_t i = 1; i < num_polys; ++i) {
141  alpha *= factor;
142  B += alpha * evals[i];
143  }
144 
145  // Compute (A)-(B)
146  A[0] -= B;
147  return A;
148 }
149 
150 } // namespace internal
151 
152 template<typename ppT>
153 bdfg21<ppT>::phase_1_output::phase_1_output(
154  const libff::G1<ppT> &witness_phase_1,
155  polynomial<Field> &&f_over_Z_T_polynomial)
156  : public_witness_phase_1(witness_phase_1)
157  , private_f_over_Z_T_polynomial(f_over_Z_T_polynomial)
158 {
159 }
160 
161 template<typename ppT>
162 bdfg21<ppT>::evaluation_witness::evaluation_witness(
163  const libff::G1<ppT> &W, const libff::G1<ppT> &W_prime)
164  : W(W), W_prime(W_prime)
165 {
166 }
167 
168 template<typename ppT>
169 typename bdfg21<ppT>::evaluations bdfg21<ppT>::evaluate_polynomials(
170  const std::vector<std::vector<polynomial<Field>>> &f_sets,
171  const std::vector<Field> z_s)
172 {
173  assert(f_sets.size() == z_s.size());
174 
175 #ifdef MULTICORE
176 #pragma omp parallel for
177 #endif
178  evaluations evals;
179  evals.resize(z_s.size());
180  for (size_t i = 0; i < z_s.size(); ++i) {
181  const std::vector<polynomial<Field>> &f_set = f_sets[i];
182  const Field &z = z_s[i];
183 
184  std::vector<Field> z_evals;
185  z_evals.reserve(f_set.size());
186  for (const polynomial<Field> f : f_set) {
187  z_evals.push_back(libfqfft::evaluate_polynomial(f.size(), f, z));
188  }
189 
190  assert(z_evals.size() == f_set.size());
191  evals[i] = std::move(z_evals);
192  }
193 
194  return evals;
195 }
196 
197 template<typename ppT>
198 typename bdfg21<ppT>::phase_1_output bdfg21<ppT>::
199  create_evaluation_witness_phase_1(
200  const std::vector<std::vector<polynomial<Field>>> &f_sets,
201  const std::vector<Field> &T,
202  const typename bdfg21<ppT>::evaluations &evaluations,
203  const typename bdfg21<ppT>::srs &srs,
204  const libff::Fr<ppT> &gamma)
205 {
206  assert(f_sets.size() == T.size());
207  assert(evaluations.size() == T.size());
208 
209  // See Section 4.1 [BDFG21].
210  //
211  // The algorithm in the paper requires that the polynomial:
212  //
213  // f = \sum_{i = 1}^k \gamma^{i-1} Z_{T\S_i} (f_i - r_i)
214  //
215  // is computed, and then divided by $Z_T$. The first part of the witness is
216  // then computed as $[ (f/Z_T)(x) ]_1$, and the polynomial $f$ is reused in
217  // a later phase.
218  //
219  // In our setting, for each $j = 1, ..., |T|$, let $z_j \in T$ be the
220  // $j$-th point in $T$, and let $F_j$ be the set of indices $i$ of
221  // polynomials s.t. $f_i$ is to be evaluated at $z_j$.
222  //
223  // Then f can be written as:
224  //
225  // f = \sum_j \sum_{i \in F_j} \gamma^{i-1} Z_{T\S_i} (f_i - r_i)
226  //
227  // and f/Z_T as:
228  //
229  // f/Z_T =
230  // \sum_j \sum_{i \in F_j} \gamma^{i-1} (f_i - r_i) Z_{T\S_i} / Z_T
231  //
232  // Note that for $i \in F_j$ we have:
233  //
234  // Z_{T\S_i}(X) / Z_T(X) = 1 / (X - z_j)
235  //
236  // and therefore we can compute (f / Z_T)(X) as:
237  //
238  // (f/Z_T)(X) = \sum_j G_j(X)
239  //
240  // where
241  //
242  // G_j(X) = H_j(X) / (X - z_j)
243  // H_j(X) = \sum_{i \in F_j} \gamma^{i-1} (f_i(X) - f_i(z_j))
244 
245  polynomial<Field> f_over_Z_T{0};
246  Field gamma_power = Field::one();
247 
248  for (size_t j = 0; j < T.size(); ++j) {
249  const Field &z_j = T[j];
250 
251  // Compute the polynomial H_j and G_j = H_j / (X - z_j) polynomials
252  // (see above)
253 
254  polynomial<Field> H_j = internal::compute_bdfg21_f_minus_r_polynomial(
255  f_sets[j], evaluations[j], gamma_power, gamma);
256  std::vector<Field> G_j;
257  std::vector<Field> remainder;
258  libfqfft::_polynomial_division(G_j, remainder, H_j, {-z_j, 1});
259  assert(libfqfft::_is_zero(remainder));
260 
261  // Update gamma_power
262  for (size_t i = 0; i < f_sets[j].size(); ++i) {
263  gamma_power *= gamma;
264  }
265 
266  // The polynomial f is the sum of the g_j's
267  internal::polynomial_add_inplace(f_over_Z_T, G_j);
268  }
269 
270  const libff::G1<ppT> f_over_Z_T_witness =
271  kzg10<ppT>::commit(srs, f_over_Z_T);
272  return phase_1_output(f_over_Z_T_witness, std::move(f_over_Z_T));
273 }
274 
275 template<typename ppT>
276 typename bdfg21<ppT>::evaluation_witness bdfg21<ppT>::create_evaluation_witness(
277  const std::vector<std::vector<polynomial<Field>>> &f_sets,
278  const std::vector<Field> &T,
279  const evaluations &evaluations,
280  const srs &srs,
281  const Field &gamma,
282  const phase_1_output &phase_1_out,
283  const Field &z)
284 {
285  assert(f_sets.size() == T.size());
286  assert(f_sets.size() == evaluations.size());
287 
288  // See Section 4.1 [BDFG21].
289  //
290  // Compute the polynomial
291  //
292  // L(X)
293  // = \sum_{i=1}^k \gamma^{i-1} Z_{T\S_i}(z) (f_i(X) - r_i(z))
294  // - Z_T(z) (f / Z_T)(X)
295  //
296  // $L$ has a root at $z$ and is therefore divisble by $(X - z)$. Compute
297  // $[ (L / (x - z))(x) ]_1$, the second part of the evaluation witness.
298 
299  // Note that, using the notation of create_evaluation_witness_phase_1, this
300  // is equal to:
301  //
302  // L(X) = \sum_j Z_{T\{z_j}}(z)
303  // \sum_{i \in F_j} \gamma^{i-1} (f_i(X) - f_i(z_i))
304  // - Z_T(z) (f / Z_T)(X)
305 
306  // Compute the values Z_{T\{z_j}}(z)
307  const std::vector<Field> Z_T_minus_z_j_at_z =
308  internal::compute_Z_T_minus_z_j_values(T, z);
309  const Field Z_T_at_z = Z_T_minus_z_j_at_z[0] * (z - T[0]);
310  assert(Z_T_at_z == Z_T_minus_z_j_at_z[1] * (z - T[1]));
311 
312  // Compute L
313  Field gamma_power(1);
314  polynomial<Field> L = internal::polynomial_scalar_mul(
315  phase_1_out.private_f_over_Z_T_polynomial, -Z_T_at_z);
316 
317  for (size_t j = 0; j < T.size(); ++j) {
318 
319  // Compute intermediate polynomials:
320  //
321  // H(X) = Z_{T\{z_j}}(z) *
322  // \sum_{i \in F_j} \gamma^{i-1} (f_i(X) - f_i(z_i))
323 
324  // TODO: We could cache the polynomials computed during phase1 and
325  // multiply them by Z_T_minus_z_j_at_z[j] here (leveraging the fact
326  // that r_i is always a constant in out setting). For now, just
327  // recompute (including Z_T_minus_z_j_at_z in the start_factor)
328 
329  polynomial<Field> H_j = internal::compute_bdfg21_f_minus_r_polynomial(
330  f_sets[j],
331  evaluations[j],
332  gamma_power * Z_T_minus_z_j_at_z[j],
333  gamma);
334  internal::polynomial_add_inplace(L, H_j);
335 
336  // Update gamma_power
337  for (size_t i = 0; i < f_sets[j].size(); ++i) {
338  gamma_power *= gamma;
339  }
340  }
341 
342  // Compute L(X) / (X - z)
343  assert(Field::zero() == libfqfft::evaluate_polynomial(L.size(), L, z));
344  std::vector<Field> L_over_X_minus_z;
345  std::vector<Field> remainder;
346  libfqfft::_polynomial_division(L_over_X_minus_z, remainder, L, {-z, 1});
347  assert(libfqfft::_is_zero(remainder));
348 
349  libff::G1<ppT> W_prime = kzg10<ppT>::commit(srs, L_over_X_minus_z);
350  return evaluation_witness(phase_1_out.public_witness_phase_1, W_prime);
351 }
352 
353 template<typename ppT>
354 bool bdfg21<ppT>::verify_evaluations(
355  const std::vector<libff::Fr<ppT>> &T,
356  const typename bdfg21<ppT>::evaluations &evaluations,
357  const srs &srs,
358  const libff::Fr<ppT> &gamma,
359  const libff::Fr<ppT> &z,
360  const typename bdfg21<ppT>::evaluation_witness &witness,
361  const std::vector<std::vector<commitment>> &cm_sets)
362 {
363  // See Section 4.1 [BDFG21].
364  //
365  // The verifier accepts if:
366  //
367  // e(F + z W', [1]_2) = e(W', [x]_2)
368  //
369  // where
370  //
371  // F = \sum_{i=1}^k \gamma^{i-1} Z_{T\S_i}(z) cm_i
372  // - [ \sum_{i=1}^k \gamma^{i-1} Z_{T\S_i}(z) r_i(z) ]_1
373  // - Z_T(z) W
374  //
375  // as in create_evaluation_witness,
376  //
377  // r_i(X) = f_i(z_j) = evaluation[j][k]
378  //
379  // for appropriate j and k.
380 
381  // Compute Z_T(z) and Z_{T\S_i}(z).
382 
383  const std::vector<Field> Z_T_minus_z_j_at_z =
384  internal::compute_Z_T_minus_z_j_values(T, z);
385  const Field Z_T_at_z = Z_T_minus_z_j_at_z[0] * (z - T[0]);
386  assert(Z_T_at_z == Z_T_minus_z_j_at_z[1] * (z - T[1]));
387 
388  // For i = 1, ...,k we sum value
389  //
390  // G_i = \gamma^{i-1} Z_{T\S_i}(z) cm_i (in G1)
391  //
392  // and
393  //
394  // H_i = \gamma^{i-1} Z_{T\S_i}(z) r_i(z) (in Fr)
395  //
396  // to obtain G and H. This is done by iterating through the z_j values in T
397  // (for j = 0, ..., T.size()-1), and through the polynomials
398  // evaluations[j][k] (for k = 0, ..., evaluations[j].size()-1). The index i
399  // is implicit.
400 
401  // TODO: For a large number of polynomials it may be faster to compute all
402  // the scalar factors and compute A as an MSM (although this requires
403  // allocation and copying).
404 
405  Field gamma_power = 1;
406 
407  libff::G1<ppT> G = libff::G1<ppT>::zero();
408  Field H = Field::zero();
409 
410  for (size_t j = 0; j < T.size(); ++j) {
411  const Field &Z_T_minus_S_at_z = Z_T_minus_z_j_at_z[j];
412  const std::vector<Field> &evals = evaluations[j];
413  const std::vector<commitment> &cms = cm_sets[j];
414  assert(cms.size() == evals.size());
415 
416  for (size_t k = 0; k < evals.size(); ++k) {
417  const Field factor = gamma_power * Z_T_minus_S_at_z;
418 
419  // G = G + ( \gamma^{i-1} Z_{T\S_i}(z) ) cm_i
420  G = G + (factor * cms[k]);
421 
422  // H = H + \gamma^{i-1} Z_{T\S_i}(z) r_i(z)
423  H += factor * evals[k];
424 
425  gamma_power *= gamma;
426  }
427  }
428 
429  // F = G + H * [1]_1 - Z_T_at_z * W
430  const libff::G1<ppT> F =
431  G - H * libff::G1<ppT>::one() - Z_T_at_z * witness.W;
432 
433  // The pairing check can be performed as:
434  //
435  // e(A, B) * e(C, D) = 1
436  //
437  // where
438  //
439  // A = F + z W'
440  // B = -[1]_2
441  // C = W'
442  // D = [x]_2
443 
444  const libff::G1_precomp<ppT> A =
445  ppT::precompute_G1(F + z * witness.W_prime);
446  const libff::G2_precomp<ppT> B = ppT::precompute_G2(-libff::G2<ppT>::one());
447  const libff::G1_precomp<ppT> C = ppT::precompute_G1(witness.W_prime);
448  const libff::G2_precomp<ppT> D = ppT::precompute_G2(srs.alpha_g2);
449 
450  const libff::Fqk<ppT> miller_result = ppT::double_miller_loop(A, B, C, D);
451  const libff::GT<ppT> result = ppT::final_exponentiation(miller_result);
452  return result == libff::GT<ppT>::one();
453 }
454 
455 } // namespace libsnark
456 
457 #endif // __LIBSNARK_POLYNOMIAL_COMMITMENTS_BDFG21_TCC__