Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bytecode_trace.test.cpp
Go to the documentation of this file.
1#include <algorithm>
2#include <cstddef>
3#include <gmock/gmock.h>
4#include <gtest/gtest.h>
5
6#include <cstdint>
7#include <memory>
8#include <sys/types.h>
9#include <vector>
10
22
23namespace bb::avm2::tracegen {
24namespace {
25
26using C = Column;
28
29using simulation::Instruction;
30using simulation::InstructionFetchingEvent;
31
32TEST(BytecodeTraceGenTest, BasicRetrieval)
33{
34 TestTraceContainer trace;
35 BytecodeTraceBuilder builder;
36
37 const AppendOnlyTreeSnapshot snapshot_before = { .root = 12, .next_available_leaf_index = 1 };
38 const AppendOnlyTreeSnapshot snapshot_after = { .root = 34, .next_available_leaf_index = 2 };
39
40 builder.process_retrieval({ {
41 .bytecode_id = 43,
42 .address = 0xc0ffee,
43 .current_class_id = 34,
44 .contract_class = { .artifact_hash = 100, .private_functions_root = 200 },
45 .nullifier_tree_root = 300,
46 .public_data_tree_root = 400,
47 .retrieved_bytecodes_snapshot_before = snapshot_before,
48 .retrieved_bytecodes_snapshot_after = snapshot_after,
49 .is_new_class = true,
50 } },
51 trace);
52 const auto rows = trace.as_rows();
53
54 // One retrieval event.
55 ASSERT_EQ(rows.size(), 1);
56
57 EXPECT_THAT(
58 rows.at(0),
59 AllOf(ROW_FIELD_EQ(bc_retrieval_sel, 1),
60 ROW_FIELD_EQ(bc_retrieval_bytecode_id, 43),
61 ROW_FIELD_EQ(bc_retrieval_address, 0xc0ffee),
62 ROW_FIELD_EQ(bc_retrieval_current_class_id, 34),
63 ROW_FIELD_EQ(bc_retrieval_artifact_hash, 100),
64 ROW_FIELD_EQ(bc_retrieval_private_functions_root, 200),
65 ROW_FIELD_EQ(bc_retrieval_nullifier_tree_root, 300),
66 ROW_FIELD_EQ(bc_retrieval_public_data_tree_root, 400),
67 ROW_FIELD_EQ(bc_retrieval_prev_retrieved_bytecodes_tree_root, snapshot_before.root),
68 ROW_FIELD_EQ(bc_retrieval_prev_retrieved_bytecodes_tree_size, snapshot_before.next_available_leaf_index),
69 ROW_FIELD_EQ(bc_retrieval_next_retrieved_bytecodes_tree_root, snapshot_after.root),
70 ROW_FIELD_EQ(bc_retrieval_next_retrieved_bytecodes_tree_size, snapshot_after.next_available_leaf_index),
71 ROW_FIELD_EQ(bc_retrieval_instance_exists, 1),
72 ROW_FIELD_EQ(bc_retrieval_is_new_class, 1),
73 ROW_FIELD_EQ(bc_retrieval_no_remaining_bytecodes, 0),
74 ROW_FIELD_EQ(bc_retrieval_remaining_bytecodes_inv,
76 ROW_FIELD_EQ(bc_retrieval_error, 0),
77 ROW_FIELD_EQ(bc_retrieval_should_retrieve, 1)));
78}
79
80TEST(BytecodeTraceGenTest, RetrievalExistingClass)
81{
82 TestTraceContainer trace;
83 BytecodeTraceBuilder builder;
84
85 const AppendOnlyTreeSnapshot snapshot = { .root = FF(12), .next_available_leaf_index = 2 };
86
87 builder.process_retrieval({ {
88 .bytecode_id = 43,
89 .address = 0xc0ffee,
90 .current_class_id = 34,
91 .contract_class = { .artifact_hash = 100, .private_functions_root = 200 },
92 .nullifier_tree_root = 300,
93 .public_data_tree_root = 400,
94 .retrieved_bytecodes_snapshot_before = snapshot,
95 .retrieved_bytecodes_snapshot_after = snapshot,
96 .is_new_class = false,
97 } },
98 trace);
99 const auto rows = trace.as_rows();
100
101 // One retrieval event.
102 ASSERT_EQ(rows.size(), 1);
103
104 EXPECT_THAT(
105 rows.at(0),
106 AllOf(ROW_FIELD_EQ(bc_retrieval_sel, 1),
107 ROW_FIELD_EQ(bc_retrieval_bytecode_id, 43),
108 ROW_FIELD_EQ(bc_retrieval_address, 0xc0ffee),
109 ROW_FIELD_EQ(bc_retrieval_current_class_id, 34),
110 ROW_FIELD_EQ(bc_retrieval_artifact_hash, 100),
111 ROW_FIELD_EQ(bc_retrieval_private_functions_root, 200),
112 ROW_FIELD_EQ(bc_retrieval_nullifier_tree_root, 300),
113 ROW_FIELD_EQ(bc_retrieval_public_data_tree_root, 400),
114 ROW_FIELD_EQ(bc_retrieval_instance_exists, 1),
115 ROW_FIELD_EQ(bc_retrieval_is_new_class, 0),
116 ROW_FIELD_EQ(bc_retrieval_no_remaining_bytecodes, 0),
117 ROW_FIELD_EQ(bc_retrieval_remaining_bytecodes_inv,
119 ROW_FIELD_EQ(bc_retrieval_error, 0),
120 ROW_FIELD_EQ(bc_retrieval_should_retrieve, 1),
121 ROW_FIELD_EQ(bc_retrieval_prev_retrieved_bytecodes_tree_root, snapshot.root),
122 ROW_FIELD_EQ(bc_retrieval_prev_retrieved_bytecodes_tree_size, snapshot.next_available_leaf_index),
123 ROW_FIELD_EQ(bc_retrieval_next_retrieved_bytecodes_tree_root, snapshot.root),
124 ROW_FIELD_EQ(bc_retrieval_next_retrieved_bytecodes_tree_size, snapshot.next_available_leaf_index)));
125}
126
127TEST(BytecodeTraceGenTest, MultipleRetrievalEvents)
128{
129 TestTraceContainer trace;
130 BytecodeTraceBuilder builder;
131
132 const AppendOnlyTreeSnapshot snapshot_before = { .root = 12, .next_available_leaf_index = 1 };
133 const AppendOnlyTreeSnapshot snapshot_after_0 = { .root = 34, .next_available_leaf_index = 2 };
134 const AppendOnlyTreeSnapshot snapshot_after_1 = { .root = 56, .next_available_leaf_index = 3 };
135
136 // Two new bytecodes, one existing:
137 builder.process_retrieval(
138 {
139 simulation::BytecodeRetrievalEvent{
140 .bytecode_id = 43,
141 .address = 0xc0ffee,
142 .current_class_id = 34,
143 .contract_class = { .artifact_hash = 100, .private_functions_root = 200 },
144 .nullifier_tree_root = 300,
145 .public_data_tree_root = 400,
146 .retrieved_bytecodes_snapshot_before = snapshot_before,
147 .retrieved_bytecodes_snapshot_after = snapshot_after_0,
148 .is_new_class = true,
149 },
150 simulation::BytecodeRetrievalEvent{
151 .bytecode_id = 21,
152 .address = 0xdeadbeef,
153 .current_class_id = 56,
154 .contract_class = { .artifact_hash = 100, .private_functions_root = 200 },
155 .nullifier_tree_root = 300,
156 .public_data_tree_root = 400,
157 .retrieved_bytecodes_snapshot_before = snapshot_after_0,
158 .retrieved_bytecodes_snapshot_after = snapshot_after_1,
159 .is_new_class = true,
160 },
161 simulation::BytecodeRetrievalEvent{
162 .bytecode_id = 21,
163 .address = 0xdeadb33f,
164 .current_class_id = 56,
165 .contract_class = { .artifact_hash = 100, .private_functions_root = 200 },
166 .nullifier_tree_root = 300,
167 .public_data_tree_root = 400,
168 .retrieved_bytecodes_snapshot_before = snapshot_after_1,
169 .retrieved_bytecodes_snapshot_after = snapshot_after_1,
170 .is_new_class = false,
171 },
172 },
173 trace);
174 const auto rows = trace.as_rows();
175
176 // Three retrieval events.
177 ASSERT_EQ(rows.size(), 3);
178
179 EXPECT_THAT(
180 rows.at(0),
181 AllOf(ROW_FIELD_EQ(bc_retrieval_sel, 1),
182 ROW_FIELD_EQ(bc_retrieval_bytecode_id, 43),
183 ROW_FIELD_EQ(bc_retrieval_address, 0xc0ffee),
184 ROW_FIELD_EQ(bc_retrieval_current_class_id, 34),
185 ROW_FIELD_EQ(bc_retrieval_artifact_hash, 100),
186 ROW_FIELD_EQ(bc_retrieval_private_functions_root, 200),
187 ROW_FIELD_EQ(bc_retrieval_nullifier_tree_root, 300),
188 ROW_FIELD_EQ(bc_retrieval_public_data_tree_root, 400),
189 ROW_FIELD_EQ(bc_retrieval_prev_retrieved_bytecodes_tree_root, snapshot_before.root),
190 ROW_FIELD_EQ(bc_retrieval_prev_retrieved_bytecodes_tree_size, snapshot_before.next_available_leaf_index),
191 ROW_FIELD_EQ(bc_retrieval_next_retrieved_bytecodes_tree_root, snapshot_after_0.root),
192 ROW_FIELD_EQ(bc_retrieval_next_retrieved_bytecodes_tree_size, snapshot_after_0.next_available_leaf_index),
193 ROW_FIELD_EQ(bc_retrieval_instance_exists, 1),
194 ROW_FIELD_EQ(bc_retrieval_is_new_class, 1),
195 ROW_FIELD_EQ(bc_retrieval_no_remaining_bytecodes, 0),
196 ROW_FIELD_EQ(bc_retrieval_remaining_bytecodes_inv,
198 ROW_FIELD_EQ(bc_retrieval_error, 0),
199 ROW_FIELD_EQ(bc_retrieval_should_retrieve, 1)));
200
201 EXPECT_THAT(
202 rows.at(1),
203 AllOf(ROW_FIELD_EQ(bc_retrieval_sel, 1),
204 ROW_FIELD_EQ(bc_retrieval_bytecode_id, 21),
205 ROW_FIELD_EQ(bc_retrieval_address, 0xdeadbeef),
206 ROW_FIELD_EQ(bc_retrieval_current_class_id, 56),
207 ROW_FIELD_EQ(bc_retrieval_artifact_hash, 100),
208 ROW_FIELD_EQ(bc_retrieval_private_functions_root, 200),
209 ROW_FIELD_EQ(bc_retrieval_nullifier_tree_root, 300),
210 ROW_FIELD_EQ(bc_retrieval_public_data_tree_root, 400),
211 ROW_FIELD_EQ(bc_retrieval_prev_retrieved_bytecodes_tree_root, snapshot_after_0.root),
212 ROW_FIELD_EQ(bc_retrieval_prev_retrieved_bytecodes_tree_size, snapshot_after_0.next_available_leaf_index),
213 ROW_FIELD_EQ(bc_retrieval_next_retrieved_bytecodes_tree_root, snapshot_after_1.root),
214 ROW_FIELD_EQ(bc_retrieval_next_retrieved_bytecodes_tree_size, snapshot_after_1.next_available_leaf_index),
215 ROW_FIELD_EQ(bc_retrieval_instance_exists, 1),
216 ROW_FIELD_EQ(bc_retrieval_is_new_class, 1),
217 ROW_FIELD_EQ(bc_retrieval_no_remaining_bytecodes, 0),
218 ROW_FIELD_EQ(bc_retrieval_remaining_bytecodes_inv,
220 ROW_FIELD_EQ(bc_retrieval_error, 0),
221 ROW_FIELD_EQ(bc_retrieval_should_retrieve, 1)));
222
223 EXPECT_THAT(
224 rows.at(2),
225 AllOf(ROW_FIELD_EQ(bc_retrieval_sel, 1),
226 ROW_FIELD_EQ(bc_retrieval_bytecode_id, 21),
227 ROW_FIELD_EQ(bc_retrieval_address, 0xdeadb33f),
228 ROW_FIELD_EQ(bc_retrieval_current_class_id, 56),
229 ROW_FIELD_EQ(bc_retrieval_artifact_hash, 100),
230 ROW_FIELD_EQ(bc_retrieval_private_functions_root, 200),
231 ROW_FIELD_EQ(bc_retrieval_nullifier_tree_root, 300),
232 ROW_FIELD_EQ(bc_retrieval_public_data_tree_root, 400),
233 ROW_FIELD_EQ(bc_retrieval_prev_retrieved_bytecodes_tree_root, snapshot_after_1.root),
234 ROW_FIELD_EQ(bc_retrieval_prev_retrieved_bytecodes_tree_size, snapshot_after_1.next_available_leaf_index),
235 ROW_FIELD_EQ(bc_retrieval_next_retrieved_bytecodes_tree_root, snapshot_after_1.root),
236 ROW_FIELD_EQ(bc_retrieval_next_retrieved_bytecodes_tree_size, snapshot_after_1.next_available_leaf_index),
237 ROW_FIELD_EQ(bc_retrieval_instance_exists, 1),
238 ROW_FIELD_EQ(bc_retrieval_is_new_class, 0),
239 ROW_FIELD_EQ(bc_retrieval_no_remaining_bytecodes, 0),
240 ROW_FIELD_EQ(bc_retrieval_remaining_bytecodes_inv,
242 ROW_FIELD_EQ(bc_retrieval_error, 0),
243 ROW_FIELD_EQ(bc_retrieval_should_retrieve, 1)));
244}
245
246TEST(BytecodeTraceGenTest, RetrievalInstanceNotFoundError)
247{
248 TestTraceContainer trace;
249 BytecodeTraceBuilder builder;
250
251 const AppendOnlyTreeSnapshot snapshot = { .root = FF(12), .next_available_leaf_index = 1 };
252
253 // The simulation sets class-related fields == 0 when the instance is not found.
254 builder.process_retrieval({ {
255 .bytecode_id = 0,
256 .address = 0xc0ffee,
257 .current_class_id = 0,
258 .contract_class = {},
259 .nullifier_tree_root = 300,
260 .public_data_tree_root = 400,
261 .retrieved_bytecodes_snapshot_before = snapshot,
262 .retrieved_bytecodes_snapshot_after = snapshot,
263 .is_new_class = false,
265 } },
266 trace);
267 const auto rows = trace.as_rows();
268
269 // One retrieval event.
270 ASSERT_EQ(rows.size(), 1);
271
272 EXPECT_THAT(
273 rows.at(0),
274 AllOf(ROW_FIELD_EQ(bc_retrieval_sel, 1),
275 ROW_FIELD_EQ(bc_retrieval_address, 0xc0ffee),
276 ROW_FIELD_EQ(bc_retrieval_instance_exists, 0),
277 ROW_FIELD_EQ(bc_retrieval_is_new_class, 0),
278 ROW_FIELD_EQ(bc_retrieval_no_remaining_bytecodes, 0),
279 ROW_FIELD_EQ(bc_retrieval_remaining_bytecodes_inv,
281 ROW_FIELD_EQ(bc_retrieval_error, 1),
282 ROW_FIELD_EQ(bc_retrieval_should_retrieve, 0),
283 // Class-related fields are zeroed:
284 ROW_FIELD_EQ(bc_retrieval_bytecode_id, 0),
285 ROW_FIELD_EQ(bc_retrieval_current_class_id, 0),
286 ROW_FIELD_EQ(bc_retrieval_artifact_hash, 0),
287 ROW_FIELD_EQ(bc_retrieval_private_functions_root, 0),
288 ROW_FIELD_EQ(bc_retrieval_prev_retrieved_bytecodes_tree_root, snapshot.root),
289 ROW_FIELD_EQ(bc_retrieval_prev_retrieved_bytecodes_tree_size, snapshot.next_available_leaf_index),
290 ROW_FIELD_EQ(bc_retrieval_next_retrieved_bytecodes_tree_root, snapshot.root),
291 ROW_FIELD_EQ(bc_retrieval_next_retrieved_bytecodes_tree_size, snapshot.next_available_leaf_index)));
292}
293
294TEST(BytecodeTraceGenTest, RetrievalLimitError)
295{
296 TestTraceContainer trace;
297 BytecodeTraceBuilder builder;
298
299 // Set next leaf index to max tree size + 1:
300 const AppendOnlyTreeSnapshot snapshot = { .root = FF(12),
301 .next_available_leaf_index =
303 // The simulation sets class-related fields == 0 when the limit is reached.
304 builder.process_retrieval({ {
305 .bytecode_id = 0,
306 .address = 0xc0ffee,
307 .current_class_id = 34,
308 .contract_class = {},
309 .nullifier_tree_root = 300,
310 .public_data_tree_root = 400,
311 .retrieved_bytecodes_snapshot_before = snapshot,
312 .retrieved_bytecodes_snapshot_after = snapshot,
313 .is_new_class = true,
315 } },
316 trace);
317 const auto rows = trace.as_rows();
318
319 // One retrieval event.
320 ASSERT_EQ(rows.size(), 1);
321
322 EXPECT_THAT(
323 rows.at(0),
324 AllOf(ROW_FIELD_EQ(bc_retrieval_sel, 1),
325 ROW_FIELD_EQ(bc_retrieval_address, 0xc0ffee),
326 ROW_FIELD_EQ(bc_retrieval_instance_exists, 1),
327 ROW_FIELD_EQ(bc_retrieval_is_new_class, 1),
328 ROW_FIELD_EQ(bc_retrieval_no_remaining_bytecodes, 1),
329 ROW_FIELD_EQ(bc_retrieval_remaining_bytecodes_inv, 0),
330 ROW_FIELD_EQ(bc_retrieval_error, 1),
331 ROW_FIELD_EQ(bc_retrieval_should_retrieve, 0),
332 ROW_FIELD_EQ(bc_retrieval_current_class_id, 34),
333 // Class-related fields are zeroed:
334 ROW_FIELD_EQ(bc_retrieval_bytecode_id, 0),
335 ROW_FIELD_EQ(bc_retrieval_artifact_hash, 0),
336 ROW_FIELD_EQ(bc_retrieval_private_functions_root, 0),
337 ROW_FIELD_EQ(bc_retrieval_prev_retrieved_bytecodes_tree_root, snapshot.root),
338 ROW_FIELD_EQ(bc_retrieval_prev_retrieved_bytecodes_tree_size, snapshot.next_available_leaf_index),
339 ROW_FIELD_EQ(bc_retrieval_next_retrieved_bytecodes_tree_root, snapshot.root),
340 ROW_FIELD_EQ(bc_retrieval_next_retrieved_bytecodes_tree_size, snapshot.next_available_leaf_index)));
341}
342
343TEST(BytecodeTraceGenTest, BasicShortLength)
344{
345 TestTraceContainer trace;
346 BytecodeTraceBuilder builder;
347
348 builder.process_decomposition(
349 {
350 simulation::BytecodeDecompositionEvent{
351 .bytecode_id = 43,
352 .bytecode = std::make_shared<std::vector<uint8_t>>(std::vector<uint8_t>{ 12, 31, 5, 2 }),
353 },
354 },
355 trace);
356 auto rows = trace.as_rows();
357
358 // One extra empty row is prepended. Note that precomputed_first_row is not set through process_decomposition()
359 // because it pertains to another subtrace.
360 ASSERT_EQ(rows.size(), 4 + 1);
361
362 // We do not inspect row at index 0 as it is completely empty.
363 EXPECT_THAT(rows.at(1),
364 AllOf(ROW_FIELD_EQ(bc_decomposition_sel, 1),
365 ROW_FIELD_EQ(bc_decomposition_id, 43),
366 ROW_FIELD_EQ(bc_decomposition_bytes, 12),
367 ROW_FIELD_EQ(bc_decomposition_bytes_pc_plus_1, 31),
368 ROW_FIELD_EQ(bc_decomposition_bytes_pc_plus_2, 5),
369 ROW_FIELD_EQ(bc_decomposition_bytes_pc_plus_3, 2),
370 ROW_FIELD_EQ(bc_decomposition_bytes_pc_plus_4, 0),
371 ROW_FIELD_EQ(bc_decomposition_pc, 0),
372 ROW_FIELD_EQ(bc_decomposition_bytes_remaining, 4),
373 ROW_FIELD_EQ(bc_decomposition_sel_windows_gt_remaining, 1),
374 ROW_FIELD_EQ(bc_decomposition_windows_min_remaining_inv, FF(DECOMPOSE_WINDOW_SIZE - 4).invert()),
375 ROW_FIELD_EQ(bc_decomposition_sel_windows_eq_remaining, 0),
376 ROW_FIELD_EQ(bc_decomposition_bytes_to_read, 4),
377 ROW_FIELD_EQ(bc_decomposition_last_of_contract, 0),
378 ROW_FIELD_EQ(bc_decomposition_sel_packed, 1),
379 ROW_FIELD_EQ(bc_decomposition_start, 1),
380 ROW_FIELD_EQ(bc_decomposition_next_packed_pc, 0),
381 ROW_FIELD_EQ(bc_decomposition_next_packed_pc_min_pc_inv, 0)));
382
383 EXPECT_THAT(rows.at(2),
384 AllOf(ROW_FIELD_EQ(bc_decomposition_sel, 1),
385 ROW_FIELD_EQ(bc_decomposition_id, 43),
386 ROW_FIELD_EQ(bc_decomposition_bytes, 31),
387 ROW_FIELD_EQ(bc_decomposition_bytes_pc_plus_1, 5),
388 ROW_FIELD_EQ(bc_decomposition_bytes_pc_plus_2, 2),
389 ROW_FIELD_EQ(bc_decomposition_bytes_pc_plus_3, 0),
390 ROW_FIELD_EQ(bc_decomposition_pc, 1),
391 ROW_FIELD_EQ(bc_decomposition_bytes_remaining, 3),
392 ROW_FIELD_EQ(bc_decomposition_sel_windows_gt_remaining, 1),
393 ROW_FIELD_EQ(bc_decomposition_windows_min_remaining_inv, FF(DECOMPOSE_WINDOW_SIZE - 3).invert()),
394 ROW_FIELD_EQ(bc_decomposition_sel_windows_eq_remaining, 0),
395 ROW_FIELD_EQ(bc_decomposition_bytes_to_read, 3),
396 ROW_FIELD_EQ(bc_decomposition_sel_packed, 0),
397 ROW_FIELD_EQ(bc_decomposition_next_packed_pc, 31),
398 ROW_FIELD_EQ(bc_decomposition_next_packed_pc_min_pc_inv, FF(31 - 1).invert()),
399 ROW_FIELD_EQ(bc_decomposition_last_of_contract, 0)));
400
401 EXPECT_THAT(rows.at(3),
402 AllOf(ROW_FIELD_EQ(bc_decomposition_sel, 1),
403 ROW_FIELD_EQ(bc_decomposition_id, 43),
404 ROW_FIELD_EQ(bc_decomposition_bytes, 5),
405 ROW_FIELD_EQ(bc_decomposition_bytes_pc_plus_1, 2),
406 ROW_FIELD_EQ(bc_decomposition_bytes_pc_plus_2, 0),
407 ROW_FIELD_EQ(bc_decomposition_pc, 2),
408 ROW_FIELD_EQ(bc_decomposition_bytes_remaining, 2),
409 ROW_FIELD_EQ(bc_decomposition_sel_windows_gt_remaining, 1),
410 ROW_FIELD_EQ(bc_decomposition_windows_min_remaining_inv, FF(DECOMPOSE_WINDOW_SIZE - 2).invert()),
411 ROW_FIELD_EQ(bc_decomposition_sel_windows_eq_remaining, 0),
412 ROW_FIELD_EQ(bc_decomposition_bytes_to_read, 2),
413 ROW_FIELD_EQ(bc_decomposition_sel_packed, 0),
414 ROW_FIELD_EQ(bc_decomposition_next_packed_pc, 31),
415 ROW_FIELD_EQ(bc_decomposition_next_packed_pc_min_pc_inv, FF(31 - 2).invert()),
416 ROW_FIELD_EQ(bc_decomposition_last_of_contract, 0)));
417
418 EXPECT_THAT(rows.at(4),
419 AllOf(ROW_FIELD_EQ(bc_decomposition_sel, 1),
420 ROW_FIELD_EQ(bc_decomposition_id, 43),
421 ROW_FIELD_EQ(bc_decomposition_bytes, 2),
422 ROW_FIELD_EQ(bc_decomposition_bytes_pc_plus_1, 0),
423 ROW_FIELD_EQ(bc_decomposition_pc, 3),
424 ROW_FIELD_EQ(bc_decomposition_bytes_remaining, 1),
425 ROW_FIELD_EQ(bc_decomposition_sel_windows_gt_remaining, 1),
426 ROW_FIELD_EQ(bc_decomposition_windows_min_remaining_inv, FF(DECOMPOSE_WINDOW_SIZE - 1).invert()),
427 ROW_FIELD_EQ(bc_decomposition_sel_windows_eq_remaining, 0),
428 ROW_FIELD_EQ(bc_decomposition_bytes_to_read, 1),
429 ROW_FIELD_EQ(bc_decomposition_sel_packed, 0),
430 ROW_FIELD_EQ(bc_decomposition_next_packed_pc, 31),
431 ROW_FIELD_EQ(bc_decomposition_next_packed_pc_min_pc_inv, FF(31 - 3).invert()),
432 ROW_FIELD_EQ(bc_decomposition_last_of_contract, 1)));
433}
434
435TEST(BytecodeTraceGenTest, BasicSingleByte)
436{
437 TestTraceContainer trace;
438 BytecodeTraceBuilder builder;
439
440 builder.process_decomposition(
441 {
442 simulation::BytecodeDecompositionEvent{
443 .bytecode_id = 43,
444 .bytecode = std::make_shared<std::vector<uint8_t>>(std::vector<uint8_t>{ 24 }),
445 },
446 },
447 trace);
448 auto rows = trace.as_rows();
449
450 // One extra empty row is prepended. Note that precomputed_first_row is not set through process_decomposition()
451 // because it pertains to another subtrace.
452 ASSERT_EQ(rows.size(), 1 + 1);
453
454 // We do not inspect row at index 0 as it is completely empty.
455 EXPECT_THAT(rows.at(1),
456 AllOf(ROW_FIELD_EQ(bc_decomposition_sel, 1),
457 ROW_FIELD_EQ(bc_decomposition_id, 43),
458 ROW_FIELD_EQ(bc_decomposition_bytes, 24),
459 ROW_FIELD_EQ(bc_decomposition_bytes_pc_plus_1, 0),
460 ROW_FIELD_EQ(bc_decomposition_bytes_pc_plus_2, 0),
461 ROW_FIELD_EQ(bc_decomposition_bytes_pc_plus_3, 0),
462 ROW_FIELD_EQ(bc_decomposition_bytes_pc_plus_4, 0),
463 ROW_FIELD_EQ(bc_decomposition_pc, 0),
464 ROW_FIELD_EQ(bc_decomposition_bytes_remaining, 1),
465 ROW_FIELD_EQ(bc_decomposition_sel_windows_gt_remaining, 1),
466 ROW_FIELD_EQ(bc_decomposition_windows_min_remaining_inv, FF(DECOMPOSE_WINDOW_SIZE - 1).invert()),
467 ROW_FIELD_EQ(bc_decomposition_sel_windows_eq_remaining, 0),
468 ROW_FIELD_EQ(bc_decomposition_bytes_to_read, 1),
469 ROW_FIELD_EQ(bc_decomposition_last_of_contract, 1),
470 ROW_FIELD_EQ(bc_decomposition_sel_packed, 1),
471 ROW_FIELD_EQ(bc_decomposition_start, 1),
472 ROW_FIELD_EQ(bc_decomposition_next_packed_pc, 0),
473 ROW_FIELD_EQ(bc_decomposition_next_packed_pc_min_pc_inv, 0)));
474}
475
476TEST(BytecodeTraceGenTest, BasicLongerThanWindowSize)
477{
478 TestTraceContainer trace;
479 BytecodeTraceBuilder builder;
480
481 constexpr auto bytecode_size = DECOMPOSE_WINDOW_SIZE + 8;
482 std::vector<uint8_t> bytecode(bytecode_size);
483 const uint8_t first_byte = 17; // Arbitrary start value and we increment by one. We will hit invalid opcodes
484 // but it should not matter.
485
486 for (uint8_t i = 0; i < bytecode_size; i++) {
487 bytecode[i] = i + first_byte;
488 }
489
490 builder.process_decomposition(
491 {
492 simulation::BytecodeDecompositionEvent{
493 .bytecode_id = 7,
495 },
496 },
497 trace);
498 auto rows = trace.as_rows();
499
500 // One extra empty row is prepended. Note that precomputed_first_row is not set through process_decomposition()
501 // because it pertains to another subtrace.
502 ASSERT_EQ(rows.size(), bytecode_size + 1);
503
504 // We do not inspect row at index 0 as it is completely empty.
505 EXPECT_THAT(rows.at(1),
506 AllOf(ROW_FIELD_EQ(bc_decomposition_sel, 1),
507 ROW_FIELD_EQ(bc_decomposition_id, 7),
508 ROW_FIELD_EQ(bc_decomposition_bytes, first_byte),
509 ROW_FIELD_EQ(bc_decomposition_pc, 0),
510 ROW_FIELD_EQ(bc_decomposition_bytes_remaining, bytecode_size),
511 ROW_FIELD_EQ(bc_decomposition_sel_windows_gt_remaining, 0),
512 ROW_FIELD_EQ(bc_decomposition_windows_min_remaining_inv, FF(-8).invert()),
513 ROW_FIELD_EQ(bc_decomposition_sel_windows_eq_remaining, 0),
514 ROW_FIELD_EQ(bc_decomposition_bytes_to_read, DECOMPOSE_WINDOW_SIZE),
515 ROW_FIELD_EQ(bc_decomposition_sel_packed, 1),
516 ROW_FIELD_EQ(bc_decomposition_start, 1),
517 ROW_FIELD_EQ(bc_decomposition_next_packed_pc, 0),
518 ROW_FIELD_EQ(bc_decomposition_next_packed_pc_min_pc_inv, 0),
519 ROW_FIELD_EQ(bc_decomposition_last_of_contract, 0)));
520
521 // We are interested to inspect the boundary aroud bytes_remaining == windows size
522
523 EXPECT_THAT(rows.at(9),
524 AllOf(ROW_FIELD_EQ(bc_decomposition_sel, 1),
525 ROW_FIELD_EQ(bc_decomposition_id, 7),
526 ROW_FIELD_EQ(bc_decomposition_bytes, first_byte + 8),
527 ROW_FIELD_EQ(bc_decomposition_pc, 8),
528 ROW_FIELD_EQ(bc_decomposition_bytes_remaining, DECOMPOSE_WINDOW_SIZE),
529 ROW_FIELD_EQ(bc_decomposition_sel_windows_gt_remaining, 0),
530 ROW_FIELD_EQ(bc_decomposition_windows_min_remaining_inv, 0),
531 ROW_FIELD_EQ(bc_decomposition_sel_windows_eq_remaining, 1),
532 ROW_FIELD_EQ(bc_decomposition_bytes_to_read, DECOMPOSE_WINDOW_SIZE),
533 ROW_FIELD_EQ(bc_decomposition_last_of_contract, 0)));
534
535 EXPECT_THAT(rows.at(10),
536 AllOf(ROW_FIELD_EQ(bc_decomposition_sel, 1),
537 ROW_FIELD_EQ(bc_decomposition_id, 7),
538 ROW_FIELD_EQ(bc_decomposition_bytes, first_byte + 9),
539 ROW_FIELD_EQ(bc_decomposition_pc, 9),
540 ROW_FIELD_EQ(bc_decomposition_bytes_remaining, DECOMPOSE_WINDOW_SIZE - 1),
541 ROW_FIELD_EQ(bc_decomposition_sel_windows_gt_remaining, 1),
542 ROW_FIELD_EQ(bc_decomposition_windows_min_remaining_inv, 1),
543 ROW_FIELD_EQ(bc_decomposition_sel_windows_eq_remaining, 0),
544 ROW_FIELD_EQ(bc_decomposition_bytes_to_read, DECOMPOSE_WINDOW_SIZE - 1),
545 ROW_FIELD_EQ(bc_decomposition_sel_packed, 0),
546 ROW_FIELD_EQ(bc_decomposition_next_packed_pc, 31),
547 ROW_FIELD_EQ(bc_decomposition_next_packed_pc_min_pc_inv, FF(31 - 9).invert()),
548 ROW_FIELD_EQ(bc_decomposition_last_of_contract, 0)));
549
550 // Last row
551 EXPECT_THAT(rows.at(bytecode_size),
552 AllOf(ROW_FIELD_EQ(bc_decomposition_sel, 1),
553 ROW_FIELD_EQ(bc_decomposition_id, 7),
554 ROW_FIELD_EQ(bc_decomposition_bytes, first_byte + bytecode_size - 1),
555 ROW_FIELD_EQ(bc_decomposition_pc, bytecode_size - 1),
556 ROW_FIELD_EQ(bc_decomposition_bytes_remaining, 1),
557 ROW_FIELD_EQ(bc_decomposition_sel_windows_gt_remaining, 1),
558 ROW_FIELD_EQ(bc_decomposition_windows_min_remaining_inv, FF(DECOMPOSE_WINDOW_SIZE - 1).invert()),
559 ROW_FIELD_EQ(bc_decomposition_sel_windows_eq_remaining, 0),
560 ROW_FIELD_EQ(bc_decomposition_bytes_to_read, 1),
561 ROW_FIELD_EQ(bc_decomposition_sel_packed, 0),
562 ROW_FIELD_EQ(bc_decomposition_next_packed_pc, 62),
563 ROW_FIELD_EQ(bc_decomposition_next_packed_pc_min_pc_inv, FF(62 - (bytecode_size - 1)).invert()),
564 ROW_FIELD_EQ(bc_decomposition_last_of_contract, 1)));
565}
566
567TEST(BytecodeTraceGenTest, MultipleEvents)
568{
569 TestTraceContainer trace;
570 BytecodeTraceBuilder builder;
571
572 std::vector<uint32_t> bc_sizes = { DECOMPOSE_WINDOW_SIZE + 2, 17, DECOMPOSE_WINDOW_SIZE, 1 };
574
575 std::transform(bc_sizes.begin(), bc_sizes.end(), bytecodes.begin(), [](uint32_t bc_size) -> std::vector<uint8_t> {
576 std::vector<uint8_t> bytecode(bc_size);
577 for (uint8_t i = 0; i < static_cast<uint8_t>(bc_size); i++) {
578 bytecode[i] = i * i; // Arbitrary bytecode that we will not inspect below
579 }
580
581 return bytecode;
582 });
583
584 builder.process_decomposition(
585 {
586 simulation::BytecodeDecompositionEvent{
587 .bytecode_id = 0,
588 .bytecode = std::make_shared<std::vector<uint8_t>>(bytecodes[0]),
589 },
590 simulation::BytecodeDecompositionEvent{
591 .bytecode_id = 1,
592 .bytecode = std::make_shared<std::vector<uint8_t>>(bytecodes[1]),
593 },
594 simulation::BytecodeDecompositionEvent{
595 .bytecode_id = 2,
596 .bytecode = std::make_shared<std::vector<uint8_t>>(bytecodes[2]),
597 },
598 simulation::BytecodeDecompositionEvent{
599 .bytecode_id = 3,
600 .bytecode = std::make_shared<std::vector<uint8_t>>(bytecodes[3]),
601 },
602 },
603 trace);
604 auto rows = trace.as_rows();
605
606 // One extra empty row is prepended.
607 ASSERT_EQ(rows.size(), (2 * DECOMPOSE_WINDOW_SIZE) + 20 + 1);
608
609 size_t row_pos = 1;
610 for (uint32_t i = 0; i < 4; i++) {
611 uint32_t next_packed_pc = 0;
612 for (uint32_t j = 0; j < bc_sizes[i]; j++) {
613 const auto bytes_rem = bc_sizes[i] - j;
614 EXPECT_THAT(
615 rows.at(row_pos),
616 AllOf(
617 ROW_FIELD_EQ(bc_decomposition_sel, 1),
618 ROW_FIELD_EQ(bc_decomposition_id, i),
619 ROW_FIELD_EQ(bc_decomposition_pc, j),
620 ROW_FIELD_EQ(bc_decomposition_bytes_remaining, bytes_rem),
621 ROW_FIELD_EQ(bc_decomposition_sel_windows_gt_remaining, DECOMPOSE_WINDOW_SIZE > bytes_rem ? 1 : 0),
623 bc_decomposition_windows_min_remaining_inv,
624 bytes_rem == DECOMPOSE_WINDOW_SIZE ? 0 : (FF(DECOMPOSE_WINDOW_SIZE) - FF(bytes_rem)).invert()),
625 ROW_FIELD_EQ(bc_decomposition_sel_windows_eq_remaining, bytes_rem == DECOMPOSE_WINDOW_SIZE ? 1 : 0),
626 ROW_FIELD_EQ(bc_decomposition_bytes_to_read, std::min(DECOMPOSE_WINDOW_SIZE, bytes_rem)),
627 ROW_FIELD_EQ(bc_decomposition_sel_packed, j == next_packed_pc ? 1 : 0),
628 ROW_FIELD_EQ(bc_decomposition_next_packed_pc, next_packed_pc),
629 ROW_FIELD_EQ(bc_decomposition_next_packed_pc_min_pc_inv,
630 j == next_packed_pc ? 0 : FF(next_packed_pc - j).invert()),
631 ROW_FIELD_EQ(bc_decomposition_start, j == 0 ? 1 : 0),
632 ROW_FIELD_EQ(bc_decomposition_last_of_contract, j == bc_sizes[i] - 1 ? 1 : 0)));
633 row_pos++;
634 next_packed_pc += j % 31 == 0 ? 31 : 0;
635 }
636 }
637}
638
639TEST(BytecodeTraceGenTest, BasicHashing)
640{
641 TestTraceContainer trace;
642 BytecodeTraceBuilder builder;
643
644 builder.process_hashing(
645 {
646 simulation::BytecodeHashingEvent{
647 .bytecode_id =
648 FF(0xdeadbeef), // Not the real hash, but for tracegen testing we don't need the correct value.
649 .bytecode_length_in_bytes = 93,
650 .bytecode_fields = { 10, 20, 30 },
651 },
652 },
653 trace);
654 const auto rows = trace.as_rows();
655
656 // One extra empty row is prepended.
657 EXPECT_THAT(rows.at(1),
658 AllOf(ROW_FIELD_EQ(bc_hashing_sel, 1),
659 ROW_FIELD_EQ(bc_hashing_start, 1),
660 ROW_FIELD_EQ(bc_hashing_sel_not_start, 0),
661 ROW_FIELD_EQ(bc_hashing_sel_not_padding_1, 1),
662 ROW_FIELD_EQ(bc_hashing_sel_not_padding_2, 1),
663 ROW_FIELD_EQ(bc_hashing_padding, 2),
664 ROW_FIELD_EQ(bc_hashing_end, 0),
665 ROW_FIELD_EQ(bc_hashing_bytecode_id, FF(0xdeadbeef)),
666 ROW_FIELD_EQ(bc_hashing_pc_index, 0),
667 // We don't increment at start to account for the prepended first field length | separator:
668 ROW_FIELD_EQ(bc_hashing_pc_index_1, 0),
669 ROW_FIELD_EQ(bc_hashing_pc_index_2, 31),
670 ROW_FIELD_EQ(bc_hashing_packed_fields_0, simulation::compute_public_bytecode_first_field(93)),
671 ROW_FIELD_EQ(bc_hashing_packed_fields_1, 10),
672 ROW_FIELD_EQ(bc_hashing_packed_fields_2, 20),
673 ROW_FIELD_EQ(bc_hashing_size_in_bytes, 93),
674 ROW_FIELD_EQ(bc_hashing_input_len, 4),
675 ROW_FIELD_EQ(bc_hashing_rounds_rem, 2)));
676
677 // Latched row
678 EXPECT_THAT(rows.at(2),
679 AllOf(ROW_FIELD_EQ(bc_hashing_sel, 1),
680 ROW_FIELD_EQ(bc_hashing_start, 0),
681 ROW_FIELD_EQ(bc_hashing_sel_not_start, 1),
682 ROW_FIELD_EQ(bc_hashing_sel_not_padding_1, 0),
683 ROW_FIELD_EQ(bc_hashing_sel_not_padding_2, 0),
684 ROW_FIELD_EQ(bc_hashing_padding, 2),
685 ROW_FIELD_EQ(bc_hashing_end, 1),
686 ROW_FIELD_EQ(bc_hashing_bytecode_id, FF(0xdeadbeef)),
687 ROW_FIELD_EQ(bc_hashing_pc_index, 62),
688 ROW_FIELD_EQ(bc_hashing_pc_index_1, 93),
689 ROW_FIELD_EQ(bc_hashing_pc_index_2, 124),
690 ROW_FIELD_EQ(bc_hashing_packed_fields_0, 30),
691 ROW_FIELD_EQ(bc_hashing_packed_fields_1, 0),
692 ROW_FIELD_EQ(bc_hashing_packed_fields_2, 0),
693 ROW_FIELD_EQ(bc_hashing_size_in_bytes, 93),
694 ROW_FIELD_EQ(bc_hashing_input_len, 4),
695 ROW_FIELD_EQ(bc_hashing_rounds_rem, 1)));
696}
697
698std::vector<Instruction> gen_random_instructions(std::span<const WireOpCode> opcodes)
699{
700 std::vector<Instruction> instructions;
701 instructions.reserve(opcodes.size());
702 for (const auto& opcode : opcodes) {
703 instructions.emplace_back(testing::random_instruction(opcode));
704 }
705 return instructions;
706}
707
708std::vector<uint8_t> create_bytecode(std::span<const Instruction> instructions)
709{
710 std::vector<uint8_t> bytecode;
711 for (const auto& instruction : instructions) {
712 auto serialized_instruction = instruction.serialize();
713 bytecode.insert(bytecode.end(),
714 std::make_move_iterator(serialized_instruction.begin()),
715 std::make_move_iterator(serialized_instruction.end()));
716 }
717 return bytecode;
718}
719
720std::vector<size_t> gen_pcs(std::span<const WireOpCode> opcodes)
721{
722 std::vector<size_t> pcs;
723 pcs.reserve(opcodes.size());
724 size_t pc = 0;
725 for (const auto& opcode : opcodes) {
726 pcs.emplace_back(pc);
727 pc += get_wire_instruction_spec().at(opcode).size_in_bytes;
728 }
729 return pcs;
730}
731
732std::vector<InstructionFetchingEvent> create_instruction_fetching_events(
733 const std::vector<Instruction>& instructions,
734 const std::vector<size_t>& pcs,
735 const std::shared_ptr<std::vector<uint8_t>>& bytecode_ptr,
736 const BytecodeId bytecode_id)
737{
739 events.reserve(instructions.size());
740
741 for (size_t i = 0; i < instructions.size(); i++) {
742 events.emplace_back(InstructionFetchingEvent{
743 .bytecode_id = bytecode_id,
744 .pc = static_cast<uint32_t>(pcs.at(i)),
745 .instruction = instructions.at(i),
746 .bytecode = bytecode_ptr,
747 });
748 }
749 return events;
750}
751
752// We build a random InstructionFetchingEvent for each wire opcode.
753// We then verify that the bytes (bd0, bd1, ...) correspond to the serialized instruction.
754TEST(BytecodeTraceGenTest, InstrDecompositionInBytesEachOpcode)
755{
756 TestTraceContainer trace;
757 BytecodeTraceBuilder builder;
758
759 constexpr std::array<C, 37> bd_columns = {
760 C::instr_fetching_bd0, C::instr_fetching_bd1, C::instr_fetching_bd2, C::instr_fetching_bd3,
761 C::instr_fetching_bd4, C::instr_fetching_bd5, C::instr_fetching_bd6, C::instr_fetching_bd7,
762 C::instr_fetching_bd8, C::instr_fetching_bd9, C::instr_fetching_bd10, C::instr_fetching_bd11,
763 C::instr_fetching_bd12, C::instr_fetching_bd13, C::instr_fetching_bd14, C::instr_fetching_bd15,
764 C::instr_fetching_bd16, C::instr_fetching_bd17, C::instr_fetching_bd18, C::instr_fetching_bd19,
765 C::instr_fetching_bd20, C::instr_fetching_bd21, C::instr_fetching_bd22, C::instr_fetching_bd23,
766 C::instr_fetching_bd24, C::instr_fetching_bd25, C::instr_fetching_bd26, C::instr_fetching_bd27,
767 C::instr_fetching_bd28, C::instr_fetching_bd29, C::instr_fetching_bd30, C::instr_fetching_bd31,
768 C::instr_fetching_bd32, C::instr_fetching_bd33, C::instr_fetching_bd34, C::instr_fetching_bd35,
769 C::instr_fetching_bd36,
770 };
771
772 constexpr std::array<C, 7> operand_columns = {
773 C::instr_fetching_op1, C::instr_fetching_op2, C::instr_fetching_op3, C::instr_fetching_op4,
774 C::instr_fetching_op5, C::instr_fetching_op6, C::instr_fetching_op7,
775 };
776
777 constexpr BytecodeId bytecode_id = 1;
778 constexpr auto num_opcodes = static_cast<size_t>(WireOpCode::LAST_OPCODE_SENTINEL);
779
781 opcodes.reserve(num_opcodes);
782 for (size_t i = 0; i < num_opcodes; i++) {
783 opcodes.emplace_back(static_cast<WireOpCode>(i));
784 }
785
786 std::vector<Instruction> instructions = gen_random_instructions(opcodes);
787 std::vector<size_t> pcs = gen_pcs(opcodes);
788 std::vector<uint8_t> bytecode = create_bytecode(instructions);
789
792 create_instruction_fetching_events(instructions, pcs, bytecode_ptr, bytecode_id);
793
794 builder.process_instruction_fetching(events, trace);
795
796 for (uint32_t i = 0; i < num_opcodes; i++) {
797 const auto& instr = instructions.at(i);
798 const auto instr_encoded = instr.serialize();
799 const auto w_opcode = static_cast<WireOpCode>(i);
800
801 // Check size_in_bytes column
802 const auto expected_size_in_bytes = get_wire_instruction_spec().at(w_opcode).size_in_bytes;
803 ASSERT_EQ(instr_encoded.size(), expected_size_in_bytes);
804 EXPECT_EQ(FF(expected_size_in_bytes), trace.get(C::instr_fetching_instr_size, i));
805
806 // Inspect each byte
807 for (size_t j = 0; j < static_cast<size_t>(expected_size_in_bytes); j++) {
808 EXPECT_EQ(FF(instr_encoded.at(j)), trace.get(bd_columns.at(j), i));
809 }
810
811 // Check exection opcode
812 EXPECT_EQ(FF(static_cast<uint8_t>(get_wire_instruction_spec().at(w_opcode).exec_opcode)),
813 trace.get(C::instr_fetching_exec_opcode, i));
814
815 // Check indirect
816 EXPECT_EQ(FF(instr.addressing_mode), trace.get(C::instr_fetching_addressing_mode, i));
817
818 // Check PCs
819 EXPECT_EQ(FF(pcs.at(i)), trace.get(C::instr_fetching_pc, i));
820
821 // Check operands
822 size_t operand_idx = 0;
823 for (const auto& operand : instr.operands) {
824 EXPECT_EQ(FF(operand), trace.get(operand_columns.at(operand_idx++), i));
825 }
826 }
827}
828
829TEST(BytecodeTraceGenTest, InstrFetchingSingleBytecode)
830{
831 TestTraceContainer trace;
832 BytecodeTraceBuilder builder;
833
834 constexpr BytecodeId bytecode_id = 1;
835 constexpr size_t num_of_opcodes = 10;
836 constexpr std::array<WireOpCode, num_of_opcodes> opcodes = {
841 };
842
843 std::vector<Instruction> instructions = gen_random_instructions(opcodes);
844 std::vector<size_t> pcs = gen_pcs(opcodes);
845 std::vector<uint8_t> bytecode = create_bytecode(instructions);
846
847 std::vector<InstructionFetchingEvent> events = create_instruction_fetching_events(
848 instructions, pcs, std::make_shared<std::vector<uint8_t>>(bytecode), bytecode_id);
849
850 builder.process_instruction_fetching(events, trace);
851
852 const auto rows = trace.as_rows();
853 const auto bytecode_size = bytecode.size();
854 EXPECT_EQ(rows.size(), num_of_opcodes);
855
856 for (size_t i = 0; i < num_of_opcodes; i++) {
857 const auto pc = pcs.at(i);
858 const auto instr_size = get_wire_instruction_spec().at(opcodes.at(i)).size_in_bytes;
859 const auto has_tag = get_wire_instruction_spec().at(opcodes.at(i)).tag_operand_idx.has_value();
860 const auto tag_is_op2 =
861 has_tag ? static_cast<int>(get_wire_instruction_spec().at(opcodes.at(i)).tag_operand_idx.value() == 2) : 0;
862 const auto bytes_remaining = bytecode_size - pc;
863 const auto bytes_to_read = std::min<size_t>(DECOMPOSE_WINDOW_SIZE, bytes_remaining);
864
865 EXPECT_LE(instr_size, bytes_to_read);
866 const auto instr_abs_diff = bytes_to_read - instr_size;
867
868 EXPECT_LT(pc, bytecode_size);
869 const auto pc_abs_diff = bytecode_size - pc - 1;
870
871 ASSERT_LE(bytecode_size, UINT16_MAX);
872
873 EXPECT_THAT(rows.at(i),
874 AllOf(ROW_FIELD_EQ(instr_fetching_sel, 1),
875 ROW_FIELD_EQ(instr_fetching_pc, pc),
876 ROW_FIELD_EQ(instr_fetching_bd0, static_cast<uint8_t>(opcodes.at(i))),
877 ROW_FIELD_EQ(instr_fetching_bytecode_id, bytecode_id),
878 ROW_FIELD_EQ(instr_fetching_bytes_to_read, bytes_to_read),
879 ROW_FIELD_EQ(instr_fetching_bytecode_size, bytecode_size),
880 ROW_FIELD_EQ(instr_fetching_instr_size, instr_size),
881 ROW_FIELD_EQ(instr_fetching_instr_abs_diff, instr_abs_diff),
882 ROW_FIELD_EQ(instr_fetching_pc_abs_diff, pc_abs_diff),
883 ROW_FIELD_EQ(instr_fetching_pc_out_of_range, 0),
884 ROW_FIELD_EQ(instr_fetching_opcode_out_of_range, 0),
885 ROW_FIELD_EQ(instr_fetching_instr_out_of_range, 0),
886 ROW_FIELD_EQ(instr_fetching_tag_out_of_range, 0),
887 ROW_FIELD_EQ(instr_fetching_sel_parsing_err, 0),
888 ROW_FIELD_EQ(instr_fetching_sel_pc_in_range, 1),
889 ROW_FIELD_EQ(instr_fetching_sel_has_tag, has_tag),
890 ROW_FIELD_EQ(instr_fetching_sel_tag_is_op2, tag_is_op2),
891 ROW_FIELD_EQ(instr_fetching_sel_pc_in_range, 1)));
892 }
893}
894
895// Test involving 3 different bytecode_id's for each 2 opcodes (same bytecode).
896TEST(BytecodeTraceGenTest, InstrFetchingMultipleBytecodes)
897{
898 TestTraceContainer trace;
899 BytecodeTraceBuilder builder;
900
901 constexpr size_t num_of_opcodes = 2;
902 constexpr std::array<WireOpCode, num_of_opcodes> opcodes = {
905 };
906
907 std::vector<Instruction> instructions = gen_random_instructions(opcodes);
908 std::vector<size_t> pcs = gen_pcs(opcodes);
909 std::vector<uint8_t> bytecode = create_bytecode(instructions);
910
912 for (size_t i = 0; i < 3; i++) {
914 auto new_events =
915 create_instruction_fetching_events(instructions, pcs, bytecode_ptr, static_cast<BytecodeId>(i + 1));
916 events.insert(events.end(), new_events.begin(), new_events.end());
917 }
918
919 builder.process_instruction_fetching(events, trace);
920
921 const auto rows = trace.as_rows();
922 EXPECT_EQ(rows.size(), 6);
923
924 for (size_t i = 0; i < 3; i++) {
925 EXPECT_THAT(rows.at(2 * i), ROW_FIELD_EQ(instr_fetching_pc, 0));
926 }
927}
928
929// Test which processes three single instruction events, each of one with a different parsing error.
930// The bytecode can be filled with trivial bytes of size 20 with all bytes being increasing from 0 to 19.
931// First byte at index 0 is set to LAST_OPCODE_SENTINEL + 1.
932// Then consider for the instruction events pc = 0, pc = 19, pc = 38.
933// pc == 0 will correspond to the error OPCODE_OUT_OF_RANGE
934// pc == 19 will have INSTRUCTION_OUT_OF_RANGE
935// pc == 38 will have PC_OUT_OF_RANGE
936// Check for each row that column instr_fetching_parsing_err in addition to the column of the respective error.
937// It is not an issue that the instruction is generated at random in the event and is not consistent with the
938// bytecode for this test case.
939TEST(BytecodeTraceGenTest, InstrFetchingParsingErrors)
940{
941 TestTraceContainer trace;
942 BytecodeTraceBuilder builder;
943
944 constexpr BytecodeId bytecode_id = 1;
945 constexpr size_t bytecode_size = 20;
946 std::vector<uint8_t> bytecode(bytecode_size);
947 for (size_t i = 0; i < bytecode_size; i++) {
948 bytecode[i] = static_cast<uint8_t>(i);
949 }
950 bytecode[0] = static_cast<uint8_t>(WireOpCode::LAST_OPCODE_SENTINEL) + 1;
951
954 events.emplace_back(InstructionFetchingEvent{
955 .bytecode_id = bytecode_id,
956 .pc = 0,
957 .bytecode = bytecode_ptr,
959 });
960 events.emplace_back(InstructionFetchingEvent{
961 .bytecode_id = bytecode_id,
962 .pc = 19,
963 .bytecode = bytecode_ptr,
965 });
966 events.emplace_back(InstructionFetchingEvent{
967 .bytecode_id = bytecode_id,
968 .pc = 38,
969 .bytecode = bytecode_ptr,
971 });
972
973 builder.process_instruction_fetching(events, trace);
974
975 const auto rows = trace.as_rows();
976 ASSERT_EQ(rows.size(), 3);
977
978 EXPECT_THAT(rows.at(0),
979 AllOf(ROW_FIELD_EQ(instr_fetching_sel, 1),
980 ROW_FIELD_EQ(instr_fetching_sel_pc_in_range, 1),
981 ROW_FIELD_EQ(instr_fetching_pc, 0),
982 ROW_FIELD_EQ(instr_fetching_bytes_to_read, 20),
983 ROW_FIELD_EQ(instr_fetching_instr_size, 0),
984 ROW_FIELD_EQ(instr_fetching_instr_abs_diff,
985 20), // instr_size <= bytes_to_read: bytes_to_read - instr_size
986 ROW_FIELD_EQ(instr_fetching_sel_parsing_err, 1),
987 ROW_FIELD_EQ(instr_fetching_pc_abs_diff, 19), // bytecode_size - pc - 1 if bytecode_size > pc
988 ROW_FIELD_EQ(instr_fetching_opcode_out_of_range, 1)));
989
990 EXPECT_THAT(rows.at(1),
991 AllOf(ROW_FIELD_EQ(instr_fetching_sel, 1),
992 ROW_FIELD_EQ(instr_fetching_sel_pc_in_range, 1),
993 ROW_FIELD_EQ(instr_fetching_pc, 19), // OR_16 opcode
994 ROW_FIELD_EQ(instr_fetching_bytes_to_read, 1),
995 ROW_FIELD_EQ(instr_fetching_instr_size, 8), // OR_16 is 8 bytes long
996 ROW_FIELD_EQ(instr_fetching_instr_abs_diff,
997 6), // instr_size > bytes_to_read: instr_size - bytes_to_read - 1
998 ROW_FIELD_EQ(instr_fetching_sel_parsing_err, 1),
999 ROW_FIELD_EQ(instr_fetching_pc_abs_diff, 0), // bytecode_size - pc - 1 if bytecode_size > pc
1000 ROW_FIELD_EQ(instr_fetching_instr_out_of_range, 1)));
1001
1002 EXPECT_THAT(
1003 rows.at(2),
1004 AllOf(ROW_FIELD_EQ(instr_fetching_sel, 1),
1005 ROW_FIELD_EQ(instr_fetching_sel_pc_in_range, 0),
1006 ROW_FIELD_EQ(instr_fetching_pc, 38),
1007 ROW_FIELD_EQ(instr_fetching_bytes_to_read, 0),
1008 ROW_FIELD_EQ(instr_fetching_instr_size, 0),
1009 ROW_FIELD_EQ(instr_fetching_instr_abs_diff, 0), // instr_size <= bytes_to_read: bytes_to_read - instr_size
1010 ROW_FIELD_EQ(instr_fetching_sel_parsing_err, 1),
1011 ROW_FIELD_EQ(instr_fetching_pc_abs_diff, 18), // pc - bytecode_size if bytecode_size <= pc
1012 ROW_FIELD_EQ(instr_fetching_pc_out_of_range, 1)));
1013}
1014
1015// Test on error tag out of range
1016TEST(BytecodeTraceGenTest, InstrFetchingErrorTagOutOfRange)
1017{
1019 using simulation::Operand;
1021 TestTraceContainer trace;
1022 BytecodeTraceBuilder builder;
1023
1024 auto instr_cast = random_instruction(WireOpCode::CAST_16);
1025 auto instr_set = random_instruction(WireOpCode::SET_64);
1026 constexpr uint32_t cast_size = 7;
1027 constexpr uint32_t set_64_size = 13;
1028
1029 instr_cast.operands.at(2) = Operand::from<uint8_t>(0x09); // tag operand mutation to 0x09 which is out of range
1030 instr_set.operands.at(1) = Operand::from<uint8_t>(0x0A); // tag operand mutation to 0x0A which is out of range
1031
1032 auto bytecode = instr_cast.serialize();
1033 ASSERT_EQ(bytecode.size(), cast_size);
1034
1035 auto instr_set_bytecode = instr_set.serialize();
1036 ASSERT_EQ(instr_set_bytecode.size(), set_64_size);
1037
1038 bytecode.insert(bytecode.end(), instr_set_bytecode.begin(), instr_set_bytecode.end());
1039
1040 const auto bytecode_ptr = std::make_shared<std::vector<uint8_t>>(bytecode);
1041
1043 events.emplace_back(InstructionFetchingEvent{
1044 .bytecode_id = 1,
1045 .pc = 0,
1046 .instruction = deserialize_instruction(bytecode, 0), // Reflect more the real code path than passing instr_cast.
1047 .bytecode = bytecode_ptr,
1049 });
1050
1051 events.emplace_back(InstructionFetchingEvent{
1052 .bytecode_id = 1,
1053 .pc = cast_size,
1054 .instruction =
1055 deserialize_instruction(bytecode, cast_size), // Reflect more the real code path than passing instr_set.
1056 .bytecode = bytecode_ptr,
1058 });
1059
1060 builder.process_instruction_fetching(events, trace);
1061
1062 const auto rows = trace.as_rows();
1063 ASSERT_EQ(rows.size(), 2);
1064
1065 EXPECT_THAT(rows.at(0),
1066 AllOf(ROW_FIELD_EQ(instr_fetching_sel, 1),
1067 ROW_FIELD_EQ(instr_fetching_sel_pc_in_range, 1),
1068 ROW_FIELD_EQ(instr_fetching_sel_has_tag, 1),
1069 ROW_FIELD_EQ(instr_fetching_sel_tag_is_op2, 0),
1070 ROW_FIELD_EQ(instr_fetching_tag_value, 9),
1071 ROW_FIELD_EQ(instr_fetching_pc, 0),
1072 ROW_FIELD_EQ(instr_fetching_bytes_to_read, cast_size + set_64_size),
1073 ROW_FIELD_EQ(instr_fetching_instr_size, cast_size),
1074 ROW_FIELD_EQ(instr_fetching_instr_abs_diff,
1075 set_64_size), // instr_size <= bytes_to_read: bytes_to_read - instr_size
1076 ROW_FIELD_EQ(instr_fetching_sel_parsing_err, 1),
1077 ROW_FIELD_EQ(instr_fetching_pc_abs_diff,
1078 cast_size + set_64_size - 1), // bytecode_size - pc - 1 if bytecode_size > pc
1079 ROW_FIELD_EQ(instr_fetching_tag_out_of_range, 1)));
1080
1081 EXPECT_THAT(
1082 rows.at(1),
1083 AllOf(ROW_FIELD_EQ(instr_fetching_sel, 1),
1084 ROW_FIELD_EQ(instr_fetching_sel_pc_in_range, 1),
1085 ROW_FIELD_EQ(instr_fetching_sel_has_tag, 1),
1086 ROW_FIELD_EQ(instr_fetching_sel_tag_is_op2, 1),
1087 ROW_FIELD_EQ(instr_fetching_tag_value, 10),
1088 ROW_FIELD_EQ(instr_fetching_pc, cast_size),
1089 ROW_FIELD_EQ(instr_fetching_bytes_to_read, set_64_size),
1090 ROW_FIELD_EQ(instr_fetching_instr_size, set_64_size),
1091 ROW_FIELD_EQ(instr_fetching_instr_abs_diff, 0), // instr_size <= bytes_to_read: bytes_to_read - instr_size
1092 ROW_FIELD_EQ(instr_fetching_sel_parsing_err, 1),
1093 ROW_FIELD_EQ(instr_fetching_pc_abs_diff, set_64_size - 1), // bytecode_size - pc - 1 if bytecode_size > pc
1094 ROW_FIELD_EQ(instr_fetching_tag_out_of_range, 1)));
1095}
1096
1097} // namespace
1098} // namespace bb::avm2::tracegen
std::shared_ptr< Napi::ThreadSafeFunction > bytecode
#define MAX_PUBLIC_CALLS_TO_UNIQUE_CONTRACT_CLASS_IDS
std::vector< AvmFullRowConstRef > as_rows() const
const FF & get(Column col, uint32_t row) const
AluTraceBuilder builder
Definition alu.test.cpp:124
TestTraceContainer trace
std::vector< uint8_t > create_bytecode(const std::vector< bb::avm2::simulation::Instruction > &instructions)
Instruction instruction
#define ROW_FIELD_EQ(field_name, expression)
Definition macros.hpp:7
FF compute_public_bytecode_first_field(size_t bytecode_size)
Instruction deserialize_instruction(std::span< const uint8_t > bytecode, size_t pos)
Attempts to deserialize the instruction at position pos in bytecode. Called by bytecode managers duri...
Instruction random_instruction(WireOpCode w_opcode)
Definition fixtures.cpp:125
AvmFlavorSettings::FF FF
Definition field.hpp:10
const std::unordered_map< WireOpCode, WireInstructionSpec > & get_wire_instruction_spec()
constexpr uint32_t DECOMPOSE_WINDOW_SIZE
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
STL namespace.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13