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
37
38using namespace bb::avm2::simulation;
39using namespace bb::avm2::tracegen;
40using namespace bb::avm2::constraining;
41
44using bb::avm2::FF;
48
51
52namespace {
53
54avm2::Fq random_fq_scalar(std::mt19937_64& rng)
55{
56 std::uniform_int_distribution<uint64_t> dist(0, std::numeric_limits<uint64_t>::max());
57
59 for (size_t i = 0; i < 4; ++i) {
60 limbs[i] = dist(rng);
61 }
62
63 return avm2::Fq(limbs[0], limbs[1], limbs[2], limbs[3]);
64}
65
66// Right now just mutate the address within the u32 range
67MemoryAddress mutate_memory_address(MemoryAddress addr, std::mt19937_64& rng)
68{
69 int choose_mutation = std::uniform_int_distribution<int>(0, 2)(rng);
70 switch (choose_mutation) {
71 case 0: {
72 // Mutate by fixed amount
73 std::uniform_int_distribution<int32_t> offset_dist(-1024, 1024);
74 uint32_t offset = static_cast<uint32_t>(offset_dist(rng));
75 return addr + offset;
76 } break;
77 case 1: {
78 // Random new address
79 std::uniform_int_distribution<uint32_t> dist(0, std::numeric_limits<uint32_t>::max());
80 return dist(rng);
81 }
82 default:
83 // No mutation
84 return addr;
85 }
86}
87
88} // namespace
89
93 avm2::Fq scalar = avm2::Fq::zero();
94 // Addresses are organised as:
95 // p_x, p_y, p_inf, q_x, q_y, q_inf, output_addr
96 std::array<MemoryAddress, 7> addresses{};
97 EccFuzzerInput() = default;
98
99 // Serialize to buffer
100 void to_buffer(uint8_t* buffer) const
101 {
102 size_t offset = 0;
104 offset += sizeof(AffinePoint);
106 offset += sizeof(AffinePoint);
107 avm2::Fq::serialize_to_buffer(scalar, buffer + offset);
108 offset += sizeof(avm2::Fq);
109 // Serialize memory addresses
110 std::memcpy(buffer + offset, &addresses[0], sizeof(MemoryAddress) * 7);
111 }
112
113 static EccFuzzerInput from_buffer(const uint8_t* buffer)
114 {
115 EccFuzzerInput input;
116 size_t offset = 0;
118 offset += sizeof(AffinePoint);
120 offset += sizeof(AffinePoint);
121 input.scalar = avm2::Fq::serialize_from_buffer(buffer + offset);
122 offset += sizeof(avm2::Fq);
123 // Deserialize memory addresses
124 std::memcpy(&input.addresses[0], buffer + offset, sizeof(MemoryAddress) * 7);
125
126 return input;
127 }
128};
129
130extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size, size_t max_size, unsigned int seed)
131{
132 if (size < sizeof(EccFuzzerInput)) {
133 // Initialize with default input
134 EccFuzzerInput input;
135 input.to_buffer(data);
136 return sizeof(EccFuzzerInput);
137 }
138
139 std::mt19937_64 rng(seed);
140
141 // Deserialize current input
143
144 // We want to define sensible mutation of points as random bits are unlikely to yield valid points.
145 // Lib Fuzzer will stack 5-6 mutations on top of each other by default
147 int choice = dist(rng);
148
149 switch (choice) {
150 case 0: {
151 // Set P to random valid point
152 avm2::Fq rand_scalar = random_fq_scalar(rng);
153 input.p = AffinePoint::one() * rand_scalar;
154 break;
155 }
156 case 1: {
157 // Set P to random invalid point
158 avm2::FF rand_x = FF(random_fq_scalar(rng));
159 avm2::FF rand_y = FF(random_fq_scalar(rng));
160 input.p = AffinePoint(FF(rand_x), FF(rand_y));
161 while (input.p.on_curve()) {
162 // Ensure it's invalid
163 input.p = AffinePoint(FF(rand_x + FF(1)), FF(rand_y));
164 }
165
166 break;
167 }
168 case 2: {
169 // Set P to point at infinity
170 // Note: using input.p.set_infinity() did not work here (input.p.is_point_at_infinity() == 0 afterwards)
171 input.p = AffinePoint::infinity();
172 break;
173 }
174 case 3: {
175 // Swap P and Q
176 std::swap(input.p, input.q);
177 break;
178 }
179 case 4: {
180 // Set P = -Q
181 input.p = input.q * avm2::Fq(-1);
182 break;
183 }
184 case 5: {
185 // Set scalar to random point
186 input.scalar = random_fq_scalar(rng);
187 break;
188 }
189 case 6: {
190 // Set scalar to one
191 input.scalar = avm2::Fq::one();
192 break;
193 }
194 case 7: {
195 // Mutate memory addresses
196 // Select a random address to mutate
198 size_t addr_index = addr_dist(rng);
199 input.addresses[addr_index] = mutate_memory_address(input.addresses[addr_index], rng);
200 break;
201 }
202 default:
203 break;
204 }
205
206 // Serialize mutated input back to buffer
207 input.to_buffer(data);
208
209 if (max_size > sizeof(EccFuzzerInput)) {
210 return sizeof(EccFuzzerInput);
211 }
212
213 return sizeof(EccFuzzerInput);
214}
215
216extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
217{
219
220 if (size < sizeof(EccFuzzerInput)) {
221 info("Input size too small");
222 return 0;
223 }
224
225 // Parse input
227 bool error = false;
228
229 EmbeddedCurvePoint point_p = EmbeddedCurvePoint(input.p);
230 EmbeddedCurvePoint point_q = EmbeddedCurvePoint(input.q);
231
232 // Set up gadgets and event emitters
236 EventEmitter<EccAddEvent> ecadd_emitter;
237 EventEmitter<ScalarMulEvent> scalar_mul_emitter;
238 EventEmitter<EccAddMemoryEvent> add_memory_emitter;
239 EventEmitter<ToRadixEvent> to_radix_emitter;
240 EventEmitter<ToRadixMemoryEvent> to_radix_memory_emitter;
241 EventEmitter<MemoryEvent> memory_emitter;
242
245 GreaterThan greater_than(field_gt, range_check, greater_than_emitter);
247 ToRadix to_radix(execution_id_manager, greater_than, to_radix_emitter, to_radix_memory_emitter);
248 Ecc ecc(execution_id_manager, greater_than, to_radix, ecadd_emitter, scalar_mul_emitter, add_memory_emitter);
249
250 MemoryProvider mem_provider(range_check, execution_id_manager, memory_emitter);
251 auto mem = mem_provider.make_memory(0);
252
253 mem->set(/*p_x_addr*/ input.addresses[0], MemoryValue::from_tag(MemoryTag::FF, point_p.x()));
254 mem->set(/*p_y_addr*/ input.addresses[1], MemoryValue::from_tag(MemoryTag::FF, point_p.y()));
255 mem->set(/*p_inf*/ input.addresses[2], MemoryValue::from_tag(MemoryTag::U1, point_p.is_infinity() ? FF(1) : FF(0)));
256 mem->set(/*q_x_addr*/ input.addresses[3], MemoryValue::from_tag(MemoryTag::FF, point_q.x()));
257 mem->set(/*q_y_addr*/ input.addresses[4], MemoryValue::from_tag(MemoryTag::FF, point_q.y()));
258 mem->set(/*q_inf*/ input.addresses[5], MemoryValue::from_tag(MemoryTag::U1, point_q.is_infinity() ? FF(1) : FF(0)));
259
260 EmbeddedCurvePoint scalar_mul_result;
261
262 try {
263 ecc.add(*mem, point_p, point_q, /* output_addr */ input.addresses[6]);
264 scalar_mul_result = ecc.scalar_mul(input.p, FF(uint256_t(input.scalar)));
265 } catch (std::exception& e) {
266 // info("Caught exception during ECC add: {}", e.what());
267 error = true;
268 }
269 if (!error) {
270 EmbeddedCurvePoint expected_result = point_p + point_q;
271
272 // Verify output in memory
273 MemoryValue res_x = mem->get(input.addresses[6]);
274 MemoryValue res_y = mem->get(input.addresses[6] + 1);
275 MemoryValue res_inf = mem->get(input.addresses[6] + 2);
276
277 EmbeddedCurvePoint result_point = EmbeddedCurvePoint(res_x.as_ff(), res_y.as_ff(), res_inf.as_ff() == FF(1));
278
279 BB_ASSERT(result_point.x() == expected_result.x(), "Result x-coordinate mismatch");
280 BB_ASSERT(result_point.y() == expected_result.y(), "Result y-coordinate mismatch");
281 BB_ASSERT(result_point.is_infinity() == expected_result.is_infinity(), "Result infinity flag mismatch");
282
283 // Non mem-aware ecmul result:
284 expected_result = point_p * input.scalar;
285
286 BB_ASSERT(scalar_mul_result.x() == expected_result.x(), "Mul result x-coordinate mismatch");
287 BB_ASSERT(scalar_mul_result.y() == expected_result.y(), "Mul result y-coordinate mismatch");
288 BB_ASSERT(scalar_mul_result.is_infinity() == expected_result.is_infinity(),
289 "Mul result infinity flag mismatch");
290 }
291
292 // Initialize trace container and execution trace columns
293 auto trace = TestTraceContainer({ {
294 { avm2::Column::execution_context_id, 0 },
295 // Point P
296 { avm2::Column::execution_register_0_, point_p.x() }, // = px
297 { avm2::Column::execution_register_1_, point_p.y() }, // = py
298 { avm2::Column::execution_register_2_, point_p.is_infinity() ? FF(1) : FF(0) }, // = p_inf
299 // Point Q
300 { avm2::Column::execution_register_3_, point_q.x() }, // = qx
301 { avm2::Column::execution_register_4_, point_q.y() }, // = qy
302 { avm2::Column::execution_register_5_, point_q.is_infinity() ? FF(1) : FF(0) }, // = q_inf
303 // Dst address
304 { avm2::Column::execution_rop_6_, input.addresses[6] }, // = dst_addr
305 { avm2::Column::execution_sel_exec_dispatch_ecc_add, 1 }, // = sel
306 { avm2::Column::execution_sel_opcode_error, error ? 1 : 0 }, // = sel_err
307 } });
308
313 ToRadixTraceBuilder to_radix_builder;
315
319 gt_builder.process(greater_than_emitter.dump_events(), trace);
320 to_radix_builder.process(to_radix_emitter.dump_events(), trace);
321 builder.process_add_with_memory(add_memory_emitter.dump_events(), trace);
322 builder.process_add(ecadd_emitter.dump_events(), trace);
323 builder.process_scalar_mul(scalar_mul_emitter.dump_events(), trace);
324
325 if (getenv("AVM_DEBUG") != nullptr) {
326 info("Debugging trace:");
328 debugger.run();
329 }
330
331 check_relation<ecc_rel>(trace);
332 check_relation<scalar_mul_rel>(trace);
333 check_all_interactions<EccTraceBuilder>(trace);
334 check_interaction<ExecutionTraceBuilder, bb::avm2::perm_execution_dispatch_to_ecc_add_settings>(trace);
335
336 return 0;
337}
GreaterThan greater_than
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
FieldGreaterThan field_gt
EventEmitter< simulation::FieldGreaterThanEvent > field_gt_emitter
EventEmitter< simulation::RangeCheckEvent > range_check_emitter
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 set(MemoryAddress index, MemoryValue value) override
const MemoryValue & get(MemoryAddress index) const override
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.
void process(const simulation::EventEmitterInterface< simulation::ToRadixEvent >::Container &events, TraceContainer &trace)
Processes the non-memory aware to_radix subtrace ingesting ToRadixEvent events. The populated number ...
static affine_element serialize_from_buffer(const uint8_t *buffer, bool write_x_first=false)
Restore point from a buffer.
static constexpr affine_element infinity()
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.
#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
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: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
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
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
AffinePoint p
void to_buffer(uint8_t *buffer) const
avm2::Fq scalar
EccFuzzerInput()=default
static EccFuzzerInput from_buffer(const uint8_t *buffer)
AffinePoint q
std::array< MemoryAddress, 7 > addresses