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 range_check_builder.process({ { .value = c_decomp.lo, .num_bits = 64 },
842 { .value = c_decomp.hi, .num_bits = 64 },
843 { .value = b_decomp.lo, .num_bits = 64 },
844 { .value = b_decomp.hi, .num_bits = 64 } },
845 trace);
846 }
847
848 return trace;
849 }
850
851 TestTraceContainer process_div_with_tracegen(ThreeOperandTestParams params)
852 {
853 TestTraceContainer trace;
854 auto [a, b, c] = params;
855 bool div_0_error = b.as_ff() == FF(0);
856 auto mem_tag = a.get_tag();
857
858 MemoryValue remainder = MemoryValue::from_tag(MemoryTag::FF, 0);
859 if (!div_0_error && mem_tag == b.get_tag() && mem_tag != MemoryTag::FF) {
860 remainder = a - b * c;
861 }
862
863 builder.process(
864 {
865 { .operation = simulation::AluOperation::DIV, .a = a, .b = b, .c = c, .error = div_0_error },
866 },
867 trace);
868
869 if (mem_tag == MemoryTag::U128) {
870 auto c_decomp = simulation::decompose_128(static_cast<uint128_t>(c.as_ff()));
871 auto b_decomp = simulation::decompose_128(static_cast<uint128_t>(b.as_ff()));
872
873 range_check_builder.process({ { .value = c_decomp.lo, .num_bits = 64 },
874 { .value = c_decomp.hi, .num_bits = 64 },
875 { .value = b_decomp.lo, .num_bits = 64 },
876 { .value = b_decomp.hi, .num_bits = 64 } },
877 trace);
878 }
879 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
880 precomputed_builder.process_tag_parameters(trace);
881 gt_builder.process({ { .a = static_cast<uint128_t>(b.as_ff()),
882 .b = static_cast<uint128_t>(remainder.as_ff()),
883 .result = true } },
884 trace);
885 return trace;
886 }
887};
888
889INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluDivConstrainingTest, ::testing::ValuesIn(TEST_VALUES_DIV));
890
891TEST_P(AluDivConstrainingTest, AluDiv)
892{
893 auto trace = process_div_trace(GetParam());
894 check_all_interactions<AluTraceBuilder>(trace);
895 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
896 check_relation<alu>(trace);
897}
898
899TEST_P(AluDivConstrainingTest, AluDivTraceGen)
900{
901 auto trace = process_div_with_tracegen(GetParam());
902 check_all_interactions<AluTraceBuilder>(trace);
903 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
904 check_relation<alu>(trace);
905}
906
907TEST_F(AluDivConstrainingTest, AluDivByZeroMismatchTagsTraceGen)
908{
909 auto a = MemoryValue::from_tag(MemoryTag::U128, 2);
910 auto b = MemoryValue::from_tag(MemoryTag::U64, 0);
911 auto c = MemoryValue::from_tag(MemoryTag::FF, 0);
912
913 auto trace = process_div_with_tracegen({ a, b, c });
914 check_relation<alu>(trace);
915 check_all_interactions<AluTraceBuilder>(trace);
916}
917
918TEST_F(AluDivConstrainingTest, AluDivByZeroTagFFAndMismatchTagsTraceGen)
919{
920 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
921 auto b = MemoryValue::from_tag(MemoryTag::U32, 0);
922 auto c = MemoryValue::from_tag(MemoryTag::FF, 0);
923
924 auto trace = process_div_with_tracegen({ a, b, c });
925 check_relation<alu>(trace);
926 check_all_interactions<AluTraceBuilder>(trace);
927}
928
929TEST_F(AluDivConstrainingTest, NegativeAluDivUnderflow)
930{
931 // Test that for a < b, the circuit does not accept c != 0
932 auto a = MemoryValue::from_tag(MemoryTag::U32, 2);
933 auto b = MemoryValue::from_tag(MemoryTag::U32, 5);
934 auto c = a / b;
935 auto trace = process_div_trace({ a, b, c });
936 check_all_interactions<AluTraceBuilder>(trace);
937 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
938 check_relation<alu>(trace);
939
940 // Good path: 2/5 gives 0 with remainder = 2
941 // Bad path: 5 * c = 2 - r => set c = 2, so r = p - 8:
942
943 c = MemoryValue::from_tag(MemoryTag::U32, 2);
944 auto wrong_remainder = a.as_ff() - b.as_ff() * c.as_ff();
945
946 trace.set(Column::alu_ic, 0, c);
947 trace.set(Column::alu_helper1, 0, wrong_remainder);
948 trace.set(Column::alu_gt_input_b, 0, wrong_remainder);
949
950 // All relations will pass...
951 check_relation<alu>(trace);
952 // ... but now r > b, so the gt lookup will fail:
953 EXPECT_THROW_WITH_MESSAGE(check_all_interactions<AluTraceBuilder>(trace), "LOOKUP_ALU_INT_GT");
954}
955
956TEST_F(AluDivConstrainingTest, NegativeAluDivU128Carry)
957{
958 // Test that for a < b, the circuit does not accept c != 0
959 auto a = MemoryValue::from_tag(MemoryTag::U128, 2);
960 auto b = MemoryValue::from_tag(MemoryTag::U128, (uint256_t(1) << 64) + 2);
961 auto c = a / b;
962
963 auto trace = process_div_trace({ a, b, c });
964
965 check_all_interactions<AluTraceBuilder>(trace);
966 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
967 check_relation<alu>(trace);
968
969 // Check we cannot provide a c s.t. a - r = b * c over/underflows
970
971 c = MemoryValue::from_tag(MemoryTag::U128, (uint256_t(1) << 64) + 3);
972 auto wrong_remainder = a.as_ff() - FF(static_cast<uint256_t>(b.as_ff()) * static_cast<uint256_t>(c.as_ff()));
973
974 // We now have c and wrong_remainder s.t. a - wrong_remainder == b * c in the field...
975
976 trace.set(Column::alu_ic, 0, c);
977 trace.set(Column::alu_helper1, 0, wrong_remainder);
978
979 // ...but we haven't provided a correct decomposition of the new bad c:
980 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DECOMPOSITION");
981
982 auto c_decomp = simulation::decompose_128(c.as<uint128_t>());
983 trace.set(Column::alu_a_lo, 0, c_decomp.lo);
984 trace.set(Column::alu_a_hi, 0, c_decomp.hi);
985
986 // Setting the decomposed values still (correctly) fails:
987 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ALU_DIV_U128");
988}
989
990TEST_F(AluDivConstrainingTest, NegativeAluDivByZero)
991{
992 auto a = MemoryValue::from_tag(MemoryTag::U32, 2);
993 auto b = MemoryValue::from_tag(MemoryTag::U32, 5);
994 auto c = a / b;
995
996 for (const bool with_tracegen : { false, true }) {
997 auto trace = with_tracegen ? process_div_with_tracegen({ a, b, c }) : process_div_trace({ a, b, c });
998 check_all_interactions<AluTraceBuilder>(trace);
999 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1000 check_relation<alu>(trace);
1001
1002 // Set b, b_inv to 0...
1003 trace.set(Column::alu_ib, 0, 0);
1004 trace.set(Column::alu_b_inv, 0, 0);
1005 // ...and since we haven't set the error correctly, we expect the below to fail:
1006 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1007 // We need to set the div_0_err and...
1008 trace.set(Column::alu_sel_div_0_err, 0, 1);
1009 trace.set(Column::alu_sel_div_no_err, 0, 0);
1010 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ERR_CHECK");
1011 // ...the overall sel_err:
1012 trace.set(Column::alu_sel_err, 0, 1);
1013 trace.set(Column::alu_sel_int_gt, 0, 0);
1014 trace.set(Column::alu_gt_input_a, 0, 0);
1015 trace.set(Column::alu_gt_input_b, 0, 0);
1016 trace.set(Column::alu_gt_result_c, 0, 0);
1017 check_relation<alu>(trace);
1018
1019 // If we try and have div_0_err on without doing a div, the below should fail:
1020 trace.set(Column::alu_sel_op_div, 0, 0);
1021 trace.set(Column::alu_sel_op_mul, 0, 1);
1022 trace.set(Column::alu_op_id, 0, AVM_EXEC_OP_ID_ALU_MUL);
1023 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1024
1025 trace.set(Column::alu_sel_op_div, 0, 1);
1026 trace.set(Column::alu_sel_op_mul, 0, 0);
1027 trace.set(Column::alu_op_id, 0, AVM_EXEC_OP_ID_ALU_DIV);
1028 check_relation<alu>(trace);
1029
1030 // If we try and set b != 0 with div_0_err on, the below should fail:
1031 trace.set(Column::alu_ib, 0, b);
1032 trace.set(Column::alu_b_inv, 0, b.as_ff().invert());
1033 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1034 }
1035}
1036
1037TEST_F(AluDivConstrainingTest, NegativeAluDivFF)
1038{
1039 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
1040 auto b = MemoryValue::from_tag(MemoryTag::FF, 5);
1041 auto c = a / b;
1042 auto trace = process_div_with_tracegen({ a, b, c });
1043 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TAG_ERR_CHECK");
1044 // This case should be recoverable, so we set the tag err selectors:
1045 trace.set(Column::alu_sel_tag_err, 0, 1);
1046 trace.set(Column::alu_sel_err, 0, 1);
1047 trace.set(Column::alu_sel_div_no_err, 0, 0);
1048 trace.set(Column::alu_sel_int_gt, 0, 0);
1049 trace.set(Column::alu_gt_input_a, 0, 0);
1050 trace.set(Column::alu_gt_input_b, 0, 0);
1051 trace.set(Column::alu_gt_result_c, 0, 0);
1052 check_relation<alu>(trace);
1053 check_all_interactions<AluTraceBuilder>(trace);
1054 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1055}
1056
1057TEST_F(AluDivConstrainingTest, NegativeAluDivByZeroFF)
1058{
1059 // For DIV, we can have both FF and dividing by zero errors:
1060 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
1061 auto b = MemoryValue::from_tag(MemoryTag::FF, 5);
1062 auto c = a / b;
1063 auto trace = process_div_with_tracegen({ a, b, c });
1064 trace.set(Column::alu_sel_tag_err, 0, 1);
1065 trace.set(Column::alu_sel_err, 0, 1);
1066 trace.set(Column::alu_sel_div_no_err, 0, 0);
1067 trace.set(Column::alu_sel_int_gt, 0, 0);
1068 trace.set(Column::alu_gt_input_a, 0, 0);
1069 trace.set(Column::alu_gt_input_b, 0, 0);
1070 trace.set(Column::alu_gt_result_c, 0, 0);
1071 check_relation<alu>(trace);
1072 // Set b, b_inv to 0 with dividing by 0 errors:
1073 trace.set(Column::alu_ib, 0, 0);
1074 trace.set(Column::alu_b_inv, 0, 0);
1075 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1076 trace.set(Column::alu_sel_div_0_err, 0, 1);
1077 check_relation<alu>(trace);
1078 check_all_interactions<AluTraceBuilder>(trace);
1079 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1080}
1081
1082TEST_F(AluDivConstrainingTest, NegativeAluDivByZeroFFTagMismatch)
1083{
1084 // For DIV, we can have FF, tag mismatch, and dividing by zero errors:
1085 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
1086 auto b = MemoryValue::from_tag(MemoryTag::FF, 5);
1087 auto c = a / b;
1088 auto trace = process_div_with_tracegen({ a, b, c });
1089 trace.set(Column::alu_sel_tag_err, 0, 1);
1090 trace.set(Column::alu_sel_err, 0, 1);
1091 trace.set(Column::alu_sel_div_no_err, 0, 0);
1092 trace.set(Column::alu_sel_int_gt, 0, 0);
1093 trace.set(Column::alu_gt_input_a, 0, 0);
1094 trace.set(Column::alu_gt_input_b, 0, 0);
1095 trace.set(Column::alu_gt_result_c, 0, 0);
1096 check_relation<alu>(trace);
1097 // Setting b to u8 also creates a tag mismatch:
1098 trace.set(Column::alu_ib_tag, 0, static_cast<uint8_t>(MemoryTag::U8));
1099 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "AB_TAGS_CHECK");
1100 trace.set(Column::alu_sel_ab_tag_mismatch, 0, 1);
1101 trace.set(Column::alu_ab_tags_diff_inv,
1102 0,
1103 (FF(static_cast<uint8_t>(MemoryTag::FF)) - FF(static_cast<uint8_t>(MemoryTag::U8))).invert());
1104 check_relation<alu>(trace);
1105 // We can also handle dividing by 0:
1106 trace.set(Column::alu_ib, 0, 0);
1107 trace.set(Column::alu_b_inv, 0, 0);
1108 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1109 trace.set(Column::alu_sel_div_0_err, 0, 1);
1110 trace.set(Column::alu_sel_div_no_err, 0, 0);
1111 check_relation<alu>(trace);
1112 check_all_interactions<AluTraceBuilder>(trace);
1113 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1114}
1115
1116// FDIV TESTS
1117
1118// Note: The test framework below converts all inputs to FF values to allow for many happy path tests without adding
1119// new vectors. Non-FF values are tested separately.
1120const std::vector<MemoryValue> TEST_VALUES_FDIV_OUT = {
1121 MemoryValue::from_tag(MemoryTag::FF, 0), // Dividing by zero, so expecting an error
1122 MemoryValue::from_tag(MemoryTag::FF, 4),
1123 MemoryValue::from_tag(MemoryTag::FF, FF("0x1e980ebbc51694827ee20074ac28b250a037a43eb44b38e6aa367c57a05e6d48")),
1124 MemoryValue::from_tag(MemoryTag::FF, FF("0x135b52945a13d9aa49b9b57c33cd568ba9ae5ce9ca4a2d06e7f3fbd4f9999998")),
1125 MemoryValue::from_tag(MemoryTag::FF, FF("0x135b52945a13d9aa49b9b57c33cd568ba9ae5ce9ca4a2d071b272f07f9999998")),
1126 MemoryValue::from_tag(MemoryTag::FF, FF("0x135b52945a13d9aa49b9b57c33cd568bdce1901cfd7d603a1b272f07f9999998")),
1127 MemoryValue::from_tag(MemoryTag::FF, FF::modulus - 2),
1128};
1129
1130const std::vector<ThreeOperandTestParams> TEST_VALUES_FDIV = zip_helper(TEST_VALUES_FDIV_OUT);
1131
1132class AluFDivConstrainingTest : public AluConstrainingTest,
1133 public ::testing::WithParamInterface<ThreeOperandTestParams> {
1134 public:
1135 TestTraceContainer process_fdiv_trace(ThreeOperandTestParams params)
1136 {
1137 auto [a, b, c] = params;
1138 a = MemoryValue::from_tag(MemoryTag::FF, a);
1139 b = MemoryValue::from_tag(MemoryTag::FF, b);
1140 c = MemoryValue::from_tag(MemoryTag::FF, c);
1141 auto div_0_error = b.as_ff() == FF(0);
1142
1143 auto mem_tag = a.get_tag();
1144 auto tag = static_cast<uint8_t>(mem_tag);
1145
1146 auto trace = TestTraceContainer({
1147 {
1148 { C::alu_b_inv, div_0_error ? 0 : b.as_ff().invert() },
1149 { C::alu_ia, a },
1150 { C::alu_ia_tag, tag },
1151 { C::alu_ib, b },
1152 { C::alu_ib_tag, tag },
1153 { C::alu_ic, c },
1154 { C::alu_ic_tag, tag },
1155 { C::alu_max_bits, get_tag_bits(mem_tag) },
1156 { C::alu_max_value, get_tag_max_value(mem_tag) },
1157 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_FDIV },
1158 { C::alu_sel, 1 },
1159 { C::alu_sel_div_0_err, div_0_error ? 1 : 0 },
1160 { C::alu_sel_err, div_0_error ? 1 : 0 },
1161 { C::alu_sel_is_ff, 1 },
1162 { C::alu_sel_op_fdiv, 1 },
1163 { C::alu_tag_u128_diff_inv, FF(-static_cast<uint8_t>(MemoryTag::U128)).invert() },
1164 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
1165 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
1166 { C::execution_mem_tag_reg_2_, tag }, // = ic_tag
1167 { C::execution_register_0_, a }, // = ia
1168 { C::execution_register_1_, b }, // = ib
1169 { C::execution_register_2_, c }, // = ic
1170 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
1171 { C::execution_sel_opcode_error, div_0_error ? 1 : 0 }, // = sel_err
1172 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_FDIV }, // = alu_op_id
1173 },
1174 });
1175
1176 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1177 precomputed_builder.process_tag_parameters(trace);
1178
1179 return trace;
1180 }
1181
1182 TestTraceContainer process_fdiv_with_tracegen(ThreeOperandTestParams params, bool upcast_to_ff = false)
1183 {
1184 TestTraceContainer trace;
1185 auto [a, b, c] = params;
1186
1187 if (upcast_to_ff) {
1188 a = MemoryValue::from_tag(MemoryTag::FF, a);
1189 b = MemoryValue::from_tag(MemoryTag::FF, b);
1190 c = MemoryValue::from_tag(MemoryTag::FF, c);
1191 }
1192
1193 bool div_0_error = b.as_ff() == FF(0);
1194
1195 builder.process(
1196 {
1197 { .operation = simulation::AluOperation::FDIV, .a = a, .b = b, .c = c, .error = div_0_error },
1198 },
1199 trace);
1200
1201 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1202 precomputed_builder.process_tag_parameters(trace);
1203
1204 return trace;
1205 }
1206};
1207
1208INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluFDivConstrainingTest, ::testing::ValuesIn(TEST_VALUES_FDIV));
1209
1210TEST_P(AluFDivConstrainingTest, AluFDiv)
1211{
1212 auto trace = process_fdiv_trace(GetParam());
1213 check_all_interactions<AluTraceBuilder>(trace);
1214 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1215 check_relation<alu>(trace);
1216}
1217
1218TEST_P(AluFDivConstrainingTest, AluFDivTraceGen)
1219{
1220 auto trace = process_fdiv_with_tracegen(GetParam(), true);
1221 // InteractiveDebugger debugger(trace);
1222 // debugger.run();
1223 check_all_interactions<AluTraceBuilder>(trace);
1224 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1225 check_relation<alu>(trace);
1226}
1227
1228TEST_F(AluFDivConstrainingTest, AluFDivByZeroNonFFTagTraceGen)
1229{
1230 auto a = MemoryValue::from_tag(MemoryTag::U8, 2);
1231 auto b = MemoryValue::from_tag(MemoryTag::U8, 0);
1232 auto c = MemoryValue::from_tag(MemoryTag::FF, 0);
1233
1234 auto trace = process_fdiv_with_tracegen({ a, b, c });
1235 check_relation<alu>(trace);
1236 check_all_interactions<AluTraceBuilder>(trace);
1237}
1238
1239TEST_F(AluFDivConstrainingTest, AluFDivByZeroNonFFTagMismatchTraceGen)
1240{
1241 auto a = MemoryValue::from_tag(MemoryTag::U8, 2);
1242 auto b = MemoryValue::from_tag(MemoryTag::U16, 0);
1243 auto c = MemoryValue::from_tag(MemoryTag::FF, 0);
1244 auto trace = process_fdiv_with_tracegen({ a, b, c });
1245 check_relation<alu>(trace);
1246 check_all_interactions<AluTraceBuilder>(trace);
1247}
1248
1249TEST_F(AluFDivConstrainingTest, NegativeAluFDivByZero)
1250{
1251 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
1252 auto b = MemoryValue::from_tag(MemoryTag::FF, 5);
1253 auto c = a / b;
1254 auto trace = process_fdiv_trace({ a, b, c });
1255 check_all_interactions<AluTraceBuilder>(trace);
1256 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1257 check_relation<alu>(trace);
1258
1259 // Set b, b_inv to 0...
1260 trace.set(Column::alu_ib, 0, 0);
1261 trace.set(Column::alu_b_inv, 0, 0);
1262 // ...and since we haven't set the error correctly, we expect the below to fail:
1263 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1264 // We need to set the div_0_err and...
1265 trace.set(Column::alu_sel_div_0_err, 0, 1);
1266 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ERR_CHECK");
1267 // ...the overall sel_err:
1268 trace.set(Column::alu_sel_err, 0, 1);
1269 check_relation<alu>(trace);
1270
1271 // If we try and set b != 0 with div_0_err on, the below should fail:
1272 trace.set(Column::alu_ib, 0, b);
1273 trace.set(Column::alu_b_inv, 0, b.as_ff().invert());
1274 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1275}
1276
1277TEST_F(AluFDivConstrainingTest, NegativeAluFDivByZeroNonFFTagMismatch)
1278{
1279 auto a = MemoryValue::from_tag(MemoryTag::U8, 4);
1280 auto b = MemoryValue::from_tag(MemoryTag::U8, 2);
1281 // An incorrect c_tag fails the relation rather than throwing a tag error - we want to test the throw here, so
1282 // setting c to be the correct tag:
1283 auto c = MemoryValue::from_tag(MemoryTag::FF, 2);
1284 auto tag = static_cast<uint8_t>(MemoryTag::U8);
1285
1286 auto trace = TestTraceContainer({
1287 {
1288 { C::alu_b_inv, b.as_ff().invert() },
1289 { C::alu_ia, a },
1290 { C::alu_ia_tag, tag },
1291 { C::alu_ib, b },
1292 { C::alu_ib_tag, tag },
1293 { C::alu_ic, c },
1294 { C::alu_ic_tag, static_cast<uint8_t>(MemoryTag::FF) },
1295 { C::alu_max_bits, get_tag_bits(MemoryTag::U8) },
1296 { C::alu_max_value, get_tag_max_value(MemoryTag::U8) },
1297 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_FDIV },
1298 { C::alu_sel, 1 },
1299 { C::alu_sel_op_fdiv, 1 },
1300 { C::alu_sel_tag_err, 1 },
1301 { C::alu_sel_err, 1 },
1302 { C::alu_tag_ff_diff_inv, (FF(tag) - FF(static_cast<uint8_t>(MemoryTag::FF))).invert() },
1303 { C::alu_tag_u128_diff_inv, (FF(tag) - FF(static_cast<uint8_t>(MemoryTag::U128))).invert() },
1304 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
1305 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
1306 { C::execution_mem_tag_reg_2_, static_cast<uint8_t>(MemoryTag::FF) }, // = ic_tag
1307 { C::execution_register_0_, a }, // = ia
1308 { C::execution_register_1_, b }, // = ib
1309 { C::execution_register_2_, c }, // = ic
1310 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
1311 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_FDIV }, // = alu_op_id
1312 { C::execution_sel_opcode_error, 1 }, // = sel_err
1313 },
1314 });
1315
1316 // Every column is set up correctly. All checks should pass:
1317 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1318 precomputed_builder.process_tag_parameters(trace);
1319 check_relation<alu>(trace);
1320 check_all_interactions<AluTraceBuilder>(trace);
1321 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1322
1323 // We un-toggle sel_tag_err and sel_err and expect the following failure:
1324 trace.set(Column::alu_sel_tag_err, 0, 0);
1325 trace.set(Column::alu_sel_err, 0, 0);
1326 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TAG_ERR_CHECK");
1327
1328 // We try to cheat by setting the tag diff inverse to 0 and claiming a is FF, but expect the following failure:
1329 trace.set(Column::alu_tag_ff_diff_inv, 0, 0);
1330 trace.set(Column::alu_sel_is_ff, 0, 1);
1331 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TAG_IS_FF");
1332
1333 // Reset to the correct values:
1334 trace.set(Column::alu_tag_ff_diff_inv, 0, (FF(tag) - FF(static_cast<uint8_t>(MemoryTag::FF))).invert());
1335 trace.set(Column::alu_sel_is_ff, 0, 0);
1336 trace.set(Column::alu_sel_tag_err, 0, 1);
1337 trace.set(Column::alu_sel_err, 0, 1);
1338 check_relation<alu>(trace);
1339
1340 // For FDIV, we can have both FF and dividing by zero errors:
1341 trace.set(Column::alu_ib, 0, 0);
1342 trace.set(Column::alu_b_inv, 0, 0);
1343 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "DIV_0_ERR");
1344 trace.set(Column::alu_sel_div_0_err, 0, 1);
1345 check_relation<alu>(trace);
1346 check_all_interactions<AluTraceBuilder>(trace);
1347 trace.set(Column::execution_register_1_, 0, 0);
1348 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1349
1350 // Setting b to u16 also creates a tag mismatch we can handle with the same selectors:
1351 trace.set(Column::alu_ib_tag, 0, static_cast<uint8_t>(MemoryTag::U16));
1352 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "AB_TAGS_CHECK");
1353 trace.set(Column::alu_sel_ab_tag_mismatch, 0, 1);
1354 trace.set(Column::alu_ab_tags_diff_inv, 0, (FF(tag) - FF(static_cast<uint8_t>(MemoryTag::U16))).invert());
1355 check_relation<alu>(trace);
1356}
1357
1358// EQ TESTS
1359
1360const std::vector<MemoryValue> TEST_VALUES_EQ_OUT(NUM_OF_TAGS, MemoryValue::from_tag(MemoryTag::U1, 0));
1361
1362const std::vector<ThreeOperandTestParams> TEST_VALUES_EQ = zip_helper(TEST_VALUES_EQ_OUT);
1363
1364class AluEQConstrainingTest : public AluConstrainingTest, public ::testing::WithParamInterface<ThreeOperandTestParams> {
1365 public:
1366 TestTraceContainer process_eq_with_tracegen(const ThreeOperandTestParams& params)
1367 {
1368 TestTraceContainer trace;
1369 auto [a, b, c] = params;
1370
1371 builder.process(
1372 {
1373 { .operation = simulation::AluOperation::EQ, .a = a, .b = b, .c = c },
1374 },
1375 trace);
1376
1377 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1378 precomputed_builder.process_tag_parameters(trace);
1379 return trace;
1380 }
1381};
1382
1383INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluEQConstrainingTest, ::testing::ValuesIn(TEST_VALUES_EQ));
1384
1385TEST_P(AluEQConstrainingTest, AluEQTraceGen)
1386{
1387 const MemoryValue& param = std::get<0>(GetParam());
1388 auto trace =
1389 process_eq_with_tracegen(ThreeOperandTestParams{ param, param, MemoryValue::from_tag(MemoryTag::U1, 1) });
1390 check_all_interactions<AluTraceBuilder>(trace);
1391 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1392 check_relation<alu>(trace);
1393}
1394
1395TEST_P(AluEQConstrainingTest, AluInEQTraceGen)
1396{
1397 auto trace = process_eq_with_tracegen(GetParam());
1398 check_all_interactions<AluTraceBuilder>(trace);
1399 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1400 check_relation<alu>(trace);
1401}
1402
1403TEST_P(AluEQConstrainingTest, NegativeAluEqResult)
1404{
1405 auto params = GetParam();
1406 for (const bool is_eq : { false, true }) {
1407 auto trace = process_eq_with_tracegen(is_eq ? ThreeOperandTestParams{ std::get<0>(params),
1408 std::get<0>(params),
1409 MemoryValue::from_tag(MemoryTag::U1, 1) }
1410 : params);
1411 check_relation<alu>(trace);
1412 check_all_interactions<AluTraceBuilder>(trace);
1413 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1414 bool c = trace.get(Column::alu_ic, 0) == 1;
1415 // Swap the result bool:
1416 trace.set(Column::alu_ic, 0, static_cast<uint8_t>(!c));
1417 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "EQ_OP_MAIN");
1418 }
1419}
1420
1421TEST_P(AluEQConstrainingTest, NegativeAluEqHelper)
1422{
1423 auto trace = process_eq_with_tracegen(GetParam());
1424 check_relation<alu>(trace);
1425 check_all_interactions<AluTraceBuilder>(trace);
1426 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1427 auto ab_diff_inv = trace.get(Column::alu_ab_diff_inv, 0);
1428 trace.set(Column::alu_ab_diff_inv, 0, ab_diff_inv + 1);
1429 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "EQ_OP_MAIN");
1430}
1431
1432// LT TESTS
1433
1434const std::vector<MemoryValue> TEST_VALUES_LT_OUT = {
1435 MemoryValue::from_tag(MemoryTag::U1, 0), MemoryValue::from_tag(MemoryTag::U1, 0),
1436 MemoryValue::from_tag(MemoryTag::U1, 0), MemoryValue::from_tag(MemoryTag::U1, 1),
1437 MemoryValue::from_tag(MemoryTag::U1, 0), MemoryValue::from_tag(MemoryTag::U1, 0),
1438 MemoryValue::from_tag(MemoryTag::U1, 0),
1439};
1440
1441const std::vector<ThreeOperandTestParams> TEST_VALUES_LT = zip_helper(TEST_VALUES_LT_OUT);
1442
1443class AluLTConstrainingTest : public AluConstrainingTest, public ::testing::WithParamInterface<ThreeOperandTestParams> {
1444 public:
1445 TestTraceContainer process_lt_trace(ThreeOperandTestParams params)
1446 {
1447 auto [a, b, c] = params;
1448 auto mem_tag = a.get_tag();
1449 auto tag = static_cast<uint8_t>(mem_tag);
1450 auto is_ff = mem_tag == MemoryTag::FF;
1451
1452 auto trace = TestTraceContainer({
1453 {
1454 { C::alu_ia, a },
1455 { C::alu_ia_tag, tag },
1456 { C::alu_ib, b },
1457 { C::alu_ib_tag, tag },
1458 { C::alu_ic, c },
1459 { C::alu_ic_tag, static_cast<uint8_t>(MemoryTag::U1) },
1460 { C::alu_gt_input_a, b },
1461 { C::alu_gt_input_b, a },
1462 { C::alu_gt_result_c, c },
1463 { C::alu_max_bits, get_tag_bits(mem_tag) },
1464 { C::alu_max_value, get_tag_max_value(mem_tag) },
1465 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_LT },
1466 { C::alu_sel, 1 },
1467 { C::alu_sel_ff_gt, static_cast<uint8_t>(is_ff) },
1468 { C::alu_sel_int_gt, static_cast<uint8_t>(!is_ff) },
1469 { C::alu_sel_is_ff, static_cast<uint8_t>(is_ff) },
1470 { C::alu_sel_op_lt, 1 },
1471 { C::alu_tag_ff_diff_inv, is_ff ? 0 : FF(tag - static_cast<uint8_t>(MemoryTag::FF)).invert() },
1472 { C::alu_sel_is_u128, tag == static_cast<uint8_t>(MemoryTag::U128) ? 1 : 0 },
1473 { C::alu_tag_u128_diff_inv,
1474 tag == static_cast<uint8_t>(MemoryTag::U128)
1475 ? 0
1476 : FF(tag - static_cast<uint8_t>(MemoryTag::U128)).invert() },
1477 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
1478 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
1479 { C::execution_mem_tag_reg_2_, static_cast<uint8_t>(MemoryTag::U1) }, // = ic_tag
1480 { C::execution_register_0_, a }, // = ia
1481 { C::execution_register_1_, b }, // = ib
1482 { C::execution_register_2_, c }, // = ic
1483 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
1484 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_LT }, // = alu_op_id
1485 },
1486 });
1487
1488 if (is_ff) {
1489 field_gt_builder.process({ { .a = b, .b = a, .gt_result = c.as_ff() == 1 } }, trace);
1490 } else {
1491 gt_builder.process({ { .a = static_cast<uint128_t>(b.as_ff()),
1492 .b = static_cast<uint128_t>(a.as_ff()),
1493 .result = c.as_ff() == 1 } },
1494 trace);
1495 }
1496
1497 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1498 precomputed_builder.process_tag_parameters(trace);
1499 return trace;
1500 }
1501
1502 TestTraceContainer process_lt_with_tracegen(ThreeOperandTestParams params)
1503 {
1504 TestTraceContainer trace;
1505 auto [a, b, c] = params;
1506 auto is_ff = a.get_tag() == MemoryTag::FF;
1507
1508 builder.process(
1509 {
1510 { .operation = simulation::AluOperation::LT, .a = a, .b = b, .c = c },
1511 },
1512 trace);
1513
1514 if (is_ff) {
1515 field_gt_builder.process({ { .a = b, .b = a, .gt_result = c.as_ff() == 1 } }, trace);
1516 } else {
1517 gt_builder.process({ { .a = static_cast<uint128_t>(b.as_ff()),
1518 .b = static_cast<uint128_t>(a.as_ff()),
1519 .result = c.as_ff() == 1 } },
1520 trace);
1521 }
1522 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1523 precomputed_builder.process_tag_parameters(trace);
1524 return trace;
1525 }
1526};
1527
1528INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluLTConstrainingTest, ::testing::ValuesIn(TEST_VALUES_LT));
1529
1530TEST_P(AluLTConstrainingTest, AluLT)
1531{
1532 auto trace = process_lt_trace(GetParam());
1533 check_all_interactions<AluTraceBuilder>(trace);
1534 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1535 check_relation<alu>(trace);
1536}
1537
1538TEST_P(AluLTConstrainingTest, AluLTTraceGen)
1539{
1540 auto trace = process_lt_with_tracegen(GetParam());
1541 check_all_interactions<AluTraceBuilder>(trace);
1542 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1543 check_relation<alu>(trace);
1544}
1545
1546TEST_P(AluLTConstrainingTest, NegativeAluLT)
1547{
1548 auto params = GetParam();
1549 auto trace = process_lt_trace(params);
1550 auto is_ff = std::get<0>(params).get_tag() == MemoryTag::FF;
1551 check_relation<alu>(trace);
1552 check_all_interactions<AluTraceBuilder>(trace);
1553 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1554 bool c = trace.get(Column::alu_ic, 0) == 1;
1555 // Swap the result bool:
1556 trace.set(Column::alu_ic, 0, static_cast<uint8_t>(!c));
1557 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "GT_ASSIGN_RESULT_C");
1558 trace.set(Column::alu_gt_result_c, 0, static_cast<uint8_t>(!c));
1559
1560 if (is_ff) {
1561 EXPECT_THROW_WITH_MESSAGE((check_interaction<AluTraceBuilder, lookup_alu_ff_gt_settings>(trace)),
1562 "LOOKUP_ALU_FF_GT");
1563 } else {
1564 EXPECT_THROW_WITH_MESSAGE((check_interaction<AluTraceBuilder, lookup_alu_int_gt_settings>(trace)),
1565 "LOOKUP_ALU_INT_GT");
1566 }
1567}
1568
1569// LTE TESTS
1570
1571const std::vector<ThreeOperandTestParams> TEST_VALUES_LTE = zip_helper(TEST_VALUES_LT_OUT);
1572
1573class AluLTEConstrainingTest : public AluConstrainingTest,
1574 public ::testing::WithParamInterface<ThreeOperandTestParams> {
1575 public:
1576 TestTraceContainer process_lte_trace(ThreeOperandTestParams params, bool eq = false)
1577 {
1578 auto [a, b, c] = params;
1579 auto mem_tag = a.get_tag();
1580 auto tag = static_cast<uint8_t>(mem_tag);
1581 auto is_ff = mem_tag == MemoryTag::FF;
1582 b = eq ? a : b;
1583 c = eq ? MemoryValue::from_tag(MemoryTag::U1, 1) : c;
1584
1585 auto trace = TestTraceContainer({
1586 {
1587 { C::alu_ia, a },
1588 { C::alu_ia_tag, tag },
1589 { C::alu_ib, b },
1590 { C::alu_ib_tag, tag },
1591 { C::alu_ic, c },
1592 { C::alu_ic_tag, static_cast<uint8_t>(MemoryTag::U1) },
1593 { C::alu_gt_input_a, a },
1594 { C::alu_gt_input_b, b },
1595 { C::alu_gt_result_c, c.as_ff() == 0 ? 1 : 0 },
1596 { C::alu_max_bits, get_tag_bits(mem_tag) },
1597 { C::alu_max_value, get_tag_max_value(mem_tag) },
1598 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_LTE },
1599 { C::alu_sel, 1 },
1600 { C::alu_sel_ff_gt, static_cast<uint8_t>(is_ff) },
1601 { C::alu_sel_int_gt, static_cast<uint8_t>(!is_ff) },
1602 { C::alu_sel_is_ff, static_cast<uint8_t>(is_ff) },
1603 { C::alu_sel_op_lte, 1 },
1604 { C::alu_tag_ff_diff_inv, is_ff ? 0 : FF(tag - static_cast<uint8_t>(MemoryTag::FF)).invert() },
1605 { C::alu_sel_is_u128, tag == static_cast<uint8_t>(MemoryTag::U128) ? 1 : 0 },
1606 { C::alu_tag_u128_diff_inv,
1607 tag == static_cast<uint8_t>(MemoryTag::U128)
1608 ? 0
1609 : FF(tag - static_cast<uint8_t>(MemoryTag::U128)).invert() },
1610 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
1611 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
1612 { C::execution_mem_tag_reg_2_, static_cast<uint8_t>(MemoryTag::U1) }, // = ic_tag
1613 { C::execution_register_0_, a }, // = ia
1614 { C::execution_register_1_, b }, // = ib
1615 { C::execution_register_2_, c }, // = ic
1616 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
1617 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_LTE }, // = alu_op_id
1618 },
1619 });
1620
1621 if (is_ff) {
1622 field_gt_builder.process({ { .a = a, .b = b, .gt_result = c.as_ff() == 0 } }, trace);
1623 } else {
1624 gt_builder.process({ { .a = static_cast<uint128_t>(a.as_ff()),
1625 .b = static_cast<uint128_t>(b.as_ff()),
1626 .result = c.as_ff() == 0 } },
1627 trace);
1628 }
1629 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1630 precomputed_builder.process_tag_parameters(trace);
1631 return trace;
1632 }
1633
1634 TestTraceContainer process_lte_with_tracegen(ThreeOperandTestParams params, bool eq = false)
1635 {
1636 TestTraceContainer trace;
1637 auto [a, b, c] = params;
1638 auto is_ff = a.get_tag() == MemoryTag::FF;
1639 b = eq ? a : b;
1640 c = eq ? MemoryValue::from_tag(MemoryTag::U1, 1) : c;
1641
1642 builder.process(
1643 {
1644 { .operation = simulation::AluOperation::LTE, .a = a, .b = b, .c = c },
1645 },
1646 trace);
1647
1648 if (is_ff) {
1649 field_gt_builder.process({ { .a = a, .b = b, .gt_result = c.as_ff() == 0 } }, trace);
1650 } else {
1651 gt_builder.process({ { .a = static_cast<uint128_t>(a.as_ff()),
1652 .b = static_cast<uint128_t>(b.as_ff()),
1653 .result = c.as_ff() == 0 } },
1654 trace);
1655 }
1656 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1657 precomputed_builder.process_tag_parameters(trace);
1658 return trace;
1659 }
1660};
1661
1662INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluLTEConstrainingTest, ::testing::ValuesIn(TEST_VALUES_LTE));
1663
1664TEST_P(AluLTEConstrainingTest, AluLTE)
1665{
1666 auto trace = process_lte_trace(GetParam());
1667 check_all_interactions<AluTraceBuilder>(trace);
1668 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1669 check_relation<alu>(trace);
1670}
1671
1672TEST_P(AluLTEConstrainingTest, AluLTEEq)
1673{
1674 auto trace = process_lte_trace(GetParam(), true);
1675 check_all_interactions<AluTraceBuilder>(trace);
1676 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1677 check_relation<alu>(trace);
1678}
1679
1680TEST_P(AluLTEConstrainingTest, AluLTETraceGen)
1681{
1682 auto trace = process_lte_with_tracegen(GetParam());
1683 check_all_interactions<AluTraceBuilder>(trace);
1684 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1685 check_relation<alu>(trace);
1686}
1687
1688TEST_P(AluLTEConstrainingTest, AluLTEEqTraceGen)
1689{
1690 auto trace = process_lte_with_tracegen(GetParam(), true);
1691 check_all_interactions<AluTraceBuilder>(trace);
1692 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1693 check_relation<alu>(trace);
1694}
1695
1696TEST_P(AluLTEConstrainingTest, NegativeAluLTEResult)
1697{
1698 auto params = GetParam();
1699
1700 for (const bool is_eq : { false, true }) {
1701 auto trace = process_lte_trace(params, is_eq);
1702 auto is_ff = std::get<0>(params).get_tag() == MemoryTag::FF;
1703 check_relation<alu>(trace);
1704 check_all_interactions<AluTraceBuilder>(trace);
1705 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1706 bool c = trace.get(Column::alu_ic, 0) == 1;
1707 // Swap the result bool:
1708 trace.set(Column::alu_ic, 0, static_cast<uint8_t>(!c));
1709 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "GT_ASSIGN_RESULT_C");
1710 trace.set(Column::alu_gt_result_c, 0, static_cast<uint8_t>(c));
1711
1712 if (is_ff) {
1713 EXPECT_THROW_WITH_MESSAGE((check_interaction<AluTraceBuilder, lookup_alu_ff_gt_settings>(trace)),
1714 "LOOKUP_ALU_FF_GT");
1715 } else {
1716 EXPECT_THROW_WITH_MESSAGE((check_interaction<AluTraceBuilder, lookup_alu_int_gt_settings>(trace)),
1717 "LOOKUP_ALU_INT_GT");
1718 }
1719 }
1720}
1721
1722TEST_P(AluLTEConstrainingTest, NegativeAluLTEInput)
1723{
1724 auto params = GetParam();
1725
1726 for (const bool is_eq : { false, true }) {
1727 auto trace = process_lte_trace(params, is_eq);
1728 auto is_ff = std::get<0>(params).get_tag() == MemoryTag::FF;
1729 check_relation<alu>(trace);
1730 check_all_interactions<AluTraceBuilder>(trace);
1731 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1732 bool c = trace.get(Column::alu_ic, 0) == 1;
1733 auto a = trace.get(Column::alu_ia, 0);
1734 auto wrong_b = c ? a - 1 : a + 1;
1735 trace.set(Column::alu_ib, 0, wrong_b);
1736 trace.set(Column::alu_gt_input_b, 0, wrong_b);
1737 // We rely on lookups, so we expect the relations to still pass...
1738 check_relation<alu>(trace);
1739
1740 // ... but the lookup will fail (TODO(MW): properly add a gt and => range check events so it fails because c
1741 // is wrong, rather than because this test has not processed the events):
1742 if (is_ff) {
1743 EXPECT_THROW_WITH_MESSAGE((check_interaction<AluTraceBuilder, lookup_alu_ff_gt_settings>(trace)),
1744 "LOOKUP_ALU_FF_GT");
1745 } else {
1746 EXPECT_THROW_WITH_MESSAGE((check_interaction<AluTraceBuilder, lookup_alu_int_gt_settings>(trace)),
1747 "LOOKUP_ALU_INT_GT");
1748 }
1749 }
1750}
1751
1752// NOT Opcode TESTS
1753
1754const std::vector<MemoryValue> TEST_VALUES_NOT_OUT = {
1755 MemoryValue::from_tag(MemoryTag::U1, 0),
1756 MemoryValue::from_tag(MemoryTag::U8, 55),
1757 MemoryValue::from_tag(MemoryTag::U16, 65505),
1758 MemoryValue::from_tag(MemoryTag::U32, 9),
1759 MemoryValue::from_tag(MemoryTag::U64, 9),
1760 MemoryValue::from_tag(MemoryTag::U128, 9),
1761 MemoryValue::from_tag(static_cast<MemoryTag>(0), 0), // For FF, b is the default value of 0 with tag 0
1762};
1763
1764const std::vector<TwoOperandTestParams> TEST_VALUES_NOT = zip_helper_two_op(TEST_VALUES_NOT_OUT);
1765
1766class AluNotConstrainingTest : public AluConstrainingTest, public ::testing::WithParamInterface<TwoOperandTestParams> {
1767 public:
1768 TestTraceContainer process_not_with_tracegen(const TwoOperandTestParams& params, bool error = false)
1769 {
1770 TestTraceContainer trace;
1771 auto [a, b] = params;
1772 auto is_ff = a.get_tag() == MemoryTag::FF;
1773
1774 builder.process(
1775 {
1776 { .operation = simulation::AluOperation::NOT, .a = a, .b = b, .error = error || is_ff },
1777 },
1778 trace);
1779
1780 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
1781 precomputed_builder.process_tag_parameters(trace);
1782 return trace;
1783 }
1784};
1785
1786INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluNotConstrainingTest, ::testing::ValuesIn(TEST_VALUES_NOT));
1787
1788TEST_P(AluNotConstrainingTest, AluNotTraceGen)
1789{
1790 auto trace = process_not_with_tracegen(GetParam());
1791 check_all_interactions<AluTraceBuilder>(trace);
1792 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1793 check_relation<alu>(trace);
1794}
1795
1796TEST_P(AluNotConstrainingTest, NegativeAluNotTraceGen)
1797{
1798 auto params = GetParam();
1799 auto trace = process_not_with_tracegen(params);
1800 check_all_interactions<AluTraceBuilder>(trace);
1801 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1802 check_relation<alu>(trace);
1803 trace.set(Column::alu_ib, 0, trace.get(Column::alu_ib, 0) + 1); // Mutate output
1804 // The FF case <==> tag_err for NOT, so NOT_OP_MAIN is gated:
1805 if (std::get<0>(params).get_tag() != MemoryTag::FF) {
1806 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "NOT_OP_MAIN");
1807 }
1808}
1809
1810// Unconditional check for AB_TAGS_CHECK error for NOT opcode. This cannot satsify the constraints.
1811TEST_P(AluNotConstrainingTest, AluNotTraceGenTagError)
1812{
1813 auto [a, b] = GetParam();
1814 auto trace = process_not_with_tracegen(
1815 TwoOperandTestParams{ a, MemoryValue::from_tag(TAG_ERROR_TEST_VALUES.at(b.get_tag()), b.as_ff()) }, true);
1816 check_all_interactions<AluTraceBuilder>(trace);
1817 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1818 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "ONLY_RELEVANT_CHECK_AB_TAGS_ERROR");
1819}
1820
1821// Supported TAG error when a is of FF type.
1822TEST_F(AluNotConstrainingTest, AluNotTraceGenTagErrorFF)
1823{
1824 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
1825 auto b = MemoryValue::from_tag(MemoryTag::FF, 253);
1826 auto trace = process_not_with_tracegen(TwoOperandTestParams{ a, b }, true);
1827 check_all_interactions<AluTraceBuilder>(trace);
1828 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1829 check_relation<alu>(trace);
1830}
1831
1832// SHL TESTS
1833
1834const std::vector<MemoryValue> TEST_VALUES_SHL_OUT = {
1835 MemoryValue::from_tag(MemoryTag::U1, 1),
1836 MemoryValue::from_tag(MemoryTag::U8, 0),
1837 MemoryValue::from_tag(MemoryTag::U16, 0),
1838 MemoryValue::from_tag(MemoryTag::U32, 0xfffffec0),
1839 MemoryValue::from_tag(MemoryTag::U64, 0xfffffffffffffec0ULL),
1840 MemoryValue::from_tag(MemoryTag::U128, (uint256_t(1) << 128) - 320), // 0xfffffffffffffffffffffffffffffec0
1841};
1842
1843const std::vector<ThreeOperandTestParams> TEST_VALUES_SHL = zip_helper(TEST_VALUES_SHL_OUT);
1844
1845class AluShlConstrainingTest : public AluConstrainingTest,
1846 public ::testing::WithParamInterface<ThreeOperandTestParams> {
1847 public:
1848 TestTraceContainer process_shl_trace(ThreeOperandTestParams params)
1849 {
1850 auto [a, b, c] = params;
1851
1852 auto mem_tag = a.get_tag();
1853 auto tag = static_cast<uint8_t>(mem_tag);
1854 auto tag_bits = get_tag_bits(mem_tag);
1855 auto a_num = static_cast<uint128_t>(a.as_ff());
1856 auto b_num = static_cast<uint128_t>(b.as_ff());
1857
1858 auto overflow = b_num > tag_bits;
1859 uint128_t shift_lo_bits = overflow ? tag_bits : tag_bits - b_num;
1860 uint128_t shift_hi_bits = overflow ? tag_bits : b_num;
1861 auto two_pow_shift_lo_bits = static_cast<uint128_t>(1) << shift_lo_bits;
1862 auto a_lo = overflow ? b_num - tag_bits : a_num % two_pow_shift_lo_bits;
1863 auto a_hi = a_num >> shift_lo_bits;
1864
1865 auto trace = TestTraceContainer({
1866 {
1867 { C::alu_a_hi, a_hi },
1868 { C::alu_a_hi_bits, shift_hi_bits },
1869 { C::alu_a_lo, a_lo },
1870 { C::alu_a_lo_bits, shift_lo_bits },
1871 { C::alu_helper1, static_cast<uint128_t>(1) << b_num },
1872 { C::alu_ia, a },
1873 { C::alu_ia_tag, tag },
1874 { C::alu_ib, b },
1875 { C::alu_ib_tag, tag },
1876 { C::alu_ic, c },
1877 { C::alu_ic_tag, tag },
1878 { C::alu_max_bits, tag_bits },
1879 { C::alu_max_value, get_tag_max_value(mem_tag) },
1880 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_SHL },
1881 { C::alu_sel, 1 },
1882 { C::alu_sel_decompose_a, 1 },
1883 { C::alu_sel_op_shl, 1 },
1884 { C::alu_sel_shift_ops_no_overflow, overflow ? 0 : 1 },
1885 { C::alu_shift_lo_bits, shift_lo_bits },
1886 { C::alu_tag_ff_diff_inv, FF(tag - static_cast<uint8_t>(MemoryTag::FF)).invert() },
1887 { C::alu_sel_is_u128, tag == static_cast<uint8_t>(MemoryTag::U128) ? 1 : 0 },
1888 { C::alu_tag_u128_diff_inv,
1889 tag == static_cast<uint8_t>(MemoryTag::U128)
1890 ? 0
1891 : FF(tag - static_cast<uint8_t>(MemoryTag::U128)).invert() },
1892 { C::alu_two_pow_shift_lo_bits, two_pow_shift_lo_bits },
1893 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
1894 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
1895 { C::execution_mem_tag_reg_2_, tag }, // = ic_tag
1896 { C::execution_register_0_, a }, // = ia
1897 { C::execution_register_1_, b }, // = ib
1898 { C::execution_register_2_, c }, // = ic
1899 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
1900 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_SHL }, // = alu_op_id
1901
1902 },
1903 });
1904
1905 precomputed_builder.process_misc(trace, std::max(NUM_OF_TAGS, static_cast<uint8_t>(shift_lo_bits + 1)));
1906 precomputed_builder.process_tag_parameters(trace);
1907 precomputed_builder.process_power_of_2(trace);
1908 range_check_builder.process({ { .value = a_lo, .num_bits = static_cast<uint8_t>(shift_lo_bits) },
1909 { .value = a_hi, .num_bits = static_cast<uint8_t>(shift_hi_bits) } },
1910 trace);
1911
1912 return trace;
1913 }
1914
1915 TestTraceContainer process_shl_with_tracegen(ThreeOperandTestParams params, bool error = false)
1916 {
1917 TestTraceContainer trace;
1918 auto [a, b, c] = params;
1919 auto b_num = static_cast<uint128_t>(b.as_ff());
1920 auto tag_bits = get_tag_bits(a.get_tag());
1921 auto overflow = b_num > tag_bits;
1922 uint128_t shift_lo_bits = overflow ? tag_bits : tag_bits - b_num;
1923 auto a_lo = overflow ? b_num - tag_bits
1924 : static_cast<uint128_t>(a.as_ff()) % (static_cast<uint128_t>(1) << shift_lo_bits);
1925
1926 builder.process(
1927 {
1928 { .operation = simulation::AluOperation::SHL, .a = a, .b = b, .c = c, .error = error },
1929 },
1930 trace);
1931
1932 precomputed_builder.process_misc(trace, std::max(NUM_OF_TAGS, static_cast<uint8_t>(shift_lo_bits + 1)));
1933 precomputed_builder.process_tag_parameters(trace);
1934 precomputed_builder.process_power_of_2(trace);
1935 range_check_builder.process({ { .value = a_lo, .num_bits = static_cast<uint8_t>(shift_lo_bits) },
1936 { .value = static_cast<uint128_t>(a.as_ff()) >> shift_lo_bits,
1937 .num_bits = static_cast<uint8_t>(overflow ? tag_bits : b_num) } },
1938 trace);
1939 return trace;
1940 }
1941};
1942
1943INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluShlConstrainingTest, ::testing::ValuesIn(TEST_VALUES_SHL));
1944
1945TEST_P(AluShlConstrainingTest, AluShl)
1946{
1947 auto trace = process_shl_trace(GetParam());
1948 check_all_interactions<AluTraceBuilder>(trace);
1949 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1950 check_relation<alu>(trace);
1951}
1952
1953TEST_P(AluShlConstrainingTest, AluShlTraceGen)
1954{
1955 auto trace = process_shl_with_tracegen(GetParam());
1956 check_all_interactions<AluTraceBuilder>(trace);
1957 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
1958 check_relation<alu>(trace);
1959}
1960
1961TEST_F(AluShlConstrainingTest, NegativeAluShlFF)
1962{
1963 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
1964 auto b = MemoryValue::from_tag(MemoryTag::FF, 5);
1965 auto c = MemoryValue::from_tag(MemoryTag::FF, 2 << 5);
1966 auto trace = process_shl_with_tracegen({ a, b, c }, true);
1967 check_relation<alu>(trace);
1968 check_all_interactions<AluTraceBuilder>(trace);
1969
1970 // Check the edge case of FF tag (=> max_bits = 0) and b = 0:
1971 trace.set(Column::alu_ib, 0, 0);
1972 check_relation<alu>(trace);
1973 check_all_interactions<AluTraceBuilder>(trace);
1974
1975 // Disable tag and error selectors:
1976 trace.set(Column::alu_sel_tag_err, 0, 0);
1977 trace.set(Column::alu_sel_err, 0, 0);
1978 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TAG_ERR_CHECK");
1979}
1980
1981TEST_F(AluShlConstrainingTest, NegativeAluShlTagMismatchOverflow)
1982{
1983 auto a = MemoryValue::from_tag(MemoryTag::U8, 2);
1984 auto b = MemoryValue::from_tag(MemoryTag::U32, 256);
1985 auto c = MemoryValue::from_tag(MemoryTag::U8, 0);
1986 auto trace = process_shl_with_tracegen({ a, b, c }, true);
1987 check_relation<alu>(trace);
1988 check_all_interactions<AluTraceBuilder>(trace);
1989
1990 // Disable tag and error selectors:
1991 trace.set(Column::alu_sel_tag_err, 0, 0);
1992 trace.set(Column::alu_sel_err, 0, 0);
1993 // Disable ab tag mismatch error:
1994 trace.set(Column::alu_sel_ab_tag_mismatch, 0, 0);
1995 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "AB_TAGS_CHECK");
1996
1997 // Second attempt with setting the ab tags diff inverse to zero:
1998 trace.set(Column::alu_ab_tags_diff_inv, 0, 0);
1999 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "AB_TAGS_CHECK");
2000
2001 // Reset only ab tag diff related columns:
2002 trace.set(Column::alu_ab_tags_diff_inv,
2003 0,
2004 (FF(static_cast<uint8_t>(MemoryTag::U8)) - FF(static_cast<uint8_t>(MemoryTag::U32))).invert());
2005 trace.set(Column::alu_sel_ab_tag_mismatch, 0, 1);
2006 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TAG_ERR_CHECK");
2007}
2008
2009// SHR TESTS
2010
2011const std::vector<MemoryValue> TEST_VALUES_SHR_OUT = {
2012 MemoryValue::from_tag(MemoryTag::U1, 1),
2013 MemoryValue::from_tag(MemoryTag::U8, 0),
2014 MemoryValue::from_tag(MemoryTag::U16, 0),
2015 MemoryValue::from_tag(MemoryTag::U32, 0x7ffffff),
2016 MemoryValue::from_tag(MemoryTag::U64, 0x7ffffffffffffffULL),
2017 MemoryValue::from_tag(MemoryTag::U128,
2018 (uint256_t(1) << 128) - 1 - (uint256_t(248) << 120)), // 0x7ffffffffffffffffffffffffffffff
2019};
2020
2021const std::vector<ThreeOperandTestParams> TEST_VALUES_SHR = zip_helper(TEST_VALUES_SHR_OUT);
2022
2023class AluShrConstrainingTest : public AluConstrainingTest,
2024 public ::testing::WithParamInterface<ThreeOperandTestParams> {
2025 public:
2026 TestTraceContainer process_shr_trace(ThreeOperandTestParams params)
2027 {
2028 auto [a, b, c] = params;
2029
2030 auto mem_tag = a.get_tag();
2031 auto tag = static_cast<uint8_t>(mem_tag);
2032 auto tag_bits = get_tag_bits(mem_tag);
2033 auto a_num = static_cast<uint128_t>(a.as_ff());
2034 auto b_num = static_cast<uint128_t>(b.as_ff());
2035
2036 auto overflow = b_num > tag_bits;
2037 uint128_t shift_lo_bits = overflow ? tag_bits : b_num;
2038 uint128_t shift_hi_bits = overflow ? tag_bits : tag_bits - b_num;
2039 auto two_pow_shift_lo_bits = static_cast<uint128_t>(1) << shift_lo_bits;
2040 auto a_lo = overflow ? b_num - tag_bits : a_num % two_pow_shift_lo_bits;
2041 auto a_hi = a_num >> shift_lo_bits;
2042
2043 auto trace = TestTraceContainer({
2044 {
2045 { C::alu_a_hi, a_hi },
2046 { C::alu_a_hi_bits, shift_hi_bits },
2047 { C::alu_a_lo, a_lo },
2048 { C::alu_a_lo_bits, shift_lo_bits },
2049 { C::alu_ia, a },
2050 { C::alu_ia_tag, tag },
2051 { C::alu_ib, b },
2052 { C::alu_ib_tag, tag },
2053 { C::alu_ic, c },
2054 { C::alu_ic_tag, tag },
2055 { C::alu_max_bits, tag_bits },
2056 { C::alu_max_value, get_tag_max_value(mem_tag) },
2057 { C::alu_op_id, AVM_EXEC_OP_ID_ALU_SHR },
2058 { C::alu_sel, 1 },
2059 { C::alu_sel_decompose_a, 1 },
2060 { C::alu_sel_op_shr, 1 },
2061 { C::alu_sel_shift_ops_no_overflow, overflow ? 0 : 1 },
2062 { C::alu_shift_lo_bits, shift_lo_bits },
2063 { C::alu_tag_ff_diff_inv, FF(tag - static_cast<uint8_t>(MemoryTag::FF)).invert() },
2064 { C::alu_sel_is_u128, tag == static_cast<uint8_t>(MemoryTag::U128) ? 1 : 0 },
2065 { C::alu_tag_u128_diff_inv,
2066 tag == static_cast<uint8_t>(MemoryTag::U128)
2067 ? 0
2068 : FF(tag - static_cast<uint8_t>(MemoryTag::U128)).invert() },
2069 { C::alu_two_pow_shift_lo_bits, two_pow_shift_lo_bits },
2070 { C::execution_mem_tag_reg_0_, tag }, // = ia_tag
2071 { C::execution_mem_tag_reg_1_, tag }, // = ib_tag
2072 { C::execution_mem_tag_reg_2_, tag }, // = ic_tag
2073 { C::execution_register_0_, a }, // = ia
2074 { C::execution_register_1_, b }, // = ib
2075 { C::execution_register_2_, c }, // = ic
2076 { C::execution_sel_exec_dispatch_alu, 1 }, // = sel
2077 { C::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_SHR }, // = alu_op_id
2078
2079 },
2080 });
2081
2082 precomputed_builder.process_misc(trace, std::max(NUM_OF_TAGS, static_cast<uint8_t>(shift_lo_bits + 1)));
2083 precomputed_builder.process_tag_parameters(trace);
2084 precomputed_builder.process_power_of_2(trace);
2085 range_check_builder.process({ { .value = a_lo, .num_bits = static_cast<uint8_t>(shift_lo_bits) },
2086 { .value = a_hi, .num_bits = static_cast<uint8_t>(shift_hi_bits) } },
2087 trace);
2088
2089 return trace;
2090 }
2091
2092 TestTraceContainer process_shr_with_tracegen(ThreeOperandTestParams params, bool error = false)
2093 {
2094 TestTraceContainer trace;
2095 auto [a, b, c] = params;
2096 auto b_num = static_cast<uint128_t>(b.as_ff());
2097 auto tag_bits = get_tag_bits(a.get_tag());
2098 auto overflow = b_num > tag_bits;
2099 uint128_t shift_lo_bits = overflow ? tag_bits : b_num;
2100 auto a_lo = overflow ? b_num - tag_bits
2101 : static_cast<uint128_t>(a.as_ff()) % (static_cast<uint128_t>(1) << shift_lo_bits);
2102
2103 builder.process(
2104 {
2105 { .operation = simulation::AluOperation::SHR, .a = a, .b = b, .c = c, .error = error },
2106 },
2107 trace);
2108
2109 precomputed_builder.process_misc(trace, std::max(NUM_OF_TAGS, static_cast<uint8_t>(shift_lo_bits + 1)));
2110 precomputed_builder.process_tag_parameters(trace);
2111 precomputed_builder.process_power_of_2(trace);
2112 range_check_builder.process({ { .value = a_lo, .num_bits = static_cast<uint8_t>(shift_lo_bits) },
2113 { .value = static_cast<uint128_t>(a.as_ff()) >> shift_lo_bits,
2114 .num_bits = static_cast<uint8_t>(overflow ? tag_bits : tag_bits - b_num) } },
2115 trace);
2116 return trace;
2117 }
2118};
2119
2120INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluShrConstrainingTest, ::testing::ValuesIn(TEST_VALUES_SHR));
2121
2122TEST_P(AluShrConstrainingTest, AluShr)
2123{
2124 auto trace = process_shr_trace(GetParam());
2125 check_all_interactions<AluTraceBuilder>(trace);
2126 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
2127 check_relation<alu>(trace);
2128}
2129
2130TEST_P(AluShrConstrainingTest, AluShrTraceGen)
2131{
2132 auto trace = process_shr_with_tracegen(GetParam());
2133 check_all_interactions<AluTraceBuilder>(trace);
2134 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_alu_settings>(trace);
2135 check_relation<alu>(trace);
2136}
2137
2138TEST_F(AluShrConstrainingTest, NegativeAluShrFF)
2139{
2140 auto a = MemoryValue::from_tag(MemoryTag::FF, 2);
2141 auto b = MemoryValue::from_tag(MemoryTag::FF, 5);
2142 auto c = MemoryValue::from_tag(MemoryTag::FF, 2 << 5);
2143 auto trace = process_shr_with_tracegen({ a, b, c }, true);
2144 check_relation<alu>(trace);
2145 check_all_interactions<AluTraceBuilder>(trace);
2146
2147 // Check the edge case of FF tag (=> max_bits = 0) and b = 0:
2148 trace.set(Column::alu_ib, 0, 0);
2149 check_relation<alu>(trace);
2150 check_all_interactions<AluTraceBuilder>(trace);
2151
2152 // Disable tag and error selectors:
2153 trace.set(Column::alu_sel_tag_err, 0, 0);
2154 trace.set(Column::alu_sel_err, 0, 0);
2155 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TAG_ERR_CHECK");
2156}
2157
2158TEST_F(AluShrConstrainingTest, NegativeAluShrTagMismatchOverflow)
2159{
2160 auto a = MemoryValue::from_tag(MemoryTag::U16, 2);
2161 auto b = MemoryValue::from_tag(MemoryTag::U64, 123456);
2162 auto c = MemoryValue::from_tag(MemoryTag::U16, 0);
2163 auto trace = process_shr_with_tracegen({ a, b, c }, true);
2164 check_relation<alu>(trace);
2165 check_all_interactions<AluTraceBuilder>(trace);
2166 // Disable tag and error selectors:
2167 trace.set(Column::alu_sel_tag_err, 0, 0);
2168 trace.set(Column::alu_sel_err, 0, 0);
2169 // Disable ab tag mismatch error:
2170 trace.set(Column::alu_sel_ab_tag_mismatch, 0, 0);
2171 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "AB_TAGS_CHECK");
2172
2173 // Second attempt with setting the ab tags diff inverse to zero:
2174 trace.set(Column::alu_ab_tags_diff_inv, 0, 0);
2175 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "AB_TAGS_CHECK");
2176
2177 // Reset only ab tag diff related columns:
2178 trace.set(Column::alu_ab_tags_diff_inv,
2179 0,
2180 (FF(static_cast<uint8_t>(MemoryTag::U16)) - FF(static_cast<uint8_t>(MemoryTag::U64))).invert());
2181 trace.set(Column::alu_sel_ab_tag_mismatch, 0, 1);
2182 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TAG_ERR_CHECK");
2183}
2184
2185// TRUNCATE operation (SET/CAST opcodes)
2186
2187// Truncation is a special case as we always have FF MemoryValue inputs:
2188const std::vector<ThreeOperandTestParams> TEST_VALUES_TRUNCATE = {
2189 // Trivial truncation cases
2190 { MemoryValue::from_tag(MemoryTag::FF, 1),
2191 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U1)),
2192 MemoryValue::from_tag(MemoryTag::U1, 1) },
2193 { MemoryValue::from_tag(MemoryTag::FF, 42),
2194 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U8)),
2195 MemoryValue::from_tag(MemoryTag::U8, 42) },
2196 { MemoryValue::from_tag(MemoryTag::FF, 12345),
2197 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U16)),
2198 MemoryValue::from_tag(MemoryTag::U16, 12345) },
2199 { MemoryValue::from_tag(MemoryTag::FF, 123456789),
2200 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U32)),
2201 MemoryValue::from_tag(MemoryTag::U32, 123456789) },
2202 { MemoryValue::from_tag(MemoryTag::FF, 1234567890123456789ULL),
2203 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U64)),
2204 MemoryValue::from_tag(MemoryTag::U64, 1234567890123456789ULL) },
2205 { MemoryValue::from_tag(MemoryTag::FF, (uint256_t(1) << 127) + 23423429816234ULL),
2206 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U128)),
2207 MemoryValue::from_tag(MemoryTag::U128, (uint256_t(1) << 127) + 23423429816234ULL) },
2208 { MemoryValue::from_tag(MemoryTag::FF, FF::modulus - 3),
2209 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::FF)),
2210 MemoryValue::from_tag(MemoryTag::FF, FF::modulus - 3) },
2211 // Truncation cases (< 128 bits)
2212 { MemoryValue::from_tag(MemoryTag::FF, 212),
2213 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U1)),
2214 MemoryValue::from_tag(MemoryTag::U1, 0) },
2215 { MemoryValue::from_tag(MemoryTag::FF, 257),
2216 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U8)),
2217 MemoryValue::from_tag(MemoryTag::U8, 1) },
2218 { MemoryValue::from_tag(MemoryTag::FF, 65540),
2219 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U16)),
2220 MemoryValue::from_tag(MemoryTag::U16, 4) },
2221 { MemoryValue::from_tag(MemoryTag::FF, 4294967298ULL),
2222 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U32)),
2223 MemoryValue::from_tag(MemoryTag::U32, 2) },
2224 { MemoryValue::from_tag(MemoryTag::FF, 18446744073709551615ULL + 4),
2225 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U64)),
2226 MemoryValue::from_tag(MemoryTag::U64, 3) },
2227 // Truncation cases (>= 128 bits)
2228 { MemoryValue::from_tag(MemoryTag::FF, (uint256_t(134534) << 129) + 986132),
2229 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U1)),
2230 MemoryValue::from_tag(MemoryTag::U1, 0) },
2231 { MemoryValue::from_tag(MemoryTag::FF, FF::modulus - 128735618772ULL),
2232 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U8)),
2233 MemoryValue::from_tag(MemoryTag::U8, 45) },
2234 { MemoryValue::from_tag(MemoryTag::FF, (uint256_t(999) << 128) - 986132ULL),
2235 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U16)),
2236 MemoryValue::from_tag(MemoryTag::U16, 62444) },
2237 { MemoryValue::from_tag(MemoryTag::FF, (uint256_t(134534) << 198) + 986132ULL),
2238 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U32)),
2239 MemoryValue::from_tag(MemoryTag::U32, 986132ULL) },
2240 { MemoryValue::from_tag(MemoryTag::FF, (uint256_t(134534) << 198) - 986132ULL),
2241 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U64)),
2242 MemoryValue::from_tag(MemoryTag::U64, static_cast<uint64_t>(-986132ULL)) },
2243 { MemoryValue::from_tag(MemoryTag::FF, FF::modulus - 8723),
2244 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U128)),
2245 MemoryValue::from_tag(MemoryTag::U128, static_cast<uint128_t>(FF::modulus - 8723)) },
2246};
2247
2248class AluTruncateConstrainingTest : public AluConstrainingTest,
2249 public ::testing::WithParamInterface<ThreeOperandTestParams> {
2250 public:
2251 TestTraceContainer process_truncate_with_tracegen(const ThreeOperandTestParams& params, TestTraceContainer& trace)
2252 {
2253 auto [a, b, c] = params;
2254
2255 builder.process(
2256 {
2257 { .operation = simulation::AluOperation::TRUNCATE, .a = a, .b = b, .c = c },
2258 },
2259 trace);
2260
2261 precomputed_builder.process_misc(trace, NUM_OF_TAGS);
2262 precomputed_builder.process_tag_parameters(trace);
2263
2264 auto is_non_trivial = trace.get(Column::alu_sel_trunc_non_trivial, 0) == 1;
2265
2266 if (is_non_trivial) {
2267 auto a_decomp = simulation::decompose_256(static_cast<uint256_t>(a.as_ff()));
2268 auto dst_tag = c.get_tag();
2269 uint8_t bits = get_tag_bits(dst_tag);
2270 range_check_builder.process({ { .value = dst_tag == MemoryTag::U128 ? 0 : a_decomp.lo >> bits,
2271 .num_bits = static_cast<uint8_t>(128 - bits) } },
2272 trace);
2273 auto is_gte_128 = trace.get(Column::alu_sel_trunc_gte_128, 0) == 1;
2274 if (is_gte_128) {
2275 auto p_limbs = simulation::decompose_256(FF::modulus);
2276 simulation::LimbsComparisonWitness p_sub_a_witness = { .lo = p_limbs.lo - a_decomp.lo,
2277 .hi = p_limbs.hi - a_decomp.hi,
2278 .borrow = false };
2279 field_gt_builder.process({ { .operation = simulation::FieldGreaterOperation::CANONICAL_DECOMPOSITION,
2280 .a = a,
2281 .a_limbs = a_decomp,
2282 .p_sub_a_witness = p_sub_a_witness } },
2283 trace);
2284 }
2285 }
2286
2287 return trace;
2288 }
2289
2290 TestTraceContainer process_set_with_tracegen(const ThreeOperandTestParams& params)
2291 {
2292 TestTraceContainer trace;
2293 auto [a, b, _c] = params;
2294 auto dst_tag = static_cast<MemoryTag>(static_cast<uint8_t>(b.as_ff()));
2295 auto c = MemoryValue::from_tag_truncating(dst_tag, a);
2296 trace.set(0,
2297 { {
2298 { Column::execution_sel_exec_dispatch_set, 1 },
2299 { Column::execution_rop_2_, a },
2300 { Column::execution_rop_1_, static_cast<uint8_t>(dst_tag) },
2301 { Column::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_TRUNCATE },
2302 { Column::execution_register_0_, c.as_ff() },
2303 { Column::execution_mem_tag_reg_0_, static_cast<uint8_t>(dst_tag) },
2304 } });
2305
2306 process_truncate_with_tracegen(params, trace);
2307
2308 return trace;
2309 }
2310
2311 TestTraceContainer process_cast_with_tracegen(const ThreeOperandTestParams& params)
2312 {
2313 TestTraceContainer trace;
2314 auto [a, b, _c] = params;
2315 auto dst_tag = static_cast<MemoryTag>(static_cast<uint8_t>(b.as_ff()));
2316 auto c = MemoryValue::from_tag_truncating(dst_tag, a);
2317 trace.set(0,
2318 { {
2319 { Column::execution_sel_exec_dispatch_cast, 1 },
2320 { Column::execution_register_0_, a },
2321 { Column::execution_rop_2_, static_cast<uint8_t>(dst_tag) },
2322 { Column::execution_subtrace_operation_id, AVM_EXEC_OP_ID_ALU_TRUNCATE },
2323 { Column::execution_register_1_, c.as_ff() },
2324 { Column::execution_mem_tag_reg_1_, static_cast<uint8_t>(dst_tag) },
2325 } });
2326
2327 process_truncate_with_tracegen(params, trace);
2328
2329 return trace;
2330 }
2331};
2332
2333INSTANTIATE_TEST_SUITE_P(AluConstrainingTest, AluTruncateConstrainingTest, ::testing::ValuesIn(TEST_VALUES_TRUNCATE));
2334
2335TEST_P(AluTruncateConstrainingTest, AluSet)
2336{
2337 auto trace = process_set_with_tracegen(GetParam());
2338 check_all_interactions<AluTraceBuilder>(trace);
2339 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_set_settings>(trace);
2340 check_relation<alu>(trace);
2341}
2342
2343TEST_P(AluTruncateConstrainingTest, AluCast)
2344{
2345 auto trace = process_cast_with_tracegen(GetParam());
2346 check_all_interactions<AluTraceBuilder>(trace);
2347 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_set_settings>(trace);
2348 check_relation<alu>(trace);
2349}
2350
2351TEST_P(AluTruncateConstrainingTest, NegativeTruncateWrongTrivialCase)
2352{
2353 TestTraceContainer trace;
2354 process_truncate_with_tracegen(GetParam(), trace);
2355 check_relation<alu>(trace);
2356 bool is_trivial = trace.get(Column::alu_sel_trunc_trivial, 0) == 1;
2357 trace.set(Column::alu_sel_trunc_trivial, 0, static_cast<uint8_t>(!is_trivial));
2358 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "SEL_TRUNC");
2359 trace.set(Column::alu_sel_trunc_trivial, 0, static_cast<uint8_t>(is_trivial));
2360 trace.set(Column::alu_sel_trunc_non_trivial, 0, static_cast<uint8_t>(is_trivial));
2361 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "SEL_TRUNC");
2362}
2363
2364TEST_P(AluTruncateConstrainingTest, NegativeTruncateWrong128BitsCase)
2365{
2366 TestTraceContainer trace;
2367 process_truncate_with_tracegen(GetParam(), trace);
2368 check_relation<alu>(trace);
2369 bool is_lt_128 = trace.get(Column::alu_sel_trunc_lt_128, 0) == 1;
2370 trace.set(Column::alu_sel_trunc_lt_128, 0, static_cast<uint8_t>(!is_lt_128));
2371 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "SEL_TRUNC");
2372 trace.set(Column::alu_sel_trunc_lt_128, 0, static_cast<uint8_t>(is_lt_128));
2373 check_relation<alu>(trace);
2374 bool is_gte_128 = trace.get(Column::alu_sel_trunc_gte_128, 0) == 1;
2375 trace.set(Column::alu_sel_trunc_gte_128, 0, static_cast<uint8_t>(!is_gte_128));
2376 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "SEL_TRUNC");
2377}
2378
2379TEST_F(AluTruncateConstrainingTest, NegativeTruncateWrongMid)
2380{
2381 TestTraceContainer trace;
2382 process_truncate_with_tracegen({ MemoryValue::from_tag(MemoryTag::FF, 4294967298ULL),
2383 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U32)),
2384 MemoryValue::from_tag(MemoryTag::U32, 2) },
2385 trace);
2386 check_relation<alu>(trace);
2387 trace.set(Column::alu_mid, 0, 1234ULL);
2388 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TRUNC_LO_128_DECOMPOSITION");
2389}
2390
2391TEST_F(AluTruncateConstrainingTest, NegativeTruncateWrongMidBits)
2392{
2393 TestTraceContainer trace;
2394 process_truncate_with_tracegen({ MemoryValue::from_tag(MemoryTag::FF, FF::modulus - 2),
2395 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U1)),
2396 MemoryValue::from_tag(MemoryTag::U1, 1) },
2397 trace);
2398 check_relation<alu>(trace);
2399 trace.set(Column::alu_mid_bits, 0, 32);
2400 EXPECT_THROW_WITH_MESSAGE(check_relation<alu>(trace), "TRUNC_MID_BITS");
2401}
2402
2403TEST_F(AluTruncateConstrainingTest, NegativeTruncateWrongLo128FromCanonDec)
2404{
2405 TestTraceContainer trace;
2406 process_truncate_with_tracegen({ MemoryValue::from_tag(MemoryTag::FF, (uint256_t(134534) << 198) - 986132ULL),
2407 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U64)),
2408 MemoryValue::from_tag(MemoryTag::U64, static_cast<uint64_t>(-986132ULL)) },
2409 trace);
2410 check_relation<alu>(trace);
2411 check_all_interactions<AluTraceBuilder>(trace);
2412 trace.set(Column::alu_a_lo, 0, 1234ULL);
2414 (check_interaction<AluTraceBuilder, lookup_alu_large_trunc_canonical_dec_settings>(trace)),
2415 "Failed.*LARGE_TRUNC_CANONICAL_DEC. Could not find tuple in destination.");
2416}
2417
2418TEST_F(AluTruncateConstrainingTest, NegativeTruncateWrongMidIntoRangeCheck)
2419{
2420 TestTraceContainer trace;
2421 process_truncate_with_tracegen({ MemoryValue::from_tag(MemoryTag::FF, (uint256_t(134534) << 198) - 986132ULL),
2422 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U64)),
2423 MemoryValue::from_tag(MemoryTag::U64, static_cast<uint64_t>(-986132ULL)) },
2424 trace);
2425 check_relation<alu>(trace);
2426 check_all_interactions<AluTraceBuilder>(trace);
2427 trace.set(Column::alu_mid, 0, 1234ULL);
2428 EXPECT_THROW_WITH_MESSAGE((check_interaction<AluTraceBuilder, lookup_alu_range_check_trunc_mid_settings>(trace)),
2429 "Failed.*RANGE_CHECK_TRUNC_MID. Could not find tuple in destination.");
2430}
2431
2432TEST_F(AluTruncateConstrainingTest, NegativeCastWrongDispatching)
2433{
2434 auto trace =
2435 process_cast_with_tracegen({ MemoryValue::from_tag(MemoryTag::FF, 4294967298ULL),
2436 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U32)),
2437 MemoryValue::from_tag(MemoryTag::U32, 2) });
2438 check_relation<alu>(trace);
2439 check_all_interactions<AluTraceBuilder>(trace);
2440 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_cast_settings>(trace);
2441 trace.set(Column::execution_register_0_, 0, trace.get(Column::execution_register_0_, 0) + 1);
2443 (check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_cast_settings>(trace)),
2444 "Failed.*EXECUTION_DISPATCH_TO_CAST. Could not find tuple in destination.");
2445}
2446
2447TEST_F(AluTruncateConstrainingTest, NegativeSetWrongDispatching)
2448{
2449 auto trace = process_set_with_tracegen({ MemoryValue::from_tag(MemoryTag::FF, 4294967298ULL),
2450 MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(MemoryTag::U32)),
2451 MemoryValue::from_tag(MemoryTag::U32, 2) });
2452 check_relation<alu>(trace);
2453 check_all_interactions<AluTraceBuilder>(trace);
2454 check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_set_settings>(trace);
2455 trace.set(Column::execution_rop_2_, 0, trace.get(Column::execution_rop_2_, 0) + 1);
2457 (check_interaction<ExecutionTraceBuilder, lookup_execution_dispatch_to_set_settings>(trace)),
2458 "Failed.*EXECUTION_DISPATCH_TO_SET. Could not find tuple in destination.");
2459}
2460
2461} // namespace
2462} // namespace bb::avm2::constraining
INSTANTIATE_TEST_SUITE_P(AcirTests, AcirIntegrationSingleTest, testing::Values("a_1327_concrete_in_generic", "a_1_mul", "a_2_div", "a_3_add", "a_4_sub", "a_5_over", "a_6", "a_6_array", "a_7", "a_7_function", "aes128_encrypt", "arithmetic_binary_operations", "array_dynamic", "array_dynamic_blackbox_input", "array_dynamic_main_output", "array_dynamic_nested_blackbox_input", "array_eq", "array_if_cond_simple", "array_len", "array_neq", "array_sort", "array_to_slice", "array_to_slice_constant_length", "assert", "assert_statement", "assign_ex", "bigint", "bit_and", "bit_not", "bit_shifts_comptime", "bit_shifts_runtime", "blake3", "bool_not", "bool_or", "break_and_continue", "brillig_acir_as_brillig", "brillig_array_eq", "brillig_array_to_slice", "brillig_arrays", "brillig_assert", "brillig_bit_shifts_runtime", "brillig_blake2s", "brillig_blake3", "brillig_calls", "brillig_calls_array", "brillig_calls_conditionals", "brillig_conditional", "brillig_cow", "brillig_cow_assign", "brillig_cow_regression", "brillig_ecdsa_secp256k1", "brillig_ecdsa_secp256r1", "brillig_embedded_curve", "brillig_fns_as_values", "brillig_hash_to_field", "brillig_identity_function", "brillig_keccak", "brillig_loop", "brillig_nested_arrays", "brillig_not", "brillig_oracle", "brillig_pedersen", "brillig_recursion", "brillig_references", "brillig_schnorr", "brillig_sha256", "brillig_signed_cmp", "brillig_signed_div", "brillig_slices", "brillig_to_be_bytes", "brillig_to_bits", "brillig_to_bytes_integration", "brillig_to_le_bytes", "brillig_top_level", "brillig_uninitialized_arrays", "brillig_wrapping", "cast_bool", "closures_mut_ref", "conditional_1", "conditional_2", "conditional_regression_421", "conditional_regression_547", "conditional_regression_661", "conditional_regression_short_circuit", "conditional_regression_underflow", "custom_entry", "databus", "debug_logs", "diamond_deps_0", "double_verify_nested_proof", "double_verify_proof", "ecdsa_secp256k1", "ecdsa_secp256r1", "ecdsa_secp256r1_3x", "eddsa", "embedded_curve_ops", "field_attribute", "generics", "global_consts", "hash_to_field", "hashmap", "higher_order_functions", "if_else_chain", "import", "inline_never_basic", "integer_array_indexing", "keccak256", "main_bool_arg", "main_return", "merkle_insert", "missing_closure_env", "modules", "modules_more", "modulus", "nested_array_dynamic", "nested_array_dynamic_simple", "nested_array_in_slice", "nested_arrays_from_brillig", "no_predicates_basic", "no_predicates_brillig", "no_predicates_numeric_generic_poseidon", "operator_overloading", "pedersen_check", "pedersen_commitment", "pedersen_hash", "poseidon_bn254_hash", "poseidonsponge_x5_254", "pred_eq", "prelude", "references", "regression", "regression_2660", "regression_3051", "regression_3394", "regression_3607", "regression_3889", "regression_4088", "regression_4124", "regression_4202", "regression_4449", "regression_4709", "regression_5045", "regression_capacity_tracker", "regression_mem_op_predicate", "regression_method_cannot_be_found", "regression_struct_array_conditional", "schnorr", "sha256", "sha2_byte", "side_effects_constrain_array", "signed_arithmetic", "signed_comparison", "signed_division", "simple_2d_array", "simple_add_and_ret_arr", "simple_array_param", "simple_bitwise", "simple_comparison", "simple_mut", "simple_not", "simple_print", "simple_program_addition", "simple_radix", "simple_shield", "simple_shift_left_right", "slice_coercion", "slice_dynamic_index", "slice_loop", "slices", "strings", "struct", "struct_array_inputs", "struct_fields_ordering", "struct_inputs", "submodules", "to_be_bytes", "to_bytes_consistent", "to_bytes_integration", "to_le_bytes", "trait_as_return_type", "trait_impl_base_type", "traits_in_crates_1", "traits_in_crates_2", "tuple_inputs", "tuples", "type_aliases", "u128", "u16_support", "unconstrained_empty", "unit_value", "unsafe_range_constraint", "witness_compression", "xor"))
TEST_P(AcirIntegrationSingleTest, DISABLED_ProveAndVerifyProgram)
MemoryTag dst_tag
TEST_F(ChonkAPITests, DISABLED_ProveAndVerifyFileBasedFlow)
bb::field< bb::Bn254FrParams > FF
Definition field.cpp:22
#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
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessage)
Definition macros.hpp:7
TEST_F(AvmRecursiveTests, GoblinRecursion)
A test of the Goblinized AVM recursive verifier.
TestTraceContainer empty_trace()
Definition fixtures.cpp:153
TaggedValue MemoryValue
uint8_t get_tag_bits(ValueTag tag)
uint256_t get_tag_max_value(ValueTag tag)
AvmFlavorSettings::FF FF
Definition field.hpp:10
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:44
static constexpr uint256_t modulus
constexpr field invert() const noexcept