2 *****************************************************************************
3 * @author This file is part of libsnark, developed by Clearmatics Ltd
4 * (originally developed by SCIPR Lab) and contributors
6 * @copyright MIT license (see LICENSE file)
7 *****************************************************************************/
9 #ifndef LIBSNARK_GADGETLIB1_GADGETS_CURVE_SCALAR_MULTIPLICATION_TCC_
10 #define LIBSNARK_GADGETLIB1_GADGETS_CURVE_SCALAR_MULTIPLICATION_TCC_
15 template<typename ppT, typename groupT, typename groupVariableT>
16 variable_or_identity<ppT, groupT, groupVariableT>::variable_or_identity(
17 protoboard<FieldT> &pb, const std::string &annotation_prefix)
18 : gadget<FieldT>(pb, annotation_prefix), value(pb, annotation_prefix)
20 is_identity_var.allocate(pb, " is_identity");
21 is_identity = pb_linear_combination<FieldT>(is_identity_var);
22 generate_boolean_r1cs_constraint(
23 pb, is_identity, FMT(annotation_prefix, " is_identity_is_bool"));
26 template<typename ppT, typename groupT, typename groupVariableT>
27 variable_or_identity<ppT, groupT, groupVariableT>::variable_or_identity(
28 protoboard<FieldT> &pb,
30 const std::string &annotation_prefix)
31 : gadget<FieldT>(pb, annotation_prefix), value(pb, P, annotation_prefix)
33 is_identity.assign(pb, P.is_zero() ? FieldT::one() : FieldT::zero());
34 is_identity.evaluate(pb);
37 template<typename ppT, typename groupT, typename groupVariableT>
38 void variable_or_identity<ppT, groupT, groupVariableT>::generate_r1cs_witness(
41 const bool is_zero = elt.is_zero();
42 value.generate_r1cs_witness(is_zero ? groupT::one() : elt);
43 generate_r1cs_witness(is_zero);
46 template<typename ppT, typename groupT, typename groupVariableT>
47 void variable_or_identity<ppT, groupT, groupVariableT>::generate_r1cs_witness(
50 this->pb.val(is_identity_var) = is_zero ? FieldT::one() : FieldT::zero();
53 template<typename ppT, typename groupT, typename groupVariableT>
54 groupT variable_or_identity<ppT, groupT, groupVariableT>::get_element() const
56 if (this->pb.lc_val(is_identity) == FieldT::one()) {
57 return groupT::zero();
60 return value.get_element();
67 typename variableSelectorT>
68 variable_or_identity_selector<ppT, groupT, variableT, variableSelectorT>::
69 variable_or_identity_selector(
70 protoboard<FieldT> &pb,
71 const pb_linear_combination<FieldT> &selector,
72 const variableOrIdentity &zero_case,
73 const variableOrIdentity &one_case,
74 const variableOrIdentity &result,
75 const std::string &annotation_prefix)
76 : gadget<libff::Fr<ppT>>(pb, annotation_prefix)
83 FMT(annotation_prefix, " value_selector"))
84 , zero_case_is_identity(zero_case.is_identity)
85 , one_case_is_identity(one_case.is_identity)
86 , result_is_identity(result.is_identity)
94 typename variableSelectorT>
95 void variable_or_identity_selector<ppT, groupT, variableT, variableSelectorT>::
96 generate_r1cs_constraints()
98 value_selector.generate_r1cs_constraints();
99 // result.is_identity - zero_case = selector * (one_case - zero_case)
100 this->pb.add_r1cs_constraint(
101 r1cs_constraint<FieldT>(
102 value_selector.selector,
103 one_case_is_identity - zero_case_is_identity,
104 result_is_identity - zero_case_is_identity),
105 FMT(this->annotation_prefix, " result_is_identity_constraint"));
112 typename variableSelectorT>
113 void variable_or_identity_selector<ppT, groupT, variableT, variableSelectorT>::
114 generate_r1cs_witness()
116 value_selector.generate_r1cs_witness();
117 if (this->pb.lc_val(value_selector.selector) == FieldT::one()) {
118 this->pb.lc_val(result_is_identity) =
119 this->pb.lc_val(one_case_is_identity);
121 this->pb.lc_val(result_is_identity) =
122 this->pb.lc_val(zero_case_is_identity);
130 typename variableSelectorT>
131 variable_and_variable_or_identity_selector<
136 variable_and_variable_or_identity_selector(
137 protoboard<FieldT> &pb,
138 const pb_linear_combination<FieldT> &selector,
139 const variableOrIdentity &zero_case,
140 const variableT &one_case,
141 const variableOrIdentity &result,
142 const std::string &annotation_prefix)
143 : gadget<libff::Fr<ppT>>(pb, annotation_prefix)
150 FMT(annotation_prefix, " value_selector"))
151 , zero_case_is_identity(zero_case.is_identity)
160 typename variableSelectorT>
161 void variable_and_variable_or_identity_selector<
165 variableSelectorT>::generate_r1cs_constraints()
167 value_selector.generate_r1cs_constraints();
168 // result.is_identity = (1 - selector) * zero_case.is_identity
169 this->pb.add_r1cs_constraint(
170 r1cs_constraint<FieldT>(
171 FieldT::one() - value_selector.selector,
172 zero_case_is_identity,
174 FMT(this->annotation_prefix, " result_is_identity_constraint"));
181 typename variableSelectorT>
182 void variable_and_variable_or_identity_selector<
186 variableSelectorT>::generate_r1cs_witness()
188 value_selector.generate_r1cs_witness();
189 const bool selector_value =
190 this->pb.lc_val(value_selector.selector) == FieldT::one();
191 if (selector_value) {
192 result.generate_r1cs_witness(false);
194 const bool zero_case_is_identity_value =
195 this->pb.lc_val(zero_case_is_identity) == FieldT::one();
196 result.generate_r1cs_witness(zero_case_is_identity_value);
203 typename groupVariableT,
204 typename variableSelectorT,
206 add_variable_or_identity<
212 add_variable_or_identity(
213 protoboard<FieldT> &pb,
214 const variableOrIdentity &A,
215 const variableOrIdentity &B,
216 const variableOrIdentity &result,
217 const std::string &annotation_prefix)
218 : gadget<FieldT>(pb, annotation_prefix)
219 , add_result(pb, FMT(annotation_prefix, " add_result"))
220 , add(pb, A.value, B.value, add_result, FMT(annotation_prefix, " add"))
221 , A_not_identity_result(
222 pb, FMT(annotation_prefix, " A_not_identity_result"))
223 , selector_A_not_identity(
228 A_not_identity_result,
229 FMT(annotation_prefix, " selector_A_not_identity"))
233 A_not_identity_result,
236 FMT(annotation_prefix, " selector_A"))
244 typename groupVariableT,
245 typename variableSelectorT,
247 void add_variable_or_identity<
252 addGadgetT>::generate_r1cs_constraints()
254 add.generate_r1cs_constraints();
255 selector_A_not_identity.generate_r1cs_constraints();
256 selector_A.generate_r1cs_constraints();
258 // result.is_identity = A.is_identity * B.is_identity
259 // (no need to call generate_r1cs_constraints() on result itself)
260 this->pb.add_r1cs_constraint(
261 r1cs_constraint<libff::Fr<ppT>>(
262 selector_A.selector, // A.is_identity
263 selector_A_not_identity.selector, // B.is_identity
265 FMT(this->annotation_prefix, " result.is_identity"));
271 typename groupVariableT,
272 typename variableSelectorT,
274 void add_variable_or_identity<
279 addGadgetT>::generate_r1cs_witness()
281 add.generate_r1cs_witness();
282 selector_A_not_identity.generate_r1cs_witness();
284 // Generate result.value via the result of selector_A, and set
285 // result.is_identity manually via result.generate_r1cs_witness().
286 selector_A.generate_r1cs_witness();
288 const libff::Fr<ppT> result_is_identity =
289 this->pb.lc_val(selector_A.selector) *
290 this->pb.lc_val(selector_A_not_identity.selector);
291 result.generate_r1cs_witness(result_is_identity == libff::Fr<ppT>::one());
297 typename groupVariableT,
298 typename variableSelectorT,
300 add_variable_and_variable_or_identity<
306 add_variable_and_variable_or_identity(
307 protoboard<FieldT> &pb,
308 const variableOrIdentity &A,
309 const groupVariableT &B,
310 const groupVariableT &result,
311 const std::string &annotation_prefix)
312 : gadget<FieldT>(pb, annotation_prefix)
314 , add_result(pb, FMT(annotation_prefix, " add_result"))
315 , add(pb, A.value, B, add_result, FMT(annotation_prefix, " add"))
322 FMT(annotation_prefix, " selector_A"))
329 typename groupVariableT,
330 typename variableSelectorT,
332 void add_variable_and_variable_or_identity<
337 addGadgetT>::generate_r1cs_constraints()
339 add.generate_r1cs_constraints();
340 selector_A.generate_r1cs_constraints();
346 typename groupVariableT,
347 typename variableSelectorT,
349 void add_variable_and_variable_or_identity<
354 addGadgetT>::generate_r1cs_witness()
356 add.generate_r1cs_witness();
357 selector_A.generate_r1cs_witness();
363 typename groupVariableT,
365 dbl_variable_or_identity<ppT, groupT, groupVariableT, dblGadgetT>::
366 dbl_variable_or_identity(
367 protoboard<FieldT> &pb,
368 const variableOrIdentity &A,
369 const variableOrIdentity &result,
370 const std::string &annotation_prefix)
371 : gadget<libff::Fr<ppT>>(pb, annotation_prefix)
372 , A_is_identity(A.is_identity)
375 pb, A.value, result.value, FMT(annotation_prefix, " double_gadget"))
382 typename groupVariableT,
384 void dbl_variable_or_identity<ppT, groupT, groupVariableT, dblGadgetT>::
385 generate_r1cs_constraints()
387 // It should be possible to do this by simply assigning A.is_identity to
388 // result.is_identity, but result.is_identity has already been allocated at
391 this->pb.add_r1cs_constraint(
392 r1cs_constraint<FieldT>(
393 A_is_identity, libff::Fr<ppT>::one(), result.is_identity),
394 FMT(this->annotation_prefix, " result_is_identity_constraint"));
395 double_gadget.generate_r1cs_constraints();
401 typename groupVariableT,
403 void dbl_variable_or_identity<ppT, groupT, groupVariableT, dblGadgetT>::
404 generate_r1cs_witness()
406 A_is_identity.evaluate(this->pb);
407 this->pb.lc_val(result.is_identity) = this->pb.lc_val(A_is_identity);
408 double_gadget.generate_r1cs_witness();
413 typename groupVariableT,
417 point_mul_by_const_scalar_gadget<
423 point_mul_by_const_scalar_gadget(
424 protoboard<FieldT> &pb,
425 const scalarT &scalar,
426 const groupVariableT &P,
427 const groupVariableT &result,
428 const std::string &annotation_prefix)
429 : gadget<FieldT>(pb, annotation_prefix), _scalar(scalar), _result(result)
431 const size_t last_bit = _scalar.num_bits() - 1;
432 const groupVariableT *last_value = &P;
434 // Temporary vector of intermediate variables. Reserve the maximum number
435 // of possible entries to ensure no reallocation (i.e. last_value is always
437 std::vector<groupVariableT> values;
438 values.reserve(2 * last_bit);
440 for (size_t i = last_bit - 1; i > 0; --i) {
442 values.emplace_back(pb, FMT(annotation_prefix, " value[%zu]", i));
443 _dbl_gadgets.emplace_back(new dbl_gadget(
447 FMT(annotation_prefix, " double[%zu]", i)));
448 last_value = &values.back();
451 if (_scalar.test_bit(i)) {
452 values.emplace_back(pb, FMT(annotation_prefix, " value[%zu]", i));
453 _add_gadgets.emplace_back(new add_gadget(
458 FMT(annotation_prefix, " add[%zu]", i)));
459 last_value = &values.back();
463 // Depending on the value of the final (lowest-order) bit, perform final
464 // double or double-and-add into result.
466 if (_scalar.test_bit(0)) {
468 values.emplace_back(pb, FMT(annotation_prefix, " value[0]"));
469 _dbl_gadgets.emplace_back(new dbl_gadget(
473 FMT(annotation_prefix, " double[0]")));
474 last_value = &values.back();
477 _add_gadgets.emplace_back(new add_gadget(
478 pb, *last_value, P, result, FMT(annotation_prefix, " add[0]")));
481 _dbl_gadgets.emplace_back(new dbl_gadget(
482 pb, *last_value, result, FMT(annotation_prefix, " double[0]")));
488 typename groupVariableT,
492 void point_mul_by_const_scalar_gadget<
497 scalarT>::generate_r1cs_constraints()
499 const size_t last_bit = _scalar.num_bits() - 1;
502 for (ssize_t i = last_bit - 1; i >= 0; --i) {
503 // Double gadget constraints
504 _dbl_gadgets[dbl_idx++]->generate_r1cs_constraints();
506 // Add gadget constraints
507 if (_scalar.test_bit(i)) {
508 _add_gadgets[add_idx++]->generate_r1cs_constraints();
515 typename groupVariableT,
519 void point_mul_by_const_scalar_gadget<
524 scalarT>::generate_r1cs_witness()
526 const size_t last_bit = _scalar.num_bits() - 1;
529 for (ssize_t i = last_bit - 1; i >= 0; --i) {
530 // Double gadget constraints
531 _dbl_gadgets[dbl_idx++]->generate_r1cs_witness();
533 // Add gadget constraints
534 if (_scalar.test_bit(i)) {
535 _add_gadgets[add_idx++]->generate_r1cs_witness();
542 typename groupVariableT,
546 const groupVariableT &point_mul_by_const_scalar_gadget<
551 scalarT>::result() const
560 typename selectorGadgetT,
563 point_mul_by_scalar_gadget<
570 point_mul_by_scalar_gadget(
571 protoboard<Field> &pb,
572 const pb_linear_combination<Field> &scalar,
574 const groupVarOrIdentity &result,
575 const std::string &annotation_prefix)
576 : gadget<Field>(pb, annotation_prefix)
579 create_variable_array_for_bits(pb, annotation_prefix),
581 FMT(annotation_prefix, " scalar_as_bits"))
583 add_gadgets.reserve(nFr::num_bits - 1);
584 dbl_gadgets.reserve(nFr::num_bits - 1);
585 selector_gadgets.reserve(nFr::num_bits);
588 // res = select(scalar_as_bits[n-1], P, one)
589 // for i = n-2 ... 0:
591 // res = select(scalar_as_bits[i], add(res, P), res)
593 // Lowest order bit has smallest index in scalar_unpacked.bits
595 // First selector must choose (bit[num_bits - 1] ? P : zero)
597 selector_gadgets.emplace_back(
599 scalar_unpacked.bits[nFr::num_bits - 1],
600 groupVarOrIdentity(pb, groupT::zero(), FMT(annotation_prefix, " zero")),
604 FMT(annotation_prefix, " select_result[%zu]", nFr::num_bits - 1)),
605 FMT(annotation_prefix, " selector_gadgets[%zu]", nFr::num_bits - 1));
607 for (size_t i = nFr::num_bits - 2; i > 0; --i) {
609 dbl_gadgets.emplace_back(
611 selector_gadgets.back().result,
613 pb, FMT(annotation_prefix, " dbl_result[%zu]", i)),
614 FMT(annotation_prefix, " dbl_gadgets[%zu]", i));
617 add_gadgets.emplace_back(
619 dbl_gadgets.back().result,
621 groupVarT(pb, FMT(annotation_prefix, " add_result[%zu]", i)),
622 FMT(annotation_prefix, " add_gadgets[%zu]", i));
624 // Select the doubled or double-and-added output value, based on the
625 // currenet bit of scalar_unpacked.
627 // If bit == 0, the selector should output result of the dbl (*last).
628 // For bit == 1, it should output the result of the add.
630 selector_gadgets.emplace_back(
632 scalar_unpacked.bits[i],
633 dbl_gadgets.back().result,
634 add_gadgets.back().result,
636 pb, FMT(annotation_prefix, " select_result[%zu]", i)),
637 FMT(annotation_prefix, " selector_gadgets[%zu]", i));
640 // Handle the 0-th bit. Perform the double as usual, but adjust the inputs
641 // to the final add based on the 0-th bit (namely, if the 0-th bit is 0, we
642 // use a dummy addition to avoid division by 0 - see comment in
643 // scalar_multiplication.hpp).
645 dbl_gadgets.emplace_back(
647 selector_gadgets.back().result,
648 groupVarOrIdentity(pb, FMT(annotation_prefix, " dbl_result[0]")),
649 FMT(annotation_prefix, " dbl_gadgets[0]"));
654 // left_input = dbl_gadgets.back().result
657 // left_input = GroupT::one()
658 // right_input = GroupT::one() + GroupT::one()
660 // final_add = addVarAndVarOrIdentityGadget(left_input, right_input)
662 pb_linear_combination<Field> final_add_input_select;
663 final_add_input_select.assign(
664 pb, pb_variable<Field>(0) - scalar_unpacked.bits[0]);
666 final_add_input_left.reset(new selectVarOrVarIdentityGadget(
668 final_add_input_select,
669 dbl_gadgets.back().result,
673 FMT(annotation_prefix, " final_add_input_left_dummy")),
675 pb, FMT(annotation_prefix, " final_add_input_left_result")),
676 FMT(annotation_prefix, " final_add_input_left")));
678 final_add_input_right.reset(new selectorGadgetT(
680 final_add_input_select,
684 groupT::one() + groupT::one(),
685 FMT(annotation_prefix, " final_add_input_right_dummy")),
686 groupVarT(pb, FMT(annotation_prefix, " final_add_input_left_result")),
687 FMT(annotation_prefix, " final_add_input_right")));
689 add_gadgets.emplace_back(
691 final_add_input_left->result,
692 final_add_input_right->result,
693 groupVarT(pb, FMT(annotation_prefix, " add_result[0]")),
694 FMT(annotation_prefix, " add_gadgets[0]"));
696 // Final selector outputs to the result:
699 // result = final_add
701 // result = dbl_gadgets.back().result
703 selector_gadgets.emplace_back(
705 scalar_unpacked.bits[0],
706 dbl_gadgets.back().result,
707 add_gadgets.back().result,
709 FMT(annotation_prefix, " selector_gadgets[0]"));
711 assert(selector_gadgets.size() == nFr::num_bits);
712 assert(dbl_gadgets.size() == nFr::num_bits - 1);
713 assert(add_gadgets.size() == nFr::num_bits - 1);
720 typename selectorGadgetT,
723 void point_mul_by_scalar_gadget<
729 dblGadgetT>::generate_r1cs_constraints()
731 scalar_unpacked.generate_r1cs_constraints(true);
732 selector_gadgets[0].generate_r1cs_constraints();
733 for (size_t i = 0; i < nFr::num_bits - 2; ++i) {
734 dbl_gadgets[i].generate_r1cs_constraints();
735 add_gadgets[i].generate_r1cs_constraints();
736 selector_gadgets[i + 1].generate_r1cs_constraints();
739 dbl_gadgets.back().generate_r1cs_constraints();
740 final_add_input_left->generate_r1cs_constraints();
741 final_add_input_right->generate_r1cs_constraints();
742 add_gadgets.back().generate_r1cs_constraints();
743 selector_gadgets.back().generate_r1cs_constraints();
750 typename selectorGadgetT,
753 void point_mul_by_scalar_gadget<
759 dblGadgetT>::generate_r1cs_witness()
761 // Assign and evaluate in the same order as in the constructor /
762 // generate_r1cs_constraints - first selector_gadget is used first,
763 // followed by repeated double-add-select.
765 scalar_unpacked.generate_r1cs_witness_from_packed();
766 selector_gadgets[0].generate_r1cs_witness();
768 for (size_t i = 0; i < nFr::num_bits - 2; ++i) {
769 dbl_gadgets[i].generate_r1cs_witness();
770 add_gadgets[i].generate_r1cs_witness();
772 selector_gadgets[i + 1].generate_r1cs_witness();
775 dbl_gadgets.back().generate_r1cs_witness();
776 final_add_input_right->selector.evaluate(this->pb);
778 final_add_input_left->generate_r1cs_witness();
779 final_add_input_right->generate_r1cs_witness();
780 add_gadgets.back().generate_r1cs_witness();
781 selector_gadgets.back().generate_r1cs_witness();
788 typename selectorGadgetT,
791 const variable_or_identity<ppT, groupT, groupVarT> &point_mul_by_scalar_gadget<
797 dblGadgetT>::result() const
799 return selector_gadgets.back().result;
806 typename selectorGadgetT,
809 pb_variable_array<libff::Fr<ppT>> point_mul_by_scalar_gadget<
816 create_variable_array_for_bits(
817 protoboard<Field> &pb, const std::string &annotation_prefix)
819 pb_variable_array<Field> bits;
820 bits.allocate(pb, nFr::num_bits, FMT(annotation_prefix, " unpacked_bits"));
824 } // namespace libsnark
826 #endif // LIBSNARK_GADGETLIB1_GADGETS_CURVE_SCALAR_MULTIPLICATION_TCC_