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
11
12namespace bb::avm2::simulation {
13
14namespace {
15
17
18struct LeafValue {
20
21 LeafValue(const FF& key)
22 : key(key)
23 {}
24
25 static bool is_updateable() { return false; }
26
27 bool operator==(LeafValue const& other) const { return key == other.key; }
28
29 fr get_key() const { return key; }
30
31 bool is_empty() const { return key.is_zero(); }
32
33 std::vector<fr> get_hash_inputs(fr nextKey, fr nextIndex) const
34 {
35 return std::vector<fr>({ key, nextKey, nextIndex });
36 }
37
38 static LeafValue empty() { return { fr::zero() }; }
39
40 static LeafValue padding(index_t i) { return { i }; }
41
42 static std::string name() { return "LeafValue"; }
43
44 [[maybe_unused]] friend std::ostream& operator<<(std::ostream& os, const LeafValue& v)
45 {
46 os << "key = " << v.key;
47 return os;
48 }
49};
50
51struct UpdatableLeafValue {
52 FF key;
54
55 UpdatableLeafValue(const FF& key, const FF& value)
56 : key(key)
57 , value(value)
58 {}
59
60 static bool is_updateable() { return true; }
61
62 bool operator==(UpdatableLeafValue const& other) const { return key == other.key && value == other.value; }
63
64 fr get_key() const { return key; }
65
66 bool is_empty() const { return key.is_zero() && value.is_zero(); }
67
68 std::vector<fr> get_hash_inputs(fr nextKey, fr nextIndex) const
69 {
70 return std::vector<fr>({ key, value, nextKey, nextIndex });
71 }
72
73 static UpdatableLeafValue empty() { return { fr::zero(), fr::zero() }; }
74
75 static UpdatableLeafValue padding(index_t i) { return { i, fr::zero() }; }
76
77 static std::string name() { return "UpdatableLeafValue"; }
78
79 [[maybe_unused]] friend std::ostream& operator<<(std::ostream& os, const UpdatableLeafValue& v)
80 {
81 os << "key = " << v.key << " : value = " << v.value;
82 return os;
83 }
84};
85
86using Tree = IndexedMemoryTree<LeafValue, aztec::NullifierMerkleHashPolicy>;
87using UpdatableTree = IndexedMemoryTree<UpdatableLeafValue, aztec::NullifierMerkleHashPolicy>;
88
89TEST(IndexedMemoryTree, Append)
90{
91 Tree tree(5, 1);
92 auto prev_snapshot = tree.get_snapshot();
93
94 LeafValue leaf(42);
95 auto result = tree.insert_indexed_leaves({ { leaf } });
96
97 auto snapshot_after = tree.get_snapshot();
98
99 EXPECT_EQ(result.insertion_witness_data.size(), 1);
100 EXPECT_EQ(result.low_leaf_witness_data.size(), 1);
101
102 LeafUpdateWitnessData<LeafValue> low_leaf_witness_data = result.low_leaf_witness_data[0];
103 LeafUpdateWitnessData<LeafValue> insertion_witness_data = result.insertion_witness_data[0];
104
105 // Low leaf is the prefill
106 EXPECT_EQ(low_leaf_witness_data.index, 0);
107
108 // Memberhsip check old padding leaf
109 IndexedLeaf<LeafValue> padding_leaf(LeafValue::padding(1), 0, 0);
110 EXPECT_EQ(low_leaf_witness_data.leaf, padding_leaf);
111
112 EXPECT_EQ(
114 DOM_SEP__NULLIFIER_MERKLE, Poseidon2::hash(padding_leaf.get_hash_inputs()), 0, low_leaf_witness_data.path),
115 prev_snapshot.root);
116
117 // Reconstruct intermediate root:
118 padding_leaf.nextIndex = 1;
119 padding_leaf.nextKey = leaf.key;
120
121 auto intermediate_root = unconstrained_root_from_path(
122 DOM_SEP__NULLIFIER_MERKLE, Poseidon2::hash(padding_leaf.get_hash_inputs()), 0, low_leaf_witness_data.path);
123
124 // Insertion leaf should be the new leaf
125
126 // Membership check a zero at the insertion index
127 EXPECT_EQ(unconstrained_root_from_path(DOM_SEP__NULLIFIER_MERKLE, 0, 1, insertion_witness_data.path),
128 intermediate_root);
129
130 IndexedLeaf<LeafValue> inserted_leaf(leaf, 0, 0);
131 EXPECT_EQ(insertion_witness_data.leaf, inserted_leaf);
132
133 auto final_root = unconstrained_root_from_path(
134 DOM_SEP__NULLIFIER_MERKLE, Poseidon2::hash(inserted_leaf.get_hash_inputs()), 1, insertion_witness_data.path);
135
136 EXPECT_EQ(snapshot_after.root, final_root);
137 EXPECT_EQ(snapshot_after.next_available_leaf_index, 2);
138}
139
140TEST(IndexedMemoryTree, Update)
141{
142 UpdatableTree tree(5, 1);
143 auto prev_snapshot = tree.get_snapshot();
144
145 UpdatableLeafValue leaf(1, 43);
146 auto result = tree.insert_indexed_leaves({ { leaf } });
147
148 auto snapshot_after = tree.get_snapshot();
149
150 EXPECT_EQ(result.insertion_witness_data.size(), 1);
151 EXPECT_EQ(result.low_leaf_witness_data.size(), 1);
152
153 LeafUpdateWitnessData<UpdatableLeafValue> low_leaf_witness_data = result.low_leaf_witness_data[0];
154 LeafUpdateWitnessData<UpdatableLeafValue> insertion_witness_data = result.insertion_witness_data[0];
155
156 // Low leaf is the prefill
157 EXPECT_EQ(low_leaf_witness_data.index, 0);
158
159 // Memberhsip check old padding leaf
160 IndexedLeaf<UpdatableLeafValue> padding_leaf(UpdatableLeafValue::padding(1), 0, 0);
161 EXPECT_EQ(low_leaf_witness_data.leaf, padding_leaf);
162
163 EXPECT_EQ(
165 DOM_SEP__NULLIFIER_MERKLE, Poseidon2::hash(padding_leaf.get_hash_inputs()), 0, low_leaf_witness_data.path),
166 prev_snapshot.root);
167
168 // Update padding leaf
169 padding_leaf.leaf.value = leaf.value;
170
171 auto intermediate_root = unconstrained_root_from_path(
172 DOM_SEP__NULLIFIER_MERKLE, Poseidon2::hash(padding_leaf.get_hash_inputs()), 0, low_leaf_witness_data.path);
173
174 // No insertion
175 EXPECT_EQ(snapshot_after.root, intermediate_root);
176 EXPECT_EQ(snapshot_after.next_available_leaf_index, 1);
177}
178
179TEST(IndexedMemoryTree, GetLeaves)
180{
181 Tree tree(5, 1);
182
184
185 // Insert leaves 100, 110, 120...
186 for (size_t i = 10; i < 20; i++) {
187 leaves.push_back(LeafValue(i * 10));
188 }
189
190 tree.insert_indexed_leaves(leaves);
191
192 EXPECT_EQ(tree.get_low_indexed_leaf(FF(100)), GetLowIndexedLeafResponse(true, 1));
193 EXPECT_EQ(tree.get_low_indexed_leaf(FF(105)), GetLowIndexedLeafResponse(false, 1));
194 EXPECT_EQ(tree.get_low_indexed_leaf(FF(190)), GetLowIndexedLeafResponse(true, 10));
195 EXPECT_EQ(tree.get_low_indexed_leaf(FF(195)), GetLowIndexedLeafResponse(false, 10));
196
197 // Pading leaf
198 EXPECT_EQ(tree.get_leaf_preimage(0), IndexedLeaf<LeafValue>(LeafValue::padding(1), 1, FF(100)));
199
200 EXPECT_EQ(tree.get_leaf_preimage(1), IndexedLeaf<LeafValue>(LeafValue(100), 2, FF(110)));
201 // Last leaf
202 EXPECT_EQ(tree.get_leaf_preimage(10), IndexedLeaf<LeafValue>(LeafValue(190), 0, 0));
203}
204
205TEST(IndexedMemoryTree, GetSiblingPath)
206{
207 Tree tree(5, 1);
208 LeafValue leaf(100);
209 tree.insert_indexed_leaves({ { leaf } });
210
211 auto path = tree.get_sibling_path(1);
212
213 EXPECT_EQ(path.size(), 5);
214 EXPECT_EQ(
215 unconstrained_root_from_path(DOM_SEP__NULLIFIER_MERKLE, Poseidon2::hash(leaf.get_hash_inputs(0, 0)), 1, path),
216 tree.get_snapshot().root);
217}
218
219TEST(IndexedMemoryTree, Full)
220{
221 Tree tree(2, 1);
222 tree.insert_indexed_leaves({ { LeafValue(100) } });
223 tree.insert_indexed_leaves({ { LeafValue(110) } });
224 tree.insert_indexed_leaves({ { LeafValue(120) } });
225 EXPECT_THROW_WITH_MESSAGE(tree.insert_indexed_leaves({ { LeafValue(130) } }), "IndexedMemoryTree is full");
226}
227
228} // namespace
229
230} // namespace bb::avm2::simulation
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:193
#define DOM_SEP__NULLIFIER_MERKLE
Native Poseidon2 hash function implementation.
Definition poseidon2.hpp:22
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
AVM range check gadget for witness generation.
FF unconstrained_root_from_path(uint64_t domain_separator, 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 CoarseTransactionPhase &phase)
Definition avm_io.hpp:488
AvmFlavorSettings::FF FF
Definition field.hpp:10
bool is_empty(const LeafType &leaf)
Definition types.hpp:42
bool operator==(schnorr_signature const &lhs, schnorr_signature const &rhs)
Definition schnorr.hpp:38
Key get_key(int64_t keyCount)
Definition fixtures.hpp:30
std::variant< TreeWithStore< FrTree >, TreeWithStore< NullifierTree >, TreeWithStore< PublicDataTree > > Tree
Definition fork.hpp:28
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
BB_INLINE constexpr bool is_zero() const noexcept
static constexpr field zero()