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  *****************************************************************************/
 
    7 #ifndef SET_COMMITMENT_GADGET_TCC_
 
    8 #define SET_COMMITMENT_GADGET_TCC_
 
   10 #include <libsnark/common/data_structures/set_commitment.hpp>
 
   15 template<typename FieldT, typename HashT>
 
   16 set_commitment_gadget<FieldT, HashT>::set_commitment_gadget(
 
   17     protoboard<FieldT> &pb,
 
   18     const size_t max_entries,
 
   19     const pb_variable_array<FieldT> &element_bits,
 
   20     const set_commitment_variable<FieldT, HashT> &root_digest,
 
   21     const set_membership_proof_variable<FieldT, HashT> &proof,
 
   22     const pb_linear_combination<FieldT> &check_successful,
 
   23     const std::string &annotation_prefix)
 
   24     : gadget<FieldT>(pb, annotation_prefix)
 
   25     , tree_depth(libff::log2(max_entries))
 
   26     , element_bits(element_bits)
 
   27     , root_digest(root_digest)
 
   29     , check_successful(check_successful)
 
   31     element_block.reset(new block_variable<FieldT>(
 
   32         pb, {element_bits}, FMT(annotation_prefix, " element_block")));
 
   34     if (tree_depth == 0) {
 
   35         hash_element.reset(new HashT(
 
   40             FMT(annotation_prefix, " hash_element")));
 
   42         element_digest.reset(new digest_variable<FieldT>(
 
   44             HashT::get_digest_len(),
 
   45             FMT(annotation_prefix, " element_digest")));
 
   46         hash_element.reset(new HashT(
 
   51             FMT(annotation_prefix, " hash_element")));
 
   52         check_membership.reset(new merkle_tree_check_read_gadget<FieldT, HashT>(
 
   60             FMT(annotation_prefix, " check_membership")));
 
   64 template<typename FieldT, typename HashT>
 
   65 void set_commitment_gadget<FieldT, HashT>::generate_r1cs_constraints()
 
   67     hash_element->generate_r1cs_constraints();
 
   70         check_membership->generate_r1cs_constraints();
 
   74 template<typename FieldT, typename HashT>
 
   75 void set_commitment_gadget<FieldT, HashT>::generate_r1cs_witness()
 
   77     hash_element->generate_r1cs_witness();
 
   80         check_membership->generate_r1cs_witness();
 
   84 template<typename FieldT, typename HashT>
 
   85 size_t set_commitment_gadget<FieldT, HashT>::root_size_in_bits()
 
   87     return merkle_tree_check_read_gadget<FieldT, HashT>::root_size_in_bits();
 
   90 template<typename FieldT, typename HashT> void test_set_commitment_gadget()
 
   92     const size_t digest_len = HashT::get_digest_len();
 
   93     const size_t max_set_size = 16;
 
   94     const size_t value_size =
 
   95         (HashT::get_block_len() > 0 ? HashT::get_block_len() : 10);
 
   97     set_commitment_accumulator<HashT> accumulator(max_set_size, value_size);
 
   99     std::vector<libff::bit_vector> set_elems;
 
  100     for (size_t i = 0; i < max_set_size; ++i) {
 
  101         libff::bit_vector elem(value_size);
 
  103             elem.begin(), elem.end(), [&]() { return std::rand() % 2; });
 
  104         set_elems.emplace_back(elem);
 
  105         accumulator.add(elem);
 
  106         assert(accumulator.is_in_set(elem));
 
  109     protoboard<FieldT> pb;
 
  110     pb_variable_array<FieldT> element_bits;
 
  111     element_bits.allocate(pb, value_size, "element_bits");
 
  112     set_commitment_variable<FieldT, HashT> root_digest(
 
  113         pb, digest_len, "root_digest");
 
  115     pb_variable<FieldT> check_succesful;
 
  116     check_succesful.allocate(pb, "check_succesful");
 
  118     set_membership_proof_variable<FieldT, HashT> proof(
 
  119         pb, max_set_size, "proof");
 
  121     set_commitment_gadget<FieldT, HashT> sc(
 
  129     sc.generate_r1cs_constraints();
 
  131     // test all elements from set
 
  132     for (size_t i = 0; i < max_set_size; ++i) {
 
  133         element_bits.fill_with_bits(pb, set_elems[i]);
 
  134         pb.val(check_succesful) = FieldT::one();
 
  135         proof.generate_r1cs_witness(
 
  136             accumulator.get_membership_proof(set_elems[i]));
 
  137         sc.generate_r1cs_witness();
 
  138         root_digest.generate_r1cs_witness(accumulator.get_commitment());
 
  139         assert(pb.is_satisfied());
 
  141     printf("membership tests OK\n");
 
  143     /* test an element not in set */
 
  144     for (size_t i = 0; i < value_size; ++i) {
 
  145         pb.val(element_bits[i]) = FieldT(std::rand() % 2);
 
  148     // do not require the check result to be successful
 
  149     pb.val(check_succesful) = FieldT::zero();
 
  150     // try it with invalid proof
 
  151     proof.generate_r1cs_witness(accumulator.get_membership_proof(set_elems[0]));
 
  152     sc.generate_r1cs_witness();
 
  153     root_digest.generate_r1cs_witness(accumulator.get_commitment());
 
  154     assert(pb.is_satisfied());
 
  156     // now require the check result to be succesful
 
  157     pb.val(check_succesful) = FieldT::one();
 
  158     // try it with invalid proof
 
  159     proof.generate_r1cs_witness(accumulator.get_membership_proof(set_elems[0]));
 
  160     sc.generate_r1cs_witness();
 
  161     root_digest.generate_r1cs_witness(accumulator.get_commitment());
 
  162     // the protoboard should be unsatisfied
 
  163     assert(!pb.is_satisfied());
 
  164     printf("non-membership test OK\n");
 
  167 } // namespace libsnark
 
  169 #endif // SET_COMMITMENT_GADGET_TCC_