Clearmatics Libsnark  0.1
C++ library for zkSNARK proofs
kzg10_batched.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_KZG10_BATCHED_TCC__
10 #define __LIBSNARK_POLYNOMIAL_COMMITMENTS_KZG10_BATCHED_TCC__
11 
12 #include "libsnark/polynomial_commitments/kzg10_batched.hpp"
13 
14 namespace libsnark
15 {
16 
17 namespace internal
18 {
19 
20 template<typename FieldT>
21 static polynomial<FieldT> polynomial_scalar_mul(
22  const polynomial<FieldT> &poly, const FieldT &factor)
23 {
24  polynomial<FieldT> out;
25  out.reserve(poly.size());
26  for (const FieldT &c : poly) {
27  out.push_back(c * factor);
28  }
29  return out;
30 }
31 
32 template<typename FieldT>
33 static void polynomial_scalar_mul_inplace(
34  polynomial<FieldT> &poly, const FieldT &factor)
35 {
36  for (FieldT &c : poly) {
37  c *= factor;
38  }
39 }
40 
41 template<typename FieldT>
42 static void polynomial_add_inplace(
43  polynomial<FieldT> &poly, const polynomial<FieldT> &other)
44 {
45  // If `other` is longer, resize `polynomial` and copy any trailing
46  // coefficients. We then add in the coefficients from other which were not
47  // copied.
48  const size_t p_orig_size = poly.size();
49  if (p_orig_size < other.size()) {
50  poly.resize(other.size());
51  std::copy(
52  other.begin() + p_orig_size,
53  other.end(),
54  poly.begin() + p_orig_size);
55  for (size_t i = 0; i < p_orig_size; ++i) {
56  poly[i] += other[i];
57  }
58 
59  return;
60  }
61 
62  // Add coefficients of `other` into `polynomial`.
63  for (size_t i = 0; i < other.size(); ++i) {
64  poly[i] += other[i];
65  }
66 }
67 
68 template<typename FieldT>
69 static polynomial<FieldT> polynomial_accumulate_with_power_factors(
70  const std::vector<polynomial<FieldT>> &polys, const FieldT &factor)
71 {
72  const size_t t = polys.size();
73  if (t == 1) {
74  return polys[0];
75  }
76 
77  // Start with
78  // f_accum = polys[t-2] + factor * polys[t-1].
79  //
80  // For each remaining polynomial f:
81  // f_accum <- factor * f_accum + f
82 
83  size_t i = t - 1;
84  polynomial<FieldT> f_accum = polynomial_scalar_mul(polys[i], factor);
85  polynomial_add_inplace(f_accum, polys[--i]);
86  while (i > 0) {
87  polynomial_scalar_mul_inplace(f_accum, factor);
88  polynomial_add_inplace(f_accum, polys[--i]);
89  }
90 
91  return f_accum;
92 }
93 
94 } // namespace internal
95 
96 template<typename ppT>
97 kzg10_batched_2_point<ppT>::evaluations::evaluations(
98  std::vector<Field> &&s_1s, std::vector<Field> &&s_2s)
99  : s_1s(s_1s), s_2s(s_2s)
100 {
101 }
102 
103 template<typename ppT>
104 kzg10_batched_2_point<ppT>::evaluation_witness::evaluation_witness(
105  const libff::G1<ppT> &W_1, const libff::G1<ppT> &W_2)
106  : W_1(W_1), W_2(W_2)
107 {
108 }
109 
110 template<typename ppT>
111 typename kzg10_batched_2_point<ppT>::evaluations kzg10_batched_2_point<ppT>::
112  evaluate_polynomials(
113  const std::vector<polynomial<Field>> &fs,
114  const std::vector<polynomial<Field>> &gs,
115  const Field &z_1,
116  const Field &z_2)
117 {
118  // Denote the numbers of polynomials as t1 and t2, consistent with [GWC19].
119  const size_t t1 = fs.size();
120  const size_t t2 = gs.size();
121 
122  std::vector<Field> s_1s;
123  s_1s.reserve(t1);
124  for (const polynomial<Field> &f_i : fs) {
125  s_1s.push_back(libfqfft::evaluate_polynomial(f_i.size(), f_i, z_1));
126  }
127 
128  std::vector<Field> s_2s;
129  s_2s.reserve(t2);
130  for (const polynomial<Field> &g_i : gs) {
131  s_2s.push_back(libfqfft::evaluate_polynomial(g_i.size(), g_i, z_2));
132  }
133 
134  return evaluations(std::move(s_1s), std::move(s_2s));
135 }
136 
137 template<typename ppT>
138 typename kzg10_batched_2_point<ppT>::evaluation_witness kzg10_batched_2_point<
139  ppT>::
140  create_evaluation_witness(
141  const std::vector<polynomial<Field>> &fs,
142  const std::vector<polynomial<Field>> &gs,
143  const Field &z_1,
144  const Field &z_2,
145  const evaluations &evaluations,
146  const srs &srs,
147  const Field &gamma_1,
148  const Field &gamma_2)
149 {
150  assert(fs.size() == evaluations.s_1s.size());
151  assert(gs.size() == evaluations.s_2s.size());
152 
153  // For convenience of variable naming, let $f_i \in fs$ and $g_i in gs$ be
154  // the two sets of polynomials. These are denoted $f_i$ and $f'_i$ in
155  // [GWC19]. Similarly, $h_1$ and $h_2$ are used in place of $h$ and
156  // $h'$ in the paper, and $\gamma_1$ and $\gamma_2$ in place of
157  // $\gamma$ and $\gamma'$.
158  //
159  // To generate the witness we must compute:
160  //
161  // h_1(X) = \sum_{i=1}^{t_1} \gamma_1^{i-1} (f_i(X) - f_i(z_1))/(X - z_1)
162  //
163  // h_2(X) = \sum_{i=1}^{t_2} \gamma_2^{i-1} (g_i(X) - g_i(z_2))/(X - z_2)
164  //
165  // then evaluate:
166  //
167  // W_1 = [h_1(x)]_1
168  // W_2 = [h_2(x)]_2
169  //
170  // This is computed as follows:
171  //
172  // f_accum(X) = \sum_{i=1}^{t_1} \gamma_1^{i-1} f_i(X)
173  // f_accum_eval = f_accum(z_1)
174  // W_1 = kzg10::create_evaluation_witness(f_accum, z_1, f_accum_eval)
175  // = [ (f_accum(X) - f_accum(z_1)) / (X - z_1) ]_1
176  // = [ h_1(X) ]_1
177  //
178  // Similarly for $\gamma_2$, $g_i$s and $z_2$.
179  //
180  // The evaluations `f_accum_eval` and `g_accum_eval` of the accumulated
181  // polynomials `f_accum` and `g_accum` are computed as:
182  //
183  // s_1[0] + s_1[1] * gamma_1 + ... + s_1[t1 - 1] gamma_1^{t1 - 1}
184  //
185  // which can be viewed as the evaluation at `gamma_1` of the polynomial
186  // with coefficients `s_1`.
187  //
188  // Note that this approach assumes that the highest degree of the
189  // polynomials f_s and g_s is larger than the number of polynomials, and
190  // therefore it is more efficient to reuse the existing evaluations rather
191  // than evaluating the accumulated polynomials.
192 
193  const polynomial<Field> f_accum =
194  internal::polynomial_accumulate_with_power_factors(fs, gamma_1);
195  const libff::Fr<ppT> f_accum_eval =
196  kzg10<ppT>::evaluate_polynomial(evaluations.s_1s, gamma_1);
197  const libff::G1<ppT> f_accum_witness =
198  kzg10<ppT>::create_evaluation_witness(f_accum, z_1, f_accum_eval, srs);
199 
200  const polynomial<Field> g_accum =
201  internal::polynomial_accumulate_with_power_factors(gs, gamma_2);
202  const libff::Fr<ppT> g_accum_eval =
203  kzg10<ppT>::evaluate_polynomial(evaluations.s_2s, gamma_2);
204  const libff::G1<ppT> g_accum_witness =
205  kzg10<ppT>::create_evaluation_witness(g_accum, z_2, g_accum_eval, srs);
206 
207  return evaluation_witness(f_accum_witness, g_accum_witness);
208 }
209 
210 template<typename ppT>
211 bool kzg10_batched_2_point<ppT>::verify_evaluations(
212  const Field &z_1,
213  const Field &z_2,
214  const evaluations &evaluations,
215  const srs &srs,
216  const Field &gamma_1,
217  const Field &gamma_2,
218  const evaluation_witness &witness,
219  const std::vector<commitment> &cm_1s,
220  const std::vector<commitment> &cm_2s,
221  const Field &r)
222 {
223  // See Section 3, p13 of [GWC19].
224 
225  const size_t t1 = cm_1s.size();
226  const size_t t2 = cm_2s.size();
227  assert(t1 == evaluations.s_1s.size());
228  assert(t2 == evaluations.s_2s.size());
229 
230  // Compute:
231  //
232  // F = \sum_{i=1}^{t1} \gamma_1^{i-1} (cm_1[i] - [s_1[i]]_1) + (G)
233  // r \sum_{i=1}^{t2} \gamma_2^{i-1} (cm_2[i] - [s_2[i]]_1) (H)
234 
235  const std::vector<Field> &s_1s = evaluations.s_1s;
236  const std::vector<Field> &s_2s = evaluations.s_2s;
237 
238  // Compute:
239  //
240  // s_1_accum = \sum_i \gamma_1^{i-1} s_1[i] (in the scalar field)
241  // cm_1_accum = \sum_i \gamma_1^{i-1} cm_1[i] (in G1)
242  // G = cm_1_accum - s_1_accum * G1::one()
243 
244  Field s_1_accum = s_1s[t1 - 1];
245  libff::G1<ppT> cm_1_accum = cm_1s[t1 - 1];
246  // Note use of underflow to terminate after i = 0.
247  for (size_t i = t1 - 2; i < t1; --i) {
248  cm_1_accum = (gamma_1 * cm_1_accum) + cm_1s[i];
249  s_1_accum = (s_1_accum * gamma_1) + s_1s[i];
250  }
251  const libff::G1<ppT> G = cm_1_accum - s_1_accum * libff::G1<ppT>::one();
252 
253  // Similarly:
254  //
255  // s_2_accum = \sum_i \gamma_2^{i-1} s_2[i] (in the scalar field)
256  // cm_2_accum = \sum_i \gamma_2^{i-1} cm_2[i] (in G1)
257  // H = cm_2_accum - s_2_accum * G1::one()
258 
259  Field s_2_accum = s_2s[t2 - 1];
260  libff::G1<ppT> cm_2_accum = cm_2s[t2 - 1];
261  for (size_t i = t2 - 2; i < t2; --i) {
262  cm_2_accum = gamma_2 * cm_2_accum + cm_2s[i];
263  s_2_accum = (s_2_accum * gamma_2) + s_2s[i];
264  }
265  const libff::G1<ppT> H =
266  r * (cm_2_accum - s_2_accum * libff::G1<ppT>::one());
267 
268  const libff::G1<ppT> F = G + H;
269 
270  // The pairing check takes the form:
271  //
272  // e(F + z_1 * W_1 + r * z_2 * W_2, G2::one())
273  // * e(-W_1 - r * W_2, srs.alpha_g2) == 1
274  //
275  // (see p13 of [GWC19]).
276 
277  // A = F + z_1 * W_1 + r * z_2 * W2
278  // B = G2::one()
279  // C = -W_1 - r * W_2
280  // D = srs.alpha_g2
281 
282  const libff::G1<ppT> &W_1 = witness.W_1;
283  const libff::G1<ppT> &W_2 = witness.W_2;
284 
285  const libff::G1<ppT> r_W_2 = r * W_2;
286  const libff::G1<ppT> A = F + z_1 * W_1 + z_2 * r_W_2;
287  const libff::G1<ppT> C = -(W_1 + r_W_2);
288 
289  const libff::G1_precomp<ppT> _A = ppT::precompute_G1(A);
290  const libff::G2_precomp<ppT> _B = ppT::precompute_G2(libff::G2<ppT>::one());
291  const libff::G1_precomp<ppT> _C = ppT::precompute_G1(C);
292  const libff::G2_precomp<ppT> _D = ppT::precompute_G2(srs.alpha_g2);
293 
294  const libff::Fqk<ppT> miller_result =
295  ppT::double_miller_loop(_A, _B, _C, _D);
296  const libff::GT<ppT> result = ppT::final_exponentiation(miller_result);
297  return result == libff::GT<ppT>::one();
298 }
299 
300 } // namespace libsnark
301 
302 #endif // __LIBSNARK_POLYNOMIAL_COMMITMENTS_KZG10_BATCHED_TCC__