Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Adding fuzzer for ultra bigfield and relaxing ultra circuit checker #10433

Merged
merged 13 commits into from
Dec 9, 2024
2 changes: 1 addition & 1 deletion barretenberg/cpp/CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you create a separate asan build?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? Do we really need to build with asan in the same directory as ordinary fuzzing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to change the directory without adding ASAN by default to the build. So either this change is not needed or it would be beneficial to create a separate build with ASAN, so it is easy to reconfigure

"cacheVariables": {
"FUZZING": "ON",
"ENABLE_ASAN": "ON",
Expand Down
1 change: 1 addition & 0 deletions barretenberg/cpp/cmake/module.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ template <typename Builder> 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

// 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) {
Expand Down Expand Up @@ -93,6 +104,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<Auxiliary>(values, params);
if (!result) {
return report_fail("Failed Auxiliary relation at row idx = ", idx);
Expand All @@ -101,6 +113,19 @@ bool UltraCircuitChecker::check_block(Builder& builder,
if (!result) {
return report_fail("Failed DeltaRangeConstraint relation at row idx = ", idx);
}
#else
if (values.q_aux == 1) {
// Bigfield related auxiliary gates
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<Auxiliary>(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);
Expand Down Expand Up @@ -297,6 +322,127 @@ void UltraCircuitChecker::populate_values(
}
}

#ifdef ULTRA_FUZZ
template <typename Builder> bool UltraCircuitChecker::relaxed_check_delta_range_relation(Builder& builder)
Sarkoxed marked this conversation as resolved.
Show resolved Hide resolved
{
std::unordered_map<uint32_t, uint64_t> 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<uint256_t>(range_tags[tag]);
uint256_t value = static_cast<uint256_t>(builder.get_variable(i));
if (value > range) {
info("Failed range constraint on variable with index = ", i, ": ", value, " > ", range);
return false;
}
}
}

// Processed block 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<uint256_t>(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<uint256_t>(w3 - w2);
if (delta > 3) {
info("Failed sort constraint relation at row idx = ", idx, " with delta2 = ", delta);
return false;
}
delta = static_cast<uint256_t>(w4 - w3);
if (delta > 3) {
info("Failed sort constraint at row idx = ", idx, " with delta3 = ", delta);
return false;
}
delta = static_cast<uint256_t>(w5 - w4);
if (delta > 3) {
info("Failed sort constraint at row idx = ", idx, " with delta4 = ", delta);
return false;
}
}
return true;
}

template <typename Builder> 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<uint32_t>(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 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 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<uint32_t> tmp_state(ram_array.state.size());

// check write and read RAM records
for (auto& rr : ram_array.records) {
// uint32_t index = rr.index;
uint32_t index = static_cast<uint32_t>(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<UltraCircuitBuilder_<UltraExecutionTraceBlocks>>(
const UltraCircuitBuilder_<UltraExecutionTraceBlocks>& builder_in);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ class UltraCircuitChecker {
MemoryCheckData& memory_data,
LookupHashTable& lookup_hash_table);

#ifdef ULTRA_FUZZ
template <typename Builder> static bool relaxed_check_aux_relation(Builder& builder);
template <typename Builder> 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
Expand Down
11 changes: 6 additions & 5 deletions barretenberg/cpp/src/barretenberg/common/fuzzer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include "barretenberg/circuit_checker/circuit_checker.hpp"
#include "barretenberg/numeric/uint256/uint256.hpp"
#include "barretenberg/stdlib_circuit_builders/standard_circuit_builder.hpp"
#include <concepts>
#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to include this header here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RunWithBuilders function uses ultra circuit. But I guess we can leave only the circuit_checker include


// NOLINTBEGIN(cppcoreguidelines-macro-usage, google-runtime-int)
#define PARENS ()
Expand Down Expand Up @@ -44,8 +44,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<size_t> structural_mutation_distribution; // Holds the values to quickly select a structural mutation
// based on chosen probabilities
std::vector<size_t> value_mutation_distribution; // Holds the values to quickly select a value mutation based on
Expand Down Expand Up @@ -157,7 +156,7 @@ concept ArithmeticFuzzHelperConstraint = requires {
template <typename T>
concept CheckableComposer = requires(T a) {
{
CircuitChecker::check(a)
bb::CircuitChecker::check(a)
} -> std::same_as<bool>;
};

Expand Down Expand Up @@ -220,7 +219,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
Expand Down Expand Up @@ -691,6 +690,8 @@ constexpr void RunWithBuilders(const uint8_t* Data, const size_t Size, FastRando
{
if (Composers & 1) {
RunWithBuilder<Fuzzer, bb::StandardCircuitBuilder>(Data, Size, VarianceRNG);
} else if (Composers & 2) {
RunWithBuilder<Fuzzer, bb::UltraCircuitBuilder>(Data, Size, VarianceRNG);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#pragma once
#include <cstdint>

enum CircuitType : uint64_t { Standard = 1 << 0 };
enum CircuitType : uint64_t { Standard = 1 << 0, Ultra = 1 << 1 };
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ template <typename FF_> 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;
Expand All @@ -189,7 +189,7 @@ template <typename FF_> 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;
Expand Down Expand Up @@ -239,7 +239,7 @@ template <typename FF_> 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
*
Expand Down
Loading
Loading