Clearmatics Libsnark  0.1
C++ library for zkSNARK proofs
merkle_tree_check_read_gadget.tcc
Go to the documentation of this file.
1 /** @file
2  *****************************************************************************
3 
4  Implementation of interfaces for the Merkle tree check read.
5 
6  See merkle_tree_check_read_gadget.hpp .
7 
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  *****************************************************************************/
13 
14 #ifndef MERKLE_TREE_CHECK_READ_GADGET_TCC_
15 #define MERKLE_TREE_CHECK_READ_GADGET_TCC_
16 
17 namespace libsnark
18 {
19 
20 template<typename FieldT, typename HashT>
21 merkle_tree_check_read_gadget<FieldT, HashT>::merkle_tree_check_read_gadget(
22  protoboard<FieldT> &pb,
23  const size_t tree_depth,
24  const pb_linear_combination_array<FieldT> &address_bits,
25  const digest_variable<FieldT> &leaf,
26  const digest_variable<FieldT> &root,
27  const merkle_authentication_path_variable<FieldT, HashT> &path,
28  const pb_linear_combination<FieldT> &read_successful,
29  const std::string &annotation_prefix)
30  : gadget<FieldT>(pb, annotation_prefix)
31  , digest_size(HashT::get_digest_len())
32  , tree_depth(tree_depth)
33  , address_bits(address_bits)
34  , leaf(leaf)
35  , root(root)
36  , path(path)
37  , read_successful(read_successful)
38 {
39  /*
40  The tricky part here is ordering. For Merkle tree
41  authentication paths, path[0] corresponds to one layer below
42  the root (and path[tree_depth-1] corresponds to the layer
43  containing the leaf), while address_bits has the reverse order:
44  address_bits[0] is LSB, and corresponds to layer containing the
45  leaf, and address_bits[tree_depth-1] is MSB, and corresponds to
46  the subtree directly under the root.
47  */
48  assert(tree_depth > 0);
49  assert(tree_depth == address_bits.size());
50 
51  for (size_t i = 0; i < tree_depth - 1; ++i) {
52  internal_output.emplace_back(digest_variable<FieldT>(
53  pb,
54  digest_size,
55  FMT(this->annotation_prefix, " internal_output_%zu", i)));
56  }
57 
58  computed_root.reset(new digest_variable<FieldT>(
59  pb, digest_size, FMT(this->annotation_prefix, " computed_root")));
60 
61  for (size_t i = 0; i < tree_depth; ++i) {
62  block_variable<FieldT> inp(
63  pb,
64  path.left_digests[i],
65  path.right_digests[i],
66  FMT(this->annotation_prefix, " inp_%zu", i));
67  hasher_inputs.emplace_back(inp);
68  hashers.emplace_back(HashT(
69  pb,
70  2 * digest_size,
71  inp,
72  (i == 0 ? *computed_root : internal_output[i - 1]),
73  FMT(this->annotation_prefix, " load_hashers_%zu", i)));
74  }
75 
76  for (size_t i = 0; i < tree_depth; ++i) {
77  /*
78  The propagators take a computed hash value (or leaf in the
79  base case) and propagate it one layer up, either in the left
80  or the right slot of authentication_path_variable.
81  */
82  propagators.emplace_back(digest_selector_gadget<FieldT>(
83  pb,
84  digest_size,
85  i < tree_depth - 1 ? internal_output[i] : leaf,
86  address_bits[tree_depth - 1 - i],
87  path.left_digests[i],
88  path.right_digests[i],
89  FMT(this->annotation_prefix, " digest_selector_%zu", i)));
90  }
91 
92  check_root.reset(new bit_vector_copy_gadget<FieldT>(
93  pb,
94  computed_root->bits,
95  root.bits,
96  read_successful,
97  FieldT::capacity(),
98  FMT(annotation_prefix, " check_root")));
99 }
100 
101 template<typename FieldT, typename HashT>
102 void merkle_tree_check_read_gadget<FieldT, HashT>::generate_r1cs_constraints()
103 {
104  /* ensure correct hash computations */
105  for (size_t i = 0; i < tree_depth; ++i) {
106  // Note that we check root outside and have enforced booleanity of
107  // path.left_digests/path.right_digests outside in
108  // path.generate_r1cs_constraints
109  hashers[i].generate_r1cs_constraints(false);
110  }
111 
112  /* ensure consistency of path.left_digests/path.right_digests with
113  * internal_output */
114  for (size_t i = 0; i < tree_depth; ++i) {
115  propagators[i].generate_r1cs_constraints();
116  }
117 
118  check_root->generate_r1cs_constraints(false, false);
119 }
120 
121 template<typename FieldT, typename HashT>
122 void merkle_tree_check_read_gadget<FieldT, HashT>::generate_r1cs_witness()
123 {
124  /* do the hash computations bottom-up */
125  for (int i = tree_depth - 1; i >= 0; --i) {
126  /* propagate previous input */
127  propagators[i].generate_r1cs_witness();
128 
129  /* compute hash */
130  hashers[i].generate_r1cs_witness();
131  }
132 
133  check_root->generate_r1cs_witness();
134 }
135 
136 template<typename FieldT, typename HashT>
137 size_t merkle_tree_check_read_gadget<FieldT, HashT>::root_size_in_bits()
138 {
139  return HashT::get_digest_len();
140 }
141 
142 template<typename FieldT, typename HashT>
143 size_t merkle_tree_check_read_gadget<FieldT, HashT>::expected_constraints(
144  const size_t tree_depth)
145 {
146  /* NB: this includes path constraints */
147  const size_t hasher_constraints =
148  tree_depth * HashT::expected_constraints(false);
149  const size_t propagator_constraints = tree_depth * HashT::get_digest_len();
150  const size_t authentication_path_constraints =
151  2 * tree_depth * HashT::get_digest_len();
152  const size_t check_root_constraints =
153  3 * libff::div_ceil(HashT::get_digest_len(), FieldT::capacity());
154 
155  return hasher_constraints + propagator_constraints +
156  authentication_path_constraints + check_root_constraints;
157 }
158 
159 template<typename FieldT, typename HashT>
160 void test_merkle_tree_check_read_gadget()
161 {
162  /* prepare test */
163  const size_t digest_len = HashT::get_digest_len();
164  const size_t tree_depth = 16;
165  std::vector<merkle_authentication_node> path(tree_depth);
166 
167  libff::bit_vector prev_hash(digest_len);
168  std::generate(
169  prev_hash.begin(), prev_hash.end(), [&]() { return std::rand() % 2; });
170  libff::bit_vector leaf = prev_hash;
171 
172  libff::bit_vector address_bits;
173 
174  size_t address = 0;
175  for (long level = tree_depth - 1; level >= 0; --level) {
176  const bool computed_is_right = (std::rand() % 2);
177  address |= (computed_is_right ? 1ul << (tree_depth - 1 - level) : 0);
178  address_bits.push_back(computed_is_right);
179  libff::bit_vector other(digest_len);
180  std::generate(
181  other.begin(), other.end(), [&]() { return std::rand() % 2; });
182 
183  libff::bit_vector block = prev_hash;
184  block.insert(
185  computed_is_right ? block.begin() : block.end(),
186  other.begin(),
187  other.end());
188  libff::bit_vector h = HashT::get_hash(block);
189 
190  path[level] = other;
191 
192  prev_hash = h;
193  }
194  libff::bit_vector root = prev_hash;
195 
196  /* execute test */
197  protoboard<FieldT> pb;
198  pb_variable_array<FieldT> address_bits_va;
199  address_bits_va.allocate(pb, tree_depth, "address_bits");
200  digest_variable<FieldT> leaf_digest(pb, digest_len, "input_block");
201  digest_variable<FieldT> root_digest(pb, digest_len, "output_digest");
202  merkle_authentication_path_variable<FieldT, HashT> path_var(
203  pb, tree_depth, "path_var");
204  merkle_tree_check_read_gadget<FieldT, HashT> ml(
205  pb,
206  tree_depth,
207  address_bits_va,
208  leaf_digest,
209  root_digest,
210  path_var,
211  ONE,
212  "ml");
213 
214  path_var.generate_r1cs_constraints();
215  ml.generate_r1cs_constraints();
216 
217  address_bits_va.fill_with_bits(pb, address_bits);
218  assert(
219  address_bits_va.get_field_element_from_bits(pb).as_ulong() == address);
220  leaf_digest.generate_r1cs_witness(leaf);
221  path_var.generate_r1cs_witness(address, path);
222  ml.generate_r1cs_witness();
223 
224  /* make sure that read checker didn't accidentally overwrite anything */
225  address_bits_va.fill_with_bits(pb, address_bits);
226  leaf_digest.generate_r1cs_witness(leaf);
227  root_digest.generate_r1cs_witness(root);
228  assert(pb.is_satisfied());
229 
230 #ifndef NDEBUG
231  const size_t num_constraints = pb.num_constraints();
232  const size_t expected_constraints =
233  merkle_tree_check_read_gadget<FieldT, HashT>::expected_constraints(
234  tree_depth);
235 #endif
236  assert(num_constraints == expected_constraints);
237 }
238 
239 } // namespace libsnark
240 
241 #endif // MERKLE_TREE_CHECK_READ_GADGET_TCC_