diff --git a/barretenberg/cpp/CMakePresets.json b/barretenberg/cpp/CMakePresets.json index b2c5917d629..615125bd51e 100644 --- a/barretenberg/cpp/CMakePresets.json +++ b/barretenberg/cpp/CMakePresets.json @@ -268,7 +268,7 @@ "displayName": "Build with fuzzing", "description": "Build default preset but with fuzzing enabled", "inherits": "clang16-dbg", - "binaryDir": "build-fuzzing", + "binaryDir": "build-fuzzing-asan", "cacheVariables": { "FUZZING": "ON", "ENABLE_ASAN": "ON", diff --git a/barretenberg/cpp/cmake/module.cmake b/barretenberg/cpp/cmake/module.cmake index 924952e8d0b..2e971c2df0c 100644 --- a/barretenberg/cpp/cmake/module.cmake +++ b/barretenberg/cpp/cmake/module.cmake @@ -204,6 +204,7 @@ function(barretenberg_module MODULE_NAME) file(GLOB_RECURSE FUZZERS_SOURCE_FILES *.fuzzer.cpp) if(FUZZING AND FUZZERS_SOURCE_FILES) + add_definitions(-DULTRA_FUZZ) foreach(FUZZER_SOURCE_FILE ${FUZZERS_SOURCE_FILES}) get_filename_component(FUZZER_NAME_STEM ${FUZZER_SOURCE_FILE} NAME_WE) add_executable( diff --git a/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.cpp b/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.cpp index 9f065205a9a..4396802c758 100644 --- a/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.cpp +++ b/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.cpp @@ -47,12 +47,24 @@ template bool UltraCircuitChecker::check(const Builder& build block_idx++; } +#ifdef ULTRA_FUZZ + result = result & relaxed_check_delta_range_relation(builder); + if (!result) { + return false; + } + result = result & relaxed_check_aux_relation(builder); + if (!result) { + return false; + } +#endif +#ifndef ULTRA_FUZZ // Tag check is only expected to pass after entire execution trace (all blocks) have been processed result = result && check_tag_data(tag_data); if (!result) { info("Failed tag check."); return false; } +#endif return result; }; @@ -93,6 +105,7 @@ bool UltraCircuitChecker::check_block(Builder& builder, if (!result) { return report_fail("Failed Elliptic relation at row idx = ", idx); } +#ifndef ULTRA_FUZZ result = result && check_relation(values, params); if (!result) { return report_fail("Failed Auxiliary relation at row idx = ", idx); @@ -101,6 +114,19 @@ bool UltraCircuitChecker::check_block(Builder& builder, if (!result) { return report_fail("Failed DeltaRangeConstraint relation at row idx = ", idx); } +#else + // Bigfield related auxiliary gates + if (values.q_aux == 1) { + bool f0 = values.q_o == 1 && (values.q_4 == 1 || values.q_m == 1); + bool f1 = values.q_r == 1 && (values.q_o == 1 || values.q_4 == 1 || values.q_m == 1); + if (f0 && f1) { + result = result && check_relation(values, params); + if (!result) { + return report_fail("Failed Non Native Auxiliary relation at row idx = ", idx); + } + } + } +#endif result = result && check_lookup(values, lookup_hash_table); if (!result) { return report_fail("Failed Lookup check relation at row idx = ", idx); @@ -297,6 +323,151 @@ void UltraCircuitChecker::populate_values( } } +#ifdef ULTRA_FUZZ + +/** + * @brief Check that delta range relation is satisfied + * @details For fuzzing purposes, we skip delta range finalization step + * because of its complexity. Instead, we simply check all the range constraints + * in the old-fashioned way. + * In case there're any processed sort constraints, we also check them using ranges. + * + * @tparam Builder + * @param builder Circuit Builder + * @return all the variables are properly range constrained + */ +template bool UltraCircuitChecker::relaxed_check_delta_range_relation(Builder& builder) +{ + std::unordered_map range_tags; + for (const auto& list : builder.range_lists) { + range_tags[list.second.range_tag] = list.first; + } + + // Unprocessed blocks check + for (uint32_t i = 0; i < builder.real_variable_tags.size(); i++) { + uint32_t tag = builder.real_variable_tags[i]; + if (tag != 0 && range_tags.contains(tag)) { + uint256_t range = static_cast(range_tags[tag]); + uint256_t value = static_cast(builder.get_variable(i)); + if (value > range) { + info("Failed range constraint on variable with index = ", i, ": ", value, " > ", range); + return false; + } + } + } + + // Processed blocks check + auto block = builder.blocks.delta_range; + for (size_t idx = 0; idx < block.size(); idx++) { + if (block.q_delta_range()[idx] == 0) { + continue; + } + bb::fr w1 = builder.get_variable(block.w_l()[idx]); + bb::fr w2 = builder.get_variable(block.w_r()[idx]); + bb::fr w3 = builder.get_variable(block.w_o()[idx]); + bb::fr w4 = builder.get_variable(block.w_4()[idx]); + bb::fr w5 = idx == block.size() - 1 ? builder.get_variable(0) : builder.get_variable(block.w_l()[idx + 1]); + + uint256_t delta = static_cast(w2 - w1); + if (delta > 3) { + info("Failed sort constraint relation at row idx = ", idx, " with delta1 = ", delta); + info(w1 - w2); + return false; + } + delta = static_cast(w3 - w2); + if (delta > 3) { + info("Failed sort constraint relation at row idx = ", idx, " with delta2 = ", delta); + return false; + } + delta = static_cast(w4 - w3); + if (delta > 3) { + info("Failed sort constraint at row idx = ", idx, " with delta3 = ", delta); + return false; + } + delta = static_cast(w5 - w4); + if (delta > 3) { + info("Failed sort constraint at row idx = ", idx, " with delta4 = ", delta); + return false; + } + } + return true; +} + +/** + * @brief Check that aux relation is satisfied + * @details For fuzzing purposes, we skip RAM/ROM finalization step + * because of its complexity. + * Instead + * - For ROM gates we simply check that the state is consistent with read calls + * - For RAM gates we simulate the call trace for the state and compare the final + * result with the state in builder, hence checking it's overall consistency + * + * @tparam Builder + * @param builder Circuit Builder + * @return all the memory calls are valid + */ +template bool UltraCircuitChecker::relaxed_check_aux_relation(Builder& builder) +{ + for (size_t i = 0; i < builder.rom_arrays.size(); i++) { + auto rom_array = builder.rom_arrays[i]; + + // check set and read ROM records + for (auto& rr : rom_array.records) { + uint32_t value_witness_1 = rr.value_column1_witness; + uint32_t value_witness_2 = rr.value_column2_witness; + uint32_t index = static_cast(builder.get_variable(rr.index_witness)); + + uint32_t table_witness_1 = rom_array.state[index][0]; + uint32_t table_witness_2 = rom_array.state[index][1]; + + if (builder.get_variable(value_witness_1) != builder.get_variable(table_witness_1)) { + info("Failed SET/Read ROM[0] in table = ", i, " at idx = ", index); + return false; + } + if (builder.get_variable(value_witness_2) != builder.get_variable(table_witness_2)) { + info("Failed SET/Read ROM[1] in table = ", i, " at idx = ", index); + return false; + } + } + } + + for (size_t i = 0; i < builder.ram_arrays.size(); i++) { + auto ram_array = builder.ram_arrays[i]; + + std::vector tmp_state(ram_array.state.size()); + + // Simulate the memory call trace + for (auto& rr : ram_array.records) { + uint32_t index = static_cast(builder.get_variable(rr.index_witness)); + uint32_t value_witness = rr.value_witness; + auto access_type = rr.access_type; + + uint32_t table_witness = tmp_state[index]; + + switch (access_type) { + case Builder::RamRecord::AccessType::READ: + if (builder.get_variable(value_witness) != builder.get_variable(table_witness)) { + info("Failed RAM read in table = ", i, " at idx = ", index); + return false; + } + break; + case Builder::RamRecord::AccessType::WRITE: + tmp_state[index] = value_witness; + break; + default: + return false; + } + } + + if (tmp_state != ram_array.state) { + info("Failed RAM final state check at table = ", i); + return false; + } + } + return true; +} +#endif + // Template method instantiations for each check method template bool UltraCircuitChecker::check>( const UltraCircuitBuilder_& builder_in); diff --git a/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.hpp b/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.hpp index 0201c6fdf1c..e5c932f818a 100644 --- a/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.hpp +++ b/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.hpp @@ -64,6 +64,11 @@ class UltraCircuitChecker { MemoryCheckData& memory_data, LookupHashTable& lookup_hash_table); +#ifdef ULTRA_FUZZ + template static bool relaxed_check_aux_relation(Builder& builder); + template static bool relaxed_check_delta_range_relation(Builder& builder); +#endif + /** * @brief Check that a given relation is satisfied for the provided inputs corresponding to a single row * @note Assumes the relation constraints should evaluate to zero on each row and thus does not apply to linearly diff --git a/barretenberg/cpp/src/barretenberg/common/fuzzer.hpp b/barretenberg/cpp/src/barretenberg/common/fuzzer.hpp index 51e9f202463..b4fe4f5f623 100644 --- a/barretenberg/cpp/src/barretenberg/common/fuzzer.hpp +++ b/barretenberg/cpp/src/barretenberg/common/fuzzer.hpp @@ -1,8 +1,6 @@ #pragma once #include "barretenberg/circuit_checker/circuit_checker.hpp" #include "barretenberg/numeric/uint256/uint256.hpp" -#include "barretenberg/stdlib_circuit_builders/standard_circuit_builder.hpp" -#include // NOLINTBEGIN(cppcoreguidelines-macro-usage, google-runtime-int) #define PARENS () @@ -44,8 +42,7 @@ struct HavocSettings { size_t VAL_MUT_NON_MONTGOMERY_PROBABILITY; // The probability of not converting to montgomery form before applying // value mutations size_t VAL_MUT_SMALL_ADDITION_PROBABILITY; // The probability of performing small additions - size_t VAL_MUT_SMALL_MULTIPLICATION_PROBABILITY; // The probability of performing small multiplications - size_t VAL_MUT_SPECIAL_VALUE_PROBABILITY; // The probability of assigning special values (0,1, p-1, p-2, p-1/2) + size_t VAL_MUT_SPECIAL_VALUE_PROBABILITY; // The probability of assigning special values (0,1, p-1, p-2, p-1/2) std::vector structural_mutation_distribution; // Holds the values to quickly select a structural mutation // based on chosen probabilities std::vector value_mutation_distribution; // Holds the values to quickly select a value mutation based on @@ -157,7 +154,7 @@ concept ArithmeticFuzzHelperConstraint = requires { template concept CheckableComposer = requires(T a) { { - CircuitChecker::check(a) + bb::CircuitChecker::check(a) } -> std::same_as; }; @@ -220,7 +217,7 @@ inline static FF mutateFieldElement(FF e, T& rng) e = FF(value_data); \ } - // Pick the last value from the mutation distrivution vector + // Pick the last value from the mutation distribution vector // Choose mutation const size_t choice = rng.next() % 4; // 50% probability to use standard mutation @@ -691,7 +688,9 @@ constexpr void RunWithBuilders(const uint8_t* Data, const size_t Size, FastRando { if (Composers & 1) { RunWithBuilder(Data, Size, VarianceRNG); + } else if (Composers & 2) { + RunWithBuilder(Data, Size, VarianceRNG); } } -// NOLINTEND(cppcoreguidelines-macro-usage, google-runtime-int) \ No newline at end of file +// NOLINTEND(cppcoreguidelines-macro-usage, google-runtime-int) diff --git a/barretenberg/cpp/src/barretenberg/common/fuzzer_constants.hpp b/barretenberg/cpp/src/barretenberg/common/fuzzer_constants.hpp index 0a9603ceefb..c9a66be8c02 100644 --- a/barretenberg/cpp/src/barretenberg/common/fuzzer_constants.hpp +++ b/barretenberg/cpp/src/barretenberg/common/fuzzer_constants.hpp @@ -1,4 +1,4 @@ #pragma once #include -enum CircuitType : uint64_t { Standard = 1 << 0 }; +enum CircuitType : uint64_t { Standard = 1 << 0, Ultra = 1 << 1 }; diff --git a/barretenberg/cpp/src/barretenberg/relations/auxiliary_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/auxiliary_relation.hpp index bc7ca4ee266..d7c967e6b27 100644 --- a/barretenberg/cpp/src/barretenberg/relations/auxiliary_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/auxiliary_relation.hpp @@ -176,7 +176,7 @@ template class AuxiliaryRelationImpl { auto non_native_field_identity = non_native_field_gate_1 + non_native_field_gate_2 + non_native_field_gate_3; non_native_field_identity *= q_2; - // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm + // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * q_4 // deg 2 auto limb_accumulator_1 = w_2_shift * SUBLIMB_SHIFT; limb_accumulator_1 += w_1_shift; @@ -189,7 +189,7 @@ template class AuxiliaryRelationImpl { limb_accumulator_1 -= w_4; limb_accumulator_1 *= q_4; - // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm + // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * q_m // deg 2 auto limb_accumulator_2 = w_3_shift * SUBLIMB_SHIFT; limb_accumulator_2 += w_2_shift; @@ -239,7 +239,7 @@ template class AuxiliaryRelationImpl { * Partial degree: 1 * Total degree: 2 * - * A ROM/ROM access gate can be evaluated with the identity: + * A ROM/RAM access gate can be evaluated with the identity: * * qc + w1 \eta + w2 η₂ + w3 η₃ - w4 = 0 * diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.fuzzer.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.fuzzer.hpp index 30c4078fa6c..472e898420f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.fuzzer.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.fuzzer.hpp @@ -389,7 +389,7 @@ template class BigFieldBase { e = bb::fq(value_data); \ } - // Pick the last value from the mutation distrivution vector + // Pick the last value from the mutation distribution vector const size_t mutation_type_count = havoc_config.value_mutation_distribution.size(); // Choose mutation const size_t choice = rng.next() % havoc_config.value_mutation_distribution[mutation_type_count - 1]; @@ -594,7 +594,7 @@ template class BigFieldBase { static constexpr size_t ADD = 3; static constexpr size_t SUBTRACT = 3; static constexpr size_t MULTIPLY = 3; - static constexpr size_t ADD_TWO = static_cast(-1); + static constexpr size_t ADD_TWO = 4; #ifndef DISABLE_DIVISION static constexpr size_t DIVIDE = 3; #else @@ -629,7 +629,7 @@ template class BigFieldBase { static constexpr size_t SQR = 2; static constexpr size_t ASSERT_EQUAL = 2; static constexpr size_t ASSERT_NOT_EQUAL = 2; - static constexpr size_t ADD_TWO = 0; + static constexpr size_t ADD_TWO = 1; #ifndef DISABLE_DIVISION static constexpr size_t DIVIDE = 16; #endif @@ -702,7 +702,6 @@ template class BigFieldBase { mult_madd_or_div.arguments.multOpArgs.add_elements_count % MULT_MADD_MAXIMUM_ADDED_ELEMENTS; if (mult_madd_or_div.arguments.multOpArgs.add_elements_count < MULT_MADD_MINIMUM_ADDED_ELEMENTS) { - mult_madd_or_div.arguments.multOpArgs.add_elements_count = MULT_MADD_MINIMUM_ADDED_ELEMENTS; } mult_madd_or_div.arguments.multOpArgs.mult_pairs_count = @@ -813,6 +812,10 @@ template class BigFieldBase { { const bool reconstruct = static_cast(VarianceRNG.next() % 2); +#ifdef SHOW_INFORMATION + std::cout << " reconstruction? " << reconstruct << std::endl; +#endif + if (!reconstruct) { return this->bigfield; } @@ -838,8 +841,11 @@ template class BigFieldBase { if (b.get_value() > b.get_maximum_value()) { abort(); } - for (auto& limb : b.binary_basis_limbs) { + for (size_t i = 0; i < 4; i++) { + auto limb = b.binary_basis_limbs[i]; if (limb.maximum_value < limb.element.get_value()) { + info("LIMB ", i, " VALUE IS NOT PROPERLY RESTRICTED"); + info(limb); abort(); } } @@ -922,7 +928,11 @@ template class BigFieldBase { abort(); } } - + ExecutionHandler add_two(const ExecutionHandler& other1, const ExecutionHandler& other2) + { + return ExecutionHandler(this->base + other1.base + other2.base, + this->bf().add_two(other1.bigfield, other2.bigfield)); + } ExecutionHandler madd(const ExecutionHandler& other1, const ExecutionHandler& other2) { @@ -1047,21 +1057,26 @@ template class BigFieldBase { abort(); } - switch (VarianceRNG.next() % 6) { + uint32_t switch_case = VarianceRNG.next() % 5; + +#ifdef SHOW_INFORMATION + std::cout << " using " << switch_case << " constructor" << std::endl; +#endif + switch (switch_case) { case 0: /* Construct via bigfield_t */ return ExecutionHandler(this->base, bigfield_t(this->bigfield)); case 1: /* Construct via uint256_t */ return ExecutionHandler(this->base, bigfield_t(builder, bf_u256())); - case 2: - /* Construct via byte_array */ - /* - * Bug: https://github.com/AztecProtocol/aztec2-internal/issues/1496 - * - * Remove of change this invocation if that issue is a false positive */ - return ExecutionHandler(this->base, bigfield_t(this->bigfield.to_byte_array())); - case 3: { + // case 2: // TODO(alex): Uncomment once fixed + // /* Construct via byte_array */ + // /* + // * Bug: https://github.com/AztecProtocol/aztec2-internal/issues/1496 + // * + // * Remove of change this invocation if that issue is a false positive */ + // return ExecutionHandler(this->base, bigfield_t(this->bigfield.to_byte_array())); + case 2: { const uint256_t u256 = bf_u256(); const uint256_t u256_lo = u256.slice(0, bigfield_t::NUM_LIMB_BITS * 2); const uint256_t u256_hi = u256.slice(bigfield_t::NUM_LIMB_BITS * 2, bigfield_t::NUM_LIMB_BITS * 4); @@ -1071,7 +1086,7 @@ template class BigFieldBase { /* Construct via two field_t's */ return ExecutionHandler(this->base, bigfield_t(field_lo, field_hi)); } - case 4: { + case 3: { /* Invoke assignment operator */ bigfield_t bf_new(builder); @@ -1079,7 +1094,7 @@ template class BigFieldBase { return ExecutionHandler(this->base, bigfield_t(bf_new)); } - case 5: { + case 4: { /* Invoke move constructor */ auto bf_copy = bf(); @@ -1406,6 +1421,42 @@ template class BigFieldBase { } return 0; }; + /** + * @brief Execute the ADD_TWO instruction + * + * @param builder + * @param stack + * @param instruction + * @return if everything is ok, 1 if we should stop execution, since an expected error was encountered + size_t + */ + static inline size_t execute_ADD_TWO(Builder* builder, + std::vector& stack, + Instruction& instruction) + { + (void)builder; + if (stack.size() == 0) { + return 1; + } + size_t first_index = instruction.arguments.fourArgs.in1 % stack.size(); + size_t second_index = instruction.arguments.fourArgs.in2 % stack.size(); + size_t third_index = instruction.arguments.fourArgs.in3 % stack.size(); + size_t output_index = instruction.arguments.fourArgs.out; + PRINT_THREE_ARG_INSTRUCTION(first_index, second_index, third_index, stack, "ADD_TWO:", "+", "+") + + ExecutionHandler result; + result = stack[first_index].add_two(stack[second_index], stack[third_index]); + // If the output index is larger than the number of elements in stack, append + if (output_index >= stack.size()) { + PRINT_RESULT("", "pushed to ", stack.size(), result) + stack.push_back(result); + } else { + PRINT_RESULT("", "saved to ", output_index, result) + stack[output_index] = result; + } + return 0; + }; + /** * @brief Execute the MADD instruction * @@ -1678,6 +1729,9 @@ template class BigFieldBase { size_t first_index = instruction.arguments.threeArgs.in1 % stack.size(); size_t output_index = instruction.arguments.threeArgs.out % stack.size(); bool predicate = instruction.arguments.threeArgs.in2 % 2; + + PRINT_SINGLE_ARG_INSTRUCTION(first_index, stack, "Negating", "is negated " + std::to_string(predicate)) + ExecutionHandler result; result = stack[first_index].conditional_negate(builder, predicate); // If the output index is larger than the number of elements in stack, append @@ -1714,6 +1768,10 @@ template class BigFieldBase { bool predicate = instruction.arguments.fourArgs.in3 % 2; ExecutionHandler result; + + PRINT_TWO_ARG_INSTRUCTION( + first_index, second_index, stack, "Selecting #" + std::to_string(predicate) + " from", ", ") + result = stack[first_index].conditional_select(builder, stack[second_index], predicate); // If the output index is larger than the number of elements in stack, append if (output_index >= stack.size()) { @@ -1745,11 +1803,16 @@ template class BigFieldBase { size_t first_index = instruction.arguments.twoArgs.in % stack.size(); size_t output_index = instruction.arguments.twoArgs.out; ExecutionHandler result; + + PRINT_SINGLE_ARG_INSTRUCTION(first_index, stack, "Setting value", "") + result = stack[first_index].set(builder); // If the output index is larger than the number of elements in stack, append if (output_index >= stack.size()) { + PRINT_RESULT("", "pushed to ", stack.size(), result) stack.push_back(result); } else { + PRINT_RESULT("", "saved to ", stack.size(), result) stack[output_index] = result; } return 0; @@ -1810,24 +1873,23 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) (void)argv; // These are the settings, optimized for the safeuint class (under them, fuzzer reaches maximum expected // coverage in 40 seconds) - fuzzer_havoc_settings = HavocSettings{ - .GEN_LLVM_POST_MUTATION_PROB = 30, // Out of 200 - .GEN_MUTATION_COUNT_LOG = 5, // -Fully checked - .GEN_STRUCTURAL_MUTATION_PROBABILITY = 300, // Fully checked - .GEN_VALUE_MUTATION_PROBABILITY = 700, // Fully checked - .ST_MUT_DELETION_PROBABILITY = 100, // Fully checked - .ST_MUT_DUPLICATION_PROBABILITY = 80, // Fully checked - .ST_MUT_INSERTION_PROBABILITY = 120, // Fully checked - .ST_MUT_MAXIMUM_DELETION_LOG = 6, // 2 because of limit - .ST_MUT_MAXIMUM_DUPLICATION_LOG = 2, // -Fully checked - .ST_MUT_SWAP_PROBABILITY = 50, // Fully checked - .VAL_MUT_LLVM_MUTATE_PROBABILITY = 250, // Fully checked - .VAL_MUT_MONTGOMERY_PROBABILITY = 130, // Fully checked - .VAL_MUT_NON_MONTGOMERY_PROBABILITY = 50, // Fully checked - .VAL_MUT_SMALL_ADDITION_PROBABILITY = 110, // Fully checked - .VAL_MUT_SPECIAL_VALUE_PROBABILITY = 130 // Fully checked - - }; + fuzzer_havoc_settings = HavocSettings{ .GEN_LLVM_POST_MUTATION_PROB = 30, // Out of 200 + .GEN_MUTATION_COUNT_LOG = 5, // -Fully checked + .GEN_STRUCTURAL_MUTATION_PROBABILITY = 300, // Fully checked + .GEN_VALUE_MUTATION_PROBABILITY = 700, // Fully checked + .ST_MUT_DELETION_PROBABILITY = 100, // Fully checked + .ST_MUT_DUPLICATION_PROBABILITY = 80, // Fully checked + .ST_MUT_INSERTION_PROBABILITY = 120, // Fully checked + .ST_MUT_MAXIMUM_DELETION_LOG = 6, // 2 because of limit + .ST_MUT_MAXIMUM_DUPLICATION_LOG = 2, // -Fully checked + .ST_MUT_SWAP_PROBABILITY = 50, // Fully checked + .VAL_MUT_LLVM_MUTATE_PROBABILITY = 250, // Fully checked + .VAL_MUT_MONTGOMERY_PROBABILITY = 130, // Fully checked + .VAL_MUT_NON_MONTGOMERY_PROBABILITY = 50, // Fully checked + .VAL_MUT_SMALL_ADDITION_PROBABILITY = 110, // Fully checked + .VAL_MUT_SPECIAL_VALUE_PROBABILITY = 130, // Fully checked + .structural_mutation_distribution = {}, + .value_mutation_distribution = {} }; /** * @brief This is used, when we need to determine the probabilities of various mutations. Left here for * posterity diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp index 06db1ba8d94..d29f5ab7fdb 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp @@ -283,6 +283,7 @@ template class bigfield { bigfield add_to_lower_limb(const field_t& other, uint256_t other_maximum_value) const; bigfield operator+(const bigfield& other) const; + bigfield add_two(const bigfield& add_a, const bigfield& add_b) const; bigfield operator-(const bigfield& other) const; bigfield operator*(const bigfield& other) const; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp index aacf11886bf..d756044298b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp @@ -494,6 +494,58 @@ bigfield bigfield::operator+(const bigfield& other) cons return result; } +/** + * @brief Create constraints for summing three + * bigfield elements efficiently + * + * @tparam Builder + * @tparam T + * @param add_a + * @param add_b + * @return The sum of three terms + */ +template +bigfield bigfield::add_two(const bigfield& add_a, const bigfield& add_b) const +{ + reduction_check(); + add_a.reduction_check(); + add_b.reduction_check(); + + Builder* ctx = (context == nullptr) ? (add_a.context == nullptr ? add_b.context : add_a.context) : context; + + if (is_constant() && add_a.is_constant() && add_b.is_constant()) { + auto result = bigfield(ctx, uint256_t((get_value() + add_a.get_value() + add_b.get_value()) % modulus_u512)); + result.set_origin_tag(OriginTag(this->get_origin_tag(), add_a.get_origin_tag(), add_b.get_origin_tag())); + return result; + } + + bigfield result(ctx); + result.binary_basis_limbs[0].maximum_value = binary_basis_limbs[0].maximum_value + + add_a.binary_basis_limbs[0].maximum_value + + add_b.binary_basis_limbs[0].maximum_value; + result.binary_basis_limbs[1].maximum_value = binary_basis_limbs[1].maximum_value + + add_a.binary_basis_limbs[1].maximum_value + + add_b.binary_basis_limbs[1].maximum_value; + result.binary_basis_limbs[2].maximum_value = binary_basis_limbs[2].maximum_value + + add_a.binary_basis_limbs[2].maximum_value + + add_b.binary_basis_limbs[2].maximum_value; + result.binary_basis_limbs[3].maximum_value = binary_basis_limbs[3].maximum_value + + add_a.binary_basis_limbs[3].maximum_value + + add_b.binary_basis_limbs[3].maximum_value; + + result.binary_basis_limbs[0].element = + binary_basis_limbs[0].element.add_two(add_a.binary_basis_limbs[0].element, add_b.binary_basis_limbs[0].element); + result.binary_basis_limbs[1].element = + binary_basis_limbs[1].element.add_two(add_a.binary_basis_limbs[1].element, add_b.binary_basis_limbs[1].element); + result.binary_basis_limbs[2].element = + binary_basis_limbs[2].element.add_two(add_a.binary_basis_limbs[2].element, add_b.binary_basis_limbs[2].element); + result.binary_basis_limbs[3].element = + binary_basis_limbs[3].element.add_two(add_a.binary_basis_limbs[3].element, add_b.binary_basis_limbs[3].element); + result.prime_basis_limb = prime_basis_limb.add_two(add_a.prime_basis_limb, add_b.prime_basis_limb); + result.set_origin_tag(OriginTag(this->get_origin_tag(), add_a.get_origin_tag(), add_b.get_origin_tag())); + return result; +} + // to make sure we don't go to negative values, add p before subtracting other /** * Subtraction operator. @@ -803,14 +855,15 @@ bigfield bigfield::sum(const std::vector& term if (terms.size() == 1) { return terms[0]; } - std::vector halved; - for (size_t i = 0; i < terms.size() / 2; i++) { - halved.push_back(terms[2 * i] + terms[2 * i + 1]); + + bigfield acc = terms[0]; + for (size_t i = 1; i < (terms.size() + 1) / 2; i++) { + acc = acc.add_two(terms[2 * i - 1], terms[2 * i]); } - if (terms.size() & 1) { - halved.push_back(terms[terms.size() - 1]); + if ((terms.size() & 1) == 0) { + acc += terms[terms.size() - 1]; } - return sum(halved); + return acc; } /** diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_ultra.fuzzer.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_ultra.fuzzer.cpp new file mode 100644 index 00000000000..278d3a862e9 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_ultra.fuzzer.cpp @@ -0,0 +1,3 @@ +#include "barretenberg/common/fuzzer_constants.hpp" +constexpr uint64_t FuzzerCircuitTypes = CircuitType::Ultra; +#include "bigfield.fuzzer.hpp" \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp index b49b3a7ad7c..572fc9590a0 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.cpp @@ -47,9 +47,11 @@ void UltraCircuitBuilder_::finalize_circuit(const bool ensure_no add_gates_to_ensure_all_polys_are_non_zero(); } process_non_native_field_multiplications(); +#ifndef ULTRA_FUZZ process_ROM_arrays(); process_RAM_arrays(); process_range_lists(); +#endif circuit_finalized = true; } else { // Gates added after first call to finalize will not be processed since finalization is only performed once