Clearmatics Libsnark  0.1
C++ library for zkSNARK proofs
r1cs.tcc
Go to the documentation of this file.
1 /** @file
2  *****************************************************************************
3 
4  Declaration of interfaces for:
5  - a R1CS constraint,
6  - a R1CS variable assignment, and
7  - a R1CS constraint system.
8 
9  See r1cs.hpp .
10 
11  *****************************************************************************
12  * @author This file is part of libsnark, developed by SCIPR Lab
13  * and contributors (see AUTHORS).
14  * @copyright MIT license (see LICENSE file)
15  *****************************************************************************/
16 
17 #ifndef R1CS_TCC_
18 #define R1CS_TCC_
19 
20 #include <algorithm>
21 #include <cassert>
22 #include <libff/algebra/fields/bigint.hpp>
23 #include <libff/common/profiling.hpp>
24 #include <libff/common/utils.hpp>
25 #include <set>
26 
27 namespace libsnark
28 {
29 
30 template<typename FieldT>
31 r1cs_constraint<FieldT>::r1cs_constraint(
32  const linear_combination<FieldT> &a,
33  const linear_combination<FieldT> &b,
34  const linear_combination<FieldT> &c)
35  : a(a), b(b), c(c)
36 {
37 }
38 
39 template<typename FieldT>
40 r1cs_constraint<FieldT>::r1cs_constraint(
41  const std::initializer_list<linear_combination<FieldT>> &A,
42  const std::initializer_list<linear_combination<FieldT>> &B,
43  const std::initializer_list<linear_combination<FieldT>> &C)
44 {
45  for (auto lc_A : A) {
46  a.terms.insert(a.terms.end(), lc_A.terms.begin(), lc_A.terms.end());
47  }
48  for (auto lc_B : B) {
49  b.terms.insert(b.terms.end(), lc_B.terms.begin(), lc_B.terms.end());
50  }
51  for (auto lc_C : C) {
52  c.terms.insert(c.terms.end(), lc_C.terms.begin(), lc_C.terms.end());
53  }
54 }
55 
56 template<typename FieldT>
57 bool r1cs_constraint<FieldT>::operator==(
58  const r1cs_constraint<FieldT> &other) const
59 {
60  return (this->a == other.a && this->b == other.b && this->c == other.c);
61 }
62 
63 template<typename FieldT>
64 std::ostream &operator<<(std::ostream &out, const r1cs_constraint<FieldT> &c)
65 {
66  out << c.a;
67  out << c.b;
68  out << c.c;
69 
70  return out;
71 }
72 
73 template<typename FieldT>
74 std::istream &operator>>(std::istream &in, r1cs_constraint<FieldT> &c)
75 {
76  in >> c.a;
77  in >> c.b;
78  in >> c.c;
79 
80  return in;
81 }
82 
83 template<typename FieldT>
84 size_t r1cs_constraint_system<FieldT>::num_inputs() const
85 {
86  return primary_input_size;
87 }
88 
89 template<typename FieldT>
90 size_t r1cs_constraint_system<FieldT>::num_variables() const
91 {
92  return primary_input_size + auxiliary_input_size;
93 }
94 
95 template<typename FieldT>
96 size_t r1cs_constraint_system<FieldT>::num_constraints() const
97 {
98  return constraints.size();
99 }
100 
101 template<typename FieldT> bool r1cs_constraint_system<FieldT>::is_valid() const
102 {
103  if (this->num_inputs() > this->num_variables())
104  return false;
105 
106  for (size_t c = 0; c < constraints.size(); ++c) {
107  if (!(constraints[c].a.is_valid(this->num_variables()) &&
108  constraints[c].b.is_valid(this->num_variables()) &&
109  constraints[c].c.is_valid(this->num_variables()))) {
110  return false;
111  }
112  }
113 
114  return true;
115 }
116 
117 template<typename FieldT>
118 void dump_r1cs_constraint(
119  const r1cs_constraint<FieldT> &constraint,
120  const r1cs_variable_assignment<FieldT> &full_variable_assignment,
121  const std::map<size_t, std::string> &variable_annotations)
122 {
123  printf("terms for a:\n");
124  constraint.a.print_with_assignment(
125  full_variable_assignment, variable_annotations);
126  printf("terms for b:\n");
127  constraint.b.print_with_assignment(
128  full_variable_assignment, variable_annotations);
129  printf("terms for c:\n");
130  constraint.c.print_with_assignment(
131  full_variable_assignment, variable_annotations);
132 }
133 
134 template<typename FieldT>
135 bool r1cs_constraint_system<FieldT>::is_satisfied(
136  const r1cs_primary_input<FieldT> &primary_input,
137  const r1cs_auxiliary_input<FieldT> &auxiliary_input) const
138 {
139  assert(primary_input.size() == num_inputs());
140  assert(primary_input.size() + auxiliary_input.size() == num_variables());
141 
142  r1cs_variable_assignment<FieldT> full_variable_assignment = primary_input;
143  full_variable_assignment.insert(
144  full_variable_assignment.end(),
145  auxiliary_input.begin(),
146  auxiliary_input.end());
147 
148  for (size_t c = 0; c < constraints.size(); ++c) {
149  const FieldT ares = constraints[c].a.evaluate(full_variable_assignment);
150  const FieldT bres = constraints[c].b.evaluate(full_variable_assignment);
151  const FieldT cres = constraints[c].c.evaluate(full_variable_assignment);
152 
153  if (!(ares * bres == cres)) {
154 #ifdef DEBUG
155  auto it = constraint_annotations.find(c);
156  printf(
157  "constraint %zu (%s) unsatisfied\n",
158  c,
159  (it == constraint_annotations.end() ? "no annotation"
160  : it->second.c_str()));
161  printf("<a,(1,x)> = ");
162  ares.print();
163  printf("<b,(1,x)> = ");
164  bres.print();
165  printf("<c,(1,x)> = ");
166  cres.print();
167  printf("constraint was:\n");
168  dump_r1cs_constraint(
169  constraints[c], full_variable_assignment, variable_annotations);
170 #endif // DEBUG
171  return false;
172  }
173  }
174 
175  return true;
176 }
177 
178 template<typename FieldT>
179 void r1cs_constraint_system<FieldT>::add_constraint(
180  const r1cs_constraint<FieldT> &c)
181 {
182  constraints.emplace_back(c);
183 }
184 
185 template<typename FieldT>
186 void r1cs_constraint_system<FieldT>::add_constraint(
187  const r1cs_constraint<FieldT> &c, const std::string &annotation)
188 {
189 #ifdef DEBUG
190  constraint_annotations[constraints.size()] = annotation;
191 #else
192  libff::UNUSED(annotation);
193 #endif
194  constraints.emplace_back(c);
195 }
196 
197 template<typename FieldT>
198 void r1cs_constraint_system<FieldT>::swap_AB_if_beneficial()
199 {
200  libff::enter_block("Call to r1cs_constraint_system::swap_AB_if_beneficial");
201 
202  libff::enter_block("Estimate densities");
203  libff::bit_vector touched_by_A(this->num_variables() + 1, false),
204  touched_by_B(this->num_variables() + 1, false);
205 
206  for (size_t i = 0; i < this->constraints.size(); ++i) {
207  for (size_t j = 0; j < this->constraints[i].a.terms.size(); ++j) {
208  touched_by_A[this->constraints[i].a.terms[j].index] = true;
209  }
210 
211  for (size_t j = 0; j < this->constraints[i].b.terms.size(); ++j) {
212  touched_by_B[this->constraints[i].b.terms[j].index] = true;
213  }
214  }
215 
216  size_t non_zero_A_count = 0, non_zero_B_count = 0;
217  for (size_t i = 0; i < this->num_variables() + 1; ++i) {
218  non_zero_A_count += touched_by_A[i] ? 1 : 0;
219  non_zero_B_count += touched_by_B[i] ? 1 : 0;
220  }
221 
222  if (!libff::inhibit_profiling_info) {
223  libff::print_indent();
224  printf("* Non-zero A-count (estimate): %zu\n", non_zero_A_count);
225  libff::print_indent();
226  printf("* Non-zero B-count (estimate): %zu\n", non_zero_B_count);
227  }
228  libff::leave_block("Estimate densities");
229 
230  if (non_zero_B_count > non_zero_A_count) {
231  libff::enter_block("Perform the swap");
232  for (size_t i = 0; i < this->constraints.size(); ++i) {
233  std::swap(this->constraints[i].a, this->constraints[i].b);
234  }
235  libff::leave_block("Perform the swap");
236  } else {
237  libff::print_indent();
238  printf("Swap is not beneficial, not performing\n");
239  }
240 
241  libff::leave_block("Call to r1cs_constraint_system::swap_AB_if_beneficial");
242 }
243 
244 template<typename FieldT>
245 bool r1cs_constraint_system<FieldT>::operator==(
246  const r1cs_constraint_system<FieldT> &other) const
247 {
248  return (
249  this->constraints == other.constraints &&
250  this->primary_input_size == other.primary_input_size &&
251  this->auxiliary_input_size == other.auxiliary_input_size);
252 }
253 
254 template<typename FieldT>
255 std::ostream &operator<<(
256  std::ostream &out, const r1cs_constraint_system<FieldT> &cs)
257 {
258  out << cs.primary_input_size << "\n";
259  out << cs.auxiliary_input_size << "\n";
260 
261  out << cs.num_constraints() << "\n";
262  for (const r1cs_constraint<FieldT> &c : cs.constraints) {
263  out << c;
264  }
265 
266  return out;
267 }
268 
269 template<typename FieldT>
270 std::istream &operator>>(std::istream &in, r1cs_constraint_system<FieldT> &cs)
271 {
272  in >> cs.primary_input_size;
273  in >> cs.auxiliary_input_size;
274 
275  cs.constraints.clear();
276 
277  size_t s;
278  in >> s;
279 
280  char b;
281  in.read(&b, 1);
282 
283  cs.constraints.reserve(s);
284 
285  for (size_t i = 0; i < s; ++i) {
286  r1cs_constraint<FieldT> c;
287  in >> c;
288  cs.constraints.emplace_back(c);
289  }
290 
291  return in;
292 }
293 
294 template<typename FieldT>
295 void r1cs_constraint_system<FieldT>::report_linear_constraint_statistics() const
296 {
297 #ifdef DEBUG
298  for (size_t i = 0; i < constraints.size(); ++i) {
299  auto &constr = constraints[i];
300  bool a_is_const = true;
301  for (auto &t : constr.a.terms) {
302  a_is_const = a_is_const && (t.index == 0);
303  }
304 
305  bool b_is_const = true;
306  for (auto &t : constr.b.terms) {
307  b_is_const = b_is_const && (t.index == 0);
308  }
309 
310  if (a_is_const || b_is_const) {
311  auto it = constraint_annotations.find(i);
312  printf(
313  "%s\n",
314  (it == constraint_annotations.end()
315  ? FMT("", "constraint_%zu", i)
316  : it->second)
317  .c_str());
318  }
319  }
320 #endif
321 }
322 
323 } // namespace libsnark
324 #endif // R1CS_TCC_