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