Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
to_radix.test.cpp
Go to the documentation of this file.
1#include <cstdint>
2#include <gmock/gmock.h>
3#include <gtest/gtest.h>
4
5#include "barretenberg/aztec/aztec_constants.hpp"
30
31namespace bb::avm2::constraining {
32namespace {
33
34using ::testing::Return;
35using ::testing::StrictMock;
36
37using tracegen::ExecutionTraceBuilder;
38using tracegen::GreaterThanTraceBuilder;
39using tracegen::MemoryTraceBuilder;
40using tracegen::PrecomputedTraceBuilder;
41using tracegen::TestTraceContainer;
42using tracegen::ToRadixTraceBuilder;
43
45using C = Column;
46using to_radix = bb::avm2::to_radix<FF>;
47using to_radix_mem = bb::avm2::to_radix_mem<FF>;
48using ToRadixSimulator = simulation::ToRadix;
49
50using simulation::EventEmitter;
51using simulation::GreaterThan;
52using simulation::GreaterThanEvent;
53using simulation::MockExecutionIdManager;
54using simulation::MockFieldGreaterThan;
55using simulation::NoopEventEmitter;
56using simulation::PureGreaterThan;
57using simulation::RangeCheck;
58using simulation::RangeCheckEvent;
59using simulation::ToRadixEvent;
60using simulation::ToRadixMemoryEvent;
61
62constexpr uint64_t MAX_MEM = AVM_MEMORY_SIZE;
63
64TEST(ToRadixConstrainingTest, EmptyRow)
65{
66 check_relation<to_radix>(testing::empty_trace());
67}
68
69TEST(ToRadixConstrainingTest, ToLeBitsBasicTest)
70{
71 EventEmitter<ToRadixEvent> to_radix_event_emitter;
72 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
73
74 PureGreaterThan gt;
75 StrictMock<MockExecutionIdManager> execution_id_manager;
76 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
77
78 auto [bits, truncated] = to_radix_simulator.to_le_bits(FF::one(), 254);
79
80 EXPECT_EQ(bits.size(), 254);
81 EXPECT_FALSE(truncated);
82
83 TestTraceContainer trace({
84 { { C::precomputed_first_row, 1 } },
85 });
86
87 ToRadixTraceBuilder builder;
88 builder.process(to_radix_event_emitter.dump_events(), trace);
89 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 254);
90 check_relation<to_radix>(trace);
91}
92
93TEST(ToRadixConstrainingTest, ToLeBitsPMinusOne)
94{
95 EventEmitter<ToRadixEvent> to_radix_event_emitter;
96 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
97
98 PureGreaterThan gt;
99 StrictMock<MockExecutionIdManager> execution_id_manager;
100 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
101
102 auto [bits, truncated] = to_radix_simulator.to_le_bits(FF::neg_one(), 254);
103
104 EXPECT_EQ(bits.size(), 254);
105 EXPECT_FALSE(truncated);
106
107 TestTraceContainer trace({
108 { { C::precomputed_first_row, 1 } },
109 });
110
111 ToRadixTraceBuilder builder;
112 builder.process(to_radix_event_emitter.dump_events(), trace);
113 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 254);
114 check_relation<to_radix>(trace);
115}
116
117TEST(ToRadixConstrainingTest, ToLeBitsShortest)
118{
119 EventEmitter<ToRadixEvent> to_radix_event_emitter;
120 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
121
122 PureGreaterThan gt;
123 StrictMock<MockExecutionIdManager> execution_id_manager;
124 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
125
126 auto [bits, truncated] = to_radix_simulator.to_le_bits(FF::one(), 1);
127
128 EXPECT_EQ(bits.size(), 1);
129 EXPECT_FALSE(truncated);
130
131 TestTraceContainer trace({
132 { { C::precomputed_first_row, 1 } },
133 });
134
135 ToRadixTraceBuilder builder;
136 builder.process(to_radix_event_emitter.dump_events(), trace);
137 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 1);
138 check_relation<to_radix>(trace);
139}
140
141TEST(ToRadixConstrainingTest, ToLeBitsPadded)
142{
143 EventEmitter<ToRadixEvent> to_radix_event_emitter;
144 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
145
146 PureGreaterThan gt;
147 StrictMock<MockExecutionIdManager> execution_id_manager;
148 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
149
150 auto [bits, truncated] = to_radix_simulator.to_le_bits(FF::one(), 500);
151
152 EXPECT_EQ(bits.size(), 500);
153 EXPECT_FALSE(truncated);
154
155 TestTraceContainer trace({
156 { { C::precomputed_first_row, 1 } },
157 });
158
159 ToRadixTraceBuilder builder;
160 builder.process(to_radix_event_emitter.dump_events(), trace);
161 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 500);
162 check_relation<to_radix>(trace);
163}
164
165TEST(ToRadixConstrainingTest, ToLeRadixBasic)
166{
167 EventEmitter<ToRadixEvent> to_radix_event_emitter;
168 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
169
170 PureGreaterThan gt;
171 StrictMock<MockExecutionIdManager> execution_id_manager;
172 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
173
174 FF value = FF::one();
175 auto [bytes, truncated] = to_radix_simulator.to_le_radix(value, 32, 256);
176
177 auto expected_bytes = value.to_buffer();
178 // to_buffer is BE
179 std::reverse(expected_bytes.begin(), expected_bytes.end());
180 EXPECT_EQ(bytes, expected_bytes);
181 EXPECT_FALSE(truncated);
182
183 TestTraceContainer trace({
184 { { C::precomputed_first_row, 1 } },
185 });
186
187 ToRadixTraceBuilder builder;
188 builder.process(to_radix_event_emitter.dump_events(), trace);
189 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 32);
190 check_relation<to_radix>(trace);
191}
192
193TEST(ToRadixConstrainingTest, ToLeRadixPMinusOne)
194{
195 EventEmitter<ToRadixEvent> to_radix_event_emitter;
196 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
197
198 PureGreaterThan gt;
199 StrictMock<MockExecutionIdManager> execution_id_manager;
200 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
201
202 FF value = FF::neg_one();
203 auto [bytes, truncated] = to_radix_simulator.to_le_radix(value, 32, 256);
204
205 auto expected_bytes = value.to_buffer();
206 // to_buffer is BE
207 std::reverse(expected_bytes.begin(), expected_bytes.end());
208 EXPECT_EQ(bytes, expected_bytes);
209 EXPECT_FALSE(truncated);
210
211 TestTraceContainer trace({
212 { { C::precomputed_first_row, 1 } },
213 });
214
215 ToRadixTraceBuilder builder;
216 builder.process(to_radix_event_emitter.dump_events(), trace);
217 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 32);
218 check_relation<to_radix>(trace);
219}
220
221TEST(ToRadixConstrainingTest, ToLeRadixOneByte)
222{
223 EventEmitter<ToRadixEvent> to_radix_event_emitter;
224 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
225
226 PureGreaterThan gt;
227 StrictMock<MockExecutionIdManager> execution_id_manager;
228 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
229
230 auto [bytes, truncated] = to_radix_simulator.to_le_radix(FF::one(), 1, 256);
231
232 std::vector<uint8_t> expected_bytes = { 1 };
233 EXPECT_EQ(bytes, expected_bytes);
234 EXPECT_FALSE(truncated);
235
236 TestTraceContainer trace({
237 { { C::precomputed_first_row, 1 } },
238 });
239
240 ToRadixTraceBuilder builder;
241 builder.process(to_radix_event_emitter.dump_events(), trace);
242 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 1);
243 check_relation<to_radix>(trace);
244}
245
246TEST(ToRadixConstrainingTest, ToLeRadixPadded)
247{
248 EventEmitter<ToRadixEvent> to_radix_event_emitter;
249 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
250
251 PureGreaterThan gt;
252 StrictMock<MockExecutionIdManager> execution_id_manager;
253 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
254
255 FF value = FF::neg_one();
256 auto [bytes, truncated] = to_radix_simulator.to_le_radix(value, 64, 256);
257
258 auto expected_bytes = value.to_buffer();
259 // to_buffer is BE
260 std::reverse(expected_bytes.begin(), expected_bytes.end());
261 expected_bytes.resize(64);
262 EXPECT_EQ(bytes, expected_bytes);
263 EXPECT_FALSE(truncated);
264
265 TestTraceContainer trace({
266 { { C::precomputed_first_row, 1 } },
267 });
268
269 ToRadixTraceBuilder builder;
270 builder.process(to_radix_event_emitter.dump_events(), trace);
271 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 64);
272 check_relation<to_radix>(trace);
273}
274
275TEST(ToRadixConstrainingTest, ToLeBitsInteractions)
276{
277 EventEmitter<ToRadixEvent> to_radix_event_emitter;
278 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
279
280 PureGreaterThan gt;
281 StrictMock<MockExecutionIdManager> execution_id_manager;
282 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
283
284 to_radix_simulator.to_le_bits(FF::neg_one(), 254);
285
286 TestTraceContainer trace({
287 { { C::precomputed_first_row, 1 } },
288 });
289
290 ToRadixTraceBuilder to_radix_builder;
291 to_radix_builder.process(to_radix_event_emitter.dump_events(), trace);
292 tracegen::PrecomputedTraceBuilder precomputed_builder;
297
298 check_interaction<ToRadixTraceBuilder,
304
305 check_relation<to_radix>(trace);
306}
307
308TEST(ToRadixConstrainingTest, ToLeRadixInteractions)
309{
310 EventEmitter<ToRadixEvent> to_radix_event_emitter;
311 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
312
313 PureGreaterThan gt;
314 StrictMock<MockExecutionIdManager> execution_id_manager;
315 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
316
317 to_radix_simulator.to_le_radix(FF::neg_one(), 32, 256);
318
319 TestTraceContainer trace({
320 { { C::precomputed_first_row, 1 } },
321 });
322
323 ToRadixTraceBuilder to_radix_builder;
324 to_radix_builder.process(to_radix_event_emitter.dump_events(), trace);
325 tracegen::PrecomputedTraceBuilder precomputed_builder;
326
331
332 check_interaction<ToRadixTraceBuilder,
338
339 check_relation<to_radix>(trace);
340}
341
342TEST(ToRadixConstrainingTest, NegativeOverflowCheck)
343{
344 TestTraceContainer trace({
345 { { C::precomputed_first_row, 1 } },
346 });
347
348 std::vector<uint8_t> modulus_le_bits(256, 0);
349 for (size_t i = 0; i < 256; i++) {
350 modulus_le_bits[i] = static_cast<uint8_t>(FF::modulus.get_bit(i));
351 }
352
353 ToRadixEvent event = { .value = FF::zero(), .radix = 2, .limbs = modulus_le_bits };
354 std::vector<ToRadixEvent> events = { event };
355
356 ToRadixTraceBuilder builder;
357 builder.process(events, trace);
358
359 EXPECT_THROW_WITH_MESSAGE(check_relation<to_radix>(trace, to_radix::SR_OVERFLOW_CHECK),
361}
362
363TEST(ToRadixConstrainingTest, NegativeConsistency)
364{
365 EventEmitter<ToRadixEvent> to_radix_event_emitter;
366 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
367
368 PureGreaterThan gt;
369 StrictMock<MockExecutionIdManager> execution_id_manager;
370 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
371
372 to_radix_simulator.to_le_radix(FF(256), 32, 256);
373
374 TestTraceContainer trace({
375 { { C::precomputed_first_row, 1 } },
376 });
377
378 ToRadixTraceBuilder builder;
379 builder.process(to_radix_event_emitter.dump_events(), trace);
380
381 // Disable the selector in the middle
382 trace.set(Column::to_radix_sel, 6, 0);
383
384 EXPECT_THROW_WITH_MESSAGE(check_relation<to_radix>(trace, to_radix::SR_TRACE_CONTINUITY),
386
387 // Mutate the radix
388 trace.set(Column::to_radix_radix, 5, 200);
389
390 EXPECT_THROW_WITH_MESSAGE(check_relation<to_radix>(trace, to_radix::SR_RADIX_CONTINUITY),
392
393 // Mutate the value
394 trace.set(Column::to_radix_value, 4, 27);
395
396 EXPECT_THROW_WITH_MESSAGE(check_relation<to_radix>(trace, to_radix::SR_VALUE_CONTINUITY),
398
399 // Mutate the safe_limbs
400 trace.set(Column::to_radix_safe_limbs, 3, 200);
401
402 EXPECT_THROW_WITH_MESSAGE(check_relation<to_radix>(trace, to_radix::SR_SAFE_LIMBS_CONTINUITY),
404}
405
407// ToRadix Memory Tests
409
410TEST(ToRadixMemoryConstrainingTest, EmptyRow)
411{
412 check_relation<to_radix_mem>(testing::empty_trace());
413}
414
415TEST(ToRadixMemoryConstrainingTest, BasicTest)
416{
417 // Values
418 FF value = FF(1337);
419 uint32_t radix = 10;
420 uint32_t num_limbs = 4;
421 uint32_t dst_addr = 10;
422
423 TestTraceContainer trace = TestTraceContainer({
424 // Row 0
425 {
426 { C::precomputed_first_row, 1 },
427 // GT check - Dst > MAX_MEM = false
428 { C::gt_sel, 1 },
429 { C::gt_input_a, dst_addr + num_limbs },
430 { C::gt_input_b, MAX_MEM },
431 { C::gt_res, 0 }, // GT should return true
432 },
433 // Row 1
434 {
435 // Execution Trace (No gas)
436 { C::execution_sel, 1 },
437 { C::execution_sel_exec_dispatch_to_radix, 1 },
438 { C::execution_register_0_, value },
439 { C::execution_register_1_, radix },
440 { C::execution_register_2_, num_limbs },
441 { C::execution_register_3_, 0 }, // is_output_bits
442 { C::execution_rop_4_, dst_addr },
443 // To Radix Mem
444 { C::to_radix_mem_sel, 1 },
445 { C::to_radix_mem_max_mem_size, MAX_MEM },
446 { C::to_radix_mem_two, 2 },
447 { C::to_radix_mem_two_five_six, 256 },
448 // Memory Inputs
449 { C::to_radix_mem_execution_clk, 0 },
450 { C::to_radix_mem_space_id, 0 },
451 { C::to_radix_mem_dst_addr, dst_addr },
452 { C::to_radix_mem_write_addr_upper_bound, dst_addr + num_limbs },
453 // To Radix Inputs
454 { C::to_radix_mem_value_to_decompose, value },
455 { C::to_radix_mem_radix, radix },
456 { C::to_radix_mem_num_limbs, num_limbs },
457 { C::to_radix_mem_is_output_bits, 0 },
458 // Control Flow
459 { C::to_radix_mem_start, 1 },
460 { C::to_radix_mem_num_limbs_minus_one_inv, num_limbs - 1 == 0 ? 0 : FF(num_limbs - 1).invert() },
461 // Helpers
462 { C::to_radix_mem_sel_num_limbs_is_zero, 0 },
463 { C::to_radix_mem_num_limbs_inv, FF(num_limbs).invert() },
464 { C::to_radix_mem_sel_value_is_zero, 0 },
465 { C::to_radix_mem_value_inv, value.invert() },
466 { C::to_radix_mem_sel_radix_eq_2, 0 },
467 { C::to_radix_mem_radix_min_two_inv, (FF(radix) - FF(2)).invert() },
468 // Output
469 { C::to_radix_mem_limb_value, 1 },
470 { C::to_radix_mem_sel_should_decompose, 1 },
471 { C::to_radix_mem_sel_should_write_mem, 1 },
472 { C::to_radix_mem_limb_index_to_lookup, num_limbs - 1 },
473 { C::to_radix_mem_value_found, 1 },
474 { C::to_radix_mem_output_tag, static_cast<uint8_t>(MemoryTag::U8) },
475
476 // GT check - 2 > radix = false
477 { C::gt_sel, 1 },
478 { C::gt_input_a, 2 },
479 { C::gt_input_b, radix },
480 { C::gt_res, 0 }, // GT should return false
481 },
482 // Row 2
483 {
484 { C::to_radix_mem_sel, 1 },
485 // Memory Inputs
486 { C::to_radix_mem_execution_clk, 0 },
487 { C::to_radix_mem_space_id, 0 },
488 { C::to_radix_mem_dst_addr, dst_addr + 1 },
489 // To Radix Inputs
490 { C::to_radix_mem_value_to_decompose, value },
491 { C::to_radix_mem_radix, radix },
492 { C::to_radix_mem_num_limbs, num_limbs - 1 },
493 { C::to_radix_mem_is_output_bits, 0 },
494 // Control Flow
495 // num_limbs_minus_one = (num_limbs - 1) - 1)
496 { C::to_radix_mem_num_limbs_minus_one_inv, FF(num_limbs - 2).invert() },
497 // Output
498 { C::to_radix_mem_limb_value, 3 },
499 { C::to_radix_mem_sel_should_decompose, 1 },
500 { C::to_radix_mem_sel_should_write_mem, 1 },
501 { C::to_radix_mem_limb_index_to_lookup, num_limbs - 2 },
502 { C::to_radix_mem_output_tag, static_cast<uint8_t>(MemoryTag::U8) },
503 // GT check - Radix > 256 = false
504 { C::gt_sel, 1 },
505 { C::gt_input_a, radix },
506 { C::gt_input_b, 256 },
507 { C::gt_res, 0 }, // GT should return false
508 },
509 // Row 3
510 {
511 { C::to_radix_mem_sel, 1 },
512 // Memory Inputs
513 { C::to_radix_mem_execution_clk, 0 },
514 { C::to_radix_mem_space_id, 0 },
515 { C::to_radix_mem_dst_addr, dst_addr + 2 },
516 // To Radix Inputs
517 { C::to_radix_mem_value_to_decompose, value },
518 { C::to_radix_mem_radix, radix },
519 { C::to_radix_mem_num_limbs, num_limbs - 2 },
520 { C::to_radix_mem_is_output_bits, 0 },
521 // Control Flow
522 // num_limbs_minus_one = (num_limbs - 2) - 1)
523 { C::to_radix_mem_num_limbs_minus_one_inv, FF(num_limbs - 3).invert() },
524 // Output
525 { C::to_radix_mem_limb_value, 3 },
526 { C::to_radix_mem_sel_should_decompose, 1 },
527 { C::to_radix_mem_sel_should_write_mem, 1 },
528 { C::to_radix_mem_limb_index_to_lookup, num_limbs - 3 },
529 { C::to_radix_mem_output_tag, static_cast<uint8_t>(MemoryTag::U8) },
530 },
531 // Row 4
532 {
533 { C::to_radix_mem_sel, 1 },
534 // Memory Inputs
535 { C::to_radix_mem_execution_clk, 0 },
536 { C::to_radix_mem_space_id, 0 },
537 { C::to_radix_mem_dst_addr, 13 },
538 // To Radix Inputs
539 { C::to_radix_mem_value_to_decompose, value },
540 { C::to_radix_mem_radix, radix },
541 { C::to_radix_mem_num_limbs, num_limbs - 3 },
542 { C::to_radix_mem_is_output_bits, 0 },
543 // Control Flow
544 { C::to_radix_mem_last, 1 },
545 // Output
546 { C::to_radix_mem_limb_value, 7 },
547 { C::to_radix_mem_sel_should_decompose, 1 },
548 { C::to_radix_mem_sel_should_write_mem, 1 },
549 { C::to_radix_mem_limb_index_to_lookup, num_limbs - 4 },
550 { C::to_radix_mem_output_tag, static_cast<uint8_t>(MemoryTag::U8) },
551 },
552 });
553
554 // Set the memory values and addresses
555 MemoryAddress value_addr = 0xdeadbeef;
556 MemoryAddress radix_addr = 0x12345678;
557 MemoryAddress num_limbs_addr = 0xc0ffee;
558 MemoryAddress is_output_bits_addr = 0xfeedface;
559
561 { value_addr, MemoryValue::from<FF>(value) },
562 { radix_addr, MemoryValue::from<uint32_t>(radix) },
563 { num_limbs_addr, MemoryValue::from<uint32_t>(num_limbs) },
564 { is_output_bits_addr, MemoryValue::from<uint1_t>(false) },
565 { dst_addr, MemoryValue::from<uint8_t>(1) },
566 { dst_addr + 1, MemoryValue::from<uint8_t>(3) },
567 { dst_addr + 2, MemoryValue::from<uint8_t>(3) },
568 { dst_addr + 3, MemoryValue::from<uint8_t>(7) },
569 };
570
571 for (uint32_t i = 0; i < memory_values.size(); ++i) {
572 const auto& [addr, value] = memory_values[i];
573 trace.set(i,
574 {
575 { { C::memory_sel, 1 },
576 { C::memory_space_id, 0 },
577 { C::memory_address, addr },
578 { C::memory_value, value.as_ff() },
579 { C::memory_tag, static_cast<uint8_t>(value.get_tag()) },
580 { C::memory_rw, i > 3 ? 1 : 0 } },
581 });
582 }
583
584 EventEmitter<ToRadixEvent> to_radix_event_emitter;
585 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
586
587 PureGreaterThan gt;
588 StrictMock<MockExecutionIdManager> execution_id_manager;
589 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
590
591 // Generate the events for the to_radix subtrace
592 to_radix_simulator.to_le_radix(value, num_limbs, radix);
593
594 ToRadixTraceBuilder builder;
595 auto events = to_radix_event_emitter.get_events();
596 builder.process(to_radix_event_emitter.dump_events(), trace);
597
598 PrecomputedTraceBuilder precomputed_builder;
601 precomputed_builder.process_misc(trace, NUM_RADIXES); // Needed for precomputed safe limbs table
602
603 check_relation<to_radix_mem>(trace);
604 check_all_interactions<ToRadixTraceBuilder>(trace);
605
606 // Negative test: disable memory write after the start row:
607 trace.set(Column::to_radix_mem_sel_should_write_mem, 2, 0);
610
611 // Negative test: disable decomposition after the start row:
612 trace.set(Column::to_radix_mem_sel_should_decompose, 2, 0);
615}
616
617TEST(ToRadixMemoryConstrainingTest, DstOutOfRange)
618{
619 // Values
620 FF value = FF(1337);
621 uint32_t radix = 10;
622 uint32_t num_limbs = 2;
623 auto dst_addr = static_cast<uint64_t>(MAX_MEM - 1); // This will cause an out-of-bounds error
624
625 TestTraceContainer trace = TestTraceContainer({
626 // Row 0
627 {
628 { C::precomputed_first_row, 1 },
629 // GT check
630 { C::gt_sel, 1 },
631 { C::gt_input_a, dst_addr + num_limbs },
632 { C::gt_input_b, MAX_MEM },
633 { C::gt_res, 1 }, // GT should return true
634 },
635 // Row 1
636 {
637 // Execution Trace (No gas)
638 { C::execution_sel, 1 },
639 { C::execution_sel_exec_dispatch_to_radix, 1 },
640 { C::execution_register_0_, value },
641 { C::execution_register_1_, radix },
642 { C::execution_register_2_, num_limbs },
643 { C::execution_register_3_, 0 }, // is_output_bits
644 { C::execution_rop_4_, dst_addr },
645 { C::execution_sel_opcode_error, 1 },
646
647 // To Radix Mem
648 { C::to_radix_mem_sel, 1 },
649 { C::to_radix_mem_max_mem_size, MAX_MEM },
650 { C::to_radix_mem_two, 2 },
651 { C::to_radix_mem_two_five_six, 256 },
652 // Memory Inputs
653 { C::to_radix_mem_execution_clk, 0 },
654 { C::to_radix_mem_space_id, 0 },
655 { C::to_radix_mem_dst_addr, dst_addr },
656 { C::to_radix_mem_write_addr_upper_bound, dst_addr + num_limbs },
657 // To Radix Inputs
658 { C::to_radix_mem_value_to_decompose, value },
659 { C::to_radix_mem_radix, radix },
660 { C::to_radix_mem_num_limbs, num_limbs },
661 { C::to_radix_mem_is_output_bits, 0 },
662 // Errors
663 { C::to_radix_mem_sel_dst_out_of_range_err, 1 },
664 { C::to_radix_mem_input_validation_error, 1 },
665 { C::to_radix_mem_err, 1 },
666 // Control Flow
667 { C::to_radix_mem_start, 1 },
668 { C::to_radix_mem_last, 1 },
669 { C::to_radix_mem_num_limbs_minus_one_inv, num_limbs - 1 == 0 ? 0 : FF(num_limbs - 1).invert() },
670 // Helpers
671 { C::to_radix_mem_sel_num_limbs_is_zero, 0 },
672 { C::to_radix_mem_num_limbs_inv, FF(num_limbs).invert() },
673 { C::to_radix_mem_sel_value_is_zero, 0 },
674 { C::to_radix_mem_value_inv, value.invert() },
675 { C::to_radix_mem_sel_radix_eq_2, 0 },
676 { C::to_radix_mem_radix_min_two_inv, (FF(radix) - FF(2)).invert() },
677 },
678 });
679
680 check_relation<to_radix_mem>(trace);
681 check_interaction<ToRadixTraceBuilder, lookup_to_radix_mem_check_dst_addr_in_range_settings>(trace);
682 check_interaction<ExecutionTraceBuilder, perm_execution_dispatch_to_to_radix_settings>(trace);
683}
684
685TEST(ToRadixMemoryConstrainingTest, InvalidRadix)
686{
687 // Values
688 FF value = FF(1337);
689 uint32_t radix = 0; // Invalid radix
690 uint32_t num_limbs = 2;
691 uint32_t dst_addr = 10;
692
693 TestTraceContainer trace = TestTraceContainer({
694 // Row 0
695 {
696 { C::precomputed_first_row, 1 },
697 // GT check
698 { C::gt_sel, 1 },
699 { C::gt_input_a, 2 },
700 { C::gt_input_b, radix },
701 { C::gt_res, 1 }, // GT should return true
702 },
703 // Row 1
704 {
705 { C::to_radix_mem_sel, 1 },
706 { C::to_radix_mem_max_mem_size, MAX_MEM },
707 { C::to_radix_mem_two, 2 },
708 { C::to_radix_mem_two_five_six, 256 },
709 // Memory Inputs
710 { C::to_radix_mem_execution_clk, 0 },
711 { C::to_radix_mem_space_id, 0 },
712 { C::to_radix_mem_dst_addr, dst_addr },
713 { C::to_radix_mem_write_addr_upper_bound, dst_addr + num_limbs },
714 // To Radix Inputs
715 { C::to_radix_mem_value_to_decompose, value },
716 { C::to_radix_mem_radix, radix },
717 { C::to_radix_mem_num_limbs, num_limbs },
718 { C::to_radix_mem_is_output_bits, 0 },
719 // Errors
720 { C::to_radix_mem_sel_radix_lt_2_err, 1 },
721 { C::to_radix_mem_input_validation_error, 1 },
722 { C::to_radix_mem_err, 1 },
723 // Control Flow
724 { C::to_radix_mem_start, 1 },
725 { C::to_radix_mem_last, 1 },
726 { C::to_radix_mem_num_limbs_minus_one_inv, num_limbs - 1 == 0 ? 0 : FF(num_limbs - 1).invert() },
727 // Helpers
728 { C::to_radix_mem_sel_num_limbs_is_zero, 0 },
729 { C::to_radix_mem_num_limbs_inv, FF(num_limbs).invert() },
730 { C::to_radix_mem_sel_value_is_zero, 0 },
731 { C::to_radix_mem_value_inv, value.invert() },
732 { C::to_radix_mem_sel_radix_eq_2, 0 },
733 { C::to_radix_mem_radix_min_two_inv, (FF(radix) - FF(2)).invert() },
734 },
735 });
736 check_relation<to_radix_mem>(trace);
737 check_interaction<ToRadixTraceBuilder, lookup_to_radix_mem_check_radix_lt_2_settings>(trace);
738}
739
740TEST(ToRadixMemoryConstrainingTest, InvalidBitwiseRadix)
741{
742 // Values
743 FF value = FF(1337);
744 uint32_t radix = 3; // Invalid radix since is_output_bits is true
745 uint32_t num_limbs = 2;
746 uint32_t dst_addr = 10;
747 bool is_output_bits = true;
748
749 TestTraceContainer trace = TestTraceContainer({
750 // Row 0
751 {
752 { C::precomputed_first_row, 1 },
753 // GT check
754 { C::gt_sel, 1 },
755 { C::gt_input_a, 2 },
756 { C::gt_input_b, radix },
757 { C::gt_res, 0 }, // GT should return false
758 },
759 // Row 1
760 {
761 { C::to_radix_mem_sel, 1 },
762 { C::to_radix_mem_max_mem_size, MAX_MEM },
763 { C::to_radix_mem_two, 2 },
764 { C::to_radix_mem_two_five_six, 256 },
765 // Memory Inputs
766 { C::to_radix_mem_execution_clk, 0 },
767 { C::to_radix_mem_space_id, 0 },
768 { C::to_radix_mem_dst_addr, dst_addr },
769 { C::to_radix_mem_write_addr_upper_bound, dst_addr + num_limbs },
770 // To Radix Inputs
771 { C::to_radix_mem_value_to_decompose, value },
772 { C::to_radix_mem_radix, radix },
773 { C::to_radix_mem_num_limbs, num_limbs },
774 { C::to_radix_mem_is_output_bits, is_output_bits ? 1 : 0 },
775 // Errors
776 { C::to_radix_mem_sel_invalid_bitwise_radix, 1 }, // Invalid bitwise radix
777 { C::to_radix_mem_input_validation_error, 1 },
778 { C::to_radix_mem_err, 1 },
779 // Control Flow
780 { C::to_radix_mem_start, 1 },
781 { C::to_radix_mem_last, 1 },
782 { C::to_radix_mem_num_limbs_minus_one_inv, num_limbs - 1 == 0 ? 0 : FF(num_limbs - 1).invert() },
783 // Helpers
784 { C::to_radix_mem_sel_num_limbs_is_zero, 0 },
785 { C::to_radix_mem_num_limbs_inv, FF(num_limbs).invert() },
786 { C::to_radix_mem_sel_value_is_zero, 0 },
787 { C::to_radix_mem_value_inv, value.invert() },
788 { C::to_radix_mem_sel_radix_eq_2, 0 },
789 { C::to_radix_mem_radix_min_two_inv, (FF(radix) - FF(2)).invert() },
790 },
791 });
792 check_relation<to_radix_mem>(trace);
793 check_interaction<ToRadixTraceBuilder, lookup_to_radix_mem_check_radix_lt_2_settings>(trace);
794}
795
796TEST(ToRadixMemoryConstrainingTest, InvalidNumLimbsForValue)
797{
798 // Values
799 FF value = FF(1337);
800 uint32_t radix = 3;
801 uint32_t num_limbs = 0; // num limbs should not be 0 if value != 0
802 uint32_t dst_addr = 10;
803 bool is_output_bits = false;
804
805 TestTraceContainer trace = TestTraceContainer({
806 // Row 0
807 {
808 { C::precomputed_first_row, 1 },
809 // GT check
810 { C::gt_sel, 1 },
811 { C::gt_input_a, 2 },
812 { C::gt_input_b, radix },
813 { C::gt_res, 0 }, // GT should return false
814 },
815 // Row 1
816 {
817 { C::to_radix_mem_sel, 1 },
818 { C::to_radix_mem_max_mem_size, MAX_MEM },
819 { C::to_radix_mem_two, 2 },
820 { C::to_radix_mem_two_five_six, 256 },
821 // Memory Inputs
822 { C::to_radix_mem_execution_clk, 0 },
823 { C::to_radix_mem_space_id, 0 },
824 { C::to_radix_mem_dst_addr, dst_addr },
825 { C::to_radix_mem_write_addr_upper_bound, dst_addr + num_limbs },
826 // To Radix Inputs
827 { C::to_radix_mem_value_to_decompose, value },
828 { C::to_radix_mem_radix, radix },
829 { C::to_radix_mem_num_limbs, num_limbs },
830 { C::to_radix_mem_is_output_bits, is_output_bits ? 1 : 0 },
831 // Errors
832 { C::to_radix_mem_input_validation_error, 1 },
833 { C::to_radix_mem_err, 1 },
834 // Control Flow
835 { C::to_radix_mem_start, 1 },
836 { C::to_radix_mem_last, 1 },
837 { C::to_radix_mem_num_limbs_minus_one_inv, num_limbs - 1 == 0 ? 0 : FF(num_limbs - 1).invert() },
838 // Helpers
839 { C::to_radix_mem_sel_num_limbs_is_zero, 1 }, // num limbs is zero
840 { C::to_radix_mem_num_limbs_inv, 0 },
841 { C::to_radix_mem_sel_value_is_zero, 0 },
842 { C::to_radix_mem_value_inv, value.invert() },
843 { C::to_radix_mem_sel_radix_eq_2, 0 },
844 { C::to_radix_mem_radix_min_two_inv, (FF(radix) - FF(2)).invert() },
845 },
846 });
847 check_relation<to_radix_mem>(trace);
848 check_interaction<ToRadixTraceBuilder, lookup_to_radix_mem_check_radix_lt_2_settings>(trace);
849}
850
851TEST(ToRadixMemoryConstrainingTest, TruncationError)
852{
853 // Values
854 FF value = FF(1337);
855 uint32_t radix = 10;
856 uint32_t num_limbs = 3;
857 uint32_t dst_addr = 10;
858 bool is_output_bits = false;
859
860 TestTraceContainer trace = TestTraceContainer({
861 // Row 0
862 {
863 { C::precomputed_first_row, 1 },
864 // GT check
865 { C::gt_sel, 1 },
866 { C::gt_input_a, 2 },
867 { C::gt_input_b, radix },
868 { C::gt_res, 0 }, // GT should return false
869 },
870 // Row 1
871 {
872 { C::to_radix_mem_sel, 1 },
873 { C::to_radix_mem_max_mem_size, MAX_MEM },
874 { C::to_radix_mem_two, 2 },
875 { C::to_radix_mem_two_five_six, 256 },
876 // Memory Inputs
877 { C::to_radix_mem_execution_clk, 0 },
878 { C::to_radix_mem_space_id, 0 },
879 { C::to_radix_mem_dst_addr, dst_addr },
880 { C::to_radix_mem_write_addr_upper_bound, dst_addr + num_limbs },
881 // To Radix Inputs
882 { C::to_radix_mem_value_to_decompose, value },
883 { C::to_radix_mem_radix, radix },
884 { C::to_radix_mem_num_limbs, num_limbs },
885 { C::to_radix_mem_is_output_bits, is_output_bits ? 1 : 0 },
886 // Errors
887 { C::to_radix_mem_err, 1 },
888 // Control Flow
889 { C::to_radix_mem_start, 1 },
890 { C::to_radix_mem_last, 1 },
891 { C::to_radix_mem_num_limbs_minus_one_inv, num_limbs - 1 == 0 ? 0 : FF(num_limbs - 1).invert() },
892 // Decomposition
893 { C::to_radix_mem_sel_should_decompose, 1 },
894 { C::to_radix_mem_limb_index_to_lookup, num_limbs - 1 },
895 { C::to_radix_mem_limb_value, 3 },
896 { C::to_radix_mem_value_found, 0 },
897 // Helpers
898 { C::to_radix_mem_num_limbs_inv, FF(num_limbs).invert() },
899 { C::to_radix_mem_sel_value_is_zero, 0 },
900 { C::to_radix_mem_value_inv, value.invert() },
901 { C::to_radix_mem_sel_radix_eq_2, 0 },
902 { C::to_radix_mem_radix_min_two_inv, (FF(radix) - FF(2)).invert() },
903 },
904 });
905 check_relation<to_radix_mem>(trace);
906 check_interaction<ToRadixTraceBuilder, lookup_to_radix_mem_check_radix_lt_2_settings>(trace);
907
908 // Negative test: truncation error should be on if found = false on the start row
909 trace.set(C::to_radix_mem_err, 1, 0);
910 EXPECT_THROW_WITH_MESSAGE(check_relation<to_radix_mem>(trace, to_radix_mem::SR_ERR_COMPUTATION),
912 trace.set(C::to_radix_mem_err, 1, 1);
913
914 // Negative test: truncation error can't be on if found = true on the start row
915 trace.set(C::to_radix_mem_value_found, 1, 1);
916 EXPECT_THROW_WITH_MESSAGE(check_relation<to_radix_mem>(trace, to_radix_mem::SR_ERR_COMPUTATION),
918}
919
920TEST(ToRadixMemoryConstrainingTest, ZeroNumLimbsAndZeroValueIsNoop)
921{
922 // Values
923 FF value = FF(0);
924 uint32_t radix = 3;
925 uint32_t num_limbs = 0; // num limbs can be zero since value is zero
926 uint32_t dst_addr = 10;
927 bool is_output_bits = false;
928
929 TestTraceContainer trace = TestTraceContainer({
930 // Row 0
931 {
932 { C::precomputed_first_row, 1 },
933 // GT check
934 { C::gt_sel, 1 },
935 { C::gt_input_a, 2 },
936 { C::gt_input_b, radix },
937 { C::gt_res, 0 }, // GT should return false
938 },
939 // Row 1
940 {
941 { C::to_radix_mem_sel, 1 },
942 { C::to_radix_mem_max_mem_size, MAX_MEM },
943 { C::to_radix_mem_two, 2 },
944 { C::to_radix_mem_two_five_six, 256 },
945 // Memory Inputs
946 { C::to_radix_mem_execution_clk, 0 },
947 { C::to_radix_mem_space_id, 0 },
948 { C::to_radix_mem_dst_addr, dst_addr },
949 { C::to_radix_mem_write_addr_upper_bound, dst_addr + num_limbs },
950 // To Radix Inputs
951 { C::to_radix_mem_value_to_decompose, value },
952 { C::to_radix_mem_radix, radix },
953 { C::to_radix_mem_num_limbs, num_limbs },
954 { C::to_radix_mem_is_output_bits, is_output_bits ? 1 : 0 },
955 // Control Flow
956 { C::to_radix_mem_start, 1 },
957 { C::to_radix_mem_last, 1 },
958 { C::to_radix_mem_num_limbs_minus_one_inv, num_limbs - 1 == 0 ? 0 : FF(num_limbs - 1).invert() },
959 // Helpers
960 { C::to_radix_mem_sel_num_limbs_is_zero, 1 }, // num limbs is zero
961 { C::to_radix_mem_num_limbs_inv, 0 },
962 { C::to_radix_mem_sel_value_is_zero, 1 },
963 { C::to_radix_mem_value_inv, 0 },
964 { C::to_radix_mem_sel_radix_eq_2, 0 },
965 { C::to_radix_mem_radix_min_two_inv, (FF(radix) - FF(2)).invert() },
966 },
967 });
968 check_relation<to_radix_mem>(trace);
969 check_interaction<ToRadixTraceBuilder, lookup_to_radix_mem_check_radix_lt_2_settings>(trace);
970}
971
972TEST(ToRadixMemoryConstrainingTest, ComplexTest)
973{
974 EventEmitter<ToRadixEvent> to_radix_event_emitter;
975 EventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
976 EventEmitter<RangeCheckEvent> range_check_emitter;
977 EventEmitter<GreaterThanEvent> gt_emitter;
978
979 simulation::MemoryStore memory;
980 StrictMock<MockExecutionIdManager> execution_id_manager;
981 StrictMock<MockFieldGreaterThan> field_gt;
983 GreaterThan gt(field_gt, range_check, gt_emitter);
984 EXPECT_CALL(execution_id_manager, get_execution_id()).WillOnce(Return(0)).WillOnce(Return(1));
985 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
986
987 FF value = FF::neg_one();
988 uint32_t radix = 2;
989 uint32_t num_limbs = 256;
991 bool is_output_bits = true;
992 // Two calls to test transitions between contiguous chunks of computation
993 to_radix_simulator.to_be_radix(memory, value, radix, num_limbs, is_output_bits, dst_addr);
994 to_radix_simulator.to_be_radix(
995 memory, /*value=*/FF(1337), /*radix=*/10, /*num_limbs=*/6, /*is_output_bits=*/false, /*dst_addr=*/0xdeadbeef);
996
997 TestTraceContainer trace;
998 ToRadixTraceBuilder builder;
999 builder.process(to_radix_event_emitter.dump_events(), trace);
1000 builder.process_with_memory(to_radix_mem_event_emitter.dump_events(), trace);
1001
1002 GreaterThanTraceBuilder gt_builder;
1003 gt_builder.process(gt_emitter.dump_events(), trace);
1004
1005 PrecomputedTraceBuilder precomputed_builder;
1008 precomputed_builder.process_misc(trace, NUM_RADIXES); // Needed for precomputed safe limbs table
1009
1010 check_relation<to_radix>(trace);
1011 check_relation<to_radix_mem>(trace);
1012 // Skip the memory writes
1013 check_interaction<ToRadixTraceBuilder,
1024}
1025
1026// =====================================================================
1027// Ghost Row Injection Vulnerability Tests
1028// =====================================================================
1029// These tests verify that ghost rows (sel=0) cannot fire permutations.
1030// The fix: sel_should_write_mem * (1 - sel) = 0 ensures sel_should_write_mem
1031// is forced to 0 when sel=0, preventing ghost rows from firing permutations.
1032
1033// Test that ghost rows (sel=0) cannot set sel_should_write_mem=1
1034TEST(ToRadixMemoryConstrainingTest, NegativeGhostRowMemoryWrite_RelationsOnly)
1035{
1036 // Try to create a ghost row (sel=0) with sel_should_write_mem=1
1037 // which would fire the #[WRITE_MEM] permutation
1038 TestTraceContainer trace({
1039 {
1040 { C::precomputed_first_row, 1 },
1041 },
1042 {
1043 { C::to_radix_mem_sel, 0 }, // Ghost row: gadget not active
1044 { C::to_radix_mem_sel_should_write_mem, 1 }, // Try to fire memory write anyway
1045 { C::to_radix_mem_execution_clk, 1 },
1046 { C::to_radix_mem_space_id, 1 },
1047 { C::to_radix_mem_dst_addr, 100 },
1048 { C::to_radix_mem_limb_value, 999 }, // Arbitrary limb value
1049 { C::to_radix_mem_output_tag, 2 }, // U8 tag
1050 },
1051 });
1052
1053 // The fix: sel_should_write_mem * (1 - sel) = 0
1054 // When sel=0 and sel_should_write_mem=1: 1 * (1-0) = 1 != 0 -> FAILS
1057}
1058
1059// Test that the fix blocks ghost row injection attacks with full traces.
1060// Attack pattern:
1061// 1. Create legitimate memory WRITE events (destination side)
1062// 2. Build memory trace from those events
1063// 3. Inject ghost to_radix_mem row with sel=0 but sel_should_write_mem=1
1064// 4. The fix should cause the relation check to fail
1065TEST(ToRadixMemoryConstrainingTest, NegativeGhostRowInjectionBlocked)
1066{
1067 TestTraceContainer trace;
1068 MemoryTraceBuilder memory_trace_builder;
1069 PrecomputedTraceBuilder precomputed_trace_builder;
1070
1071 // Attacker-controlled values
1072 uint32_t malicious_clk = 42;
1073 uint16_t malicious_space_id = 1;
1074 MemoryAddress malicious_addr = 0xDEAD;
1075 uint8_t malicious_limb_value = 0x99;
1076 MemoryTag malicious_tag = MemoryTag::U8;
1077
1078 // Create legitimate memory events
1080 {
1081 .execution_clk = malicious_clk,
1083 .addr = malicious_addr,
1084 .value = MemoryValue::from_tag(malicious_tag, malicious_limb_value),
1085 .space_id = malicious_space_id,
1086 },
1087 };
1088
1089 // Build memory trace (destination side)
1090 precomputed_trace_builder.process_sel_range_8(trace);
1091 precomputed_trace_builder.process_sel_range_16(trace);
1092 precomputed_trace_builder.process_misc(trace, 1 << 16);
1093 precomputed_trace_builder.process_tag_parameters(trace);
1094 memory_trace_builder.process(mem_events, trace);
1095
1096 // Find where the memory row was placed
1097 uint32_t memory_row = 0;
1098 for (uint32_t row = 0; row < trace.get_num_rows(); row++) {
1099 if (trace.get(C::memory_sel, row) == 1) {
1100 memory_row = row;
1101 break;
1102 }
1103 }
1104
1105 // Inject ghost to_radix_mem row
1106 // Ghost row: sel = 0, but sel_should_write_mem = 1 (attack attempt)
1107 uint32_t ghost_row = 0;
1108 trace.set(ghost_row,
1109 std::vector<std::pair<Column, FF>>{
1110 { C::precomputed_first_row, 1 },
1111 { C::execution_clk, ghost_row },
1112 { C::to_radix_mem_sel, 0 },
1113 { C::to_radix_mem_sel_should_write_mem, 1 },
1114 { C::to_radix_mem_execution_clk, malicious_clk },
1115 { C::to_radix_mem_space_id, malicious_space_id },
1116 { C::to_radix_mem_dst_addr, malicious_addr },
1117 { C::to_radix_mem_limb_value, malicious_limb_value },
1118 { C::to_radix_mem_output_tag, static_cast<uint8_t>(malicious_tag) },
1119 });
1120
1121 trace.set(C::memory_sel_to_radix_write, memory_row, 1);
1122
1123 // The fix: sel_should_write_mem * (1 - sel) = 0 should cause the relation check to fail
1124 EXPECT_THROW_WITH_MESSAGE(check_relation<to_radix_mem>(trace),
1126}
1127
1128// We test that the bitwise radix error must not be raised when is_output_bits is true and radix is 2
1129TEST(ToRadixMemoryConstrainingTest, NegativeBitwiseRadixError)
1130{
1131 TestTraceContainer trace({
1132 {
1133 { C::to_radix_mem_start, 1 },
1134 { C::to_radix_mem_sel, 1 },
1135 { C::to_radix_mem_sel_radix_eq_2, 1 },
1136 { C::to_radix_mem_is_output_bits, 1 },
1137 },
1138 });
1139
1140 check_relation<to_radix_mem>(trace, to_radix_mem::SR_IS_OUTPUT_BITS_IMPLY_RADIX_2);
1141
1142 // Activate maliciously the sel_invalid_bitwise_radix flag
1143 trace.set(C::to_radix_mem_sel_invalid_bitwise_radix, 0, 1);
1144
1147}
1148
1149// We test that the bitwise radix error must be raised when is_output_bits is true and radix is not 2
1150TEST(ToRadixMemoryConstrainingTest, NegativeBitwiseRadixNoError)
1151{
1152 TestTraceContainer trace({
1153 {
1154 { C::to_radix_mem_start, 1 },
1155 { C::to_radix_mem_sel, 1 },
1156 { C::to_radix_mem_sel_radix_eq_2, 0 },
1157 { C::to_radix_mem_is_output_bits, 1 },
1158 { C::to_radix_mem_sel_invalid_bitwise_radix, 1 },
1159 },
1160 });
1161
1162 check_relation<to_radix_mem>(trace, to_radix_mem::SR_IS_OUTPUT_BITS_IMPLY_RADIX_2);
1163
1164 // De-activate maliciously the sel_invalid_bitwise_radix flag
1165 trace.set(C::to_radix_mem_sel_invalid_bitwise_radix, 0, 0);
1166
1169}
1170
1171} // namespace
1172
1173} // namespace bb::avm2::constraining
DeduplicatingEventEmitter< GreaterThanEvent > gt_emitter
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:224
FieldGreaterThan field_gt
EventEmitter< simulation::RangeCheckEvent > range_check_emitter
RangeCheck range_check
static TaggedValue from_tag(ValueTag tag, FF value)
static constexpr size_t SR_SEL_SHOULD_WRITE_MEM_CONTINUITY
static constexpr size_t SR_SEL_SHOULD_DECOMPOSE_CONTINUITY
static constexpr size_t SR_ERR_COMPUTATION
static constexpr size_t SR_SEL_SHOULD_WRITE_MEM_REQUIRES_SEL
static std::string get_subrelation_label(size_t index)
static constexpr size_t SR_IS_OUTPUT_BITS_IMPLY_RADIX_2
static constexpr size_t SR_TRACE_CONTINUITY
Definition to_radix.hpp:40
static constexpr size_t SR_RADIX_CONTINUITY
Definition to_radix.hpp:43
static constexpr size_t SR_VALUE_CONTINUITY
Definition to_radix.hpp:44
static constexpr size_t SR_OVERFLOW_CHECK
Definition to_radix.hpp:42
static std::string get_subrelation_label(size_t index)
Definition to_radix.hpp:47
static constexpr size_t SR_SAFE_LIMBS_CONTINUITY
Definition to_radix.hpp:45
void process(const simulation::EventEmitterInterface< simulation::AluEvent >::Container &events, TraceContainer &trace)
Process the ALU events and populate the ALU relevant columns in the trace.
void process(const simulation::EventEmitterInterface< simulation::GreaterThanEvent >::Container &events, TraceContainer &trace)
Process the greater-than events and populate the relevant columns in the trace.
Definition gt_trace.cpp:20
void process_to_radix_p_decompositions(TraceContainer &trace)
Populate the TORADIXBE p-decomposition table.
void process_misc(TraceContainer &trace, const uint32_t num_rows=PRECOMPUTED_TRACE_SIZE)
Populate miscellaneous precomputed columns: first_row selector and idx (row index).
void process_to_radix_safe_limbs(TraceContainer &trace)
Populate the TORADIXBE safe-limbs table (one row per radix 0..255).
void process_sel_range_8(TraceContainer &trace)
Generate a selector column that activates the first 2^8 (256) rows.
const FF & get(Column col, uint32_t row) const
void set(Column col, uint32_t row, const FF &value, bool use_atomic_limbs=false)
constexpr bool get_bit(uint64_t bit_index) const
PrecomputedTraceBuilder precomputed_builder
Definition alu.test.cpp:120
AluTraceBuilder builder
Definition alu.test.cpp:124
GreaterThanTraceBuilder gt_builder
Definition alu.test.cpp:123
ExecutionIdManager execution_id_manager
uint32_t dst_addr
GreaterThan gt
TestTraceContainer trace
void check_interaction(tracegen::TestTraceContainer &trace)
TEST(AvmFixedVKTests, FixedVKCommitments)
Test that the fixed VK commitments agree with the ones computed from precomputed columns.
TestTraceContainer empty_trace()
Definition fixtures.cpp:156
lookup_settings< lookup_to_radix_limb_less_than_radix_range_settings_ > lookup_to_radix_limb_less_than_radix_range_settings
lookup_settings< lookup_to_radix_limb_p_diff_range_settings_ > lookup_to_radix_limb_p_diff_range_settings
lookup_settings< lookup_to_radix_mem_check_dst_addr_in_range_settings_ > lookup_to_radix_mem_check_dst_addr_in_range_settings
lookup_settings< lookup_to_radix_mem_check_radix_lt_2_settings_ > lookup_to_radix_mem_check_radix_lt_2_settings
lookup_settings< lookup_to_radix_limb_range_settings_ > lookup_to_radix_limb_range_settings
lookup_settings< lookup_to_radix_mem_check_radix_gt_256_settings_ > lookup_to_radix_mem_check_radix_gt_256_settings
AvmFlavorSettings::FF FF
Definition field.hpp:10
lookup_settings< lookup_to_radix_fetch_safe_limbs_settings_ > lookup_to_radix_fetch_safe_limbs_settings
constexpr size_t NUM_RADIXES
Definition to_radix.hpp:11
lookup_settings< lookup_to_radix_mem_input_output_to_radix_settings_ > lookup_to_radix_mem_input_output_to_radix_settings
uint32_t MemoryAddress
lookup_settings< lookup_to_radix_fetch_p_limb_settings_ > lookup_to_radix_fetch_p_limb_settings
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
MemoryStore memory
static constexpr uint256_t modulus
constexpr field invert() const noexcept
BB_INLINE std::vector< uint8_t > to_buffer() const