Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
cycle_group.fuzzer.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
35#pragma once
40#pragma clang diagnostic push
41
42// -Wc99-designator prevents us from using designators and nested designators
43// in struct intializations
44// such as {.in.first = a, .out = b}, since it's not a part of c++17 standard
45// However the use of them in this particular file heavily increases
46// the readability and conciseness of the CycleGroupBase::Instruction initializations
47#pragma clang diagnostic ignored "-Wc99-designator"
48
49#ifdef FUZZING_SHOW_INFORMATION
54struct FormattedArgs {
55 std::string lhs;
56 std::string rhs;
57 std::string out;
58};
59
67template <typename Stack>
68inline FormattedArgs format_single_arg(const Stack& stack, size_t first_index, size_t output_index)
69{
70 std::string rhs = stack[first_index].cycle_group.is_constant() ? "c" : "w";
71 std::string out = rhs;
72 rhs += std::to_string(first_index);
73 out += std::to_string(output_index >= stack.size() ? stack.size() : output_index);
74 out = (output_index >= stack.size() ? "auto " : "") + out;
75 return FormattedArgs{ .lhs = "", .rhs = rhs, .out = out };
76}
77
86template <typename Stack>
87inline FormattedArgs format_two_arg(const Stack& stack, size_t first_index, size_t second_index, size_t output_index)
88{
89 std::string lhs = stack[first_index].cycle_group.is_constant() ? "c" : "w";
90 std::string rhs = stack[second_index].cycle_group.is_constant() ? "c" : "w";
91 std::string out =
92 (stack[first_index].cycle_group.is_constant() && stack[second_index].cycle_group.is_constant()) ? "c" : "w";
93 lhs += std::to_string(first_index);
94 rhs += std::to_string(second_index);
95 out += std::to_string(output_index >= stack.size() ? stack.size() : output_index);
96 out = (output_index >= stack.size() ? "auto " : "") + out;
97 return FormattedArgs{ .lhs = lhs, .rhs = rhs, .out = out };
98}
99#endif
100
101#define HAVOC_TESTING
102
103// This is a global variable, so that the execution handling class could alter it and signal to the input tester
104// that the input should fail
106
108
109// #define FUZZING_SHOW_INFORMATION
110
111// #define DISABLE_MULTIPLICATION
112// #define DISABLE_BATCH_MUL
114
115constexpr size_t MINIMUM_MUL_ELEMENTS = 0;
116constexpr size_t MAXIMUM_MUL_ELEMENTS = 8;
117
118// This is an external function in Libfuzzer used internally by custom mutators
119extern "C" size_t LLVMFuzzerMutate(uint8_t* Data, size_t Size, size_t MaxSize);
120
128enum class SpecialScalarValue : uint8_t {
129 One = 0,
130 MinusOne,
133 RootOfUnity13, // 13th root of unity (arbitrary small root)
134 Two, // Small even number
135 HalfModulus, // (p-1)/2
136 Zero,
137 COUNT // Sentinel value for total count
138};
139
140// Number of special values excluding Zero
141constexpr uint8_t SPECIAL_VALUE_COUNT_NO_ZERO = static_cast<uint8_t>(SpecialScalarValue::Zero);
142
143// Number of special values including Zero
144constexpr uint8_t SPECIAL_VALUE_COUNT = static_cast<uint8_t>(SpecialScalarValue::COUNT);
145
153{
154 switch (type) {
156 return FF::one();
158 return -FF::one();
160 return FF::one().sqrt().second;
162 return FF::one().sqrt().second.invert();
164 return FF::get_root_of_unity(13);
166 return FF(2);
168 return FF((FF::modulus - 1) / 2);
170 return FF::zero();
172 // Fallthrough to abort - COUNT should never be used as a value
173 default:
174 abort(); // Invalid enum value
175 }
176}
177
181template <typename Builder> class CycleGroupBase {
182 private:
189 using GroupElement = typename Curve::Element;
192 using BaseField = typename Curve::BaseField;
193
194 public:
196 public:
197 enum OPCODE {
208#ifndef DISABLE_MULTIPLICATION
210#endif
211#ifndef DISABLE_BATCH_MUL
213#endif
215 _LAST
216 };
217
218 struct Element {
219 Element(ScalarField s = ScalarField::one(), GroupElement g = GroupElement::one())
220 : scalar(s)
221 , value(g) {};
224 };
225
226 struct TwoArgs {
227 uint8_t in;
228 uint8_t out;
229 };
230
231 struct MulArgs {
232 uint8_t in;
233 uint8_t out;
235 };
236
237 struct ThreeArgs {
238 uint8_t in1;
239 uint8_t in2;
240 uint8_t out;
241 };
242
243 struct FourArgs {
244 uint8_t in1;
245 uint8_t in2;
246 uint8_t in3;
247 uint8_t out;
248 };
249
256
268
269 // The type of the instruction
271
272 // Instruction arguments
274
282 template <typename T>
283 inline static Instruction generateRandom(T& rng)
284 requires SimpleRng<T>
285 {
286 OPCODE instruction_opcode = static_cast<OPCODE>(rng.next() % (OPCODE::_LAST));
287 uint8_t in, in1, in2, in3, out;
288 Instruction instr;
289
290 switch (instruction_opcode) {
291 case OPCODE::CONSTANT:
292 case OPCODE::WITNESS:
294 auto scalar = ScalarField(static_cast<uint64_t>(fast_log_distributed_uint256(rng)));
295 auto el = GroupElement::one() * scalar;
296 return { .id = instruction_opcode, .arguments.element = Element(scalar, el) };
297 }
298 case OPCODE::DBL:
299 case OPCODE::NEG:
301 case OPCODE::SET:
302 in = static_cast<uint8_t>(rng.next() & 0xff);
303 out = static_cast<uint8_t>(rng.next() & 0xff);
304 return { .id = instruction_opcode, .arguments.twoArgs = { .in = in, .out = out } };
305 case OPCODE::ADD:
306 case OPCODE::SUBTRACT:
307 in1 = static_cast<uint8_t>(rng.next() & 0xff);
308 in2 = static_cast<uint8_t>(rng.next() & 0xff);
309 out = static_cast<uint8_t>(rng.next() & 0xff);
310 return { .id = instruction_opcode,
311 .arguments.threeArgs.in1 = in1,
312 .arguments.threeArgs.in2 = in2,
313 .arguments.threeArgs.out = out };
315 in1 = static_cast<uint8_t>(rng.next() & 0xff);
316 in2 = static_cast<uint8_t>(rng.next() & 0xff);
317 in3 = static_cast<uint8_t>(rng.next() & 0xff);
318 out = static_cast<uint8_t>(rng.next() & 0xff);
319 return { .id = instruction_opcode,
320 .arguments.fourArgs.in1 = in1,
321 .arguments.fourArgs.in2 = in2,
322 .arguments.fourArgs.in3 = in3,
323 .arguments.fourArgs.out = out };
324#ifndef DISABLE_MULTIPLICATION
325 case OPCODE::MULTIPLY:
326 in = static_cast<uint8_t>(rng.next() & 0xff);
327 out = static_cast<uint8_t>(rng.next() & 0xff);
328 return { .id = instruction_opcode,
329 .arguments.mulArgs.scalar = ScalarField(fast_log_distributed_uint256(rng)),
330 .arguments.mulArgs.in = in,
331 .arguments.mulArgs.out = out };
332#endif
333#ifndef DISABLE_BATCH_MUL
334 case OPCODE::BATCH_MUL: {
335 uint8_t mult_size0 =
337 static_cast<uint8_t>(rng.next() % ((MAXIMUM_MUL_ELEMENTS - MINIMUM_MUL_ELEMENTS) / 2));
338 uint8_t mult_size1 =
340 static_cast<uint8_t>(rng.next() % ((MAXIMUM_MUL_ELEMENTS - MINIMUM_MUL_ELEMENTS) / 2));
341 uint8_t mult_size =
342 mult_size0 +
343 mult_size1; // Sample the amount of batch mul participants from the binomial distribution
344 instr.id = instruction_opcode;
345 instr.arguments.batchMulArgs.add_elements_count = mult_size;
346 for (size_t i = 0; i < mult_size; i++) {
347 instr.arguments.batchMulArgs.inputs[i] = static_cast<uint8_t>(rng.next() & 0xff);
348 }
349 for (size_t i = 0; i < mult_size; i++) {
350 instr.arguments.batchMulArgs.scalars[i] = ScalarField(fast_log_distributed_uint256(rng));
351 }
352 instr.arguments.batchMulArgs.output_index = static_cast<uint8_t>(rng.next() & 0xff);
353 return instr;
354 }
355#endif
357 return { .id = instruction_opcode, .arguments.randomseed = rng.next() * rng.next() };
358
359 default:
360 abort(); // We missed some instructions in switch
361 }
362 }
363
367 template <typename FF> inline static uint256_t to_uint256_montgomery(const FF& value, bool as_montgomery)
368 {
369 if (as_montgomery) {
371 }
372 return uint256_t(value);
373 }
374
378 template <typename FF> inline static FF from_uint256_montgomery(const uint256_t& data, bool from_montgomery)
379 {
380 if (from_montgomery) {
381 return FF(data).from_montgomery_form();
382 }
383 return FF(data);
384 }
385
395 template <typename T>
396 inline static Element mutateGroupElement(Element e, T& rng, HavocSettings& havoc_config)
397 requires SimpleRng<T>
398 {
399 // We can't just randomely modify a point on a curve
400 // But we can modify it's scalar
401 // With a certain probability, we apply changes to the Montgomery form, rather than the plain form. This
402 // has merit, since the computation is performed in montgomery form and comparisons are often performed
403 // in it, too.
404 // By the same logic we can switch between Jacobian and Affine coordinates.
405 // Libfuzzer comparison tracing logic can then be enabled in Montgomery form
406 bool convert_to_montgomery = (rng.next() % (havoc_config.VAL_MUT_MONTGOMERY_PROBABILITY +
407 havoc_config.VAL_MUT_NON_MONTGOMERY_PROBABILITY)) <
408 havoc_config.VAL_MUT_MONTGOMERY_PROBABILITY;
409 bool normalize = (rng.next() % (havoc_config.VAL_MUT_MONTGOMERY_PROBABILITY +
410 havoc_config.VAL_MUT_NON_MONTGOMERY_PROBABILITY)) <
411 havoc_config.VAL_MUT_MONTGOMERY_PROBABILITY;
412 uint256_t value_data;
413
414 // Pick the last value from the mutation distribution vector
415 const size_t mutation_type_count = havoc_config.value_mutation_distribution.size();
416 // Choose mutation
417 const size_t choice = rng.next() % havoc_config.value_mutation_distribution[mutation_type_count - 1];
418 if (choice < havoc_config.value_mutation_distribution[0]) {
419 // Delegate mutation to libfuzzer (bit/byte mutations, autodictionary, etc)
420 value_data = to_uint256_montgomery(e.scalar, convert_to_montgomery);
421 LLVMFuzzerMutate((uint8_t*)&value_data, sizeof(uint256_t), sizeof(uint256_t));
422 e.scalar = from_uint256_montgomery<ScalarField>(value_data, convert_to_montgomery);
423 e.value = GroupElement::one() * e.scalar;
424 } else if (choice < havoc_config.value_mutation_distribution[1]) {
425 // Small addition/subtraction
426 if (convert_to_montgomery) {
427 e.scalar = e.scalar.to_montgomery_form();
428 }
429 auto extra = ScalarField(rng.next() & 0xff);
430
431 // With 50% probability we add/sub a small value
432 if (rng.next() & 1) {
433 auto switch_sign = static_cast<bool>(rng.next() & 1);
434 if (!switch_sign) {
435 e.scalar += extra;
436 e.value += GroupElement::one() * extra;
437 } else {
438 e.scalar -= extra;
439 e.value -= GroupElement::one() * extra;
440 }
441 } else {
442 // otherwise we multiply by a small value
443 e.scalar *= extra;
444 e.value *= extra;
445 }
446 if (normalize) {
447 e.value = e.value.normalize();
448 }
449 if (convert_to_montgomery) {
450 e.scalar = e.scalar.from_montgomery_form();
451 }
452 } else if (choice < havoc_config.value_mutation_distribution[2]) {
453 if (convert_to_montgomery) {
454 e.scalar = e.scalar.to_montgomery_form();
455 }
456 // Substitute scalar element with a special value
457 auto special_value = static_cast<SpecialScalarValue>(rng.next() % SPECIAL_VALUE_COUNT);
458 e.scalar = get_special_scalar_value<ScalarField>(special_value);
459 if (convert_to_montgomery) {
460 e.scalar = e.scalar.to_montgomery_form();
461 }
462 e.value = GroupElement::one() * e.scalar;
463 }
464 // Return value
465 return e;
466 }
476 template <typename T>
477 inline static ScalarField mutateScalarElement(ScalarField e, T& rng, HavocSettings& havoc_config)
478 requires SimpleRng<T>
479 {
480 // With a certain probability, we apply changes to the Montgomery form, rather than the plain form. This
481 // has merit, since the computation is performed in montgomery form and comparisons are often performed
482 // in it, too.
483 // Libfuzzer comparison tracing logic can then be enabled in Montgomery form
484 bool convert_to_montgomery = (rng.next() % (havoc_config.VAL_MUT_MONTGOMERY_PROBABILITY +
485 havoc_config.VAL_MUT_NON_MONTGOMERY_PROBABILITY)) <
486 havoc_config.VAL_MUT_MONTGOMERY_PROBABILITY;
487 uint256_t value_data;
488
489 // Pick the last value from the mutation distrivution vector
490 const size_t mutation_type_count = havoc_config.value_mutation_distribution.size();
491 // Choose mutation
492 const size_t choice = rng.next() % havoc_config.value_mutation_distribution[mutation_type_count - 1];
493 if (choice < havoc_config.value_mutation_distribution[0]) {
494 // Delegate mutation to libfuzzer (bit/byte mutations, autodictionary, etc)
495 value_data = to_uint256_montgomery(e, convert_to_montgomery);
496 LLVMFuzzerMutate((uint8_t*)&value_data, sizeof(uint256_t), sizeof(uint256_t));
497 e = from_uint256_montgomery<ScalarField>(value_data, convert_to_montgomery);
498 } else if (choice < havoc_config.value_mutation_distribution[1]) {
499 // Small addition/subtraction
500 if (convert_to_montgomery) {
501 e = e.to_montgomery_form();
502 }
503 auto extra = ScalarField(rng.next() & 0xff);
504
505 // With 50% probability we add/sub a small value
506 if (rng.next() & 1) {
507 auto switch_sign = static_cast<bool>(rng.next() & 1);
508 if (!switch_sign) {
509 e += extra;
510 } else {
511 e -= extra;
512 }
513 } else {
514 // otherwise we multiply by a small value
515 e *= extra;
516 }
517 if (convert_to_montgomery) {
518 e = e.from_montgomery_form();
519 }
520 } else if (choice < havoc_config.value_mutation_distribution[2]) {
521 if (convert_to_montgomery) {
522 e = e.to_montgomery_form();
523 }
524 // Substitute scalar element with a special value excluding zero
525 // I think that zeros from mutateGroupElement are enough zeros produced
526 auto special_value = static_cast<SpecialScalarValue>(rng.next() % SPECIAL_VALUE_COUNT_NO_ZERO);
527 e = get_special_scalar_value<ScalarField>(special_value);
528 if (convert_to_montgomery) {
529 e = e.to_montgomery_form();
530 }
531 }
532 // Return value
533 return e;
534 }
544 template <typename T>
546 requires SimpleRng<T>
547 {
548#define PUT_RANDOM_BYTE_IF_LUCKY(variable) \
549 if (rng.next() & 1) { \
550 variable = rng.next() & 0xff; \
551 }
552 // Depending on instruction type...
553 switch (instruction.id) {
554 case OPCODE::CONSTANT:
555 case OPCODE::WITNESS:
557 // If it represents pushing a value on the stack with a 50% probability randomly sample a bit_range
558 // Maybe mutate the value
559 if (rng.next() & 1) {
560 instruction.arguments.element =
561 mutateGroupElement(instruction.arguments.element, rng, havoc_config);
562 }
563 break;
564
565 case OPCODE::DBL:
566 case OPCODE::NEG:
568 case OPCODE::SET:
569 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.twoArgs.in);
570 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.twoArgs.out);
571 break;
572#ifndef DISABLE_MULTIPLICATION
573 case OPCODE::MULTIPLY:
574 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.mulArgs.in);
575 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.mulArgs.out);
576 if (rng.next() & 1) {
577 instruction.arguments.mulArgs.scalar =
578 mutateScalarElement(instruction.arguments.mulArgs.scalar, rng, havoc_config);
579 }
580 break;
581#endif
582 case OPCODE::ADD:
583 case OPCODE::SUBTRACT:
584 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.threeArgs.in1);
585 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.threeArgs.in2);
586 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.threeArgs.out);
587 break;
589 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.fourArgs.in1);
590 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.fourArgs.in2);
591 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.fourArgs.in3);
592 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.fourArgs.out);
593 break;
594#ifndef DISABLE_BATCH_MUL
596 if (rng.next() & 1) {
597 uint8_t mult_size0 =
599 static_cast<uint8_t>(rng.next() % ((MAXIMUM_MUL_ELEMENTS - MINIMUM_MUL_ELEMENTS) / 2));
600 uint8_t mult_size1 =
602 static_cast<uint8_t>(rng.next() % ((MAXIMUM_MUL_ELEMENTS - MINIMUM_MUL_ELEMENTS) / 2));
603 uint8_t mult_size =
604 mult_size0 +
605 mult_size1; // Sample the amount of batch mul participants from the binomial distribution
606 instruction.arguments.batchMulArgs.add_elements_count = mult_size;
607 }
608 if (instruction.arguments.batchMulArgs.add_elements_count && (rng.next() & 1)) {
609 size_t mut_count =
610 static_cast<uint8_t>(rng.next() % (instruction.arguments.batchMulArgs.add_elements_count));
611 for (size_t i = 0; i < mut_count; i++) {
612 size_t ind =
613 rng.next() % static_cast<size_t>(instruction.arguments.batchMulArgs.add_elements_count);
614 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.batchMulArgs.inputs[ind]);
615 }
616 }
617 if (instruction.arguments.batchMulArgs.add_elements_count && (rng.next() & 1)) {
618 size_t mut_count =
619 static_cast<uint8_t>(rng.next() % (instruction.arguments.batchMulArgs.add_elements_count));
620 for (size_t i = 0; i < mut_count; i++) {
621 size_t ind =
622 rng.next() % static_cast<size_t>(instruction.arguments.batchMulArgs.add_elements_count);
623 instruction.arguments.batchMulArgs.scalars[ind] =
624 mutateScalarElement(instruction.arguments.batchMulArgs.scalars[ind], rng, havoc_config);
625 }
626 }
627 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.batchMulArgs.output_index);
628 break;
629#endif
631 instruction.arguments.randomseed = rng.next();
632 break;
633 default:
634 abort(); // We missed some instructions in switch
635 return instruction;
636 }
637 return instruction;
638 }
639 };
640 // We use argsizes to both specify the size of data needed to parse the instruction and to signal that the
641 // instruction is enabled (if it is -1,it's disabled )
642 class ArgSizes {
643 public:
644 static constexpr size_t CONSTANT = sizeof(typename Instruction::Element);
645 static constexpr size_t WITNESS = sizeof(typename Instruction::Element);
646 static constexpr size_t CONSTANT_WITNESS = sizeof(typename Instruction::Element);
647 static constexpr size_t DBL = 2;
648 static constexpr size_t NEG = 2;
649 static constexpr size_t ASSERT_EQUAL = 2;
650 static constexpr size_t SET = 2;
651 static constexpr size_t ADD = 3;
652 static constexpr size_t SUBTRACT = 3;
653 static constexpr size_t COND_ASSIGN = 4;
654#ifndef DISABLE_MULTIPLICATION
655 static constexpr size_t MULTIPLY = sizeof(typename Instruction::MulArgs);
656#endif
657#ifndef DISABLE_BATCH_MUL
658 static constexpr size_t BATCH_MUL = sizeof(typename Instruction::BatchMulArgs);
659#endif
660 static constexpr size_t RANDOMSEED = sizeof(uint32_t);
661 };
662
669 public:
670 static constexpr size_t SET = 0;
671 static constexpr size_t RANDOMSEED = 0;
672
673 static constexpr size_t CONSTANT = 1;
674 static constexpr size_t WITNESS = 1;
675 static constexpr size_t CONSTANT_WITNESS = 1;
676 static constexpr size_t ADD = 1;
677 static constexpr size_t SUBTRACT = 1;
678 static constexpr size_t DBL = 1;
679 static constexpr size_t NEG = 1;
680 static constexpr size_t COND_ASSIGN = 1;
681
682#ifndef DISABLE_MULTIPLICATION
683 static constexpr size_t MULTIPLY = 2;
684#endif
685 static constexpr size_t ASSERT_EQUAL = 2;
686
687#ifndef DISABLE_BATCH_MUL
688 static constexpr size_t BATCH_MUL = 4;
689#endif
690 static constexpr size_t _LIMIT = 64;
691 };
696 class Parser {
697 public:
705 template <typename Instruction::OPCODE opcode> inline static Instruction parseInstructionArgs(uint8_t* Data)
706 {
707 Instruction instr;
708 instr.id = static_cast<typename Instruction::OPCODE>(opcode);
709 switch (opcode) {
713 auto scalar = ScalarField::serialize_from_buffer(Data);
714 auto el = GroupElement::one() * scalar;
715 instr.arguments.element = typename Instruction::Element(scalar, el);
716 break;
717 }
722 instr.arguments.twoArgs = { .in = *Data, .out = *(Data + 1) };
723 break;
726 instr.arguments.threeArgs = { .in1 = *Data, .in2 = *(Data + 1), .out = *(Data + 2) };
727 break;
729 instr.arguments.fourArgs = { .in1 = *Data, .in2 = *(Data + 1), .in3 = *(Data + 2), .out = *(Data + 3) };
730 break;
731
732#ifndef DISABLE_MULTIPLICATION
734 instr.arguments.mulArgs.in = *Data;
735 instr.arguments.mulArgs.out = *(Data + 1);
736 instr.arguments.mulArgs.scalar = ScalarField::serialize_from_buffer(Data + 2);
737 break;
738#endif
739#ifndef DISABLE_BATCH_MUL
741 // In case of LLVM native instruction mutator
745 }
746 instr.arguments.batchMulArgs.output_index = *(Data + 1);
747
749 memcpy(instr.arguments.batchMulArgs.inputs, Data + 2, n);
750
751 size_t offset = n + 2;
752 for (size_t i = 0; i < n; i++) {
753 instr.arguments.batchMulArgs.scalars[i] = ScalarField::serialize_from_buffer(Data + offset);
754 offset += sizeof(ScalarField);
755 }
756 break;
757 }
758#endif
760 memcpy(&instr.arguments.randomseed, Data, sizeof(uint32_t));
761 break;
762 default:
763 abort(); // We missed some instructions in switch
764 }
765 return instr;
766 }
774 template <typename Instruction::OPCODE instruction_opcode>
775 inline static void writeInstruction(Instruction& instruction, uint8_t* Data)
776 {
777 *Data = instruction.id;
778 switch (instruction_opcode) {
782 ScalarField::serialize_to_buffer(instruction.arguments.element.scalar, Data + 1);
783 break;
788 *(Data + 1) = instruction.arguments.twoArgs.in;
789 *(Data + 2) = instruction.arguments.twoArgs.out;
790 break;
793 *(Data + 1) = instruction.arguments.threeArgs.in1;
794 *(Data + 2) = instruction.arguments.threeArgs.in2;
795 *(Data + 3) = instruction.arguments.threeArgs.out;
796 break;
798 *(Data + 1) = instruction.arguments.fourArgs.in1;
799 *(Data + 2) = instruction.arguments.fourArgs.in2;
800 *(Data + 3) = instruction.arguments.fourArgs.in3;
801 *(Data + 4) = instruction.arguments.fourArgs.out;
802 break;
803#ifndef DISABLE_MULTIPLICATION
805 *(Data + 1) = instruction.arguments.mulArgs.in;
806 *(Data + 2) = instruction.arguments.mulArgs.out;
807 ScalarField::serialize_to_buffer(instruction.arguments.mulArgs.scalar, Data + 3);
808 break;
809#endif
810#ifndef DISABLE_BATCH_MUL
812 *(Data + 1) = instruction.arguments.batchMulArgs.add_elements_count;
813 *(Data + 2) = instruction.arguments.batchMulArgs.output_index;
814
815 size_t n = instruction.arguments.batchMulArgs.add_elements_count;
816
817 memcpy(Data + 3, instruction.arguments.batchMulArgs.inputs, n);
818 size_t offset = n + 3;
819 for (size_t i = 0; i < n; i++) {
820 ScalarField::serialize_to_buffer(instruction.arguments.batchMulArgs.scalars[i], Data + offset);
821 offset += sizeof(ScalarField);
822 }
823 break;
824 }
825#endif
827 memcpy(Data + 1, &instruction.arguments.randomseed, sizeof(uint32_t));
828 break;
829 default:
830 abort(); // We missed some instructions in switch
831 }
832 return;
833 };
834 };
840 private:
841 static bool_t construct_predicate(Builder* builder, const bool predicate)
842 {
843 /* The context field of a predicate can be nullptr;
844 * in that case, the function that handles the predicate
845 * will use the context of another input parameter
846 */
847 const bool predicate_is_const = static_cast<bool>(VarianceRNG.next() & 1);
848 if (predicate_is_const) {
849 const bool predicate_has_ctx = static_cast<bool>(VarianceRNG.next() % 2);
850 debug_log("bool_t(", (predicate_has_ctx ? "&builder," : "nullptr,"), (predicate ? "true)" : "false)"));
851 return bool_t(predicate_has_ctx ? builder : nullptr, predicate);
852 }
853 debug_log("bool_t(witness_t(&builder, ", (predicate ? "true));" : "false))"));
854 return bool_t(witness_t(builder, predicate));
855 }
856
858 {
859 const bool reconstruct = static_cast<bool>(VarianceRNG.next() % 2);
860 if (!reconstruct) {
861 return this->cycle_group;
862 }
863 return cycle_group_t(this->cycle_group);
864 }
865
866 public:
870
871 ExecutionHandler() = default;
873 : base_scalar(s)
874 , base(g)
875 , cycle_group(w_g)
876 {}
877
878 private:
888 const ExecutionHandler& other,
889 const ScalarField& base_scalar_res,
890 const GroupElement& base_res)
891 {
892 uint8_t dbl_path = VarianceRNG.next() % 4;
893 switch (dbl_path) {
894 case 0:
895 debug_log("left.dbl", "\n");
896 return ExecutionHandler(base_scalar_res, base_res, this->cg().dbl());
897 case 1:
898 debug_log("right.dbl", "\n");
899 return ExecutionHandler(base_scalar_res, base_res, other.cg().dbl());
900 case 2:
901 return ExecutionHandler(base_scalar_res, base_res, this->cg() + other.cg());
902 case 3:
903 return ExecutionHandler(base_scalar_res, base_res, other.cg() + this->cg());
904 }
905 return {};
906 }
907
917 const ScalarField& base_scalar_res,
918 const GroupElement& base_res)
919 {
920 uint8_t inf_path = VarianceRNG.next() % 2;
921 cycle_group_t res;
922 switch (inf_path) {
923 case 0:
924 return ExecutionHandler(base_scalar_res, base_res, this->cg() + other.cg());
925 case 1:
926 return ExecutionHandler(base_scalar_res, base_res, other.cg() + this->cg());
927 }
928 return {};
929 }
930
939 const ScalarField& base_scalar_res,
940 const GroupElement& base_res)
941 {
942 bool smth_inf = this->cycle_group.is_point_at_infinity().get_value() ||
943 other.cycle_group.is_point_at_infinity().get_value();
944 uint8_t add_option = smth_inf ? 4 + (VarianceRNG.next() % 2) : VarianceRNG.next() % 6;
945
946 switch (add_option) {
947 case 0:
948 debug_log("left.unconditional_add(right);", "\n");
949 return ExecutionHandler(base_scalar_res, base_res, this->cg().unconditional_add(other.cg()));
950 case 1:
951 debug_log("right.unconditional_add(left);", "\n");
952 return ExecutionHandler(base_scalar_res, base_res, other.cg().unconditional_add(this->cg()));
953 case 2:
954 debug_log("left.checked_unconditional_add(right);", "\n");
955 return ExecutionHandler(base_scalar_res, base_res, this->cg().checked_unconditional_add(other.cg()));
956 case 3:
957 debug_log("right.checked_unconditional_add(left);", "\n");
958 return ExecutionHandler(base_scalar_res, base_res, other.cg().checked_unconditional_add(this->cg()));
959 case 4:
960 return ExecutionHandler(base_scalar_res, base_res, this->cg() + other.cg());
961 case 5:
962 return ExecutionHandler(base_scalar_res, base_res, other.cg() + this->cg());
963 }
964 return {};
965 }
966
967 public:
969 {
970 ScalarField base_scalar_res = this->base_scalar + other.base_scalar;
971 GroupElement base_res = this->base + other.base;
972
973 // Test doubling path when points are equal
974 if (other.cg().get_value() == this->cg().get_value()) {
975 return handle_add_doubling_case(builder, other, base_scalar_res, base_res);
976 }
977
978 // Test infinity path when points are negations
979 if (other.cg().get_value() == -this->cg().get_value()) {
980 return handle_add_infinity_case(other, base_scalar_res, base_res);
981 }
982
983 // Test normal addition paths
984 return handle_add_normal_case(other, base_scalar_res, base_res);
985 }
986
987 private:
992 const ExecutionHandler& other,
993 const ScalarField& base_scalar_res,
994 const GroupElement& base_res)
995 {
996 uint8_t dbl_path = VarianceRNG.next() % 3;
997
998 switch (dbl_path) {
999 case 0:
1000 debug_log("left.dbl();", "\n");
1001 return ExecutionHandler(base_scalar_res, base_res, this->cg().dbl());
1002 case 1:
1003 debug_log("-right.dbl();", "\n");
1004 return ExecutionHandler(base_scalar_res, base_res, -other.cg().dbl());
1005 case 2:
1006 return ExecutionHandler(base_scalar_res, base_res, this->cg() - other.cg());
1007 }
1008 return {};
1009 }
1010
1015 const ScalarField& base_scalar_res,
1016 const GroupElement& base_res)
1017 {
1018 return ExecutionHandler(base_scalar_res, base_res, this->cg() - other.cg());
1019 }
1020
1025 const ScalarField& base_scalar_res,
1026 const GroupElement& base_res)
1027 {
1028 bool smth_inf = this->cycle_group.is_point_at_infinity().get_value() ||
1029 other.cycle_group.is_point_at_infinity().get_value();
1030 uint8_t add_option = smth_inf ? 2 : VarianceRNG.next() % 3;
1031
1032 switch (add_option) {
1033 case 0:
1034 debug_log("left.unconditional_subtract(right);", "\n");
1035 return ExecutionHandler(base_scalar_res, base_res, this->cg().unconditional_subtract(other.cg()));
1036 case 1:
1037 debug_log("left.checked_unconditional_subtract(right);", "\n");
1038 return ExecutionHandler(
1039 base_scalar_res, base_res, this->cg().checked_unconditional_subtract(other.cg()));
1040 case 2:
1041 return ExecutionHandler(base_scalar_res, base_res, this->cg() - other.cg());
1042 }
1043 return {};
1044 }
1045
1046 public:
1051 {
1052 ScalarField base_scalar_res = this->base_scalar - other.base_scalar;
1053 GroupElement base_res = this->base - other.base;
1054
1055 if (other.cg().get_value() == -this->cg().get_value()) {
1056 return handle_sub_doubling_case(builder, other, base_scalar_res, base_res);
1057 }
1058 if (other.cg().get_value() == this->cg().get_value()) {
1059 return handle_sub_infinity_case(other, base_scalar_res, base_res);
1060 }
1061 return handle_sub_normal_case(other, base_scalar_res, base_res);
1062 }
1063
1065 {
1066 bool is_witness = VarianceRNG.next() & 1;
1067 debug_log(" * cycle_scalar_t",
1068 (is_witness ? "::from_witness(&builder, " : "("),
1069 "ScalarField(\"",
1070 multiplier,
1071 "\"));");
1072 auto scalar = is_witness ? cycle_scalar_t::from_witness(builder, multiplier) : cycle_scalar_t(multiplier);
1073 return ExecutionHandler(this->base_scalar * multiplier, this->base * multiplier, this->cg() * scalar);
1074 }
1075
1077 const std::vector<ExecutionHandler>& to_add,
1078 const std::vector<ScalarField>& to_mul)
1079 {
1081 to_add_cg.reserve(to_add.size());
1083 to_mul_cs.reserve(to_mul.size());
1084
1085 GroupElement accumulator_cg = GroupElement::one();
1086 ScalarField accumulator_cs = ScalarField::zero();
1087
1088 for (size_t i = 0; i < to_add.size(); i++) {
1089 to_add_cg.push_back(to_add[i].cycle_group);
1090
1091 bool is_witness = VarianceRNG.next() & 1;
1092 debug_log("cycle_scalar_t",
1093 (is_witness ? "::from_witness(&builder, " : "("),
1094 "ScalarField(\"",
1095 to_mul[i],
1096 "\")), ");
1097 auto scalar = is_witness ? cycle_scalar_t::from_witness(builder, to_mul[i]) : cycle_scalar_t(to_mul[i]);
1098 to_mul_cs.push_back(scalar);
1099
1100 accumulator_cg += to_add[i].base * to_mul[i];
1101 accumulator_cs += to_add[i].base_scalar * to_mul[i];
1102 }
1103 accumulator_cg -= GroupElement::one();
1104
1105 // Handle the linearly dependant case that is
1106 // assumed to not happen in real life
1107 if (accumulator_cg.is_point_at_infinity()) {
1108 to_add_cg.push_back(cycle_group_t(GroupElement::one()));
1109 to_mul_cs.push_back(cycle_scalar_t(ScalarField::one()));
1110 accumulator_cg += GroupElement::one();
1111 accumulator_cs += ScalarField::one();
1112 }
1113
1114 auto batch_mul_res = cycle_group_t::batch_mul(to_add_cg, to_mul_cs);
1115 return ExecutionHandler(accumulator_cs, accumulator_cg, batch_mul_res);
1116 }
1117
1118 ExecutionHandler operator-() { return ExecutionHandler(-this->base_scalar, -this->base, -this->cg()); }
1119
1121 {
1122 return ExecutionHandler(this->base_scalar + this->base_scalar, this->base.dbl(), this->cg().dbl());
1123 }
1124
1126 {
1127 ScalarField new_base_scalar = predicate ? other.base_scalar : this->base_scalar;
1128 GroupElement new_base = predicate ? other.base : this->base;
1129 cycle_group_t new_cycle_group =
1130 cycle_group_t::conditional_assign(construct_predicate(builder, predicate), other.cg(), this->cg());
1131 return ExecutionHandler(new_base_scalar, new_base, new_cycle_group);
1132 }
1133
1135 {
1136 if (other.cg().is_constant()) {
1137 if (this->cg().is_constant()) {
1138 // Assert equal does nothing in this case
1139 return;
1140 }
1141 }
1142 auto to_add = cycle_group_t::from_witness(builder, AffineElement(this->base - other.base));
1143 auto to_ae = other.cg() + to_add;
1144 this->cg().assert_equal(to_ae);
1145 }
1146
1147 /* Explicit re-instantiation using the various cycle_group_t constructors */
1149 {
1150 uint32_t switch_case = VarianceRNG.next() % 4;
1151 switch (switch_case) {
1152 case 0:
1153 debug_log("cycle_group_t(", "\n");
1154 /* construct via cycle_group_t */
1155 return ExecutionHandler(this->base_scalar, this->base, cycle_group_t(this->cycle_group));
1156 case 1: {
1157 debug_log("cycle_group_t::from",
1158 (this->cycle_group.is_constant() ? "" : "_constant"),
1159 "_witness(&builder, e.get_value());");
1160 /* construct via AffineElement */
1161 AffineElement e = this->cycle_group.get_value();
1162 if (this->cycle_group.is_constant()) {
1163 return ExecutionHandler(
1164 this->base_scalar, this->base, cycle_group_t::from_constant_witness(builder, e));
1165 }
1166 return ExecutionHandler(this->base_scalar, this->base, cycle_group_t::from_witness(builder, e));
1167 }
1168 case 2: {
1169 debug_log("tmp = el;", "\n");
1170 debug_log("res = cycle_group_t(tmp);", "\n");
1171 /* Invoke assigment operator */
1172 cycle_group_t cg_new(builder);
1173 cg_new = this->cg();
1174 return ExecutionHandler(this->base_scalar, this->base, cycle_group_t(cg_new));
1175 }
1176 case 3: {
1177 debug_log("tmp = el;", "\n");
1178 debug_log("res = cycle_group_t(std::move(tmp));", "\n");
1179 /* Invoke move constructor */
1180 cycle_group_t cg_copy = this->cg();
1181 return ExecutionHandler(this->base_scalar, this->base, cycle_group_t(std::move(cg_copy)));
1182 }
1183 default:
1184 abort();
1185 }
1186 }
1195 static inline size_t execute_CONSTANT(Builder* builder,
1198 {
1199 (void)builder;
1200 stack.push_back(
1201 ExecutionHandler(instruction.arguments.element.scalar,
1202 instruction.arguments.element.value,
1203 cycle_group_t(static_cast<AffineElement>(instruction.arguments.element.value))));
1204 debug_log("// scalar = ", instruction.arguments.element.scalar);
1205 debug_log(
1206 "auto c", stack.size() - 1, " = cycle_group_t(ae(\"", instruction.arguments.element.scalar, "\"));\n");
1207 return 0;
1208 };
1209
1218 static inline size_t execute_WITNESS(Builder* builder,
1221 {
1222 stack.push_back(ExecutionHandler(
1223 instruction.arguments.element.scalar,
1224 instruction.arguments.element.value,
1225 cycle_group_t::from_witness(builder, static_cast<AffineElement>(instruction.arguments.element.value))));
1226 debug_log("// scalar = ", instruction.arguments.element.scalar);
1227 debug_log("auto w",
1228 stack.size() - 1,
1229 " = cycle_group_t::from_witness(&builder, ae(\"",
1230 instruction.arguments.element.scalar,
1231 "\"));\n");
1232 return 0;
1233 }
1234
1247 {
1248 stack.push_back(
1249 ExecutionHandler(instruction.arguments.element.scalar,
1250 instruction.arguments.element.value,
1252 builder, static_cast<AffineElement>(instruction.arguments.element.value))));
1253 debug_log("// scalar = ", instruction.arguments.element.scalar);
1254 debug_log("auto cw",
1255 stack.size() - 1,
1256 " = cycle_group_t::from_constant_witness(&builder, ae(\"",
1257 instruction.arguments.element.scalar,
1258 "\"));\n");
1259 return 0;
1260 }
1261
1270 static inline size_t execute_DBL(Builder* builder,
1273 {
1274 (void)builder;
1275 if (stack.size() == 0) {
1276 return 1;
1277 }
1278 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
1279 size_t output_index = instruction.arguments.twoArgs.out;
1280
1281 if constexpr (SHOW_FUZZING_INFO) {
1282 auto args = format_single_arg(stack, first_index, output_index);
1283 debug_log(args.out, " = ", args.rhs, ".dbl();", "\n");
1284 }
1285 ExecutionHandler result = stack[first_index].dbl();
1286 // If the output index is larger than the number of elements in stack, append
1287 if (output_index >= stack.size()) {
1288 stack.push_back(result);
1289 } else {
1290 stack[output_index] = result;
1291 }
1292 return 0;
1293 };
1294
1303 static inline size_t execute_NEG(Builder* builder,
1306 {
1307 (void)builder;
1308 if (stack.size() == 0) {
1309 return 1;
1310 }
1311 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
1312 size_t output_index = instruction.arguments.twoArgs.out;
1313
1314 if constexpr (SHOW_FUZZING_INFO) {
1315 auto args = format_single_arg(stack, first_index, output_index);
1316 debug_log(args.out, " = -", args.rhs, ";", "\n");
1317 }
1318 ExecutionHandler result = -stack[first_index];
1319 // If the output index is larger than the number of elements in stack, append
1320 if (output_index >= stack.size()) {
1321 stack.push_back(result);
1322 } else {
1323 stack[output_index] = result;
1324 }
1325 return 0;
1326 };
1327
1336 static inline size_t execute_ASSERT_EQUAL(Builder* builder,
1339 {
1340 if (stack.size() == 0) {
1341 return 1;
1342 }
1343 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
1344 size_t second_index = instruction.arguments.twoArgs.out % stack.size();
1345
1346 if constexpr (SHOW_FUZZING_INFO) {
1347 auto args = format_two_arg(stack, first_index, second_index, 0);
1348 debug_log("assert_equal(", args.lhs, ", ", args.rhs, ", builder);", "\n");
1349 }
1350 stack[first_index].assert_equal(builder, stack[second_index]);
1351 return 0;
1352 };
1353
1362 static inline size_t execute_SET(Builder* builder,
1365 {
1366 (void)builder;
1367 if (stack.size() == 0) {
1368 return 1;
1369 }
1370 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
1371 size_t output_index = instruction.arguments.twoArgs.out;
1372
1373 ExecutionHandler result;
1374 if constexpr (SHOW_FUZZING_INFO) {
1375 auto args = format_single_arg(stack, first_index, output_index);
1376 debug_log(args.out, " = ");
1377 // Need to split logs here, since `set` produces extra logs
1378 result = stack[first_index].set(builder);
1379 debug_log(args.rhs, ");", "\n");
1380 } else {
1381 result = stack[first_index].set(builder);
1382 }
1383 // If the output index is larger than the number of elements in stack, append
1384 if (output_index >= stack.size()) {
1385 stack.push_back(result);
1386 } else {
1387 stack[output_index] = result;
1388 }
1389 return 0;
1390 };
1391
1400 static inline size_t execute_ADD(Builder* builder,
1403 {
1404 (void)builder;
1405 if (stack.size() == 0) {
1406 return 1;
1407 }
1408 size_t first_index = instruction.arguments.threeArgs.in1 % stack.size();
1409 size_t second_index = instruction.arguments.threeArgs.in2 % stack.size();
1410 size_t output_index = instruction.arguments.threeArgs.out;
1411
1412 if constexpr (SHOW_FUZZING_INFO) {
1413 auto args = format_two_arg(stack, first_index, second_index, output_index);
1414 debug_log(args.out, " = ", args.lhs, " + ", args.rhs, ";", "\n");
1415 }
1416 ExecutionHandler result = stack[first_index].operator_add(builder, stack[second_index]);
1417 // If the output index is larger than the number of elements in stack, append
1418 if (output_index >= stack.size()) {
1419 stack.push_back(result);
1420 } else {
1421 stack[output_index] = result;
1422 }
1423 return 0;
1424 };
1425
1434 static inline size_t execute_SUBTRACT(Builder* builder,
1437 {
1438 (void)builder;
1439 if (stack.size() == 0) {
1440 return 1;
1441 }
1442 size_t first_index = instruction.arguments.threeArgs.in1 % stack.size();
1443 size_t second_index = instruction.arguments.threeArgs.in2 % stack.size();
1444 size_t output_index = instruction.arguments.threeArgs.out;
1445
1446 if constexpr (SHOW_FUZZING_INFO) {
1447 auto args = format_two_arg(stack, first_index, second_index, output_index);
1448 debug_log(args.out, " = ", args.lhs, " - ", args.rhs, ";", "\n");
1449 }
1450 ExecutionHandler result = stack[first_index].operator_sub(builder, stack[second_index]);
1451 // If the output index is larger than the number of elements in stack, append
1452 if (output_index >= stack.size()) {
1453 stack.push_back(result);
1454 } else {
1455 stack[output_index] = result;
1456 }
1457 return 0;
1458 };
1459
1468 static inline size_t execute_COND_ASSIGN(Builder* builder,
1471 {
1472 (void)builder;
1473 if (stack.size() == 0) {
1474 return 1;
1475 }
1476 size_t first_index = instruction.arguments.fourArgs.in1 % stack.size();
1477 size_t second_index = instruction.arguments.fourArgs.in2 % stack.size();
1478 size_t output_index = instruction.arguments.fourArgs.out % stack.size();
1479 bool predicate = instruction.arguments.fourArgs.in3 % 2;
1480
1481 ExecutionHandler result;
1482 if constexpr (SHOW_FUZZING_INFO) {
1483 auto args = format_two_arg(stack, first_index, second_index, output_index);
1484 debug_log(args.out, " = cycle_group_t::conditional_assign(");
1485 // Need to split logs here, since `conditional_assign` produces extra logs
1486 result = stack[first_index].conditional_assign(builder, stack[second_index], predicate);
1487 debug_log(args.rhs, ", ", args.lhs, ");", "\n");
1488 } else {
1489 result = stack[first_index].conditional_assign(builder, stack[second_index], predicate);
1490 }
1491
1492 // If the output index is larger than the number of elements in stack, append
1493 if (output_index >= stack.size()) {
1494 stack.push_back(result);
1495 } else {
1496 stack[output_index] = result;
1497 }
1498 return 0;
1499 };
1500
1509 static inline size_t execute_MULTIPLY(Builder* builder,
1512 {
1513
1514 if (stack.size() == 0) {
1515 return 1;
1516 }
1517 size_t first_index = instruction.arguments.mulArgs.in % stack.size();
1518 size_t output_index = instruction.arguments.mulArgs.out;
1519 ScalarField scalar = instruction.arguments.mulArgs.scalar;
1520
1521 if constexpr (SHOW_FUZZING_INFO) {
1522 auto args = format_single_arg(stack, first_index, output_index);
1523 debug_log(args.out, " = ", args.rhs);
1524 }
1525 ExecutionHandler result = stack[first_index].mul(builder, scalar);
1526 // If the output index is larger than the number of elements in stack, append
1527 if (output_index >= stack.size()) {
1528 stack.push_back(result);
1529 } else {
1530 stack[output_index] = result;
1531 }
1532 return 0;
1533 };
1534
1543 static inline size_t execute_BATCH_MUL(Builder* builder,
1546 {
1547 (void)builder;
1548 if (stack.size() == 0) {
1549 return 1;
1550 }
1552 std::vector<ScalarField> to_mul;
1553 for (size_t i = 0; i < instruction.arguments.batchMulArgs.add_elements_count; i++) {
1554 to_add.push_back(stack[(size_t)instruction.arguments.batchMulArgs.inputs[i] % stack.size()]);
1555 to_mul.push_back(instruction.arguments.batchMulArgs.scalars[i]);
1556 }
1557 size_t output_index = (size_t)instruction.arguments.batchMulArgs.output_index;
1558
1559 ExecutionHandler result;
1560 if constexpr (SHOW_FUZZING_INFO) {
1561 std::string res = "";
1562 bool is_const = true;
1563 for (size_t i = 0; i < instruction.arguments.batchMulArgs.add_elements_count; i++) {
1564 size_t idx = instruction.arguments.batchMulArgs.inputs[i] % stack.size();
1565 std::string el = stack[idx].cycle_group.is_constant() ? "c" : "w";
1566 el += std::to_string(idx);
1567 res += el + ", ";
1568 is_const &= stack[idx].cycle_group.is_constant();
1569 }
1570 std::string out = is_const ? "c" : "w";
1571 out = ((output_index >= stack.size()) ? "auto " : "") + out;
1572 out += std::to_string(output_index >= stack.size() ? stack.size() : output_index);
1573 debug_log(out, " = cycle_group_t::batch_mul({", res, "}, {");
1574 // Need to split logs here, since `batch_mul` produces extra logs
1575 result = ExecutionHandler::batch_mul(builder, to_add, to_mul);
1576 debug_log("});", "\n");
1577 } else {
1578 result = ExecutionHandler::batch_mul(builder, to_add, to_mul);
1579 }
1580 // If the output index is larger than the number of elements in stack, append
1581 if (output_index >= stack.size()) {
1582 stack.push_back(result);
1583 } else {
1584 stack[output_index] = result;
1585 }
1586 return 0;
1587 };
1588
1597 static inline size_t execute_RANDOMSEED(Builder* builder,
1600 {
1601 (void)builder;
1602 (void)stack;
1603
1604 VarianceRNG.reseed(instruction.arguments.randomseed);
1605 return 0;
1606 };
1607 };
1608
1613
1624 {
1625 (void)builder;
1626 for (size_t i = 0; i < stack.size(); i++) {
1627 auto element = stack[i];
1628 if (element.cycle_group.get_value() != AffineElement(element.base)) {
1629 std::cerr << "Failed at " << i << " with actual value " << AffineElement(element.base)
1630 << " and value in CycleGroup " << element.cycle_group.get_value() << std::endl;
1631 return false;
1632 }
1633 if ((AffineElement::one() * element.base_scalar) != AffineElement(element.base)) {
1634 std::cerr << "Failed at " << i << " with actual mul value " << element.base
1635 << " and value in scalar * CG " << element.cycle_group.get_value() * element.base_scalar
1636 << std::endl;
1637 return false;
1638 }
1639 }
1640 return true;
1641 }
1642};
1643
1644#ifdef HAVOC_TESTING
1645
1646extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
1647{
1648 (void)argc;
1649 (void)argv;
1650 // These are the settings, optimized for the cycle group class (under them, fuzzer reaches maximum expected
1651 // coverage in 40 seconds)
1652 fuzzer_havoc_settings = HavocSettings{ .GEN_LLVM_POST_MUTATION_PROB = 30, // Out of 200
1653 .GEN_MUTATION_COUNT_LOG = 5, // -Fully checked
1654 .GEN_STRUCTURAL_MUTATION_PROBABILITY = 300, // Fully checked
1655 .GEN_VALUE_MUTATION_PROBABILITY = 700, // Fully checked
1656 .ST_MUT_DELETION_PROBABILITY = 100, // Fully checked
1657 .ST_MUT_DUPLICATION_PROBABILITY = 80, // Fully checked
1658 .ST_MUT_INSERTION_PROBABILITY = 120, // Fully checked
1659 .ST_MUT_MAXIMUM_DELETION_LOG = 6, // 2 because of limit
1660 .ST_MUT_MAXIMUM_DUPLICATION_LOG = 2, // -Fully checked
1661 .ST_MUT_SWAP_PROBABILITY = 50, // Fully checked
1662 .VAL_MUT_LLVM_MUTATE_PROBABILITY = 250, // Fully checked
1663 .VAL_MUT_MONTGOMERY_PROBABILITY = 130, // Fully checked
1664 .VAL_MUT_NON_MONTGOMERY_PROBABILITY = 50, // Fully checked
1665 .VAL_MUT_SMALL_ADDITION_PROBABILITY = 110, // Fully checked
1666 .VAL_MUT_SPECIAL_VALUE_PROBABILITY = 130, // Fully checked
1667 .structural_mutation_distribution = {},
1668 .value_mutation_distribution = {} };
1674 /*
1675 std::random_device rd;
1676 std::uniform_int_distribution<uint64_t> dist(0, ~(uint64_t)(0));
1677 srandom(static_cast<unsigned int>(dist(rd)));
1678
1679 fuzzer_havoc_settings =
1680 HavocSettings{ .GEN_MUTATION_COUNT_LOG = static_cast<size_t>((random() % 8) + 1),
1681 .GEN_STRUCTURAL_MUTATION_PROBABILITY = static_cast<size_t>(random() % 100),
1682 .GEN_VALUE_MUTATION_PROBABILITY = static_cast<size_t>(random() % 100),
1683 .ST_MUT_DELETION_PROBABILITY = static_cast<size_t>(random() % 100),
1684 .ST_MUT_DUPLICATION_PROBABILITY = static_cast<size_t>(random() % 100),
1685 .ST_MUT_INSERTION_PROBABILITY = static_cast<size_t>((random() % 99) + 1),
1686 .ST_MUT_MAXIMUM_DELETION_LOG = static_cast<size_t>((random() % 8) + 1),
1687 .ST_MUT_MAXIMUM_DUPLICATION_LOG = static_cast<size_t>((random() % 8) + 1),
1688 .ST_MUT_SWAP_PROBABILITY = static_cast<size_t>(random() % 100),
1689 .VAL_MUT_LLVM_MUTATE_PROBABILITY = static_cast<size_t>(random() % 100),
1690 .VAL_MUT_MONTGOMERY_PROBABILITY = static_cast<size_t>(random() % 100),
1691 .VAL_MUT_NON_MONTGOMERY_PROBABILITY = static_cast<size_t>(random() % 100),
1692 .VAL_MUT_SMALL_ADDITION_PROBABILITY = static_cast<size_t>(random() % 100),
1693 .VAL_MUT_SPECIAL_VALUE_PROBABILITY = static_cast<size_t>(random() % 100)
1694
1695 };
1696 while (fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY == 0 &&
1697 fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY == 0) {
1698 fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY = static_cast<size_t>(random() % 8);
1699 fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY = static_cast<size_t>(random() % 8);
1700 }
1701 */
1702
1703 // fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB = static_cast<size_t>(((random() % (20 - 1)) + 1) * 10);
1708 /*
1709 std::cerr << "CUSTOM MUTATOR SETTINGS:" << std::endl
1710 << "################################################################" << std::endl
1711 << "GEN_LLVM_POST_MUTATION_PROB: " << fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB << std::endl
1712 << "GEN_MUTATION_COUNT_LOG: " << fuzzer_havoc_settings.GEN_MUTATION_COUNT_LOG << std::endl
1713 << "GEN_STRUCTURAL_MUTATION_PROBABILITY: " <<
1714 fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY
1715 << std::endl
1716 << "GEN_VALUE_MUTATION_PROBABILITY: " << fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY <<
1717 std::endl
1718 << "ST_MUT_DELETION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_DELETION_PROBABILITY << std::endl
1719 << "ST_MUT_DUPLICATION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_DUPLICATION_PROBABILITY <<
1720 std::endl
1721 << "ST_MUT_INSERTION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_INSERTION_PROBABILITY << std::endl
1722 << "ST_MUT_MAXIMUM_DELETION_LOG: " << fuzzer_havoc_settings.ST_MUT_MAXIMUM_DELETION_LOG << std::endl
1723 << "ST_MUT_MAXIMUM_DUPLICATION_LOG: " << fuzzer_havoc_settings.ST_MUT_MAXIMUM_DUPLICATION_LOG <<
1724 std::endl
1725 << "ST_MUT_SWAP_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_SWAP_PROBABILITY << std::endl
1726 << "VAL_MUT_LLVM_MUTATE_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_LLVM_MUTATE_PROBABILITY
1727 << std::endl
1728 << "VAL_MUT_MONTGOMERY_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_MONTGOMERY_PROBABILITY <<
1729 std::endl
1730 << "VAL_MUT_NON_MONTGOMERY_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_NON_MONTGOMERY_PROBABILITY
1731 << std::endl
1732 << "VAL_MUT_SMALL_ADDITION_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_SMALL_ADDITION_PROBABILITY
1733 << std::endl
1734 << "VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY: "
1735 << fuzzer_havoc_settings.VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY << std::endl
1736 << "VAL_MUT_SPECIAL_VALUE_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_SPECIAL_VALUE_PROBABILITY
1737 << std::endl;
1738 */
1739 std::vector<size_t> structural_mutation_distribution;
1740 std::vector<size_t> value_mutation_distribution;
1741 size_t temp = 0;
1742 temp += fuzzer_havoc_settings.ST_MUT_DELETION_PROBABILITY;
1743 structural_mutation_distribution.push_back(temp);
1744 temp += fuzzer_havoc_settings.ST_MUT_DUPLICATION_PROBABILITY;
1745 structural_mutation_distribution.push_back(temp);
1746 temp += fuzzer_havoc_settings.ST_MUT_INSERTION_PROBABILITY;
1747 structural_mutation_distribution.push_back(temp);
1748 temp += fuzzer_havoc_settings.ST_MUT_SWAP_PROBABILITY;
1749 structural_mutation_distribution.push_back(temp);
1750 fuzzer_havoc_settings.structural_mutation_distribution = structural_mutation_distribution;
1751
1752 temp = 0;
1753 temp += fuzzer_havoc_settings.VAL_MUT_LLVM_MUTATE_PROBABILITY;
1754 value_mutation_distribution.push_back(temp);
1755 temp += fuzzer_havoc_settings.VAL_MUT_SMALL_ADDITION_PROBABILITY;
1756 value_mutation_distribution.push_back(temp);
1757 temp += fuzzer_havoc_settings.VAL_MUT_SPECIAL_VALUE_PROBABILITY;
1758 value_mutation_distribution.push_back(temp);
1759
1760 fuzzer_havoc_settings.value_mutation_distribution = value_mutation_distribution;
1761 return 0;
1762}
1763#endif
1764
1769extern "C" size_t LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size)
1770{
1771 RunWithBuilders<CycleGroupBase, FuzzerCircuitTypes>(Data, Size, VarianceRNG);
1772 return 0;
1773}
1774
1775#pragma clang diagnostic pop
bb::field< bb::Bn254FrParams > FF
Definition field.cpp:24
SpecialScalarValue
Special scalar field values used for mutation testing.
static constexpr size_t CONSTANT_WITNESS
static constexpr size_t ADD
static constexpr size_t CONSTANT
static constexpr size_t MULTIPLY
static constexpr size_t COND_ASSIGN
static constexpr size_t ASSERT_EQUAL
static constexpr size_t RANDOMSEED
static constexpr size_t DBL
static constexpr size_t BATCH_MUL
static constexpr size_t SET
static constexpr size_t WITNESS
static constexpr size_t NEG
static constexpr size_t SUBTRACT
This class implements the execution of cycle group with an oracle to detect discrepancies.
static size_t execute_SUBTRACT(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the subtraction operator instruction.
static size_t execute_COND_ASSIGN(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the COND_ASSIGN instruction.
static size_t execute_DBL(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the DBL instruction.
ExecutionHandler(ScalarField s, GroupElement g, cycle_group_t w_g)
ExecutionHandler handle_add_doubling_case(Builder *builder, const ExecutionHandler &other, const ScalarField &base_scalar_res, const GroupElement &base_res)
Handle addition when points are equal (requires doubling)
static bool_t construct_predicate(Builder *builder, const bool predicate)
ExecutionHandler handle_sub_normal_case(const ExecutionHandler &other, const ScalarField &base_scalar_res, const GroupElement &base_res)
Handle normal subtraction case (no special edge cases)
static size_t execute_NEG(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the NEG instruction.
static size_t execute_WITNESS(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the witness instruction (push witness cycle group to the stack)
ExecutionHandler handle_sub_doubling_case(Builder *builder, const ExecutionHandler &other, const ScalarField &base_scalar_res, const GroupElement &base_res)
Handle subtraction when points are negations: x - (-x) = 2x (doubling case)
static size_t execute_BATCH_MUL(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the BATCH_MUL instruction.
static size_t execute_RANDOMSEED(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the RANDOMSEED instruction.
static size_t execute_ASSERT_EQUAL(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the ASSERT_EQUAL instruction.
static size_t execute_SET(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the SET instruction.
static size_t execute_CONSTANT_WITNESS(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the constant_witness instruction (push a cycle group witness equal to the constant to the sta...
static size_t execute_ADD(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the addition operator instruction.
ExecutionHandler mul(Builder *builder, const ScalarField &multiplier)
static ExecutionHandler batch_mul(Builder *builder, const std::vector< ExecutionHandler > &to_add, const std::vector< ScalarField > &to_mul)
ExecutionHandler handle_sub_infinity_case(const ExecutionHandler &other, const ScalarField &base_scalar_res, const GroupElement &base_res)
Handle subtraction when points are equal: x - x = 0 (point at infinity)
ExecutionHandler operator_sub(Builder *builder, const ExecutionHandler &other)
Subtract two ExecutionHandlers, exploring different code paths for edge cases.
static size_t execute_MULTIPLY(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the multiply instruction.
void assert_equal(Builder *builder, ExecutionHandler &other)
ExecutionHandler handle_add_infinity_case(const ExecutionHandler &other, const ScalarField &base_scalar_res, const GroupElement &base_res)
Handle addition when points are negations (result is point at infinity)
ExecutionHandler handle_add_normal_case(const ExecutionHandler &other, const ScalarField &base_scalar_res, const GroupElement &base_res)
Handle normal addition (no special edge cases)
static size_t execute_CONSTANT(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the constant instruction (push constant cycle group to the stack)
ExecutionHandler operator_add(Builder *builder, const ExecutionHandler &other)
ExecutionHandler conditional_assign(Builder *builder, ExecutionHandler &other, const bool predicate)
ExecutionHandler set(Builder *builder)
static uint256_t to_uint256_montgomery(const FF &value, bool as_montgomery)
Convert a scalar field element to uint256_t, optionally using Montgomery form.
static Instruction generateRandom(T &rng)
Generates a random instruction.
static Instruction mutateInstruction(Instruction instruction, T &rng, HavocSettings &havoc_config)
Mutate a single instruction.
static FF from_uint256_montgomery(const uint256_t &data, bool from_montgomery)
Convert uint256_t back to scalar field element, optionally from Montgomery form.
static ScalarField mutateScalarElement(ScalarField e, T &rng, HavocSettings &havoc_config)
Mutate the value of a scalar element.
static Element mutateGroupElement(Element e, T &rng, HavocSettings &havoc_config)
Mutate the value of a group element.
Optional subclass that governs limits on the use of certain instructions, since some of them can be t...
Parser class handles the parsing and writing the instructions back to data buffer.
static void writeInstruction(Instruction &instruction, uint8_t *Data)
Write a single instruction to buffer.
static Instruction parseInstructionArgs(uint8_t *Data)
Parse a single instruction from data.
The class parametrizing CycleGroup fuzzing instructions, execution, etc.
typename bb::stdlib::witness_t< Builder > witness_t
typename bb::stdlib::cycle_group< Builder >::Curve Curve
typename bb::stdlib::cycle_group< Builder > cycle_group_t
typename bb::stdlib::field_t< Builder > field_t
static bool postProcess(Builder *builder, std::vector< CycleGroupBase::ExecutionHandler > &stack)
Check that the resulting values are equal to expected.
std::vector< ExecutionHandler > ExecutionState
typename bb::stdlib::bool_t< Builder > bool_t
typename cycle_group_t::cycle_scalar cycle_scalar_t
typename Curve::Element GroupElement
typename Curve::AffineElement AffineElement
typename Curve::ScalarField ScalarField
typename Curve::BaseField BaseField
Class for quickly deterministically creating new random values. We don't care about distribution much...
Definition fuzzer.hpp:63
void reseed(uint32_t seed)
Definition fuzzer.hpp:75
uint32_t next()
Definition fuzzer.hpp:68
typename Group::element Element
Definition bn254.hpp:21
bb::fq BaseField
Definition bn254.hpp:19
typename Group::affine_element AffineElement
Definition bn254.hpp:22
bb::fr ScalarField
Definition bn254.hpp:18
Implements boolean logic in-circuit.
Definition bool.hpp:60
cycle_group represents a group Element of the proving system's embedded curve, i.e....
static cycle_group from_constant_witness(Builder *_context, const AffineElement &_in)
Converts a native AffineElement into a witness, but constrains the witness values to be known constan...
static cycle_group conditional_assign(const bool_t &predicate, const cycle_group &lhs, const cycle_group &rhs)
static cycle_group from_witness(Builder *_context, const AffineElement &_in)
Converts an AffineElement into a circuit witness.
::bb::stdlib::cycle_scalar< Builder > cycle_scalar
static cycle_group batch_mul(const std::vector< cycle_group > &base_points, const std::vector< BigScalarField > &scalars, GeneratorContext context={})
Concept for a simple PRNG which returns a uint32_t when next is called.
Definition fuzzer.hpp:140
AluTraceBuilder builder
Definition alu.test.cpp:124
const std::vector< MemoryValue > data
constexpr size_t MINIMUM_MUL_ELEMENTS
constexpr uint8_t SPECIAL_VALUE_COUNT_NO_ZERO
constexpr size_t MAXIMUM_MUL_ELEMENTS
constexpr uint8_t SPECIAL_VALUE_COUNT
SpecialScalarValue
Special scalar field values used for mutation testing.
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize)
FastRandom VarianceRNG(0)
bool circuit_should_fail
int LLVMFuzzerInitialize(int *argc, char ***argv)
FF get_special_scalar_value(SpecialScalarValue type)
Generate a special scalar field value for testing.
size_t LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
Fuzzer entry function.
#define PUT_RANDOM_BYTE_IF_LUCKY(variable)
ssize_t offset
Definition engine.cpp:62
void debug_log(Args &&... args)
Compile-time debug logging helper.
Definition fuzzer.hpp:127
constexpr bool SHOW_FUZZING_INFO
Definition fuzzer.hpp:123
Instruction instruction
AffineElement * rhs
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
ScalarField scalars[MAXIMUM_MUL_ELEMENTS]
Element(ScalarField s=ScalarField::one(), GroupElement g=GroupElement::one())
size_t GEN_LLVM_POST_MUTATION_PROB
Definition fuzzer.hpp:28
static constexpr field get_root_of_unity(size_t subgroup_size) noexcept
static constexpr uint256_t modulus
BB_INLINE constexpr field to_montgomery_form() const noexcept
constexpr std::pair< bool, field > sqrt() const noexcept
Compute square root of the field element.
BB_INLINE constexpr field from_montgomery_form() const noexcept