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_KZG10_BATCHED_TCC__
10 #define __LIBSNARK_POLYNOMIAL_COMMITMENTS_KZG10_BATCHED_TCC__
12 #include "libsnark/polynomial_commitments/kzg10_batched.hpp"
20 template<typename FieldT>
21 static polynomial<FieldT> polynomial_scalar_mul(
22 const polynomial<FieldT> &poly, const FieldT &factor)
24 polynomial<FieldT> out;
25 out.reserve(poly.size());
26 for (const FieldT &c : poly) {
27 out.push_back(c * factor);
32 template<typename FieldT>
33 static void polynomial_scalar_mul_inplace(
34 polynomial<FieldT> &poly, const FieldT &factor)
36 for (FieldT &c : poly) {
41 template<typename FieldT>
42 static void polynomial_add_inplace(
43 polynomial<FieldT> &poly, const polynomial<FieldT> &other)
45 // If `other` is longer, resize `polynomial` and copy any trailing
46 // coefficients. We then add in the coefficients from other which were not
48 const size_t p_orig_size = poly.size();
49 if (p_orig_size < other.size()) {
50 poly.resize(other.size());
52 other.begin() + p_orig_size,
54 poly.begin() + p_orig_size);
55 for (size_t i = 0; i < p_orig_size; ++i) {
62 // Add coefficients of `other` into `polynomial`.
63 for (size_t i = 0; i < other.size(); ++i) {
68 template<typename FieldT>
69 static polynomial<FieldT> polynomial_accumulate_with_power_factors(
70 const std::vector<polynomial<FieldT>> &polys, const FieldT &factor)
72 const size_t t = polys.size();
78 // f_accum = polys[t-2] + factor * polys[t-1].
80 // For each remaining polynomial f:
81 // f_accum <- factor * f_accum + f
84 polynomial<FieldT> f_accum = polynomial_scalar_mul(polys[i], factor);
85 polynomial_add_inplace(f_accum, polys[--i]);
87 polynomial_scalar_mul_inplace(f_accum, factor);
88 polynomial_add_inplace(f_accum, polys[--i]);
94 } // namespace internal
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)
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)
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,
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();
122 std::vector<Field> s_1s;
124 for (const polynomial<Field> &f_i : fs) {
125 s_1s.push_back(libfqfft::evaluate_polynomial(f_i.size(), f_i, z_1));
128 std::vector<Field> s_2s;
130 for (const polynomial<Field> &g_i : gs) {
131 s_2s.push_back(libfqfft::evaluate_polynomial(g_i.size(), g_i, z_2));
134 return evaluations(std::move(s_1s), std::move(s_2s));
137 template<typename ppT>
138 typename kzg10_batched_2_point<ppT>::evaluation_witness kzg10_batched_2_point<
140 create_evaluation_witness(
141 const std::vector<polynomial<Field>> &fs,
142 const std::vector<polynomial<Field>> &gs,
145 const evaluations &evaluations,
147 const Field &gamma_1,
148 const Field &gamma_2)
150 assert(fs.size() == evaluations.s_1s.size());
151 assert(gs.size() == evaluations.s_2s.size());
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'$.
159 // To generate the witness we must compute:
161 // h_1(X) = \sum_{i=1}^{t_1} \gamma_1^{i-1} (f_i(X) - f_i(z_1))/(X - z_1)
163 // h_2(X) = \sum_{i=1}^{t_2} \gamma_2^{i-1} (g_i(X) - g_i(z_2))/(X - z_2)
170 // This is computed as follows:
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
178 // Similarly for $\gamma_2$, $g_i$s and $z_2$.
180 // The evaluations `f_accum_eval` and `g_accum_eval` of the accumulated
181 // polynomials `f_accum` and `g_accum` are computed as:
183 // s_1[0] + s_1[1] * gamma_1 + ... + s_1[t1 - 1] gamma_1^{t1 - 1}
185 // which can be viewed as the evaluation at `gamma_1` of the polynomial
186 // with coefficients `s_1`.
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.
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);
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);
207 return evaluation_witness(f_accum_witness, g_accum_witness);
210 template<typename ppT>
211 bool kzg10_batched_2_point<ppT>::verify_evaluations(
214 const evaluations &evaluations,
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,
223 // See Section 3, p13 of [GWC19].
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());
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)
235 const std::vector<Field> &s_1s = evaluations.s_1s;
236 const std::vector<Field> &s_2s = evaluations.s_2s;
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()
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];
251 const libff::G1<ppT> G = cm_1_accum - s_1_accum * libff::G1<ppT>::one();
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()
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];
265 const libff::G1<ppT> H =
266 r * (cm_2_accum - s_2_accum * libff::G1<ppT>::one());
268 const libff::G1<ppT> F = G + H;
270 // The pairing check takes the form:
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
275 // (see p13 of [GWC19]).
277 // A = F + z_1 * W_1 + r * z_2 * W2
279 // C = -W_1 - r * W_2
282 const libff::G1<ppT> &W_1 = witness.W_1;
283 const libff::G1<ppT> &W_2 = witness.W_2;
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);
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);
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();
300 } // namespace libsnark
302 #endif // __LIBSNARK_POLYNOMIAL_COMMITMENTS_KZG10_BATCHED_TCC__