Skip to content

Commit d06ed9b

Browse files
authored
fix(avm)!: handle zero copy size (#16567)
Handles the situation when `copy_size = 0` for `cd_copy` or `rd_copy`. This is not an error but effectively a no-op. Adds tests for this as well
2 parents 5c178eb + 11a6a39 commit d06ed9b

File tree

7 files changed

+225
-89
lines changed

7 files changed

+225
-89
lines changed

barretenberg/cpp/pil/vm2/data_copy.pil

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,6 @@ namespace data_copy;
118118
pol commit sel_rd_copy;
119119
sel_rd_copy * (1 - sel_rd_copy) = 0;
120120

121-
// Two varieties depending of if we gate by error
122-
pol SEL_NO_ERR = SEL * (1 - err);
123-
124121
pol commit clk;
125122

126123
// Things are range checked to 32 bits
@@ -226,11 +223,21 @@ namespace data_copy;
226223
#[START_AFTER_END]
227224
(sel_cd_copy' + sel_rd_copy') * sel_end * (sel_start' - 1) = 0;
228225

226+
pol commit sel_write_count_is_zero;
227+
pol commit write_count_zero_inv; // Could optimise by using the existing write_count_minus_on_inv
228+
// sel_write_count_is_zero = 1 IFF copy_size = 0 && sel_start = 1 (and there are no errors)
229+
#[ZERO_SIZED_WRITE]
230+
sel_start_no_err * (copy_size * (sel_write_count_is_zero * (1 - write_count_zero_inv) + write_count_zero_inv) - 1 + sel_write_count_is_zero) = 0;
231+
#[END_IF_WRITE_IS_ZERO]
232+
sel_start_no_err * sel_write_count_is_zero * (sel_end - 1) = 0;
233+
234+
pol SEL_PERFORM_COPY = SEL * (1 - err) * (1 - sel_write_count_is_zero);
235+
229236
pol WRITE_COUNT_MINUS_ONE = copy_size - 1;
230237
pol commit write_count_minus_one_inv;
231238
// sel_end = 1 IFF copy_size - 1 = 0;
232239
#[END_WRITE_CONDITION]
233-
SEL_NO_ERR * (WRITE_COUNT_MINUS_ONE * (sel_end * (1 - write_count_minus_one_inv) + write_count_minus_one_inv) - 1 + sel_end) = 0;
240+
SEL_PERFORM_COPY * (WRITE_COUNT_MINUS_ONE * (sel_end * (1 - write_count_minus_one_inv) + write_count_minus_one_inv) - 1 + sel_end) = 0;
234241

235242
#[END_ON_ERR] // sel_end = 1 if error
236243
err * (sel_end - 1) = 0;
@@ -248,15 +255,15 @@ namespace data_copy;
248255
// If sel_offset_gt_max_read = 1 (i.e. when offset > MAX_READ_INDEX, reads_left = 0)
249256
// otherwise, reads_left = MAX_READ_INDEX - offset
250257
#[INIT_READS_LEFT]
251-
sel_start_no_err * ( reads_left - (max_read_index - offset) * (1 - offset_gt_max_read_index)) = 0;
258+
sel_start_no_err * (1 - sel_write_count_is_zero) * (reads_left - (max_read_index - offset) * (1 - offset_gt_max_read_index)) = 0;
252259

253260
//////////////////////////////
254261
// Execute Data Copy
255262
//////////////////////////////
256263
// Most of these relations are either gated explicitly by an err or by sel_end (which is 1 when err = 1)
257264
// ===== Writing to dst_context_id =====
258265
pol commit sel_mem_write;
259-
sel_mem_write = SEL_NO_ERR; // We write if there is no error
266+
sel_mem_write = SEL_PERFORM_COPY; // We write if there is no error and copy_size != 0
260267
// Data copy size decrements for each row until we end
261268
#[DECR_COPY_SIZE]
262269
SEL * (1 - sel_end) * (copy_size' - copy_size + 1) = 0;
@@ -271,8 +278,8 @@ namespace data_copy;
271278

272279
// ===== Reading for nested call =====
273280
pol commit read_addr; // The addr to start reading the data from: src_addr + offset;
274-
#[INIT_READ_ADDR]
275-
SEL * sel_start_no_err * (read_addr - src_addr - offset) = 0;
281+
#[INIT_READ_ADDR] // Only occurs at the start if we have not errored
282+
sel_start_no_err * (1 - sel_write_count_is_zero) * (read_addr - src_addr - offset) = 0;
276283
// Subsequent read addrs are incremented by 1 unless this is a padding row
277284
#[INCR_READ_ADDR]
278285
SEL * (1 - padding) * (1 - sel_end) * (read_addr' - read_addr - 1) = 0;
@@ -283,16 +290,16 @@ namespace data_copy;
283290
pol commit padding; // Padding = 1 if reads_left = 0
284291
pol commit reads_left_inv;
285292
#[PADDING_CONDITION]
286-
SEL_NO_ERR * (reads_left * (padding * (1 - reads_left_inv) + reads_left_inv) - 1 + padding) = 0;
293+
SEL_PERFORM_COPY * (reads_left * (padding * (1 - reads_left_inv) + reads_left_inv) - 1 + padding) = 0;
287294

288295
// Read from memory if we are not the top level call and not a padding row
289296
pol commit sel_mem_read; // If the current row is a memory op read
290-
sel_mem_read = SEL_NO_ERR * (1 - is_top_level) * (1 - padding);
297+
sel_mem_read = SEL_PERFORM_COPY * (1 - is_top_level) * (1 - padding);
291298

292299
// === Value Padding ===
293300
pol commit value;
294301
#[PAD_VALUE]
295-
SEL_NO_ERR * padding * value = 0;
302+
SEL_PERFORM_COPY * padding * value = 0;
296303

297304
#[MEM_READ]
298305
sel_mem_read { clk, read_addr, value, /*mem_tag=*/precomputed.zero/*FF*/, /*rw=*/precomputed.zero/*(read)*/, src_context_id }
@@ -305,7 +312,7 @@ namespace data_copy;
305312
// After calldata hashing
306313
pol commit cd_copy_col_read;
307314
#[CD_COPY_COLUMN]
308-
cd_copy_col_read = SEL_NO_ERR * (1 - padding) * is_top_level * sel_cd_copy;
315+
cd_copy_col_read = SEL_PERFORM_COPY * (1 - padding) * is_top_level * sel_cd_copy;
309316

310317
#[COL_READ]
311318
cd_copy_col_read { value, dst_context_id, read_addr }
@@ -357,3 +364,4 @@ namespace data_copy;
357364
src_addr, src_data_size,
358365
sel_rd_copy, err
359366
};
367+

barretenberg/cpp/src/barretenberg/vm2/constraining/relations/data_copy.test.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,29 @@ class NestedCdConstrainingBuilderTest : public DataCopyConstrainingBuilderTest {
7474
}
7575
};
7676

77+
TEST_F(NestedCdConstrainingBuilderTest, CdZeroCopy)
78+
{
79+
uint32_t copy_size = 0;
80+
uint32_t cd_offset = 0; // Offset into calldata
81+
82+
EXPECT_CALL(context, get_calldata(cd_offset, copy_size)).WillOnce(::testing::Return(std::vector<FF>{}));
83+
84+
copy_data.cd_copy(context, copy_size, cd_offset, dst_addr);
85+
86+
tracegen::DataCopyTraceBuilder builder;
87+
builder.process(event_emitter.dump_events(), trace);
88+
89+
tracegen::GreaterThanTraceBuilder gt_builder;
90+
gt_builder.process(gt_event_emitter.dump_events(), trace);
91+
92+
check_relation<data_copy>(trace);
93+
check_interaction<DataCopyTraceBuilder,
94+
lookup_data_copy_max_read_index_gt_settings,
95+
lookup_data_copy_offset_gt_max_read_index_settings,
96+
lookup_data_copy_check_src_addr_in_range_settings,
97+
lookup_data_copy_check_dst_addr_in_range_settings>(trace);
98+
}
99+
77100
TEST_F(NestedCdConstrainingBuilderTest, SimpleNestedCdCopy)
78101
{
79102
uint32_t copy_size = static_cast<uint32_t>(data.size());
@@ -194,6 +217,29 @@ class EnqueuedCdConstrainingBuilderTest : public DataCopyConstrainingBuilderTest
194217
}
195218
};
196219

220+
TEST_F(EnqueuedCdConstrainingBuilderTest, CdZeroCopy)
221+
{
222+
uint32_t copy_size = 0;
223+
uint32_t cd_offset = 0; // Offset into calldata
224+
225+
EXPECT_CALL(context, get_calldata(cd_offset, copy_size)).WillOnce(::testing::Return(std::vector<FF>{}));
226+
227+
copy_data.cd_copy(context, copy_size, cd_offset, dst_addr);
228+
229+
tracegen::DataCopyTraceBuilder builder;
230+
builder.process(event_emitter.dump_events(), trace);
231+
232+
tracegen::GreaterThanTraceBuilder gt_builder;
233+
gt_builder.process(gt_event_emitter.dump_events(), trace);
234+
235+
check_relation<data_copy>(trace);
236+
check_interaction<DataCopyTraceBuilder,
237+
lookup_data_copy_max_read_index_gt_settings,
238+
lookup_data_copy_offset_gt_max_read_index_settings,
239+
lookup_data_copy_check_src_addr_in_range_settings,
240+
lookup_data_copy_check_dst_addr_in_range_settings>(trace);
241+
}
242+
197243
TEST_F(EnqueuedCdConstrainingBuilderTest, SimpleEnqueuedCdCopy)
198244
{
199245
auto copy_size = static_cast<uint32_t>(data.size());
@@ -343,6 +389,42 @@ TEST(DataCopyWithExecutionPerm, CdCopy)
343389
perm_data_copy_dispatch_rd_copy_settings>(trace);
344390
}
345391

392+
class NestedRdConstrainingBuilderTest : public DataCopyConstrainingBuilderTest {
393+
protected:
394+
NestedRdConstrainingBuilderTest()
395+
{
396+
// Set up parent context
397+
EXPECT_CALL(context, has_parent).WillRepeatedly(Return(true));
398+
EXPECT_CALL(context, get_last_child_id).WillRepeatedly(Return(2));
399+
EXPECT_CALL(context, get_context_id).WillRepeatedly(Return(2));
400+
EXPECT_CALL(context, get_last_rd_size).WillRepeatedly(Return(data.size()));
401+
EXPECT_CALL(context, get_last_rd_addr).WillRepeatedly(Return(0));
402+
}
403+
};
404+
405+
TEST_F(NestedRdConstrainingBuilderTest, RdZeroCopy)
406+
{
407+
uint32_t copy_size = 0;
408+
uint32_t rd_offset = 0; // Offset into calldata
409+
410+
EXPECT_CALL(context, get_returndata(rd_offset, copy_size)).WillOnce(::testing::Return(std::vector<FF>{}));
411+
412+
copy_data.rd_copy(context, copy_size, rd_offset, dst_addr);
413+
414+
tracegen::DataCopyTraceBuilder builder;
415+
builder.process(event_emitter.dump_events(), trace);
416+
417+
tracegen::GreaterThanTraceBuilder gt_builder;
418+
gt_builder.process(gt_event_emitter.dump_events(), trace);
419+
420+
check_relation<data_copy>(trace);
421+
check_interaction<DataCopyTraceBuilder,
422+
lookup_data_copy_max_read_index_gt_settings,
423+
lookup_data_copy_offset_gt_max_read_index_settings,
424+
lookup_data_copy_check_src_addr_in_range_settings,
425+
lookup_data_copy_check_dst_addr_in_range_settings>(trace);
426+
}
427+
346428
TEST(DataCopyWithExecutionPerm, RdCopy)
347429
{
348430
// Current Context

barretenberg/cpp/src/barretenberg/vm2/generated/columns.hpp

Lines changed: 3 additions & 3 deletions
Large diffs are not rendered by default.

barretenberg/cpp/src/barretenberg/vm2/generated/flavor_variables.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,10 @@ namespace bb::avm2 {
133133

134134
struct AvmFlavorVariables {
135135
static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 133;
136-
static constexpr size_t NUM_WITNESS_ENTITIES = 2934;
136+
static constexpr size_t NUM_WITNESS_ENTITIES = 2936;
137137
static constexpr size_t NUM_SHIFTED_ENTITIES = 316;
138138
static constexpr size_t NUM_WIRES = NUM_WITNESS_ENTITIES + NUM_PRECOMPUTED_ENTITIES;
139-
static constexpr size_t NUM_ALL_ENTITIES = 3383;
139+
static constexpr size_t NUM_ALL_ENTITIES = 3385;
140140

141141
// Need to be templated for recursive verifier
142142
template <typename FF_>

barretenberg/cpp/src/barretenberg/vm2/generated/relations/data_copy.hpp

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ template <typename FF_> class data_copyImpl {
1414
public:
1515
using FF = FF_;
1616

17-
static constexpr std::array<size_t, 33> SUBRELATION_PARTIAL_LENGTHS = { 3, 3, 3, 3, 3, 3, 5, 3, 4, 3, 3,
18-
3, 3, 3, 3, 3, 3, 4, 6, 3, 4, 3,
19-
4, 5, 4, 5, 5, 6, 5, 5, 6, 3, 3 };
17+
static constexpr std::array<size_t, 35> SUBRELATION_PARTIAL_LENGTHS = { 3, 3, 3, 3, 3, 3, 5, 3, 4, 3, 3, 3,
18+
3, 3, 3, 3, 3, 4, 5, 4, 7, 3, 5, 4,
19+
4, 5, 4, 5, 5, 7, 6, 6, 7, 3, 3 };
2020

2121
template <typename AllEntities> inline static bool skip(const AllEntities& in)
2222
{
@@ -46,26 +46,30 @@ template <typename FF> class data_copy : public Relation<data_copyImpl<FF>> {
4646
case 17:
4747
return "START_AFTER_END";
4848
case 18:
49-
return "END_WRITE_CONDITION";
49+
return "ZERO_SIZED_WRITE";
5050
case 19:
51-
return "END_ON_ERR";
51+
return "END_IF_WRITE_IS_ZERO";
5252
case 20:
53-
return "INIT_READS_LEFT";
53+
return "END_WRITE_CONDITION";
54+
case 21:
55+
return "END_ON_ERR";
5456
case 22:
57+
return "INIT_READS_LEFT";
58+
case 24:
5559
return "DECR_COPY_SIZE";
56-
case 23:
60+
case 25:
5761
return "INCR_WRITE_ADDR";
58-
case 24:
62+
case 26:
5963
return "INIT_READ_ADDR";
60-
case 25:
64+
case 27:
6165
return "INCR_READ_ADDR";
62-
case 26:
66+
case 28:
6367
return "DECR_READ_COUNT";
64-
case 27:
65-
return "PADDING_CONDITION";
6668
case 29:
69+
return "PADDING_CONDITION";
70+
case 31:
6771
return "PAD_VALUE";
68-
case 30:
72+
case 32:
6973
return "CD_COPY_COLUMN";
7074
}
7175
return std::to_string(index);
@@ -74,17 +78,19 @@ template <typename FF> class data_copy : public Relation<data_copyImpl<FF>> {
7478
// Subrelation indices constants, to be used in tests.
7579
static constexpr size_t SR_TOP_LEVEL_COND = 6;
7680
static constexpr size_t SR_START_AFTER_END = 17;
77-
static constexpr size_t SR_END_WRITE_CONDITION = 18;
78-
static constexpr size_t SR_END_ON_ERR = 19;
79-
static constexpr size_t SR_INIT_READS_LEFT = 20;
80-
static constexpr size_t SR_DECR_COPY_SIZE = 22;
81-
static constexpr size_t SR_INCR_WRITE_ADDR = 23;
82-
static constexpr size_t SR_INIT_READ_ADDR = 24;
83-
static constexpr size_t SR_INCR_READ_ADDR = 25;
84-
static constexpr size_t SR_DECR_READ_COUNT = 26;
85-
static constexpr size_t SR_PADDING_CONDITION = 27;
86-
static constexpr size_t SR_PAD_VALUE = 29;
87-
static constexpr size_t SR_CD_COPY_COLUMN = 30;
81+
static constexpr size_t SR_ZERO_SIZED_WRITE = 18;
82+
static constexpr size_t SR_END_IF_WRITE_IS_ZERO = 19;
83+
static constexpr size_t SR_END_WRITE_CONDITION = 20;
84+
static constexpr size_t SR_END_ON_ERR = 21;
85+
static constexpr size_t SR_INIT_READS_LEFT = 22;
86+
static constexpr size_t SR_DECR_COPY_SIZE = 24;
87+
static constexpr size_t SR_INCR_WRITE_ADDR = 25;
88+
static constexpr size_t SR_INIT_READ_ADDR = 26;
89+
static constexpr size_t SR_INCR_READ_ADDR = 27;
90+
static constexpr size_t SR_DECR_READ_COUNT = 28;
91+
static constexpr size_t SR_PADDING_CONDITION = 29;
92+
static constexpr size_t SR_PAD_VALUE = 31;
93+
static constexpr size_t SR_CD_COPY_COLUMN = 32;
8894
};
8995

9096
} // namespace bb::avm2

0 commit comments

Comments
 (0)