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 <cstdint>
5#include <vector>
6
7#include "barretenberg/aztec/aztec_constants.hpp"
19
20namespace bb::avm2::tracegen {
21
22using C = Column;
23
30void PrecomputedTraceBuilder::process_misc(TraceContainer& trace, const uint32_t num_rows)
31{
32 // First row.
33 trace.set(C::precomputed_first_row, 0, 1);
34
35 // Note: precomputed_zero is intentionally not populated.
36 // The trace container defaults unset values to FF(0), which is the desired value.
37
38 // Idx
39 trace.reserve_column(C::precomputed_idx, num_rows);
40 for (uint32_t i = 0; i < num_rows; i++) {
41 trace.set(C::precomputed_idx, i, i);
42 }
43}
44
51{
52 // 256 per input (a and b), and 3 different bitwise ops
53 constexpr auto num_rows = 256 * 256;
54 static_assert(num_rows <= PRECOMPUTED_TRACE_SIZE);
55 static_assert(num_rows == (1U << 16),
56 "bitwise table must span 2^16 rows for sel_range_16 to be a sound outer selector for "
57 "the committed sel_bitwise granular selector (see precomputed.pil)");
58 trace.reserve_column(C::precomputed_bitwise_input_a, num_rows);
59 trace.reserve_column(C::precomputed_bitwise_input_b, num_rows);
60 trace.reserve_column(C::precomputed_bitwise_output_and, num_rows);
61 trace.reserve_column(C::precomputed_bitwise_output_or, num_rows);
62 trace.reserve_column(C::precomputed_bitwise_output_xor, num_rows);
63
64 // row # is derived as:
65 // - input_b: bits 0...7 (0 being LSB)
66 // - input_a: bits 8...15
67 for (uint32_t a = 0; a < 256; a++) {
68 for (uint32_t b = 0; b < 256; b++) {
69 trace.set((a << 8) | b,
70 { {
71 { C::precomputed_bitwise_input_a, FF(a) },
72 { C::precomputed_bitwise_input_b, FF(b) },
73 { C::precomputed_bitwise_output_and, FF(a & b) },
74 { C::precomputed_bitwise_output_or, FF(a | b) },
75 { C::precomputed_bitwise_output_xor, FF(a ^ b) },
76 } });
77 }
78 }
79}
80
87{
88 constexpr auto num_rows = 1 << 8; // 256
89 // Set this selector high for the first 2^8 rows
90 // For these rows, idx will be 0...255
91 trace.reserve_column(C::precomputed_sel_range_8, num_rows);
92 for (uint32_t i = 0; i < num_rows; i++) {
93 trace.set(C::precomputed_sel_range_8, i, 1);
94 }
95}
96
103{
104 constexpr auto num_rows = 1 << 16; // 2^16
105 // Set this selector high for the first 2^16 rows
106 // For these rows, idx will be 0...2^16-1
107 trace.reserve_column(C::precomputed_sel_range_16, num_rows);
108 for (uint32_t i = 0; i < num_rows; i++) {
109 trace.set(C::precomputed_sel_range_16, i, 1);
110 }
111}
112
118{
119 // This corresponds to the fact that sel_range_8 is used as the dst selector for lookups involving powers of 2.
120 constexpr auto num_rows = 1 << 8; // 2^8 = 256
121 trace.reserve_column(C::precomputed_power_of_2, num_rows);
122 for (uint32_t i = 0; i < num_rows; i++) {
123 trace.set(C::precomputed_power_of_2, i, uint256_t(1) << uint256_t(i));
124 }
125}
126
132{
133 constexpr std::array<uint32_t, 64> round_constants{
134 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
135 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
136 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
137 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
138 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
139 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
140 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
141 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
142 };
143 constexpr auto num_rows = round_constants.size();
144 trace.reserve_column(C::precomputed_sha256_compression_round_constant, num_rows);
145 trace.reserve_column(C::precomputed_sel_sha256_compression, num_rows);
146 for (uint32_t i = 0; i < num_rows; i++) {
147 trace.set(i,
148 { { { C::precomputed_sel_sha256_compression, 1 },
149 { C::precomputed_sha256_compression_round_constant, round_constants[i] } } });
150 }
151}
152
158{
160
161 constexpr uint32_t NUM_TAGS = static_cast<uint32_t>(MemoryTag::MAX) + 1;
162 for (uint32_t i = 0; i < NUM_TAGS; i++) {
163 const auto tag = static_cast<MemoryTag>(i);
164 trace.set(i, // Column number corresponds to MemoryTag enum value.
165 { {
166 { C::precomputed_sel_tag_parameters, 1 },
167 { C::precomputed_tag_byte_length, get_tag_bytes(tag) },
168 { C::precomputed_tag_max_bits, get_tag_bits(tag) },
169 { C::precomputed_tag_max_value, get_tag_max_value(tag) },
170 } });
171 }
172}
173
182{
183 const std::array<Column, NUM_OP_DC_SELECTORS> sel_op_dc_columns = {
184 C::precomputed_sel_op_dc_0, C::precomputed_sel_op_dc_1, C::precomputed_sel_op_dc_2,
185 C::precomputed_sel_op_dc_3, C::precomputed_sel_op_dc_4, C::precomputed_sel_op_dc_5,
186 C::precomputed_sel_op_dc_6, C::precomputed_sel_op_dc_7, C::precomputed_sel_op_dc_8,
187 C::precomputed_sel_op_dc_9, C::precomputed_sel_op_dc_10, C::precomputed_sel_op_dc_11,
188 C::precomputed_sel_op_dc_12, C::precomputed_sel_op_dc_13, C::precomputed_sel_op_dc_14,
189 C::precomputed_sel_op_dc_15,
190 };
191
192 // First set the selector for this table lookup.
193 constexpr uint32_t num_rows = 1 << 8; // 256
194 constexpr uint32_t num_opcodes = static_cast<uint32_t>(WireOpCode::LAST_OPCODE_SENTINEL);
195 trace.reserve_column(C::precomputed_opcode_out_of_range, num_rows - num_opcodes);
196 for (uint32_t i = num_opcodes; i < num_rows; i++) {
197 trace.set(C::precomputed_opcode_out_of_range, i, 1);
198 }
199
200 for (size_t i = 0; i < NUM_OP_DC_SELECTORS; i++) {
201 trace.reserve_column(sel_op_dc_columns.at(i), num_opcodes);
202 }
203 trace.reserve_column(C::precomputed_exec_opcode, num_opcodes);
204 trace.reserve_column(C::precomputed_instr_size, num_opcodes);
205
206 // Fill the lookup tables with the operand decomposition selectors.
207 for (const auto& [wire_opcode, wire_instruction_spec] : get_wire_instruction_spec()) {
208 for (size_t i = 0; i < NUM_OP_DC_SELECTORS; i++) {
209 trace.set(sel_op_dc_columns.at(i),
210 static_cast<uint32_t>(wire_opcode),
211 wire_instruction_spec.op_dc_selectors.at(i));
212 }
213 trace.set(C::precomputed_exec_opcode,
214 static_cast<uint32_t>(wire_opcode),
215 static_cast<uint32_t>(wire_instruction_spec.exec_opcode));
216 trace.set(C::precomputed_instr_size, static_cast<uint32_t>(wire_opcode), wire_instruction_spec.size_in_bytes);
217
218 if (wire_instruction_spec.tag_operand_idx.has_value()) {
219 trace.set(C::precomputed_sel_has_tag, static_cast<uint32_t>(wire_opcode), 1);
220
221 if (wire_instruction_spec.tag_operand_idx.value() == 2) {
222 trace.set(C::precomputed_sel_tag_is_op2, static_cast<uint32_t>(wire_opcode), 1);
223 }
224 }
225 }
226}
227
236{
237 constexpr std::array<Column, AVM_MAX_REGISTERS> MEM_OP_REG_COLUMNS = {
238 Column::precomputed_sel_mem_op_reg_0_,
239 Column::precomputed_sel_mem_op_reg_1_,
240 Column::precomputed_sel_mem_op_reg_2_,
241 Column::precomputed_sel_mem_op_reg_3_,
242 };
243 constexpr std::array<Column, AVM_MAX_REGISTERS> RW_COLUMNS = {
244 Column::precomputed_rw_reg_0_,
245 Column::precomputed_rw_reg_1_,
246 Column::precomputed_rw_reg_2_,
247 Column::precomputed_rw_reg_3_,
248 };
249 constexpr std::array<Column, AVM_MAX_REGISTERS> DO_TAG_CHECK_COLUMNS = {
250 Column::precomputed_sel_tag_check_reg_0_,
251 Column::precomputed_sel_tag_check_reg_1_,
252 Column::precomputed_sel_tag_check_reg_2_,
253 Column::precomputed_sel_tag_check_reg_3_,
254 };
255 constexpr std::array<Column, AVM_MAX_REGISTERS> EXPECTED_TAG_COLUMNS = {
256 Column::precomputed_expected_tag_reg_0_,
257 Column::precomputed_expected_tag_reg_1_,
258 Column::precomputed_expected_tag_reg_2_,
259 Column::precomputed_expected_tag_reg_3_,
260 };
261
262 constexpr std::array<Column, AVM_MAX_OPERANDS> SEL_OP_IS_ADDRESS_COLUMNS = {
263 Column::precomputed_sel_op_is_address_0_,
264 Column::precomputed_sel_op_is_address_1_,
265 Column::precomputed_sel_op_is_address_2_,
266 Column::precomputed_sel_op_is_address_3_,
267 Column::precomputed_sel_op_is_address_4_
268 };
269
270 for (const auto& [exec_opcode, exec_instruction_spec] : get_exec_instruction_spec()) {
271 // Basic information.
272 trace.set(static_cast<uint32_t>(exec_opcode),
273 { {
274 { C::precomputed_sel_exec_spec, 1 },
275 { C::precomputed_exec_opcode_opcode_gas, exec_instruction_spec.gas_cost.opcode_gas },
276 { C::precomputed_exec_opcode_base_da_gas, exec_instruction_spec.gas_cost.base_da },
277 { C::precomputed_exec_opcode_dynamic_l2_gas, exec_instruction_spec.gas_cost.dyn_l2 },
278 { C::precomputed_exec_opcode_dynamic_da_gas, exec_instruction_spec.gas_cost.dyn_da },
279 } });
280
281 // Register information.
282 const auto& register_info = exec_instruction_spec.register_info;
283 for (size_t i = 0; i < AVM_MAX_REGISTERS; i++) {
284 trace.set(MEM_OP_REG_COLUMNS.at(i), static_cast<uint32_t>(exec_opcode), register_info.is_active(i) ? 1 : 0);
285 trace.set(RW_COLUMNS.at(i), static_cast<uint32_t>(exec_opcode), register_info.is_write(i) ? 1 : 0);
286 trace.set(DO_TAG_CHECK_COLUMNS.at(i),
287 static_cast<uint32_t>(exec_opcode),
288 register_info.need_tag_check(i) ? 1 : 0);
289 trace.set(EXPECTED_TAG_COLUMNS.at(i),
290 static_cast<uint32_t>(exec_opcode),
291 static_cast<uint32_t>(register_info.expected_tag(i).value_or(static_cast<ValueTag>(0))));
292 }
293
294 // Whether an operand is an address
295 for (size_t i = 0; i < AVM_MAX_OPERANDS; i++) {
296 trace.set(SEL_OP_IS_ADDRESS_COLUMNS.at(i),
297 static_cast<uint32_t>(exec_opcode),
298 i < exec_instruction_spec.num_addresses ? 1 : 0);
299 }
300
301 // Gadget / Subtrace Selectors / Decomposable selectors
302 auto dispatch_to_subtrace = get_subtrace_info_map().at(exec_opcode);
303 trace.set(static_cast<uint32_t>(exec_opcode),
304 { { { C::precomputed_subtrace_id, get_subtrace_id(dispatch_to_subtrace.subtrace_selector) },
305 { C::precomputed_subtrace_operation_id, dispatch_to_subtrace.subtrace_operation_id },
306 { C::precomputed_dyn_gas_id, exec_instruction_spec.dyn_gas_id } } });
307 }
308}
309
317{
318 const auto& p_limbs_per_radix = get_p_limbs_per_radix();
319
320 trace.reserve_column(C::precomputed_sel_to_radix_p_limb_counts, p_limbs_per_radix.size());
321 trace.reserve_column(C::precomputed_to_radix_safe_limbs, p_limbs_per_radix.size());
322
323 for (size_t i = 0; i < p_limbs_per_radix.size(); ++i) {
324 size_t decomposition_len = p_limbs_per_radix[i].size();
325 trace.set(C::precomputed_sel_to_radix_p_limb_counts, static_cast<uint32_t>(i), 1);
326 // Use 0 as fallback when decomposition_len == 0 (i.e. p_limbs_per_radix[0] and p_limbs_per_radix[1])
327 trace.set(C::precomputed_to_radix_safe_limbs,
328 static_cast<uint32_t>(i),
329 decomposition_len > 0 ? decomposition_len - 1 : 0);
330 trace.set(C::precomputed_to_radix_num_limbs_for_p, static_cast<uint32_t>(i), decomposition_len);
331 }
332}
333
342{
343 const auto& p_limbs_per_radix = get_p_limbs_per_radix();
344
345 uint32_t row = 0;
346 for (size_t i = 0; i < p_limbs_per_radix.size(); ++i) {
347 size_t decomposition_len = p_limbs_per_radix[i].size();
348 for (size_t j = 0; j < decomposition_len; ++j) {
349 trace.set(C::precomputed_sel_p_decomposition, row, 1);
350 trace.set(C::precomputed_p_decomposition_radix, row, i);
351 trace.set(C::precomputed_p_decomposition_limb_index, row, j);
352 trace.set(C::precomputed_p_decomposition_limb, row, p_limbs_per_radix[i][j]);
353 row++;
354 }
355 }
356}
357
364{
365 constexpr uint32_t num_rows = 1 << 8; // 256
366
367 for (uint32_t i = static_cast<uint32_t>(MemoryTag::MAX) + 1; i < num_rows; i++) {
368 trace.set(C::precomputed_sel_mem_tag_out_of_range, i, 1);
369 }
370}
371
378{
379 constexpr uint32_t num_rows = 1 << 16; // 65536
380
381 static_assert(num_rows == (1U << 16),
382 "addressing-gas table must span 2^16 rows for sel_range_16 to be a sound outer selector for "
383 "the committed sel_addressing_gas granular selector (see precomputed.pil)");
384 // sel_addressing_gas is committed and populated by the lookup builder (toggled on used rows), not here.
385 trace.reserve_column(C::precomputed_addressing_gas, num_rows);
386
387 for (uint32_t i = 0; i < num_rows; i++) {
388 trace.set(C::precomputed_addressing_gas, i, compute_addressing_gas(static_cast<uint16_t>(i)));
389 }
390}
391
400{
401 for (const auto& [phase_value, spec] : get_tx_phase_spec_map()) {
402
403 const uint32_t row = static_cast<uint32_t>(phase_value);
404 // Populate all columns that are part of the #[READ_PHASE_SPEC] lookup in tx.pil.
406 { C::precomputed_sel_phase, 1 },
407 { C::precomputed_is_public_call_request, spec.is_public_call_request ? 1 : 0 },
408 { C::precomputed_is_teardown, spec.is_teardown ? 1 : 0 },
409 { C::precomputed_is_collect_fee, spec.is_collect_fee ? 1 : 0 },
410 { C::precomputed_is_tree_padding, spec.is_tree_padding ? 1 : 0 },
411 { C::precomputed_is_cleanup, spec.is_cleanup ? 1 : 0 },
412 { C::precomputed_is_revertible, spec.is_revertible ? 1 : 0 },
413 { C::precomputed_read_pi_start_offset, spec.read_pi_start_offset },
414 { C::precomputed_read_pi_length_offset, spec.read_pi_length_offset },
415 { C::precomputed_sel_append_note_hash, spec.append_note_hash ? 1 : 0 },
416 { C::precomputed_sel_append_nullifier, spec.append_nullifier ? 1 : 0 },
417 { C::precomputed_sel_append_l2_l1_msg, spec.append_l2_l1_msg ? 1 : 0 },
418 { C::precomputed_next_phase_on_revert, spec.next_phase_on_revert },
419 };
420
421 trace.set(row, row_data);
422 }
423}
424
430{
431 uint32_t row = 1;
432 for (const auto& round_constant : simulation::keccak_round_constants) {
433 trace.set(row,
434 { {
435 { C::precomputed_sel_keccak, 1 },
436 { C::precomputed_keccak_round_constant, round_constant },
437 } });
438 row++;
439 }
440}
441
450{
451 constexpr uint32_t NUM_ROWS = 1 << 8;
452
453 constexpr uint8_t NUM_VALID_ENV_VARS = static_cast<uint8_t>(EnvironmentVariable::MAX) + 1;
454
455 for (uint8_t enum_value = 0; enum_value < NUM_VALID_ENV_VARS; enum_value++) {
456 const auto& envvar_spec = GetEnvVarSpec::get_table(enum_value);
457 trace.set(static_cast<uint32_t>(enum_value),
458 { {
459 { C::precomputed_sel_envvar_pi_lookup_col0, envvar_spec.envvar_pi_lookup_col0 },
460 { C::precomputed_sel_envvar_pi_lookup_col1, envvar_spec.envvar_pi_lookup_col1 },
461 { C::precomputed_envvar_pi_row_idx, envvar_spec.envvar_pi_row_idx },
462 { C::precomputed_is_address, envvar_spec.is_address ? 1 : 0 },
463 { C::precomputed_is_sender, envvar_spec.is_sender ? 1 : 0 },
464 { C::precomputed_is_transactionfee, envvar_spec.is_transactionfee ? 1 : 0 },
465 { C::precomputed_is_isstaticcall, envvar_spec.is_isstaticcall ? 1 : 0 },
466 { C::precomputed_is_l2gasleft, envvar_spec.is_l2gasleft ? 1 : 0 },
467 { C::precomputed_is_dagasleft, envvar_spec.is_dagasleft ? 1 : 0 },
468 { C::precomputed_out_tag, envvar_spec.out_tag },
469 } });
470 }
471
472 // Flag invalid enum values (those beyond the valid range) as out-of-range.
473 // Valid rows default to 0 (the trace container's default).
474 for (uint32_t i = NUM_VALID_ENV_VARS; i < NUM_ROWS; i++) {
475 trace.set(C::precomputed_invalid_envvar_enum, i, 1);
476 }
477}
478
486{
487 // Set valid rows based on the precomputed table
488 for (uint8_t enum_value = 0; enum_value <= static_cast<uint8_t>(ContractInstanceMember::MAX); enum_value++) {
489 const auto& spec = GetContractInstanceSpec::get_table(enum_value);
490
491 trace.set(static_cast<uint32_t>(enum_value),
492 { {
493 { C::precomputed_is_valid_member_enum, spec.is_valid_member_enum ? 1 : 0 },
494 { C::precomputed_is_deployer, spec.is_deployer ? 1 : 0 },
495 { C::precomputed_is_class_id, spec.is_class_id ? 1 : 0 },
496 { C::precomputed_is_init_hash, spec.is_init_hash ? 1 : 0 },
497 { C::precomputed_is_immutables_hash, spec.is_immutables_hash ? 1 : 0 },
498 } });
499 }
500}
501
502} // namespace bb::avm2::tracegen
static Table get_table(uint8_t member_enum)
Look up the precomputed table entry for a given member enum value.
static Table get_table(uint8_t envvar)
void process_sha256_round_constants(TraceContainer &trace)
Populate the 64 SHA-256 round constants (K_0 .. K_63) and their selector. The sel_sha256_compression ...
void process_to_radix_p_decompositions(TraceContainer &trace)
Populate the TORADIXBE p-decomposition table.
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_bitwise(TraceContainer &trace)
Populate the 8-bit bitwise lookup table (AND, OR, XOR).
void process_keccak_round_constants(TraceContainer &trace)
Populate the 24 Keccak-f[1600] round constants and their selector. Row 0 is intentionally left empty ...
void process_to_radix_safe_limbs(TraceContainer &trace)
Populate the TORADIXBE safe-limbs table (one row per radix 0..255).
void process_memory_tag_range(TraceContainer &trace)
Populate the memory tag out-of-range selector.
void process_exec_instruction_spec(TraceContainer &trace)
Populate the execution-level instruction specification table.
void process_get_env_var_table(TraceContainer &trace)
Populate the GETENVVAR lookup table.
void process_power_of_2(TraceContainer &trace)
Generate a column where row i holds 2^i, for i in [0, 255], for the values of 254 and 255 the values ...
void process_get_contract_instance_table(TraceContainer &trace)
Populate the GETCONTRACTINSTANCE lookup table.
void process_phase_table(TraceContainer &trace)
Populate the transaction phase specification table.
void process_addressing_gas(TraceContainer &trace)
Populate the addressing-mode gas lookup table (65536 rows).
void process_sel_range_16(TraceContainer &trace)
Generate a selector column that activates the first 2^16 (65536) rows.
void process_tag_parameters(TraceContainer &trace)
Populate the memory tag parameters table (byte length, max bits, max value per tag).
void process_sel_range_8(TraceContainer &trace)
Generate a selector column that activates the first 2^8 (256) rows.
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()
constexpr uint32_t PRECOMPUTED_TRACE_SIZE
FF get_subtrace_id(SubtraceSel subtrace_sel)
Get the subtrace ID for a given subtrace enum.
AvmFlavorSettings::FF FF
Definition field.hpp:10
const std::unordered_map< WireOpCode, WireInstructionSpec > & get_wire_instruction_spec()
const std::array< std::vector< uint8_t >, NUM_RADIXES > & get_p_limbs_per_radix()
Gets the p limbs per radix array. Each element is a vector containing the little endian decomposition...
Definition to_radix.cpp:60
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
uint16_t compute_addressing_gas(uint16_t addressing_mode)
Computes the gas cost for addressing.
Definition gas.cpp:16
ValueTag MemoryTag
uint8_t get_tag_bytes(ValueTag tag)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13