Clearmatics Libsnark  0.1
C++ library for zkSNARK proofs
r1cs_to_qap.tcc
Go to the documentation of this file.
1 /** @file
2  *****************************************************************************
3 
4  Implementation of interfaces for a R1CS-to-QAP reduction.
5 
6  See r1cs_to_qap.hpp .
7 
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  *****************************************************************************/
13 
14 #ifndef R1CS_TO_QAP_TCC_
15 #define R1CS_TO_QAP_TCC_
16 
17 #include <libff/common/profiling.hpp>
18 #include <libff/common/utils.hpp>
19 #include <libfqfft/evaluation_domain/get_evaluation_domain.hpp>
20 
21 namespace libsnark
22 {
23 
24 /**
25  * Instance map for the R1CS-to-QAP reduction.
26  *
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.
31  */
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)
35 {
36  libff::enter_block("Call to r1cs_to_qap_instance_map");
37 
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);
42 
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);
49 
50  libff::enter_block("Compute polynomials A, B, C in Lagrange basis");
51  /**
52  * add and process the constraints
53  * input_i * 0 = 0
54  * to ensure soundness of input consistency
55  */
56  for (size_t i = 0; i <= cs.num_inputs(); ++i) {
57  A_in_Lagrange_basis[i][cs.num_constraints() + i] = FieldT::one();
58  }
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;
64  }
65 
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;
69  }
70 
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;
74  }
75  }
76  libff::leave_block("Compute polynomials A, B, C in Lagrange basis");
77 
78  libff::leave_block("Call to r1cs_to_qap_instance_map");
79 
80  return qap_instance<FieldT>(
81  domain,
82  cs.num_variables(),
83  domain->m,
84  cs.num_inputs(),
85  std::move(A_in_Lagrange_basis),
86  std::move(B_in_Lagrange_basis),
87  std::move(C_in_Lagrange_basis));
88 }
89 
90 /**
91  * Instance map for the R1CS-to-QAP reduction followed by evaluation of the
92  * resulting QAP instance.
93  *
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"
101  * where
102  * m = number of variables of the QAP
103  * n = degree of the QAP
104  */
105 template<typename FieldT>
106 qap_instance_evaluation<FieldT> r1cs_to_qap_instance_map_with_evaluation(
107  const r1cs_constraint_system<FieldT> &cs,
108  const FieldT &t,
109  bool force_pow_2_domain)
110 {
111  libff::enter_block("Call to r1cs_to_qap_instance_map_with_evaluation");
112 
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);
117 
118  std::vector<FieldT> At, Bt, Ct, Ht;
119 
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);
124 
125  const FieldT Zt = domain->compute_vanishing_polynomial(t);
126 
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);
129  /**
130  * add and process the constraints
131  * input_i * 0 = 0
132  * to ensure soundness of input consistency
133  */
134  for (size_t i = 0; i <= cs.num_inputs(); ++i) {
135  At[i] = u[cs.num_constraints() + i];
136  }
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;
142  }
143 
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;
147  }
148 
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;
152  }
153  }
154 
155  FieldT ti = FieldT::one();
156  for (size_t i = 0; i < domain->m + 1; ++i) {
157  Ht.emplace_back(ti);
158  ti *= t;
159  }
160  libff::leave_block("Compute evaluations of A, B, C, H at t");
161 
162  libff::leave_block("Call to r1cs_to_qap_instance_map_with_evaluation");
163 
164  return qap_instance_evaluation<FieldT>(
165  domain,
166  cs.num_variables(),
167  domain->m,
168  cs.num_inputs(),
169  t,
170  std::move(At),
171  std::move(Bt),
172  std::move(Ct),
173  std::move(Ht),
174  Zt);
175 }
176 
177 /**
178  * Witness map for the R1CS-to-QAP reduction.
179  *
180  * The witness map takes zero knowledge into account when d1,d2,d3 are random.
181  *
182  * More precisely, compute the coefficients
183  * h_0,h_1,...,h_n
184  * of the polynomial
185  * H(z) := (A(z)*B(z)-C(z))/Z(z)
186  * where
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"
191  * and
192  * m = number of variables of the QAP
193  * n = degree of the QAP
194  *
195  *
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 )
205  *
206  * The code below is not as simple as the above high-level description due to
207  * some reshuffling to save space.
208  */
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,
214  const FieldT &d1,
215  const FieldT &d2,
216  const FieldT &d3,
217  bool force_pow_2_domain)
218 {
219  libff::enter_block("Call to r1cs_to_qap_witness_map");
220 
221  /* sanity check */
222  assert(cs.is_satisfied(primary_input, auxiliary_input));
223 
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);
228 
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());
234 
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());
238 
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());
243  }
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);
248  }
249  libff::leave_block("Compute evaluation of polynomials A, B on set S");
250 
251  libff::enter_block("Compute coefficients of polynomial A");
252  domain->iFFT(aA);
253  libff::leave_block("Compute coefficients of polynomial A");
254 
255  libff::enter_block("Compute coefficients of polynomial B");
256  domain->iFFT(aB);
257  libff::leave_block("Compute coefficients of polynomial B");
258 
259  libff::enter_block("Compute ZK-patch");
260  std::vector<FieldT> coefficients_for_H(domain->m + 1, FieldT::zero());
261 #ifdef MULTICORE
262 #pragma omp parallel for
263 #endif
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];
267  }
268  coefficients_for_H[0] -= d3;
269  domain->add_poly_Z(d1 * d2, coefficients_for_H);
270  libff::leave_block("Compute ZK-patch");
271 
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");
275 
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");
279 
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;
283 #ifdef MULTICORE
284 #pragma omp parallel for
285 #endif
286  for (size_t i = 0; i < domain->m; ++i) {
287  H_tmp[i] = aA[i] * aB[i];
288  }
289  // destroy aB
290  std::vector<FieldT>().swap(aB);
291 
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);
296  }
297  libff::leave_block("Compute evaluation of polynomial C on set S");
298 
299  libff::enter_block("Compute coefficients of polynomial C");
300  domain->iFFT(aC);
301  libff::leave_block("Compute coefficients of polynomial C");
302 
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");
306 
307 #ifdef MULTICORE
308 #pragma omp parallel for
309 #endif
310  for (size_t i = 0; i < domain->m; ++i) {
311  H_tmp[i] = (H_tmp[i] - aC[i]);
312  }
313 
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");
317 
318  libff::leave_block("Compute evaluation of polynomial H on set T");
319 
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");
323 
324  libff::enter_block("Compute sum of H and ZK-patch");
325 #ifdef MULTICORE
326 #pragma omp parallel for
327 #endif
328  for (size_t i = 0; i < domain->m; ++i) {
329  coefficients_for_H[i] += H_tmp[i];
330  }
331  libff::leave_block("Compute sum of H and ZK-patch");
332 
333  libff::leave_block("Call to r1cs_to_qap_witness_map");
334 
335  return qap_witness<FieldT>(
336  cs.num_variables(),
337  domain->m,
338  cs.num_inputs(),
339  d1,
340  d2,
341  d3,
342  full_variable_assignment,
343  std::move(coefficients_for_H));
344 }
345 
346 } // namespace libsnark
347 
348 #endif // R1CS_TO_QAP_TCC_