Skip to content

Commit b884d8f

Browse files
committed
feat(avm): mutate enqueued calls
1 parent d64da2a commit b884d8f

File tree

6 files changed

+214
-143
lines changed

6 files changed

+214
-143
lines changed

barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/tx_data.cpp

Lines changed: 56 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,60 @@
11
#include "barretenberg/avm_fuzzer/mutations/tx_data.hpp"
2+
23
#include "barretenberg/avm_fuzzer/fuzz_lib/constants.hpp"
34
#include "barretenberg/avm_fuzzer/mutations/basic_types/field.hpp"
45
#include "barretenberg/avm_fuzzer/mutations/basic_types/vector.hpp"
56
#include "barretenberg/avm_fuzzer/mutations/fuzzer_data.hpp"
67
#include "barretenberg/avm_fuzzer/mutations/instructions/instruction_block.hpp"
8+
#include "barretenberg/avm_fuzzer/mutations/tx_types/public_call_request.hpp"
9+
#include "barretenberg/vm2/common/avm_io.hpp"
710
#include "barretenberg/vm2/common/aztec_constants.hpp"
811
#include "barretenberg/vm2/common/aztec_types.hpp"
912
#include "barretenberg/vm2/common/tagged_value.hpp"
1013
#include "barretenberg/vm2/simulation/lib/contract_crypto.hpp"
1114

15+
#include <optional>
1216
#include <random>
1317

