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