check_update.hpp
Go to the documentation of this file.
1 //---------------------------------------------------------------------------//
2 // Copyright (c) 2018-2021 Mikhail Komarov <nemo@nil.foundation>
3 // Copyright (c) 2020-2021 Nikita Kaskov <nbering@nil.foundation>
4 //
5 // MIT License
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a copy
8 // of this software and associated documentation files (the "Software"), to deal
9 // in the Software without restriction, including without limitation the rights
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 // copies of the Software, and to permit persons to whom the Software is
12 // furnished to do so, subject to the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included in all
15 // copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 // SOFTWARE.
24 //---------------------------------------------------------------------------//
25 // @file Declaration of interfaces for the Merkle tree check update component.
26 //
27 // The component checks the following: given two roots R1 and R2, address A, two
28 // values V1 and V2, and authentication path P, check that
29 // - P is a valid authentication path for the value V1 as the A-th leaf in a Merkle tree with root R1, and
30 // - P is a valid authentication path for the value V2 as the A-th leaf in a Merkle tree with root R2.
31 //---------------------------------------------------------------------------//
32 
33 #ifndef CRYPTO3_ZK_BLUEPRINT_MERKLE_TREE_CHECK_UPDATE_COMPONENT_HPP
34 #define CRYPTO3_ZK_BLUEPRINT_MERKLE_TREE_CHECK_UPDATE_COMPONENT_HPP
35 
42 
43 namespace nil {
44  namespace crypto3 {
45  namespace zk {
46  namespace components {
47 
48  template<typename FieldType, typename Hash>
49  class merkle_tree_check_update_components : public component<FieldType> {
50 
51  std::vector<Hash> prev_hashers;
52  std::vector<block_variable<FieldType>> prev_hasher_inputs;
53  std::vector<digest_selector_component<FieldType>> prev_propagators;
54  std::vector<digest_variable<FieldType>> prev_internal_output;
55 
56  std::vector<Hash> next_hashers;
57  std::vector<block_variable<FieldType>> next_hasher_inputs;
58  std::vector<digest_selector_component<FieldType>> next_propagators;
59  std::vector<digest_variable<FieldType>> next_internal_output;
60 
61  std::shared_ptr<digest_variable<FieldType>> computed_next_root;
62  std::shared_ptr<bit_vector_copy_component<FieldType>> check_next_root;
63 
64  public:
65  const std::size_t digest_size;
66  const std::size_t tree_depth;
67 
76 
77  /* Note that while it is necessary to generate R1CS constraints
78  for prev_path, it is not necessary to do so for next_path. See
79  comment in the implementation of generate_r1cs_constraints() */
80 
83  const std::size_t tree_depth,
92  component<FieldType>(bp),
93  digest_size(Hash::get_digest_len()), tree_depth(tree_depth), address_bits(address_bits),
97  assert(tree_depth > 0);
98  assert(tree_depth == address_bits.size());
99 
100  for (std::size_t i = 0; i < tree_depth - 1; ++i) {
101  prev_internal_output.emplace_back(digest_variable<FieldType>(bp, digest_size));
102  next_internal_output.emplace_back(digest_variable<FieldType>(bp, digest_size));
103  }
104 
105  computed_next_root.reset(new digest_variable<FieldType>(bp, digest_size));
106 
107  for (std::size_t i = 0; i < tree_depth; ++i) {
108  block_variable<FieldType> prev_inp(bp, prev_path.left_digests[i],
109  prev_path.right_digests[i]);
110  prev_hasher_inputs.emplace_back(prev_inp);
111  prev_hashers.emplace_back(Hash(bp, 2 * digest_size, prev_inp,
112  (i == 0 ? prev_root_digest : prev_internal_output[i - 1])));
113 
114  block_variable<FieldType> next_inp(bp, next_path.left_digests[i],
115  next_path.right_digests[i]);
116  next_hasher_inputs.emplace_back(next_inp);
117  next_hashers.emplace_back(
118  Hash(bp, 2 * digest_size, next_inp,
119  (i == 0 ? *computed_next_root : next_internal_output[i - 1])));
120  }
121 
122  for (std::size_t i = 0; i < tree_depth; ++i) {
123  prev_propagators.emplace_back(digest_selector_component<FieldType>(
124  bp, digest_size, i < tree_depth - 1 ? prev_internal_output[i] : prev_leaf_digest,
125  address_bits[tree_depth - 1 - i], prev_path.left_digests[i],
126  prev_path.right_digests[i]));
127  next_propagators.emplace_back(digest_selector_component<FieldType>(
128  bp, digest_size, i < tree_depth - 1 ? next_internal_output[i] : next_leaf_digest,
129  address_bits[tree_depth - 1 - i], next_path.left_digests[i],
130  next_path.right_digests[i]));
131  }
132 
133  check_next_root.reset(new bit_vector_copy_component<FieldType>(
134  bp, computed_next_root->bits, next_root_digest.bits, update_successful,
135  FieldType::capacity()));
136  }
137 
139  /* ensure correct hash computations */
140  for (std::size_t i = 0; i < tree_depth; ++i) {
141  prev_hashers[i].generate_r1cs_constraints(
142  false); // we check root outside and prev_left/prev_right above
143  next_hashers[i].generate_r1cs_constraints(
144  true); // however we must check right side hashes
145  }
146 
147  /* ensure consistency of internal_left/internal_right with internal_output */
148  for (std::size_t i = 0; i < tree_depth; ++i) {
149  prev_propagators[i].generate_r1cs_constraints();
150  next_propagators[i].generate_r1cs_constraints();
151  }
152 
153  /* ensure that prev auxiliary input and next auxiliary input match */
154  for (std::size_t i = 0; i < tree_depth; ++i) {
155  for (std::size_t j = 0; j < digest_size; ++j) {
156  /*
157  addr * (prev_left - next_left) + (1 - addr) * (prev_right - next_right) = 0
158  addr * (prev_left - next_left - prev_right + next_right) = next_right - prev_right
159  */
160  this->bp.add_r1cs_constraint(snark::r1cs_constraint<FieldType>(
161  address_bits[tree_depth - 1 - i],
162  prev_path.left_digests[i].bits[j] - next_path.left_digests[i].bits[j] -
163  prev_path.right_digests[i].bits[j] + next_path.right_digests[i].bits[j],
164  next_path.right_digests[i].bits[j] - prev_path.right_digests[i].bits[j]));
165  }
166  }
167 
168  /* Note that while it is necessary to generate R1CS constraints
169  for prev_path, it is not necessary to do so for next_path.
170 
171  This holds, because { next_path.left_inputs[i],
172  next_path.right_inputs[i] } is a pair { hash_output,
173  auxiliary_input }. The bitness for hash_output is enforced
174  above by next_hashers[i].generate_r1cs_constraints.
175 
176  Because auxiliary input is the same for prev_path and next_path
177  (enforced above), we have that auxiliary_input part is also
178  constrained to be boolean, because prev_path is *all*
179  constrained to be all boolean. */
180 
181  check_next_root->generate_r1cs_constraints(false, false);
182  }
183 
185  /* do the hash computations bottom-up */
186  for (int i = tree_depth - 1; i >= 0; --i) {
187  /* ensure consistency of prev_path and next_path */
188  if (this->bp.val(address_bits[tree_depth - 1 - i]) == FieldType::value_type::zero()) {
189  next_path.left_digests[i].generate_r1cs_witness(prev_path.left_digests[i].get_digest());
190  } else {
191  next_path.right_digests[i].generate_r1cs_witness(
192  prev_path.right_digests[i].get_digest());
193  }
194 
195  /* propagate previous input */
196  prev_propagators[i].generate_r1cs_witness();
197  next_propagators[i].generate_r1cs_witness();
198 
199  /* compute hash */
200  prev_hashers[i].generate_r1cs_witness();
201  next_hashers[i].generate_r1cs_witness();
202  }
203 
204  check_next_root->generate_r1cs_witness();
205  }
206 
207  static std::size_t root_size_in_bits() {
208  return Hash::get_digest_len();
209  }
210  /* for debugging purposes */
211  static std::size_t expected_constraints(const std::size_t tree_depth) {
212  /* NB: this includes path constraints */
213  const std::size_t prev_hasher_constraints = tree_depth * Hash::expected_constraints(false);
214  const std::size_t next_hasher_constraints = tree_depth * Hash::expected_constraints(true);
215  const std::size_t prev_authentication_path_constraints =
216  2 * tree_depth * Hash::get_digest_len();
217  const std::size_t prev_propagator_constraints = tree_depth * Hash::get_digest_len();
218  const std::size_t next_propagator_constraints = tree_depth * Hash::get_digest_len();
219  const std::size_t check_next_root_constraints =
220  3 * (Hash::get_digest_len() + (FieldType::capacity()) - 1) / FieldType::capacity();
221  const std::size_t aux_equality_constraints = tree_depth * Hash::get_digest_len();
222 
223  return (prev_hasher_constraints + next_hasher_constraints +
224  prev_authentication_path_constraints + prev_propagator_constraints +
225  next_propagator_constraints + check_next_root_constraints + aux_equality_constraints);
226  }
227  };
228 
229  } // namespace components
230  } // namespace zk
231  } // namespace crypto3
232 } // namespace nil
233 
234 #endif // CRYPTO3_ZK_BLUEPRINT_MERKLE_TREE_CHECK_UPDATE_COMPONENT_HPP
Definition: blueprint_linear_combination.hpp:47
Definition: blueprint_variable.hpp:57
Definition: blueprint.hpp:46
Definition: component.hpp:37
blueprint< FieldType > & bp
Definition: component.hpp:39
Definition: digest_selector_component.hpp:39
digest_variable< FieldType > prev_root_digest
Definition: check_update.hpp:70
static std::size_t expected_constraints(const std::size_t tree_depth)
Definition: check_update.hpp:211
digest_variable< FieldType > next_leaf_digest
Definition: check_update.hpp:72
static std::size_t root_size_in_bits()
Definition: check_update.hpp:207
blueprint_linear_combination< FieldType > update_successful
Definition: check_update.hpp:75
void generate_r1cs_constraints()
Definition: check_update.hpp:138
digest_variable< FieldType > next_root_digest
Definition: check_update.hpp:73
const std::size_t tree_depth
Definition: check_update.hpp:66
void generate_r1cs_witness()
Definition: check_update.hpp:184
merkle_tree_check_update_components(blueprint< FieldType > &bp, const std::size_t tree_depth, const blueprint_variable_vector< FieldType > &address_bits, const digest_variable< FieldType > &prev_leaf_digest, const digest_variable< FieldType > &prev_root_digest, const merkle_authentication_path_variable< FieldType, Hash > &prev_path, const digest_variable< FieldType > &next_leaf_digest, const digest_variable< FieldType > &next_root_digest, const merkle_authentication_path_variable< FieldType, Hash > &next_path, const blueprint_linear_combination< FieldType > &update_successful)
Definition: check_update.hpp:81
digest_variable< FieldType > prev_leaf_digest
Definition: check_update.hpp:69
const std::size_t digest_size
Definition: check_update.hpp:65
blueprint_variable_vector< FieldType > address_bits
Definition: check_update.hpp:68
merkle_authentication_path_variable< FieldType, Hash > prev_path
Definition: check_update.hpp:71
merkle_authentication_path_variable< FieldType, Hash > next_path
Definition: check_update.hpp:74
Definition: pair.hpp:31