Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
memory.test.cpp
Go to the documentation of this file.
1#include <gmock/gmock.h>
2#include <gtest/gtest.h>
3
4#include <cstdint>
5
17
18namespace bb::avm2::constraining {
19namespace {
20
21using simulation::MemoryEvent;
22using simulation::RangeCheckEvent;
23
24using tracegen::MemoryTraceBuilder;
25using tracegen::PrecomputedTraceBuilder;
26using tracegen::RangeCheckTraceBuilder;
27using tracegen::TestTraceContainer;
29using C = Column;
31
32TEST(MemoryConstrainingTest, EmptyRow)
33{
34 check_relation<memory>(testing::empty_trace());
35}
36
37// Several memory events with trace generation.
38TEST(MemoryConstrainingTest, MultipleEventsWithTraceGen)
39{
40 TestTraceContainer trace;
41 MemoryTraceBuilder memory_trace_builder;
42 PrecomputedTraceBuilder precomputed_trace_builder;
43 RangeCheckTraceBuilder range_check_trace_builder;
44
45 std::vector<MemoryEvent> mem_events = {
46 // 1) READ: space_id = 17, addr = 120, clk = 13787, value = 0, tag = FF
47 {
48 .execution_clk = 13787,
50 .addr = 120,
52 .space_id = 17,
53 },
54 // 2) WRITE: space_id = 17, addr = 120, clk = 13787, value = 12345, tag = U16
55 {
56 .execution_clk = 13787,
58 .addr = 120,
60 .space_id = 17,
61 },
62 // 3) WRITE: space_id = 17, addr = 120, clk = 13788, value = 123, tag = U32
63 {
64 .execution_clk = 13788,
66 .addr = 120,
68 .space_id = 17,
69 },
70 // 4) READ: space_id = 17, addr = 120, clk = 25000, value = 123, tag = U32
71 {
72 .execution_clk = 25000,
74 .addr = 120,
76 .space_id = 17,
77 },
78 // 5) WRITE: space_id = 17, addr = 121, clk = 45, value = 99999, tag = U128
79 {
80 .execution_clk = 45,
82 .addr = 121,
84 .space_id = 17,
85 },
86 // 6) READ: space_id = 17, addr = 121, clk = 49, value = 99999, tag = U128
87 {
88 .execution_clk = 49,
90 .addr = 121,
92 .space_id = 17,
93 },
94 // 7) READ: space_id = 17, addr = 121, clk = 49, value = 99999, tag = U128
95 {
96 .execution_clk = 49,
98 .addr = 121,
100 .space_id = 17,
101 },
102 // 8) READ: space_id = 17, addr = 121, clk = 765, value = 99999, tag = U128
103 {
104 .execution_clk = 765,
106 .addr = 121,
108 .space_id = 17,
109 },
110 // 9) WRITE: space_id = 18, addr = 2, clk = 10, value = p-1, tag = FF
111 {
112 .execution_clk = 10,
114 .addr = 2,
115 .value = MemoryValue::from_tag(MemoryTag::FF, FF::modulus - 1),
116 .space_id = 18,
117 },
118 };
119
120 // Range check event per non-FF memory write event.
121 std::vector<RangeCheckEvent> range_check_events = {
122 {
123 .value = 12345,
124 .num_bits = 16,
125 },
126 {
127 .value = 123,
128 .num_bits = 32,
129 },
130 {
131 .value = 99999,
132 .num_bits = 128,
133 },
134 };
135
136 precomputed_trace_builder.process_sel_range_8(trace);
137 precomputed_trace_builder.process_sel_range_16(trace);
138 precomputed_trace_builder.process_misc(trace, 1 << 16);
139 precomputed_trace_builder.process_tag_parameters(trace);
140 range_check_trace_builder.process(range_check_events, trace);
141 memory_trace_builder.process(mem_events, trace);
142
143 // For the selector consistency, we need to make the read/write come from some trace.
144 trace.visit_column(Column::memory_sel,
145 [&](uint32_t row, const FF&) { trace.set(Column::memory_sel_register_op_0_, row, 1); });
146
147 check_relation<memory>(trace);
148 check_all_interactions<MemoryTraceBuilder>(trace);
149}
150
151// Trace must be contiguous.
152TEST(MemoryConstrainingTest, ContiguousTrace)
153{
154 TestTraceContainer trace({
155 { { C::precomputed_first_row, 1 }, { C::memory_sel, 0 } },
156 { { C::memory_sel, 1 } },
157 { { C::memory_sel, 1 } },
158 { { C::memory_sel, 1 } },
159 { { C::memory_sel, 0 } },
160 });
161
162 check_relation<memory>(trace, memory::SR_MEM_CONTIGUOUS);
163
164 // Mutate the trace to make it non-contiguous.
165 trace.set(C::memory_sel, 2, 0);
166 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_MEM_CONTIGUOUS), "MEM_CONTIGUOUS");
167}
168
169// Boolean selector for range check is active at all active rows except the last one.
170TEST(MemoryConstrainingTest, SelRngChk)
171{
172 TestTraceContainer trace({
173 { { C::memory_sel, 1 }, { C::memory_sel_rng_chk, 1 } },
174 { { C::memory_sel, 1 }, { C::memory_sel_rng_chk, 1 } },
175 { { C::memory_sel, 1 }, { C::memory_sel_rng_chk, 0 } },
176 { { C::memory_sel, 0 }, { C::memory_sel_rng_chk, 0 } },
177 });
178
179 check_relation<memory>(trace, memory::SR_SEL_RNG_CHK);
180
181 // Disable the range check for the penultimate row.
182 trace.set(C::memory_sel_rng_chk, 1, 0);
183 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_SEL_RNG_CHK), "SEL_RNG_CHK");
184
185 // Reset
186 trace.set(C::memory_sel_rng_chk, 1, 1);
187
188 // Disable the range check at the first row.
189 trace.set(C::memory_sel_rng_chk, 0, 0);
190 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_SEL_RNG_CHK), "SEL_RNG_CHK");
191
192 // Reset
193 trace.set(C::memory_sel_rng_chk, 0, 1);
194
195 // Enable the range check at the last active row.
196 trace.set(C::memory_sel_rng_chk, 2, 1);
197 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_SEL_RNG_CHK), "SEL_RNG_CHK");
198}
199
200// Global address is derived from space_id and address.
201TEST(MemoryConstrainingTest, GlobalAddr)
202{
203 TestTraceContainer trace({
204 { { C::memory_sel, 1 },
205 { C::memory_space_id, 12345 },
206 { C::memory_address, 6789 },
207 { C::memory_global_addr, (12345LLU << 32) + 6789 } },
208 { { C::memory_sel, 1 },
209 { C::memory_space_id, UINT16_MAX },
210 { C::memory_address, UINT32_MAX },
211 { C::memory_global_addr, UINT64_MAX >> 16 } },
212 { { C::memory_sel, 1 },
213 { C::memory_space_id, 0 },
214 { C::memory_address, 987654321 },
215 { C::memory_global_addr, 987654321 } },
216 { { C::memory_sel, 1 },
217 { C::memory_space_id, 1LLU << 12 },
218 { C::memory_address, 0 },
219 { C::memory_global_addr, 1LLU << 44 } },
220 });
221
222 check_relation<memory>(trace, memory::SR_GLOBAL_ADDR);
223
224 // Mutate the trace to make the global address incorrect.
225 trace.set(C::memory_global_addr, 1, trace.get(C::memory_global_addr, 1) + 1);
226 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_GLOBAL_ADDR), "GLOBAL_ADDR");
227
228 // Reset
229 trace.set(C::memory_global_addr, 1, trace.get(C::memory_global_addr, 1) - 1);
230 check_relation<memory>(trace, memory::SR_GLOBAL_ADDR);
231
232 // Mutate the trace to make the global address == address. (No space_id.)
233 trace.set(C::memory_global_addr, 2, UINT32_MAX);
234 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_GLOBAL_ADDR), "GLOBAL_ADDR");
235}
236
237// Timestamp is derived from clk and rw.
238TEST(MemoryConstrainingTest, Timestamp)
239{
240 TestTraceContainer trace({
241 { { C::memory_sel, 1 }, { C::memory_clk, 1 }, { C::memory_rw, 0 }, { C::memory_timestamp, 2 } },
242 { { C::memory_sel, 1 }, { C::memory_clk, 2 }, { C::memory_rw, 1 }, { C::memory_timestamp, 5 } },
243 { { C::memory_sel, 1 }, { C::memory_clk, 3 }, { C::memory_rw, 0 }, { C::memory_timestamp, 6 } },
244 { { C::memory_sel, 1 },
245 { C::memory_clk, UINT32_MAX },
246 { C::memory_rw, 1 },
247 { C::memory_timestamp, (1LLU << 33) - 1 } },
248 });
249
250 check_relation<memory>(trace, memory::SR_TIMESTAMP);
251
252 // Mutate the trace to make the timestamp incorrect.
253 trace.set(C::memory_timestamp, 1, trace.get(C::memory_timestamp, 1) + 1);
254 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_TIMESTAMP), "TIMESTAMP");
255
256 // Reset
257 trace.set(C::memory_timestamp, 1, trace.get(C::memory_timestamp, 1) - 1);
258 check_relation<memory>(trace, memory::SR_TIMESTAMP);
259
260 // Mutate the trace to make the timestamp == clk. (No rw.)
261 trace.set(C::memory_timestamp, 3, UINT32_MAX);
262 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_TIMESTAMP), "TIMESTAMP");
263}
264
265// last_access is derived from global_addr and global_addr'
266TEST(MemoryConstrainingTest, LastAccess)
267{
268 TestTraceContainer trace({
269 { { C::memory_sel_rng_chk, 1 },
270 { C::memory_global_addr, 12345 },
271 { C::memory_last_access, 1 },
272 { C::memory_glob_addr_diff_inv, 1 } },
273 { { C::memory_sel_rng_chk, 1 }, { C::memory_global_addr, 12346 }, { C::memory_last_access, 0 } },
274 { { C::memory_sel_rng_chk, 1 }, { C::memory_global_addr, 12346 }, { C::memory_last_access, 0 } },
275 { { C::memory_sel_rng_chk, 1 },
276 { C::memory_global_addr, 12346 },
277 { C::memory_last_access, 1 },
278 { C::memory_glob_addr_diff_inv, 1 } },
279 { { C::memory_sel_rng_chk, 0 },
280 { C::memory_global_addr, 12347 },
281 { C::memory_last_access, 1 },
282 { C::memory_glob_addr_diff_inv, 1 } },
283 });
284
285 check_relation<memory>(trace, memory::SR_LAST_ACCESS);
286
287 // Mutate the trace to make the last access incorrect (last_access == 0 instead of 1).
288 trace.set(C::memory_last_access, 0, 0);
289 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_LAST_ACCESS), "LAST_ACCESS");
290
291 // Reset
292 trace.set(C::memory_last_access, 0, 1);
293 check_relation<memory>(trace, memory::SR_LAST_ACCESS);
294
295 // Mutate glob_addr_diff_inv == 0.
296 trace.set(C::memory_glob_addr_diff_inv, 0, 0);
297 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_LAST_ACCESS), "LAST_ACCESS");
298
299 // Reset
300 trace.set(C::memory_glob_addr_diff_inv, 0, 1);
301 check_relation<memory>(trace, memory::SR_LAST_ACCESS);
302
303 // Mutate the trace to make the last access == 1, instead of 0.
304 trace.set(C::memory_last_access, 2, 1);
305 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_LAST_ACCESS), "LAST_ACCESS");
306}
307
308// diff is derived as global_addr' - global_addr when last_access == 1.
309TEST(MemoryConstrainingTest, DiffWithLastAccess)
310{
311 // We set some dummy values for timestamp and rw to ensure that they do not interfer with diff derivation.
312 TestTraceContainer trace({
313 { { C::memory_sel_rng_chk, 1 },
314 { C::memory_global_addr, 12345 },
315 { C::memory_last_access, 1 },
316 { C::memory_diff, 10000 },
317 { C::memory_timestamp, 76 },
318 { C::memory_rw, 1 } },
319 { { C::memory_sel_rng_chk, 1 },
320 { C::memory_global_addr, 22345 },
321 { C::memory_last_access, 1 },
322 { C::memory_diff, 12 },
323 { C::memory_timestamp, 254 },
324 { C::memory_rw, 1 } },
325 { { C::memory_sel_rng_chk, 1 },
326 { C::memory_global_addr, 22357 },
327 { C::memory_last_access, 1 },
328 { C::memory_diff, FF(-22357) },
329 { C::memory_timestamp, 259 },
330 { C::memory_rw, 1 } },
331 { { C::memory_sel_rng_chk, 0 }, { C::memory_last_access, 0 } },
332 });
333
334 check_relation<memory>(trace, memory::SR_DIFF);
335
336 // Mutate the trace to make the diff incorrect.
337 trace.set(C::memory_diff, 1, trace.get(C::memory_diff, 1) + 1);
338 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_DIFF), "DIFF");
339}
340
341// diff is derived as timestamp' - timestamp - rw' * rw when last_access == 0.
342TEST(MemoryConstrainingTest, DiffWithoutLastAccess)
343{
344 TestTraceContainer trace({
345 { { C::memory_sel_rng_chk, 1 }, { C::memory_timestamp, 77 }, { C::memory_rw, 1 }, { C::memory_diff, 1 } },
346 { { C::memory_sel_rng_chk, 1 }, { C::memory_timestamp, 79 }, { C::memory_rw, 1 }, { C::memory_diff, 8700 } },
347 { { C::memory_sel_rng_chk, 1 }, { C::memory_timestamp, 8779 }, { C::memory_rw, 0 }, { C::memory_diff, 10000 } },
348 { { C::memory_sel_rng_chk, 1 }, { C::memory_timestamp, 18779 }, { C::memory_rw, 0 }, { C::memory_diff, 2 } },
349 { { C::memory_sel_rng_chk, 1 },
350 { C::memory_timestamp, 18781 },
351 { C::memory_rw, 1 },
352 { C::memory_diff, FF(-18781) } },
353 { { C::memory_sel_rng_chk, 0 }, { C::memory_last_access, 0 } },
354 });
355
356 check_relation<memory>(trace, memory::SR_DIFF);
357
358 // Mutate the trace to make the diff incorrect.
359 trace.set(C::memory_diff, 0, trace.get(C::memory_diff, 0) + 1);
360 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_DIFF), "DIFF");
361
362 // Reset
363 trace.set(C::memory_diff, 0, trace.get(C::memory_diff, 0) - 1);
364 check_relation<memory>(trace, memory::SR_DIFF);
365
366 // Mutate the trace to make the diff incorrect.
367 trace.set(C::memory_diff, 1, trace.get(C::memory_diff, 1) + 1);
368 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_DIFF), "DIFF");
369}
370
371// diff correct decomposition into 3 16-bit limbs.
372TEST(MemoryConstrainingTest, DiffDecomp)
373{
374 TestTraceContainer trace({
375 { { C::memory_diff, 87 }, { C::memory_limb_0_, 87 }, { C::memory_limb_1_, 0 }, { C::memory_limb_2_, 0 } },
376 { { C::memory_diff, 1ULL << 16 },
377 { C::memory_limb_0_, 0 },
378 { C::memory_limb_1_, 1 },
379 { C::memory_limb_2_, 0 } },
380 { { C::memory_diff, 1ULL << 32 },
381 { C::memory_limb_0_, 0 },
382 { C::memory_limb_1_, 0 },
383 { C::memory_limb_2_, 1 } },
384 { { C::memory_diff, UINT64_MAX >> 16 },
385 { C::memory_limb_0_, UINT16_MAX },
386 { C::memory_limb_1_, UINT16_MAX },
387 { C::memory_limb_2_, UINT16_MAX } },
388 });
389
390 check_relation<memory>(trace, memory::SR_DIFF_DECOMP);
391
392 // Mutate the trace to make the diff decomposition incorrect.
393 trace.set(C::memory_limb_0_, 0, trace.get(C::memory_limb_0_, 0) + 1);
394 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_DIFF_DECOMP), "DIFF_DECOMP");
395
396 // Reset
397 trace.set(C::memory_limb_0_, 0, trace.get(C::memory_limb_0_, 0) - 1);
398 check_relation<memory>(trace, memory::SR_DIFF_DECOMP);
399
400 // Mutate the trace to make the diff decomposition incorrect.
401 trace.set(C::memory_limb_1_, 1, trace.get(C::memory_limb_1_, 1) + 1);
402 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_DIFF_DECOMP), "DIFF_DECOMP");
403
404 // Reset
405 trace.set(C::memory_limb_1_, 1, trace.get(C::memory_limb_1_, 1) - 1);
406 check_relation<memory>(trace, memory::SR_DIFF_DECOMP);
407
408 // Mutate the trace to make the diff decomposition incorrect.
409 trace.set(C::memory_limb_2_, 2, trace.get(C::memory_limb_2_, 2) + 1);
410 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_DIFF_DECOMP), "DIFF_DECOMP");
411}
412
413// Correct memory value (and tag) initialization after first row.
414TEST(MemoryConstrainingTest, MemoryInitValueFirstRow)
415{
416 TestTraceContainer trace({
417 { { C::precomputed_first_row, 1 } },
418 { { C::memory_sel, 1 }, { C::memory_value, 0 }, { C::memory_tag, static_cast<uint8_t>(MemoryTag::FF) } },
419 });
420
422
423 // Mutate the trace to make the memory value incorrect.
424 trace.set(C::memory_value, 1, trace.get(C::memory_value, 1) + 1);
425 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_MEMORY_INIT_VALUE), "MEMORY_INIT_VALUE");
426
427 // Reset
428 trace.set(C::memory_value, 1, trace.get(C::memory_value, 1) - 1);
430
431 // Mutate the trace to make the memory tag incorrect.
432 trace.set(C::memory_tag, 1, static_cast<uint8_t>(MemoryTag::U16));
433 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_MEMORY_INIT_TAG), "MEMORY_INIT_TAG");
434}
435
436// Correct memory value (and tag) initialization after last_access == 1.
437TEST(MemoryConstrainingTest, MemoryInitValueLastAccess)
438{
439 TestTraceContainer trace({
440 { { C::memory_sel, 1 }, { C::memory_last_access, 1 } },
441 { { C::memory_sel, 1 }, { C::memory_value, 0 }, { C::memory_tag, static_cast<uint8_t>(MemoryTag::FF) } },
442 });
443
445
446 // Mutate the trace to make the memory value incorrect.
447 trace.set(C::memory_value, 1, trace.get(C::memory_value, 1) + 1);
448 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_MEMORY_INIT_VALUE), "MEMORY_INIT_VALUE");
449
450 // Reset
451 trace.set(C::memory_value, 1, trace.get(C::memory_value, 1) - 1);
453
454 // Mutate the trace to make the memory tag incorrect.
455 trace.set(C::memory_tag, 1, static_cast<uint8_t>(MemoryTag::U1));
456 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_MEMORY_INIT_TAG), "MEMORY_INIT_TAG");
457}
458
459// Correct read-write consistency for memory value (and tag).
460TEST(MemoryConstrainingTest, ReadWriteConsistency)
461{
462 TestTraceContainer trace({
463 { { C::memory_sel, 1 },
464 { C::memory_rw, 1 },
465 { C::memory_value, 12 },
466 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U8) } }, // Write U8(12)
467 { { C::memory_sel, 1 },
468 { C::memory_rw, 0 },
469 { C::memory_value, 12 },
470 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U8) } }, // Read U8(12)
471 { { C::memory_sel, 1 },
472 { C::memory_rw, 1 },
473 { C::memory_value, 17 },
474 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U64) } }, // Write U64(17)
475 { { C::memory_sel, 1 },
476 { C::memory_rw, 1 },
477 { C::memory_value, 12345 },
478 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U128) } }, // Write U128(12345)
479 { { C::memory_sel, 1 },
480 { C::memory_rw, 0 },
481 { C::memory_value, 12345 },
482 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U128) } }, // Read U128(12345)
483 { { C::memory_sel, 1 },
484 { C::memory_last_access, 1 },
485 { C::memory_rw, 0 },
486 { C::memory_value, 12345 },
487 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U128) } }, // Read U128(12345)
488 });
489
491
492 // Mutate the trace to make teh first read value (row 1) incorrect.
493 trace.set(C::memory_value, 1, trace.get(C::memory_value, 1) + 1);
495 "READ_WRITE_CONSISTENCY_VALUE");
496
497 // Reset
498 trace.set(C::memory_value, 1, trace.get(C::memory_value, 1) - 1);
500
501 // Mutate the trace to make the first read tag (row 1) incorrect.
502 trace.set(C::memory_tag, 1, static_cast<uint8_t>(MemoryTag::U16));
504 "READ_WRITE_CONSISTENCY_TAG");
505}
506
507// Selector on tag == FF.
508TEST(MemoryConstrainingTest, TagIsFF)
509{
510 TestTraceContainer trace({
511 { { C::memory_sel, 1 },
512 { C::memory_tag, static_cast<uint8_t>(MemoryTag::FF) },
513 { C::memory_sel_tag_is_ff, 1 } },
514 { { C::memory_sel, 1 },
515 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U1) },
516 { C::memory_sel_tag_is_ff, 0 },
517 { C::memory_tag_ff_diff_inv,
518 (FF(static_cast<uint8_t>(MemoryTag::U1)) - FF(static_cast<uint8_t>(MemoryTag::FF))).invert() } },
519 { { C::memory_sel, 1 },
520 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U8) },
521 { C::memory_sel_tag_is_ff, 0 },
522 { C::memory_tag_ff_diff_inv,
523 (FF(static_cast<uint8_t>(MemoryTag::U8)) - FF(static_cast<uint8_t>(MemoryTag::FF))).invert() } },
524 { { C::memory_sel, 1 },
525 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U16) },
526 { C::memory_sel_tag_is_ff, 0 },
527 { C::memory_tag_ff_diff_inv,
528 (FF(static_cast<uint8_t>(MemoryTag::U16)) - FF(static_cast<uint8_t>(MemoryTag::FF))).invert() } },
529 { { C::memory_sel, 1 },
530 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U32) },
531 { C::memory_sel_tag_is_ff, 0 },
532 { C::memory_tag_ff_diff_inv,
533 (FF(static_cast<uint8_t>(MemoryTag::U32)) - FF(static_cast<uint8_t>(MemoryTag::FF))).invert() } },
534 { { C::memory_sel, 1 },
535 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U64) },
536 { C::memory_sel_tag_is_ff, 0 },
537 { C::memory_tag_ff_diff_inv,
538 (FF(static_cast<uint8_t>(MemoryTag::U64)) - FF(static_cast<uint8_t>(MemoryTag::FF))).invert() } },
539 { { C::memory_sel, 1 },
540 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U128) },
541 { C::memory_sel_tag_is_ff, 0 },
542 { C::memory_tag_ff_diff_inv,
543 (FF(static_cast<uint8_t>(MemoryTag::U128)) - FF(static_cast<uint8_t>(MemoryTag::FF))).invert() } },
544 });
545
546 check_relation<memory>(trace, memory::SR_TAG_IS_FF);
547
548 // Attempt to de-activate sel_tag_is_ff when tag == FF.
549 trace.set(C::memory_sel_tag_is_ff, 0, 0);
550 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_TAG_IS_FF), "TAG_IS_FF");
551
552 // Try to change value for diff_inv
553 trace.set(C::memory_tag_ff_diff_inv, 0, 1);
554 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_TAG_IS_FF), "TAG_IS_FF");
555
556 // Reset
557 trace.set(C::memory_sel_tag_is_ff, 0, 1);
558 trace.set(C::memory_tag_ff_diff_inv, 0, 0);
559 check_relation<memory>(trace, memory::SR_TAG_IS_FF);
560
561 // Attempt to activate sel_tag_is_ff when tag != FF.
562 trace.set(C::memory_sel_tag_is_ff, 1, 1);
563 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_TAG_IS_FF), "TAG_IS_FF");
564
565 // Try to modify value for tag_ff_diff_inv
566 trace.set(C::memory_tag_ff_diff_inv, 1, 0);
567 EXPECT_THROW_WITH_MESSAGE(check_relation<memory>(trace, memory::SR_TAG_IS_FF), "TAG_IS_FF");
568}
569
570// Boolean selector sel_rng_write is active for write operations and tag != FF.
571TEST(MemoryConstrainingTest, SelRngWrite)
572{
573 TestTraceContainer trace({
574 { { C::memory_sel, 1 }, { C::memory_rw, 1 }, { C::memory_sel_tag_is_ff, 1 }, { C::memory_sel_rng_write, 0 } },
575 { { C::memory_sel, 1 }, { C::memory_rw, 1 }, { C::memory_sel_tag_is_ff, 0 }, { C::memory_sel_rng_write, 1 } },
576 { { C::memory_sel, 1 }, { C::memory_rw, 0 }, { C::memory_sel_tag_is_ff, 1 }, { C::memory_sel_rng_write, 0 } },
577 { { C::memory_sel, 1 }, { C::memory_rw, 0 }, { C::memory_sel_tag_is_ff, 0 }, { C::memory_sel_rng_write, 0 } },
578 });
579
580 check_relation<memory>(trace, memory::SR_SEL_RNG_WRITE);
581}
582
583// Negative test: attempts to write a value which is not present in the range check trace.
584TEST(MemoryConstrainingTest, NegativeWriteValueOutOfRange)
585{
586 TestTraceContainer trace({
587 { { C::memory_sel, 1 },
588 { C::memory_rw, 1 },
589 { C::memory_value, 12345 },
590 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U16) },
591 { C::memory_sel_rng_write, 1 },
592 { C::memory_max_bits, 128 },
593 { C::range_check_sel, 1 },
594 { C::range_check_value, 12345 },
595 { C::range_check_rng_chk_bits, 128 } },
596 });
597
598 check_interaction<MemoryTraceBuilder, lookup_memory_range_check_write_tagged_value_settings>(trace);
599
600 // Mutate the trace to make the value incorrect in range check.
601 trace.set(C::range_check_value, 0, trace.get(C::range_check_value, 1) + 1);
603 (check_interaction<MemoryTraceBuilder, lookup_memory_range_check_write_tagged_value_settings>(trace)),
604 "Failed.*RANGE_CHECK_WRITE_TAGGED_VALUE. Could not find tuple in destination.");
605}
606
607// Negative test: retrieve wrong max_bits value from precomputed table.
608TEST(MemoryConstrainingTest, NegativeMaxBitsOutOfRange)
609{
610 TestTraceContainer trace({
611 { { C::memory_sel, 1 },
612 { C::memory_sel_rng_write, 1 },
613 { C::memory_tag, static_cast<uint8_t>(MemoryTag::U32) },
614 { C::memory_max_bits, 32 } },
615 });
616
617 PrecomputedTraceBuilder precomputed_trace_builder;
618 precomputed_trace_builder.process_tag_parameters(trace);
619 precomputed_trace_builder.process_misc(trace, 100); // 100 is an arbitrary upper bound for the number of tags.
620
621 check_interaction<MemoryTraceBuilder, lookup_memory_tag_max_bits_settings>(trace);
622
623 // Mutate the trace to make the max_bits incorrect.
624 trace.set(C::memory_max_bits, 0, trace.get(C::memory_max_bits, 0) + 1);
625 EXPECT_THROW_WITH_MESSAGE((check_interaction<MemoryTraceBuilder, lookup_memory_tag_max_bits_settings>(trace)),
626 "Failed.*LOOKUP_MEMORY_TAG_MAX_BITS. Could not find tuple in destination.");
627}
628
629// Negative test: limbs of diff cannot be larger than 16 bits.
630TEST(MemoryConstrainingTest, NegativeDiffLimbOutOfRange)
631{
632 TestTraceContainer trace({
633 { { C::memory_sel, 1 },
634 { C::memory_sel_rng_chk, 1 },
635 { C::memory_limb_0_, UINT16_MAX },
636 { C::memory_limb_1_, UINT16_MAX },
637 { C::memory_limb_2_, UINT16_MAX } },
638 });
639
640 PrecomputedTraceBuilder precomputed_trace_builder;
641 precomputed_trace_builder.process_misc(trace, 1 << 16);
642 precomputed_trace_builder.process_sel_range_16(trace);
643
644 check_interaction<MemoryTraceBuilder,
648
649 // Mutate the trace to make the limb_0 incorrect.
650 trace.set(C::memory_limb_0_, 0, UINT16_MAX + 1);
651 EXPECT_THROW_WITH_MESSAGE((check_interaction<MemoryTraceBuilder, lookup_memory_range_check_limb_0_settings>(trace)),
652 "Failed.*RANGE_CHECK_LIMB_0. Could not find tuple in destination.");
653
654 check_interaction<MemoryTraceBuilder, lookup_memory_range_check_limb_1_settings>(trace);
655
656 // Mutate the trace to make the limb_1 incorrect.
657 trace.set(C::memory_limb_1_, 0, UINT16_MAX + 1);
658 EXPECT_THROW_WITH_MESSAGE((check_interaction<MemoryTraceBuilder, lookup_memory_range_check_limb_1_settings>(trace)),
659 "Failed.*RANGE_CHECK_LIMB_1. Could not find tuple in destination.");
660
661 check_interaction<MemoryTraceBuilder, lookup_memory_range_check_limb_2_settings>(trace);
662
663 // Mutate the trace to make the limb_2 incorrect.
664 trace.set(C::memory_limb_2_, 0, UINT16_MAX + 1);
665 EXPECT_THROW_WITH_MESSAGE((check_interaction<MemoryTraceBuilder, lookup_memory_range_check_limb_2_settings>(trace)),
666 "Failed.*RANGE_CHECK_LIMB_2. Could not find tuple in destination.");
667}
668
669} // namespace
670} // namespace bb::avm2::constraining
static TaggedValue from_tag(ValueTag tag, FF value)
static constexpr size_t SR_TAG_IS_FF
Definition memory.hpp:53
static constexpr size_t SR_READ_WRITE_CONSISTENCY_TAG
Definition memory.hpp:52
static constexpr size_t SR_DIFF
Definition memory.hpp:47
static constexpr size_t SR_LAST_ACCESS
Definition memory.hpp:46
static constexpr size_t SR_READ_WRITE_CONSISTENCY_VALUE
Definition memory.hpp:51
static constexpr size_t SR_MEMORY_INIT_TAG
Definition memory.hpp:50
static constexpr size_t SR_MEMORY_INIT_VALUE
Definition memory.hpp:49
static constexpr size_t SR_MEM_CONTIGUOUS
Definition memory.hpp:42
static constexpr size_t SR_TIMESTAMP
Definition memory.hpp:45
static constexpr size_t SR_GLOBAL_ADDR
Definition memory.hpp:44
static constexpr size_t SR_SEL_RNG_WRITE
Definition memory.hpp:54
static constexpr size_t SR_DIFF_DECOMP
Definition memory.hpp:48
static constexpr size_t SR_SEL_RNG_CHK
Definition memory.hpp:43
const FF & get(Column col, uint32_t row) const
void visit_column(Column col, const std::function< void(uint32_t, const FF &)> &visitor) const
void set(Column col, uint32_t row, const FF &value)
TestTraceContainer trace
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessage)
Definition macros.hpp:7
void check_interaction(tracegen::TestTraceContainer &trace)
TEST(TxExecutionConstrainingTest, WriteTreeValue)
Definition tx.test.cpp:402
TestTraceContainer empty_trace()
Definition fixtures.cpp:153
lookup_settings< lookup_memory_range_check_limb_0_settings_ > lookup_memory_range_check_limb_0_settings
lookup_settings< lookup_memory_range_check_limb_1_settings_ > lookup_memory_range_check_limb_1_settings
lookup_settings< lookup_memory_range_check_limb_2_settings_ > lookup_memory_range_check_limb_2_settings
AvmFlavorSettings::FF FF
Definition field.hpp:10
typename Flavor::FF FF
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
MemoryStore memory