Skip to content

Commit

Permalink
3791 - bytecode parsing and proof generation
Browse files Browse the repository at this point in the history
  • Loading branch information
jeanmon committed Jan 23, 2024
1 parent 6dac650 commit e392ef5
Show file tree
Hide file tree
Showing 8 changed files with 536 additions and 3 deletions.
225 changes: 225 additions & 0 deletions barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_execution.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
#include "AvmMini_execution.hpp"
#include "barretenberg/common/serialize.hpp"
#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp"
#include "barretenberg/vm/avm_trace/AvmMini_common.hpp"
#include "barretenberg/vm/avm_trace/AvmMini_instructions.hpp"
#include "barretenberg/vm/avm_trace/AvmMini_opcode.hpp"
#include "barretenberg/vm/avm_trace/AvmMini_trace.hpp"
#include "barretenberg/vm/generated/AvmMini_composer.hpp"
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>

namespace avm_trace {

/**
* @brief Run the bytecode, generate the corresponding execution trace and prove the correctness
* of the execution of the supplied bytecode.
*
* @param bytecode A vector of bytes representing the bytecode to execute.
* @param calldata expressed as a vector of finite field elements.
* @throws runtime_error exception when the bytecode is invalid.
* @return A zk proof of the execution.
*/
plonk::proof Execution::run_and_prove(std::vector<uint8_t> const& bytecode, std::vector<FF> const& calldata)
{
auto instructions = parse(bytecode);
auto trace = gen_trace(instructions, calldata);
auto circuit_builder = bb::AvmMiniCircuitBuilder();
circuit_builder.set_trace(std::move(trace));

auto composer = bb::honk::AvmMiniComposer();
auto prover = composer.create_prover(circuit_builder);
return prover.construct_proof();
}

/**
* @brief Parsing of the supplied bytecode into a vector of instructions. It essentially
* checks that each opcode value is in the defined range and extracts the operands
* for each opcode.
*
* @param bytecode The bytecode to be parsed as a vector of bytes/uint8_t
* @throws runtime_error exception when the bytecode is invalid.
* @return Vector of instructions
*/
std::vector<Instruction> Execution::parse(std::vector<uint8_t> const& bytecode)
{
std::vector<Instruction> instructions;
size_t pos = 0;
const auto length = bytecode.size();

static_assert(sizeof(uint32_t) / sizeof(uint8_t) == AVM_OPERAND_BYTE_LENGTH);

while (pos < length) {
const uint8_t opcode_byte = bytecode.at(pos);
pos += AVM_OPCODE_BYTE_LENGTH;

if (!Bytecode::is_valid(opcode_byte)) {
throw std::runtime_error("Invalid opcode byte: " + std::to_string(opcode_byte));
}

const auto opcode = static_cast<OpCode>(opcode_byte);
auto in_tag_u8 = static_cast<uint8_t>(AvmMemoryTag::U0);

if (Bytecode::has_in_tag(opcode)) {
in_tag_u8 = bytecode.at(pos);
if (in_tag_u8 == static_cast<uint8_t>(AvmMemoryTag::U0) || in_tag_u8 == 7) {
throw std::runtime_error("Instruction tag is invalid at position " + std::to_string(pos) +
" value: " + std::to_string(in_tag_u8));
}
pos += AVM_IN_TAG_BYTE_LENGTH;
}

auto const in_tag = static_cast<AvmMemoryTag>(in_tag_u8);
std::vector<uint32_t> operands{};
size_t num_of_operands{};
size_t operands_size{};

if (opcode == OpCode::SET) {
switch (in_tag) {
case AvmMemoryTag::U8:
num_of_operands = 2;
operands_size = 5;
break;
case AvmMemoryTag::U16:
num_of_operands = 2;
operands_size = 6;
break;
case AvmMemoryTag::U32:
num_of_operands = 2;
operands_size = 8;
break;
case AvmMemoryTag::U64:
num_of_operands = 3;
operands_size = 12;
break;
case AvmMemoryTag::U128:
num_of_operands = 5;
operands_size = 20;
break;
default:
throw std::runtime_error("Instruction tag for SET opcode is invalid at position " +
std::to_string(pos) + " value: " + std::to_string(in_tag_u8));
break;
}
} else {
num_of_operands = Bytecode::OPERANDS_NUM.at(opcode);
operands_size = AVM_OPERAND_BYTE_LENGTH * num_of_operands;
}

if (pos + operands_size > length) {
throw std::runtime_error("Bytecode does not contain enough bytes for operands at position " +
std::to_string(pos));
}

if (opcode == OpCode::SET && in_tag == AvmMemoryTag::U8) {
operands.push_back(static_cast<uint32_t>(bytecode.at(pos)));
uint8_t const* ptr = &bytecode.at(pos + 1);
uint32_t operand{};
serialize::read(ptr, operand);
operands.push_back(operand);
pos += operands_size;
} else if (opcode == OpCode::SET && in_tag == AvmMemoryTag::U16) {
uint8_t const* ptr = &bytecode.at(pos);
uint16_t operand{};
serialize::read(ptr, operand);
operands.push_back(static_cast<uint32_t>(operand));
ptr = &bytecode.at(pos + 2);
uint32_t operand2{};
serialize::read(ptr, operand2);
operands.push_back(operand2);
pos += operands_size;
} else {
for (size_t i = 0; i < num_of_operands; i++) {
uint8_t const* ptr = &bytecode.at(pos);
uint32_t operand{};
serialize::read(ptr, operand);
operands.push_back(operand);
pos += AVM_OPERAND_BYTE_LENGTH;
}
}

instructions.emplace_back(opcode, operands, static_cast<AvmMemoryTag>(in_tag));
}

return instructions;
}

/**
* @brief Generate the execution trace pertaining to the supplied instructions.
*
* @param instructions A vector of the instructions to be executed.
* @param calldata expressed as a vector of finite field elements.
* @return The trace as a vector of Row.
*/
std::vector<Row> Execution::gen_trace(std::vector<Instruction> const& instructions, std::vector<FF> const& calldata)
{
AvmMiniTraceBuilder trace_builder{};

for (auto const& inst : instructions) {
switch (inst.op_code) {
case OpCode::ADD:
trace_builder.add(inst.operands.at(0), inst.operands.at(1), inst.operands.at(2), inst.in_tag);
break;
case OpCode::SUB:
trace_builder.sub(inst.operands.at(0), inst.operands.at(1), inst.operands.at(2), inst.in_tag);
break;
case OpCode::MUL:
trace_builder.mul(inst.operands.at(0), inst.operands.at(1), inst.operands.at(2), inst.in_tag);
break;
case OpCode::DIV:
trace_builder.div(inst.operands.at(0), inst.operands.at(1), inst.operands.at(2), inst.in_tag);
break;
case OpCode::CALLDATACOPY:
trace_builder.call_data_copy(inst.operands.at(0), inst.operands.at(1), inst.operands.at(2), calldata);
break;
case OpCode::JUMP:
trace_builder.jump(inst.operands.at(0));
break;
case OpCode::INTERNALCALL:
trace_builder.internal_call(inst.operands.at(0));
break;
case OpCode::INTERNALRETURN:
trace_builder.internal_return();
break;
case OpCode::SET: {
uint32_t dst_offset{};
uint128_t val{};
switch (inst.in_tag) {
case AvmMemoryTag::U8:
case AvmMemoryTag::U16:
case AvmMemoryTag::U32:
val = inst.operands.at(0);
dst_offset = inst.operands.at(1);
break;
case AvmMemoryTag::U64:
val = inst.operands.at(0);
val <<= 32;
val += inst.operands.at(1);
dst_offset = inst.operands.at(2);
break;
case AvmMemoryTag::U128:
for (size_t i = 0; i < 4; i++) {
val += inst.operands.at(i);
val <<= 32;
}
dst_offset = inst.operands.at(4);
break;
default:
break;
}
trace_builder.set(val, dst_offset, inst.in_tag);
break;
}
case OpCode::RETURN:
trace_builder.return_op(inst.operands.at(0), inst.operands.at(1));
break;
default:
break;
}
}
return trace_builder.finalize();
}

} // namespace avm_trace
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include "barretenberg/plonk/proof_system/types/proof.hpp"
#include "barretenberg/vm/avm_trace/AvmMini_common.hpp"
#include "barretenberg/vm/avm_trace/AvmMini_instructions.hpp"
#include "barretenberg/vm/avm_trace/AvmMini_trace.hpp"
#include <cstddef>
#include <cstdint>
#include <vector>

