2 *****************************************************************************
4 Implementation of interfaces for the Merkle tree check read.
6 See merkle_tree_check_read_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_READ_GADGET_TCC_
15 #define MERKLE_TREE_CHECK_READ_GADGET_TCC_
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)
37 , read_successful(read_successful)
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.
48 assert(tree_depth > 0);
49 assert(tree_depth == address_bits.size());
51 for (size_t i = 0; i < tree_depth - 1; ++i) {
52 internal_output.emplace_back(digest_variable<FieldT>(
55 FMT(this->annotation_prefix, " internal_output_%zu", i)));
58 computed_root.reset(new digest_variable<FieldT>(
59 pb, digest_size, FMT(this->annotation_prefix, " computed_root")));
61 for (size_t i = 0; i < tree_depth; ++i) {
62 block_variable<FieldT> inp(
65 path.right_digests[i],
66 FMT(this->annotation_prefix, " inp_%zu", i));
67 hasher_inputs.emplace_back(inp);
68 hashers.emplace_back(HashT(
72 (i == 0 ? *computed_root : internal_output[i - 1]),
73 FMT(this->annotation_prefix, " load_hashers_%zu", i)));
76 for (size_t i = 0; i < tree_depth; ++i) {
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.
82 propagators.emplace_back(digest_selector_gadget<FieldT>(
85 i < tree_depth - 1 ? internal_output[i] : leaf,
86 address_bits[tree_depth - 1 - i],
88 path.right_digests[i],
89 FMT(this->annotation_prefix, " digest_selector_%zu", i)));
92 check_root.reset(new bit_vector_copy_gadget<FieldT>(
98 FMT(annotation_prefix, " check_root")));
101 template<typename FieldT, typename HashT>
102 void merkle_tree_check_read_gadget<FieldT, HashT>::generate_r1cs_constraints()
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);
112 /* ensure consistency of path.left_digests/path.right_digests with
114 for (size_t i = 0; i < tree_depth; ++i) {
115 propagators[i].generate_r1cs_constraints();
118 check_root->generate_r1cs_constraints(false, false);
121 template<typename FieldT, typename HashT>
122 void merkle_tree_check_read_gadget<FieldT, HashT>::generate_r1cs_witness()
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();
130 hashers[i].generate_r1cs_witness();
133 check_root->generate_r1cs_witness();
136 template<typename FieldT, typename HashT>
137 size_t merkle_tree_check_read_gadget<FieldT, HashT>::root_size_in_bits()
139 return HashT::get_digest_len();
142 template<typename FieldT, typename HashT>
143 size_t merkle_tree_check_read_gadget<FieldT, HashT>::expected_constraints(
144 const size_t tree_depth)
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());
155 return hasher_constraints + propagator_constraints +
156 authentication_path_constraints + check_root_constraints;
159 template<typename FieldT, typename HashT>
160 void test_merkle_tree_check_read_gadget()
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);
167 libff::bit_vector prev_hash(digest_len);
169 prev_hash.begin(), prev_hash.end(), [&]() { return std::rand() % 2; });
170 libff::bit_vector leaf = prev_hash;
172 libff::bit_vector address_bits;
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);
181 other.begin(), other.end(), [&]() { return std::rand() % 2; });
183 libff::bit_vector block = prev_hash;
185 computed_is_right ? block.begin() : block.end(),
188 libff::bit_vector h = HashT::get_hash(block);
194 libff::bit_vector root = prev_hash;
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(
214 path_var.generate_r1cs_constraints();
215 ml.generate_r1cs_constraints();
217 address_bits_va.fill_with_bits(pb, address_bits);
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();
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());
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(
236 assert(num_constraints == expected_constraints);
239 } // namespace libsnark
241 #endif // MERKLE_TREE_CHECK_READ_GADGET_TCC_