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