Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bytecode.cpp
Go to the documentation of this file.
2
10
11extern "C" size_t LLVMFuzzerMutate(uint8_t* Data, size_t Size, size_t MaxSize);
12
13namespace {
14
15avm2::Fq random_fq_scalar(std::mt19937_64& rng)
16{
17 std::uniform_int_distribution<uint64_t> dist(0, std::numeric_limits<uint64_t>::max());
18
20 for (size_t i = 0; i < 4; ++i) {
21 limbs[i] = dist(rng);
22 }
23
24 return avm2::Fq(limbs[0], limbs[1], limbs[2], limbs[3]);
25}
26
27void mutate_point(AffinePoint& point, std::mt19937_64& rng)
28{
29 // Generates a new valid on-curve point via scalar multiplication of the generator
30 // We make some assumptions for this point mutation:
31 // - The point is not at infinity
32 // - The point is valid (on curve)
33
34 // Generate scalar
35 Fq scalar = random_fq_scalar(rng);
36 point = grumpkin::g1::affine_element::one() * scalar;
37}
38
39} // namespace
40
41namespace bb::avm2::fuzzer {
42
44 std::vector<ContractInstance>& contract_instances,
45 const std::vector<AztecAddress>& contract_addresses,
46 std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes,
47 std::mt19937_64& rng)
48{
50
51 // Skip if no contracts to mutate
52 if (contract_classes.empty()) {
53 return;
54 }
55
56 // Select a random contract
57 size_t idx = std::uniform_int_distribution<size_t>(0, contract_classes.size() - 1)(rng);
58
59 ContractClassWithCommitment& klass = contract_classes[idx];
60 ContractInstance& instance = contract_instances[idx];
61 const AztecAddress& address = contract_addresses[idx];
62
63 // Copy bytecode and mutate it, we allow the default byte-wise fuzzing strategy to modify the
64 // bytecode, including expanding or shrinking it.
65 std::vector<uint8_t> bytecode = klass.packed_bytecode;
66 size_t original_size = bytecode.size();
67 size_t max_size = original_size * 2; // Allow growth up to 2x original size
68 // We have to resize before calling LLVMFuzzerMutate to ensure there's enough space without writing OOB
69 // We have to resize after so that the vector's metadata is correct
70 // LLVMFuzzerMutate is a C function that operates on raw pointers
71 bytecode.resize(max_size);
72 size_t new_size = LLVMFuzzerMutate(bytecode.data(), original_size, max_size);
73 bytecode.resize(new_size); // We need to resize here in case it shrunk
74
75 // Compute new bytecode commitment and class ID
78 klass.artifact_hash, klass.private_functions_root, new_bytecode_commitment);
79
80 // Store original class ID before modifications
81 FF original_class_id = instance.original_contract_class_id;
82
83 // Copy into NEW contract class with updated bytecode and id, we don't modify the existing one in case
84 // other instances refer to it
85 ContractClassWithCommitment new_class = klass;
86 new_class.id = new_class_id;
88 new_class.public_bytecode_commitment = new_bytecode_commitment;
89
90 // Update instance's current class ID to point to the newly upgraded-to class
91 fuzz_info("Contract at address ", address, ", upgraded from ", original_class_id, " -> ", new_class_id);
92 instance.current_contract_class_id = new_class_id;
93
94 // Add the new contract class to the vector (for serialization to TS)
95 contract_classes.push_back(new_class);
96
97 // Compute public data tree writes for UpdateCheck to pass
98 FF delayed_public_mutable_slot =
100
101 // Build preimage
102 // todo(ilyas): make this somewhat random but also take into account the mutation on global variables.timestamp
103 FF metadata = 0; // The lower 32 bits are the timestamp_of_change, we set to 0 so it has "taken effect"
104 FF hash = Poseidon2::hash({ metadata, original_class_id, new_class_id });
105
106 std::array<FF, 4> values = { metadata, original_class_id, new_class_id, hash };
107
108 for (size_t i = 0; i < 4; i++) {
109 FF storage_slot = delayed_public_mutable_slot + i;
110 FF leaf_slot = Poseidon2::hash(
112 public_data_writes.push_back(bb::crypto::merkle_tree::PublicDataLeafValue{ leaf_slot, values[i] });
113 }
114}
115
117 std::vector<ContractInstance>& contract_instances,
118 std::vector<AztecAddress>& contract_addresses,
119 std::mt19937_64& rng)
120{
121 // Skip if no contracts to mutate
122 if (contract_classes.empty()) {
123 return;
124 }
125
126 // Select a random contract
127 size_t idx = std::uniform_int_distribution<size_t>(0, contract_classes.size() - 1)(rng);
128
129 ContractClassWithCommitment& klass = contract_classes[idx];
130
131 // We don't mutate the packed bytecode here, only some metadata fields
132 // We also do not mutate the class id directly such that it may fail.
133 // The fuzzer (and AVM) asserts that class IDs are correctly derived in class_id_derivation.cpp
134 auto choice = std::uniform_int_distribution<int>(0, 1)(rng);
135 switch (choice) {
136 case 0:
137 // Mutate artifact hash
139 break;
140 case 1:
141 // Mutate private functions root
143 break;
144 default:
145 break;
146 }
147
148 auto new_class_id = simulation::compute_contract_class_id(
150
151 for (size_t i = 0; i < contract_instances.size(); i++) {
152 // Check if an original instance (i.e. it has address nullifier) refers to this class
153 if (contract_instances[i].original_contract_class_id == klass.id) {
154 fuzz_info("Mutated original contract class ", klass.id, " at address ", contract_addresses[i]);
155 // Since this is an original instance, we also need to update the address nullifier
156 auto original_address = simulation::compute_contract_address(contract_instances[i]);
157 // Update instance's current class ID to point to the mutated class
158 contract_instances[i].current_contract_class_id = new_class_id;
159 contract_instances[i].original_contract_class_id = new_class_id;
160 auto contract_addr = std::ranges::find_if(
161 contract_addresses, [&](const AztecAddress& addr) { return addr == original_address; });
162 *contract_addr = simulation::compute_contract_address(contract_instances[i]);
163 }
164 // Check if an upgraded instance refers to this class
165 if (contract_instances[i].current_contract_class_id == klass.id) {
166 fuzz_info("Mutated current contract class ", klass.id, " at address ", contract_addresses[i]);
167 // Update instance's current class ID to point to the mutated class
168 contract_instances[i].current_contract_class_id = new_class_id;
169 // No need to update address as it depends on original class ID only
170 }
171 }
172 // Update class ID
173 klass.id = new_class_id;
174}
175
177 std::vector<AztecAddress>& contract_addresses,
178 std::mt19937_64& rng)
179{
180 // Skip if no contracts to mutate
181 if (contract_instances.empty()) {
182 return;
183 }
184
185 // Select a random contract
186 size_t idx = std::uniform_int_distribution<size_t>(0, contract_instances.size() - 1)(rng);
187
188 ContractInstance& instance = contract_instances[idx];
189 auto original_address = simulation::compute_contract_address(instance);
190
191 // We don't mutate the class IDs here, only the other fields
192 constexpr size_t num_mutable_fields = 7;
193 auto choice = std::uniform_int_distribution<int>(0, num_mutable_fields - 1)(rng);
194 switch (choice) {
195 case 0:
196 // Mutate salt
198 break;
199 case 1:
200 // Mutate deployer
202 break;
203 case 2:
204 // Mutate initialization hash
206 break;
207 case 3:
208 // Mutate nullifier key
209 mutate_point(instance.public_keys.nullifier_key, rng);
210 break;
211 case 4:
212 // Mutate incoming viewing key
213 mutate_point(instance.public_keys.incoming_viewing_key, rng);
214 break;
215 case 5:
216 // Mutate outgoing viewing key
217 mutate_point(instance.public_keys.outgoing_viewing_key, rng);
218 break;
219 case 6:
220 // Mutate tagging key
221 mutate_point(instance.public_keys.tagging_key, rng);
222 break;
223 default:
224 break;
225 }
226
228
229 // This should always find a contract address, since we are mutating an existing instance
230 auto contract_address =
231 std::ranges::find_if(contract_addresses, [&](const AztecAddress& addr) { return addr == original_address; });
232 *contract_address = new_address;
233}
234
235} // namespace bb::avm2::fuzzer
#define fuzz_info(...)
Definition constants.hpp:51
void mutate_field(bb::avm2::FF &value, std::mt19937_64 &rng, const FieldMutationConfig &config)
Definition field.cpp:99
std::shared_ptr< Napi::ThreadSafeFunction > instance
std::shared_ptr< Napi::ThreadSafeFunction > bytecode
#define DOM_SEP__PUBLIC_STORAGE_MAP_SLOT
#define UPDATED_CLASS_IDS_SLOT
#define DOM_SEP__PUBLIC_LEAF_SLOT
#define CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize)
Native Poseidon2 hash function implementation.
Definition poseidon2.hpp:22
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
static constexpr element one
Definition group.hpp:46
constexpr FieldMutationConfig BASIC_FIELD_MUTATION_CONFIGURATION
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize)
void mutate_contract_instances(std::vector< ContractInstance > &contract_instances, std::vector< AztecAddress > &contract_addresses, std::mt19937_64 &rng)
Definition bytecode.cpp:176
void mutate_contract_classes(std::vector< ContractClassWithCommitment > &contract_classes, std::vector< ContractInstance > &contract_instances, std::vector< AztecAddress > &contract_addresses, std::mt19937_64 &rng)
Definition bytecode.cpp:116
void mutate_bytecode(std::vector< ContractClassWithCommitment > &contract_classes, std::vector< ContractInstance > &contract_instances, const std::vector< AztecAddress > &contract_addresses, std::vector< bb::crypto::merkle_tree::PublicDataLeafValue > &public_data_writes, std::mt19937_64 &rng)
Definition bytecode.cpp:43
FF compute_public_bytecode_commitment(std::span< const uint8_t > bytecode)
FF compute_contract_class_id(const FF &artifact_hash, const FF &private_fn_root, const FF &public_bytecode_commitment)
FF compute_contract_address(const ContractInstance &contract_instance)
AvmFlavorSettings::FF FF
Definition field.hpp:10
AvmFlavorSettings::G1::Fq Fq
Definition field.hpp:11
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
FF original_class_id
std::vector< uint8_t > packed_bytecode