Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bytecode_trace.cpp
Go to the documentation of this file.
2
3#include <cmath>
4#include <cstddef>
5#include <cstdint>
6#include <memory>
7#include <ranges>
8#include <stdexcept>
9#include <vector>
10
22
24
25namespace bb::avm2::tracegen {
26
30{
31 using C = Column;
32
33 // We start from row 1 because we need a row of zeroes for the shifts.
34 uint32_t row = 1;
35
36 for (const auto& event : events) {
37 const auto& bytecode = *event.bytecode;
38 const auto id = event.bytecode_id;
39 auto bytecode_at = [&bytecode](size_t i) -> uint8_t { return i < bytecode.size() ? bytecode[i] : 0; };
40 const uint32_t bytecode_len = static_cast<uint32_t>(bytecode.size());
41
42 for (uint32_t i = 0; i < bytecode_len; i++) {
43 const uint32_t remaining = bytecode_len - i;
44 const uint32_t bytes_to_read = std::min(remaining, DECOMPOSE_WINDOW_SIZE);
45 const bool is_last = remaining == 1;
46 const bool is_windows_eq_remaining = remaining == DECOMPOSE_WINDOW_SIZE;
47
48 // Check that we still expect the max public bytecode in bytes to fit within 24 bits (i.e. <= 0xffffff).
49 static_assert(MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS * 32 <= 0xffffff);
50
51 // We set the decomposition in bytes, and other values.
52 trace.set(
53 row + i,
54 { {
55 { C::bc_decomposition_sel, 1 },
56 { C::bc_decomposition_id, id },
57 { C::bc_decomposition_pc, i },
58 { C::bc_decomposition_last_of_contract, is_last ? 1 : 0 },
59 { C::bc_decomposition_bytes_remaining, remaining },
60 { C::bc_decomposition_bytes_rem_inv, FF(remaining).invert() }, // remaining != 0 for activated rows
61 { C::bc_decomposition_bytes_rem_min_one_inv, is_last ? 0 : FF(remaining - 1).invert() },
62 { C::bc_decomposition_bytes_to_read, bytes_to_read },
63 { C::bc_decomposition_sel_windows_gt_remaining, DECOMPOSE_WINDOW_SIZE > remaining ? 1 : 0 },
64 { C::bc_decomposition_windows_min_remaining_inv,
65 is_windows_eq_remaining ? 0 : (FF(DECOMPOSE_WINDOW_SIZE) - FF(remaining)).invert() },
66 { C::bc_decomposition_is_windows_eq_remaining, is_windows_eq_remaining ? 1 : 0 },
67 // Sliding window.
68 { C::bc_decomposition_bytes, bytecode_at(i) },
69 { C::bc_decomposition_bytes_pc_plus_1, bytecode_at(i + 1) },
70 { C::bc_decomposition_bytes_pc_plus_2, bytecode_at(i + 2) },
71 { C::bc_decomposition_bytes_pc_plus_3, bytecode_at(i + 3) },
72 { C::bc_decomposition_bytes_pc_plus_4, bytecode_at(i + 4) },
73 { C::bc_decomposition_bytes_pc_plus_5, bytecode_at(i + 5) },
74 { C::bc_decomposition_bytes_pc_plus_6, bytecode_at(i + 6) },
75 { C::bc_decomposition_bytes_pc_plus_7, bytecode_at(i + 7) },
76 { C::bc_decomposition_bytes_pc_plus_8, bytecode_at(i + 8) },
77 { C::bc_decomposition_bytes_pc_plus_9, bytecode_at(i + 9) },
78 { C::bc_decomposition_bytes_pc_plus_10, bytecode_at(i + 10) },
79 { C::bc_decomposition_bytes_pc_plus_11, bytecode_at(i + 11) },
80 { C::bc_decomposition_bytes_pc_plus_12, bytecode_at(i + 12) },
81 { C::bc_decomposition_bytes_pc_plus_13, bytecode_at(i + 13) },
82 { C::bc_decomposition_bytes_pc_plus_14, bytecode_at(i + 14) },
83 { C::bc_decomposition_bytes_pc_plus_15, bytecode_at(i + 15) },
84 { C::bc_decomposition_bytes_pc_plus_16, bytecode_at(i + 16) },
85 { C::bc_decomposition_bytes_pc_plus_17, bytecode_at(i + 17) },
86 { C::bc_decomposition_bytes_pc_plus_18, bytecode_at(i + 18) },
87 { C::bc_decomposition_bytes_pc_plus_19, bytecode_at(i + 19) },
88 { C::bc_decomposition_bytes_pc_plus_20, bytecode_at(i + 20) },
89 { C::bc_decomposition_bytes_pc_plus_21, bytecode_at(i + 21) },
90 { C::bc_decomposition_bytes_pc_plus_22, bytecode_at(i + 22) },
91 { C::bc_decomposition_bytes_pc_plus_23, bytecode_at(i + 23) },
92 { C::bc_decomposition_bytes_pc_plus_24, bytecode_at(i + 24) },
93 { C::bc_decomposition_bytes_pc_plus_25, bytecode_at(i + 25) },
94 { C::bc_decomposition_bytes_pc_plus_26, bytecode_at(i + 26) },
95 { C::bc_decomposition_bytes_pc_plus_27, bytecode_at(i + 27) },
96 { C::bc_decomposition_bytes_pc_plus_28, bytecode_at(i + 28) },
97 { C::bc_decomposition_bytes_pc_plus_29, bytecode_at(i + 29) },
98 { C::bc_decomposition_bytes_pc_plus_30, bytecode_at(i + 30) },
99 { C::bc_decomposition_bytes_pc_plus_31, bytecode_at(i + 31) },
100 { C::bc_decomposition_bytes_pc_plus_32, bytecode_at(i + 32) },
101 { C::bc_decomposition_bytes_pc_plus_33, bytecode_at(i + 33) },
102 { C::bc_decomposition_bytes_pc_plus_34, bytecode_at(i + 34) },
103 { C::bc_decomposition_bytes_pc_plus_35, bytecode_at(i + 35) },
104 { C::bc_decomposition_bytes_pc_plus_36, bytecode_at(i + 36) },
105 } });
106 }
107
108 // We set the packed field every 31 bytes.
109 auto bytecode_field_at = [&](size_t i) -> FF {
110 // We need to read uint256_ts because reading FFs messes up the order of the bytes.
111 uint256_t as_int = 0;
112 if (bytecode_len - i >= 32) {
113 as_int = from_buffer<uint256_t>(bytecode, i);
114 } else {
115 std::vector<uint8_t> tail(bytecode.begin() + static_cast<ssize_t>(i), bytecode.end());
116 tail.resize(32, 0);
117 as_int = from_buffer<uint256_t>(tail, 0);
118 }
119 return as_int >> 8;
120 };
121 for (uint32_t i = 0; i < bytecode_len; i += 31) {
122 trace.set(row + i,
123 { {
124 { C::bc_decomposition_sel_packed, 1 },
125 { C::bc_decomposition_packed_field, bytecode_field_at(i) },
126 } });
127 }
128
129 // We advance to the next bytecode.
130 row += bytecode_len;
131 }
132}
133
136{
137 using C = Column;
138 uint32_t row = 1;
139
140 for (const auto& event : events) {
141 const auto id = event.bytecode_id;
142 const auto& fields = event.bytecode_fields;
143
144 uint32_t pc_index = 0;
145 FF incremental_hash = event.bytecode_length;
146 for (uint32_t i = 0; i < fields.size(); i++) {
147 FF output_hash = Poseidon2::hash({ fields[i], incremental_hash });
148 bool end_of_bytecode = i == fields.size() - 1;
149 trace.set(row,
150 { { { C::bc_hashing_sel, 1 },
151 { C::bc_hashing_start, i == 0 ? 1 : 0 },
152 { C::bc_hashing_latch, end_of_bytecode },
153 { C::bc_hashing_bytecode_id, id },
154 { C::bc_hashing_pc_index, pc_index },
155 { C::bc_hashing_packed_field, fields[i] },
156 { C::bc_hashing_incremental_hash, incremental_hash },
157 { C::bc_hashing_output_hash, output_hash } } });
158 incremental_hash = output_hash;
159 pc_index += 31;
160 row++;
161 }
162 }
163}
164
168{
169 using C = Column;
170
171 uint32_t row = 1;
172 for (const auto& event : events) {
173 trace.set(row,
174 { {
175 { C::bc_retrieval_sel, 1 },
176 { C::bc_retrieval_bytecode_id, event.bytecode_id },
177 { C::bc_retrieval_address, event.address },
178 { C::bc_retrieval_error, event.error ? 1 : 0 },
179
180 // Contract instance members (for lookup into contract_instance_retrieval)
181 { C::bc_retrieval_current_class_id, event.current_class_id },
182
183 // Tree context (for lookup into contract_instance_retrieval)
184 { C::bc_retrieval_public_data_tree_root, event.public_data_tree_root },
185 { C::bc_retrieval_nullifier_tree_root, event.nullifier_root },
186
187 // Instance existence determined by shared contract instance retrieval
188 { C::bc_retrieval_instance_exists, event.error ? 0 : 1 },
189
190 // Contract class for bytecode operations
191 { C::bc_retrieval_artifact_hash, event.contract_class.artifact_hash },
192 { C::bc_retrieval_private_function_root, event.contract_class.private_function_root },
193
194 } });
195 row++;
196 }
197}
198
202{
203 using C = Column;
210
211 // We start from row 1 because we need a row of zeroes for the shifts.
212 uint32_t row = 1;
213
214 for (const auto& event : events) {
215 const auto bytecode_id = event.bytecode_id;
216 const auto bytecode_size = event.bytecode->size();
217
218 auto get_operand = [&](size_t i) -> FF {
219 return i < event.instruction.operands.size() ? static_cast<FF>(event.instruction.operands[i]) : 0;
220 };
221 auto bytecode_at = [&](size_t i) -> uint8_t { return i < bytecode_size ? (*event.bytecode)[i] : 0; };
222
223 const uint8_t wire_opcode = bytecode_at(event.pc);
224 const bool wire_opcode_in_range =
225 event.error != PC_OUT_OF_RANGE && wire_opcode < static_cast<uint8_t>(WireOpCode::LAST_OPCODE_SENTINEL);
226
227 uint32_t size_in_bytes = 0;
228 ExecutionOpCode exec_opcode = static_cast<ExecutionOpCode>(0);
229 std::array<uint8_t, NUM_OP_DC_SELECTORS> op_dc_selectors{};
230 uint8_t has_tag = 0;
231 uint8_t tag_is_op2 = 0;
232 uint8_t tag_value = 0;
233
234 if (wire_opcode_in_range) {
235 const auto& wire_instr_spec = WIRE_INSTRUCTION_SPEC.at(static_cast<WireOpCode>(wire_opcode));
236 size_in_bytes = wire_instr_spec.size_in_bytes;
237 exec_opcode = wire_instr_spec.exec_opcode;
238 op_dc_selectors = wire_instr_spec.op_dc_selectors;
239
240 if (wire_instr_spec.tag_operand_idx.has_value()) {
241 const auto tag_value_idx = wire_instr_spec.tag_operand_idx.value();
242 assert((tag_value_idx == 2 || tag_value_idx == 3) &&
243 "Current constraints support only tag for operand index equal to 2 or 3");
244 has_tag = 1;
245
246 if (tag_value_idx == 2) {
247 tag_is_op2 = 1;
248 tag_value = static_cast<uint8_t>(get_operand(1)); // in instruction.operands, op2 has index 1
249 } else {
250 tag_value = static_cast<uint8_t>(get_operand(2));
251 }
252 }
253 }
254
255 const uint32_t bytes_remaining =
256 event.error == PC_OUT_OF_RANGE ? 0 : static_cast<uint32_t>(bytecode_size - event.pc);
257 const uint32_t bytes_to_read = std::min(bytes_remaining, DECOMPOSE_WINDOW_SIZE);
258
259 uint32_t instr_abs_diff = 0;
260 if (size_in_bytes <= bytes_to_read) {
261 instr_abs_diff = bytes_to_read - size_in_bytes;
262 } else {
263 instr_abs_diff = size_in_bytes - bytes_to_read - 1;
264 }
265
266 uint32_t bytecode_size_u32 = static_cast<uint32_t>(bytecode_size);
267 uint32_t pc_abs_diff =
268 bytecode_size_u32 > event.pc ? bytecode_size_u32 - event.pc - 1 : event.pc - bytecode_size_u32;
269
270 trace.set(row,
271 { {
272 { C::instr_fetching_sel, 1 },
273 { C::instr_fetching_bytecode_id, bytecode_id },
274 { C::instr_fetching_pc, event.pc },
275 // indirect + operands.
276 { C::instr_fetching_indirect, event.instruction.indirect },
277 { C::instr_fetching_op1, get_operand(0) },
278 { C::instr_fetching_op2, get_operand(1) },
279 { C::instr_fetching_op3, get_operand(2) },
280 { C::instr_fetching_op4, get_operand(3) },
281 { C::instr_fetching_op5, get_operand(4) },
282 { C::instr_fetching_op6, get_operand(5) },
283 { C::instr_fetching_op7, get_operand(6) },
284 // Single bytes.
285 { C::instr_fetching_bd0, wire_opcode },
286 { C::instr_fetching_bd1, bytecode_at(event.pc + 1) },
287 { C::instr_fetching_bd2, bytecode_at(event.pc + 2) },
288 { C::instr_fetching_bd3, bytecode_at(event.pc + 3) },
289 { C::instr_fetching_bd4, bytecode_at(event.pc + 4) },
290 { C::instr_fetching_bd5, bytecode_at(event.pc + 5) },
291 { C::instr_fetching_bd6, bytecode_at(event.pc + 6) },
292 { C::instr_fetching_bd7, bytecode_at(event.pc + 7) },
293 { C::instr_fetching_bd8, bytecode_at(event.pc + 8) },
294 { C::instr_fetching_bd9, bytecode_at(event.pc + 9) },
295 { C::instr_fetching_bd10, bytecode_at(event.pc + 10) },
296 { C::instr_fetching_bd11, bytecode_at(event.pc + 11) },
297 { C::instr_fetching_bd12, bytecode_at(event.pc + 12) },
298 { C::instr_fetching_bd13, bytecode_at(event.pc + 13) },
299 { C::instr_fetching_bd14, bytecode_at(event.pc + 14) },
300 { C::instr_fetching_bd15, bytecode_at(event.pc + 15) },
301 { C::instr_fetching_bd16, bytecode_at(event.pc + 16) },
302 { C::instr_fetching_bd17, bytecode_at(event.pc + 17) },
303 { C::instr_fetching_bd18, bytecode_at(event.pc + 18) },
304 { C::instr_fetching_bd19, bytecode_at(event.pc + 19) },
305 { C::instr_fetching_bd20, bytecode_at(event.pc + 20) },
306 { C::instr_fetching_bd21, bytecode_at(event.pc + 21) },
307 { C::instr_fetching_bd22, bytecode_at(event.pc + 22) },
308 { C::instr_fetching_bd23, bytecode_at(event.pc + 23) },
309 { C::instr_fetching_bd24, bytecode_at(event.pc + 24) },
310 { C::instr_fetching_bd25, bytecode_at(event.pc + 25) },
311 { C::instr_fetching_bd26, bytecode_at(event.pc + 26) },
312 { C::instr_fetching_bd27, bytecode_at(event.pc + 27) },
313 { C::instr_fetching_bd28, bytecode_at(event.pc + 28) },
314 { C::instr_fetching_bd29, bytecode_at(event.pc + 29) },
315 { C::instr_fetching_bd30, bytecode_at(event.pc + 30) },
316 { C::instr_fetching_bd31, bytecode_at(event.pc + 31) },
317 { C::instr_fetching_bd32, bytecode_at(event.pc + 32) },
318 { C::instr_fetching_bd33, bytecode_at(event.pc + 33) },
319 { C::instr_fetching_bd34, bytecode_at(event.pc + 34) },
320 { C::instr_fetching_bd35, bytecode_at(event.pc + 35) },
321 { C::instr_fetching_bd36, bytecode_at(event.pc + 36) },
322
323 // From instruction table.
324 { C::instr_fetching_exec_opcode, static_cast<uint32_t>(exec_opcode) },
325 { C::instr_fetching_instr_size, size_in_bytes },
326 { C::instr_fetching_sel_has_tag, has_tag },
327 { C::instr_fetching_sel_tag_is_op2, tag_is_op2 },
328
329 // Fill operand decomposition selectors
330 { C::instr_fetching_sel_op_dc_0, op_dc_selectors.at(0) },
331 { C::instr_fetching_sel_op_dc_1, op_dc_selectors.at(1) },
332 { C::instr_fetching_sel_op_dc_2, op_dc_selectors.at(2) },
333 { C::instr_fetching_sel_op_dc_3, op_dc_selectors.at(3) },
334 { C::instr_fetching_sel_op_dc_4, op_dc_selectors.at(4) },
335 { C::instr_fetching_sel_op_dc_5, op_dc_selectors.at(5) },
336 { C::instr_fetching_sel_op_dc_6, op_dc_selectors.at(6) },
337 { C::instr_fetching_sel_op_dc_7, op_dc_selectors.at(7) },
338 { C::instr_fetching_sel_op_dc_8, op_dc_selectors.at(8) },
339 { C::instr_fetching_sel_op_dc_9, op_dc_selectors.at(9) },
340 { C::instr_fetching_sel_op_dc_10, op_dc_selectors.at(10) },
341 { C::instr_fetching_sel_op_dc_11, op_dc_selectors.at(11) },
342 { C::instr_fetching_sel_op_dc_12, op_dc_selectors.at(12) },
343 { C::instr_fetching_sel_op_dc_13, op_dc_selectors.at(13) },
344 { C::instr_fetching_sel_op_dc_14, op_dc_selectors.at(14) },
345 { C::instr_fetching_sel_op_dc_15, op_dc_selectors.at(15) },
346 { C::instr_fetching_sel_op_dc_16, op_dc_selectors.at(16) },
347
348 // Parsing errors
349 { C::instr_fetching_pc_out_of_range, event.error == PC_OUT_OF_RANGE ? 1 : 0 },
350 { C::instr_fetching_opcode_out_of_range, event.error == OPCODE_OUT_OF_RANGE ? 1 : 0 },
351 { C::instr_fetching_instr_out_of_range, event.error == INSTRUCTION_OUT_OF_RANGE ? 1 : 0 },
352 { C::instr_fetching_tag_out_of_range, event.error == TAG_OUT_OF_RANGE ? 1 : 0 },
353 { C::instr_fetching_sel_parsing_err, event.error.has_value() ? 1 : 0 },
354
355 // selector for lookups
356 { C::instr_fetching_sel_pc_in_range, event.error != PC_OUT_OF_RANGE ? 1 : 0 },
357
358 { C::instr_fetching_bytecode_size, bytecode_size },
359 { C::instr_fetching_bytes_to_read, bytes_to_read },
360 { C::instr_fetching_instr_abs_diff, instr_abs_diff },
361 { C::instr_fetching_pc_abs_diff, pc_abs_diff },
362 { C::instr_fetching_pc_size_in_bits,
363 AVM_PC_SIZE_IN_BITS }, // Remove when we support constants in lookups
364 { C::instr_fetching_tag_value, tag_value },
365 } });
366 row++;
367 }
368}
369
372 // Bytecode Hashing
374 .add<lookup_bc_hashing_iv_is_len_settings, InteractionType::LookupSequential>()
375 // TODO(dbanks12): re-enable once C++ and PIL use standard poseidon2 hashing for bytecode commitments.
376 // .add<lookup_bc_hashing_poseidon2_hash_settings, InteractionType::LookupSequential>()
377 // Bytecode Retrieval
378 // .add<lookup_bc_retrieval_bytecode_hash_is_correct_settings, InteractionType::LookupSequential>()
380 .add<lookup_bc_retrieval_contract_instance_retrieval_settings, InteractionType::LookupSequential>()
381 // Bytecode Decomposition
383 // Instruction Fetching
384 .add<lookup_instr_fetching_bytes_from_bc_dec_settings, InteractionType::LookupGeneric>()
386 .add<lookup_instr_fetching_wire_instruction_info_settings, InteractionType::LookupIntoIndexedByClk>()
388 .add<lookup_instr_fetching_tag_value_validation_settings, InteractionType::LookupIntoIndexedByClk>()
390
391} // namespace bb::avm2::tracegen
#define AVM_PC_SIZE_IN_BITS
#define MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS
void process_retrieval(const simulation::EventEmitterInterface< simulation::BytecodeRetrievalEvent >::Container &events, TraceContainer &trace)
static const InteractionDefinition interactions
void process_decomposition(const simulation::EventEmitterInterface< simulation::BytecodeDecompositionEvent >::Container &events, TraceContainer &trace)
void process_hashing(const simulation::EventEmitterInterface< simulation::BytecodeHashingEvent >::Container &events, TraceContainer &trace)
void process_instruction_fetching(const simulation::EventEmitterInterface< simulation::InstructionFetchingEvent >::Container &events, TraceContainer &trace)
InteractionDefinition & add(auto &&... args)
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...
TestTraceContainer trace
constexpr uint32_t DECOMPOSE_WINDOW_SIZE
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_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_bc_hashing_get_packed_field_settings_ > lookup_bc_hashing_get_packed_field_settings
lookup_settings< lookup_bc_retrieval_class_id_derivation_settings_ > lookup_bc_retrieval_class_id_derivation_settings
const std::unordered_map< WireOpCode, WireInstructionSpec > WIRE_INSTRUCTION_SPEC
lookup_settings< lookup_bc_decomposition_bytes_are_bytes_settings_ > lookup_bc_decomposition_bytes_are_bytes_settings
AvmFlavorSettings::FF FF
Definition field.hpp:10
simulation::PublicDataTreeReadWriteEvent event