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
25
26using namespace bb::avm2::simulation;
27using namespace bb::avm2::tracegen;
28using namespace bb::avm2::constraining;
29using namespace bb::avm2::fuzzing;
30
31using bb::avm2::FF;
32
34
36 bool is_write = false;
39 uint64_t leaf_index = 0;
40 std::array<FF, 64> sibling_path;
41 FF root = 0;
42 uint64_t tree_height = 10;
43
44 uint64_t trimmed_leaf_index() const
45 {
46 // Truncate upper bits of leaf index by 2**tree_height
47 return leaf_index & ((1ULL << tree_height) - 1);
48 }
49
51 {
52 // Trim sibling path by tree height
54 }
55
57};
58
59namespace {
60
61bool predict_if_will_throw(const MerkleCheckFuzzerInput& input)
62{
63 if (static_cast<uint128_t>(input.leaf_index) > (static_cast<uint128_t>(1) << input.tree_height)) {
64 fuzz_info("Should throw: leaf index is too large, leaf index: ",
65 input.leaf_index,
66 ", tree height: ",
67 input.tree_height);
68 return true;
69 }
70
71 if (input.tree_height > 64) {
72 fuzz_info("Should throw: tree height is too large, tree height: ", input.tree_height);
73 return true;
74 }
75
76 FF expected_root =
78
79 if (expected_root != input.root) {
80 fuzz_info("Should throw: root does not match expected root");
81 return true;
82 }
83
84 return false;
85}
86
87} // namespace
88
89extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size, size_t max_size, unsigned int seed)
90{
92 try {
93 // Deserialize current input
94 msgpack::unpack((reinterpret_cast<const char*>(data)), size).get().convert(input);
95 } catch (const std::exception& e) {
96 // Leave default
97 input = {};
98 }
99
100 std::mt19937_64 rng(seed);
101
102 // Choose mutation
103 auto dist = std::uniform_int_distribution<int>(0, 6);
104
105 int choice = dist(rng);
106
107 auto random_field = [&rng]() -> FF {
108 std::uniform_int_distribution<uint64_t> dist(0, std::numeric_limits<uint64_t>::max());
109 return FF(dist(rng), dist(rng), dist(rng), dist(rng));
110 };
111
112 switch (choice) {
113 case 0: {
114 // Set correct root
115 input.root =
117
118 break;
119 }
120 case 1: {
121 // Flip is_write
122 input.is_write = !input.is_write;
123 break;
124 }
125 case 2: {
126 // Random new current value
127 input.current_value = random_field();
128 break;
129 }
130 case 3: {
131 // Swap current and new value
132 std::swap(input.current_value, input.new_value);
133 break;
134 }
135 case 4: {
136 // Modify leaf index
137 std::uniform_int_distribution<uint64_t> dist(0, std::numeric_limits<uint64_t>::max());
138 input.leaf_index = dist(rng);
139 break;
140 }
141 case 5: {
142 // Modify tree height
144 input.tree_height = dist(rng);
145 break;
146 }
147 case 6: {
148 // Update sibling path item at random index
149 std::uniform_int_distribution<size_t> dist(0, input.sibling_path.size() - 1);
150 size_t index = dist(rng);
151 input.sibling_path[index] = random_field();
152 break;
153 }
154 default:
155 break;
156 }
157
158 auto [mutated_data, mutated_data_size] = msgpack_encode_buffer(input);
159 if (mutated_data_size > max_size) {
160 delete[] mutated_data;
161 return 0;
162 }
163
164 memcpy(data, mutated_data, mutated_data_size);
165 delete[] mutated_data;
166
167 return mutated_data_size;
168}
169
170extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
171{
173 try {
174 msgpack::unpack((reinterpret_cast<const char*>(data)), size).get().convert(input);
175 } catch (const std::exception& e) {
176 // Invalid input, return early
177 return 0;
178 }
179
180 // Setup gadgets
182 // Pure since it's unused
184
185 EventEmitter<Poseidon2HashEvent> poseidon2_hash_emitter;
186 EventEmitter<Poseidon2PermutationEvent> poseidon2_perm_emitter;
187 EventEmitter<Poseidon2PermutationMemoryEvent> poseidon2_perm_mem_emitter;
189 execution_id_manager, gt, poseidon2_hash_emitter, poseidon2_perm_emitter, poseidon2_perm_mem_emitter);
190
191 EventEmitter<MerkleCheckEvent> merkle_check_emitter;
192 MerkleCheck merkle_check(poseidon2, merkle_check_emitter);
193
194 bool will_throw = predict_if_will_throw(input);
196
197 try {
198 if (input.is_write) {
199 new_root = merkle_check.write(
200 input.current_value, input.new_value, input.leaf_index, input.trimmed_sibling_path(), input.root);
201 } else {
202 merkle_check.assert_membership(
203 input.current_value, input.leaf_index, input.trimmed_sibling_path(), input.root);
204 }
205 } catch (std::exception& e) {
206 if (!will_throw) {
207 // Unexpected throw
208 throw e;
209 }
210 // We can't continue executing since the gadget threw
211 return 0;
212 }
213
214 if (will_throw) {
215 throw std::runtime_error("Expected exception was not thrown");
216 }
217
218 if (new_root.has_value()) {
219 FF expected_new_root =
221 if (new_root.value() != expected_new_root) {
222 throw std::runtime_error("New root does not match expected root");
223 }
224 }
225
226 if (poseidon2_perm_mem_emitter.get_events().size() > 0) {
227 throw std::runtime_error("Poseidon2 permutation memory events were emitted");
228 }
229
230 // Build the trace
231
232 auto trace = TestTraceContainer({ { { avm2::Column::precomputed_first_row, 1 } } });
233
234 Poseidon2TraceBuilder poseidon2_trace_builder;
235 poseidon2_trace_builder.process_permutation(poseidon2_perm_emitter.dump_events(), trace);
236 poseidon2_trace_builder.process_hash(poseidon2_hash_emitter.dump_events(), trace);
237
238 MerkleCheckTraceBuilder merkle_check_trace_builder;
239 merkle_check_trace_builder.process(merkle_check_emitter.dump_events(), trace);
240
241 if (getenv("AVM_DEBUG") != nullptr) {
242 info("Debugging trace:");
244 debugger.run();
245 }
246
247 check_relation<merkle_check_rel>(trace);
248 check_all_interactions<MerkleCheckTraceBuilder>(trace);
249
250 return 0;
251}
#define fuzz_info(...)
Definition constants.hpp:51
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)
void info(Args... args)
Definition log.hpp:89
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)
crypto::Poseidon2< crypto::Poseidon2Bn254ScalarFieldParams > poseidon2
FF unconstrained_root_from_path(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:44
std::array< FF, 64 > sibling_path
std::span< const FF > trimmed_sibling_path() const
MSGPACK_FIELDS(is_write, current_value, new_value, leaf_index, sibling_path, root, tree_height)
uint64_t trimmed_leaf_index() const