Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
indexed_memory_tree.test.cpp
Go to the documentation of this file.
2
3#include <gmock/gmock.h>
4#include <gtest/gtest.h>
5#include <iostream>
6
10
11namespace bb::avm2::simulation {
12
13namespace {
14
16
17struct LeafValue {
19
20 LeafValue(const FF& key)
21 : key(key)
22 {}
23
24 static bool is_updateable() { return false; }
25
26 bool operator==(LeafValue const& other) const { return key == other.key; }
27
28 fr get_key() const { return key; }
29
30 bool is_empty() const { return key.is_zero(); }
31
32 std::vector<fr> get_hash_inputs(fr nextKey, fr nextIndex) const
33 {
34 return std::vector<fr>({ key, nextKey, nextIndex });
35 }
36
37 static LeafValue empty() { return { fr::zero() }; }
38
39 static LeafValue padding(index_t i) { return { i }; }
40
41 static std::string name() { return "LeafValue"; }
42
43 [[maybe_unused]] friend std::ostream& operator<<(std::ostream& os, const LeafValue& v)
44 {
45 os << "key = " << v.key;
46 return os;
47 }
48};
49
50struct UpdatableLeafValue {
51 FF key;
53
54 UpdatableLeafValue(const FF& key, const FF& value)
55 : key(key)
56 , value(value)
57 {}
58
59 static bool is_updateable() { return true; }
60
61 bool operator==(UpdatableLeafValue const& other) const { return key == other.key && value == other.value; }
62
63 fr get_key() const { return key; }
64
65 bool is_empty() const { return key.is_zero() && value.is_zero(); }
66
67 std::vector<fr> get_hash_inputs(fr nextKey, fr nextIndex) const
68 {
69 return std::vector<fr>({ key, value, nextKey, nextIndex });
70 }
71
72 static UpdatableLeafValue empty() { return { fr::zero(), fr::zero() }; }
73
74 static UpdatableLeafValue padding(index_t i) { return { i, fr::zero() }; }
75
76 static std::string name() { return "UpdatableLeafValue"; }
77
78 [[maybe_unused]] friend std::ostream& operator<<(std::ostream& os, const UpdatableLeafValue& v)
79 {
80 os << "key = " << v.key << " : value = " << v.value;
81 return os;
82 }
83};
84
85using Tree = IndexedMemoryTree<LeafValue, Poseidon2HashPolicy>;
86using UpdatableTree = IndexedMemoryTree<UpdatableLeafValue, Poseidon2HashPolicy>;
87
88TEST(IndexedMemoryTree, Append)
89{
90 Tree tree(5, 1);
91 auto prev_snapshot = tree.get_snapshot();
92
93 LeafValue leaf(42);
94 auto result = tree.insert_indexed_leaves({ { leaf } });
95
96 auto snapshot_after = tree.get_snapshot();
97
98 EXPECT_EQ(result.insertion_witness_data.size(), 1);
99 EXPECT_EQ(result.low_leaf_witness_data.size(), 1);
100
101 LeafUpdateWitnessData<LeafValue> low_leaf_witness_data = result.low_leaf_witness_data[0];
102 LeafUpdateWitnessData<LeafValue> insertion_witness_data = result.insertion_witness_data[0];
103
104 // Low leaf is the prefill
105 EXPECT_EQ(low_leaf_witness_data.index, 0);
106
107 // Memberhsip check old padding leaf
108 IndexedLeaf<LeafValue> padding_leaf(LeafValue::padding(1), 0, 0);
109 EXPECT_EQ(low_leaf_witness_data.leaf, padding_leaf);
110
111 EXPECT_EQ(
112 unconstrained_root_from_path(Poseidon2::hash(padding_leaf.get_hash_inputs()), 0, low_leaf_witness_data.path),
113 prev_snapshot.root);
114
115 // Reconstruct intermediate root:
116 padding_leaf.nextIndex = 1;
117 padding_leaf.nextKey = leaf.key;
118
119 auto intermediate_root =
120 unconstrained_root_from_path(Poseidon2::hash(padding_leaf.get_hash_inputs()), 0, low_leaf_witness_data.path);
121
122 // Insertion leaf should be the new leaf
123
124 // Membership check a zero at the insertion index
125 EXPECT_EQ(unconstrained_root_from_path(0, 1, insertion_witness_data.path), intermediate_root);
126
127 IndexedLeaf<LeafValue> inserted_leaf(leaf, 0, 0);
128 EXPECT_EQ(insertion_witness_data.leaf, inserted_leaf);
129
130 auto final_root =
131 unconstrained_root_from_path(Poseidon2::hash(inserted_leaf.get_hash_inputs()), 1, insertion_witness_data.path);
132
133 EXPECT_EQ(snapshot_after.root, final_root);
134 EXPECT_EQ(snapshot_after.nextAvailableLeafIndex, 2);
135}
136
137TEST(IndexedMemoryTree, Update)
138{
139 UpdatableTree tree(5, 1);
140 auto prev_snapshot = tree.get_snapshot();
141
142 UpdatableLeafValue leaf(1, 43);
143 auto result = tree.insert_indexed_leaves({ { leaf } });
144
145 auto snapshot_after = tree.get_snapshot();
146
147 EXPECT_EQ(result.insertion_witness_data.size(), 1);
148 EXPECT_EQ(result.low_leaf_witness_data.size(), 1);
149
150 LeafUpdateWitnessData<UpdatableLeafValue> low_leaf_witness_data = result.low_leaf_witness_data[0];
151 LeafUpdateWitnessData<UpdatableLeafValue> insertion_witness_data = result.insertion_witness_data[0];
152
153 // Low leaf is the prefill
154 EXPECT_EQ(low_leaf_witness_data.index, 0);
155
156 // Memberhsip check old padding leaf
157 IndexedLeaf<UpdatableLeafValue> padding_leaf(UpdatableLeafValue::padding(1), 0, 0);
158 EXPECT_EQ(low_leaf_witness_data.leaf, padding_leaf);
159
160 EXPECT_EQ(
161 unconstrained_root_from_path(Poseidon2::hash(padding_leaf.get_hash_inputs()), 0, low_leaf_witness_data.path),
162 prev_snapshot.root);
163
164 // Update padding leaf
165 padding_leaf.leaf.value = leaf.value;
166
167 auto intermediate_root =
168 unconstrained_root_from_path(Poseidon2::hash(padding_leaf.get_hash_inputs()), 0, low_leaf_witness_data.path);
169
170 // No insertion
171 EXPECT_EQ(snapshot_after.root, intermediate_root);
172 EXPECT_EQ(snapshot_after.nextAvailableLeafIndex, 1);
173}
174
175TEST(IndexedMemoryTree, GetLeaves)
176{
177 Tree tree(5, 1);
178
180
181 // Insert leaves 100, 110, 120...
182 for (size_t i = 10; i < 20; i++) {
183 leaves.push_back(LeafValue(i * 10));
184 }
185
186 tree.insert_indexed_leaves(leaves);
187
188 EXPECT_EQ(tree.get_low_indexed_leaf(FF(100)), GetLowIndexedLeafResponse(true, 1));
189 EXPECT_EQ(tree.get_low_indexed_leaf(FF(105)), GetLowIndexedLeafResponse(false, 1));
190 EXPECT_EQ(tree.get_low_indexed_leaf(FF(190)), GetLowIndexedLeafResponse(true, 10));
191 EXPECT_EQ(tree.get_low_indexed_leaf(FF(195)), GetLowIndexedLeafResponse(false, 10));
192
193 // Pading leaf
194 EXPECT_EQ(tree.get_leaf_preimage(0), IndexedLeaf<LeafValue>(LeafValue::padding(1), 1, FF(100)));
195
196 EXPECT_EQ(tree.get_leaf_preimage(1), IndexedLeaf<LeafValue>(LeafValue(100), 2, FF(110)));
197 // Last leaf
198 EXPECT_EQ(tree.get_leaf_preimage(10), IndexedLeaf<LeafValue>(LeafValue(190), 0, 0));
199}
200
201TEST(IndexedMemoryTree, GetSiblingPath)
202{
203 Tree tree(5, 1);
204 LeafValue leaf(100);
205 tree.insert_indexed_leaves({ { leaf } });
206
207 auto path = tree.get_sibling_path(1);
208
209 EXPECT_EQ(path.size(), 5);
210 EXPECT_EQ(unconstrained_root_from_path(Poseidon2::hash(leaf.get_hash_inputs(0, 0)), 1, path),
211 tree.get_snapshot().root);
212}
213
214TEST(IndexedMemoryTree, Full)
215{
216 Tree tree(2, 1);
217 tree.insert_indexed_leaves({ { LeafValue(100) } });
218 tree.insert_indexed_leaves({ { LeafValue(110) } });
219 tree.insert_indexed_leaves({ { LeafValue(120) } });
220 EXPECT_THROW_WITH_MESSAGE(tree.insert_indexed_leaves({ { LeafValue(130) } }), "IndexedMemoryTree is full");
221}
222
223} // namespace
224
225} // namespace bb::avm2::simulation
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
Implements a parallelized batch insertion indexed tree Accepts template argument of the type of store...
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessage)
Definition macros.hpp:7
TEST(EmitUnencryptedLogTest, Basic)
FF unconstrained_root_from_path(const FF &leaf_value, const uint64_t leaf_index, std::span< const FF > path)
Definition merkle.cpp:12
std::ostream & operator<<(std::ostream &os, const WrittenPublicDataSlotLeafValue &v)
bool is_empty(const LeafType &leaf)
Definition types.hpp:42
bool operator==(ecdsa_signature const &lhs, ecdsa_signature const &rhs)
Definition ecdsa.hpp:45
Key get_key(int64_t keyCount)
Definition fixtures.hpp:30
std::variant< TreeWithStore< FrTree >, TreeWithStore< NullifierTree >, TreeWithStore< PublicDataTree > > Tree
Definition fork.hpp:25
typename Flavor::FF FF
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr field zero()