Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
nullifier_tree_check.test.cpp
Go to the documentation of this file.
2
3#include <gmock/gmock.h>
4#include <gtest/gtest.h>
5
14
15namespace bb::avm2::simulation {
16
17using ::testing::_;
18using ::testing::ElementsAre;
19using ::testing::Return;
20using ::testing::StrictMock;
21
23
24namespace {
25
26TEST(AvmSimulationNullifierTree, ReadExists)
27{
28 StrictMock<MockPoseidon2> poseidon2;
29 StrictMock<MockMerkleCheck> merkle_check;
30 StrictMock<MockFieldGreaterThan> field_gt;
31
32 EventEmitter<NullifierTreeCheckEvent> event_emitter;
33 NullifierTreeCheck nullifier_tree_check(poseidon2, merkle_check, field_gt, event_emitter);
34
37 uint64_t low_leaf_index = 30;
38 std::vector<FF> sibling_path = { 1, 2, 3, 4, 5 };
39 AppendOnlyTreeSnapshot snapshot = AppendOnlyTreeSnapshot{ .root = 123456, .nextAvailableLeafIndex = 128 };
40
41 FF nullifier = 42;
42
43 EXPECT_CALL(poseidon2, hash(low_leaf.get_hash_inputs())).WillRepeatedly(Return(FF(low_leaf_hash)));
44 EXPECT_CALL(merkle_check, assert_membership(low_leaf_hash, low_leaf_index, _, snapshot.root))
45 .WillRepeatedly(Return());
46
47 nullifier_tree_check.assert_read(
48 nullifier, /*contract_address*/ std::nullopt, true, low_leaf, low_leaf_index, sibling_path, snapshot);
49
50 NullifierTreeReadWriteEvent expect_event = {
51 .nullifier = nullifier,
52 .prev_snapshot = snapshot,
53 .next_snapshot = snapshot,
54 .low_leaf_preimage = low_leaf,
55 .low_leaf_hash = low_leaf_hash,
56 .low_leaf_index = low_leaf_index,
57 };
58 EXPECT_THAT(event_emitter.dump_events(), ElementsAre(expect_event));
59
60 // Negative test: nullifier does not exist
62 nullifier_tree_check.assert_read(
63 nullifier, /*contract_address*/ std::nullopt, false, low_leaf, low_leaf_index, sibling_path, snapshot),
64 "Nullifier non-membership check failed");
65}
66
67TEST(AvmSimulationNullifierTree, ReadNotExistsLowPointsToInfinity)
68{
69 StrictMock<MockPoseidon2> poseidon2;
70 StrictMock<MockMerkleCheck> merkle_check;
71 StrictMock<MockFieldGreaterThan> field_gt;
72
73 EventEmitter<NullifierTreeCheckEvent> event_emitter;
74 NullifierTreeCheck nullifier_tree_check(poseidon2, merkle_check, field_gt, event_emitter);
75
78 uint64_t low_leaf_index = 30;
79 std::vector<FF> sibling_path = { 1, 2, 3, 4, 5 };
80 AppendOnlyTreeSnapshot snapshot = AppendOnlyTreeSnapshot{ .root = 123456, .nextAvailableLeafIndex = 128 };
81 FF nullifier = 42;
82
83 EXPECT_CALL(poseidon2, hash(low_leaf.get_hash_inputs())).WillRepeatedly(Return(FF(low_leaf_hash)));
84 EXPECT_CALL(merkle_check, assert_membership(low_leaf_hash, low_leaf_index, _, snapshot.root))
85 .WillRepeatedly(Return());
86 EXPECT_CALL(field_gt, ff_gt(nullifier, low_leaf.leaf.nullifier)).WillRepeatedly(Return(true));
87
88 nullifier_tree_check.assert_read(
89 nullifier, /*contract_address*/ std::nullopt, false, low_leaf, low_leaf_index, sibling_path, snapshot);
90 NullifierTreeReadWriteEvent expect_event = {
91 .nullifier = nullifier,
92 .prev_snapshot = snapshot,
93 .next_snapshot = snapshot,
94 .low_leaf_preimage = low_leaf,
95 .low_leaf_hash = low_leaf_hash,
96 .low_leaf_index = low_leaf_index,
97 };
98 EXPECT_THAT(event_emitter.dump_events(), ElementsAre(expect_event));
99
100 // Negative test: nullifier exists
102 nullifier_tree_check.assert_read(
103 nullifier, /*contract_address*/ std::nullopt, true, low_leaf, low_leaf_index, sibling_path, snapshot),
104 "Nullifier membership check failed");
105
106 // Negative test: failed nullifier > low_leaf_preimage.value.nullifier
107 EXPECT_CALL(field_gt, ff_gt(nullifier, low_leaf.leaf.nullifier)).WillOnce(Return(false));
109 nullifier_tree_check.assert_read(
110 nullifier, /*contract_address*/ std::nullopt, true, low_leaf, low_leaf_index, sibling_path, snapshot),
111 "Low leaf value is GTE leaf value");
112}
113
114TEST(AvmSimulationNullifierTree, ReadNotExistsLowPointsToAnotherLeaf)
115{
116 StrictMock<MockPoseidon2> poseidon2;
117 StrictMock<MockMerkleCheck> merkle_check;
118 StrictMock<MockFieldGreaterThan> field_gt;
119
120 EventEmitter<NullifierTreeCheckEvent> event_emitter;
121 NullifierTreeCheck nullifier_tree_check(poseidon2, merkle_check, field_gt, event_emitter);
122
125 uint64_t low_leaf_index = 30;
126 std::vector<FF> sibling_path = { 1, 2, 3, 4, 5 };
127 AppendOnlyTreeSnapshot snapshot = AppendOnlyTreeSnapshot{ .root = 123456, .nextAvailableLeafIndex = 128 };
128 FF nullifier = 42;
129
130 EXPECT_CALL(poseidon2, hash(low_leaf.get_hash_inputs())).WillRepeatedly(Return(FF(low_leaf_hash)));
131 EXPECT_CALL(merkle_check, assert_membership(low_leaf_hash, low_leaf_index, _, snapshot.root))
132 .WillRepeatedly(Return());
133 EXPECT_CALL(field_gt, ff_gt(nullifier, low_leaf.leaf.nullifier)).WillRepeatedly(Return(true));
134 EXPECT_CALL(field_gt, ff_gt(low_leaf.nextKey, nullifier)).WillRepeatedly(Return(true));
135
136 nullifier_tree_check.assert_read(
137 nullifier, /*contract_address*/ std::nullopt, false, low_leaf, low_leaf_index, sibling_path, snapshot);
138 NullifierTreeReadWriteEvent expect_event = {
139 .nullifier = nullifier,
140 .prev_snapshot = snapshot,
141 .next_snapshot = snapshot,
142 .low_leaf_preimage = low_leaf,
143 .low_leaf_hash = low_leaf_hash,
144 .low_leaf_index = low_leaf_index,
145 };
146 EXPECT_THAT(event_emitter.dump_events(), ElementsAre(expect_event));
147
148 // Negative test: nullifier exists
150 nullifier_tree_check.assert_read(
151 nullifier, /*contract_address*/ std::nullopt, true, low_leaf, low_leaf_index, sibling_path, snapshot),
152 "Nullifier membership check failed");
153
154 // Negative test: failed low_leaf_preimage.nextKey > nullifier
155 EXPECT_CALL(field_gt, ff_gt(low_leaf.nextKey, nullifier)).WillOnce(Return(false));
157 nullifier_tree_check.assert_read(
158 nullifier, /*contract_address*/ std::nullopt, true, low_leaf, low_leaf_index, sibling_path, snapshot),
159 "Leaf value is GTE low leaf next value");
160}
161
162TEST(AvmSimulationNullifierTree, WriteExists)
163{
164 StrictMock<MockPoseidon2> poseidon2;
165 StrictMock<MockMerkleCheck> merkle_check;
166 StrictMock<MockFieldGreaterThan> field_gt;
167
168 EventEmitter<NullifierTreeCheckEvent> event_emitter;
169 NullifierTreeCheck nullifier_tree_check(poseidon2, merkle_check, field_gt, event_emitter);
170
173 uint64_t low_leaf_index = 30;
174 std::vector<FF> sibling_path = { 1, 2, 3, 4, 5 };
175 AppendOnlyTreeSnapshot snapshot = AppendOnlyTreeSnapshot{ .root = 123456, .nextAvailableLeafIndex = 128 };
176
177 FF nullifier = 42;
178
179 EXPECT_CALL(poseidon2, hash(low_leaf.get_hash_inputs())).WillRepeatedly(Return(FF(low_leaf_hash)));
180 EXPECT_CALL(merkle_check, assert_membership(low_leaf_hash, low_leaf_index, _, snapshot.root))
181 .WillRepeatedly(Return());
182
183 AppendOnlyTreeSnapshot result_snapshot = nullifier_tree_check.write(nullifier,
184 /*contract_address*/ std::nullopt,
185 10,
186 low_leaf,
187 low_leaf_index,
188 sibling_path,
189 snapshot,
190 /*insertion_path*/ std::nullopt);
191
192 EXPECT_EQ(result_snapshot, snapshot);
193
194 NullifierTreeReadWriteEvent expect_event = {
195 .nullifier = nullifier,
196 .prev_snapshot = snapshot,
197 .next_snapshot = snapshot,
198 .low_leaf_preimage = low_leaf,
199 .low_leaf_hash = low_leaf_hash,
200 .low_leaf_index = low_leaf_index,
201 .write = true,
202 .nullifier_counter = 10,
203 };
204 EXPECT_THAT(event_emitter.dump_events(), ElementsAre(expect_event));
205}
206
207TEST(AvmSimulationNullifierTree, Siloing)
208{
209 StrictMock<MockPoseidon2> poseidon2;
210 StrictMock<MockMerkleCheck> merkle_check;
211 StrictMock<MockFieldGreaterThan> field_gt;
212
213 EventEmitter<NullifierTreeCheckEvent> event_emitter;
214 NullifierTreeCheck nullifier_tree_check(poseidon2, merkle_check, field_gt, event_emitter);
215
216 FF nullifier = 42;
218 std::vector<FF> siloed_nullifier_hash_inputs = { GENERATOR_INDEX__OUTER_NULLIFIER, contract_address, nullifier };
219 FF siloed_nullifier = RawPoseidon2::hash(siloed_nullifier_hash_inputs);
220
223 uint64_t low_leaf_index = 30;
224 std::vector<FF> sibling_path = { 1, 2, 3, 4, 5 };
225 AppendOnlyTreeSnapshot snapshot = AppendOnlyTreeSnapshot{ .root = 123456, .nextAvailableLeafIndex = 128 };
226
227 EXPECT_CALL(poseidon2, hash(siloed_nullifier_hash_inputs)).WillRepeatedly(Return(FF(siloed_nullifier)));
228 EXPECT_CALL(poseidon2, hash(low_leaf.get_hash_inputs())).WillRepeatedly(Return(FF(low_leaf_hash)));
229 EXPECT_CALL(merkle_check, assert_membership(low_leaf_hash, low_leaf_index, _, snapshot.root))
230 .WillRepeatedly(Return());
231
232 nullifier_tree_check.assert_read(nullifier, contract_address, 10, low_leaf, low_leaf_index, sibling_path, snapshot);
233
234 NullifierTreeReadWriteEvent read_event = {
235 .nullifier = nullifier,
236 .prev_snapshot = snapshot,
237 .next_snapshot = snapshot,
238 .low_leaf_preimage = low_leaf,
239 .low_leaf_hash = low_leaf_hash,
240 .low_leaf_index = low_leaf_index,
241 .siloing_data = NullifierSiloingData{ .siloed_nullifier = siloed_nullifier, .address = contract_address },
242 };
243 nullifier_tree_check.write(nullifier,
245 10,
246 low_leaf,
247 low_leaf_index,
248 sibling_path,
249 snapshot,
250 /*insertion_path*/ std::nullopt);
251
252 NullifierTreeReadWriteEvent write_event = {
253 .nullifier = nullifier,
254 .prev_snapshot = snapshot,
255 .next_snapshot = snapshot,
256 .low_leaf_preimage = low_leaf,
257 .low_leaf_hash = low_leaf_hash,
258 .low_leaf_index = low_leaf_index,
259 .write = true,
260 .siloing_data = NullifierSiloingData{ .siloed_nullifier = siloed_nullifier, .address = contract_address },
261 .nullifier_counter = 10,
262 };
263 EXPECT_THAT(event_emitter.dump_events(), ElementsAre(read_event, write_event));
264}
265
266TEST(AvmSimulationNullifierTree, WriteAppend)
267{
268 StrictMock<MockPoseidon2> poseidon2;
269 StrictMock<MockMerkleCheck> merkle_check;
270 StrictMock<MockFieldGreaterThan> field_gt;
271
272 EventEmitter<NullifierTreeCheckEvent> event_emitter;
273 NullifierTreeCheck nullifier_tree_check(poseidon2, merkle_check, field_gt, event_emitter);
274
275 FF nullifier = 100;
276 FF low_nullifier = 40;
277
278 MemoryTree<Poseidon2HashPolicy> nullifier_tree(8);
279
283 uint64_t low_leaf_index = 0;
284 nullifier_tree.update_element(low_leaf_index, low_leaf_hash);
285
286 AppendOnlyTreeSnapshot prev_snapshot =
287 AppendOnlyTreeSnapshot{ .root = nullifier_tree.root(), .nextAvailableLeafIndex = 128 };
288 std::vector<FF> low_leaf_sibling_path = nullifier_tree.get_sibling_path(low_leaf_index);
289
290 NullifierTreeLeafPreimage updated_low_leaf = low_leaf;
291 updated_low_leaf.nextIndex = prev_snapshot.nextAvailableLeafIndex;
292 updated_low_leaf.nextKey = nullifier;
293 FF updated_low_leaf_hash = RawPoseidon2::hash(updated_low_leaf.get_hash_inputs());
294 nullifier_tree.update_element(low_leaf_index, updated_low_leaf_hash);
295
296 FF intermediate_root = nullifier_tree.root();
297 std::vector<FF> insertion_sibling_path = nullifier_tree.get_sibling_path(prev_snapshot.nextAvailableLeafIndex);
298
301 FF new_leaf_hash = RawPoseidon2::hash(new_leaf.get_hash_inputs());
302 nullifier_tree.update_element(prev_snapshot.nextAvailableLeafIndex, new_leaf_hash);
303
304 AppendOnlyTreeSnapshot next_snapshot =
305 AppendOnlyTreeSnapshot{ .root = nullifier_tree.root(),
306 .nextAvailableLeafIndex = prev_snapshot.nextAvailableLeafIndex + 1 };
307
308 EXPECT_CALL(poseidon2, hash(_)).WillRepeatedly([](const std::vector<FF>& input) {
309 return RawPoseidon2::hash(input);
310 });
311 EXPECT_CALL(merkle_check, write(low_leaf_hash, updated_low_leaf_hash, low_leaf_index, _, prev_snapshot.root))
312 .WillRepeatedly(Return(intermediate_root));
313 EXPECT_CALL(field_gt, ff_gt(nullifier, low_leaf.leaf.nullifier)).WillRepeatedly(Return(true));
314 EXPECT_CALL(field_gt, ff_gt(low_leaf.nextKey, nullifier)).WillRepeatedly(Return(true));
315 EXPECT_CALL(merkle_check, write(FF(0), new_leaf_hash, prev_snapshot.nextAvailableLeafIndex, _, intermediate_root))
316 .WillRepeatedly(Return(next_snapshot.root));
317
318 AppendOnlyTreeSnapshot result_snapshot = nullifier_tree_check.write(nullifier,
319 /*contract_address*/ std::nullopt,
320 0,
321 low_leaf,
322 low_leaf_index,
323 low_leaf_sibling_path,
324 prev_snapshot,
325 insertion_sibling_path);
326
327 EXPECT_EQ(next_snapshot, result_snapshot);
328
329 NullifierTreeReadWriteEvent expect_event = { .nullifier = nullifier,
330 .prev_snapshot = prev_snapshot,
331 .next_snapshot = next_snapshot,
332 .low_leaf_preimage = low_leaf,
333 .low_leaf_hash = low_leaf_hash,
334 .low_leaf_index = low_leaf_index,
335 .write = true,
336 .append_data = NullifierAppendData{
337 .updated_low_leaf_hash = updated_low_leaf_hash,
338 .new_leaf_hash = new_leaf_hash,
339 .intermediate_root = intermediate_root,
340 } };
341
342 EXPECT_THAT(event_emitter.dump_events(), ElementsAre(expect_event));
343
344 // Negative test: nullifier exists
346 EXPECT_THROW_WITH_MESSAGE(nullifier_tree_check.write(nullifier,
347 /*contract_address*/ std::nullopt,
348 0,
349 low_leaf,
350 low_leaf_index,
351 low_leaf_sibling_path,
352 prev_snapshot,
353 insertion_sibling_path),
354 "Nullifier non-membership check failed");
355 low_leaf.leaf.nullifier = low_nullifier;
356
357 // Negative test: failed nullifier > low_leaf_preimage.value.nullifier
358 EXPECT_CALL(field_gt, ff_gt(nullifier, low_leaf.leaf.nullifier)).WillOnce(Return(false));
359 EXPECT_THROW_WITH_MESSAGE(nullifier_tree_check.write(nullifier,
360 /*contract_address*/ std::nullopt,
361 0,
362 low_leaf,
363 low_leaf_index,
364 low_leaf_sibling_path,
365 prev_snapshot,
366 insertion_sibling_path),
367 "Low leaf value is GTE leaf value");
368 EXPECT_CALL(field_gt, ff_gt(nullifier, low_leaf.leaf.nullifier)).WillOnce(Return(true));
369
370 // Negative test: failed low_leaf_preimage.nextKey > nullifier
371 EXPECT_CALL(field_gt, ff_gt(low_leaf.nextKey, nullifier)).WillOnce(Return(false));
372 EXPECT_THROW_WITH_MESSAGE(nullifier_tree_check.write(nullifier,
373 /*contract_address*/ std::nullopt,
374 0,
375 low_leaf,
376 low_leaf_index,
377 low_leaf_sibling_path,
378 prev_snapshot,
379 insertion_sibling_path),
380 "Leaf value is GTE low leaf next value");
381}
382
383} // namespace
384
385} // namespace bb::avm2::simulation
#define GENERATOR_INDEX__OUTER_NULLIFIER
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
EventEmitter< DataCopyEvent > event_emitter
NullifierTreeLeafPreimage low_leaf
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessage)
Definition macros.hpp:7
void hash(State &state) noexcept
crypto::Poseidon2< crypto::Poseidon2Bn254ScalarFieldParams > poseidon2
TEST(EmitUnencryptedLogTest, Basic)
::bb::crypto::merkle_tree::NullifierLeafValue NullifierLeafValue
IndexedLeaf< NullifierLeafValue > NullifierTreeLeafPreimage
typename Flavor::FF FF
void write(B &buf, field2< base_field, Params > const &value)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::vector< fr > get_hash_inputs() const
NiceMock< MockFieldGreaterThan > field_gt