@@ -6,13 +6,33 @@ include "range_check.pil";
66include "context.pil";
77
88/** This trace handles CALLDATACOPY and RETURNDATACOPY
9- * The data_copy gadget handles CALLDATACOPY (both enqueued and nested) and RETURNDATACOPY
9+ * The data_copy gadget handles CALLDATACOPY and RETURNDATACOPY (both enqueued and nested)
10+ *
11+ * Opcode operands (relevant in EXECUTION when interacting with this gadget):
12+ * - register[0]: copy_size
13+ * - register[1]: copy_offset
14+ * - rop[2]: dst_addr
15+ *
16+ * Memory I/O, this subtrace can potentially read and write across two different memory space ids (indicated by the context_ids)
17+ * All reads are performed in the src context (using the src_context_id) and writes are performed in the current executing
18+ * context (using dst_context_id).
19+ * - M[src_addr]: aka value[0] (the first value read from the src context)
20+ * - the memory tag is ignored for these reads
21+ * - M[src_addr + max_read_index]: aka value[max_read_index] (the last value read from the src context)
22+ * - max_read_index is derived from the copy_size, see pil relations for an explanation
23+ * - the memory tag is ignored for these reads
24+ * - M[dst_addr]: aka output[0] (the first value written to the dst context)
25+ * - guaranteed by this gadget to be FF
26+ * - M[dst_addr + copy_size]: aka output[copy_size] (the last value written to the dst context)
27+ * - guaranteed by this gadget to be FF
28+ *
1029 * ERROR HANDLING:
1130 * There is one potential errors that is checked: memory out of range accesses
1231 * If there are no errors, we read and write the calldata/returndata from the parent/child context to the current context
1332 * COMPUTING AMOUNT OF DATA TO READ
1433 * We need to ensure that we do not read outside the bounds designated by the parent/child context for their respective data.
1534 * this max_read_index is computed via min(data_size, copy_size + copy_offset).
35+ *
1636 * READING / WRITING DATA
1737 * At each row, the i-th data is simultaneously read from the parent/child and written into the current context
1838 * For top level calldatacopy, the data is retrieved from the calldata column.
@@ -104,21 +124,9 @@ namespace data_copy;
104124 pol commit clk;
105125
106126 // Things are range checked to 32 bits
107- pol commit thirty_two;
127+ pol commit thirty_two; // todo: while we do not support constants in lookups
108128 SEL * (thirty_two - 32) = 0;
109129
110- //////////////////////////////
111- // Error Flags
112- //////////////////////////////
113- pol commit src_out_of_range_err; // Read slices should be in MEM range
114- src_out_of_range_err * (1 - src_out_of_range_err) = 0;
115- pol commit dst_out_of_range_err; // Write slices should be in MEM range
116- dst_out_of_range_err * (1 - dst_out_of_range_err) = 0;
117-
118- // Consolidate the errors
119- pol commit err;
120- err = 1 - (1 - dst_out_of_range_err) * (1 - src_out_of_range_err);
121-
122130 ///////////////////////////////
123131 // Inputs from execution trace
124132 ///////////////////////////////
@@ -137,14 +145,14 @@ namespace data_copy;
137145 //////////////////////////////
138146 // These relations occur independent of if we error (mainly because they help in finding out if there is an error)
139147 // Start and end constrained in "Control Flow Management" section
140- pol commit sel_start;
141- sel_start * (1 - sel_start ) = 0;
148+ pol commit sel_start; // sel_start = 1 <==> SEL = 1
149+ sel_start * (1 - SEL ) = 0;
142150
143151 // End controls most of the row propagation, so if we error we also set end to turn off row propagation
144- pol commit sel_end;
145- sel_end * (1 - sel_end ) = 0;
152+ pol commit sel_end; // sel_end = 1 <==> SEL = 1
153+ sel_end * (1 - SEL ) = 0;
146154
147- // Check if this is a nested or enqueued call
155+ // is_top_level if this is an enqueued call
148156 pol commit is_top_level; // == 1 iff parent_id == 0
149157 is_top_level * (1 - is_top_level) = 0;
150158 pol commit parent_id_inv; // For zero-check of has_parent_ctx
@@ -161,48 +169,51 @@ namespace data_copy;
161169 // 1) (offset + copy_size) > src_data_size or
162170 // 2) (offset + copy_size) <= src_data_size
163171 // if (1) then max_read_index = src_data_size, otherwise max_read_index = (offset + copy_size)
164- // these are enforced to be correct by are 32 bit range check of |a - b|
165-
166- // Convert comparisons to subtractions - check we don't underflow by range checking the absolute difference
167- pol OFFSET_PLUS_SIZE = offset + copy_size;
168- pol DATA_SIZE_LT = OFFSET_PLUS_SIZE - src_data_size - 1; // (offset + copy_size) > src_data_size
169- pol DATA_SIZE_GTE = src_data_size - OFFSET_PLUS_SIZE; // (offset + copy_size) <= src_data_size
172+ pol commit offset_plus_size;
173+ offset_plus_size = sel_start * (offset + copy_size);
174+ pol commit offset_plus_size_is_gt;
170175
171- pol commit src_data_size_is_lt; // Prover claims which one is the smaller of the two
172- src_data_size_is_lt * (1 - src_data_size_is_lt) = 0;
173-
174- pol MAX_READ_DIFF = src_data_size_is_lt * DATA_SIZE_LT + (1 - src_data_size_is_lt) * DATA_SIZE_GTE;
175- pol commit abs_diff_max_read_index; // Needed for the range check lookup
176- SEL * sel_start * (abs_diff_max_read_index - MAX_READ_DIFF) = 0;
177- #[RANGE_MAX_READ_SIZE_DIFF]
178- sel_start { abs_diff_max_read_index, thirty_two } in range_check.sel { range_check.value, range_check.rng_chk_bits };
176+ #[MAX_READ_INDEX_GT]
177+ sel_start { offset_plus_size, src_data_size, offset_plus_size_is_gt }
178+ in
179+ gt.sel { gt.input_a, gt.input_b, gt.res };
179180
180- // Based on the prover's claim, we select the smaller of the two
181- pol MAX_READ_INDEX = src_data_size_is_lt * src_data_size + (1 - src_data_size_is_lt) * OFFSET_PLUS_SIZE;
181+ // Set max_read_index based on the conditions (1) or (2) from above
182+ pol commit max_read_index;
183+ max_read_index = sel_start * ((src_data_size - offset_plus_size) * offset_plus_size_is_gt + offset_plus_size);
182184
183185 //////////////////////////////
184186 // Error Handling
185187 //////////////////////////////
186- // The one error that we need to handle
187- // Memory Out of Range: If reading or writing would access an address outside of the AVM memory range
188- // If there is an error, no data copy operation is performed
188+ pol commit src_out_of_range_err; // Read slices should be in MEM range
189+ src_out_of_range_err * (1 - src_out_of_range_err) = 0;
190+ pol commit dst_out_of_range_err; // Write slices should be in MEM range
191+ dst_out_of_range_err * (1 - dst_out_of_range_err) = 0;
189192
190- // Memory Out of Range, this section checks that the maximum number of reads ans writes do not
191- // If top level, we trivially succeed since there is no mem read i.e. we cannot have a src_out_of_range_err
192- pol MAX_READ_ADDR = (src_addr + MAX_READ_INDEX) * (1 - is_top_level);
193- pol commit abs_read_diff;
194- #[SRC_OUT_OF_RANGE] // MAX_MEM_ADDR < MAX_READ_ADDR or MAX_MEM_ADDR >= MAX_READ_ADDR
195- SEL * sel_start * (src_out_of_range_err * (MAX_READ_ADDR - MAX_MEM_ADDR - 1) + (1 - src_out_of_range_err) * (MAX_MEM_ADDR - MAX_READ_ADDR) - abs_read_diff) = 0;
193+ pol commit max_mem_addr; // todo: While we do not support constants
194+ sel_start * (max_mem_addr - constants.AVM_HIGHEST_MEM_ADDRESS) = 0;
195+
196+ // MAX_READ_ADDR = 0 if this is a top level call since there aren't any memory reads at the top level
197+ // conceptually cd copy and rd copy don't perform reads at the top level.
198+ pol MAX_READ_ADDR = src_addr + max_read_index;
199+ pol commit max_read_addr;
200+ max_read_addr = sel_start * MAX_READ_ADDR;
201+ #[CHECK_SRC_ADDR_IN_RANGE]
202+ sel_start { max_read_addr, max_mem_addr, src_out_of_range_err }
203+ in
204+ gt.sel { gt.input_a, gt.input_b, gt.res };
196205
197206 pol MAX_WRITE_ADDR = dst_addr + copy_size;
198- pol commit abs_write_diff;
199- #[DST_OUT_OF_RANGE] // MAX_MEM_ADDR < MAX_WRITE_ADDR or MAX_MEM_ADDR >= MAX_WRITE_ADDR
200- SEL * sel_start * (dst_out_of_range_err * (MAX_WRITE_ADDR - MAX_MEM_ADDR - 1) + (1 - dst_out_of_range_err) * (MAX_MEM_ADDR - MAX_WRITE_ADDR) - abs_write_diff) = 0;
207+ pol commit max_write_addr;
208+ max_write_addr = sel_start * MAX_WRITE_ADDR;
209+ #[CHECK_DST_ADDR_IN_RANGE]
210+ sel_start { max_write_addr, max_mem_addr, dst_out_of_range_err }
211+ in
212+ gt.sel { gt.input_a, gt.input_b, gt.res };
201213
202- #[RANGE_READ]
203- sel_start { abs_read_diff, thirty_two } in range_check.sel { range_check.value, range_check.rng_chk_bits };
204- #[RANGE_WRITE]
205- sel_start { abs_write_diff, thirty_two } in range_check.sel { range_check.value, range_check.rng_chk_bits };
214+ // Consolidate the errors
215+ pol commit err;
216+ err = 1 - (1 - dst_out_of_range_err) * (1 - src_out_of_range_err);
206217
207218 //////////////////////////////
208219 // Control flow management
@@ -225,23 +236,19 @@ namespace data_copy;
225236 err * (sel_end - 1) = 0;
226237
227238 pol commit reads_left; // Number of reads of the src data, if reads_left = 0 but copy_size != 0 then it is a padding row
228- // Src data elements are read from indicies [offset, MAX_READ_INDEX], therefore reads_left = MAX_READ_INDEX - offset
229- // We need to be careful that MAX_READ_INDEX - offset does not underflow (i.e. when offset > MAX_READ_INDEX, reads_left = 0)
230- pol OFFSET_GT_MAX_READ = offset - MAX_READ_INDEX - 1; // offset > MAX_READ_INDEX
231- pol OFFSET_LTE_MAX_READ = MAX_READ_INDEX - offset; // offset <= MAX_READ_INDEX
232- pol commit sel_offset_gt_max_read;
233- sel_offset_gt_max_read * (1 - sel_offset_gt_max_read) = 0;
234-
235- pol commit abs_max_read_offset; // Needed for lookup
236- abs_max_read_offset = SEL * sel_start_no_err * (sel_offset_gt_max_read * OFFSET_GT_MAX_READ + (1 - sel_offset_gt_max_read) * OFFSET_LTE_MAX_READ);
237-
238- #[RANGE_READS_LEFT]
239- sel_start_no_err { abs_max_read_offset, thirty_two } in range_check.sel { range_check.value, range_check.rng_chk_bits };
239+ // src data elements are read from indicies [offset, max_read_index], therefore reads_left = max_read_index - offset
240+ // We need to be careful that max_read_index - offset does not underflow (i.e. when offset > max_read_index, reads_left = 0)
241+ // We test that condition here
242+ pol commit offset_gt_max_read_index;
243+ #[OFFSET_GT_MAX_READ_INDEX]
244+ sel_start_no_err { offset, max_read_index, offset_gt_max_read_index }
245+ in
246+ gt.sel { gt.input_a, gt.input_b, gt.res };
240247
241248 // If sel_offset_gt_max_read = 1 (i.e. when offset > MAX_READ_INDEX, reads_left = 0)
242249 // otherwise, reads_left = MAX_READ_INDEX - offset
243250 #[INIT_READS_LEFT]
244- SEL * sel_start_no_err * (reads_left - OFFSET_LTE_MAX_READ * (1 - sel_offset_gt_max_read)) = 0;
251+ sel_start_no_err * ( reads_left - (max_read_index - offset) * (1 - offset_gt_max_read_index)) = 0;
245252
246253 //////////////////////////////
247254 // Execute Data Copy
0 commit comments