2 *****************************************************************************
3 * @author This file is part of libsnark, developed by SCIPR Lab
4 * and contributors (see AUTHORS).
5 * @copyright MIT license (see LICENSE file)
6 *****************************************************************************/
8 #ifndef BASIC_GADGETS_TCC_
9 #define BASIC_GADGETS_TCC_
11 #include <libff/common/profiling.hpp>
12 #include <libff/common/utils.hpp>
17 template<typename FieldT>
18 void generate_boolean_r1cs_constraint(
19 protoboard<FieldT> &pb,
20 const pb_linear_combination<FieldT> &lc,
21 const std::string &annotation_prefix)
22 /* forces lc to take value 0 or 1 by adding constraint lc * (1-lc) = 0 */
24 pb.add_r1cs_constraint(
25 r1cs_constraint<FieldT>(lc, 1 - lc, 0),
26 FMT(annotation_prefix, " boolean_r1cs_constraint"));
29 template<typename FieldT>
30 void generate_r1cs_equals_const_constraint(
31 protoboard<FieldT> &pb,
32 const pb_linear_combination<FieldT> &lc,
34 const std::string &annotation_prefix)
36 pb.add_r1cs_constraint(
37 r1cs_constraint<FieldT>(1, lc, c),
38 FMT(annotation_prefix, " constness_constraint"));
41 template<typename FieldT>
42 void packing_gadget<FieldT>::generate_r1cs_constraints(
43 const bool enforce_bitness)
44 /* adds constraint result = \sum bits[i] * 2^i */
46 this->pb.add_r1cs_constraint(
47 r1cs_constraint<FieldT>(1, pb_packing_sum<FieldT>(bits), packed),
48 FMT(this->annotation_prefix, " packing_constraint"));
50 if (enforce_bitness) {
51 for (size_t i = 0; i < bits.size(); ++i) {
52 generate_boolean_r1cs_constraint<FieldT>(
55 FMT(this->annotation_prefix, " bitness_%zu", i));
60 template<typename FieldT>
61 void packing_gadget<FieldT>::generate_r1cs_witness_from_packed()
63 packed.evaluate(this->pb);
65 this->pb.lc_val(packed).as_bigint().num_bits() <=
66 bits.size()); // `bits` is large enough to represent this packed value
67 bits.fill_with_bits_of_field_element(this->pb, this->pb.lc_val(packed));
70 template<typename FieldT>
71 void packing_gadget<FieldT>::generate_r1cs_witness_from_bits()
73 bits.evaluate(this->pb);
74 this->pb.lc_val(packed) = bits.get_field_element_from_bits(this->pb);
77 template<typename FieldT>
78 multipacking_gadget<FieldT>::multipacking_gadget(
79 protoboard<FieldT> &pb,
80 const pb_linear_combination_array<FieldT> &bits,
81 const pb_linear_combination_array<FieldT> &packed_vars,
82 const size_t chunk_size,
83 const std::string &annotation_prefix)
84 : gadget<FieldT>(pb, annotation_prefix)
86 , packed_vars(packed_vars)
87 , chunk_size(chunk_size)
88 , num_chunks(libff::div_ceil(bits.size(), chunk_size))
89 // last_chunk_size(bits.size() - (num_chunks-1) * chunk_size)
91 assert(packed_vars.size() == num_chunks);
92 for (size_t i = 0; i < num_chunks; ++i) {
93 packers.emplace_back(packing_gadget<FieldT>(
95 pb_linear_combination_array<FieldT>(
96 bits.begin() + i * chunk_size,
97 bits.begin() + std::min((i + 1) * chunk_size, bits.size())),
99 FMT(this->annotation_prefix, " packers_%zu", i)));
103 template<typename FieldT>
104 void multipacking_gadget<FieldT>::generate_r1cs_constraints(
105 const bool enforce_bitness)
107 for (size_t i = 0; i < num_chunks; ++i) {
108 packers[i].generate_r1cs_constraints(enforce_bitness);
112 template<typename FieldT>
113 void multipacking_gadget<FieldT>::generate_r1cs_witness_from_packed()
115 for (size_t i = 0; i < num_chunks; ++i) {
116 packers[i].generate_r1cs_witness_from_packed();
120 template<typename FieldT>
121 void multipacking_gadget<FieldT>::generate_r1cs_witness_from_bits()
123 for (size_t i = 0; i < num_chunks; ++i) {
124 packers[i].generate_r1cs_witness_from_bits();
128 template<typename FieldT> size_t multipacking_num_chunks(const size_t num_bits)
130 return libff::div_ceil(num_bits, FieldT::capacity());
133 template<typename FieldT>
134 field_vector_copy_gadget<FieldT>::field_vector_copy_gadget(
135 protoboard<FieldT> &pb,
136 const pb_variable_array<FieldT> &source,
137 const pb_variable_array<FieldT> &target,
138 const pb_linear_combination<FieldT> &do_copy,
139 const std::string &annotation_prefix)
140 : gadget<FieldT>(pb, annotation_prefix)
145 assert(source.size() == target.size());
148 template<typename FieldT>
149 void field_vector_copy_gadget<FieldT>::generate_r1cs_constraints()
151 for (size_t i = 0; i < source.size(); ++i) {
152 this->pb.add_r1cs_constraint(
153 r1cs_constraint<FieldT>(do_copy, source[i] - target[i], 0),
154 FMT(this->annotation_prefix, " copying_check_%zu", i));
158 template<typename FieldT>
159 void field_vector_copy_gadget<FieldT>::generate_r1cs_witness()
161 do_copy.evaluate(this->pb);
163 this->pb.lc_val(do_copy) == FieldT::one() ||
164 this->pb.lc_val(do_copy) == FieldT::zero());
165 if (this->pb.lc_val(do_copy) != FieldT::zero()) {
166 for (size_t i = 0; i < source.size(); ++i) {
167 this->pb.val(target[i]) = this->pb.val(source[i]);
172 template<typename FieldT>
173 bit_vector_copy_gadget<FieldT>::bit_vector_copy_gadget(
174 protoboard<FieldT> &pb,
175 const pb_variable_array<FieldT> &source_bits,
176 const pb_variable_array<FieldT> &target_bits,
177 const pb_linear_combination<FieldT> &do_copy,
178 const size_t chunk_size,
179 const std::string &annotation_prefix)
180 : gadget<FieldT>(pb, annotation_prefix)
181 , source_bits(source_bits)
182 , target_bits(target_bits)
184 , chunk_size(chunk_size)
185 , num_chunks(libff::div_ceil(source_bits.size(), chunk_size))
187 assert(source_bits.size() == target_bits.size());
189 packed_source.allocate(
190 pb, num_chunks, FMT(annotation_prefix, " packed_source"));
191 pack_source.reset(new multipacking_gadget<FieldT>(
196 FMT(annotation_prefix, " pack_source")));
198 packed_target.allocate(
199 pb, num_chunks, FMT(annotation_prefix, " packed_target"));
200 pack_target.reset(new multipacking_gadget<FieldT>(
205 FMT(annotation_prefix, " pack_target")));
207 copier.reset(new field_vector_copy_gadget<FieldT>(
212 FMT(annotation_prefix, " copier")));
215 template<typename FieldT>
216 void bit_vector_copy_gadget<FieldT>::generate_r1cs_constraints(
217 const bool enforce_source_bitness, const bool enforce_target_bitness)
219 pack_source->generate_r1cs_constraints(enforce_source_bitness);
220 pack_target->generate_r1cs_constraints(enforce_target_bitness);
222 copier->generate_r1cs_constraints();
225 template<typename FieldT>
226 void bit_vector_copy_gadget<FieldT>::generate_r1cs_witness()
228 do_copy.evaluate(this->pb);
230 this->pb.lc_val(do_copy) == FieldT::zero() ||
231 this->pb.lc_val(do_copy) == FieldT::one());
232 if (this->pb.lc_val(do_copy) == FieldT::one()) {
233 for (size_t i = 0; i < source_bits.size(); ++i) {
234 this->pb.val(target_bits[i]) = this->pb.val(source_bits[i]);
238 pack_source->generate_r1cs_witness_from_bits();
239 pack_target->generate_r1cs_witness_from_bits();
242 template<typename FieldT>
243 void dual_variable_gadget<FieldT>::generate_r1cs_constraints(
244 const bool enforce_bitness)
246 consistency_check->generate_r1cs_constraints(enforce_bitness);
249 template<typename FieldT>
250 void dual_variable_gadget<FieldT>::generate_r1cs_witness_from_packed()
252 consistency_check->generate_r1cs_witness_from_packed();
255 template<typename FieldT>
256 void dual_variable_gadget<FieldT>::generate_r1cs_witness_from_bits()
258 consistency_check->generate_r1cs_witness_from_bits();
261 template<typename FieldT>
262 void disjunction_gadget<FieldT>::generate_r1cs_constraints()
264 linear_combination<FieldT> sum = pb_sum(inputs);
266 // inv * sum = output
267 this->pb.add_r1cs_constraint(
268 r1cs_constraint<FieldT>(inv, sum, output),
269 FMT(this->annotation_prefix, " inv*sum=output"));
271 // (1-output) * sum = 0
272 this->pb.add_r1cs_constraint(
273 r1cs_constraint<FieldT>(FieldT::one() - output, sum, FieldT::zero()),
274 FMT(this->annotation_prefix, " (1-output)*sum=0"));
277 template<typename FieldT>
278 void disjunction_gadget<FieldT>::generate_r1cs_witness()
280 FieldT sum = FieldT::zero();
282 for (size_t i = 0; i < inputs.size(); ++i) {
283 sum += this->pb.lc_val(inputs[i]);
287 this->pb.val(inv) = FieldT::zero();
288 this->pb.val(output) = FieldT::zero();
290 this->pb.val(inv) = sum.inverse();
291 this->pb.val(output) = FieldT::one();
295 template<typename FieldT>
296 void conjunction_gadget<FieldT>::generate_r1cs_constraints()
298 /* inv * (n-sum) = 1-output */
299 linear_combination<FieldT> a1, b1, c1;
301 b1.add_term(ONE, inputs.size());
302 for (size_t i = 0; i < inputs.size(); ++i) {
303 b1.add_term(inputs[i], -1);
306 c1.add_term(output, -1);
308 this->pb.add_r1cs_constraint(
309 r1cs_constraint<FieldT>(a1, b1, c1),
310 FMT(this->annotation_prefix, " inv*(n-sum)=(1-output)"));
312 /* output * (n-sum) = 0 */
313 linear_combination<FieldT> a2, b2, c2;
315 b2.add_term(ONE, inputs.size());
316 for (size_t i = 0; i < inputs.size(); ++i) {
317 b2.add_term(inputs[i], -1);
321 this->pb.add_r1cs_constraint(
322 r1cs_constraint<FieldT>(a2, b2, c2),
323 FMT(this->annotation_prefix, " output*(n-sum)=0"));
326 template<typename FieldT>
327 void conjunction_gadget<FieldT>::generate_r1cs_witness()
329 FieldT sum = FieldT(inputs.size());
331 for (size_t i = 0; i < inputs.size(); ++i) {
332 sum -= this->pb.val(inputs[i]);
336 this->pb.val(inv) = FieldT::zero();
337 this->pb.val(output) = FieldT::one();
339 this->pb.val(inv) = sum.inverse();
340 this->pb.val(output) = FieldT::zero();
344 template<typename FieldT>
345 void comparison_gadget<FieldT>::generate_r1cs_constraints()
348 packed(alpha) = 2^n + B - A
350 not_all_zeros = \bigvee_{i=0}^{n-1} alpha_i
352 if B - A > 0, then 2^n + B - A > 2^n,
353 so alpha_n = 1 and not_all_zeros = 1
354 if B - A = 0, then 2^n + B - A = 2^n,
355 so alpha_n = 1 and not_all_zeros = 0
356 if B - A < 0, then 2^n + B - A \in {0, 1, \ldots, 2^n-1},
359 therefore alpha_n = less_or_eq and alpha_n * not_all_zeros = less
362 /* not_all_zeros to be Boolean, alpha_i are Boolean by packing gadget */
363 generate_boolean_r1cs_constraint<FieldT>(
366 FMT(this->annotation_prefix, " not_all_zeros"));
368 /* constraints for packed(alpha) = 2^n + B - A */
369 pack_alpha->generate_r1cs_constraints(true);
370 this->pb.add_r1cs_constraint(
371 r1cs_constraint<FieldT>(1, (FieldT(2) ^ n) + B - A, alpha_packed),
372 FMT(this->annotation_prefix, " main_constraint"));
375 all_zeros_test->generate_r1cs_constraints();
376 this->pb.add_r1cs_constraint(
377 r1cs_constraint<FieldT>(less_or_eq, not_all_zeros, less),
378 FMT(this->annotation_prefix, " less"));
381 template<typename FieldT>
382 void comparison_gadget<FieldT>::generate_r1cs_witness()
384 A.evaluate(this->pb);
385 B.evaluate(this->pb);
387 /* unpack 2^n + B - A into alpha_packed */
388 this->pb.val(alpha_packed) =
389 (FieldT(2) ^ n) + this->pb.lc_val(B) - this->pb.lc_val(A);
390 pack_alpha->generate_r1cs_witness_from_packed();
393 all_zeros_test->generate_r1cs_witness();
394 this->pb.val(less) = this->pb.val(less_or_eq) * this->pb.val(not_all_zeros);
397 template<typename FieldT>
398 void inner_product_gadget<FieldT>::generate_r1cs_constraints()
401 S_i = \sum_{k=0}^{i+1} A[i] * B[i]
403 S[i+1] - S[i] = A[i] * B[i]
405 for (size_t i = 0; i < A.size(); ++i) {
406 this->pb.add_r1cs_constraint(
407 r1cs_constraint<FieldT>(
410 (i == A.size() - 1 ? result : S[i]) +
411 (i == 0 ? 0 * ONE : -S[i - 1])),
412 FMT(this->annotation_prefix, " S_%zu", i));
416 template<typename FieldT>
417 void inner_product_gadget<FieldT>::generate_r1cs_witness()
419 FieldT total = FieldT::zero();
420 for (size_t i = 0; i < A.size(); ++i) {
421 A[i].evaluate(this->pb);
422 B[i].evaluate(this->pb);
424 total += this->pb.lc_val(A[i]) * this->pb.lc_val(B[i]);
425 this->pb.val(i == A.size() - 1 ? result : S[i]) = total;
429 template<typename FieldT>
430 void loose_multiplexing_gadget<FieldT>::generate_r1cs_constraints()
432 /* \alpha_i (index - i) = 0 */
433 for (size_t i = 0; i < arr.size(); ++i) {
434 this->pb.add_r1cs_constraint(
435 r1cs_constraint<FieldT>(alpha[i], index - i, 0),
436 FMT(this->annotation_prefix, " alpha_%zu", i));
439 /* 1 * (\sum \alpha_i) = success_flag */
440 linear_combination<FieldT> a, b, c;
442 for (size_t i = 0; i < arr.size(); ++i) {
443 b.add_term(alpha[i]);
445 c.add_term(success_flag);
446 this->pb.add_r1cs_constraint(
447 r1cs_constraint<FieldT>(a, b, c),
448 FMT(this->annotation_prefix, " main_constraint"));
450 /* now success_flag is constrained to either 0 (if index is out of
451 range) or \alpha_i. constrain it and \alpha_i to zero */
452 generate_boolean_r1cs_constraint<FieldT>(
453 this->pb, success_flag, FMT(this->annotation_prefix, " success_flag"));
456 compute_result->generate_r1cs_constraints();
459 template<typename FieldT>
460 void loose_multiplexing_gadget<FieldT>::generate_r1cs_witness()
462 /* assumes that idx can be fit in ulong; true for our purposes for now */
463 const libff::bigint<FieldT::num_limbs> valint =
464 this->pb.val(index).as_bigint();
465 unsigned long idx = valint.as_ulong();
466 const libff::bigint<FieldT::num_limbs> arrsize(arr.size());
468 if (idx >= arr.size() ||
469 mpn_cmp(valint.data, arrsize.data, FieldT::num_limbs) >= 0) {
470 for (size_t i = 0; i < arr.size(); ++i) {
471 this->pb.val(alpha[i]) = FieldT::zero();
474 this->pb.val(success_flag) = FieldT::zero();
476 for (size_t i = 0; i < arr.size(); ++i) {
477 this->pb.val(alpha[i]) =
478 (i == idx ? FieldT::one() : FieldT::zero());
481 this->pb.val(success_flag) = FieldT::one();
484 compute_result->generate_r1cs_witness();
487 template<typename FieldT, typename VarT>
488 void create_linear_combination_constraints(
489 protoboard<FieldT> &pb,
490 const std::vector<FieldT> &base,
491 const std::vector<std::pair<VarT, FieldT>> &v,
493 const std::string &annotation_prefix)
495 for (size_t i = 0; i < base.size(); ++i) {
496 linear_combination<FieldT> a, b, c;
499 b.add_term(ONE, base[i]);
502 b.add_term(p.first.all_vars[i], p.second);
505 c.add_term(target.all_vars[i]);
507 pb.add_r1cs_constraint(
508 r1cs_constraint<FieldT>(a, b, c),
509 FMT(annotation_prefix, " linear_combination_%zu", i));
513 template<typename FieldT, typename VarT>
514 void create_linear_combination_witness(
515 protoboard<FieldT> &pb,
516 const std::vector<FieldT> &base,
517 const std::vector<std::pair<VarT, FieldT>> &v,
520 for (size_t i = 0; i < base.size(); ++i) {
521 pb.val(target.all_vars[i]) = base[i];
524 pb.val(target.all_vars[i]) +=
525 p.second * pb.val(p.first.all_vars[i]);
530 } // namespace libsnark
531 #endif // BASIC_GADGETS_TCC_