Clearmatics Libsnark  0.1
C++ library for zkSNARK proofs
merkle_tree_check_update_gadget.tcc
Go to the documentation of this file.
1 /** @file
2  *****************************************************************************
3 
4  Implementation of interfaces for the Merkle tree check update gadget.
5 
6  See merkle_tree_check_update_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_UPDATE_GADGET_TCC_
15 #define MERKLE_TREE_CHECK_UPDATE_GADGET_TCC_
16 
17 namespace libsnark
18 {
19 
20 template<typename FieldT, typename HashT>
21 merkle_tree_check_update_gadget<FieldT, HashT>::merkle_tree_check_update_gadget(
22  protoboard<FieldT> &pb,
23  const size_t tree_depth,
24  const pb_variable_array<FieldT> &address_bits,
25  const digest_variable<FieldT> &prev_leaf_digest,
26  const digest_variable<FieldT> &prev_root_digest,
27  const merkle_authentication_path_variable<FieldT, HashT> &prev_path,
28  const digest_variable<FieldT> &next_leaf_digest,
29  const digest_variable<FieldT> &next_root_digest,
30  const merkle_authentication_path_variable<FieldT, HashT> &next_path,
31  const pb_linear_combination<FieldT> &update_successful,
32  const std::string &annotation_prefix)
33  : gadget<FieldT>(pb, annotation_prefix)
34  , digest_size(HashT::get_digest_len())
35  , tree_depth(tree_depth)
36  , address_bits(address_bits)
37  , prev_leaf_digest(prev_leaf_digest)
38  , prev_root_digest(prev_root_digest)
39  , prev_path(prev_path)
40  , next_leaf_digest(next_leaf_digest)
41  , next_root_digest(next_root_digest)
42  , next_path(next_path)
43  , update_successful(update_successful)
44 {
45  assert(tree_depth > 0);
46  assert(tree_depth == address_bits.size());
47 
48  for (size_t i = 0; i < tree_depth - 1; ++i) {
49  prev_internal_output.emplace_back(digest_variable<FieldT>(
50  pb,
51  digest_size,
52  FMT(this->annotation_prefix, " prev_internal_output_%zu", i)));
53  next_internal_output.emplace_back(digest_variable<FieldT>(
54  pb,
55  digest_size,
56  FMT(this->annotation_prefix, " next_internal_output_%zu", i)));
57  }
58 
59  computed_next_root.reset(new digest_variable<FieldT>(
60  pb, digest_size, FMT(this->annotation_prefix, " computed_root")));
61 
62  for (size_t i = 0; i < tree_depth; ++i) {
63  block_variable<FieldT> prev_inp(
64  pb,
65  prev_path.left_digests[i],
66  prev_path.right_digests[i],
67  FMT(this->annotation_prefix, " prev_inp_%zu", i));
68  prev_hasher_inputs.emplace_back(prev_inp);
69  prev_hashers.emplace_back(HashT(
70  pb,
71  2 * digest_size,
72  prev_inp,
73  (i == 0 ? prev_root_digest : prev_internal_output[i - 1]),
74  FMT(this->annotation_prefix, " prev_hashers_%zu", i)));
75 
76  block_variable<FieldT> next_inp(
77  pb,
78  next_path.left_digests[i],
79  next_path.right_digests[i],
80  FMT(this->annotation_prefix, " next_inp_%zu", i));
81  next_hasher_inputs.emplace_back(next_inp);
82  next_hashers.emplace_back(HashT(
83  pb,
84  2 * digest_size,
85  next_inp,
86  (i == 0 ? *computed_next_root : next_internal_output[i - 1]),
87  FMT(this->annotation_prefix, " next_hashers_%zu", i)));
88  }
89 
90  for (size_t i = 0; i < tree_depth; ++i) {
91  prev_propagators.emplace_back(digest_selector_gadget<FieldT>(
92  pb,
93  digest_size,
94  i < tree_depth - 1 ? prev_internal_output[i] : prev_leaf_digest,
95  address_bits[tree_depth - 1 - i],
96  prev_path.left_digests[i],
97  prev_path.right_digests[i],
98  FMT(this->annotation_prefix, " prev_propagators_%zu", i)));
99  next_propagators.emplace_back(digest_selector_gadget<FieldT>(
100  pb,
101  digest_size,
102  i < tree_depth - 1 ? next_internal_output[i] : next_leaf_digest,
103  address_bits[tree_depth - 1 - i],
104  next_path.left_digests[i],
105  next_path.right_digests[i],
106  FMT(this->annotation_prefix, " next_propagators_%zu", i)));
107  }
108 
109  check_next_root.reset(new bit_vector_copy_gadget<FieldT>(
110  pb,
111  computed_next_root->bits,
112  next_root_digest.bits,
113  update_successful,
114  FieldT::capacity(),
115  FMT(annotation_prefix, " check_next_root")));
116 }
117 
118 template<typename FieldT, typename HashT>
119 void merkle_tree_check_update_gadget<FieldT, HashT>::generate_r1cs_constraints()
120 {
121  // ensure correct hash computations
122  for (size_t i = 0; i < tree_depth; ++i) {
123  // we check root outside and prev_left/prev_right above
124  prev_hashers[i].generate_r1cs_constraints(false);
125  // however we must check right side hashes
126  next_hashers[i].generate_r1cs_constraints(true);
127  }
128 
129  // ensure consistency of internal_left/internal_right with internal_output
130  for (size_t i = 0; i < tree_depth; ++i) {
131  prev_propagators[i].generate_r1cs_constraints();
132  next_propagators[i].generate_r1cs_constraints();
133  }
134 
135  /* ensure that prev auxiliary input and next auxiliary input match */
136  for (size_t i = 0; i < tree_depth; ++i) {
137  for (size_t j = 0; j < digest_size; ++j) {
138  /*
139  addr * (prev_left - next_left) + (1 - addr) * (prev_right -
140  next_right) = 0 addr * (prev_left - next_left - prev_right +
141  next_right) = next_right - prev_right
142  */
143  this->pb.add_r1cs_constraint(
144  r1cs_constraint<FieldT>(
145  address_bits[tree_depth - 1 - i],
146  prev_path.left_digests[i].bits[j] -
147  next_path.left_digests[i].bits[j] -
148  prev_path.right_digests[i].bits[j] +
149  next_path.right_digests[i].bits[j],
150  next_path.right_digests[i].bits[j] -
151  prev_path.right_digests[i].bits[j]),
152  FMT(this->annotation_prefix, " aux_check_%zu_%zu", i, j));
153  }
154  }
155 
156  /* Note that while it is necessary to generate R1CS constraints
157  for prev_path, it is not necessary to do so for next_path.
158 
159  This holds, because { next_path.left_inputs[i],
160  next_path.right_inputs[i] } is a pair { hash_output,
161  auxiliary_input }. The bitness for hash_output is enforced
162  above by next_hashers[i].generate_r1cs_constraints.
163 
164  Because auxiliary input is the same for prev_path and next_path
165  (enforced above), we have that auxiliary_input part is also
166  constrained to be boolean, because prev_path is *all*
167  constrained to be all boolean. */
168 
169  check_next_root->generate_r1cs_constraints(false, false);
170 }
171 
172 template<typename FieldT, typename HashT>
173 void merkle_tree_check_update_gadget<FieldT, HashT>::generate_r1cs_witness()
174 {
175  /* do the hash computations bottom-up */
176  for (int i = tree_depth - 1; i >= 0; --i) {
177  /* ensure consistency of prev_path and next_path */
178  if (this->pb.val(address_bits[tree_depth - 1 - i]) == FieldT::one()) {
179  next_path.left_digests[i].generate_r1cs_witness(
180  prev_path.left_digests[i].get_digest());
181  } else {
182  next_path.right_digests[i].generate_r1cs_witness(
183  prev_path.right_digests[i].get_digest());
184  }
185 
186  /* propagate previous input */
187  prev_propagators[i].generate_r1cs_witness();
188  next_propagators[i].generate_r1cs_witness();
189 
190  /* compute hash */
191  prev_hashers[i].generate_r1cs_witness();
192  next_hashers[i].generate_r1cs_witness();
193  }
194 
195  check_next_root->generate_r1cs_witness();
196 }
197 
198 template<typename FieldT, typename HashT>
199 size_t merkle_tree_check_update_gadget<FieldT, HashT>::root_size_in_bits()
200 {
201  return HashT::get_digest_len();
202 }
203 
204 template<typename FieldT, typename HashT>
205 size_t merkle_tree_check_update_gadget<FieldT, HashT>::expected_constraints(
206  const size_t tree_depth)
207 {
208  /* NB: this includes path constraints */
209  const size_t prev_hasher_constraints =
210  tree_depth * HashT::expected_constraints(false);
211  const size_t next_hasher_constraints =
212  tree_depth * HashT::expected_constraints(true);
213  const size_t prev_authentication_path_constraints =
214  2 * tree_depth * HashT::get_digest_len();
215  const size_t prev_propagator_constraints =
216  tree_depth * HashT::get_digest_len();
217  const size_t next_propagator_constraints =
218  tree_depth * HashT::get_digest_len();
219  const size_t check_next_root_constraints =
220  3 * libff::div_ceil(HashT::get_digest_len(), FieldT::capacity());
221  const size_t aux_equality_constraints =
222  tree_depth * HashT::get_digest_len();
223 
224  return (
225  prev_hasher_constraints + next_hasher_constraints +
226  prev_authentication_path_constraints + prev_propagator_constraints +
227  next_propagator_constraints + check_next_root_constraints +
228  aux_equality_constraints);
229 }
230 
231 template<typename FieldT, typename HashT>
232 void test_merkle_tree_check_update_gadget()
233 {
234  /* prepare test */
235  const size_t digest_len = HashT::get_digest_len();
236 
237  const size_t tree_depth = 16;
238  std::vector<merkle_authentication_node> prev_path(tree_depth);
239 
240  libff::bit_vector prev_load_hash(digest_len);
241  std::generate(prev_load_hash.begin(), prev_load_hash.end(), [&]() {
242  return std::rand() % 2;
243  });
244  libff::bit_vector prev_store_hash(digest_len);
245  std::generate(prev_store_hash.begin(), prev_store_hash.end(), [&]() {
246  return std::rand() % 2;
247  });
248 
249  libff::bit_vector loaded_leaf = prev_load_hash;
250  libff::bit_vector stored_leaf = prev_store_hash;
251 
252  libff::bit_vector address_bits;
253 
254  size_t address = 0;
255  for (long level = tree_depth - 1; level >= 0; --level) {
256  const bool computed_is_right = (std::rand() % 2);
257  address |= (computed_is_right ? 1ul << (tree_depth - 1 - level) : 0);
258  address_bits.push_back(computed_is_right);
259  libff::bit_vector other(digest_len);
260  std::generate(
261  other.begin(), other.end(), [&]() { return std::rand() % 2; });
262 
263  libff::bit_vector load_block = prev_load_hash;
264  load_block.insert(
265  computed_is_right ? load_block.begin() : load_block.end(),
266  other.begin(),
267  other.end());
268  libff::bit_vector store_block = prev_store_hash;
269  store_block.insert(
270  computed_is_right ? store_block.begin() : store_block.end(),
271  other.begin(),
272  other.end());
273 
274  libff::bit_vector load_h = HashT::get_hash(load_block);
275  libff::bit_vector store_h = HashT::get_hash(store_block);
276 
277  prev_path[level] = other;
278 
279  prev_load_hash = load_h;
280  prev_store_hash = store_h;
281  }
282 
283  libff::bit_vector load_root = prev_load_hash;
284  libff::bit_vector store_root = prev_store_hash;
285 
286  /* execute the test */
287  protoboard<FieldT> pb;
288  pb_variable_array<FieldT> address_bits_va;
289  address_bits_va.allocate(pb, tree_depth, "address_bits");
290  digest_variable<FieldT> prev_leaf_digest(
291  pb, digest_len, "prev_leaf_digest");
292  digest_variable<FieldT> prev_root_digest(
293  pb, digest_len, "prev_root_digest");
294  merkle_authentication_path_variable<FieldT, HashT> prev_path_var(
295  pb, tree_depth, "prev_path_var");
296  digest_variable<FieldT> next_leaf_digest(
297  pb, digest_len, "next_leaf_digest");
298  digest_variable<FieldT> next_root_digest(
299  pb, digest_len, "next_root_digest");
300  merkle_authentication_path_variable<FieldT, HashT> next_path_var(
301  pb, tree_depth, "next_path_var");
302  merkle_tree_check_update_gadget<FieldT, HashT> mls(
303  pb,
304  tree_depth,
305  address_bits_va,
306  prev_leaf_digest,
307  prev_root_digest,
308  prev_path_var,
309  next_leaf_digest,
310  next_root_digest,
311  next_path_var,
312  ONE,
313  "mls");
314 
315  prev_path_var.generate_r1cs_constraints();
316  mls.generate_r1cs_constraints();
317 
318  address_bits_va.fill_with_bits(pb, address_bits);
319  assert(
320  address_bits_va.get_field_element_from_bits(pb).as_ulong() == address);
321  prev_leaf_digest.generate_r1cs_witness(loaded_leaf);
322  prev_path_var.generate_r1cs_witness(address, prev_path);
323  next_leaf_digest.generate_r1cs_witness(stored_leaf);
324  address_bits_va.fill_with_bits(pb, address_bits);
325  mls.generate_r1cs_witness();
326 
327  /* make sure that update check will check for the right things */
328  prev_leaf_digest.generate_r1cs_witness(loaded_leaf);
329  next_leaf_digest.generate_r1cs_witness(stored_leaf);
330  prev_root_digest.generate_r1cs_witness(load_root);
331  next_root_digest.generate_r1cs_witness(store_root);
332  address_bits_va.fill_with_bits(pb, address_bits);
333  assert(pb.is_satisfied());
334 
335 #ifndef NDEBUG
336  const size_t num_constraints = pb.num_constraints();
337  const size_t expected_constraints =
338  merkle_tree_check_update_gadget<FieldT, HashT>::expected_constraints(
339  tree_depth);
340 #endif
341  assert(num_constraints == expected_constraints);
342 }
343 
344 } // namespace libsnark
345 
346 #endif // MERKLE_TREE_CHECK_UPDATE_GADGET_TCC_