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_