Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
sha256.test.cpp
Go to the documentation of this file.
1#include <gmock/gmock.h>
2#include <gtest/gtest.h>
3
4#include <cstdint>
5
28// Temporary imports, see comment in test.
34
35namespace bb::avm2::constraining {
36namespace {
37
38// todo(ilyas): add negative tests
39
40using ::testing::Return;
41using ::testing::StrictMock;
42
43using simulation::Bitwise;
44using simulation::BitwiseEvent;
45using simulation::DeduplicatingEventEmitter;
46using simulation::EventEmitter;
47using simulation::FieldGreaterThan;
48using simulation::FieldGreaterThanEvent;
49using simulation::GreaterThan;
50using simulation::GreaterThanEvent;
51using simulation::MemoryStore;
52using simulation::MockExecutionIdManager;
53using simulation::PureBitwise;
54using simulation::PureGreaterThan;
55using simulation::RangeCheck;
56using simulation::RangeCheckEvent;
57using simulation::Sha256;
58using simulation::Sha256CompressionEvent;
59
60using tracegen::BitwiseTraceBuilder;
61using tracegen::GreaterThanTraceBuilder;
62using tracegen::PrecomputedTraceBuilder;
63using tracegen::Sha256TraceBuilder;
64using tracegen::TestTraceContainer;
65
67using C = Column;
69using sha256_mem = bb::avm2::sha256_mem<FF>;
70
71TEST(Sha256ConstrainingTest, EmptyRow)
72{
73 check_relation<sha256>(testing::empty_trace());
74 check_relation<sha256_mem>(testing::empty_trace());
75}
76
77// This test imports a bunch of external code since hand-generating the sha256 trace is a bit laborious atm.
78// The test is a bit of a placeholder for now.
79// TOOD: Replace this with a hardcoded test vector and write a negative test
80TEST(Sha256ConstrainingTest, Basic)
81{
82 MemoryStore mem;
83 StrictMock<MockExecutionIdManager> execution_id_manager;
84 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
85 PureGreaterThan gt;
86 PureBitwise bitwise;
87
88 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
89 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, sha256_event_emitter);
90
91 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
92 MemoryAddress state_addr = 0;
93 for (uint32_t i = 0; i < 8; ++i) {
94 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
95 }
96
97 std::array<uint32_t, 16> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
98 MemoryAddress input_addr = 8;
99 for (uint32_t i = 0; i < 16; ++i) {
100 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
101 }
102 MemoryAddress output_addr = 25;
103
104 // We do two compression operations just to ensure the "after-latch" relations are correct
105 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
106 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
107 TestTraceContainer trace;
108 trace.set(C::precomputed_first_row, 0, 1);
109 Sha256TraceBuilder builder;
110 const auto sha256_event_container = sha256_event_emitter.dump_events();
111 builder.process(sha256_event_container, trace);
112
113 check_relation<sha256>(trace);
114}
115
116TEST(Sha256ConstrainingTest, Interaction)
117{
118 MemoryStore mem;
119 StrictMock<MockExecutionIdManager> execution_id_manager;
120 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
121 EventEmitter<BitwiseEvent> bitwise_event_emitter;
122 EventEmitter<GreaterThanEvent> gt_event_emitter;
123 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
124 EventEmitter<RangeCheckEvent> range_check_event_emitter;
125
127 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
128 GreaterThan gt(field_gt, range_check, gt_event_emitter);
129
131
132 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
133 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, sha256_event_emitter);
134
135 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
136 MemoryAddress state_addr = 0;
137 for (uint32_t i = 0; i < 8; ++i) {
138 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
139 }
140
141 std::array<uint32_t, 16> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
142 MemoryAddress input_addr = 8;
143 for (uint32_t i = 0; i < 16; ++i) {
144 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
145 }
146 MemoryAddress output_addr = 25;
147
148 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
149
150 TestTraceContainer trace;
151 Sha256TraceBuilder builder;
152 PrecomputedTraceBuilder precomputed_builder;
153 // Build just enough clk rows for the lookup
156
157 BitwiseTraceBuilder bitwise_builder;
158 bitwise_builder.process(bitwise_event_emitter.dump_events(), trace);
159
160 GreaterThanTraceBuilder gt_builder;
161 gt_builder.process(gt_event_emitter.dump_events(), trace);
162
163 builder.process(sha256_event_emitter.get_events(), trace);
164
165 // Check bitwise and round constant lookups
166 check_interaction<Sha256TraceBuilder,
210
211 check_relation<sha256>(trace);
212}
213
217
218TEST(Sha256MemoryConstrainingTest, Basic)
219{
220 MemoryStore mem;
221 StrictMock<MockExecutionIdManager> execution_id_manager;
222 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
223
224 EventEmitter<RangeCheckEvent> range_check_event_emitter;
225 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
226 EventEmitter<GreaterThanEvent> gt_event_emitter;
227
229 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
230 GreaterThan gt(field_gt, range_check, gt_event_emitter);
231 PureBitwise bitwise;
232
233 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
234 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, sha256_event_emitter);
235
236 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
237 MemoryAddress state_addr = 0;
238 for (uint32_t i = 0; i < 8; ++i) {
239 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
240 }
241
242 std::array<uint32_t, 16> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
243 MemoryAddress input_addr = 8;
244 for (uint32_t i = 0; i < 16; ++i) {
245 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
246 }
247 MemoryAddress output_addr = 25;
248
249 // We do two compression operations just to ensure the "after-latch" relations are correct
250 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
251 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
252 TestTraceContainer trace;
253 trace.set(C::precomputed_first_row, 0, 1);
254
255 Sha256TraceBuilder builder;
256 const auto sha256_event_container = sha256_event_emitter.dump_events();
257 builder.process(sha256_event_container, trace);
258 GreaterThanTraceBuilder gt_builder;
259 gt_builder.process(gt_event_emitter.dump_events(), trace);
260
261 check_relation<sha256_mem>(trace);
262 check_relation<sha256>(trace);
263 check_interaction<Sha256TraceBuilder,
267}
268
269TEST(Sha256MemoryConstrainingTest, SimpleOutOfRangeMemoryAddresses)
270{
271 MemoryStore mem;
272 StrictMock<MockExecutionIdManager> execution_id_manager;
273 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
274
275 EventEmitter<RangeCheckEvent> range_check_event_emitter;
276 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
277 EventEmitter<GreaterThanEvent> gt_event_emitter;
278
280 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
281 GreaterThan gt(field_gt, range_check, gt_event_emitter);
282 PureBitwise bitwise;
283
284 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
285 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, sha256_event_emitter);
286
287 MemoryAddress state_addr = static_cast<MemoryAddress>(AVM_HIGHEST_MEM_ADDRESS - 6); // This will be out of range
288 MemoryAddress input_addr = 8;
289 MemoryAddress output_addr = 25;
290
291 EXPECT_THROW_WITH_MESSAGE(sha256_gadget.compression(mem, state_addr, input_addr, output_addr),
292 ".*Memory address out of range.*");
293 TestTraceContainer trace;
294 trace.set(C::precomputed_first_row, 0, 1);
295
296 Sha256TraceBuilder builder;
297 const auto sha256_event_container = sha256_event_emitter.dump_events();
298 builder.process(sha256_event_container, trace);
299 GreaterThanTraceBuilder gt_builder;
300 gt_builder.process(gt_event_emitter.dump_events(), trace);
301
302 check_relation<sha256_mem>(trace);
303 check_relation<sha256>(trace);
304 check_interaction<Sha256TraceBuilder,
308}
309
310TEST(Sha256MemoryConstrainingTest, MultiOutOfRangeMemoryAddresses)
311{
312 MemoryStore mem;
313 StrictMock<MockExecutionIdManager> execution_id_manager;
314 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
315
316 EventEmitter<RangeCheckEvent> range_check_event_emitter;
317 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
318 EventEmitter<GreaterThanEvent> gt_event_emitter;
319
321 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
322 GreaterThan gt(field_gt, range_check, gt_event_emitter);
323 PureBitwise bitwise;
324
325 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
326 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, sha256_event_emitter);
327
328 MemoryAddress state_addr = static_cast<MemoryAddress>(AVM_HIGHEST_MEM_ADDRESS - 6); // This will be out of range
329 MemoryAddress input_addr = static_cast<MemoryAddress>(AVM_HIGHEST_MEM_ADDRESS - 2); // This will be out of range
330 MemoryAddress output_addr = static_cast<MemoryAddress>(AVM_HIGHEST_MEM_ADDRESS - 20); // This will be out of range
331
332 EXPECT_THROW_WITH_MESSAGE(sha256_gadget.compression(mem, state_addr, input_addr, output_addr),
333 ".*Memory address out of range.*");
334 TestTraceContainer trace;
335 trace.set(C::precomputed_first_row, 0, 1);
336
337 Sha256TraceBuilder builder;
338 const auto sha256_event_container = sha256_event_emitter.dump_events();
339 builder.process(sha256_event_container, trace);
340 GreaterThanTraceBuilder gt_builder;
341 gt_builder.process(gt_event_emitter.dump_events(), trace);
342
343 check_relation<sha256_mem>(trace);
344 check_relation<sha256>(trace);
345 check_interaction<Sha256TraceBuilder,
349}
350
351TEST(Sha256MemoryConstrainingTest, InvalidStateTagErr)
352{
353 MemoryStore mem;
354 StrictMock<MockExecutionIdManager> execution_id_manager;
355 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
356
357 EventEmitter<RangeCheckEvent> range_check_event_emitter;
358 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
359 EventEmitter<GreaterThanEvent> gt_event_emitter;
360
362 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
363 GreaterThan gt(field_gt, range_check, gt_event_emitter);
364 PureBitwise bitwise;
365
366 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
367 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, sha256_event_emitter);
368
369 std::array<uint32_t, 7> state = { 0, 1, 2, 3, 4, 5, 6 };
370 MemoryAddress state_addr = 0;
371 for (uint32_t i = 0; i < 7; ++i) {
372 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
373 }
374 // Add an invalid tag
375 mem.set(state_addr + 7, MemoryValue::from<uint64_t>(7));
376
377 MemoryAddress input_addr = 8;
378 MemoryAddress output_addr = 25;
379
380 EXPECT_THROW_WITH_MESSAGE(sha256_gadget.compression(mem, state_addr, input_addr, output_addr),
381 ".*Invalid tag for sha256 state values.*");
382 TestTraceContainer trace;
383 trace.set(C::precomputed_first_row, 0, 1);
384
385 Sha256TraceBuilder builder;
386 const auto sha256_event_container = sha256_event_emitter.dump_events();
387 builder.process(sha256_event_container, trace);
388 GreaterThanTraceBuilder gt_builder;
389 gt_builder.process(gt_event_emitter.dump_events(), trace);
390
391 check_relation<sha256_mem>(trace);
392 check_relation<sha256>(trace);
393 check_interaction<Sha256TraceBuilder,
397}
398
399TEST(Sha256MemoryConstrainingTest, InvalidInputTagErr)
400{
401 MemoryStore mem;
402 StrictMock<MockExecutionIdManager> execution_id_manager;
403 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
404
405 EventEmitter<RangeCheckEvent> range_check_event_emitter;
406 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
407 EventEmitter<GreaterThanEvent> gt_event_emitter;
408
410 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
411 GreaterThan gt(field_gt, range_check, gt_event_emitter);
412 PureBitwise bitwise;
413
414 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
415 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, sha256_event_emitter);
416
417 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
418 MemoryAddress state_addr = 0;
419 for (uint32_t i = 0; i < 8; ++i) {
420 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
421 }
422
423 std::array<uint32_t, 14> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
424 MemoryAddress input_addr = 8;
425 for (uint32_t i = 0; i < 14; ++i) {
426 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
427 }
428 mem.set(input_addr + 14, MemoryValue::from<uint64_t>(14)); // Add an invalid tag
429 mem.set(input_addr + 15, MemoryValue::from<uint64_t>(15)); // Add an invalid tag
430 MemoryAddress output_addr = 25;
431
432 EXPECT_THROW_WITH_MESSAGE(sha256_gadget.compression(mem, state_addr, input_addr, output_addr),
433 ".*Invalid tag for sha256 input values.*");
434 TestTraceContainer trace;
435 trace.set(C::precomputed_first_row, 0, 1);
436
437 Sha256TraceBuilder builder;
438 const auto sha256_event_container = sha256_event_emitter.dump_events();
439 builder.process(sha256_event_container, trace);
440 GreaterThanTraceBuilder gt_builder;
441 gt_builder.process(gt_event_emitter.dump_events(), trace);
442 if (getenv("AVM_DEBUG") != nullptr) {
443 InteractiveDebugger debugger(trace);
444 debugger.run();
445 }
446
447 check_relation<sha256_mem>(trace);
448 check_relation<sha256>(trace);
449 check_interaction<Sha256TraceBuilder,
453}
454
455TEST(Sha256MemoryConstrainingTest, PropagateError)
456{
457 MemoryStore mem;
458 StrictMock<MockExecutionIdManager> execution_id_manager;
459 EXPECT_CALL(execution_id_manager, get_execution_id()).WillOnce(Return(0));
460
461 EventEmitter<RangeCheckEvent> range_check_event_emitter;
462 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
463 EventEmitter<GreaterThanEvent> gt_event_emitter;
464 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
465
467 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
468 GreaterThan gt(field_gt, range_check, gt_event_emitter);
469 PureBitwise bitwise;
470
471 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, sha256_event_emitter);
472
473 MemoryAddress state_addr = 0;
474 MemoryAddress input_addr = 8;
475 MemoryAddress output_addr = 25;
476
477 // Set up execution trace
478 TestTraceContainer trace({
479 {
480 { C::precomputed_first_row, 1 },
481 // First invocation fails
482 { C::execution_sel, 1 },
483 { C::execution_context_id, mem.get_space_id() },
484 { C::execution_sel_exec_dispatch_sha256_compression, 1 },
485 { C::execution_rop_0_, output_addr },
486 { C::execution_rop_1_, state_addr },
487 { C::execution_rop_2_, input_addr },
488 { C::execution_sel_opcode_error, 1 },
489 },
490 });
491 // Add the state values to memory and the memory trace
492 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
493 for (uint32_t i = 0; i < state.size(); ++i) {
494 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
495 trace.set(i,
496 { {
497 { C::memory_sel, 1 },
498 { C::memory_space_id, mem.get_space_id() },
499 { C::memory_address, state_addr + i },
500 { C::memory_value, state[i] },
501 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U32) },
502 } });
503 }
504
505 // Add the input values to memory and the memory trace
506 std::array<uint32_t, 13> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
507 for (uint32_t i = 0; i < input.size(); ++i) {
508 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
509 trace.set(i + state.size(),
510 { {
511 { C::memory_sel, 1 },
512 { C::memory_space_id, mem.get_space_id() },
513 { C::memory_address, input_addr + i },
514 { C::memory_value, input[i] },
515 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U32) },
516 } });
517 }
518
519 // Add a 14th input that has an invalid tag
520 mem.set(input_addr + 13, MemoryValue::from<uint64_t>(13));
521 trace.set(state.size() + input.size(),
522 { {
523 { C::memory_sel, 1 },
524 { C::memory_space_id, mem.get_space_id() },
525 { C::memory_address, input_addr + 13 },
526 { C::memory_value, 13 },
527 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U64) }, // Invalid tag
528 } });
529
530 EXPECT_THROW(sha256_gadget.compression(mem, state_addr, input_addr, output_addr),
531 std::runtime_error); // This will be out of range and throw an error
532
533 Sha256TraceBuilder builder;
534 const auto sha256_event_container = sha256_event_emitter.dump_events();
535 builder.process(sha256_event_container, trace);
536
537 GreaterThanTraceBuilder gt_builder;
538 gt_builder.process(gt_event_emitter.dump_events(), trace);
539
540 PrecomputedTraceBuilder precomputed_builder;
541 precomputed_builder.process_misc(trace, 65); // Enough for round constants
543
544 if (getenv("AVM_DEBUG") != nullptr) {
545 InteractiveDebugger debugger(trace);
546 debugger.run();
547 }
548
549 check_relation<sha256_mem>(trace);
550 check_relation<sha256>(trace);
551 check_all_interactions<Sha256TraceBuilder>(trace);
552}
553
554TEST(Sha256MemoryConstrainingTest, Complex)
555{
556 MemoryStore mem;
557 StrictMock<MockExecutionIdManager> execution_id_manager;
558 EXPECT_CALL(execution_id_manager, get_execution_id()).WillOnce(Return(0)).WillOnce(Return(1));
559
560 EventEmitter<RangeCheckEvent> range_check_event_emitter;
561 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
562 EventEmitter<GreaterThanEvent> gt_event_emitter;
563 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
564 EventEmitter<BitwiseEvent> bitwise_event_emitter;
565
567 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
568 GreaterThan gt(field_gt, range_check, gt_event_emitter);
570
571 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, sha256_event_emitter);
572
573 MemoryAddress state_addr = 0;
574 MemoryAddress input_addr = 8;
575 MemoryAddress output_addr = 25;
576
577 // Set up execution trace
578 TestTraceContainer trace({
579 {
580 { C::precomputed_first_row, 1 },
581 // First invocation fails
582 { C::execution_sel, 1 },
583 { C::execution_context_id, mem.get_space_id() },
584 { C::execution_sel_exec_dispatch_sha256_compression, 1 },
585 { C::execution_rop_0_, static_cast<MemoryAddress>(AVM_HIGHEST_MEM_ADDRESS - 1) },
586 { C::execution_rop_1_, state_addr },
587 { C::execution_rop_2_, input_addr },
588 { C::execution_sel_opcode_error, 1 },
589 },
590 {
591 // Second invocation passes
592 { C::execution_sel, 1 },
593 { C::execution_context_id, mem.get_space_id() },
594 { C::execution_sel_exec_dispatch_sha256_compression, 1 },
595 { C::execution_rop_0_, output_addr },
596 { C::execution_rop_1_, state_addr },
597 { C::execution_rop_2_, input_addr },
598 },
599 });
600 // Add the state values to memory and the memory trace
601 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
602 for (uint32_t i = 0; i < state.size(); ++i) {
603 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
604 trace.set(i,
605 { {
606 { C::memory_sel, 1 },
607 { C::memory_clk, 1 },
608 { C::memory_space_id, mem.get_space_id() },
609 { C::memory_address, state_addr + i },
610 { C::memory_value, state[i] },
611 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U32) },
612 } });
613 }
614
615 // Add the input values to memory and the memory trace
616 std::array<uint32_t, 16> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
617 for (uint32_t i = 0; i < input.size(); ++i) {
618 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
619 trace.set(i + state.size(),
620 { {
621 { C::memory_sel, 1 },
622 { C::memory_clk, 1 },
623 { C::memory_space_id, mem.get_space_id() },
624 { C::memory_address, input_addr + i },
625 { C::memory_value, input[i] },
626 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U32) },
627 } });
628 }
629
630 // Compute the expected output and set it in memory
631 std::array<uint32_t, 8> expected_output = simulation::sha256_block(state, input);
632 for (uint32_t i = 0; i < expected_output.size(); ++i) {
633 mem.set(output_addr + i, MemoryValue::from<uint32_t>(expected_output[i]));
634 trace.set(i + state.size() + input.size(),
635 { {
636 { C::memory_sel, 1 },
637 { C::memory_clk, 1 },
638 { C::memory_space_id, mem.get_space_id() },
639 { C::memory_address, output_addr + i },
640 { C::memory_value, expected_output[i] },
641 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U32) },
642 { C::memory_rw, 1 }, // Write operations
643 } });
644 }
645
646 EXPECT_THROW(
647 sha256_gadget.compression(mem, state_addr, input_addr, static_cast<MemoryAddress>(AVM_HIGHEST_MEM_ADDRESS - 1)),
648 std::runtime_error); // This will be out of range and throw an error
649 sha256_gadget.compression(mem, state_addr, input_addr, output_addr); // This will succeed
650
651 Sha256TraceBuilder builder;
652 const auto sha256_event_container = sha256_event_emitter.dump_events();
653 builder.process(sha256_event_container, trace);
654
655 GreaterThanTraceBuilder gt_builder;
656 gt_builder.process(gt_event_emitter.dump_events(), trace);
657
658 PrecomputedTraceBuilder precomputed_builder;
659 precomputed_builder.process_misc(trace, 65); // Enough for round constants
661
662 BitwiseTraceBuilder bitwise_builder;
663 bitwise_builder.process(bitwise_event_emitter.dump_events(), trace);
664
665 if (getenv("AVM_DEBUG") != nullptr) {
666 InteractiveDebugger debugger(trace);
667 debugger.run();
668 }
669
670 check_relation<sha256_mem>(trace);
671 check_relation<sha256>(trace);
672 check_all_interactions<Sha256TraceBuilder>(trace);
673}
674
678
679// This test verifies that input_addr IS properly constrained on non-start rows.
680//
681// The sha256_mem.pil file propagates execution_clk, space_id, output_addr, and input_addr
682// using CONTINUITY_* constraints. The CONTINUITY_INPUT_ADDR constraint ensures that
683// input_addr increments by 1 during input rounds (when sel_is_input_round=1) and stays
684// constant otherwise. This prevents malicious provers from reading arbitrary memory.
685//
686// APPROACH: Generate a valid SHA256 trace, then tamper with input_addr on a non-start row.
687// The CONTINUITY_INPUT_ADDR constraint should catch this tampering and cause relation check to fail.
688TEST(Sha256MemoryConstrainingTest, InputAddrTamperingIsCaughtByConstraint)
689{
690 // Step 1: Generate a valid SHA256 compression trace using the actual simulation
691 MemoryStore mem;
692 StrictMock<MockExecutionIdManager> execution_id_manager;
693 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
694 PureGreaterThan gt;
695 PureBitwise bitwise;
696
697 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
698 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, sha256_event_emitter);
699
700 // Set up valid memory for state and input
701 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
702 MemoryAddress state_addr = 0;
703 for (uint32_t i = 0; i < 8; ++i) {
704 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
705 }
706
707 std::array<uint32_t, 16> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
708 MemoryAddress input_addr = 8; // Legitimate input address
709 for (uint32_t i = 0; i < 16; ++i) {
710 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
711 }
712 MemoryAddress output_addr = 25;
713
714 // Execute SHA256 compression (this generates valid events)
715 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
716
717 // Build trace from events
718 TestTraceContainer trace;
719 trace.set(C::precomputed_first_row, 0, 1);
720 Sha256TraceBuilder builder;
721 builder.process(sha256_event_emitter.dump_events(), trace);
722
723 // Step 2: Verify the trace is valid BEFORE tampering
724 ASSERT_NO_THROW(check_relation<sha256_mem>(trace)) << "Trace should be valid before tampering";
725
726 // Step 3: Tamper with input_addr on row 1 (a non-start input round)
727 // The constraint should catch this because input_addr[1] must equal input_addr[0] + 1
728 constexpr uint32_t MALICIOUS_ADDR = 9999;
729 trace.set(C::sha256_input_addr, 1, MALICIOUS_ADDR);
730
731 // Step 4: Verify the CONTINUITY_INPUT_ADDR constraint catches the tampering
732 // The constraint enforces: input_addr' = input_addr + sel_is_input_round
733 EXPECT_THROW_WITH_MESSAGE(check_relation<sha256_mem>(trace, sha256_mem::SR_CONTINUITY_INPUT_ADDR),
734 "CONTINUITY_INPUT_ADDR");
735}
736
740
741// This test verifies that init_a through init_h are properly propagated across multi-row computation.
742//
743// BACKGROUND: SHA256 compression uses init_a-init_h values loaded from memory on row 0, and these
744// values are used in the final output calculation (OUT_X = x + init_x) on the last row.
745//
746// The PROPAGATE_INIT_* constraints (sha256.pil lines 111-126) ensure that init values remain
747// constant across all rounds. Without these constraints, a malicious prover could:
748// 1. Set correct init_a on row 0 (to pass memory read constraint)
749// 2. Set arbitrary init_a on later rows
750// 3. Corrupt the final SHA256 output
751//
752// This test verifies that tampering with init_a is caught by the PROPAGATE_INIT_A constraint.
753TEST(Sha256ConstrainingTest, InitStateTamperingIsCaughtByPropagationConstraint)
754{
755 // Generate a valid SHA256 compression trace
756 MemoryStore mem;
757 StrictMock<MockExecutionIdManager> execution_id_manager;
758 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
759 PureGreaterThan gt;
760 PureBitwise bitwise;
761
762 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
763 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, sha256_event_emitter);
764
765 // Set up valid memory for state and input
766 std::array<uint32_t, 8> state = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
767 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 };
768 MemoryAddress state_addr = 0;
769 for (uint32_t i = 0; i < 8; ++i) {
770 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
771 }
772
773 std::array<uint32_t, 16> input = { 0x61626380, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x18 };
774 MemoryAddress input_addr = 8;
775 for (uint32_t i = 0; i < 16; ++i) {
776 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
777 }
778 MemoryAddress output_addr = 25;
779
780 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
781
782 TestTraceContainer trace;
783 trace.set(C::precomputed_first_row, 0, 1);
784 Sha256TraceBuilder builder;
785 builder.process(sha256_event_emitter.dump_events(), trace);
786
787 // Verify the trace is valid before tampering
788 ASSERT_NO_THROW(check_relation<sha256>(trace));
789
790 // Find a row where perform_round=1 (any round row works, but use row 1 for simplicity)
791 // Row 0 is start, rows 1-64 are rounds with perform_round=1
792 constexpr uint32_t TAMPER_ROW = 1;
793 ASSERT_EQ(trace.get(C::sha256_perform_round, TAMPER_ROW), FF(1)) << "Row 1 should have perform_round=1";
794
795 // Tamper with init_a on row 1 (making it different from row 0)
796 FF original_init_a = trace.get(C::sha256_init_a, TAMPER_ROW);
797 FF tampered_init_a = original_init_a + FF(0x12345678);
798 trace.set(C::sha256_init_a, TAMPER_ROW, tampered_init_a);
799
800 // The PROPAGATE_INIT_A constraint should catch this:
801 // perform_round * (init_a' - init_a) = 0
802 // On row 0: perform_round=1, init_a'=tampered, init_a=original -> constraint violated
803 EXPECT_THROW_WITH_MESSAGE(check_relation<sha256>(trace, sha256::SR_PROPAGATE_INIT_A), "PROPAGATE_INIT_A");
804}
805
806} // namespace
807} // namespace bb::avm2::constraining
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:193
#define AVM_HIGHEST_MEM_ADDRESS
FieldGreaterThan field_gt
RangeCheck range_check
static constexpr size_t SR_CONTINUITY_INPUT_ADDR
static constexpr size_t SR_PROPAGATE_INIT_A
Definition sha256.hpp:42
void set(MemoryAddress index, MemoryValue value) override
uint16_t get_space_id() const override
void process(const simulation::EventEmitterInterface< simulation::AluEvent >::Container &events, TraceContainer &trace)
Process the ALU events and populate the ALU relevant columns in the trace.
void process(const simulation::EventEmitterInterface< simulation::GreaterThanEvent >::Container &events, TraceContainer &trace)
Process the greater-than events and populate the relevant columns in the trace.
Definition gt_trace.cpp:20
void process_sha256_round_constants(TraceContainer &trace)
Populate the 64 SHA-256 round constants (K_0 .. K_63) and their selector. The sel_sha256_compression ...
void process_misc(TraceContainer &trace, const uint32_t num_rows=PRECOMPUTED_TRACE_SIZE)
Populate miscellaneous precomputed columns: first_row selector and idx (row index).
const FF & get(Column col, uint32_t row) const
void set(Column col, uint32_t row, const FF &value)
PrecomputedTraceBuilder precomputed_builder
Definition alu.test.cpp:120
AluTraceBuilder builder
Definition alu.test.cpp:124
GreaterThanTraceBuilder gt_builder
Definition alu.test.cpp:123
EventEmitter< GreaterThanEvent > gt_event_emitter
ExecutionIdManager execution_id_manager
MemoryStore mem
EventEmitter< RangeCheckEvent > range_check_event_emitter
GreaterThan gt
TestTraceContainer trace
void check_interaction(tracegen::TestTraceContainer &trace)
TEST(AvmFixedVKTests, FixedVKCommitments)
Test that the fixed VK commitments agree with the ones computed from precomputed columns.
std::array< uint32_t, 8 > sha256_block(const std::array< uint32_t, 8 > &h_init, const std::array< uint32_t, 16 > &input)
TestTraceContainer empty_trace()
Definition fixtures.cpp:153
lookup_settings< lookup_sha256_range_rhs_e_6_settings_ > lookup_sha256_range_rhs_e_6_settings
lookup_settings< lookup_sha256_w_s_1_xor_1_settings_ > lookup_sha256_w_s_1_xor_1_settings
lookup_settings< lookup_sha256_range_comp_w_rhs_settings_ > lookup_sha256_range_comp_w_rhs_settings
lookup_settings< lookup_sha256_range_rhs_a_13_settings_ > lookup_sha256_range_rhs_a_13_settings
lookup_settings< lookup_sha256_range_rhs_e_11_settings_ > lookup_sha256_range_rhs_e_11_settings
lookup_settings< lookup_sha256_range_rhs_a_22_settings_ > lookup_sha256_range_rhs_a_22_settings
lookup_settings< lookup_sha256_range_comp_h_rhs_settings_ > lookup_sha256_range_comp_h_rhs_settings
lookup_settings< lookup_sha256_mem_check_input_addr_in_range_settings_ > lookup_sha256_mem_check_input_addr_in_range_settings
lookup_settings< lookup_sha256_maj_xor_0_settings_ > lookup_sha256_maj_xor_0_settings
lookup_settings< lookup_sha256_s_1_xor_0_settings_ > lookup_sha256_s_1_xor_0_settings
lookup_settings< lookup_sha256_w_s_0_xor_0_settings_ > lookup_sha256_w_s_0_xor_0_settings
lookup_settings< lookup_sha256_range_rhs_w_18_settings_ > lookup_sha256_range_rhs_w_18_settings
lookup_settings< lookup_sha256_w_s_1_xor_0_settings_ > lookup_sha256_w_s_1_xor_0_settings
lookup_settings< lookup_sha256_range_comp_w_lhs_settings_ > lookup_sha256_range_comp_w_lhs_settings
lookup_settings< lookup_sha256_s_1_xor_1_settings_ > lookup_sha256_s_1_xor_1_settings
lookup_settings< lookup_sha256_range_comp_b_rhs_settings_ > lookup_sha256_range_comp_b_rhs_settings
lookup_settings< lookup_sha256_range_rhs_w_10_settings_ > lookup_sha256_range_rhs_w_10_settings
lookup_settings< lookup_sha256_ch_xor_settings_ > lookup_sha256_ch_xor_settings
lookup_settings< lookup_sha256_range_comp_c_rhs_settings_ > lookup_sha256_range_comp_c_rhs_settings
lookup_settings< lookup_sha256_range_comp_next_e_lhs_settings_ > lookup_sha256_range_comp_next_e_lhs_settings
lookup_settings< lookup_sha256_maj_and_0_settings_ > lookup_sha256_maj_and_0_settings
AvmFlavorSettings::FF FF
Definition field.hpp:10
lookup_settings< lookup_sha256_ch_and_0_settings_ > lookup_sha256_ch_and_0_settings
lookup_settings< lookup_sha256_maj_and_2_settings_ > lookup_sha256_maj_and_2_settings
lookup_settings< lookup_sha256_range_rhs_w_3_settings_ > lookup_sha256_range_rhs_w_3_settings
lookup_settings< lookup_sha256_round_constant_settings_ > lookup_sha256_round_constant_settings
lookup_settings< lookup_sha256_mem_check_state_addr_in_range_settings_ > lookup_sha256_mem_check_state_addr_in_range_settings
lookup_settings< lookup_sha256_range_rhs_e_25_settings_ > lookup_sha256_range_rhs_e_25_settings
lookup_settings< lookup_sha256_range_comp_next_e_rhs_settings_ > lookup_sha256_range_comp_next_e_rhs_settings
lookup_settings< lookup_sha256_maj_xor_1_settings_ > lookup_sha256_maj_xor_1_settings
lookup_settings< lookup_sha256_range_comp_f_rhs_settings_ > lookup_sha256_range_comp_f_rhs_settings
lookup_settings< lookup_sha256_range_comp_next_a_lhs_settings_ > lookup_sha256_range_comp_next_a_lhs_settings
lookup_settings< lookup_sha256_s_0_xor_0_settings_ > lookup_sha256_s_0_xor_0_settings
lookup_settings< lookup_sha256_w_s_0_xor_1_settings_ > lookup_sha256_w_s_0_xor_1_settings
lookup_settings< lookup_sha256_range_rhs_a_2_settings_ > lookup_sha256_range_rhs_a_2_settings
lookup_settings< lookup_sha256_range_comp_g_rhs_settings_ > lookup_sha256_range_comp_g_rhs_settings
lookup_settings< lookup_sha256_range_rhs_w_17_settings_ > lookup_sha256_range_rhs_w_17_settings
lookup_settings< lookup_sha256_s_0_xor_1_settings_ > lookup_sha256_s_0_xor_1_settings
lookup_settings< lookup_sha256_range_rhs_w_7_settings_ > lookup_sha256_range_rhs_w_7_settings
lookup_settings< lookup_sha256_ch_and_1_settings_ > lookup_sha256_ch_and_1_settings
lookup_settings< lookup_sha256_maj_and_1_settings_ > lookup_sha256_maj_and_1_settings
lookup_settings< lookup_sha256_mem_check_output_addr_in_range_settings_ > lookup_sha256_mem_check_output_addr_in_range_settings
lookup_settings< lookup_sha256_range_comp_next_a_rhs_settings_ > lookup_sha256_range_comp_next_a_rhs_settings
lookup_settings< lookup_sha256_range_rhs_w_19_settings_ > lookup_sha256_range_rhs_w_19_settings
lookup_settings< lookup_sha256_range_comp_d_rhs_settings_ > lookup_sha256_range_comp_d_rhs_settings
uint32_t MemoryAddress
lookup_settings< lookup_sha256_range_comp_a_rhs_settings_ > lookup_sha256_range_comp_a_rhs_settings
lookup_settings< lookup_sha256_range_comp_e_rhs_settings_ > lookup_sha256_range_comp_e_rhs_settings
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
PureSha256 sha256
Bitwise bitwise
NoopEventEmitter< FieldGreaterThanEvent > field_gt_event_emitter
NoopEventEmitter< BitwiseEvent > bitwise_event_emitter