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
19#include <algorithm>
20#include <array>
21#include <cstddef>
22#include <cstring>
23#include <vector>
24
25namespace bb {
26
30const size_t INTERNAL_STATE_SIZE = 32;
31
65
66static_assert(sizeof(VMSettings) == 4, "VMSettings must be exactly 4 bytes");
67
68const size_t SETTINGS_SIZE = sizeof(VMSettings);
69
76enum class Instruction {
77 SET_VALUE,
78 ADD,
80 INCREMENT,
81 MUL,
83 SUB,
85 DIV,
87 INV,
88 NEG,
89 SQR,
91 POW,
92 SQRT,
93 IS_ZERO,
94 EQUAL,
95 NOT_EQUAL,
101};
102
103const size_t INSTRUCTION_HEADER_SIZE = 1;
104const size_t INDEX_SIZE = 1;
105
106static_assert(1 << (8 * INDEX_SIZE) > INTERNAL_STATE_SIZE, "INDEX_SIZE is too small");
107
108// Instruction size constants
109const size_t SET_VALUE_SIZE =
124const size_t POW_SIZE = INSTRUCTION_HEADER_SIZE + INDEX_SIZE * 3 + sizeof(uint64_t);
133const size_t BATCH_INVERT_SIZE =
134 INSTRUCTION_HEADER_SIZE + INDEX_SIZE + sizeof(uint8_t);
135
146template <typename Field> struct FieldVM {
152 static constexpr bool LARGE_MODULUS = (Field::modulus.data[3] >= 0x4000000000000000ULL);
153
159 static constexpr bool SUPPORTS_SQRT = []() {
160 if constexpr (requires { Field::primitive_root_log_size(); }) {
161 // For fields that define primitive_root_log_size, check if it's large enough
162 return Field::primitive_root_log_size() >= 6;
163 } else {
164 // For other fields, assume they support sqrt
165 return true;
166 }
167 }();
168
174 std::array<Field, INTERNAL_STATE_SIZE> field_internal_state;
175
182
187
192
196 size_t max_steps;
197
201 size_t step_count{};
202
211 FieldVM(bool with_debug = false, size_t max_steps = SIZE_MAX)
214 {
215 // Initialize with all operations enabled by default
217 settings.enable_add = true;
220 settings.enable_mul = true;
222 settings.enable_sub = true;
224 settings.enable_div = true;
226 settings.enable_inv = true;
227 settings.enable_neg = true;
228 settings.enable_sqr = true;
230 settings.enable_pow = true;
233 settings.enable_equal = true;
240 settings.reserved = 0;
241
242 for (size_t i = 0; i < INTERNAL_STATE_SIZE; i++) {
243 field_internal_state[i] = Field::zero();
245 }
246 }
247
258 size_t execute_instruction(const unsigned char* data_ptr)
259 {
267 auto get_index = [&](const unsigned char* data_ptr_index, size_t offset) -> size_t {
268 return static_cast<size_t>(data_ptr_index[offset]) % INTERNAL_STATE_SIZE;
269 };
270
271 // Read the instruction and map it to a valid instruction by taking modulo
272 constexpr size_t NUM_INSTRUCTIONS =
273 static_cast<size_t>(Instruction::BATCH_INVERT) + 1;
274 uint8_t original_instruction = *data_ptr;
275 Instruction instruction = static_cast<Instruction>(original_instruction % NUM_INSTRUCTIONS);
276 if (with_debug) {
277 const char* instruction_names[] = { "SET_VALUE", "ADD", "ADD_ASSIGN",
278 "INCREMENT", "MUL", "MUL_ASSIGN",
279 "SUB", "SUB_ASSIGN", "DIV",
280 "DIV_ASSIGN", "INV", "NEG",
281 "SQR", "SQR_ASSIGN", "POW",
282 "SQRT", "IS_ZERO", "EQUAL",
283 "NOT_EQUAL", "TO_MONTGOMERY", "FROM_MONTGOMERY",
284 "REDUCE_ONCE", "SELF_REDUCE", "BATCH_INVERT" };
285 const char* instruction_name =
286 (static_cast<int>(instruction) >= 0 &&
287 static_cast<int>(instruction) <
288 static_cast<int>(sizeof(instruction_names) / sizeof(instruction_names[0])))
289 ? instruction_names[static_cast<int>(instruction)]
290 : "UNKNOWN";
291 std::cout << "Executing instruction: " << instruction_name << " (" << static_cast<int>(instruction)
292 << ") at step: " << step_count << std::endl;
293 }
294
302 auto get_value = [&](const unsigned char* data_ptr_value, size_t offset) -> numeric::uint256_t {
304 for (size_t i = 0; i < 4; i++) {
305 limbs[i] = *reinterpret_cast<const uint64_t*>(data_ptr_value + offset + i * 8);
306 }
307 return numeric::uint256_t(limbs[0], limbs[1], limbs[2], limbs[3]);
308 };
309
317 auto get_uint64 = [&](const unsigned char* data_ptr_value, size_t offset) -> uint64_t {
318 return *reinterpret_cast<const uint64_t*>(data_ptr_value + offset);
319 };
320 // Execute the instruction
321 switch (instruction) {
324 return SET_VALUE_SIZE; // Skip disabled operation but return correct size
325 }
326 // Read the value and set the field and uint256_t values
327 {
328 size_t index = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
329 auto value = get_value(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
330 field_internal_state[index] = Field(value);
331 uint_internal_state[index] = value % Field::modulus;
332 if (with_debug) {
333 info("SET_VALUE: index: ", index, " value: ", value);
334 }
335 }
336 return SET_VALUE_SIZE;
337 case Instruction::ADD:
338 if (!settings.enable_add) {
339 return ADD_SIZE; // Skip disabled operation but return correct size
340 }
341 {
342 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
343 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
344 size_t index3 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE * 2);
346 if constexpr (LARGE_MODULUS) {
347 uint_internal_state[index3] = ((static_cast<uint512_t>(uint_internal_state[index1]) +
348 static_cast<uint512_t>(uint_internal_state[index2])) %
349 static_cast<uint512_t>(Field::modulus))
350 .lo;
351 } else {
352 uint_internal_state[index3] =
353 (uint_internal_state[index1] + uint_internal_state[index2]) % Field::modulus;
354 }
355 if (with_debug) {
356 info("ADD: index1: ",
357 index1,
358 " index2: ",
359 index2,
360 " index3: ",
361 index3,
362 " value: ",
363 field_internal_state[index3]);
364 }
365 }
366 return ADD_SIZE;
369 return ADD_ASSIGN_SIZE; // Skip disabled operation but return correct size
370 }
371 // Read the two operands
372 {
373 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
374 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
376 if constexpr (LARGE_MODULUS) {
377 uint_internal_state[index1] = ((static_cast<uint512_t>(uint_internal_state[index1]) +
378 static_cast<uint512_t>(uint_internal_state[index2])) %
379 static_cast<uint512_t>(Field::modulus))
380 .lo;
381 } else {
382 uint_internal_state[index1] =
383 (uint_internal_state[index1] + uint_internal_state[index2]) % Field::modulus;
384 }
385 if (with_debug) {
386 info("ADD_ASSIGN: index1: ", index1, " index2: ", index2, " value: ", field_internal_state[index1]);
387 }
388 }
389 return ADD_ASSIGN_SIZE;
392 return INCREMENT_SIZE; // Skip disabled operation but return correct size
393 }
394 // Read the operand
395 {
396 size_t index = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
397 field_internal_state[index]++;
398 if constexpr (LARGE_MODULUS) {
399 uint_internal_state[index] =
400 ((static_cast<uint512_t>(uint_internal_state[index]) + static_cast<uint512_t>(1)) %
401 static_cast<uint512_t>(Field::modulus))
402 .lo;
403 } else {
404 uint_internal_state[index] = (uint_internal_state[index] + 1) % Field::modulus;
405 }
406 if (with_debug) {
407 info("INCREMENT: index: ", index, " value: ", field_internal_state[index]);
408 }
409 }
410 return INCREMENT_SIZE;
411 case Instruction::MUL:
412 if (!settings.enable_mul) {
413 return MUL_SIZE; // Skip disabled operation but return correct size
414 }
415 // Read the two operands
416 {
417 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
418 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
419 size_t index3 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE * 2);
421 uint_internal_state[index3] = ((static_cast<uint512_t>(uint_internal_state[index1]) *
422 static_cast<uint512_t>(uint_internal_state[index2])) %
423 static_cast<uint512_t>(Field::modulus))
424 .lo;
425 if (with_debug) {
426 info("MUL: index1: ",
427 index1,
428 " index2: ",
429 index2,
430 " index3: ",
431 index3,
432 " value: ",
433 field_internal_state[index3]);
434 }
435 }
436 return MUL_SIZE;
439 return MUL_ASSIGN_SIZE; // Skip disabled operation but return correct size
440 }
441 // Read the two operands
442 {
443 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
444 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
446 uint_internal_state[index1] = ((static_cast<uint512_t>(uint_internal_state[index1]) *
447 static_cast<uint512_t>(uint_internal_state[index2])) %
448 static_cast<uint512_t>(Field::modulus))
449 .lo;
450 if (with_debug) {
451 info("MUL_ASSIGN: index1: ", index1, " index2: ", index2, " value: ", field_internal_state[index1]);
452 }
453 }
454 return MUL_ASSIGN_SIZE;
455 case Instruction::SUB:
456 if (!settings.enable_sub) {
457 return SUB_SIZE; // Skip disabled operation but return correct size
458 }
459 // Read the two operands
460 {
461 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
462 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
463 size_t index3 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE * 2);
465 if constexpr (LARGE_MODULUS) {
466 uint_internal_state[index3] =
467 ((static_cast<uint512_t>(Field::modulus) + static_cast<uint512_t>(uint_internal_state[index1]) -
468 static_cast<uint512_t>(uint_internal_state[index2])) %
469 static_cast<uint512_t>(Field::modulus))
470 .lo;
471 } else {
472 uint_internal_state[index3] =
473 (Field::modulus + uint_internal_state[index1] - uint_internal_state[index2]) % Field::modulus;
474 }
475 if (with_debug) {
476 info("SUB: index1: ",
477 index1,
478 " index2: ",
479 index2,
480 " index3: ",
481 index3,
482 " value: ",
483 field_internal_state[index3]);
484 }
485 }
486 return SUB_SIZE;
489 return SUB_ASSIGN_SIZE; // Skip disabled operation but return correct size
490 }
491 // Read the two operands
492 {
493 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
494 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
496 if constexpr (LARGE_MODULUS) {
497 uint_internal_state[index1] =
498 ((static_cast<uint512_t>(Field::modulus) + static_cast<uint512_t>(uint_internal_state[index1]) -
499 static_cast<uint512_t>(uint_internal_state[index2])) %
500 static_cast<uint512_t>(Field::modulus))
501 .lo;
502 } else {
503 uint_internal_state[index1] =
504 (Field::modulus + uint_internal_state[index1] - uint_internal_state[index2]) % Field::modulus;
505 }
506 if (with_debug) {
507 info("SUB_ASSIGN: index1: ", index1, " index2: ", index2, " value: ", field_internal_state[index1]);
508 }
509 }
510 return SUB_ASSIGN_SIZE;
511 case Instruction::DIV:
512 if (!settings.enable_div) {
513 return DIV_SIZE; // Skip disabled operation but return correct size
514 }
515 // Read the two operands
516 {
517 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
518 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
519 size_t index3 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE * 2);
520 // Skip division if divisor is zero
521 if (!field_internal_state[index2].is_zero()) {
523 // For uint256_t, we'll compute division using the field result
524 assert((uint512_t(static_cast<numeric::uint256_t>(field_internal_state[index3])) *
526 uint512_t(Field::modulus) ==
528 uint_internal_state[index3] = static_cast<numeric::uint256_t>(field_internal_state[index3]);
529 }
530 if (with_debug) {
531 info("DIV: index1: ",
532 index1,
533 " index2: ",
534 index2,
535 " index3: ",
536 index3,
537 " value: ",
538 field_internal_state[index3]);
539 }
540 }
541 return DIV_SIZE;
544 return DIV_ASSIGN_SIZE; // Skip disabled operation but return correct size
545 }
546 // Read the two operands
547 {
548 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
549 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
550 auto original_value = uint_internal_state[index1];
551 // Skip division if divisor is zero
552 if (!field_internal_state[index2].is_zero()) {
554 // For uint256_t, we'll compute division using the field result
555 if (!((uint512_t(static_cast<numeric::uint256_t>(field_internal_state[index1])) *
556 static_cast<uint512_t>(uint_internal_state[index2])) %
557 static_cast<uint512_t>(Field::modulus) ==
558 static_cast<uint512_t>(uint_internal_state[index1]))) {
559 // Deliberately set to different value
561 }
562 uint_internal_state[index1] = static_cast<numeric::uint256_t>(field_internal_state[index1]);
563 }
564 if (with_debug) {
565 info("DIV_ASSIGN: index1: ",
566 index1,
567 " index2: ",
568 index2,
569 " value: ",
570 field_internal_state[index1],
571 " original_value: ",
572 original_value);
573 }
574 }
575 return DIV_ASSIGN_SIZE;
576 case Instruction::INV:
577 if (!settings.enable_inv) {
578 return INV_SIZE; // Skip disabled operation but return correct size
579 }
580 // Read the operand
581 {
582 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
583 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
584 auto original_value = uint_internal_state[index2];
585 // Skip inversion if operand is zero
586 if (!field_internal_state[index1].is_zero()) {
587 field_internal_state[index2] = field_internal_state[index1].invert();
588 // For uint256_t, we'll compute inversion using the field result
589 if (!((uint512_t(static_cast<numeric::uint256_t>(field_internal_state[index2])) *
590 static_cast<uint512_t>(uint_internal_state[index1])) %
591 static_cast<uint512_t>(Field::modulus) ==
592 static_cast<uint512_t>(1))) {
593 // Deliberately set to different value
595 } else {
596 uint_internal_state[index2] = static_cast<numeric::uint256_t>(field_internal_state[index2]);
597 }
598 }
599 if (with_debug) {
600 info("INV: index1: ",
601 index1,
602 " index2: ",
603 index2,
604 " value: ",
605 field_internal_state[index2],
606 " original_value: ",
607 original_value);
608 }
609 }
610 return INV_SIZE;
611 case Instruction::NEG:
612 if (!settings.enable_neg) {
613 return NEG_SIZE; // Skip disabled operation but return correct size
614 }
615 // Read the operand
616 {
617 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
618 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
620 if constexpr (LARGE_MODULUS) {
621 uint_internal_state[index2] = ((static_cast<uint512_t>(Field::modulus) -
622 static_cast<uint512_t>(uint_internal_state[index1])) %
623 static_cast<uint512_t>(Field::modulus))
624 .lo;
625 } else {
626 uint_internal_state[index2] = (Field::modulus - uint_internal_state[index1]) % Field::modulus;
627 }
628 if (with_debug) {
629 info("NEG: index1: ", index1, " index2: ", index2, " value: ", field_internal_state[index2]);
630 }
631 }
632 return NEG_SIZE;
633 case Instruction::SQR:
634 if (!settings.enable_sqr) {
635 return SQR_SIZE; // Skip disabled operation but return correct size
636 }
637 // Read the operand
638 {
639 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
640 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
641 field_internal_state[index2] = field_internal_state[index1].sqr();
642 if constexpr (LARGE_MODULUS) {
643 uint_internal_state[index2] = ((static_cast<uint512_t>(uint_internal_state[index1]) *
644 static_cast<uint512_t>(uint_internal_state[index1])) %
645 static_cast<uint512_t>(Field::modulus))
646 .lo;
647 } else {
648 uint_internal_state[index2] = ((static_cast<uint512_t>(uint_internal_state[index1]) *
649 static_cast<uint512_t>(uint_internal_state[index1])) %
650 static_cast<uint512_t>(Field::modulus))
651 .lo;
652 }
653 if (with_debug) {
654 info("SQR: index1: ", index1, " index2: ", index2, " value: ", field_internal_state[index2]);
655 }
656 }
657 return SQR_SIZE;
660 return SQR_ASSIGN_SIZE; // Skip disabled operation but return correct size
661 }
662 // Read the operand
663 {
664 size_t index = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
665 field_internal_state[index].self_sqr();
666 if constexpr (LARGE_MODULUS) {
667 uint_internal_state[index] = ((static_cast<uint512_t>(uint_internal_state[index]) *
668 static_cast<uint512_t>(uint_internal_state[index])) %
669 static_cast<uint512_t>(Field::modulus))
670 .lo;
671 } else {
672 uint_internal_state[index] = ((static_cast<uint512_t>(uint_internal_state[index]) *
673 static_cast<uint512_t>(uint_internal_state[index])) %
674 static_cast<uint512_t>(Field::modulus))
675 .lo;
676 }
677 if (with_debug) {
678 info("SQR_ASSIGN: index: ", index, " value: ", field_internal_state[index]);
679 }
680 }
681 return SQR_ASSIGN_SIZE;
682 case Instruction::POW:
683 if (!settings.enable_pow) {
684 return POW_SIZE; // Skip disabled operation but return correct size
685 }
686 // Read the operand and exponent
687 {
688 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
689 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
690 uint64_t exponent = get_uint64(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE * 2);
691 field_internal_state[index2] = field_internal_state[index1].pow(exponent);
692 auto multiplicand = static_cast<uint512_t>(uint_internal_state[index1]);
693 auto current = static_cast<uint512_t>(1);
694 while (exponent > 0) {
695 if (exponent & 1) {
696 current = (current * multiplicand) % static_cast<uint512_t>(Field::modulus);
697 }
698 multiplicand = (multiplicand * multiplicand) % static_cast<uint512_t>(Field::modulus);
699 exponent >>= 1;
700 }
701 uint_internal_state[index2] = current.lo;
702 if (with_debug) {
703 info("POW: index1: ",
704 index1,
705 " index2: ",
706 index2,
707 " exponent: ",
708 exponent,
709 " value: ",
710 field_internal_state[index2]);
711 }
712 }
713 return POW_SIZE;
716 return SQRT_SIZE; // Skip disabled/unsupported operation but return correct size
717 }
718 // Read the operand
719 {
720 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
721 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
722 if constexpr (SUPPORTS_SQRT) {
723 auto [found, root] = field_internal_state[index1].sqrt();
724 if (found) {
725 field_internal_state[index2] = root;
726 assert(
727 ((static_cast<uint512_t>(static_cast<numeric::uint256_t>(field_internal_state[index2])) *
728 static_cast<uint512_t>(static_cast<numeric::uint256_t>(field_internal_state[index2]))) %
729 static_cast<uint512_t>(Field::modulus)) ==
730 static_cast<uint512_t>(uint_internal_state[index1]));
731 uint_internal_state[index2] = static_cast<numeric::uint256_t>(root);
732 }
733 if (with_debug) {
734 info("SQRT: index1: ",
735 index1,
736 " index2: ",
737 index2,
738 " found: ",
739 found,
740 " value: ",
741 field_internal_state[index2]);
742 }
743 }
744 }
745 return SQRT_SIZE;
748 return IS_ZERO_SIZE; // Skip disabled operation but return correct size
749 }
750 // Read the operand
751 {
752 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
753 assert((uint_internal_state[index1] == 0) == field_internal_state[index1].is_zero());
754 if (with_debug) {
755 info("IS_ZERO: index1: ", index1, " result: ", field_internal_state[index1].is_zero());
756 }
757 }
758 return IS_ZERO_SIZE;
760 if (!settings.enable_equal) {
761 return EQUAL_SIZE; // Skip disabled operation but return correct size
762 }
763 // Read the operands
764 {
765 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
766 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
767 assert((field_internal_state[index1] == field_internal_state[index2]) ==
768 (uint_internal_state[index1] == uint_internal_state[index2]));
769 if (with_debug) {
770 info("EQUAL: index1: ",
771 index1,
772 " index2: ",
773 index2,
774 " result: ",
775 field_internal_state[index1] == field_internal_state[index2]);
776 }
777 }
778 return EQUAL_SIZE;
781 return NOT_EQUAL_SIZE; // Skip disabled operation but return correct size
782 }
783 // Read the operands
784 {
785 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
786 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
787 assert((field_internal_state[index1] != field_internal_state[index2]) ==
788 (uint_internal_state[index1] != uint_internal_state[index2]));
789 if (with_debug) {
790 info("NOT_EQUAL: index1: ",
791 index1,
792 " index2: ",
793 index2,
794 " result: ",
795 field_internal_state[index1] != field_internal_state[index2]);
796 }
797 }
798 return NOT_EQUAL_SIZE;
801 return TO_MONTGOMERY_SIZE; // Skip disabled operation but return correct size
802 }
803 // Read the operand
804 {
805 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
806 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
807 field_internal_state[index2] = field_internal_state[index1].to_montgomery_form();
808 uint_internal_state[index2] = ((static_cast<uint512_t>(uint_internal_state[index1]) << 256) %
809 static_cast<uint512_t>(Field::modulus))
810 .lo;
811 // Note: uint_internal_state doesn't track Montgomery form
812 if (with_debug) {
813 info("TO_MONTGOMERY: index1: ",
814 index1,
815 " index2: ",
816 index2,
817 " value: ",
818 field_internal_state[index2]);
819 }
820 }
821 return TO_MONTGOMERY_SIZE;
824 return FROM_MONTGOMERY_SIZE; // Skip disabled operation but return correct size
825 }
826 // Read the operand
827 {
828 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
829 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
830 field_internal_state[index2] = field_internal_state[index1].from_montgomery_form();
831 if constexpr (LARGE_MODULUS) {
832 // For large modulus fields, use uint512_t to prevent overflow
833 auto value = static_cast<uint512_t>(uint_internal_state[index1]);
834 for (size_t i = 0; i < 256; i++) {
835 if (value & 1) {
836 value += static_cast<uint512_t>(Field::modulus);
837 }
838 value >>= 1;
839 }
840 uint_internal_state[index2] = value.lo;
841 } else {
842 // For small modulus fields, use uint256_t
844 for (size_t i = 0; i < 256; i++) {
845 if (uint_internal_state[index2] & 1) {
846 uint_internal_state[index2] += Field::modulus;
847 }
848 uint_internal_state[index2] >>= 1;
849 }
850 }
851 if (with_debug) {
852 info("FROM_MONTGOMERY: index1: ",
853 index1,
854 " index2: ",
855 index2,
856 " value: ",
857 field_internal_state[index2]);
858 }
859 }
863 return REDUCE_ONCE_SIZE; // Skip disabled operation but return correct size
864 }
865 // Read the operand
866 {
867 size_t index1 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
868 size_t index2 = get_index(data_ptr, INSTRUCTION_HEADER_SIZE + INDEX_SIZE);
869 field_internal_state[index2] = field_internal_state[index1].reduce_once();
871 if (with_debug) {
872 info(
873 "REDUCE_ONCE: index1: ", index1, " index2: ", index2, " value: ", field_internal_state[index2]);
874 }
875 }
876 return REDUCE_ONCE_SIZE;
879 return SELF_REDUCE_SIZE; // Skip disabled operation but return correct size
880 }
881 // Read the operand
882 {
883 size_t index = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
884 field_internal_state[index].self_reduce_once();
885 if (with_debug) {
886 info("SELF_REDUCE: index: ", index, " value: ", field_internal_state[index]);
887 }
888 }
889 return SELF_REDUCE_SIZE;
892 return BATCH_INVERT_SIZE; // Skip disabled operation but return correct size
893 }
894 // Read the operands and count
895 {
896 size_t start_index = get_index(data_ptr, INSTRUCTION_HEADER_SIZE);
897 size_t count =
898 static_cast<size_t>(data_ptr[INSTRUCTION_HEADER_SIZE + INDEX_SIZE]) % (INTERNAL_STATE_SIZE / 2);
899 if (count == 0) {
900 count = 1; // Ensure at least one element
901 }
902 if (start_index + count > INTERNAL_STATE_SIZE) {
903 count = INTERNAL_STATE_SIZE - start_index;
904 }
905
906 // Store original values for comparison
907 std::vector<Field> original_elements;
908 std::vector<numeric::uint256_t> original_uint_elements;
909 std::vector<size_t> valid_indices;
910
911 for (size_t i = 0; i < count; i++) {
912 size_t idx = start_index + i;
913 original_elements.push_back(field_internal_state[idx]);
914 original_uint_elements.push_back(uint_internal_state[idx]);
915 valid_indices.push_back(idx);
916 }
917
918 // Perform individual inversions for comparison
919 std::vector<Field> individual_inverses;
920 std::vector<numeric::uint256_t> individual_uint_inverses;
921 for (size_t idx : valid_indices) {
922 if (!field_internal_state[idx].is_zero()) {
923 Field inv = field_internal_state[idx].invert();
924 individual_inverses.push_back(inv);
925 individual_uint_inverses.push_back(static_cast<numeric::uint256_t>(inv));
926 } else {
927 individual_inverses.push_back(Field::zero());
928 individual_uint_inverses.push_back(numeric::uint256_t(0));
929 }
930 }
931
932 // Collect non-zero elements for batch inversion
933 std::vector<Field> non_zero_elements;
934 std::vector<size_t> non_zero_indices;
935 for (size_t i = 0; i < count; i++) {
936 size_t idx = start_index + i;
937 if (!field_internal_state[idx].is_zero()) {
938 non_zero_elements.push_back(field_internal_state[idx]);
939 non_zero_indices.push_back(idx);
940 }
941 }
942
943 if (!non_zero_elements.empty()) {
944 // Perform batch inversion (modifies non_zero_elements in-place)
945 Field::batch_invert(non_zero_elements);
946
947 // Store batch results back
948 for (size_t i = 0; i < non_zero_indices.size(); i++) {
949 size_t idx = non_zero_indices[i];
950 field_internal_state[idx] = non_zero_elements[i];
951 uint_internal_state[idx] = static_cast<numeric::uint256_t>(non_zero_elements[i]);
952 }
953 }
954
955 // Verify that batch inversion produces the same results as individual inversions
956 for (size_t i = 0; i < valid_indices.size(); i++) {
957 size_t idx = valid_indices[i];
958 if (!original_elements[i].is_zero()) {
959 // Check that batch and individual inversions match
960 assert(field_internal_state[idx] == individual_inverses[i]);
961 assert(uint_internal_state[idx] == individual_uint_inverses[i]);
962
963 // Verify the inverse property: a * a^(-1) = 1
964 Field product = original_elements[i] * field_internal_state[idx];
965 assert(product == Field::one());
966
967 // Verify uint arithmetic consistency
968 auto uint_product = (static_cast<uint512_t>(original_uint_elements[i]) *
969 static_cast<uint512_t>(uint_internal_state[idx])) %
970 static_cast<uint512_t>(Field::modulus);
971 assert(uint_product == static_cast<uint512_t>(1));
972 } else {
973 // Zero elements should remain zero
974 assert(field_internal_state[idx] == Field::zero());
975 assert(uint_internal_state[idx] == numeric::uint256_t(0));
976 }
977 }
978
979 if (with_debug) {
980 info("BATCH_INVERT: start_index: ",
981 start_index,
982 " count: ",
983 count,
984 " non_zero_count: ",
985 non_zero_elements.size());
986 }
987 }
988 return BATCH_INVERT_SIZE;
989 }
990 }
991
1000 std::vector<uint8_t> data;
1001 size_t size;
1002 };
1003
1017 size_t Size,
1018 size_t max_steps)
1019 {
1020 std::vector<ParsedInstruction> instructions;
1021 size_t data_offset = 0;
1022 size_t steps_parsed = 0;
1023
1024 // Skip settings if present
1025 if (Size >= SETTINGS_SIZE) {
1026 const VMSettings* settings_ptr = reinterpret_cast<const VMSettings*>(Data);
1027 settings = *settings_ptr;
1028 data_offset += SETTINGS_SIZE;
1029 }
1030
1031 while (data_offset < Size && steps_parsed < max_steps) {
1032 if (data_offset >= Size) {
1033 break;
1034 }
1035
1036 Instruction instruction = static_cast<Instruction>(Data[data_offset]);
1037 size_t instruction_size = 0;
1038
1039 // Map the instruction to a valid instruction by taking modulo
1040 constexpr size_t NUM_INSTRUCTIONS =
1041 static_cast<size_t>(Instruction::BATCH_INVERT) + 1;
1042 uint8_t original_instruction = Data[data_offset];
1043 instruction = static_cast<Instruction>(original_instruction % NUM_INSTRUCTIONS);
1044
1045 // Determine instruction size based on type
1046 switch (instruction) {
1048 instruction_size = SET_VALUE_SIZE;
1049 break;
1050 case Instruction::ADD:
1051 instruction_size = ADD_SIZE;
1052 break;
1054 instruction_size = ADD_ASSIGN_SIZE;
1055 break;
1057 instruction_size = INCREMENT_SIZE;
1058 break;
1059 case Instruction::MUL:
1060 instruction_size = MUL_SIZE;
1061 break;
1063 instruction_size = MUL_ASSIGN_SIZE;
1064 break;
1065 case Instruction::SUB:
1066 instruction_size = SUB_SIZE;
1067 break;
1069 instruction_size = SUB_ASSIGN_SIZE;
1070 break;
1071 case Instruction::DIV:
1072 instruction_size = DIV_SIZE;
1073 break;
1075 instruction_size = DIV_ASSIGN_SIZE;
1076 break;
1077 case Instruction::INV:
1078 instruction_size = INV_SIZE;
1079 break;
1080 case Instruction::NEG:
1081 instruction_size = NEG_SIZE;
1082 break;
1083 case Instruction::SQR:
1084 instruction_size = SQR_SIZE;
1085 break;
1087 instruction_size = SQR_ASSIGN_SIZE;
1088 break;
1089 case Instruction::POW:
1090 instruction_size = POW_SIZE;
1091 break;
1092 case Instruction::SQRT:
1093 instruction_size = SQRT_SIZE;
1094 break;
1096 instruction_size = IS_ZERO_SIZE;
1097 break;
1098 case Instruction::EQUAL:
1099 instruction_size = EQUAL_SIZE;
1100 break;
1102 instruction_size = NOT_EQUAL_SIZE;
1103 break;
1105 instruction_size = TO_MONTGOMERY_SIZE;
1106 break;
1108 instruction_size = FROM_MONTGOMERY_SIZE;
1109 break;
1111 instruction_size = REDUCE_ONCE_SIZE;
1112 break;
1114 instruction_size = SELF_REDUCE_SIZE;
1115 break;
1117 instruction_size = BATCH_INVERT_SIZE;
1118 break;
1119 }
1120
1121 // Check if we have enough data for this instruction
1122 if (data_offset + instruction_size > Size) {
1123 break;
1124 }
1125
1126 // Create parsed instruction
1127 ParsedInstruction parsed;
1128 parsed.instruction = instruction;
1129 parsed.size = instruction_size;
1130 parsed.data.resize(instruction_size);
1131
1132 // Only copy the data that's actually available
1133 size_t data_to_copy = std::min(instruction_size, Size - data_offset);
1134 std::memcpy(parsed.data.data(), Data + data_offset, data_to_copy);
1135
1136 // If we couldn't copy all the data, pad with zeros
1137 if (data_to_copy < instruction_size) {
1138 std::memset(parsed.data.data() + data_to_copy, 0, instruction_size - data_to_copy);
1139 }
1140
1141 instructions.push_back(parsed);
1142
1143 data_offset += instruction_size;
1144 steps_parsed++;
1145 }
1146
1147 return { instructions, data_offset };
1148 }
1149
1160 {
1161 if (with_debug) {
1162 const char* instruction_names[] = { "SET_VALUE", "ADD", "ADD_ASSIGN",
1163 "INCREMENT", "MUL", "MUL_ASSIGN",
1164 "SUB", "SUB_ASSIGN", "DIV",
1165 "DIV_ASSIGN", "INV", "NEG",
1166 "SQR", "SQR_ASSIGN", "POW",
1167 "SQRT", "IS_ZERO", "EQUAL",
1168 "NOT_EQUAL", "TO_MONTGOMERY", "FROM_MONTGOMERY",
1169 "REDUCE_ONCE", "SELF_REDUCE", "BATCH_INVERT" };
1170 const char* instruction_name =
1171 (static_cast<int>(parsed.instruction) >= 0 &&
1172 static_cast<int>(parsed.instruction) <
1173 static_cast<int>(sizeof(instruction_names) / sizeof(instruction_names[0])))
1174 ? instruction_names[static_cast<int>(parsed.instruction)]
1175 : "UNKNOWN";
1176 std::cout << "Executing instruction: " << instruction_name << " (" << static_cast<int>(parsed.instruction)
1177 << ") at step: " << step_count << std::endl;
1178 }
1179
1180 // Execute the instruction using the existing logic
1181 size_t consumed = execute_instruction(parsed.data.data());
1182 return consumed > 0;
1183 }
1184
1197 size_t run(const unsigned char* Data, size_t Size, bool reset_steps = true)
1198 {
1199 if (reset_steps) {
1200 step_count = 0;
1201 }
1202
1203 if (with_debug) {
1204 std::cout << "Starting VM run with " << Size << " bytes of data, max_steps: " << max_steps << std::endl;
1205 }
1206
1207 // First parse all instructions into a vector
1208 auto [instructions, bytes_consumed] = parse_instructions(Data, Size, max_steps);
1209
1210 if (with_debug) {
1211 std::cout << "Parsed " << instructions.size() << " instructions, consumed " << bytes_consumed << " bytes"
1212 << std::endl;
1213 }
1214
1215 // Then execute the parsed instructions
1216 for (const auto& instruction : instructions) {
1217 if (step_count >= max_steps) {
1218 break;
1219 }
1220
1222 if (!success) {
1223 break;
1224 }
1225 step_count++;
1226 }
1227
1228 return bytes_consumed; // Return the number of bytes consumed during parsing
1229 }
1230
1241 {
1242 for (size_t i = 0; i < INTERNAL_STATE_SIZE; i++) {
1243 if (field_internal_state[i] != Field(uint_internal_state[i])) {
1244 if (with_debug) {
1245 std::cout << "check_internal_state: index: " << i << " field: " << field_internal_state[i]
1246 << " uint: " << uint_internal_state[i] << std::endl;
1247 }
1248 return false;
1249 }
1250 }
1251 return true;
1252 }
1253
1263 {
1265 result.reserve(INTERNAL_STATE_SIZE);
1266 for (size_t i = 0; i < INTERNAL_STATE_SIZE; i++) {
1267 result.push_back(uint_internal_state[i]);
1268 }
1269 return result;
1270 }
1271
1277 size_t get_step_count() const { return step_count; }
1278
1285
1291 void set_max_steps(size_t new_max_steps) { max_steps = new_max_steps; }
1292
1297
1303 size_t get_max_steps() const { return max_steps; }
1304
1310 bool has_remaining_steps() const { return step_count < max_steps; }
1311
1322 {
1323 if constexpr (LARGE_MODULUS) {
1324 return (uint512_t(value) % uint512_t(Field::modulus)).lo;
1325 } else {
1326 return value % Field::modulus;
1327 }
1328 }
1329
1340 {
1341 for (size_t i = 0; i < std::min(state.size(), size_t(INTERNAL_STATE_SIZE)); i++) {
1342 // Check that uint_internal_state matches the reduced state
1343 if (uint_internal_state[i] != reduce_to_modulus(state[i])) {
1344 return false;
1345 }
1346 // Check that field_internal_state is consistent with uint_internal_state
1347 if (field_internal_state[i] != Field(uint_internal_state[i])) {
1348 return false;
1349 }
1350 }
1351 return true;
1352 }
1353};
1354
1355} // namespace bb
void info(Args... args)
Definition log.hpp:70
uint64_t get_index(uint64_t max_index=0)
ssize_t offset
Definition engine.cpp:36
Instruction instruction
uintx< uint256_t > uint512_t
Definition uintx.hpp:307
Entry point for Barretenberg command-line interface.
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.