Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
field.fuzzer.hpp
Go to the documentation of this file.
1
13#pragma once
14
20#include <algorithm>
21#include <array>
22#include <cstddef>
23#include <cstring>
24#include <vector>
25
26namespace bb {
27
31const size_t INTERNAL_STATE_SIZE = 32;
32
66
67static_assert(sizeof(VMSettings) == 4, "VMSettings must be exactly 4 bytes");
68
69const size_t SETTINGS_SIZE = sizeof(VMSettings);
70
77enum class Instruction {
78 SET_VALUE,
79 ADD,
81 INCREMENT,
82 MUL,
84 SUB,
86 DIV,
88 INV,
89 NEG,
90 SQR,
92 POW,
93 SQRT,
94 IS_ZERO,
95 EQUAL,
96 NOT_EQUAL,
102};
103
104const size_t INSTRUCTION_HEADER_SIZE = 1;
105const size_t INDEX_SIZE = 1;
106
107static_assert(1 << (8 * INDEX_SIZE) > INTERNAL_STATE_SIZE, "INDEX_SIZE is too small");
108
109// Instruction size constants
110const size_t SET_VALUE_SIZE =
125const size_t POW_SIZE = INSTRUCTION_HEADER_SIZE + INDEX_SIZE * 3 + sizeof(uint64_t);
134const size_t BATCH_INVERT_SIZE =
135 INSTRUCTION_HEADER_SIZE + INDEX_SIZE + sizeof(uint8_t);
136
147template <typename Field> struct FieldVM {
153 static constexpr bool LARGE_MODULUS = (Field::modulus.data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD);
154
160 static constexpr bool SUPPORTS_SQRT = []() {
161 if constexpr (requires { Field::primitive_root_log_size(); }) {
162 // For fields that define primitive_root_log_size, check if it's large enough
163 return Field::primitive_root_log_size() >= 6;
164 } else {
165 // For other fields, assume they support sqrt
166 return true;
167 }
168 }();
169
175 std::array<Field, INTERNAL_STATE_SIZE> field_internal_state;
176
183
188
193
197 size_t max_steps;
198
202 size_t step_count{};
203
212 FieldVM(bool with_debug = false, size_t max_steps = SIZE_MAX)
215 {
216 // Initialize with all operations enabled by default
218 settings.enable_add = true;
221 settings.enable_mul = true;
223 settings.enable_sub = true;
225 settings.enable_div = true;
227 settings.enable_inv = true;
228 settings.enable_neg = true;
229 settings.enable_sqr = true;
231 settings.enable_pow = true;
234 settings.enable_equal = true;
241 settings.reserved = 0;
242
243 for (size_t i = 0; i < INTERNAL_STATE_SIZE; i++) {
244 field_internal_state[i] = Field::zero();
246 }
247 }
248
259 size_t execute_instruction(const unsigned char* data_ptr)
260 {
268 auto get_index = [&](const unsigned char* data_ptr_index, size_t offset) -> size_t {
269 return static_cast<size_t>(data_ptr_index[offset]) % INTERNAL_STATE_SIZE;
270 };
271
272 // Read the instruction and map it to a valid instruction by taking modulo
273 constexpr size_t NUM_INSTRUCTIONS =
274 static_cast<size_t>(Instruction::BATCH_INVERT) + 1;
275 uint8_t original_instruction = *data_ptr;
276 Instruction instruction = static_cast<Instruction>(original_instruction % NUM_INSTRUCTIONS);
277 if (with_debug) {
278 const char* instruction_names[] = { "SET_VALUE", "ADD", "ADD_ASSIGN",
279 "INCREMENT", "MUL", "MUL_ASSIGN",
280 "SUB", "SUB_ASSIGN", "DIV",
281 "DIV_ASSIGN", "INV", "NEG",
282 "SQR", "SQR_ASSIGN", "POW",
283 "SQRT", "IS_ZERO", "EQUAL",
284 "NOT_EQUAL", "TO_MONTGOMERY", "FROM_MONTGOMERY",
285 "REDUCE_ONCE", "SELF_REDUCE", "BATCH_INVERT" };
286 const char* instruction_name =
287 (static_cast<int>(instruction) >= 0 &&
288 static_cast<int>(instruction) <
289 static_cast<int>(sizeof(instruction_names) / sizeof(instruction_names[0])))
290 ? instruction_names[static_cast<int>(instruction)]
291 : "UNKNOWN";
292 std::cout << "Executing instruction: " << instruction_name << " (" << static_cast<int>(instruction)
293 << ") at step: " << step_count << std::endl;
294 }
295
303 auto get_value = [&](const unsigned char* data_ptr_value, size_t offset) -> numeric::uint256_t {
305 for (size_t i = 0; i < 4; i++) {
306 limbs[i] = *reinterpret_cast<const uint64_t*>(data_ptr_value + offset + i * 8);
307 }
308 return numeric::uint256_t(limbs[0], limbs[1], limbs[2], limbs[3]);
309 };
310
318 auto get_uint64 = [&](const unsigned char* data_ptr_value, size_t offset) -> uint64_t {
319 return *reinterpret_cast<const uint64_t*>(data_ptr_value + offset);
320 };
321 // Execute the instruction
322 switch (instruction) {
325 return SET_VALUE_SIZE; // Skip disabled operation but return correct size
326 }
327 // Read the value and set the field and uint256_t values
328 {
329 size_t index = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
330 auto value = get_value(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
332 uint_internal_state[index] = value % Field::modulus;
333 if (with_debug) {
334 info("SET_VALUE: index: ", index, " value: ", value);
335 }
336 }
337 return SET_VALUE_SIZE;
338 case Instruction::ADD:
339 if (!settings.enable_add) {
340 return ADD_SIZE; // Skip disabled operation but return correct size
341 }
342 {
343 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
344 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
345 size_t index3 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE * 2);
347 if constexpr (LARGE_MODULUS) {
348 uint_internal_state[index3] = ((static_cast<uint512_t>(uint_internal_state[index1]) +
349 static_cast<uint512_t>(uint_internal_state[index2])) %
350 static_cast<uint512_t>(Field::modulus))
351 .lo;
352 } else {
353 uint_internal_state[index3] =
354 (uint_internal_state[index1] + uint_internal_state[index2]) % Field::modulus;
355 }
356 if (with_debug) {
357 info("ADD: index1: ",
358 index1,
359 " index2: ",
360 index2,
361 " index3: ",
362 index3,
363 " value: ",
364 field_internal_state[index3]);
365 }
366 }
367 return ADD_SIZE;
370 return ADD_ASSIGN_SIZE; // Skip disabled operation but return correct size
371 }
372 // Read the two operands
373 {
374 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
375 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
377 if constexpr (LARGE_MODULUS) {
378 uint_internal_state[index1] = ((static_cast<uint512_t>(uint_internal_state[index1]) +
379 static_cast<uint512_t>(uint_internal_state[index2])) %
380 static_cast<uint512_t>(Field::modulus))
381 .lo;
382 } else {
383 uint_internal_state[index1] =
384 (uint_internal_state[index1] + uint_internal_state[index2]) % Field::modulus;
385 }
386 if (with_debug) {
387 info("ADD_ASSIGN: index1: ", index1, " index2: ", index2, " value: ", field_internal_state[index1]);
388 }
389 }
390 return ADD_ASSIGN_SIZE;
393 return INCREMENT_SIZE; // Skip disabled operation but return correct size
394 }
395 // Read the operand
396 {
397 size_t index = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
399 if constexpr (LARGE_MODULUS) {
401 ((static_cast<uint512_t>(uint_internal_state[index]) + static_cast<uint512_t>(1)) %
402 static_cast<uint512_t>(Field::modulus))
403 .lo;
404 } else {
405 uint_internal_state[index] = (uint_internal_state[index] + 1) % Field::modulus;
406 }
407 if (with_debug) {
408 info("INCREMENT: index: ", index, " value: ", field_internal_state[index]);
409 }
410 }
411 return INCREMENT_SIZE;
412 case Instruction::MUL:
413 if (!settings.enable_mul) {
414 return MUL_SIZE; // Skip disabled operation but return correct size
415 }
416 // Read the two operands
417 {
418 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
419 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
420 size_t index3 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE * 2);
422 uint_internal_state[index3] = ((static_cast<uint512_t>(uint_internal_state[index1]) *
423 static_cast<uint512_t>(uint_internal_state[index2])) %
424 static_cast<uint512_t>(Field::modulus))
425 .lo;
426 if (with_debug) {
427 info("MUL: index1: ",
428 index1,
429 " index2: ",
430 index2,
431 " index3: ",
432 index3,
433 " value: ",
434 field_internal_state[index3]);
435 }
436 }
437 return MUL_SIZE;
440 return MUL_ASSIGN_SIZE; // Skip disabled operation but return correct size
441 }
442 // Read the two operands
443 {
444 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
445 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
447 uint_internal_state[index1] = ((static_cast<uint512_t>(uint_internal_state[index1]) *
448 static_cast<uint512_t>(uint_internal_state[index2])) %
449 static_cast<uint512_t>(Field::modulus))
450 .lo;
451 if (with_debug) {
452 info("MUL_ASSIGN: index1: ", index1, " index2: ", index2, " value: ", field_internal_state[index1]);
453 }
454 }
455 return MUL_ASSIGN_SIZE;
456 case Instruction::SUB:
457 if (!settings.enable_sub) {
458 return SUB_SIZE; // Skip disabled operation but return correct size
459 }
460 // Read the two operands
461 {
462 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
463 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
464 size_t index3 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE * 2);
466 if constexpr (LARGE_MODULUS) {
467 uint_internal_state[index3] =
468 ((static_cast<uint512_t>(Field::modulus) + static_cast<uint512_t>(uint_internal_state[index1]) -
469 static_cast<uint512_t>(uint_internal_state[index2])) %
470 static_cast<uint512_t>(Field::modulus))
471 .lo;
472 } else {
473 uint_internal_state[index3] =
474 (Field::modulus + uint_internal_state[index1] - uint_internal_state[index2]) % Field::modulus;
475 }
476 if (with_debug) {
477 info("SUB: index1: ",
478 index1,
479 " index2: ",
480 index2,
481 " index3: ",
482 index3,
483 " value: ",
484 field_internal_state[index3]);
485 }
486 }
487 return SUB_SIZE;
490 return SUB_ASSIGN_SIZE; // Skip disabled operation but return correct size
491 }
492 // Read the two operands
493 {
494 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
495 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
497 if constexpr (LARGE_MODULUS) {
498 uint_internal_state[index1] =
499 ((static_cast<uint512_t>(Field::modulus) + static_cast<uint512_t>(uint_internal_state[index1]) -
500 static_cast<uint512_t>(uint_internal_state[index2])) %
501 static_cast<uint512_t>(Field::modulus))
502 .lo;
503 } else {
504 uint_internal_state[index1] =
505 (Field::modulus + uint_internal_state[index1] - uint_internal_state[index2]) % Field::modulus;
506 }
507 if (with_debug) {
508 info("SUB_ASSIGN: index1: ", index1, " index2: ", index2, " value: ", field_internal_state[index1]);
509 }
510 }
511 return SUB_ASSIGN_SIZE;
512 case Instruction::DIV:
513 if (!settings.enable_div) {
514 return DIV_SIZE; // Skip disabled operation but return correct size
515 }
516 // Read the two operands
517 {
518 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
519 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
520 size_t index3 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE * 2);
521 // Skip division if divisor is zero
522 if (!field_internal_state[index2].is_zero()) {
524 // For uint256_t, we'll compute division using the field result
525 assert((uint512_t(static_cast<numeric::uint256_t>(field_internal_state[index3])) *
527 uint512_t(Field::modulus) ==
529 uint_internal_state[index3] = static_cast<numeric::uint256_t>(field_internal_state[index3]);
530 }
531 if (with_debug) {
532 info("DIV: index1: ",
533 index1,
534 " index2: ",
535 index2,
536 " index3: ",
537 index3,
538 " value: ",
539 field_internal_state[index3]);
540 }
541 }
542 return DIV_SIZE;
545 return DIV_ASSIGN_SIZE; // Skip disabled operation but return correct size
546 }
547 // Read the two operands
548 {
549 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
550 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
551 auto original_value = uint_internal_state[index1];
552 // Skip division if divisor is zero
553 if (!field_internal_state[index2].is_zero()) {
555 // For uint256_t, we'll compute division using the field result
556 if (!((uint512_t(static_cast<numeric::uint256_t>(field_internal_state[index1])) *
557 static_cast<uint512_t>(uint_internal_state[index2])) %
558 static_cast<uint512_t>(Field::modulus) ==
559 static_cast<uint512_t>(uint_internal_state[index1]))) {
560 // Deliberately set to different value
562 }
563 uint_internal_state[index1] = static_cast<numeric::uint256_t>(field_internal_state[index1]);
564 }
565 if (with_debug) {
566 info("DIV_ASSIGN: index1: ",
567 index1,
568 " index2: ",
569 index2,
570 " value: ",
571 field_internal_state[index1],
572 " original_value: ",
573 original_value);
574 }
575 }
576 return DIV_ASSIGN_SIZE;
577 case Instruction::INV:
578 if (!settings.enable_inv) {
579 return INV_SIZE; // Skip disabled operation but return correct size
580 }
581 // Read the operand
582 {
583 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
584 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
585 auto original_value = uint_internal_state[index2];
586 // Skip inversion if operand is zero
587 if (!field_internal_state[index1].is_zero()) {
588 field_internal_state[index2] = field_internal_state[index1].invert();
589 // For uint256_t, we'll compute inversion using the field result
590 if (!((uint512_t(static_cast<numeric::uint256_t>(field_internal_state[index2])) *
591 static_cast<uint512_t>(uint_internal_state[index1])) %
592 static_cast<uint512_t>(Field::modulus) ==
593 static_cast<uint512_t>(1))) {
594 // Deliberately set to different value
596 } else {
597 uint_internal_state[index2] = static_cast<numeric::uint256_t>(field_internal_state[index2]);
598 }
599 }
600 if (with_debug) {
601 info("INV: index1: ",
602 index1,
603 " index2: ",
604 index2,
605 " value: ",
606 field_internal_state[index2],
607 " original_value: ",
608 original_value);
609 }
610 }
611 return INV_SIZE;
612 case Instruction::NEG:
613 if (!settings.enable_neg) {
614 return NEG_SIZE; // Skip disabled operation but return correct size
615 }
616 // Read the operand
617 {
618 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
619 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
621 if constexpr (LARGE_MODULUS) {
622 uint_internal_state[index2] = ((static_cast<uint512_t>(Field::modulus) -
623 static_cast<uint512_t>(uint_internal_state[index1])) %
624 static_cast<uint512_t>(Field::modulus))
625 .lo;
626 } else {
627 uint_internal_state[index2] = (Field::modulus - uint_internal_state[index1]) % Field::modulus;
628 }
629 if (with_debug) {
630 info("NEG: index1: ", index1, " index2: ", index2, " value: ", field_internal_state[index2]);
631 }
632 }
633 return NEG_SIZE;
634 case Instruction::SQR:
635 if (!settings.enable_sqr) {
636 return SQR_SIZE; // Skip disabled operation but return correct size
637 }
638 // Read the operand
639 {
640 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
641 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
642 field_internal_state[index2] = field_internal_state[index1].sqr();
643 if constexpr (LARGE_MODULUS) {
644 uint_internal_state[index2] = ((static_cast<uint512_t>(uint_internal_state[index1]) *
645 static_cast<uint512_t>(uint_internal_state[index1])) %
646 static_cast<uint512_t>(Field::modulus))
647 .lo;
648 } else {
649 uint_internal_state[index2] = ((static_cast<uint512_t>(uint_internal_state[index1]) *
650 static_cast<uint512_t>(uint_internal_state[index1])) %
651 static_cast<uint512_t>(Field::modulus))
652 .lo;
653 }
654 if (with_debug) {
655 info("SQR: index1: ", index1, " index2: ", index2, " value: ", field_internal_state[index2]);
656 }
657 }
658 return SQR_SIZE;
661 return SQR_ASSIGN_SIZE; // Skip disabled operation but return correct size
662 }
663 // Read the operand
664 {
665 size_t index = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
666 field_internal_state[index].self_sqr();
667 if constexpr (LARGE_MODULUS) {
669 static_cast<uint512_t>(uint_internal_state[index])) %
670 static_cast<uint512_t>(Field::modulus))
671 .lo;
672 } else {
674 static_cast<uint512_t>(uint_internal_state[index])) %
675 static_cast<uint512_t>(Field::modulus))
676 .lo;
677 }
678 if (with_debug) {
679 info("SQR_ASSIGN: index: ", index, " value: ", field_internal_state[index]);
680 }
681 }
682 return SQR_ASSIGN_SIZE;
683 case Instruction::POW:
684 if (!settings.enable_pow) {
685 return POW_SIZE; // Skip disabled operation but return correct size
686 }
687 // Read the operand and exponent
688 {
689 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
690 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
691 uint64_t exponent = get_uint64(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE * 2);
692 field_internal_state[index2] = field_internal_state[index1].pow(exponent);
693 auto multiplicand = static_cast<uint512_t>(uint_internal_state[index1]);
694 auto current = static_cast<uint512_t>(1);
695 while (exponent > 0) {
696 if (exponent & 1) {
697 current = (current * multiplicand) % static_cast<uint512_t>(Field::modulus);
698 }
699 multiplicand = (multiplicand * multiplicand) % static_cast<uint512_t>(Field::modulus);
700 exponent >>= 1;
701 }
702 uint_internal_state[index2] = current.lo;
703 if (with_debug) {
704 info("POW: index1: ",
705 index1,
706 " index2: ",
707 index2,
708 " exponent: ",
709 exponent,
710 " value: ",
711 field_internal_state[index2]);
712 }
713 }
714 return POW_SIZE;
717 return SQRT_SIZE; // Skip disabled/unsupported operation but return correct size
718 }
719 // Read the operand
720 {
721 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
722 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
723 if constexpr (SUPPORTS_SQRT) {
724 auto [found, root] = field_internal_state[index1].sqrt();
725 if (found) {
726 field_internal_state[index2] = root;
727 assert(
728 ((static_cast<uint512_t>(static_cast<numeric::uint256_t>(field_internal_state[index2])) *
729 static_cast<uint512_t>(static_cast<numeric::uint256_t>(field_internal_state[index2]))) %
730 static_cast<uint512_t>(Field::modulus)) ==
731 static_cast<uint512_t>(uint_internal_state[index1]));
732 uint_internal_state[index2] = static_cast<numeric::uint256_t>(root);
733 }
734 if (with_debug) {
735 info("SQRT: index1: ",
736 index1,
737 " index2: ",
738 index2,
739 " found: ",
740 found,
741 " value: ",
742 field_internal_state[index2]);
743 }
744 }
745 }
746 return SQRT_SIZE;
749 return IS_ZERO_SIZE; // Skip disabled operation but return correct size
750 }
751 // Read the operand
752 {
753 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
754 assert((uint_internal_state[index1] == 0) == field_internal_state[index1].is_zero());
755 if (with_debug) {
756 info("IS_ZERO: index1: ", index1, " result: ", field_internal_state[index1].is_zero());
757 }
758 }
759 return IS_ZERO_SIZE;
761 if (!settings.enable_equal) {
762 return EQUAL_SIZE; // Skip disabled operation but return correct size
763 }
764 // Read the operands
765 {
766 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
767 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
768 assert((field_internal_state[index1] == field_internal_state[index2]) ==
769 (uint_internal_state[index1] == uint_internal_state[index2]));
770 if (with_debug) {
771 info("EQUAL: index1: ",
772 index1,
773 " index2: ",
774 index2,
775 " result: ",
776 field_internal_state[index1] == field_internal_state[index2]);
777 }
778 }
779 return EQUAL_SIZE;
782 return NOT_EQUAL_SIZE; // Skip disabled operation but return correct size
783 }
784 // Read the operands
785 {
786 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
787 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
788 assert((field_internal_state[index1] != field_internal_state[index2]) ==
789 (uint_internal_state[index1] != uint_internal_state[index2]));
790 if (with_debug) {
791 info("NOT_EQUAL: index1: ",
792 index1,
793 " index2: ",
794 index2,
795 " result: ",
796 field_internal_state[index1] != field_internal_state[index2]);
797 }
798 }
799 return NOT_EQUAL_SIZE;
802 return TO_MONTGOMERY_SIZE; // Skip disabled operation but return correct size
803 }
804 // Read the operand
805 {
806 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
807 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
808 field_internal_state[index2] = field_internal_state[index1].to_montgomery_form();
809 uint_internal_state[index2] = ((static_cast<uint512_t>(uint_internal_state[index1]) << 256) %
810 static_cast<uint512_t>(Field::modulus))
811 .lo;
812 // Note: uint_internal_state doesn't track Montgomery form
813 if (with_debug) {
814 info("TO_MONTGOMERY: index1: ",
815 index1,
816 " index2: ",
817 index2,
818 " value: ",
819 field_internal_state[index2]);
820 }
821 }
822 return TO_MONTGOMERY_SIZE;
825 return FROM_MONTGOMERY_SIZE; // Skip disabled operation but return correct size
826 }
827 // Read the operand
828 {
829 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
830 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
831 field_internal_state[index2] = field_internal_state[index1].from_montgomery_form();
832 if constexpr (LARGE_MODULUS) {
833 // For large modulus fields, use uint512_t to prevent overflow
834 auto value = static_cast<uint512_t>(uint_internal_state[index1]);
835 for (size_t i = 0; i < 256; i++) {
836 if (value & 1) {
837 value += static_cast<uint512_t>(Field::modulus);
838 }
839 value >>= 1;
840 }
841 uint_internal_state[index2] = value.lo;
842 } else {
843 // For small modulus fields, use uint256_t
845 for (size_t i = 0; i < 256; i++) {
846 if (uint_internal_state[index2] & 1) {
847 uint_internal_state[index2] += Field::modulus;
848 }
849 uint_internal_state[index2] >>= 1;
850 }
851 }
852 if (with_debug) {
853 info("FROM_MONTGOMERY: index1: ",
854 index1,
855 " index2: ",
856 index2,
857 " value: ",
858 field_internal_state[index2]);
859 }
860 }
864 return REDUCE_ONCE_SIZE; // Skip disabled operation but return correct size
865 }
866 // Read the operand
867 {
868 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
869 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
870 field_internal_state[index2] = field_internal_state[index1].reduce_once();
872 if (with_debug) {
873 info(
874 "REDUCE_ONCE: index1: ", index1, " index2: ", index2, " value: ", field_internal_state[index2]);
875 }
876 }
877 return REDUCE_ONCE_SIZE;
880 return SELF_REDUCE_SIZE; // Skip disabled operation but return correct size
881 }
882 // Read the operand
883 {
884 size_t index = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
885 field_internal_state[index].self_reduce_once();
886 if (with_debug) {
887 info("SELF_REDUCE: index: ", index, " value: ", field_internal_state[index]);
888 }
889 }
890 return SELF_REDUCE_SIZE;
893 return BATCH_INVERT_SIZE; // Skip disabled operation but return correct size
894 }
895 // Read the operands and count
896 {
897 size_t start_index = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
898 size_t count =
899 static_cast<size_t>(data_ptr[INSTRUCTION_HEADER_SIZE + INDEX_SIZE]) % (INTERNAL_STATE_SIZE / 2);
900 if (count == 0) {
901 count = 1; // Ensure at least one element
902 }
903 if (start_index + count > INTERNAL_STATE_SIZE) {
904 count = INTERNAL_STATE_SIZE - start_index;
905 }
906
907 // Store original values for comparison
908 std::vector<Field> original_elements;
909 std::vector<numeric::uint256_t> original_uint_elements;
910 std::vector<size_t> valid_indices;
911
912 for (size_t i = 0; i < count; i++) {
913 size_t idx = start_index + i;
914 original_elements.push_back(field_internal_state[idx]);
915 original_uint_elements.push_back(uint_internal_state[idx]);
916 valid_indices.push_back(idx);
917 }
918
919 // Perform individual inversions for comparison
920 std::vector<Field> individual_inverses;
921 std::vector<numeric::uint256_t> individual_uint_inverses;
922 for (size_t idx : valid_indices) {
923 if (!field_internal_state[idx].is_zero()) {
924 Field inv = field_internal_state[idx].invert();
925 individual_inverses.push_back(inv);
926 individual_uint_inverses.push_back(static_cast<numeric::uint256_t>(inv));
927 } else {
928 individual_inverses.push_back(Field::zero());
929 individual_uint_inverses.push_back(numeric::uint256_t(0));
930 }
931 }
932
933 // Collect non-zero elements for batch inversion
934 std::vector<Field> non_zero_elements;
935 std::vector<size_t> non_zero_indices;
936 for (size_t i = 0; i < count; i++) {
937 size_t idx = start_index + i;
938 if (!field_internal_state[idx].is_zero()) {
939 non_zero_elements.push_back(field_internal_state[idx]);
940 non_zero_indices.push_back(idx);
941 }
942 }
943
944 if (!non_zero_elements.empty()) {
945 // Perform batch inversion (modifies non_zero_elements in-place)
946 Field::batch_invert(non_zero_elements);
947
948 // Store batch results back
949 for (size_t i = 0; i < non_zero_indices.size(); i++) {
950 size_t idx = non_zero_indices[i];
951 field_internal_state[idx] = non_zero_elements[i];
952 uint_internal_state[idx] = static_cast<numeric::uint256_t>(non_zero_elements[i]);
953 }
954 }
955
956 // Verify that batch inversion produces the same results as individual inversions
957 for (size_t i = 0; i < valid_indices.size(); i++) {
958 size_t idx = valid_indices[i];
959 if (!original_elements[i].is_zero()) {
960 // Check that batch and individual inversions match
961 assert(field_internal_state[idx] == individual_inverses[i]);
962 assert(uint_internal_state[idx] == individual_uint_inverses[i]);
963
964 // Verify the inverse property: a * a^(-1) = 1
965 [[maybe_unused]] Field product = original_elements[i] * field_internal_state[idx];
966 assert(product == Field::one());
967
968 // Verify uint arithmetic consistency
969 [[maybe_unused]] auto uint_product = (static_cast<uint512_t>(original_uint_elements[i]) *
970 static_cast<uint512_t>(uint_internal_state[idx])) %
971 static_cast<uint512_t>(Field::modulus);
972 assert(uint_product == static_cast<uint512_t>(1));
973 } else {
974 // Zero elements should remain zero
975 assert(field_internal_state[idx] == Field::zero());
976 assert(uint_internal_state[idx] == numeric::uint256_t(0));
977 }
978 }
979
980 if (with_debug) {
981 info("BATCH_INVERT: start_index: ",
982 start_index,
983 " count: ",
984 count,
985 " non_zero_count: ",
986 non_zero_elements.size());
987 }
988 }
989 return BATCH_INVERT_SIZE;
990 }
991 }
992
1001 std::vector<uint8_t> data;
1002 size_t size;
1003 };
1004
1018 size_t Size,
1019 size_t max_steps)
1020 {
1021 std::vector<ParsedInstruction> instructions;
1022 size_t data_offset = 0;
1023 size_t steps_parsed = 0;
1024
1025 // Skip settings if present
1026 if (Size >= SETTINGS_SIZE) {
1027 const VMSettings* settings_ptr = reinterpret_cast<const VMSettings*>(Data);
1028 settings = *settings_ptr;
1029 data_offset += SETTINGS_SIZE;
1030 }
1031
1032 while (data_offset < Size && steps_parsed < max_steps) {
1033 if (data_offset >= Size) {
1034 break;
1035 }
1036
1037 Instruction instruction = static_cast<Instruction>(Data[data_offset]);
1038 size_t instruction_size = 0;
1039
1040 // Map the instruction to a valid instruction by taking modulo
1041 constexpr size_t NUM_INSTRUCTIONS =
1042 static_cast<size_t>(Instruction::BATCH_INVERT) + 1;
1043 uint8_t original_instruction = Data[data_offset];
1044 instruction = static_cast<Instruction>(original_instruction % NUM_INSTRUCTIONS);
1045
1046 // Determine instruction size based on type
1047 switch (instruction) {
1049 instruction_size = SET_VALUE_SIZE;
1050 break;
1051 case Instruction::ADD:
1052 instruction_size = ADD_SIZE;
1053 break;
1055 instruction_size = ADD_ASSIGN_SIZE;
1056 break;
1058 instruction_size = INCREMENT_SIZE;
1059 break;
1060 case Instruction::MUL:
1061 instruction_size = MUL_SIZE;
1062 break;
1064 instruction_size = MUL_ASSIGN_SIZE;
1065 break;
1066 case Instruction::SUB:
1067 instruction_size = SUB_SIZE;
1068 break;
1070 instruction_size = SUB_ASSIGN_SIZE;
1071 break;
1072 case Instruction::DIV:
1073 instruction_size = DIV_SIZE;
1074 break;
1076 instruction_size = DIV_ASSIGN_SIZE;
1077 break;
1078 case Instruction::INV:
1079 instruction_size = INV_SIZE;
1080 break;
1081 case Instruction::NEG:
1082 instruction_size = NEG_SIZE;
1083 break;
1084 case Instruction::SQR:
1085 instruction_size = SQR_SIZE;
1086 break;
1088 instruction_size = SQR_ASSIGN_SIZE;
1089 break;
1090 case Instruction::POW:
1091 instruction_size = POW_SIZE;
1092 break;
1093 case Instruction::SQRT:
1094 instruction_size = SQRT_SIZE;
1095 break;
1097 instruction_size = IS_ZERO_SIZE;
1098 break;
1099 case Instruction::EQUAL:
1100 instruction_size = EQUAL_SIZE;
1101 break;
1103 instruction_size = NOT_EQUAL_SIZE;
1104 break;
1106 instruction_size = TO_MONTGOMERY_SIZE;
1107 break;
1109 instruction_size = FROM_MONTGOMERY_SIZE;
1110 break;
1112 instruction_size = REDUCE_ONCE_SIZE;
1113 break;
1115 instruction_size = SELF_REDUCE_SIZE;
1116 break;
1118 instruction_size = BATCH_INVERT_SIZE;
1119 break;
1120 }
1121
1122 // Check if we have enough data for this instruction
1123 if (data_offset + instruction_size > Size) {
1124 break;
1125 }
1126
1127 // Create parsed instruction
1128 ParsedInstruction parsed;
1129 parsed.instruction = instruction;
1130 parsed.size = instruction_size;
1131 parsed.data.resize(instruction_size);
1132
1133 // Only copy the data that's actually available
1134 size_t data_to_copy = std::min(instruction_size, Size - data_offset);
1135 std::memcpy(parsed.data.data(), Data + data_offset, data_to_copy);
1136
1137 // If we couldn't copy all the data, pad with zeros
1138 if (data_to_copy < instruction_size) {
1139 std::memset(parsed.data.data() + data_to_copy, 0, instruction_size - data_to_copy);
1140 }
1141
1142 instructions.push_back(parsed);
1143
1144 data_offset += instruction_size;
1145 steps_parsed++;
1146 }
1147
1148 return { instructions, data_offset };
1149 }
1150
1161 {
1162 if (with_debug) {
1163 const char* instruction_names[] = { "SET_VALUE", "ADD", "ADD_ASSIGN",
1164 "INCREMENT", "MUL", "MUL_ASSIGN",
1165 "SUB", "SUB_ASSIGN", "DIV",
1166 "DIV_ASSIGN", "INV", "NEG",
1167 "SQR", "SQR_ASSIGN", "POW",
1168 "SQRT", "IS_ZERO", "EQUAL",
1169 "NOT_EQUAL", "TO_MONTGOMERY", "FROM_MONTGOMERY",
1170 "REDUCE_ONCE", "SELF_REDUCE", "BATCH_INVERT" };
1171 const char* instruction_name =
1172 (static_cast<int>(parsed.instruction) >= 0 &&
1173 static_cast<int>(parsed.instruction) <
1174 static_cast<int>(sizeof(instruction_names) / sizeof(instruction_names[0])))
1175 ? instruction_names[static_cast<int>(parsed.instruction)]
1176 : "UNKNOWN";
1177 std::cout << "Executing instruction: " << instruction_name << " (" << static_cast<int>(parsed.instruction)
1178 << ") at step: " << step_count << std::endl;
1179 }
1180
1181 // Execute the instruction using the existing logic
1182 size_t consumed = execute_instruction(parsed.data.data());
1183 return consumed > 0;
1184 }
1185
1198 size_t run(const unsigned char* Data, size_t Size, bool reset_steps = true)
1199 {
1200 if (reset_steps) {
1201 step_count = 0;
1202 }
1203
1204 if (with_debug) {
1205 std::cout << "Starting VM run with " << Size << " bytes of data, max_steps: " << max_steps << std::endl;
1206 }
1207
1208 // First parse all instructions into a vector
1209 auto [instructions, bytes_consumed] = parse_instructions(Data, Size, max_steps);
1210
1211 if (with_debug) {
1212 std::cout << "Parsed " << instructions.size() << " instructions, consumed " << bytes_consumed << " bytes"
1213 << std::endl;
1214 }
1215
1216 // Then execute the parsed instructions
1217 for (const auto& instruction : instructions) {
1218 if (step_count >= max_steps) {
1219 break;
1220 }
1221
1223 if (!success) {
1224 break;
1225 }
1226 step_count++;
1227 }
1228
1229 return bytes_consumed; // Return the number of bytes consumed during parsing
1230 }
1231
1242 {
1243 for (size_t i = 0; i < INTERNAL_STATE_SIZE; i++) {
1244 if (field_internal_state[i] != Field(uint_internal_state[i])) {
1245 if (with_debug) {
1246 std::cout << "check_internal_state: index: " << i << " field: " << field_internal_state[i]
1247 << " uint: " << uint_internal_state[i] << std::endl;
1248 }
1249 return false;
1250 }
1251 }
1252 return true;
1253 }
1254
1264 {
1266 result.reserve(INTERNAL_STATE_SIZE);
1267 for (size_t i = 0; i < INTERNAL_STATE_SIZE; i++) {
1268 result.push_back(uint_internal_state[i]);
1269 }
1270 return result;
1271 }
1272
1278 size_t get_step_count() const { return step_count; }
1279
1286
1292 void set_max_steps(size_t new_max_steps) { max_steps = new_max_steps; }
1293
1298
1304 size_t get_max_steps() const { return max_steps; }
1305
1311 bool has_remaining_steps() const { return step_count < max_steps; }
1312
1323 {
1324 if constexpr (LARGE_MODULUS) {
1325 return (uint512_t(value) % uint512_t(Field::modulus)).lo;
1326 } else {
1327 return value % Field::modulus;
1328 }
1329 }
1330
1341 {
1342 for (size_t i = 0; i < std::min(state.size(), size_t(INTERNAL_STATE_SIZE)); i++) {
1343 // Check that uint_internal_state matches the reduced state
1344 if (uint_internal_state[i] != reduce_to_modulus(state[i])) {
1345 return false;
1346 }
1347 // Check that field_internal_state is consistent with uint_internal_state
1348 if (field_internal_state[i] != Field(uint_internal_state[i])) {
1349 return false;
1350 }
1351 }
1352 return true;
1353 }
1354};
1355
1356} // namespace bb
#define info(...)
Definition log.hpp:93
uint64_t get_index(uint64_t max_index=0)
ssize_t offset
Definition engine.cpp:52
Instruction instruction
uintx< uint256_t > uint512_t
Definition uintx.hpp:306
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
const size_t SET_VALUE_SIZE
Size of SET_VALUE instruction.
const size_t MUL_SIZE
Size of MUL instruction.
const size_t SETTINGS_SIZE
const size_t INTERNAL_STATE_SIZE
Constant defining the number of elements in the VM's internal state.
const size_t INCREMENT_SIZE
Size of INCREMENT instruction.
const size_t SQR_ASSIGN_SIZE
Size of SQR_ASSIGN instruction.
const size_t NOT_EQUAL_SIZE
Size of NOT_EQUAL instruction.
const size_t DIV_SIZE
Size of DIV instruction.
const size_t ADD_SIZE
Size of ADD instruction.
const size_t INV_SIZE
Size of INV instruction.
const size_t DIV_ASSIGN_SIZE
Size of DIV_ASSIGN instruction.
Instruction
Enumeration of VM instructions that can be executed.
@ POW
Raise a field element to a power.
@ SQR
Square a field element.
@ TO_MONTGOMERY
Convert to Montgomery form.
@ SUB
Subtract two field elements.
@ FROM_MONTGOMERY
Convert from Montgomery form.
@ DIV
Divide two field elements.
@ INV
Invert a field element.
@ MUL
Multiply two field elements.
@ SQRT
Compute square root of a field element.
@ IS_ZERO
Check if a field element is zero.
@ NOT_EQUAL
Check if two field elements are not equal.
@ REDUCE_ONCE
Reduce a field element once.
@ ADD_ASSIGN
Add-assign operation.
@ DIV_ASSIGN
Divide-assign operation.
@ NEG
Negate a field element.
@ MUL_ASSIGN
Multiply-assign operation.
@ BATCH_INVERT
Batch invert multiple field elements.
@ SET_VALUE
Set a field element to a specific value.
@ INCREMENT
Increment a field element by 1.
@ EQUAL
Check if two field elements are equal.
@ ADD
Add two field elements.
@ SELF_REDUCE
Self-reduce a field element.
@ SUB_ASSIGN
Subtract-assign operation.
@ SQR_ASSIGN
Square-assign operation.
const size_t FROM_MONTGOMERY_SIZE
Size of FROM_MONTGOMERY instruction.
const size_t INSTRUCTION_HEADER_SIZE
Size of instruction header in bytes.
const size_t SELF_REDUCE_SIZE
Size of SELF_REDUCE instruction.
const size_t BATCH_INVERT_SIZE
Size of BATCH_INVERT instruction.
const size_t MUL_ASSIGN_SIZE
Size of MUL_ASSIGN instruction.
const size_t NEG_SIZE
Size of NEG instruction.
const size_t IS_ZERO_SIZE
Size of IS_ZERO instruction.
const size_t EQUAL_SIZE
Size of EQUAL instruction.
const size_t POW_SIZE
Size of POW instruction.
const size_t SUB_SIZE
Size of SUB instruction.
const size_t TO_MONTGOMERY_SIZE
Size of TO_MONTGOMERY instruction.
const size_t REDUCE_ONCE_SIZE
Size of REDUCE_ONCE instruction.
const size_t SQRT_SIZE
Size of SQRT instruction.
const size_t ADD_ASSIGN_SIZE
Size of ADD_ASSIGN instruction.
const size_t INDEX_SIZE
Size of index field in bytes.
const size_t SUB_ASSIGN_SIZE
Size of SUB_ASSIGN instruction.
const size_t SQR_SIZE
Size of SQR instruction.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Structure to hold parsed instruction data.
Instruction instruction
The instruction type.
std::vector< uint8_t > data
The instruction data.
size_t size
The size of the instruction data.
Virtual machine for field arithmetic operations.
size_t run(const unsigned char *Data, size_t Size, bool reset_steps=true)
Run the VM on input data.
std::vector< numeric::uint256_t > export_uint_state() const
Export the final uint state as a vector of uint256_t values.
std::array< numeric::uint256_t, INTERNAL_STATE_SIZE > uint_internal_state
Internal state array of uint256_t values.
void set_max_steps(size_t new_max_steps)
Set a new step limit for the VM.
void reset_step_count()
Reset the step counter to 0.
std::array< Field, INTERNAL_STATE_SIZE > field_internal_state
Internal state array of field elements.
bool with_debug
Flag to enable debug output.
std::pair< std::vector< ParsedInstruction >, size_t > parse_instructions(const unsigned char *Data, size_t Size, size_t max_steps)
Parse instructions from data buffer into a vector.
VMSettings settings
VM settings controlling which operations are enabled.
static numeric::uint256_t reduce_to_modulus(const numeric::uint256_t &value)
Reduce a uint256_t value to the field's modulus.
size_t max_steps
Maximum number of steps the VM can execute.
size_t get_step_count() const
Get the number of steps executed.
bool was_stopped_by_max_steps() const
Check if the VM was stopped due to reaching max steps.
size_t execute_instruction(const unsigned char *data_ptr)
Execute a single VM instruction.
static constexpr bool SUPPORTS_SQRT
Flag indicating if the field supports square root operations.
bool verify_initial_state(const std::vector< numeric::uint256_t > &state) const
Verify that the initial state is correctly loaded.
static constexpr bool LARGE_MODULUS
Flag indicating if the field has a large modulus requiring uint512_t for arithmetic.
size_t step_count
Number of steps executed so far.
FieldVM(bool with_debug=false, size_t max_steps=SIZE_MAX)
Constructor for FieldVM.
bool has_remaining_steps() const
Check if there are remaining steps available.
bool check_internal_state() const
Check internal state consistency between field and uint256_t representations.
bool execute_parsed_instruction(const ParsedInstruction &parsed)
Execute a parsed instruction.
size_t get_max_steps() const
Get the current step limit.
Settings structure to control which operations are enabled in the VM.
bool enable_to_montgomery
Enable TO_MONTGOMERY operations.
bool enable_inv
Enable INV operations.
bool enable_neg
Enable NEG operations.
bool enable_mul_assign
Enable MUL_ASSIGN operations.
uint8_t reserved
Reserved for future use.
bool enable_mul
Enable MUL operations.
bool enable_equal
Enable EQUAL operations.
bool enable_sqr_assign
Enable SQR_ASSIGN operations.
bool enable_self_reduce
Enable SELF_REDUCE operations.
bool enable_batch_invert
Enable BATCH_INVERT operations.
bool enable_div
Enable DIV operations.
bool enable_sub_assign
Enable SUB_ASSIGN operations.
bool enable_set_value
Enable SET_VALUE operations.
bool enable_div_assign
Enable DIV_ASSIGN operations.
bool enable_from_montgomery
Enable FROM_MONTGOMERY operations.
bool enable_add
Enable ADD operations.
bool enable_reduce_once
Enable REDUCE_ONCE operations.
bool enable_not_equal
Enable NOT_EQUAL operations.
bool enable_sqrt
Enable SQRT operations.
bool enable_add_assign
Enable ADD_ASSIGN operations.
bool enable_sub
Enable SUB operations.
bool enable_is_zero
Enable IS_ZERO operations.
bool enable_increment
Enable INCREMENT operations.
bool enable_sqr
Enable SQR operations.
bool enable_pow
Enable POW operations.