Skip to content

Commit

Permalink
feat: mvp execution hints
Browse files Browse the repository at this point in the history
  • Loading branch information
Maddiaa0 committed May 31, 2024
1 parent c402502 commit 27392a6
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 32 deletions.
28 changes: 28 additions & 0 deletions barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "barretenberg/vm/generated/avm_circuit_builder.hpp"
#include "constants.hpp"
#include <cstdint>
#include <unordered_map>

namespace bb::avm_trace {

Expand Down Expand Up @@ -35,4 +36,31 @@ static const size_t NUM_MEM_SPACES = 256;
static const uint8_t INTERNAL_CALL_SPACE_ID = 255;
static const uint32_t MAX_SIZE_INTERNAL_STACK = 1 << 16;

// TODO: find a more suitable place for these
/**
* Auxiliary hints are required when the executor is unable to work out a witness value
* These hints will be required for any opcodes that will require database information
*/
struct ExecutionHints {
// slot -> value
std::unordered_map<uint32_t, FF> storage_values;

// Note hash value -> exists
std::unordered_map<uint32_t, bool> note_hash_exists;

// Nullifier -> exists
std::unordered_map<uint32_t, bool> nullifier_exists;

// L1 to L2 message exists
std::unordered_map<uint32_t, bool> l1_to_l2_msg_exists;

ExecutionHints()
{
storage_values = std::unordered_map<uint32_t, FF>();
note_hash_exists = std::unordered_map<uint32_t, bool>();
nullifier_exists = std::unordered_map<uint32_t, bool>();
l1_to_l2_msg_exists = std::unordered_map<uint32_t, bool>();
}
};

} // namespace bb::avm_trace
Original file line number Diff line number Diff line change
Expand Up @@ -153,15 +153,16 @@ std::vector<Row> Execution::gen_trace(std::vector<Instruction> const& instructio
std::vector<Row> Execution::gen_trace(std::vector<Instruction> const& instructions,
std::vector<FF>& returndata,
std::vector<FF> const& calldata,
std::vector<FF> const& public_inputs_vec)
std::vector<FF> const& public_inputs_vec,
ExecutionHints const& execution_hints)

{
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/6718): construction of the public input columns
// should be done in the kernel - this is stubbed and underconstrained
info("before convert public inputs");
VmPublicInputs public_inputs = convert_public_inputs(public_inputs_vec);
info("after convert public inputs");
AvmTraceBuilder trace_builder(public_inputs);
AvmTraceBuilder trace_builder(public_inputs, execution_hints);

// Copied version of pc maintained in trace builder. The value of pc is evolving based
// on opcode logic and therefore is not maintained here. However, the next opcode in the execution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ class Execution {
static std::vector<Row> gen_trace(std::vector<Instruction> const& instructions,
std::vector<FF>& returndata,
std::vector<FF> const& calldata,
std::vector<FF> const& public_inputs);
std::vector<FF> const& public_inputs,
ExecutionHints const& execution_hints = {});
static std::vector<Row> gen_trace(std::vector<Instruction> const& instructions,
std::vector<FF> const& calldata = {});
static std::vector<Row> gen_trace(std::vector<Instruction> const& instructions,
std::vector<FF> const& calldata,
std::vector<FF> const& public_inputs);

static std::tuple<AvmFlavor::VerificationKey, bb::HonkProof> prove(std::vector<uint8_t> const& bytecode,
std::vector<FF> const& calldata = {});
static bool verify(AvmFlavor::VerificationKey vk, HonkProof const& proof);
Expand Down
56 changes: 47 additions & 9 deletions barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ namespace bb::avm_trace {
* @brief Constructor of a trace builder of AVM. Only serves to set the capacity of the
* underlying traces.
*/
AvmTraceBuilder::AvmTraceBuilder(VmPublicInputs public_inputs)
AvmTraceBuilder::AvmTraceBuilder(VmPublicInputs public_inputs, ExecutionHints execution_hints)
// NOTE: we initialise the environment builder here as it requires public inputs
: kernel_trace_builder(public_inputs)
, execution_hints(execution_hints)
{
main_trace.reserve(AVM_TRACE_SIZE);
}
Expand Down Expand Up @@ -1248,6 +1249,34 @@ Row AvmTraceBuilder::create_kernel_output_opcode_with_metadata(
};
}

// TODO: fix the naming here - we need it to be different as we are writing a hint
Row AvmTraceBuilder::create_sload(
uint32_t clk, uint32_t data_offset, FF const& data_value, FF const& slot_value, uint32_t slot_offset)
{
// We write the sload into memory, where the sload is an injected value that is mapped to the public inputs
mem_trace_builder.write_into_memory(
call_ptr, clk, IntermRegister::IA, data_offset, data_value, AvmMemoryTag::FF, AvmMemoryTag::FF);

return Row{
.avm_main_clk = clk,
.avm_main_ia = data_value,
.avm_main_ib = slot_value,
.avm_main_ind_a = 0,
.avm_main_ind_b = 0,
.avm_main_internal_return_ptr = internal_return_ptr,
.avm_main_mem_idx_a = data_offset,
.avm_main_mem_idx_b = slot_offset,
.avm_main_mem_op_a = 1,
.avm_main_mem_op_b = 1,
.avm_main_pc = pc++,
.avm_main_q_kernel_output_lookup = 1,
.avm_main_r_in_tag = static_cast<uint32_t>(AvmMemoryTag::FF),
.avm_main_rwa = 1,
.avm_main_rwb = 0,
.avm_main_w_in_tag = static_cast<uint32_t>(AvmMemoryTag::FF),
};
}

