Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
data_copy_trace.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cassert>
5#include <cstdint>
6
11
12namespace bb::avm2::tracegen {
13
46{
47 using C = Column;
48 uint32_t row = 1;
49 for (const auto& event : events) {
50 const bool is_cd_copy = event.operation == simulation::DataCopyOperation::CD_COPY;
51 const bool is_rd_copy = event.operation == simulation::DataCopyOperation::RD_COPY;
52 const bool is_top_level = event.read_context_id == 0;
53 const FF parent_id_inv = is_top_level ? 0 : FF(event.read_context_id); // Will be inverted in batch later
54
55 // While we know at this point data copy size and data offset are guaranteed to be U32
56 // we cast to a wider integer type to detect overflows
57 const uint64_t copy_size = static_cast<uint64_t>(event.data_copy_size);
58 const uint64_t data_offset = static_cast<uint64_t>(event.data_offset);
59 const uint64_t read_index_upper_bound =
60 std::min(data_offset + copy_size, static_cast<uint64_t>(event.src_data_size));
61
62 const uint64_t read_addr_upper_bound = static_cast<uint64_t>(event.src_data_addr) + read_index_upper_bound;
63 const uint64_t write_addr_upper_bound = static_cast<uint64_t>(event.dst_addr) + copy_size;
64
65 // Clamp reads at the memory boundary when src reads exceed memory.
66 const bool read_address_overflow = read_addr_upper_bound > AVM_MEMORY_SIZE;
67 const bool write_address_overflow = write_addr_upper_bound > AVM_MEMORY_SIZE;
68 const uint64_t clamped_read_index_upper_bound =
69 read_address_overflow ? (static_cast<uint64_t>(AVM_MEMORY_SIZE) - event.src_data_addr)
70 : read_index_upper_bound;
71
72 trace.set(row,
73 { {
74 // Unconditional values
75 { C::data_copy_sel, 1 },
76 { C::data_copy_clk, event.execution_clk },
77 { C::data_copy_start, 1 },
78 { C::data_copy_sel_cd_copy, is_cd_copy ? 1 : 0 },
79 { C::data_copy_sel_cd_copy_start, is_cd_copy ? 1 : 0 },
80 { C::data_copy_sel_rd_copy_start, is_rd_copy ? 1 : 0 },
81
82 { C::data_copy_src_context_id, event.read_context_id },
83 { C::data_copy_dst_context_id, event.write_context_id },
84
85 { C::data_copy_copy_size, event.data_copy_size },
86 { C::data_copy_offset, event.data_offset },
87
88 { C::data_copy_src_addr, event.src_data_addr },
89 { C::data_copy_src_data_size, event.src_data_size },
90 { C::data_copy_dst_addr, event.dst_addr },
91
92 { C::data_copy_is_top_level, is_top_level ? 1 : 0 },
93 { C::data_copy_parent_id_inv, parent_id_inv }, // Will be inverted in batch later
94
95 // Compute read index upper bound
96 { C::data_copy_offset_plus_size, data_offset + copy_size },
97 { C::data_copy_offset_plus_size_is_gt, data_offset + copy_size > event.src_data_size ? 1 : 0 },
98
99 // Src address range clamping
100 { C::data_copy_mem_size, static_cast<uint64_t>(AVM_MEMORY_SIZE) },
101 { C::data_copy_read_addr_upper_bound, read_addr_upper_bound },
102 { C::data_copy_src_reads_exceed_mem, read_address_overflow ? 1 : 0 },
103 { C::data_copy_clamped_read_index_upper_bound, clamped_read_index_upper_bound },
104
105 // Dst address range check
106 { C::data_copy_write_addr_upper_bound, write_addr_upper_bound },
107
108 } });
109
111 // Dst Address Range Check (only error type)
113 if (write_address_overflow) {
114 trace.set(row,
115 { {
116 { C::data_copy_end, 1 },
117 { C::data_copy_dst_out_of_range_err, 1 },
118 } });
119 row++;
120 continue; // Go to the next event
121 }
122
123 // If there is an error, the copying data is empty. Therefore, we have to perform this
124 // assertion after the error check.
125 BB_ASSERT_EQ(event.copying_data.size(), copy_size, "Copying data size is not equal to copy size");
126
128 // Check for Zero Sized Copy
130 // This has to happen outside of the next loop since we will not enter it if the copy size is zero
131 if (copy_size == 0) {
132 trace.set(row,
133 { {
134 { C::data_copy_start_no_err, 1 },
135 { C::data_copy_end, 1 },
136 { C::data_copy_sel_write_count_is_zero, 1 },
137 { C::data_copy_sel_has_reads, clamped_read_index_upper_bound > data_offset ? 1 : 0 },
138 } });
139 row++;
140 continue; // Go to the next event
141 }
142
144 // Process Data Copy Rows
146 uint32_t reads_left = data_offset >= clamped_read_index_upper_bound
147 ? 0
148 : static_cast<uint32_t>(clamped_read_index_upper_bound - data_offset);
149
150 for (uint32_t i = 0; i < copy_size; i++) {
151 bool start = i == 0;
152 auto current_copy_size = copy_size - i;
153 bool end = (current_copy_size - 1) == 0;
154
155 bool is_padding_row = reads_left == 0;
156
157 // These are guaranteed not to overflow since clamped reads stay within memory bounds
158 uint64_t read_addr = event.src_data_addr + data_offset + i;
159 bool read_cd_col = is_cd_copy && is_top_level && !is_padding_row;
160
161 // Read from memory if this is not a padding row and we are either RD_COPY-ing or a nested CD_COPY
162 bool sel_mem_read = !is_padding_row && (is_rd_copy || !is_top_level);
163 FF value = is_padding_row ? 0 : event.copying_data[i].as_ff();
164 // Circuit only enforces tag consistency for memory reads.
165 FF tag = sel_mem_read ? static_cast<FF>(static_cast<uint8_t>(event.copying_data[i].get_tag())) : 0;
166
167 trace.set(
168 row,
169 { {
170 { C::data_copy_sel, 1 },
171 { C::data_copy_clk, event.execution_clk },
172 { C::data_copy_sel_cd_copy, is_cd_copy ? 1 : 0 },
173
174 { C::data_copy_src_context_id, event.read_context_id },
175 { C::data_copy_dst_context_id, event.write_context_id },
176 { C::data_copy_dst_addr, event.dst_addr + i },
177
178 { C::data_copy_start_no_err, start ? 1 : 0 },
179 { C::data_copy_end, end ? 1 : 0 },
180 { C::data_copy_copy_size, current_copy_size },
181 { C::data_copy_write_count_minus_one_inv,
182 current_copy_size - 1 }, // Will be inverted in batch later
183
184 { C::data_copy_sel_mem_write, 1 },
185
186 { C::data_copy_is_top_level, is_top_level ? 1 : 0 },
187 { C::data_copy_parent_id_inv, parent_id_inv }, // Will be inverted in batch later
188
189 { C::data_copy_sel_mem_read, sel_mem_read ? 1 : 0 },
190 { C::data_copy_read_addr, read_addr },
191 { C::data_copy_read_addr_plus_one, read_cd_col ? read_addr + 1 : 0 },
192
193 { C::data_copy_reads_left_inv, reads_left }, // Will be inverted in batch later
194 { C::data_copy_padding, is_padding_row ? 1 : 0 },
195 { C::data_copy_value, value },
196 { C::data_copy_tag, tag },
197
198 { C::data_copy_cd_copy_col_read, read_cd_col ? 1 : 0 },
199
200 // Reads Left
201 { C::data_copy_reads_left, reads_left },
202 { C::data_copy_sel_has_reads, (start && clamped_read_index_upper_bound > data_offset) ? 1 : 0 },
203
204 // Non-zero Copy Size
205 { C::data_copy_write_count_zero_inv, start ? FF(copy_size) : 0 }, // Will be inverted in batch later
206 } });
207
208 if (reads_left > 0) {
209 reads_left--;
210 }
211
212 row++;
213 }
214 }
215
216 // Batch invert the columns.
217 trace.invert_columns({ { C::data_copy_parent_id_inv,
218 C::data_copy_write_count_zero_inv,
219 C::data_copy_reads_left_inv,
220 C::data_copy_write_count_minus_one_inv } });
221}
222
225 // Enqueued Call Col Read
227 // GT checks
228 .add<InteractionType::LookupGeneric, lookup_data_copy_offset_plus_size_is_gt_data_size_settings>(Column::gt_sel)
230 .add<InteractionType::LookupGeneric, lookup_data_copy_check_dst_addr_in_range_settings>(Column::gt_sel)
232} // namespace bb::avm2::tracegen
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
#define AVM_MEMORY_SIZE
static const InteractionDefinition interactions
void process(const simulation::EventEmitterInterface< simulation::DataCopyEvent >::Container &events, TraceContainer &trace)
Builds the data copy trace.
InteractionDefinition & add(auto &&... args)
TestTraceContainer trace
AvmFlavorSettings::FF FF
Definition field.hpp:10
simulation::PublicDataTreeReadWriteEvent event
Settings to be passed ot GenericLookupRelationImpl.