r1cs_to_sap.hpp
Go to the documentation of this file.
1 //---------------------------------------------------------------------------//
2 // Copyright (c) 2018-2021 Mikhail Komarov <nemo@nil.foundation>
3 // Copyright (c) 2020-2021 Nikita Kaskov <nbering@nil.foundation>
4 //
5 // MIT License
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a copy
8 // of this software and associated documentation files (the "Software"), to deal
9 // in the Software without restriction, including without limitation the rights
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 // copies of the Software, and to permit persons to whom the Software is
12 // furnished to do so, subject to the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included in all
15 // copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 // SOFTWARE.
24 //---------------------------------------------------------------------------//
25 // @file Declaration of interfaces for a R1CS-to-SAP reduction, that is, constructing
26 // a SAP ("Square Arithmetic Program") from a R1CS ("Rank-1 Constraint System").
27 //
28 // SAPs are defined and constructed from R1CS in \[GM17].
29 //
30 // The implementation of the reduction follows, extends, and optimizes
31 // the efficient approach described in Appendix E of \[BCGTV13].
32 //
33 // References:
34 //
35 // \[BCGTV13]
36 // "SNARKs for C: Verifying Program Executions Succinctly and in Zero Knowledge",
37 // Eli Ben-Sasson, Alessandro Chiesa, Daniel Genkin, Eran Tromer, Madars Virza,
38 // CRYPTO 2013,
39 // <http://eprint.iacr.org/2013/507>
40 //
41 // \[GM17]:
42 // "Snarky Signatures: Minimal Signatures of Knowledge from
43 // Simulation-Extractable SNARKs",
44 // Jens Groth and Mary Maller,
45 // IACR-CRYPTO-2017,
46 // <https://eprint.iacr.org/2017/540>
47 //---------------------------------------------------------------------------//
48 
49 #ifndef CRYPTO3_ZK_R1CS_TO_SAP_BASIC_POLICY_HPP
50 #define CRYPTO3_ZK_R1CS_TO_SAP_BASIC_POLICY_HPP
51 
53 #include <nil/crypto3/math/evaluation_domain.hpp>
54 
57 
58 namespace nil {
59  namespace crypto3 {
60  namespace zk {
61  namespace snark {
62  namespace reductions {
63  template<typename FieldType>
64  class r1cs_to_sap {
65  typedef FieldType field_type;
66 
70  static typename FieldType::value_type times_four(typename FieldType::value_type x) {
71  typename FieldType::value_type times_two = x + x;
72  return times_two + times_two;
73  }
74 
75  public:
80  static std::shared_ptr<math::evaluation_domain<FieldType>>
82  /*
83  * the SAP instance will have:
84  * - two constraints for every constraint in the original constraint system
85  * - two constraints for every public input, except the 0th, which
86  * contributes just one extra constraint
87  * see comments in instance_map for details on where these
88  * constraints come from.
89  */
90  return math::make_evaluation_domain<FieldType>(2 * cs.num_constraints() +
91  2 * cs.num_inputs() + 1);
92  }
93 
98  const std::shared_ptr<math::evaluation_domain<FieldType>> domain = get_domain(cs);
99 
100  std::size_t sap_num_variables = cs.num_variables() + cs.num_constraints() + cs.num_inputs();
101 
102  std::vector<std::map<std::size_t, typename FieldType::value_type>> A_in_Lagrange_basis(sap_num_variables + 1);
103  std::vector<std::map<std::size_t, typename FieldType::value_type>> C_in_Lagrange_basis(sap_num_variables + 1);
104 
119  std::size_t extra_var_offset = cs.num_variables() + 1;
120  for (std::size_t i = 0; i < cs.num_constraints(); ++i) {
121  for (std::size_t j = 0; j < cs.constraints[i].a.terms.size(); ++j) {
122  A_in_Lagrange_basis[cs.constraints[i].a.terms[j].index][2 * i] +=
123  cs.constraints[i].a.terms[j].coeff;
124  A_in_Lagrange_basis[cs.constraints[i].a.terms[j].index][2 * i + 1] +=
125  cs.constraints[i].a.terms[j].coeff;
126  }
127 
128  for (std::size_t j = 0; j < cs.constraints[i].b.terms.size(); ++j) {
129  A_in_Lagrange_basis[cs.constraints[i].b.terms[j].index][2 * i] +=
130  cs.constraints[i].b.terms[j].coeff;
131  A_in_Lagrange_basis[cs.constraints[i].b.terms[j].index][2 * i + 1] -=
132  cs.constraints[i].b.terms[j].coeff;
133  }
134 
135  for (std::size_t j = 0; j < cs.constraints[i].c.terms.size(); ++j) {
136  C_in_Lagrange_basis[cs.constraints[i].c.terms[j].index][2 * i] +=
137  times_four(cs.constraints[i].c.terms[j].coeff);
138  }
139 
140  C_in_Lagrange_basis[extra_var_offset + i][2 * i] += FieldType::value_type::one();
141  C_in_Lagrange_basis[extra_var_offset + i][2 * i + 1] += FieldType::value_type::one();
142  }
143 
165  std::size_t extra_constr_offset = 2 * cs.num_constraints();
166  std::size_t extra_var_offset2 = cs.num_variables() + cs.num_constraints();
172  A_in_Lagrange_basis[0][extra_constr_offset] = FieldType::value_type::one();
173  C_in_Lagrange_basis[0][extra_constr_offset] = FieldType::value_type::one();
174 
175  for (std::size_t i = 1; i <= cs.num_inputs(); ++i) {
176  A_in_Lagrange_basis[i][extra_constr_offset + 2 * i - 1] +=
177  FieldType::value_type::one();
178  A_in_Lagrange_basis[0][extra_constr_offset + 2 * i - 1] +=
179  FieldType::value_type::one();
180  C_in_Lagrange_basis[i][extra_constr_offset + 2 * i - 1] +=
181  times_four(FieldType::value_type::one());
182  C_in_Lagrange_basis[extra_var_offset2 + i][extra_constr_offset + 2 * i - 1] +=
183  FieldType::value_type::one();
184 
185  A_in_Lagrange_basis[i][extra_constr_offset + 2 * i] += FieldType::value_type::one();
186  A_in_Lagrange_basis[0][extra_constr_offset + 2 * i] -= FieldType::value_type::one();
187  C_in_Lagrange_basis[extra_var_offset2 + i][2 * cs.num_constraints() + 2 * i] +=
188  FieldType::value_type::one();
189  }
190 
191  return sap_instance<FieldType>(domain,
192  sap_num_variables,
193  domain->m,
194  cs.num_inputs(),
195  std::move(A_in_Lagrange_basis),
196  std::move(C_in_Lagrange_basis));
197  }
198 
205  const typename FieldType::value_type &t) {
206 
207  const std::shared_ptr<math::evaluation_domain<FieldType>> domain = get_domain(cs);
208 
209  std::size_t sap_num_variables = cs.num_variables() + cs.num_constraints() + cs.num_inputs();
210 
211  std::vector<typename FieldType::value_type> At, Ct, Ht;
212 
213  At.resize(sap_num_variables + 1, FieldType::value_type::zero());
214  Ct.resize(sap_num_variables + 1, FieldType::value_type::zero());
215  Ht.reserve(domain->m + 1);
216 
217  const typename FieldType::value_type Zt = domain->compute_vanishing_polynomial(t);
218 
219  const std::vector<typename FieldType::value_type> u =
220  domain->evaluate_all_lagrange_polynomials(t);
224  std::size_t extra_var_offset = cs.num_variables() + 1;
225  for (std::size_t i = 0; i < cs.num_constraints(); ++i) {
226  for (std::size_t j = 0; j < cs.constraints[i].a.terms.size(); ++j) {
227  At[cs.constraints[i].a.terms[j].index] +=
228  u[2 * i] * cs.constraints[i].a.terms[j].coeff;
229  At[cs.constraints[i].a.terms[j].index] +=
230  u[2 * i + 1] * cs.constraints[i].a.terms[j].coeff;
231  }
232 
233  for (std::size_t j = 0; j < cs.constraints[i].b.terms.size(); ++j) {
234  At[cs.constraints[i].b.terms[j].index] +=
235  u[2 * i] * cs.constraints[i].b.terms[j].coeff;
236  At[cs.constraints[i].b.terms[j].index] -=
237  u[2 * i + 1] * cs.constraints[i].b.terms[j].coeff;
238  }
239 
240  for (std::size_t j = 0; j < cs.constraints[i].c.terms.size(); ++j) {
241  Ct[cs.constraints[i].c.terms[j].index] +=
242  times_four(u[2 * i] * cs.constraints[i].c.terms[j].coeff);
243  }
244 
245  Ct[extra_var_offset + i] += u[2 * i];
246  Ct[extra_var_offset + i] += u[2 * i + 1];
247  }
248 
249  std::size_t extra_constr_offset = 2 * cs.num_constraints();
250  std::size_t extra_var_offset2 = cs.num_variables() + cs.num_constraints();
251 
252  At[0] += u[extra_constr_offset];
253  Ct[0] += u[extra_constr_offset];
254 
255  for (std::size_t i = 1; i <= cs.num_inputs(); ++i) {
256  At[i] += u[extra_constr_offset + 2 * i - 1];
257  At[0] += u[extra_constr_offset + 2 * i - 1];
258  Ct[i] += times_four(u[extra_constr_offset + 2 * i - 1]);
259  Ct[extra_var_offset2 + i] += u[extra_constr_offset + 2 * i - 1];
260 
261  At[i] += u[extra_constr_offset + 2 * i];
262  At[0] -= u[extra_constr_offset + 2 * i];
263  Ct[extra_var_offset2 + i] += u[extra_constr_offset + 2 * i];
264  }
265 
266  typename FieldType::value_type ti = FieldType::value_type::one();
267  for (std::size_t i = 0; i < domain->m + 1; ++i) {
268  Ht.emplace_back(ti);
269  ti *= t;
270  }
271 
273  sap_num_variables,
274  domain->m,
275  cs.num_inputs(),
276  t,
277  std::move(At),
278  std::move(Ct),
279  std::move(Ht),
280  Zt);
281  }
282 
314  const r1cs_primary_input<FieldType> &primary_input,
315  const r1cs_auxiliary_input<FieldType> &auxiliary_input,
316  const typename FieldType::value_type &d1,
317  const typename FieldType::value_type &d2) {
318  /* sanity check */
319  assert(cs.is_satisfied(primary_input, auxiliary_input));
320 
321  const std::shared_ptr<math::evaluation_domain<FieldType>> domain = get_domain(cs);
322 
323  std::size_t sap_num_variables = cs.num_variables() + cs.num_constraints() + cs.num_inputs();
324 
325  r1cs_variable_assignment<FieldType> full_variable_assignment = primary_input;
326  full_variable_assignment.insert(
327  full_variable_assignment.end(), auxiliary_input.begin(), auxiliary_input.end());
337  for (std::size_t i = 0; i < cs.num_constraints(); ++i) {
343  typename FieldType::value_type extra_var =
344  cs.constraints[i].a.evaluate(full_variable_assignment) -
345  cs.constraints[i].b.evaluate(full_variable_assignment);
346  extra_var = extra_var * extra_var;
347  full_variable_assignment.push_back(extra_var);
348  }
349  for (std::size_t i = 1; i <= cs.num_inputs(); ++i) {
355  typename FieldType::value_type extra_var =
356  full_variable_assignment[i - 1] - FieldType::value_type::one();
357  extra_var = extra_var * extra_var;
358  full_variable_assignment.push_back(extra_var);
359  }
360 
361  std::vector<typename FieldType::value_type> aA(domain->m, FieldType::value_type::zero());
362 
363  /* account for all constraints, as in instance_map */
364  for (std::size_t i = 0; i < cs.num_constraints(); ++i) {
365  aA[2 * i] += cs.constraints[i].a.evaluate(full_variable_assignment);
366  aA[2 * i] += cs.constraints[i].b.evaluate(full_variable_assignment);
367 
368  aA[2 * i + 1] += cs.constraints[i].a.evaluate(full_variable_assignment);
369  aA[2 * i + 1] -= cs.constraints[i].b.evaluate(full_variable_assignment);
370  }
371 
372  std::size_t extra_constr_offset = 2 * cs.num_constraints();
373 
374  aA[extra_constr_offset] += FieldType::value_type::one();
375 
376  for (std::size_t i = 1; i <= cs.num_inputs(); ++i) {
377  aA[extra_constr_offset + 2 * i - 1] += full_variable_assignment[i - 1];
378  aA[extra_constr_offset + 2 * i - 1] += FieldType::value_type::one();
379 
380  aA[extra_constr_offset + 2 * i] += full_variable_assignment[i - 1];
381  aA[extra_constr_offset + 2 * i] -= FieldType::value_type::one();
382  }
383 
384  domain->inverse_fft(aA);
385 
386  std::vector<typename FieldType::value_type> coefficients_for_H(
387  domain->m + 1, FieldType::value_type::zero());
388 #ifdef MULTICORE
389 #pragma omp parallel for
390 #endif
391  /* add coefficients of the polynomial (2*d1*A - d2) + d1*d1*Z */
392  for (std::size_t i = 0; i < domain->m; ++i) {
393  coefficients_for_H[i] = (d1 * aA[i]) + (d1 * aA[i]);
394  }
395  coefficients_for_H[0] -= d2;
396  domain->add_poly_z(d1 * d1, coefficients_for_H);
397 
399  typename FieldType::value_type(
401  domain->fft(aA);
402 
403  std::vector<typename FieldType::value_type> &H_tmp =
404  aA; // can overwrite aA because it is not used later
405 #ifdef MULTICORE
406 #pragma omp parallel for
407 #endif
408  for (std::size_t i = 0; i < domain->m; ++i) {
409  H_tmp[i] = aA[i] * aA[i];
410  }
411 
412  std::vector<typename FieldType::value_type> aC(domain->m, FieldType::value_type::zero());
413  /* again, accounting for all constraints */
414  std::size_t extra_var_offset = cs.num_variables() + 1;
415  for (std::size_t i = 0; i < cs.num_constraints(); ++i) {
416  aC[2 * i] += times_four(cs.constraints[i].c.evaluate(full_variable_assignment));
417 
418  aC[2 * i] += full_variable_assignment[extra_var_offset + i - 1];
419  aC[2 * i + 1] += full_variable_assignment[extra_var_offset + i - 1];
420  }
421 
422  std::size_t extra_var_offset2 = cs.num_variables() + cs.num_constraints();
423  aC[extra_constr_offset] += FieldType::value_type::one();
424 
425  for (std::size_t i = 1; i <= cs.num_inputs(); ++i) {
426  aC[extra_constr_offset + 2 * i - 1] += times_four(full_variable_assignment[i - 1]);
427 
428  aC[extra_constr_offset + 2 * i - 1] +=
429  full_variable_assignment[extra_var_offset2 + i - 1];
430  aC[extra_constr_offset + 2 * i] += full_variable_assignment[extra_var_offset2 + i - 1];
431  }
432 
433  domain->inverse_fft(aC);
434 
436  typename FieldType::value_type(
438  domain->fft(aC);
439 
440 #ifdef MULTICORE
441 #pragma omp parallel for
442 #endif
443  for (std::size_t i = 0; i < domain->m; ++i) {
444  H_tmp[i] = (H_tmp[i] - aC[i]);
445  }
446 
447  domain->divide_by_z_on_coset(H_tmp);
448 
449  domain->inverse_fft(H_tmp);
450  multiply_by_coset(H_tmp,
451  typename FieldType::value_type(
453  .inversed());
454 
455 #ifdef MULTICORE
456 #pragma omp parallel for
457 #endif
458  for (std::size_t i = 0; i < domain->m; ++i) {
459  coefficients_for_H[i] += H_tmp[i];
460  }
461 
462  return sap_witness<FieldType>(sap_num_variables,
463  domain->m,
464  cs.num_inputs(),
465  d1,
466  d2,
467  full_variable_assignment,
468  std::move(coefficients_for_H));
469  }
470  };
471  } // namespace reductions
472  } // namespace snark
473  } // namespace zk
474  } // namespace crypto3
475 } // namespace nil
476 
477 #endif // CRYPTO3_ZK_R1CS_TO_SAP_BASIC_POLICY_HPP
static sap_instance< FieldType > instance_map(const r1cs_constraint_system< FieldType > &cs)
Definition: r1cs_to_sap.hpp:97
static sap_witness< FieldType > witness_map(const r1cs_constraint_system< FieldType > &cs, const r1cs_primary_input< FieldType > &primary_input, const r1cs_auxiliary_input< FieldType > &auxiliary_input, const typename FieldType::value_type &d1, const typename FieldType::value_type &d2)
Definition: r1cs_to_sap.hpp:313
static sap_instance_evaluation< FieldType > instance_map_with_evaluation(const r1cs_constraint_system< FieldType > &cs, const typename FieldType::value_type &t)
Definition: r1cs_to_sap.hpp:204
static std::shared_ptr< math::evaluation_domain< FieldType > > get_domain(const r1cs_constraint_system< FieldType > &cs)
Definition: r1cs_to_sap.hpp:81
OutputIterator move(const SinglePassRange &rng, OutputIterator result)
Definition: move.hpp:45
void multiply_by_coset(Range &a, const FieldValueType &g)
Definition: coset.hpp:37
std::vector< typename FieldType::value_type > r1cs_auxiliary_input
Definition: r1cs.hpp:104
std::vector< typename FieldType::value_type > r1cs_primary_input
Definition: r1cs.hpp:101
std::vector< typename FieldType::value_type > r1cs_variable_assignment
Definition: r1cs.hpp:107
Definition: pair.hpp:31
Definition: fields/params.hpp:58
bool is_satisfied(const r1cs_primary_input< FieldType > &primary_input, const r1cs_auxiliary_input< FieldType > &auxiliary_input) const
Definition: r1cs.hpp:162
std::vector< r1cs_constraint< FieldType > > constraints
Definition: r1cs.hpp:130
std::size_t num_constraints() const
Definition: r1cs.hpp:143
std::size_t num_inputs() const
Definition: r1cs.hpp:135
std::size_t num_variables() const
Definition: r1cs.hpp:139