Row AvmTraceBuilder::create_kernel_output_opcode_with_set_metadata_output(
uint32_t clk, uint32_t data_offset, AvmMemoryTag data_r_tag, uint32_t metadata_offset, FF write_value)
{
Expand Down Expand Up @@ -1327,8 +1356,7 @@ void AvmTraceBuilder::op_l1_to_l2_msg_exists(uint32_t log_offset, uint32_t dest_
auto const clk = static_cast<uint32_t>(main_trace.size());

// TODO(https://github.com/AztecProtocol/aztec-packages/issues/6481): success or fail must come from hint - it is
// always 1 for now
uint32_t result = 1;
bool result = execution_hints.l1_to_l2_msg_exists.at(log_offset);
Row row =
create_kernel_output_opcode_with_set_metadata_output(clk, log_offset, AvmMemoryTag::FF, dest_offset, result);
kernel_trace_builder.op_l1_to_l2_msg_exists(clk, row.avm_main_ia, result);
Expand All @@ -1342,7 +1370,7 @@ void AvmTraceBuilder::op_note_hash_exists(uint32_t note_offset, uint32_t dest_of
auto const clk = static_cast<uint32_t>(main_trace.size());

// TODO(ISSUE_NUMBER): success or fail must come from hint - it is always 1 for now
uint32_t result = 1;
bool result = execution_hints.note_hash_exists.at(note_offset);
Row row =
create_kernel_output_opcode_with_set_metadata_output(clk, note_offset, AvmMemoryTag::FF, dest_offset, result);
kernel_trace_builder.op_note_hash_exists(clk, row.avm_main_ia, result);
Expand All @@ -1356,7 +1384,7 @@ void AvmTraceBuilder::op_nullifier_exists(uint32_t note_offset, uint32_t dest_of
auto const clk = static_cast<uint32_t>(main_trace.size());

// TODO(ISSUE_NUMBER): success or fail must come from hint - it is always 1 for now
uint32_t result = 1;
bool result = execution_hints.nullifier_exists.at(note_offset);
Row row =
create_kernel_output_opcode_with_set_metadata_output(clk, note_offset, AvmMemoryTag::FF, dest_offset, result);
kernel_trace_builder.op_nullifier_exists(clk, row.avm_main_ia, result);
Expand All @@ -1365,13 +1393,23 @@ void AvmTraceBuilder::op_nullifier_exists(uint32_t note_offset, uint32_t dest_of
main_trace.push_back(row);
}

void AvmTraceBuilder::op_sload(uint32_t slot_offset, uint32_t value_offset)
void AvmTraceBuilder::op_sload(uint32_t slot_offset, uint32_t write_offset)
{
auto const clk = static_cast<uint32_t>(main_trace.size());

Row row =
create_kernel_output_opcode_with_metadata(clk, value_offset, AvmMemoryTag::FF, slot_offset, AvmMemoryTag::FF);
kernel_trace_builder.op_sload(clk, row.avm_main_ib, row.avm_main_ia);
// Read the slot
AvmMemTraceBuilder::MemRead slot_read = mem_trace_builder.read_and_load_from_memory(
call_ptr, clk, IntermRegister::IB, slot_offset, AvmMemoryTag::FF, AvmMemoryTag::FF);

// Get the data value from the execution_hints
// TODO: for now the hints are being offset by the offset - this will NOT fly, but i struggled to get the hash
// working for FF
FF value = execution_hints.storage_values.at(write_offset);
// TODO: throw error if the hint does not exist

Row row = create_sload(clk, write_offset, value, slot_read.val, slot_offset);
kernel_trace_builder.op_sload(clk, row.avm_main_ib, value);

row.avm_main_sel_op_sload = FF(1);

main_trace.push_back(row);
Expand Down
9 changes: 8 additions & 1 deletion barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_trace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace bb::avm_trace {
class AvmTraceBuilder {

public:
AvmTraceBuilder(VmPublicInputs public_inputs = {});
AvmTraceBuilder(VmPublicInputs public_inputs = {}, ExecutionHints execution_hints = {});

std::vector<Row> finalize(uint32_t min_trace_size = 0, bool range_check_required = false);
void reset();
Expand Down Expand Up @@ -253,6 +253,13 @@ class AvmTraceBuilder {
0; // After a nested call, it should be initialized with MAX_SIZE_INTERNAL_STACK * call_ptr
uint8_t call_ptr = 0;

// TODO: more info
// Execution hints aid witness solving for instructions that require auxiliary information to construct
ExecutionHints execution_hints;

Row create_sload(
uint32_t clk, uint32_t data_offset, FF const& data_value, FF const& slot_value, uint32_t solt_offset);

// TODO(ilyas: #6383): Temporary way to bulk read slices
template <typename MEM, size_t T>
void read_slice_to_memory(uint8_t space_id,
Expand Down
84 changes: 84 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 @@ -1449,6 +1449,90 @@ TEST_F(AvmExecutionTests, kernelOutputEmitOpcodes)
validate_trace(std::move(trace));
}

// SLOAD and SSTORE
TEST_F(AvmExecutionTests, kernelOutputStorageOpcodes)
{
// Sload from a value that has not previously been written to will require a hint to process
std::string bytecode_hex = to_hex(OpCode::SET) + // opcode Set
"00" // Indirect flag
"03" // U32
"00000001" // value 1
"00000001" // dst_offset 1
// Cast set to field
+ to_hex(OpCode::CAST) + // opcode CAST
"00" // Indirect flag
"06" // tag field
"00000001" // dst 1
"00000001" // dst 1
+ to_hex(OpCode::EMITNOTEHASH) + // opcode EMITNOTEHASH
"00" // Indirect flag
"00000001" // src offset 1
+ to_hex(OpCode::EMITNULLIFIER) + // opcode EMITNOTEHASH
"00" // Indirect flag
"00000001" // src offset 1
+ to_hex(OpCode::EMITUNENCRYPTEDLOG) + // opcode EMITNOTEHASH
"00" // Indirect flag
"00000001" // src offset 1
+ to_hex(OpCode::RETURN) + // opcode RETURN
"00" // Indirect flag
"00000000" // ret offset 0
"00000000"; // ret size 0

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

// 2 instructions
ASSERT_THAT(instructions, SizeIs(6));

std::vector<FF> calldata = {};
std::vector<FF> reutrndata = {};

// Craft public inputs object - in this case all return values are known to be a fixed value 1
std::vector<FF> public_inputs_vec(PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH);

auto trace = Execution::gen_trace(instructions);

// CHECK EMIT NOTE HASH
// Check output data + side effect counters have been set correctly
auto emit_note_hash_row =
std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_emit_note_hash == 1; });
EXPECT_EQ(emit_note_hash_row->avm_main_ia, 1);
EXPECT_EQ(emit_note_hash_row->avm_kernel_side_effect_counter, 0);

// Get the row of the first note hash out
uint32_t emit_note_hash_out_offset = AvmKernelTraceBuilder::START_EMIT_NOTE_HASH_WRITE_OFFSET;
auto emit_note_hash_kernel_out_row = std::ranges::find_if(
trace.begin(), trace.end(), [&](Row r) { return r.avm_main_clk == emit_note_hash_out_offset; });
EXPECT_EQ(emit_note_hash_kernel_out_row->avm_kernel_kernel_value_out__is_public, 1);
EXPECT_EQ(emit_note_hash_kernel_out_row->avm_kernel_kernel_side_effect_out__is_public, 0);

// CHECK EMIT NULLIFIER
auto emit_nullifier_row =
std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_emit_nullifier == 1; });
EXPECT_EQ(emit_nullifier_row->avm_main_ia, 1);
EXPECT_EQ(emit_nullifier_row->avm_kernel_side_effect_counter, 1);

uint32_t emit_nullifier_out_offset = AvmKernelTraceBuilder::START_EMIT_NULLIFIER_WRITE_OFFSET;
auto emit_nullifier_kernel_out_row = std::ranges::find_if(
trace.begin(), trace.end(), [&](Row r) { return r.avm_main_clk == emit_nullifier_out_offset; });
EXPECT_EQ(emit_nullifier_kernel_out_row->avm_kernel_kernel_value_out__is_public, 1);
EXPECT_EQ(emit_nullifier_kernel_out_row->avm_kernel_kernel_side_effect_out__is_public, 1);

// CHECK EMIT UNENCRYPTED LOG
auto emit_log_row = std::ranges::find_if(
trace.begin(), trace.end(), [](Row r) { return r.avm_main_sel_op_emit_unencrypted_log == 1; });
EXPECT_EQ(emit_log_row->avm_main_ia, 1);
EXPECT_EQ(emit_log_row->avm_kernel_side_effect_counter, 2);

uint32_t emit_log_out_offset = AvmKernelTraceBuilder::START_EMIT_UNENCRYPTED_LOG_WRITE_OFFSET;
auto emit_log_kernel_out_row =
std::ranges::find_if(trace.begin(), trace.end(), [&](Row r) { return r.avm_main_clk == emit_log_out_offset; });
EXPECT_EQ(emit_log_kernel_out_row->avm_kernel_kernel_value_out__is_public, 1);
EXPECT_EQ(emit_log_kernel_out_row->avm_kernel_kernel_side_effect_out__is_public, 2);

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

// Negative test detecting an invalid opcode byte.
TEST_F(AvmExecutionTests, invalidOpcode)
{
Expand Down
Loading

0 comments on commit 27392a6

Please sign in to comment.