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