1#include <gmock/gmock.h>
2#include <gtest/gtest.h>
29using ::testing::StrictMock;
35using simulation::EventEmitter;
36using simulation::MockExecutionIdManager;
37using simulation::MockGreaterThan;
38using simulation::Poseidon2;
39using simulation::Poseidon2HashEvent;
40using simulation::Poseidon2PermutationEvent;
41using simulation::Poseidon2PermutationMemoryEvent;
42using tracegen::BytecodeTraceBuilder;
43using tracegen::Poseidon2TraceBuilder;
44using tracegen::PrecomputedTraceBuilder;
45using tracegen::TestTraceContainer;
53class BytecodeHashingConstrainingTest :
public ::testing::Test {
67class BytecodeHashingConstrainingTestTraceHelper :
public BytecodeHashingConstrainingTest {
69 TestTraceContainer process_bc_hashing_trace(std::vector<std::vector<FF>> all_bytecode_fields,
70 std::vector<FF> bytecode_ids,
71 std::vector<size_t> bytecode_size_in_bytes)
76 TestTraceContainer
trace({
77 { { C::precomputed_first_row, 1 } },
80 for (uint32_t j = 0; j < all_bytecode_fields.size(); j++) {
81 uint32_t pc_index = 0;
82 auto bytecode_fields = all_bytecode_fields[j];
83 auto bytecode_id = bytecode_ids[j];
84 bytecode_fields.insert(bytecode_fields.begin(),
87 auto bytecode_field_at = [&bytecode_fields](
size_t i) ->
FF {
88 return i < bytecode_fields.size() ? bytecode_fields[i] : 0;
90 auto padding_amount = (3 - (bytecode_fields.size() % 3)) % 3;
91 auto num_rounds = (bytecode_fields.size() + padding_amount) / 3;
92 for (uint32_t i = 0; i < bytecode_fields.size(); i += 3) {
94 bool end = i + 3 >= bytecode_fields.size();
95 auto pc_index_1 = start ? 0 : pc_index + 31;
98 { C::bc_hashing_bytecode_id, bytecode_id },
99 { C::bc_hashing_end, end },
100 { C::bc_hashing_size_in_bytes, bytecode_size_in_bytes[j] },
101 { C::bc_hashing_input_len, bytecode_fields.size() },
102 { C::bc_hashing_rounds_rem, num_rounds },
103 { C::bc_hashing_packed_fields_0, bytecode_field_at(i) },
104 { C::bc_hashing_packed_fields_1, bytecode_field_at(i + 1) },
105 { C::bc_hashing_packed_fields_2, bytecode_field_at(i + 2) },
106 { C::bc_hashing_pc_index, pc_index },
107 { C::bc_hashing_pc_index_1, pc_index_1 },
108 { C::bc_hashing_pc_index_2, pc_index_1 + 31 },
109 { C::bc_hashing_sel, 1 },
110 { C::bc_hashing_sel_not_padding_1, end && padding_amount == 2 ? 0 : 1 },
111 { C::bc_hashing_sel_not_padding_2, end && padding_amount > 0 ? 0 : 1 },
112 { C::bc_hashing_padding, padding_amount },
113 { C::bc_hashing_sel_not_start, !start },
114 { C::bc_hashing_start, start },
118 pc_index = pc_index_1 + 62;
127TEST_F(BytecodeHashingConstrainingTest, EmptyRow)
132TEST_F(BytecodeHashingConstrainingTest, SingleBytecodeHashOneRow)
136 std::vector<FF> bytecode_fields = { 1, 2 };
139 for (
auto bytecode_field : bytecode_fields) {
147 auto trace = TestTraceContainer({
148 { { C::precomputed_first_row, 1 } },
150 { C::bc_hashing_size_in_bytes,
bytecode.size() },
151 { C::bc_hashing_input_len, 3 },
152 { C::bc_hashing_end, 1 },
153 { C::bc_hashing_packed_fields_0, sep },
154 { C::bc_hashing_packed_fields_1, 1 },
155 { C::bc_hashing_packed_fields_2, 2 },
156 { C::bc_hashing_pc_index_1, 0 },
157 { C::bc_hashing_pc_index_2, 31 },
158 { C::bc_hashing_sel_not_padding_1, 1 },
159 { C::bc_hashing_sel_not_padding_2, 1 },
160 { C::bc_hashing_bytecode_id, hash },
161 { C::bc_hashing_pc_index, 0 },
162 { C::bc_hashing_rounds_rem, 1 },
163 { C::bc_hashing_sel, 1 },
164 { C::bc_hashing_start, 1 },
173 check_relation<bc_hashing>(trace);
174 check_all_interactions<BytecodeTraceBuilder>(trace);
177TEST_F(BytecodeHashingConstrainingTestTraceHelper, SingleBytecodeHash100Fields)
180 FF hash =
FF(
"0x09348974e76c3602893d7a4b4bb52c2ec746f1ade5004ac471d0fbb4587a81a6");
182 std::vector<FF> bytecode_fields = {};
183 for (uint32_t i = 1; i < 100; i++) {
184 bytecode_fields.push_back(
FF(i));
187 for (
auto bytecode_field : bytecode_fields) {
192 TestTraceContainer
trace = process_bc_hashing_trace({ bytecode_fields }, { hash }, {
bytecode.size() });
197 check_relation<bc_hashing>(trace);
198 check_all_interactions<BytecodeTraceBuilder>(trace);
201TEST_F(BytecodeHashingConstrainingTestTraceHelper, SingleBytecodeHashMax)
206 prepended_fields.insert(prepended_fields.end(), bytecode_fields.begin(), bytecode_fields.end());
209 TestTraceContainer
trace = process_bc_hashing_trace({ bytecode_fields }, { hash }, {
bytecode.size() });
213 check_relation<bc_hashing>(trace);
214 check_all_interactions<BytecodeTraceBuilder>(trace);
217TEST_F(BytecodeHashingConstrainingTestTraceHelper, MultipleBytecodeHash)
224 std::vector<FF> hashes;
225 std::vector<size_t> byte_sizes;
226 for (uint32_t i = 0; i < all_bytecode.size(); i++) {
229 prepended_fields.insert(prepended_fields.end(), all_bytecode_fields[i].begin(), all_bytecode_fields[i].end());
231 byte_sizes.push_back(all_bytecode[i].size());
234 TestTraceContainer
trace = process_bc_hashing_trace(all_bytecode_fields, hashes, byte_sizes);
237 for (uint32_t j = 0; j < all_bytecode.size(); j++) {
238 const auto&
bytecode = all_bytecode[j];
239 decomp_events.push_back(simulation::BytecodeDecompositionEvent{
242 builder.process_decomposition({ decomp_events },
trace);
244 check_relation<bc_hashing>(trace);
245 check_all_interactions<BytecodeTraceBuilder>(trace);
248TEST_F(BytecodeHashingConstrainingTest, BytecodeInteractions)
250 TestTraceContainer
trace({
251 { { C::precomputed_first_row, 1 } },
257 prepended_fields.insert(prepended_fields.end(), fields.begin(), fields.end());
260 builder.process_hashing({ { .bytecode_id = hash,
261 .bytecode_length_in_bytes =
static_cast<uint32_t
>(
bytecode.size()),
262 .bytecode_fields = fields } },
270 perm_builder(C::bc_decomposition_sel_packed);
271 perm_builder.process(trace);
277 check_relation<bc_hashing>(trace);
278 check_relation<bb::avm2::bc_decomposition<FF>>(
trace);
282TEST_F(BytecodeHashingConstrainingTest, NegativeLatchNotSel)
284 TestTraceContainer
trace;
287 { C::bc_hashing_end, 1 },
288 { C::bc_hashing_sel, 1 },
294 "SEL_ON_START_OR_END");
297TEST_F(BytecodeHashingConstrainingTest, NegativeInvalidStartAfterLatch)
299 TestTraceContainer
trace({
300 { { C::precomputed_first_row, 1 } },
303 { { .bytecode_id = 1, .bytecode_length_in_bytes = 62, .bytecode_fields =
random_fields(2) },
304 { .bytecode_id = 2, .bytecode_length_in_bytes = 93, .bytecode_fields =
random_fields(3) } },
309 trace.
set(Column::bc_hashing_start, 2, 0);
313TEST_F(BytecodeHashingConstrainingTest, NegativeInvalidPCIncrement)
315 TestTraceContainer
trace({
316 { { C::precomputed_first_row, 1 } },
320 { .bytecode_id = 1, .bytecode_length_in_bytes = 124, .bytecode_fields =
random_fields(4) },
328 trace.
set(Column::bc_hashing_pc_index, 2, 10);
330 trace.
set(Column::bc_hashing_pc_index, 2, 62);
331 trace.
set(Column::bc_hashing_pc_index_1, 2, 97);
333 trace.
set(Column::bc_hashing_pc_index_1, 2, 93);
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_in_bytes = 62, .bytecode_fields = { 1, 2 } } },
350 trace.
set(Column::bc_hashing_packed_fields_0, 1, 1);
352 "START_IS_FIRST_FIELD");
355TEST_F(BytecodeHashingConstrainingTest, NegativeBytecodeInteraction)
357 TestTraceContainer
trace({
358 { { C::precomputed_first_row, 1 } },
364 prepended_fields.insert(prepended_fields.end(), fields.begin(), fields.end());
367 builder.process_hashing({ { .bytecode_id = hash, .bytecode_length_in_bytes = 150, .bytecode_fields = fields } },
374 perm_builder(C::bc_decomposition_sel_packed);
375 perm_builder.process(trace);
379 trace.
set(Column::bc_hashing_pc_index, 2, 0);
384 "Failed.*GET_PACKED_FIELD_0. Could not find tuple in destination.");
385 trace.
set(Column::bc_hashing_pc_index, 2, 62);
387 trace.
set(Column::bc_hashing_packed_fields_1, 2, 0);
392 "Failed.*GET_PACKED_FIELD_1. Could not find tuple in destination.");
393 trace.
set(Column::bc_hashing_packed_fields_1, 2, prepended_fields[4]);
396 trace.
set(Column::bc_hashing_pc_index_2, 2, 0);
401 "Failed.*GET_PACKED_FIELD_2. Could not find tuple in destination.");
404 trace.
set(Column::bc_hashing_pc_index_2, 2, 124);
411 trace.
set(Column::bc_hashing_bytecode_id, 2, 0);
416 "Failed.*GET_PACKED_FIELD_.*. Could not find tuple in destination.");
419TEST_F(BytecodeHashingConstrainingTestTraceHelper, NegativePaddingSelectors)
425 TestTraceContainer
trace = process_bc_hashing_trace({ fields }, { 1 }, {
bytecode.size() });
431 trace.
set(Column::bc_hashing_end, 2, 0);
433 trace.
set(Column::bc_hashing_end, 2, 1);
436 trace.
set(Column::bc_hashing_sel_not_padding_2, 2, 1);
438 "PADDING_CONSISTENCY");
439 trace.
set(Column::bc_hashing_sel_not_padding_2, 2, 0);
442 trace.
set(Column::bc_hashing_packed_fields_1, 2, 1);
444 trace.
set(Column::bc_hashing_packed_fields_2, 2, 1);
448TEST_F(BytecodeHashingConstrainingTestTraceHelper, NegativePaddingUnder)
454 prepended_fields.insert(prepended_fields.end(), fields.begin(), fields.end());
457 TestTraceContainer
trace = process_bc_hashing_trace({ fields }, { hash }, {
bytecode.size() });
463 trace.
set(Column::bc_hashing_sel_not_padding_1, 2, 1);
466 "PADDING_COMPUTATION");
468 trace.
set(Column::bc_hashing_padding, 2, 1);
469 trace.
set(Column::bc_hashing_padding, 1, 1);
470 trace.
set(Column::bc_hashing_input_len, 1, 5);
471 check_relation<bc_hashing>(trace);
477 "Failed.*GET_PACKED_FIELD_1. Could not find tuple in destination.");
480TEST_F(BytecodeHashingConstrainingTestTraceHelper, NegativePaddingOver)
486 prepended_fields.insert(prepended_fields.end(), fields.begin(), fields.end());
489 TestTraceContainer
trace = process_bc_hashing_trace({ fields }, { hash }, {
bytecode.size() });
495 trace.
set(Column::bc_hashing_sel_not_padding_1, 2, 0);
498 "PADDING_COMPUTATION");
500 trace.
set(Column::bc_hashing_padding, 2, 2);
501 trace.
set(Column::bc_hashing_padding, 1, 2);
502 trace.
set(Column::bc_hashing_packed_fields_1, 2, 0);
503 trace.
set(Column::bc_hashing_input_len, 1, 4);
504 check_relation<bc_hashing>(trace);
515 "PACKED_ROW_NEEDS_PERM_SELECTOR");
518TEST_F(BytecodeHashingConstrainingTestTraceHelper, NegativeInputLen)
524 TestTraceContainer
trace = process_bc_hashing_trace({ fields }, { 1 }, {
bytecode.size() });
529 trace.
set(Column::bc_hashing_input_len, 1, 0);
531 (check_interaction<BytecodeTraceBuilder, lookup_bc_hashing_poseidon2_hash_settings>(trace)),
532 "LOOKUP_BC_HASHING_POSEIDON2_HASH");
534 trace.
set(Column::bc_hashing_input_len, 1, 4);
537 trace.
set(Column::bc_hashing_input_len, 1, 0);
539 "BYTECODE_LENGTH_FIELDS");
542TEST_F(BytecodeHashingConstrainingTestTraceHelper, NegativeRounds)
548 TestTraceContainer
trace = process_bc_hashing_trace({ fields }, { 1 }, {
bytecode.size() });
553 trace.
set(Column::bc_hashing_rounds_rem, 1, 3);
557 trace.
set(Column::bc_hashing_rounds_rem, 2, 2);
561TEST_F(BytecodeHashingConstrainingTestTraceHelper, NegativeOutputHash)
565 prepended_fields.insert(prepended_fields.end(), bytecode_fields.begin(), bytecode_fields.end());
567 TestTraceContainer
trace = process_bc_hashing_trace({ bytecode_fields }, { hash }, { bytecode_fields.size() * 31 });
569 check_relation<bc_hashing>(trace);
570 check_interaction<BytecodeTraceBuilder, lookup_bc_hashing_poseidon2_hash_settings>(trace);
573 trace.
set(Column::bc_hashing_bytecode_id, 2, 123);
575 (check_interaction<BytecodeTraceBuilder, lookup_bc_hashing_poseidon2_hash_settings>(trace)),
576 "LOOKUP_BC_HASHING_POSEIDON2_HASH");
579TEST_F(BytecodeHashingConstrainingTest, NegativeSingleBytecodeHashIncrements)
591 auto bad_hash =
poseidon2.
hash({ sep, bytecode_fields[1], bytecode_fields[2] });
593 auto trace = TestTraceContainer({
594 { { C::precomputed_first_row, 1 } },
596 { C::bc_hashing_end, 1 },
597 { C::bc_hashing_packed_fields_0, sep },
598 { C::bc_hashing_packed_fields_1, bytecode_fields[1] },
599 { C::bc_hashing_packed_fields_2, bytecode_fields[2] },
600 { C::bc_hashing_pc_index_1, 31 },
601 { C::bc_hashing_pc_index_2, 62 },
602 { C::bc_hashing_sel_not_padding_1, 1 },
603 { C::bc_hashing_sel_not_padding_2, 1 },
604 { C::bc_hashing_bytecode_id, bad_hash },
605 { C::bc_hashing_pc_index, 31 },
606 { C::bc_hashing_sel, 1 },
607 { C::bc_hashing_sel_not_start, 0 },
608 { C::bc_hashing_start, 1 },
620TEST_F(BytecodeHashingConstrainingTest, NegativeSingleBytecodeHashLength)
631 auto bad_hash =
poseidon2.
hash({ 0xa, 0xb, 0xc, sep, bytecode_fields[0], bytecode_fields[1], bytecode_fields[2] });
633 auto trace = TestTraceContainer({
634 { { C::precomputed_first_row, 1 } },
636 { C::bc_hashing_input_len, 7 },
637 { C::bc_hashing_packed_fields_0, sep },
638 { C::bc_hashing_packed_fields_1, bytecode_fields[0] },
639 { C::bc_hashing_packed_fields_2, bytecode_fields[1] },
640 { C::bc_hashing_pc_index_1, 0 },
641 { C::bc_hashing_pc_index_2, 31 },
642 { C::bc_hashing_sel_not_padding_1, 1 },
643 { C::bc_hashing_sel_not_padding_2, 1 },
644 { C::bc_hashing_bytecode_id, bad_hash },
645 { C::bc_hashing_pc_index, 0 },
646 { C::bc_hashing_rounds_rem, 2 },
647 { C::bc_hashing_sel, 1 },
648 { C::bc_hashing_start, 1 },
651 { C::bc_hashing_input_len, 7 },
652 { C::bc_hashing_end, 1 },
653 { C::bc_hashing_packed_fields_0, bytecode_fields[2] },
654 { C::bc_hashing_packed_fields_1, 0 },
655 { C::bc_hashing_packed_fields_2, 0 },
656 { C::bc_hashing_pc_index_1, 93 },
657 { C::bc_hashing_pc_index_2, 124 },
658 { C::bc_hashing_bytecode_id, bad_hash },
659 { C::bc_hashing_pc_index, 62 },
660 { C::bc_hashing_rounds_rem, 1 },
661 { C::bc_hashing_sel, 1 },
662 { C::bc_hashing_sel_not_start, 1 },
674 (check_interaction<BytecodeTraceBuilder, lookup_bc_hashing_poseidon2_hash_settings>(trace)),
675 "LOOKUP_BC_HASHING_POSEIDON2_HASH");
678 "BYTECODE_LENGTH_FIELDS");
681TEST_F(BytecodeHashingConstrainingTest, NegativeSingleBytecodeHashLengthBytes)
693 auto bad_hash =
poseidon2.
hash({ sep, bytecode_fields[0], bytecode_fields[1], bytecode_fields[2] });
695 auto trace = TestTraceContainer({
696 { { C::precomputed_first_row, 1 } },
698 { C::bc_hashing_size_in_bytes, 92 },
699 { C::bc_hashing_input_len, 4 },
700 { C::bc_hashing_packed_fields_0, sep },
701 { C::bc_hashing_packed_fields_1, bytecode_fields[0] },
702 { C::bc_hashing_packed_fields_2, bytecode_fields[1] },
703 { C::bc_hashing_pc_index_1, 0 },
704 { C::bc_hashing_pc_index_2, 31 },
705 { C::bc_hashing_sel_not_padding_1, 1 },
706 { C::bc_hashing_sel_not_padding_2, 1 },
707 { C::bc_hashing_bytecode_id, bad_hash },
708 { C::bc_hashing_pc_index, 0 },
709 { C::bc_hashing_rounds_rem, 2 },
710 { C::bc_hashing_padding, 2 },
711 { C::bc_hashing_sel, 1 },
712 { C::bc_hashing_start, 1 },
715 { C::bc_hashing_input_len, 4 },
716 { C::bc_hashing_end, 1 },
717 { C::bc_hashing_packed_fields_0, bytecode_fields[2] },
718 { C::bc_hashing_packed_fields_1, 0 },
719 { C::bc_hashing_packed_fields_2, 0 },
720 { C::bc_hashing_pc_index_1, 93 },
721 { C::bc_hashing_pc_index_2, 124 },
722 { C::bc_hashing_bytecode_id, bad_hash },
723 { C::bc_hashing_pc_index, 62 },
724 { C::bc_hashing_rounds_rem, 1 },
725 { C::bc_hashing_sel, 1 },
726 { C::bc_hashing_sel_not_start, 1 },
740 (check_interaction<BytecodeTraceBuilder, perm_bc_hashing_bytecode_length_bytes_settings>(trace)),
741 "PERM_BC_HASHING_BYTECODE_LENGTH_BYTES");
751TEST_F(BytecodeHashingConstrainingTest, NegativeGhostRowInjectionBlocked)
755 TestTraceContainer
trace({
756 { { C::precomputed_first_row, 1 } },
758 { C::bc_hashing_sel, 0 },
759 { C::bc_hashing_sel_not_padding_1, 1 },
760 { C::bc_hashing_sel_not_padding_2, 0 },
768 trace.
set(C::bc_hashing_sel_not_padding_1, 1, 0);
769 trace.
set(C::bc_hashing_sel_not_padding_2, 1, 1);
775TEST_F(BytecodeHashingConstrainingTestTraceHelper, NegativePaddingPropagationMultiRow)
782 TestTraceContainer
trace = process_bc_hashing_trace({ fields }, { 1 }, {
bytecode.size() });
789 trace.
set(C::bc_hashing_padding, 2, 1);
791 "PADDING_PROPAGATION");
794TEST_F(BytecodeHashingConstrainingTestTraceHelper, NegativeBytecodeFieldLengthViaPadding)
803 TestTraceContainer
trace = process_bc_hashing_trace({ fields }, { 1 }, {
bytecode.size() });
811 trace.
set(C::bc_hashing_padding, 1, 0);
813 "BYTECODE_LENGTH_FIELDS");
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
std::shared_ptr< Napi::ThreadSafeFunction > 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_PACKED_ROW_NEEDS_PERM_SELECTOR
static constexpr size_t SR_PADDING_CONSISTENCY
static constexpr size_t SR_PADDING_PROPAGATION
static constexpr size_t SR_ROUNDS_DECREMENT
static constexpr size_t SR_SEL_ON_START_OR_END
static constexpr size_t SR_PADDING_END
static constexpr size_t SR_PC_INCREMENTS_2
static constexpr size_t SR_START_IS_FIRST_FIELD
static constexpr size_t SR_START_AFTER_LATCH
static constexpr size_t SR_PADDED_BY_ZERO_2
static constexpr size_t SR_PADDING_COMPUTATION
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)
Processes the hash events for the Poseidon2 hash function. It populates the columns for the poseidon2...
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 set(Column col, uint32_t row, const FF &value)
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
PrecomputedTraceBuilder precomputed_builder
void check_multipermutation_interaction(tracegen::TestTraceContainer &trace)
TEST_F(AvmRecursiveTests, TwoLayerAvmRecursionFailsWithWrongPIs)
std::vector< FF > encode_bytecode(std::span< const uint8_t > bytecode)
Encodes the bytecode into a vector of field elements. Each field element represents 31 bytes of the b...
FF compute_public_bytecode_first_field(size_t bytecode_size)
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)