2 *****************************************************************************
4 Implementation of interfaces for a R1CS-to-QAP reduction.
8 *****************************************************************************
9 * @author This file is part of libsnark, developed by SCIPR Lab
10 * and contributors (see AUTHORS).
11 * @copyright MIT license (see LICENSE file)
12 *****************************************************************************/
14 #ifndef R1CS_TO_QAP_TCC_
15 #define R1CS_TO_QAP_TCC_
17 #include <libff/common/profiling.hpp>
18 #include <libff/common/utils.hpp>
19 #include <libfqfft/evaluation_domain/get_evaluation_domain.hpp>
25 * Instance map for the R1CS-to-QAP reduction.
27 * Namely, given a R1CS constraint system cs, construct a QAP instance for
28 * which: A := (A_0(z),A_1(z),...,A_m(z)) B := (B_0(z),B_1(z),...,B_m(z)) C :=
29 * (C_0(z),C_1(z),...,C_m(z)) where m = number of variables of the QAP and each
30 * A_i,B_i,C_i is expressed in the Lagrange basis.
32 template<typename FieldT>
33 qap_instance<FieldT> r1cs_to_qap_instance_map(
34 const r1cs_constraint_system<FieldT> &cs, bool force_pow_2_domain)
36 libff::enter_block("Call to r1cs_to_qap_instance_map");
38 const size_t min_n = cs.num_constraints() + cs.num_inputs() + 1;
39 const size_t n = force_pow_2_domain ? 1 << libff::log2(min_n) : min_n;
40 std::shared_ptr<libfqfft::evaluation_domain<FieldT>> domain =
41 libfqfft::get_evaluation_domain<FieldT>(n);
43 std::vector<std::map<size_t, FieldT>> A_in_Lagrange_basis(
44 cs.num_variables() + 1);
45 std::vector<std::map<size_t, FieldT>> B_in_Lagrange_basis(
46 cs.num_variables() + 1);
47 std::vector<std::map<size_t, FieldT>> C_in_Lagrange_basis(
48 cs.num_variables() + 1);
50 libff::enter_block("Compute polynomials A, B, C in Lagrange basis");
52 * add and process the constraints
54 * to ensure soundness of input consistency
56 for (size_t i = 0; i <= cs.num_inputs(); ++i) {
57 A_in_Lagrange_basis[i][cs.num_constraints() + i] = FieldT::one();
59 /* process all other constraints */
60 for (size_t i = 0; i < cs.num_constraints(); ++i) {
61 for (size_t j = 0; j < cs.constraints[i].a.terms.size(); ++j) {
62 A_in_Lagrange_basis[cs.constraints[i].a.terms[j].index][i] +=
63 cs.constraints[i].a.terms[j].coeff;
66 for (size_t j = 0; j < cs.constraints[i].b.terms.size(); ++j) {
67 B_in_Lagrange_basis[cs.constraints[i].b.terms[j].index][i] +=
68 cs.constraints[i].b.terms[j].coeff;
71 for (size_t j = 0; j < cs.constraints[i].c.terms.size(); ++j) {
72 C_in_Lagrange_basis[cs.constraints[i].c.terms[j].index][i] +=
73 cs.constraints[i].c.terms[j].coeff;
76 libff::leave_block("Compute polynomials A, B, C in Lagrange basis");
78 libff::leave_block("Call to r1cs_to_qap_instance_map");
80 return qap_instance<FieldT>(
85 std::move(A_in_Lagrange_basis),
86 std::move(B_in_Lagrange_basis),
87 std::move(C_in_Lagrange_basis));
91 * Instance map for the R1CS-to-QAP reduction followed by evaluation of the
92 * resulting QAP instance.
94 * Namely, given a R1CS constraint system cs and a field element t, construct
95 * a QAP instance (evaluated at t) for which:
96 * At := (A_0(t),A_1(t),...,A_m(t))
97 * Bt := (B_0(t),B_1(t),...,B_m(t))
98 * Ct := (C_0(t),C_1(t),...,C_m(t))
99 * Ht := (1,t,t^2,...,t^n)
100 * Zt := Z(t) = "vanishing polynomial of a certain set S, evaluated at t"
102 * m = number of variables of the QAP
103 * n = degree of the QAP
105 template<typename FieldT>
106 qap_instance_evaluation<FieldT> r1cs_to_qap_instance_map_with_evaluation(
107 const r1cs_constraint_system<FieldT> &cs,
109 bool force_pow_2_domain)
111 libff::enter_block("Call to r1cs_to_qap_instance_map_with_evaluation");
113 const size_t min_n = cs.num_constraints() + cs.num_inputs() + 1;
114 const size_t n = force_pow_2_domain ? 1 << libff::log2(min_n) : min_n;
115 std::shared_ptr<libfqfft::evaluation_domain<FieldT>> domain =
116 libfqfft::get_evaluation_domain<FieldT>(n);
118 std::vector<FieldT> At, Bt, Ct, Ht;
120 At.resize(cs.num_variables() + 1, FieldT::zero());
121 Bt.resize(cs.num_variables() + 1, FieldT::zero());
122 Ct.resize(cs.num_variables() + 1, FieldT::zero());
123 Ht.reserve(domain->m + 1);
125 const FieldT Zt = domain->compute_vanishing_polynomial(t);
127 libff::enter_block("Compute evaluations of A, B, C, H at t");
128 const std::vector<FieldT> u = domain->evaluate_all_lagrange_polynomials(t);
130 * add and process the constraints
132 * to ensure soundness of input consistency
134 for (size_t i = 0; i <= cs.num_inputs(); ++i) {
135 At[i] = u[cs.num_constraints() + i];
137 /* process all other constraints */
138 for (size_t i = 0; i < cs.num_constraints(); ++i) {
139 for (size_t j = 0; j < cs.constraints[i].a.terms.size(); ++j) {
140 At[cs.constraints[i].a.terms[j].index] +=
141 u[i] * cs.constraints[i].a.terms[j].coeff;
144 for (size_t j = 0; j < cs.constraints[i].b.terms.size(); ++j) {
145 Bt[cs.constraints[i].b.terms[j].index] +=
146 u[i] * cs.constraints[i].b.terms[j].coeff;
149 for (size_t j = 0; j < cs.constraints[i].c.terms.size(); ++j) {
150 Ct[cs.constraints[i].c.terms[j].index] +=
151 u[i] * cs.constraints[i].c.terms[j].coeff;
155 FieldT ti = FieldT::one();
156 for (size_t i = 0; i < domain->m + 1; ++i) {
160 libff::leave_block("Compute evaluations of A, B, C, H at t");
162 libff::leave_block("Call to r1cs_to_qap_instance_map_with_evaluation");
164 return qap_instance_evaluation<FieldT>(
178 * Witness map for the R1CS-to-QAP reduction.
180 * The witness map takes zero knowledge into account when d1,d2,d3 are random.
182 * More precisely, compute the coefficients
185 * H(z) := (A(z)*B(z)-C(z))/Z(z)
187 * A(z) := A_0(z) + \sum_{k=1}^{m} w_k A_k(z) + d1 * Z(z)
188 * B(z) := B_0(z) + \sum_{k=1}^{m} w_k B_k(z) + d2 * Z(z)
189 * C(z) := C_0(z) + \sum_{k=1}^{m} w_k C_k(z) + d3 * Z(z)
190 * Z(z) := "vanishing polynomial of set S"
192 * m = number of variables of the QAP
193 * n = degree of the QAP
196 * This is done as follows, where T is the coset gS of S, for g the
197 * multiplicative generator of the field, (i.e. T={g*s | s \in S}):
198 * (1) compute evaluations of A,B,C on S = {sigma_1,...,sigma_n}
199 * (2) compute coefficients of A,B,C (iFFT)
200 * (3) compute evaluations of A,B,C on T (cosetFFT)
201 * (4) compute evaluation of H on T (in terms of evaluations of A, B, C)
202 * (5) compute coefficients of H (icosetFFT)
203 * (6) patch H to account for d1,d2,d3 (i.e., add coefficients of the
204 * polynomial (A d2 + B d1 - d3) + d1*d2*Z )
206 * The code below is not as simple as the above high-level description due to
207 * some reshuffling to save space.
209 template<typename FieldT>
210 qap_witness<FieldT> r1cs_to_qap_witness_map(
211 const r1cs_constraint_system<FieldT> &cs,
212 const r1cs_primary_input<FieldT> &primary_input,
213 const r1cs_auxiliary_input<FieldT> &auxiliary_input,
217 bool force_pow_2_domain)
219 libff::enter_block("Call to r1cs_to_qap_witness_map");
222 assert(cs.is_satisfied(primary_input, auxiliary_input));
224 const size_t min_n = cs.num_constraints() + cs.num_inputs() + 1;
225 const size_t n = force_pow_2_domain ? 1 << libff::log2(min_n) : min_n;
226 std::shared_ptr<libfqfft::evaluation_domain<FieldT>> domain =
227 libfqfft::get_evaluation_domain<FieldT>(n);
229 r1cs_variable_assignment<FieldT> full_variable_assignment = primary_input;
230 full_variable_assignment.insert(
231 full_variable_assignment.end(),
232 auxiliary_input.begin(),
233 auxiliary_input.end());
235 libff::enter_block("Compute evaluation of polynomials A, B on set S");
236 std::vector<FieldT> aA(domain->m, FieldT::zero()),
237 aB(domain->m, FieldT::zero());
239 /* account for the additional constraints input_i * 0 = 0 */
240 for (size_t i = 0; i <= cs.num_inputs(); ++i) {
241 aA[i + cs.num_constraints()] =
242 (i > 0 ? full_variable_assignment[i - 1] : FieldT::one());
244 /* account for all other constraints */
245 for (size_t i = 0; i < cs.num_constraints(); ++i) {
246 aA[i] += cs.constraints[i].a.evaluate(full_variable_assignment);
247 aB[i] += cs.constraints[i].b.evaluate(full_variable_assignment);
249 libff::leave_block("Compute evaluation of polynomials A, B on set S");
251 libff::enter_block("Compute coefficients of polynomial A");
253 libff::leave_block("Compute coefficients of polynomial A");
255 libff::enter_block("Compute coefficients of polynomial B");
257 libff::leave_block("Compute coefficients of polynomial B");
259 libff::enter_block("Compute ZK-patch");
260 std::vector<FieldT> coefficients_for_H(domain->m + 1, FieldT::zero());
262 #pragma omp parallel for
264 /* add coefficients of the polynomial (d2*A + d1*B - d3) + d1*d2*Z */
265 for (size_t i = 0; i < domain->m; ++i) {
266 coefficients_for_H[i] = d2 * aA[i] + d1 * aB[i];
268 coefficients_for_H[0] -= d3;
269 domain->add_poly_Z(d1 * d2, coefficients_for_H);
270 libff::leave_block("Compute ZK-patch");
272 libff::enter_block("Compute evaluation of polynomial A on set T");
273 domain->cosetFFT(aA, FieldT::multiplicative_generator);
274 libff::leave_block("Compute evaluation of polynomial A on set T");
276 libff::enter_block("Compute evaluation of polynomial B on set T");
277 domain->cosetFFT(aB, FieldT::multiplicative_generator);
278 libff::leave_block("Compute evaluation of polynomial B on set T");
280 libff::enter_block("Compute evaluation of polynomial H on set T");
281 // can overwrite aA because it is not used later
282 std::vector<FieldT> &H_tmp = aA;
284 #pragma omp parallel for
286 for (size_t i = 0; i < domain->m; ++i) {
287 H_tmp[i] = aA[i] * aB[i];
290 std::vector<FieldT>().swap(aB);
292 libff::enter_block("Compute evaluation of polynomial C on set S");
293 std::vector<FieldT> aC(domain->m, FieldT::zero());
294 for (size_t i = 0; i < cs.num_constraints(); ++i) {
295 aC[i] += cs.constraints[i].c.evaluate(full_variable_assignment);
297 libff::leave_block("Compute evaluation of polynomial C on set S");
299 libff::enter_block("Compute coefficients of polynomial C");
301 libff::leave_block("Compute coefficients of polynomial C");
303 libff::enter_block("Compute evaluation of polynomial C on set T");
304 domain->cosetFFT(aC, FieldT::multiplicative_generator);
305 libff::leave_block("Compute evaluation of polynomial C on set T");
308 #pragma omp parallel for
310 for (size_t i = 0; i < domain->m; ++i) {
311 H_tmp[i] = (H_tmp[i] - aC[i]);
314 libff::enter_block("Divide by Z on set T");
315 domain->divide_by_Z_on_coset(H_tmp);
316 libff::leave_block("Divide by Z on set T");
318 libff::leave_block("Compute evaluation of polynomial H on set T");
320 libff::enter_block("Compute coefficients of polynomial H");
321 domain->icosetFFT(H_tmp, FieldT::multiplicative_generator);
322 libff::leave_block("Compute coefficients of polynomial H");
324 libff::enter_block("Compute sum of H and ZK-patch");
326 #pragma omp parallel for
328 for (size_t i = 0; i < domain->m; ++i) {
329 coefficients_for_H[i] += H_tmp[i];
331 libff::leave_block("Compute sum of H and ZK-patch");
333 libff::leave_block("Call to r1cs_to_qap_witness_map");
335 return qap_witness<FieldT>(
342 full_variable_assignment,
343 std::move(coefficients_for_H));
346 } // namespace libsnark
348 #endif // R1CS_TO_QAP_TCC_