namespace avm_trace {

class Execution {
public:
Execution() = default;
static plonk::proof run_and_prove(std::vector<uint8_t> const& bytecode, std::vector<FF> const& calldata);
static size_t const AVM_OPERAND_BYTE_LENGTH = 4; // Keep in sync with TS code
static size_t const AVM_OPCODE_BYTE_LENGTH = 1; // Keep in sync with TS code
static size_t const AVM_IN_TAG_BYTE_LENGTH = 1; // Keep in sync with TS code

private:
AvmMiniTraceBuilder trace_builder{};
static std::vector<Instruction> parse(std::vector<uint8_t> const& bytecode);
static std::vector<Row> gen_trace(std::vector<Instruction> const& instructions, std::vector<FF> const& calldata);
};

} // namespace avm_trace
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include "barretenberg/vm/avm_trace/AvmMini_common.hpp"
#include "barretenberg/vm/avm_trace/AvmMini_opcode.hpp"
#include <cstdint>
#include <vector>

namespace avm_trace {

class Instruction {
public:
OpCode op_code;
std::vector<uint32_t> operands;
AvmMemoryTag in_tag;

Instruction() = delete;
explicit Instruction(OpCode op_code, std::vector<uint32_t> operands, AvmMemoryTag in_tag)
: op_code(op_code)
, operands(std::move(operands))
, in_tag(in_tag){};
};

} // namespace avm_trace
Loading

0 comments on commit e392ef5

Please sign in to comment.