1#include <gmock/gmock.h>
2#include <gtest/gtest.h>
29using ::testing::StrictMock;
34using simulation::EventEmitter;
35using simulation::MockExecutionIdManager;
36using simulation::MockGreaterThan;
37using simulation::Poseidon2;
38using simulation::Poseidon2HashEvent;
39using simulation::Poseidon2PermutationEvent;
40using simulation::Poseidon2PermutationMemoryEvent;
41using tracegen::BytecodeTraceBuilder;
42using tracegen::Poseidon2TraceBuilder;
43using tracegen::PrecomputedTraceBuilder;
44using tracegen::TestTraceContainer;
51class BytecodeHashingConstrainingTest :
public ::testing::Test {
65class BytecodeHashingConstrainingTestTraceHelper :
public BytecodeHashingConstrainingTest {
67 TestTraceContainer process_bc_hashing_trace(std::vector<std::vector<FF>> all_bytecode_fields,
68 std::vector<FF> bytecode_ids)
73 TestTraceContainer
trace({
74 { { C::precomputed_first_row, 1 } },
77 for (uint32_t j = 0; j < all_bytecode_fields.size(); j++) {
78 uint32_t pc_index = 0;
79 auto bytecode_fields = all_bytecode_fields[j];
80 auto bytecode_id = bytecode_ids[j];
83 auto bytecode_field_at = [&bytecode_fields](
size_t i) ->
FF {
84 return i < bytecode_fields.size() ? bytecode_fields[i] : 0;
86 auto padding_amount = (3 - (bytecode_fields.size() % 3)) % 3;
87 auto num_rounds = (bytecode_fields.size() + padding_amount) / 3;
88 for (uint32_t i = 0; i < bytecode_fields.size(); i += 3) {
90 bool end = i + 3 >= bytecode_fields.size();
91 auto pc_index_1 = start ? 0 : pc_index + 31;
94 { C::bc_hashing_bytecode_id, bytecode_id },
95 { C::bc_hashing_latch, end },
96 { C::bc_hashing_output_hash,
hash },
97 { C::bc_hashing_input_len, bytecode_fields.size() },
98 { C::bc_hashing_rounds_rem, num_rounds },
99 { C::bc_hashing_packed_fields_0, bytecode_field_at(i) },
100 { C::bc_hashing_packed_fields_1, bytecode_field_at(i + 1) },
101 { C::bc_hashing_packed_fields_2, bytecode_field_at(i + 2) },
102 { C::bc_hashing_pc_index, pc_index },
103 { C::bc_hashing_pc_index_1, pc_index_1 },
104 { C::bc_hashing_pc_index_2, pc_index_1 + 31 },
105 { C::bc_hashing_sel, 1 },
106 { C::bc_hashing_sel_not_padding_1, end && padding_amount == 2 ? 0 : 1 },
107 { C::bc_hashing_sel_not_padding_2, end && padding_amount > 0 ? 0 : 1 },
108 { C::bc_hashing_sel_not_start, !start },
109 { C::bc_hashing_start, start },
117 { C::bc_hashing_pc_at_final_field,
118 padding_amount == 2 ? pc_index : pc_index_1 + (31 * (1 - padding_amount)) },
123 pc_index = pc_index_1 + 62;
132TEST_F(BytecodeHashingConstrainingTest, EmptyRow)
137TEST_F(BytecodeHashingConstrainingTest, SingleBytecodeHashOneRow)
141 std::vector<FF> bytecode_fields = { 1, 2 };
142 std::vector<uint8_t> bytecode = {};
144 for (
auto bytecode_field : bytecode_fields) {
147 bytecode.insert(bytecode.end(), bytes.begin() + 1, bytes.end());
152 auto trace = TestTraceContainer({
153 { { C::precomputed_first_row, 1 } },
155 { C::bc_hashing_input_len, 3 },
156 { C::bc_hashing_latch, 1 },
158 { C::bc_hashing_packed_fields_1, 1 },
159 { C::bc_hashing_packed_fields_2, 2 },
160 { C::bc_hashing_pc_at_final_field, 31 },
161 { C::bc_hashing_pc_index_1, 0 },
162 { C::bc_hashing_pc_index_2, 31 },
163 { C::bc_hashing_sel_not_padding_1, 1 },
164 { C::bc_hashing_sel_not_padding_2, 1 },
165 { C::bc_hashing_bytecode_id,
hash },
166 { C::bc_hashing_output_hash,
hash },
167 { C::bc_hashing_pc_index, 0 },
168 { C::bc_hashing_rounds_rem, 1 },
169 { C::bc_hashing_sel, 1 },
170 { C::bc_hashing_start, 1 },
179 check_relation<bc_hashing>(
trace);
180 check_all_interactions<BytecodeTraceBuilder>(
trace);
183TEST_F(BytecodeHashingConstrainingTestTraceHelper, SingleBytecodeHash100Fields)
186 FF hash =
FF(
"0x16d621c3387156ef53754679e7b2c9be8f0bceeb44aa59a74991df3b0b42a0bf");
188 std::vector<FF> bytecode_fields = {};
189 for (uint32_t i = 1; i < 100; i++) {
190 bytecode_fields.push_back(
FF(i));
193 TestTraceContainer
trace = process_bc_hashing_trace({ bytecode_fields }, {
hash });
195 std::vector<uint8_t> bytecode = {};
196 for (
auto bytecode_field : bytecode_fields) {
199 bytecode.insert(bytecode.end(), bytes.begin() + 1, bytes.end());
205 check_relation<bc_hashing>(
trace);
206 check_all_interactions<BytecodeTraceBuilder>(
trace);
207 EXPECT_EQ(
trace.
get(C::bc_hashing_output_hash, 1),
hash);
210TEST_F(BytecodeHashingConstrainingTestTraceHelper, SingleBytecodeHashMax)
215 prepended_fields.insert(prepended_fields.end(), bytecode_fields.begin(), bytecode_fields.end());
218 TestTraceContainer
trace = process_bc_hashing_trace({ bytecode_fields }, {
hash });
222 check_relation<bc_hashing>(
trace);
223 check_all_interactions<BytecodeTraceBuilder>(
trace);
226TEST_F(BytecodeHashingConstrainingTestTraceHelper, MultipleBytecodeHash)
233 std::vector<FF> hashes;
234 for (uint32_t i = 0; i < all_bytecode.size(); i++) {
237 prepended_fields.insert(prepended_fields.end(), all_bytecode_fields[i].begin(), all_bytecode_fields[i].end());
241 TestTraceContainer
trace = process_bc_hashing_trace(all_bytecode_fields, hashes);
244 for (uint32_t j = 0; j < all_bytecode.size(); j++) {
245 const auto& bytecode = all_bytecode[j];
246 decomp_events.push_back(simulation::BytecodeDecompositionEvent{
249 builder.process_decomposition({ decomp_events },
trace);
251 check_relation<bc_hashing>(
trace);
252 check_all_interactions<BytecodeTraceBuilder>(
trace);
255TEST_F(BytecodeHashingConstrainingTest, BytecodeInteractions)
257 TestTraceContainer
trace({
258 { { C::precomputed_first_row, 1 } },
264 prepended_fields.insert(prepended_fields.end(), fields.begin(), fields.end());
267 builder.process_hashing({ { .bytecode_id =
hash, .bytecode_length = 40, .bytecode_fields = fields } },
trace);
274 perm_builder(C::bc_decomposition_sel_packed);
275 perm_builder.process(
trace);
277 check_interaction<BytecodeTraceBuilder, lookup_bc_hashing_check_final_bytes_remaining_settings>(
trace);
282 check_relation<bc_hashing>(
trace);
283 check_relation<bb::avm2::bc_decomposition<FF>>(
trace);
287TEST_F(BytecodeHashingConstrainingTest, NegativeLatchNotSel)
289 TestTraceContainer
trace;
292 { C::bc_hashing_latch, 1 },
293 { C::bc_hashing_sel, 1 },
299 "SEL_TOGGLED_AT_LATCH");
302TEST_F(BytecodeHashingConstrainingTest, NegativeInvalidStartAfterLatch)
304 TestTraceContainer
trace({
305 { { C::precomputed_first_row, 1 } },
307 builder.process_hashing({ { .bytecode_id = 1, .bytecode_length = 62, .bytecode_fields =
random_fields(2) },
308 { .bytecode_id = 2, .bytecode_length = 93, .bytecode_fields =
random_fields(3) } },
313 trace.
set(Column::bc_hashing_start, 2, 0);
317TEST_F(BytecodeHashingConstrainingTest, NegativeInvalidPCIncrement)
319 TestTraceContainer
trace({
320 { { C::precomputed_first_row, 1 } },
324 { .bytecode_id = 1, .bytecode_length = 124, .bytecode_fields =
random_fields(4) },
330 trace.
set(Column::bc_hashing_pc_index, 2, 10);
336 trace.
set(Column::bc_hashing_pc_index_2, 2, 10);
340TEST_F(BytecodeHashingConstrainingTest, NegativeStartIsSeparator)
342 TestTraceContainer
trace({
343 { { C::precomputed_first_row, 1 } },
345 builder.process_hashing({ { .bytecode_id = 1, .bytecode_length = 62, .bytecode_fields = { 1, 2 } } },
trace);
349 trace.
set(Column::bc_hashing_packed_fields_0, 1, 1);
351 "START_IS_SEPARATOR");
354TEST_F(BytecodeHashingConstrainingTest, NegativeBytecodeInteraction)
356 TestTraceContainer
trace({
357 { { C::precomputed_first_row, 1 } },
363 prepended_fields.insert(prepended_fields.end(), fields.begin(), fields.end());
366 builder.process_hashing({ { .bytecode_id =
hash, .bytecode_length = 150, .bytecode_fields = fields } },
trace);
372 perm_builder(C::bc_decomposition_sel_packed);
373 perm_builder.process(
trace);
377 trace.
set(Column::bc_hashing_pc_index, 2, 0);
382 "Failed.*GET_PACKED_FIELD_0. Could not find tuple in destination.");
383 trace.
set(Column::bc_hashing_pc_index, 2, 62);
385 trace.
set(Column::bc_hashing_packed_fields_1, 2, 0);
390 "Failed.*GET_PACKED_FIELD_1. Could not find tuple in destination.");
391 trace.
set(Column::bc_hashing_packed_fields_1, 2, prepended_fields[4]);
394 trace.
set(Column::bc_hashing_pc_index_2, 2, 0);
399 "Failed.*GET_PACKED_FIELD_2. Could not find tuple in destination.");
402 trace.
set(Column::bc_hashing_pc_index_2, 2, 124);
409 trace.
set(Column::bc_hashing_bytecode_id, 2, 0);
414 "Failed.*GET_PACKED_FIELD_.*. Could not find tuple in destination.");
417TEST_F(BytecodeHashingConstrainingTestTraceHelper, NegativePaddingSelectors)
423 TestTraceContainer
trace = process_bc_hashing_trace({ fields }, { 1 });
429 trace.
set(Column::bc_hashing_latch, 2, 0);
431 trace.
set(Column::bc_hashing_latch, 2, 1);
434 trace.
set(Column::bc_hashing_sel_not_padding_2, 2, 1);
436 "PADDING_CONSISTENCY");
437 trace.
set(Column::bc_hashing_sel_not_padding_2, 2, 0);
440 trace.
set(Column::bc_hashing_packed_fields_1, 2, 1);
442 trace.
set(Column::bc_hashing_packed_fields_2, 2, 1);
446TEST_F(BytecodeHashingConstrainingTestTraceHelper, NegativePaddingUnder)
452 prepended_fields.insert(prepended_fields.end(), fields.begin(), fields.end());
455 TestTraceContainer
trace = process_bc_hashing_trace({ fields }, {
hash });
461 trace.
set(Column::bc_hashing_sel_not_padding_1, 2, 1);
464 "PADDING_CORRECTNESS");
466 trace.
set(Column::bc_hashing_pc_at_final_field, 2, 93);
473 "Failed.*GET_PACKED_FIELD_1. Could not find tuple in destination.");
476 (check_interaction<BytecodeTraceBuilder, lookup_bc_hashing_check_final_bytes_remaining_settings>(
trace)),
477 "Failed.*CHECK_FINAL_BYTES_REMAINING. Could not find tuple in destination.");
480TEST_F(BytecodeHashingConstrainingTestTraceHelper, NegativePaddingOver)
486 TestTraceContainer
trace = process_bc_hashing_trace({ fields }, { 1 });
492 trace.
set(Column::bc_hashing_sel_not_padding_1, 2, 0);
495 trace.
set(Column::bc_hashing_packed_fields_1, 2, 0);
496 trace.
set(Column::bc_hashing_pc_at_final_field, 2, 62);
499 (check_interaction<BytecodeTraceBuilder, lookup_bc_hashing_check_final_bytes_remaining_settings>(
trace)),
500 "Failed.*CHECK_FINAL_BYTES_REMAINING. Could not find tuple in destination.");
503TEST_F(BytecodeHashingConstrainingTestTraceHelper, NegativeInputLen)
509 TestTraceContainer
trace = process_bc_hashing_trace({ fields }, { 1 });
514 trace.
set(Column::bc_hashing_input_len, 1, 0);
516 (check_interaction<BytecodeTraceBuilder, lookup_bc_hashing_poseidon2_hash_settings>(
trace)),
517 "LOOKUP_BC_HASHING_POSEIDON2_HASH");
519 trace.
set(Column::bc_hashing_input_len, 1, 4);
522 trace.
set(Column::bc_hashing_input_len, 2, 0);
524 "BYTECODE_LENGTH_FIELDS");
527TEST_F(BytecodeHashingConstrainingTestTraceHelper, NegativeRounds)
533 TestTraceContainer
trace = process_bc_hashing_trace({ fields }, { 1 });
538 trace.
set(Column::bc_hashing_rounds_rem, 1, 3);
542 trace.
set(Column::bc_hashing_rounds_rem, 2, 2);
546TEST_F(BytecodeHashingConstrainingTestTraceHelper, NegativeOutputHash)
550 prepended_fields.insert(prepended_fields.end(), bytecode_fields.begin(), bytecode_fields.end());
552 TestTraceContainer
trace = process_bc_hashing_trace({ bytecode_fields }, {
hash });
554 check_relation<bc_hashing>(
trace);
555 check_interaction<BytecodeTraceBuilder, lookup_bc_hashing_poseidon2_hash_settings>(
trace);
558 trace.
set(Column::bc_hashing_output_hash, 2, 123);
560 (check_interaction<BytecodeTraceBuilder, lookup_bc_hashing_poseidon2_hash_settings>(
trace)),
561 "LOOKUP_BC_HASHING_POSEIDON2_HASH");
564TEST_F(BytecodeHashingConstrainingTest, NegativeSingleBytecodeHashIncrements)
571 std::vector<uint8_t> bytecode =
random_bytes(
static_cast<size_t>(31 * 3));
576 auto trace = TestTraceContainer({
577 { { C::precomputed_first_row, 1 } },
579 { C::bc_hashing_latch, 1 },
581 { C::bc_hashing_packed_fields_1, bytecode_fields[1] },
582 { C::bc_hashing_packed_fields_2, bytecode_fields[2] },
583 { C::bc_hashing_pc_at_final_field, 62 },
584 { C::bc_hashing_pc_index_1, 31 },
585 { C::bc_hashing_pc_index_2, 62 },
586 { C::bc_hashing_sel_not_padding_1, 1 },
587 { C::bc_hashing_sel_not_padding_2, 1 },
588 { C::bc_hashing_bytecode_id, 1 },
589 { C::bc_hashing_output_hash, bad_hash },
590 { C::bc_hashing_pc_index, 31 },
591 { C::bc_hashing_sel, 1 },
592 { C::bc_hashing_sel_not_start, 0 },
593 { C::bc_hashing_start, 1 },
605TEST_F(BytecodeHashingConstrainingTest, NegativeSingleBytecodeHashLength)
611 std::vector<uint8_t> bytecode =
random_bytes(
static_cast<size_t>(31 * 3));
620 bytecode_fields[2] });
622 auto trace = TestTraceContainer({
623 { { C::precomputed_first_row, 1 } },
625 { C::bc_hashing_input_len, 7 },
627 { C::bc_hashing_packed_fields_1, bytecode_fields[0] },
628 { C::bc_hashing_packed_fields_2, bytecode_fields[1] },
629 { C::bc_hashing_pc_index_1, 0 },
630 { C::bc_hashing_pc_index_2, 31 },
631 { C::bc_hashing_sel_not_padding_1, 1 },
632 { C::bc_hashing_sel_not_padding_2, 1 },
633 { C::bc_hashing_bytecode_id, 1 },
634 { C::bc_hashing_output_hash, bad_hash },
635 { C::bc_hashing_pc_index, 0 },
636 { C::bc_hashing_rounds_rem, 2 },
637 { C::bc_hashing_sel, 1 },
638 { C::bc_hashing_start, 1 },
641 { C::bc_hashing_input_len, 7 },
642 { C::bc_hashing_latch, 1 },
643 { C::bc_hashing_packed_fields_0, bytecode_fields[2] },
644 { C::bc_hashing_packed_fields_1, 0 },
645 { C::bc_hashing_packed_fields_2, 0 },
646 { C::bc_hashing_pc_at_final_field, 62 },
647 { C::bc_hashing_pc_index_1, 93 },
648 { C::bc_hashing_pc_index_2, 124 },
649 { C::bc_hashing_bytecode_id, 1 },
650 { C::bc_hashing_output_hash, bad_hash },
651 { C::bc_hashing_pc_index, 62 },
652 { C::bc_hashing_rounds_rem, 1 },
653 { C::bc_hashing_sel, 1 },
654 { C::bc_hashing_sel_not_start, 1 },
666 (check_interaction<BytecodeTraceBuilder, lookup_bc_hashing_poseidon2_hash_settings>(
trace)),
667 "LOOKUP_BC_HASHING_POSEIDON2_HASH");
670 "BYTECODE_LENGTH_FIELDS");
673TEST_F(BytecodeHashingConstrainingTest, NegativeSingleBytecodeHashOutputConsistency)
679 std::vector<uint8_t> bytecode =
random_bytes(
static_cast<size_t>(31 * 5));
687 bytecode_fields[4] });
688 auto bad_hash =
poseidon2.
hash({ 0xa, 0xb, 0xc, bytecode_fields[2], bytecode_fields[3], bytecode_fields[4] });
690 auto trace = TestTraceContainer({
691 { { C::precomputed_first_row, 1 } },
693 { C::bc_hashing_input_len, 6 },
695 { C::bc_hashing_packed_fields_1, bytecode_fields[0] },
696 { C::bc_hashing_packed_fields_2, bytecode_fields[1] },
697 { C::bc_hashing_pc_index_1, 0 },
698 { C::bc_hashing_pc_index_2, 31 },
699 { C::bc_hashing_sel_not_padding_1, 1 },
700 { C::bc_hashing_sel_not_padding_2, 1 },
701 { C::bc_hashing_bytecode_id, good_hash },
702 { C::bc_hashing_output_hash, good_hash },
703 { C::bc_hashing_pc_index, 0 },
704 { C::bc_hashing_rounds_rem, 2 },
705 { C::bc_hashing_sel, 1 },
706 { C::bc_hashing_start, 1 },
709 { C::bc_hashing_input_len, 6 },
710 { C::bc_hashing_latch, 1 },
711 { C::bc_hashing_packed_fields_0, bytecode_fields[2] },
712 { C::bc_hashing_packed_fields_1, bytecode_fields[3] },
713 { C::bc_hashing_packed_fields_2, bytecode_fields[4] },
714 { C::bc_hashing_pc_at_final_field, 124 },
715 { C::bc_hashing_pc_index_1, 93 },
716 { C::bc_hashing_pc_index_2, 124 },
717 { C::bc_hashing_sel_not_padding_1, 1 },
718 { C::bc_hashing_sel_not_padding_2, 1 },
719 { C::bc_hashing_bytecode_id, good_hash },
720 { C::bc_hashing_output_hash, bad_hash },
721 { C::bc_hashing_pc_index, 62 },
722 { C::bc_hashing_rounds_rem, 1 },
723 { C::bc_hashing_sel, 1 },
724 { C::bc_hashing_sel_not_start, 1 },
734 check_all_interactions<BytecodeTraceBuilder>(
trace);
738 trace.
set(Column::bc_hashing_bytecode_id, 2, bad_hash);
741 (check_interaction<BytecodeTraceBuilder, lookup_bc_hashing_check_final_bytes_remaining_settings>(
trace)),
742 "Failed.*CHECK_FINAL_BYTES_REMAINING. Could not find tuple in destination.");
#define GENERATOR_INDEX__PUBLIC_BYTECODE
#define MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS
StrictMock< MockGreaterThan > mock_gt
EventEmitter< Poseidon2PermutationMemoryEvent > perm_mem_event_emitter
EventEmitter< Poseidon2PermutationEvent > perm_event_emitter
EventEmitter< Poseidon2HashEvent > hash_event_emitter
Poseidon2TraceBuilder poseidon2_builder
StrictMock< MockExecutionIdManager > mock_execution_id_manager
static constexpr size_t SR_SEL_TOGGLED_AT_LATCH
static constexpr size_t SR_PADDING_CONSISTENCY
static constexpr size_t SR_ROUNDS_DECREMENT
static constexpr size_t SR_PADDING_END
static constexpr size_t SR_PC_INCREMENTS_2
static constexpr size_t SR_START_AFTER_LATCH
static constexpr size_t SR_PADDED_BY_ZERO_2
static constexpr size_t SR_PADDING_CORRECTNESS
static constexpr size_t SR_HASH_IS_ID
static constexpr size_t SR_START_IS_SEPARATOR
static constexpr size_t SR_ID_PROPAGATION
static constexpr size_t SR_PADDED_BY_ZERO_1
static constexpr size_t SR_PC_INCREMENTS
static constexpr size_t SR_BYTECODE_LENGTH_FIELDS
static constexpr size_t SR_PC_INCREMENTS_1
void process_hash(const simulation::EventEmitterInterface< simulation::Poseidon2HashEvent >::Container &hash_events, TraceContainer &trace)
void process_misc(TraceContainer &trace, const uint32_t num_rows=MAX_AVM_TRACE_SIZE)
const FF & get(Column col, uint32_t row) const
void set(Column col, uint32_t row, const FF &value)
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
Implements a parallelized batch insertion indexed tree Accepts template argument of the type of store...
PrecomputedTraceBuilder precomputed_builder
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessage)
void hash(State &state) noexcept
TEST_F(AvmRecursiveTests, GoblinRecursion)
A test of the Goblinized AVM recursive verifier.
void check_multipermutation_interaction(tracegen::TestTraceContainer &trace)
std::vector< FF > encode_bytecode(std::span< const uint8_t > bytecode)
std::vector< uint8_t > random_bytes(size_t n)
TestTraceContainer empty_trace()
std::vector< FF > random_fields(size_t n)
permutation_settings< perm_bc_hashing_get_packed_field_2_settings_ > perm_bc_hashing_get_packed_field_2_settings
permutation_settings< perm_bc_hashing_get_packed_field_1_settings_ > perm_bc_hashing_get_packed_field_1_settings
permutation_settings< perm_bc_hashing_get_packed_field_0_settings_ > perm_bc_hashing_get_packed_field_0_settings
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
std::vector< uint8_t > to_buffer(T const &value)