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
29// Temporary imports, see comment in test.
35
36namespace bb::avm2::constraining {
37namespace {
38
39// todo(ilyas): add negative tests
40
41using ::testing::Return;
42using ::testing::StrictMock;
43
44using simulation::Bitwise;
45using simulation::BitwiseEvent;
46using simulation::DeduplicatingEventEmitter;
47using simulation::EventEmitter;
48using simulation::FieldGreaterThan;
49using simulation::FieldGreaterThanEvent;
50using simulation::GreaterThan;
51using simulation::GreaterThanEvent;
52using simulation::MemoryStore;
53using simulation::MockExecutionIdManager;
54using simulation::PureBitwise;
55using simulation::PureGreaterThan;
56using simulation::RangeCheck;
57using simulation::RangeCheckEvent;
58using simulation::Sha256;
59using simulation::Sha256CompressionEvent;
60
61using tracegen::BitwiseTraceBuilder;
62using tracegen::GreaterThanTraceBuilder;
63using tracegen::PrecomputedTraceBuilder;
64using tracegen::RangeCheckTraceBuilder;
65using tracegen::Sha256TraceBuilder;
66using tracegen::TestTraceContainer;
67
69using C = Column;
71using sha256_mem = bb::avm2::sha256_mem<FF>;
72
73TEST(Sha256ConstrainingTest, EmptyRow)
74{
75 check_relation<sha256>(testing::empty_trace());
76 check_relation<sha256_mem>(testing::empty_trace());
77}
78
79// This test imports a bunch of external code since hand-generating the sha256 trace is a bit laborious atm.
80// The test is a bit of a placeholder for now.
81// TOOD: Replace this with a hardcoded test vector and write a negative test
82TEST(Sha256ConstrainingTest, Basic)
83{
84 MemoryStore mem;
85 StrictMock<MockExecutionIdManager> execution_id_manager;
86 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
87 PureGreaterThan gt;
88 PureBitwise bitwise;
89 EventEmitter<RangeCheckEvent> range_check_event_emitter;
91
92 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
93 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, range_check, sha256_event_emitter);
94
95 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
96 MemoryAddress state_addr = 0;
97 for (uint32_t i = 0; i < 8; ++i) {
98 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
99 }
100
101 std::array<uint32_t, 16> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
102 MemoryAddress input_addr = 8;
103 for (uint32_t i = 0; i < 16; ++i) {
104 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
105 }
106 MemoryAddress output_addr = 25;
107
108 // We do two compression operations just to ensure the "after-latch" relations are correct
109 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
110 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
111 TestTraceContainer trace;
112 trace.set(C::precomputed_first_row, 0, 1);
113 Sha256TraceBuilder builder;
114 const auto sha256_event_container = sha256_event_emitter.dump_events();
115 builder.process(sha256_event_container, trace);
116
117 check_relation<sha256>(trace);
118}
119
120TEST(Sha256ConstrainingTest, Interaction)
121{
122 MemoryStore mem;
123 StrictMock<MockExecutionIdManager> execution_id_manager;
124 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
125 EventEmitter<BitwiseEvent> bitwise_event_emitter;
126 EventEmitter<GreaterThanEvent> gt_event_emitter;
127 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
128 EventEmitter<RangeCheckEvent> range_check_event_emitter;
129
131 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
132 GreaterThan gt(field_gt, range_check, gt_event_emitter);
133
135
136 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
137 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, range_check, sha256_event_emitter);
138
139 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
140 MemoryAddress state_addr = 0;
141 for (uint32_t i = 0; i < 8; ++i) {
142 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
143 }
144
145 std::array<uint32_t, 16> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
146 MemoryAddress input_addr = 8;
147 for (uint32_t i = 0; i < 16; ++i) {
148 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
149 }
150 MemoryAddress output_addr = 25;
151
152 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
153
154 TestTraceContainer trace;
155 Sha256TraceBuilder builder;
156 PrecomputedTraceBuilder precomputed_builder;
157 // Build just enough clk rows for the lookup
160
161 BitwiseTraceBuilder bitwise_builder;
162 bitwise_builder.process(bitwise_event_emitter.dump_events(), trace);
163
164 GreaterThanTraceBuilder gt_builder;
165 gt_builder.process(gt_event_emitter.dump_events(), trace);
166
167 RangeCheckTraceBuilder range_check_builder;
169
170 builder.process(sha256_event_emitter.get_events(), trace);
171
172 // Check bitwise and round constant lookups
173 check_interaction<Sha256TraceBuilder,
217
218 check_relation<sha256>(trace);
219}
220
224
225TEST(Sha256MemoryConstrainingTest, Basic)
226{
227 MemoryStore mem;
228 StrictMock<MockExecutionIdManager> execution_id_manager;
229 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
230
231 EventEmitter<RangeCheckEvent> range_check_event_emitter;
232 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
233 EventEmitter<GreaterThanEvent> gt_event_emitter;
234
236 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
237 GreaterThan gt(field_gt, range_check, gt_event_emitter);
238 PureBitwise bitwise;
239
240 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
241 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, range_check, sha256_event_emitter);
242
243 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
244 MemoryAddress state_addr = 0;
245 for (uint32_t i = 0; i < 8; ++i) {
246 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
247 }
248
249 std::array<uint32_t, 16> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
250 MemoryAddress input_addr = 8;
251 for (uint32_t i = 0; i < 16; ++i) {
252 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
253 }
254 MemoryAddress output_addr = 25;
255
256 // We do two compression operations just to ensure the "after-latch" relations are correct
257 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
258 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
259 TestTraceContainer trace;
260 trace.set(C::precomputed_first_row, 0, 1);
261
262 Sha256TraceBuilder builder;
263 const auto sha256_event_container = sha256_event_emitter.dump_events();
264 builder.process(sha256_event_container, trace);
265 GreaterThanTraceBuilder gt_builder;
266 gt_builder.process(gt_event_emitter.dump_events(), trace);
267
268 check_relation<sha256_mem>(trace);
269 check_relation<sha256>(trace);
270 check_interaction<Sha256TraceBuilder,
274}
275
276TEST(Sha256MemoryConstrainingTest, SimpleOutOfRangeMemoryAddresses)
277{
278 MemoryStore mem;
279 StrictMock<MockExecutionIdManager> execution_id_manager;
280 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
281
282 EventEmitter<RangeCheckEvent> range_check_event_emitter;
283 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
284 EventEmitter<GreaterThanEvent> gt_event_emitter;
285
287 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
288 GreaterThan gt(field_gt, range_check, gt_event_emitter);
289 PureBitwise bitwise;
290
291 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
292 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, range_check, sha256_event_emitter);
293
294 MemoryAddress state_addr = static_cast<MemoryAddress>(AVM_HIGHEST_MEM_ADDRESS - 6); // This will be out of range
295 MemoryAddress input_addr = 8;
296 MemoryAddress output_addr = 25;
297
298 EXPECT_THROW_WITH_MESSAGE(sha256_gadget.compression(mem, state_addr, input_addr, output_addr),
299 ".*Memory address out of range.*");
300 TestTraceContainer trace;
301 trace.set(C::precomputed_first_row, 0, 1);
302
303 Sha256TraceBuilder builder;
304 const auto sha256_event_container = sha256_event_emitter.dump_events();
305 builder.process(sha256_event_container, trace);
306 GreaterThanTraceBuilder gt_builder;
307 gt_builder.process(gt_event_emitter.dump_events(), trace);
308
309 check_relation<sha256_mem>(trace);
310 check_relation<sha256>(trace);
311 check_interaction<Sha256TraceBuilder,
315}
316
317TEST(Sha256MemoryConstrainingTest, MultiOutOfRangeMemoryAddresses)
318{
319 MemoryStore mem;
320 StrictMock<MockExecutionIdManager> execution_id_manager;
321 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
322
323 EventEmitter<RangeCheckEvent> range_check_event_emitter;
324 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
325 EventEmitter<GreaterThanEvent> gt_event_emitter;
326
328 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
329 GreaterThan gt(field_gt, range_check, gt_event_emitter);
330 PureBitwise bitwise;
331
332 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
333 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, range_check, sha256_event_emitter);
334
335 MemoryAddress state_addr = static_cast<MemoryAddress>(AVM_HIGHEST_MEM_ADDRESS - 6); // This will be out of range
336 MemoryAddress input_addr = static_cast<MemoryAddress>(AVM_HIGHEST_MEM_ADDRESS - 2); // This will be out of range
337 MemoryAddress output_addr = static_cast<MemoryAddress>(AVM_HIGHEST_MEM_ADDRESS - 20); // This will be out of range
338
339 EXPECT_THROW_WITH_MESSAGE(sha256_gadget.compression(mem, state_addr, input_addr, output_addr),
340 ".*Memory address out of range.*");
341 TestTraceContainer trace;
342 trace.set(C::precomputed_first_row, 0, 1);
343
344 Sha256TraceBuilder builder;
345 const auto sha256_event_container = sha256_event_emitter.dump_events();
346 builder.process(sha256_event_container, trace);
347 GreaterThanTraceBuilder gt_builder;
348 gt_builder.process(gt_event_emitter.dump_events(), trace);
349
350 check_relation<sha256_mem>(trace);
351 check_relation<sha256>(trace);
352 check_interaction<Sha256TraceBuilder,
356}
357
358TEST(Sha256MemoryConstrainingTest, InvalidStateTagErr)
359{
360 MemoryStore mem;
361 StrictMock<MockExecutionIdManager> execution_id_manager;
362 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
363
364 EventEmitter<RangeCheckEvent> range_check_event_emitter;
365 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
366 EventEmitter<GreaterThanEvent> gt_event_emitter;
367
369 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
370 GreaterThan gt(field_gt, range_check, gt_event_emitter);
371 PureBitwise bitwise;
372
373 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
374 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, range_check, sha256_event_emitter);
375
376 std::array<uint32_t, 7> state = { 0, 1, 2, 3, 4, 5, 6 };
377 MemoryAddress state_addr = 0;
378 for (uint32_t i = 0; i < 7; ++i) {
379 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
380 }
381 // Add an invalid tag
382 mem.set(state_addr + 7, MemoryValue::from<uint64_t>(7));
383
384 MemoryAddress input_addr = 8;
385 MemoryAddress output_addr = 25;
386
387 EXPECT_THROW_WITH_MESSAGE(sha256_gadget.compression(mem, state_addr, input_addr, output_addr),
388 ".*Invalid tag for sha256 state values.*");
389 TestTraceContainer trace;
390 trace.set(C::precomputed_first_row, 0, 1);
391
392 Sha256TraceBuilder builder;
393 const auto sha256_event_container = sha256_event_emitter.dump_events();
394 builder.process(sha256_event_container, trace);
395 GreaterThanTraceBuilder gt_builder;
396 gt_builder.process(gt_event_emitter.dump_events(), trace);
397
398 check_relation<sha256_mem>(trace);
399 check_relation<sha256>(trace);
400 check_interaction<Sha256TraceBuilder,
404}
405
406TEST(Sha256MemoryConstrainingTest, InvalidInputTagErr)
407{
408 MemoryStore mem;
409 StrictMock<MockExecutionIdManager> execution_id_manager;
410 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
411
412 EventEmitter<RangeCheckEvent> range_check_event_emitter;
413 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
414 EventEmitter<GreaterThanEvent> gt_event_emitter;
415
417 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
418 GreaterThan gt(field_gt, range_check, gt_event_emitter);
419 PureBitwise bitwise;
420
421 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
422 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, range_check, sha256_event_emitter);
423
424 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
425 MemoryAddress state_addr = 0;
426 for (uint32_t i = 0; i < 8; ++i) {
427 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
428 }
429
430 std::array<uint32_t, 14> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
431 MemoryAddress input_addr = 8;
432 for (uint32_t i = 0; i < 14; ++i) {
433 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
434 }
435 mem.set(input_addr + 14, MemoryValue::from<uint64_t>(14)); // Add an invalid tag
436 mem.set(input_addr + 15, MemoryValue::from<uint64_t>(15)); // Add an invalid tag
437 MemoryAddress output_addr = 25;
438
439 EXPECT_THROW_WITH_MESSAGE(sha256_gadget.compression(mem, state_addr, input_addr, output_addr),
440 ".*Invalid tag for sha256 input values.*");
441 TestTraceContainer trace;
442 trace.set(C::precomputed_first_row, 0, 1);
443
444 Sha256TraceBuilder builder;
445 const auto sha256_event_container = sha256_event_emitter.dump_events();
446 builder.process(sha256_event_container, trace);
447 GreaterThanTraceBuilder gt_builder;
448 gt_builder.process(gt_event_emitter.dump_events(), trace);
449 if (getenv("AVM_DEBUG") != nullptr) {
450 InteractiveDebugger debugger(trace);
451 debugger.run();
452 }
453
454 check_relation<sha256_mem>(trace);
455 check_relation<sha256>(trace);
456 check_interaction<Sha256TraceBuilder,
460}
461
462TEST(Sha256MemoryConstrainingTest, PropagateError)
463{
464 MemoryStore mem;
465 StrictMock<MockExecutionIdManager> execution_id_manager;
466 EXPECT_CALL(execution_id_manager, get_execution_id()).WillOnce(Return(0));
467
468 EventEmitter<RangeCheckEvent> range_check_event_emitter;
469 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
470 EventEmitter<GreaterThanEvent> gt_event_emitter;
471 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
472
474 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
475 GreaterThan gt(field_gt, range_check, gt_event_emitter);
476 PureBitwise bitwise;
477
478 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, range_check, sha256_event_emitter);
479
480 MemoryAddress state_addr = 0;
481 MemoryAddress input_addr = 8;
482 MemoryAddress output_addr = 25;
483
484 // Set up execution trace
485 TestTraceContainer trace({
486 {
487 { C::precomputed_first_row, 1 },
488 // First invocation fails
489 { C::execution_sel, 1 },
490 { C::execution_context_id, mem.get_space_id() },
491 { C::execution_sel_exec_dispatch_sha256_compression, 1 },
492 { C::execution_rop_0_, output_addr },
493 { C::execution_rop_1_, state_addr },
494 { C::execution_rop_2_, input_addr },
495 { C::execution_sel_opcode_error, 1 },
496 },
497 });
498 // Add the state values to memory and the memory trace
499 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
500 for (uint32_t i = 0; i < state.size(); ++i) {
501 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
502 trace.set(i,
503 { {
504 { C::memory_sel, 1 },
505 { C::memory_space_id, mem.get_space_id() },
506 { C::memory_address, state_addr + i },
507 { C::memory_value, state[i] },
508 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U32) },
509 } });
510 }
511
512 // Add the input values to memory and the memory trace
513 std::array<uint32_t, 13> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
514 for (uint32_t i = 0; i < input.size(); ++i) {
515 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
516 trace.set(i + state.size(),
517 { {
518 { C::memory_sel, 1 },
519 { C::memory_space_id, mem.get_space_id() },
520 { C::memory_address, input_addr + i },
521 { C::memory_value, input[i] },
522 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U32) },
523 } });
524 }
525
526 // Add a 14th input that has an invalid tag
527 mem.set(input_addr + 13, MemoryValue::from<uint64_t>(13));
528 trace.set(state.size() + input.size(),
529 { {
530 { C::memory_sel, 1 },
531 { C::memory_space_id, mem.get_space_id() },
532 { C::memory_address, input_addr + 13 },
533 { C::memory_value, 13 },
534 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U64) }, // Invalid tag
535 } });
536
537 EXPECT_THROW(sha256_gadget.compression(mem, state_addr, input_addr, output_addr),
538 std::runtime_error); // This will be out of range and throw an error
539
540 Sha256TraceBuilder builder;
541 const auto sha256_event_container = sha256_event_emitter.dump_events();
542 builder.process(sha256_event_container, trace);
543
544 GreaterThanTraceBuilder gt_builder;
545 gt_builder.process(gt_event_emitter.dump_events(), trace);
546
547 PrecomputedTraceBuilder precomputed_builder;
548 precomputed_builder.process_misc(trace, 65); // Enough for round constants
550
551 if (getenv("AVM_DEBUG") != nullptr) {
552 InteractiveDebugger debugger(trace);
553 debugger.run();
554 }
555
556 check_relation<sha256_mem>(trace);
557 check_relation<sha256>(trace);
558 check_all_interactions<Sha256TraceBuilder>(trace);
559}
560
561TEST(Sha256MemoryConstrainingTest, Complex)
562{
563 MemoryStore mem;
564 StrictMock<MockExecutionIdManager> execution_id_manager;
565 EXPECT_CALL(execution_id_manager, get_execution_id()).WillOnce(Return(0)).WillOnce(Return(1));
566
567 EventEmitter<RangeCheckEvent> range_check_event_emitter;
568 DeduplicatingEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
569 EventEmitter<GreaterThanEvent> gt_event_emitter;
570 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
571 EventEmitter<BitwiseEvent> bitwise_event_emitter;
572
574 FieldGreaterThan field_gt(range_check, field_gt_event_emitter);
575 GreaterThan gt(field_gt, range_check, gt_event_emitter);
577
578 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, range_check, sha256_event_emitter);
579
580 MemoryAddress state_addr = 0;
581 MemoryAddress input_addr = 8;
582 MemoryAddress output_addr = 25;
583
584 // Set up execution trace
585 TestTraceContainer trace({
586 {
587 { C::precomputed_first_row, 1 },
588 // First invocation fails
589 { C::execution_sel, 1 },
590 { C::execution_context_id, mem.get_space_id() },
591 { C::execution_sel_exec_dispatch_sha256_compression, 1 },
592 { C::execution_rop_0_, static_cast<MemoryAddress>(AVM_HIGHEST_MEM_ADDRESS - 1) },
593 { C::execution_rop_1_, state_addr },
594 { C::execution_rop_2_, input_addr },
595 { C::execution_sel_opcode_error, 1 },
596 },
597 {
598 // Second invocation passes
599 { C::execution_sel, 1 },
600 { C::execution_context_id, mem.get_space_id() },
601 { C::execution_sel_exec_dispatch_sha256_compression, 1 },
602 { C::execution_rop_0_, output_addr },
603 { C::execution_rop_1_, state_addr },
604 { C::execution_rop_2_, input_addr },
605 },
606 });
607 // Add the state values to memory and the memory trace
608 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
609 for (uint32_t i = 0; i < state.size(); ++i) {
610 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
611 trace.set(i,
612 { {
613 { C::memory_sel, 1 },
614 { C::memory_clk, 1 },
615 { C::memory_space_id, mem.get_space_id() },
616 { C::memory_address, state_addr + i },
617 { C::memory_value, state[i] },
618 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U32) },
619 } });
620 }
621
622 // Add the input values to memory and the memory trace
623 std::array<uint32_t, 16> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
624 for (uint32_t i = 0; i < input.size(); ++i) {
625 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
626 trace.set(i + state.size(),
627 { {
628 { C::memory_sel, 1 },
629 { C::memory_clk, 1 },
630 { C::memory_space_id, mem.get_space_id() },
631 { C::memory_address, input_addr + i },
632 { C::memory_value, input[i] },
633 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U32) },
634 } });
635 }
636
637 // Compute the expected output and set it in memory
638 std::array<uint32_t, 8> expected_output = simulation::sha256_block(state, input);
639 for (uint32_t i = 0; i < expected_output.size(); ++i) {
640 mem.set(output_addr + i, MemoryValue::from<uint32_t>(expected_output[i]));
641 trace.set(i + state.size() + input.size(),
642 { {
643 { C::memory_sel, 1 },
644 { C::memory_clk, 1 },
645 { C::memory_space_id, mem.get_space_id() },
646 { C::memory_address, output_addr + i },
647 { C::memory_value, expected_output[i] },
648 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U32) },
649 { C::memory_rw, 1 }, // Write operations
650 } });
651 }
652
653 EXPECT_THROW(
654 sha256_gadget.compression(mem, state_addr, input_addr, static_cast<MemoryAddress>(AVM_HIGHEST_MEM_ADDRESS - 1)),
655 std::runtime_error); // This will be out of range and throw an error
656 sha256_gadget.compression(mem, state_addr, input_addr, output_addr); // This will succeed
657
658 Sha256TraceBuilder builder;
659 const auto sha256_event_container = sha256_event_emitter.dump_events();
660 builder.process(sha256_event_container, trace);
661
662 GreaterThanTraceBuilder gt_builder;
663 gt_builder.process(gt_event_emitter.dump_events(), trace);
664
665 RangeCheckTraceBuilder range_check_builder;
667
668 PrecomputedTraceBuilder precomputed_builder;
669 precomputed_builder.process_misc(trace, 65); // Enough for round constants
671
672 BitwiseTraceBuilder bitwise_builder;
673 bitwise_builder.process(bitwise_event_emitter.dump_events(), trace);
674
675 if (getenv("AVM_DEBUG") != nullptr) {
676 InteractiveDebugger debugger(trace);
677 debugger.run();
678 }
679
680 check_relation<sha256_mem>(trace);
681 check_relation<sha256>(trace);
682 check_all_interactions<Sha256TraceBuilder>(trace);
683}
684
688
689// This test verifies that input_addr IS properly constrained on non-start rows.
690//
691// The sha256_mem.pil file propagates execution_clk, space_id, output_addr, and input_addr
692// using CONTINUITY_* constraints. The CONTINUITY_INPUT_ADDR constraint ensures that
693// input_addr increments by 1 during input rounds (when sel_is_input_round=1) and stays
694// constant otherwise. This prevents malicious provers from reading arbitrary memory.
695//
696// APPROACH: Generate a valid SHA256 trace, then tamper with input_addr on a non-start row.
697// The CONTINUITY_INPUT_ADDR constraint should catch this tampering and cause relation check to fail.
698TEST(Sha256MemoryConstrainingTest, InputAddrTamperingIsCaughtByConstraint)
699{
700 // Step 1: Generate a valid SHA256 compression trace using the actual simulation
701 MemoryStore mem;
702 StrictMock<MockExecutionIdManager> execution_id_manager;
703 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
704 PureGreaterThan gt;
705 PureBitwise bitwise;
706 EventEmitter<RangeCheckEvent> range_check_event_emitter;
708
709 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
710 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, range_check, sha256_event_emitter);
711
712 // Set up valid memory for state and input
713 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
714 MemoryAddress state_addr = 0;
715 for (uint32_t i = 0; i < 8; ++i) {
716 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
717 }
718
719 std::array<uint32_t, 16> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
720 MemoryAddress input_addr = 8; // Legitimate input address
721 for (uint32_t i = 0; i < 16; ++i) {
722 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
723 }
724 MemoryAddress output_addr = 25;
725
726 // Execute SHA256 compression (this generates valid events)
727 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
728
729 // Build trace from events
730 TestTraceContainer trace;
731 trace.set(C::precomputed_first_row, 0, 1);
732 Sha256TraceBuilder builder;
733 builder.process(sha256_event_emitter.dump_events(), trace);
734
735 // Step 2: Verify the trace is valid BEFORE tampering
736 ASSERT_NO_THROW(check_relation<sha256_mem>(trace)) << "Trace should be valid before tampering";
737
738 // Step 3: Tamper with input_addr on row 1 (a non-start input round)
739 // The constraint should catch this because input_addr[1] must equal input_addr[0] + 1
740 constexpr uint32_t MALICIOUS_ADDR = 9999;
741 trace.set(C::sha256_input_addr, 1, MALICIOUS_ADDR);
742
743 // Step 4: Verify the CONTINUITY_INPUT_ADDR constraint catches the tampering
744 // The constraint enforces: input_addr' = input_addr + sel_is_input_round
745 EXPECT_THROW_WITH_MESSAGE(check_relation<sha256_mem>(trace, sha256_mem::SR_CONTINUITY_INPUT_ADDR),
747}
748
752
753// This test verifies that init_a through init_h are properly propagated across multi-row computation.
754//
755// BACKGROUND: SHA256 compression uses init_a-init_h values loaded from memory on row 0, and these
756// values are used in the final output calculation (OUT_X = x + init_x) on the last row.
757//
758// The PROPAGATE_INIT_* constraints (sha256.pil lines 111-126) ensure that init values remain
759// constant across all rounds. Without these constraints, a malicious prover could:
760// 1. Set correct init_a on row 0 (to pass memory read constraint)
761// 2. Set arbitrary init_a on later rows
762// 3. Corrupt the final SHA256 output
763//
764// This test verifies that tampering with init_a is caught by the PROPAGATE_INIT_A constraint.
765TEST(Sha256ConstrainingTest, InitStateTamperingIsCaughtByPropagationConstraint)
766{
767 // Generate a valid SHA256 compression trace
768 MemoryStore mem;
769 StrictMock<MockExecutionIdManager> execution_id_manager;
770 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
771 PureGreaterThan gt;
772 PureBitwise bitwise;
773 EventEmitter<RangeCheckEvent> range_check_event_emitter;
775
776 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
777 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, range_check, sha256_event_emitter);
778
779 // Set up valid memory for state and input
780 std::array<uint32_t, 8> state = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
781 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 };
782 MemoryAddress state_addr = 0;
783 for (uint32_t i = 0; i < 8; ++i) {
784 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
785 }
786
787 std::array<uint32_t, 16> input = { 0x61626380, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x18 };
788 MemoryAddress input_addr = 8;
789 for (uint32_t i = 0; i < 16; ++i) {
790 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
791 }
792 MemoryAddress output_addr = 25;
793
794 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
795
796 TestTraceContainer trace;
797 trace.set(C::precomputed_first_row, 0, 1);
798 Sha256TraceBuilder builder;
799 builder.process(sha256_event_emitter.dump_events(), trace);
800
801 // Verify the trace is valid before tampering
802 ASSERT_NO_THROW(check_relation<sha256>(trace));
803
804 // Find a row where perform_round=1 (any round row works, but use row 1 for simplicity)
805 // Row 0 is start, rows 1-64 are rounds with perform_round=1
806 constexpr uint32_t TAMPER_ROW = 1;
807 ASSERT_EQ(trace.get(C::sha256_perform_round, TAMPER_ROW), FF(1)) << "Row 1 should have perform_round=1";
808
809 // Tamper with init_a on row 1 (making it different from row 0)
810 FF original_init_a = trace.get(C::sha256_init_a, TAMPER_ROW);
811 FF tampered_init_a = original_init_a + FF(0x12345678);
812 trace.set(C::sha256_init_a, TAMPER_ROW, tampered_init_a);
813
814 // The PROPAGATE_INIT_A constraint should catch this:
815 // perform_round * (init_a' - init_a) = 0
816 // On row 0: perform_round=1, init_a'=tampered, init_a=original -> constraint violated
817 EXPECT_THROW_WITH_MESSAGE(check_relation<sha256>(trace, sha256::SR_PROPAGATE_INIT_A),
819}
820
824
825// Verifies that ROUNDS_REM_INIT (start * (rounds_remaining - 64) = 0) catches a malicious prover
826// that tries to start the round counter at a value other than 64 on the start row.
827TEST(Sha256ConstrainingTest, RoundsRemainingInitTamperingIsCaught)
828{
829 MemoryStore mem;
830 StrictMock<MockExecutionIdManager> execution_id_manager;
831 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
832 PureGreaterThan gt;
833 PureBitwise bitwise;
834 EventEmitter<RangeCheckEvent> range_check_event_emitter;
836
837 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
838 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, range_check, sha256_event_emitter);
839
840 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
841 MemoryAddress state_addr = 0;
842 for (uint32_t i = 0; i < 8; ++i) {
843 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
844 }
845
846 std::array<uint32_t, 16> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
847 MemoryAddress input_addr = 8;
848 for (uint32_t i = 0; i < 16; ++i) {
849 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
850 }
851 MemoryAddress output_addr = 25;
852
853 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
854
855 TestTraceContainer trace;
856 trace.set(C::precomputed_first_row, 0, 1);
857 Sha256TraceBuilder builder;
858 builder.process(sha256_event_emitter.dump_events(), trace);
859
860 // Original trace must satisfy all sha256 relations.
861 ASSERT_NO_THROW(check_relation<sha256>(trace));
862 // Row 0 is the precomputed first row latch; the sha256 start row is row 1.
863 constexpr uint32_t START_ROW = 1;
864 ASSERT_EQ(trace.get(C::sha256_start, START_ROW), FF(1)) << "Row 1 should be the sha256 start row";
865 ASSERT_EQ(trace.get(C::sha256_rounds_remaining, START_ROW), FF(64))
866 << "rounds_remaining should be 64 on the start row";
867
868 // Tamper with rounds_remaining on the start row: set it to 50 instead of 64.
869 trace.set(C::sha256_rounds_remaining, START_ROW, FF(50));
870
871 EXPECT_THROW_WITH_MESSAGE(check_relation<sha256>(trace, sha256::SR_ROUNDS_REM_INIT),
873}
874
875// Verifies that ROUNDS_REM_DECREMENT (perform_round * (rounds_remaining - rounds_remaining' - 1) = 0)
876// catches a malicious prover that tries to skip ahead or stall the round counter on a perform_round row.
877TEST(Sha256ConstrainingTest, RoundsRemainingDecrementTamperingIsCaught)
878{
879 MemoryStore mem;
880 StrictMock<MockExecutionIdManager> execution_id_manager;
881 EXPECT_CALL(execution_id_manager, get_execution_id()).WillRepeatedly(Return(1));
882 PureGreaterThan gt;
883 PureBitwise bitwise;
884 EventEmitter<RangeCheckEvent> range_check_event_emitter;
886
887 EventEmitter<Sha256CompressionEvent> sha256_event_emitter;
888 Sha256 sha256_gadget(execution_id_manager, bitwise, gt, range_check, sha256_event_emitter);
889
890 std::array<uint32_t, 8> state = { 0, 1, 2, 3, 4, 5, 6, 7 };
891 MemoryAddress state_addr = 0;
892 for (uint32_t i = 0; i < 8; ++i) {
893 mem.set(state_addr + i, MemoryValue::from<uint32_t>(state[i]));
894 }
895
896 std::array<uint32_t, 16> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
897 MemoryAddress input_addr = 8;
898 for (uint32_t i = 0; i < 16; ++i) {
899 mem.set(input_addr + i, MemoryValue::from<uint32_t>(input[i]));
900 }
901 MemoryAddress output_addr = 25;
902
903 sha256_gadget.compression(mem, state_addr, input_addr, output_addr);
904
905 TestTraceContainer trace;
906 trace.set(C::precomputed_first_row, 0, 1);
907 Sha256TraceBuilder builder;
908 builder.process(sha256_event_emitter.dump_events(), trace);
909
910 // Original trace must satisfy all sha256 relations.
911 ASSERT_NO_THROW(check_relation<sha256>(trace));
912
913 // Pick a perform_round row and tamper with rounds_remaining so the row->row+1 delta is no longer 1.
914 // The decrement constraint at row TAMPER_ROW - 1 (also a perform_round row) checks
915 // (rounds_remaining[TAMPER_ROW-1] - rounds_remaining[TAMPER_ROW] - 1) = 0, which now fails.
916 constexpr uint32_t TAMPER_ROW = 5;
917 ASSERT_EQ(trace.get(C::sha256_perform_round, TAMPER_ROW), FF(1));
918 FF original = trace.get(C::sha256_rounds_remaining, TAMPER_ROW);
919 trace.set(C::sha256_rounds_remaining, TAMPER_ROW, original - FF(1));
920
921 EXPECT_THROW_WITH_MESSAGE(check_relation<sha256>(trace, sha256::SR_ROUNDS_REM_DECREMENT),
923}
924
925} // namespace
926} // namespace bb::avm2::constraining
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:224
FieldGreaterThan field_gt
RangeCheck range_check
static constexpr size_t SR_CONTINUITY_INPUT_ADDR
static std::string get_subrelation_label(size_t index)
static constexpr size_t SR_ROUNDS_REM_DECREMENT
Definition sha256.hpp:42
static std::string get_subrelation_label(size_t index)
Definition sha256.hpp:52
static constexpr size_t SR_PROPAGATE_INIT_A
Definition sha256.hpp:43
static constexpr size_t SR_ROUNDS_REM_INIT
Definition sha256.hpp:41
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).
void process(const simulation::EventEmitterInterface< simulation::RangeCheckEvent >::Container &events, TraceContainer &trace)
Processes range check events and populates the trace with decomposed value columns.
const FF & get(Column col, uint32_t row) const
void set(Column col, uint32_t row, const FF &value, bool use_atomic_limbs=false)
RangeCheckTraceBuilder range_check_builder
Definition alu.test.cpp:121
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:156
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