Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions avm-transpiler/src/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pub enum AvmOpcode {
POSEIDON2,
SHA256, // temp - may be removed, but alot of contracts rely on it
PEDERSEN, // temp - may be removed, but alot of contracts rely on it
ECADD,
// Conversions
TORADIXLE,
}
Expand Down Expand Up @@ -163,6 +164,7 @@ impl AvmOpcode {
AvmOpcode::POSEIDON2 => "POSEIDON2",
AvmOpcode::SHA256 => "SHA256 ",
AvmOpcode::PEDERSEN => "PEDERSEN",
AvmOpcode::ECADD => "ECADD",
// Conversions
AvmOpcode::TORADIXLE => "TORADIXLE",
}
Expand Down
24 changes: 24 additions & 0 deletions avm-transpiler/src/transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,30 @@ fn handle_black_box_function(avm_instrs: &mut Vec<AvmInstruction>, operation: &B
],
});
}
// This will be changed to utilise relative memory offsets
BlackBoxOp::EmbeddedCurveAdd {
input1_x: p1_x_offset,
input1_y: p1_y_offset,
input1_infinite: p1_infinite_offset,
input2_x: p2_x_offset,
input2_y: p2_y_offset,
input2_infinite: p2_infinite_offset,
result,
} => avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::ECADD,
// The result (SIXTH operand) is indirect.
indirect: Some(0b1000000),
Comment thread
IlyasRidhuan marked this conversation as resolved.
operands: vec![
AvmOperand::U32 { value: p1_x_offset.0 as u32 },
AvmOperand::U32 { value: p1_y_offset.0 as u32 },
AvmOperand::U32 { value: p1_infinite_offset.0 as u32 },
AvmOperand::U32 { value: p2_x_offset.0 as u32 },
AvmOperand::U32 { value: p2_y_offset.0 as u32 },
AvmOperand::U32 { value: p2_infinite_offset.0 as u32 },
AvmOperand::U32 { value: result.pointer.0 as u32 },
],
..Default::default()
}),
_ => panic!("Transpiler doesn't know how to process {:?}", operation),
}
}
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 } }, // dst_offset
// Gadget - Conversion
{ OpCode::TORADIXLE,
{ OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } },
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 @@ -664,6 +664,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_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
107 changes: 107 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 @@ -3520,6 +3520,113 @@ 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_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)
{
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);
}

mem_trace_builder.write_into_memory(
call_ptr, clk, IntermRegister::IA, direct_output_offset, result.x, AvmMemoryTag::U0, AvmMemoryTag::FF);
mem_trace_builder.write_into_memory(
call_ptr, clk, IntermRegister::IB, direct_output_offset + 1, result.y, AvmMemoryTag::U0, AvmMemoryTag::FF);
main_trace.push_back(Row{
.avm_main_clk = clk,
.avm_main_ia = result.x,
.avm_main_ib = result.y,
.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_idx_b = FF(direct_output_offset + 1),
.avm_main_mem_op_a = FF(1),
.avm_main_mem_op_b = FF(1),
.avm_main_pc = FF(pc),
.avm_main_rwa = FF(1),
.avm_main_rwb = FF(1),
.avm_main_w_in_tag = FF(static_cast<uint32_t>(AvmMemoryTag::FF)),
});
clk++;
write_slice_to_memory(call_ptr,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can directly perform an indirect write and consolidate the first slice writing with a direct write at direct_output_offset+1

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_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
33 changes: 33 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,33 @@
#include "barretenberg/vm/avm_trace/gadgets/avm_ecc.hpp"
#include "barretenberg/vm/avm_trace/avm_common.hpp"

namespace bb::avm_trace {
using element = grumpkin::g1::affine_element;

Comment thread
IlyasRidhuan marked this conversation as resolved.
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();
}

element AvmEccTraceBuilder::embedded_curve_add(element lhs, element rhs, uint32_t clk)
{
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
29 changes: 29 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,29 @@
#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
62 changes: 62 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,68 @@ 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> calldata = { a.x, a.y, b.x, b.y };
auto trace = Execution::gen_trace(instructions, returndata, calldata, public_inputs_vec);

EXPECT_EQ(returndata, expected_output);

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

// Positive test for Kernel Input opcodes
TEST_F(AvmExecutionTests, kernelInputOpcodes)
Expand Down
Loading