2  *****************************************************************************
 
    4  Implementation of interfaces for memory_checker_gadget.
 
    6  See memory_checker_gadget.hpp .
 
    8  *****************************************************************************
 
    9  * @author     This file is part of libsnark, developed by SCIPR Lab
 
   10  *             and contributors (see AUTHORS).
 
   11  * @copyright  MIT license (see LICENSE file)
 
   12  *****************************************************************************/
 
   14 #ifndef MEMORY_CHECKER_GADGET_TCC_
 
   15 #define MEMORY_CHECKER_GADGET_TCC_
 
   20 template<typename ramT>
 
   21 memory_checker_gadget<ramT>::memory_checker_gadget(
 
   22     ram_protoboard<ramT> &pb,
 
   23     const size_t timestamp_size,
 
   24     const memory_line_variable_gadget<ramT> &line1,
 
   25     const memory_line_variable_gadget<ramT> &line2,
 
   26     const std::string &annotation_prefix)
 
   27     : ram_gadget_base<ramT>(pb, annotation_prefix), line1(line1), line2(line2)
 
   29     /* compare the two timestamps */
 
   30     timestamps_leq.allocate(
 
   31         pb, FMT(this->annotation_prefix, " timestamps_leq"));
 
   32     timestamps_less.allocate(
 
   33         pb, FMT(this->annotation_prefix, " timestamps_less"));
 
   34     compare_timestamps.reset(new comparison_gadget<FieldT>(
 
   37         line1.timestamp->packed,
 
   38         line2.timestamp->packed,
 
   41         FMT(this->annotation_prefix, " compare_ts")));
 
   43     /* compare the two addresses */
 
   44     const size_t address_size = pb.ap.address_size();
 
   45     addresses_eq.allocate(pb, FMT(this->annotation_prefix, " addresses_eq"));
 
   46     addresses_leq.allocate(pb, FMT(this->annotation_prefix, " addresses_leq"));
 
   47     addresses_less.allocate(
 
   48         pb, FMT(this->annotation_prefix, " addresses_less"));
 
   49     compare_addresses.reset(new comparison_gadget<FieldT>(
 
   52         line1.address->packed,
 
   53         line2.address->packed,
 
   56         FMT(this->annotation_prefix, " compare_addresses")));
 
   59       Add variables that will contain flags representing the following
 
   61       - "line1.contents_after = line2.contents_before" (to check that contents
 
   62       do not change between instructions);
 
   63       - "line2.contents_before = 0" (for the first access at an address); and
 
   64       - "line2.timestamp = 0" (for wrap-around checks to ensure only one 'cycle'
 
   67       More precisely, each of the above flags is "loose" (i.e., it equals 0 if
 
   68       the relation holds, but can be either 0 or 1 if the relation does not
 
   71     loose_contents_after1_equals_contents_before2.allocate(
 
   73         FMT(this->annotation_prefix,
 
   74             " loose_contents_after1_equals_contents_before2"));
 
   75     loose_contents_before2_equals_zero.allocate(
 
   77         FMT(this->annotation_prefix, " loose_contents_before2_equals_zero"));
 
   78     loose_timestamp2_is_zero.allocate(
 
   79         pb, FMT(this->annotation_prefix, " loose_timestamp2_is_zero"));
 
   82 template<typename ramT>
 
   83 void memory_checker_gadget<ramT>::generate_r1cs_constraints()
 
   85     /* compare the two timestamps */
 
   86     compare_timestamps->generate_r1cs_constraints();
 
   88     /* compare the two addresses */
 
   89     compare_addresses->generate_r1cs_constraints();
 
   90     this->pb.add_r1cs_constraint(
 
   91         r1cs_constraint<FieldT>(
 
   92             addresses_leq, 1 - addresses_less, addresses_eq),
 
   93         FMT(this->annotation_prefix, " addresses_eq"));
 
   96       Add constraints for the following three flags:
 
   97        - loose_contents_after1_equals_contents_before2;
 
   98        - loose_contents_before2_equals_zero;
 
   99        - loose_timestamp2_is_zero.
 
  101     this->pb.add_r1cs_constraint(
 
  102         r1cs_constraint<FieldT>(
 
  103             loose_contents_after1_equals_contents_before2,
 
  104             line1.contents_after->packed - line2.contents_before->packed,
 
  106         FMT(this->annotation_prefix,
 
  107             " loose_contents_after1_equals_contents_before2"));
 
  108     generate_boolean_r1cs_constraint<FieldT>(
 
  110         loose_contents_after1_equals_contents_before2,
 
  111         FMT(this->annotation_prefix,
 
  112             " loose_contents_after1_equals_contents_before2"));
 
  114     this->pb.add_r1cs_constraint(
 
  115         r1cs_constraint<FieldT>(
 
  116             loose_contents_before2_equals_zero,
 
  117             line2.contents_before->packed,
 
  119         FMT(this->annotation_prefix, " loose_contents_before2_equals_zero"));
 
  120     generate_boolean_r1cs_constraint<FieldT>(
 
  122         loose_contents_before2_equals_zero,
 
  123         FMT(this->annotation_prefix, " loose_contents_before2_equals_zero"));
 
  125     this->pb.add_r1cs_constraint(
 
  126         r1cs_constraint<FieldT>(
 
  127             loose_timestamp2_is_zero, line2.timestamp->packed, 0),
 
  128         FMT(this->annotation_prefix, " loose_timestamp2_is_zero"));
 
  129     generate_boolean_r1cs_constraint<FieldT>(
 
  131         loose_timestamp2_is_zero,
 
  132         FMT(this->annotation_prefix, " loose_timestamp2_is_zero"));
 
  135       The three cases that need to be checked are:
 
  137       line1.address = line2.address => line1.contents_after =
 
  138       line2.contents_before (i.e. contents do not change between accesses to the
 
  141       line1.address < line2.address => line2.contents_before = 0
 
  142       (i.e. access to new address has the "before" value set to 0)
 
  144       line1.address > line2.address => line2.timestamp = 0
 
  145       (i.e. there is only one cycle with non-decreasing addresses, except
 
  146       for the case where we go back to a unique pre-set timestamp; we choose
 
  147       timestamp 0 to be the one that touches address 0)
 
  149       As usual, we implement "A => B" as "NOT (A AND (NOT B))".
 
  151     this->pb.add_r1cs_constraint(
 
  152         r1cs_constraint<FieldT>(
 
  153             addresses_eq, 1 - loose_contents_after1_equals_contents_before2, 0),
 
  154         FMT(this->annotation_prefix,
 
  155             " memory_retains_contents_between_accesses"));
 
  156     this->pb.add_r1cs_constraint(
 
  157         r1cs_constraint<FieldT>(
 
  158             addresses_less, 1 - loose_contents_before2_equals_zero, 0),
 
  159         FMT(this->annotation_prefix, " new_address_starts_at_zero"));
 
  160     this->pb.add_r1cs_constraint(
 
  161         r1cs_constraint<FieldT>(
 
  162             1 - addresses_leq, 1 - loose_timestamp2_is_zero, 0),
 
  163         FMT(this->annotation_prefix, " only_one_cycle"));
 
  166 template<typename ramT>
 
  167 void memory_checker_gadget<ramT>::generate_r1cs_witness()
 
  169     /* compare the two addresses */
 
  170     compare_addresses->generate_r1cs_witness();
 
  171     this->pb.val(addresses_eq) = this->pb.val(addresses_leq) *
 
  172                                  (FieldT::one() - this->pb.val(addresses_less));
 
  174     /* compare the two timestamps */
 
  175     compare_timestamps->generate_r1cs_witness();
 
  178       compare the values of:
 
  179       - loose_contents_after1_equals_contents_before2;
 
  180       - loose_contents_before2_equals_zero;
 
  181       - loose_timestamp2_is_zero.
 
  183     this->pb.val(loose_contents_after1_equals_contents_before2) =
 
  184         (this->pb.val(line1.contents_after->packed) ==
 
  185          this->pb.val(line2.contents_before->packed))
 
  188     this->pb.val(loose_contents_before2_equals_zero) =
 
  189         this->pb.val(line2.contents_before->packed).is_zero() ? FieldT::one()
 
  191     this->pb.val(loose_timestamp2_is_zero) =
 
  192         (this->pb.val(line2.timestamp->packed) == FieldT::zero()
 
  197 } // namespace libsnark
 
  199 #endif // MEMORY_CHECKER_GADGET_TCC_