barretenberg
Loading...
Searching...
No Matches
fuzzer.hpp
1#pragma once
2#include "barretenberg/numeric/uint256/uint256.hpp"
3#include "barretenberg/proof_system/circuit_builder/standard_circuit_builder.hpp"
4#include <concepts>
5
6// NOLINTBEGIN(cppcoreguidelines-macro-usage, google-runtime-int)
7#define PARENS ()
8
9// Rescan macro tokens 256 times
10#define EXPAND(arg) EXPAND1(EXPAND1(EXPAND1(EXPAND1(arg))))
11#define EXPAND1(arg) EXPAND2(EXPAND2(EXPAND2(EXPAND2(arg))))
12#define EXPAND2(arg) EXPAND3(EXPAND3(EXPAND3(EXPAND3(arg))))
13#define EXPAND3(arg) EXPAND4(EXPAND4(EXPAND4(EXPAND4(arg))))
14#define EXPAND4(arg) arg
15
16#define FOR_EACH(macro, ...) __VA_OPT__(EXPAND(FOR_EACH_HELPER(macro, __VA_ARGS__)))
17#define FOR_EACH_HELPER(macro, a1, ...) macro(a1) __VA_OPT__(FOR_EACH_AGAIN PARENS(macro, __VA_ARGS__))
18#define FOR_EACH_AGAIN() FOR_EACH_HELPER
19
20#define ALL_POSSIBLE_OPCODES \
21 CONSTANT, WITNESS, CONSTANT_WITNESS, ADD, SUBTRACT, MULTIPLY, DIVIDE, ADD_TWO, MADD, MULT_MADD, MSUB_DIV, SQR, \
22 ASSERT_EQUAL, ASSERT_NOT_EQUAL, SQR_ADD, ASSERT_EQUAL, ASSERT_NOT_EQUAL, SQR_ADD, SUBTRACT_WITH_CONSTRAINT, \
23 DIVIDE_WITH_CONSTRAINTS, SLICE, ASSERT_ZERO, ASSERT_NOT_ZERO, COND_NEGATE, ADD_MULTI, ASSERT_VALID, \
24 COND_SELECT, DOUBLE, RANDOMSEED, SELECT_IF_ZERO, SELECT_IF_EQ, REVERSE, GET_BIT, SET_BIT, SET, INVERT, AND, \
25 OR, XOR, MODULO, SHL, SHR, ROL, ROR, NOT
26
28 size_t GEN_LLVM_POST_MUTATION_PROB; // Controls frequency of additional mutation after structural ones
29 size_t GEN_MUTATION_COUNT_LOG; // This is the logarithm of the number of micromutations applied during mutation of a
30 // testcase
31 size_t GEN_STRUCTURAL_MUTATION_PROBABILITY; // The probability of applying a structural mutation
32 // (DELETION/DUPLICATION/INSERTION/SWAP)
33 size_t GEN_VALUE_MUTATION_PROBABILITY; // The probability of applying a value mutation
34 size_t ST_MUT_DELETION_PROBABILITY; // The probability of applying DELETION mutation
35 size_t ST_MUT_DUPLICATION_PROBABILITY; // The probability of applying DUPLICATION mutation
36 size_t ST_MUT_INSERTION_PROBABILITY; // The probability of applying INSERTION mutation
37 size_t ST_MUT_MAXIMUM_DELETION_LOG; // The logarithm of the maximum of deletions
38 size_t ST_MUT_MAXIMUM_DUPLICATION_LOG; // The logarithm of the maximum of duplication
39 size_t ST_MUT_SWAP_PROBABILITY; // The probability of a SWAP mutation
40 size_t VAL_MUT_LLVM_MUTATE_PROBABILITY; // The probablity of using the LLVM mutator on field element value
41 size_t VAL_MUT_MONTGOMERY_PROBABILITY; // The probability of converting to montgomery form before applying value
42 // mutations
43 size_t VAL_MUT_NON_MONTGOMERY_PROBABILITY; // The probability of not converting to montgomery form before applying
44 // value mutations
45 size_t VAL_MUT_SMALL_ADDITION_PROBABILITY; // The probability of performing small additions
46 size_t VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY; // The probability of performing small multiplications
47 size_t VAL_MUT_SPECIAL_VALUE_PROBABILITY; // The probability of assigning special values (0,1, p-1, p-2, p-1/2)
48 std::vector<size_t> structural_mutation_distribution; // Holds the values to quickly select a structural mutation
49 // based on chosen probabilities
50 std::vector<size_t> value_mutation_distribution; // Holds the values to quickly select a value mutation based on
51 // chosen probabilities
52};
53#ifdef HAVOC_TESTING
54
55HavocSettings fuzzer_havoc_settings;
56#endif
57// This is an external function in Libfuzzer used internally by custom mutators
58extern "C" size_t LLVMFuzzerMutate(uint8_t* Data, size_t Size, size_t MaxSize);
59
65 uint32_t state;
66
67 public:
68 FastRandom(uint32_t seed) { reseed(seed); }
69 uint32_t next()
70 {
71 state = static_cast<uint32_t>(
72 (static_cast<uint64_t>(state) * static_cast<uint64_t>(363364578) + static_cast<uint64_t>(537)) %
73 static_cast<uint64_t>(3758096939));
74 return state;
75 }
76 void reseed(uint32_t seed)
77 {
78 if (seed == 0) {
79 seed = 1;
80 }
81 state = seed;
82 }
83};
84
90template <typename T>
91concept SimpleRng = requires(T a) {
92 {
93 a.next()
94 } -> std::convertible_to<uint32_t>;
95 };
101template <typename T>
102concept InstructionArgumentSizes = requires {
103 {
104 std::make_tuple(T::CONSTANT,
105 T::WITNESS,
106 T::CONSTANT_WITNESS,
107 T::ADD,
108 T::SUBTRACT,
109 T::MULTIPLY,
110 T::DIVIDE,
111 T::ADD_TWO,
112 T::MADD,
113 T::MULT_MADD,
114 T::MSUB_DIV,
115 T::SQR,
116 T::SQR_ADD,
117 T::SUBTRACT_WITH_CONSTRAINT,
118 T::DIVIDE_WITH_CONSTRAINTS,
119 T::SLICE,
120 T::ASSERT_ZERO,
121 T::ASSERT_NOT_ZERO)
122 } -> std::same_as<std::tuple<size_t>>;
123 };
124
130template <typename T>
132 requires {
133 {
134 std::make_tuple(T::GEN_MUTATION_COUNT_LOG, T::GEN_STRUCTURAL_MUTATION_PROBABILITY)
135 } -> std::same_as<std::tuple<size_t>>;
136 T::GEN_MUTATION_COUNT_LOG <= 7;
137 };
143template <typename T>
145 typename T::ArgSizes;
146 typename T::Instruction;
147 typename T::ExecutionState;
148 typename T::ExecutionHandler;
150 };
151
157template <typename T>
158concept CheckableComposer = requires(T a) {
159 {
160 a.check_circuit()
161 } -> std::same_as<bool>;
162 };
163
171template <typename T, typename Composer, typename Context>
172concept PostProcessingEnabled = requires(Composer composer, Context context) {
173 {
174 T::postProcess(&composer, context)
175 } -> std::same_as<bool>;
176 };
177
184template <typename T>
185concept InstructionWeightsEnabled = requires {
186 typename T::InstructionWeights;
187 T::InstructionWeights::_LIMIT;
188 };
189
199template <typename T, typename FF>
200inline static FF mutateFieldElement(FF e, T& rng)
201 requires SimpleRng<T>
202{
203 // With a certain probability, we apply changes to the Montgomery form, rather than the plain form. This
204 // has merit, since the computation is performed in montgomery form and comparisons are often performed
205 // in it, too. Libfuzzer comparison tracing logic can then be enabled in Montgomery form
206 bool convert_to_montgomery = (rng.next() & 1);
207 uint256_t value_data;
208 // Conversion at the start
209#define MONT_CONVERSION_LOCAL \
210 if (convert_to_montgomery) { \
211 value_data = uint256_t(e.to_montgomery_form()); \
212 } else { \
213 value_data = uint256_t(e); \
214 }
215 // Inverse conversion at the end
216#define INV_MONT_CONVERSION_LOCAL \
217 if (convert_to_montgomery) { \
218 e = FF(value_data).from_montgomery_form(); \
219 } else { \
220 e = FF(value_data); \
221 }
222
223 // Pick the last value from the mutation distrivution vector
224 // Choose mutation
225 const size_t choice = rng.next() % 4;
226 // 50% probability to use standard mutation
227 if (choice < 2) {
228 // Delegate mutation to libfuzzer (bit/byte mutations, autodictionary, etc)
229 MONT_CONVERSION_LOCAL
230 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
231 LLVMFuzzerMutate(reinterpret_cast<uint8_t*>(&value_data), sizeof(uint256_t), sizeof(uint256_t));
232 INV_MONT_CONVERSION_LOCAL
233 } else if (choice < 3) { // 25% to use small additions
234
235 // Small addition/subtraction
236 if (convert_to_montgomery) {
237 e = e.to_montgomery_form();
238 }
239 if (rng.next() & 1) {
240 value_data = e + FF(rng.next() & 0xff);
241 } else {
242 value_data = e - FF(rng.next() & 0xff);
243 }
244 if (convert_to_montgomery) {
245 e = e.from_montgomery_form();
246 }
247 } else { // 25% to use special values
248
249 // Substitute field element with a special value
250 MONT_CONVERSION_LOCAL
251 switch (rng.next() % 8) {
252 case 0:
253 e = FF::zero();
254 break;
255 case 1:
256 e = FF::one();
257 break;
258 case 2:
259 e = -FF::one();
260 break;
261 case 3:
262 e = FF::one().sqrt().second;
263 break;
264 case 4:
265 e = FF::one().sqrt().second.invert();
266 break;
267 case 5:
268 e = FF::get_root_of_unity(8);
269 break;
270 case 6:
271 e = FF(2);
272 break;
273 case 7:
274 e = FF((FF::modulus - 1) / 2);
275 break;
276 default:
277 abort();
278 break;
279 }
280 INV_MONT_CONVERSION_LOCAL
281 }
282 // Return instruction
283 return e;
284}
285
291template <typename T>
294 private:
301 inline static void swapTwoInstructions(std::vector<typename T::Instruction>& instructions, FastRandom& rng)
302 {
303 const size_t instructions_count = instructions.size();
304 if (instructions_count <= 2) {
305 return;
306 }
307 const size_t first_element_index = rng.next() % instructions_count;
308 size_t second_element_index = rng.next() % instructions_count;
309 if (first_element_index == second_element_index) {
310 second_element_index = (second_element_index + 1) % instructions_count;
311 }
312 std::iter_swap(instructions.begin() + static_cast<int>(first_element_index),
313 instructions.begin() + static_cast<int>(second_element_index));
314 }
315
323 inline static void deleteInstructions(std::vector<typename T::Instruction>& instructions,
324 FastRandom& rng,
325 HavocSettings& havoc_settings)
326 {
327
328 const size_t instructions_count = instructions.size();
329 if (instructions_count == 0) {
330 return;
331 }
332 if ((rng.next() & 1) != 0U) {
333 instructions.erase(instructions.begin() + (rng.next() % instructions_count));
334 } else {
335 // We get the logarithm of number of instructions and subtract 1 to delete at most half
336 const size_t max_deletion_log =
337 std::min(static_cast<size_t>(64 - __builtin_clzll(static_cast<uint64_t>(instructions_count)) - 1),
338 havoc_settings.ST_MUT_MAXIMUM_DELETION_LOG);
339
340 if (max_deletion_log == 0) {
341 return;
342 }
343 const size_t deletion_size = 1 << (rng.next() % max_deletion_log);
344 const size_t start = rng.next() % (instructions_count + 1 - deletion_size);
345 instructions.erase(instructions.begin() + static_cast<int>(start),
346 instructions.begin() + static_cast<int>(start + deletion_size));
347 }
348 }
356 inline static void duplicateInstruction(std::vector<typename T::Instruction>& instructions,
357 FastRandom& rng,
358 HavocSettings& havoc_settings)
359 {
360 const size_t instructions_count = instructions.size();
361 if (instructions_count == 0) {
362 return;
363 }
364 const size_t duplication_size = 1 << (rng.next() % havoc_settings.ST_MUT_MAXIMUM_DUPLICATION_LOG);
365 typename T::Instruction chosen_instruction = instructions[rng.next() % instructions_count];
366 instructions.insert(
367 instructions.begin() + (rng.next() % (instructions_count + 1)), duplication_size, chosen_instruction);
368 }
369 inline static void insertRandomInstruction(std::vector<typename T::Instruction>& instructions,
370 FastRandom& rng,
371 HavocSettings& havoc_settings)
372 {
373 (void)havoc_settings;
374 instructions.insert(instructions.begin() + static_cast<int>(rng.next() % (instructions.size() + 1)),
375 T::Instruction::template generateRandom<FastRandom>(rng));
376 }
384 inline static void mutateInstructionStructure(std::vector<typename T::Instruction>& instructions,
385 FastRandom& rng,
386 HavocSettings& havoc_settings)
387 {
388 const size_t structural_mutators_count = havoc_settings.structural_mutation_distribution.size();
389 const size_t prob_pool = havoc_settings.structural_mutation_distribution[structural_mutators_count - 1];
390 const size_t choice = rng.next() % prob_pool;
391 if (choice < havoc_settings.structural_mutation_distribution[0]) {
392 deleteInstructions(instructions, rng, havoc_settings);
393 } else if (choice < havoc_settings.structural_mutation_distribution[1]) {
394
395 duplicateInstruction(instructions, rng, havoc_settings);
396 } else if (choice < havoc_settings.structural_mutation_distribution[2]) {
397 insertRandomInstruction(instructions, rng, havoc_settings);
398 } else {
399
400 swapTwoInstructions(instructions, rng);
401 }
402 }
410 inline static void mutateInstructionValue(std::vector<typename T::Instruction>& instructions,
411 FastRandom& rng,
412 HavocSettings& havoc_settings)
413 {
414
415 const size_t instructions_count = instructions.size();
416 if (instructions_count == 0) {
417 return;
418 }
419 const size_t chosen = rng.next() % instructions_count;
420 instructions[chosen] =
421 T::Instruction::template mutateInstruction<FastRandom>(instructions[chosen], rng, havoc_settings);
422 }
423
424 static void mutateInstructionVector(std::vector<typename T::Instruction>& instructions, FastRandom& rng)
425 {
426#ifdef HAVOC_TESTING
427 // If we are testing which havoc settings are best, then we use global parameters
428 const size_t mutation_count = 1 << fuzzer_havoc_settings.GEN_MUTATION_COUNT_LOG;
429#else
430 const size_t mutation_count = 1 << T::HavocConfig::MUTATION_COUNT_LOG;
431 HavocSettings fuzzer_havoc_settings;
432 // FILL the values
433#endif
434 for (size_t i = 0; i < mutation_count; i++) {
435 uint32_t val = rng.next();
436 if ((val % (fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY +
437 fuzzer_havoc_settings.GEN_VALUE_MUTATION_PROBABILITY)) <
438 fuzzer_havoc_settings.GEN_STRUCTURAL_MUTATION_PROBABILITY) {
439 // mutate structure
440 mutateInstructionStructure(instructions, rng, fuzzer_havoc_settings);
441 } else {
442 // mutate a single instruction vector
443
444 mutateInstructionValue(instructions, rng, fuzzer_havoc_settings);
445 }
446 }
447 }
448
449 public:
458 static std::vector<typename T::Instruction> crossoverInstructionVector(
459 const std::vector<typename T::Instruction>& vecA,
460 const std::vector<typename T::Instruction>& vecB,
461 FastRandom& rng)
462 {
463 // Get vector sizes
464 const size_t vecA_size = vecA.size();
465 const size_t vecB_size = vecB.size();
466 // If one of them is empty, just return the other one
467 if (vecA_size == 0) {
468 return vecB;
469 }
470 if (vecB_size == 0) {
471 return vecA;
472 }
473 std::vector<typename T::Instruction> result;
474 // Choose the size of th resulting vector
475 const size_t final_result_size = rng.next() % (vecA_size + vecB_size) + 1;
476 size_t indexA = 0;
477 size_t indexB = 0;
478 size_t* inIndex = &indexA;
479 size_t inSize = vecA_size;
480 auto inIterator = vecA.begin();
481 size_t current_result_size = 0;
482 bool currentlyUsingA = true;
483 // What we do is basically pick a sequence from one, follow with a sequence from the other
484 while (current_result_size < final_result_size && (indexA < vecA_size || indexB < vecB_size)) {
485 // Get the size left
486 size_t result_size_left = final_result_size - current_result_size;
487 // If we can still read from this vector
488 if (*inIndex < inSize) {
489 // Get the size left in this vector and in the output vector and pick the lowest
490 size_t inSizeLeft = inSize - *inIndex;
491 size_t maxExtraSize = std::min(result_size_left, inSizeLeft);
492 if (maxExtraSize != 0) {
493 // If not zero, get a random number of elements from input
494 size_t copySize = (rng.next() % maxExtraSize) + 1;
495 result.insert(result.begin() + static_cast<long>(current_result_size),
496 inIterator + static_cast<long>((*inIndex)),
497
498 inIterator + static_cast<long>((*inIndex) + copySize));
499 // Update indexes and sizes
500 *inIndex += copySize;
501 current_result_size += copySize;
502 }
503 }
504 // Switch input vector
505 inIndex = currentlyUsingA ? &indexB : &indexA;
506 inSize = currentlyUsingA ? vecB_size : vecA_size;
507 inIterator = currentlyUsingA ? vecB.begin() : vecA.begin();
508 currentlyUsingA = !currentlyUsingA;
509 }
510 // Return spliced vector
511 return result;
512 }
520 static std::vector<typename T::Instruction> parseDataIntoInstructions(const uint8_t* Data, size_t Size)
521 {
522 std::vector<typename T::Instruction> fuzzingInstructions;
523 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
524 auto* pData = const_cast<uint8_t*>(Data);
525 size_t size_left = Size;
526 while (size_left != 0) {
527 uint8_t chosen_operation = *pData;
528 size_left -= 1;
529 pData++;
530 // If the opcode is enabled (exists and arguments' size is not -1), check if it's the right opcode. If it
531 // is, parse it with a designated function
532#define PARSE_OPCODE(name) \
533 if constexpr (requires { T::ArgSizes::name; }) \
534 if constexpr (T::ArgSizes::name != size_t(-1)) { \
535 if (chosen_operation == T::Instruction::OPCODE::name) { \
536 if (size_left < T::ArgSizes::name) { \
537 return fuzzingInstructions; \
538 } \
539 fuzzingInstructions.push_back( \
540 T::Parser::template parseInstructionArgs<T::Instruction::OPCODE::name>(pData)); \
541 size_left -= T::ArgSizes::name; \
542 pData += T::ArgSizes::name; \
543 continue; \
544 } \
545 }
546 // Create handlers for all opcodes that are in ArgsSizes
547#define PARSE_ALL_OPCODES(...) FOR_EACH(PARSE_OPCODE, __VA_ARGS__)
548
549 PARSE_ALL_OPCODES(ALL_POSSIBLE_OPCODES)
550 }
551 return fuzzingInstructions;
552 }
561 static size_t writeInstructionsToBuffer(std::vector<typename T::Instruction>& instructions,
562 uint8_t* Data,
563 size_t MaxSize)
564 {
565 uint8_t* pData = Data;
566 size_t size_left = MaxSize;
567 for (auto& instruction : instructions) {
568 // If the opcode is enabled and it's this opcode, use a designated function to serialize it
569#define WRITE_OPCODE_IF(name) \
570 if constexpr (requires { T::ArgSizes::name; }) \
571 if constexpr (T::ArgSizes::name != (size_t)-1) { \
572 if (instruction.id == T::Instruction::OPCODE::name) { \
573 if (size_left >= (T::ArgSizes::name + 1)) { \
574 T::Parser::template writeInstruction<T::Instruction::OPCODE::name>(instruction, pData); \
575 size_left -= (T::ArgSizes::name + 1); \
576 pData += (T::ArgSizes::name + 1); \
577 } else { \
578 return MaxSize - size_left; \
579 } \
580 continue; \
581 } \
582 }
583 // Create handlers for all opcodes that are in ArgsSizes
584#define WRITE_ALL_OPCODES(...) FOR_EACH(WRITE_OPCODE_IF, __VA_ARGS__)
585
586 WRITE_ALL_OPCODES(ALL_POSSIBLE_OPCODES)
587 }
588 return MaxSize - size_left;
589 }
590
597 template <typename Composer>
598 // TODO(@Rumata888)(from Zac: maybe try to fix? not comfortable refactoring this myself. Issue #1807)
599 // NOLINTNEXTLINE(readability-function-size, google-readability-function-size)
600 inline static void executeInstructions(std::vector<typename T::Instruction>& instructions)
602 {
603 typename T::ExecutionState state;
604 Composer composer = Composer();
605 bool circuit_should_fail = false;
606 size_t total_instruction_weight = 0;
607 (void)total_instruction_weight;
608 for (auto& instruction : instructions) {
609 // If instruction enabled and this is it, delegate to the handler
610#define EXECUTE_OPCODE_IF(name) \
611 if constexpr (requires { T::ArgSizes::name; }) \
612 if constexpr (T::ArgSizes::name != size_t(-1)) { \
613 if (instruction.id == T::Instruction::OPCODE::name) { \
614 if constexpr (InstructionWeightsEnabled<T>) { \
615 if (!((total_instruction_weight + T::InstructionWeights::name) > T::InstructionWeights::_LIMIT)) { \
616 total_instruction_weight += T::InstructionWeights::name; \
617 if (T::ExecutionHandler::execute_##name(&composer, state, instruction)) { \
618 return; \
619 } \
620 } else { \
621 return; \
622 } \
623 } else { \
624 \
625 if (T::ExecutionHandler::execute_##name(&composer, state, instruction)) { \
626 return; \
627 } \
628 } \
629 } \
630 }
631#define EXECUTE_ALL_OPCODES(...) FOR_EACH(EXECUTE_OPCODE_IF, __VA_ARGS__)
632
633 EXECUTE_ALL_OPCODES(ALL_POSSIBLE_OPCODES)
634 }
635 bool final_value_check = true;
636 // If there is a posprocessing function, use it
638 final_value_check = T::postProcess(&composer, state);
639#ifdef SHOW_INFORMATION
640 if (!final_value_check) {
641 std::cerr << "Final value check failed" << std::endl;
642 }
643#endif
644 }
645 bool check_result = composer.check_circuit() && final_value_check;
646 // If the circuit is correct, but it should fail, abort
647 if (check_result && circuit_should_fail) {
648 abort();
649 }
650 // If the circuit is incorrect, but there's no reason, abort
651 if ((!check_result) && (!circuit_should_fail)) {
652 if (!final_value_check) {
653 std::cerr << "Final value check failed" << std::endl;
654 } else {
655 std::cerr << "Circuit failed" << std::endl;
656 }
657
658 abort();
659 }
660 }
661
670 static size_t MutateInstructionBuffer(uint8_t* Data, size_t Size, size_t MaxSize, FastRandom& rng)
671 {
672 // Parse the vector
673 std::vector<typename T::Instruction> instructions = parseDataIntoInstructions(Data, Size);
674 // Mutate the vector of instructions
675 mutateInstructionVector(instructions, rng);
676 // Serialize the vector of instructions back to buffer
677 return writeInstructionsToBuffer(instructions, Data, MaxSize);
678 }
679};
680
681template <template <typename> class Fuzzer, typename Composer>
682constexpr void RunWithBuilder(const uint8_t* Data, const size_t Size, FastRandom& VarianceRNG)
683{
684 VarianceRNG.reseed(0);
685 auto instructions = ArithmeticFuzzHelper<Fuzzer<Composer>>::parseDataIntoInstructions(Data, Size);
686 ArithmeticFuzzHelper<Fuzzer<Composer>>::template executeInstructions<Composer>(instructions);
687}
688
689template <template <typename> class Fuzzer, uint64_t Composers>
690constexpr void RunWithBuilders(const uint8_t* Data, const size_t Size, FastRandom& VarianceRNG)
691{
692 if (Composers & 1) {
693 RunWithBuilder<Fuzzer, proof_system::StandardCircuitBuilder>(Data, Size, VarianceRNG);
694 }
695}
696
697// NOLINTEND(cppcoreguidelines-macro-usage, google-runtime-int)
A templated class containing most of the fuzzing logic for a generic Arithmetic class.
Definition: fuzzer.hpp:293
static size_t writeInstructionsToBuffer(std::vector< typename T::Instruction > &instructions, uint8_t *Data, size_t MaxSize)
Write instructions into the buffer until there are no instructions left or there is no more space.
Definition: fuzzer.hpp:561
static std::vector< typename T::Instruction > parseDataIntoInstructions(const uint8_t *Data, size_t Size)
Parses a given data buffer into a vector of instructions for testing the arithmetic.
Definition: fuzzer.hpp:520
static size_t MutateInstructionBuffer(uint8_t *Data, size_t Size, size_t MaxSize, FastRandom &rng)
Interpret the data buffer as a series of arithmetic instructions and mutate it accordingly.
Definition: fuzzer.hpp:670
static std::vector< typename T::Instruction > crossoverInstructionVector(const std::vector< typename T::Instruction > &vecA, const std::vector< typename T::Instruction > &vecB, FastRandom &rng)
Splice two instruction vectors into one randomly.
Definition: fuzzer.hpp:458
static void executeInstructions(std::vector< typename T::Instruction > &instructions)
Execute instructions in a loop.
Definition: fuzzer.hpp:600
Class for quickly deterministically creating new random values. We don't care about distribution much...
Definition: fuzzer.hpp:64
Definition: uint256.hpp:25
Definition: standard_composer.hpp:14
Concept specifying the class used by the fuzzer.
Definition: fuzzer.hpp:144
Fuzzer uses only composers with check_circuit function.
Definition: fuzzer.hpp:158
Concept for Havoc Configurations.
Definition: fuzzer.hpp:131
Concept for forcing ArgumentSizes to be size_t.
Definition: fuzzer.hpp:102
This concept is used when we want to limit the number of executions of certain instructions (for exam...
Definition: fuzzer.hpp:185
The fuzzer can use a postprocessing function that is specific to the type being fuzzed.
Definition: fuzzer.hpp:172
Concept for a simple PRNG which returns a uint32_t when next is called.
Definition: fuzzer.hpp:91
Definition: fuzzer.hpp:27