Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ecc.fuzzer.cpp
Go to the documentation of this file.
2#include <cassert>
3#include <cstdint>
4#include <fuzzer/FuzzedDataProvider.h>
5#include <random>
6
36
37using namespace bb::avm2::simulation;
38using namespace bb::avm2::tracegen;
39using namespace bb::avm2::constraining;
40
43using bb::avm2::FF;
47
49
50namespace {
51
52avm2::Fq random_fq_scalar(std::mt19937_64& rng)
53{
54 std::uniform_int_distribution<uint64_t> dist(0, std::numeric_limits<uint64_t>::max());
55
57 for (size_t i = 0; i < 4; ++i) {
58 limbs[i] = dist(rng);
59 }
60
61 return avm2::Fq(limbs[0], limbs[1], limbs[2], limbs[3]);
62}
63
64// Right now just mutate the address within the u32 range
65MemoryAddress mutate_memory_address(MemoryAddress addr, std::mt19937_64& rng)
66{
67 int choose_mutation = std::uniform_int_distribution<int>(0, 2)(rng);
68 switch (choose_mutation) {
69 case 0: {
70 // Mutate by fixed amount
71 std::uniform_int_distribution<int32_t> offset_dist(-1024, 1024);
72 uint32_t offset = static_cast<uint32_t>(offset_dist(rng));
73 return addr + offset;
74 } break;
75 case 1: {
76 // Random new address
77 std::uniform_int_distribution<uint32_t> dist(0, std::numeric_limits<uint32_t>::max());
78 return dist(rng);
79 }
80 default:
81 // No mutation
82 return addr;
83 }
84}
85
86} // namespace
87
91 // Addresses are organised as:
92 // p_x, p_y, p_inf, q_x, q_y, q_inf, output_addr
93 std::array<MemoryAddress, 7> addresses{};
94 EccFuzzerInput() = default;
95
96 // Serialize to buffer
97 void to_buffer(uint8_t* buffer) const
98 {
99 size_t offset = 0;
101 offset += sizeof(AffinePoint);
103 offset += sizeof(AffinePoint);
104 // Serialize memory addresses
105 std::memcpy(buffer + offset, &addresses[0], sizeof(MemoryAddress) * 7);
106 }
107
108 static EccFuzzerInput from_buffer(const uint8_t* buffer)
109 {
110 EccFuzzerInput input;
111 size_t offset = 0;
113 offset += sizeof(AffinePoint);
115 offset += sizeof(AffinePoint);
116 // Deserialize memory addresses
117 std::memcpy(&input.addresses[0], buffer + offset, sizeof(MemoryAddress) * 7);
118
119 return input;
120 }
121};
122
123extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size, size_t max_size, unsigned int seed)
124{
125 if (size < sizeof(EccFuzzerInput)) {
126 // Initialize with default input
127 EccFuzzerInput input;
128 input.to_buffer(data);
129 return sizeof(EccFuzzerInput);
130 }
131
132 std::mt19937_64 rng(seed);
133
134 // Deserialize current input
136
137 // We want to define sensible mutation of points as random bits are unlikely to yield valid points.
138 // Lib Fuzzer will stack 5-6 mutations on top of each other by default
140 int choice = dist(rng);
141
142 switch (choice) {
143 case 0: {
144 // Set P to random valid point
145 avm2::Fq rand_scalar = random_fq_scalar(rng);
146 input.p = AffinePoint::one() * rand_scalar;
147 break;
148 }
149 case 1: {
150 // Set P to random invalid point
151 avm2::FF rand_x = FF(random_fq_scalar(rng));
152 avm2::FF rand_y = FF(random_fq_scalar(rng));
153 input.p = AffinePoint(FF(rand_x), FF(rand_y));
154 while (input.p.on_curve()) {
155 // Ensure it's invalid
156 input.p = AffinePoint(FF(rand_x + FF(1)), FF(rand_y));
157 }
158
159 break;
160 }
161 case 2: {
162 // Set P to point at infinity
163 input.p.set_infinity();
164 break;
165 }
166 case 3: {
167 // Swap P and Q
168 std::swap(input.p, input.q);
169 break;
170 }
171 case 4: {
172 // Mutate memory addresses
173 // Select a random address to mutate
175 size_t addr_index = addr_dist(rng);
176 input.addresses[addr_index] = mutate_memory_address(input.addresses[addr_index], rng);
177 break;
178 }
179 default:
180 break;
181 }
182
183 // Serialize mutated input back to buffer
184 input.to_buffer(data);
185
186 if (max_size > sizeof(EccFuzzerInput)) {
187 return sizeof(EccFuzzerInput);
188 }
189
190 return sizeof(EccFuzzerInput);
191}
192
193extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
194{
196
197 if (size < sizeof(EccFuzzerInput)) {
198 info("Input size too small");
199 return 0;
200 }
201
202 // Parse input
204 bool error = false;
205
206 EmbeddedCurvePoint point_p =
208 EmbeddedCurvePoint point_q =
210
211 // Set up gadgets and event emitters
215 EventEmitter<EccAddEvent> ecadd_emitter;
216 EventEmitter<ScalarMulEvent> scalar_mul_emitter;
217 EventEmitter<EccAddMemoryEvent> add_memory_emitter;
218 EventEmitter<ToRadixEvent> to_radix_emitter;
219 EventEmitter<ToRadixMemoryEvent> to_radix_memory_emitter;
220 EventEmitter<MemoryEvent> memory_emitter;
221
224 GreaterThan greater_than(field_gt, range_check, greater_than_emitter);
226 ToRadix to_radix(execution_id_manager, greater_than, to_radix_emitter, to_radix_memory_emitter);
227 Ecc ecc(execution_id_manager, greater_than, to_radix, ecadd_emitter, scalar_mul_emitter, add_memory_emitter);
228
229 MemoryProvider mem_provider(range_check, execution_id_manager, memory_emitter);
230 auto mem = mem_provider.make_memory(0);
231
232 mem->set(/*p_x_addr*/ input.addresses[0], MemoryValue::from_tag(MemoryTag::FF, point_p.x()));
233 mem->set(/*p_y_addr*/ input.addresses[1], MemoryValue::from_tag(MemoryTag::FF, point_p.y()));
234 mem->set(/*p_inf*/ input.addresses[2], MemoryValue::from_tag(MemoryTag::U1, point_p.is_infinity() ? FF(1) : FF(0)));
235 mem->set(/*q_x_addr*/ input.addresses[3], MemoryValue::from_tag(MemoryTag::FF, point_q.x()));
236 mem->set(/*q_y_addr*/ input.addresses[4], MemoryValue::from_tag(MemoryTag::FF, point_q.y()));
237 mem->set(/*q_inf*/ input.addresses[5], MemoryValue::from_tag(MemoryTag::U1, point_q.is_infinity() ? FF(1) : FF(0)));
238
239 try {
240 ecc.add(*mem, input.p, input.q, /* output_addr */ input.addresses[6]);
241 } catch (std::exception& e) {
242 // info("Caught exception during ECC add: {}", e.what());
243 error = true;
244 }
245 if (!error) {
246 AffinePoint expected_result = input.p + input.q;
247
248 // Verify output in memory
249 MemoryValue res_x = mem->get(input.addresses[6]);
250 MemoryValue res_y = mem->get(input.addresses[6] + 1);
251 MemoryValue res_inf = mem->get(input.addresses[6] + 2);
252
253 EmbeddedCurvePoint result_point = EmbeddedCurvePoint(res_x.as_ff(), res_y.as_ff(), res_inf.as_ff() == FF(1));
254
255 BB_ASSERT(result_point.x() == expected_result.x, "Result x-coordinate mismatch");
256 BB_ASSERT(result_point.y() == expected_result.y, "Result y-coordinate mismatch");
257 BB_ASSERT(result_point.is_infinity() == expected_result.is_point_at_infinity(),
258 "Result infinity flag mismatch");
259 }
260
261 // Initialize trace container and execution trace columns
262 auto trace = TestTraceContainer({ {
263 { avm2::Column::execution_context_id, 0 },
264 // Point P
265 { avm2::Column::execution_register_0_, point_p.x() }, // = px
266 { avm2::Column::execution_register_1_, point_p.y() }, // = py
267 { avm2::Column::execution_register_2_, point_p.is_infinity() ? FF(1) : FF(0) }, // = p_inf
268 // Point Q
269 { avm2::Column::execution_register_3_, point_q.x() }, // = qx
270 { avm2::Column::execution_register_4_, point_q.y() }, // = qy
271 { avm2::Column::execution_register_5_, point_q.is_infinity() ? FF(1) : FF(0) }, // = q_inf
272 // Dst address
273 { avm2::Column::execution_rop_6_, input.addresses[6] }, // = dst_addr
274 { avm2::Column::execution_sel_exec_dispatch_ecc_add, 1 }, // = sel
275 { avm2::Column::execution_sel_opcode_error, error ? 1 : 0 }, // = sel_err
276 } });
277
282 ToRadixTraceBuilder to_radix_builder;
284
288 gt_builder.process(greater_than_emitter.dump_events(), trace);
289 to_radix_builder.process(to_radix_emitter.dump_events(), trace);
290 builder.process_add_with_memory(add_memory_emitter.dump_events(), trace);
291 builder.process_add(ecadd_emitter.dump_events(), trace);
292
293 if (getenv("AVM_DEBUG") != nullptr) {
294 info("Debugging trace:");
296 debugger.run();
297 }
298
299 check_relation<ecc_rel>(trace);
300 check_all_interactions<EccTraceBuilder>(trace);
301 check_interaction<ExecutionTraceBuilder, bb::avm2::perm_execution_dispatch_to_ecc_add_settings>(trace);
302
303 return 0;
304}
DeduplicatingEventEmitter< FieldGreaterThanEvent > field_gt_emitter
GreaterThan greater_than
FieldGreaterThan field_gt
DeduplicatingEventEmitter< RangeCheckEvent > range_check_emitter
#define BB_ASSERT(expression,...)
Definition assert.hpp:67
void run(uint32_t starting_row=0)
Definition debugger.cpp:76
constexpr bool is_infinity() const noexcept
constexpr const BaseField & x() const noexcept
constexpr const BaseField & y() const noexcept
static TaggedValue from_tag(ValueTag tag, FF value)
EventEmitter< Event >::Container dump_events()
std::unique_ptr< MemoryInterface > make_memory(uint16_t space_id) override
Definition memory.hpp:57
void process(const simulation::EventEmitterInterface< simulation::FieldGreaterThanEvent >::Container &events, TraceContainer &trace)
void process(const simulation::EventEmitterInterface< simulation::GreaterThanEvent >::Container &events, TraceContainer &trace)
Definition gt_trace.cpp:11
void process_misc(TraceContainer &trace, const uint32_t num_rows=MAX_AVM_TRACE_SIZE)
void process(const simulation::EventEmitterInterface< simulation::RangeCheckEvent >::Container &events, TraceContainer &trace)
void process(const simulation::EventEmitterInterface< simulation::ToRadixEvent >::Container &events, TraceContainer &trace)
bool get(std::vector< uint8_t > const &key, std::vector< uint8_t > &value)
constexpr bool is_point_at_infinity() const noexcept
static affine_element serialize_from_buffer(const uint8_t *buffer, bool write_x_first=false)
Restore point from a buffer.
constexpr bool on_curve() const noexcept
static constexpr affine_element one() noexcept
static void serialize_to_buffer(const affine_element &value, uint8_t *buffer, bool write_x_first=false)
Serialize the point to the given buffer.
constexpr affine_element set_infinity() const noexcept
void info(Args... args)
Definition log.hpp:75
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
ExecutionIdManager execution_id_manager
MemoryStore mem
const std::vector< MemoryValue > data
TestTraceContainer trace
bool expected_result
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)
ssize_t offset
Definition engine.cpp:36
uint8_t buffer[RANDOM_BUFFER_SIZE]
Definition engine.cpp:34
StandardAffinePoint< AvmFlavorSettings::EmbeddedCurve::AffineElement > EmbeddedCurvePoint
Definition field.hpp:12
AvmFlavorSettings::G1::Fq Fq
Definition field.hpp:11
uint32_t MemoryAddress
grumpkin::g1::affine_element AffinePoint
AvmFlavorSettings::FF FF
Definition field.hpp:10
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
AffinePoint p
void to_buffer(uint8_t *buffer) const
EccFuzzerInput()=default
static EccFuzzerInput from_buffer(const uint8_t *buffer)
AffinePoint q
std::array< MemoryAddress, 7 > addresses