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 BB_ASSERT(false, "This should not happen");
66 __builtin_unreachable();
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 trace.set(C::precomputed_sel_to_radix_p_limb_counts, static_cast<uint32_t>(i), 1);
297 // Use 0 as fallback when decomposition_len == 0 (i.e. p_limbs_per_radix[0] and p_limbs_per_radix[1])
298 trace.set(C::precomputed_to_radix_safe_limbs,
299 static_cast<uint32_t>(i),
300 decomposition_len > 0 ? decomposition_len - 1 : 0);
301 trace.set(C::precomputed_to_radix_num_limbs_for_p, static_cast<uint32_t>(i), decomposition_len);
302 }
303}
304
306{
307 const auto& p_limbs_per_radix = get_p_limbs_per_radix();
308
309 uint32_t row = 0;
310 for (size_t i = 0; i < p_limbs_per_radix.size(); ++i) {
311 size_t decomposition_len = p_limbs_per_radix[i].size();
312 for (size_t j = 0; j < decomposition_len; ++j) {
313 trace.set(C::precomputed_sel_p_decomposition, row, 1);
314 trace.set(C::precomputed_p_decomposition_radix, row, i);
315 trace.set(C::precomputed_p_decomposition_limb_index, row, j);
316 trace.set(C::precomputed_p_decomposition_limb, row, p_limbs_per_radix[i][j]);
317 row++;
318 }
319 }
320}
321
323{
324 constexpr uint32_t num_rows = 1 << 8; // 256
325
326 for (uint32_t i = static_cast<uint32_t>(MemoryTag::MAX) + 1; i < num_rows; i++) {
327 trace.set(C::precomputed_sel_mem_tag_out_of_range, i, 1);
328 }
329}
330
332{
333 constexpr uint32_t num_rows = 1 << 16; // 65536
334 trace.reserve_column(C::precomputed_sel_addressing_gas, num_rows);
335 trace.reserve_column(C::precomputed_addressing_gas, num_rows);
336
337 for (uint32_t i = 0; i < num_rows; i++) {
338 trace.set(C::precomputed_sel_addressing_gas, i, 1);
339 trace.set(C::precomputed_addressing_gas, i, compute_addressing_gas(static_cast<uint16_t>(i)));
340 }
341}
342
344{
345 using C = Column;
346
347 for (const auto& [phase_value, spec] : get_tx_phase_spec_map()) {
348
349 const uint32_t row = static_cast<uint32_t>(phase_value);
350 // Populate all columns that are part of the #[READ_PHASE_SPEC] lookup in tx.pil.
352 { C::precomputed_sel_phase, 1 },
353 { C::precomputed_is_public_call_request, spec.is_public_call_request ? 1 : 0 },
354 { C::precomputed_is_teardown, spec.is_teardown ? 1 : 0 },
355 { C::precomputed_is_collect_fee, spec.is_collect_fee ? 1 : 0 },
356 { C::precomputed_is_tree_padding, spec.is_tree_padding ? 1 : 0 },
357 { C::precomputed_is_cleanup, spec.is_cleanup ? 1 : 0 },
358 { C::precomputed_is_revertible, spec.is_revertible ? 1 : 0 },
359 { C::precomputed_read_pi_start_offset, spec.read_pi_start_offset },
360 { C::precomputed_read_pi_length_offset, spec.read_pi_length_offset },
361 { C::precomputed_sel_non_revertible_append_note_hash, spec.non_revertible_append_note_hash ? 1 : 0 },
362 { C::precomputed_sel_non_revertible_append_nullifier, spec.non_revertible_append_nullifier ? 1 : 0 },
363 { C::precomputed_sel_non_revertible_append_l2_l1_msg, spec.non_revertible_append_l2_l1_msg ? 1 : 0 },
364 { C::precomputed_sel_revertible_append_note_hash, spec.revertible_append_note_hash ? 1 : 0 },
365 { C::precomputed_sel_revertible_append_nullifier, spec.revertible_append_nullifier ? 1 : 0 },
366 { C::precomputed_sel_revertible_append_l2_l1_msg, spec.revertible_append_l2_l1_msg ? 1 : 0 },
367 { C::precomputed_next_phase_on_revert, spec.next_phase_on_revert },
368 };
369
370 trace.set(row, row_data);
371 }
372}
373
375{
376 uint32_t row = 1;
377 for (const auto& round_constant : simulation::keccak_round_constants) {
378 trace.set(row,
379 { {
380 { C::precomputed_sel_keccak, 1 },
381 { C::precomputed_keccak_round_constant, round_constant },
382 } });
383 row++;
384 }
385}
386
391{
392 constexpr uint32_t NUM_ROWS = 1 << 8;
393
394 // Start by flagging `invalid_envvar_enum` as 1 for all rows.
395 // "valid" rows will be reset manually to 0 below.
396 for (uint32_t i = 0; i < NUM_ROWS; i++) {
397 trace.set(C::precomputed_invalid_envvar_enum, i, 1);
398 }
399
400 for (uint8_t enum_value = 0; enum_value <= static_cast<uint8_t>(EnvironmentVariable::MAX); enum_value++) {
401 const auto& envvar_spec = GetEnvVarSpec::get_table(enum_value);
402 trace.set(static_cast<uint32_t>(enum_value),
403 { {
404 { C::precomputed_invalid_envvar_enum, 0 }, // Reset the invalid enum flag for valid rows
405 { C::precomputed_sel_envvar_pi_lookup_col0, envvar_spec.envvar_pi_lookup_col0 },
406 { C::precomputed_sel_envvar_pi_lookup_col1, envvar_spec.envvar_pi_lookup_col1 },
407 { C::precomputed_envvar_pi_row_idx, envvar_spec.envvar_pi_row_idx },
408 { C::precomputed_is_address, envvar_spec.is_address ? 1 : 0 },
409 { C::precomputed_is_sender, envvar_spec.is_sender ? 1 : 0 },
410 { C::precomputed_is_transactionfee, envvar_spec.is_transactionfee ? 1 : 0 },
411 { C::precomputed_is_isstaticcall, envvar_spec.is_isstaticcall ? 1 : 0 },
412 { C::precomputed_is_l2gasleft, envvar_spec.is_l2gasleft ? 1 : 0 },
413 { C::precomputed_is_dagasleft, envvar_spec.is_dagasleft ? 1 : 0 },
414 { C::precomputed_out_tag, envvar_spec.out_tag },
415 } });
416 }
417}
418
423{
424 // Set valid rows based on the precomputed table
425 for (uint8_t enum_value = 0; enum_value <= static_cast<uint8_t>(ContractInstanceMember::MAX); enum_value++) {
426 const auto& spec = GetContractInstanceSpec::get_table(enum_value);
427
428 trace.set(static_cast<uint32_t>(enum_value),
429 { {
430 { C::precomputed_is_valid_member_enum, spec.is_valid_member_enum ? 1 : 0 },
431 { C::precomputed_is_deployer, spec.is_deployer ? 1 : 0 },
432 { C::precomputed_is_class_id, spec.is_class_id ? 1 : 0 },
433 { C::precomputed_is_init_hash, spec.is_init_hash ? 1 : 0 },
434 } });
435 }
436}
437
438} // namespace bb::avm2::tracegen
#define BB_ASSERT(expression,...)
Definition assert.hpp:80
#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
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
uint16_t compute_addressing_gas(uint16_t addressing_mode)
Computes the gas cost for addressing.
Definition gas.cpp:16
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