Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
data_copy.cpp
Go to the documentation of this file.
2
3#include <stdexcept>
4
9
10namespace bb::avm2::simulation {
11
13
14namespace {
15
17 const uint32_t clk,
18 const uint32_t cd_copy_size,
19 const uint32_t cd_offset,
21 const std::vector<FF>& calldata = {})
22{
23 return DataCopyEvent{ .execution_clk = clk,
24 .operation = DataCopyOperation::CD_COPY,
25 .calldata = calldata,
26 .write_context_id = context.get_context_id(),
27 .read_context_id = context.get_parent_id(),
28 .data_copy_size = cd_copy_size,
29 .data_offset = cd_offset,
30 .data_addr = context.get_parent_cd_addr(),
31 .data_size = context.get_parent_cd_size(),
32 .is_nested = context.has_parent(),
33 .dst_addr = dst_addr };
34}
35
37 const uint32_t clk,
38 const uint32_t rd_copy_size,
39 const uint32_t rd_offset,
41 const std::vector<FF>& returndata = {})
42{
43 return DataCopyEvent{ .execution_clk = clk,
44 .operation = DataCopyOperation::RD_COPY,
45 .calldata = returndata,
46 .write_context_id = context.get_context_id(),
47 // This handles the case where there is no last child (i.e. new enqueued call)
48 .read_context_id = context.get_last_child_id(),
49 .data_copy_size = rd_copy_size,
50 .data_offset = rd_offset,
51 .data_addr = context.get_last_rd_addr(),
52 .data_size = context.get_last_rd_size(),
53 .is_nested = context.has_parent(),
54 .dst_addr = dst_addr };
55}
56
57} // namespace
58
59// This is std::min but creates the relevant greater than event
60uint64_t DataCopy::min(uint64_t a, uint64_t b)
61{
62 // Looks weird but ironically similar to the std::min implementation
63 // i.e if a == b, return a
64 if (gt.gt(a, b)) {
65 return b;
66 }
67 return a;
68}
69
88 const uint32_t copy_size,
89 const uint32_t offset,
91{
92 auto& memory = context.get_memory();
94
95 try {
96 // This section is a bit leaky, but is necessary to ensure the correct gt events are generated.
97 // This work is duplicated in context.get_calldata() - but it avoids us having a gt there.
98
99 // Operations are performed over uint64_t in case the addition overflows, but the result in guaranteed to
100 // fit in 32 bits since get_parent_cd_size() returns a u32 (constrained by a CALL or 0 if an enqueued call).
101 uint64_t max_read_index = min(static_cast<uint64_t>(offset) + copy_size, context.get_parent_cd_size());
102
103 // Check that we will not access out of bounds memory.
104 // todo(ilyas): think of a way to not need to leak enqueued/nested context information here.
105 uint64_t max_read_addr = max_read_index + context.get_parent_cd_addr();
106 uint64_t max_write_addr = static_cast<uint64_t>(dst_addr) + copy_size;
107
108 // Need all of this to happen regardless
109 bool read_out_of_range = gt.gt(max_read_addr, MAX_MEM_ADDR);
110 bool write_out_of_range = gt.gt(max_write_addr, MAX_MEM_ADDR);
111
112 if (read_out_of_range || write_out_of_range) {
113 throw std::runtime_error("Attempting to access out of bounds memory");
114 }
115
116 // If we get to this point, we know we will be error free
117 std::vector<FF> padded_calldata(copy_size, 0); // Initialize with zeros
118 // Calldata is retrieved from [offset, max_read_index]
119 // if offset > max_read_index, we will read nothing
120 if (!gt.gt(offset, max_read_index)) {
121 padded_calldata = context.get_calldata(offset, copy_size);
122 }
123
124 for (uint32_t i = 0; i < copy_size; i++) {
125 memory.set(dst_addr + i, MemoryValue::from<FF>(padded_calldata[i]));
126 }
127
128 events.emit(create_cd_event(context, clk, copy_size, offset, dst_addr, padded_calldata));
129 } catch (const std::exception& e) {
130 debug("CD_COPY exception: ", e.what());
131 events.emit(create_cd_event(context, clk, copy_size, offset, dst_addr));
132
133 // Re-throw something generic that execution will interpret as an opcode error.
134 throw DataCopyException();
135 }
136}
137
146 const uint32_t copy_size,
147 const uint32_t offset,
149{
150 auto& memory = context.get_memory();
151 uint32_t clk = execution_id_manager.get_execution_id();
152
153 try {
154 // Check cd_copy for why we do this here even though it is in get_returndata()
155 uint64_t max_read_index = min(static_cast<uint64_t>(offset) + copy_size, context.get_last_rd_size());
156
157 uint64_t max_read_addr = max_read_index + context.get_last_rd_addr();
158 uint64_t max_write_addr = static_cast<uint64_t>(dst_addr) + copy_size;
159
160 // Need both of this to happen regardless
161 bool read_out_of_range = gt.gt(max_read_addr, MAX_MEM_ADDR);
162 bool write_out_of_range = gt.gt(max_write_addr, MAX_MEM_ADDR);
163
164 if (read_out_of_range || write_out_of_range) {
165 throw std::runtime_error("Attempting to access out of bounds memory");
166 }
167
168 // If we get to this point, we know we will be error free
169
170 // This is typically handled by the loop within get_returndata(), but we need to emit a range check in circuit
171 // so we need to be explicit about it.
172 // Returndata is retrieved from [offset, max_read_index], if offset > max_read_index, we will read nothing.
173 std::vector<FF> padded_returndata(copy_size, 0); // Initialize with zeros
174 if (!gt.gt(offset, max_read_index)) {
175 padded_returndata = context.get_returndata(offset, copy_size);
176 }
177
178 for (uint32_t i = 0; i < copy_size; i++) {
179 memory.set(dst_addr + i, MemoryValue::from<FF>(padded_returndata[i]));
180 }
181
182 events.emit(create_rd_event(context, clk, copy_size, offset, dst_addr, padded_returndata));
183
184 } catch (const std::exception& e) {
185 debug("RD_COPY exception: ", e.what());
186 events.emit(create_rd_event(context, clk, copy_size, offset, dst_addr));
187
188 // Re-throw something generic that execution will interpret as an opcode error.
189 throw DataCopyException();
190 }
191}
192
193} // namespace bb::avm2::simulation
#define AVM_HIGHEST_MEM_ADDRESS
ExecutionIdGetterInterface & execution_id_manager
Definition data_copy.hpp:47
uint64_t min(uint64_t a, uint64_t b)
Definition data_copy.cpp:60
EventEmitterInterface< DataCopyEvent > & events
Definition data_copy.hpp:49
void cd_copy(ContextInterface &context, const uint32_t cd_copy_size, const uint32_t cd_offset, const MemoryAddress dst_addr) override
Writes calldata into dst_addr. There is slight difference in how enqueued and nested contexts,...
Definition data_copy.cpp:87
void rd_copy(ContextInterface &context, const uint32_t rd_copy_size, const uint32_t rd_offset, const MemoryAddress dst_addr) override
Copies returndata from the last executed context to the dst_addr.
virtual uint32_t get_execution_id() const =0
void debug(Args... args)
Definition log.hpp:59
uint32_t dst_addr
FF a
FF b
ssize_t offset
Definition engine.cpp:36
constexpr uint32_t MAX_MEM_ADDR
Definition data_copy.cpp:12
uint32_t MemoryAddress
std::vector< FF > returndata
uint32_t cd_offset