Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
emit_public_log.fuzzer.cpp
Go to the documentation of this file.
2#include <cassert>
3#include <cstdint>
4
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;
34
36
37const uint8_t default_log_fields = 16;
38// Set to slightly above the maximum size so we hit error_too_many_log_fields
40
44 uint32_t log_size = 0;
45 uint64_t selection_encoding = 0;
46 bool is_static = false;
48 false; // Since we generate log_size values in the test, we must pass a flag to modify their tag(s)
49
50 std::array<FF, default_log_fields> init_log_values{};
51
52 void print() const
53 {
54 info("contract_address: ", contract_address);
55 info("log_offset: ", log_offset);
56 info("log_size: ", log_size);
57 info("selection_encoding: ", selection_encoding);
58 info("is_static: ", is_static);
59 info("tag_mismatch: ", tag_mismatch);
60 for (size_t i = 0; i < init_log_values.size(); i++) {
61 info("init_log_value ", i, ": ", init_log_values[i]);
62 }
63 }
64
65 void to_buffer(uint8_t* buffer) const
66 {
67 size_t offset = 0;
69 offset += sizeof(contract_address);
71 offset += sizeof(log_offset);
73 offset += sizeof(log_size);
75 offset += sizeof(selection_encoding);
77 offset += sizeof(is_static);
79 offset += sizeof(tag_mismatch);
81 }
82
84 {
86 size_t offset = 0;
88 offset += sizeof(input.contract_address);
89 std::memcpy(&input.log_offset, buffer + offset, sizeof(input.log_offset));
90 offset += sizeof(input.log_offset);
91 std::memcpy(&input.log_size, buffer + offset, sizeof(input.log_size));
92 offset += sizeof(input.log_size);
94 offset += sizeof(input.selection_encoding);
95 std::memcpy(&input.is_static, buffer + offset, sizeof(input.is_static));
96 offset += sizeof(input.is_static);
97 std::memcpy(&input.tag_mismatch, buffer + offset, sizeof(input.tag_mismatch));
98 offset += sizeof(input.tag_mismatch);
99 std::memcpy(&input.init_log_values[0], buffer + offset, sizeof(FF) * input.init_log_values.size());
100
101 return input;
102 }
103
104 bool is_error() const
105 {
106 uint64_t end_log_address = static_cast<uint64_t>(log_offset) + static_cast<uint64_t>(log_size) - 1;
107 bool error_memory_out_of_bounds = end_log_address > AVM_HIGHEST_MEM_ADDRESS;
108 // TODO(MW): Since this fuzzer only emits one log for now, we just check its size, once it emits multiple
109 // we would need to check prev_emitted_log_fields + total_size:
110 bool error_too_many_log_fields = PUBLIC_LOG_HEADER_LENGTH + log_size > FLAT_PUBLIC_LOGS_PAYLOAD_LENGTH;
111 return error_memory_out_of_bounds || error_too_many_log_fields || tag_mismatch || is_static;
112 }
113};
114
116{
117 return { .id = context->get_context_id(),
118 .pc = context->get_pc(),
119 .contract_addr = context->get_address(),
120 .is_static = context->get_is_static(),
121 .numPublicLogFields = context->get_side_effect_tracker().get_side_effects().get_num_public_log_fields() };
122}
123
124// TODO(MW): multiple events, std::vector<std::vector<FF>>
126{
127 std::vector<FF> log_fields;
128 auto total_log_fields_size = input.log_size + PUBLIC_LOG_HEADER_LENGTH;
129 log_fields.reserve(total_log_fields_size);
130 // Assign log length and address
131 log_fields.emplace_back(input.log_size);
132 log_fields.emplace_back(input.contract_address);
133
134 size_t max_index = std::min(static_cast<size_t>(input.log_size), input.init_log_values.size());
135 // Place initial values
136 for (size_t j = 0; j < max_index; j++) {
137 log_fields.emplace_back(input.init_log_values[j]);
138 }
139 // If size > init_log_values, fill gaps
140 for (size_t j = input.init_log_values.size(); j < input.log_size; j++) {
141 // Copied from memory.fuzzer:
142 auto entry_idx = (input.selection_encoding >> j) % log_fields.size();
143 // TODO(MW): make sure to exclude size/address fields?
144 auto entry_value = log_fields[entry_idx];
145 FF modified_value = entry_value + input.init_log_values[j % input.init_log_values.size()];
146 log_fields.emplace_back(modified_value);
147 }
148 // The first two fields are size (IS_WRITE_LOG_LENGTH) and contract address (is_write_contract_address), which are
149 // not read from memory, so we start at j = PUBLIC_LOG_HEADER_LENGTH
150 MemoryAddress addr = input.log_offset;
151 for (size_t j = PUBLIC_LOG_HEADER_LENGTH; j < total_log_fields_size; j++) {
152 mem->set(addr++, MemoryValue::from(log_fields[j]));
153 }
154
155 // Choose an index to set to an incorrect tag if we are testing a mismatch
156 if (input.tag_mismatch) {
157 size_t set_incorrect_tag_at = ((input.selection_encoding >> max_index) % log_fields.size()) + 2;
158 MemoryAddress addr = static_cast<MemoryAddress>(input.log_offset + set_incorrect_tag_at - 2);
159 MemoryTag incorrect_tag = MemoryTag::FF;
160 uint64_t incr = 0;
161 while (incorrect_tag == MemoryTag::FF) {
162 // TODO(MW): use rng here?
163 incorrect_tag =
164 static_cast<MemoryTag>((static_cast<uint64_t>(incorrect_tag) + input.selection_encoding + incr++) %
165 static_cast<uint64_t>(MemoryTag::MAX));
166 }
167 mem->set(addr, MemoryValue::from_tag_truncating(incorrect_tag, log_fields[set_incorrect_tag_at]));
168 }
169
170 return log_fields;
171}
172
173extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size, size_t max_size, unsigned int seed)
174{
175 if (size < sizeof(EmitPublicLogFuzzerInput)) {
176 // Initialize with default input
178 input.to_buffer(data);
179 return sizeof(EmitPublicLogFuzzerInput);
180 }
181
182 std::mt19937_64 rng(seed);
183
184 // Deserialize current input
186
187 // Choose mutation case
189 int choice = dist(rng);
190 switch (choice) {
191 case 0: {
192 // Set contract address
193 std::uniform_int_distribution<uint64_t> addr_dist(0, std::numeric_limits<uint64_t>::max());
194 input.contract_address = FF(addr_dist(rng), addr_dist(rng), addr_dist(rng), addr_dist(rng));
195 break;
196 }
197 case 1: {
198 // Set log address
199 std::uniform_int_distribution<int> addr_change(-4000, 4000);
200 int new_addr = static_cast<int>(input.log_offset) + addr_change(rng);
201 input.log_offset = static_cast<uint32_t>(new_addr);
202 break;
203 }
204 case 2: {
205 // Set log size
207 input.log_size = num_fields_dist(rng);
208 break;
209 }
210 case 3: {
211 // Toggle selection encoding for a random entry, as long as this log is not empty
212 if (input.log_size != 0) {
213 std::uniform_int_distribution<size_t> entry_dist(0, input.log_size - 1);
214 size_t entry_idx = entry_dist(rng);
215 input.selection_encoding ^= (1ULL << entry_idx);
216 }
217 break;
218 }
219 case 4: {
220 // Modify a random initial value
221 std::uniform_int_distribution<size_t> index_dist(0, input.init_log_values.size() - 1);
222 size_t value_idx = index_dist(rng);
223 std::uniform_int_distribution<uint64_t> dist(0, std::numeric_limits<uint64_t>::max());
224 FF value = FF(dist(rng), dist(rng), dist(rng), dist(rng));
225 input.init_log_values[value_idx] = value;
226 break;
227 }
228 case 5: {
229 // Toggle error cases
230 // Note that memory out of bounds and too many log fields are already covered by other mutations
231 // TODO(MW): Add more? E.g. set incorrect log_size/log_offset in emit_public_log call?
233 int choice = err_dist(rng);
234 switch (choice) {
235 case 0: {
236 // Toggle is_static
237 input.is_static = !input.is_static;
238 break;
239 }
240 case 1: {
241 // Toggle tag_mismatch
242 input.tag_mismatch = !input.tag_mismatch;
243 break;
244 }
245 default:
246 break;
247 }
248 }
249 default:
250 break;
251 }
252
253 // Serialize mutated input back to buffer
254 input.to_buffer(data);
255
256 if (max_size > sizeof(EmitPublicLogFuzzerInput)) {
257 return sizeof(EmitPublicLogFuzzerInput);
258 }
259
260 return sizeof(EmitPublicLogFuzzerInput);
261}
262
263extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
264{
266
267 if (size < sizeof(EmitPublicLogFuzzerInput)) {
268 return 0;
269 }
270
272 bool error = false;
273
274 // Set up gadgets and event emitters
275 EventEmitter<EmitPublicLogEvent> emit_log_emitter;
276
277 GadgetFuzzerContextHelper context_helper(input.contract_address, input.is_static, 1);
278 EmitPublicLog emit_public_log(context_helper.execution_id_manager, context_helper.greater_than, emit_log_emitter);
279
280 auto context =
282
283 // TODO(MW): multiple log events
284 std::vector<FF> log_fields = generate_and_set_log_fields(input, &context->get_memory());
285
286 try {
287 emit_public_log.emit_public_log(
288 context->get_memory(), *context, input.contract_address, input.log_offset, input.log_size);
289 } catch (const EmitPublicLogException& e) {
290 // TODO(MW): Ensure error is expected
291 error = true;
292 }
293
295
300 ExecutionTraceBuilder ex_builder;
302
304
305 if (!error) {
306 // TODO(MW): use below to check values:
307 // auto public_logs = side_effect_tracker.get_side_effects().public_logs;
308 trace.set(avm2::Column::public_inputs_cols_0_, pi_row, log_fields.size());
309 trace.set(avm2::Column::public_inputs_sel, pi_row, 1);
310
311 // Set public input columns
312 for (FF log_field : log_fields) {
313 pi_row++;
314 trace.set(avm2::Column::public_inputs_sel, pi_row, 1);
315 // Logs only use cols_0
316 trace.set(avm2::Column::public_inputs_cols_0_, pi_row, log_field);
317 }
318 }
319
320 // Precomputed values
324 precomputed_builder.process_misc(trace, pi_row + 1); // Need enough for public input columns
325
326 // TODO(MW): Properly set these via calls (lookup changed to perm recently)
327 // TODO(MW): Set before_context_event.prev_num_public_log_fields in multiple calls
328 ExecutionEvent ex_event = { .wire_instruction =
329 bb::avm2::testing::InstructionBuilder(WireOpCode::EMITPUBLICLOG).build(),
330 .inputs = { MemoryValue::from<uint32_t>(input.log_size) },
331 .after_context_event = fill_context_event(context) };
332 ex_builder.process({ ex_event }, trace);
333 auto exec_log_row = trace.get_column_rows(avm2::Column::execution_sel_exec_dispatch_emit_public_log);
334 trace.set(avm2::Column::execution_rop_1_, exec_log_row - 1, input.log_offset);
335 trace.set(avm2::Column::execution_register_0_, exec_log_row - 1, input.log_size);
336 trace.set(avm2::Column::execution_sel_opcode_error, exec_log_row - 1, error ? 1 : 0);
337
338 range_check_builder.process(context_helper.range_check_emitter.dump_events(), trace);
339 field_gt_builder.process(context_helper.field_gt_emitter.dump_events(), trace);
340 gt_builder.process(context_helper.greater_than_emitter.dump_events(), trace);
341 builder.process(emit_log_emitter.dump_events(), trace);
342
343 if (getenv("AVM_DEBUG") != nullptr) {
344 info("Debugging trace:");
346 debugger.run();
347 }
348
349 check_relation<emit_log_rel>(trace);
350 check_all_interactions<EmitPublicLogTraceBuilder>(trace);
351 check_interaction<ExecutionTraceBuilder, bb::avm2::perm_execution_dispatch_to_emit_public_log_settings>(trace);
352
353 return 0;
354}
#define FLAT_PUBLIC_LOGS_PAYLOAD_LENGTH
#define PUBLIC_LOG_HEADER_LENGTH
#define AVM_HIGHEST_MEM_ADDRESS
#define AVM_PUBLIC_INPUTS_AVM_ACCUMULATED_DATA_PUBLIC_LOGS_ROW_IDX
void run(uint32_t starting_row=0)
Definition debugger.cpp:76
static TaggedValue from(T value)
static TaggedValue from_tag_truncating(ValueTag tag, FF value)
Sets up gadgets and instance managers to provide a context for fuzzing. NOTE: rudimentary set up for ...
DeduplicatingEventEmitter< GreaterThanEvent > greater_than_emitter
std::unique_ptr< simulation::ContextInterface > make_enqueued_fuzzing_context(AztecAddress address=AztecAddress(0), AztecAddress msg_sender=AztecAddress(0), bool is_static=false, FF transaction_fee=FF(0), std::span< const FF > calldata={}, Gas gas_limit=GAS_LIMIT, Gas gas_used=GAS_USED_BY_PRIVATE, TransactionPhase phase=TransactionPhase::APP_LOGIC)
DeduplicatingEventEmitter< RangeCheckEvent > range_check_emitter
DeduplicatingEventEmitter< FieldGreaterThanEvent > field_gt_emitter
void set(MemoryAddress index, MemoryValue value) override
simulation::Instruction build() const
void process(const simulation::EventEmitterInterface< simulation::AluEvent >::Container &events, TraceContainer &trace)
Process the ALU events and populate the ALU relevant columns in the trace.
void process(const simulation::EventEmitterInterface< simulation::ExecutionEvent >::Container &ex_events, TraceContainer &trace)
void process(const simulation::EventEmitterInterface< simulation::FieldGreaterThanEvent >::Container &events, TraceContainer &trace)
Processes FieldGreaterThanEvent events and generates trace rows for the ff_gt gadget.
void process(const simulation::EventEmitterInterface< simulation::GreaterThanEvent >::Container &events, TraceContainer &trace)
Process the greater-than events and populate the relevant columns in the trace.
Definition gt_trace.cpp:20
void process_misc(TraceContainer &trace, const uint32_t num_rows=PRECOMPUTED_TRACE_SIZE)
void process(const simulation::EventEmitterInterface< simulation::RangeCheckEvent >::Container &events, TraceContainer &trace)
Processes range check events and populates the trace with decomposed value columns.
uint32_t get_column_rows(Column col) const
void set(Column col, uint32_t row, const FF &value)
#define info(...)
Definition log.hpp:93
RangeCheckTraceBuilder range_check_builder
Definition alu.test.cpp:121
PrecomputedTraceBuilder precomputed_builder
Definition alu.test.cpp:120
FieldGreaterThanTraceBuilder field_gt_builder
Definition alu.test.cpp:122
AluTraceBuilder builder
Definition alu.test.cpp:124
GreaterThanTraceBuilder gt_builder
Definition alu.test.cpp:123
MemoryStore mem
const std::vector< MemoryValue > data
TestTraceContainer trace
const uint32_t max_log_fields
const uint8_t default_log_fields
ContextEvent fill_context_event(std::unique_ptr< ContextInterface > &context)
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::vector< FF > generate_and_set_log_fields(const EmitPublicLogFuzzerInput &input, MemoryInterface *mem)
ssize_t offset
Definition engine.cpp:52
std::unique_ptr< uint8_t[]> buffer
Definition engine.cpp:50
AVM range check gadget for witness generation.
AvmFlavorSettings::FF FF
Definition field.hpp:10
uint32_t MemoryAddress
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static EmitPublicLogFuzzerInput from_buffer(const uint8_t *buffer)
void to_buffer(uint8_t *buffer) const
std::array< FF, default_log_fields > init_log_values