Skip to content
Closed
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
70 changes: 48 additions & 22 deletions barretenberg/cpp/src/barretenberg/api/api_chonk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
#include "barretenberg/api/file_io.hpp"
#include "barretenberg/api/json_output.hpp"
#include "barretenberg/api/log.hpp"
#include "barretenberg/bbapi/bbapi.hpp"
#include "barretenberg/bbapi/bbapi_handlers.hpp"
#include "barretenberg/bbapi/bbapi_shared.hpp"
#include "barretenberg/bbapi/bbapi_wire_convert.hpp"
#include "barretenberg/bbapi/generated/bb_types.hpp"
#include "barretenberg/chonk/chonk.hpp"
#include "barretenberg/chonk/chonk_verifier.hpp"
#include "barretenberg/chonk/mock_circuit_producer.hpp"
Expand Down Expand Up @@ -33,16 +36,21 @@ namespace { // anonymous namespace
*/
void write_chonk_vk(std::vector<uint8_t> bytecode, const std::filesystem::path& output_path, const API::Flags& flags)
{
bbapi::BBApiRequest request;
auto response =
bbapi::ChonkComputeVk{ .circuit = { .bytecode = std::move(bytecode) }, .use_zk_flavor = flags.use_zk_flavor }
.execute();
bbapi::handle_chonk_compute_vk(request,
bbapi::wire::ChonkComputeVk{
.circuit = bbapi::wire::CircuitInputNoVK{ .bytecode = std::move(bytecode) },
.use_zk_flavor = flags.use_zk_flavor,
});

const bool is_stdout = output_path == "-";
if (is_stdout) {
write_bytes_to_stdout(response.bytes);
} else if (flags.output_format == "json") {
// Note: Chonk VK doesn't have a hash, so we pass an empty string
std::string json_content = VkJson::build(response.fields, "", flags.scheme);
// Note: Chonk VK doesn't have a hash, so we pass an empty string.
auto fields = bbapi::fr_vec_from_wire(response.fields);
std::string json_content = VkJson::build(fields, "", flags.scheme);
write_file(output_path / "vk.json", std::vector<uint8_t>(json_content.begin(), json_content.end()));
info("VK (JSON) saved to ", output_path / "vk.json");
} else {
Expand All @@ -60,21 +68,25 @@ void ChonkAPI::prove(const Flags& flags,
request.vk_policy = bbapi::parse_vk_policy(flags.vk_policy);
std::vector<PrivateExecutionStepRaw> raw_steps = PrivateExecutionStepRaw::load_and_decompress(input_path);

bbapi::ChonkStart{ .num_circuits = static_cast<uint32_t>(raw_steps.size()) }.execute(request);
bbapi::handle_chonk_start(request,
bbapi::wire::ChonkStart{ .num_circuits = static_cast<uint32_t>(raw_steps.size()) });
info("Chonk: starting with ", raw_steps.size(), " circuits");
for (size_t i = 0; i < raw_steps.size(); ++i) {
const auto& step = raw_steps[i];
bbapi::ChonkLoad{
.circuit = { .name = step.function_name, .bytecode = step.bytecode, .verification_key = step.vk },
}
.execute(request);
bbapi::handle_chonk_load(request,
bbapi::wire::ChonkLoad{ .circuit = bbapi::wire::CircuitInput{
.name = step.function_name,
.bytecode = step.bytecode,
.verification_key = step.vk,
} });

// NOLINTNEXTLINE(bugprone-unchecked-optional-access): we know the optional has been set here.
info("Chonk: accumulating " + step.function_name);
bbapi::ChonkAccumulate{ .witness = step.witness }.execute(request);
bbapi::handle_chonk_accumulate(request, bbapi::wire::ChonkAccumulate{ .witness = step.witness });
}

auto proof = bbapi::ChonkProve{}.execute(request).proof;
auto wire_proof = bbapi::handle_chonk_prove(request, bbapi::wire::ChonkProve{}).proof;
auto proof = bbapi::chonk_proof_from_wire(std::move(wire_proof));

const bool output_to_stdout = output_dir == "-";

Expand Down Expand Up @@ -117,7 +129,9 @@ bool ChonkAPI::verify([[maybe_unused]] const Flags& flags,

auto vk_buffer = read_vk_file(vk_path);

auto response = bbapi::ChonkVerify{ .proof = std::move(proof), .vk = std::move(vk_buffer) }.execute();
bbapi::BBApiRequest request;
auto response = bbapi::handle_chonk_verify(
request, bbapi::wire::ChonkVerify{ .proof = bbapi::chonk_proof_to_wire(proof), .vk = std::move(vk_buffer) });
return response.valid;
}

Expand Down Expand Up @@ -147,7 +161,14 @@ bool ChonkAPI::batch_verify([[maybe_unused]] const Flags& flags, const std::file

info("ChonkAPI::batch_verify - found ", proofs.size(), " proof/vk pairs in ", proofs_dir.string());

auto response = bbapi::ChonkBatchVerify{ .proofs = std::move(proofs), .vks = std::move(vks) }.execute();
std::vector<bbapi::wire::ChonkProof> wire_proofs;
wire_proofs.reserve(proofs.size());
for (const auto& p : proofs) {
wire_proofs.push_back(bbapi::chonk_proof_to_wire(p));
}
bbapi::BBApiRequest request;
auto response = bbapi::handle_chonk_batch_verify(
request, bbapi::wire::ChonkBatchVerify{ .proofs = std::move(wire_proofs), .vks = std::move(vks) });
return response.valid;
}

Expand Down Expand Up @@ -221,12 +242,14 @@ bool ChonkAPI::check_precomputed_vks(const Flags& flags, const std::filesystem::
return false;
}
const bool use_zk_flavor = (i == raw_steps.size() - 1);
auto response =
bbapi::ChonkCheckPrecomputedVk{
.circuit = { .name = step.function_name, .bytecode = step.bytecode, .verification_key = step.vk },
auto response = bbapi::handle_chonk_check_precomputed_vk(
request,
bbapi::wire::ChonkCheckPrecomputedVk{
.circuit = bbapi::wire::CircuitInput{ .name = step.function_name,
.bytecode = step.bytecode,
.verification_key = step.vk },
.use_zk_flavor = use_zk_flavor,
}
.execute();
});

if (!response.valid) {
info("VK mismatch detected for function ", step.function_name);
Expand Down Expand Up @@ -271,9 +294,12 @@ void chonk_gate_count(const std::string& bytecode_path, bool include_gates_per_o
bbapi::BBApiRequest request;

auto bytecode = get_bytecode(bytecode_path);
auto response = bbapi::ChonkStats{ .circuit = { .name = "ivc_circuit", .bytecode = std::move(bytecode) },
.include_gates_per_opcode = include_gates_per_opcode }
.execute(request);
auto response = bbapi::handle_chonk_stats(
request,
bbapi::wire::ChonkStats{
.circuit = bbapi::wire::CircuitInputNoVK{ .name = "ivc_circuit", .bytecode = std::move(bytecode) },
.include_gates_per_opcode = include_gates_per_opcode,
});

// Build the circuit report. It always has one function, corresponding to the ACIR constraint systems.
// NOTE: can be reconsidered
Expand Down
173 changes: 45 additions & 128 deletions barretenberg/cpp/src/barretenberg/api/api_msgpack.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "barretenberg/api/api_msgpack.hpp"
#include "barretenberg/bbapi/c_bind.hpp"
#include "barretenberg/bbapi/bbapi_handlers.hpp"
#include "barretenberg/bbapi/bbapi_shared.hpp"
#include "barretenberg/bbapi/generated/bb_ipc_server.hpp"
#include "barretenberg/common/log.hpp"
#include "barretenberg/serialize/msgpack.hpp"
#include <cstdint>
Expand All @@ -20,79 +22,56 @@ int process_msgpack_commands(std::istream& input_stream)
// Redirect std::cout to stderr to prevent accidental writes to stdout
auto* original_cout_buf = std::cout.rdbuf();
std::cout.rdbuf(std::cerr.rdbuf());

// Create an ostream that writes directly to stdout
std::ostream stdout_stream(original_cout_buf);

// Process length-encoded msgpack buffers
// Dispatcher is the codegen-emitted handler that owns the
// command-name → handle_<method> table and runs the per-call
// serialize / deserialize / exception → ErrorResponse plumbing.
// BBApiRequest lives across calls so IVC state (loaded circuit,
// accumulator, etc.) persists between Chonk* invocations.
bb::bbapi::BBApiRequest request;
auto handler = bb::bbapi::make_bb_handler(request);

while (!input_stream.eof()) {
// Read 4-byte length prefix in little-endian format
uint32_t length = 0;
input_stream.read(reinterpret_cast<char*>(&length), sizeof(length));

if (input_stream.gcount() != sizeof(length)) {
// End of stream or incomplete length
break;
break; // EOF or incomplete length
}

// Read the msgpack buffer
std::vector<uint8_t> buffer(length);
input_stream.read(reinterpret_cast<char*>(buffer.data()), static_cast<std::streamsize>(length));

if (input_stream.gcount() != static_cast<std::streamsize>(length)) {
std::cerr << "Error: Incomplete msgpack buffer read" << '\n';
// Restore original cout buffer before returning
std::cout.rdbuf(original_cout_buf);
return 1;
}

// Deserialize the msgpack buffer
// The buffer should contain a tuple of arguments (array) matching the bbapi function signature.
// Since bbapi(Command) takes one argument, we expect a 1-element array containing the Command.
auto unpacked = msgpack::unpack(reinterpret_cast<const char*>(buffer.data()), buffer.size());
auto obj = unpacked.get();

// First, expect an array (the tuple of arguments)
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
if (obj.type != msgpack::type::ARRAY || obj.via.array.size != 1) {
throw_or_abort("Expected an array of size 1 (tuple of arguments) for bbapi command deserialization");
}

// NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
auto& tuple_arr = obj.via.array;
auto& command_obj = tuple_arr.ptr[0];

// Now access the Command itself, which should be an array of size 2 [command-name, payload]
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
if (command_obj.type != msgpack::type::ARRAY || command_obj.via.array.size != 2) {
throw_or_abort("Expected Command to be an array of size 2 [command-name, payload]");
}

// NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
auto& command_arr = command_obj.via.array;
if (command_arr.ptr[0].type != msgpack::type::STR) {
throw_or_abort("Expected first element of Command to be a string (type name)");
#ifndef BB_NO_EXCEPTIONS
std::vector<uint8_t> response;
try {
response = handler(buffer);
} catch (const ::ipc::ShutdownRequested& shutdown) {
// Write the shutdown response (already framed by the handler) and exit.
const auto& resp = shutdown.response();
uint32_t resp_length = static_cast<uint32_t>(resp.size());
stdout_stream.write(reinterpret_cast<const char*>(&resp_length), sizeof(resp_length));
stdout_stream.write(reinterpret_cast<const char*>(resp.data()), static_cast<std::streamsize>(resp.size()));
stdout_stream.flush();
std::cout.rdbuf(original_cout_buf);
return 0;
}
#else
std::vector<uint8_t> response = handler(buffer);
#endif

// Convert to Command (which is a NamedUnion)
bb::bbapi::Command command;
command_obj.convert(command);

// Execute the command
auto response = bbapi::bbapi(std::move(command));

// Serialize the response
msgpack::sbuffer response_buffer;
msgpack::pack(response_buffer, response);

// Write length-encoded response directly to stdout
uint32_t response_length = static_cast<uint32_t>(response_buffer.size());
uint32_t response_length = static_cast<uint32_t>(response.size());
stdout_stream.write(reinterpret_cast<const char*>(&response_length), sizeof(response_length));
stdout_stream.write(response_buffer.data(), static_cast<std::streamsize>(response_buffer.size()));
stdout_stream.write(reinterpret_cast<const char*>(response.data()),
static_cast<std::streamsize>(response.size()));
stdout_stream.flush();
}

// Restore original cout buffer
std::cout.rdbuf(original_cout_buf);
return 0;
}
Expand All @@ -113,82 +92,20 @@ int execute_msgpack_ipc_server(std::unique_ptr<ipc::IpcServer> server)

std::cerr << "IPC server ready" << '\n';

// Run server with msgpack handler
server->run([](int client_id, std::span<const uint8_t> request) -> std::vector<uint8_t> {
try {
// Deserialize msgpack command
// The buffer should contain a tuple of arguments (array) matching the bbapi function signature.
// Since bbapi(Command) takes one argument, we expect a 1-element array containing the Command.
auto unpacked = msgpack::unpack(reinterpret_cast<const char*>(request.data()), request.size());
auto obj = unpacked.get();

// First, expect an array (the tuple of arguments)
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
if (obj.type != msgpack::type::ARRAY || obj.via.array.size != 1) {
std::cerr << "Error: Expected an array of size 1 (tuple of arguments) from client " << client_id
<< '\n';
return {}; // Return empty to skip response
}

// NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
auto& tuple_arr = obj.via.array;
auto& command_obj = tuple_arr.ptr[0];

// Now access the Command itself, which should be an array of size 2 [command-name, payload]
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
if (command_obj.type != msgpack::type::ARRAY || command_obj.via.array.size != 2) {
std::cerr << "Error: Expected Command to be an array of size 2 [command-name, payload] from client "
<< client_id << '\n';
return {};
}

// NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
auto& command_arr = command_obj.via.array;
if (command_arr.ptr[0].type != msgpack::type::STR) {
std::cerr << "Error: Expected first element of Command to be a string (type name) from client "
<< client_id << '\n';
return {};
}

// Check if this is a Shutdown command
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
std::string command_name(command_arr.ptr[0].via.str.ptr, command_arr.ptr[0].via.str.size);
bool is_shutdown = (command_name == "Shutdown");

// Convert to Command and execute
bb::bbapi::Command command;
command_obj.convert(command);
auto response = bbapi::bbapi(std::move(command));

// Serialize response
msgpack::sbuffer response_buffer;
msgpack::pack(response_buffer, response);
std::vector<uint8_t> result(response_buffer.data(), response_buffer.data() + response_buffer.size());

// If this was a shutdown command, throw exception with response
// This signals the server to send the response and then exit gracefully
if (is_shutdown) {
throw ipc::ShutdownRequested(std::move(result));
}

return result;
} catch (const ipc::ShutdownRequested&) {
// Re-throw shutdown request
throw;
} catch (const std::exception& e) {
// Log error to stderr for debugging (goes to log file if logger enabled)
std::cerr << "Error processing request from client " << client_id << ": " << e.what() << '\n';
std::cerr.flush();

// Create error response with exception message
bb::bbapi::ErrorResponse error_response{ .message = std::string(e.what()) };
bb::bbapi::CommandResponse response = error_response;

// Serialize and return error response to client
msgpack::sbuffer response_buffer;
msgpack::pack(response_buffer, response);
return std::vector<uint8_t>(response_buffer.data(), response_buffer.data() + response_buffer.size());
}
// The codegen-emitted make_bb_handler<BBApiRequest> instantiates a
// dispatch table keyed by command name; each entry deserialises the
// wire-typed payload and calls our handle_<method>(BBApiRequest&, wire::X&&)
// overload (see bbapi_handlers.hpp). It throws ipc::ShutdownRequested on
// the Shutdown command; the runtime's run() catches that and exits cleanly.
//
// BBApiRequest lives for the lifetime of this server so IVC state
// (loaded_circuit_constraints, ivc_in_progress, etc.) is preserved
// across the call sequence (ChonkStart → ChonkLoad → ChonkAccumulate
// → ChonkProve), matching the bbapi() global_request semantics.
bb::bbapi::BBApiRequest request;
auto handler = bb::bbapi::make_bb_handler(request);
server->run([&handler](int /*client_id*/, std::span<const uint8_t> raw) {
return handler(std::vector<uint8_t>(raw.begin(), raw.end()));
});

server->close();
Expand Down
Loading
Loading