Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
alu.test.cpp
Go to the documentation of this file.
1#include <gmock/gmock.h>
2#include <gtest/gtest.h>
3
4#include <cstdint>
5#include <utility>
6#include <vector>
7
33
34namespace bb::avm2::constraining {
35namespace {
36
37using tracegen::TestTraceContainer;
39using C = Column;
40using alu = bb::avm2::alu<FF>;
41using simulation::RangeCheckEvent;
42using tracegen::AluTraceBuilder;
43using tracegen::ExecutionTraceBuilder;
44using tracegen::FieldGreaterThanTraceBuilder;
45using tracegen::GreaterThanTraceBuilder;
46using tracegen::PrecomputedTraceBuilder;
47using tracegen::RangeCheckTraceBuilder;
48
49constexpr uint8_t NUM_OF_TAGS = static_cast<uint8_t>(MemoryTag::MAX) + 1;
50
51// Generic structure for three-operand opcodes
52using ThreeOperandTestParams = std::tuple<MemoryValue, MemoryValue, MemoryValue>;
53
54// Generic structure for two-operand opcodes
55using TwoOperandTestParams = std::tuple<MemoryValue, MemoryValue>;
56
58 {
61 },
62 {
65 },
66 {
69 },
70 {
73 },
74 {
77 },
78 {
81 },
82 {
85 },
86};
87
88const std::unordered_map<MemoryTag, MemoryTag> TAG_ERROR_TEST_VALUES = {
92};
93
95{
97 uint32_t i = 0;
98 for (const auto c : out) {
99 ThreeOperandTestParams params = tuple_cat(TEST_VALUES_IN.at(i), std::make_tuple(c));
100 res.push_back(params);
101 i++;
102 }
103 return res;
104}
105
107{
109 uint32_t i = 0;
110 for (const auto c : out) {
111 TwoOperandTestParams params = std::make_tuple(std::get<0>(TEST_VALUES_IN.at(i)), c);
112 res.push_back(params);
113 i++;
114 }
115 return res;
116}
117
118class AluConstrainingTest : public ::testing::Test {
119 public:
120 PrecomputedTraceBuilder precomputed_builder;
121 RangeCheckTraceBuilder range_check_builder;
122 FieldGreaterThanTraceBuilder field_gt_builder;
123 GreaterThanTraceBuilder gt_builder;
124 AluTraceBuilder builder;
125};
126
127TEST_F(AluConstrainingTest, EmptyRow)
128{
129 check_relation<alu>(testing::empty_trace());
130}
131
132TEST_F(AluConstrainingTest, NegativeAluWrongOpId)
133{
134 auto trace = TestTraceContainer({
135 {
136 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_ADD + 1 },
137 { C::alu_sel_op_add, 1 },
138 },
139 });
140
141 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace, alu::SR_DISPATCH_OPERATION), "DISPATCH_OPERATION");
142}
143
144// ADD TESTS
145
146const std::vector<MemoryValue> TEST_VALUES_ADD_OUT = {
154};
155
156const std::vector<ThreeOperandTestParams> TEST_VALUES_ADD = zip_helper(TEST_VALUES_ADD_OUT);
157
158class AluAddConstrainingTest : public AluConstrainingTest,
159 public ::testing::WithParamInterface<ThreeOperandTestParams> {
160 public:
161 TestTraceContainer process_basic_add_trace(ThreeOperandTestParams params)
162 {
163 auto [a, b, c] = params;
164 auto tag = static_cast<uint8_t>(a.get_tag());
165 auto trace = TestTraceContainer({
166 {
167 { C::alu_ia, a },
168 { C::alu_ia_tag, tag },
169 { C::alu_ib, b },
170 { C::alu_ib_tag, tag },
171 { C::alu_ic, c },
172 { C::alu_ic_tag, tag },
173 { C::alu_max_bits, get_tag_bits(a.get_tag()) },
174 { C::alu_max_value, get_tag_max_value(a.get_tag()) },
175 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_ADD },
176 { C::alu_sel, 1 },
177 { C::alu_sel_op_add, 1 },
178 { C::alu_sel_is_ff, tag == 0 ? 1 : 0 },
179 { C::alu_tag_ff_diff_inv, tag == 0 ? 0 : FF(tag).invert() },
180 { C::alu_sel_is_u128, tag == static_cast<uint8_t>(MemoryTag::U128) ? 1 : 0 },
181 { C::alu_tag_u128_diff_inv,
182 tag == static_cast<uint8_t>(MemoryTag::U128)
183 ? 0
184 : FF(tag - static_cast<uint8_t>(MemoryTag::U128)).invert() },
185 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
186 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
187 { C::execution_mem_tag_reg_2_, tag }, // = ic_tag
188 { C::execution_register_0_, a }, // = ia
189 { C::execution_register_1_, b }, // = ib
190 { C::execution_register_2_, c }, // = ic
191 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
192 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_ADD }, // = alu_op_id
193 },
194 });
195
196 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
197 precomputed_builder.process_tag_parameters(trace);
198 return trace;
199 }
200
201 TestTraceContainer process_basic_add_with_tracegen(ThreeOperandTestParams params, bool error = false)
202 {
203 TestTraceContainer trace;
204 auto [a, b, c] = params;
205
206 builder.process(
207 {
208 { .operation = simulation::AluOperation::ADD, .a = a, .b = b, .c = c, .error = error },
209 },
210 trace);
211
212 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
213 precomputed_builder.process_tag_parameters(trace);
214 return trace;
215 }
216
217 TestTraceContainer process_carry_add_trace(ThreeOperandTestParams params)
218 {
219 auto [a, b, c] = params;
220 auto mem_tag = a.get_tag();
221 b = MemoryValue::from_tag(mem_tag, get_tag_max_value(mem_tag));
222 c = a - MemoryValue::from_tag(mem_tag, 1);
223 auto tag = static_cast<uint8_t>(mem_tag);
224 auto trace = TestTraceContainer({
225 {
226 { C::alu_cf, 1 },
227 { C::alu_ia, a },
228 { C::alu_ia_tag, tag },
229 { C::alu_ib, b },
230 { C::alu_ib_tag, tag },
231 { C::alu_ic, c },
232 { C::alu_ic_tag, tag },
233 { C::alu_max_bits, get_tag_bits(mem_tag) },
234 { C::alu_max_value, get_tag_max_value(mem_tag) },
235 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_ADD },
236 { C::alu_sel, 1 },
237 { C::alu_sel_op_add, 1 },
238 { C::alu_sel_is_ff, tag == 0 ? 1 : 0 },
239 { C::alu_tag_ff_diff_inv, tag == 0 ? 0 : FF(tag).invert() },
240 { C::alu_sel_is_u128, tag == static_cast<uint8_t>(MemoryTag::U128) ? 1 : 0 },
241 { C::alu_tag_u128_diff_inv,
242 tag == static_cast<uint8_t>(MemoryTag::U128)
243 ? 0
244 : FF(tag - static_cast<uint8_t>(MemoryTag::U128)).invert() },
245 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
246 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
247 { C::execution_mem_tag_reg_2_, tag }, // = ic_tag
248 { C::execution_register_0_, a }, // = ia
249 { C::execution_register_1_, b }, // = ib
250 { C::execution_register_2_, c }, // = ic
251 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
252 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_ADD }, // = alu_op_id
253 },
254 });
255
256 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
257 precomputed_builder.process_tag_parameters(trace);
258 return trace;
259 }
260
261 TestTraceContainer process_carry_add_with_tracegen(ThreeOperandTestParams params)
262 {
263 TestTraceContainer trace;
264 auto [a, b, c] = params;
265 auto mem_tag = a.get_tag();
266 b = MemoryValue::from_tag(mem_tag, get_tag_max_value(mem_tag));
267 c = a - MemoryValue::from_tag(mem_tag, 1);
268
269 builder.process(
270 {
271 { .operation = simulation::AluOperation::ADD, .a = a, .b = b, .c = c },
272 },
273 trace);
274
275 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
276 precomputed_builder.process_tag_parameters(trace);
277 return trace;
278 }
279};
280
281INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluAddConstrainingTest, ::testing::ValuesIn(TEST_VALUES_ADD));
282
283TEST_P(AluAddConstrainingTest, AluBasicAdd)
284{
285 auto trace = process_basic_add_trace(GetParam());
286 check_all_interactions<AluTraceBuilder>(trace);
287 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
288 check_relation<alu>(trace);
289}
290
291TEST_P(AluAddConstrainingTest, AluBasicAddTraceGen)
292{
293 auto trace = process_basic_add_with_tracegen(GetParam());
294 check_all_interactions<AluTraceBuilder>(trace);
295 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
296 check_relation<alu>(trace);
297}
298
299TEST_P(AluAddConstrainingTest, AluCarryAdd)
300{
301 auto trace = process_carry_add_trace(GetParam());
302 check_all_interactions<AluTraceBuilder>(trace);
303 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
304 check_relation<alu>(trace);
305}
306
307TEST_P(AluAddConstrainingTest, AluCarryAddTraceGen)
308{
309 auto trace = process_carry_add_with_tracegen(GetParam());
310 check_all_interactions<AluTraceBuilder>(trace);
311 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
312 check_relation<alu>(trace);
313}
314
315TEST_P(AluAddConstrainingTest, NegativeBasicAdd)
316{
317 auto trace = process_basic_add_trace(GetParam());
318 check_relation<alu>(trace);
319 trace.set(Column::alu_ic, 0, trace.get(Column::alu_ic, 0) + 1);
320 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ALU_ADD_SUB");
321}
322
323TEST_P(AluAddConstrainingTest, NegativeAluCarryAdd)
324{
325 auto params = GetParam();
326 auto trace = process_carry_add_trace(params);
327 auto correct_max_value = trace.get(Column::alu_max_value, 0);
328 auto is_ff = std::get<0>(params).get_tag() == MemoryTag::FF;
329 check_all_interactions<AluTraceBuilder>(trace);
330 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
331 check_relation<alu>(trace);
332 // We get the correct overflowed result 'for free' with FF whether cf is on or not
333 if (!is_ff) {
334 trace.set(Column::alu_cf, 0, 0);
335 // If we are overflowing, we need to set the carry flag...
336 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ALU_ADD_SUB");
337
338 trace.set(Column::alu_cf, 0, 1);
339 trace.set(Column::alu_max_value, 0, 0);
340 // ...and the correct max_value:
341 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ALU_ADD_SUB");
342 EXPECT_THROW_WITH_MESSAGE(check_all_interactions<AluTraceBuilder>(trace), "LOOKUP_ALU_TAG_MAX_BITS_VALUE");
343 trace.set(Column::alu_max_value, 0, correct_max_value);
344 }
345
346 // TODO(MW): The below should fail the range check on c in memory, but we cannot test this yet.
347 // Instead, we assume the carry flag is correct and show an overflow fails:
348 trace.set(Column::alu_ic, 0, correct_max_value + 2);
349 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ALU_ADD_SUB");
350}
351
352TEST_P(AluAddConstrainingTest, NegativeAddWrongTagABMismatch)
353{
354 auto params = GetParam();
355 auto tag = static_cast<uint8_t>(std::get<0>(params).get_tag());
356 auto trace = process_basic_add_trace(params);
357 trace.set(Column::alu_ib_tag, 0, tag - 1);
358 // ab_tags_diff_inv = inv(a_tag - b_tag) = inv(1) = 1:
359 trace.set(Column::alu_ab_tags_diff_inv, 0, 1);
360 trace.set(Column::alu_sel_ab_tag_mismatch, 0, 1);
361 // If we set the mismatch error, we need to make sure the ALU tag error selector is correct:
362 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TAG_ERR_CHECK");
363 trace.set(Column::alu_sel_tag_err, 0, 1);
364 // If we set one error, we need to make sure the overall ALU error selector is correct:
365 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ERR_CHECK");
366 trace.set(Column::alu_sel_err, 0, 1);
367 // Though the tags don't match, with error handling we can return the error rather than fail:
368 check_relation<alu>(trace);
369 // Correctly using the error, but injecting the wrong inverse will fail:
370 trace.set(Column::alu_ab_tags_diff_inv, 0, 0);
371 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "AB_TAGS_CHECK");
372 trace.set(Column::alu_ab_tags_diff_inv, 0, 1);
373 // Correcting the inverse, but removing the error will fail:
374 trace.set(Column::alu_sel_ab_tag_mismatch, 0, 0);
375 trace.set(Column::alu_sel_tag_err, 0, 0);
376 trace.set(Column::alu_sel_err, 0, 0);
377 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "AB_TAGS_CHECK");
378}
379
380TEST_P(AluAddConstrainingTest, NegativeAddTraceGenWrongTagABMismatch)
381{
382 auto [a, b, c] = GetParam();
383 auto trace = process_basic_add_with_tracegen(
384 { a, MemoryValue::from_tag(TAG_ERROR_TEST_VALUES.at(b.get_tag()), b.as_ff()), c }, true);
385 check_all_interactions<AluTraceBuilder>(trace);
386 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
387 check_relation<alu>(trace);
388}
389
390TEST_P(AluAddConstrainingTest, NegativeAddWrongTagCMismatch)
391{
392 auto params = GetParam();
393 auto tag = static_cast<uint8_t>(std::get<0>(params).get_tag());
394 auto trace = process_basic_add_trace(params);
395 check_relation<alu>(trace);
396 trace.set(Column::alu_ic_tag, 0, tag - 1);
397 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "C_TAG_CHECK");
398}
399
400// SUB TESTS
401
402const std::vector<MemoryValue> TEST_VALUES_SUB_OUT = {
410};
411
412const std::vector<ThreeOperandTestParams> TEST_VALUES_SUB = zip_helper(TEST_VALUES_SUB_OUT);
413
414class AluSubConstrainingTest : public AluConstrainingTest,
415 public ::testing::WithParamInterface<ThreeOperandTestParams> {
416 public:
417 TestTraceContainer process_sub_trace(ThreeOperandTestParams params)
418 {
419 auto [a, b, c] = params;
420 auto tag = static_cast<uint8_t>(a.get_tag());
421 auto trace = TestTraceContainer({
422 {
423 { C::alu_cf, a.as_ff() - b.as_ff() != c.as_ff() ? 1 : 0 },
424 { C::alu_ia, a },
425 { C::alu_ia_tag, tag },
426 { C::alu_ib, b },
427 { C::alu_ib_tag, tag },
428 { C::alu_ic, c },
429 { C::alu_ic_tag, tag },
430 { C::alu_max_bits, get_tag_bits(a.get_tag()) },
431 { C::alu_max_value, get_tag_max_value(a.get_tag()) },
432 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_SUB },
433 { C::alu_sel, 1 },
434 { C::alu_sel_op_sub, 1 },
435 { C::alu_sel_is_ff, tag == 0 ? 1 : 0 },
436 { C::alu_tag_ff_diff_inv, tag == 0 ? 0 : FF(tag).invert() },
437 { C::alu_sel_is_u128, tag == static_cast<uint8_t>(MemoryTag::U128) ? 1 : 0 },
438 { C::alu_tag_u128_diff_inv,
439 tag == static_cast<uint8_t>(MemoryTag::U128)
440 ? 0
441 : FF(tag - static_cast<uint8_t>(MemoryTag::U128)).invert() },
442 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
443 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
444 { C::execution_mem_tag_reg_2_, tag }, // = ic_tag
445 { C::execution_register_0_, a }, // = ia
446 { C::execution_register_1_, b }, // = ib
447 { C::execution_register_2_, c }, // = ic
448 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
449 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_SUB }, // = alu_op_id
450 },
451 });
452
453 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
454 precomputed_builder.process_tag_parameters(trace);
455 return trace;
456 }
457
458 TestTraceContainer process_sub_with_tracegen(ThreeOperandTestParams params)
459 {
460 TestTraceContainer trace;
461 auto [a, b, c] = params;
462
463 builder.process(
464 {
465 { .operation = simulation::AluOperation::SUB, .a = a, .b = b, .c = c },
466 },
467 trace);
468
469 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
470 precomputed_builder.process_tag_parameters(trace);
471 return trace;
472 }
473};
474
475INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluSubConstrainingTest, ::testing::ValuesIn(TEST_VALUES_SUB));
476
477TEST_P(AluSubConstrainingTest, AluSub)
478{
479 auto trace = process_sub_trace(GetParam());
480 check_all_interactions<AluTraceBuilder>(trace);
481 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
482 check_relation<alu>(trace);
483}
484
485TEST_P(AluSubConstrainingTest, AluSubTraceGen)
486{
487 auto trace = process_sub_with_tracegen(GetParam());
488 check_all_interactions<AluTraceBuilder>(trace);
489 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
490 check_relation<alu>(trace);
491}
492
493TEST_P(AluSubConstrainingTest, AluSubNegative)
494{
495 auto params = GetParam();
496 auto is_ff = std::get<0>(params).get_tag() == MemoryTag::FF;
497 auto trace = process_sub_trace(GetParam());
498 check_all_interactions<AluTraceBuilder>(trace);
499 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
500 check_relation<alu>(trace);
501
502 auto c = trace.get(Column::alu_ic, 0);
503
504 trace.set(Column::alu_ic, 0, c + 1);
505 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ALU_ADD_SUB");
506
507 trace.set(Column::alu_ic, 0, c);
508 check_relation<alu>(trace);
509
510 // We get the correct underflowed result 'for free' with FF whether cf is on or not
511 if (!is_ff) {
512 trace.set(Column::alu_cf, 0, trace.get(Column::alu_cf, 0) == 1 ? 0 : 1);
513 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ALU_ADD_SUB");
514 }
515}
516
517// MUL TESTS
518
519const std::vector<MemoryValue> TEST_VALUES_MUL_OUT = {
520 MemoryValue::from_tag(MemoryTag::U1, 0),
521 MemoryValue::from_tag(MemoryTag::U8, 16),
522 MemoryValue::from_tag(MemoryTag::U16, 64456),
523 MemoryValue::from_tag(MemoryTag::U32, (uint256_t(1) << 32) - 50),
524 MemoryValue::from_tag(MemoryTag::U64, (uint256_t(1) << 64) - 50),
525 MemoryValue::from_tag(MemoryTag::U128, (uint256_t(1) << 128) - 50),
526 MemoryValue::from_tag(MemoryTag::FF, FF::modulus - 8),
527};
528
529const std::vector<ThreeOperandTestParams> TEST_VALUES_MUL = zip_helper(TEST_VALUES_MUL_OUT);
530
531class AluMulConstrainingTest : public AluConstrainingTest,
532 public ::testing::WithParamInterface<ThreeOperandTestParams> {
533 public:
534 TestTraceContainer process_mul_trace(ThreeOperandTestParams params)
535 {
536 auto [a, b, c] = params;
537 auto mem_tag = a.get_tag();
538 auto tag = static_cast<uint8_t>(mem_tag);
539
540 auto is_u128 = mem_tag == MemoryTag::U128;
541
542 auto c_int = static_cast<uint256_t>(a.as_ff()) * static_cast<uint256_t>(b.as_ff());
543
544 uint256_t c_hi = 0;
545 if (mem_tag != MemoryTag::FF && mem_tag != MemoryTag::U128) {
546 c_hi = c_int >> static_cast<uint256_t>(get_tag_bits(mem_tag));
547 }
548
549 auto trace = TestTraceContainer({
550 {
551 { C::alu_c_hi, c_hi },
552 { C::alu_constant_64, 64 },
553 { C::alu_ia, a },
554 { C::alu_ia_tag, tag },
555 { C::alu_ib, b },
556 { C::alu_ib_tag, tag },
557 { C::alu_ic, c },
558 { C::alu_ic_tag, tag },
559 { C::alu_max_bits, get_tag_bits(mem_tag) },
560 { C::alu_max_value, get_tag_max_value(mem_tag) },
561 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_MUL },
562 { C::alu_sel, 1 },
563 { C::alu_sel_decompose_a, is_u128 ? 1 : 0 },
564 { C::alu_sel_is_ff, mem_tag == MemoryTag::FF ? 1 : 0 },
565 { C::alu_tag_ff_diff_inv, tag == 0 ? 0 : FF(tag).invert() },
566 { C::alu_sel_is_u128, is_u128 ? 1 : 0 },
567 { C::alu_sel_mul_div_u128, is_u128 ? 1 : 0 },
568 { C::alu_sel_op_mul, 1 },
569 { C::alu_sel_mul_no_err_non_ff, mem_tag == MemoryTag::FF ? 0 : 1 },
570 { C::alu_tag_u128_diff_inv, is_u128 ? 0 : FF(tag - static_cast<uint8_t>(MemoryTag::U128)).invert() },
571 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
572 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
573 { C::execution_mem_tag_reg_2_, tag }, // = ic_tag
574 { C::execution_register_0_, a }, // = ia
575 { C::execution_register_1_, b }, // = ib
576 { C::execution_register_2_, c }, // = ic
577 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
578 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_MUL }, // = alu_op_id
579 },
580 });
581
582 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
583 precomputed_builder.process_tag_parameters(trace);
584
585 std::vector<RangeCheckEvent> range_check_events;
586
587 if (is_u128) {
588 auto a_decomp = simulation::decompose_128(a.as<uint128_t>());
589 auto b_decomp = simulation::decompose_128(b.as<uint128_t>());
590 // c_hi = (c_hi_full - a_hi * b_hi) % 2^64
591 auto hi_operand = static_cast<uint256_t>(a_decomp.hi) * static_cast<uint256_t>(b_decomp.hi);
592 c_hi = ((c_int >> 128) - hi_operand) % (uint256_t(1) << 64);
593 trace.set(0,
594 { { { Column::alu_a_lo, a_decomp.lo },
595 { Column::alu_a_lo_bits, 64 },
596 { Column::alu_a_hi, a_decomp.hi },
597 { Column::alu_a_hi_bits, 64 },
598 { Column::alu_b_lo, b_decomp.lo },
599 { Column::alu_b_hi, b_decomp.hi },
600 { Column::alu_c_hi, c_hi },
601 { Column::alu_cf, hi_operand > (uint256_t(1) << 64) ? 1 : 0 } } });
602
603 range_check_events.insert(range_check_events.end(),
604 { { .value = a_decomp.lo, .num_bits = 64 },
605 { .value = a_decomp.hi, .num_bits = 64 },
606 { .value = b_decomp.lo, .num_bits = 64 },
607 { .value = b_decomp.hi, .num_bits = 64 } });
608 }
609
610 range_check_events.push_back({ .value = static_cast<uint128_t>(c_hi), .num_bits = 64 });
611 range_check_builder.process(range_check_events, trace);
612
613 return trace;
614 }
615
616 TestTraceContainer process_mul_with_tracegen(ThreeOperandTestParams params)
617 {
618 TestTraceContainer trace;
619 auto [a, b, c] = params;
620 auto mem_tag = a.get_tag();
621
622 builder.process(
623 {
624 { .operation = simulation::AluOperation::MUL, .a = a, .b = b, .c = c },
625 },
626 trace);
627
628 uint256_t a_int = static_cast<uint256_t>(a.as_ff());
629 uint256_t b_int = static_cast<uint256_t>(b.as_ff());
630 auto c_hi = mem_tag == MemoryTag::FF ? 0 : (a_int * b_int) >> get_tag_bits(mem_tag);
631 if (mem_tag == MemoryTag::U128) {
632 auto a_decomp = simulation::decompose_128(a.as<uint128_t>());
633 auto b_decomp = simulation::decompose_128(b.as<uint128_t>());
634 // c_hi = (c_hi_full - a_hi * b_hi) % 2^64
635 auto c_hi_full = (a_int * b_int) >> 128;
636 auto hi_operand = static_cast<uint256_t>(a_decomp.hi) * static_cast<uint256_t>(b_decomp.hi);
637 c_hi = (c_hi_full - hi_operand) % (uint256_t(1) << 64);
638 range_check_builder.process({ { .value = a_decomp.lo, .num_bits = 64 },
639 { .value = a_decomp.hi, .num_bits = 64 },
640 { .value = b_decomp.lo, .num_bits = 64 },
641 { .value = b_decomp.hi, .num_bits = 64 },
642 { .value = static_cast<uint128_t>(c_hi), .num_bits = 64 } },
643 trace);
644 } else {
645 range_check_builder.process({ { .value = static_cast<uint128_t>(c_hi), .num_bits = 64 } }, trace);
646 }
647 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
648 precomputed_builder.process_tag_parameters(trace);
649 return trace;
650 }
651};
652
653INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluMulConstrainingTest, ::testing::ValuesIn(TEST_VALUES_MUL));
654
655TEST_P(AluMulConstrainingTest, AluMul)
656{
657 auto trace = process_mul_trace(GetParam());
658 check_all_interactions<AluTraceBuilder>(trace);
659 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
660 check_relation<alu>(trace);
661}
662
663TEST_P(AluMulConstrainingTest, AluMulTraceGen)
664{
665 auto trace = process_mul_with_tracegen(GetParam());
666 check_all_interactions<AluTraceBuilder>(trace);
667 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
668 check_relation<alu>(trace);
669}
670
671TEST_F(AluConstrainingTest, AluMulU128Carry)
672{
673 auto a = MemoryValue::from_tag(MemoryTag::U128, get_tag_max_value(MemoryTag::U128)); // = -1
674 auto b = MemoryValue::from_tag(MemoryTag::U128, get_tag_max_value(MemoryTag::U128) - 2); // = -3
675 auto c = a * b; // = 3
676 auto overflow_c_int = static_cast<uint256_t>(a.as_ff()) * static_cast<uint256_t>(b.as_ff());
677
678 auto tag = static_cast<uint8_t>(MemoryTag::U128);
679
680 auto a_decomp = simulation::decompose_128(a.as<uint128_t>());
681 auto b_decomp = simulation::decompose_128(b.as<uint128_t>());
682
683 // c_hi = old_c_hi - a_hi * b_hi % 2^64
684 uint256_t hi_operand =
685 ((overflow_c_int >> 128) - static_cast<uint256_t>(a_decomp.hi) * static_cast<uint256_t>(b_decomp.hi));
686 auto c_hi = hi_operand % (uint256_t(1) << 64);
687 auto cf = hi_operand >> 64;
688 auto trace = TestTraceContainer({
689 {
690 { C::alu_a_hi, a_decomp.hi },
691 { C::alu_a_hi_bits, 64 },
692 { C::alu_a_lo, a_decomp.lo },
693 { C::alu_a_lo_bits, 64 },
694 { C::alu_b_hi, b_decomp.hi },
695 { C::alu_b_lo, b_decomp.lo },
696 { C::alu_c_hi, c_hi },
697 { C::alu_cf, cf },
698 { C::alu_constant_64, 64 },
699 { C::alu_ia, a },
700 { C::alu_ia_tag, tag },
701 { C::alu_ib, b },
702 { C::alu_ib_tag, tag },
703 { C::alu_ic, c },
704 { C::alu_ic_tag, tag },
705 { C::alu_max_bits, get_tag_bits(MemoryTag::U128) },
706 { C::alu_max_value, get_tag_max_value(MemoryTag::U128) },
707 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_MUL },
708 { C::alu_sel, 1 },
709 { C::alu_sel_decompose_a, 1 },
710 { C::alu_sel_is_u128, 1 },
711 { C::alu_sel_mul_div_u128, 1 },
712 { C::alu_sel_op_mul, 1 },
713 { C::alu_sel_mul_no_err_non_ff, 1 },
714 { C::alu_tag_u128_diff_inv, 0 },
715 { C::alu_tag_ff_diff_inv, FF(tag).invert() },
716 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
717 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
718 { C::execution_mem_tag_reg_2_, tag }, // = ic_tag
719 { C::execution_register_0_, a }, // = ia
720 { C::execution_register_1_, b }, // = ib
721 { C::execution_register_2_, c }, // = ic
722 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
723 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_MUL }, // = alu_op_id
724 },
725 });
726
727 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
728 precomputed_builder.process_tag_parameters(trace);
729 range_check_builder.process({ { .value = a_decomp.lo, .num_bits = 64 },
730 { .value = a_decomp.hi, .num_bits = 64 },
731 { .value = b_decomp.lo, .num_bits = 64 },
732 { .value = b_decomp.hi, .num_bits = 64 },
733 { .value = static_cast<uint128_t>(c_hi), .num_bits = 64 } },
734 trace);
735
736 check_all_interactions<AluTraceBuilder>(trace);
737 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
738 check_relation<alu>(trace);
739
740 // Below = (a * b mod p) mod 2^128
741 auto should_fail_overflowed = MemoryValue::from_tag_truncating(MemoryTag::U128, a.as_ff() * b.as_ff());
742 trace.set(Column::alu_ic, 0, should_fail_overflowed);
743 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ALU_MUL_U128");
744}
745
746TEST_P(AluMulConstrainingTest, NegativeAluMul)
747{
748 auto trace = process_mul_trace(GetParam());
749 check_all_interactions<AluTraceBuilder>(trace);
750 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
751 check_relation<alu>(trace);
752 trace.set(Column::alu_ic, 0, trace.get(Column::alu_ic, 0) + 1);
753 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ALU_MUL");
754}
755
756// DIV TESTS
757
758const std::vector<MemoryValue> TEST_VALUES_DIV_OUT = {
759 MemoryValue::from_tag(MemoryTag::U1, 0), // Dividing by zero, so expecting an error
760 MemoryValue::from_tag(MemoryTag::U8, 4),
761 MemoryValue::from_tag(MemoryTag::U16, 0),
762 MemoryValue::from_tag(MemoryTag::U32, 0x33333331),
763 MemoryValue::from_tag(MemoryTag::U64, 0x3333333333333331ULL),
764 MemoryValue::from_tag(MemoryTag::U128, (((uint256_t(1) << 128) - 11) / 5)), // 0x3333333333333333333333333333331
765};
766
767const std::vector<ThreeOperandTestParams> TEST_VALUES_DIV = zip_helper(TEST_VALUES_DIV_OUT);
768
769class AluDivConstrainingTest : public AluConstrainingTest,
770 public ::testing::WithParamInterface<ThreeOperandTestParams> {
771 public:
772 TestTraceContainer process_div_trace(ThreeOperandTestParams params)
773 {
774 auto [a, b, c] = params;
775 auto mem_tag = a.get_tag();
776 auto tag = static_cast<uint8_t>(mem_tag);
777 auto remainder = a - b * c;
778
779 auto div_0_error = b.as_ff() == FF(0);
780 auto is_u128 = mem_tag == MemoryTag::U128;
781
782 auto trace = TestTraceContainer({
783 {
784 { C::alu_b_inv, div_0_error ? 0 : b.as_ff().invert() },
785 { C::alu_constant_64, 64 },
786 { C::alu_helper1, div_0_error ? 0 : remainder.as_ff() },
787 { C::alu_ia, a },
788 { C::alu_ia_tag, tag },
789 { C::alu_ib, b },
790 { C::alu_ib_tag, tag },
791 { C::alu_ic, c },
792 { C::alu_ic_tag, tag },
793 { C::alu_max_bits, get_tag_bits(mem_tag) },
794 { C::alu_max_value, get_tag_max_value(mem_tag) },
795 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_DIV },
796 { C::alu_sel, 1 },
797 { C::alu_sel_decompose_a, is_u128 ? 1 : 0 },
798 { C::alu_sel_div_0_err, div_0_error ? 1 : 0 },
799 { C::alu_sel_div_no_err, div_0_error ? 0 : 1 },
800 { C::alu_sel_int_gt, div_0_error ? 0 : 1 },
801 { C::alu_gt_input_a, b.as_ff() },
802 { C::alu_gt_input_b, div_0_error ? 0 : remainder.as_ff() },
803 { C::alu_gt_result_c, div_0_error ? 0 : 1 },
804 { C::alu_sel_err, div_0_error ? 1 : 0 },
805 { C::alu_sel_is_u128, is_u128 ? 1 : 0 },
806 { C::alu_sel_mul_div_u128, is_u128 ? 1 : 0 },
807 { C::alu_sel_op_div, 1 },
808 { C::alu_tag_ff_diff_inv, FF(tag - static_cast<uint8_t>(MemoryTag::FF)).invert() },
809 { C::alu_tag_u128_diff_inv, is_u128 ? 0 : FF(tag - static_cast<uint8_t>(MemoryTag::U128)).invert() },
810 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
811 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
812 { C::execution_mem_tag_reg_2_, tag }, // = ic_tag
813 { C::execution_register_0_, a }, // = ia
814 { C::execution_register_1_, b }, // = ib
815 { C::execution_register_2_, c }, // = ic
816 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
817 { C::execution_sel_opcode_error, div_0_error ? 1 : 0 }, // = sel_err
818 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_DIV }, // = alu_op_id
819 },
820 });
821
822 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
823 precomputed_builder.process_tag_parameters(trace);
824 gt_builder.process({ { .a = static_cast<uint128_t>(b.as_ff()),
825 .b = static_cast<uint128_t>(remainder.as_ff()),
826 .result = true } },
827 trace);
828
829 if (is_u128) {
830 auto c_decomp = simulation::decompose_128(c.as<uint128_t>());
831 auto b_decomp = simulation::decompose_128(b.as<uint128_t>());
832
833 trace.set(0,
834 { { { Column::alu_a_lo, c_decomp.lo },
835 { Column::alu_a_lo_bits, 64 },
836 { Column::alu_a_hi, c_decomp.hi },
837 { Column::alu_a_hi_bits, 64 },
838 { Column::alu_b_lo, b_decomp.lo },
839 { Column::alu_b_hi, b_decomp.hi } } });
840
841 // Combine remainder range check with U128 decomposition range checks in a single
842 // process call, since process() always starts from row 0.
843 if (!div_0_error) {
844 range_check_builder.process(
845 { { .value = static_cast<uint128_t>(remainder.as_ff()), .num_bits = get_tag_bits(mem_tag) },
846 { .value = c_decomp.lo, .num_bits = 64 },
847 { .value = c_decomp.hi, .num_bits = 64 },
848 { .value = b_decomp.lo, .num_bits = 64 },
849 { .value = b_decomp.hi, .num_bits = 64 } },
850 trace);
851 } else {
852 range_check_builder.process({ { .value = c_decomp.lo, .num_bits = 64 },
853 { .value = c_decomp.hi, .num_bits = 64 },
854 { .value = b_decomp.lo, .num_bits = 64 },
855 { .value = b_decomp.hi, .num_bits = 64 } },
856 trace);
857 }
858 } else if (!div_0_error) {
859 // Range check the remainder fits within max_bits.
860 range_check_builder.process(
861 { { .value = static_cast<uint128_t>(remainder.as_ff()), .num_bits = get_tag_bits(mem_tag) } }, trace);
862 }
863
864 return trace;
865 }
866
867 TestTraceContainer process_div_with_tracegen(ThreeOperandTestParams params)
868 {
869 TestTraceContainer trace;
870 auto [a, b, c] = params;
871 bool div_0_error = b.as_ff() == FF(0);
872 auto mem_tag = a.get_tag();
873
874 MemoryValue remainder = MemoryValue::from_tag(MemoryTag::FF, 0);
875 if (!div_0_error && mem_tag == b.get_tag() && mem_tag != MemoryTag::FF) {
876 remainder = a - b * c;
877 }
878
879 builder.process(
880 {
881 { .operation = simulation::AluOperation::DIV, .a = a, .b = b, .c = c, .error = div_0_error },
882 },
883 trace);
884
885 if (mem_tag == MemoryTag::U128) {
886 auto c_decomp = simulation::decompose_128(static_cast<uint128_t>(c.as_ff()));
887 auto b_decomp = simulation::decompose_128(static_cast<uint128_t>(b.as_ff()));
888
889 // Combine remainder range check with U128 decomposition range checks in a single
890 // process call, since process() always starts from row 0.
891 if (!div_0_error) {
892 range_check_builder.process(
893 { { .value = static_cast<uint128_t>(remainder.as_ff()), .num_bits = get_tag_bits(mem_tag) },
894 { .value = c_decomp.lo, .num_bits = 64 },
895 { .value = c_decomp.hi, .num_bits = 64 },
896 { .value = b_decomp.lo, .num_bits = 64 },
897 { .value = b_decomp.hi, .num_bits = 64 } },
898 trace);
899 } else {
900 range_check_builder.process({ { .value = c_decomp.lo, .num_bits = 64 },
901 { .value = c_decomp.hi, .num_bits = 64 },
902 { .value = b_decomp.lo, .num_bits = 64 },
903 { .value = b_decomp.hi, .num_bits = 64 } },
904 trace);
905 }
906 } else if (!div_0_error) {
907 // Range check the remainder fits within max_bits.
908 range_check_builder.process(
909 { { .value = static_cast<uint128_t>(remainder.as_ff()), .num_bits = get_tag_bits(mem_tag) } }, trace);
910 }
911 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
912 precomputed_builder.process_tag_parameters(trace);
913 gt_builder.process({ { .a = static_cast<uint128_t>(b.as_ff()),
914 .b = static_cast<uint128_t>(remainder.as_ff()),
915 .result = true } },
916 trace);
917 return trace;
918 }
919};
920
921INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluDivConstrainingTest, ::testing::ValuesIn(TEST_VALUES_DIV));
922
923TEST_P(AluDivConstrainingTest, AluDiv)
924{
925 auto trace = process_div_trace(GetParam());
926 check_all_interactions<AluTraceBuilder>(trace);
927 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
928 check_relation<alu>(trace);
929}
930
931TEST_P(AluDivConstrainingTest, AluDivTraceGen)
932{
933 auto trace = process_div_with_tracegen(GetParam());
934 check_all_interactions<AluTraceBuilder>(trace);
935 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
936 check_relation<alu>(trace);
937}
938
939TEST_F(AluDivConstrainingTest, AluDivByZeroMismatchTagsTraceGen)
940{
941 auto a = MemoryValue::from_tag(MemoryTag::U128, 2);
942 auto b = MemoryValue::from_tag(MemoryTag::U64, 0);
943 auto c = MemoryValue::from_tag(MemoryTag::FF, 0);
944
945 auto trace = process_div_with_tracegen({ a, b, c });
946 check_relation<alu>(trace);
947 check_all_interactions<AluTraceBuilder>(trace);
948}
949
950TEST_F(AluDivConstrainingTest, AluDivByZeroTagFFAndMismatchTagsTraceGen)
951{
952 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
953 auto b = MemoryValue::from_tag(MemoryTag::U32, 0);
954 auto c = MemoryValue::from_tag(MemoryTag::FF, 0);
955
956 auto trace = process_div_with_tracegen({ a, b, c });
957 check_relation<alu>(trace);
958 check_all_interactions<AluTraceBuilder>(trace);
959}
960
961TEST_F(AluDivConstrainingTest, NegativeAluDivUnderflow)
962{
963 // Test that for a < b, the circuit does not accept c != 0
964 auto a = MemoryValue::from_tag(MemoryTag::U32, 2);
965 auto b = MemoryValue::from_tag(MemoryTag::U32, 5);
966 auto c = a / b;
967 auto trace = process_div_trace({ a, b, c });
968 check_all_interactions<AluTraceBuilder>(trace);
969 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
970 check_relation<alu>(trace);
971
972 // Good path: 2/5 gives 0 with remainder = 2
973 // Bad path: 5 * c = 2 - r => set c = 2, so r = p - 8:
974
975 c = MemoryValue::from_tag(MemoryTag::U32, 2);
976 auto wrong_remainder = a.as_ff() - b.as_ff() * c.as_ff();
977
978 trace.set(Column::alu_ic, 0, c);
979 trace.set(Column::alu_helper1, 0, wrong_remainder);
980 trace.set(Column::alu_gt_input_b, 0, wrong_remainder);
981
982 // All relations will pass...
983 check_relation<alu>(trace);
984 // ... but the remainder (p - 8) is not in range, so the range check fails:
986 (check_interaction<AluTraceBuilder, lookup_alu_range_check_div_remainder_settings>(trace)),
987 "RANGE_CHECK_DIV_REMAINDER");
988 // ... and r > b, so the gt lookup also fails:
989 EXPECT_THROW_WITH_MESSAGE((check_interaction<AluTraceBuilder, lookup_alu_int_gt_settings>(trace)),
990 "LOOKUP_ALU_INT_GT");
991}
992
993TEST_F(AluDivConstrainingTest, NegativeAluDivU128Carry)
994{
995 // Test that for a < b, the circuit does not accept c != 0
996 auto a = MemoryValue::from_tag(MemoryTag::U128, 2);
997 auto b = MemoryValue::from_tag(MemoryTag::U128, (uint256_t(1) << 64) + 2);
998 auto c = a / b;
999
1000 auto trace = process_div_trace({ a, b, c });
1001
1002 check_all_interactions<AluTraceBuilder>(trace);
1003 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1004 check_relation<alu>(trace);
1005
1006 // Check we cannot provide a c s.t. a - r = b * c over/underflows
1007
1008 c = MemoryValue::from_tag(MemoryTag::U128, (uint256_t(1) << 64) + 3);
1009 auto wrong_remainder = a.as_ff() - FF(static_cast<uint256_t>(b.as_ff()) * static_cast<uint256_t>(c.as_ff()));
1010
1011 // We now have c and wrong_remainder s.t. a - wrong_remainder == b * c in the field...
1012
1013 trace.set(Column::alu_ic, 0, c);
1014 trace.set(Column::alu_helper1, 0, wrong_remainder);
1015
1016 // ...but we haven't provided a correct decomposition of the new bad c:
1017 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DECOMPOSITION");
1018
1019 auto c_decomp = simulation::decompose_128(c.as<uint128_t>());
1020 trace.set(Column::alu_a_lo, 0, c_decomp.lo);
1021 trace.set(Column::alu_a_hi, 0, c_decomp.hi);
1022
1023 // Setting the decomposed values still (correctly) fails:
1024 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ALU_DIV_U128");
1025}
1026
1027TEST_F(AluDivConstrainingTest, NegativeAluDivByZero)
1028{
1029 auto a = MemoryValue::from_tag(MemoryTag::U32, 2);
1030 auto b = MemoryValue::from_tag(MemoryTag::U32, 5);
1031 auto c = a / b;
1032
1033 for (const bool with_tracegen : { false, true }) {
1034 auto trace = with_tracegen ? process_div_with_tracegen({ a, b, c }) : process_div_trace({ a, b, c });
1035 check_all_interactions<AluTraceBuilder>(trace);
1036 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1037 check_relation<alu>(trace);
1038
1039 // Set b, b_inv to 0...
1040 trace.set(Column::alu_ib, 0, 0);
1041 trace.set(Column::alu_b_inv, 0, 0);
1042 // ...and since we haven't set the error correctly, we expect the below to fail:
1043 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1044 // We need to set the div_0_err and...
1045 trace.set(Column::alu_sel_div_0_err, 0, 1);
1046 trace.set(Column::alu_sel_div_no_err, 0, 0);
1047 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ERR_CHECK");
1048 // ...the overall sel_err:
1049 trace.set(Column::alu_sel_err, 0, 1);
1050 trace.set(Column::alu_sel_int_gt, 0, 0);
1051 trace.set(Column::alu_gt_input_a, 0, 0);
1052 trace.set(Column::alu_gt_input_b, 0, 0);
1053 trace.set(Column::alu_gt_result_c, 0, 0);
1054 check_relation<alu>(trace);
1055
1056 // If we try and have div_0_err on without doing a div, the below should fail:
1057 trace.set(Column::alu_sel_op_div, 0, 0);
1058 trace.set(Column::alu_sel_op_mul, 0, 1);
1059 trace.set(Column::alu_op_id, 0, AVM_EXEC_OP_ID_ALU_MUL);
1060 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1061
1062 trace.set(Column::alu_sel_op_div, 0, 1);
1063 trace.set(Column::alu_sel_op_mul, 0, 0);
1064 trace.set(Column::alu_op_id, 0, AVM_EXEC_OP_ID_ALU_DIV);
1065 check_relation<alu>(trace);
1066
1067 // If we try and set b != 0 with div_0_err on, the below should fail:
1068 trace.set(Column::alu_ib, 0, b);
1069 trace.set(Column::alu_b_inv, 0, b.as_ff().invert());
1070 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1071 }
1072}
1073
1074TEST_F(AluDivConstrainingTest, NegativeAluDivFF)
1075{
1076 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
1077 auto b = MemoryValue::from_tag(MemoryTag::FF, 5);
1078 auto c = a / b;
1079 auto trace = process_div_with_tracegen({ a, b, c });
1080 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TAG_ERR_CHECK");
1081 // This case should be recoverable, so we set the tag err selectors:
1082 trace.set(Column::alu_sel_tag_err, 0, 1);
1083 trace.set(Column::alu_sel_err, 0, 1);
1084 trace.set(Column::alu_sel_div_no_err, 0, 0);
1085 trace.set(Column::alu_sel_int_gt, 0, 0);
1086 trace.set(Column::alu_gt_input_a, 0, 0);
1087 trace.set(Column::alu_gt_input_b, 0, 0);
1088 trace.set(Column::alu_gt_result_c, 0, 0);
1089 check_relation<alu>(trace);
1090 check_all_interactions<AluTraceBuilder>(trace);
1091 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1092}
1093
1094TEST_F(AluDivConstrainingTest, NegativeAluDivByZeroFF)
1095{
1096 // For DIV, we can have both FF and dividing by zero errors:
1097 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
1098 auto b = MemoryValue::from_tag(MemoryTag::FF, 5);
1099 auto c = a / b;
1100 auto trace = process_div_with_tracegen({ a, b, c });
1101 trace.set(Column::alu_sel_tag_err, 0, 1);
1102 trace.set(Column::alu_sel_err, 0, 1);
1103 trace.set(Column::alu_sel_div_no_err, 0, 0);
1104 trace.set(Column::alu_sel_int_gt, 0, 0);
1105 trace.set(Column::alu_gt_input_a, 0, 0);
1106 trace.set(Column::alu_gt_input_b, 0, 0);
1107 trace.set(Column::alu_gt_result_c, 0, 0);
1108 check_relation<alu>(trace);
1109 // Set b, b_inv to 0 with dividing by 0 errors:
1110 trace.set(Column::alu_ib, 0, 0);
1111 trace.set(Column::alu_b_inv, 0, 0);
1112 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1113 trace.set(Column::alu_sel_div_0_err, 0, 1);
1114 check_relation<alu>(trace);
1115 check_all_interactions<AluTraceBuilder>(trace);
1116 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1117}
1118
1119TEST_F(AluDivConstrainingTest, NegativeAluDivByZeroFFTagMismatch)
1120{
1121 // For DIV, we can have FF, tag mismatch, and dividing by zero errors:
1122 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
1123 auto b = MemoryValue::from_tag(MemoryTag::FF, 5);
1124 auto c = a / b;
1125 auto trace = process_div_with_tracegen({ a, b, c });
1126 trace.set(Column::alu_sel_tag_err, 0, 1);
1127 trace.set(Column::alu_sel_err, 0, 1);
1128 trace.set(Column::alu_sel_div_no_err, 0, 0);
1129 trace.set(Column::alu_sel_int_gt, 0, 0);
1130 trace.set(Column::alu_gt_input_a, 0, 0);
1131 trace.set(Column::alu_gt_input_b, 0, 0);
1132 trace.set(Column::alu_gt_result_c, 0, 0);
1133 check_relation<alu>(trace);
1134 // Setting b to u8 also creates a tag mismatch:
1135 trace.set(Column::alu_ib_tag, 0, static_cast<uint8_t>(MemoryTag::U8));
1136 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "AB_TAGS_CHECK");
1137 trace.set(Column::alu_sel_ab_tag_mismatch, 0, 1);
1138 trace.set(Column::alu_ab_tags_diff_inv,
1139 0,
1140 (FF(static_cast<uint8_t>(MemoryTag::FF)) - FF(static_cast<uint8_t>(MemoryTag::U8))).invert());
1141 check_relation<alu>(trace);
1142 // We can also handle dividing by 0:
1143 trace.set(Column::alu_ib, 0, 0);
1144 trace.set(Column::alu_b_inv, 0, 0);
1145 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1146 trace.set(Column::alu_sel_div_0_err, 0, 1);
1147 trace.set(Column::alu_sel_div_no_err, 0, 0);
1148 check_relation<alu>(trace);
1149 check_all_interactions<AluTraceBuilder>(trace);
1150 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1151}
1152
1153// FDIV TESTS
1154
1155// Note: The test framework below converts all inputs to FF values to allow for many happy path tests without adding
1156// new vectors. Non-FF values are tested separately.
1157const std::vector<MemoryValue> TEST_VALUES_FDIV_OUT = {
1158 MemoryValue::from_tag(MemoryTag::FF, 0), // Dividing by zero, so expecting an error
1159 MemoryValue::from_tag(MemoryTag::FF, 4),
1160 MemoryValue::from_tag(MemoryTag::FF, FF("0x1e980ebbc51694827ee20074ac28b250a037a43eb44b38e6aa367c57a05e6d48")),
1161 MemoryValue::from_tag(MemoryTag::FF, FF("0x135b52945a13d9aa49b9b57c33cd568ba9ae5ce9ca4a2d06e7f3fbd4f9999998")),
1162 MemoryValue::from_tag(MemoryTag::FF, FF("0x135b52945a13d9aa49b9b57c33cd568ba9ae5ce9ca4a2d071b272f07f9999998")),
1163 MemoryValue::from_tag(MemoryTag::FF, FF("0x135b52945a13d9aa49b9b57c33cd568bdce1901cfd7d603a1b272f07f9999998")),
1164 MemoryValue::from_tag(MemoryTag::FF, FF::modulus - 2),
1165};
1166
1167const std::vector<ThreeOperandTestParams> TEST_VALUES_FDIV = zip_helper(TEST_VALUES_FDIV_OUT);
1168
1169class AluFDivConstrainingTest : public AluConstrainingTest,
1170 public ::testing::WithParamInterface<ThreeOperandTestParams> {
1171 public:
1172 TestTraceContainer process_fdiv_trace(ThreeOperandTestParams params)
1173 {
1174 auto [a, b, c] = params;
1175 a = MemoryValue::from_tag(MemoryTag::FF, a);
1176 b = MemoryValue::from_tag(MemoryTag::FF, b);
1177 c = MemoryValue::from_tag(MemoryTag::FF, c);
1178 auto div_0_error = b.as_ff() == FF(0);
1179
1180 auto mem_tag = a.get_tag();
1181 auto tag = static_cast<uint8_t>(mem_tag);
1182
1183 auto trace = TestTraceContainer({
1184 {
1185 { C::alu_b_inv, div_0_error ? 0 : b.as_ff().invert() },
1186 { C::alu_ia, a },
1187 { C::alu_ia_tag, tag },
1188 { C::alu_ib, b },
1189 { C::alu_ib_tag, tag },
1190 { C::alu_ic, c },
1191 { C::alu_ic_tag, tag },
1192 { C::alu_max_bits, get_tag_bits(mem_tag) },
1193 { C::alu_max_value, get_tag_max_value(mem_tag) },
1194 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_FDIV },
1195 { C::alu_sel, 1 },
1196 { C::alu_sel_div_0_err, div_0_error ? 1 : 0 },
1197 { C::alu_sel_err, div_0_error ? 1 : 0 },
1198 { C::alu_sel_is_ff, 1 },
1199 { C::alu_sel_op_fdiv, 1 },
1200 { C::alu_tag_u128_diff_inv, FF(-static_cast<uint8_t>(MemoryTag::U128)).invert() },
1201 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
1202 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
1203 { C::execution_mem_tag_reg_2_, tag }, // = ic_tag
1204 { C::execution_register_0_, a }, // = ia
1205 { C::execution_register_1_, b }, // = ib
1206 { C::execution_register_2_, c }, // = ic
1207 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
1208 { C::execution_sel_opcode_error, div_0_error ? 1 : 0 }, // = sel_err
1209 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_FDIV }, // = alu_op_id
1210 },
1211 });
1212
1213 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1214 precomputed_builder.process_tag_parameters(trace);
1215
1216 return trace;
1217 }
1218
1219 TestTraceContainer process_fdiv_with_tracegen(ThreeOperandTestParams params, bool upcast_to_ff = false)
1220 {
1221 TestTraceContainer trace;
1222 auto [a, b, c] = params;
1223
1224 if (upcast_to_ff) {
1225 a = MemoryValue::from_tag(MemoryTag::FF, a);
1226 b = MemoryValue::from_tag(MemoryTag::FF, b);
1227 c = MemoryValue::from_tag(MemoryTag::FF, c);
1228 }
1229
1230 bool div_0_error = b.as_ff() == FF(0);
1231
1232 builder.process(
1233 {
1234 { .operation = simulation::AluOperation::FDIV, .a = a, .b = b, .c = c, .error = div_0_error },
1235 },
1236 trace);
1237
1238 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1239 precomputed_builder.process_tag_parameters(trace);
1240
1241 return trace;
1242 }
1243};
1244
1245INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluFDivConstrainingTest, ::testing::ValuesIn(TEST_VALUES_FDIV));
1246
1247TEST_P(AluFDivConstrainingTest, AluFDiv)
1248{
1249 auto trace = process_fdiv_trace(GetParam());
1250 check_all_interactions<AluTraceBuilder>(trace);
1251 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1252 check_relation<alu>(trace);
1253}
1254
1255TEST_P(AluFDivConstrainingTest, AluFDivTraceGen)
1256{
1257 auto trace = process_fdiv_with_tracegen(GetParam(), true);
1258 // InteractiveDebugger debugger(trace);
1259 // debugger.run();
1260 check_all_interactions<AluTraceBuilder>(trace);
1261 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1262 check_relation<alu>(trace);
1263}
1264
1265TEST_F(AluFDivConstrainingTest, AluFDivByZeroNonFFTagTraceGen)
1266{
1267 auto a = MemoryValue::from_tag(MemoryTag::U8, 2);
1268 auto b = MemoryValue::from_tag(MemoryTag::U8, 0);
1269 auto c = MemoryValue::from_tag(MemoryTag::FF, 0);
1270
1271 auto trace = process_fdiv_with_tracegen({ a, b, c });
1272 check_relation<alu>(trace);
1273 check_all_interactions<AluTraceBuilder>(trace);
1274}
1275
1276TEST_F(AluFDivConstrainingTest, AluFDivByZeroNonFFTagMismatchTraceGen)
1277{
1278 auto a = MemoryValue::from_tag(MemoryTag::U8, 2);
1279 auto b = MemoryValue::from_tag(MemoryTag::U16, 0);
1280 auto c = MemoryValue::from_tag(MemoryTag::FF, 0);
1281 auto trace = process_fdiv_with_tracegen({ a, b, c });
1282 check_relation<alu>(trace);
1283 check_all_interactions<AluTraceBuilder>(trace);
1284}
1285
1286TEST_F(AluFDivConstrainingTest, NegativeAluFDivByZero)
1287{
1288 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
1289 auto b = MemoryValue::from_tag(MemoryTag::FF, 5);
1290 auto c = a / b;
1291 auto trace = process_fdiv_trace({ a, b, c });
1292 check_all_interactions<AluTraceBuilder>(trace);
1293 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1294 check_relation<alu>(trace);
1295
1296 // Set b, b_inv to 0...
1297 trace.set(Column::alu_ib, 0, 0);
1298 trace.set(Column::alu_b_inv, 0, 0);
1299 // ...and since we haven't set the error correctly, we expect the below to fail:
1300 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1301 // We need to set the div_0_err and...
1302 trace.set(Column::alu_sel_div_0_err, 0, 1);
1303 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ERR_CHECK");
1304 // ...the overall sel_err:
1305 trace.set(Column::alu_sel_err, 0, 1);
1306 check_relation<alu>(trace);
1307
1308 // If we try and set b != 0 with div_0_err on, the below should fail:
1309 trace.set(Column::alu_ib, 0, b);
1310 trace.set(Column::alu_b_inv, 0, b.as_ff().invert());
1311 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1312}
1313
1314TEST_F(AluFDivConstrainingTest, NegativeAluFDivByZeroNonFFTagMismatch)
1315{
1316 auto a = MemoryValue::from_tag(MemoryTag::U8, 4);
1317 auto b = MemoryValue::from_tag(MemoryTag::U8, 2);
1318 // An incorrect c_tag fails the relation rather than throwing a tag error - we want to test the throw here, so
1319 // setting c to be the correct tag:
1320 auto c = MemoryValue::from_tag(MemoryTag::FF, 2);
1321 auto tag = static_cast<uint8_t>(MemoryTag::U8);
1322
1323 auto trace = TestTraceContainer({
1324 {
1325 { C::alu_b_inv, b.as_ff().invert() },
1326 { C::alu_ia, a },
1327 { C::alu_ia_tag, tag },
1328 { C::alu_ib, b },
1329 { C::alu_ib_tag, tag },
1330 { C::alu_ic, c },
1331 { C::alu_ic_tag, static_cast<uint8_t>(MemoryTag::FF) },
1332 { C::alu_max_bits, get_tag_bits(MemoryTag::U8) },
1333 { C::alu_max_value, get_tag_max_value(MemoryTag::U8) },
1334 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_FDIV },
1335 { C::alu_sel, 1 },
1336 { C::alu_sel_op_fdiv, 1 },
1337 { C::alu_sel_tag_err, 1 },
1338 { C::alu_sel_err, 1 },
1339 { C::alu_tag_ff_diff_inv, (FF(tag) - FF(static_cast<uint8_t>(MemoryTag::FF))).invert() },
1340 { C::alu_tag_u128_diff_inv, (FF(tag) - FF(static_cast<uint8_t>(MemoryTag::U128))).invert() },
1341 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
1342 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
1343 { C::execution_mem_tag_reg_2_, static_cast<uint8_t>(MemoryTag::FF) }, // = ic_tag
1344 { C::execution_register_0_, a }, // = ia
1345 { C::execution_register_1_, b }, // = ib
1346 { C::execution_register_2_, c }, // = ic
1347 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
1348 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_FDIV }, // = alu_op_id
1349 { C::execution_sel_opcode_error, 1 }, // = sel_err
1350 },
1351 });
1352
1353 // Every column is set up correctly. All checks should pass:
1354 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1355 precomputed_builder.process_tag_parameters(trace);
1356 check_relation<alu>(trace);
1357 check_all_interactions<AluTraceBuilder>(trace);
1358 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1359
1360 // We un-toggle sel_tag_err and sel_err and expect the following failure:
1361 trace.set(Column::alu_sel_tag_err, 0, 0);
1362 trace.set(Column::alu_sel_err, 0, 0);
1363 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TAG_ERR_CHECK");
1364
1365 // We try to cheat by setting the tag diff inverse to 0 and claiming a is FF, but expect the following failure:
1366 trace.set(Column::alu_tag_ff_diff_inv, 0, 0);
1367 trace.set(Column::alu_sel_is_ff, 0, 1);
1368 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TAG_IS_FF");
1369
1370 // Reset to the correct values:
1371 trace.set(Column::alu_tag_ff_diff_inv, 0, (FF(tag) - FF(static_cast<uint8_t>(MemoryTag::FF))).invert());
1372 trace.set(Column::alu_sel_is_ff, 0, 0);
1373 trace.set(Column::alu_sel_tag_err, 0, 1);
1374 trace.set(Column::alu_sel_err, 0, 1);
1375 check_relation<alu>(trace);
1376
1377 // For FDIV, we can have both FF and dividing by zero errors:
1378 trace.set(Column::alu_ib, 0, 0);
1379 trace.set(Column::alu_b_inv, 0, 0);
1380 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1381 trace.set(Column::alu_sel_div_0_err, 0, 1);
1382 check_relation<alu>(trace);
1383 check_all_interactions<AluTraceBuilder>(trace);
1384 trace.set(Column::execution_register_1_, 0, 0);
1385 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1386
1387 // Setting b to u16 also creates a tag mismatch we can handle with the same selectors:
1388 trace.set(Column::alu_ib_tag, 0, static_cast<uint8_t>(MemoryTag::U16));
1389 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "AB_TAGS_CHECK");
1390 trace.set(Column::alu_sel_ab_tag_mismatch, 0, 1);
1391 trace.set(Column::alu_ab_tags_diff_inv, 0, (FF(tag) - FF(static_cast<uint8_t>(MemoryTag::U16))).invert());
1392 check_relation<alu>(trace);
1393}
1394
1395// EQ TESTS
1396
1397const std::vector<MemoryValue> TEST_VALUES_EQ_OUT(NUM_OF_TAGS, MemoryValue::from_tag(MemoryTag::U1, 0));
1398
1399const std::vector<ThreeOperandTestParams> TEST_VALUES_EQ = zip_helper(TEST_VALUES_EQ_OUT);
1400
1401class AluEQConstrainingTest : public AluConstrainingTest, public ::testing::WithParamInterface<ThreeOperandTestParams> {
1402 public:
1403 TestTraceContainer process_eq_with_tracegen(const ThreeOperandTestParams& params)
1404 {
1405 TestTraceContainer trace;
1406 auto [a, b, c] = params;
1407
1408 builder.process(
1409 {
1410 { .operation = simulation::AluOperation::EQ, .a = a, .b = b, .c = c },
1411 },
1412 trace);
1413
1414 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1415 precomputed_builder.process_tag_parameters(trace);
1416 return trace;
1417 }
1418};
1419
1420INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluEQConstrainingTest, ::testing::ValuesIn(TEST_VALUES_EQ));
1421
1422TEST_P(AluEQConstrainingTest, AluEQTraceGen)
1423{
1424 const MemoryValue& param = std::get<0>(GetParam());
1425 auto trace =
1426 process_eq_with_tracegen(ThreeOperandTestParams{ param, param, MemoryValue::from_tag(MemoryTag::U1, 1) });
1427 check_all_interactions<AluTraceBuilder>(trace);
1428 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1429 check_relation<alu>(trace);
1430}
1431
1432TEST_P(AluEQConstrainingTest, AluInEQTraceGen)
1433{
1434 auto trace = process_eq_with_tracegen(GetParam());
1435 check_all_interactions<AluTraceBuilder>(trace);
1436 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1437 check_relation<alu>(trace);
1438}
1439
1440TEST_P(AluEQConstrainingTest, NegativeAluEqResult)
1441{
1442 auto params = GetParam();
1443 for (const bool is_eq : { false, true }) {
1444 auto trace = process_eq_with_tracegen(is_eq ? ThreeOperandTestParams{ std::get<0>(params),
1445 std::get<0>(params),
1446 MemoryValue::from_tag(MemoryTag::U1, 1) }
1447 : params);
1448 check_relation<alu>(trace);
1449 check_all_interactions<AluTraceBuilder>(trace);
1450 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1451 bool c = trace.get(Column::alu_ic, 0) == 1;
1452 // Swap the result bool:
1453 trace.set(Column::alu_ic, 0, static_cast<uint8_t>(!c));
1454 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "EQ_OP_MAIN");
1455 }
1456}
1457
1458TEST_P(AluEQConstrainingTest, NegativeAluEqHelper)
1459{
1460 auto trace = process_eq_with_tracegen(GetParam());
1461 check_relation<alu>(trace);
1462 check_all_interactions<AluTraceBuilder>(trace);
1463 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1464 auto ab_diff_inv = trace.get(Column::alu_ab_diff_inv, 0);
1465 trace.set(Column::alu_ab_diff_inv, 0, ab_diff_inv + 1);
1466 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "EQ_OP_MAIN");
1467}
1468
1469// LT TESTS
1470
1471const std::vector<MemoryValue> TEST_VALUES_LT_OUT = {
1472 MemoryValue::from_tag(MemoryTag::U1, 0), MemoryValue::from_tag(MemoryTag::U1, 0),
1473 MemoryValue::from_tag(MemoryTag::U1, 0), MemoryValue::from_tag(MemoryTag::U1, 1),
1474 MemoryValue::from_tag(MemoryTag::U1, 0), MemoryValue::from_tag(MemoryTag::U1, 0),
1475 MemoryValue::from_tag(MemoryTag::U1, 0),
1476};
1477
1478const std::vector<ThreeOperandTestParams> TEST_VALUES_LT = zip_helper(TEST_VALUES_LT_OUT);
1479
1480class AluLTConstrainingTest : public AluConstrainingTest, public ::testing::WithParamInterface<ThreeOperandTestParams> {
1481 public:
1482 TestTraceContainer process_lt_trace(ThreeOperandTestParams params)
1483 {
1484 auto [a, b, c] = params;
1485 auto mem_tag = a.get_tag();
1486 auto tag = static_cast<uint8_t>(mem_tag);
1487 auto is_ff = mem_tag == MemoryTag::FF;
1488
1489 auto trace = TestTraceContainer({
1490 {
1491 { C::alu_ia, a },
1492 { C::alu_ia_tag, tag },
1493 { C::alu_ib, b },
1494 { C::alu_ib_tag, tag },
1495 { C::alu_ic, c },
1496 { C::alu_ic_tag, static_cast<uint8_t>(MemoryTag::U1) },
1497 { C::alu_gt_input_a, b },
1498 { C::alu_gt_input_b, a },
1499 { C::alu_gt_result_c, c },
1500 { C::alu_max_bits, get_tag_bits(mem_tag) },
1501 { C::alu_max_value, get_tag_max_value(mem_tag) },
1502 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_LT },
1503 { C::alu_sel, 1 },
1504 { C::alu_sel_ff_gt, static_cast<uint8_t>(is_ff) },
1505 { C::alu_sel_int_gt, static_cast<uint8_t>(!is_ff) },
1506 { C::alu_sel_is_ff, static_cast<uint8_t>(is_ff) },
1507 { C::alu_sel_op_lt, 1 },
1508 { C::alu_tag_ff_diff_inv, is_ff ? 0 : FF(tag - static_cast<uint8_t>(MemoryTag::FF)).invert() },
1509 { C::alu_sel_is_u128, tag == static_cast<uint8_t>(MemoryTag::U128) ? 1 : 0 },
1510 { C::alu_tag_u128_diff_inv,
1511 tag == static_cast<uint8_t>(MemoryTag::U128)
1512 ? 0
1513 : FF(tag - static_cast<uint8_t>(MemoryTag::U128)).invert() },
1514 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
1515 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
1516 { C::execution_mem_tag_reg_2_, static_cast<uint8_t>(MemoryTag::U1) }, // = ic_tag
1517 { C::execution_register_0_, a }, // = ia
1518 { C::execution_register_1_, b }, // = ib
1519 { C::execution_register_2_, c }, // = ic
1520 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
1521 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_LT }, // = alu_op_id
1522 },
1523 });
1524
1525 if (is_ff) {
1526 field_gt_builder.process({ { .a = b, .b = a, .gt_result = c.as_ff() == 1 } }, trace);
1527 } else {
1528 gt_builder.process({ { .a = static_cast<uint128_t>(b.as_ff()),
1529 .b = static_cast<uint128_t>(a.as_ff()),
1530 .result = c.as_ff() == 1 } },
1531 trace);
1532 }
1533
1534 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1535 precomputed_builder.process_tag_parameters(trace);
1536 return trace;
1537 }
1538
1539 TestTraceContainer process_lt_with_tracegen(ThreeOperandTestParams params)
1540 {
1541 TestTraceContainer trace;
1542 auto [a, b, c] = params;
1543 auto is_ff = a.get_tag() == MemoryTag::FF;
1544
1545 builder.process(
1546 {
1547 { .operation = simulation::AluOperation::LT, .a = a, .b = b, .c = c },
1548 },
1549 trace);
1550
1551 if (is_ff) {
1552 field_gt_builder.process({ { .a = b, .b = a, .gt_result = c.as_ff() == 1 } }, trace);
1553 } else {
1554 gt_builder.process({ { .a = static_cast<uint128_t>(b.as_ff()),
1555 .b = static_cast<uint128_t>(a.as_ff()),
1556 .result = c.as_ff() == 1 } },
1557 trace);
1558 }
1559 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1560 precomputed_builder.process_tag_parameters(trace);
1561 return trace;
1562 }
1563};
1564
1565INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluLTConstrainingTest, ::testing::ValuesIn(TEST_VALUES_LT));
1566
1567TEST_P(AluLTConstrainingTest, AluLT)
1568{
1569 auto trace = process_lt_trace(GetParam());
1570 check_all_interactions<AluTraceBuilder>(trace);
1571 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1572 check_relation<alu>(trace);
1573}
1574
1575TEST_P(AluLTConstrainingTest, AluLTTraceGen)
1576{
1577 auto trace = process_lt_with_tracegen(GetParam());
1578 check_all_interactions<AluTraceBuilder>(trace);
1579 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1580 check_relation<alu>(trace);
1581}
1582
1583TEST_P(AluLTConstrainingTest, NegativeAluLT)
1584{
1585 auto params = GetParam();
1586 auto trace = process_lt_trace(params);
1587 auto is_ff = std::get<0>(params).get_tag() == MemoryTag::FF;
1588 check_relation<alu>(trace);
1589 check_all_interactions<AluTraceBuilder>(trace);
1590 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1591 bool c = trace.get(Column::alu_ic, 0) == 1;
1592 // Swap the result bool:
1593 trace.set(Column::alu_ic, 0, static_cast<uint8_t>(!c));
1594 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "GT_ASSIGN_RESULT_C");
1595 trace.set(Column::alu_gt_result_c, 0, static_cast<uint8_t>(!c));
1596
1597 if (is_ff) {
1598 EXPECT_THROW_WITH_MESSAGE((check_interaction<AluTraceBuilder, lookup_alu_ff_gt_settings>(trace)),
1599 "LOOKUP_ALU_FF_GT");
1600 } else {
1601 EXPECT_THROW_WITH_MESSAGE((check_interaction<AluTraceBuilder, lookup_alu_int_gt_settings>(trace)),
1602 "LOOKUP_ALU_INT_GT");
1603 }
1604}
1605
1606// LTE TESTS
1607
1608const std::vector<ThreeOperandTestParams> TEST_VALUES_LTE = zip_helper(TEST_VALUES_LT_OUT);
1609
1610class AluLTEConstrainingTest : public AluConstrainingTest,
1611 public ::testing::WithParamInterface<ThreeOperandTestParams> {
1612 public:
1613 TestTraceContainer process_lte_trace(ThreeOperandTestParams params, bool eq = false)
1614 {
1615 auto [a, b, c] = params;
1616 auto mem_tag = a.get_tag();
1617 auto tag = static_cast<uint8_t>(mem_tag);
1618 auto is_ff = mem_tag == MemoryTag::FF;
1619 b = eq ? a : b;
1620 c = eq ? MemoryValue::from_tag(MemoryTag::U1, 1) : c;
1621
1622 auto trace = TestTraceContainer({
1623 {
1624 { C::alu_ia, a },
1625 { C::alu_ia_tag, tag },
1626 { C::alu_ib, b },
1627 { C::alu_ib_tag, tag },
1628 { C::alu_ic, c },
1629 { C::alu_ic_tag, static_cast<uint8_t>(MemoryTag::U1) },
1630 { C::alu_gt_input_a, a },
1631 { C::alu_gt_input_b, b },
1632 { C::alu_gt_result_c, c.as_ff() == 0 ? 1 : 0 },
1633 { C::alu_max_bits, get_tag_bits(mem_tag) },
1634 { C::alu_max_value, get_tag_max_value(mem_tag) },
1635 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_LTE },
1636 { C::alu_sel, 1 },
1637 { C::alu_sel_ff_gt, static_cast<uint8_t>(is_ff) },
1638 { C::alu_sel_int_gt, static_cast<uint8_t>(!is_ff) },
1639 { C::alu_sel_is_ff, static_cast<uint8_t>(is_ff) },
1640 { C::alu_sel_op_lte, 1 },
1641 { C::alu_tag_ff_diff_inv, is_ff ? 0 : FF(tag - static_cast<uint8_t>(MemoryTag::FF)).invert() },
1642 { C::alu_sel_is_u128, tag == static_cast<uint8_t>(MemoryTag::U128) ? 1 : 0 },
1643 { C::alu_tag_u128_diff_inv,
1644 tag == static_cast<uint8_t>(MemoryTag::U128)
1645 ? 0
1646 : FF(tag - static_cast<uint8_t>(MemoryTag::U128)).invert() },
1647 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
1648 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
1649 { C::execution_mem_tag_reg_2_, static_cast<uint8_t>(MemoryTag::U1) }, // = ic_tag
1650 { C::execution_register_0_, a }, // = ia
1651 { C::execution_register_1_, b }, // = ib
1652 { C::execution_register_2_, c }, // = ic
1653 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
1654 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_LTE }, // = alu_op_id
1655 },
1656 });
1657
1658 if (is_ff) {
1659 field_gt_builder.process({ { .a = a, .b = b, .gt_result = c.as_ff() == 0 } }, trace);
1660 } else {
1661 gt_builder.process({ { .a = static_cast<uint128_t>(a.as_ff()),
1662 .b = static_cast<uint128_t>(b.as_ff()),
1663 .result = c.as_ff() == 0 } },
1664 trace);
1665 }
1666 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1667 precomputed_builder.process_tag_parameters(trace);
1668 return trace;
1669 }
1670
1671 TestTraceContainer process_lte_with_tracegen(ThreeOperandTestParams params, bool eq = false)
1672 {
1673 TestTraceContainer trace;
1674 auto [a, b, c] = params;
1675 auto is_ff = a.get_tag() == MemoryTag::FF;
1676 b = eq ? a : b;
1677 c = eq ? MemoryValue::from_tag(MemoryTag::U1, 1) : c;
1678
1679 builder.process(
1680 {
1681 { .operation = simulation::AluOperation::LTE, .a = a, .b = b, .c = c },
1682 },
1683 trace);
1684
1685 if (is_ff) {
1686 field_gt_builder.process({ { .a = a, .b = b, .gt_result = c.as_ff() == 0 } }, trace);
1687 } else {
1688 gt_builder.process({ { .a = static_cast<uint128_t>(a.as_ff()),
1689 .b = static_cast<uint128_t>(b.as_ff()),
1690 .result = c.as_ff() == 0 } },
1691 trace);
1692 }
1693 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1694 precomputed_builder.process_tag_parameters(trace);
1695 return trace;
1696 }
1697};
1698
1699INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluLTEConstrainingTest, ::testing::ValuesIn(TEST_VALUES_LTE));
1700
1701TEST_P(AluLTEConstrainingTest, AluLTE)
1702{
1703 auto trace = process_lte_trace(GetParam());
1704 check_all_interactions<AluTraceBuilder>(trace);
1705 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1706 check_relation<alu>(trace);
1707}
1708
1709TEST_P(AluLTEConstrainingTest, AluLTEEq)
1710{
1711 auto trace = process_lte_trace(GetParam(), true);
1712 check_all_interactions<AluTraceBuilder>(trace);
1713 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1714 check_relation<alu>(trace);
1715}
1716
1717TEST_P(AluLTEConstrainingTest, AluLTETraceGen)
1718{
1719 auto trace = process_lte_with_tracegen(GetParam());
1720 check_all_interactions<AluTraceBuilder>(trace);
1721 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1722 check_relation<alu>(trace);
1723}
1724
1725TEST_P(AluLTEConstrainingTest, AluLTEEqTraceGen)
1726{
1727 auto trace = process_lte_with_tracegen(GetParam(), true);
1728 check_all_interactions<AluTraceBuilder>(trace);
1729 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1730 check_relation<alu>(trace);
1731}
1732
1733TEST_P(AluLTEConstrainingTest, NegativeAluLTEResult)
1734{
1735 auto params = GetParam();
1736
1737 for (const bool is_eq : { false, true }) {
1738 auto trace = process_lte_trace(params, is_eq);
1739 auto is_ff = std::get<0>(params).get_tag() == MemoryTag::FF;
1740 check_relation<alu>(trace);
1741 check_all_interactions<AluTraceBuilder>(trace);
1742 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1743 bool c = trace.get(Column::alu_ic, 0) == 1;
1744 // Swap the result bool:
1745 trace.set(Column::alu_ic, 0, static_cast<uint8_t>(!c));
1746 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "GT_ASSIGN_RESULT_C");
1747 trace.set(Column::alu_gt_result_c, 0, static_cast<uint8_t>(c));
1748
1749 if (is_ff) {
1750 EXPECT_THROW_WITH_MESSAGE((check_interaction<AluTraceBuilder, lookup_alu_ff_gt_settings>(trace)),
1751 "LOOKUP_ALU_FF_GT");
1752 } else {
1753 EXPECT_THROW_WITH_MESSAGE((check_interaction<AluTraceBuilder, lookup_alu_int_gt_settings>(trace)),
1754 "LOOKUP_ALU_INT_GT");
1755 }
1756 }
1757}
1758
1759TEST_P(AluLTEConstrainingTest, NegativeAluLTEInput)
1760{
1761 auto params = GetParam();
1762
1763 for (const bool is_eq : { false, true }) {
1764 auto trace = process_lte_trace(params, is_eq);
1765 auto is_ff = std::get<0>(params).get_tag() == MemoryTag::FF;
1766 check_relation<alu>(trace);
1767 check_all_interactions<AluTraceBuilder>(trace);
1768 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1769 bool c = trace.get(Column::alu_ic, 0) == 1;
1770 auto a = trace.get(Column::alu_ia, 0);
1771 auto wrong_b = c ? a - 1 : a + 1;
1772 trace.set(Column::alu_ib, 0, wrong_b);
1773 trace.set(Column::alu_gt_input_b, 0, wrong_b);
1774 // We rely on lookups, so we expect the relations to still pass...
1775 check_relation<alu>(trace);
1776
1777 // ... but the lookup will fail (TODO(MW): properly add a gt and => range check events so it fails because c
1778 // is wrong, rather than because this test has not processed the events):
1779 if (is_ff) {
1780 EXPECT_THROW_WITH_MESSAGE((check_interaction<AluTraceBuilder, lookup_alu_ff_gt_settings>(trace)),
1781 "LOOKUP_ALU_FF_GT");
1782 } else {
1783 EXPECT_THROW_WITH_MESSAGE((check_interaction<AluTraceBuilder, lookup_alu_int_gt_settings>(trace)),
1784 "LOOKUP_ALU_INT_GT");
1785 }
1786 }
1787}
1788
1789// NOT Opcode TESTS
1790
1791const std::vector<MemoryValue> TEST_VALUES_NOT_OUT = {
1792 MemoryValue::from_tag(MemoryTag::U1, 0),
1793 MemoryValue::from_tag(MemoryTag::U8, 55),
1794 MemoryValue::from_tag(MemoryTag::U16, 65505),
1795 MemoryValue::from_tag(MemoryTag::U32, 9),
1796 MemoryValue::from_tag(MemoryTag::U64, 9),
1797 MemoryValue::from_tag(MemoryTag::U128, 9),
1798 MemoryValue::from_tag(static_cast<MemoryTag>(0), 0), // For FF, b is the default value of 0 with tag 0
1799};
1800
1801const std::vector<TwoOperandTestParams> TEST_VALUES_NOT = zip_helper_two_op(TEST_VALUES_NOT_OUT);
1802
1803class AluNotConstrainingTest : public AluConstrainingTest, public ::testing::WithParamInterface<TwoOperandTestParams> {
1804 public:
1805 TestTraceContainer process_not_with_tracegen(const TwoOperandTestParams& params, bool error = false)
1806 {
1807 TestTraceContainer trace;
1808 auto [a, b] = params;
1809 auto is_ff = a.get_tag() == MemoryTag::FF;
1810
1811 builder.process(
1812 {
1813 { .operation = simulation::AluOperation::NOT, .a = a, .b = b, .error = error || is_ff },
1814 },
1815 trace);
1816
1817 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1818 precomputed_builder.process_tag_parameters(trace);
1819 return trace;
1820 }
1821};
1822
1823INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluNotConstrainingTest, ::testing::ValuesIn(TEST_VALUES_NOT));
1824
1825TEST_P(AluNotConstrainingTest, AluNotTraceGen)
1826{
1827 auto trace = process_not_with_tracegen(GetParam());
1828 check_all_interactions<AluTraceBuilder>(trace);
1829 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1830 check_relation<alu>(trace);
1831}
1832
1833TEST_P(AluNotConstrainingTest, NegativeAluNotTraceGen)
1834{
1835 auto params = GetParam();
1836 auto trace = process_not_with_tracegen(params);
1837 check_all_interactions<AluTraceBuilder>(trace);
1838 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1839 check_relation<alu>(trace);
1840 trace.set(Column::alu_ib, 0, trace.get(Column::alu_ib, 0) + 1); // Mutate output
1841 // The FF case <==> tag_err for NOT, so NOT_OP_MAIN is gated:
1842 if (std::get<0>(params).get_tag() != MemoryTag::FF) {
1843 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "NOT_OP_MAIN");
1844 }
1845}
1846
1847// Unconditional check for AB_TAGS_CHECK error for NOT opcode. This cannot satsify the constraints.
1848TEST_P(AluNotConstrainingTest, AluNotTraceGenTagError)
1849{
1850 auto [a, b] = GetParam();
1851 auto trace = process_not_with_tracegen(
1852 TwoOperandTestParams{ a, MemoryValue::from_tag(TAG_ERROR_TEST_VALUES.at(b.get_tag()), b.as_ff()) }, true);
1853 check_all_interactions<AluTraceBuilder>(trace);
1854 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1855 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ONLY_RELEVANT_CHECK_AB_TAGS_ERROR");
1856}
1857
1858// Supported TAG error when a is of FF type.
1859TEST_F(AluNotConstrainingTest, AluNotTraceGenTagErrorFF)
1860{
1861 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
1862 auto b = MemoryValue::from_tag(MemoryTag::FF, 253);
1863 auto trace = process_not_with_tracegen(TwoOperandTestParams{ a, b }, true);
1864 check_all_interactions<AluTraceBuilder>(trace);
1865 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1866 check_relation<alu>(trace);
1867}
1868
1869// SHL TESTS
1870
1871const std::vector<MemoryValue> TEST_VALUES_SHL_OUT = {
1872 MemoryValue::from_tag(MemoryTag::U1, 1),
1873 MemoryValue::from_tag(MemoryTag::U8, 0),
1874 MemoryValue::from_tag(MemoryTag::U16, 0),
1875 MemoryValue::from_tag(MemoryTag::U32, 0xfffffec0),
1876 MemoryValue::from_tag(MemoryTag::U64, 0xfffffffffffffec0ULL),
1877 MemoryValue::from_tag(MemoryTag::U128, (uint256_t(1) << 128) - 320), // 0xfffffffffffffffffffffffffffffec0
1878};
1879
1880const std::vector<ThreeOperandTestParams> TEST_VALUES_SHL = zip_helper(TEST_VALUES_SHL_OUT);
1881
1882class AluShlConstrainingTest : public AluConstrainingTest,
1883 public ::testing::WithParamInterface<ThreeOperandTestParams> {
1884 public:
1885 TestTraceContainer process_shl_trace(ThreeOperandTestParams params)
1886 {
1887 auto [a, b, c] = params;
1888
1889 auto mem_tag = a.get_tag();
1890 auto tag = static_cast<uint8_t>(mem_tag);
1891 auto tag_bits = get_tag_bits(mem_tag);
1892 auto a_num = static_cast<uint128_t>(a.as_ff());
1893 auto b_num = static_cast<uint128_t>(b.as_ff());
1894
1895 auto overflow = b_num > tag_bits;
1896 uint128_t shift_lo_bits = overflow ? tag_bits : tag_bits - b_num;
1897 uint128_t shift_hi_bits = overflow ? tag_bits : b_num;
1898 auto two_pow_shift_lo_bits = static_cast<uint128_t>(1) << shift_lo_bits;
1899 auto a_lo = overflow ? b_num - tag_bits : a_num % two_pow_shift_lo_bits;
1900 auto a_hi = a_num >> shift_lo_bits;
1901
1902 auto trace = TestTraceContainer({
1903 {
1904 { C::alu_a_hi, a_hi },
1905 { C::alu_a_hi_bits, shift_hi_bits },
1906 { C::alu_a_lo, a_lo },
1907 { C::alu_a_lo_bits, shift_lo_bits },
1908 { C::alu_helper1, static_cast<uint128_t>(1) << b_num },
1909 { C::alu_ia, a },
1910 { C::alu_ia_tag, tag },
1911 { C::alu_ib, b },
1912 { C::alu_ib_tag, tag },
1913 { C::alu_ic, c },
1914 { C::alu_ic_tag, tag },
1915 { C::alu_max_bits, tag_bits },
1916 { C::alu_max_value, get_tag_max_value(mem_tag) },
1917 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_SHL },
1918 { C::alu_sel, 1 },
1919 { C::alu_sel_decompose_a, 1 },
1920 { C::alu_sel_op_shl, 1 },
1921 { C::alu_sel_shift_ops_no_overflow, overflow ? 0 : 1 },
1922 { C::alu_shift_lo_bits, shift_lo_bits },
1923 { C::alu_tag_ff_diff_inv, FF(tag - static_cast<uint8_t>(MemoryTag::FF)).invert() },
1924 { C::alu_sel_is_u128, tag == static_cast<uint8_t>(MemoryTag::U128) ? 1 : 0 },
1925 { C::alu_tag_u128_diff_inv,
1926 tag == static_cast<uint8_t>(MemoryTag::U128)
1927 ? 0
1928 : FF(tag - static_cast<uint8_t>(MemoryTag::U128)).invert() },
1929 { C::alu_two_pow_shift_lo_bits, two_pow_shift_lo_bits },
1930 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
1931 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
1932 { C::execution_mem_tag_reg_2_, tag }, // = ic_tag
1933 { C::execution_register_0_, a }, // = ia
1934 { C::execution_register_1_, b }, // = ib
1935 { C::execution_register_2_, c }, // = ic
1936 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
1937 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_SHL }, // = alu_op_id
1938
1939 },
1940 });
1941
1942 precomputed_builder.process_misc(trace, std::max(NUM_OF_TAGS, static_cast<uint8_t>(shift_lo_bits + 1)));
1943 precomputed_builder.process_tag_parameters(trace);
1944 precomputed_builder.process_power_of_2(trace);
1945 range_check_builder.process({ { .value = a_lo, .num_bits = static_cast<uint8_t>(shift_lo_bits) },
1946 { .value = a_hi, .num_bits = static_cast<uint8_t>(shift_hi_bits) } },
1947 trace);
1948
1949 return trace;
1950 }
1951
1952 TestTraceContainer process_shl_with_tracegen(ThreeOperandTestParams params, bool error = false)
1953 {
1954 TestTraceContainer trace;
1955 auto [a, b, c] = params;
1956 auto b_num = static_cast<uint128_t>(b.as_ff());
1957 auto tag_bits = get_tag_bits(a.get_tag());
1958 auto overflow = b_num > tag_bits;
1959 uint128_t shift_lo_bits = overflow ? tag_bits : tag_bits - b_num;
1960 auto a_lo = overflow ? b_num - tag_bits
1961 : static_cast<uint128_t>(a.as_ff()) % (static_cast<uint128_t>(1) << shift_lo_bits);
1962
1963 builder.process(
1964 {
1965 { .operation = simulation::AluOperation::SHL, .a = a, .b = b, .c = c, .error = error },
1966 },
1967 trace);
1968
1969 precomputed_builder.process_misc(trace, std::max(NUM_OF_TAGS, static_cast<uint8_t>(shift_lo_bits + 1)));
1970 precomputed_builder.process_tag_parameters(trace);
1971 precomputed_builder.process_power_of_2(trace);
1972 range_check_builder.process({ { .value = a_lo, .num_bits = static_cast<uint8_t>(shift_lo_bits) },
1973 { .value = static_cast<uint128_t>(a.as_ff()) >> shift_lo_bits,
1974 .num_bits = static_cast<uint8_t>(overflow ? tag_bits : b_num) } },
1975 trace);
1976 return trace;
1977 }
1978};
1979
1980INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluShlConstrainingTest, ::testing::ValuesIn(TEST_VALUES_SHL));
1981
1982TEST_P(AluShlConstrainingTest, AluShl)
1983{
1984 auto trace = process_shl_trace(GetParam());
1985 check_all_interactions<AluTraceBuilder>(trace);
1986 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1987 check_relation<alu>(trace);
1988}
1989
1990TEST_P(AluShlConstrainingTest, AluShlTraceGen)
1991{
1992 auto trace = process_shl_with_tracegen(GetParam());
1993 check_all_interactions<AluTraceBuilder>(trace);
1994 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1995 check_relation<alu>(trace);
1996}
1997
1998TEST_F(AluShlConstrainingTest, NegativeAluShlFF)
1999{
2000 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
2001 auto b = MemoryValue::from_tag(MemoryTag::FF, 5);
2002 auto c = MemoryValue::from_tag(MemoryTag::FF, 2 << 5);
2003 auto trace = process_shl_with_tracegen({ a, b, c }, true);
2004 check_relation<alu>(trace);
2005 check_all_interactions<AluTraceBuilder>(trace);
2006
2007 // Check the edge case of FF tag (=> max_bits = 0) and b = 0:
2008 trace.set(Column::alu_ib, 0, 0);
2009 check_relation<alu>(trace);
2010 check_all_interactions<AluTraceBuilder>(trace);
2011
2012 // Disable tag and error selectors:
2013 trace.set(Column::alu_sel_tag_err, 0, 0);
2014 trace.set(Column::alu_sel_err, 0, 0);
2015 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TAG_ERR_CHECK");
2016}
2017
2018TEST_F(AluShlConstrainingTest, NegativeAluShlTagMismatchOverflow)
2019{
2020 auto a = MemoryValue::from_tag(MemoryTag::U8, 2);
2021 auto b = MemoryValue::from_tag(MemoryTag::U32, 256);
2022 auto c = MemoryValue::from_tag(MemoryTag::U8, 0);
2023 auto trace = process_shl_with_tracegen({ a, b, c }, true);
2024 check_relation<alu>(trace);
2025 check_all_interactions<AluTraceBuilder>(trace);
2026
2027 // Disable tag and error selectors:
2028 trace.set(Column::alu_sel_tag_err, 0, 0);
2029 trace.set(Column::alu_sel_err, 0, 0);
2030 // Disable ab tag mismatch error:
2031 trace.set(Column::alu_sel_ab_tag_mismatch, 0, 0);
2032 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "AB_TAGS_CHECK");
2033
2034 // Second attempt with setting the ab tags diff inverse to zero:
2035 trace.set(Column::alu_ab_tags_diff_inv, 0, 0);
2036 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "AB_TAGS_CHECK");
2037
2038 // Reset only ab tag diff related columns:
2039 trace.set(Column::alu_ab_tags_diff_inv,
2040 0,
2041 (FF(static_cast<uint8_t>(MemoryTag::U8)) - FF(static_cast<uint8_t>(MemoryTag::U32))).invert());
2042 trace.set(Column::alu_sel_ab_tag_mismatch, 0, 1);
2043 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TAG_ERR_CHECK");
2044}
2045
2046// SHR TESTS
2047
2048const std::vector<MemoryValue> TEST_VALUES_SHR_OUT = {
2049 MemoryValue::from_tag(MemoryTag::U1, 1),
2050 MemoryValue::from_tag(MemoryTag::U8, 0),
2051 MemoryValue::from_tag(MemoryTag::U16, 0),
2052 MemoryValue::from_tag(MemoryTag::U32, 0x7ffffff),
2053 MemoryValue::from_tag(MemoryTag::U64, 0x7ffffffffffffffULL),
2054 MemoryValue::from_tag(MemoryTag::U128,
2055 (uint256_t(1) << 128) - 1 - (uint256_t(248) << 120)), // 0x7ffffffffffffffffffffffffffffff
2056};
2057
2058const std::vector<ThreeOperandTestParams> TEST_VALUES_SHR = zip_helper(TEST_VALUES_SHR_OUT);
2059
2060class AluShrConstrainingTest : public AluConstrainingTest,
2061 public ::testing::WithParamInterface<ThreeOperandTestParams> {
2062 public:
2063 TestTraceContainer process_shr_trace(ThreeOperandTestParams params)
2064 {
2065 auto [a, b, c] = params;
2066
2067 auto mem_tag = a.get_tag();
2068 auto tag = static_cast<uint8_t>(mem_tag);
2069 auto tag_bits = get_tag_bits(mem_tag);
2070 auto a_num = static_cast<uint128_t>(a.as_ff());
2071 auto b_num = static_cast<uint128_t>(b.as_ff());
2072
2073 auto overflow = b_num > tag_bits;
2074 uint128_t shift_lo_bits = overflow ? tag_bits : b_num;
2075 uint128_t shift_hi_bits = overflow ? tag_bits : tag_bits - b_num;
2076 auto two_pow_shift_lo_bits = static_cast<uint128_t>(1) << shift_lo_bits;
2077 auto a_lo = overflow ? b_num - tag_bits : a_num % two_pow_shift_lo_bits;
2078 auto a_hi = a_num >> shift_lo_bits;
2079
2080 auto trace = TestTraceContainer({
2081 {
2082 { C::alu_a_hi, a_hi },
2083 { C::alu_a_hi_bits, shift_hi_bits },
2084 { C::alu_a_lo, a_lo },
2085 { C::alu_a_lo_bits, shift_lo_bits },
2086 { C::alu_ia, a },
2087 { C::alu_ia_tag, tag },
2088 { C::alu_ib, b },
2089 { C::alu_ib_tag, tag },
2090 { C::alu_ic, c },
2091 { C::alu_ic_tag, tag },
2092 { C::alu_max_bits, tag_bits },
2093 { C::alu_max_value, get_tag_max_value(mem_tag) },
2094 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_SHR },
2095 { C::alu_sel, 1 },
2096 { C::alu_sel_decompose_a, 1 },
2097 { C::alu_sel_op_shr, 1 },
2098 { C::alu_sel_shift_ops_no_overflow, overflow ? 0 : 1 },
2099 { C::alu_shift_lo_bits, shift_lo_bits },
2100 { C::alu_tag_ff_diff_inv, FF(tag - static_cast<uint8_t>(MemoryTag::FF)).invert() },
2101 { C::alu_sel_is_u128, tag == static_cast<uint8_t>(MemoryTag::U128) ? 1 : 0 },
2102 { C::alu_tag_u128_diff_inv,
2103 tag == static_cast<uint8_t>(MemoryTag::U128)
2104 ? 0
2105 : FF(tag - static_cast<uint8_t>(MemoryTag::U128)).invert() },
2106 { C::alu_two_pow_shift_lo_bits, two_pow_shift_lo_bits },
2107 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
2108 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
2109 { C::execution_mem_tag_reg_2_, tag }, // = ic_tag
2110 { C::execution_register_0_, a }, // = ia
2111 { C::execution_register_1_, b }, // = ib
2112 { C::execution_register_2_, c }, // = ic
2113 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
2114 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_SHR }, // = alu_op_id
2115
2116 },
2117 });
2118
2119 precomputed_builder.process_misc(trace, std::max(NUM_OF_TAGS, static_cast<uint8_t>(shift_lo_bits + 1)));
2120 precomputed_builder.process_tag_parameters(trace);
2121 precomputed_builder.process_power_of_2(trace);
2122 range_check_builder.process({ { .value = a_lo, .num_bits = static_cast<uint8_t>(shift_lo_bits) },
2123 { .value = a_hi, .num_bits = static_cast<uint8_t>(shift_hi_bits) } },
2124 trace);
2125
2126 return trace;
2127 }
2128
2129 TestTraceContainer process_shr_with_tracegen(ThreeOperandTestParams params, bool error = false)
2130 {
2131 TestTraceContainer trace;
2132 auto [a, b, c] = params;
2133 auto b_num = static_cast<uint128_t>(b.as_ff());
2134 auto tag_bits = get_tag_bits(a.get_tag());
2135 auto overflow = b_num > tag_bits;
2136 uint128_t shift_lo_bits = overflow ? tag_bits : b_num;
2137 auto a_lo = overflow ? b_num - tag_bits
2138 : static_cast<uint128_t>(a.as_ff()) % (static_cast<uint128_t>(1) << shift_lo_bits);
2139
2140 builder.process(
2141 {
2142 { .operation = simulation::AluOperation::SHR, .a = a, .b = b, .c = c, .error = error },
2143 },
2144 trace);
2145
2146 precomputed_builder.process_misc(trace, std::max(NUM_OF_TAGS, static_cast<uint8_t>(shift_lo_bits + 1)));
2147 precomputed_builder.process_tag_parameters(trace);
2148 precomputed_builder.process_power_of_2(trace);
2149 range_check_builder.process({ { .value = a_lo, .num_bits = static_cast<uint8_t>(shift_lo_bits) },
2150 { .value = static_cast<uint128_t>(a.as_ff()) >> shift_lo_bits,
2151 .num_bits = static_cast<uint8_t>(overflow ? tag_bits : tag_bits - b_num) } },
2152 trace);
2153 return trace;
2154 }
2155};
2156
2157INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluShrConstrainingTest, ::testing::ValuesIn(TEST_VALUES_SHR));
2158
2159TEST_P(AluShrConstrainingTest, AluShr)
2160{
2161 auto trace = process_shr_trace(GetParam());
2162 check_all_interactions<AluTraceBuilder>(trace);
2163 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
2164 check_relation<alu>(trace);
2165}
2166
2167TEST_P(AluShrConstrainingTest, AluShrTraceGen)
2168{
2169 auto trace = process_shr_with_tracegen(GetParam());
2170 check_all_interactions<AluTraceBuilder>(trace);
2171 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
2172 check_relation<alu>(trace);
2173}
2174
2175TEST_F(AluShrConstrainingTest, NegativeAluShrFF)
2176{
2177 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
2178 auto b = MemoryValue::from_tag(MemoryTag::FF, 5);
2179 auto c = MemoryValue::from_tag(MemoryTag::FF, 2 << 5);
2180 auto trace = process_shr_with_tracegen({ a, b, c }, true);
2181 check_relation<alu>(trace);
2182 check_all_interactions<AluTraceBuilder>(trace);
2183
2184 // Check the edge case of FF tag (=> max_bits = 0) and b = 0:
2185 trace.set(Column::alu_ib, 0, 0);
2186 check_relation<alu>(trace);
2187 check_all_interactions<AluTraceBuilder>(trace);
2188
2189 // Disable tag and error selectors:
2190 trace.set(Column::alu_sel_tag_err, 0, 0);
2191 trace.set(Column::alu_sel_err, 0, 0);
2192 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TAG_ERR_CHECK");
2193}
2194
2195TEST_F(AluShrConstrainingTest, NegativeAluShrTagMismatchOverflow)
2196{
2197 auto a = MemoryValue::from_tag(MemoryTag::U16, 2);
2198 auto b = MemoryValue::from_tag(MemoryTag::U64, 123456);
2199 auto c = MemoryValue::from_tag(MemoryTag::U16, 0);
2200 auto trace = process_shr_with_tracegen({ a, b, c }, true);
2201 check_relation<alu>(trace);
2202 check_all_interactions<AluTraceBuilder>(trace);
2203 // Disable tag and error selectors:
2204 trace.set(Column::alu_sel_tag_err, 0, 0);
2205 trace.set(Column::alu_sel_err, 0, 0);
2206 // Disable ab tag mismatch error:
2207 trace.set(Column::alu_sel_ab_tag_mismatch, 0, 0);
2208 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "AB_TAGS_CHECK");
2209
2210 // Second attempt with setting the ab tags diff inverse to zero:
2211 trace.set(Column::alu_ab_tags_diff_inv, 0, 0);
2212 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "AB_TAGS_CHECK");
2213
2214 // Reset only ab tag diff related columns:
2215 trace.set(Column::alu_ab_tags_diff_inv,
2216 0,
2217 (FF(static_cast<uint8_t>(MemoryTag::U16)) - FF(static_cast<uint8_t>(MemoryTag::U64))).invert());
2218 trace.set(Column::alu_sel_ab_tag_mismatch, 0, 1);
2219 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TAG_ERR_CHECK");
2220}
2221
2222// TRUNCATE operation (SET/CAST opcodes)
2223
2224// Truncation is a special case as we always have FF MemoryValue inputs:
2225const std::vector<ThreeOperandTestParams> TEST_VALUES_TRUNCATE = {
2226 // Trivial truncation cases
2227 { MemoryValue::from_tag(MemoryTag::FF, 1),
2228 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U1)),
2229 MemoryValue::from_tag(MemoryTag::U1, 1) },
2230 { MemoryValue::from_tag(MemoryTag::FF, 42),
2231 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U8)),
2232 MemoryValue::from_tag(MemoryTag::U8, 42) },
2233 { MemoryValue::from_tag(MemoryTag::FF, 12345),
2234 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U16)),
2235 MemoryValue::from_tag(MemoryTag::U16, 12345) },
2236 { MemoryValue::from_tag(MemoryTag::FF, 123456789),
2237 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U32)),
2238 MemoryValue::from_tag(MemoryTag::U32, 123456789) },
2239 { MemoryValue::from_tag(MemoryTag::FF, 1234567890123456789ULL),
2240 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U64)),
2241 MemoryValue::from_tag(MemoryTag::U64, 1234567890123456789ULL) },
2242 { MemoryValue::from_tag(MemoryTag::FF, (uint256_t(1) << 127) + 23423429816234ULL),
2243 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U128)),
2244 MemoryValue::from_tag(MemoryTag::U128, (uint256_t(1) << 127) + 23423429816234ULL) },
2245 { MemoryValue::from_tag(MemoryTag::FF, FF::modulus - 3),
2246 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::FF)),
2247 MemoryValue::from_tag(MemoryTag::FF, FF::modulus - 3) },
2248 // Truncation cases (< 128 bits)
2249 { MemoryValue::from_tag(MemoryTag::FF, 212),
2250 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U1)),
2251 MemoryValue::from_tag(MemoryTag::U1, 0) },
2252 { MemoryValue::from_tag(MemoryTag::FF, 257),
2253 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U8)),
2254 MemoryValue::from_tag(MemoryTag::U8, 1) },
2255 { MemoryValue::from_tag(MemoryTag::FF, 65540),
2256 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U16)),
2257 MemoryValue::from_tag(MemoryTag::U16, 4) },
2258 { MemoryValue::from_tag(MemoryTag::FF, 4294967298ULL),
2259 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U32)),
2260 MemoryValue::from_tag(MemoryTag::U32, 2) },
2261 { MemoryValue::from_tag(MemoryTag::FF, 18446744073709551615ULL + 4),
2262 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U64)),
2263 MemoryValue::from_tag(MemoryTag::U64, 3) },
2264 // Truncation cases (>= 128 bits)
2265 { MemoryValue::from_tag(MemoryTag::FF, (uint256_t(134534) << 129) + 986132),
2266 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U1)),
2267 MemoryValue::from_tag(MemoryTag::U1, 0) },
2268 { MemoryValue::from_tag(MemoryTag::FF, FF::modulus - 128735618772ULL),
2269 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U8)),
2270 MemoryValue::from_tag(MemoryTag::U8, 45) },
2271 { MemoryValue::from_tag(MemoryTag::FF, (uint256_t(999) << 128) - 986132ULL),
2272 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U16)),
2273 MemoryValue::from_tag(MemoryTag::U16, 62444) },
2274 { MemoryValue::from_tag(MemoryTag::FF, (uint256_t(134534) << 198) + 986132ULL),
2275 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U32)),
2276 MemoryValue::from_tag(MemoryTag::U32, 986132ULL) },
2277 { MemoryValue::from_tag(MemoryTag::FF, (uint256_t(134534) << 198) - 986132ULL),
2278 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U64)),
2279 MemoryValue::from_tag(MemoryTag::U64, static_cast<uint64_t>(-986132ULL)) },
2280 { MemoryValue::from_tag(MemoryTag::FF, FF::modulus - 8723),
2281 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U128)),
2282 MemoryValue::from_tag(MemoryTag::U128, static_cast<uint128_t>(FF::modulus - 8723)) },
2283};
2284
2285class AluTruncateConstrainingTest : public AluConstrainingTest,
2286 public ::testing::WithParamInterface<ThreeOperandTestParams> {
2287 public:
2288 TestTraceContainer process_truncate_with_tracegen(const ThreeOperandTestParams& params, TestTraceContainer& trace)
2289 {
2290 auto [a, b, c] = params;
2291
2292 builder.process(
2293 {
2294 { .operation = simulation::AluOperation::TRUNCATE, .a = a, .b = b, .c = c },
2295 },
2296 trace);
2297
2298 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
2299 precomputed_builder.process_tag_parameters(trace);
2300
2301 auto is_non_trivial = trace.get(Column::alu_sel_trunc_non_trivial, 0) == 1;
2302
2303 if (is_non_trivial) {
2304 auto a_decomp = simulation::decompose_256(static_cast<uint256_t>(a.as_ff()));
2305 auto dst_tag = c.get_tag();
2306 uint8_t bits = get_tag_bits(dst_tag);
2307 range_check_builder.process({ { .value = dst_tag == MemoryTag::U128 ? 0 : a_decomp.lo >> bits,
2308 .num_bits = static_cast<uint8_t>(128 - bits) } },
2309 trace);
2310 auto is_gte_128 = trace.get(Column::alu_sel_trunc_gte_128, 0) == 1;
2311 if (is_gte_128) {
2312 auto p_limbs = simulation::decompose_256(FF::modulus);
2313 simulation::LimbsComparisonWitness p_sub_a_witness = { .lo = p_limbs.lo - a_decomp.lo,
2314 .hi = p_limbs.hi - a_decomp.hi,
2315 .borrow = false };
2316 field_gt_builder.process({ { .operation = simulation::FieldGreaterOperation::CANONICAL_DECOMPOSITION,
2317 .a = a,
2318 .a_limbs = a_decomp,
2319 .p_sub_a_witness = p_sub_a_witness } },
2320 trace);
2321 }
2322 }
2323
2324 return trace;
2325 }
2326
2327 TestTraceContainer process_set_with_tracegen(const ThreeOperandTestParams& params)
2328 {
2329 TestTraceContainer trace;
2330 auto [a, b, _c] = params;
2331 auto dst_tag = static_cast<MemoryTag>(static_cast<uint8_t>(b.as_ff()));
2332 auto c = MemoryValue::from_tag_truncating(dst_tag, a);
2333 trace.set(0,
2334 { {
2335 { Column::execution_sel_exec_dispatch_set, 1 },
2336 { Column::execution_rop_2_, a },
2337 { Column::execution_rop_1_, static_cast<uint8_t>(dst_tag) },
2338 { Column::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_TRUNCATE },
2339 { Column::execution_register_0_, c.as_ff() },
2340 { Column::execution_mem_tag_reg_0_, static_cast<uint8_t>(dst_tag) },
2341 } });
2342
2343 process_truncate_with_tracegen(params, trace);
2344
2345 return trace;
2346 }
2347
2348 TestTraceContainer process_cast_with_tracegen(const ThreeOperandTestParams& params)
2349 {
2350 TestTraceContainer trace;
2351 auto [a, b, _c] = params;
2352 auto dst_tag = static_cast<MemoryTag>(static_cast<uint8_t>(b.as_ff()));
2353 auto c = MemoryValue::from_tag_truncating(dst_tag, a);
2354 trace.set(0,
2355 { {
2356 { Column::execution_sel_exec_dispatch_cast, 1 },
2357 { Column::execution_register_0_, a },
2358 { Column::execution_rop_2_, static_cast<uint8_t>(dst_tag) },
2359 { Column::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_TRUNCATE },
2360 { Column::execution_register_1_, c.as_ff() },
2361 { Column::execution_mem_tag_reg_1_, static_cast<uint8_t>(dst_tag) },
2362 } });
2363
2364 process_truncate_with_tracegen(params, trace);
2365
2366 return trace;
2367 }
2368};
2369
2370INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluTruncateConstrainingTest, ::testing::ValuesIn(TEST_VALUES_TRUNCATE));
2371
2372TEST_P(AluTruncateConstrainingTest, AluSet)
2373{
2374 auto trace = process_set_with_tracegen(GetParam());
2375 check_all_interactions<AluTraceBuilder>(trace);
2376 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_set_settings>(trace);
2377 check_relation<alu>(trace);
2378}
2379
2380TEST_P(AluTruncateConstrainingTest, AluCast)
2381{
2382 auto trace = process_cast_with_tracegen(GetParam());
2383 check_all_interactions<AluTraceBuilder>(trace);
2384 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_set_settings>(trace);
2385 check_relation<alu>(trace);
2386}
2387
2388TEST_P(AluTruncateConstrainingTest, NegativeTruncateWrongTrivialCase)
2389{
2390 TestTraceContainer trace;
2391 process_truncate_with_tracegen(GetParam(), trace);
2392 check_relation<alu>(trace);
2393 bool is_trivial = trace.get(Column::alu_sel_trunc_trivial, 0) == 1;
2394 trace.set(Column::alu_sel_trunc_trivial, 0, static_cast<uint8_t>(!is_trivial));
2395 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "SEL_TRUNC");
2396 trace.set(Column::alu_sel_trunc_trivial, 0, static_cast<uint8_t>(is_trivial));
2397 trace.set(Column::alu_sel_trunc_non_trivial, 0, static_cast<uint8_t>(is_trivial));
2398 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "SEL_TRUNC");
2399}
2400
2401TEST_P(AluTruncateConstrainingTest, NegativeTruncateWrong128BitsCase)
2402{
2403 TestTraceContainer trace;
2404 process_truncate_with_tracegen(GetParam(), trace);
2405 check_relation<alu>(trace);
2406 bool is_lt_128 = trace.get(Column::alu_sel_trunc_lt_128, 0) == 1;
2407 trace.set(Column::alu_sel_trunc_lt_128, 0, static_cast<uint8_t>(!is_lt_128));
2408 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "SEL_TRUNC");
2409 trace.set(Column::alu_sel_trunc_lt_128, 0, static_cast<uint8_t>(is_lt_128));
2410 check_relation<alu>(trace);
2411 bool is_gte_128 = trace.get(Column::alu_sel_trunc_gte_128, 0) == 1;
2412 trace.set(Column::alu_sel_trunc_gte_128, 0, static_cast<uint8_t>(!is_gte_128));
2413 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "SEL_TRUNC");
2414}
2415
2416TEST_F(AluTruncateConstrainingTest, NegativeTruncateWrongMid)
2417{
2418 TestTraceContainer trace;
2419 process_truncate_with_tracegen({ MemoryValue::from_tag(MemoryTag::FF, 4294967298ULL),
2420 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U32)),
2421 MemoryValue::from_tag(MemoryTag::U32, 2) },
2422 trace);
2423 check_relation<alu>(trace);
2424 trace.set(Column::alu_mid, 0, 1234ULL);
2425 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TRUNC_LO_128_DECOMPOSITION");
2426}
2427
2428TEST_F(AluTruncateConstrainingTest, NegativeTruncateWrongMidBits)
2429{
2430 TestTraceContainer trace;
2431 process_truncate_with_tracegen({ MemoryValue::from_tag(MemoryTag::FF, FF::modulus - 2),
2432 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U1)),
2433 MemoryValue::from_tag(MemoryTag::U1, 1) },
2434 trace);
2435 check_relation<alu>(trace);
2436 trace.set(Column::alu_mid_bits, 0, 32);
2437 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TRUNC_MID_BITS");
2438}
2439
2440TEST_F(AluTruncateConstrainingTest, NegativeTruncateWrongLo128FromCanonDec)
2441{
2442 TestTraceContainer trace;
2443 process_truncate_with_tracegen({ MemoryValue::from_tag(MemoryTag::FF, (uint256_t(134534) << 198) - 986132ULL),
2444 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U64)),
2445 MemoryValue::from_tag(MemoryTag::U64, static_cast<uint64_t>(-986132ULL)) },
2446 trace);
2447 check_relation<alu>(trace);
2448 check_all_interactions<AluTraceBuilder>(trace);
2449 trace.set(Column::alu_a_lo, 0, 1234ULL);
2451 (check_interaction<AluTraceBuilder, lookup_alu_large_trunc_canonical_dec_settings>(trace)),
2452 "Failed.*LARGE_TRUNC_CANONICAL_DEC. Could not find tuple in destination.");
2453}
2454
2455TEST_F(AluTruncateConstrainingTest, NegativeTruncateWrongMidIntoRangeCheck)
2456{
2457 TestTraceContainer trace;
2458 process_truncate_with_tracegen({ MemoryValue::from_tag(MemoryTag::FF, (uint256_t(134534) << 198) - 986132ULL),
2459 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U64)),
2460 MemoryValue::from_tag(MemoryTag::U64, static_cast<uint64_t>(-986132ULL)) },
2461 trace);
2462 check_relation<alu>(trace);
2463 check_all_interactions<AluTraceBuilder>(trace);
2464 trace.set(Column::alu_mid, 0, 1234ULL);
2465 EXPECT_THROW_WITH_MESSAGE((check_interaction<AluTraceBuilder, lookup_alu_range_check_trunc_mid_settings>(trace)),
2466 "Failed.*RANGE_CHECK_TRUNC_MID. Could not find tuple in destination.");
2467}
2468
2469TEST_F(AluTruncateConstrainingTest, NegativeCastWrongDispatching)
2470{
2471 auto trace =
2472 process_cast_with_tracegen({ MemoryValue::from_tag(MemoryTag::FF, 4294967298ULL),
2473 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U32)),
2474 MemoryValue::from_tag(MemoryTag::U32, 2) });
2475 check_relation<alu>(trace);
2476 check_all_interactions<AluTraceBuilder>(trace);
2477 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_cast_settings>(trace);
2478 trace.set(Column::execution_register_0_, 0, trace.get(Column::execution_register_0_, 0) + 1);
2480 (check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_cast_settings>(trace)),
2481 "Failed.*EXECUTION_DISPATCH_TO_CAST. Could not find tuple in destination.");
2482}
2483
2484TEST_F(AluTruncateConstrainingTest, NegativeSetWrongDispatching)
2485{
2486 auto trace = process_set_with_tracegen({ MemoryValue::from_tag(MemoryTag::FF, 4294967298ULL),
2487 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U32)),
2488 MemoryValue::from_tag(MemoryTag::U32, 2) });
2489 check_relation<alu>(trace);
2490 check_all_interactions<AluTraceBuilder>(trace);
2491 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_set_settings>(trace);
2492 trace.set(Column::execution_rop_2_, 0, trace.get(Column::execution_rop_2_, 0) + 1);
2494 (check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_set_settings>(trace)),
2495 "Failed.*EXECUTION_DISPATCH_TO_SET. Could not find tuple in destination.");
2496}
2497
2498} // namespace
2499} // namespace bb::avm2::constraining
MemoryTag dst_tag
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:193
bb::field< bb::Bn254FrParams > FF
Definition field.cpp:24
#define AVM_EXEC_OP_ID_ALU_TRUNCATE
#define AVM_EXEC_OP_ID_ALU_LTE
#define AVM_EXEC_OP_ID_ALU_DIV
#define AVM_EXEC_OP_ID_ALU_ADD
#define AVM_EXEC_OP_ID_ALU_SHL
#define AVM_EXEC_OP_ID_ALU_SUB
#define AVM_EXEC_OP_ID_ALU_MUL
#define AVM_EXEC_OP_ID_ALU_FDIV
#define AVM_EXEC_OP_ID_ALU_SHR
#define AVM_EXEC_OP_ID_ALU_LT
static TaggedValue from_tag(ValueTag tag, FF value)
static constexpr size_t SR_DISPATCH_OPERATION
Definition alu.hpp:41
RangeCheckTraceBuilder range_check_builder
Definition alu.test.cpp:121
PrecomputedTraceBuilder precomputed_builder
Definition alu.test.cpp:120
FieldGreaterThanTraceBuilder field_gt_builder
Definition alu.test.cpp:122
AluTraceBuilder builder
Definition alu.test.cpp:124
GreaterThanTraceBuilder gt_builder
Definition alu.test.cpp:123
TestTraceContainer trace
FF a
FF b
TEST_F(BoomerangIPARecursiveTests, FullRecursiveVerifierMediumRandom)
TEST_P(AvmRecursiveTestsParameterized, TwoLayerAvmRecursion)
A test of the Two Layer AVM recursive verifier.
INSTANTIATE_TEST_SUITE_P(PaddingVariants, AvmRecursiveTestsParameterized, ::testing::Values(false, true), [](const auto &info) { return info.param ? "Padded" :"Unpadded";})
TEST_F(AvmRecursiveTests, TwoLayerAvmRecursionFailsWithWrongPIs)
TestTraceContainer empty_trace()
Definition fixtures.cpp:153
TaggedValue MemoryValue
AvmFlavorSettings::FF FF
Definition field.hpp:10
uint8_t get_tag_bits(ValueTag tag)
uint256_t get_tag_max_value(ValueTag tag)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
constexpr auto tuple_cat(T &&... ts)
Definition tuplet.hpp:1101
unsigned __int128 uint128_t
Definition serialize.hpp:45
static constexpr uint256_t modulus
constexpr field invert() const noexcept