Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
indexed_memory_tree.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cstddef>
4
7
8namespace bb::avm2::simulation {
9
10template <typename LeafType, typename HashingPolicy> class IndexedMemoryTree {
11 public:
12 IndexedMemoryTree(size_t depth, size_t num_default_values);
13 IndexedMemoryTree(size_t depth, std::span<IndexedLeaf<LeafType>> initial_leaves);
14
16
17 IndexedLeaf<LeafType> get_leaf_preimage(size_t leaf_index) const;
18
19 SiblingPath get_sibling_path(size_t leaf_index) const;
20
22
24
25 private:
27
29 size_t depth;
30 size_t max_leaves;
32};
33
34template <typename LeafType, typename HashingPolicy>
36 : tree(depth)
37 , depth(depth)
38 , max_leaves(1 << depth)
39{
40 // We need to create the tree inserting the prefill values. Indexed trees need some leaves to exist from the start
41 // in order to be able to provide insertion proofs. Users can customize how many default leaves they want the tree
42 // to start with, but there must be at least one.
43 BB_ASSERT_GT(num_default_values, static_cast<size_t>(0), "Number of default values is not greater than 0");
44
45 std::vector<LeafType> default_leaves;
46 default_leaves.reserve(num_default_values);
47
48 for (size_t i = 0; i < num_default_values; ++i) {
49 // Avoid a completely empty leaf in the default values by adding one to the index.
50 default_leaves.push_back(LeafType::padding(i + 1));
51 }
52
53 leaves.reserve(max_leaves);
54
55 // Compute the pointers for the prefill leaves and insert them in the tree.
56 for (size_t i = 0; i < default_leaves.size(); ++i) {
57 // If it's the last leaf, point to infinity (next_index = 0 and next_key = 0)
58 index_t next_index = i == (default_leaves.size() - 1) ? 0 : i + 1;
59 FF next_key = i == (default_leaves.size() - 1) ? 0 : default_leaves.at(i + 1).get_key();
60
61 IndexedLeaf<LeafType> initial_leaf(default_leaves.at(i), next_index, next_key);
62
63 append_leaf(initial_leaf);
64
65 FF leaf_hash = HashingPolicy::hash(initial_leaf.get_hash_inputs());
66 tree.update_element(i, leaf_hash);
67 }
68}
69
70// This constructor takes an initial set of leaves to insert into the tree. It is assumed that the user is responsible
71// for checking the leaves conform to the requirements of an indexed tree (i.e., one leaf is of zero value, the keys are
72// in insertion order, and that the nextIndex and nextKey values are correctly set).
73template <typename LeafType, typename HashingPolicy>
75 std::span<IndexedLeaf<LeafType>> initial_leaves)
76 : tree(depth)
77 , depth(depth)
78 , max_leaves(1 << depth)
79{
80 // It is assumed that you have included any prefill values as part of the initial_leaves. Remember indexed trees
81 // need at least 1 prefill leaf (with value 0) in order to work
82 BB_ASSERT_GT(initial_leaves.size(), static_cast<size_t>(0), "Initial leaves size is not greater than 0");
83
84 // Compute the pointers for the prefill leaves and insert them in the tree.
85 for (size_t i = 0; i < initial_leaves.size(); ++i) {
86 auto& leaf = initial_leaves[i];
87 append_leaf(leaf);
88 FF leaf_hash = HashingPolicy::hash(leaf.get_hash_inputs());
89 tree.update_element(i, leaf_hash);
90 }
91
92 leaves.assign(initial_leaves.begin(), initial_leaves.end());
94}
95
96template <typename LeafType, typename HashingPolicy>
98{
99 uint256_t key_integer = static_cast<uint256_t>(key);
100 uint256_t low_key_integer = 0;
101 size_t low_index = 0;
102 // Linear search for the low indexed leaf.
103 for (size_t i = 0; i < leaves.size(); ++i) {
104 uint256_t leaf_key_integer = static_cast<uint256_t>(leaves.at(i).leaf.get_key());
105 if (leaf_key_integer == key_integer) {
106 return GetLowIndexedLeafResponse(true, i);
107 }
108 if (leaf_key_integer < key_integer && leaf_key_integer > low_key_integer) {
109 low_key_integer = leaf_key_integer;
110 low_index = i;
111 }
112 }
113
114 return GetLowIndexedLeafResponse(false, low_index);
115}
116
117template <typename LeafType, typename HashingPolicy>
119{
120 BB_ASSERT_DEBUG(leaf_index < leaves.size(), "Leaf index out of bounds");
121 return leaves.at(leaf_index);
122}
123
124template <typename LeafType, typename HashingPolicy>
126{
127 return tree.get_sibling_path(leaf_index);
128}
129
130template <typename LeafType, typename HashingPolicy>
132{
134 .root = tree.root(),
135 .next_available_leaf_index = leaves.size(),
136 };
137}
138
139template <typename LeafType, typename HashingPolicy>
141 std::span<const LeafType> leaves_to_insert)
142{
144 result.insertion_witness_data.reserve(leaves_to_insert.size());
145 result.low_leaf_witness_data.reserve(leaves_to_insert.size());
146
147 for (const auto& leaf_to_insert : leaves_to_insert) {
148 FF key = leaf_to_insert.get_key();
149 GetLowIndexedLeafResponse find_low_leaf_result = get_low_indexed_leaf(key);
150 IndexedLeaf<LeafType>& low_leaf = leaves.at(find_low_leaf_result.index);
151
153 low_leaf, find_low_leaf_result.index, tree.get_sibling_path(find_low_leaf_result.index)));
154
155 if (!find_low_leaf_result.is_already_present) {
156 // If the leaf is not already present, we point the low leaf to the new leaf and then insert the new leaf.
157 IndexedLeaf<LeafType> new_indexed_leaf(leaf_to_insert, low_leaf.nextIndex, low_leaf.nextKey);
158 index_t insertion_index = leaves.size();
159
160 low_leaf.nextIndex = insertion_index;
162 FF low_leaf_hash = HashingPolicy::hash(low_leaf.get_hash_inputs());
163 tree.update_element(find_low_leaf_result.index, low_leaf_hash);
164
165 append_leaf(new_indexed_leaf);
166 FF new_leaf_hash = HashingPolicy::hash(new_indexed_leaf.get_hash_inputs());
167 tree.update_element(insertion_index, new_leaf_hash);
168
170 new_indexed_leaf, insertion_index, tree.get_sibling_path(insertion_index)));
171
172 } else if (LeafType::is_updateable()) {
173 // Update the current leaf's value, don't change it's link
175 FF low_leaf_hash = HashingPolicy::hash(low_leaf.get_hash_inputs());
176 tree.update_element(find_low_leaf_result.index, low_leaf_hash);
177
178 // Push an empty insertion witness
179 result.insertion_witness_data.push_back(
181 } else {
182 throw std::runtime_error("Leaf is not updateable");
183 }
184 }
185
186 return result;
187}
188
189template <typename LeafType, typename HashingPolicy>
191{
192 if (leaves.size() == max_leaves) {
193 throw std::runtime_error("IndexedMemoryTree is full");
194 }
195
196 leaves.push_back(leaf);
197}
198
199} // namespace bb::avm2::simulation
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:123
#define BB_ASSERT_DEBUG(expression,...)
Definition assert.hpp:55
AppendOnlyTreeSnapshot get_snapshot() const
SequentialInsertionResult< LeafType > insert_indexed_leaves(std::span< const LeafType > leaves)
IndexedMemoryTree(size_t depth, std::span< IndexedLeaf< LeafType > > initial_leaves)
IndexedMemoryTree(size_t depth, size_t num_default_values)
void append_leaf(const IndexedLeaf< LeafType > &leaf)
GetLowIndexedLeafResponse get_low_indexed_leaf(const FF &key) const
std::vector< IndexedLeaf< LeafType > > leaves
IndexedLeaf< LeafType > get_leaf_preimage(size_t leaf_index) const
SiblingPath get_sibling_path(size_t leaf_index) const
NullifierTreeLeafPreimage low_leaf
::bb::crypto::merkle_tree::fr_sibling_path SiblingPath
Definition db.hpp:36
::bb::crypto::merkle_tree::index_t index_t
Definition db.hpp:37
AvmFlavorSettings::FF FF
Definition field.hpp:10
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::vector< fr > get_hash_inputs() const
std::vector< crypto::merkle_tree::LeafUpdateWitnessData< LeafValueType > > low_leaf_witness_data
std::vector< crypto::merkle_tree::LeafUpdateWitnessData< LeafValueType > > insertion_witness_data