Skip to content

Commit

Permalink
feat(avm): ecc
Browse files Browse the repository at this point in the history
  • Loading branch information
IlyasRidhuan committed Jun 5, 2024
1 parent a664c7a commit 03177a2
Show file tree
Hide file tree
Showing 14 changed files with 328 additions and 35 deletions.
6 changes: 3 additions & 3 deletions avm-transpiler/src/transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -844,14 +844,14 @@ fn handle_black_box_function(avm_instrs: &mut Vec<AvmInstruction>, operation: &B
result,
} => avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::ECADD,
indirect: Some(ALL_DIRECT),
indirect: Some(0b1000000),
operands: vec![
AvmOperand::U32 { value: input1_x.0 as u32 },
AvmOperand::U32 { value: input1_y.0 as u32 },
AvmOperand::U8 { value: input1_infinite.0 as u8 },
AvmOperand::U32 { value: input1_infinite.0 as u32 },
AvmOperand::U32 { value: input2_x.0 as u32 },
AvmOperand::U32 { value: input2_y.0 as u32 },
AvmOperand::U8 { value: input2_infinite.0 as u8 },
AvmOperand::U32 { value: input2_infinite.0 as u32 },
AvmOperand::U32 { value: result.pointer.0 as u32 },
],
..Default::default()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,16 @@ const std::unordered_map<OpCode, std::vector<OperandType>> OPCODE_WIRE_FORMAT =
{ OpCode::SHA256, { OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } },
{ OpCode::PEDERSEN,
{ OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } },
// TEMP ECADD without relative memory
{ OpCode::ECADD,
{ OperandType::INDIRECT,
OperandType::UINT32, // lhs.x
OperandType::UINT32, // lhs.y
OperandType::UINT32, // lhs.is_infinite
OperandType::UINT32, // rhs.x
OperandType::UINT32, // rhs.y
OperandType::UINT32, // rhs.is_infinite
OperandType::UINT32 } }, // res_offset
// Gadget - Conversion
{ OpCode::TORADIXLE,
{ OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } },
Expand Down Expand Up @@ -258,6 +268,9 @@ std::vector<Instruction> Deserialization::parse(std::vector<uint8_t> const& byte
case OperandType::TAG: {
uint8_t tag_u8 = bytecode.at(pos);
if (tag_u8 == static_cast<uint8_t>(AvmMemoryTag::U0) || tag_u8 > MAX_MEM_TAG) {
info("tag_u8: ", static_cast<int>(tag_u8));
info("pos: ", pos);
info("opcode: ", static_cast<int>(opcode));
throw_or_abort("Instruction tag is invalid at position " + std::to_string(pos) +
" value: " + std::to_string(tag_u8) + " for opcode: " + to_hex(opcode));
}
Expand Down
10 changes: 10 additions & 0 deletions barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_execution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,16 @@ std::vector<Row> Execution::gen_trace(std::vector<Instruction> const& instructio
std::get<uint32_t>(inst.operands.at(3)),
std::get<uint32_t>(inst.operands.at(4)));
break;
case OpCode::ECADD:
trace_builder.op_embedded_ec_add(std::get<uint8_t>(inst.operands.at(0)),
std::get<uint32_t>(inst.operands.at(1)),
std::get<uint32_t>(inst.operands.at(2)),
std::get<uint32_t>(inst.operands.at(3)),
std::get<uint32_t>(inst.operands.at(4)),
std::get<uint32_t>(inst.operands.at(5)),
std::get<uint32_t>(inst.operands.at(6)),
std::get<uint32_t>(inst.operands.at(7)));
break;
case OpCode::REVERT:
trace_builder.op_revert(std::get<uint8_t>(inst.operands.at(0)),
std::get<uint32_t>(inst.operands.at(1)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ static const inline std::unordered_map<OpCode, GasTableEntry> GAS_COST_TABLE = {
{ OpCode::POSEIDON2, temp_default_gas_entry },
{ OpCode::SHA256, temp_default_gas_entry },
{ OpCode::PEDERSEN, temp_default_gas_entry },
{ OpCode::ECADD, temp_default_gas_entry },

// Conversions
{ OpCode::TORADIXLE, temp_default_gas_entry },
Expand Down Expand Up @@ -146,4 +147,4 @@ class AvmGasTraceBuilder {
uint32_t remaining_da_gas = 0;
};

} // namespace bb::avm_trace
} // namespace bb::avm_trace
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ enum class OpCode : uint8_t {
POSEIDON2,
SHA256,
PEDERSEN,
ECADD,
// Conversions
TORADIXLE,
// Future Gadgets -- pending changes in noir
Expand Down
108 changes: 108 additions & 0 deletions barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3297,6 +3297,114 @@ void AvmTraceBuilder::op_pedersen_hash(uint8_t indirect,
write_slice_to_memory(
call_ptr, clk, output_offset, AvmMemoryTag::FF, AvmMemoryTag::FF, FF(internal_return_ptr), { output });
}

void AvmTraceBuilder::op_embedded_ec_add(uint8_t indirect,
uint32_t lhs_x_offset,
uint32_t lhs_y_offset,
uint32_t lhs_is_inf_offset,
uint32_t rhs_x_offset,
uint32_t rhs_y_offset,
uint32_t rhs_is_inf_offset,
uint32_t output_offset)
{
// output_offset could be indirect
auto clk = static_cast<uint32_t>(main_trace.size()) + 1;
// Load lhs point
auto lhs_x_read = mem_trace_builder.read_and_load_from_memory(
call_ptr, clk, IntermRegister::IA, lhs_x_offset, AvmMemoryTag::FF, AvmMemoryTag::U0);
auto lhs_y_read = mem_trace_builder.read_and_load_from_memory(
call_ptr, clk, IntermRegister::IB, lhs_y_offset, AvmMemoryTag::FF, AvmMemoryTag::U0);
// Load rhs point
auto rhs_x_read = mem_trace_builder.read_and_load_from_memory(
call_ptr, clk, IntermRegister::IC, rhs_x_offset, AvmMemoryTag::FF, AvmMemoryTag::U0);
auto rhs_y_read = mem_trace_builder.read_and_load_from_memory(
call_ptr, clk, IntermRegister::ID, rhs_y_offset, AvmMemoryTag::FF, AvmMemoryTag::U0);

// Save this clk time to line up with the gadget op.
auto ecc_clk = clk;
main_trace.push_back(Row{
.avm_main_clk = clk,
.avm_main_ia = lhs_x_read.val,
.avm_main_ib = lhs_y_read.val,
.avm_main_ic = rhs_x_read.val,
.avm_main_id = rhs_y_read.val,
.avm_main_internal_return_ptr = FF(internal_return_ptr),
.avm_main_mem_idx_a = FF(lhs_x_offset),
.avm_main_mem_idx_b = FF(lhs_y_offset),
.avm_main_mem_idx_c = FF(rhs_x_offset),
.avm_main_mem_idx_d = FF(rhs_y_offset),
.avm_main_mem_op_a = FF(1),
.avm_main_mem_op_b = FF(1),
.avm_main_mem_op_c = FF(1),
.avm_main_mem_op_d = FF(1),
.avm_main_pc = FF(pc++),
.avm_main_r_in_tag = FF(static_cast<uint32_t>(AvmMemoryTag::FF)),
});
clk++;
// Load the infinite bools separately since they have a different memory tag
auto lhs_is_inf_read = mem_trace_builder.read_and_load_from_memory(
call_ptr, clk, IntermRegister::IA, lhs_is_inf_offset, AvmMemoryTag::U8, AvmMemoryTag::U0);
auto rhs_is_inf_read = mem_trace_builder.read_and_load_from_memory(
call_ptr, clk, IntermRegister::IB, rhs_is_inf_offset, AvmMemoryTag::U8, AvmMemoryTag::U0);

main_trace.push_back(Row{
.avm_main_clk = clk,
.avm_main_ia = lhs_is_inf_read.val,
.avm_main_ib = rhs_is_inf_read.val,
.avm_main_internal_return_ptr = FF(internal_return_ptr),
.avm_main_mem_idx_a = FF(lhs_is_inf_offset),
.avm_main_mem_idx_b = FF(rhs_is_inf_offset),
.avm_main_mem_op_a = FF(1),
.avm_main_mem_op_b = FF(1),
.avm_main_pc = FF(pc),
.avm_main_r_in_tag = FF(static_cast<uint32_t>(AvmMemoryTag::U8)),
});
clk++;
grumpkin::g1::affine_element lhs = uint8_t(lhs_is_inf_read.val) == 1
? grumpkin::g1::affine_element::infinity()
: grumpkin::g1::affine_element{ lhs_x_read.val, lhs_y_read.val };
grumpkin::g1::affine_element rhs = uint8_t(rhs_is_inf_read.val) == 1
? grumpkin::g1::affine_element::infinity()
: grumpkin::g1::affine_element{ rhs_x_read.val, rhs_y_read.val };
auto result = ecc_trace_builder.embedded_curve_add(lhs, rhs, ecc_clk);
// Write across two lines since we have different mem_tags
uint32_t direct_output_offset = output_offset;
bool indirect_flag_output = is_operand_indirect(indirect, 6);
if (indirect_flag_output) {
auto read_ind_output =
mem_trace_builder.indirect_read_and_load_from_memory(call_ptr, clk, IndirectRegister::IND_A, output_offset);
direct_output_offset = uint32_t(read_ind_output.val);
}
auto read_output = mem_trace_builder.read_and_load_from_memory(
call_ptr, clk, IntermRegister::IA, direct_output_offset, AvmMemoryTag::U32, AvmMemoryTag::U0);
main_trace.push_back(Row{
.avm_main_clk = clk,
.avm_main_ia = read_output.val,
.avm_main_ind_a = indirect_flag_output ? FF(output_offset) : FF(0),
.avm_main_ind_op_a = FF(static_cast<uint32_t>(indirect_flag_output)),
.avm_main_internal_return_ptr = FF(internal_return_ptr),
.avm_main_mem_idx_a = FF(direct_output_offset),
.avm_main_mem_op_a = FF(1),
.avm_main_pc = FF(pc),
.avm_main_r_in_tag = FF(static_cast<uint32_t>(AvmMemoryTag::U32)),
});
clk++;
write_slice_to_memory(call_ptr,
clk,
direct_output_offset,
AvmMemoryTag::FF,
AvmMemoryTag::FF,
FF(internal_return_ptr),
{ result.x, result.y });
clk++;
write_slice_to_memory(call_ptr,
clk,
direct_output_offset + 2,
AvmMemoryTag::U8,
AvmMemoryTag::U8,
FF(internal_return_ptr),
{ result.is_point_at_infinity() });
}
// Finalise Lookup Counts
//
// For log derivative lookups, we require a column that contains the number of times each lookup is consumed
Expand Down
11 changes: 11 additions & 0 deletions barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "barretenberg/vm/avm_trace/avm_opcode.hpp"
#include "barretenberg/vm/avm_trace/constants.hpp"
#include "barretenberg/vm/avm_trace/gadgets/avm_conversion_trace.hpp"
#include "barretenberg/vm/avm_trace/gadgets/avm_ecc.hpp"
#include "barretenberg/vm/avm_trace/gadgets/avm_keccak.hpp"
#include "barretenberg/vm/avm_trace/gadgets/avm_pedersen.hpp"
#include "barretenberg/vm/avm_trace/gadgets/avm_poseidon2.hpp"
Expand Down Expand Up @@ -192,6 +193,15 @@ class AvmTraceBuilder {
uint32_t output_offset,
uint32_t input_offset,
uint32_t input_size_offset);
// Embedded EC Add - the offsets are temporary
void op_embedded_ec_add(uint8_t indirect,
uint32_t lhs_x_offset,
uint32_t lhs_y_offset,
uint32_t lhs_is_inf_offset,
uint32_t rhs_x_offset,
uint32_t rhs_y_offset,
uint32_t rhs_is_inf_offset,
uint32_t output_offset);

private:
// Used for the standard indirect address resolution of three operands opcode.
Expand All @@ -217,6 +227,7 @@ class AvmTraceBuilder {
AvmPoseidon2TraceBuilder poseidon2_trace_builder;
AvmKeccakTraceBuilder keccak_trace_builder;
AvmPedersenTraceBuilder pedersen_trace_builder;
AvmEccTraceBuilder ecc_trace_builder;

/**
* @brief Create a kernel lookup opcode object
Expand Down
35 changes: 35 additions & 0 deletions barretenberg/cpp/src/barretenberg/vm/avm_trace/gadgets/avm_ecc.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

#include "barretenberg/vm/avm_trace/gadgets/avm_ecc.hpp"
#include "barretenberg/vm/avm_trace/avm_common.hpp"

namespace bb::avm_trace {

AvmEccTraceBuilder::AvmEccTraceBuilder()
{
ecc_trace.reserve(AVM_TRACE_SIZE);
}

std::vector<AvmEccTraceBuilder::EccTraceEntry> AvmEccTraceBuilder::finalize()
{
return std::move(ecc_trace);
}

void AvmEccTraceBuilder::reset()
{
ecc_trace.clear();
}

grumpkin::g1::affine_element AvmEccTraceBuilder::embedded_curve_add(grumpkin::g1::affine_element lhs,
grumpkin::g1::affine_element rhs,
uint32_t clk)
{
grumpkin::g1::affine_element result = lhs + rhs;
std::tuple<FF, FF, bool> p1 = { lhs.x, lhs.y, lhs.is_point_at_infinity() };
std::tuple<FF, FF, bool> p2 = { rhs.x, rhs.y, rhs.is_point_at_infinity() };
std::tuple<FF, FF, bool> result_tuple = { result.x, result.y, result.is_point_at_infinity() };
ecc_trace.push_back({ clk, p1, p2, result_tuple });

return result;
}

} // namespace bb::avm_trace
30 changes: 30 additions & 0 deletions barretenberg/cpp/src/barretenberg/vm/avm_trace/gadgets/avm_ecc.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

#pragma once

#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp"
#include "barretenberg/ecc/groups/affine_element.hpp"
#include "barretenberg/vm/avm_trace/avm_common.hpp"

namespace bb::avm_trace {
class AvmEccTraceBuilder {
public:
struct EccTraceEntry {
uint32_t clk = 0;
std::tuple<FF, FF, bool> p1; // x, y, is_infinity
std::tuple<FF, FF, bool> p2;
std::tuple<FF, FF, bool> result;
};

AvmEccTraceBuilder();
void reset();
// Finalize the trace
std::vector<EccTraceEntry> finalize();
grumpkin::g1::affine_element embedded_curve_add(grumpkin::g1::affine_element lhs,
grumpkin::g1::affine_element rhs,
uint32_t clk);

private:
std::vector<EccTraceEntry> ecc_trace;
};

} // namespace bb::avm_trace
71 changes: 71 additions & 0 deletions barretenberg/cpp/src/barretenberg/vm/tests/avm_execution.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1331,6 +1331,77 @@ TEST_F(AvmExecutionTests, pedersenHashOpCode)

validate_trace(std::move(trace));
}
//
// Positive test with EmbeddedCurveAdd
TEST_F(AvmExecutionTests, embeddedCurveAddOpCode)
{

// TODO: Look for hardcoded test vectors since bb is missing them
grumpkin::g1::affine_element a = grumpkin::g1::affine_element::random_element();
auto a_is_inf = a.is_point_at_infinity();
grumpkin::g1::affine_element b = grumpkin::g1::affine_element::random_element();
auto b_is_inf = b.is_point_at_infinity();
grumpkin::g1::affine_element res = a + b;
auto expected_output = std::vector<FF>{ res.x, res.y, res.is_point_at_infinity() };
std::string bytecode_hex = to_hex(OpCode::CALLDATACOPY) + // Calldatacopy
"00" // Indirect flag
"00000000" // cd_offset
"00000002" // copy_size
"00000000" // dst_offset
+ to_hex(OpCode::SET) + // opcode SET for direct src_length
"00" // Indirect flag
"01" // U8
+ to_hex<uint8_t>(a_is_inf) + //
"00000002" // dst_offset
+ to_hex(OpCode::CALLDATACOPY) + // calldatacopy
"00" // Indirect flag
"00000002" // cd_offset
"00000002" // copy_size
"00000003" // dst_offset
+ to_hex(OpCode::SET) + // opcode SET for direct src_length
"00" // Indirect flag
"01" // U32
+ to_hex<uint8_t>(b_is_inf) + // value 2
"00000005" // dst_offset
+ to_hex(OpCode::SET) + // opcode SET for direct src_length
"00" // Indirect flag
"03" // U32
"00000007" // value
"00000006" // dst_offset
+ to_hex(OpCode::ECADD) + // opcode ECADD
"40" // Indirect flag ( sixth operand indirect)
"00000000" // hash_index offset (direct)
"00000001" // dest offset (direct)
"00000002" // input offset (indirect)
"00000003" // length offset (direct)
"00000004" // length offset (direct)
"00000005" // length offset (direct)
"00000006" // length offset (direct)
+ to_hex(OpCode::RETURN) + // opcode RETURN
"00" // Indirect flag
"00000007" // ret offset 3
"00000003"; // ret size 1

auto bytecode = hex_to_bytes(bytecode_hex);
auto instructions = Deserialization::parse(bytecode);

// Assign a vector that we will mutate internally in gen_trace to store the return values;
std::vector<FF> returndata = std::vector<FF>();
std::vector<FF> calldata = { a.x, a.y, b.x, b.y };
auto trace = Execution::gen_trace(instructions, returndata, calldata, public_inputs_vec);

// Find the first row enabling the pedersen selector
// auto row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_pedersen == 1;
// }); EXPECT_EQ(row->avm_main_ind_a, 4); // Register A is indirect EXPECT_EQ(row->avm_main_mem_idx_a, 0); //
// Indirect(4) -> 1 EXPECT_EQ(row->avm_main_ia, 1); // The first input
// // The second row loads the U32 values
// std::advance(row, 1);
// EXPECT_EQ(row->avm_main_ia, 2); // Input length is 2
// EXPECT_EQ(row->avm_main_ib, 5); // Hash offset is 5
EXPECT_EQ(returndata, expected_output);

validate_trace(std::move(trace));
}

// Positive test for Kernel Input opcodes
TEST_F(AvmExecutionTests, kernelInputOpcodes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,7 @@ contract AvmTest {
#[aztec(public)]
fn elliptic_curve_add_and_double() -> EmbeddedCurvePoint {
let g = EmbeddedCurvePoint { x: 1, y: 17631683881184975370165255887551781615748388533673675138860, is_infinite: false };

let doubled = g + g;
let added = g + doubled;
added
g + g
}

/************************************************************************
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/bb-prover/src/avm_proving.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ describe('AVM WitGen, proof generation and verification', () => {
TIMEOUT,
);

// TODO: requires revert
// TODO: requires waiting for memory to change to u32
// it("Should prove to radix",
// async () => {
// await proveAndVerifyAvmTestContract('to_radix_le', [new Fr(10)]);
Expand Down
Loading

0 comments on commit 03177a2

Please sign in to comment.