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 <gmock/gmock.h>
2#include <gtest/gtest.h>
3
23
24namespace bb::avm2::constraining {
25namespace {
26
27using ::testing::Return;
28using ::testing::StrictMock;
29
30using tracegen::GreaterThanTraceBuilder;
31using tracegen::PrecomputedTraceBuilder;
32using tracegen::TestTraceContainer;
33using tracegen::ToRadixTraceBuilder;
34
36using C = Column;
37using to_radix = bb::avm2::to_radix<FF>;
38using to_radix_mem = bb::avm2::to_radix_mem<FF>;
39using ToRadixSimulator = simulation::ToRadix;
40
41using simulation::EventEmitter;
42using simulation::FakeGreaterThan;
43using simulation::GreaterThan;
44using simulation::GreaterThanEvent;
45using simulation::MockExecutionIdManager;
46using simulation::MockFieldGreaterThan;
47using simulation::NoopEventEmitter;
48using simulation::RangeCheck;
49using simulation::RangeCheckEvent;
50using simulation::ToRadixEvent;
51using simulation::ToRadixMemoryEvent;
52
53TEST(ToRadixConstrainingTest, EmptyRow)
54{
55 check_relation<to_radix>(testing::empty_trace());
56}
57
58TEST(ToRadixConstrainingTest, ToLeBitsBasicTest)
59{
60 EventEmitter<ToRadixEvent> to_radix_event_emitter;
61 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
62
63 FakeGreaterThan gt;
64 StrictMock<MockExecutionIdManager> execution_id_manager;
65 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
66
67 auto bits = to_radix_simulator.to_le_bits(FF::one(), 254);
68
69 EXPECT_EQ(bits.size(), 254);
70
71 TestTraceContainer trace = TestTraceContainer::from_rows({
72 { .precomputed_first_row = 1 },
73 });
74
75 ToRadixTraceBuilder builder;
76 builder.process(to_radix_event_emitter.dump_events(), trace);
77 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 254);
78 check_relation<to_radix>(trace);
79}
80
81TEST(ToRadixConstrainingTest, ToLeBitsPMinusOne)
82{
83 EventEmitter<ToRadixEvent> to_radix_event_emitter;
84 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
85
86 FakeGreaterThan gt;
87 StrictMock<MockExecutionIdManager> execution_id_manager;
88 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
89
90 auto bits = to_radix_simulator.to_le_bits(FF::neg_one(), 254);
91
92 EXPECT_EQ(bits.size(), 254);
93
94 TestTraceContainer trace = TestTraceContainer::from_rows({
95 { .precomputed_first_row = 1 },
96 });
97
98 ToRadixTraceBuilder builder;
99 builder.process(to_radix_event_emitter.dump_events(), trace);
100 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 254);
101 check_relation<to_radix>(trace);
102}
103
104TEST(ToRadixConstrainingTest, ToLeBitsShortest)
105{
106 EventEmitter<ToRadixEvent> to_radix_event_emitter;
107 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
108
109 FakeGreaterThan gt;
110 StrictMock<MockExecutionIdManager> execution_id_manager;
111 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
112
113 auto bits = to_radix_simulator.to_le_bits(FF::one(), 1);
114
115 EXPECT_EQ(bits.size(), 1);
116
117 TestTraceContainer trace = TestTraceContainer::from_rows({
118 { .precomputed_first_row = 1 },
119 });
120
121 ToRadixTraceBuilder builder;
122 builder.process(to_radix_event_emitter.dump_events(), trace);
123 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 1);
124 check_relation<to_radix>(trace);
125}
126
127TEST(ToRadixConstrainingTest, ToLeBitsPadded)
128{
129 EventEmitter<ToRadixEvent> to_radix_event_emitter;
130 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
131
132 FakeGreaterThan gt;
133 StrictMock<MockExecutionIdManager> execution_id_manager;
134 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
135
136 auto bits = to_radix_simulator.to_le_bits(FF::one(), 500);
137
138 EXPECT_EQ(bits.size(), 500);
139
140 TestTraceContainer trace = TestTraceContainer::from_rows({
141 { .precomputed_first_row = 1 },
142 });
143
144 ToRadixTraceBuilder builder;
145 builder.process(to_radix_event_emitter.dump_events(), trace);
146 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 500);
147 check_relation<to_radix>(trace);
148}
149
150TEST(ToRadixConstrainingTest, ToLeRadixBasic)
151{
152 EventEmitter<ToRadixEvent> to_radix_event_emitter;
153 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
154
155 FakeGreaterThan gt;
156 StrictMock<MockExecutionIdManager> execution_id_manager;
157 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
158
159 FF value = FF::one();
160 auto bytes = to_radix_simulator.to_le_radix(value, 32, 256);
161
162 auto expected_bytes = value.to_buffer();
163 // to_buffer is BE
164 std::reverse(expected_bytes.begin(), expected_bytes.end());
165 EXPECT_EQ(bytes, expected_bytes);
166
167 TestTraceContainer trace = TestTraceContainer::from_rows({
168 { .precomputed_first_row = 1 },
169 });
170
171 ToRadixTraceBuilder builder;
172 builder.process(to_radix_event_emitter.dump_events(), trace);
173 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 32);
174 check_relation<to_radix>(trace);
175}
176
177TEST(ToRadixConstrainingTest, ToLeRadixPMinusOne)
178{
179 EventEmitter<ToRadixEvent> to_radix_event_emitter;
180 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
181
182 FakeGreaterThan gt;
183 StrictMock<MockExecutionIdManager> execution_id_manager;
184 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
185
186 FF value = FF::neg_one();
187 auto bytes = to_radix_simulator.to_le_radix(value, 32, 256);
188
189 auto expected_bytes = value.to_buffer();
190 // to_buffer is BE
191 std::reverse(expected_bytes.begin(), expected_bytes.end());
192 EXPECT_EQ(bytes, expected_bytes);
193
194 TestTraceContainer trace = TestTraceContainer::from_rows({
195 { .precomputed_first_row = 1 },
196 });
197
198 ToRadixTraceBuilder builder;
199 builder.process(to_radix_event_emitter.dump_events(), trace);
200 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 32);
201 check_relation<to_radix>(trace);
202}
203
204TEST(ToRadixConstrainingTest, ToLeRadixOneByte)
205{
206 EventEmitter<ToRadixEvent> to_radix_event_emitter;
207 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
208
209 FakeGreaterThan gt;
210 StrictMock<MockExecutionIdManager> execution_id_manager;
211 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
212
213 auto bytes = to_radix_simulator.to_le_radix(FF::one(), 1, 256);
214
215 std::vector<uint8_t> expected_bytes = { 1 };
216 EXPECT_EQ(bytes, expected_bytes);
217
218 TestTraceContainer trace = TestTraceContainer::from_rows({
219 { .precomputed_first_row = 1 },
220 });
221
222 ToRadixTraceBuilder builder;
223 builder.process(to_radix_event_emitter.dump_events(), trace);
224 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 1);
225 check_relation<to_radix>(trace);
226}
227
228TEST(ToRadixConstrainingTest, ToLeRadixPadded)
229{
230 EventEmitter<ToRadixEvent> to_radix_event_emitter;
231 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
232
233 FakeGreaterThan gt;
234 StrictMock<MockExecutionIdManager> execution_id_manager;
235 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
236
237 FF value = FF::neg_one();
238 auto bytes = to_radix_simulator.to_le_radix(value, 64, 256);
239
240 auto expected_bytes = value.to_buffer();
241 // to_buffer is BE
242 std::reverse(expected_bytes.begin(), expected_bytes.end());
243 expected_bytes.resize(64);
244 EXPECT_EQ(bytes, expected_bytes);
245
246 TestTraceContainer trace = TestTraceContainer::from_rows({
247 { .precomputed_first_row = 1 },
248 });
249
250 ToRadixTraceBuilder builder;
251 builder.process(to_radix_event_emitter.dump_events(), trace);
252 EXPECT_EQ(trace.get_num_rows(), /*start_row=*/1 + 64);
253 check_relation<to_radix>(trace);
254}
255
256TEST(ToRadixConstrainingTest, ToLeBitsInteractions)
257{
258 EventEmitter<ToRadixEvent> to_radix_event_emitter;
259 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
260
261 FakeGreaterThan gt;
262 StrictMock<MockExecutionIdManager> execution_id_manager;
263 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
264
265 to_radix_simulator.to_le_bits(FF::neg_one(), 254);
266
267 TestTraceContainer trace = TestTraceContainer::from_rows({
268 { .precomputed_first_row = 1 },
269 });
270
271 ToRadixTraceBuilder to_radix_builder;
272 to_radix_builder.process(to_radix_event_emitter.dump_events(), trace);
273 tracegen::PrecomputedTraceBuilder precomputed_builder;
274 precomputed_builder.process_misc(trace, 257);
275 precomputed_builder.process_sel_range_8(trace);
276 precomputed_builder.process_to_radix_safe_limbs(trace);
277 precomputed_builder.process_to_radix_p_decompositions(trace);
278
279 check_interaction<ToRadixTraceBuilder,
285
286 check_relation<to_radix>(trace);
287}
288
289TEST(ToRadixConstrainingTest, ToLeRadixInteractions)
290{
291 EventEmitter<ToRadixEvent> to_radix_event_emitter;
292 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
293
294 FakeGreaterThan gt;
295 StrictMock<MockExecutionIdManager> execution_id_manager;
296 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
297
298 to_radix_simulator.to_le_radix(FF::neg_one(), 32, 256);
299
300 TestTraceContainer trace = TestTraceContainer::from_rows({
301 { .precomputed_first_row = 1 },
302 });
303
304 ToRadixTraceBuilder to_radix_builder;
305 to_radix_builder.process(to_radix_event_emitter.dump_events(), trace);
306 tracegen::PrecomputedTraceBuilder precomputed_builder;
307
308 precomputed_builder.process_misc(trace, 257);
309 precomputed_builder.process_sel_range_8(trace);
310 precomputed_builder.process_to_radix_safe_limbs(trace);
311 precomputed_builder.process_to_radix_p_decompositions(trace);
312
313 check_interaction<ToRadixTraceBuilder,
319
320 check_relation<to_radix>(trace);
321}
322
323TEST(ToRadixConstrainingTest, NegativeOverflowCheck)
324{
325 TestTraceContainer trace = TestTraceContainer::from_rows({
326 { .precomputed_first_row = 1 },
327 });
328
329 std::vector<uint8_t> modulus_le_bits(256, 0);
330 for (size_t i = 0; i < 256; i++) {
331 modulus_le_bits[i] = static_cast<uint8_t>(FF::modulus.get_bit(i));
332 }
333
334 ToRadixEvent event = { .value = FF::zero(), .radix = 2, .limbs = modulus_le_bits };
335 std::vector<ToRadixEvent> events = { event };
336
337 ToRadixTraceBuilder builder;
338 builder.process(events, trace);
339
340 EXPECT_THROW_WITH_MESSAGE(check_relation<to_radix>(trace, to_radix::SR_OVERFLOW_CHECK), "OVERFLOW_CHECK");
341}
342
343TEST(ToRadixConstrainingTest, NegativeConsistency)
344{
345 EventEmitter<ToRadixEvent> to_radix_event_emitter;
346 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
347
348 FakeGreaterThan gt;
349 StrictMock<MockExecutionIdManager> execution_id_manager;
350 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
351
352 to_radix_simulator.to_le_radix(FF(256), 32, 256);
353
354 TestTraceContainer trace = TestTraceContainer::from_rows({
355 { .precomputed_first_row = 1 },
356 });
357
358 ToRadixTraceBuilder builder;
359 builder.process(to_radix_event_emitter.dump_events(), trace);
360
361 // Disable the selector in the middle
362 trace.set(Column::to_radix_sel, 6, 0);
363
365 "SELECTOR_CONSISTENCY");
366
367 // Mutate the radix
368 trace.set(Column::to_radix_radix, 5, 200);
369
371 "CONSTANT_CONSISTENCY_RADIX");
372
373 // Mutate the value
374 trace.set(Column::to_radix_value, 4, 27);
375
377 "CONSTANT_CONSISTENCY_VALUE");
378
379 // Mutate the safe_limbs
380 trace.set(Column::to_radix_safe_limbs, 3, 200);
381
383 "CONSTANT_CONSISTENCY_SAFE_LIMBS");
384}
385
387// ToRadix Memory Tests
389
390TEST(ToRadixMemoryConstrainingTest, EmptyRow)
391{
392 check_relation<to_radix_mem>(testing::empty_trace());
393}
394
395TEST(ToRadixMemoryConstrainingTest, BasicTest)
396{
397 // Values
398 FF value = FF(1337);
399 uint32_t radix = 10;
400 uint32_t num_limbs = 4;
401 uint32_t dst_addr = 10;
402
403 TestTraceContainer trace = TestTraceContainer({
404 // Row 0
405 {
406 { C::precomputed_first_row, 1 },
407 // GT check - Dst > AVM_HIGHEST_MEM_ADDRESS = false
408 { C::gt_sel, 1 },
409 { C::gt_input_a, dst_addr + num_limbs - 1 },
410 { C::gt_input_b, AVM_HIGHEST_MEM_ADDRESS },
411 { C::gt_res, 0 }, // GT should return true
412 // Execution Trace (No gas)
413 { C::execution_sel, 1 },
414 { C::execution_sel_execute_to_radix, 1 },
415 { C::execution_register_0_, value },
416 { C::execution_register_1_, radix },
417 { C::execution_register_2_, num_limbs },
418 { C::execution_register_3_, 0 }, // is_output_bits
419 { C::execution_rop_4_, dst_addr },
420
421 },
422 // Row 1
423 {
424 // To Radix Mem
425 { C::to_radix_mem_sel, 1 },
426 { C::to_radix_mem_max_mem_addr, AVM_HIGHEST_MEM_ADDRESS },
427 { C::to_radix_mem_two, 2 },
428 { C::to_radix_mem_two_five_six, 256 },
429 // Memory Inputs
430 { C::to_radix_mem_execution_clk, 0 },
431 { C::to_radix_mem_space_id, 0 },
432 { C::to_radix_mem_dst_addr, dst_addr },
433 { C::to_radix_mem_max_write_addr, dst_addr + num_limbs - 1 },
434 // To Radix Inputs
435 { C::to_radix_mem_value_to_decompose, value },
436 { C::to_radix_mem_radix, radix },
437 { C::to_radix_mem_num_limbs, num_limbs },
438 { C::to_radix_mem_is_output_bits, 0 },
439 // Control Flow
440 { C::to_radix_mem_start, 1 },
441 { C::to_radix_mem_num_limbs_minus_one_inv, num_limbs - 1 == 0 ? 0 : FF(num_limbs - 1).invert() },
442 // Helpers
443 { C::to_radix_mem_sel_num_limbs_is_zero, 0 },
444 { C::to_radix_mem_num_limbs_inv, FF(num_limbs).invert() },
445 { C::to_radix_mem_sel_value_is_zero, 0 },
446 { C::to_radix_mem_value_inv, value.invert() },
447 // Output
448 { C::to_radix_mem_output_limb_value, 1 },
449 { C::to_radix_mem_sel_should_exec, 1 },
450 { C::to_radix_mem_limb_index_to_lookup, num_limbs - 1 },
451 { C::to_radix_mem_output_tag, static_cast<uint8_t>(MemoryTag::U8) },
452
453 // GT check - 2 > radix = false
454 { C::gt_sel, 1 },
455 { C::gt_input_a, 2 },
456 { C::gt_input_b, radix },
457 { C::gt_res, 0 }, // GT should return false
458 },
459 // Row 2
460 {
461 { C::to_radix_mem_sel, 1 },
462 // Memory Inputs
463 { C::to_radix_mem_execution_clk, 0 },
464 { C::to_radix_mem_space_id, 0 },
465 { C::to_radix_mem_dst_addr, dst_addr + 1 },
466 // To Radix Inputs
467 { C::to_radix_mem_value_to_decompose, value },
468 { C::to_radix_mem_radix, radix },
469 { C::to_radix_mem_num_limbs, num_limbs - 1 },
470 { C::to_radix_mem_is_output_bits, 0 },
471 // Control Flow
472 // num_limbs_minus_one = (num_limbs - 1) - 1)
473 { C::to_radix_mem_num_limbs_minus_one_inv, FF(num_limbs - 2).invert() },
474 // Output
475 { C::to_radix_mem_output_limb_value, 3 },
476 { C::to_radix_mem_sel_should_exec, 1 },
477 { C::to_radix_mem_limb_index_to_lookup, num_limbs - 2 },
478 { C::to_radix_mem_output_tag, static_cast<uint8_t>(MemoryTag::U8) },
479 // GT check - Radix > 256 = false
480 { C::gt_sel, 1 },
481 { C::gt_input_a, radix },
482 { C::gt_input_b, 256 },
483 { C::gt_res, 0 }, // GT should return false
484 },
485 // Row 3
486 {
487 { C::to_radix_mem_sel, 1 },
488 // Memory Inputs
489 { C::to_radix_mem_execution_clk, 0 },
490 { C::to_radix_mem_space_id, 0 },
491 { C::to_radix_mem_dst_addr, dst_addr + 2 },
492 // To Radix Inputs
493 { C::to_radix_mem_value_to_decompose, value },
494 { C::to_radix_mem_radix, radix },
495 { C::to_radix_mem_num_limbs, num_limbs - 2 },
496 { C::to_radix_mem_is_output_bits, 0 },
497 // Control Flow
498 // num_limbs_minus_one = (num_limbs - 2) - 1)
499 { C::to_radix_mem_num_limbs_minus_one_inv, FF(num_limbs - 3).invert() },
500 // Output
501 { C::to_radix_mem_output_limb_value, 3 },
502 { C::to_radix_mem_sel_should_exec, 1 },
503 { C::to_radix_mem_limb_index_to_lookup, num_limbs - 3 },
504 { C::to_radix_mem_output_tag, static_cast<uint8_t>(MemoryTag::U8) },
505 },
506 // Row 4
507 {
508 { C::to_radix_mem_sel, 1 },
509 // Memory Inputs
510 { C::to_radix_mem_execution_clk, 0 },
511 { C::to_radix_mem_space_id, 0 },
512 { C::to_radix_mem_dst_addr, 13 },
513 // To Radix Inputs
514 { C::to_radix_mem_value_to_decompose, value },
515 { C::to_radix_mem_radix, radix },
516 { C::to_radix_mem_num_limbs, num_limbs - 3 },
517 { C::to_radix_mem_is_output_bits, 0 },
518 // Control Flow
519 { C::to_radix_mem_last, 1 },
520 // Output
521 { C::to_radix_mem_output_limb_value, 7 },
522 { C::to_radix_mem_sel_should_exec, 1 },
523 { C::to_radix_mem_limb_index_to_lookup, num_limbs - 4 },
524 { C::to_radix_mem_output_tag, static_cast<uint8_t>(MemoryTag::U8) },
525 },
526 });
527
528 // Set the memory values and addresses
529 MemoryAddress value_addr = 0xdeadbeef;
530 MemoryAddress radix_addr = 0x12345678;
531 MemoryAddress num_limbs_addr = 0xc0ffee;
532 MemoryAddress is_output_bits_addr = 0xfeedface;
533
535 { value_addr, MemoryValue::from<FF>(value) },
536 { radix_addr, MemoryValue::from<uint32_t>(radix) },
537 { num_limbs_addr, MemoryValue::from<uint32_t>(num_limbs) },
538 { is_output_bits_addr, MemoryValue::from<uint1_t>(false) },
539 { dst_addr, MemoryValue::from<uint8_t>(1) },
540 { dst_addr + 1, MemoryValue::from<uint8_t>(3) },
541 { dst_addr + 2, MemoryValue::from<uint8_t>(3) },
542 { dst_addr + 3, MemoryValue::from<uint8_t>(7) },
543 };
544
545 for (uint32_t i = 0; i < memory_values.size(); ++i) {
546 const auto& [addr, value] = memory_values[i];
547 trace.set(i,
548 {
549 { { C::memory_sel, 1 },
550 { C::memory_space_id, 0 },
551 { C::memory_address, addr },
552 { C::memory_value, value.as_ff() },
553 { C::memory_tag, static_cast<uint8_t>(value.get_tag()) },
554 { C::memory_rw, i > 3 ? 1 : 0 } },
555 });
556 }
557
558 EventEmitter<ToRadixEvent> to_radix_event_emitter;
559 NoopEventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
560
561 FakeGreaterThan gt;
562 StrictMock<MockExecutionIdManager> execution_id_manager;
563 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
564
565 // Generate the events for the to_radix subtrace
566 to_radix_simulator.to_le_radix(value, num_limbs, radix);
567
568 ToRadixTraceBuilder builder;
569 auto events = to_radix_event_emitter.get_events();
570 builder.process(to_radix_event_emitter.dump_events(), trace);
571
572 PrecomputedTraceBuilder precomputed_builder;
573 precomputed_builder.process_to_radix_safe_limbs(trace);
574 precomputed_builder.process_to_radix_p_decompositions(trace);
575 precomputed_builder.process_misc(trace, 257); // Needed for precomputed safe limbs table
576
577 check_relation<to_radix_mem>(trace);
578 check_all_interactions<ToRadixTraceBuilder>(trace);
579}
580
581TEST(ToRadixMemoryConstrainingTest, DstOutOfRange)
582{
583 // Values
584 FF value = FF(1337);
585 uint32_t radix = 10;
586 uint32_t num_limbs = 2;
587 uint32_t dst_addr = AVM_HIGHEST_MEM_ADDRESS - 1; // This will cause an out-of-bounds error
588
589 TestTraceContainer trace = TestTraceContainer({
590 // Row 0
591 {
592 { C::precomputed_first_row, 1 },
593 // GT check
594 { C::gt_sel, 1 },
595 { C::gt_input_a, dst_addr + num_limbs - 1 },
596 { C::gt_input_b, AVM_HIGHEST_MEM_ADDRESS },
597 { C::gt_res, 1 }, // GT should return true
598 },
599 // Row 1
600 {
601 // Execution Trace (No gas)
602 { C::execution_sel, 1 },
603 { C::execution_sel_execute_to_radix, 1 },
604 { C::execution_register_0_, value },
605 { C::execution_register_1_, radix },
606 { C::execution_register_2_, num_limbs },
607 { C::execution_register_3_, 0 }, // is_output_bits
608 { C::execution_rop_4_, dst_addr },
609 { C::execution_sel_opcode_error, 1 },
610
611 // To Radix Mem
612 { C::to_radix_mem_sel, 1 },
613 { C::to_radix_mem_max_mem_addr, AVM_HIGHEST_MEM_ADDRESS },
614 { C::to_radix_mem_two, 2 },
615 { C::to_radix_mem_two_five_six, 256 },
616 // Memory Inputs
617 { C::to_radix_mem_execution_clk, 0 },
618 { C::to_radix_mem_space_id, 0 },
619 { C::to_radix_mem_dst_addr, dst_addr },
620 { C::to_radix_mem_max_write_addr, dst_addr + num_limbs - 1 },
621 // To Radix Inputs
622 { C::to_radix_mem_value_to_decompose, value },
623 { C::to_radix_mem_radix, radix },
624 { C::to_radix_mem_num_limbs, num_limbs },
625 { C::to_radix_mem_is_output_bits, 0 },
626 // Errors
627 { C::to_radix_mem_sel_dst_out_of_range_err, 1 },
628 { C::to_radix_mem_err, 1 },
629 // Control Flow
630 { C::to_radix_mem_start, 1 },
631 { C::to_radix_mem_last, 1 },
632 { C::to_radix_mem_num_limbs_minus_one_inv, num_limbs - 1 == 0 ? 0 : FF(num_limbs - 1).invert() },
633 // Helpers
634 { C::to_radix_mem_sel_num_limbs_is_zero, 0 },
635 { C::to_radix_mem_num_limbs_inv, FF(num_limbs).invert() },
636 { C::to_radix_mem_sel_value_is_zero, 0 },
637 { C::to_radix_mem_value_inv, value.invert() },
638 // Output
639 { C::to_radix_mem_sel_should_exec, 0 },
640 },
641 });
642
643 check_relation<to_radix_mem>(trace);
644 check_interaction<ToRadixTraceBuilder,
647}
648
649TEST(ToRadixMemoryConstrainingTest, InvalidRadix)
650{
651 // Values
652 FF value = FF(1337);
653 uint32_t radix = 0; // Invalid radix
654 uint32_t num_limbs = 2;
655 uint32_t dst_addr = 10;
656
657 TestTraceContainer trace = TestTraceContainer({
658 // Row 0
659 {
660 { C::precomputed_first_row, 1 },
661 // GT check
662 { C::gt_sel, 1 },
663 { C::gt_input_a, 2 },
664 { C::gt_input_b, radix },
665 { C::gt_res, 1 }, // GT should return true
666 },
667 // Row 1
668 {
669 { C::to_radix_mem_sel, 1 },
670 { C::to_radix_mem_max_mem_addr, AVM_HIGHEST_MEM_ADDRESS },
671 { C::to_radix_mem_two, 2 },
672 { C::to_radix_mem_two_five_six, 256 },
673 // Memory Inputs
674 { C::to_radix_mem_execution_clk, 0 },
675 { C::to_radix_mem_space_id, 0 },
676 { C::to_radix_mem_dst_addr, dst_addr },
677 { C::to_radix_mem_max_write_addr, dst_addr + num_limbs - 1 },
678 // To Radix Inputs
679 { C::to_radix_mem_value_to_decompose, value },
680 { C::to_radix_mem_radix, radix },
681 { C::to_radix_mem_num_limbs, num_limbs },
682 { C::to_radix_mem_is_output_bits, 0 },
683 // Errors
684 { C::to_radix_mem_sel_radix_lt_2_err, 1 },
685 { C::to_radix_mem_err, 1 },
686 // Control Flow
687 { C::to_radix_mem_start, 1 },
688 { C::to_radix_mem_last, 1 },
689 { C::to_radix_mem_num_limbs_minus_one_inv, num_limbs - 1 == 0 ? 0 : FF(num_limbs - 1).invert() },
690 // Helpers
691 { C::to_radix_mem_sel_num_limbs_is_zero, 0 },
692 { C::to_radix_mem_num_limbs_inv, FF(num_limbs).invert() },
693 { C::to_radix_mem_sel_value_is_zero, 0 },
694 { C::to_radix_mem_value_inv, value.invert() },
695 // Output
696 { C::to_radix_mem_sel_should_exec, 0 },
697 },
698 });
699 check_relation<to_radix_mem>(trace);
700 check_interaction<ToRadixTraceBuilder, lookup_to_radix_mem_check_radix_lt_2_settings>(trace);
701}
702
703TEST(ToRadixMemoryConstrainingTest, InvalidBitwiseRadix)
704{
705 // Values
706 FF value = FF(1337);
707 uint32_t radix = 3; // Invalid radix since is_output_bits is true
708 uint32_t num_limbs = 2;
709 uint32_t dst_addr = 10;
710 bool is_output_bits = true;
711
712 TestTraceContainer trace = TestTraceContainer({
713 // Row 0
714 {
715 { C::precomputed_first_row, 1 },
716 // GT check
717 { C::gt_sel, 1 },
718 { C::gt_input_a, 2 },
719 { C::gt_input_b, radix },
720 { C::gt_res, 0 }, // GT should return false
721 },
722 // Row 1
723 {
724 { C::to_radix_mem_sel, 1 },
725 { C::to_radix_mem_max_mem_addr, AVM_HIGHEST_MEM_ADDRESS },
726 { C::to_radix_mem_two, 2 },
727 { C::to_radix_mem_two_five_six, 256 },
728 // Memory Inputs
729 { C::to_radix_mem_execution_clk, 0 },
730 { C::to_radix_mem_space_id, 0 },
731 { C::to_radix_mem_dst_addr, dst_addr },
732 { C::to_radix_mem_max_write_addr, dst_addr + num_limbs - 1 },
733 // To Radix Inputs
734 { C::to_radix_mem_value_to_decompose, value },
735 { C::to_radix_mem_radix, radix },
736 { C::to_radix_mem_num_limbs, num_limbs },
737 { C::to_radix_mem_is_output_bits, is_output_bits ? 1 : 0 },
738 // Errors
739 { C::to_radix_mem_sel_invalid_bitwise_radix, 1 }, // Invalid bitwise radix
740 { C::to_radix_mem_err, 1 },
741 // Control Flow
742 { C::to_radix_mem_start, 1 },
743 { C::to_radix_mem_last, 1 },
744 { C::to_radix_mem_num_limbs_minus_one_inv, num_limbs - 1 == 0 ? 0 : FF(num_limbs - 1).invert() },
745 // Helpers
746 { C::to_radix_mem_sel_num_limbs_is_zero, 0 },
747 { C::to_radix_mem_num_limbs_inv, FF(num_limbs).invert() },
748 { C::to_radix_mem_sel_value_is_zero, 0 },
749 { C::to_radix_mem_value_inv, value.invert() },
750 // Output
751 { C::to_radix_mem_sel_should_exec, 0 },
752 },
753 });
754 check_relation<to_radix_mem>(trace);
755 check_interaction<ToRadixTraceBuilder, lookup_to_radix_mem_check_radix_lt_2_settings>(trace);
756}
757
758TEST(ToRadixMemoryConstrainingTest, InvalidNumLimbsForValue)
759{
760 // Values
761 FF value = FF(1337);
762 uint32_t radix = 3;
763 uint32_t num_limbs = 0; // num limbs should not be 0 if value != 0
764 uint32_t dst_addr = 10;
765 bool is_output_bits = false;
766
767 TestTraceContainer trace = TestTraceContainer({
768 // Row 0
769 {
770 { C::precomputed_first_row, 1 },
771 // GT check
772 { C::gt_sel, 1 },
773 { C::gt_input_a, 2 },
774 { C::gt_input_b, radix },
775 { C::gt_res, 0 }, // GT should return false
776 },
777 // Row 1
778 {
779 { C::to_radix_mem_sel, 1 },
780 { C::to_radix_mem_max_mem_addr, AVM_HIGHEST_MEM_ADDRESS },
781 { C::to_radix_mem_two, 2 },
782 { C::to_radix_mem_two_five_six, 256 },
783 // Memory Inputs
784 { C::to_radix_mem_execution_clk, 0 },
785 { C::to_radix_mem_space_id, 0 },
786 { C::to_radix_mem_dst_addr, dst_addr },
787 { C::to_radix_mem_max_write_addr, dst_addr + num_limbs - 1 },
788 // To Radix Inputs
789 { C::to_radix_mem_value_to_decompose, value },
790 { C::to_radix_mem_radix, radix },
791 { C::to_radix_mem_num_limbs, num_limbs },
792 { C::to_radix_mem_is_output_bits, is_output_bits ? 1 : 0 },
793 // Errors
794 { C::to_radix_mem_sel_invalid_num_limbs_err, 1 }, // num_limbs should not be 0 if value != 0
795 { C::to_radix_mem_err, 1 },
796 // Control Flow
797 { C::to_radix_mem_start, 1 },
798 { C::to_radix_mem_last, 1 },
799 { C::to_radix_mem_num_limbs_minus_one_inv, num_limbs - 1 == 0 ? 0 : FF(num_limbs - 1).invert() },
800 // Helpers
801 { C::to_radix_mem_sel_num_limbs_is_zero, 1 }, // num limbs is zero
802 { C::to_radix_mem_num_limbs_inv, 0 },
803 { C::to_radix_mem_sel_value_is_zero, 0 },
804 { C::to_radix_mem_value_inv, value.invert() },
805 // Output
806 { C::to_radix_mem_sel_should_exec, 0 },
807 },
808 });
809 check_relation<to_radix_mem>(trace);
810 check_interaction<ToRadixTraceBuilder, lookup_to_radix_mem_check_radix_lt_2_settings>(trace);
811}
812
813TEST(ToRadixMemoryConstrainingTest, ZeroNumLimbsAndZeroValueIsNoop)
814{
815 // Values
816 FF value = FF(0);
817 uint32_t radix = 3;
818 uint32_t num_limbs = 0; // num limbs can be zero since value is zero
819 uint32_t dst_addr = 10;
820 bool is_output_bits = false;
821
822 TestTraceContainer trace = TestTraceContainer({
823 // Row 0
824 {
825 { C::precomputed_first_row, 1 },
826 // GT check
827 { C::gt_sel, 1 },
828 { C::gt_input_a, 2 },
829 { C::gt_input_b, radix },
830 { C::gt_res, 0 }, // GT should return false
831 },
832 // Row 1
833 {
834 { C::to_radix_mem_sel, 1 },
835 { C::to_radix_mem_max_mem_addr, AVM_HIGHEST_MEM_ADDRESS },
836 { C::to_radix_mem_two, 2 },
837 { C::to_radix_mem_two_five_six, 256 },
838 // Memory Inputs
839 { C::to_radix_mem_execution_clk, 0 },
840 { C::to_radix_mem_space_id, 0 },
841 { C::to_radix_mem_dst_addr, dst_addr },
842 { C::to_radix_mem_max_write_addr, dst_addr + num_limbs - 1 },
843 // To Radix Inputs
844 { C::to_radix_mem_value_to_decompose, value },
845 { C::to_radix_mem_radix, radix },
846 { C::to_radix_mem_num_limbs, num_limbs },
847 { C::to_radix_mem_is_output_bits, is_output_bits ? 1 : 0 },
848 // Control Flow
849 { C::to_radix_mem_start, 1 },
850 { C::to_radix_mem_last, 1 },
851 { C::to_radix_mem_num_limbs_minus_one_inv, num_limbs - 1 == 0 ? 0 : FF(num_limbs - 1).invert() },
852 // Helpers
853 { C::to_radix_mem_sel_num_limbs_is_zero, 1 }, // num limbs is zero
854 { C::to_radix_mem_num_limbs_inv, 0 },
855 { C::to_radix_mem_sel_value_is_zero, 1 },
856 { C::to_radix_mem_value_inv, 0 },
857 // Output
858 { C::to_radix_mem_sel_should_exec, 0 }, // Should still not_exec since num_limbs == 0
859 },
860 });
861 check_relation<to_radix_mem>(trace);
862 check_interaction<ToRadixTraceBuilder, lookup_to_radix_mem_check_radix_lt_2_settings>(trace);
863}
864
865TEST(ToRadixMemoryConstrainingTest, ComplexTest)
866{
867 EventEmitter<ToRadixEvent> to_radix_event_emitter;
868 EventEmitter<ToRadixMemoryEvent> to_radix_mem_event_emitter;
869 EventEmitter<RangeCheckEvent> range_check_emitter;
870 EventEmitter<GreaterThanEvent> gt_emitter;
871
872 simulation::MemoryStore memory;
873 StrictMock<MockExecutionIdManager> execution_id_manager;
874 StrictMock<MockFieldGreaterThan> field_gt;
875 RangeCheck range_check(range_check_emitter);
876 GreaterThan gt(field_gt, range_check, gt_emitter);
877 EXPECT_CALL(execution_id_manager, get_execution_id()).WillOnce(Return(0)).WillOnce(Return(1));
878 ToRadixSimulator to_radix_simulator(execution_id_manager, gt, to_radix_event_emitter, to_radix_mem_event_emitter);
879
880 FF value = FF::neg_one();
881 uint32_t radix = 2;
882 uint32_t num_limbs = 256;
884 bool is_output_bits = true;
885 // Two calls to test transitions between contiguous chunks of computation
886 to_radix_simulator.to_be_radix(memory, value, radix, num_limbs, is_output_bits, dst_addr);
887 to_radix_simulator.to_be_radix(
888 memory, /*value=*/FF(1337), /*radix=*/10, /*num_limbs=*/2, /*is_output_bits=*/false, /*dst_addr=*/0xdeadbeef);
889
890 TestTraceContainer trace;
891 ToRadixTraceBuilder builder;
892 builder.process(to_radix_event_emitter.dump_events(), trace);
893 builder.process_with_memory(to_radix_mem_event_emitter.dump_events(), trace);
894
895 GreaterThanTraceBuilder gt_builder;
896 gt_builder.process(gt_emitter.dump_events(), trace);
897
898 PrecomputedTraceBuilder precomputed_builder;
899 precomputed_builder.process_to_radix_safe_limbs(trace);
900 precomputed_builder.process_to_radix_p_decompositions(trace);
901 precomputed_builder.process_misc(trace, 257); // Needed for precomputed safe limbs table
902
903 check_relation<to_radix>(trace);
904 check_relation<to_radix_mem>(trace);
905 // Skip the memory writes
906 check_interaction<ToRadixTraceBuilder,
917}
918
919} // namespace
920
921} // namespace bb::avm2::constraining
#define AVM_HIGHEST_MEM_ADDRESS
static constexpr size_t SR_SELECTOR_CONSISTENCY
Definition to_radix.hpp:63
static constexpr size_t SR_OVERFLOW_CHECK
Definition to_radix.hpp:64
static constexpr size_t SR_CONSTANT_CONSISTENCY_SAFE_LIMBS
Definition to_radix.hpp:67
static constexpr size_t SR_CONSTANT_CONSISTENCY_RADIX
Definition to_radix.hpp:65
static constexpr size_t SR_CONSTANT_CONSISTENCY_VALUE
Definition to_radix.hpp:66
static TestTraceContainer from_rows(const std::vector< AvmFullRow > &rows)
void set(Column col, uint32_t row, const FF &value)
PrecomputedTraceBuilder precomputed_builder
Definition alu.test.cpp:119
AluTraceBuilder builder
Definition alu.test.cpp:123
GreaterThanTraceBuilder gt_builder
Definition alu.test.cpp:122
ExecutionIdManager execution_id_manager
uint32_t dst_addr
RangeCheck range_check
GreaterThan gt
TestTraceContainer trace
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessage)
Definition macros.hpp:7
void check_interaction(tracegen::TestTraceContainer &trace)
AvmFlavorSettings::FF FF
TEST(TxExecutionConstrainingTest, WriteTreeValue)
Definition tx.test.cpp:508
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
permutation_settings< perm_to_radix_mem_dispatch_exec_to_radix_settings_ > perm_to_radix_mem_dispatch_exec_to_radix_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
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
typename Flavor::FF FF
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
MemoryStore memory
NiceMock< MockFieldGreaterThan > field_gt