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_TCC__
10 #define __LIBSNARK_POLYNOMIAL_COMMITMENTS_KZG10_TCC__
12 #include "libsnark/polynomial_commitments/kzg10.hpp"
14 #include <libff/algebra/scalar_multiplication/multiexp.hpp>
15 #include <libff/algebra/scalar_multiplication/wnaf.hpp>
16 #include <libfqfft/polynomial_arithmetic/basic_operations.hpp>
17 #include <libfqfft/polynomial_arithmetic/naive_evaluate.hpp>
22 template<typename ppT>
24 std::vector<libff::G1<ppT>> &&alpha_powers_g1,
25 const libff::G2<ppT> &alpha_g2)
26 : alpha_powers_g1(alpha_powers_g1), alpha_g2(alpha_g2)
30 template<typename ppT>
31 typename kzg10<ppT>::srs kzg10<ppT>::setup_from_secret(
32 size_t max_degree, const Field &alpha)
34 const libff::bigint<Field::num_limbs> alpha_bigint = alpha.as_bigint();
35 const size_t window_size = std::max(
36 libff::wnaf_opt_window_size<libff::G1<ppT>>(alpha_bigint.num_bits()),
38 const std::vector<long> naf =
39 libff::find_wnaf<Field::num_limbs>(window_size, alpha_bigint);
41 // TODO: perform in concurrent batches?
42 std::vector<libff::G1<ppT>> alpha_powers_g1;
43 alpha_powers_g1.reserve(max_degree + 1);
44 libff::G1<ppT> alpha_i_g1 = libff::G1<ppT>::one();
45 alpha_powers_g1.push_back(alpha_i_g1);
46 for (size_t i = 1; i < max_degree + 1; ++i) {
47 alpha_i_g1 = libff::fixed_window_wnaf_exp<libff::G1<ppT>>(
48 window_size, alpha_i_g1, naf);
49 alpha_powers_g1.push_back(alpha_i_g1);
52 // assert((libff::G1<ppT> * alpha.pow(max_degree)) ==
53 // alpha_powers_g1[max_degree]);
54 return srs(std::move(alpha_powers_g1), alpha * libff::G2<ppT>::one());
57 template<typename ppT>
58 typename kzg10<ppT>::srs kzg10<ppT>::setup(size_t max_degree)
60 const Field alpha = Field::random_element();
61 return setup_from_secret(max_degree, alpha);
64 template<typename ppT>
65 typename kzg10<ppT>::commitment kzg10<ppT>::commit(
66 const srs &srs, const polynomial<libff::Fr<ppT>> &phi)
68 const size_t num_coefficients = phi.size();
70 // Caller is responsible for checking this.
71 assert(num_coefficients <= srs.alpha_powers_g1.size());
74 const size_t chunks = omp_get_max_threads();
76 const size_t chunks = 1;
79 // The commitment is the encoding [ \phi(\alpha) ]_1. This is computed
80 // using the elements [ \alpha^t ]_1 for t=0, ..., max_degree from the srs.
81 return libff::multi_exp<
84 libff::multi_exp_method_BDLO12_signed>(
85 srs.alpha_powers_g1.begin(),
86 srs.alpha_powers_g1.begin() + num_coefficients,
92 template<typename ppT>
93 bool kzg10<ppT>::verify_poly(
94 const typename kzg10<ppT>::srs &srs,
95 typename kzg10<ppT>::commitment C,
96 const polynomial<libff::Fr<ppT>> &phi)
98 return C == commit(srs, phi);
101 template<typename ppT>
102 libff::Fr<ppT> kzg10<ppT>::evaluate_polynomial(
103 const polynomial<libff::Fr<ppT>> &phi, const libff::Fr<ppT> i)
105 const size_t num_coefficients = phi.size();
106 return libfqfft::evaluate_polynomial(num_coefficients, phi, i);
109 template<typename ppT>
110 typename kzg10<ppT>::evaluation_witness kzg10<ppT>::create_evaluation_witness(
111 const polynomial<libff::Fr<ppT>> &phi,
112 const libff::Fr<ppT> &i,
113 const libff::Fr<ppT> &evaluation,
114 const typename kzg10<ppT>::srs &srs)
117 // w_i = [ \psi_i(\alpha) ]_1
119 // \psi_i(x) = (\phi(x) - \phi(i)) / (x - i)
120 // (see [KZG10] Section 3.2)
122 // TODO: Find a more optimal way to compute the special case of dividing by
124 std::vector<Field> psi;
125 std::vector<Field> remainder;
127 std::vector<Field> phi_minus_phi_i = phi;
128 phi_minus_phi_i[0] -= evaluation;
129 libfqfft::_polynomial_division(psi, remainder, phi_minus_phi_i, {-i, 1});
130 if (!libfqfft::_is_zero(remainder)) {
131 throw std::invalid_argument(
132 "invalid evaluation point (poly div faild)");
134 return commit(srs, psi);
137 template<typename ppT>
138 bool kzg10<ppT>::verify_evaluation(
140 const Field &evaluation,
141 const typename kzg10<ppT>::srs &srs,
142 const evaluation_witness &witness,
143 const typename kzg10<ppT>::commitment &C)
145 // Verify the equality:
147 // \psi(\alpha) (\alpha - i) = \phi(\alpha) - \phi(i)
149 // via the pairing equality:
151 // e([\psi(\alpha)]_1, [\alpha - i]_2) = e(C - [phi_i]_1, [1]_2)
153 // which (multiplying both sides by e(C - [phi_i]_1, [1]_2)^{-1}) holds iff:
156 // = e([\psi(\alpha)]_1, [\alpha - i]_2) * e(C - [phi_i]_1, [1]_2)^{-1}
157 // = e([\psi(\alpha)]_1, [\alpha - i]_2) * e([phi_i]_1 - C, [1]_2)
158 // = e([\psi(\alpha)]_1, [\alpha]_2 - [i]_2) * e([phi_i]_1 - C, [1]_2)
160 // where id_T is the identity of the group GT (GT::one()). This
161 // last line can now be computed in terms of available variables:
163 // e(_A = witness.w, _B = srs.alpha_g2 - i * G2::one()) *
164 // e(_C = witness.phi_i * G1::one() - C, _D = G2::one())
166 // See Section 3: https://eprint.iacr.org/2019/953.pdf for further
169 const libff::G1_precomp<ppT> _A = ppT::precompute_G1(witness);
170 const libff::G2_precomp<ppT> _B =
171 ppT::precompute_G2(srs.alpha_g2 - i * libff::G2<ppT>::one());
172 const libff::G1_precomp<ppT> _C =
173 ppT::precompute_G1(evaluation * libff::G1<ppT>::one() - C);
174 const libff::G2_precomp<ppT> _D = ppT::precompute_G2(libff::G2<ppT>::one());
176 const libff::Fqk<ppT> miller_result =
177 ppT::double_miller_loop(_A, _B, _C, _D);
178 const libff::GT<ppT> result = ppT::final_exponentiation(miller_result);
179 return result == libff::GT<ppT>::one();
182 } // namespace libsnark
184 #endif // __LIBSNARK_POLYNOMIAL_COMMITMENTS_KZG10_TCC__