Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
poseidon2.test.cpp
Go to the documentation of this file.
1#include <gmock/gmock.h>
2#include <gtest/gtest.h>
3
4#include <cstdint>
5
23
24// Temporary imports, see comment in test.
27
28namespace bb::avm2::constraining {
29
30using ::testing::ElementsAreArray;
31using ::testing::NiceMock;
32using ::testing::Return;
33using ::testing::ReturnRef;
34
35using tracegen::Poseidon2TraceBuilder;
36using tracegen::TestTraceContainer;
37
39using C = Column;
43
57
78
80{
81 check_relation<poseidon2_hash>(testing::empty_trace());
82 check_relation<poseidon2_perm>(testing::empty_trace());
83 check_relation<poseidon2_mem>(testing::empty_trace());
84}
85
86// These tests imports a bunch of external code since hand-generating the poseidon2 trace is a bit laborious atm.
88{
89 // Taken From barretenberg/crypto/poseidon2/poseidon2.test.cpp
90 FF a("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789");
91 FF b("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789");
92 FF c("0x9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789");
93 FF d("0x9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789");
94
95 std::array<FF, 4> input = { a, b, c, d };
96 auto result = poseidon2.permutation(input);
97
98 std::array<FF, 4> expected = {
99 FF("0x2bf1eaf87f7d27e8dc4056e9af975985bccc89077a21891d6c7b6ccce0631f95"),
100 FF("0x0c01fa1b8d0748becafbe452c0cb0231c38224ea824554c9362518eebdd5701f"),
101 FF("0x018555a8eb50cf07f64b019ebaf3af3c925c93e631f3ecd455db07bbb52bbdd3"),
102 FF("0x0cbea457c91c22c6c31fd89afd2541efc2edf31736b9f721e823b2165c90fd41"),
103 };
104
105 EXPECT_THAT(result, ElementsAreArray(expected));
106
109
110 builder.process_permutation(perm_event_emitter.dump_events(), trace);
111 EXPECT_EQ(trace.get_num_rows(), 1);
112
113 check_relation<poseidon2_perm>(trace);
114}
115
116TEST_F(Poseidon2ConstrainingTest, HashWithSinglePermutation)
117{
118 FF a("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789");
119 FF b("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789");
120 FF c("0x9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789");
121
122 std::vector<FF> input = { a, b, c };
123 poseidon2.hash(input);
124
125 // This first row column is set becuase it is used in the relations for Poseidon2Hash.
126 // This could be replaced by having the precomputed tables in the trace, but currently that would
127 // mean the clk column of length 2^21 -1 will be include :O
129 { { C::precomputed_first_row, 1 } },
130 });
132
133 builder.process_hash(hash_event_emitter.dump_events(), trace);
134 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 1);
135
136 check_relation<poseidon2_hash>(trace);
137}
138
139TEST_F(Poseidon2ConstrainingTest, HashWithMultiplePermutation)
140{
141 // Taken From barretenberg/crypto/poseidon2/poseidon2.test.cpp
142 FF a("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789");
143 FF b("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789");
144 FF c("0x9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789");
145 FF d("0x9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789");
146
147 std::vector<FF> input = { a, b, c, d };
148 poseidon2.hash(input);
149
151 { { C::precomputed_first_row, 1 } },
152 });
154
155 builder.process_hash(hash_event_emitter.dump_events(), trace);
156 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 2);
157
158 check_relation<poseidon2_hash>(trace);
159}
160
161TEST_F(Poseidon2ConstrainingTest, MultipleHashInvocations)
162{
163 std::vector<FF> input = { 1, 2, 3, 4 };
164
165 FF result = poseidon2.hash(input);
167 EXPECT_EQ(result, bb_result);
168
169 result = poseidon2.hash({ result, 1, 2, 3, 4 });
170 bb_result = crypto::Poseidon2<crypto::Poseidon2Bn254ScalarFieldParams>::hash({ bb_result, 1, 2, 3, 4 });
171 EXPECT_EQ(result, bb_result);
172
174 { { C::precomputed_first_row, 1 } },
175 });
177
178 builder.process_hash(hash_event_emitter.dump_events(), trace);
179 builder.process_permutation(perm_event_emitter.dump_events(), trace);
180 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + /*first_invocation=*/2 + /*second_invokcation=*/2);
181
182 check_relation<poseidon2_hash>(trace);
183}
184
185TEST_F(Poseidon2ConstrainingTest, HashPermInteractions)
186{
187 std::vector<FF> input = { 1, 2, 3, 4 };
188
189 poseidon2.hash(input);
190
192 { { C::precomputed_first_row, 1 } },
193 });
195
196 builder.process_hash(hash_event_emitter.dump_events(), trace);
197 builder.process_permutation(perm_event_emitter.dump_events(), trace);
198 check_interaction<Poseidon2TraceBuilder, lookup_poseidon2_hash_poseidon2_perm_settings>(trace);
199
200 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + /*first_invocation=*/2);
201
202 check_relation<poseidon2_hash>(trace);
203 check_relation<poseidon2_perm>(trace);
204 check_all_interactions<Poseidon2TraceBuilder>(trace);
205}
206
207TEST_F(Poseidon2ConstrainingTest, NegativeHashPermInteractions)
208{
209 std::vector<FF> input = { 1, 2, 3, 4 };
210
211 poseidon2.hash(input);
212
214 { { C::precomputed_first_row, 1 } },
215 });
217
218 builder.process_hash(hash_event_emitter.dump_events(), trace);
219
221 (check_interaction<Poseidon2TraceBuilder, lookup_poseidon2_hash_poseidon2_perm_settings>(trace)),
222 "Failed.*POSEIDON2_PERM. Could not find tuple in destination.");
223
224 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + /*first_invocation=*/2);
225
226 check_relation<poseidon2_hash>(trace);
227}
228
230// Memory Aware Poseidon2
232
234 protected:
235 Poseidon2MemoryConstrainingTest() { ON_CALL(memory, get_space_id).WillByDefault(Return(0)); }
236
239
240 NiceMock<MockMemory> memory;
242 MemoryValue::from<FF>(1), MemoryValue::from<FF>(2), MemoryValue::from<FF>(3), MemoryValue::from<FF>(4)
243 };
244
245 uint32_t src_address = 0;
246 uint32_t dst_address = 4;
247};
248
250{
251 // Read 4 inputs
252 EXPECT_CALL(memory, get)
253 .WillOnce(ReturnRef(inputs[0]))
254 .WillOnce(ReturnRef(inputs[1]))
255 .WillOnce(ReturnRef(inputs[2]))
256 .WillOnce(ReturnRef(inputs[3]));
257 EXPECT_CALL(memory, set).Times(4); // Write 4 outputs
258
259 poseidon2.permutation(memory, src_address, dst_address);
260
261 builder.process_permutation_with_memory(perm_mem_event_emitter.dump_events(), trace);
262
263 check_relation<poseidon2_mem>(trace);
264}
265
266TEST_F(Poseidon2MemoryConstrainingTest, PermutationMemoryInteractions)
267{
268 // Read 4 inputs
269 EXPECT_CALL(memory, get)
270 .WillOnce(ReturnRef(inputs[0]))
271 .WillOnce(ReturnRef(inputs[1]))
272 .WillOnce(ReturnRef(inputs[2]))
273 .WillOnce(ReturnRef(inputs[3]));
274 EXPECT_CALL(memory, set).Times(4); // Write 4 outputs
275
276 // Expected bb output from inputs = {1, 2, 3 ,4}
277 std::vector<FF> outputs = { FF("0x224785a48a72c75e2cbb698143e71d5d41bd89a2b9a7185871e39a54ce5785b1"),
278 FF("0x225bb800db22c4f4b09ace45cb484d42b0dd7dfe8708ee26aacde6f2c1fb2cb8"),
279 FF("0x1180f4260e60b4264c987b503075ea8374b53ed06c5145f8c21c2aadb5087d21"),
280 FF("0x16c877b5b9c04d873218804ccbf65d0eeb12db447f66c9ca26fec380055df7e9") };
281
282 // Set the execution and gt traces
284 // Row 0
285 {
286 // Execution
287 { C::execution_sel, 1 },
288 { C::execution_sel_exec_dispatch_poseidon2_perm, 1 },
289 { C::execution_rop_0_, src_address },
290 { C::execution_rop_1_, dst_address },
291 // GT - dst out of range check
292 { C::gt_sel, 1 },
293 { C::gt_input_a, dst_address + 3 }, // highest write address is src_address + 3
294 { C::gt_input_b, AVM_HIGHEST_MEM_ADDRESS },
295 { C::gt_res, 0 },
296 },
297 {
298 // GT - src out of range check
299 { C::gt_sel, 1 },
300 { C::gt_input_a, src_address + 3 }, // highest read address is dst_address + 3
301 { C::gt_input_b, AVM_HIGHEST_MEM_ADDRESS },
302 { C::gt_res, 0 },
303 },
304 });
305
306 // Set up memory trace
307 for (uint32_t i = 0; i < inputs.size(); ++i) {
308 // Set memory reads
309 trace.set(C::memory_address, i, src_address + i);
310 trace.set(C::memory_value, i, inputs[i]);
311 trace.set(C::memory_tag, i, static_cast<uint32_t>(inputs[i].get_tag()));
312 trace.set(C::memory_sel, i, 1);
313 // Set memory writes
314 uint32_t write_index = i + static_cast<uint32_t>(inputs.size());
315 trace.set(C::memory_address, write_index, dst_address + i);
316 trace.set(C::memory_value, write_index, outputs[i]);
317 trace.set(C::memory_sel, write_index, 1);
318 trace.set(C::memory_rw, write_index, 1);
319 }
320
321 poseidon2.permutation(memory, src_address, dst_address);
322
323 builder.process_permutation_with_memory(perm_mem_event_emitter.dump_events(), trace);
324 builder.process_permutation(perm_event_emitter.dump_events(), trace);
325
326 check_all_interactions<Poseidon2TraceBuilder>(trace);
327 check_relation<poseidon2_mem>(trace);
328}
329
330TEST_F(Poseidon2MemoryConstrainingTest, PermutationMemoryInvalidTag)
331{
332 // Third input is of the wrong type
333 std::vector<MemoryValue> inputs = {
334 MemoryValue::from<FF>(1), MemoryValue::from<FF>(2), MemoryValue::from<uint64_t>(3), MemoryValue::from<FF>(4)
335 };
336
337 // Still load all the inputs even though there is in invalid tag
338 EXPECT_CALL(memory, get)
339 .WillOnce(ReturnRef(inputs[0]))
340 .WillOnce(ReturnRef(inputs[1]))
341 .WillOnce(ReturnRef(inputs[2]))
342 .WillOnce(ReturnRef(inputs[3]));
343
344 // Set the execution and gt traces
346 // Row 0
347 {
348 // Execution
349 { C::execution_sel, 1 },
350 { C::execution_sel_exec_dispatch_poseidon2_perm, 1 },
351 { C::execution_rop_0_, src_address },
352 { C::execution_rop_1_, dst_address },
353 { C::execution_sel_opcode_error, 1 }, // Invalid tag error
354 // GT - dst out of range check
355 { C::gt_sel, 1 },
356 { C::gt_input_a, dst_address + 3 }, // highest write address is src_address + 3
357 { C::gt_input_b, AVM_HIGHEST_MEM_ADDRESS },
358 { C::gt_res, 0 },
359 },
360 {
361 // GT - src out of range check
362 { C::gt_sel, 1 },
363 { C::gt_input_a, src_address + 3 }, // highest read address is dst_address + 3
364 { C::gt_input_b, AVM_HIGHEST_MEM_ADDRESS },
365 { C::gt_res, 0 },
366 },
367 });
368
369 // Set up memory trace
370 for (uint32_t i = 0; i < inputs.size(); ++i) {
371 // Set memory reads
372 trace.set(C::memory_address, i, src_address + i);
373 trace.set(C::memory_value, i, inputs[i]);
374 trace.set(C::memory_tag, i, static_cast<uint32_t>(inputs[i].get_tag()));
375 trace.set(C::memory_sel, i, 1);
376 }
377
378 EXPECT_THROW_WITH_MESSAGE(poseidon2.permutation(memory, src_address, dst_address),
379 "Poseidon2Exception.* input tag is not FF");
380
381 builder.process_permutation_with_memory(perm_mem_event_emitter.dump_events(), trace);
382
383 // Don't expect any permutation events since the memory access was invalid
384 EXPECT_EQ(perm_event_emitter.dump_events().size(), 0);
385
386 check_relation<poseidon2_mem>(trace);
387 check_all_interactions<Poseidon2TraceBuilder>(trace);
388}
389
390TEST_F(Poseidon2MemoryConstrainingTest, PermutationMemoryInvalidAddressRange)
391{
392 uint32_t src_address = static_cast<uint32_t>(AVM_HIGHEST_MEM_ADDRESS - 2);
393
395 // Row 0
396 {
397 // Execution
398 { C::execution_sel, 1 },
399 { C::execution_sel_exec_dispatch_poseidon2_perm, 1 },
400 { C::execution_rop_0_, src_address },
401 { C::execution_rop_1_, dst_address },
402 { C::execution_sel_opcode_error, 1 }, // Invalid address error
403 // GT Subtrace - dst out of range check
404 { C::gt_sel, 1 },
405 { C::gt_input_a, dst_address + 3 }, // highest write address is src_address + 3
406 { C::gt_input_b, AVM_HIGHEST_MEM_ADDRESS },
407 { C::gt_res, 0 },
408 },
409 {
410 // GT Subtrace - src out of range check
411 { C::gt_sel, 1 },
412 { C::gt_input_a, static_cast<uint64_t>(src_address) + 3 }, // highest read address is dst_address + 3
413 { C::gt_input_b, AVM_HIGHEST_MEM_ADDRESS },
414 { C::gt_res, 1 },
415 },
416 });
417
418 EXPECT_THROW_WITH_MESSAGE(poseidon2.permutation(memory, src_address, dst_address),
419 "Poseidon2Exception.* src or dst address out of range");
420
422 builder.process_permutation_with_memory(perm_mem_event_emitter.dump_events(), trace);
423
424 // Don't expect any permutation events since the address range was invalid
425 EXPECT_EQ(perm_event_emitter.dump_events().size(), 0);
426
427 check_relation<poseidon2_mem>(trace);
428 check_all_interactions<Poseidon2TraceBuilder>(trace);
429}
430
431} // namespace bb::avm2::constraining
#define AVM_HIGHEST_MEM_ADDRESS
EventEmitter< Poseidon2PermutationMemoryEvent > perm_mem_event_emitter
EventEmitter< Poseidon2PermutationEvent > perm_event_emitter
EventEmitter< Poseidon2HashEvent > hash_event_emitter
EventEmitter< Poseidon2HashEvent > hash_event_emitter
EventEmitter< GreaterThanEvent > gt_event_emitter
EventEmitter< Poseidon2PermutationMemoryEvent > perm_mem_event_emitter
EventEmitter< Poseidon2PermutationEvent > perm_event_emitter
DeduplicatingEventEmitter< FieldGreaterThanEvent > field_gt_event_emitter
void set(Column col, uint32_t row, const FF &value)
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
AluTraceBuilder builder
Definition alu.test.cpp:123
TestTraceContainer trace
FF a
FF b
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessage)
Definition macros.hpp:7
TEST_F(AvmRecursiveTests, GoblinRecursion)
A test of the Goblinized AVM recursive verifier.
TestTraceContainer empty_trace()
Definition fixtures.cpp:153
AvmFlavorSettings::FF FF
Definition field.hpp:10
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13