diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.cpp index bac52c7b9904..a7106ee766cf 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.cpp @@ -13,6 +13,7 @@ #include "barretenberg/avm_fuzzer/fuzzer_comparison_helper.hpp" #include "barretenberg/avm_fuzzer/mutations/fuzzer_data.hpp" #include "barretenberg/avm_fuzzer/mutations/tx_data.hpp" +#include "barretenberg/avm_fuzzer/mutations/tx_types/gas.hpp" #include "barretenberg/common/log.hpp" #include "barretenberg/vm2/avm_api.hpp" #include "barretenberg/vm2/common/avm_io.hpp" @@ -333,6 +334,12 @@ size_t mutate_tx_data(FuzzerContext& context, .calldata_hash = calldata_hash }, .calldata = calldata }); } + + // Compute effective gas fees matching TS computeEffectiveGasFees + // This must be done after any mutation that could affect gas settings or global variables + tx_data.tx.effective_gas_fees = + compute_effective_gas_fees(tx_data.global_variables.gas_fees, tx_data.tx.gas_settings); + auto [mutated_serialized_fuzzer_data, mutated_serialized_fuzzer_data_size] = msgpack_encode_buffer(tx_data); if (mutated_serialized_fuzzer_data_size > max_size) { delete[] mutated_serialized_fuzzer_data; diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/tx_data.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/tx_data.cpp index 5455e0ddf2a8..9944aaacacbd 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/tx_data.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/tx_data.cpp @@ -6,6 +6,7 @@ #include "barretenberg/avm_fuzzer/mutations/fuzzer_data.hpp" #include "barretenberg/avm_fuzzer/mutations/instructions/instruction_block.hpp" #include "barretenberg/avm_fuzzer/mutations/tx_types/accumulated_data.hpp" +#include "barretenberg/avm_fuzzer/mutations/tx_types/gas.hpp" #include "barretenberg/avm_fuzzer/mutations/tx_types/public_call_request.hpp" #include "barretenberg/vm2/common/avm_io.hpp" #include "barretenberg/vm2/common/aztec_constants.hpp" @@ -58,20 +59,6 @@ void mutate_teardown(std::optional& teardown_call namespace bb::avm2::fuzzer { -// Gas bounds for mutation -constexpr uint32_t MIN_GAS = 1000; -constexpr uint32_t MAX_GAS = 10000000; - -// Fee bounds for mutation -constexpr uint128_t MIN_FEE = 1; -constexpr uint128_t MAX_FEE = 1000; - -constexpr uint32_t AVM_MAX_PROCESSABLE_DA_GAS = (MAX_NOTE_HASHES_PER_TX * AVM_EMITNOTEHASH_BASE_DA_GAS) + - (MAX_NULLIFIERS_PER_TX * AVM_EMITNULLIFIER_BASE_DA_GAS) + - (MAX_L2_TO_L1_MSGS_PER_TX * AVM_SENDL2TOL1MSG_BASE_DA_GAS) + - (MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * AVM_SSTORE_DYN_DA_GAS) + - (PUBLIC_LOGS_LENGTH * AVM_EMITUNENCRYPTEDLOG_BASE_DA_GAS); - void mutate_tx(Tx& tx, std::vector& contract_addresses, std::mt19937_64& rng) { auto choice = TX_MUTATION_CONFIGURATION.select(rng); @@ -102,98 +89,38 @@ void mutate_tx(Tx& tx, std::vector& contract_addresses, std::mt199 fuzz_info("Mutating revertible accumulated data"); mutate_revertible_accumulated_data(tx.revertible_accumulated_data, rng); break; - - // case 2: - // // Mutate gas_settings - // mutate_gas_settings(tx.gas_settings, rng); - // break; - // case 3: - // // Mutate effective_gas_fees - // mutate_gas_fees(tx.effective_gas_fees, rng); - // break; - // case 4: - // // Mutate Deployment data - // break; - // case 8: - // // Mutate gas_used_by_private - // break; - // case 9: - // // Mutate fee_payer - // break; - //} - } -} - -void mutate_gas_settings(GasSettings& gas_settings, std::mt19937_64& rng) -{ - auto choice = std::uniform_int_distribution(0, 3)(rng); - - switch (choice) { - case 0: - // Pick a Gas Limit between [0, AVM_MAX_PROCESSABLE_L2_GAS] - // fixme: probably should not mutate both l2_gas and da_gas to max in one go - gas_settings.gas_limits.l2_gas = std::uniform_int_distribution(0, AVM_MAX_PROCESSABLE_L2_GAS)(rng); - gas_settings.gas_limits.da_gas = std::uniform_int_distribution(0, AVM_MAX_PROCESSABLE_DA_GAS)(rng); - break; - case 1: - // Mutate teardown_gas_limits - gas_settings.teardown_gas_limits.l2_gas = - std::uniform_int_distribution(0, AVM_MAX_PROCESSABLE_L2_GAS)(rng); - gas_settings.teardown_gas_limits.da_gas = - std::uniform_int_distribution(0, AVM_MAX_PROCESSABLE_DA_GAS)(rng); - break; - case 2: - // Mutate max_fees_per_gas - // mutate_gas_fees(gas_settings.max_fees_per_gas, rng); - break; - case 3: - // Mutate max_priority_fees_per_gas - // mutate_gas_fees(gas_settings.max_priority_fees_per_gas, rng); - break; - } -} - -void mutate_gas(Gas& gas, std::mt19937_64& rng) -{ - auto choice = std::uniform_int_distribution(0, 2)(rng); - - switch (choice) { - case 0: - // Mutate l2_gas - gas.l2_gas = std::uniform_int_distribution(MIN_GAS, MAX_GAS)(rng); - break; - case 1: - // Mutate da_gas - gas.da_gas = std::uniform_int_distribution(MIN_GAS, MAX_GAS)(rng); - break; - case 2: - // Set both to same value - gas.l2_gas = gas.da_gas = std::uniform_int_distribution(MIN_GAS, MAX_GAS)(rng); - break; - } -} - -void mutate_gas_fees(GasFees& fees, std::mt19937_64& rng) -{ - auto choice = std::uniform_int_distribution(0, 3)(rng); - - switch (choice) { - case 0: - // Mutate fee_per_da_gas - fees.fee_per_da_gas = std::uniform_int_distribution(MIN_FEE, MAX_FEE)(rng); - break; - case 1: - // Mutate fee_per_l2_gas - fees.fee_per_l2_gas = std::uniform_int_distribution(MIN_FEE, MAX_FEE)(rng); - break; - case 2: - // Set both to zero - fees.fee_per_da_gas = 0; - fees.fee_per_l2_gas = 0; - break; - case 3: - // Set both to same non-zero value - fees.fee_per_da_gas = fees.fee_per_l2_gas = std::uniform_int_distribution(1, MAX_FEE)(rng); + case TxMutationOptions::GasSettings: + // Mutate gas_settings + fuzz_info("Mutating gas settings"); + mutate_gas_settings(tx.gas_settings, rng); + // Ensure effective_gas_fees <= max_fees_per_gas after mutation + tx.effective_gas_fees.fee_per_da_gas = + std::min(tx.effective_gas_fees.fee_per_da_gas, tx.gas_settings.max_fees_per_gas.fee_per_da_gas); + tx.effective_gas_fees.fee_per_l2_gas = + std::min(tx.effective_gas_fees.fee_per_l2_gas, tx.gas_settings.max_fees_per_gas.fee_per_l2_gas); + break; + case TxMutationOptions::GasFees: + // Mutate effective_gas_fees + fuzz_info("Mutating effective gas fees"); + mutate_gas_fees(tx.effective_gas_fees, rng); + // Ensure effective_gas_fees <= max_fees_per_gas after mutation + tx.effective_gas_fees.fee_per_da_gas = + std::min(tx.effective_gas_fees.fee_per_da_gas, tx.gas_settings.max_fees_per_gas.fee_per_da_gas); + tx.effective_gas_fees.fee_per_l2_gas = + std::min(tx.effective_gas_fees.fee_per_l2_gas, tx.gas_settings.max_fees_per_gas.fee_per_l2_gas); + break; + case TxMutationOptions::GasUsedByPrivate: + // Mutate gas_used_by_private + fuzz_info("Mutating gas used by private"); + mutate_gas(tx.gas_used_by_private, rng); + // Ensure gas_used_by_private <= gas_limits after mutation + tx.gas_used_by_private.l2_gas = std::min(tx.gas_used_by_private.l2_gas, tx.gas_settings.gas_limits.l2_gas); + tx.gas_used_by_private.da_gas = std::min(tx.gas_used_by_private.da_gas, tx.gas_settings.gas_limits.da_gas); + break; + case TxMutationOptions::FeePayer: + // Mutate fee_payer + fuzz_info("Mutating fee payer"); + mutate_field(tx.fee_payer, rng, BASIC_FIELD_MUTATION_CONFIGURATION); break; } } diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/tx_data.hpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/tx_data.hpp index 3bc6d10d6534..1934d4bb7d27 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/tx_data.hpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/tx_data.hpp @@ -15,9 +15,13 @@ enum class TxMutationOptions { TearDownEnqueuedCall, NonRevertibleData, RevertibleData, + GasSettings, + GasFees, + GasUsedByPrivate, + FeePayer }; -using TxMutationConfig = WeightedSelectionConfig; +using TxMutationConfig = WeightedSelectionConfig; constexpr TxMutationConfig TX_MUTATION_CONFIGURATION = TxMutationConfig({ { TxMutationOptions::SetupEnqueuedCalls, 30 }, @@ -25,21 +29,16 @@ constexpr TxMutationConfig TX_MUTATION_CONFIGURATION = TxMutationConfig({ { TxMutationOptions::TearDownEnqueuedCall, 10 }, { TxMutationOptions::NonRevertibleData, 15 }, { TxMutationOptions::RevertibleData, 15 }, + { TxMutationOptions::GasSettings, 5 }, + { TxMutationOptions::GasFees, 3 }, + { TxMutationOptions::GasUsedByPrivate, 1 }, + { TxMutationOptions::FeePayer, 1 }, }); namespace bb::avm2::fuzzer { void mutate_tx(Tx& tx, std::vector& contract_addresses, std::mt19937_64& rng); -// GasSettings mutation -void mutate_gas_settings(GasSettings& gas_settings, std::mt19937_64& rng); - -// Gas mutation -void mutate_gas(Gas& gas, std::mt19937_64& rng); - -// GasFees mutation -void mutate_gas_fees(GasFees& fees, std::mt19937_64& rng); - void mutate_fuzzer_data_vec(const FuzzerContext& context, std::vector& enqueued_calls, std::mt19937_64& rng, diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/tx_types/gas.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/tx_types/gas.cpp new file mode 100644 index 000000000000..9a7fda0e8bc9 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/tx_types/gas.cpp @@ -0,0 +1,139 @@ +#include "barretenberg/avm_fuzzer/mutations/tx_types/gas.hpp" + +#include "barretenberg/avm_fuzzer/fuzz_lib/constants.hpp" +#include "barretenberg/avm_fuzzer/mutations/basic_types/field.hpp" +#include "barretenberg/avm_fuzzer/mutations/basic_types/vector.hpp" +#include "barretenberg/avm_fuzzer/mutations/configuration.hpp" +#include "barretenberg/common/serialize.hpp" +#include "barretenberg/numeric/uint256/uint256.hpp" +#include "barretenberg/vm2/common/avm_io.hpp" + +using bb::avm2::AztecAddress; +using bb::avm2::FF; + +namespace { + +constexpr uint128_t MAX_U128 = ~static_cast(0); + +uint128_t generate_u128(std::mt19937_64& rng, uint128_t min = 0, uint128_t max = MAX_U128) +{ + uint64_t high = std::uniform_int_distribution()(rng); + uint64_t low = std::uniform_int_distribution()(rng); + uint128_t value = (static_cast(high) << 64) | static_cast(low); + // Scale to desired range + return min + (value % (max - min + 1)); +} + +} // namespace + +namespace bb::avm2::fuzzer { + +Gas generate_gas(std::mt19937_64& rng) +{ + uint32_t l2_gas = std::uniform_int_distribution(MIN_GAS, AVM_MAX_PROCESSABLE_L2_GAS)(rng); + uint32_t da_gas = std::uniform_int_distribution(MIN_GAS, AVM_MAX_PROCESSABLE_DA_GAS)(rng); + + return Gas{ l2_gas, da_gas }; +} + +void mutate_gas(Gas& gas, std::mt19937_64& rng) +{ + auto choice = std::uniform_int_distribution(0, 1)(rng); + + switch (choice) { + case 0: + // Mutate l2_gas + gas.l2_gas = std::uniform_int_distribution(MIN_GAS, AVM_MAX_PROCESSABLE_L2_GAS)(rng); + break; + case 1: + // Mutate da_gas + gas.da_gas = std::uniform_int_distribution(MIN_GAS, AVM_MAX_PROCESSABLE_DA_GAS)(rng); + break; + } +} + +GasFees generate_gas_fees(std::mt19937_64& rng) +{ + uint128_t fee_per_da_gas = generate_u128(rng, MIN_FEE, MAX_FEE); + uint128_t fee_per_l2_gas = generate_u128(rng, MIN_FEE, MAX_FEE); + + return GasFees{ + fee_per_da_gas, + fee_per_l2_gas, + }; +} + +void mutate_gas_fees(GasFees& fees, std::mt19937_64& rng) +{ + auto choice = std::uniform_int_distribution(0, 1)(rng); + + switch (choice) { + case 0: + // Mutate fee_per_da_gas + fees.fee_per_da_gas = generate_u128(rng, MIN_FEE, MAX_FEE); + break; + case 1: + // Mutate fee_per_l2_gas + fees.fee_per_l2_gas = generate_u128(rng, MIN_FEE, MAX_FEE); + break; + } +} + +GasSettings generate_gas_settings(std::mt19937_64& rng) +{ + Gas gas_limits = generate_gas(rng); + Gas teardown_gas_limits = generate_gas(rng); + GasFees max_fees_per_gas = generate_gas_fees(rng); + GasFees max_priority_fees_per_gas = generate_gas_fees(rng); + + return GasSettings{ + gas_limits, + teardown_gas_limits, + max_fees_per_gas, + max_priority_fees_per_gas, + }; +} + +void mutate_gas_settings(GasSettings& gas_settings, std::mt19937_64& rng) +{ + auto choice = GAS_SETTINGS_MUTATION_CONFIGURATION.select(rng); + + switch (choice) { + case GasSettingsMutationOptions::GasLimits: + // Mutate gas_limits + mutate_gas(gas_settings.gas_limits, rng); + break; + case GasSettingsMutationOptions::TeardownGasLimits: + // Mutate teardown_gas_limits + mutate_gas(gas_settings.teardown_gas_limits, rng); + break; + case GasSettingsMutationOptions::MaxFeesPerGas: + // Mutate max_fees_per_gas + mutate_gas_fees(gas_settings.max_fees_per_gas, rng); + break; + case GasSettingsMutationOptions::MaxPriorityFeesPerGas: + // Mutate max_priority_fees_per_gas + mutate_gas_fees(gas_settings.max_priority_fees_per_gas, rng); + break; + } +} + +GasFees compute_effective_gas_fees(const GasFees& gas_fees, const GasSettings& gas_settings) +{ + // Match TS computeEffectiveGasFees from yarn-project/stdlib/src/fees/transaction_fee.ts + // priorityFees = min(maxPriorityFeesPerGas, maxFeesPerGas - gasFees) + // effectiveFees = gasFees + priorityFees + auto min_u128 = [](uint128_t a, uint128_t b) { return a < b ? a : b; }; + + uint128_t priority_da = min_u128(gas_settings.max_priority_fees_per_gas.fee_per_da_gas, + gas_settings.max_fees_per_gas.fee_per_da_gas - gas_fees.fee_per_da_gas); + uint128_t priority_l2 = min_u128(gas_settings.max_priority_fees_per_gas.fee_per_l2_gas, + gas_settings.max_fees_per_gas.fee_per_l2_gas - gas_fees.fee_per_l2_gas); + + return GasFees{ + .fee_per_da_gas = gas_fees.fee_per_da_gas + priority_da, + .fee_per_l2_gas = gas_fees.fee_per_l2_gas + priority_l2, + }; +} + +} // namespace bb::avm2::fuzzer diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/tx_types/gas.hpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/tx_types/gas.hpp new file mode 100644 index 000000000000..82c99801fa8c --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/tx_types/gas.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include "barretenberg/avm_fuzzer/common/weighted_selection.hpp" +#include "barretenberg/vm2/common/avm_io.hpp" + +namespace bb::avm2::fuzzer { + +// Fee bounds for mutation. +// MIN_FEE must be >= 1 to prevent underflow in compute_effective_gas_fees, since +// global_variables.gas_fees is hardcoded to {1, 1}. This can change once we enable +// smart mutations of global variables that maintain the invariant max_fees_per_gas >= gas_fees. +constexpr uint128_t MIN_FEE = 1; +constexpr uint128_t MAX_FEE = 1000; +// +// Gas bounds for mutation +constexpr uint32_t MIN_GAS = 0; +constexpr uint32_t AVM_MAX_PROCESSABLE_DA_GAS = (MAX_NOTE_HASHES_PER_TX * AVM_EMITNOTEHASH_BASE_DA_GAS) + + (MAX_NULLIFIERS_PER_TX * AVM_EMITNULLIFIER_BASE_DA_GAS) + + (MAX_L2_TO_L1_MSGS_PER_TX * AVM_SENDL2TOL1MSG_BASE_DA_GAS) + + (MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * AVM_SSTORE_DYN_DA_GAS) + + (PUBLIC_LOGS_LENGTH * AVM_EMITUNENCRYPTEDLOG_BASE_DA_GAS); + +enum class GasSettingsMutationOptions : uint8_t { + GasLimits, + TeardownGasLimits, + MaxFeesPerGas, + MaxPriorityFeesPerGas, +}; + +using GasSettingsMutationConfig = WeightedSelectionConfig; + +constexpr GasSettingsMutationConfig GAS_SETTINGS_MUTATION_CONFIGURATION = GasSettingsMutationConfig({ + { GasSettingsMutationOptions::GasLimits, 20 }, + { GasSettingsMutationOptions::TeardownGasLimits, 10 }, + { GasSettingsMutationOptions::MaxFeesPerGas, 20 }, + { GasSettingsMutationOptions::MaxPriorityFeesPerGas, 5 }, +}); + +Gas generate_gas(std::mt19937_64& rng); +void mutate_gas(Gas& gas, std::mt19937_64& rng); + +GasSettings generate_gas_settings(std::mt19937_64& rng); +void mutate_gas_settings(GasSettings& data, std::mt19937_64& rng); + +GasFees generate_gas_fees(std::mt19937_64& rng); +void mutate_gas_fees(GasFees& gas_fees, std::mt19937_64& rng); + +// Compute effective gas fees matching TS computeEffectiveGasFees. +// Requires: maxFeesPerGas >= gasFees (otherwise underflow) +GasFees compute_effective_gas_fees(const GasFees& gas_fees, const GasSettings& gas_settings); + +} // namespace bb::avm2::fuzzer