Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
precomputed_trace.cpp
Go to the documentation of this file.
2
3#include <array>
4#include <cstddef>
5#include <cstdint>
6#include <vector>
7
20
21namespace bb::avm2::tracegen {
22
23using C = Column;
24
25void PrecomputedTraceBuilder::process_misc(TraceContainer& trace, const uint32_t num_rows)
26{
27 // First row.
28 trace.set(C::precomputed_first_row, 0, 1);
29
30 // Clk
31 // TODO: What a waste of 64MB. Can we elegantly have a flag for this?
32 trace.reserve_column(C::precomputed_clk, num_rows);
33 for (uint32_t i = 0; i < num_rows; i++) {
34 trace.set(C::precomputed_clk, i, i);
35 }
36}
37
39{
40 // 256 per input (a and b), and 3 different bitwise ops
41 constexpr auto num_rows = 256 * 256 * 3;
42 trace.reserve_column(C::precomputed_sel_bitwise, num_rows);
43 trace.reserve_column(C::precomputed_bitwise_input_a, num_rows);
44 trace.reserve_column(C::precomputed_bitwise_input_b, num_rows);
45 trace.reserve_column(C::precomputed_bitwise_output, num_rows);
46
47 // row # is derived as:
48 // - input_b: bits 0...7 (0 being LSB)
49 // - input_a: bits 8...15
50 // - op_id: bits 16...
51 // In other words, the first 256*256 rows are for op_id 0. Next are for op_id 1, followed by op_id 2.
52 auto row_from_inputs = [](BitwiseOperation op_id, uint32_t input_a, uint32_t input_b) -> uint32_t {
53 return (static_cast<uint32_t>(op_id) << 16) | (input_a << 8) | input_b;
54 };
55 auto compute_operation = [](BitwiseOperation op_id, uint32_t a, uint32_t b) -> uint32_t {
56 switch (op_id) {
58 return a & b;
60 return a | b;
62 return a ^ b;
63 }
64
65 assert(false && "This should not happen");
66 return 0; // Should never happen. To please the compiler.
67 };
68
70 for (uint32_t a = 0; a < 256; a++) {
71 for (uint32_t b = 0; b < 256; b++) {
72 trace.set(row_from_inputs(op_id, a, b),
73 { {
74 { C::precomputed_sel_bitwise, 1 },
75 { C::precomputed_bitwise_op_id, static_cast<uint8_t>(op_id) },
76 { C::precomputed_bitwise_input_a, FF(a) },
77 { C::precomputed_bitwise_input_b, FF(b) },
78 { C::precomputed_bitwise_output, FF(compute_operation(op_id, a, b)) },
79 } });
80 }
81 }
82 }
83}
84
92{
93 constexpr auto num_rows = 1 << 8; // 256
94 // Set this selector high for the first 2^8 rows
95 // For these rows, clk will be 0...255
96 trace.reserve_column(C::precomputed_sel_range_8, num_rows);
97 for (uint32_t i = 0; i < num_rows; i++) {
98 trace.set(C::precomputed_sel_range_8, i, 1);
99 }
100}
101
109{
110 constexpr auto num_rows = 1 << 16; // 2^16
111 // Set this selector high for the first 2^16 rows
112 // For these rows, clk will be 0...2^16-1
113 trace.reserve_column(C::precomputed_sel_range_16, num_rows);
114 for (uint32_t i = 0; i < num_rows; i++) {
115 trace.set(C::precomputed_sel_range_16, i, 1);
116 }
117}
118
124{
125 constexpr auto num_rows = 1 << 8; // 2^8 = 256
126 trace.reserve_column(C::precomputed_power_of_2, num_rows);
127 for (uint32_t i = 0; i < num_rows; i++) {
128 trace.set(C::precomputed_power_of_2, i, uint256_t(1) << uint256_t(i));
129 }
130}
131
133{
134 constexpr std::array<uint32_t, 64> round_constants{
135 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
136 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
137 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
138 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
139 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
140 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
141 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
142 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
143 };
144 constexpr auto num_rows = round_constants.size();
145 trace.reserve_column(C::precomputed_sha256_compression_round_constant, num_rows);
146 trace.reserve_column(C::precomputed_sel_sha256_compression, num_rows);
147 for (uint32_t i = 0; i < num_rows; i++) {
148 trace.set(i,
149 { { { C::precomputed_sel_sha256_compression, 1 },
150 { C::precomputed_sha256_compression_round_constant, round_constants[i] } } });
151 }
152}
153
155{
157
158 constexpr uint32_t NUM_TAGS = static_cast<uint32_t>(MemoryTag::MAX) + 1;
159 for (uint32_t i = 0; i < NUM_TAGS; i++) {
160 const auto tag = static_cast<MemoryTag>(i);
161 trace.set(i, // Column number corresponds to MemoryTag enum value.
162 { {
163 { C::precomputed_sel_tag_parameters, 1 },
164 { C::precomputed_tag_byte_length, get_tag_bytes(tag) },
165 { C::precomputed_tag_max_bits, get_tag_bits(tag) },
166 { C::precomputed_tag_max_value, get_tag_max_value(tag) },
167 } });
168 }
169}
170
172{
173 const std::array<Column, NUM_OP_DC_SELECTORS> sel_op_dc_columns = {
174 C::precomputed_sel_op_dc_0, C::precomputed_sel_op_dc_1, C::precomputed_sel_op_dc_2,
175 C::precomputed_sel_op_dc_3, C::precomputed_sel_op_dc_4, C::precomputed_sel_op_dc_5,
176 C::precomputed_sel_op_dc_6, C::precomputed_sel_op_dc_7, C::precomputed_sel_op_dc_8,
177 C::precomputed_sel_op_dc_9, C::precomputed_sel_op_dc_10, C::precomputed_sel_op_dc_11,
178 C::precomputed_sel_op_dc_12, C::precomputed_sel_op_dc_13, C::precomputed_sel_op_dc_14,
179 C::precomputed_sel_op_dc_15, C::precomputed_sel_op_dc_16, C::precomputed_sel_op_dc_17,
180 };
181
182 // First set the selector for this table lookup.
183 constexpr uint32_t num_rows = 1 << 8; // 256
184 constexpr uint32_t num_opcodes = static_cast<uint32_t>(WireOpCode::LAST_OPCODE_SENTINEL);
185 trace.reserve_column(C::precomputed_opcode_out_of_range, num_rows - num_opcodes);
186 for (uint32_t i = num_opcodes; i < num_rows; i++) {
187 trace.set(C::precomputed_opcode_out_of_range, i, 1);
188 }
189
190 for (size_t i = 0; i < NUM_OP_DC_SELECTORS; i++) {
191 trace.reserve_column(sel_op_dc_columns.at(i), num_opcodes);
192 }
193 trace.reserve_column(C::precomputed_exec_opcode, num_opcodes);
194 trace.reserve_column(C::precomputed_instr_size, num_opcodes);
195
196 // Fill the lookup tables with the operand decomposition selectors.
197 for (const auto& [wire_opcode, wire_instruction_spec] : get_wire_instruction_spec()) {
198 for (size_t i = 0; i < NUM_OP_DC_SELECTORS; i++) {
199 trace.set(sel_op_dc_columns.at(i),
200 static_cast<uint32_t>(wire_opcode),
201 wire_instruction_spec.op_dc_selectors.at(i));
202 }
203 trace.set(C::precomputed_exec_opcode,
204 static_cast<uint32_t>(wire_opcode),
205 static_cast<uint32_t>(wire_instruction_spec.exec_opcode));
206 trace.set(C::precomputed_instr_size, static_cast<uint32_t>(wire_opcode), wire_instruction_spec.size_in_bytes);
207
208 if (wire_instruction_spec.tag_operand_idx.has_value()) {
209 trace.set(C::precomputed_sel_has_tag, static_cast<uint32_t>(wire_opcode), 1);
210
211 if (wire_instruction_spec.tag_operand_idx.value() == 2) {
212 trace.set(C::precomputed_sel_tag_is_op2, static_cast<uint32_t>(wire_opcode), 1);
213 }
214 }
215 }
216}
217
219{
220 constexpr std::array<Column, AVM_MAX_REGISTERS> MEM_OP_REG_COLUMNS = {
221 Column::precomputed_sel_mem_op_reg_0_, Column::precomputed_sel_mem_op_reg_1_,
222 Column::precomputed_sel_mem_op_reg_2_, Column::precomputed_sel_mem_op_reg_3_,
223 Column::precomputed_sel_mem_op_reg_4_, Column::precomputed_sel_mem_op_reg_5_,
224 };
225 constexpr std::array<Column, AVM_MAX_REGISTERS> RW_COLUMNS = {
226 Column::precomputed_rw_reg_0_, Column::precomputed_rw_reg_1_, Column::precomputed_rw_reg_2_,
227 Column::precomputed_rw_reg_3_, Column::precomputed_rw_reg_4_, Column::precomputed_rw_reg_5_,
228 };
229 constexpr std::array<Column, AVM_MAX_REGISTERS> DO_TAG_CHECK_COLUMNS = {
230 Column::precomputed_sel_tag_check_reg_0_, Column::precomputed_sel_tag_check_reg_1_,
231 Column::precomputed_sel_tag_check_reg_2_, Column::precomputed_sel_tag_check_reg_3_,
232 Column::precomputed_sel_tag_check_reg_4_, Column::precomputed_sel_tag_check_reg_5_,
233 };
234 constexpr std::array<Column, AVM_MAX_REGISTERS> EXPECTED_TAG_COLUMNS = {
235 Column::precomputed_expected_tag_reg_0_, Column::precomputed_expected_tag_reg_1_,
236 Column::precomputed_expected_tag_reg_2_, Column::precomputed_expected_tag_reg_3_,
237 Column::precomputed_expected_tag_reg_4_, Column::precomputed_expected_tag_reg_5_,
238 };
239
240 constexpr std::array<Column, AVM_MAX_OPERANDS> SEL_OP_IS_ADDRESS_COLUMNS = {
241 Column::precomputed_sel_op_is_address_0_, Column::precomputed_sel_op_is_address_1_,
242 Column::precomputed_sel_op_is_address_2_, Column::precomputed_sel_op_is_address_3_,
243 Column::precomputed_sel_op_is_address_4_, Column::precomputed_sel_op_is_address_5_,
244 Column::precomputed_sel_op_is_address_6_,
245 };
246
247 for (const auto& [exec_opcode, exec_instruction_spec] : get_exec_instruction_spec()) {
248 // Basic information.
249 trace.set(static_cast<uint32_t>(exec_opcode),
250 { {
251 { C::precomputed_sel_exec_spec, 1 },
252 { C::precomputed_exec_opcode_opcode_gas, exec_instruction_spec.gas_cost.opcode_gas },
253 { C::precomputed_exec_opcode_base_da_gas, exec_instruction_spec.gas_cost.base_da },
254 { C::precomputed_exec_opcode_dynamic_l2_gas, exec_instruction_spec.gas_cost.dyn_l2 },
255 { C::precomputed_exec_opcode_dynamic_da_gas, exec_instruction_spec.gas_cost.dyn_da },
256 } });
257
258 // Register information.
259 const auto& register_info = get_exec_instruction_spec().at(exec_opcode).register_info;
260 for (size_t i = 0; i < AVM_MAX_REGISTERS; i++) {
261 trace.set(MEM_OP_REG_COLUMNS.at(i), static_cast<uint32_t>(exec_opcode), register_info.is_active(i) ? 1 : 0);
262 trace.set(RW_COLUMNS.at(i), static_cast<uint32_t>(exec_opcode), register_info.is_write(i) ? 1 : 0);
263 trace.set(DO_TAG_CHECK_COLUMNS.at(i),
264 static_cast<uint32_t>(exec_opcode),
265 register_info.need_tag_check(i) ? 1 : 0);
266 trace.set(EXPECTED_TAG_COLUMNS.at(i),
267 static_cast<uint32_t>(exec_opcode),
268 static_cast<uint32_t>(register_info.expected_tag(i).value_or(static_cast<ValueTag>(0))));
269 }
270
271 // Whether an operand is an address
272 for (size_t i = 0; i < AVM_MAX_OPERANDS; i++) {
273 trace.set(SEL_OP_IS_ADDRESS_COLUMNS.at(i),
274 static_cast<uint32_t>(exec_opcode),
275 i < exec_instruction_spec.num_addresses ? 1 : 0);
276 }
277
278 // Gadget / Subtrace Selectors / Decomposable selectors
279 auto dispatch_to_subtrace = get_subtrace_info_map().at(exec_opcode);
280 trace.set(static_cast<uint32_t>(exec_opcode),
281 { { { C::precomputed_subtrace_id, get_subtrace_id(dispatch_to_subtrace.subtrace_selector) },
282 { C::precomputed_subtrace_operation_id, dispatch_to_subtrace.subtrace_operation_id },
283 { C::precomputed_dyn_gas_id, exec_instruction_spec.dyn_gas_id } } });
284 }
285}
286
288{
289 const auto& p_limbs_per_radix = get_p_limbs_per_radix();
290
291 trace.reserve_column(C::precomputed_sel_to_radix_p_limb_counts, p_limbs_per_radix.size());
292 trace.reserve_column(C::precomputed_to_radix_safe_limbs, p_limbs_per_radix.size());
293
294 for (size_t i = 0; i < p_limbs_per_radix.size(); ++i) {
295 size_t decomposition_len = p_limbs_per_radix[i].size();
296 if (decomposition_len > 0) {
297 trace.set(C::precomputed_sel_to_radix_p_limb_counts, static_cast<uint32_t>(i), 1);
298 trace.set(C::precomputed_to_radix_safe_limbs, static_cast<uint32_t>(i), decomposition_len - 1);
299 trace.set(C::precomputed_to_radix_num_limbs_for_p, static_cast<uint32_t>(i), decomposition_len);
300 }
301 }
302}
303
305{
306 const auto& p_limbs_per_radix = get_p_limbs_per_radix();
307
308 uint32_t row = 0;
309 for (size_t i = 0; i < p_limbs_per_radix.size(); ++i) {
310 size_t decomposition_len = p_limbs_per_radix[i].size();
311 for (size_t j = 0; j < decomposition_len; ++j) {
312 trace.set(C::precomputed_sel_p_decomposition, row, 1);
313 trace.set(C::precomputed_p_decomposition_radix, row, i);
314 trace.set(C::precomputed_p_decomposition_limb_index, row, j);
315 trace.set(C::precomputed_p_decomposition_limb, row, p_limbs_per_radix[i][j]);
316 row++;
317 }
318 }
319}
320
322{
323 constexpr uint32_t num_rows = 1 << 8; // 256
324
325 for (uint32_t i = static_cast<uint32_t>(MemoryTag::MAX) + 1; i < num_rows; i++) {
326 trace.set(C::precomputed_sel_mem_tag_out_of_range, i, 1);
327 }
328}
329
331{
332 constexpr uint32_t num_rows = 1 << 16; // 65536
333 trace.reserve_column(C::precomputed_sel_addressing_gas, num_rows);
334 trace.reserve_column(C::precomputed_addressing_gas, num_rows);
335
336 for (uint32_t i = 0; i < num_rows; i++) {
337 trace.set(C::precomputed_sel_addressing_gas, i, 1);
338 trace.set(C::precomputed_addressing_gas, i, compute_addressing_gas(static_cast<uint16_t>(i)));
339 }
340}
341
343{
344 using C = Column;
345
346 for (const auto& [phase_value, spec] : get_tx_phase_spec_map()) {
347
348 const uint32_t row = static_cast<uint32_t>(phase_value);
349 // Populate all columns that are part of the #[READ_PHASE_SPEC] lookup in tx.pil.
351 { C::precomputed_sel_phase, 1 },
352 { C::precomputed_is_public_call_request, spec.is_public_call_request ? 1 : 0 },
353 { C::precomputed_is_teardown, spec.is_teardown ? 1 : 0 },
354 { C::precomputed_is_collect_fee, spec.is_collect_fee ? 1 : 0 },
355 { C::precomputed_is_tree_padding, spec.is_tree_padding ? 1 : 0 },
356 { C::precomputed_is_cleanup, spec.is_cleanup ? 1 : 0 },
357 { C::precomputed_is_revertible, spec.is_revertible ? 1 : 0 },
358 { C::precomputed_read_pi_start_offset, spec.read_pi_start_offset },
359 { C::precomputed_read_pi_length_offset, spec.read_pi_length_offset },
360 { C::precomputed_sel_non_revertible_append_note_hash, spec.non_revertible_append_note_hash ? 1 : 0 },
361 { C::precomputed_sel_non_revertible_append_nullifier, spec.non_revertible_append_nullifier ? 1 : 0 },
362 { C::precomputed_sel_non_revertible_append_l2_l1_msg, spec.non_revertible_append_l2_l1_msg ? 1 : 0 },
363 { C::precomputed_sel_revertible_append_note_hash, spec.revertible_append_note_hash ? 1 : 0 },
364 { C::precomputed_sel_revertible_append_nullifier, spec.revertible_append_nullifier ? 1 : 0 },
365 { C::precomputed_sel_revertible_append_l2_l1_msg, spec.revertible_append_l2_l1_msg ? 1 : 0 },
366 { C::precomputed_next_phase_on_revert, spec.next_phase_on_revert },
367 };
368
369 trace.set(row, row_data);
370 }
371}
372
374{
375 uint32_t row = 1;
376 for (const auto& round_constant : simulation::keccak_round_constants) {
377 trace.set(row,
378 { {
379 { C::precomputed_sel_keccak, 1 },
380 { C::precomputed_keccak_round_constant, round_constant },
381 } });
382 row++;
383 }
384}
385
390{
391 constexpr uint32_t NUM_ROWS = 1 << 8;
392
393 // Start by flagging `invalid_envvar_enum` as 1 for all rows.
394 // "valid" rows will be reset manually to 0 below.
395 for (uint32_t i = 0; i < NUM_ROWS; i++) {
396 trace.set(C::precomputed_invalid_envvar_enum, i, 1);
397 }
398
399 for (uint8_t enum_value = 0; enum_value <= static_cast<uint8_t>(EnvironmentVariable::MAX); enum_value++) {
400 const auto& envvar_spec = GetEnvVarSpec::get_table(enum_value);
401 trace.set(static_cast<uint32_t>(enum_value),
402 { {
403 { C::precomputed_invalid_envvar_enum, 0 }, // Reset the invalid enum flag for valid rows
404 { C::precomputed_sel_envvar_pi_lookup_col0, envvar_spec.envvar_pi_lookup_col0 },
405 { C::precomputed_sel_envvar_pi_lookup_col1, envvar_spec.envvar_pi_lookup_col1 },
406 { C::precomputed_envvar_pi_row_idx, envvar_spec.envvar_pi_row_idx },
407 { C::precomputed_is_address, envvar_spec.is_address ? 1 : 0 },
408 { C::precomputed_is_sender, envvar_spec.is_sender ? 1 : 0 },
409 { C::precomputed_is_transactionfee, envvar_spec.is_transactionfee ? 1 : 0 },
410 { C::precomputed_is_isstaticcall, envvar_spec.is_isstaticcall ? 1 : 0 },
411 { C::precomputed_is_l2gasleft, envvar_spec.is_l2gasleft ? 1 : 0 },
412 { C::precomputed_is_dagasleft, envvar_spec.is_dagasleft ? 1 : 0 },
413 { C::precomputed_out_tag, envvar_spec.out_tag },
414 } });
415 }
416}
417
422{
423 // Set valid rows based on the precomputed table
424 for (uint8_t enum_value = 0; enum_value <= static_cast<uint8_t>(ContractInstanceMember::MAX); enum_value++) {
425 const auto& spec = GetContractInstanceSpec::get_table(enum_value);
426
427 trace.set(static_cast<uint32_t>(enum_value),
428 { {
429 { C::precomputed_is_valid_member_enum, spec.is_valid_member_enum ? 1 : 0 },
430 { C::precomputed_is_deployer, spec.is_deployer ? 1 : 0 },
431 { C::precomputed_is_class_id, spec.is_class_id ? 1 : 0 },
432 { C::precomputed_is_init_hash, spec.is_init_hash ? 1 : 0 },
433 } });
434 }
435}
436
437} // namespace bb::avm2::tracegen
#define AVM_MAX_OPERANDS
#define AVM_MAX_REGISTERS
static Table get_table(uint8_t envvar)
void process_sha256_round_constants(TraceContainer &trace)
void process_to_radix_p_decompositions(TraceContainer &trace)
void process_wire_instruction_spec(TraceContainer &trace)
void process_keccak_round_constants(TraceContainer &trace)
void process_to_radix_safe_limbs(TraceContainer &trace)
void process_memory_tag_range(TraceContainer &trace)
void process_exec_instruction_spec(TraceContainer &trace)
void process_misc(TraceContainer &trace, const uint32_t num_rows=MAX_AVM_TRACE_SIZE)
void process_get_env_var_table(TraceContainer &trace)
void process_get_contract_instance_table(TraceContainer &trace)
TestTraceContainer trace
FF a
FF b
constexpr std::array< uint64_t, 24 > keccak_round_constants
constexpr uint32_t round_constants[64]
const std::unordered_map< TransactionPhase, TxPhaseSpec > & get_tx_phase_spec_map()
const std::unordered_map< ExecutionOpCode, SubtraceInfo > & get_subtrace_info_map()
FF get_subtrace_id(SubtraceSel subtrace_sel)
Get the subtrace ID for a given subtrace enum.
const std::array< std::vector< uint8_t >, 257 > & get_p_limbs_per_radix()
Definition to_radix.cpp:48
uint32_t compute_addressing_gas(uint16_t indirect_flag)
Definition gas.cpp:10
const std::unordered_map< WireOpCode, WireInstructionSpec > & get_wire_instruction_spec()
const std::unordered_map< ExecutionOpCode, ExecInstructionSpec > & get_exec_instruction_spec()
uint8_t get_tag_bits(ValueTag tag)
uint256_t get_tag_max_value(ValueTag tag)
constexpr size_t NUM_OP_DC_SELECTORS
ValueTag MemoryTag
AvmFlavorSettings::FF FF
Definition field.hpp:10
uint8_t get_tag_bytes(ValueTag tag)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13