49bool solve_witnesses(std::vector<Acir::Expression>& expressions,
50 uint32_t num_witnesses,
61 for (
const auto& expr : expressions) {
63 for (
const auto& [coeff_bytes, w1, w2] : expr.mul_terms) {
64 witness_has_nonlinear[w1.value] =
true;
65 witness_has_nonlinear[w2.value] =
true;
70 for (
const auto& expr : expressions) {
71 for (
const auto& [coeff_bytes, w] : expr.linear_combinations) {
73 if (!witness_has_nonlinear.contains(w.value)) {
74 linear_only_witnesses.insert(w.value);
81 for (
auto& expr : expressions) {
85 for (
const auto& [coeff_bytes, w1, w2] : expr.mul_terms) {
87 value += coeff * witnesses[w1.value] * witnesses[w2.value];
90 for (
const auto& [coeff_bytes, w] : expr.linear_combinations) {
92 value += coeff * witnesses[w.value];
102 for (
const auto& [coeff_bytes, w] : expr.linear_combinations) {
103 if (linear_only_witnesses.contains(w.value)) {
105 linear_witness_coeffs[w.value] += coeff;
110 for (
const auto& [w_idx, total_coeff] : linear_witness_coeffs) {
115 for (
const auto& [coeff_bytes, w1, w2] : expr.mul_terms) {
117 value_without_witness += coeff * witnesses[w1.value] * witnesses[w2.value];
119 for (
const auto& [coeff_bytes, w] : expr.linear_combinations) {
120 if (w.value != w_idx) {
122 value_without_witness += coeff * witnesses[w.value];
128 witnesses[w_idx] = -value_without_witness / total_coeff;
144 for (
const auto& [coeff_bytes, w] : expr.linear_combinations) {
145 linear_only_witnesses.erase(w.value);
165 for (
const auto& [coeff_bytes, w1, w2] : expr.mul_terms) {
173 for (
const auto& [coeff_bytes, w] : expr.linear_combinations) {
191 for (
size_t i = 0; i <
acir_format.quad_constraints.size(); ++i) {
192 const auto& gate =
acir_format.quad_constraints[i];
194 std::cerr <<
" a=" << gate.a <<
", b=" << gate.b <<
", c=" << gate.c <<
", d=" << gate.d <<
std::endl;
202 std::cerr <<
" Represents: " << gate.mul_scaling <<
"*w" << gate.a <<
"*w" << gate.b <<
" + " << gate.a_scaling
203 <<
"*w" << gate.a <<
" + " << gate.b_scaling <<
"*w" << gate.b <<
" + " << gate.c_scaling <<
"*w"
204 << gate.c <<
" + " << gate.d_scaling <<
"*w" << gate.d <<
" + " << gate.const_scaling <<
" = 0"
209 for (
size_t expr_idx = 0; expr_idx <
acir_format.big_quad_constraints.size(); ++expr_idx) {
210 const auto& gates =
acir_format.big_quad_constraints[expr_idx];
211 std::cerr <<
"\nBig Expression " << expr_idx <<
" (" << gates.size() <<
" gates):" <<
std::endl;
212 for (
size_t i = 0; i < gates.size(); ++i) {
213 const auto& gate = gates[i];
214 std::cerr <<
" Gate " << i <<
": " << gate.mul_scaling <<
"*w" << gate.a <<
"*w" << gate.b <<
" + "
215 << gate.a_scaling <<
"*w" << gate.a <<
" + " << gate.b_scaling <<
"*w" << gate.b <<
" + "
216 << gate.c_scaling <<
"*w" << gate.c <<
" + " << gate.d_scaling <<
"*w" << gate.d <<
" + "
217 << gate.const_scaling <<
" = 0" <<
std::endl;
227void print_expressions_and_witnesses(
const std::vector<Acir::Expression>& expressions,
234 for (
const auto& [idx,
value] : witnesses) {
240 for (
size_t i = 0; i < expressions.size(); ++i) {
241 const auto& expr = expressions[i];
251 for (
const auto& [coeff_bytes, w1, w2] : expr.mul_terms) {
260 for (
const auto& [coeff_bytes, w] : expr.linear_combinations) {
268 for (
const auto& [coeff_bytes, w1, w2] : expr.mul_terms) {
270 auto it1 = witnesses.find(w1.value);
271 auto it2 = witnesses.find(w2.value);
272 if (it1 != witnesses.end() && it2 != witnesses.end()) {
273 value += coeff * it1->second * it2->second;
276 for (
const auto& [coeff_bytes, w] : expr.linear_combinations) {
278 auto it = witnesses.find(w.value);
279 if (it != witnesses.end()) {
280 value += coeff * it->second;
301bool validate_witnesses(
const std::vector<Acir::Expression>& expressions,
303 bool expect_satisfied =
true)
305 (void)expect_satisfied;
306 size_t unsatisfied_count = 0;
308 for (
size_t i = 0; i < expressions.size(); ++i) {
309 const auto& expr = expressions[i];
314 for (
const auto& [coeff_bytes, w1, w2] : expr.mul_terms) {
316 auto it1 = witnesses.find(w1.value);
317 auto it2 = witnesses.find(w2.value);
318 if (it1 != witnesses.end() && it2 != witnesses.end()) {
319 value += coeff * it1->second * it2->second;
323 for (
const auto& [coeff_bytes, w] : expr.linear_combinations) {
325 auto it = witnesses.find(w.value);
326 if (it != witnesses.end()) {
327 value += coeff * it->second;
339 return unsatisfied_count == 0;
349bool test_acir_circuit(
const uint8_t*
data,
size_t size)
357 bool disable_sanitization =
false;
358#ifdef ENABLE_UNSANITIZED_FUZZING
360 disable_sanitization = (
data[0] % 10) == 0;
371 uint32_t scale_factor = 1;
374 size_t temp_size = size / 64;
375 while (temp_size > 1 && scale_factor < 10) {
381 uint32_t max_witnesses = std::min(10u * scale_factor, 100u);
382 uint32_t max_expressions = std::min(3u * scale_factor, 20u);
383 uint32_t max_vm_steps = std::min(10u * scale_factor, 50u);
385 uint32_t num_witnesses = (
data[0] % max_witnesses) + 2;
386 uint32_t num_expressions = (
data[1] % max_expressions) + 1;
387 size_t coeff_vm_steps = (
data[2] % max_vm_steps) + 3;
388 size_t witness_vm_steps = (
data[3] % max_vm_steps) + 3;
389 uint8_t range_constraint_byte =
data[4];
391 const uint8_t* vm_data =
data + 5;
392 size_t vm_data_size = size - 5;
396 size_t coeff_consumed = coeff_vm.run(vm_data, vm_data_size,
false);
397 const auto& coeff_state = coeff_vm.field_internal_state;
400 const uint8_t* witness_vm_data = vm_data + coeff_consumed;
401 size_t witness_vm_data_size = (coeff_consumed < vm_data_size) ? (vm_data_size - coeff_consumed) : 0;
403 if (witness_vm_data_size < 10)
407 size_t witness_consumed = witness_vm.run(witness_vm_data, witness_vm_data_size,
false);
408 const auto& witness_state = witness_vm.field_internal_state;
411 const uint8_t* ptr = witness_vm_data + witness_consumed;
412 size_t remaining = (witness_consumed < witness_vm_data_size) ? (witness_vm_data_size - witness_consumed) : 0;
418 std::vector<Acir::Expression> expressions;
419 for (uint32_t e = 0; e < num_expressions && remaining > 2; ++e) {
425 uint8_t max_mul_terms =
static_cast<uint8_t
>(std::min(1u + scale_factor / 2, 5u));
426 uint8_t max_lin_terms =
static_cast<uint8_t
>(std::min(3u + scale_factor, 10u));
428 uint8_t num_mul = (ptr[0] %
std::max(max_mul_terms,
static_cast<uint8_t
>(1)));
429 uint8_t num_lin = 1 + (ptr[1] %
std::max(max_lin_terms,
static_cast<uint8_t
>(1)));
434 for (uint8_t m = 0; m < num_mul && remaining >= 3; ++m) {
437 disable_sanitization ? *
reinterpret_cast<const uint16_t*
>(ptr + 1) : ptr[1] % num_witnesses;
439 disable_sanitization ? *
reinterpret_cast<const uint16_t*
>(ptr + 1) : ptr[2] % num_witnesses;
443 std::vector<uint8_t> coeff(32);
444 coeff = coeff_state[coeff_reg].
to_buffer();
448 expr.
mul_terms.push_back(std::make_tuple(coeff, w1, w2));
452 bool force_duplicate = (num_lin > 1) && (remaining > 0) && ((ptr[0] % 3) == 0);
453 uint32_t prev_witness = 0;
455 for (uint8_t l = 0; l < num_lin && remaining >= 2; ++l) {
457 uint32_t w_idx = disable_sanitization ? ptr[1] : ptr[1] % num_witnesses;
462 if (force_duplicate && l > 0 && l < 3) {
463 w_idx = prev_witness;
465 prev_witness = w_idx;
467 std::vector<uint8_t> coeff(32);
468 coeff = coeff_state[coeff_reg].
to_buffer();
479 expr.
q_c = coeff_state[const_reg].to_buffer();
481 expr.
q_c = std::vector<uint8_t>(32, 0);
484 expressions.push_back(expr);
487 if (expressions.empty())
491 std::vector<Acir::Expression> non_trivial_expressions;
492 for (
const auto& expr : expressions) {
493 if (!is_trivial_expression(expr)) {
494 non_trivial_expressions.push_back(expr);
499 if (non_trivial_expressions.empty()) {
504 expressions = non_trivial_expressions;
508 for (uint32_t i = 0; i < num_witnesses; ++i) {
514 solve_witnesses(expressions, num_witnesses, solved_witnesses);
525 bool witnesses_corrupted =
false;
526 std::vector<uint32_t> corrupted_witness_indices;
530 if (size > 4 && (
data[size - 1] % 5) == 0) {
531 witnesses_corrupted =
true;
532 uint32_t num_to_corrupt = 1 + (
data[size - 2] % 3);
533 bool actually_corrupted =
false;
535 for (uint32_t i = 0; i < num_to_corrupt && i < num_witnesses; ++i) {
536 size_t byte_idx = size - 3 - i;
538 uint32_t witness_to_corrupt = disable_sanitization ?
data[byte_idx] :
data[byte_idx] % num_witnesses;
541 if (already_corrupted.count(witness_to_corrupt) > 0) {
544 already_corrupted.insert(witness_to_corrupt);
546 fr original_value = solved_witnesses[witness_to_corrupt];
550 fr corruption_value = coeff_state[state_idx];
552 if (corruption_value == original_value) {
557 if (corruption_value == original_value) {
559 corruption_value = original_value -
fr::one();
563 if (corruption_value != original_value) {
564 solved_witnesses[witness_to_corrupt] = corruption_value;
565 corrupted_witness_indices.push_back(witness_to_corrupt);
566 actually_corrupted =
true;
573 if (actually_corrupted) {
579 bool corrupted_witnesses_satisfy_constraints = validate_witnesses(expressions, solved_witnesses,
false);
581 if (corrupted_witnesses_satisfy_constraints) {
586 solved_witnesses = original_witnesses;
587 witnesses_corrupted =
false;
600 for (
const auto& expr : expressions) {
604 if (is_assert_equal_pattern) {
607 if (coeff1 == -coeff2 && coeff1 !=
fr::zero()) {
611 assert_equal_only_witnesses.insert(w1);
612 assert_equal_only_witnesses.insert(w2);
616 for (
const auto& [coeff_bytes, w1, w2] : expr.mul_terms) {
617 constrained_in_other_expressions.insert(w1.value);
618 constrained_in_other_expressions.insert(w2.value);
620 for (
const auto& [coeff_bytes, w] : expr.linear_combinations) {
621 constrained_in_other_expressions.insert(w.
value);
627 for (uint32_t w : constrained_in_other_expressions) {
628 assert_equal_only_witnesses.erase(w);
632 bool all_corrupted_are_assert_equal_only =
true;
633 for (uint32_t w : corrupted_witness_indices) {
634 if (assert_equal_only_witnesses.count(w) == 0) {
635 all_corrupted_are_assert_equal_only =
false;
640 if (all_corrupted_are_assert_equal_only && !assert_equal_only_witnesses.empty()) {
645 solved_witnesses = original_witnesses;
646 witnesses_corrupted =
false;
653 witnesses_corrupted =
false;
662 bool should_violate_range =
false;
663 uint32_t violated_witness_idx = 0;
664 uint32_t violated_range_bits = 0;
667 auto satisfies_range = [](
const fr&
value, uint32_t num_bits) ->
bool {
668 if (num_bits >= 254) {
677 return value_int == 0;
681 size_t msb = value_int.
get_msb();
682 return msb < num_bits;
686 if ((range_constraint_byte & 0x80) != 0 && num_witnesses > 1) {
688 uint32_t num_range_constraints = ((range_constraint_byte >> 5) & 0x3) + 1;
689 num_range_constraints = std::min(num_range_constraints, num_witnesses - 1);
692 for (uint32_t i = 0; i < num_range_constraints; ++i) {
693 uint32_t witness_idx =
694 disable_sanitization ? range_constraint_byte + i : (range_constraint_byte + i) % num_witnesses;
698 uint8_t bit_selector = (range_constraint_byte + i * 37) & 0x1F;
699 uint32_t num_bits = 0;
701 if (bit_selector < 8) {
703 }
else if (bit_selector < 14) {
705 }
else if (bit_selector < 18) {
707 }
else if (bit_selector < 21) {
709 }
else if (bit_selector < 24) {
711 }
else if (bit_selector < 28) {
713 }
else if (bit_selector < 30) {
720 auto it = minimal_range.find(witness_idx);
721 if (it == minimal_range.end() || num_bits < it->second) {
722 minimal_range[witness_idx] = num_bits;
729 bool has_accidental_violation =
false;
730 for (
const auto& [witness_idx, min_bits] : minimal_range) {
731 range_constraints.push_back({ witness_idx, min_bits });
733 auto it = solved_witnesses.find(witness_idx);
734 if (it != solved_witnesses.end()) {
735 if (!satisfies_range(it->second, min_bits)) {
736 has_accidental_violation =
true;
744 if (!has_accidental_violation && (range_constraint_byte & 0x10) != 0 && !minimal_range.empty()) {
747 size_t violate_idx = range_constraint_byte % minimal_range.size();
748 auto it = minimal_range.begin();
750 uint32_t candidate_witness = it->first;
751 uint32_t candidate_bits = it->second;
755 if (candidate_bits < 254) {
756 should_violate_range =
true;
757 violated_witness_idx = candidate_witness;
758 violated_range_bits = candidate_bits;
761 fr violation_value =
fr(1);
762 for (uint32_t
b = 0;
b < violated_range_bits; ++
b) {
763 violation_value = violation_value + violation_value;
765 solved_witnesses[violated_witness_idx] = violation_value;
768 }
else if (has_accidental_violation) {
772 for (
const auto& [witness_idx, min_bits] : minimal_range) {
773 auto it = solved_witnesses.find(witness_idx);
774 if (it != solved_witnesses.end() && min_bits < 254) {
775 if (!satisfies_range(it->second, min_bits)) {
776 should_violate_range =
true;
777 violated_witness_idx = witness_idx;
778 violated_range_bits = min_bits;
797 for (
const auto& expr : expressions) {
799 assert_zero.
value = expr;
802 opcode.
value = assert_zero;
803 circuit.
opcodes.push_back(opcode);
807 for (
const auto& [witness_idx, num_bits] : range_constraints) {
813 input.
value = witness_input;
817 range_op.
input = input;
822 bb_call.
value = range_op;
826 bb_opcode.
value = bb_call;
829 opcode.
value = bb_opcode;
830 circuit.
opcodes.push_back(opcode);
841 witness_vec.reserve(num_witnesses);
842 for (uint32_t i = 0; i < num_witnesses; ++i) {
843 auto it = solved_witnesses.find(i);
844 if (it != solved_witnesses.end()) {
845 witness_vec.push_back(it->second);
862#ifndef FUZZING_DISABLE_WARNINGS
863 info(
"Circuit builder is in a failed state: ",
builder.err());
871 if (witnesses_corrupted || should_violate_range) {
874 if (witnesses_corrupted) {
877 if (should_violate_range) {
878 std::cerr <<
"Range constraint violation passed CircuitChecker verification!" <<
std::endl;
879 std::cerr <<
"Violated witness: w" << violated_witness_idx <<
" (range: " << violated_range_bits
881 std::cerr <<
"Witness value: " << solved_witnesses[violated_witness_idx] <<
std::endl;
883 std::cerr <<
"Num witnesses: " << num_witnesses <<
", Num expressions: " << expressions.size()
885 print_expressions_and_witnesses(expressions, solved_witnesses);
893 if (!circuit_valid) {
896 std::cerr <<
"Num witnesses: " << num_witnesses <<
", Num expressions: " << expressions.size() <<
std::endl;
897 print_expressions_and_witnesses(expressions, solved_witnesses);
902 return circuit_valid;
904 }
catch (
const std::exception& e) {
926 test_acir_circuit(
data, size);
936 if (size < 10 || max_size < 50) {
941 int strategy =
static_cast<int>(
static_cast<unsigned>(
std::rand()) % 100u);
946 size_t vm_section_start = 4;
947 size_t vm_section_end = size / 2;
948 if (vm_section_end > vm_section_start) {
949 size_t pos = vm_section_start + (
static_cast<unsigned>(
std::rand()) %
950 static_cast<unsigned>(vm_section_end - vm_section_start));
951 data[pos] =
static_cast<uint8_t
>(
static_cast<unsigned>(
std::rand()) % 256u);
954 }
else if (strategy < 55) {
957 size_t expr_section_start = size / 2;
958 size_t expr_section_end = size - 10;
959 if (expr_section_end > expr_section_start) {
960 size_t pos = expr_section_start + (
static_cast<unsigned>(
std::rand()) %
961 static_cast<unsigned>(expr_section_end - expr_section_start));
962 data[pos] =
static_cast<uint8_t
>(
static_cast<unsigned>(
std::rand()) % 256u);
965 }
else if (strategy < 70) {
969 static_cast<uint8_t
>(
static_cast<unsigned>(
std::rand()) % 256u);
971 }
else if (strategy < 80) {
973 if (size < max_size && max_size - size >= 32) {
974 size_t grow_by = std::min(
static_cast<size_t>(32), max_size - size);
976 for (
size_t i = 0; i < grow_by; ++i) {
977 data[size + i] =
static_cast<uint8_t
>(
static_cast<unsigned>(
std::rand()) % 256u);
979 return size + grow_by;
981 }
else if (strategy < 85) {
984 size_t shrink_by = std::min(
static_cast<size_t>(32), size - 50);
985 return size - shrink_by;
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize)
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
LibFuzzer entry point.
size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t max_size, unsigned int seed)
Custom mutator for structure-aware mutations with size scaling.
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
constexpr uint64_t get_msb() const
const std::vector< MemoryValue > data
Field arithmetic fuzzer for testing cryptographic field operations.
Entry point for Barretenberg command-line interface.
const size_t INTERNAL_STATE_SIZE
Constant defining the number of elements in the VM's internal state.
field< Bn254FrParams > fr
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
std::vector< uint8_t > to_buffer(T const &value)
Acir::FunctionInput input
std::variant< AES128Encrypt, AND, XOR, RANGE, Blake2s, Blake3, EcdsaSecp256k1, EcdsaSecp256r1, MultiScalarMul, EmbeddedCurveAdd, Keccakf1600, RecursiveAggregation, Poseidon2Permutation, Sha256Compression > value
Acir::PublicInputs return_values
std::vector< Acir::Opcode > opcodes
uint32_t current_witness_index
std::vector< Acir::Witness > private_parameters
Acir::PublicInputs public_parameters
std::string function_name
std::vector< std::tuple< Acir::OpcodeLocation, Acir::AssertionPayload > > assert_messages
std::vector< std::tuple< std::vector< uint8_t >, Acir::Witness > > linear_combinations
std::vector< uint8_t > q_c
std::vector< std::tuple< std::vector< uint8_t >, Acir::Witness, Acir::Witness > > mul_terms
Acir::BlackBoxFuncCall value
std::variant< AssertZero, BlackBoxFuncCall, MemoryOp, MemoryInit, BrilligCall, Call > value
Virtual machine for field arithmetic operations.
static constexpr field one()
static field serialize_from_buffer(const uint8_t *buffer)
BB_INLINE std::vector< uint8_t > to_buffer() const
static constexpr field zero()