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 assert(num_default_values > 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 assert(initial_leaves.size() > 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 return leaves.at(leaf_index);
121}
122
123template <typename LeafType, typename HashingPolicy>
125{
126 return tree.get_sibling_path(leaf_index);
127}
128
129template <typename LeafType, typename HashingPolicy>
131{
133 .root = tree.root(),
134 .nextAvailableLeafIndex = leaves.size(),
135 };
136}
137
138template <typename LeafType, typename HashingPolicy>
140 std::span<const LeafType> leaves_to_insert)
141{
143 result.insertion_witness_data.reserve(leaves_to_insert.size());
144 result.low_leaf_witness_data.reserve(leaves_to_insert.size());
145
146 for (const auto& leaf_to_insert : leaves_to_insert) {
147 FF key = leaf_to_insert.get_key();
148 GetLowIndexedLeafResponse find_low_leaf_result = get_low_indexed_leaf(key);
149 IndexedLeaf<LeafType>& low_leaf = leaves.at(find_low_leaf_result.index);
150
152 low_leaf, find_low_leaf_result.index, tree.get_sibling_path(find_low_leaf_result.index)));
153
154 if (!find_low_leaf_result.is_already_present) {
155 // If the leaf is not already present, we point the low leaf to the new leaf and then insert the new leaf.
156 IndexedLeaf<LeafType> new_indexed_leaf(leaf_to_insert, low_leaf.nextIndex, low_leaf.nextKey);
157 index_t insertion_index = leaves.size();
158
159 low_leaf.nextIndex = insertion_index;
161 FF low_leaf_hash = HashingPolicy::hash(low_leaf.get_hash_inputs());
162 tree.update_element(find_low_leaf_result.index, low_leaf_hash);
163
164 append_leaf(new_indexed_leaf);
165 FF new_leaf_hash = HashingPolicy::hash(new_indexed_leaf.get_hash_inputs());
166 tree.update_element(insertion_index, new_leaf_hash);
167
169 new_indexed_leaf, insertion_index, tree.get_sibling_path(insertion_index)));
170
171 } else if (LeafType::is_updateable()) {
172 // Update the current leaf's value, don't change it's link
174 FF low_leaf_hash = HashingPolicy::hash(low_leaf.get_hash_inputs());
175 tree.update_element(find_low_leaf_result.index, low_leaf_hash);
176
177 // Push an empty insertion witness
178 result.insertion_witness_data.push_back(
180 } else {
181 throw std::runtime_error("Leaf is not updateable");
182 }
183 }
184
185 return result;
186}
187
188template <typename LeafType, typename HashingPolicy>
190{
191 if (leaves.size() == max_leaves) {
192 throw std::runtime_error("IndexedMemoryTree is full");
193 }
194
195 leaves.push_back(leaf);
196}
197
198} // namespace bb::avm2::simulation
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:27
::bb::crypto::merkle_tree::index_t index_t
Definition db.hpp:28
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