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 = Poseidon2::hash({ FF(UPDATED_CLASS_IDS_SLOT), address });
99
100 // Build preimage
101 // todo(ilyas): make this somewhat random but also take into account the mutation on global variables.timestamp
102 FF metadata = 0; // The lower 32 bits are the timestamp_of_change, we set to 0 so it has "taken effect"
103 FF hash = Poseidon2::hash({ metadata, original_class_id, new_class_id });
104
105 std::array<FF, 4> values = { metadata, original_class_id, new_class_id, hash };
106
107 for (size_t i = 0; i < 4; i++) {
108 FF storage_slot = delayed_public_mutable_slot + i;
109 FF leaf_slot = Poseidon2::hash(
111 public_data_writes.push_back(bb::crypto::merkle_tree::PublicDataLeafValue{ leaf_slot, values[i] });
112 }
113}
114
116 std::vector<ContractInstance>& contract_instances,
117 std::vector<AztecAddress>& contract_addresses,
118 std::mt19937_64& rng)
119{
120 // Skip if no contracts to mutate
121 if (contract_classes.empty()) {
122 return;
123 }
124
125 // Select a random contract
126 size_t idx = std::uniform_int_distribution<size_t>(0, contract_classes.size() - 1)(rng);
127
128 ContractClassWithCommitment& klass = contract_classes[idx];
129
130 // We don't mutate the packed bytecode here, only some metadata fields
131 // We also do not mutate the class id directly such that it may fail.
132 // The fuzzer (and AVM) asserts that class IDs are correctly derived in class_id_derivation.cpp
133 auto choice = std::uniform_int_distribution<int>(0, 1)(rng);
134 switch (choice) {
135 case 0:
136 // Mutate artifact hash
138 break;
139 case 1:
140 // Mutate private functions root
142 break;
143 default:
144 break;
145 }
146
147 auto new_class_id = simulation::compute_contract_class_id(
149
150 for (size_t i = 0; i < contract_instances.size(); i++) {
151 // Check if an original instance (i.e. it has address nullifier) refers to this class
152 if (contract_instances[i].original_contract_class_id == klass.id) {
153 fuzz_info("Mutated original contract class ", klass.id, " at address ", contract_addresses[i]);
154 // Since this is an original instance, we also need to update the address nullifier
155 auto original_address = simulation::compute_contract_address(contract_instances[i]);
156 // Update instance's current class ID to point to the mutated class
157 contract_instances[i].current_contract_class_id = new_class_id;
158 contract_instances[i].original_contract_class_id = new_class_id;
159 auto contract_addr = std::ranges::find_if(
160 contract_addresses, [&](const AztecAddress& addr) { return addr == original_address; });
161 *contract_addr = simulation::compute_contract_address(contract_instances[i]);
162 }
163 // Check if an upgraded instance refers to this class
164 if (contract_instances[i].current_contract_class_id == klass.id) {
165 fuzz_info("Mutated current contract class ", klass.id, " at address ", contract_addresses[i]);
166 // Update instance's current class ID to point to the mutated class
167 contract_instances[i].current_contract_class_id = new_class_id;
168 // No need to update address as it depends on original class ID only
169 }
170 }
171 // Update class ID
172 klass.id = new_class_id;
173}
174
176 std::vector<AztecAddress>& contract_addresses,
177 std::mt19937_64& rng)
178{
179 // Skip if no contracts to mutate
180 if (contract_instances.empty()) {
181 return;
182 }
183
184 // Select a random contract
185 size_t idx = std::uniform_int_distribution<size_t>(0, contract_instances.size() - 1)(rng);
186
187 ContractInstance& instance = contract_instances[idx];
188 auto original_address = simulation::compute_contract_address(instance);
189
190 // We don't mutate the class IDs here, only the other fields
191 constexpr size_t num_mutable_fields = 7;
192 auto choice = std::uniform_int_distribution<int>(0, num_mutable_fields - 1)(rng);
193 switch (choice) {
194 case 0:
195 // Mutate salt
197 break;
198 case 1:
199 // Mutate deployer
201 break;
202 case 2:
203 // Mutate initialization hash
205 break;
206 case 3:
207 // Mutate nullifier key
208 mutate_point(instance.public_keys.nullifier_key, rng);
209 break;
210 case 4:
211 // Mutate incoming viewing key
212 mutate_point(instance.public_keys.incoming_viewing_key, rng);
213 break;
214 case 5:
215 // Mutate outgoing viewing key
216 mutate_point(instance.public_keys.outgoing_viewing_key, rng);
217 break;
218 case 6:
219 // Mutate tagging key
220 mutate_point(instance.public_keys.tagging_key, rng);
221 break;
222 default:
223 break;
224 }
225
227
228 // This should always find a contract address, since we are mutating an existing instance
229 auto contract_address =
230 std::ranges::find_if(contract_addresses, [&](const AztecAddress& addr) { return addr == original_address; });
231 *contract_address = new_address;
232}
233
234} // 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 UPDATED_CLASS_IDS_SLOT
#define DOM_SEP__PUBLIC_LEAF_INDEX
#define CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize)
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 hash(State &state) noexcept
void mutate_contract_instances(std::vector< ContractInstance > &contract_instances, std::vector< AztecAddress > &contract_addresses, std::mt19937_64 &rng)
Definition bytecode.cpp:175
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:115
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::G1::Fq Fq
Definition field.hpp:11
AvmFlavorSettings::FF FF
Definition field.hpp:10
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
FF original_class_id
std::vector< uint8_t > packed_bytecode