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
90 changes: 23 additions & 67 deletions barretenberg/cpp/src/barretenberg/api/api_chonk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@
#include "barretenberg/api/log.hpp"
#include "barretenberg/bbapi/bbapi.hpp"
#include "barretenberg/chonk/chonk.hpp"
#include "barretenberg/chonk/chonk_verifier.hpp"
#include "barretenberg/chonk/mock_circuit_producer.hpp"
#include "barretenberg/chonk/private_execution_steps.hpp"
#include "barretenberg/chonk/proof_compression.hpp"
#include "barretenberg/common/assert.hpp"
#include "barretenberg/common/get_bytecode.hpp"
#include "barretenberg/common/map.hpp"
#include "barretenberg/common/throw_or_abort.hpp"
Expand Down Expand Up @@ -39,36 +36,27 @@ CircuitKind parse_circuit_kind(const std::string& s)
}

/**
* @brief Write a Chonk VK (raw bytes, or JSON fields when `flags.output_format == "json"`) to `output_path`.
* @brief Compute and write a Chonk VK in the per-kind flavor selected by `flags.circuit_kind`
* (app → MegaApp, kernel → MegaKernel, hiding → MegaZK). Delegates to ChonkComputeVk; the dummy
* IVC inputs that make a kernel's recursion constraints satisfiable are handled in the ACIR layer.
*/
void write_chonk_vk_data(const ChonkVkData& vk, const std::filesystem::path& output_path, const API::Flags& flags)
void write_chonk_vk(std::vector<uint8_t> bytecode, const std::filesystem::path& output_path, const API::Flags& flags)
{
const CircuitKind kind = parse_circuit_kind(flags.circuit_kind);
auto response = bbapi::ChonkComputeVk{ .circuit = { .bytecode = std::move(bytecode) }, .kind = kind }.execute();

const bool is_stdout = output_path == "-";
if (is_stdout) {
write_bytes_to_stdout(vk.bytes);
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(vk.fields, "", flags.scheme);
std::string json_content = VkJson::build(response.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 {
write_file(output_path / "vk", vk.bytes);
write_file(output_path / "vk", response.bytes);
}
}

/**
* @brief Compute and write a Chonk VK in the per-kind flavor selected by `flags.circuit_kind`
* (app → MegaApp, kernel → MegaKernel, hiding → MegaZK). Delegates to ChonkComputeVk; the dummy
* IVC inputs that make a kernel's recursion constraints satisfiable are handled in the ACIR layer.
*/
void write_chonk_vk(std::vector<uint8_t> bytecode, const std::filesystem::path& output_path, const API::Flags& flags)
{
const CircuitKind kind = parse_circuit_kind(flags.circuit_kind);
auto response = bbapi::ChonkComputeVk{ .circuit = { .bytecode = std::move(bytecode) }, .kind = kind }.execute();
write_chonk_vk_data(
{ .bytes = std::move(response.bytes), .fields = std::move(response.fields) }, output_path, flags);
}

} // anonymous namespace

void ChonkAPI::prove(const Flags& flags,
Expand Down Expand Up @@ -103,38 +91,23 @@ void ChonkAPI::prove(const Flags& flags,
bbapi::ChonkAccumulate{ .witness = std::move(step.witness) }.execute(request);
}

// The step bytecode has been moved out, so --write_vk writes the hiding kernel VK held by the IVC (the one
// ChonkProve verifies the proof against) instead of recomputing it. It is read before ChonkProve consumes the IVC.
const auto hiding_vk = flags.write_vk ? request.ivc_in_progress->get_hiding_kernel_vk_and_hash()->vk : nullptr;
auto proof = bbapi::ChonkProve{}.execute(request).proof;

const bool output_to_stdout = output_dir == "-";

const auto write_proof = [&]() {
const auto proof_fields = proof.to_field_elements();
if (output_to_stdout) {
vinfo("writing Chonk proof to stdout");
write_bytes_to_stdout(to_buffer(proof_fields));
} else if (flags.output_format == "json") {
vinfo("writing Chonk proof (JSON) in directory ", output_dir);
// Note: Chonk proof doesn't have a vk_hash, so we pass an empty string
std::string json_content = ProofJson::build(proof_fields, "", flags.scheme);
write_file(output_dir / "proof.json", std::vector<uint8_t>(json_content.begin(), json_content.end()));
info("Proof (JSON) saved to ", output_dir / "proof.json");
} else {
vinfo("writing Chonk proof in directory ", output_dir);
write_file(output_dir / "proof", to_buffer(proof_fields));
}
};

write_proof();

if (flags.write_vk) {
vinfo("writing Chonk vk in directory ", output_dir);
BB_ASSERT(hiding_vk != nullptr, "ChonkAPI::prove - hiding kernel VK was not read before proving");
// Write CHONK vk for the hiding kernel (last step) — proven as MegaZK.
write_chonk_vk_data(
{ .bytes = to_buffer(*hiding_vk), .fields = hiding_vk->to_field_elements() }, output_dir, flags);
const auto proof_fields = proof.to_field_elements();
if (output_to_stdout) {
vinfo("writing Chonk proof to stdout");
write_bytes_to_stdout(to_buffer(proof_fields));
} else if (flags.output_format == "json") {
vinfo("writing Chonk proof (JSON) in directory ", output_dir);
// Note: Chonk proof doesn't have a vk_hash, so we pass an empty string
std::string json_content = ProofJson::build(proof_fields, "", flags.scheme);
write_file(output_dir / "proof.json", std::vector<uint8_t>(json_content.begin(), json_content.end()));
info("Proof (JSON) saved to ", output_dir / "proof.json");
} else {
vinfo("writing Chonk proof in directory ", output_dir);
write_file(output_dir / "proof", to_buffer(proof_fields));
}
}

Expand Down Expand Up @@ -208,22 +181,6 @@ void ChonkAPI::proof_stats(const std::filesystem::path& proof_path, const std::f
}
}

// WORKTODO(bbapi) remove this
bool ChonkAPI::prove_and_verify(const std::filesystem::path& input_path)
{
PrivateExecutionSteps steps;
steps.parse(PrivateExecutionStepRaw::load_and_decompress(input_path));

std::shared_ptr<Chonk> ivc = steps.accumulate();
// Construct the hiding kernel as the final step of the IVC

auto proof = ivc->prove();
auto vk_and_hash = ivc->get_hiding_kernel_vk_and_hash();
ChonkNativeVerifier verifier(vk_and_hash);
const bool verified = verifier.verify(proof);
return verified;
}

void ChonkAPI::gates(const Flags& flags, const std::filesystem::path& bytecode_path)
{
BB_BENCH_NAME("ChonkAPI::gates");
Expand All @@ -241,7 +198,6 @@ void ChonkAPI::write_solidity_verifier([[maybe_unused]] const Flags& flags,
bool ChonkAPI::check_precomputed_vks(const Flags& flags, const std::filesystem::path& input_path)
{
BB_BENCH_NAME("ChonkAPI::check_precomputed_vks");
bbapi::BBApiRequest request;
std::vector<PrivateExecutionStepRaw> raw_steps = PrivateExecutionStepRaw::load_and_decompress(input_path);

bbapi::VkPolicy vk_policy = bbapi::parse_vk_policy(flags.vk_policy);
Expand Down
15 changes: 5 additions & 10 deletions barretenberg/cpp/src/barretenberg/api/api_chonk.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace bb {
* TypeScript (private function calls)
* → msgpack encode to ivc-inputs.msgpack
* → ChonkAPI::prove() loads and accumulates all steps
* → Chonk proof + VK written to output directory
* → Chonk proof written to output directory
* ```
*
* **Key entry points:**
Expand All @@ -27,7 +27,7 @@ namespace bb {
* - `write_vk()`: Computes verification key for standalone or IVC circuits
* - `check_precomputed_vks()`: Validates that precomputed VKs match computed ones
*
* @see PrivateExecutionSteps for the input format
* @see PrivateExecutionStepRaw for the input format
* @see Chonk for the underlying IVC implementation
*/
class ChonkAPI : public API {
Expand All @@ -37,11 +37,11 @@ class ChonkAPI : public API {
* @brief Main production entry point: generate a Chonk proof from private execution steps.
*
* @details Loads execution steps from ivc-inputs.msgpack, accumulates each circuit via Chonk,
* and writes the final proof (and optionally VK) to the output directory.
* and writes the final proof to the output directory.
*
* @param flags Runtime flags (vk_policy, write_vk, etc.)
* @param flags Runtime flags (vk_policy, output_format, etc.)
* @param input_path Path to ivc-inputs.msgpack containing PrivateExecutionStepRaw entries
* @param output_dir Directory to write proof (and vk if flags.write_vk is set)
* @param output_dir Directory to write the proof
*/
void prove(const Flags& flags, const std::filesystem::path& input_path, const std::filesystem::path& output_dir);

Expand All @@ -57,11 +57,6 @@ class ChonkAPI : public API {
const std::filesystem::path& proof_path,
const std::filesystem::path& vk_path) override;

/**
* @brief Test/debug function: prove and verify in one call (bypasses serialization).
*/
bool prove_and_verify(const std::filesystem::path& input_path);

/**
* @brief Output gate count statistics for a circuit.
*/
Expand Down
4 changes: 2 additions & 2 deletions barretenberg/cpp/src/barretenberg/api/file_io.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ inline std::vector<uint8_t> read_vk_file(const std::filesystem::path& vk_path)
return read_file(vk_path.string());
} catch (const std::runtime_error&) {
THROW std::runtime_error("Unable to open file: " + vk_path.string() +
"\nGenerate a vk during proving by running `bb prove` with an additional `--write_vk` "
"flag, or run `bb write_vk` to generate a standalone vk."
"\nRun `bb write_vk` to generate a vk. For UltraHonk, you can also generate it during "
"proving with `bb prove --write_vk`."
"\nIf you already have a vk file, specify its path with `--vk_path <path>`.");
}
}
Expand Down
7 changes: 6 additions & 1 deletion barretenberg/cpp/src/barretenberg/bb/cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ int parse_and_run_cli_command(int argc, char* argv[])
};

const auto add_write_vk_flag = [&](CLI::App* subcommand) {
return subcommand->add_flag("--write_vk", flags.write_vk, "Write the provided circuit's verification key");
return subcommand->add_flag(
"--write_vk", flags.write_vk, "Write the provided circuit's verification key (UltraHonk only)");
};

const auto remove_zk_option = [&](CLI::App* subcommand) {
Expand Down Expand Up @@ -1007,6 +1008,10 @@ int parse_and_run_cli_command(int argc, char* argv[])
} else if (flags.scheme == "chonk") {
ChonkAPI api;
if (prove->parsed()) {
if (flags.write_vk) {
throw_or_abort("prove --write_vk is not supported for Chonk. Use bb write_vk --scheme chonk "
"--circuit_kind hiding -b <hiding-kernel-bytecode> -o <output-directory>.");
}
if (!std::filesystem::exists(ivc_inputs_path)) {
throw_or_abort("The prove command for Chonk expect a valid file passed with --ivc_inputs_path "
"<ivc-inputs.msgpack> (default ./ivc-inputs.msgpack)");
Expand Down
5 changes: 2 additions & 3 deletions barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,8 @@ ChonkComputeVk::Response ChonkComputeVk::execute([[maybe_unused]] const BBApiReq
static_cast<int>(kind),
")");

auto constraint_system = acir_format::circuit_buf_to_mega_acir_format(std::move(circuit.bytecode));

acir_format::AcirProgram program{ constraint_system, /*witness=*/{} };
acir_format::AcirProgram program{ acir_format::circuit_buf_to_mega_acir_format(std::move(circuit.bytecode)),
/*witness=*/{} };
auto verification_key = compute_chonk_vk(program, kind);
info("ChonkComputeVk - VK derived, size: ", verification_key.bytes.size(), " bytes");
return { .bytes = std::move(verification_key.bytes), .fields = std::move(verification_key.fields) };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,13 @@ class ChonkPinnedIvcInputsTest : public ::testing::Test {
auto raw_steps = bb::PrivateExecutionStepRaw::load_and_decompress(inputs_path);
ASSERT_FALSE(raw_steps.empty()) << "no execution steps in " << inputs_path;

const auto hiding_bytecode = raw_steps.back().bytecode;
ASSERT_EQ(raw_steps.back().kind, bb::CircuitKind::HidingKernel);
auto hiding_bytecode = raw_steps.back().bytecode;
const auto hiding_vk = raw_steps.back().vk;
const auto derived_vk = bb::bbapi::ChonkComputeVk{ .circuit = { .bytecode = std::move(hiding_bytecode) },
.kind = bb::CircuitKind::HidingKernel }
.execute();
ASSERT_EQ(hiding_vk, derived_vk.bytes) << "embedded hiding VK differs from bytecode-derived VK";

bb::bbapi::BBApiRequest request;
request.vk_policy = bb::bbapi::VkPolicy::DEFAULT;
Expand All @@ -108,7 +113,7 @@ class ChonkPinnedIvcInputsTest : public ::testing::Test {
auto prove_response = bb::bbapi::ChonkProve{}.execute(request);

auto verify_response =
bb::bbapi::ChonkVerify{ .proof = std::move(prove_response.proof), .vk = hiding_vk }.execute();
bb::bbapi::ChonkVerify{ .proof = std::move(prove_response.proof), .vk = derived_vk.bytes }.execute();
EXPECT_TRUE(verify_response.valid) << "ChonkVerify rejected " << flow_dir.filename();
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ class ChonkStepProcessor {
public:
explicit ChonkStepProcessor(std::vector<CircuitKind> circuit_kinds);

std::shared_ptr<Chonk> get_ivc() const { return ivc; }
size_t get_num_circuits_accumulated() const { return ivc->get_num_circuits_accumulated(); }

void process_step(ChonkExecutionStep&& step, ChonkPrecomputedVkPolicy policy = ChonkPrecomputedVkPolicy::DEFAULT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
// =====================

#include "private_execution_steps.hpp"
#include "barretenberg/chonk/chonk.hpp"
#include "barretenberg/chonk/chonk_step_processor.hpp"
#include "barretenberg/common/bb_bench.hpp"
#include "barretenberg/common/serialize.hpp"
#include "barretenberg/common/thread.hpp"
#include "barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp"
#include "barretenberg/serialize/msgpack_impl.hpp"
#include <fstream>
#include <libdeflate.h>
#include <memory>
#include <sstream>
#include <stdexcept>

namespace bb {

Expand Down Expand Up @@ -142,56 +145,6 @@ std::vector<PrivateExecutionStepRaw> PrivateExecutionStepRaw::parse_uncompressed
return raw_steps;
}

void PrivateExecutionSteps::parse(std::vector<PrivateExecutionStepRaw>&& steps)
{
BB_BENCH();

// Preallocate space to write into diretly as push_back would not be thread safe
folding_stack.resize(steps.size());
precomputed_vks.resize(steps.size());
function_names.resize(steps.size());
kinds.resize(steps.size());

// Parse each step's bytecode/witness in parallel (thread-safe with msgpack format)
parallel_for(steps.size(), [&](size_t i) {
PrivateExecutionStepRaw step = std::move(steps[i]);
step.self_decompress();

acir_format::AcirFormat constraints = acir_format::circuit_buf_to_mega_acir_format(std::move(step.bytecode));
acir_format::WitnessVector witness = acir_format::witness_buf_to_witness_vector(std::move(step.witness));

folding_stack[i] = { std::move(constraints), std::move(witness) };
if (step.vk.empty()) {
// For backwards compatibility, but it affects performance and correctness.
precomputed_vks[i] = {};
} else {
precomputed_vks[i] = std::move(step.vk);
}
function_names[i] = std::move(step.function_name);
kinds[i] = step.kind;
});
}

std::shared_ptr<Chonk> PrivateExecutionSteps::accumulate()
{
auto step_processor = ChonkStepProcessor(kinds);

for (auto& vk : precomputed_vks) {
if (vk.empty()) {
info("DEPRECATED: Precomputed VKs expected for the given circuits.");
break;
}
}
for (size_t i = 0; i < folding_stack.size(); ++i) {
step_processor.process_step({ .name = std::move(function_names[i]),
.program = std::move(folding_stack[i]),
.precomputed_vk = std::move(precomputed_vks[i]),
.kind = kinds[i] });
}

return step_processor.get_ivc();
}

void PrivateExecutionStepRaw::compress_and_save(std::vector<PrivateExecutionStepRaw>&& steps,
const std::filesystem::path& output_path)
{
Expand Down
Loading
Loading