Skip to content
Comment thread
MirandaWood marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -573,12 +573,10 @@ struct SUCCESSCOPY_Instruction {
struct ECADD_Instruction {
ParamRef p1_x;
ParamRef p1_y;
ParamRef p1_infinite;
ParamRef p2_x;
ParamRef p2_y;
ParamRef p2_infinite;
AddressRef result;
SERIALIZATION_FIELDS(p1_x, p1_y, p1_infinite, p2_x, p2_y, p2_infinite, result);
SERIALIZATION_FIELDS(p1_x, p1_y, p2_x, p2_y, result);
};

/// @brief POSEIDON2PERM: Perform Poseidon2 permutation on 4 FF values
Expand Down Expand Up @@ -881,8 +879,8 @@ inline std::ostream& operator<<(std::ostream& os, const FuzzInstruction& instruc
<< arg.dst_address;
},
[&](ECADD_Instruction arg) {
os << "ECADD_Instruction " << arg.p1_x << " " << arg.p1_y << " " << arg.p1_infinite << " " << arg.p2_x
<< " " << arg.p2_y << " " << arg.p2_infinite << " " << arg.result;
os << "ECADD_Instruction " << arg.p1_x << " " << arg.p1_y << " " << arg.p2_x << " " << arg.p2_y << " "
<< arg.result;
},
[&](POSEIDON2PERM_Instruction arg) {
os << "POSEIDON2PERM_Instruction " << arg.src_address << " " << arg.dst_address;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1287,40 +1287,32 @@ void ProgramBlock::process_ecadd_instruction(ECADD_Instruction instruction)
#endif
auto p1_x = memory_manager.get_resolved_address_and_operand_16(instruction.p1_x);
auto p1_y = memory_manager.get_resolved_address_and_operand_16(instruction.p1_y);
auto p1_inf = memory_manager.get_resolved_address_and_operand_16(instruction.p1_infinite);
auto p2_x = memory_manager.get_resolved_address_and_operand_16(instruction.p2_x);
auto p2_y = memory_manager.get_resolved_address_and_operand_16(instruction.p2_y);
auto p2_inf = memory_manager.get_resolved_address_and_operand_16(instruction.p2_infinite);
auto result = memory_manager.get_resolved_address_and_operand_16(instruction.result);

if (!p1_x.has_value() || !p1_y.has_value() || !p1_inf.has_value() || !p2_x.has_value() || !p2_y.has_value() ||
!p2_inf.has_value() || !result.has_value()) {
if (!p1_x.has_value() || !p1_y.has_value() || !p2_x.has_value() || !p2_y.has_value() || !result.has_value()) {
return;
}

preprocess_memory_addresses(p1_x.value().first);
preprocess_memory_addresses(p1_y.value().first);
preprocess_memory_addresses(p1_inf.value().first);
preprocess_memory_addresses(p2_x.value().first);
preprocess_memory_addresses(p2_y.value().first);
preprocess_memory_addresses(p2_inf.value().first);
preprocess_memory_addresses(result.value().first);

auto ecadd_instruction = bb::avm2::testing::InstructionBuilder(bb::avm2::WireOpCode::ECADD)
.operand(p1_x.value().second)
.operand(p1_y.value().second)
.operand(p1_inf.value().second)
.operand(p2_x.value().second)
.operand(p2_y.value().second)
.operand(p2_inf.value().second)
.operand(result.value().second)
.build();
instructions.push_back(ecadd_instruction);

// ECADD writes 3 consecutive memory locations: result_x (FF), result_y (FF), result_is_inf (U1)
// ECADD writes 2 consecutive memory locations: result_x (FF), result_y (FF)
memory_manager.set_memory_address(bb::avm2::MemoryTag::FF, result.value().first.absolute_address);
memory_manager.set_memory_address(bb::avm2::MemoryTag::FF, result.value().first.absolute_address + 1);
memory_manager.set_memory_address(bb::avm2::MemoryTag::U1, result.value().first.absolute_address + 2);
}

void ProgramBlock::process_poseidon2perm_instruction(POSEIDON2PERM_Instruction instruction)
Expand Down
34 changes: 15 additions & 19 deletions barretenberg/cpp/src/barretenberg/avm_fuzzer/harness/ecc.fuzzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ struct EccFuzzerInput {
AffinePoint q = AffinePoint::one();
Fq scalar = Fq::zero();
// Addresses are organised as:
// p_x, p_y, p_inf, q_x, q_y, q_inf, output_addr
std::array<MemoryAddress, 7> addresses{};
// p_x, p_y, q_x, q_y, output_addr
std::array<MemoryAddress, 5> addresses{};
EccFuzzerInput() = default;

// Serialize to buffer
Expand All @@ -109,7 +109,7 @@ struct EccFuzzerInput {
Fq::serialize_to_buffer(scalar, buffer + offset);
offset += sizeof(Fq);
// Serialize memory addresses
std::memcpy(buffer + offset, &addresses[0], sizeof(MemoryAddress) * 7);
std::memcpy(buffer + offset, &addresses[0], sizeof(MemoryAddress) * 5);
}

static EccFuzzerInput from_buffer(const uint8_t* buffer)
Expand Down Expand Up @@ -137,7 +137,7 @@ struct EccFuzzerInput {
input.scalar = Fq::serialize_from_buffer(buffer + offset);
offset += sizeof(Fq);
// Deserialize memory addresses
std::memcpy(&input.addresses[0], buffer + offset, sizeof(MemoryAddress) * 7);
std::memcpy(&input.addresses[0], buffer + offset, sizeof(MemoryAddress) * 5);

return input;
}
Expand Down Expand Up @@ -210,7 +210,7 @@ extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size, size_t max
case 7: {
// Mutate memory addresses
// Select a random address to mutate
std::uniform_int_distribution<size_t> addr_dist(0, 6);
std::uniform_int_distribution<size_t> addr_dist(0, 4);
size_t addr_index = addr_dist(rng);
input.addresses[addr_index] = mutate_memory_address(input.addresses[addr_index], rng);
break;
Expand Down Expand Up @@ -268,15 +268,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)

mem->set(/*p_x_addr*/ input.addresses[0], MemoryValue::from_tag(MemoryTag::FF, point_p.x()));
mem->set(/*p_y_addr*/ input.addresses[1], MemoryValue::from_tag(MemoryTag::FF, point_p.y()));
mem->set(/*p_inf*/ input.addresses[2], MemoryValue::from_tag(MemoryTag::U1, point_p.is_infinity() ? FF(1) : FF(0)));
mem->set(/*q_x_addr*/ input.addresses[3], MemoryValue::from_tag(MemoryTag::FF, point_q.x()));
mem->set(/*q_y_addr*/ input.addresses[4], MemoryValue::from_tag(MemoryTag::FF, point_q.y()));
mem->set(/*q_inf*/ input.addresses[5], MemoryValue::from_tag(MemoryTag::U1, point_q.is_infinity() ? FF(1) : FF(0)));
mem->set(/*q_x_addr*/ input.addresses[2], MemoryValue::from_tag(MemoryTag::FF, point_q.x()));
mem->set(/*q_y_addr*/ input.addresses[3], MemoryValue::from_tag(MemoryTag::FF, point_q.y()));

EmbeddedCurvePoint scalar_mul_result;

try {
ecc.add(*mem, point_p, point_q, /* output_addr */ input.addresses[6]);
ecc.add(*mem, point_p, point_q, /* output_addr */ input.addresses[4]);
scalar_mul_result = ecc.scalar_mul(input.p, FF(uint256_t(input.scalar)));
} catch (std::exception& e) {
// info("Caught exception during ECC add: {}", e.what());
Expand All @@ -286,8 +284,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
EmbeddedCurvePoint expected_result = point_p + point_q;

// Verify output in memory
MemoryValue res_x = mem->get(input.addresses[6]);
MemoryValue res_y = mem->get(input.addresses[6] + 1);
MemoryValue res_x = mem->get(input.addresses[4]);
MemoryValue res_y = mem->get(input.addresses[4] + 1);

EmbeddedCurvePoint result_point = EmbeddedCurvePoint(res_x.as_ff(), res_y.as_ff());

Expand All @@ -307,15 +305,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
auto trace = TestTraceContainer({ {
{ Column::execution_context_id, 0 },
// Point P
{ Column::execution_register_0_, point_p.x() }, // = px
{ Column::execution_register_1_, point_p.y() }, // = py
{ Column::execution_register_2_, point_p.is_infinity() ? FF(1) : FF(0) }, // = p_inf
{ Column::execution_register_0_, point_p.x() }, // = px
{ Column::execution_register_1_, point_p.y() }, // = py
// Point Q
{ Column::execution_register_3_, point_q.x() }, // = qx
{ Column::execution_register_4_, point_q.y() }, // = qy
{ Column::execution_register_5_, point_q.is_infinity() ? FF(1) : FF(0) }, // = q_inf
{ Column::execution_register_2_, point_q.x() }, // = qx
{ Column::execution_register_3_, point_q.y() }, // = qy
// Dst address
{ Column::execution_rop_6_, input.addresses[6] }, // = dst_addr
{ Column::execution_rop_4_, input.addresses[4] }, // = dst_addr
{ Column::execution_sel_exec_dispatch_ecc_add, 1 }, // = sel
{ Column::execution_sel_opcode_error, error ? 1 : 0 }, // = sel_err
} });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,17 +428,15 @@ std::vector<FuzzInstruction> InstructionMutator::generate_ecadd_instruction(std:
// Random mode: use existing memory values (may fail if not valid points on curve)
return { ECADD_Instruction{ .p1_x = generate_variable_ref(rng),
.p1_y = generate_variable_ref(rng),
.p1_infinite = generate_variable_ref(rng),
.p2_x = generate_variable_ref(rng),
.p2_y = generate_variable_ref(rng),
.p2_infinite = generate_variable_ref(rng),
.result = generate_address_ref(rng, MAX_16BIT_OPERAND) } };
}

// Backfill mode: generate valid points on the Grumpkin curve and SET them
// 6 SET instructions (2 points * 3 fields each) + 1 ECADD = 7 instructions
// 4 SET instructions (2 points * 4 fields each) + 1 ECADD = 5 instructions
std::vector<FuzzInstruction> instructions;
instructions.reserve(7);
instructions.reserve(5);

// Generate a valid point via scalar multiplication of the generator (always on curve)
auto generate_point = [&rng]() {
Expand All @@ -447,17 +445,12 @@ std::vector<FuzzInstruction> InstructionMutator::generate_ecadd_instruction(std:
};

// Generate SET instructions to backfill a point at the given addresses
auto backfill_point = [&instructions](const bb::avm2::EmbeddedCurvePoint& point,
AddressRef x_addr,
AddressRef y_addr,
AddressRef inf_addr) {
auto backfill_point = [&instructions](
const bb::avm2::EmbeddedCurvePoint& point, AddressRef x_addr, AddressRef y_addr) {
instructions.push_back(
SET_FF_Instruction{ .value_tag = bb::avm2::MemoryTag::FF, .result_address = x_addr, .value = point.x() });
instructions.push_back(
SET_FF_Instruction{ .value_tag = bb::avm2::MemoryTag::FF, .result_address = y_addr, .value = point.y() });
instructions.push_back(SET_8_Instruction{ .value_tag = bb::avm2::MemoryTag::U1,
.result_address = inf_addr,
.value = static_cast<uint8_t>(point.is_infinity() ? 1 : 0) });
};

auto p1 = generate_point();
Expand All @@ -466,20 +459,16 @@ std::vector<FuzzInstruction> InstructionMutator::generate_ecadd_instruction(std:
// Generate addresses (SET_FF uses 16-bit, SET_8 uses 8-bit operands)
AddressRef p1_x_addr = generate_address_ref(rng, MAX_16BIT_OPERAND);
AddressRef p1_y_addr = generate_address_ref(rng, MAX_16BIT_OPERAND);
AddressRef p1_inf_addr = generate_address_ref(rng, MAX_8BIT_OPERAND);
AddressRef p2_x_addr = generate_address_ref(rng, MAX_16BIT_OPERAND);
AddressRef p2_y_addr = generate_address_ref(rng, MAX_16BIT_OPERAND);
AddressRef p2_inf_addr = generate_address_ref(rng, MAX_8BIT_OPERAND);

backfill_point(p1, p1_x_addr, p1_y_addr, p1_inf_addr);
backfill_point(p2, p2_x_addr, p2_y_addr, p2_inf_addr);
backfill_point(p1, p1_x_addr, p1_y_addr);
backfill_point(p2, p2_x_addr, p2_y_addr);

instructions.push_back(ECADD_Instruction{ .p1_x = p1_x_addr,
.p1_y = p1_y_addr,
.p1_infinite = p1_inf_addr,
.p2_x = p2_x_addr,
.p2_y = p2_y_addr,
.p2_infinite = p2_inf_addr,
.result = generate_address_ref(rng, MAX_16BIT_OPERAND) });

return instructions;
Expand Down Expand Up @@ -1476,8 +1465,8 @@ void InstructionMutator::mutate_successcopy_instruction(SUCCESSCOPY_Instruction&

void InstructionMutator::mutate_ecadd_instruction(ECADD_Instruction& instruction, std::mt19937_64& rng)
{
// ECADD has 7 operands, select one to mutate
int choice = std::uniform_int_distribution<int>(0, 6)(rng);
// ECADD has 5 operands, select one to mutate
int choice = std::uniform_int_distribution<int>(0, 4)(rng);
switch (choice) {
case 0:
mutate_param_ref(instruction.p1_x, rng, MemoryTag::FF, MAX_16BIT_OPERAND);
Expand All @@ -1486,18 +1475,12 @@ void InstructionMutator::mutate_ecadd_instruction(ECADD_Instruction& instruction
mutate_param_ref(instruction.p1_y, rng, MemoryTag::FF, MAX_16BIT_OPERAND);
break;
case 2:
mutate_param_ref(instruction.p1_infinite, rng, MemoryTag::U1, MAX_16BIT_OPERAND);
break;
case 3:
mutate_param_ref(instruction.p2_x, rng, MemoryTag::FF, MAX_16BIT_OPERAND);
break;
case 4:
case 3:
mutate_param_ref(instruction.p2_y, rng, MemoryTag::FF, MAX_16BIT_OPERAND);
break;
case 5:
mutate_param_ref(instruction.p2_infinite, rng, MemoryTag::U1, MAX_16BIT_OPERAND);
break;
case 6:
case 4:
mutate_address_ref(instruction.result, rng, MAX_16BIT_OPERAND);
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const std::unordered_map<WireOpCode, std::array<uint8_t, NUM_OP_DC_SELECTORS>>&
{ WireOpCode::POSEIDON2PERM, { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ WireOpCode::SHA256COMPRESSION, { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ WireOpCode::KECCAKF1600, { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ WireOpCode::ECADD, { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ WireOpCode::ECADD, { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
// Conversions
{ WireOpCode::TORADIXBE, { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
};
Expand Down Expand Up @@ -420,7 +420,7 @@ const std::unordered_map<WireOpCode, WireInstructionSpec>& get_wire_instruction_
.op_dc_selectors = get_wire_opcode_dc_selectors().at(WireOpCode::KECCAKF1600) } },
{ WireOpCode::ECADD,
{ .exec_opcode = ExecutionOpCode::ECADD,
.size_in_bytes = 17,
.size_in_bytes = 13,
.op_dc_selectors = get_wire_opcode_dc_selectors().at(WireOpCode::ECADD) } },
// Conversions
{ WireOpCode::TORADIXBE,
Expand Down Expand Up @@ -737,14 +737,12 @@ const std::unordered_map<ExecutionOpCode, ExecInstructionSpec>& get_exec_instruc
{ .num_addresses = 2,
.gas_cost = { .opcode_gas = AVM_KECCAKF1600_BASE_L2_GAS, .base_da = 0, .dyn_l2 = 0, .dyn_da = 0 } } },
{ ExecutionOpCode::ECADD,
{ .num_addresses = 7,
{ .num_addresses = 5,
.gas_cost = { .opcode_gas = AVM_ECADD_BASE_L2_GAS, .base_da = 0, .dyn_l2 = 0, .dyn_da = 0 },
.register_info = RegisterInfo().add_inputs({ /*p_x=*/ValueTag::FF,
/*p_y=*/ValueTag::FF,
/*p_inf*/ ValueTag::U1,
/*q_x*/ ValueTag::FF,
/*q_y*/ ValueTag::FF,
/*q_inf*/ ValueTag::U1 }) } },
/*q_y*/ ValueTag::FF }) } },
{ ExecutionOpCode::TORADIXBE,
{ .num_addresses = 5,
.gas_cost = { .opcode_gas = AVM_TORADIXBE_BASE_L2_GAS,
Expand Down
Loading
Loading