18+
namespace {
19+
20+
void mutate_enqueued_calls(std::vector<PublicCallRequestWithCalldata>& enqueued_calls,
21+
std::vector<AztecAddress>& contract_addresses,
22+
std::mt19937_64& rng)
23+
{
24+
auto mutate_fn = [&](PublicCallRequestWithCalldata& call, std::mt19937_64& rng) {
25+
bb::avm2::fuzzer::mutate_public_call_request(call, contract_addresses, rng);
26+
};
27+
28+
auto gen_fn = [&](std::mt19937_64& rng) {
29+
return bb::avm2::fuzzer::generate_public_call_request(contract_addresses, rng);
30+
};
31+
32+
mutate_vec<PublicCallRequestWithCalldata>(enqueued_calls, rng, mutate_fn, gen_fn, BASIC_VEC_MUTATION_CONFIGURATION);
33+
};
34+
35+
void mutate_teardown(std::optional<PublicCallRequestWithCalldata>& teardown_call,
36+
std::vector<AztecAddress>& contract_addresses,
37+
std::mt19937_64& rng)
38+
{
39+
if (!teardown_call.has_value()) {
40+
// Nothing to mutate, generate a new one
41+
teardown_call = bb::avm2::fuzzer::generate_public_call_request(contract_addresses, rng);
42+
return;
43+
}
44+
45+
// If we already have a teardown call, there's a 1 in 10 chance we discard it
46+
bool discard = std::uniform_int_distribution<int>(0, 9)(rng) == 0;
47+
if (discard) {
48+
fuzz_info("Discarding teardown enqueued call");
49+
teardown_call = PublicCallRequestWithCalldata{};
50+
} else {
51+
// Mutate existing teardown call
52+
bb::avm2::fuzzer::mutate_public_call_request(teardown_call.value(), contract_addresses, rng);
53+
}
54+
}
55+
56+
} // namespace
57+
1458
namespace bb::avm2::fuzzer {
1559

1660
// Gas bounds for mutation
@@ -29,32 +73,25 @@ constexpr uint32_t AVM_MAX_PROCESSABLE_DA_GAS = (MAX_NOTE_HASHES_PER_TX * AVM_EM
2973

3074
void mutate_tx(Tx& tx, std::vector<AztecAddress>& contract_addresses, std::mt19937_64& rng)
3175
{
32-
auto choice = std::uniform_int_distribution<uint8_t>(0, 1)(rng);
76+
auto choice = TX_MUTATION_CONFIGURATION.select(rng);
3377

3478
switch (choice) {
35-
case 0:
79+
case TxMutationOptions::SetupEnqueuedCalls:
3680
// Mutate setup enqueued calls
3781
fuzz_info("Mutating setup enqueued calls: ", tx.setup_enqueued_calls.size());
38-
mutate_vec<PublicCallRequestWithCalldata>(
39-
tx.setup_enqueued_calls,
40-
rng,
41-
[&](PublicCallRequestWithCalldata& call, std::mt19937_64& rng) {
42-
mutate_public_call_request(call, contract_addresses, rng);
43-
},
44-
[&](std::mt19937_64& rng) { return generate_public_call_request(contract_addresses, rng); },
45-
BASIC_VEC_MUTATION_CONFIGURATION);
82+
mutate_enqueued_calls(tx.setup_enqueued_calls, contract_addresses, rng);
4683
break;
47-
case 1:
84+
case TxMutationOptions::AppLogicEnqueuedCalls:
4885
// Mutate app logic enqueued calls
4986
fuzz_info("Mutating app logic enqueued calls: ", tx.app_logic_enqueued_calls.size());
50-
mutate_vec<PublicCallRequestWithCalldata>(
51-
tx.app_logic_enqueued_calls,
52-
rng,
53-
[&](PublicCallRequestWithCalldata& call, std::mt19937_64& rng) {
54-
mutate_public_call_request(call, contract_addresses, rng);
55-
},
56-
[&](std::mt19937_64& rng) { return generate_public_call_request(contract_addresses, rng); },
57-
BASIC_VEC_MUTATION_CONFIGURATION);
87+
mutate_enqueued_calls(tx.app_logic_enqueued_calls, contract_addresses, rng);
88+
break;
89+
case TxMutationOptions::TearDownEnqueuedCall:
90+
// Mutate teardown enqueued call
91+
fuzz_info("Mutating teardown enqueued call");
92+
mutate_teardown(tx.teardown_enqueued_call, contract_addresses, rng);
93+
break;
94+
5895
// case 2:
5996
// // Mutate gas_settings
6097
// mutate_gas_settings(tx.gas_settings, rng);
@@ -92,10 +129,6 @@ void mutate_tx(Tx& tx, std::vector<AztecAddress>& contract_addresses, std::mt199
92129
// BASIC_VEC_MUTATION_CONFIGURATION);
93130
// break;
94131
// break;
95-
// case 7:
96-
// // Mutate teardown enqueued call
97-
//
98-
// break;
99132
// case 8:
100133
// // Mutate gas_used_by_private
101134
// break;
@@ -180,20 +213,6 @@ void mutate_gas_fees(GasFees& fees, std::mt19937_64& rng)
180213
}
181214
}
182215

183-
void mutate_ff_vec(std::vector<FF>& vec, std::mt19937_64& rng, size_t max_size)
184-
{
185-
mutate_vec<FF>(
186-
vec,
187-
rng,
188-
[](bb::avm2::FF& value, std::mt19937_64& rng) { mutate_field(value, rng, BASIC_FIELD_MUTATION_CONFIGURATION); },
189-
generate_random_field,
190-
BASIC_VEC_MUTATION_CONFIGURATION);
191-
192-
if (vec.size() > max_size) {
193-
vec.resize(max_size);
194-
}
195-
}
196-
197216
void mutate_l2_to_l1_msg(ScopedL2ToL1Message& msg, std::mt19937_64& rng)
198217
{
199218
auto choice = std::uniform_int_distribution<uint8_t>(0, 2)(rng);
@@ -222,26 +241,6 @@ ScopedL2ToL1Message generate_l2_to_l1_msg(std::mt19937_64& rng)
222241
};
223242
}
224243

225-
void mutate_bool_vec(std::vector<bool>& vec, size_t target_size, std::mt19937_64& rng)
226-
{
227-
// Resize to match target size
228-
while (vec.size() < target_size) {
229-
vec.push_back(std::uniform_int_distribution<uint8_t>(0, 1)(rng) == 1);
230-
}
231-
while (vec.size() > target_size) {
232-
vec.pop_back();
233-
}
234-
235-
// Flip a random bool with some probability
236-
if (!vec.empty()) {
237-
auto flip_prob = std::uniform_int_distribution<uint8_t>(0, 4)(rng);
238-
if (flip_prob == 0) {
239-
auto idx = std::uniform_int_distribution<size_t>(0, vec.size() - 1)(rng);
240-
vec[idx] = !vec[idx];
241-
}
242-
}
243-
}
244-
245244
void mutate_fuzzer_data_vec(std::vector<FuzzerData>& enqueued_calls, std::mt19937_64& rng, size_t max_size)
246245
{
247246
auto choice = std::uniform_int_distribution<uint8_t>(0, 1)(rng);
@@ -278,73 +277,4 @@ void mutate_fuzzer_data_vec(std::vector<FuzzerData>& enqueued_calls, std::mt1993
278277
}
279278
}
280279

281-
void mutate_public_call_request([[maybe_unused]] PublicCallRequestWithCalldata& request,
282-
[[maybe_unused]] std::vector<AztecAddress>& contract_addresses,
283-
[[maybe_unused]] std::mt19937_64& rng)
284-
{
285-
if (contract_addresses.empty()) {
286-
return; // Nothing to mutate to
287-
}
288-
// fixme(ilyas): this should be weighted since stuff like mutate calldata hash is fail-early
289-
auto choice = std::uniform_int_distribution<uint8_t>(0, 0)(rng);
290-
//
291-
switch (choice) {
292-
case 0:
293-
// Mutate contract_address
294-
// This is likely to cause immediate failure, needs to be weighted appropriately
295-
auto contract_address_choice = std::uniform_int_distribution<size_t>(0, contract_addresses.size() - 1)(rng);
296-
auto contract_address = contract_addresses[contract_address_choice];
297-
request.request.contract_address = contract_address;
298-
break;
299-
// case 1:
300-
// // Mutate msg_sender
301-
// request.request.msg_sender = generate_random_field(rng);
302-
// break;
303-
// case 2: {
304-
// // Mutate is_static_call
305-
// request.request.is_static_call = !request.request.is_static_call;
306-
// break;
307-
// }
308-
// case 3:
309-
// // Mutate calldata_hash - the intention here is to fail the hash check
310-
// request.request.calldata_hash = generate_random_field(rng);
311-
// break;
312-
// case 4:
313-
// // Mutate calldata
314-
// mutate_ff_vec(request.calldata, rng, 256);
315-
// // fixme: recompute calldata_hash when we start doing tracegen versions
316-
// // request.calldata_hash = compute_calldata_hash(request.calldata);
317-
// break;
318-
}
319-
}
320-
321-
PublicCallRequestWithCalldata generate_public_call_request(std::vector<AztecAddress>& contract_addresses,
322-
std::mt19937_64& rng)
323-
{
324-
fuzz_info("Generating new public call request");
325-
// Generate random calldata
326-
size_t calldata_size = std::uniform_int_distribution<size_t>(0, 256)(rng);
327-
std::vector<FF> calldata{};
328-
for (size_t i = 0; i < calldata_size; ++i) {
329-
calldata.push_back(generate_random_field(rng));
330-
}
331-
332-
auto contract_address =
333-
contract_addresses.empty()
334-
? generate_random_field(rng)
335-
: contract_addresses[std::uniform_int_distribution<size_t>(0, contract_addresses.size() - 1)(rng)];
336-
fuzz_info("Using contract address: ", contract_address);
337-
FF calldata_hash = simulation::compute_calldata_hash(calldata);
338-
return PublicCallRequestWithCalldata{
339-
.request =
340-
PublicCallRequest{
341-
.msg_sender = generate_random_field(rng),
342-
.contract_address = contract_address,
343-
.is_static_call = (std::uniform_int_distribution<uint8_t>(0, 1)(rng) == 1),
344-
.calldata_hash = calldata_hash,
345-
},
346-
.calldata = calldata,
347-
};
348-
}
349-
350280
} // namespace bb::avm2::fuzzer
Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
11
#pragma once
22

3-
#include <functional>
4-
#include <optional>
53
#include <random>
64
#include <vector>
75

6+
#include "barretenberg/avm_fuzzer/common/weighted_selection.hpp"
87
#include "barretenberg/avm_fuzzer/fuzz_lib/fuzzer_data.hpp"
98
#include "barretenberg/vm2/common/avm_io.hpp"
109
#include "barretenberg/vm2/common/aztec_types.hpp"
11-
#include "barretenberg/vm2/common/field.hpp"
10+
11+
enum class TxMutationOptions {
12+
SetupEnqueuedCalls,
13+
AppLogicEnqueuedCalls,
14+
TearDownEnqueuedCall,
15+
};
16+
17+
using TxMutationConfig = WeightedSelectionConfig<TxMutationOptions, 3>;
18+
19+
constexpr TxMutationConfig TX_MUTATION_CONFIGURATION = TxMutationConfig({
20+
{ TxMutationOptions::SetupEnqueuedCalls, 30 },
21+
{ TxMutationOptions::AppLogicEnqueuedCalls, 30 },
22+
{ TxMutationOptions::TearDownEnqueuedCall, 10 },
23+
});
1224

1325
namespace bb::avm2::fuzzer {
1426

@@ -23,22 +35,10 @@ void mutate_gas(Gas& gas, std::mt19937_64& rng);
2335
// GasFees mutation
2436
void mutate_gas_fees(GasFees& fees, std::mt19937_64& rng);
2537

26-
// Field vector mutation
27-
void mutate_ff_vec(std::vector<FF>& vec, std::mt19937_64& rng, size_t max_size = 10);
28-
2938
// L2ToL1Msg vector mutation
3039
void mutate_l2_to_l1_msg(ScopedL2ToL1Message& vec, std::mt19937_64& rng);
3140
ScopedL2ToL1Message generate_l2_to_l1_msg(std::mt19937_64& rng);
3241

33-
// Boolean vector mutation
34-
void mutate_bool_vec(std::vector<bool>& vec, size_t target_size, std::mt19937_64& rng);
35-
3642
void mutate_fuzzer_data_vec(std::vector<FuzzerData>& enqueued_calls, std::mt19937_64& rng, size_t max_size = 10);
3743

38-
void mutate_public_call_request(PublicCallRequestWithCalldata& request,
39-
std::vector<AztecAddress>& contract_addreses,
40-
std::mt19937_64& rng);
41-
PublicCallRequestWithCalldata generate_public_call_request(std::vector<AztecAddress>& contract_addresses,
42-
std::mt19937_64& rng);
43-
4444
} // namespace bb::avm2::fuzzer
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#include "barretenberg/avm_fuzzer/mutations/tx_types/public_call_request.hpp"
2+
3+
#include "barretenberg/avm_fuzzer/fuzz_lib/constants.hpp"
4+
#include "barretenberg/avm_fuzzer/mutations/basic_types/field.hpp"
5+
#include "barretenberg/avm_fuzzer/mutations/basic_types/vector.hpp"
6+
#include "barretenberg/avm_fuzzer/mutations/calldata/calldata_vec.hpp"
7+
#include "barretenberg/avm_fuzzer/mutations/configuration.hpp"
8+
#include "barretenberg/vm2/common/avm_io.hpp"
9+
#include "barretenberg/vm2/simulation/lib/contract_crypto.hpp"
10+
11+
using bb::avm2::AztecAddress;
12+
using bb::avm2::FF;
13+
14+
namespace {
15+
16+
void mutate_contract_address(AztecAddress& address, std::vector<AztecAddress>& contract_addresses, std::mt19937_64& rng)
17+
{
18+
if (contract_addresses.empty()) {
19+
address = generate_random_field(rng);
20+
}
21+
// Most of the time we want to pick from the existing contract addresses, since a random address will fail early
22+
auto contract_address_choice = std::uniform_int_distribution<size_t>(0, contract_addresses.size() - 1)(rng);
23+
address = contract_addresses[contract_address_choice];
24+
25+
// 1 in 1000 chance for the contract address to be random
26+
bool random_address = std::uniform_int_distribution<int>(0, 999)(rng) == 0;
27+
if (random_address) {
28+
fuzz_info("Mutating contract address to a random address");
29+
mutate_field(address, rng, BASIC_FIELD_MUTATION_CONFIGURATION);
30+
}
31+
}
32+
33+
void mutate_calldata(PublicCallRequestWithCalldata& request, std::mt19937_64& rng)
34+
{
35+
mutate_calldata_vec(request.calldata, rng);
36+
if (request.calldata.size() > fuzzer::MAX_CALLDATA_SIZE) {
37+
request.calldata.resize(fuzzer::MAX_CALLDATA_SIZE);
38+
}
39+
40+
request.request.calldata_hash = simulation::compute_calldata_hash(request.calldata);
41+
}
42+
43+
} // namespace
44+
45+
namespace bb::avm2::fuzzer {
46+
47+
void mutate_public_call_request(PublicCallRequestWithCalldata& request,
48+
std::vector<AztecAddress>& contract_addresses,
49+
std::mt19937_64& rng)
50+
{
51+
fuzz_info("Mutating public call request");
52+
auto choice = PUB_REQUEST_MUTATION_CONFIGURATION.select(rng);
53+
54+
switch (choice) {
55+
case PublicCallRequestMutationOptions::ContractAddress:
56+
// Mutate contract_address
57+
mutate_contract_address(request.request.contract_address, contract_addresses, rng);
58+
break;
59+
case PublicCallRequestMutationOptions::MsgSender:
60+
// Mutate msg_sender
61+
mutate_field(request.request.msg_sender, rng, BASIC_FIELD_MUTATION_CONFIGURATION);
62+
break;
63+
case PublicCallRequestMutationOptions::IsStaticCall:
64+
// Mutate is_static_call
65+
request.request.is_static_call = !request.request.is_static_call;
66+
break;
67+
case PublicCallRequestMutationOptions::Calldata:
68+
// Mutate calldata, this also updates the calldata_hash
69+
mutate_calldata(request, rng);
70+
break;
71+
}
72+
}
73+
74+
PublicCallRequestWithCalldata generate_public_call_request(std::vector<AztecAddress>& contract_addresses,
75+
std::mt19937_64& rng)
76+
{
77+
fuzz_info("Generating new public call request");
78+
// Generate random calldata
79+
size_t calldata_size = std::uniform_int_distribution<size_t>(0, MAX_CALLDATA_SIZE)(rng);
80+
std::vector<FF> calldata;
81+
calldata.reserve(calldata_size);
82+
for (size_t i = 0; i < calldata_size; ++i) {
83+
calldata.push_back(generate_random_field(rng));
84+
}
85+
86+
auto contract_address =
87+
contract_addresses.empty()
88+
? generate_random_field(rng)
89+
: contract_addresses[std::uniform_int_distribution<size_t>(0, contract_addresses.size() - 1)(rng)];
90+
fuzz_info("Using contract address: ", contract_address);
91+
FF calldata_hash = simulation::compute_calldata_hash(calldata);
92+
93+
return PublicCallRequestWithCalldata{
94+
.request =
95+
PublicCallRequest{
96+
.msg_sender = generate_random_field(rng),
97+
.contract_address = contract_address,
98+
.is_static_call = (std::uniform_int_distribution<uint8_t>(0, 1)(rng) == 1),
99+
.calldata_hash = calldata_hash,
100+
},
101+
.calldata = calldata,
102+
};
103+
}
104+
105+
} // namespace bb::avm2::fuzzer

0 commit comments

Comments
 (0)