Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
merkle_check.fuzzer.cpp
Go to the documentation of this file.
2
3#include <cassert>
4#include <cstdint>
5#include <fuzzer/FuzzedDataProvider.h>
6
26
27using namespace bb::avm2::simulation;
28using namespace bb::avm2::tracegen;
29using namespace bb::avm2::constraining;
30using namespace bb::avm2::fuzzing;
31
32using bb::avm2::FF;
33
35
37 bool is_write = false;
40 uint64_t leaf_index = 0;
41 std::array<FF, 64> sibling_path;
42 FF root = 0;
43 uint64_t tree_height = 10;
45
46 uint64_t trimmed_leaf_index() const
47 {
48 // Truncate upper bits of leaf index by 2**tree_height
49 return leaf_index & ((1ULL << tree_height) - 1);
50 }
51
53 {
54 // Trim sibling path by tree height
56 }
57
60};
61
62namespace {
63
64bool predict_if_will_throw(const MerkleCheckFuzzerInput& input)
65{
66 if (static_cast<uint128_t>(input.leaf_index) > (static_cast<uint128_t>(1) << input.tree_height)) {
67 fuzz_info("Should throw: leaf index is too large, leaf index: ",
68 input.leaf_index,
69 ", tree height: ",
70 input.tree_height);
71 return true;
72 }
73
74 if (input.tree_height > 64) {
75 fuzz_info("Should throw: tree height is too large, tree height: ", input.tree_height);
76 return true;
77 }
78
79 FF expected_root = unconstrained_root_from_path(
81
82 if (expected_root != input.root) {
83 fuzz_info("Should throw: root does not match expected root");
84 return true;
85 }
86
87 return false;
88}
89
90} // namespace
91
92extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size, size_t max_size, unsigned int seed)
93{
95 try {
96 // Deserialize current input
97 msgpack::unpack((reinterpret_cast<const char*>(data)), size).get().convert(input);
98 } catch (const std::exception& e) {
99 // Leave default
100 input = {};
101 }
102
103 std::mt19937_64 rng(seed);
104
105 // Choose mutation
106 auto dist = std::uniform_int_distribution<int>(0, 6);
107
108 int choice = dist(rng);
109
110 auto random_field = [&rng]() -> FF {
111 std::uniform_int_distribution<uint64_t> dist(0, std::numeric_limits<uint64_t>::max());
112 return FF(dist(rng), dist(rng), dist(rng), dist(rng));
113 };
114
115 switch (choice) {
116 case 0: {
117 // Set correct root
120
121 break;
122 }
123 case 1: {
124 // Flip is_write
125 input.is_write = !input.is_write;
126 break;
127 }
128 case 2: {
129 // Random new current value
130 input.current_value = random_field();
131 break;
132 }
133 case 3: {
134 // Swap current and new value
135 std::swap(input.current_value, input.new_value);
136 break;
137 }
138 case 4: {
139 // Modify leaf index
140 std::uniform_int_distribution<uint64_t> dist(0, std::numeric_limits<uint64_t>::max());
141 input.leaf_index = dist(rng);
142 break;
143 }
144 case 5: {
145 // Modify tree height
147 input.tree_height = dist(rng);
148 break;
149 }
150 case 6: {
151 // Update sibling path item at random index
152 std::uniform_int_distribution<size_t> dist(0, input.sibling_path.size() - 1);
153 size_t index = dist(rng);
154 input.sibling_path[index] = random_field();
155 break;
156 }
157 default:
158 break;
159 }
160
161 auto [mutated_data, mutated_data_size] = msgpack_encode_buffer(input);
162 if (mutated_data_size > max_size) {
163 delete[] mutated_data;
164 return 0;
165 }
166
167 memcpy(data, mutated_data, mutated_data_size);
168 delete[] mutated_data;
169
170 return mutated_data_size;
171}
172
173extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
174{
176 try {
177 msgpack::unpack((reinterpret_cast<const char*>(data)), size).get().convert(input);
178 } catch (const std::exception& e) {
179 // Invalid input, return early
180 return 0;
181 }
182
183 // Setup gadgets
185 // Pure since it's unused
187
188 EventEmitter<Poseidon2HashEvent> poseidon2_hash_emitter;
189 EventEmitter<Poseidon2PermutationEvent> poseidon2_perm_emitter;
190 EventEmitter<Poseidon2PermutationMemoryEvent> poseidon2_perm_mem_emitter;
192 execution_id_manager, gt, poseidon2_hash_emitter, poseidon2_perm_emitter, poseidon2_perm_mem_emitter);
193
196
197 bool will_throw = predict_if_will_throw(input);
199
200 try {
201 if (input.is_write) {
202 new_root = merkle_check.write(input.domain_separator,
203 input.current_value,
204 input.new_value,
205 input.leaf_index,
206 input.trimmed_sibling_path(),
207 input.root);
208 } else {
209 merkle_check.assert_membership(input.domain_separator,
210 input.current_value,
211 input.leaf_index,
212 input.trimmed_sibling_path(),
213 input.root);
214 }
215 } catch (std::exception& e) {
216 if (!will_throw) {
217 // Unexpected throw
218 throw e;
219 }
220 // We can't continue executing since the gadget threw
221 return 0;
222 }
223
224 if (will_throw) {
225 throw std::runtime_error("Expected exception was not thrown");
226 }
227
228 if (new_root.has_value()) {
229 FF expected_new_root = unconstrained_root_from_path(
231 if (new_root.value() != expected_new_root) {
232 throw std::runtime_error("New root does not match expected root");
233 }
234 }
235
236 if (poseidon2_perm_mem_emitter.get_events().size() > 0) {
237 throw std::runtime_error("Poseidon2 permutation memory events were emitted");
238 }
239
240 // Build the trace
241
242 auto trace = TestTraceContainer({ { { avm2::Column::precomputed_first_row, 1 } } });
243
244 Poseidon2TraceBuilder poseidon2_trace_builder;
245 poseidon2_trace_builder.process_permutation(poseidon2_perm_emitter.dump_events(), trace);
246 poseidon2_trace_builder.process_hash(poseidon2_hash_emitter.dump_events(), trace);
247
248 MerkleCheckTraceBuilder merkle_check_trace_builder;
249 merkle_check_trace_builder.process(merkle_check_emitter.dump_events(), trace);
250
251 if (getenv("AVM_DEBUG") != nullptr) {
252 info("Debugging trace:");
254 debugger.run();
255 }
256
257 check_relation<merkle_check_rel>(trace);
258 check_all_interactions<MerkleCheckTraceBuilder>(trace);
259
260 return 0;
261}
#define fuzz_info(...)
Definition constants.hpp:51
#define DOM_SEP__MERKLE_HASH
EventEmitter< simulation::MerkleCheckEvent > merkle_check_emitter
void run(uint32_t starting_row=0)
Definition debugger.cpp:76
const Container & get_events() const
void process(const simulation::EventEmitterInterface< simulation::MerkleCheckEvent >::Container &events, TraceContainer &trace)
Trace generation for the MerkleCheck gadget. It handles both READ and WRITE MerkleCheck events....
void process_permutation(const simulation::EventEmitterInterface< simulation::Poseidon2PermutationEvent >::Container &perm_events, TraceContainer &trace)
Processes the permutation events for the Poseidon2 permutation function. It populates the columns for...
Native Poseidon2 hash function implementation.
Definition poseidon2.hpp:22
#define info(...)
Definition log.hpp:93
ExecutionIdManager execution_id_manager
const std::vector< MemoryValue > data
TestTraceContainer trace
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t max_size, unsigned int seed)
std::pair< uint8_t *, size_t > msgpack_encode_buffer(auto &&obj, uint8_t *scratch_buf=nullptr, size_t scratch_size=0)
AVM range check gadget for witness generation.
crypto::Poseidon2< crypto::Poseidon2Bn254ScalarFieldParams > poseidon2
FF unconstrained_root_from_path(uint64_t domain_separator, const FF &leaf_value, const uint64_t leaf_index, std::span< const FF > path)
Definition merkle.cpp:12
AvmFlavorSettings::FF FF
Definition field.hpp:10
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
unsigned __int128 uint128_t
Definition serialize.hpp:45
std::array< FF, 64 > sibling_path
std::span< const FF > trimmed_sibling_path() const
SERIALIZATION_FIELDS(is_write, current_value, new_value, leaf_index, sibling_path, root, tree_height, domain_separator)
uint64_t trimmed_leaf_index() const