Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
to_radix_trace.cpp
Go to the documentation of this file.
2
3#include <cassert>
4#include <memory>
5
15
16namespace bb::avm2::tracegen {
17
19 TraceContainer& trace)
20{
21 using C = Column;
22
23 const auto& p_limbs_per_radix = get_p_limbs_per_radix();
24
25 uint32_t row = 1; // We start from row 1 because this trace contains shifted columns.
26 for (const auto& event : events) {
27 FF value = event.value;
28 uint32_t radix = event.radix;
29 size_t radix_index = static_cast<size_t>(radix);
30 uint32_t safe_limbs = static_cast<uint32_t>(p_limbs_per_radix[radix_index].size()) - 1;
31
32 FF acc = 0;
33 FF exponent = 1;
34 bool found = false;
35 bool acc_under_p = false;
36
37 for (uint32_t i = 0; i < event.limbs.size(); ++i) {
38 bool is_padding = i > safe_limbs;
39 uint8_t limb = event.limbs[i];
40 uint8_t p_limb = is_padding ? 0 : p_limbs_per_radix[radix_index][static_cast<size_t>(i)];
41
42 if (limb != p_limb) {
43 acc_under_p = limb < p_limb;
44 }
45 FF limb_p_diff = limb == p_limb ? 0 : limb > p_limb ? limb - p_limb - 1 : p_limb - limb - 1;
46
47 bool is_unsafe_limb = i == safe_limbs;
48 FF safety_diff = FF(i) - FF(safe_limbs);
49
50 acc += exponent * limb;
51
52 FF rem = value - acc;
53 found = rem == 0;
54
55 bool end = i == (event.limbs.size() - 1);
56
57 trace.set(row,
58 { {
59 { C::to_radix_sel, 1 },
60 { C::to_radix_value, value },
61 { C::to_radix_radix, radix },
62 { C::to_radix_limb_index, i },
63 { C::to_radix_limb, limb },
64 { C::to_radix_start, i == 0 },
65 { C::to_radix_end, end },
66 { C::to_radix_not_end, !end },
67 { C::to_radix_exponent, exponent },
68 { C::to_radix_not_padding_limb, !is_padding },
69 { C::to_radix_acc, acc },
70 { C::to_radix_found, found },
71 { C::to_radix_limb_radix_diff, radix - 1 - limb },
72 { C::to_radix_rem_inverse, rem }, // Will be inverted in batch later
73 { C::to_radix_safe_limbs, safe_limbs },
74 { C::to_radix_is_unsafe_limb, is_unsafe_limb },
75 { C::to_radix_safety_diff_inverse, safety_diff }, // Will be inverted in batch later
76 { C::to_radix_p_limb, p_limb },
77 { C::to_radix_acc_under_p, acc_under_p },
78 { C::to_radix_limb_lt_p, limb < p_limb },
79 { C::to_radix_limb_eq_p, limb == p_limb },
80 { C::to_radix_limb_p_diff, limb_p_diff },
81 } });
82
83 row++;
84 if (is_unsafe_limb) {
85 exponent = 0;
86 }
87 exponent *= radix;
88 }
89 }
90
91 // Batch invert the columns.
92 trace.invert_columns({ { C::to_radix_safety_diff_inverse, C::to_radix_rem_inverse } });
93}
94
97{
98 using C = Column;
99
100 uint32_t row = 1; // We start from row 1 because this trace contains shifted columns.
101 for (const auto& event : events) {
102 // Helpers
103 uint8_t num_limbs_is_zero = event.num_limbs == 0 ? 1 : 0;
104 FF num_limbs_inv = event.num_limbs == 0 ? FF(0) : FF(event.num_limbs); // Will be inverted in batch later
105 uint8_t value_is_zero = event.value == FF(0) ? 1 : 0;
106 FF value_inv = event.value == FF(0) ? FF(0) : event.value; // Will be inverted in batch later
107
108 // Error Handling - Out of Memory Access
109 uint64_t dst_addr = static_cast<uint64_t>(event.dst_addr);
110 uint64_t write_addr_upper_bound = dst_addr + event.num_limbs;
111 bool write_out_of_range = write_addr_upper_bound > AVM_MEMORY_SIZE;
112
113 // Error Handling - Radix Range
114 bool invalid_radix = (event.radix < 2 || event.radix > 256);
115 bool invalid_bitwise_radix = event.is_output_bits && event.radix != 2;
116
117 // Error Handling - Num Limbs and Value
118 bool invalid_num_limbs = event.num_limbs == 0 && !(event.value == FF(0));
119
120 // Common values for the first row
121 trace.set(row,
122 { {
123 { C::to_radix_mem_sel, 1 },
124 { C::to_radix_mem_start, 1 },
125 // Unconditional Inputs
126 { C::to_radix_mem_execution_clk, event.execution_clk },
127 { C::to_radix_mem_space_id, event.space_id },
128 { C::to_radix_mem_dst_addr, dst_addr },
129 { C::to_radix_mem_value_to_decompose, event.value },
130 { C::to_radix_mem_radix, event.radix },
131 { C::to_radix_mem_num_limbs, event.num_limbs },
132 { C::to_radix_mem_is_output_bits, event.is_output_bits ? 1 : 0 },
133 // Helpers
134 { C::to_radix_mem_max_mem_size, static_cast<uint64_t>(AVM_MEMORY_SIZE) },
135 { C::to_radix_mem_write_addr_upper_bound, write_addr_upper_bound },
136 { C::to_radix_mem_two, 2 },
137 { C::to_radix_mem_two_five_six, 256 },
138 { C::to_radix_mem_sel_num_limbs_is_zero, num_limbs_is_zero },
139 { C::to_radix_mem_num_limbs_inv, num_limbs_inv },
140 { C::to_radix_mem_sel_value_is_zero, value_is_zero },
141 { C::to_radix_mem_value_inv, value_inv },
142 } });
143
144 // Input validation errors
145 if (write_out_of_range || invalid_radix || invalid_bitwise_radix || invalid_num_limbs) {
146 trace.set(row,
147 { {
148 { C::to_radix_mem_last, 1 },
149 { C::to_radix_mem_input_validation_error, 1 },
150 { C::to_radix_mem_err, 1 },
151 { C::to_radix_mem_sel_dst_out_of_range_err, write_out_of_range },
152 { C::to_radix_mem_sel_radix_lt_2_err, event.radix < 2 },
153 { C::to_radix_mem_sel_radix_gt_256_err, event.radix > 256 },
154 { C::to_radix_mem_sel_invalid_bitwise_radix, invalid_bitwise_radix ? 1 : 0 },
155 { C::to_radix_mem_sel_invalid_num_limbs_err, invalid_num_limbs ? 1 : 0 },
156 } });
157 row++;
158 continue;
159 }
160
161 // At this point, a decomposition has happened, so we can process the limbs
162
163 // Compute found for the given decomposition
164 FF acc = 0;
165 FF exponent = 1;
166 std::vector<bool> found(event.limbs.size(), false);
167 for (size_t i = 0; i < event.limbs.size(); ++i) {
168 // Limbs are BE, we compute found in LE since the to_radix subtrace is little endian
169 size_t reverse_index = event.limbs.size() - i - 1;
170 FF limb_value = event.limbs[reverse_index].as_ff();
171 acc += exponent * limb_value;
172 exponent *= event.radix;
173 found[reverse_index] = acc == event.value;
174 }
175
176 // Num limbs = 0 short circuit
177 if (event.num_limbs == 0) {
178 trace.set(row,
179 { {
180 { C::to_radix_mem_last, 1 },
181 } });
182 row++;
183 continue;
184 }
185
186 // Truncation error (still does decomposition)
187 bool truncation_error = event.num_limbs != 0 && !found.at(0);
188
189 if (truncation_error) {
190 trace.set(row,
191 { {
192 { C::to_radix_mem_last, 1 },
193 { C::to_radix_mem_err, 1 },
194 { C::to_radix_mem_sel_truncation_error, 1 },
195 // Decomposition
196 { C::to_radix_mem_sel_should_decompose, 1 },
197 { C::to_radix_mem_limb_index_to_lookup, event.num_limbs - 1 },
198 { C::to_radix_mem_limb_value, event.limbs.at(0).as_ff() },
199 { C::to_radix_mem_value_found, 0 },
200 } });
201
202 row++;
203 continue;
204 }
205
206 uint32_t remaining_limbs = static_cast<uint32_t>(event.num_limbs);
207
208 // Base case
209 for (uint32_t i = 0; i < event.num_limbs; ++i) {
210 MemoryValue limb_value = event.limbs.at(i);
211 bool last = i == (event.num_limbs - 1);
212
213 trace.set(row,
214 { {
215 { C::to_radix_mem_sel, 1 },
216 { C::to_radix_mem_num_limbs, remaining_limbs },
217 { C::to_radix_mem_num_limbs_minus_one_inv,
218 remaining_limbs - 1 == 0 ? 0 : FF(remaining_limbs - 1) }, // Will be inverted in batch later
219 { C::to_radix_mem_last, last ? 1 : 0 },
220 // Decomposition
221 { C::to_radix_mem_sel_should_decompose, 1 },
222 { C::to_radix_mem_value_to_decompose, event.value },
223 { C::to_radix_mem_limb_index_to_lookup, remaining_limbs - 1 },
224 { C::to_radix_mem_radix, event.radix },
225 { C::to_radix_mem_limb_value, limb_value.as_ff() },
226 { C::to_radix_mem_value_found, found.at(i) ? 1 : 0 },
227 // Memory write
228 { C::to_radix_mem_sel_should_write_mem, 1 },
229 { C::to_radix_mem_execution_clk, event.execution_clk },
230 { C::to_radix_mem_space_id, event.space_id },
231 { C::to_radix_mem_dst_addr, dst_addr },
232 { C::to_radix_mem_output_tag, static_cast<uint8_t>(limb_value.get_tag()) },
233 { C::to_radix_mem_is_output_bits, event.is_output_bits ? 1 : 0 },
234 } });
235
236 remaining_limbs--; // Decrement the number of limbs
237 dst_addr++; // Increment the destination address for the next limb
238
239 row++;
240 }
241 }
242
243 // Batch invert the columns.
244 trace.invert_columns(
245 { { C::to_radix_mem_num_limbs_inv, C::to_radix_mem_value_inv, C::to_radix_mem_num_limbs_minus_one_inv } });
246}
247
251 .add<lookup_to_radix_limb_less_than_radix_range_settings, InteractionType::LookupIntoIndexedByClk>()
253 .add<lookup_to_radix_fetch_p_limb_settings, InteractionType::LookupIntoPDecomposition>()
255 // Mem Aware To Radix
256 // GT checks
257 .add<lookup_to_radix_mem_check_dst_addr_in_range_settings, InteractionType::LookupGeneric>(Column::gt_sel)
259 .add<lookup_to_radix_mem_check_radix_gt_256_settings, InteractionType::LookupGeneric>(Column::gt_sel)
260 // Dispatch to To Radix
262
263} // namespace bb::avm2::tracegen
#define AVM_MEMORY_SIZE
ValueTag get_tag() const
InteractionDefinition & add(auto &&... args)
void process(const simulation::EventEmitterInterface< simulation::ToRadixEvent >::Container &events, TraceContainer &trace)
static const InteractionDefinition interactions
void process_with_memory(const simulation::EventEmitterInterface< simulation::ToRadixMemoryEvent >::Container &events, TraceContainer &trace)
uint32_t dst_addr
TestTraceContainer trace
lookup_settings< lookup_to_radix_limb_p_diff_range_settings_ > lookup_to_radix_limb_p_diff_range_settings
const std::array< std::vector< uint8_t >, 257 > & get_p_limbs_per_radix()
Definition to_radix.cpp:48
lookup_settings< lookup_to_radix_mem_check_radix_lt_2_settings_ > lookup_to_radix_mem_check_radix_lt_2_settings
lookup_settings< lookup_to_radix_limb_range_settings_ > lookup_to_radix_limb_range_settings
lookup_settings< lookup_to_radix_fetch_safe_limbs_settings_ > lookup_to_radix_fetch_safe_limbs_settings
lookup_settings< lookup_to_radix_mem_input_output_to_radix_settings_ > lookup_to_radix_mem_input_output_to_radix_settings
AvmFlavorSettings::FF FF
Definition field.hpp:10
simulation::PublicDataTreeReadWriteEvent event