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 input.p.set_infinity();
171 break;
172 }
173 case 3: {
174 // Swap P and Q
175 std::swap(input.p, input.q);
176 break;
177 }
178 case 4: {
179 // Set P = -Q
180 input.p = input.q * avm2::Fq(-1);
181 break;
182 }
183 case 5: {
184 // Set scalar to random point
185 input.scalar = random_fq_scalar(rng);
186 break;
187 }
188 case 6: {
189 // Set scalar to one
190 input.scalar = avm2::Fq::one();
191 break;
192 }
193 case 7: {
194 // Mutate memory addresses
195 // Select a random address to mutate
197 size_t addr_index = addr_dist(rng);
198 input.addresses[addr_index] = mutate_memory_address(input.addresses[addr_index], rng);
199 break;
200 }
201 default:
202 break;
203 }
204
205 // Serialize mutated input back to buffer
206 input.to_buffer(data);
207
208 if (max_size > sizeof(EccFuzzerInput)) {
209 return sizeof(EccFuzzerInput);
210 }
211
212 return sizeof(EccFuzzerInput);
213}
214
215extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
216{
218
219 if (size < sizeof(EccFuzzerInput)) {
220 info("Input size too small");
221 return 0;
222 }
223
224 // Parse input
226 bool error = false;
227
228 EmbeddedCurvePoint point_p = EmbeddedCurvePoint(input.p);
229 EmbeddedCurvePoint point_q = EmbeddedCurvePoint(input.q);
230
231 // Set up gadgets and event emitters
235 EventEmitter<EccAddEvent> ecadd_emitter;
236 EventEmitter<ScalarMulEvent> scalar_mul_emitter;
237 EventEmitter<EccAddMemoryEvent> add_memory_emitter;
238 EventEmitter<ToRadixEvent> to_radix_emitter;
239 EventEmitter<ToRadixMemoryEvent> to_radix_memory_emitter;
240 EventEmitter<MemoryEvent> memory_emitter;
241
244 GreaterThan greater_than(field_gt, range_check, greater_than_emitter);
246 ToRadix to_radix(execution_id_manager, greater_than, to_radix_emitter, to_radix_memory_emitter);
247 Ecc ecc(execution_id_manager, greater_than, to_radix, ecadd_emitter, scalar_mul_emitter, add_memory_emitter);
248
249 MemoryProvider mem_provider(range_check, execution_id_manager, memory_emitter);
250 auto mem = mem_provider.make_memory(0);
251
252 mem->set(/*p_x_addr*/ input.addresses[0], MemoryValue::from_tag(MemoryTag::FF, point_p.x()));
253 mem->set(/*p_y_addr*/ input.addresses[1], MemoryValue::from_tag(MemoryTag::FF, point_p.y()));
254 mem->set(/*p_inf*/ input.addresses[2], MemoryValue::from_tag(MemoryTag::U1, point_p.is_infinity() ? FF(1) : FF(0)));
255 mem->set(/*q_x_addr*/ input.addresses[3], MemoryValue::from_tag(MemoryTag::FF, point_q.x()));
256 mem->set(/*q_y_addr*/ input.addresses[4], MemoryValue::from_tag(MemoryTag::FF, point_q.y()));
257 mem->set(/*q_inf*/ input.addresses[5], MemoryValue::from_tag(MemoryTag::U1, point_q.is_infinity() ? FF(1) : FF(0)));
258
259 EmbeddedCurvePoint scalar_mul_result;
260
261 try {
262 ecc.add(*mem, input.p, input.q, /* output_addr */ input.addresses[6]);
263 scalar_mul_result = ecc.scalar_mul(input.p, FF(uint256_t(input.scalar)));
264 } catch (std::exception& e) {
265 // info("Caught exception during ECC add: {}", e.what());
266 error = true;
267 }
268 if (!error) {
269 EmbeddedCurvePoint expected_result = point_p + point_q;
270
271 // Verify output in memory
272 MemoryValue res_x = mem->get(input.addresses[6]);
273 MemoryValue res_y = mem->get(input.addresses[6] + 1);
274 MemoryValue res_inf = mem->get(input.addresses[6] + 2);
275
276 EmbeddedCurvePoint result_point = EmbeddedCurvePoint(res_x.as_ff(), res_y.as_ff(), res_inf.as_ff() == FF(1));
277
278 BB_ASSERT(result_point.x() == expected_result.x(), "Result x-coordinate mismatch");
279 BB_ASSERT(result_point.y() == expected_result.y(), "Result y-coordinate mismatch");
280 BB_ASSERT(result_point.is_infinity() == expected_result.is_infinity(), "Result infinity flag mismatch");
281
282 // Non mem-aware ecmul result:
283 expected_result = point_p * input.scalar;
284
285 BB_ASSERT(scalar_mul_result.x() == expected_result.x(), "Mul result x-coordinate mismatch");
286 BB_ASSERT(scalar_mul_result.y() == expected_result.y(), "Mul result y-coordinate mismatch");
287 BB_ASSERT(scalar_mul_result.is_infinity() == expected_result.is_infinity(),
288 "Mul result infinity flag mismatch");
289 }
290
291 // Initialize trace container and execution trace columns
292 auto trace = TestTraceContainer({ {
293 { avm2::Column::execution_context_id, 0 },
294 // Point P
295 { avm2::Column::execution_register_0_, point_p.x() }, // = px
296 { avm2::Column::execution_register_1_, point_p.y() }, // = py
297 { avm2::Column::execution_register_2_, point_p.is_infinity() ? FF(1) : FF(0) }, // = p_inf
298 // Point Q
299 { avm2::Column::execution_register_3_, point_q.x() }, // = qx
300 { avm2::Column::execution_register_4_, point_q.y() }, // = qy
301 { avm2::Column::execution_register_5_, point_q.is_infinity() ? FF(1) : FF(0) }, // = q_inf
302 // Dst address
303 { avm2::Column::execution_rop_6_, input.addresses[6] }, // = dst_addr
304 { avm2::Column::execution_sel_exec_dispatch_ecc_add, 1 }, // = sel
305 { avm2::Column::execution_sel_opcode_error, error ? 1 : 0 }, // = sel_err
306 } });
307
312 ToRadixTraceBuilder to_radix_builder;
314
318 gt_builder.process(greater_than_emitter.dump_events(), trace);
319 to_radix_builder.process(to_radix_emitter.dump_events(), trace);
320 builder.process_add_with_memory(add_memory_emitter.dump_events(), trace);
321 builder.process_add(ecadd_emitter.dump_events(), trace);
322 builder.process_scalar_mul(scalar_mul_emitter.dump_events(), trace);
323
324 if (getenv("AVM_DEBUG") != nullptr) {
325 info("Debugging trace:");
327 debugger.run();
328 }
329
330 check_relation<ecc_rel>(trace);
331 check_relation<scalar_mul_rel>(trace);
332 check_all_interactions<EccTraceBuilder>(trace);
333 check_interaction<ExecutionTraceBuilder, bb::avm2::perm_execution_dispatch_to_ecc_add_settings>(trace);
334
335 return 0;
336}
DeduplicatingEventEmitter< FieldGreaterThanEvent > field_gt_emitter
GreaterThan greater_than
FieldGreaterThan field_gt
DeduplicatingEventEmitter< RangeCheckEvent > range_check_emitter
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
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)
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
#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: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
avm2::Fq scalar
EccFuzzerInput()=default
static EccFuzzerInput from_buffer(const uint8_t *buffer)
AffinePoint q
std::array< MemoryAddress, 7 > addresses