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