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