1 // Copyright (c) 2015-2022 Clearmatics Technologies Ltd
3 // SPDX-License-Identifier: LGPL-3.0+
5 #ifndef __ZETH_MPC_GROTH16_MPC_UTILS_TCC__
6 #define __ZETH_MPC_GROTH16_MPC_UTILS_TCC__
8 #include "libzeth/core/evaluator_from_lagrange.hpp"
9 #include "libzeth/core/utils.hpp"
10 #include "libzeth/mpc/groth16/mpc_utils.hpp"
11 #include "libzeth/mpc/groth16/phase2.hpp"
15 #include <libfqfft/evaluation_domain/domains/basic_radix2_domain_aux.tcc>
20 template<typename ppT>
21 srs_mpc_layer_L1<ppT>::srs_mpc_layer_L1(
22 libff::G1_vector<ppT> &&T_tau_powers_g1,
23 libff::G1_vector<ppT> &&A_g1,
24 libff::G1_vector<ppT> &&B_g1,
25 libff::G2_vector<ppT> &&B_g2,
26 libff::G1_vector<ppT> &&ABC_g1)
27 : T_tau_powers_g1(std::move(T_tau_powers_g1))
28 , A_g1(std::move(A_g1))
29 , B_g1(std::move(B_g1))
30 , B_g2(std::move(B_g2))
31 , ABC_g1(std::move(ABC_g1))
35 template<typename ppT> size_t srs_mpc_layer_L1<ppT>::degree() const
37 return T_tau_powers_g1.size() + 1;
40 template<typename ppT> bool srs_mpc_layer_L1<ppT>::is_well_formed() const
42 return libzeth::container_is_well_formed(T_tau_powers_g1) &&
43 libzeth::container_is_well_formed(A_g1) &&
44 libzeth::container_is_well_formed(B_g1) &&
45 libzeth::container_is_well_formed(B_g2) &&
46 libzeth::container_is_well_formed(ABC_g1);
49 template<typename ppT>
50 void srs_mpc_layer_L1<ppT>::write(std::ostream &out) const
52 using G1 = libff::G1<ppT>;
53 using G2 = libff::G2<ppT>;
54 check_well_formed(*this, "mpc_layer1 (write)");
56 // Write the sizes first, then stream out the values.
57 const size_t num_T_tau_powers = T_tau_powers_g1.size();
58 const size_t num_polynomials = A_g1.size();
59 out.write((const char *)&num_T_tau_powers, sizeof(num_T_tau_powers));
60 out.write((const char *)&num_polynomials, sizeof(num_polynomials));
62 for (const G1 &v : T_tau_powers_g1) {
66 for (const G1 &v : A_g1) {
70 for (const G1 &v : B_g1) {
74 for (const G2 &v : B_g2) {
78 for (const G1 &v : ABC_g1) {
83 template<typename ppT>
84 srs_mpc_layer_L1<ppT> srs_mpc_layer_L1<ppT>::read(std::istream &in)
86 using G1 = libff::G1<ppT>;
87 using G2 = libff::G2<ppT>;
89 size_t num_T_tau_powers;
90 size_t num_polynomials;
92 in.read((char *)&num_T_tau_powers, sizeof(num_T_tau_powers));
93 in.read((char *)&num_polynomials, sizeof(num_polynomials));
95 libff::G1_vector<ppT> T_tau_powers_g1(num_T_tau_powers);
96 libff::G1_vector<ppT> A_g1(num_polynomials);
97 libff::G1_vector<ppT> B_g1(num_polynomials);
98 libff::G2_vector<ppT> B_g2(num_polynomials);
99 libff::G1_vector<ppT> ABC_g1(num_polynomials);
101 for (G1 &v : T_tau_powers_g1) {
117 for (G1 &v : ABC_g1) {
121 srs_mpc_layer_L1<ppT> l1(
122 std::move(T_tau_powers_g1),
127 check_well_formed(l1, "mpc_layer1 (read)");
131 template<typename ppT>
132 srs_mpc_layer_L1<ppT> mpc_compute_linearcombination(
133 const srs_powersoftau<ppT> &pot,
134 const srs_lagrange_evaluations<ppT> &lagrange,
135 const libsnark::qap_instance<libff::Fr<ppT>> &qap)
137 using Fr = libff::Fr<ppT>;
138 using G1 = libff::G1<ppT>;
139 using G2 = libff::G2<ppT>;
140 libff::enter_block("Call to mpc_compute_linearcombination");
142 // n = number of constraints in r1cs, or equivalently, n = deg(t(x))
143 // t(x) being the target polynomial of the QAP
144 // Note: In the code-base the target polynomial is also denoted Z
145 // as referred to as "the vanishing polynomial", and t is also used
146 // to represent the query point (aka "tau").
147 const size_t n = qap.degree();
148 const size_t num_variables = qap.num_variables();
150 if (n != 1ull << libff::log2(n)) {
151 throw std::invalid_argument("non-pow-2 domain");
153 if (n != lagrange.degree) {
154 throw std::invalid_argument(
155 "domain size differs from Lagrange evaluation");
158 libff::print_indent();
159 printf("n=%zu\n", n);
161 // The QAP polynomials A, B, C are of degree (n-1) as we know they
162 // are created by interpolation of an r1cs of n constraints.
163 // As a consequence, the polynomial (A.B - C) is of degree 2n-2,
164 // while the target polynomial t is of degree n.
165 // Thus, we need to have access (in the SRS) to powers up to 2n-2.
166 // To represent such polynomials we need {x^i} for in {0, ... n-2}
167 // hence we check below that we have at least n-1 elements
168 // in the set of powers of tau
169 assert(pot.tau_powers_g1.size() >= 2 * n - 1);
171 // Domain uses n-roots of unity, so
173 // => t(x) . x^i = x^(n+i) - x^i
174 libff::G1_vector<ppT> t_x_pow_i(n - 1, G1::zero());
175 libff::enter_block("computing [t(x) . x^i]_1");
176 for (size_t i = 0; i < n - 1; ++i) {
177 t_x_pow_i[i] = pot.tau_powers_g1[n + i] - pot.tau_powers_g1[i];
179 libff::leave_block("computing [t(x) . x^i]_1");
181 libff::enter_block("computing A_i, B_i, C_i, ABC_i at x");
182 libff::G1_vector<ppT> As_g1(num_variables + 1);
183 libff::G1_vector<ppT> Bs_g1(num_variables + 1);
184 libff::G2_vector<ppT> Bs_g2(num_variables + 1);
185 libff::G1_vector<ppT> Cs_g1(num_variables + 1);
186 libff::G1_vector<ppT> ABCs_g1(num_variables + 1);
188 #pragma omp parallel for
190 for (size_t j = 0; j < num_variables + 1; ++j) {
191 G1 ABC_j_at_x = G1::zero();
194 G1 A_j_at_x = G1::zero();
195 const std::map<size_t, Fr> &A_j_lagrange =
196 qap.A_in_Lagrange_basis[j];
197 for (const auto &entry : A_j_lagrange) {
198 A_j_at_x = A_j_at_x +
199 (entry.second * lagrange.lagrange_g1[entry.first]);
202 (entry.second * lagrange.beta_lagrange_g1[entry.first]);
209 G1 B_j_at_x_g1 = G1::zero();
210 G2 B_j_at_x_g2 = G2::zero();
212 const std::map<size_t, Fr> &B_j_lagrange =
213 qap.B_in_Lagrange_basis[j];
214 for (const auto &entry : B_j_lagrange) {
215 B_j_at_x_g1 = B_j_at_x_g1 + (entry.second *
216 lagrange.lagrange_g1[entry.first]);
217 B_j_at_x_g2 = B_j_at_x_g2 + (entry.second *
218 lagrange.lagrange_g2[entry.first]);
221 (entry.second * lagrange.alpha_lagrange_g1[entry.first]);
224 Bs_g1[j] = B_j_at_x_g1;
225 Bs_g2[j] = B_j_at_x_g2;
229 G1 C_j_at_x = G1::zero();
230 const std::map<size_t, Fr> &C_j_lagrange =
231 qap.C_in_Lagrange_basis[j];
232 for (const auto &entry : C_j_lagrange) {
234 C_j_at_x + entry.second * lagrange.lagrange_g1[entry.first];
238 ABC_j_at_x = ABC_j_at_x + C_j_at_x;
241 ABCs_g1[j] = ABC_j_at_x;
243 libff::leave_block("computing A_i, B_i, C_i, ABC_i at x");
245 // TODO: Consider dropping those entries we know will not be used
246 // by this circuit and using sparse vectors where it makes sense
247 // (as is done for B_i's in r1cs_gg_ppzksnark_proving_key).
248 libff::leave_block("Call to mpc_compute_linearcombination");
250 return srs_mpc_layer_L1<ppT>(
251 std::move(t_x_pow_i),
258 } // namespace libzeth
260 #endif // __ZETH_MPC_GROTH16_MPC_UTILS_TCC__