Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
biggroup.fuzzer.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
35#pragma once
43
44#pragma clang diagnostic push
45
46// -Wc99-designator prevents us from using designators and nested designators
47// in struct intializations
48// such as {.in.first = a, .out = b}, since it's not a part of c++17 standard
49// However the use of them in this particular file heavily increases
50// the readability and conciseness of the biggroup::Instruction initializations
51#pragma clang diagnostic ignored "-Wc99-designator"
52
53#ifdef FUZZING_SHOW_INFORMATION
58struct FormattedArgs {
59 std::string lhs;
60 std::string rhs;
61 std::string out;
62};
63
70template <typename Stack> inline FormattedArgs format_self_arg(const Stack& stack, size_t first_index)
71{
72 std::string out = stack[first_index].biggroup.is_constant() ? "c" : "w";
73 out += std::to_string(first_index);
74 return FormattedArgs{ .lhs = "", .rhs = "", .out = out };
75}
76
84template <typename Stack>
85inline FormattedArgs format_single_arg(const Stack& stack, size_t first_index, size_t output_index)
86{
87 std::string rhs = stack[first_index].biggroup.is_constant() ? "c" : "w";
88 std::string out = rhs;
89 rhs += std::to_string(first_index);
90 out += std::to_string(output_index >= stack.size() ? stack.size() : output_index);
91 out = (output_index >= stack.size() ? "auto " : "") + out;
92 return FormattedArgs{ .lhs = "", .rhs = rhs, .out = out };
93}
94
103template <typename Stack>
104inline FormattedArgs format_two_arg(const Stack& stack, size_t first_index, size_t second_index, size_t output_index)
105{
106 std::string lhs = stack[first_index].biggroup.is_constant() ? "c" : "w";
107 std::string rhs = stack[second_index].biggroup.is_constant() ? "c" : "w";
108 std::string out =
109 (stack[first_index].biggroup.is_constant() && stack[second_index].biggroup.is_constant()) ? "c" : "w";
110 lhs += std::to_string(first_index);
111 rhs += std::to_string(second_index);
112 out += std::to_string(output_index >= stack.size() ? stack.size() : output_index);
113 out = (output_index >= stack.size() ? "auto " : "") + out;
114 return FormattedArgs{ .lhs = lhs, .rhs = rhs, .out = out };
115}
116#endif
117
118#define HAVOC_TESTING
119
120// This is a global variable, so that the execution handling class could alter it and signal to the input tester
121// that the input should fail
123
125
126// #define FUZZING_SHOW_INFORMATION
127
128// #define DISABLE_MULTIPLICATION
129// #define DISABLE_BATCH_MUL
130
132
133constexpr size_t MINIMUM_MUL_ELEMENTS = 0;
134constexpr size_t MAXIMUM_MUL_ELEMENTS = 8;
135
136// This is an external function in Libfuzzer used internally by custom mutators
137extern "C" size_t LLVMFuzzerMutate(uint8_t* Data, size_t Size, size_t MaxSize);
138
146enum class SpecialScalarValue : uint8_t {
147 One = 0,
148 MinusOne,
151 RootOfUnity13, // 13th root of unity (arbitrary small root)
152 Two, // Small even number
153 HalfModulus, // (p-1)/2
154 Zero,
155 COUNT // Sentinel value for total count
156};
157
158// Number of special values excluding Zero
159constexpr uint8_t SPECIAL_VALUE_COUNT_NO_ZERO = static_cast<uint8_t>(SpecialScalarValue::Zero);
160
161// Number of special values including Zero
162constexpr uint8_t SPECIAL_VALUE_COUNT = static_cast<uint8_t>(SpecialScalarValue::COUNT);
163
171{
172 switch (type) {
174 return FF::one();
176 return -FF::one();
178 return FF::one().sqrt().second;
180 return FF::one().sqrt().second.invert();
182 return FF::get_root_of_unity(13);
184 return FF(2);
186 return FF((FF::modulus - 1) / 2);
188 return FF::zero();
190 // Fallthrough to abort - COUNT should never be used as a value
191 default:
192 abort(); // Invalid enum value
193 }
194}
195
199// When _use_bigfield=true, BigfieldScalar is used as the scalar field type.
200// It defaults to Curve::ScalarField (which for secp256k1/secp256r1 is already a bigfield).
201// For BN254_BF, pass bigfield<Builder, Bn254FrParams> explicitly.
202template <typename Builder,
203 typename BigGroupType,
204 bool _use_bigfield = false,
205 typename BigfieldScalar = typename BigGroupType::ScalarField>
207 private:
211
212 using Curve = BigGroupType;
213
214 using GroupElement = typename Curve::ElementNative;
215 using AffineElement = typename Curve::AffineElementNative;
216 using ScalarField = typename Curve::ScalarFieldNative;
217 using BaseField = typename Curve::BaseFieldNative;
218
221 _use_bigfield,
223 typename Curve::Group>;
224
230 {
231 using CircuitFq = typename biggroup_t::BaseField;
232 // Normalize infinity to (0, 0): the native affine representation of infinity may carry
233 // non-zero coordinates (e.g. from Jacobian conversion), but the new 2-arg circuit
234 // constructor auto-detects infinity only via limb_sum == 0.
235 const bool is_inf = native_elem.is_point_at_infinity();
236 CircuitFq x(nullptr, is_inf ? uint256_t(0) : uint256_t(native_elem.x));
237 CircuitFq y(nullptr, is_inf ? uint256_t(0) : uint256_t(native_elem.y));
238 return biggroup_t(x, y, /*assert_on_curve=*/false);
239 }
240
241 public:
243 public:
265
266 struct Element {
267 Element(ScalarField s = ScalarField::one(), GroupElement g = GroupElement::one())
268 : scalar(s)
269 , value(g) {};
272 };
273
274 struct OneArg {
275 uint8_t in;
276 };
277
278 struct TwoArgs {
279 uint8_t in;
280 uint8_t out;
281 };
282
283 struct MulArgs {
284 uint8_t in;
285 uint8_t out;
287 };
288
289 struct ThreeArgs {
290 uint8_t in1;
291 uint8_t in2;
292 uint8_t out;
293 };
294
295 struct FourArgs {
296 uint8_t in1;
297 uint8_t in2;
298 uint8_t in3;
299 uint8_t out;
300 };
301
308
321
322 // The type of the instruction
324
325 // Instruction arguments
327
335 template <typename T>
336 inline static Instruction generateRandom(T& rng)
337 requires SimpleRng<T>
338 {
339 OPCODE instruction_opcode = static_cast<OPCODE>(rng.next() % (OPCODE::_LAST));
340 uint8_t in, in1, in2, in3, out;
341 Instruction instr;
342
343 switch (instruction_opcode) {
344 case OPCODE::CONSTANT:
345 case OPCODE::WITNESS:
347 auto scalar = ScalarField(static_cast<uint64_t>(fast_log_distributed_uint256(rng)));
348 auto el = GroupElement::one() * scalar;
349 return { .id = instruction_opcode, .arguments.element = Element(scalar, el) };
350 }
352 in = static_cast<uint8_t>(rng.next() & 0xff);
353 return { .id = instruction_opcode, .arguments.oneArg = { .in = in } };
354 case OPCODE::DBL:
355 case OPCODE::NEG:
357 case OPCODE::SET:
358 in = static_cast<uint8_t>(rng.next() & 0xff);
359 out = static_cast<uint8_t>(rng.next() & 0xff);
360 return { .id = instruction_opcode, .arguments.twoArgs = { .in = in, .out = out } };
361 case OPCODE::ADD:
362 case OPCODE::SUBTRACT:
363 in1 = static_cast<uint8_t>(rng.next() & 0xff);
364 in2 = static_cast<uint8_t>(rng.next() & 0xff);
365 out = static_cast<uint8_t>(rng.next() & 0xff);
366 return { .id = instruction_opcode,
367 .arguments.threeArgs.in1 = in1,
368 .arguments.threeArgs.in2 = in2,
369 .arguments.threeArgs.out = out };
371 in1 = static_cast<uint8_t>(rng.next() & 0xff);
372 in2 = static_cast<uint8_t>(rng.next() & 0xff);
373 in3 = static_cast<uint8_t>(rng.next() & 0xff);
374 out = static_cast<uint8_t>(rng.next() & 0xff);
375 return { .id = instruction_opcode,
376 .arguments.fourArgs.in1 = in1,
377 .arguments.fourArgs.in2 = in2,
378 .arguments.fourArgs.in3 = in3,
379 .arguments.fourArgs.out = out };
380#ifndef DISABLE_MULTIPLICATION
381 case OPCODE::MULTIPLY:
382 in = static_cast<uint8_t>(rng.next() & 0xff);
383 out = static_cast<uint8_t>(rng.next() & 0xff);
384 return { .id = instruction_opcode,
385 .arguments.mulArgs.scalar = ScalarField(fast_log_distributed_uint256(rng)),
386 .arguments.mulArgs.in = in,
387 .arguments.mulArgs.out = out };
388#endif
389#ifndef DISABLE_BATCH_MUL
390 case OPCODE::BATCH_MUL: {
391 uint8_t mult_size0 =
393 static_cast<uint8_t>(rng.next() % ((MAXIMUM_MUL_ELEMENTS - MINIMUM_MUL_ELEMENTS) / 2));
394 uint8_t mult_size1 =
396 static_cast<uint8_t>(rng.next() % ((MAXIMUM_MUL_ELEMENTS - MINIMUM_MUL_ELEMENTS) / 2));
397 uint8_t mult_size =
398 mult_size0 +
399 mult_size1; // Sample the amount of batch mul participants from the binomial distribution
400 instr.id = instruction_opcode;
401 instr.arguments.batchMulArgs.add_elements_count = mult_size;
402 for (size_t i = 0; i < mult_size; i++) {
403 instr.arguments.batchMulArgs.inputs[i] = static_cast<uint8_t>(rng.next() & 0xff);
404 }
405 for (size_t i = 0; i < mult_size; i++) {
406 instr.arguments.batchMulArgs.scalars[i] = ScalarField(fast_log_distributed_uint256(rng));
407 }
408 instr.arguments.batchMulArgs.output_index = static_cast<uint8_t>(rng.next() & 0xff);
409 return instr;
410 }
411#endif
413 return { .id = instruction_opcode, .arguments.randomseed = rng.next() * rng.next() };
414
415 default:
416 abort(); // We missed some instructions in switch
417 }
418 }
419
423 template <typename FF> inline static uint256_t to_uint256_montgomery(const FF& value, bool as_montgomery)
424 {
425 if (as_montgomery) {
427 }
428 return uint256_t(value);
429 }
430
434 template <typename FF> inline static FF from_uint256_montgomery(const uint256_t& data, bool from_montgomery)
435 {
436 if (from_montgomery) {
437 return FF(data).from_montgomery_form();
438 }
439 return FF(data);
440 }
441
451 template <typename T>
452 inline static Element mutateGroupElement(Element e, T& rng, HavocSettings& havoc_config)
453 requires SimpleRng<T>
454 {
455 // We can't just randomely modify a point on a curve
456 // But we can modify it's scalar
457 // With a certain probability, we apply changes to the Montgomery form, rather than the plain form. This
458 // has merit, since the computation is performed in montgomery form and comparisons are often performed
459 // in it, too.
460 // By the same logic we can switch between Jacobian and Affine coordinates.
461 // Libfuzzer comparison tracing logic can then be enabled in Montgomery form
462 bool convert_to_montgomery = (rng.next() % (havoc_config.VAL_MUT_MONTGOMERY_PROBABILITY +
463 havoc_config.VAL_MUT_NON_MONTGOMERY_PROBABILITY)) <
464 havoc_config.VAL_MUT_MONTGOMERY_PROBABILITY;
465 bool normalize = (rng.next() % (havoc_config.VAL_MUT_MONTGOMERY_PROBABILITY +
466 havoc_config.VAL_MUT_NON_MONTGOMERY_PROBABILITY)) <
467 havoc_config.VAL_MUT_MONTGOMERY_PROBABILITY;
468 uint256_t value_data;
469
470 // Pick the last value from the mutation distribution vector
471 const size_t mutation_type_count = havoc_config.value_mutation_distribution.size();
472 // Choose mutation
473 const size_t choice = rng.next() % havoc_config.value_mutation_distribution[mutation_type_count - 1];
474 if (choice < havoc_config.value_mutation_distribution[0]) {
475 // Delegate mutation to libfuzzer (bit/byte mutations, autodictionary, etc)
476 value_data = to_uint256_montgomery(e.scalar, convert_to_montgomery);
477 LLVMFuzzerMutate((uint8_t*)&value_data, sizeof(uint256_t), sizeof(uint256_t));
478 e.scalar = from_uint256_montgomery<ScalarField>(value_data, convert_to_montgomery);
479 e.value = GroupElement::one() * e.scalar;
480 } else if (choice < havoc_config.value_mutation_distribution[1]) {
481 // Small addition/subtraction
482 if (convert_to_montgomery) {
483 e.scalar = e.scalar.to_montgomery_form();
484 }
485 auto extra = ScalarField(rng.next() & 0xff);
486
487 // With 50% probability we add/sub a small value
488 if (rng.next() & 1) {
489 auto switch_sign = static_cast<bool>(rng.next() & 1);
490 if (!switch_sign) {
491 e.scalar += extra;
492 e.value += GroupElement::one() * extra;
493 } else {
494 e.scalar -= extra;
495 e.value -= GroupElement::one() * extra;
496 }
497 } else {
498 // otherwise we multiply by a small value
499 e.scalar *= extra;
500 e.value *= extra;
501 }
502 if (normalize) {
503 e.value = e.value.normalize();
504 }
505 if (convert_to_montgomery) {
506 e.scalar = e.scalar.from_montgomery_form();
507 }
508 } else if (choice < havoc_config.value_mutation_distribution[2]) {
509 if (convert_to_montgomery) {
510 e.scalar = e.scalar.to_montgomery_form();
511 }
512 // Substitute scalar element with a special value
513 auto special_value = static_cast<SpecialScalarValue>(rng.next() % SPECIAL_VALUE_COUNT);
514 e.scalar = get_special_scalar_value<ScalarField>(special_value);
515 if (convert_to_montgomery) {
516 e.scalar = e.scalar.to_montgomery_form();
517 }
518 e.value = GroupElement::one() * e.scalar;
519 }
520 // Return value
521 return e;
522 }
532 template <typename T>
533 inline static ScalarField mutateScalarElement(ScalarField e, T& rng, HavocSettings& havoc_config)
534 requires SimpleRng<T>
535 {
536 // With a certain probability, we apply changes to the Montgomery form, rather than the plain form. This
537 // has merit, since the computation is performed in montgomery form and comparisons are often performed
538 // in it, too.
539 // Libfuzzer comparison tracing logic can then be enabled in Montgomery form
540 bool convert_to_montgomery = (rng.next() % (havoc_config.VAL_MUT_MONTGOMERY_PROBABILITY +
541 havoc_config.VAL_MUT_NON_MONTGOMERY_PROBABILITY)) <
542 havoc_config.VAL_MUT_MONTGOMERY_PROBABILITY;
543 uint256_t value_data;
544
545 // Pick the last value from the mutation distrivution vector
546 const size_t mutation_type_count = havoc_config.value_mutation_distribution.size();
547 // Choose mutation
548 const size_t choice = rng.next() % havoc_config.value_mutation_distribution[mutation_type_count - 1];
549 if (choice < havoc_config.value_mutation_distribution[0]) {
550 // Delegate mutation to libfuzzer (bit/byte mutations, autodictionary, etc)
551 value_data = to_uint256_montgomery(e, convert_to_montgomery);
552 LLVMFuzzerMutate((uint8_t*)&value_data, sizeof(uint256_t), sizeof(uint256_t));
553 e = from_uint256_montgomery<ScalarField>(value_data, convert_to_montgomery);
554 } else if (choice < havoc_config.value_mutation_distribution[1]) {
555 // Small addition/subtraction
556 if (convert_to_montgomery) {
557 e = e.to_montgomery_form();
558 }
559 auto extra = ScalarField(rng.next() & 0xff);
560
561 // With 50% probability we add/sub a small value
562 if (rng.next() & 1) {
563 auto switch_sign = static_cast<bool>(rng.next() & 1);
564 if (!switch_sign) {
565 e += extra;
566 } else {
567 e -= extra;
568 }
569 } else {
570 // otherwise we multiply by a small value
571 e *= extra;
572 }
573 if (convert_to_montgomery) {
574 e = e.from_montgomery_form();
575 }
576 } else if (choice < havoc_config.value_mutation_distribution[2]) {
577 if (convert_to_montgomery) {
578 e = e.to_montgomery_form();
579 }
580 // Substitute scalar element with a special value excluding zero
581 // I think that zeros from mutateGroupElement are enough zeros produced
582 auto special_value = static_cast<SpecialScalarValue>(rng.next() % SPECIAL_VALUE_COUNT_NO_ZERO);
583 e = get_special_scalar_value<ScalarField>(special_value);
584 if (convert_to_montgomery) {
585 e = e.to_montgomery_form();
586 }
587 }
588 // Return value
589 return e;
590 }
600 template <typename T>
602 requires SimpleRng<T>
603 {
604#define PUT_RANDOM_BYTE_IF_LUCKY(variable) \
605 if (rng.next() & 1) { \
606 variable = rng.next() & 0xff; \
607 }
608 // Depending on instruction type...
609 switch (instruction.id) {
610 case OPCODE::CONSTANT:
611 case OPCODE::WITNESS:
613 // If it represents pushing a value on the stack with a 50% probability randomly sample a bit_range
614 // Maybe mutate the value
615 if (rng.next() & 1) {
616 instruction.arguments.element =
617 mutateGroupElement(instruction.arguments.element, rng, havoc_config);
618 }
619 break;
620
621 case OPCODE::DBL:
622 case OPCODE::NEG:
624 case OPCODE::SET:
625 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.twoArgs.in);
626 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.twoArgs.out);
627 break;
628#ifndef DISABLE_MULTIPLICATION
629 case OPCODE::MULTIPLY:
630 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.mulArgs.in);
631 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.mulArgs.out);
632 if (rng.next() & 1) {
633 instruction.arguments.mulArgs.scalar =
634 mutateScalarElement(instruction.arguments.mulArgs.scalar, rng, havoc_config);
635 }
636 break;
637#endif
638 case OPCODE::ADD:
639 case OPCODE::SUBTRACT:
640 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.threeArgs.in1);
641 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.threeArgs.in2);
642 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.threeArgs.out);
643 break;
645 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.fourArgs.in1);
646 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.fourArgs.in2);
647 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.fourArgs.in3);
648 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.fourArgs.out);
649 break;
650#ifndef DISABLE_BATCH_MUL
652 if (rng.next() & 1) {
653 uint8_t mult_size0 =
655 static_cast<uint8_t>(rng.next() % ((MAXIMUM_MUL_ELEMENTS - MINIMUM_MUL_ELEMENTS) / 2));
656 uint8_t mult_size1 =
658 static_cast<uint8_t>(rng.next() % ((MAXIMUM_MUL_ELEMENTS - MINIMUM_MUL_ELEMENTS) / 2));
659 uint8_t mult_size =
660 mult_size0 +
661 mult_size1; // Sample the amount of batch mul participants from the binomial distribution
662 instruction.arguments.batchMulArgs.add_elements_count = mult_size;
663 }
664 if (instruction.arguments.batchMulArgs.add_elements_count && (rng.next() & 1)) {
665 size_t mut_count =
666 static_cast<uint8_t>(rng.next() % (instruction.arguments.batchMulArgs.add_elements_count));
667 for (size_t i = 0; i < mut_count; i++) {
668 size_t ind =
669 rng.next() % static_cast<size_t>(instruction.arguments.batchMulArgs.add_elements_count);
670 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.batchMulArgs.inputs[ind]);
671 }
672 }
673 if (instruction.arguments.batchMulArgs.add_elements_count && (rng.next() & 1)) {
674 size_t mut_count =
675 static_cast<uint8_t>(rng.next() % (instruction.arguments.batchMulArgs.add_elements_count));
676 for (size_t i = 0; i < mut_count; i++) {
677 size_t ind =
678 rng.next() % static_cast<size_t>(instruction.arguments.batchMulArgs.add_elements_count);
679 instruction.arguments.batchMulArgs.scalars[ind] =
680 mutateScalarElement(instruction.arguments.batchMulArgs.scalars[ind], rng, havoc_config);
681 }
682 }
683 PUT_RANDOM_BYTE_IF_LUCKY(instruction.arguments.batchMulArgs.output_index);
684 break;
685#endif
687 instruction.arguments.randomseed = rng.next();
688 break;
689 default:
690 abort(); // We missed some instructions in switch
691 return instruction;
692 }
693 return instruction;
694 }
695 };
696 // We use argsizes to both specify the size of data needed to parse the instruction and to signal that the
697 // instruction is enabled (if it is -1,it's disabled )
698 class ArgSizes {
699 public:
700 static constexpr size_t CONSTANT = sizeof(typename Instruction::Element);
701 static constexpr size_t WITNESS = sizeof(typename Instruction::Element);
702 static constexpr size_t CONSTANT_WITNESS = sizeof(typename Instruction::Element);
703 static constexpr size_t VALIDATE_ON_CURVE = 1;
704 static constexpr size_t DBL = 2;
705 static constexpr size_t NEG = 2;
706 static constexpr size_t ASSERT_EQUAL = 2;
707 static constexpr size_t SET = 2;
708 static constexpr size_t ADD = 3;
709 static constexpr size_t SUBTRACT = 3;
710 static constexpr size_t COND_ASSIGN = 4;
711#ifndef DISABLE_MULTIPLICATION
712 static constexpr size_t MULTIPLY = sizeof(typename Instruction::MulArgs);
713#endif
714#ifndef DISABLE_BATCH_MUL
715 static constexpr size_t BATCH_MUL = sizeof(typename Instruction::BatchMulArgs);
716#endif
717 static constexpr size_t RANDOMSEED = sizeof(uint32_t);
718 };
719
726 public:
727 static constexpr size_t SET = 0;
728 static constexpr size_t RANDOMSEED = 0;
729
730 static constexpr size_t CONSTANT = 1;
731 static constexpr size_t WITNESS = 1;
732 static constexpr size_t CONSTANT_WITNESS = 1;
733 static constexpr size_t ADD = 1;
734 static constexpr size_t SUBTRACT = 1;
735 static constexpr size_t DBL = 1;
736 static constexpr size_t NEG = 1;
737 static constexpr size_t COND_ASSIGN = 1;
738
739 static constexpr size_t VALIDATE_ON_CURVE = 2;
740
741#ifndef DISABLE_MULTIPLICATION
742 static constexpr size_t MULTIPLY = 2;
743#endif
744 static constexpr size_t ASSERT_EQUAL = 2;
745
746#ifndef DISABLE_BATCH_MUL
747 static constexpr size_t BATCH_MUL = 4;
748#endif
749 static constexpr size_t _LIMIT = 64;
750 };
751
756 class Parser {
757 public:
765 template <typename Instruction::OPCODE opcode> inline static Instruction parseInstructionArgs(uint8_t* Data)
766 {
767 Instruction instr;
768 instr.id = static_cast<typename Instruction::OPCODE>(opcode);
769 switch (opcode) {
773 auto scalar = ScalarField::serialize_from_buffer(Data);
774 auto el = GroupElement::one() * scalar;
775 instr.arguments.element = typename Instruction::Element(scalar, el);
776 break;
777 }
779 instr.arguments.oneArg = { .in = *Data };
780 break;
781 }
786 instr.arguments.twoArgs = { .in = *Data, .out = *(Data + 1) };
787 break;
790 instr.arguments.threeArgs = { .in1 = *Data, .in2 = *(Data + 1), .out = *(Data + 2) };
791 break;
793 instr.arguments.fourArgs = { .in1 = *Data, .in2 = *(Data + 1), .in3 = *(Data + 2), .out = *(Data + 3) };
794 break;
795
796#ifndef DISABLE_MULTIPLICATION
798 instr.arguments.mulArgs.in = *Data;
799 instr.arguments.mulArgs.out = *(Data + 1);
800 instr.arguments.mulArgs.scalar = ScalarField::serialize_from_buffer(Data + 2);
801 break;
802#endif
803#ifndef DISABLE_BATCH_MUL
805 // In case of LLVM native instruction mutator
809 }
810 instr.arguments.batchMulArgs.output_index = *(Data + 1);
811
813 memcpy(instr.arguments.batchMulArgs.inputs, Data + 2, n);
814
815 size_t offset = n + 2;
816 for (size_t i = 0; i < n; i++) {
817 instr.arguments.batchMulArgs.scalars[i] = ScalarField::serialize_from_buffer(Data + offset);
818 offset += sizeof(ScalarField);
819 }
820 break;
821 }
822#endif
824 memcpy(&instr.arguments.randomseed, Data, sizeof(uint32_t));
825 break;
826 default:
827 abort(); // We missed some instructions in switch
828 }
829 return instr;
830 }
838 template <typename Instruction::OPCODE instruction_opcode>
839 inline static void writeInstruction(Instruction& instruction, uint8_t* Data)
840 {
841 *Data = instruction.id;
842 switch (instruction_opcode) {
846 ScalarField::serialize_to_buffer(instruction.arguments.element.scalar, Data + 1);
847 break;
849 *(Data + 1) = instruction.arguments.oneArg.in;
850 break;
855 *(Data + 1) = instruction.arguments.twoArgs.in;
856 *(Data + 2) = instruction.arguments.twoArgs.out;
857 break;
860 *(Data + 1) = instruction.arguments.threeArgs.in1;
861 *(Data + 2) = instruction.arguments.threeArgs.in2;
862 *(Data + 3) = instruction.arguments.threeArgs.out;
863 break;
865 *(Data + 1) = instruction.arguments.fourArgs.in1;
866 *(Data + 2) = instruction.arguments.fourArgs.in2;
867 *(Data + 3) = instruction.arguments.fourArgs.in3;
868 *(Data + 4) = instruction.arguments.fourArgs.out;
869 break;
870#ifndef DISABLE_MULTIPLICATION
872 *(Data + 1) = instruction.arguments.mulArgs.in;
873 *(Data + 2) = instruction.arguments.mulArgs.out;
874 ScalarField::serialize_to_buffer(instruction.arguments.mulArgs.scalar, Data + 3);
875 break;
876#endif
877#ifndef DISABLE_BATCH_MUL
879 *(Data + 1) = instruction.arguments.batchMulArgs.add_elements_count;
880 *(Data + 2) = instruction.arguments.batchMulArgs.output_index;
881
882 size_t n = instruction.arguments.batchMulArgs.add_elements_count;
883
884 memcpy(Data + 3, instruction.arguments.batchMulArgs.inputs, n);
885 size_t offset = n + 3;
886 for (size_t i = 0; i < n; i++) {
887 ScalarField::serialize_to_buffer(instruction.arguments.batchMulArgs.scalars[i], Data + offset);
888 offset += sizeof(ScalarField);
889 }
890 break;
891 }
892#endif
894 memcpy(Data + 1, &instruction.arguments.randomseed, sizeof(uint32_t));
895 break;
896 default:
897 abort(); // We missed some instructions in switch
898 }
899 return;
900 };
901 };
902
908 private:
909 static bool_t construct_predicate(Builder* builder, const bool predicate)
910 {
911 /* The context field of a predicate can be nullptr;
912 * in that case, the function that handles the predicate
913 * will use the context of another input parameter
914 */
915 const bool predicate_is_const = static_cast<bool>(VarianceRNG.next() & 1);
916 if (predicate_is_const) {
917 const bool predicate_has_ctx = static_cast<bool>(VarianceRNG.next() % 2);
918 debug_log("bool_t(", (predicate_has_ctx ? "&builder," : "nullptr,"), (predicate ? "true)" : "false)"));
919 return bool_t(predicate_has_ctx ? builder : nullptr, predicate);
920 }
921 debug_log("bool_t(witness_t(&builder, ", (predicate ? "true));" : "false))"));
922 return bool_t(witness_t(builder, predicate));
923 }
924
926 {
927 const bool reconstruct = static_cast<bool>(VarianceRNG.next() & 1);
928 return reconstruct ? biggroup_t(this->biggroup) : this->biggroup;
929 }
930
931 public:
935
936 ExecutionHandler() = default;
938 : base_scalar(s)
939 , base(g)
940 , biggroup(w_g)
941 {}
942
943 private:
953 const ExecutionHandler& other,
954 const ScalarField& base_scalar_res,
955 const GroupElement& base_res)
956 {
957 uint8_t dbl_path = VarianceRNG.next() % 4;
958 switch (dbl_path) {
959 case 0:
960 debug_log("left.dbl", "\n");
961 return ExecutionHandler(base_scalar_res, base_res, this->bg().dbl());
962 case 1:
963 debug_log("right.dbl", "\n");
964 return ExecutionHandler(base_scalar_res, base_res, other.bg().dbl());
965 case 2:
966 return ExecutionHandler(base_scalar_res, base_res, this->bg() + other.bg());
967 case 3:
968 return ExecutionHandler(base_scalar_res, base_res, other.bg() + this->bg());
969 }
970 return {};
971 }
972
982 const ScalarField& base_scalar_res,
983 const GroupElement& base_res)
984 {
985 uint8_t inf_path = VarianceRNG.next() % 2;
986 biggroup_t res;
987 switch (inf_path) {
988 case 0:
989 return ExecutionHandler(base_scalar_res, base_res, this->bg() + other.bg());
990 case 1:
991 return ExecutionHandler(base_scalar_res, base_res, other.bg() + this->bg());
992 }
993 return {};
994 }
995
1004 const ScalarField& base_scalar_res,
1005 const GroupElement& base_res)
1006 {
1007 bool smth_inf =
1008 this->biggroup.is_point_at_infinity().get_value() || other.biggroup.is_point_at_infinity().get_value();
1009 uint8_t add_option = smth_inf ? 2 + (VarianceRNG.next() % 2) : VarianceRNG.next() % 4;
1010
1011 switch (add_option) {
1012 case 0:
1013 debug_log("left.checked_unconditional_add(right);", "\n");
1014 return ExecutionHandler(base_scalar_res, base_res, this->bg().checked_unconditional_add(other.bg()));
1015 case 1:
1016 debug_log("right.checked_unconditional_add(left);", "\n");
1017 return ExecutionHandler(base_scalar_res, base_res, other.bg().checked_unconditional_add(this->bg()));
1018 case 2:
1019 return ExecutionHandler(base_scalar_res, base_res, this->bg() + other.bg());
1020 case 3:
1021 return ExecutionHandler(base_scalar_res, base_res, other.bg() + this->bg());
1022 }
1023 return {};
1024 }
1025
1026 public:
1028 {
1029 ScalarField base_scalar_res = this->base_scalar + other.base_scalar;
1030 GroupElement base_res = this->base + other.base;
1031
1032 // Test doubling path when points are equal
1033 if (other.bg().get_value() == this->bg().get_value()) {
1034 return handle_add_doubling_case(builder, other, base_scalar_res, base_res);
1035 }
1036
1037 // Test infinity path when points are negations
1038 if (other.bg().get_value() == -this->bg().get_value()) {
1039 return handle_add_infinity_case(other, base_scalar_res, base_res);
1040 }
1041
1042 // Test normal addition paths
1043 return handle_add_normal_case(other, base_scalar_res, base_res);
1044 }
1045
1046 private:
1051 const ExecutionHandler& other,
1052 const ScalarField& base_scalar_res,
1053 const GroupElement& base_res)
1054 {
1055 uint8_t dbl_path = VarianceRNG.next() % 3;
1056
1057 switch (dbl_path) {
1058 case 0:
1059 debug_log("left.dbl();", "\n");
1060 return ExecutionHandler(base_scalar_res, base_res, this->bg().dbl());
1061 case 1:
1062 debug_log("-right.dbl();", "\n");
1063 return ExecutionHandler(base_scalar_res, base_res, -other.bg().dbl());
1064 case 2:
1065 return ExecutionHandler(base_scalar_res, base_res, this->bg() - other.bg());
1066 }
1067 return {};
1068 }
1069
1074 const ScalarField& base_scalar_res,
1075 const GroupElement& base_res)
1076 {
1077 return ExecutionHandler(base_scalar_res, base_res, this->bg() - other.bg());
1078 }
1079
1084 const ScalarField& base_scalar_res,
1085 const GroupElement& base_res)
1086 {
1087 bool smth_inf =
1088 this->biggroup.is_point_at_infinity().get_value() || other.biggroup.is_point_at_infinity().get_value();
1089 uint8_t add_option = smth_inf ? 1 : VarianceRNG.next() % 2;
1090
1091 switch (add_option) {
1092 case 0:
1093 debug_log("left.checked_unconditional_subtract(right);", "\n");
1094 return ExecutionHandler(
1095 base_scalar_res, base_res, this->bg().checked_unconditional_subtract(other.bg()));
1096 case 1:
1097 return ExecutionHandler(base_scalar_res, base_res, this->bg() - other.bg());
1098 }
1099 return {};
1100 }
1101
1102 public:
1107 {
1108 ScalarField base_scalar_res = this->base_scalar - other.base_scalar;
1109 GroupElement base_res = this->base - other.base;
1110
1111 if (other.bg().get_value() == -this->bg().get_value()) {
1112 return handle_sub_doubling_case(builder, other, base_scalar_res, base_res);
1113 }
1114 if (other.bg().get_value() == this->bg().get_value()) {
1115 return handle_sub_infinity_case(other, base_scalar_res, base_res);
1116 }
1117 return handle_sub_normal_case(other, base_scalar_res, base_res);
1118 }
1119
1121 {
1122 bool is_witness = VarianceRNG.next() & 1;
1123 debug_log(" * big_scalar_t",
1124 (is_witness ? "::from_witness(&builder, " : "("),
1125 "ScalarField(\"",
1126 multiplier,
1127 "\"));");
1128 auto scalar = is_witness ? big_scalar_t::from_witness(builder, multiplier) : big_scalar_t(multiplier);
1129 return ExecutionHandler(this->base_scalar * multiplier, this->base * multiplier, this->bg() * scalar);
1130 }
1131
1133 const std::vector<ExecutionHandler>& to_add,
1134 const std::vector<ScalarField>& to_mul)
1135 {
1136 std::vector<biggroup_t> to_add_bg;
1137 to_add_bg.reserve(to_add.size());
1138 std::vector<big_scalar_t> to_mul_bs;
1139 to_mul_bs.reserve(to_mul.size());
1140
1141 GroupElement accumulator_bg = GroupElement::one();
1142 ScalarField accumulator_bs = ScalarField::zero();
1143
1144 for (size_t i = 0; i < to_add.size(); i++) {
1145 to_add_bg.push_back(to_add[i].biggroup);
1146
1147 bool is_witness = VarianceRNG.next() & 1;
1148 debug_log("biggroup_t",
1149 (is_witness ? "::from_witness(&builder, " : "("),
1150 "ScalarField(\"",
1151 to_mul[i],
1152 "\")), ");
1153 auto scalar = is_witness ? big_scalar_t::from_witness(builder, to_mul[i]) : big_scalar_t(to_mul[i]);
1154 to_mul_bs.push_back(scalar);
1155
1156 accumulator_bg += to_add[i].base * to_mul[i];
1157 accumulator_bs += to_add[i].base_scalar * to_mul[i];
1158 }
1159 accumulator_bg -= GroupElement::one();
1160
1161 // Handle the linearly dependant case that is
1162 // assumed to not happen in real life
1163 if (accumulator_bg.is_point_at_infinity()) {
1164 to_add_bg.push_back(make_constant_biggroup(AffineElement::one()));
1165 to_mul_bs.push_back(big_scalar_t(ScalarField::one()));
1166 accumulator_bg += GroupElement::one();
1167 accumulator_bs += ScalarField::one();
1168 }
1169
1170 auto batch_mul_res = biggroup_t::batch_mul(to_add_bg, to_mul_bs);
1171 return ExecutionHandler(accumulator_bs, accumulator_bg, batch_mul_res);
1172 }
1173
1174 void validate_on_curve() const { this->bg().validate_on_curve(); }
1175
1176 ExecutionHandler operator-() { return ExecutionHandler(-this->base_scalar, -this->base, -this->bg()); }
1177
1179 {
1180 return ExecutionHandler(this->base_scalar + this->base_scalar, this->base.dbl(), this->bg().dbl());
1181 }
1182
1184 {
1185 ScalarField new_base_scalar = predicate ? other.base_scalar : this->base_scalar;
1186 GroupElement new_base = predicate ? other.base : this->base;
1187 biggroup_t new_biggroup;
1188 if (VarianceRNG.next() & 1) {
1189 debug_log("lhs.conditional_select(rhs, ");
1190 new_biggroup = this->bg().conditional_select(other.bg(), construct_predicate(builder, predicate));
1191 } else {
1192 debug_log("rhs.conditional_select(lhs, ");
1193 new_biggroup = other.bg().conditional_select(this->bg(), construct_predicate(builder, !predicate));
1194 }
1195 debug_log(");", "\n");
1196 return ExecutionHandler(new_base_scalar, new_base, new_biggroup);
1197 }
1198
1200 {
1201 if (other.bg().is_constant()) {
1202 if (this->bg().is_constant()) {
1203 // Assert equal does nothing in this case
1204 return;
1205 }
1206 }
1207 auto to_add = biggroup_t::from_witness(builder, AffineElement(this->base - other.base));
1208 auto to_ae = other.bg() + to_add;
1209 this->bg().incomplete_assert_equal(to_ae);
1210 }
1211
1212 /* Explicit re-instantiation using the various biggroup_t constructors */
1214 {
1215 uint32_t switch_case = VarianceRNG.next() % 4;
1216 switch (switch_case) {
1217 case 0:
1218 debug_log("biggroup_t(", "\n");
1219 /* construct via biggroup_t */
1220 return ExecutionHandler(this->base_scalar, this->base, biggroup_t(this->biggroup));
1221 case 1: {
1222 debug_log("biggroup_t::from",
1223 (this->biggroup.is_constant() ? "" : "_constant"),
1224 "_witness(&builder, e.get_value());");
1225 /* construct via AffineElement */
1226 AffineElement e = this->biggroup.get_value();
1227 return ExecutionHandler(this->base_scalar, this->base, biggroup_t::from_witness(builder, e));
1228 }
1229 case 2: {
1230 debug_log("tmp = el;", "\n");
1231 debug_log("res = biggroup_t(tmp);", "\n");
1232 /* Invoke assigment operator */
1233 biggroup_t bg_new = make_constant_biggroup(this->bg().get_value());
1234 bg_new = this->bg();
1235 return ExecutionHandler(this->base_scalar, this->base, biggroup_t(bg_new));
1236 }
1237 case 3: {
1238 debug_log("tmp = el;", "\n");
1239 debug_log("res = biggroup_t(std::move(tmp));", "\n");
1240 /* Invoke move constructor */
1241 biggroup_t bg_copy = this->bg();
1242 return ExecutionHandler(this->base_scalar, this->base, biggroup_t(std::move(bg_copy)));
1243 }
1244 default:
1245 abort();
1246 }
1247 }
1248
1257 static inline size_t execute_CONSTANT(Builder* builder,
1260 {
1261 (void)builder;
1262 stack.push_back(ExecutionHandler(
1263 instruction.arguments.element.scalar,
1264 instruction.arguments.element.value,
1265 make_constant_biggroup(static_cast<AffineElement>(instruction.arguments.element.value))));
1266 debug_log("// scalar = ", instruction.arguments.element.scalar);
1267 debug_log(
1268 "auto c", stack.size() - 1, " = biggroup_t(ae(\"", instruction.arguments.element.scalar, "\"));\n");
1269 return 0;
1270 };
1271
1280 static inline size_t execute_WITNESS(Builder* builder,
1283 {
1284 stack.push_back(ExecutionHandler(
1285 instruction.arguments.element.scalar,
1286 instruction.arguments.element.value,
1287 biggroup_t::from_witness(builder, static_cast<AffineElement>(instruction.arguments.element.value))));
1288 debug_log("// scalar = ", instruction.arguments.element.scalar);
1289 debug_log("auto w",
1290 stack.size() - 1,
1291 " = biggroup_t::from_witness(&builder, ae(\"",
1292 instruction.arguments.element.scalar,
1293 "\"));\n");
1294 return 0;
1295 }
1296
1309 {
1310
1311 if (VarianceRNG.next() & 1) {
1313 biggroup_t::from_witness(builder, static_cast<AffineElement>(instruction.arguments.element.value));
1314 biggroup.fix_witness();
1315
1316 stack.push_back(ExecutionHandler(
1317 instruction.arguments.element.scalar, instruction.arguments.element.value, biggroup));
1318 debug_log("// scalar = ", instruction.arguments.element.scalar);
1319 debug_log("auto cw",
1320 stack.size() - 1,
1321 " = biggroup_t::from_witness(&builder, ae(\"",
1322 instruction.arguments.element.scalar,
1323 "\"));",
1324 "\n");
1325 debug_log("cw", stack.size() - 1, ".fix_witness();", "\n");
1326 } else {
1328 make_constant_biggroup(static_cast<AffineElement>(instruction.arguments.element.value));
1329 biggroup.convert_constant_to_fixed_witness(builder);
1330
1331 stack.push_back(ExecutionHandler(
1332 instruction.arguments.element.scalar, instruction.arguments.element.value, biggroup));
1333 debug_log("// scalar = ", instruction.arguments.element.scalar);
1334 debug_log("auto cw",
1335 stack.size() - 1,
1336 " = biggroup_t(ae(\"",
1337 instruction.arguments.element.scalar,
1338 "\"));",
1339 "\n");
1340 debug_log("cw", stack.size() - 1, ".convert_constant_to_fixed_witness(&builder);", "\n");
1341 }
1342 // TODO(alex): set_public
1343 return 0;
1344 }
1345
1357 {
1358 (void)builder;
1359 if (stack.size() == 0) {
1360 return 1;
1361 }
1362 size_t first_index = instruction.arguments.oneArg.in % stack.size();
1363
1364 if constexpr (SHOW_FUZZING_INFO) {
1365 auto args = format_self_arg(stack, first_index);
1366 debug_log(args.out, ".validate_on_curve();", "\n");
1367 }
1368 stack[first_index].validate_on_curve();
1369 return 0;
1370 };
1371
1380 static inline size_t execute_DBL(Builder* builder,
1383 {
1384 (void)builder;
1385 if (stack.size() == 0) {
1386 return 1;
1387 }
1388 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
1389 size_t output_index = instruction.arguments.twoArgs.out;
1390
1391 if constexpr (SHOW_FUZZING_INFO) {
1392 auto args = format_single_arg(stack, first_index, output_index);
1393 debug_log(args.out, " = ", args.rhs, ".dbl();", "\n");
1394 }
1395 ExecutionHandler result = stack[first_index].dbl();
1396 // If the output index is larger than the number of elements in stack, append
1397 if (output_index >= stack.size()) {
1398 stack.push_back(result);
1399 } else {
1400 stack[output_index] = result;
1401 }
1402 return 0;
1403 };
1404
1413 static inline size_t execute_NEG(Builder* builder,
1416 {
1417 (void)builder;
1418 if (stack.size() == 0) {
1419 return 1;
1420 }
1421 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
1422 size_t output_index = instruction.arguments.twoArgs.out;
1423
1424 if constexpr (SHOW_FUZZING_INFO) {
1425 auto args = format_single_arg(stack, first_index, output_index);
1426 debug_log(args.out, " = -", args.rhs, ";", "\n");
1427 }
1428 ExecutionHandler result = -stack[first_index];
1429 // If the output index is larger than the number of elements in stack, append
1430 if (output_index >= stack.size()) {
1431 stack.push_back(result);
1432 } else {
1433 stack[output_index] = result;
1434 }
1435 return 0;
1436 };
1437
1446 static inline size_t execute_ASSERT_EQUAL(Builder* builder,
1449 {
1450 if (stack.size() == 0) {
1451 return 1;
1452 }
1453 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
1454 size_t second_index = instruction.arguments.twoArgs.out % stack.size();
1455
1456 if constexpr (SHOW_FUZZING_INFO) {
1457 auto args = format_two_arg(stack, first_index, second_index, 0);
1458 debug_log("incomplete_assert_equal(", args.lhs, ", ", args.rhs, ", builder);", "\n");
1459 }
1460 stack[first_index].incomplete_assert_equal(builder, stack[second_index]);
1461 return 0;
1462 };
1463
1472 static inline size_t execute_SET(Builder* builder,
1475 {
1476 (void)builder;
1477 if (stack.size() == 0) {
1478 return 1;
1479 }
1480 size_t first_index = instruction.arguments.twoArgs.in % stack.size();
1481 size_t output_index = instruction.arguments.twoArgs.out;
1482
1483 ExecutionHandler result;
1484 if constexpr (SHOW_FUZZING_INFO) {
1485 auto args = format_single_arg(stack, first_index, output_index);
1486 debug_log(args.out, " = ");
1487 // Need to split logs here, since `set` produces extra logs
1488 result = stack[first_index].set(builder);
1489 debug_log(args.rhs, ");", "\n");
1490 } else {
1491 result = stack[first_index].set(builder);
1492 }
1493 // If the output index is larger than the number of elements in stack, append
1494 if (output_index >= stack.size()) {
1495 stack.push_back(result);
1496 } else {
1497 stack[output_index] = result;
1498 }
1499 return 0;
1500 };
1501
1510 static inline size_t execute_ADD(Builder* builder,
1513 {
1514 (void)builder;
1515 if (stack.size() == 0) {
1516 return 1;
1517 }
1518 size_t first_index = instruction.arguments.threeArgs.in1 % stack.size();
1519 size_t second_index = instruction.arguments.threeArgs.in2 % stack.size();
1520 size_t output_index = instruction.arguments.threeArgs.out;
1521
1522 if constexpr (SHOW_FUZZING_INFO) {
1523 auto args = format_two_arg(stack, first_index, second_index, output_index);
1524 debug_log(args.out, " = ", args.lhs, " + ", args.rhs, ";", "\n");
1525 }
1526 ExecutionHandler result = stack[first_index].operator_add(builder, stack[second_index]);
1527 // If the output index is larger than the number of elements in stack, append
1528 if (output_index >= stack.size()) {
1529 stack.push_back(result);
1530 } else {
1531 stack[output_index] = result;
1532 }
1533 return 0;
1534 };
1535
1544 static inline size_t execute_SUBTRACT(Builder* builder,
1547 {
1548 (void)builder;
1549 if (stack.size() == 0) {
1550 return 1;
1551 }
1552 size_t first_index = instruction.arguments.threeArgs.in1 % stack.size();
1553 size_t second_index = instruction.arguments.threeArgs.in2 % stack.size();
1554 size_t output_index = instruction.arguments.threeArgs.out;
1555
1556 if constexpr (SHOW_FUZZING_INFO) {
1557 auto args = format_two_arg(stack, first_index, second_index, output_index);
1558 debug_log(args.out, " = ", args.lhs, " - ", args.rhs, ";", "\n");
1559 }
1560 ExecutionHandler result = stack[first_index].operator_sub(builder, stack[second_index]);
1561 // If the output index is larger than the number of elements in stack, append
1562 if (output_index >= stack.size()) {
1563 stack.push_back(result);
1564 } else {
1565 stack[output_index] = result;
1566 }
1567 return 0;
1568 };
1569
1578 static inline size_t execute_COND_ASSIGN(Builder* builder,
1581 {
1582 (void)builder;
1583 if (stack.size() == 0) {
1584 return 1;
1585 }
1586 size_t first_index = instruction.arguments.fourArgs.in1 % stack.size();
1587 size_t second_index = instruction.arguments.fourArgs.in2 % stack.size();
1588 size_t output_index = instruction.arguments.fourArgs.out % stack.size();
1589 bool predicate = instruction.arguments.fourArgs.in3 % 2;
1590
1591 ExecutionHandler result;
1592 if constexpr (SHOW_FUZZING_INFO) {
1593 auto args = format_two_arg(stack, first_index, second_index, output_index);
1594 debug_log("// ", args.out, " = ::conditional_select::", args.lhs, ", ", args.rhs, ";", "\n");
1595 // Need to split logs here, since `conditional_select` produces extra logs
1596 result = stack[first_index].conditional_select(builder, stack[second_index], predicate);
1597 } else {
1598 result = stack[first_index].conditional_select(builder, stack[second_index], predicate);
1599 }
1600
1601 // If the output index is larger than the number of elements in stack, append
1602 if (output_index >= stack.size()) {
1603 stack.push_back(result);
1604 } else {
1605 stack[output_index] = result;
1606 }
1607 return 0;
1608 };
1609
1618 static inline size_t execute_MULTIPLY(Builder* builder,
1621 {
1622
1623 if (stack.size() == 0) {
1624 return 1;
1625 }
1626 size_t first_index = instruction.arguments.mulArgs.in % stack.size();
1627 size_t output_index = instruction.arguments.mulArgs.out;
1628 ScalarField scalar = instruction.arguments.mulArgs.scalar;
1629
1630 if constexpr (SHOW_FUZZING_INFO) {
1631 auto args = format_single_arg(stack, first_index, output_index);
1632 debug_log(args.out, " = ", args.rhs);
1633 }
1634 ExecutionHandler result = stack[first_index].mul(builder, scalar);
1635 // If the output index is larger than the number of elements in stack, append
1636 if (output_index >= stack.size()) {
1637 stack.push_back(result);
1638 } else {
1639 stack[output_index] = result;
1640 }
1641 return 0;
1642 };
1643
1652 static inline size_t execute_BATCH_MUL(Builder* builder,
1655 {
1656 (void)builder;
1657 if (stack.size() == 0) {
1658 return 1;
1659 }
1661 std::vector<ScalarField> to_mul;
1662 for (size_t i = 0; i < instruction.arguments.batchMulArgs.add_elements_count; i++) {
1663 to_add.push_back(stack[(size_t)instruction.arguments.batchMulArgs.inputs[i] % stack.size()]);
1664 to_mul.push_back(instruction.arguments.batchMulArgs.scalars[i]);
1665 }
1666 size_t output_index = (size_t)instruction.arguments.batchMulArgs.output_index;
1667
1668 ExecutionHandler result;
1669 if constexpr (SHOW_FUZZING_INFO) {
1670 std::string res = "";
1671 bool is_const = true;
1672 for (size_t i = 0; i < instruction.arguments.batchMulArgs.add_elements_count; i++) {
1673 size_t idx = instruction.arguments.batchMulArgs.inputs[i] % stack.size();
1674 std::string el = stack[idx].biggroup.is_constant() ? "c" : "w";
1675 el += std::to_string(idx);
1676 res += el + ", ";
1677 is_const &= stack[idx].biggroup.is_constant();
1678 }
1679 std::string out = is_const ? "c" : "w";
1680 out = ((output_index >= stack.size()) ? "auto " : "") + out;
1681 out += std::to_string(output_index >= stack.size() ? stack.size() : output_index);
1682 debug_log(out, " = biggroup_t::batch_mul({", res, "}, {");
1683 // Need to split logs here, since `batch_mul` produces extra logs
1684 result = ExecutionHandler::batch_mul(builder, to_add, to_mul);
1685 debug_log("});", "\n");
1686 } else {
1687 result = ExecutionHandler::batch_mul(builder, to_add, to_mul);
1688 }
1689 // If the output index is larger than the number of elements in stack, append
1690 if (output_index >= stack.size()) {
1691 stack.push_back(result);
1692 } else {
1693 stack[output_index] = result;
1694 }
1695 return 0;
1696 };
1697
1706 static inline size_t execute_RANDOMSEED(Builder* builder,
1709 {
1710 (void)builder;
1711 (void)stack;
1712
1713 VarianceRNG.reseed(instruction.arguments.randomseed);
1714 return 0;
1715 };
1716 };
1717
1722
1733 {
1734 (void)builder;
1735 for (size_t i = 0; i < stack.size(); i++) {
1736 auto element = stack[i];
1737 if (element.biggroup.get_value() != AffineElement(element.base)) {
1738 std::cerr << "Failed at " << i << " with actual value " << AffineElement(element.base)
1739 << " and value in BigGroup " << element.biggroup.get_value() << std::endl;
1740 return false;
1741 }
1742 if ((AffineElement::one() * element.base_scalar) != AffineElement(element.base)) {
1743 std::cerr << "Failed at " << i << " with actual mul value " << element.base
1744 << " and value in scalar * BG " << element.biggroup.get_value() * element.base_scalar
1745 << std::endl;
1746 return false;
1747 }
1748 }
1749 return true;
1750 }
1751};
1752
1753template <typename Builder>
1755
1756// BN254_BF: scalar field represented as bigfield<Builder, Bn254FrParams> rather than native field_t
1757template <typename Builder>
1760 /*_use_bigfield=*/true,
1762
1763// secp256k1/secp256r1: ScalarField is already bigfield, so BigfieldScalar defaults correctly
1764template <typename Builder>
1766
1767template <typename Builder>
1769
1770#ifdef HAVOC_TESTING
1771
1772extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
1773{
1774 (void)argc;
1775 (void)argv;
1776 // These are the settings, optimized for the biggroup class (under them, fuzzer reaches maximum expected
1777 // coverage in 40 seconds)
1778 fuzzer_havoc_settings = HavocSettings{ .GEN_LLVM_POST_MUTATION_PROB = 30, // Out of 200
1779 .GEN_MUTATION_COUNT_LOG = 5, // -Fully checked
1780 .GEN_STRUCTURAL_MUTATION_PROBABILITY = 300, // Fully checked
1781 .GEN_VALUE_MUTATION_PROBABILITY = 700, // Fully checked
1782 .ST_MUT_DELETION_PROBABILITY = 100, // Fully checked
1783 .ST_MUT_DUPLICATION_PROBABILITY = 80, // Fully checked
1784 .ST_MUT_INSERTION_PROBABILITY = 120, // Fully checked
1785 .ST_MUT_MAXIMUM_DELETION_LOG = 6, // 2 because of limit
1786 .ST_MUT_MAXIMUM_DUPLICATION_LOG = 2, // -Fully checked
1787 .ST_MUT_SWAP_PROBABILITY = 50, // Fully checked
1788 .VAL_MUT_LLVM_MUTATE_PROBABILITY = 250, // Fully checked
1789 .VAL_MUT_MONTGOMERY_PROBABILITY = 130, // Fully checked
1790 .VAL_MUT_NON_MONTGOMERY_PROBABILITY = 50, // Fully checked
1791 .VAL_MUT_SMALL_ADDITION_PROBABILITY = 110, // Fully checked
1792 .VAL_MUT_SPECIAL_VALUE_PROBABILITY = 130, // Fully checked
1793 .structural_mutation_distribution = {},
1794 .value_mutation_distribution = {} };
1800 /*
1801 std::random_device rd;
1802 std::uniform_int_distribution<uint64_t> dist(0, ~(uint64_t)(0));
1803 srandom(static_cast<unsigned int>(dist(rd)));
1804
1805 fuzzer_havoc_settings =
1806 HavocSettings{ .GEN_MUTATION_COUNT_LOG = static_cast<size_t>((random() % 8) + 1),
1807 .GEN_STRUCTURAL_MUTATION_PROBABILITY = static_cast<size_t>(random() % 100),
1808 .GEN_VALUE_MUTATION_PROBABILITY = static_cast<size_t>(random() % 100),
1809 .ST_MUT_DELETION_PROBABILITY = static_cast<size_t>(random() % 100),
1810 .ST_MUT_DUPLICATION_PROBABILITY = static_cast<size_t>(random() % 100),
1811 .ST_MUT_INSERTION_PROBABILITY = static_cast<size_t>((random() % 99) + 1),
1812 .ST_MUT_MAXIMUM_DELETION_LOG = static_cast<size_t>((random() % 8) + 1),
1813 .ST_MUT_MAXIMUM_DUPLICATION_LOG = static_cast<size_t>((random() % 8) + 1),
1814 .ST_MUT_SWAP_PROBABILITY = static_cast<size_t>(random() % 100),
1815 .VAL_MUT_LLVM_MUTATE_PROBABILITY = static_cast<size_t>(random() % 100),
1816 .VAL_MUT_MONTGOMERY_PROBABILITY = static_cast<size_t>(random() % 100),
1817 .VAL_MUT_NON_MONTGOMERY_PROBABILITY = static_cast<size_t>(random() % 100),
1818 .VAL_MUT_SMALL_ADDITION_PROBABILITY = static_cast<size_t>(random() % 100),
1819 .VAL_MUT_SPECIAL_VALUE_PROBABILITY = static_cast<size_t>(random() % 100)
1820
1821 };
1822 while (fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY == 0 &&
1823 fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY == 0) {
1824 fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY = static_cast<size_t>(random() % 8);
1825 fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY = static_cast<size_t>(random() % 8);
1826 }
1827 */
1828
1829 // fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB = static_cast<size_t>(((random() % (20 - 1)) + 1) * 10);
1834 /*
1835 std::cerr << "CUSTOM MUTATOR SETTINGS:" << std::endl
1836 << "################################################################" << std::endl
1837 << "GEN_LLVM_POST_MUTATION_PROB: " << fuzzer_havoc_settings.GEN_LLVM_POST_MUTATION_PROB << std::endl
1838 << "GEN_MUTATION_COUNT_LOG: " << fuzzer_havoc_settings.GEN_MUTATION_COUNT_LOG << std::endl
1839 << "GEN_STRUCTURAL_MUTATION_PROBABILITY: " <<
1840 fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY
1841 << std::endl
1842 << "GEN_VALUE_MUTATION_PROBABILITY: " << fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY <<
1843 std::endl
1844 << "ST_MUT_DELETION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_DELETION_PROBABILITY << std::endl
1845 << "ST_MUT_DUPLICATION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_DUPLICATION_PROBABILITY <<
1846 std::endl
1847 << "ST_MUT_INSERTION_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_INSERTION_PROBABILITY << std::endl
1848 << "ST_MUT_MAXIMUM_DELETION_LOG: " << fuzzer_havoc_settings.ST_MUT_MAXIMUM_DELETION_LOG << std::endl
1849 << "ST_MUT_MAXIMUM_DUPLICATION_LOG: " << fuzzer_havoc_settings.ST_MUT_MAXIMUM_DUPLICATION_LOG <<
1850 std::endl
1851 << "ST_MUT_SWAP_PROBABILITY: " << fuzzer_havoc_settings.ST_MUT_SWAP_PROBABILITY << std::endl
1852 << "VAL_MUT_LLVM_MUTATE_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_LLVM_MUTATE_PROBABILITY
1853 << std::endl
1854 << "VAL_MUT_MONTGOMERY_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_MONTGOMERY_PROBABILITY <<
1855 std::endl
1856 << "VAL_MUT_NON_MONTGOMERY_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_NON_MONTGOMERY_PROBABILITY
1857 << std::endl
1858 << "VAL_MUT_SMALL_ADDITION_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_SMALL_ADDITION_PROBABILITY
1859 << std::endl
1860 << "VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY: "
1861 << fuzzer_havoc_settings.VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY << std::endl
1862 << "VAL_MUT_SPECIAL_VALUE_PROBABILITY: " << fuzzer_havoc_settings.VAL_MUT_SPECIAL_VALUE_PROBABILITY
1863 << std::endl;
1864 */
1865 std::vector<size_t> structural_mutation_distribution;
1866 std::vector<size_t> value_mutation_distribution;
1867 size_t temp = 0;
1868 temp += fuzzer_havoc_settings.ST_MUT_DELETION_PROBABILITY;
1869 structural_mutation_distribution.push_back(temp);
1870 temp += fuzzer_havoc_settings.ST_MUT_DUPLICATION_PROBABILITY;
1871 structural_mutation_distribution.push_back(temp);
1872 temp += fuzzer_havoc_settings.ST_MUT_INSERTION_PROBABILITY;
1873 structural_mutation_distribution.push_back(temp);
1874 temp += fuzzer_havoc_settings.ST_MUT_SWAP_PROBABILITY;
1875 structural_mutation_distribution.push_back(temp);
1876 fuzzer_havoc_settings.structural_mutation_distribution = structural_mutation_distribution;
1877
1878 temp = 0;
1879 temp += fuzzer_havoc_settings.VAL_MUT_LLVM_MUTATE_PROBABILITY;
1880 value_mutation_distribution.push_back(temp);
1881 temp += fuzzer_havoc_settings.VAL_MUT_SMALL_ADDITION_PROBABILITY;
1882 value_mutation_distribution.push_back(temp);
1883 temp += fuzzer_havoc_settings.VAL_MUT_SPECIAL_VALUE_PROBABILITY;
1884 value_mutation_distribution.push_back(temp);
1885
1886 fuzzer_havoc_settings.value_mutation_distribution = value_mutation_distribution;
1887 return 0;
1888}
1889#endif
1890
1895extern "C" size_t LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size)
1896{
1897 if constexpr (BigCurveTypes == BigCurveType::BN254) {
1898 RunWithBuilders<BigGroupBN254Base, FuzzerCircuitTypes>(Data, Size, VarianceRNG);
1899 } else if constexpr (BigCurveTypes == BigCurveType::BN254_BF) {
1900 RunWithBuilders<BigGroupBN254BFBase, FuzzerCircuitTypes>(Data, Size, VarianceRNG);
1901 } else if constexpr (BigCurveTypes == BigCurveType::Secp256k1) {
1902 RunWithBuilders<BigGroupSecp256k1Base, FuzzerCircuitTypes>(Data, Size, VarianceRNG);
1903 } else if constexpr (BigCurveTypes == BigCurveType::Secp256r1) {
1904 RunWithBuilders<BigGroupSecp256r1Base, FuzzerCircuitTypes>(Data, Size, VarianceRNG);
1905 } else {
1906 abort(); // Invalid BigGroup type
1907 }
1908 return 0;
1909}
1910
1911#pragma clang diagnostic pop
bb::field< bb::Bn254FrParams > FF
Definition field.cpp:24
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)
constexpr uint64_t BigCurveTypes
static constexpr size_t SET
static constexpr size_t DBL
static constexpr size_t NEG
static constexpr size_t ASSERT_EQUAL
static constexpr size_t RANDOMSEED
static constexpr size_t ADD
static constexpr size_t BATCH_MUL
static constexpr size_t SUBTRACT
static constexpr size_t WITNESS
static constexpr size_t CONSTANT_WITNESS
static constexpr size_t MULTIPLY
static constexpr size_t COND_ASSIGN
static constexpr size_t CONSTANT
static constexpr size_t VALIDATE_ON_CURVE
This class implements the execution of biggroup with an oracle to detect discrepancies.
static size_t execute_VALIDATE_ON_CURVE(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the VALIDATE_ON_CURVE instruction.
static size_t execute_MULTIPLY(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the multiply instruction.
static size_t execute_SUBTRACT(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the subtraction operator instruction.
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_add(Builder *builder, const ExecutionHandler &other)
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)
ExecutionHandler mul(Builder *builder, const ScalarField &multiplier)
static size_t execute_DBL(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the DBL instruction.
static size_t execute_NEG(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the NEG instruction.
static size_t execute_ADD(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the addition operator instruction.
static bool_t construct_predicate(Builder *builder, const bool predicate)
static size_t execute_CONSTANT_WITNESS(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the constant_witness instruction (push a biggroup witness set to be public or constant made w...
static size_t execute_ASSERT_EQUAL(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the ASSERT_EQUAL instruction.
static ExecutionHandler batch_mul(Builder *builder, const std::vector< ExecutionHandler > &to_add, const std::vector< ScalarField > &to_mul)
void incomplete_assert_equal(Builder *builder, ExecutionHandler &other)
static size_t execute_RANDOMSEED(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the RANDOMSEED instruction.
static size_t execute_SET(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the SET instruction.
static size_t execute_COND_ASSIGN(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the COND_ASSIGN instruction.
ExecutionHandler set(Builder *builder)
static size_t execute_CONSTANT(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the constant instruction (push constant biggroup to the stack)
ExecutionHandler handle_add_normal_case(const ExecutionHandler &other, const ScalarField &base_scalar_res, const GroupElement &base_res)
Handle normal addition (no special edge cases)
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.
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)
ExecutionHandler operator_sub(Builder *builder, const ExecutionHandler &other)
Subtract two ExecutionHandlers, exploring different code paths for edge cases.
ExecutionHandler conditional_select(Builder *builder, ExecutionHandler &other, const bool predicate)
ExecutionHandler(ScalarField s, GroupElement g, biggroup_t w_g)
static size_t execute_WITNESS(Builder *builder, std::vector< ExecutionHandler > &stack, Instruction &instruction)
Execute the witness instruction (push witness biggroup to the stack)
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)
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 Element mutateGroupElement(Element e, T &rng, HavocSettings &havoc_config)
Mutate the value of a group element.
static Instruction mutateInstruction(Instruction instruction, T &rng, HavocSettings &havoc_config)
Mutate a single instruction.
static Instruction generateRandom(T &rng)
Generates a random instruction.
static ScalarField mutateScalarElement(ScalarField e, T &rng, HavocSettings &havoc_config)
Mutate the value of a scalar element.
static FF from_uint256_montgomery(const uint256_t &data, bool from_montgomery)
Convert uint256_t back to scalar field element, optionally from Montgomery form.
Optional subclass that governs limits on the use of certain instructions, since some of them can be t...
static constexpr size_t RANDOMSEED
static constexpr size_t BATCH_MUL
static constexpr size_t CONSTANT_WITNESS
static constexpr size_t ASSERT_EQUAL
static constexpr size_t VALIDATE_ON_CURVE
static constexpr size_t COND_ASSIGN
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 BigGroup fuzzing instructions, execution, etc.
std::conditional_t< _use_bigfield, bb::stdlib::element< Builder, typename Curve::BaseField, BigfieldScalar, typename Curve::GroupNative >, typename Curve::Group > biggroup_t
typename Curve::AffineElementNative AffineElement
typename Curve::ElementNative GroupElement
std::conditional_t< _use_bigfield, BigfieldScalar, typename Curve::ScalarField > big_scalar_t
typename Curve::ScalarFieldNative ScalarField
typename bb::stdlib::bool_t< Builder > bool_t
BigGroupType Curve
static bool postProcess(Builder *builder, std::vector< BigGroupBase::ExecutionHandler > &stack)
Check that the resulting values are equal to expected.
typename bb::stdlib::field_t< Builder > field_t
static biggroup_t make_constant_biggroup(const AffineElement &native_elem)
Create a constant (no-witness) biggroup element from a native affine element.
std::vector< ExecutionHandler > ExecutionState
typename bb::stdlib::witness_t< Builder > witness_t
typename Curve::BaseFieldNative 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 bb::g1 Group
Definition bn254.hpp:20
Implements boolean logic in-circuit.
Definition bool.hpp:60
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
ECCVMCircuitBuilder Builder
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
@ Secp256k1
@ BN254_BF
@ BN254
@ Secp256r1
Instruction instruction
AffineElement * rhs
std::conditional_t< IsGoblinBigGroup< C, Fq, Fr, G >, element_goblin::goblin_element< C, goblin_field< C >, Fr, G >, element_default::element< C, Fq, Fr, G > > element
element wraps either element_default::element or element_goblin::goblin_element depending on parametr...
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
uint8_t inputs[MAXIMUM_MUL_ELEMENTS]
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