Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
instr_fetching.test.cpp
Go to the documentation of this file.
1#include <gmock/gmock.h>
2#include <gtest/gtest.h>
3
4#include <cstdint>
5#include <memory>
6#include <vector>
7
8#include "barretenberg/aztec/aztec_constants.hpp"
33
34namespace bb::avm2::constraining {
35namespace {
36
37using tracegen::BytecodeTraceBuilder;
38using tracegen::PrecomputedTraceBuilder;
39using tracegen::RangeCheckTraceBuilder;
40using tracegen::TestTraceContainer;
41
43using C = Column;
44
45using instr_fetching = instr_fetching<FF>;
46
47using simulation::BytecodeDecompositionEvent;
49using simulation::Instruction;
50using simulation::InstructionFetchingEvent;
52using simulation::RangeCheckEvent;
53
54TEST(InstrFetchingConstrainingTest, EmptyRow)
55{
56 check_relation<instr_fetching>(testing::empty_trace());
57}
58
59// Basic positive test with a hardcoded bytecode for ADD_8
60TEST(InstrFetchingConstrainingTest, Add8WithTraceGen)
61{
62 TestTraceContainer trace;
63 BytecodeTraceBuilder builder;
64 PrecomputedTraceBuilder precomputed_builder;
65
66 Instruction add_8_instruction = {
67 .opcode = WireOpCode::ADD_8,
68 .addressing_mode = 3,
69 .operands = { Operand::from<uint8_t>(0x34), Operand::from<uint8_t>(0x35), Operand::from<uint8_t>(0x36) },
70 };
71
72 std::vector<uint8_t> bytecode = add_8_instruction.serialize();
73
74 builder.process_instruction_fetching({ { .bytecode_id = 1,
75 .pc = 0,
76 .instruction = add_8_instruction,
78 trace);
79 precomputed_builder.process_misc(trace, trace.get_num_rows()); // Limit to the number of rows we need.
80
81 EXPECT_EQ(trace.get_num_rows(), 1);
82 check_relation<instr_fetching>(trace);
83}
84
85// Basic positive test with a hardcoded bytecode for ECADD
86// Cover the longest amount of operands.
87TEST(InstrFetchingConstrainingTest, EcaddWithTraceGen)
88{
89 TestTraceContainer trace;
90 BytecodeTraceBuilder builder;
91 PrecomputedTraceBuilder precomputed_builder;
92
93 Instruction ecadd_instruction = {
94 .opcode = WireOpCode::ECADD,
95 .addressing_mode = 0x1f1f,
96 .operands = { Operand::from<uint16_t>(0x1279),
97 Operand::from<uint16_t>(0x127a),
98 Operand::from<uint16_t>(0x127b),
99 Operand::from<uint16_t>(0x127c),
100 Operand::from<uint16_t>(0x127d), },
101 };
102
103 std::vector<uint8_t> bytecode = ecadd_instruction.serialize();
104 builder.process_instruction_fetching({ { .bytecode_id = 1,
105 .pc = 0,
106 .instruction = ecadd_instruction,
108 trace);
109 precomputed_builder.process_misc(trace, trace.get_num_rows()); // Limit to the number of rows we need.
110
111 EXPECT_EQ(trace.get_num_rows(), 1);
112 check_relation<instr_fetching>(trace);
113}
114
115// Helper routine generating a vector of instruction fetching events for each
116// opcode.
117std::vector<InstructionFetchingEvent> gen_instr_events_each_opcode()
118{
119 std::vector<uint8_t> bytecode;
120 std::vector<Instruction> instructions;
121 constexpr auto num_opcodes = static_cast<size_t>(WireOpCode::LAST_OPCODE_SENTINEL);
122 instructions.reserve(num_opcodes);
124
125 for (size_t i = 0; i < num_opcodes; i++) {
126 pc_positions.at(i) = static_cast<uint32_t>(bytecode.size());
127 const auto instr = testing::random_instruction(static_cast<WireOpCode>(i));
128 instructions.emplace_back(instr);
129 const auto instruction_bytes = instr.serialize();
130 bytecode.insert(bytecode.end(),
131 std::make_move_iterator(instruction_bytes.begin()),
132 std::make_move_iterator(instruction_bytes.end()));
133 }
134
135 const auto bytecode_ptr = std::make_shared<std::vector<uint8_t>>(std::move(bytecode));
136 // Always use *bytecode_ptr from now on instead of bytecode as this one was moved.
137
139 instr_events.reserve(num_opcodes);
140 for (size_t i = 0; i < num_opcodes; i++) {
141 instr_events.emplace_back(InstructionFetchingEvent{
142 .bytecode_id = 1, .pc = pc_positions.at(i), .instruction = instructions.at(i), .bytecode = bytecode_ptr });
143 }
144 return instr_events;
145}
146
147// Positive test for each opcode. We assume that decode instruction is working correctly.
148// It works as long as the relations are not constraining the correct range for TAG nor indirect.
149TEST(InstrFetchingConstrainingTest, EachOpcodeWithTraceGen)
150{
151 TestTraceContainer trace;
152 BytecodeTraceBuilder builder;
153 PrecomputedTraceBuilder precomputed_builder;
154
155 builder.process_instruction_fetching(gen_instr_events_each_opcode(), trace);
156 precomputed_builder.process_misc(trace, trace.get_num_rows()); // Limit to the number of rows we need.
157
158 constexpr auto num_opcodes = static_cast<size_t>(WireOpCode::LAST_OPCODE_SENTINEL);
159 EXPECT_EQ(trace.get_num_rows(), num_opcodes);
160 check_relation<instr_fetching>(trace);
161}
162
163// Negative test about decomposition of operands. We mutate correct operand values in the trace.
164// This also covers wrong operands which are not "involved" by the instruction.
165// We perform this for a random instruction for opcodes: REVERT_16, CAST_8, TORADIXBE
166TEST(InstrFetchingConstrainingTest, NegativeWrongOperand)
167{
168 BytecodeTraceBuilder builder;
169 PrecomputedTraceBuilder precomputed_builder;
170
172 std::vector<size_t> sub_relations = {
179 };
180
181 constexpr std::array<C, AVM_MAX_OPERANDS + 1> operand_cols = {
182 C::instr_fetching_addressing_mode,
183 C::instr_fetching_op1,
184 C::instr_fetching_op2,
185 C::instr_fetching_op3,
186 C::instr_fetching_op4,
187 C::instr_fetching_op5,
188 };
189
190 for (const auto& opcode : opcodes) {
191 TestTraceContainer trace;
192 const auto instr = testing::random_instruction(opcode);
193 builder.process_instruction_fetching(
194 { { .bytecode_id = 1,
195 .pc = 0,
196 .instruction = instr,
197 .bytecode = std::make_shared<std::vector<uint8_t>>(instr.serialize()) } },
198 trace);
199 precomputed_builder.process_misc(trace, trace.get_num_rows()); // Limit to the number of rows we need.
200
201 check_relation<instr_fetching>(trace);
202
203 EXPECT_EQ(trace.get_num_rows(), 1);
204
205 for (size_t i = 0; i < operand_cols.size(); i++) {
206 auto mutated_trace = trace;
207 const FF mutated_operand = trace.get(operand_cols.at(i), 0) + 1; // Mutate to value + 1
208 mutated_trace.set(operand_cols.at(i), 0, mutated_operand);
209 EXPECT_THROW_WITH_MESSAGE(check_relation<instr_fetching>(mutated_trace, sub_relations.at(i)),
210 instr_fetching::get_subrelation_label(sub_relations.at(i)));
211 }
212 }
213}
214
215// Positive test for interaction with instruction spec table using same events as for the test
216// EachOpcodeWithTraceGen, i.e., one event/row is generated per wire opcode.
217// It works as long as the relations are not constraining the correct range for TAG nor indirect.
218TEST(InstrFetchingConstrainingTest, WireInstructionSpecInteractions)
219{
220 TestTraceContainer trace;
221 BytecodeTraceBuilder bytecode_builder;
222 PrecomputedTraceBuilder precomputed_builder;
223
226 bytecode_builder.process_instruction_fetching(gen_instr_events_each_opcode(), trace);
227 precomputed_builder.process_misc(trace, trace.get_num_rows()); // Limit to the number of rows we need.
228
229 EXPECT_EQ(trace.get_num_rows(), 1 << 8); // 2^8 for selector against wire_instruction_spec
230
231 check_interaction<BytecodeTraceBuilder, lookup_instr_fetching_wire_instruction_info_settings>(trace);
232 check_relation<instr_fetching>(trace);
233}
234
235std::vector<RangeCheckEvent> gen_range_check_events(const std::vector<InstructionFetchingEvent>& instr_events)
236{
237 std::vector<RangeCheckEvent> range_check_events;
238 range_check_events.reserve(instr_events.size());
239
240 for (const auto& instr_event : instr_events) {
241 range_check_events.emplace_back(RangeCheckEvent{
242 .value =
243 (instr_event.error.has_value() && instr_event.error == InstrDeserializationEventError::PC_OUT_OF_RANGE)
244 ? instr_event.pc - instr_event.bytecode->size()
245 : instr_event.bytecode->size() - instr_event.pc - 1,
246 .num_bits = AVM_PC_SIZE_IN_BITS,
247 });
248 }
249 return range_check_events;
250}
251
252// Positive test for the interaction with bytecode decomposition table.
253// One event/row is generated per wire opcode (same as for test WireInstructionSpecInteractions).
254TEST(InstrFetchingConstrainingTest, BcDecompositionInteractions)
255{
256 TestTraceContainer trace;
257 BytecodeTraceBuilder bytecode_builder;
258 PrecomputedTraceBuilder precomputed_builder;
259
260 const auto instr_fetch_events = gen_instr_events_each_opcode();
261 bytecode_builder.process_instruction_fetching(instr_fetch_events, trace);
262 bytecode_builder.process_decomposition({ {
263 .bytecode_id = instr_fetch_events.at(0).bytecode_id,
264 .bytecode = instr_fetch_events.at(0).bytecode,
265 } },
266 trace);
267 precomputed_builder.process_misc(trace, trace.get_num_rows()); // Limit to the number of rows we need.
268
269 check_interaction<BytecodeTraceBuilder,
272
273 // BC Decomposition trace is the longest here and requires an extra prepended row.
274 EXPECT_EQ(trace.get_num_rows(), instr_fetch_events.at(0).bytecode->size() + 1);
275
276 check_relation<instr_fetching>(trace);
277}
278
279void check_all(const std::vector<InstructionFetchingEvent>& instr_events,
280 const std::vector<RangeCheckEvent>& range_check_events,
282{
283 TestTraceContainer trace;
284 BytecodeTraceBuilder bytecode_builder;
285 PrecomputedTraceBuilder precomputed_builder;
286 RangeCheckTraceBuilder range_check_builder;
287
292 bytecode_builder.process_instruction_fetching(instr_events, trace);
293 bytecode_builder.process_decomposition(decomposition_events, trace);
294 range_check_builder.process(range_check_events, trace);
295 precomputed_builder.process_misc(trace, trace.get_num_rows()); // Limit to the number of rows we need.
296
297 check_interaction<BytecodeTraceBuilder,
304
305 EXPECT_EQ(trace.get_num_rows(), 1 << 16); // 2^16 for range checks
306
307 check_relation<instr_fetching>(trace);
308}
309
310void check_without_range_check(const std::vector<InstructionFetchingEvent>& instr_events,
312{
313 TestTraceContainer trace;
314 BytecodeTraceBuilder bytecode_builder;
315 PrecomputedTraceBuilder precomputed_builder;
316
320 bytecode_builder.process_instruction_fetching(instr_events, trace);
321 bytecode_builder.process_decomposition(decomposition_events, trace);
322 precomputed_builder.process_misc(trace, trace.get_num_rows()); // Limit to the number of rows we need.
323
324 check_interaction<BytecodeTraceBuilder,
330
331 EXPECT_EQ(trace.get_num_rows(), 1 << 8); // 2^8 for range checks
332
333 check_relation<instr_fetching>(trace);
334}
335
336// Positive test with 5 five bytecodes and bytecode_id = 0,1,2,3,4
337// Bytecode i is generated by truncating instr_fetch_events to i * 6 instructions.
338// Check relations and all interactions.
339TEST(InstrFetchingConstrainingTest, MultipleBytecodes)
340{
341 const auto instr_fetch_events = gen_instr_events_each_opcode();
342 constexpr size_t num_of_bytecodes = 5;
345
346 for (size_t i = 0; i < num_of_bytecodes; i++) {
347 std::vector<uint8_t> bytecode;
348 const auto num_of_instr = i * 6;
349
350 for (size_t j = 0; j < num_of_instr; j++) {
351 const auto& instr = instr_fetch_events.at(j).instruction;
352 const auto instruction_bytes = instr.serialize();
353 bytecode.insert(bytecode.end(),
354 std::make_move_iterator(instruction_bytes.begin()),
355 std::make_move_iterator(instruction_bytes.end()));
356 }
357
358 const auto bytecode_ptr = std::make_shared<std::vector<uint8_t>>(std::move(bytecode));
359
360 for (size_t j = 0; j < num_of_instr; j++) {
361 auto instr_event = instr_fetch_events.at(j);
362 instr_event.bytecode_id = static_cast<BytecodeId>(i);
363 instr_event.bytecode = bytecode_ptr;
364 instr_events.emplace_back(instr_event);
365 }
366
367 decomposition_events.emplace_back(BytecodeDecompositionEvent{
368 .bytecode_id = static_cast<BytecodeId>(i),
369 .bytecode = bytecode_ptr,
370 });
371 }
372
373 check_all(instr_events, gen_range_check_events(instr_events), decomposition_events);
374}
375
376// Positive test with one single instruction with error INSTRUCTION_OUT_OF_RANGE.
377// The bytecode consists into a serialized single instruction with pc = 0 and
378// the bytecode had the last byte removed. This byte corresponds to a full operand.
379TEST(InstrFetchingConstrainingTest, SingleInstructionOutOfRange)
380{
381 Instruction add_8_instruction = {
382 .opcode = WireOpCode::ADD_8,
383 .addressing_mode = 3,
384 .operands = { Operand::from<uint8_t>(0x34), Operand::from<uint8_t>(0x35), Operand::from<uint8_t>(0x36) },
385 };
386
387 std::vector<uint8_t> bytecode = add_8_instruction.serialize();
388 bytecode.pop_back(); // Remove last byte
389 const auto bytecode_ptr = std::make_shared<std::vector<uint8_t>>(std::move(bytecode));
390
391 const std::vector<InstructionFetchingEvent> instr_events = {
392 {
393 .bytecode_id = 1,
394 .pc = 0,
395 .bytecode = bytecode_ptr,
396 .error = InstrDeserializationEventError::INSTRUCTION_OUT_OF_RANGE,
397 },
398 };
399
401 {
402 .bytecode_id = 1,
403 .bytecode = bytecode_ptr,
404 },
405 };
406
407 check_without_range_check(instr_events, decomposition_events);
408}
409
410// Positive test with one single instruction (SET_FF) with error INSTRUCTION_OUT_OF_RANGE.
411// The bytecode consists into a serialized single instruction with pc = 0 and
412// the bytecode had the two last bytes removed. The truncated instruction is cut
413// in the middle of an operand.
414TEST(InstrFetchingConstrainingTest, SingleInstructionOutOfRangeSplitOperand)
415{
416 Instruction set_ff_instruction = {
417 .opcode = WireOpCode::SET_FF,
418 .addressing_mode = 0x01,
419 .operands = { Operand::from<uint16_t>(0x1279),
420 Operand::from<uint8_t>(static_cast<uint8_t>(MemoryTag::FF)),
421 Operand::from<FF>(FF::modulus_minus_two) },
422 };
423
424 std::vector<uint8_t> bytecode = set_ff_instruction.serialize();
425 bytecode.resize(bytecode.size() - 2); // Remove last two bytes)
426 const auto bytecode_ptr = std::make_shared<std::vector<uint8_t>>(std::move(bytecode));
427
428 const std::vector<InstructionFetchingEvent> instr_events = {
429 {
430 .bytecode_id = 1,
431 .pc = 0,
432 .bytecode = bytecode_ptr,
433 .error = InstrDeserializationEventError::INSTRUCTION_OUT_OF_RANGE,
434 },
435 };
436
438 {
439 .bytecode_id = 1,
440 .bytecode = bytecode_ptr,
441 },
442 };
443
444 check_without_range_check(instr_events, decomposition_events);
445}
446
447// Positive test with error case PC_OUT_OF_RANGE. We pass a pc which is out of range.
448TEST(InstrFetchingConstrainingTest, SingleInstructionPcOutOfRange)
449{
450 Instruction add_8_instruction = {
451 .opcode = WireOpCode::SUB_8,
452 .addressing_mode = 3,
453 .operands = { Operand::from<uint8_t>(0x34), Operand::from<uint8_t>(0x35), Operand::from<uint8_t>(0x36) },
454 };
455
456 std::vector<uint8_t> bytecode = add_8_instruction.serialize();
457 const auto bytecode_ptr = std::make_shared<std::vector<uint8_t>>(std::move(bytecode));
458
459 const std::vector<InstructionFetchingEvent> instr_events = {
460 // We first need a first instruction at pc == 0 as the trace assumes this.
461 {
462 .bytecode_id = 1,
463 .pc = 0,
464 .instruction = add_8_instruction,
465 .bytecode = bytecode_ptr,
466 },
467 {
468 .bytecode_id = 1,
469 .pc = static_cast<uint32_t>(bytecode_ptr->size() + 1),
470 .bytecode = bytecode_ptr,
471 .error = InstrDeserializationEventError::PC_OUT_OF_RANGE,
472 },
473 };
474
476 {
477 .bytecode_id = 1,
478 .bytecode = bytecode_ptr,
479 },
480 };
481
482 check_all(instr_events, gen_range_check_events(instr_events), decomposition_events);
483}
484
485// Positive test with error case OPCODE_OUT_OF_RANGE. We generate bytecode of a SET_128 instruction and
486// move the PC to a position corresponding to the beginning of the 128-bit immediate value of SET_128.
487// The immediate value in SET_128 starts with byte 0xFF (which we know is not a valid opcode).
488TEST(InstrFetchingConstrainingTest, SingleInstructionOpcodeOutOfRange)
489{
490 Instruction set_128_instruction = {
491 .opcode = WireOpCode::SET_128,
492 .addressing_mode = 0,
493 .operands = { Operand::from<uint16_t>(0x1234),
494 Operand::from<uint8_t>(static_cast<uint8_t>(MemoryTag::U128)),
495 Operand::from<uint128_t>(static_cast<uint128_t>(0xFF) << 120) },
496 };
497
498 std::vector<uint8_t> bytecode = set_128_instruction.serialize();
499 const auto bytecode_ptr = std::make_shared<std::vector<uint8_t>>(std::move(bytecode));
500
501 const std::vector<InstructionFetchingEvent> instr_events = {
502 {
503 .bytecode_id = 1,
504 .pc = 0,
505 .instruction = set_128_instruction,
506 .bytecode = bytecode_ptr,
507 },
508 {
509 .bytecode_id = 1,
510 .pc = 5, // We move pc to the beginning of the 128-bit immediate value.
511 .bytecode = bytecode_ptr,
512 .error = InstrDeserializationEventError::OPCODE_OUT_OF_RANGE,
513 },
514 };
515
517 {
518 .bytecode_id = 1,
519 .bytecode = bytecode_ptr,
520 },
521 };
522
523 check_without_range_check(instr_events, decomposition_events);
524}
525
526// Positive test with one single instruction (SET_16) with error TAG_OUT_OF_RANGE.
527// The bytecode consists into a serialized single instruction with pc = 0.
528// The operand at index 1 is wrongly set to value 12
529TEST(InstrFetchingConstrainingTest, SingleInstructionTagOutOfRange)
530{
531 Instruction set_16_instruction = {
532 .opcode = WireOpCode::SET_16,
533 .addressing_mode = 0,
534 .operands = { Operand::from<uint16_t>(0x1234), Operand::from<uint8_t>(12), Operand::from<uint16_t>(0x5678) },
535 };
536
537 std::vector<uint8_t> bytecode = set_16_instruction.serialize();
538 const auto bytecode_ptr = std::make_shared<std::vector<uint8_t>>(std::move(bytecode));
539
540 const std::vector<InstructionFetchingEvent> instr_events = {
541 {
542 .bytecode_id = 1,
543 .pc = 0,
544 .instruction = set_16_instruction,
545 .bytecode = bytecode_ptr,
546 .error = InstrDeserializationEventError::TAG_OUT_OF_RANGE,
547 },
548 };
549
551 {
552 .bytecode_id = 1,
553 .bytecode = bytecode_ptr,
554 },
555 };
556
557 check_without_range_check(instr_events, decomposition_events);
558}
559
560// Negative interaction test with some values not matching the instruction spec table.
561TEST(InstrFetchingConstrainingTest, NegativeWrongWireInstructionSpecInteractions)
562{
563 BytecodeTraceBuilder bytecode_builder;
564 PrecomputedTraceBuilder precomputed_builder;
565
566 // Some arbitrary chosen opcodes. We limit to one as this unit test is costly.
567 // Test works if the following vector is extended to other opcodes though.
569
570 for (const auto& opcode : opcodes) {
571 TestTraceContainer trace;
572 const auto instr = testing::random_instruction(opcode);
573 bytecode_builder.process_instruction_fetching(
574 { { .bytecode_id = 1,
575 .pc = 0,
576 .instruction = instr,
577 .bytecode = std::make_shared<std::vector<uint8_t>>(instr.serialize()) } },
578 trace);
581 precomputed_builder.process_misc(trace, trace.get_num_rows()); // Limit to the number of rows we need.
582
583 check_interaction<BytecodeTraceBuilder, lookup_instr_fetching_wire_instruction_info_settings>(trace);
584
585 ASSERT_EQ(trace.get(C::lookup_instr_fetching_wire_instruction_info_counts, static_cast<uint32_t>(opcode)), 1);
586
587 constexpr std::array<C, 20> mutated_cols = {
588 C::instr_fetching_exec_opcode, C::instr_fetching_instr_size, C::instr_fetching_sel_has_tag,
589 C::instr_fetching_sel_tag_is_op2, C::instr_fetching_sel_op_dc_0, C::instr_fetching_sel_op_dc_1,
590 C::instr_fetching_sel_op_dc_2, C::instr_fetching_sel_op_dc_3, C::instr_fetching_sel_op_dc_4,
591 C::instr_fetching_sel_op_dc_5, C::instr_fetching_sel_op_dc_6, C::instr_fetching_sel_op_dc_7,
592 C::instr_fetching_sel_op_dc_8, C::instr_fetching_sel_op_dc_9, C::instr_fetching_sel_op_dc_10,
593 C::instr_fetching_sel_op_dc_11, C::instr_fetching_sel_op_dc_12, C::instr_fetching_sel_op_dc_13,
594 C::instr_fetching_sel_op_dc_14, C::instr_fetching_sel_op_dc_15
595 };
596
597 // Mutate execution opcode
598 for (const auto& col : mutated_cols) {
599 auto mutated_trace = trace;
600 const FF mutated_value = trace.get(col, 0) + 1; // Mutate to value + 1
601 mutated_trace.set(col, 0, mutated_value);
602
604 (check_interaction<BytecodeTraceBuilder, lookup_instr_fetching_wire_instruction_info_settings>(
605 mutated_trace)),
606 "Failed.*LOOKUP_INSTR_FETCHING_WIRE_INSTRUCTION_INFO.*Could not find tuple in destination.");
607 }
608 }
609}
610
611// Negative interaction test with some values not matching the bytecode decomposition table.
612TEST(InstrFetchingConstrainingTest, NegativeWrongBcDecompositionInteractions)
613{
614 TestTraceContainer trace;
615 BytecodeTraceBuilder bytecode_builder;
616
617 // Some arbitrary chosen opcodes. We limit to one as this unit test is costly.
618 // Test works if the following vector is extended to other opcodes though.
620
621 for (const auto& opcode : opcodes) {
622 TestTraceContainer trace;
623 const auto instr = testing::random_instruction(opcode);
624 auto bytecode_ptr = std::make_shared<std::vector<uint8_t>>(instr.serialize());
625 bytecode_builder.process_instruction_fetching({ {
626 .bytecode_id = 1,
627 .pc = 0,
628 .instruction = instr,
629 .bytecode = bytecode_ptr,
630 } },
631 trace);
632 bytecode_builder.process_decomposition({ {
633 .bytecode_id = 1,
634 .bytecode = bytecode_ptr,
635 } },
636 trace);
637
638 auto valid_trace = trace; // Keep original trace before lookup processing
639 check_interaction<BytecodeTraceBuilder, lookup_instr_fetching_bytes_from_bc_dec_settings>(valid_trace);
640
641 constexpr std::array<C, 39> mutated_cols = {
642 C::instr_fetching_pc, C::instr_fetching_bytecode_id, C::instr_fetching_bd0, C::instr_fetching_bd1,
643 C::instr_fetching_bd2, C::instr_fetching_bd3, C::instr_fetching_bd4, C::instr_fetching_bd5,
644 C::instr_fetching_bd6, C::instr_fetching_bd7, C::instr_fetching_bd8, C::instr_fetching_bd9,
645 C::instr_fetching_bd10, C::instr_fetching_bd11, C::instr_fetching_bd12, C::instr_fetching_bd13,
646 C::instr_fetching_bd14, C::instr_fetching_bd15, C::instr_fetching_bd16, C::instr_fetching_bd17,
647 C::instr_fetching_bd18, C::instr_fetching_bd19, C::instr_fetching_bd20, C::instr_fetching_bd21,
648 C::instr_fetching_bd22, C::instr_fetching_bd23, C::instr_fetching_bd24, C::instr_fetching_bd25,
649 C::instr_fetching_bd26, C::instr_fetching_bd27, C::instr_fetching_bd28, C::instr_fetching_bd29,
650 C::instr_fetching_bd30, C::instr_fetching_bd31, C::instr_fetching_bd32, C::instr_fetching_bd33,
651 C::instr_fetching_bd34, C::instr_fetching_bd35, C::instr_fetching_bd36,
652 };
653
654 // Mutate execution opcode
655 for (const auto& col : mutated_cols) {
656 auto mutated_trace = trace;
657 const FF mutated_value = trace.get(col, 0) + 1; // Mutate to value + 1
658 mutated_trace.set(col, 0, mutated_value);
659
661 (check_interaction<BytecodeTraceBuilder, lookup_instr_fetching_bytes_from_bc_dec_settings>(
662 mutated_trace)),
663 "Failed.*BYTES_FROM_BC_DEC. Could not find tuple in destination.");
664 }
665 }
666}
667
668// Negative interaction test for #[BYTECODE_SIZE_FROM_BC_DEC] where bytecode_size has the wrong value.
669// We set pc different from zero.
670TEST(InstrFetchingConstrainingTest, NegativeWrongBytecodeSizeBcDecompositionInteractions)
671{
672 TestTraceContainer trace;
673 BytecodeTraceBuilder bytecode_builder;
674 PrecomputedTraceBuilder precomputed_builder;
675
676 const uint32_t pc = 15;
677 std::vector<uint8_t> bytecode(pc, 0x23);
678
679 // Some arbitrary chosen opcodes. We limit to one as this unit test is costly.
680 // Test works if the following vector is extended to other opcodes though.
682
683 for (const auto& opcode : opcodes) {
684 TestTraceContainer trace;
685
686 const auto instr = testing::random_instruction(opcode);
687 const auto instr_bytecode = instr.serialize();
688 bytecode.insert(bytecode.end(),
689 std::make_move_iterator(instr_bytecode.begin()),
690 std::make_move_iterator(instr_bytecode.end()));
692
693 bytecode_builder.process_instruction_fetching({ {
694 .bytecode_id = 1,
695 .pc = pc,
696 .instruction = instr,
697 .bytecode = bytecode_ptr,
698 } },
699 trace);
700 bytecode_builder.process_decomposition({ {
701 .bytecode_id = 1,
702 .bytecode = bytecode_ptr,
703 } },
704 trace);
705 precomputed_builder.process_misc(trace, trace.get_num_rows()); // Limit to the number of rows we need.
706
707 auto valid_trace = trace; // Keep original trace before lookup processing
708 check_interaction<BytecodeTraceBuilder, lookup_instr_fetching_bytecode_size_from_bc_dec_settings>(valid_trace);
709
710 auto mutated_trace = trace;
711 const FF mutated_value = trace.get(C::instr_fetching_bytecode_size, 0) + 1; // Mutate to value + 1
712 mutated_trace.set(C::instr_fetching_bytecode_size, 0, mutated_value);
713
715 (check_interaction<BytecodeTraceBuilder, lookup_instr_fetching_bytecode_size_from_bc_dec_settings>(
716 mutated_trace)),
717 "Failed.*BYTECODE_SIZE_FROM_BC_DEC. Could not find tuple in destination.");
718 }
719}
720
721using ::bb::avm2::testing::InstructionBuilder;
722using simulation::EventEmitter;
723using simulation::MockExecutionIdManager;
724using simulation::MockGreaterThan;
725using simulation::Poseidon2;
726using simulation::Poseidon2HashEvent;
727using simulation::Poseidon2PermutationEvent;
728using simulation::Poseidon2PermutationMemoryEvent;
729using ::testing::StrictMock;
730using tracegen::Poseidon2TraceBuilder;
731
732TEST(InstrFetchingConstrainingTest, NegativeTruncatedBytecodeRepro)
733{
734 TestTraceContainer trace;
735 BytecodeTraceBuilder bytecode_builder;
736 PrecomputedTraceBuilder precomputed_builder;
737 RangeCheckTraceBuilder range_check_builder;
738 EventEmitter<Poseidon2HashEvent> hash_event_emitter;
739 EventEmitter<Poseidon2PermutationEvent> perm_event_emitter;
740 EventEmitter<Poseidon2PermutationMemoryEvent> perm_mem_event_emitter;
741 StrictMock<MockGreaterThan> mock_gt;
742 StrictMock<MockExecutionIdManager> mock_execution_id_manager;
743 // Note: this helper expects bytecode fields without the prepended separator and does not complete decomposition
746
747 Poseidon2TraceBuilder poseidon2_builder;
748
749 // Build some good bytecode:
750 const uint32_t pc = 15;
751 std::vector<uint8_t> bytecode(pc, 0x23);
752 const auto add_instr =
753 InstructionBuilder(WireOpCode::SUB_8).operand<uint8_t>(5).operand<uint8_t>(5).operand<uint8_t>(0).build();
754 const auto instr_bytecode = add_instr.serialize();
755 bytecode.insert(
756 bytecode.end(), std::make_move_iterator(instr_bytecode.begin()), std::make_move_iterator(instr_bytecode.end()));
757
758 std::vector<FF> fields = simulation::encode_bytecode(bytecode);
759 std::vector<FF> prepended_fields = { simulation::compute_public_bytecode_first_field(bytecode.size()) };
760 prepended_fields.insert(prepended_fields.end(), fields.begin(), fields.end());
762
763 // Remove the final byte (which has a value of zero)
764 std::vector<uint8_t> trunc_bytecode(pc, 0x23);
765 trunc_bytecode.insert(trunc_bytecode.end(),
766 std::make_move_iterator(instr_bytecode.begin()),
767 std::make_move_iterator(instr_bytecode.end()));
768 trunc_bytecode.resize(trunc_bytecode.size() - 1);
769 std::vector<FF> trunc_fields = simulation::encode_bytecode(trunc_bytecode);
770 std::vector<FF> trunc_prepended_fields = { DOM_SEP__PUBLIC_BYTECODE };
771 trunc_prepended_fields.insert(trunc_prepended_fields.end(), trunc_fields.begin(), trunc_fields.end());
772 FF trunc_hash = poseidon2.hash(trunc_prepended_fields);
773 // 'Real' bytecode: [ 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 02 00 05 05 00 ] of length 20 bytes
774 // We could previously process a truncated bytecode with the same id:
775 // 'Fake' bytecode: [ 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 02 00 05 05 ] of length 19 bytes
776 // Before introducing #[BYTECODE_LENGTH_BYTES] in bc_hashing.pil and including the size in
777 // compute_public_bytecode_first_field(), (#20254) trunc_hash == hash, meaning we could use truncated bytecode.
778 ASSERT_NE(hash, trunc_hash);
779
780 // Now, we cannot process the truncated bytecode and force a good instruction on the full bytecode to fail:
781 auto trunc_bytecode_ptr = std::make_shared<std::vector<uint8_t>>(trunc_bytecode);
783 InstructionFetchingEvent instr_event = {
784 .bytecode_id = hash,
785 .pc = pc,
786 .instruction = add_instr,
787 .bytecode = bytecode_ptr,
788 };
789 bytecode_builder.process_instruction_fetching({ instr_event }, trace);
790 bytecode_builder.process_hashing({ {
791 .bytecode_id = hash,
792 .bytecode_length_in_bytes = static_cast<uint32_t>(trunc_bytecode.size()),
793 .bytecode_fields = trunc_fields,
794 } },
795 trace);
796
797 bytecode_builder.process_decomposition({ {
798 .bytecode_id = hash,
799 .bytecode = trunc_bytecode_ptr,
800 } },
801 trace);
802
803 // Prep trace:
804 range_check_builder.process(gen_range_check_events({ instr_event }), trace);
809
810 tracegen::MultiPermutationBuilder<perm_bc_hashing_get_packed_field_0_settings,
813 perm_builder(C::bc_decomposition_sel_packed);
814 perm_builder.process(trace);
815
816 check_relation<bb::avm2::bc_hashing<FF>>(trace);
818 (check_interaction<BytecodeTraceBuilder, lookup_bc_hashing_poseidon2_hash_settings>(trace)),
819 "Failed.*LOOKUP_BC_HASHING_POSEIDON2_HASH. Could not find tuple in destination.");
820}
821
822TEST(InstrFetchingConstrainingTest, NegativeWrongTagValidationInteractions)
823{
824 TestTraceContainer trace;
825 BytecodeTraceBuilder bytecode_builder;
826 PrecomputedTraceBuilder precomputed_builder;
827
828 // Some chosen opcode with a tag. We limit to one as this unit test is costly.
829 // Test works if the following vector is extended to other opcodes though.
831
832 for (const auto& opcode : opcodes) {
833 TestTraceContainer trace;
834 const auto instr = testing::random_instruction(opcode);
835 bytecode_builder.process_instruction_fetching(
836 { { .bytecode_id = 1,
837 .pc = 0,
838 .instruction = instr,
839 .bytecode = std::make_shared<std::vector<uint8_t>>(instr.serialize()) } },
840 trace);
843 precomputed_builder.process_misc(trace, trace.get_num_rows()); // Limit to the number of rows we need.
844
845 check_interaction<BytecodeTraceBuilder, lookup_instr_fetching_tag_value_validation_settings>(trace);
846
847 auto valid_trace = trace; // Keep original trace before lookup processing
848
849 // Mutate tag out-of-range error
850 auto mutated_trace = trace;
851 ASSERT_EQ(trace.get(C::instr_fetching_tag_out_of_range, 0), 0);
852 mutated_trace.set(C::instr_fetching_tag_out_of_range, 0, 1); // Mutate by toggling the error.
853
855 (check_interaction<BytecodeTraceBuilder, lookup_instr_fetching_tag_value_validation_settings>(
856 mutated_trace)),
857 "Failed.*LOOKUP_INSTR_FETCHING_TAG_VALUE_VALIDATION.*Could not find tuple in destination.");
858 }
859}
860
861// Negative test on wrongly setting tag_out_of_range when the opcode has no tag
862TEST(InstrFetchingConstrainingTest, NegativeTagOutOfRangeNoTag)
863{
864 TestTraceContainer trace;
865 BytecodeTraceBuilder bytecode_builder;
866 PrecomputedTraceBuilder precomputed_builder;
867
868 // Some chosen opcode without a tag
870
871 const auto instr = testing::random_instruction(opcode);
872 bytecode_builder.process_instruction_fetching(
873 { { .bytecode_id = 1,
874 .pc = 0,
875 .instruction = instr,
876 .bytecode = std::make_shared<std::vector<uint8_t>>(instr.serialize()) } },
877 trace);
880 precomputed_builder.process_misc(trace, trace.get_num_rows()); // Limit to the number of rows we need.
881
882 check_interaction<BytecodeTraceBuilder, lookup_instr_fetching_tag_value_validation_settings>(trace);
883
884 // Mutate tag out-of-range error
885 ASSERT_EQ(trace.get(C::instr_fetching_tag_out_of_range, 1), 0);
886 ASSERT_EQ(trace.get(C::instr_fetching_sel_has_tag, 1), 0);
887 trace.set(C::instr_fetching_tag_out_of_range, 1, 1); // Mutate by toggling the error.
888
889 EXPECT_THROW_WITH_MESSAGE(check_relation<instr_fetching>(trace, instr_fetching::SR_TAG_OUT_OF_RANGE_ZERO),
891}
892
893// Negative test on not toggling instr_out_of_range when instr_size > bytes_to_read
894TEST(InstrFetchingConstrainingTest, NegativeNotTogglingInstrOutOfRange)
895{
896 TestTraceContainer trace({
897 {
898 { C::instr_fetching_bytes_to_read, 11 },
899 { C::instr_fetching_instr_abs_diff, 0 },
900 { C::instr_fetching_instr_out_of_range, 1 }, // Will be mutated to zero
901 { C::instr_fetching_instr_size, 12 },
902 { C::instr_fetching_sel, 1 },
903 },
904 });
905
906 check_relation<instr_fetching>(trace, instr_fetching::SR_INSTR_OUT_OF_RANGE_TOGGLE);
907
908 trace.set(C::instr_fetching_instr_out_of_range, 0, 0); // Mutate to wrong value
909
910 EXPECT_THROW_WITH_MESSAGE(check_relation<instr_fetching>(trace, instr_fetching::SR_INSTR_OUT_OF_RANGE_TOGGLE),
912}
913
914// Negative test on wrongly toggling instr_out_of_range when instr_size <= bytes_to_read
915TEST(InstrFetchingConstrainingTest, NegativeTogglingInstrInRange)
916{
917 TestTraceContainer trace({
918 {
919 { C::instr_fetching_bytes_to_read, 12 },
920 { C::instr_fetching_instr_abs_diff, 0 },
921 { C::instr_fetching_instr_out_of_range, 0 }, // Will be mutated to 1
922 { C::instr_fetching_instr_size, 12 },
923 { C::instr_fetching_sel, 1 },
924 },
925 });
926
927 check_relation<instr_fetching>(trace, instr_fetching::SR_INSTR_OUT_OF_RANGE_TOGGLE);
928
929 trace.set(C::instr_fetching_instr_out_of_range, 0, 1); // Mutate to wrong value
930
931 EXPECT_THROW_WITH_MESSAGE(check_relation<instr_fetching>(trace, instr_fetching::SR_INSTR_OUT_OF_RANGE_TOGGLE),
933}
934
935// Negative test on not toggling pc_out_of_range when pc >= bytecode_size
936TEST(InstrFetchingConstrainingTest, NegativeNotTogglingPcOutOfRange)
937{
938 TestTraceContainer trace({
939 {
940 { C::instr_fetching_bytecode_size, 12 },
941 { C::instr_fetching_pc, 12 },
942 { C::instr_fetching_pc_abs_diff, 0 },
943 { C::instr_fetching_pc_out_of_range, 1 }, // Will be mutated to 0
944 { C::instr_fetching_sel, 1 },
945 },
946 });
947
948 check_relation<instr_fetching>(trace, instr_fetching::SR_PC_OUT_OF_RANGE_TOGGLE);
949
950 trace.set(C::instr_fetching_pc_out_of_range, 0, 0); // Mutate to wrong value
951
952 EXPECT_THROW_WITH_MESSAGE(check_relation<instr_fetching>(trace, instr_fetching::SR_PC_OUT_OF_RANGE_TOGGLE),
954}
955
956// Negative test on setting sel_has_tag when pc >= bytecode_size
957TEST(InstrFetchingConstrainingTest, NegativeTagSelPcOutOfRange)
958{
959 TestTraceContainer trace({
960 { { C::precomputed_first_row, 1 } },
961 {
962 { C::instr_fetching_bytecode_size, 12 },
963 { C::instr_fetching_pc, 12 },
964 { C::instr_fetching_pc_abs_diff, 0 },
965 { C::instr_fetching_pc_out_of_range, 1 },
966 { C::instr_fetching_sel_pc_in_range, 0 },
967 { C::instr_fetching_sel_has_tag, 0 }, // Will be mutated to 1
968 { C::instr_fetching_sel, 1 },
969 },
970 });
971
972 check_relation<instr_fetching>(trace, instr_fetching::SR_SEL_HAS_TAG_ZERO);
973
974 trace.set(C::instr_fetching_sel_has_tag, 1, 1); // Mutate to wrong value
975
976 EXPECT_THROW_WITH_MESSAGE(check_relation<instr_fetching>(trace, instr_fetching::SR_SEL_HAS_TAG_ZERO),
978}
979
980// Negative test on wrongly toggling pc_out_of_range when pc < bytecode_size
981TEST(InstrFetchingConstrainingTest, NegativeTogglingPcInRange)
982{
983 TestTraceContainer trace({
984 {
985 { C::instr_fetching_bytecode_size, 12 },
986 { C::instr_fetching_pc, 11 },
987 { C::instr_fetching_pc_abs_diff, 0 },
988 { C::instr_fetching_pc_out_of_range, 0 }, // Will be mutated to 1
989 { C::instr_fetching_sel, 1 },
990 },
991 });
992
993 check_relation<instr_fetching>(trace, instr_fetching::SR_PC_OUT_OF_RANGE_TOGGLE);
994
995 trace.set(C::instr_fetching_pc_out_of_range, 0, 1); // Mutate to wrong value
996
997 EXPECT_THROW_WITH_MESSAGE(check_relation<instr_fetching>(trace, instr_fetching::SR_PC_OUT_OF_RANGE_TOGGLE),
999}
1000
1001TEST(InstrFetchingConstrainingTest, ErrorFlagSetButSelParsingErrIsZero)
1002{
1003 // Create a minimal trace that satisfies all constraints EXCEPT the (commented out) one
1004 // that should enforce sel_parsing_err = pc_out_of_range + opcode_out_of_range + instr_out_of_range +
1005 // tag_out_of_range
1006 TestTraceContainer trace({
1007 {
1008 { C::instr_fetching_sel, 1 },
1009 // Error flags - pc_out_of_range is SET to 1
1010 { C::instr_fetching_pc_out_of_range, 1 },
1011 { C::instr_fetching_opcode_out_of_range, 0 },
1012 { C::instr_fetching_instr_out_of_range, 0 },
1013 { C::instr_fetching_tag_out_of_range, 0 },
1014 // sel_parsing_err should be 1 (since pc_out_of_range = 1) but we set it to 0
1015 { C::instr_fetching_sel_parsing_err, 0 },
1016 // Values to satisfy PC_OUT_OF_RANGE_TOGGLE constraint (subrelation 4):
1017 // pc_abs_diff = sel * ((2 * pc_out_of_range - 1) * (pc - bytecode_size) - 1 + pc_out_of_range)
1018 // With pc_out_of_range = 1: pc_abs_diff = (2*1-1) * (pc - bytecode_size) - 1 + 1 = pc - bytecode_size
1019 { C::instr_fetching_bytecode_size, 10 },
1020 { C::instr_fetching_pc, 15 }, // pc > bytecode_size
1021 { C::instr_fetching_pc_abs_diff, 5 }, // pc - bytecode_size = 15 - 10 = 5
1022 { C::instr_fetching_pc_size_in_bits, 32 }, // AVM_PC_SIZE_IN_BITS constant
1023 // Values to satisfy INSTR_OUT_OF_RANGE_TOGGLE constraint (subrelation 6):
1024 // instr_abs_diff = (2 * instr_out_of_range - 1) * (instr_size - bytes_to_read) - instr_out_of_range
1025 // With instr_out_of_range = 0: instr_abs_diff = (-1) * (instr_size - bytes_to_read) = bytes_to_read -
1026 // instr_size
1027 { C::instr_fetching_bytes_to_read, 10 },
1028 { C::instr_fetching_instr_size, 5 },
1029 { C::instr_fetching_instr_abs_diff, 5 }, // bytes_to_read - instr_size = 10 - 5 = 5
1030 },
1031 });
1032
1033 EXPECT_THROW_WITH_MESSAGE(check_relation<instr_fetching>(trace),
1034 "Relation instr_fetching, subrelation .* failed at row 0");
1035}
1036
1041TEST(InstrFetchingConstrainingTest, CorrectBehaviorSelParsingErrMatchesErrors)
1042{
1043 TestTraceContainer trace({
1044 {
1045 { C::instr_fetching_sel, 1 },
1046 { C::instr_fetching_pc_out_of_range, 1 },
1047 { C::instr_fetching_opcode_out_of_range, 0 },
1048 { C::instr_fetching_instr_out_of_range, 0 },
1049 { C::instr_fetching_tag_out_of_range, 0 },
1050 { C::instr_fetching_sel_parsing_err, 1 }, // Correctly set to 1
1051 // Supporting values
1052 { C::instr_fetching_bytecode_size, 10 },
1053 { C::instr_fetching_pc, 15 },
1054 { C::instr_fetching_pc_abs_diff, 5 },
1055 { C::instr_fetching_pc_size_in_bits, 32 },
1056 { C::instr_fetching_bytes_to_read, 10 },
1057 { C::instr_fetching_instr_size, 5 },
1058 { C::instr_fetching_instr_abs_diff, 5 }, // bytes_to_read - instr_size = 10 - 5 = 5
1059 },
1060 });
1061
1062 // This should pass both before and after the fix.
1063 check_relation<instr_fetching>(trace);
1064}
1065
1069TEST(InstrFetchingConstrainingTest, CorrectBehaviorNoErrorsMeansSelParsingErrIsZero)
1070{
1071 TestTraceContainer trace({
1072 {
1073 { C::instr_fetching_sel, 1 },
1074 { C::instr_fetching_pc_out_of_range, 0 },
1075 { C::instr_fetching_opcode_out_of_range, 0 },
1076 { C::instr_fetching_instr_out_of_range, 0 },
1077 { C::instr_fetching_tag_out_of_range, 0 },
1078 { C::instr_fetching_sel_parsing_err, 0 }, // Correctly set to 0
1079 { C::instr_fetching_sel_pc_in_range, 1 }, // sel * (1 - pc_out_of_range) = 1 * 1 = 1
1080 // pc_abs_diff = sel * ((2 * pc_out_of_range - 1) * (pc - bytecode_size) - 1 + pc_out_of_range)
1081 // With pc_out_of_range = 0: pc_abs_diff = (2*0-1) * (pc - bytecode_size) - 1 + 0
1082 // = -(pc - bytecode_size) - 1 = bytecode_size - pc - 1
1083 { C::instr_fetching_bytecode_size, 20 },
1084 { C::instr_fetching_pc, 5 },
1085 { C::instr_fetching_pc_abs_diff, 14 }, // bytecode_size - pc - 1 = 20 - 5 - 1 = 14
1086 { C::instr_fetching_pc_size_in_bits, 32 },
1087 // instr_abs_diff = bytes_to_read - instr_size (when instr_out_of_range = 0)
1088 { C::instr_fetching_bytes_to_read, 15 },
1089 { C::instr_fetching_instr_size, 10 },
1090 { C::instr_fetching_instr_abs_diff, 5 }, // bytes_to_read - instr_size = 15 - 10 = 5
1091 },
1092 });
1093
1094 // This should pass both before and after the fix.
1095 check_relation<instr_fetching>(trace);
1096}
1097
1098} // namespace
1099} // namespace bb::avm2::constraining
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:224
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
EventEmitter< BytecodeDecompositionEvent > decomposition_events
static constexpr size_t SR_OP1_BYTES_DECOMPOSITION
static constexpr size_t SR_OP3_BYTES_DECOMPOSITION
static constexpr size_t SR_OP4_BYTES_DECOMPOSITION
static constexpr size_t SR_TAG_OUT_OF_RANGE_ZERO
static constexpr size_t SR_ADDRESSING_MODE_BYTES_DECOMPOSITION
static constexpr size_t SR_INSTR_OUT_OF_RANGE_TOGGLE
static std::string get_subrelation_label(size_t index)
static constexpr size_t SR_OP5_BYTES_DECOMPOSITION
static constexpr size_t SR_PC_OUT_OF_RANGE_TOGGLE
static constexpr size_t SR_OP2_BYTES_DECOMPOSITION
static constexpr size_t SR_SEL_HAS_TAG_ZERO
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 process_wire_instruction_spec(TraceContainer &trace)
Populate the wire-level instruction specification table.
void process_memory_tag_range(TraceContainer &trace)
Populate the memory tag out-of-range selector.
void process_sel_range_16(TraceContainer &trace)
Generate a selector column that activates the first 2^16 (65536) rows.
void process_sel_range_8(TraceContainer &trace)
Generate a selector column that activates the first 2^8 (256) rows.
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)
Native Poseidon2 hash function implementation.
Definition poseidon2.hpp:22
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
RangeCheckTraceBuilder range_check_builder
Definition alu.test.cpp:121
PrecomputedTraceBuilder precomputed_builder
Definition alu.test.cpp:120
AluTraceBuilder builder
Definition alu.test.cpp:124
TestTraceContainer trace
std::vector< uint8_t > bytecode
void check_interaction(tracegen::TestTraceContainer &trace)
TEST(AvmFixedVKTests, FixedVKCommitments)
Test that the fixed VK commitments agree with the ones computed from precomputed columns.
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)
Instruction random_instruction(WireOpCode w_opcode)
Definition fixtures.cpp:128
TestTraceContainer empty_trace()
Definition fixtures.cpp:156
lookup_settings< lookup_instr_fetching_wire_instruction_info_settings_ > lookup_instr_fetching_wire_instruction_info_settings
lookup_settings< lookup_instr_fetching_bytecode_size_from_bc_dec_settings_ > lookup_instr_fetching_bytecode_size_from_bc_dec_settings
lookup_settings< lookup_instr_fetching_bytes_from_bc_dec_settings_ > lookup_instr_fetching_bytes_from_bc_dec_settings
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
lookup_settings< lookup_instr_fetching_instr_abs_diff_positive_settings_ > lookup_instr_fetching_instr_abs_diff_positive_settings
lookup_settings< lookup_instr_fetching_pc_abs_diff_positive_settings_ > lookup_instr_fetching_pc_abs_diff_positive_settings
lookup_settings< lookup_instr_fetching_tag_value_validation_settings_ > lookup_instr_fetching_tag_value_validation_settings
Instruction
Enumeration of VM instructions that can be executed.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
unsigned __int128 uint128_t
Definition serialize.hpp:45
static constexpr uint256_t modulus_minus_two