2 *****************************************************************************
3 * @author This file is part of libff, developed by Clearmatics Ltd
4 * (originally developed by SCIPR Lab) and contributors
6 * @copyright MIT license (see LICENSE file)
7 *****************************************************************************/
9 #ifndef __LIBSNARK_POLYNOMIAL_COMMITMENTS_BDFG21_TCC__
10 #define __LIBSNARK_POLYNOMIAL_COMMITMENTS_BDFG21_TCC__
12 #include "libsnark/polynomial_commitments/bdfg21.hpp"
13 #include "libsnark/polynomial_commitments/kzg10_batched.hpp"
21 /// Convenience function to compute the polynomial:
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,
30 const size_t t = polys.size();
32 if (start_factor != FieldT::one()) {
33 return polynomial_scalar_mul(polys[0], start_factor);
38 // Leverage the fact that t > 1 to avoid a copy:
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
46 polynomial<FieldT> f_accum = polynomial_scalar_mul(polys[i], factor);
47 polynomial_add_inplace(f_accum, polys[--i]);
50 polynomial_scalar_mul_inplace(f_accum, factor);
51 polynomial_add_inplace(f_accum, polys[--i]);
54 if (start_factor != FieldT::one()) {
55 polynomial_scalar_mul_inplace(f_accum, start_factor);
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:
64 /// (z - z_0) * ... * (z - z_{i-1}) * (z - z_{i+1}) * ... * (z - z_{n-1})
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)
72 const size_t num_entries = T.size();
73 std::vector<FieldT> Y;
74 Y.reserve(num_entries);
78 // Y = [1, (z-z_0), (z-z_0)(z-z_1), ..., (z-z_0)..(z-z_{n-2})]
82 // Y[i] = Y[i-1] * (z - T[i-1])
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]));
89 // Then, iterate through the vector in reverse:
92 // for i = n-2, ... 0:
93 // zz <- zz * (z-z_{i+1})
96 FieldT zz = FieldT::one();
97 for (size_t i = num_entries - 2; i < num_entries; --i) {
105 /// Compute polynomials of the form:
107 /// H_J = start_factor * \sum_{i \in F_j} factor^{i-1} (f_i(X) - f_i(z_j))
109 /// where evals contains the precomputed $f_i(z_j)$ values. This also
110 /// corresponds to terms of the form:
112 /// start_factor * \sum_{i \in F_j} factor^{i-1} (f_i(X) - r_i(X))
114 /// since $r_i(X)$ (the polynomial which attains $f_i(z_j)$ at $z_j$ is a
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)
123 const size_t num_polys = f_set.size();
124 assert(evals.size() == num_polys);
128 // A(X) = start_factor * \sum_{i \in F_j} factor^{i-1} f_i(X)
130 polynomial<FieldT> A = internal::polynomial_accumulate_with_power_factors(
131 f_set, start_factor, factor);
135 // B = start_factor * \sum_{i \in F_j} factor^{i-1} f_i(z_j)
137 FieldT alpha = start_factor;
138 FieldT B = alpha * evals[0];
140 for (size_t i = 1; i < num_polys; ++i) {
142 B += alpha * evals[i];
150 } // namespace internal
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)
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)
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)
173 assert(f_sets.size() == z_s.size());
176 #pragma omp parallel for
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];
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));
190 assert(z_evals.size() == f_set.size());
191 evals[i] = std::move(z_evals);
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)
206 assert(f_sets.size() == T.size());
207 assert(evaluations.size() == T.size());
209 // See Section 4.1 [BDFG21].
211 // The algorithm in the paper requires that the polynomial:
213 // f = \sum_{i = 1}^k \gamma^{i-1} Z_{T\S_i} (f_i - r_i)
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
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$.
223 // Then f can be written as:
225 // f = \sum_j \sum_{i \in F_j} \gamma^{i-1} Z_{T\S_i} (f_i - r_i)
230 // \sum_j \sum_{i \in F_j} \gamma^{i-1} (f_i - r_i) Z_{T\S_i} / Z_T
232 // Note that for $i \in F_j$ we have:
234 // Z_{T\S_i}(X) / Z_T(X) = 1 / (X - z_j)
236 // and therefore we can compute (f / Z_T)(X) as:
238 // (f/Z_T)(X) = \sum_j G_j(X)
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))
245 polynomial<Field> f_over_Z_T{0};
246 Field gamma_power = Field::one();
248 for (size_t j = 0; j < T.size(); ++j) {
249 const Field &z_j = T[j];
251 // Compute the polynomial H_j and G_j = H_j / (X - z_j) polynomials
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));
261 // Update gamma_power
262 for (size_t i = 0; i < f_sets[j].size(); ++i) {
263 gamma_power *= gamma;
266 // The polynomial f is the sum of the g_j's
267 internal::polynomial_add_inplace(f_over_Z_T, G_j);
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));
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,
282 const phase_1_output &phase_1_out,
285 assert(f_sets.size() == T.size());
286 assert(f_sets.size() == evaluations.size());
288 // See Section 4.1 [BDFG21].
290 // Compute the polynomial
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)
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.
299 // Note that, using the notation of create_evaluation_witness_phase_1, this
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)
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]));
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);
317 for (size_t j = 0; j < T.size(); ++j) {
319 // Compute intermediate polynomials:
321 // H(X) = Z_{T\{z_j}}(z) *
322 // \sum_{i \in F_j} \gamma^{i-1} (f_i(X) - f_i(z_i))
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)
329 polynomial<Field> H_j = internal::compute_bdfg21_f_minus_r_polynomial(
332 gamma_power * Z_T_minus_z_j_at_z[j],
334 internal::polynomial_add_inplace(L, H_j);
336 // Update gamma_power
337 for (size_t i = 0; i < f_sets[j].size(); ++i) {
338 gamma_power *= gamma;
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));
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);
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,
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)
363 // See Section 4.1 [BDFG21].
365 // The verifier accepts if:
367 // e(F + z W', [1]_2) = e(W', [x]_2)
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
375 // as in create_evaluation_witness,
377 // r_i(X) = f_i(z_j) = evaluation[j][k]
379 // for appropriate j and k.
381 // Compute Z_T(z) and Z_{T\S_i}(z).
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]));
388 // For i = 1, ...,k we sum value
390 // G_i = \gamma^{i-1} Z_{T\S_i}(z) cm_i (in G1)
394 // H_i = \gamma^{i-1} Z_{T\S_i}(z) r_i(z) (in Fr)
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
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).
405 Field gamma_power = 1;
407 libff::G1<ppT> G = libff::G1<ppT>::zero();
408 Field H = Field::zero();
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());
416 for (size_t k = 0; k < evals.size(); ++k) {
417 const Field factor = gamma_power * Z_T_minus_S_at_z;
419 // G = G + ( \gamma^{i-1} Z_{T\S_i}(z) ) cm_i
420 G = G + (factor * cms[k]);
422 // H = H + \gamma^{i-1} Z_{T\S_i}(z) r_i(z)
423 H += factor * evals[k];
425 gamma_power *= gamma;
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;
433 // The pairing check can be performed as:
435 // e(A, B) * e(C, D) = 1
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);
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();
455 } // namespace libsnark
457 #endif // __LIBSNARK_POLYNOMIAL_COMMITMENTS_BDFG21_TCC__