Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
data_copy.test.cpp
Go to the documentation of this file.
2
3#include <gmock/gmock.h>
4#include <gtest/gtest.h>
5
28
29namespace bb::avm2::constraining {
30namespace {
31
32using namespace simulation;
33using ::testing::Return;
34using ::testing::ReturnRef;
35using ::testing::StrictMock;
36using tracegen::DataCopyTraceBuilder;
37using tracegen::TestTraceContainer;
38
40using C = Column;
42
43class DataCopyConstrainingBuilderTest : public ::testing::Test {
44 protected:
45 DataCopyConstrainingBuilderTest() { EXPECT_CALL(context, get_memory).WillRepeatedly(ReturnRef(mem)); }
46
47 ExecutionIdManager execution_id_manager = ExecutionIdManager(0);
48 EventEmitter<RangeCheckEvent> range_check_event_emitter;
49 RangeCheck range_check = RangeCheck(range_check_event_emitter);
50 EventEmitter<GreaterThanEvent> gt_event_emitter;
51 StrictMock<MockFieldGreaterThan> mock_field_gt;
52 GreaterThan gt = GreaterThan(mock_field_gt, range_check, gt_event_emitter);
53 EventEmitter<DataCopyEvent> event_emitter;
54 DataCopy copy_data = DataCopy(execution_id_manager, gt, event_emitter);
55 StrictMock<MockContext> context;
56
58
59 TestTraceContainer trace;
60 uint32_t dst_addr = 0; // Destination address in memory for the data.
61 const std::vector<FF> data = { 1, 2, 3, 4, 5, 6, 7, 8 };
62};
63
64class NestedCdConstrainingBuilderTest : public DataCopyConstrainingBuilderTest {
65 protected:
66 NestedCdConstrainingBuilderTest()
67 {
68 // Set up parent context
69 EXPECT_CALL(context, has_parent).WillRepeatedly(Return(true));
70 EXPECT_CALL(context, get_parent_id).WillRepeatedly(Return(1));
71 EXPECT_CALL(context, get_context_id).WillRepeatedly(Return(2));
72 EXPECT_CALL(context, get_parent_cd_size).WillRepeatedly(Return(data.size()));
73 EXPECT_CALL(context, get_parent_cd_addr).WillRepeatedly(Return(0));
74 }
75};
76
77TEST_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,
98}
99
100TEST_F(NestedCdConstrainingBuilderTest, SimpleNestedCdCopy)
101{
102 uint32_t copy_size = static_cast<uint32_t>(data.size());
103 uint32_t cd_offset = 0; // Offset into calldata
104
105 EXPECT_CALL(context, get_calldata(cd_offset, copy_size)).WillOnce(Return(data));
106
107 copy_data.cd_copy(context, copy_size, cd_offset, dst_addr);
108
109 DataCopyTraceBuilder builder;
110 builder.process(event_emitter.dump_events(), trace);
111
112 tracegen::GreaterThanTraceBuilder gt_builder;
113 gt_builder.process(gt_event_emitter.dump_events(), trace);
114
115 check_relation<data_copy>(trace);
116 check_interaction<DataCopyTraceBuilder,
121}
122
123TEST_F(NestedCdConstrainingBuilderTest, NestedCdCopyPadded)
124{
125 uint32_t cd_offset = 0;
126
127 std::vector<FF> result_cd = data;
128 ASSERT_LT(result_cd.size(), 10); // Ensure we have less than 10 elements so we can pad
129 result_cd.resize(10, 0); // Pad with zeros to 10 elements
130 uint32_t copy_size = static_cast<uint32_t>(result_cd.size()); // Request more than available
131
132 EXPECT_CALL(context, get_calldata(cd_offset, copy_size)).WillOnce(Return(result_cd));
133
134 copy_data.cd_copy(context, copy_size, cd_offset, dst_addr);
135
136 DataCopyTraceBuilder builder;
137 builder.process(event_emitter.dump_events(), trace);
138
139 tracegen::GreaterThanTraceBuilder gt_builder;
140 gt_builder.process(gt_event_emitter.dump_events(), trace);
141
142 check_relation<data_copy>(trace);
143 check_interaction<DataCopyTraceBuilder,
148}
149
150TEST_F(NestedCdConstrainingBuilderTest, NestedCdCopyPartial)
151{
152 uint32_t offset = 3;
153 uint32_t size = 4;
154
155 // Starting at offset = 3
156 std::vector<FF> result_cd = { data.begin() + offset, data.begin() + offset + size };
157
158 EXPECT_CALL(context, get_calldata(offset, size)).WillOnce(Return(result_cd));
159
160 copy_data.cd_copy(context, size, offset, dst_addr);
161
162 DataCopyTraceBuilder builder;
163 builder.process(event_emitter.dump_events(), trace);
164
165 tracegen::GreaterThanTraceBuilder gt_builder;
166 gt_builder.process(gt_event_emitter.dump_events(), trace);
167
168 check_relation<data_copy>(trace);
169 check_interaction<DataCopyTraceBuilder,
174}
175
176TEST_F(NestedCdConstrainingBuilderTest, OutofRangeError)
177{
178 uint32_t offset = 10; // Offset beyond the size of calldata
179 uint32_t size = 4;
180
181 uint32_t big_dst_addr = AVM_HIGHEST_MEM_ADDRESS - 1;
182 EXPECT_THROW_WITH_MESSAGE(copy_data.cd_copy(context, size, offset, big_dst_addr), "Error during CD/RD copy");
183
184 DataCopyTraceBuilder builder;
185 builder.process(event_emitter.dump_events(), trace);
186
187 tracegen::GreaterThanTraceBuilder gt_builder;
188 gt_builder.process(gt_event_emitter.dump_events(), trace);
189
190 check_relation<data_copy>(trace);
191 check_interaction<DataCopyTraceBuilder,
196}
197
198class EnqueuedCdConstrainingBuilderTest : public DataCopyConstrainingBuilderTest {
199 protected:
200 EnqueuedCdConstrainingBuilderTest()
201 {
202 // Set up for enqueued call
203 EXPECT_CALL(context, has_parent).WillRepeatedly(Return(false));
204 EXPECT_CALL(context, get_parent_id).WillRepeatedly(Return(0));
205 EXPECT_CALL(context, get_context_id).WillRepeatedly(Return(1));
206 EXPECT_CALL(context, get_parent_cd_size).WillRepeatedly(Return(data.size()));
207 EXPECT_CALL(context, get_parent_cd_addr).WillRepeatedly(Return(0));
208
209 // Build Calldata Column
210 tracegen::CalldataTraceBuilder calldata_builder;
211 CalldataEvent cd_event = {
212 .context_id = 1,
213 .calldata_length = static_cast<uint32_t>(data.size()),
214 .calldata = data,
215 };
216 calldata_builder.process_retrieval({ cd_event }, trace);
217 }
218};
219
220TEST_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,
241}
242
243TEST_F(EnqueuedCdConstrainingBuilderTest, SimpleEnqueuedCdCopy)
244{
245 auto copy_size = static_cast<uint32_t>(data.size());
246 uint32_t cd_offset = 0;
247
248 EXPECT_CALL(context, get_calldata(cd_offset, copy_size)).WillOnce(Return(data));
249
250 copy_data.cd_copy(context, copy_size, cd_offset, dst_addr);
251
252 DataCopyTraceBuilder builder;
253 builder.process(event_emitter.dump_events(), trace);
254
255 tracegen::GreaterThanTraceBuilder gt_builder;
256 gt_builder.process(gt_event_emitter.dump_events(), trace);
257
258 check_relation<data_copy>(trace);
259 check_interaction<DataCopyTraceBuilder,
265}
266
267TEST_F(EnqueuedCdConstrainingBuilderTest, EnqueuedCallCdCopyPadding)
268{
269 uint32_t cd_offset = 0;
270 std::vector<FF> result_cd = data;
271 ASSERT_LT(result_cd.size(), 10); // Ensure we have less than 10 elements so we can pad
272 result_cd.resize(10, 0); // Pad with zeros to 10 elements
273 auto copy_size = static_cast<uint32_t>(result_cd.size()); // Request more than available
274
275 EXPECT_CALL(context, get_calldata(cd_offset, copy_size)).WillOnce(Return(result_cd));
276
277 copy_data.cd_copy(context, copy_size, cd_offset, dst_addr);
278
279 DataCopyTraceBuilder builder;
280 builder.process(event_emitter.dump_events(), trace);
281
282 tracegen::GreaterThanTraceBuilder gt_builder;
283 gt_builder.process(gt_event_emitter.dump_events(), trace);
284
285 check_relation<data_copy>(trace);
286 check_interaction<DataCopyTraceBuilder,
293}
294
295TEST_F(EnqueuedCdConstrainingBuilderTest, EnqueuedCallCdCopyPartial)
296{
297 uint32_t offset = 3;
298 uint32_t size = 4;
299
300 // Starting at offset = 3
301 std::vector<FF> result_cd = { data.begin() + offset, data.begin() + offset + size };
302
303 EXPECT_CALL(context, get_calldata(offset, size)).WillOnce(Return(result_cd));
304
305 copy_data.cd_copy(context, size, offset, dst_addr);
306
307 DataCopyTraceBuilder builder;
308 builder.process(event_emitter.dump_events(), trace);
309
310 tracegen::GreaterThanTraceBuilder gt_builder;
311 gt_builder.process(gt_event_emitter.dump_events(), trace);
312
313 check_relation<data_copy>(trace);
314 check_interaction<DataCopyTraceBuilder,
321}
322
324// DataCopy Tests with Execution Permutation
326
327TEST(DataCopyWithExecutionPerm, CdCopy)
328{
329 // Current Context
330 uint32_t context_id = 2;
331 uint32_t cd_offset = 3;
332 uint32_t copy_size = 4;
333 MemoryAddress dst_addr = 0xdeadbeef; // Destination address in memory for the data.
334 // Parent Context
335 uint32_t parent_context_id = 99; // Parent context ID
336 uint32_t parent_cd_addr = 0xc0ffee; // Parent calldata address in memory.
337 const std::vector<FF> data = { 8, 7, 6, 5, 4, 3, 2, 1 };
338
339 // Set up Memory
340 MemoryStore mem(context_id);
341
342 // Execution clk is 0 for this test
343 StrictMock<MockExecutionIdManager> execution_id_manager;
344 EXPECT_CALL(execution_id_manager, get_execution_id()).WillOnce(Return(0));
345
346 // Mock current context
347 StrictMock<MockContext> context;
348 EXPECT_CALL(context, get_memory).WillRepeatedly(ReturnRef(mem));
349 EXPECT_CALL(context, get_parent_cd_size).WillRepeatedly(Return(data.size()));
350 EXPECT_CALL(context, has_parent).WillRepeatedly(Return(true));
351 EXPECT_CALL(context, get_parent_cd_addr).WillRepeatedly(Return(parent_cd_addr));
352 EXPECT_CALL(context, get_calldata(cd_offset, copy_size))
353 .WillRepeatedly(::testing::Invoke([&data, cd_offset, copy_size]() {
354 // Return a slice of data from the calldata
355 return std::vector<FF>(data.begin() + cd_offset, data.begin() + cd_offset + copy_size);
356 }));
357 EXPECT_CALL(context, get_context_id).WillRepeatedly(Return(context_id));
358 EXPECT_CALL(context, get_parent_id).WillRepeatedly(Return(parent_context_id));
359
360 FakeGreaterThan gt;
361
362 EventEmitter<DataCopyEvent> event_emitter;
363 DataCopy copy_data = DataCopy(execution_id_manager, gt, event_emitter);
364 // Set up execution trace
365 TestTraceContainer trace({
366 {
367 { C::precomputed_first_row, 1 },
368 { C::execution_sel, 1 },
369 { C::execution_context_id, context_id },
370 { C::execution_parent_id, parent_context_id },
371 { C::execution_sel_execute_calldata_copy, 1 },
372 { C::execution_register_0_, copy_size },
373 { C::execution_register_1_, cd_offset },
374 { C::execution_rop_2_, dst_addr },
375 { C::execution_sel_opcode_error, 0 },
376 { C::execution_parent_calldata_addr, parent_cd_addr },
377 { C::execution_parent_calldata_size, static_cast<uint32_t>(data.size()) },
378 },
379 });
380
381 copy_data.cd_copy(context, copy_size, cd_offset, dst_addr);
382
383 DataCopyTraceBuilder builder;
384 builder.process(event_emitter.dump_events(), trace);
385
386 check_relation<data_copy>(trace);
387 check_interaction<DataCopyTraceBuilder,
390}
391
392class 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
405TEST_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,
426}
427
428TEST(DataCopyWithExecutionPerm, RdCopy)
429{
430 // Current Context
431 uint32_t context_id = 2;
432 uint32_t rd_offset = 3;
433 uint32_t copy_size = 4;
434 MemoryAddress dst_addr = 0xdeadbeef; // Destination address in memory for the data.
435 // Child Context
436 uint32_t child_context_id = 1; // Child context ID
437 MemoryAddress child_rd_addr = 0xc0ffee; // Child returndata address in memory.
438 const std::vector<FF> data = { 1, 2, 3, 4, 5, 6, 7, 8 };
439
440 // Set up Memory
442
443 StrictMock<MockExecutionIdManager> execution_id_manager;
444 EXPECT_CALL(execution_id_manager, get_execution_id()).WillOnce(Return(0));
445 StrictMock<MockContext> context;
446 EXPECT_CALL(context, get_memory).WillRepeatedly(ReturnRef(mem));
447 EXPECT_CALL(context, get_last_rd_size).WillRepeatedly(Return(data.size()));
448 EXPECT_CALL(context, has_parent).WillRepeatedly(Return(true));
449 EXPECT_CALL(context, get_last_rd_addr).WillRepeatedly(Return(child_rd_addr));
450 EXPECT_CALL(context, get_returndata(rd_offset, copy_size))
451 .WillRepeatedly(::testing::Invoke([&data, rd_offset, copy_size]() {
452 // Return a slice of data from the calldata
453 return std::vector<FF>(data.begin() + rd_offset, data.begin() + rd_offset + copy_size);
454 }));
455 EXPECT_CALL(context, get_last_child_id).WillRepeatedly(Return(child_context_id));
456 EXPECT_CALL(context, get_context_id).WillRepeatedly(Return(context_id));
457
458 FakeGreaterThan gt;
459
460 EventEmitter<DataCopyEvent> event_emitter;
461 DataCopy copy_data = DataCopy(execution_id_manager, gt, event_emitter);
462 // Set up execution trace
463 TestTraceContainer trace({
464 {
465 { C::precomputed_first_row, 1 },
466 { C::execution_sel, 1 },
467 { C::execution_context_id, context_id },
468 { C::execution_last_child_id, child_context_id },
469 { C::execution_sel_execute_returndata_copy, 1 },
470 { C::execution_register_0_, copy_size },
471 { C::execution_register_1_, rd_offset },
472 { C::execution_rop_2_, dst_addr },
473 { C::execution_sel_opcode_error, 0 },
474 { C::execution_last_child_returndata_addr, child_rd_addr },
475 { C::execution_last_child_returndata_size, static_cast<uint32_t>(data.size()) },
476 },
477 });
478
479 copy_data.rd_copy(context, copy_size, rd_offset, dst_addr);
480
481 DataCopyTraceBuilder builder;
482 builder.process(event_emitter.dump_events(), trace);
483
484 check_relation<data_copy>(trace);
485 check_interaction<DataCopyTraceBuilder,
488}
489
490TEST(DataCopyWithExecutionPerm, ErrorPropagation)
491{
492 // Current Context
493 uint32_t context_id = 2;
494 uint32_t rd_offset = 10;
495 uint32_t copy_size = 4;
496 MemoryAddress big_dst_addr = AVM_HIGHEST_MEM_ADDRESS - 1;
497
498 // Child context
499 uint32_t child_context_id = 3; // Child context ID
500 uint32_t child_rd_addr = 0xc0ffee; // Last child returndata address in memory.
501 uint32_t child_data_size = 10; // Size of the last child returndata.
502
504 StrictMock<MockContext> context;
505 EXPECT_CALL(context, get_memory).WillRepeatedly(ReturnRef(mem));
506 EXPECT_CALL(context, get_last_rd_size).WillRepeatedly(Return(child_data_size));
507 EXPECT_CALL(context, has_parent).WillRepeatedly(Return(true));
508 EXPECT_CALL(context, get_last_rd_addr).WillRepeatedly(Return(child_rd_addr));
509 EXPECT_CALL(context, get_context_id).WillRepeatedly(Return(context_id));
510 EXPECT_CALL(context, get_last_child_id).WillRepeatedly(Return(child_context_id));
511
512 StrictMock<MockExecutionIdManager> execution_id_manager;
513 EXPECT_CALL(execution_id_manager, get_execution_id()).WillOnce(Return(0));
514
515 FakeGreaterThan gt;
516
517 EventEmitter<DataCopyEvent> event_emitter;
518 DataCopy copy_data = DataCopy(execution_id_manager, gt, event_emitter);
519
520 TestTraceContainer trace({
521 {
522 { C::precomputed_first_row, 1 },
523 { C::execution_sel, 1 },
524 { C::execution_context_id, context_id },
525 { C::execution_last_child_id, child_context_id },
526 { C::execution_sel_execute_returndata_copy, 1 },
527 { C::execution_register_0_, copy_size },
528 { C::execution_register_1_, rd_offset },
529 { C::execution_rop_2_, big_dst_addr },
530 { C::execution_sel_opcode_error, 1 }, // Error flag is on
531 { C::execution_last_child_returndata_addr, child_rd_addr },
532 { C::execution_last_child_returndata_size, child_data_size },
533 },
534 });
535
536 EXPECT_THROW_WITH_MESSAGE(copy_data.rd_copy(context, copy_size, rd_offset, big_dst_addr),
537 "Error during CD/RD copy");
538
539 DataCopyTraceBuilder builder;
540 builder.process(event_emitter.dump_events(), trace);
541
542 check_relation<data_copy>(trace);
543 check_interaction<DataCopyTraceBuilder,
546}
547
548} // namespace
549} // namespace bb::avm2::constraining
#define AVM_HIGHEST_MEM_ADDRESS
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.
AluTraceBuilder builder
Definition alu.test.cpp:123
GreaterThanTraceBuilder gt_builder
Definition alu.test.cpp:122
DataCopy copy_data
EventEmitter< GreaterThanEvent > gt_event_emitter
ExecutionIdManager execution_id_manager
MemoryStore mem
EventEmitter< RangeCheckEvent > range_check_event_emitter
const std::vector< FF > data
StrictMock< MockFieldGreaterThan > mock_field_gt
EventEmitter< DataCopyEvent > event_emitter
uint32_t dst_addr
RangeCheck range_check
GreaterThan gt
TestTraceContainer trace
StrictMock< MockContext > context
ssize_t offset
Definition engine.cpp:36
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessage)
Definition macros.hpp:7
TEST_F(AvmRecursiveTests, GoblinRecursion)
A test of the Goblinized AVM recursive verifier.
void check_interaction(tracegen::TestTraceContainer &trace)
TEST(TxExecutionConstrainingTest, WriteTreeValue)
Definition tx.test.cpp:508
lookup_settings< lookup_data_copy_col_read_settings_ > lookup_data_copy_col_read_settings
lookup_settings< lookup_data_copy_check_dst_addr_in_range_settings_ > lookup_data_copy_check_dst_addr_in_range_settings
permutation_settings< perm_data_copy_dispatch_rd_copy_settings_ > perm_data_copy_dispatch_rd_copy_settings
permutation_settings< perm_data_copy_dispatch_cd_copy_settings_ > perm_data_copy_dispatch_cd_copy_settings
lookup_settings< lookup_data_copy_max_read_index_gt_settings_ > lookup_data_copy_max_read_index_gt_settings
lookup_settings< lookup_data_copy_check_src_addr_in_range_settings_ > lookup_data_copy_check_src_addr_in_range_settings
lookup_settings< lookup_data_copy_offset_gt_max_read_index_settings_ > lookup_data_copy_offset_gt_max_read_index_settings
uint32_t MemoryAddress
typename Flavor::FF FF
uint32_t child_rd_addr
uint32_t parent_cd_addr
uint32_t child_context_id
DataCopy data_copy
uint32_t cd_offset