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
15
16 IndexedLeaf<LeafType> get_leaf_preimage(size_t leaf_index) const;
17
18 SiblingPath get_sibling_path(size_t leaf_index) const;
19
21
23
24 private:
25 void append_leaf(const IndexedLeaf<LeafType>& leaf);
26
28 size_t depth;
29 size_t max_leaves;
31};
32
33template <typename LeafType, typename HashingPolicy>
35 : tree(depth)
36 , depth(depth)
37 , max_leaves(1 << depth)
38{
39 // We need to create the tree inserting the prefill values. Indexed trees need some leaves to exist from the start
40 // in order to be able to provide insertion proofs. Users can customize how many default leaves they want the tree
41 // to start with, but there must be at least one.
42 assert(num_default_values > 0);
43
44 std::vector<LeafType> default_leaves;
45 default_leaves.reserve(num_default_values);
46
47 for (size_t i = 0; i < num_default_values; ++i) {
48 // Avoid a completely empty leaf in the default values by adding one to the index.
49 default_leaves.push_back(LeafType::padding(i + 1));
50 }
51
52 leaves.reserve(max_leaves);
53
54 // Compute the pointers for the prefill leaves and insert them in the tree.
55 for (size_t i = 0; i < default_leaves.size(); ++i) {
56 // If it's the last leaf, point to infinity (next_index = 0 and next_key = 0)
57 index_t next_index = i == (default_leaves.size() - 1) ? 0 : i + 1;
58 FF next_key = i == (default_leaves.size() - 1) ? 0 : default_leaves.at(i + 1).get_key();
59
60 IndexedLeaf<LeafType> initial_leaf(default_leaves.at(i), next_index, next_key);
61
62 append_leaf(initial_leaf);
63
64 FF leaf_hash = HashingPolicy::hash(initial_leaf.get_hash_inputs());
65 tree.update_element(i, leaf_hash);
66 }
67}
68
69template <typename LeafType, typename HashingPolicy>
71{
72 uint256_t key_integer = static_cast<uint256_t>(key);
73 uint256_t low_key_integer = 0;
74 size_t low_index = 0;
75 // Linear search for the low indexed leaf.
76 for (size_t i = 0; i < leaves.size(); ++i) {
77 uint256_t leaf_key_integer = static_cast<uint256_t>(leaves.at(i).leaf.get_key());
78 if (leaf_key_integer == key_integer) {
79 return GetLowIndexedLeafResponse(true, i);
80 }
81 if (leaf_key_integer < key_integer && leaf_key_integer > low_key_integer) {
82 low_key_integer = leaf_key_integer;
83 low_index = i;
84 }
85 }
86
87 return GetLowIndexedLeafResponse(false, low_index);
88}
89
90template <typename LeafType, typename HashingPolicy>
92{
93 return leaves.at(leaf_index);
94}
95
96template <typename LeafType, typename HashingPolicy>
98{
99 return tree.get_sibling_path(leaf_index);
100}
101
102template <typename LeafType, typename HashingPolicy>
104{
106 .root = tree.root(),
107 .nextAvailableLeafIndex = leaves.size(),
108 };
109}
110
111template <typename LeafType, typename HashingPolicy>
113 std::span<const LeafType> leaves_to_insert)
114{
116 result.insertion_witness_data.reserve(leaves_to_insert.size());
117 result.low_leaf_witness_data.reserve(leaves_to_insert.size());
118
119 for (const auto& leaf_to_insert : leaves_to_insert) {
120 FF key = leaf_to_insert.get_key();
121 GetLowIndexedLeafResponse find_low_leaf_result = get_low_indexed_leaf(key);
122 IndexedLeaf<LeafType>& low_leaf = leaves.at(find_low_leaf_result.index);
123
125 low_leaf, find_low_leaf_result.index, tree.get_sibling_path(find_low_leaf_result.index)));
126
127 if (!find_low_leaf_result.is_already_present) {
128 // If the leaf is not already present, we point the low leaf to the new leaf and then insert the new leaf.
129 IndexedLeaf<LeafType> new_indexed_leaf(leaf_to_insert, low_leaf.nextIndex, low_leaf.nextKey);
130 index_t insertion_index = leaves.size();
131
132 low_leaf.nextIndex = insertion_index;
134 FF low_leaf_hash = HashingPolicy::hash(low_leaf.get_hash_inputs());
135 tree.update_element(find_low_leaf_result.index, low_leaf_hash);
136
137 append_leaf(new_indexed_leaf);
138 FF new_leaf_hash = HashingPolicy::hash(new_indexed_leaf.get_hash_inputs());
139 tree.update_element(insertion_index, new_leaf_hash);
140
142 new_indexed_leaf, insertion_index, tree.get_sibling_path(insertion_index)));
143
144 } else if (LeafType::is_updateable()) {
145 // Update the current leaf's value, don't change it's link
147 FF low_leaf_hash = HashingPolicy::hash(low_leaf.get_hash_inputs());
148 tree.update_element(find_low_leaf_result.index, low_leaf_hash);
149
150 // Push an empty insertion witness
151 result.insertion_witness_data.push_back(
153 } else {
154 throw std::runtime_error("Leaf is not updateable");
155 }
156 }
157
158 return result;
159}
160
161template <typename LeafType, typename HashingPolicy>
163{
164 if (leaves.size() == max_leaves) {
165 throw std::runtime_error("IndexedMemoryTree is full");
166 }
167
168 leaves.push_back(leaf);
169}
170
171} // namespace bb::avm2::simulation
AppendOnlyTreeSnapshot get_snapshot() const
SequentialInsertionResult< LeafType > insert_indexed_leaves(std::span< const LeafType > 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
::bb::crypto::merkle_tree::index_t index_t
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