2 *****************************************************************************
4 Implementation of interfaces for the Merkle tree check update gadget.
6 See merkle_tree_check_update_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 MERKLE_TREE_CHECK_UPDATE_GADGET_TCC_
15 #define MERKLE_TREE_CHECK_UPDATE_GADGET_TCC_
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)
45 assert(tree_depth > 0);
46 assert(tree_depth == address_bits.size());
48 for (size_t i = 0; i < tree_depth - 1; ++i) {
49 prev_internal_output.emplace_back(digest_variable<FieldT>(
52 FMT(this->annotation_prefix, " prev_internal_output_%zu", i)));
53 next_internal_output.emplace_back(digest_variable<FieldT>(
56 FMT(this->annotation_prefix, " next_internal_output_%zu", i)));
59 computed_next_root.reset(new digest_variable<FieldT>(
60 pb, digest_size, FMT(this->annotation_prefix, " computed_root")));
62 for (size_t i = 0; i < tree_depth; ++i) {
63 block_variable<FieldT> prev_inp(
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(
73 (i == 0 ? prev_root_digest : prev_internal_output[i - 1]),
74 FMT(this->annotation_prefix, " prev_hashers_%zu", i)));
76 block_variable<FieldT> next_inp(
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(
86 (i == 0 ? *computed_next_root : next_internal_output[i - 1]),
87 FMT(this->annotation_prefix, " next_hashers_%zu", i)));
90 for (size_t i = 0; i < tree_depth; ++i) {
91 prev_propagators.emplace_back(digest_selector_gadget<FieldT>(
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>(
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)));
109 check_next_root.reset(new bit_vector_copy_gadget<FieldT>(
111 computed_next_root->bits,
112 next_root_digest.bits,
115 FMT(annotation_prefix, " check_next_root")));
118 template<typename FieldT, typename HashT>
119 void merkle_tree_check_update_gadget<FieldT, HashT>::generate_r1cs_constraints()
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);
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();
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) {
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
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));
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.
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.
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. */
169 check_next_root->generate_r1cs_constraints(false, false);
172 template<typename FieldT, typename HashT>
173 void merkle_tree_check_update_gadget<FieldT, HashT>::generate_r1cs_witness()
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());
182 next_path.right_digests[i].generate_r1cs_witness(
183 prev_path.right_digests[i].get_digest());
186 /* propagate previous input */
187 prev_propagators[i].generate_r1cs_witness();
188 next_propagators[i].generate_r1cs_witness();
191 prev_hashers[i].generate_r1cs_witness();
192 next_hashers[i].generate_r1cs_witness();
195 check_next_root->generate_r1cs_witness();
198 template<typename FieldT, typename HashT>
199 size_t merkle_tree_check_update_gadget<FieldT, HashT>::root_size_in_bits()
201 return HashT::get_digest_len();
204 template<typename FieldT, typename HashT>
205 size_t merkle_tree_check_update_gadget<FieldT, HashT>::expected_constraints(
206 const size_t tree_depth)
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();
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);
231 template<typename FieldT, typename HashT>
232 void test_merkle_tree_check_update_gadget()
235 const size_t digest_len = HashT::get_digest_len();
237 const size_t tree_depth = 16;
238 std::vector<merkle_authentication_node> prev_path(tree_depth);
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;
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;
249 libff::bit_vector loaded_leaf = prev_load_hash;
250 libff::bit_vector stored_leaf = prev_store_hash;
252 libff::bit_vector address_bits;
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);
261 other.begin(), other.end(), [&]() { return std::rand() % 2; });
263 libff::bit_vector load_block = prev_load_hash;
265 computed_is_right ? load_block.begin() : load_block.end(),
268 libff::bit_vector store_block = prev_store_hash;
270 computed_is_right ? store_block.begin() : store_block.end(),
274 libff::bit_vector load_h = HashT::get_hash(load_block);
275 libff::bit_vector store_h = HashT::get_hash(store_block);
277 prev_path[level] = other;
279 prev_load_hash = load_h;
280 prev_store_hash = store_h;
283 libff::bit_vector load_root = prev_load_hash;
284 libff::bit_vector store_root = prev_store_hash;
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(
315 prev_path_var.generate_r1cs_constraints();
316 mls.generate_r1cs_constraints();
318 address_bits_va.fill_with_bits(pb, address_bits);
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();
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());
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(
341 assert(num_constraints == expected_constraints);
344 } // namespace libsnark
346 #endif // MERKLE_TREE_CHECK_UPDATE_GADGET_TCC_