diff --git a/barretenberg/cpp/src/barretenberg/api/api_chonk.cpp b/barretenberg/cpp/src/barretenberg/api/api_chonk.cpp index b7258231259a..b1090eda7fbd 100644 --- a/barretenberg/cpp/src/barretenberg/api/api_chonk.cpp +++ b/barretenberg/cpp/src/barretenberg/api/api_chonk.cpp @@ -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" @@ -33,16 +36,21 @@ namespace { // anonymous namespace */ void write_chonk_vk(std::vector 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(json_content.begin(), json_content.end())); info("VK (JSON) saved to ", output_path / "vk.json"); } else { @@ -60,21 +68,25 @@ void ChonkAPI::prove(const Flags& flags, request.vk_policy = bbapi::parse_vk_policy(flags.vk_policy); std::vector raw_steps = PrivateExecutionStepRaw::load_and_decompress(input_path); - bbapi::ChonkStart{ .num_circuits = static_cast(raw_steps.size()) }.execute(request); + bbapi::handle_chonk_start(request, + bbapi::wire::ChonkStart{ .num_circuits = static_cast(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 == "-"; @@ -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; } @@ -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 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; } @@ -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); @@ -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 diff --git a/barretenberg/cpp/src/barretenberg/api/api_msgpack.cpp b/barretenberg/cpp/src/barretenberg/api/api_msgpack.cpp index 9c982a9648c4..a324aa127960 100644 --- a/barretenberg/cpp/src/barretenberg/api/api_msgpack.cpp +++ b/barretenberg/cpp/src/barretenberg/api/api_msgpack.cpp @@ -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 @@ -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_ 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(&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 buffer(length); input_stream.read(reinterpret_cast(buffer.data()), static_cast(length)); - if (input_stream.gcount() != static_cast(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(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 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(resp.size()); + stdout_stream.write(reinterpret_cast(&resp_length), sizeof(resp_length)); + stdout_stream.write(reinterpret_cast(resp.data()), static_cast(resp.size())); + stdout_stream.flush(); + std::cout.rdbuf(original_cout_buf); + return 0; } +#else + std::vector 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(response_buffer.size()); + uint32_t response_length = static_cast(response.size()); stdout_stream.write(reinterpret_cast(&response_length), sizeof(response_length)); - stdout_stream.write(response_buffer.data(), static_cast(response_buffer.size())); + stdout_stream.write(reinterpret_cast(response.data()), + static_cast(response.size())); stdout_stream.flush(); } - // Restore original cout buffer std::cout.rdbuf(original_cout_buf); return 0; } @@ -113,82 +92,20 @@ int execute_msgpack_ipc_server(std::unique_ptr server) std::cerr << "IPC server ready" << '\n'; - // Run server with msgpack handler - server->run([](int client_id, std::span request) -> std::vector { - 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(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 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(response_buffer.data(), response_buffer.data() + response_buffer.size()); - } + // The codegen-emitted make_bb_handler instantiates a + // dispatch table keyed by command name; each entry deserialises the + // wire-typed payload and calls our handle_(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 raw) { + return handler(std::vector(raw.begin(), raw.end())); }); server->close(); diff --git a/barretenberg/cpp/src/barretenberg/api/api_ultra_honk.cpp b/barretenberg/cpp/src/barretenberg/api/api_ultra_honk.cpp index 345b65b02fe1..5577ffda2a06 100644 --- a/barretenberg/cpp/src/barretenberg/api/api_ultra_honk.cpp +++ b/barretenberg/cpp/src/barretenberg/api/api_ultra_honk.cpp @@ -2,7 +2,10 @@ #include "barretenberg/api/file_io.hpp" #include "barretenberg/api/json_output.hpp" -#include "barretenberg/bbapi/bbapi_ultra_honk.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/common/bb_bench.hpp" #include "barretenberg/common/get_bytecode.hpp" #include "barretenberg/common/map.hpp" @@ -13,13 +16,13 @@ namespace bb { namespace { -void write_vk_outputs(const bbapi::CircuitComputeVk::Response& vk_response, +void write_vk_outputs(const bbapi::wire::CircuitComputeVkResponse& vk_response, const std::filesystem::path& output_dir, const API::Flags& flags) { if (flags.output_format == "json") { - std::string json_content = - VkJson::build(vk_response.fields, bytes_to_hex_string(vk_response.hash), flags.scheme); + auto fields = bbapi::uint256_vec_from_wire(vk_response.fields); + std::string json_content = VkJson::build(fields, bytes_to_hex_string(vk_response.hash), flags.scheme); write_file(output_dir / "vk.json", std::vector(json_content.begin(), json_content.end())); info("VK (JSON) saved to ", output_dir / "vk.json"); } else { @@ -30,22 +33,26 @@ void write_vk_outputs(const bbapi::CircuitComputeVk::Response& vk_response, } } -void write_proof_outputs(const bbapi::CircuitProve::Response& prove_response, +void write_proof_outputs(const bbapi::wire::CircuitProveResponse& prove_response, const std::filesystem::path& output_dir, const API::Flags& flags) { if (flags.output_format == "json") { std::string vk_hash = bytes_to_hex_string(prove_response.vk.hash); - std::string proof_json = ProofJson::build(prove_response.proof, vk_hash, flags.scheme); + auto proof_domain = bbapi::uint256_vec_from_wire(prove_response.proof); + auto pi_domain = bbapi::uint256_vec_from_wire(prove_response.public_inputs); + std::string proof_json = ProofJson::build(proof_domain, vk_hash, flags.scheme); write_file(output_dir / "proof.json", std::vector(proof_json.begin(), proof_json.end())); info("Proof (JSON) saved to ", output_dir / "proof.json"); - std::string pi_json = PublicInputsJson::build(prove_response.public_inputs, flags.scheme); + std::string pi_json = PublicInputsJson::build(pi_domain, flags.scheme); write_file(output_dir / "public_inputs.json", std::vector(pi_json.begin(), pi_json.end())); info("Public inputs (JSON) saved to ", output_dir / "public_inputs.json"); } else { - auto public_inputs_buf = to_buffer(prove_response.public_inputs); - auto proof_buf = to_buffer(prove_response.proof); + auto pi_domain = bbapi::uint256_vec_from_wire(prove_response.public_inputs); + auto proof_domain = bbapi::uint256_vec_from_wire(prove_response.proof); + auto public_inputs_buf = to_buffer(pi_domain); + auto proof_buf = to_buffer(proof_domain); write_file(output_dir / "public_inputs", public_inputs_buf); write_file(output_dir / "proof", proof_buf); @@ -76,29 +83,28 @@ void UltraHonkAPI::prove(const Flags& flags, throw_or_abort("Stdout output is not supported. Please specify an output directory."); } - // Convert flags to ProofSystemSettings - bbapi::ProofSystemSettings settings{ .ipa_accumulation = flags.ipa_accumulation, - .oracle_hash_type = flags.oracle_hash_type, - .disable_zk = flags.disable_zk }; + bbapi::wire::ProofSystemSettings settings{ .ipa_accumulation = flags.ipa_accumulation, + .oracle_hash_type = flags.oracle_hash_type, + .disable_zk = flags.disable_zk }; - // Read input files auto bytecode = get_bytecode(bytecode_path); auto witness = get_bytecode(witness_path); - // Handle VK std::vector vk_bytes; - if (!vk_path.empty() && !flags.write_vk) { vk_bytes = read_file(vk_path); } - // Prove - auto response = bbapi::CircuitProve{ .circuit = { .name = "circuit", - .bytecode = std::move(bytecode), - .verification_key = std::move(vk_bytes) }, - .witness = std::move(witness), - .settings = std::move(settings) } - .execute(); + bbapi::BBApiRequest request; + auto response = + bbapi::handle_circuit_prove(request, + bbapi::wire::CircuitProve{ + .circuit = bbapi::wire::CircuitInput{ .name = "circuit", + .bytecode = std::move(bytecode), + .verification_key = std::move(vk_bytes) }, + .witness = std::move(witness), + .settings = std::move(settings), + }); write_proof_outputs(response, output_dir, flags); if (flags.write_vk) { write_vk_outputs(response.vk, output_dir, flags); @@ -138,17 +144,18 @@ bool UltraHonkAPI::verify(const Flags& flags, vk_bytes = std::move(vk_content); } - // Convert flags to ProofSystemSettings - bbapi::ProofSystemSettings settings{ .ipa_accumulation = flags.ipa_accumulation, - .oracle_hash_type = flags.oracle_hash_type, - .disable_zk = flags.disable_zk }; + bbapi::wire::ProofSystemSettings settings{ .ipa_accumulation = flags.ipa_accumulation, + .oracle_hash_type = flags.oracle_hash_type, + .disable_zk = flags.disable_zk }; - // Execute verify command - auto response = bbapi::CircuitVerify{ .verification_key = std::move(vk_bytes), - .public_inputs = std::move(public_inputs), - .proof = std::move(proof), - .settings = settings } - .execute(); + bbapi::BBApiRequest request; + auto response = bbapi::handle_circuit_verify(request, + bbapi::wire::CircuitVerify{ + .verification_key = std::move(vk_bytes), + .public_inputs = bbapi::uint256_vec_to_wire(public_inputs), + .proof = bbapi::uint256_vec_to_wire(proof), + .settings = settings, + }); return response.verified; } @@ -171,17 +178,19 @@ void UltraHonkAPI::write_vk(const Flags& flags, throw_or_abort("Stdout output is not supported. Please specify an output directory."); } - // Read bytecode auto bytecode = get_bytecode(bytecode_path); - // Convert flags to ProofSystemSettings - bbapi::ProofSystemSettings settings{ .ipa_accumulation = flags.ipa_accumulation, - .oracle_hash_type = flags.oracle_hash_type, - .disable_zk = flags.disable_zk }; + bbapi::wire::ProofSystemSettings settings{ .ipa_accumulation = flags.ipa_accumulation, + .oracle_hash_type = flags.oracle_hash_type, + .disable_zk = flags.disable_zk }; - auto response = bbapi::CircuitComputeVk{ .circuit = { .name = "circuit", .bytecode = std::move(bytecode) }, - .settings = settings } - .execute(); + bbapi::BBApiRequest request; + auto response = bbapi::handle_circuit_compute_vk( + request, + bbapi::wire::CircuitComputeVk{ + .circuit = bbapi::wire::CircuitInputNoVK{ .name = "circuit", .bytecode = std::move(bytecode) }, + .settings = settings, + }); write_vk_outputs(response, output_dir, flags); } @@ -198,15 +207,18 @@ void UltraHonkAPI::gates([[maybe_unused]] const Flags& flags, // For now, treat the entire bytecode as a single circuit // Convert flags to ProofSystemSettings - bbapi::ProofSystemSettings settings{ .ipa_accumulation = flags.ipa_accumulation, - .oracle_hash_type = flags.oracle_hash_type, - .disable_zk = flags.disable_zk }; - - // Execute CircuitStats command - auto response = bbapi::CircuitStats{ .circuit = { .name = "circuit", .bytecode = bytecode, .verification_key = {} }, - .include_gates_per_opcode = flags.include_gates_per_opcode, - .settings = settings } - .execute(); + bbapi::wire::ProofSystemSettings settings{ .ipa_accumulation = flags.ipa_accumulation, + .oracle_hash_type = flags.oracle_hash_type, + .disable_zk = flags.disable_zk }; + + bbapi::BBApiRequest request; + auto response = bbapi::handle_circuit_stats( + request, + bbapi::wire::CircuitStats{ + .circuit = bbapi::wire::CircuitInput{ .name = "circuit", .bytecode = bytecode, .verification_key = {} }, + .include_gates_per_opcode = flags.include_gates_per_opcode, + .settings = settings, + }); vinfo("Calculated circuit size in gate_count: ", response.num_gates); @@ -245,14 +257,14 @@ void UltraHonkAPI::write_solidity_verifier(const Flags& flags, // Read VK file auto vk_bytes = read_vk_file(vk_path); - // Convert flags to ProofSystemSettings - bbapi::ProofSystemSettings settings{ .ipa_accumulation = flags.ipa_accumulation, - .oracle_hash_type = flags.oracle_hash_type, - .disable_zk = flags.disable_zk, - .optimized_solidity_verifier = flags.optimized_solidity_verifier }; + bbapi::wire::ProofSystemSettings settings{ .ipa_accumulation = flags.ipa_accumulation, + .oracle_hash_type = flags.oracle_hash_type, + .disable_zk = flags.disable_zk, + .optimized_solidity_verifier = flags.optimized_solidity_verifier }; - // Execute solidity verifier command - auto response = bbapi::CircuitWriteSolidityVerifier{ .verification_key = vk_bytes, .settings = settings }.execute(); + bbapi::BBApiRequest request; + auto response = bbapi::handle_circuit_write_solidity_verifier( + request, bbapi::wire::CircuitWriteSolidityVerifier{ .verification_key = vk_bytes, .settings = settings }); // Write output if (output_path == "-") { diff --git a/barretenberg/cpp/src/barretenberg/api/aztec_process.cpp b/barretenberg/cpp/src/barretenberg/api/aztec_process.cpp index 671f72dfd9a7..faed7ea8d637 100644 --- a/barretenberg/cpp/src/barretenberg/api/aztec_process.cpp +++ b/barretenberg/cpp/src/barretenberg/api/aztec_process.cpp @@ -1,7 +1,9 @@ #ifndef __wasm__ #include "aztec_process.hpp" #include "barretenberg/api/file_io.hpp" -#include "barretenberg/bbapi/bbapi_chonk.hpp" +#include "barretenberg/bbapi/bbapi_handlers.hpp" +#include "barretenberg/bbapi/bbapi_shared.hpp" +#include "barretenberg/bbapi/generated/bb_types.hpp" #include "barretenberg/common/base64.hpp" #include "barretenberg/common/get_bytecode.hpp" #include "barretenberg/common/thread.hpp" @@ -109,7 +111,11 @@ std::vector get_or_generate_cached_vk(const std::filesystem::path& cach // Generate new VK info("Generating verification key: ", hash_str); - auto response = bbapi::ChonkComputeVk{ .circuit = { .name = circuit_name, .bytecode = bytecode } }.execute(); + bbapi::BBApiRequest request; + auto response = + bbapi::handle_chonk_compute_vk(request, + bbapi::wire::ChonkComputeVk{ .circuit = bbapi::wire::CircuitInputNoVK{ + .name = circuit_name, .bytecode = bytecode } }); // Cache the VK write_file(vk_cache_path, response.bytes); diff --git a/barretenberg/cpp/src/barretenberg/bb/cli.cpp b/barretenberg/cpp/src/barretenberg/bb/cli.cpp index be1681019dd2..6492a8305039 100644 --- a/barretenberg/cpp/src/barretenberg/bb/cli.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/cli.cpp @@ -24,8 +24,8 @@ #include "barretenberg/bb/cli11_formatter.hpp" #include "barretenberg/bb/curve_constants.hpp" #include "barretenberg/bbapi/bbapi.hpp" -#include "barretenberg/bbapi/bbapi_ultra_honk.hpp" #include "barretenberg/bbapi/c_bind.hpp" +#include "barretenberg/bbapi/generated/bb_ipc_server.hpp" #include "barretenberg/common/assert.hpp" #include "barretenberg/common/bb_bench.hpp" #include "barretenberg/common/get_bytecode.hpp" @@ -921,7 +921,7 @@ int parse_and_run_cli_command(int argc, char* argv[]) // MSGPACK if (msgpack_schema_command->parsed()) { - std::cout << bbapi::get_msgpack_schema_as_json() << std::endl; + std::cout << bbapi::get_bb_schema_as_json() << std::endl; return 0; } if (msgpack_curve_constants_command->parsed()) { diff --git a/barretenberg/cpp/src/barretenberg/bbapi/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/bbapi/CMakeLists.txt index 8baaef5b0276..65533b38266e 100644 --- a/barretenberg/cpp/src/barretenberg/bbapi/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/bbapi/CMakeLists.txt @@ -1,6 +1,52 @@ +# BB IPC client + server library (wire types + msgpack dispatch). +# Sources are generated by ipc-codegen from bb_schema.json (committed alongside +# the C++ server). Mirrors the wsdb codegen setup. +if(NOT(FUZZING) AND NOT(WASM)) + set(BB_SCHEMA ${CMAKE_CURRENT_SOURCE_DIR}/bb_schema.json) + set(BB_GEN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/generated) + set(BB_GEN_OUTPUTS + ${BB_GEN_DIR}/bb_ipc_client.cpp + ${BB_GEN_DIR}/bb_ipc_client.hpp + ${BB_GEN_DIR}/bb_ipc_server.hpp + ${BB_GEN_DIR}/bb_types.hpp + ${BB_GEN_DIR}/msgpack_struct_map_impl.hpp + ) + set(IPC_CODEGEN_DIR ${CMAKE_SOURCE_DIR}/../../ipc-codegen) + file(GLOB_RECURSE IPC_CODEGEN_SRC + ${IPC_CODEGEN_DIR}/src/*.ts + ${IPC_CODEGEN_DIR}/templates/cpp/*.hpp + ) + add_custom_command( + OUTPUT ${BB_GEN_OUTPUTS} + COMMAND node --experimental-strip-types --experimental-transform-types --no-warnings + ${IPC_CODEGEN_DIR}/src/generate.ts + --schema ${BB_SCHEMA} + --lang cpp + --out ${BB_GEN_DIR} + --client --server + --cpp-namespace bb::bbapi + --prefix Bb + DEPENDS ${BB_SCHEMA} ${IPC_CODEGEN_SRC} + COMMENT "Generating BB IPC client + server from bb_schema.json" + VERBATIM + ) + # Custom target so other targets can depend on the codegen having run. + add_custom_target(bb_codegen DEPENDS ${BB_GEN_OUTPUTS}) +endif() + barretenberg_module(bbapi common chonk dsl crypto_poseidon2 crypto_pedersen_commitment crypto_pedersen_hash crypto_blake2s crypto_aes128 crypto_schnorr crypto_ecdsa ecc srs) -# bbapi_tests needs vm2_stub to resolve dsl's AVM recursion constraint references +if(NOT(FUZZING) AND NOT(WASM)) + add_dependencies(bbapi_objects bb_codegen) + # Codegen-emitted bb_ipc_{client,server}.hpp + bb_types.hpp include + # ipc_runtime headers (ipc::IpcClient, make_server, install_default_signal_handlers). + target_link_libraries(bbapi_objects PUBLIC ipc_runtime) +endif() + +# bbapi_tests needs vm2_stub to resolve dsl's AVM recursion constraint references, +# and ipc_runtime for the codegen-emitted server header that c_bind_exception.test.cpp +# and bbapi.test.cpp use to drive the dispatcher. if(NOT WASM AND NOT FUZZING) - target_link_libraries(bbapi_tests PRIVATE vm2_stub) + target_link_libraries(bbapi_test_objects PRIVATE ipc_runtime) + target_link_libraries(bbapi_tests PRIVATE vm2_stub ipc_runtime) endif() diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi.hpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi.hpp index 74210e06aa01..89015d074493 100644 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi.hpp +++ b/barretenberg/cpp/src/barretenberg/bbapi/bbapi.hpp @@ -7,8 +7,5 @@ * and provides unified Command and CommandResponse types for the API. */ #include "barretenberg/bbapi/bbapi_chonk.hpp" -#include "barretenberg/bbapi/bbapi_crypto.hpp" -#include "barretenberg/bbapi/bbapi_execute.hpp" #include "barretenberg/bbapi/bbapi_shared.hpp" -#include "barretenberg/bbapi/bbapi_ultra_honk.hpp" #include "barretenberg/common/named_union.hpp" diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi.test.cpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi.test.cpp index 644192ee7185..1915e7014af8 100644 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi.test.cpp +++ b/barretenberg/cpp/src/barretenberg/bbapi/bbapi.test.cpp @@ -1,7 +1,7 @@ -#include "barretenberg/bbapi/bbapi.hpp" #include "barretenberg/api/file_io.hpp" -#include "barretenberg/bbapi/bbapi_crypto.hpp" +#include "barretenberg/bbapi/bbapi_handlers.hpp" #include "barretenberg/bbapi/bbapi_shared.hpp" +#include "barretenberg/bbapi/generated/bb_types.hpp" #include "barretenberg/chonk/private_execution_steps.hpp" #include "barretenberg/common/assert.hpp" #include "barretenberg/common/serialize.hpp" @@ -12,40 +12,46 @@ using namespace bb; -// Template for testing roundtrip serialization -template class BBApiSerializationTest : public ::testing::Test {}; - -// Enumerate each command type -using Commands = ::testing::Types; - -// Typed test suites +namespace { +// Wire (command, response) pairs for the serde roundtrip test below. +template struct WirePair { + using CommandType = Cmd; + using ResponseType = Resp; +}; +} // namespace + +// Template for testing roundtrip serialization on the codegen-emitted wire +// types. The serde fidelity of every other command pair is covered by the +// ipc-codegen golden + matrix tests; this suite is a sanity check that the +// `SERIALIZATION_FIELDS`-generated msgpack adapter round-trips correctly. +using WirePairs = ::testing::Types< + WirePair, + WirePair, + WirePair, + WirePair, + WirePair, + WirePair, + WirePair, + WirePair, + WirePair, + WirePair, + WirePair, + WirePair, + WirePair>; + template class BBApiMsgpack : public ::testing::Test {}; -TYPED_TEST_SUITE(BBApiMsgpack, Commands); +TYPED_TEST_SUITE(BBApiMsgpack, WirePairs); -// Test roundtrip serialization for UltraHonk commands TYPED_TEST(BBApiMsgpack, DefaultConstructorRoundtrip) { - TypeParam command{}; + typename TypeParam::CommandType command{}; auto [actual_command, expected_command] = msgpack_roundtrip(command); EXPECT_EQ(actual_command, expected_command); - typename TypeParam::Response response{}; + typename TypeParam::ResponseType response{}; auto [actual_response, expected_response] = msgpack_roundtrip(response); EXPECT_EQ(actual_response, expected_response); - std::cout << msgpack_schema_to_string(command) << " " << msgpack_schema_to_string(response) << std::endl; } // Regression tests for input validation at API boundaries. @@ -194,27 +200,27 @@ TEST(BBApiInputValidation, MsgpackLoadRejectsTrailingData) TEST(BBApiInputValidation, AesEncryptRejectsLengthMismatch) { bbapi::BBApiRequest request{}; - bbapi::AesEncrypt cmd{ .plaintext = std::vector(16, 0), .iv = {}, .key = {}, .length = 32 }; - EXPECT_THROW_OR_ABORT(std::move(cmd).execute(request), ".*length must equal plaintext.*"); + bbapi::wire::AesEncrypt cmd{ .plaintext = std::vector(16, 0), .iv = {}, .key = {}, .length = 32 }; + EXPECT_THROW_OR_ABORT(bbapi::handle_aes_encrypt(request, std::move(cmd)), ".*length must equal plaintext.*"); } TEST(BBApiInputValidation, AesEncryptRejectsNonBlockAlignedLength) { bbapi::BBApiRequest request{}; - bbapi::AesEncrypt cmd{ .plaintext = std::vector(17, 0), .iv = {}, .key = {}, .length = 17 }; - EXPECT_THROW_OR_ABORT(std::move(cmd).execute(request), ".*multiple of 16.*"); + bbapi::wire::AesEncrypt cmd{ .plaintext = std::vector(17, 0), .iv = {}, .key = {}, .length = 17 }; + EXPECT_THROW_OR_ABORT(bbapi::handle_aes_encrypt(request, std::move(cmd)), ".*multiple of 16.*"); } TEST(BBApiInputValidation, AesDecryptRejectsLengthMismatch) { bbapi::BBApiRequest request{}; - bbapi::AesDecrypt cmd{ .ciphertext = std::vector(16, 0), .iv = {}, .key = {}, .length = 32 }; - EXPECT_THROW_OR_ABORT(std::move(cmd).execute(request), ".*length must equal ciphertext.*"); + bbapi::wire::AesDecrypt cmd{ .ciphertext = std::vector(16, 0), .iv = {}, .key = {}, .length = 32 }; + EXPECT_THROW_OR_ABORT(bbapi::handle_aes_decrypt(request, std::move(cmd)), ".*length must equal ciphertext.*"); } TEST(BBApiInputValidation, AesDecryptRejectsNonBlockAlignedLength) { bbapi::BBApiRequest request{}; - bbapi::AesDecrypt cmd{ .ciphertext = std::vector(17, 0), .iv = {}, .key = {}, .length = 17 }; - EXPECT_THROW_OR_ABORT(std::move(cmd).execute(request), ".*multiple of 16.*"); + bbapi::wire::AesDecrypt cmd{ .ciphertext = std::vector(17, 0), .iv = {}, .key = {}, .length = 17 }; + EXPECT_THROW_OR_ABORT(bbapi::handle_aes_decrypt(request, std::move(cmd)), ".*multiple of 16.*"); } diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_avm.cpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_avm.cpp deleted file mode 100644 index 945b8018930e..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_avm.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "barretenberg/bbapi/bbapi_avm.hpp" -#include "barretenberg/api/api_avm.hpp" -#include "barretenberg/vm2/tooling/stats.hpp" - -namespace bb::bbapi { - -namespace { - -// Reset the AVM per-stage timings registry so the snapshot we return reflects only this call. -void reset_avm_stats() -{ - ::bb::avm2::Stats::get().reset(); -} - -// Take a snapshot of the AVM per-stage timings registry and convert it to the wire-format struct. -std::vector snapshot_avm_stats() -{ - auto snapshot = ::bb::avm2::Stats::get().snapshot(); - std::vector result; - result.reserve(snapshot.size()); - for (auto& [name, value] : snapshot) { - result.push_back(AvmStat{ .name = std::move(name), .value_ms = value }); - } - return result; -} - -} // namespace - -AvmProve::Response AvmProve::execute(const BBApiRequest& /*request*/) && -{ - reset_avm_stats(); - auto result = avm_prove_from_bytes(std::move(inputs)); - return Response{ - .proof = std::move(result.proof), - .stats = snapshot_avm_stats(), - }; -} - -AvmVerify::Response AvmVerify::execute(const BBApiRequest& /*request*/) && -{ - bool verified = avm_verify_from_bytes(std::move(proof), std::move(public_inputs)); - return Response{ .verified = verified }; -} - -AvmCheckCircuit::Response AvmCheckCircuit::execute(const BBApiRequest& /*request*/) && -{ - reset_avm_stats(); - bool passed = avm_check_circuit_from_bytes(std::move(inputs)); - return Response{ .passed = passed, .stats = snapshot_avm_stats() }; -} - -} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_avm.hpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_avm.hpp deleted file mode 100644 index 2457b5e1fa8e..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_avm.hpp +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once -/** - * @file bbapi_avm.hpp - * @brief AVM-specific command definitions for the Barretenberg RPC API. - * - * This file contains command structures for AVM operations including proving, - * verification, and circuit checking. When built with bb (non-AVM), these - * commands return an error response. When built with bb-avm, they work normally. - */ -#include "barretenberg/bbapi/bbapi_shared.hpp" -#include "barretenberg/common/named_union.hpp" -#include "barretenberg/ecc/curves/bn254/fr.hpp" -#include "barretenberg/serialize/msgpack.hpp" -#include -#include -#include - -namespace bb::bbapi { - -/** - * @struct AvmStat - * @brief A single AVM per-stage timing entry. `value_ms` is wall-clock milliseconds captured by - * bb::avm2::Stats during a prove or check-circuit call. - */ -struct AvmStat { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "AvmStat"; - - std::string name; - uint64_t value_ms; - SERIALIZATION_FIELDS(name, value_ms); - bool operator==(const AvmStat&) const = default; -}; - -/** - * @struct AvmProve - * @brief Prove an AVM transaction from serialized inputs. - * The inputs are opaque msgpack bytes of AvmProvingInputs. Callers should call AvmVerify - * separately if they need to verify the resulting proof. - */ -struct AvmProve { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "AvmProve"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "AvmProveResponse"; - - std::vector proof; - std::vector stats; - SERIALIZATION_FIELDS(proof, stats); - bool operator==(const Response&) const = default; - }; - - std::vector inputs; - SERIALIZATION_FIELDS(inputs); - Response execute(const BBApiRequest& request = {}) &&; - bool operator==(const AvmProve&) const = default; -}; - -/** - * @struct AvmVerify - * @brief Verify an AVM proof against serialized public inputs. - */ -struct AvmVerify { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "AvmVerify"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "AvmVerifyResponse"; - - bool verified; - SERIALIZATION_FIELDS(verified); - bool operator==(const Response&) const = default; - }; - - std::vector proof; - std::vector public_inputs; - SERIALIZATION_FIELDS(proof, public_inputs); - Response execute(const BBApiRequest& request = {}) &&; - bool operator==(const AvmVerify&) const = default; -}; - -/** - * @struct AvmCheckCircuit - * @brief Check the AVM circuit from serialized inputs. - */ -struct AvmCheckCircuit { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "AvmCheckCircuit"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "AvmCheckCircuitResponse"; - - bool passed; - std::vector stats; - SERIALIZATION_FIELDS(passed, stats); - bool operator==(const Response&) const = default; - }; - - std::vector inputs; - SERIALIZATION_FIELDS(inputs); - Response execute(const BBApiRequest& request = {}) &&; - bool operator==(const AvmCheckCircuit&) const = default; -}; - -} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk.cpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk.cpp index 40569f436517..acb10d052414 100644 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk.cpp +++ b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk.cpp @@ -1,9 +1,14 @@ #include "barretenberg/bbapi/bbapi_chonk.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_verifier.hpp" #include "barretenberg/chonk/mock_circuit_producer.hpp" #include "barretenberg/chonk/proof_compression.hpp" #include "barretenberg/commitment_schemes/ipa/ipa.hpp" #include "barretenberg/commitment_schemes/verification_key.hpp" +#include "barretenberg/common/bb_bench.hpp" #include "barretenberg/common/log.hpp" #include "barretenberg/common/memory_profile.hpp" #include "barretenberg/common/serialize.hpp" @@ -23,62 +28,54 @@ namespace bb::bbapi { -ChonkStart::Response ChonkStart::execute(BBApiRequest& request) && +wire::ChonkStartResponse handle_chonk_start(BBApiRequest& request, wire::ChonkStart&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); + BB_BENCH_NAME("ChonkStart"); - request.ivc_in_progress = std::make_shared(num_circuits); + request.ivc_in_progress = std::make_shared(cmd.num_circuits); request.ivc_stack_depth = 0; - // Clear any stale loaded-circuit state from a previous session so that - // ChonkAccumulate cannot silently reuse a circuit loaded before this ChonkStart. request.loaded_circuit_name.clear(); request.loaded_circuit_constraints.reset(); request.loaded_circuit_vk.clear(); - return Response{}; + return {}; } -ChonkLoad::Response ChonkLoad::execute(BBApiRequest& request) && +wire::ChonkLoadResponse handle_chonk_load(BBApiRequest& request, wire::ChonkLoad&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); + BB_BENCH_NAME("ChonkLoad"); if (!request.ivc_in_progress) { throw_or_abort("Chonk not started. Call ChonkStart first."); } - request.loaded_circuit_name = circuit.name; - request.loaded_circuit_constraints = acir_format::circuit_buf_to_acir_format(std::move(circuit.bytecode)); - request.loaded_circuit_vk = circuit.verification_key; + request.loaded_circuit_name = cmd.circuit.name; + request.loaded_circuit_constraints = acir_format::circuit_buf_to_acir_format(std::move(cmd.circuit.bytecode)); + request.loaded_circuit_vk = cmd.circuit.verification_key; info("ChonkLoad - loaded circuit '", request.loaded_circuit_name, "'"); - - return Response{}; + return {}; } -ChonkAccumulate::Response ChonkAccumulate::execute(BBApiRequest& request) && +wire::ChonkAccumulateResponse handle_chonk_accumulate(BBApiRequest& request, wire::ChonkAccumulate&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); + BB_BENCH_NAME("ChonkAccumulate"); if (!request.ivc_in_progress) { throw_or_abort("Chonk not started. Call ChonkStart first."); } - if (!request.loaded_circuit_constraints.has_value()) { throw_or_abort("No circuit loaded. Call ChonkLoad first."); } - acir_format::WitnessVector witness_data = acir_format::witness_buf_to_witness_vector(std::move(witness)); + acir_format::WitnessVector witness_data = acir_format::witness_buf_to_witness_vector(std::move(cmd.witness)); acir_format::AcirProgram program{ std::move(request.loaded_circuit_constraints.value()), std::move(witness_data) }; - // Clear loaded state immediately after moving out of it. This ensures that if any subsequent - // step throws, the request won't appear to still have a valid circuit loaded (the optional - // would be in a moved-from state, which is technically has_value()==true but poisoned). auto loaded_vk = std::move(request.loaded_circuit_vk); auto circuit_name = std::move(request.loaded_circuit_name); request.loaded_circuit_constraints.reset(); request.loaded_circuit_vk.clear(); request.loaded_circuit_name.clear(); - // The hiding kernel (MegaZK) is definitionally the last circuit in the IVC stack; derive flag accordingly. auto chonk = std::dynamic_pointer_cast(request.ivc_in_progress); const bool is_hiding_kernel = (request.ivc_stack_depth + 1 == chonk->get_num_circuits()); @@ -86,7 +83,6 @@ ChonkAccumulate::Response ChonkAccumulate::execute(BBApiRequest& request) && auto circuit = acir_format::create_circuit(program, metadata); std::shared_ptr precomputed_vk; - if (request.vk_policy == VkPolicy::RECOMPUTE) { precomputed_vk = nullptr; } else if (request.vk_policy == VkPolicy::DEFAULT || request.vk_policy == VkPolicy::CHECK) { @@ -95,14 +91,10 @@ ChonkAccumulate::Response ChonkAccumulate::execute(BBApiRequest& request) && precomputed_vk = from_buffer>(loaded_vk); if (request.vk_policy == VkPolicy::CHECK) { - // Note that MegaZKVerificationKey = MegaVerificationKey as C++ classes but their content differs - // between ZK and non-ZK flavors. auto computed_vk = is_hiding_kernel ? std::make_shared( Chonk::HidingKernelProverInstance(circuit).get_precomputed()) : std::make_shared( Chonk::ProverInstance(circuit).get_precomputed()); - - // Dereference to compare VK contents if (*precomputed_vk != *computed_vk) { throw_or_abort("VK check failed for circuit '" + circuit_name + "': provided VK does not match computed VK"); @@ -119,61 +111,48 @@ ChonkAccumulate::Response ChonkAccumulate::execute(BBApiRequest& request) && } request.ivc_in_progress->accumulate(circuit, precomputed_vk); request.ivc_stack_depth++; - - return Response{}; + return {}; } -ChonkProve::Response ChonkProve::execute(BBApiRequest& request) && +wire::ChonkProveResponse handle_chonk_prove(BBApiRequest& request, wire::ChonkProve&& /*cmd*/) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); + BB_BENCH_NAME("ChonkProve"); if (!request.ivc_in_progress) { throw_or_abort("Chonk not started. Call ChonkStart first."); } - if (request.ivc_stack_depth == 0) { throw_or_abort("No circuits accumulated. Call ChonkAccumulate first."); } info("ChonkProve - generating proof for ", request.ivc_stack_depth, " accumulated circuits"); - // Call prove and verify using the appropriate IVC type - Response response; - bool verification_passed = false; - info("ChonkProve - using Chonk"); auto chonk = std::dynamic_pointer_cast(request.ivc_in_progress); auto proof = chonk->prove(); auto vk_and_hash = chonk->get_hiding_kernel_vk_and_hash(); - // We verify this proof. Another bb call to verify has some overhead of loading VK/proof/SRS, - // and it is mysterious if this transaction fails later in the lifecycle. info("ChonkProve - verifying the generated proof as a sanity check"); ChonkNativeVerifier verifier(vk_and_hash); - verification_passed = verifier.verify(proof); - + bool verification_passed = verifier.verify(proof); if (!verification_passed) { throw_or_abort("Failed to verify the generated proof!"); } - response.proof = std::move(proof); - request.ivc_in_progress.reset(); request.ivc_stack_depth = 0; - - return response; + return { .proof = chonk_proof_to_wire(proof) }; } -ChonkVerify::Response ChonkVerify::execute(const BBApiRequest& /*request*/) && +wire::ChonkVerifyResponse handle_chonk_verify(BBApiRequest& /*request*/, wire::ChonkVerify&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); + BB_BENCH_NAME("ChonkVerify"); using VerificationKey = Chonk::MegaVerificationKey; - validate_vk_size(vk); + validate_vk_size(cmd.vk); - // Deserialize the hiding kernel verification key directly from buffer - auto hiding_kernel_vk = std::make_shared(from_buffer(vk)); + auto hiding_kernel_vk = std::make_shared(from_buffer(cmd.vk)); + auto proof = chonk_proof_from_wire(std::move(cmd.proof)); - // Validate total proof size: must match num_public_inputs + fixed overhead const size_t expected_proof_size = static_cast(hiding_kernel_vk->num_public_inputs) + ChonkProof::PROOF_LENGTH_WITHOUT_PUB_INPUTS; if (proof.size() != expected_proof_size) { @@ -181,24 +160,22 @@ ChonkVerify::Response ChonkVerify::execute(const BBApiRequest& /*request*/) && std::to_string(proof.size())); } - // Verify the proof using ChonkNativeVerifier auto vk_and_hash = std::make_shared(hiding_kernel_vk); ChonkNativeVerifier verifier(vk_and_hash); - const bool verified = verifier.verify(proof); - - return { .valid = verified }; + return { .valid = verifier.verify(proof) }; } -ChonkVerifyFromFields::Response ChonkVerifyFromFields::execute(const BBApiRequest& /*request*/) && +wire::ChonkVerifyFromFieldsResponse handle_chonk_verify_from_fields(BBApiRequest& /*request*/, + wire::ChonkVerifyFromFields&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); + BB_BENCH_NAME("ChonkVerifyFromFields"); using VerificationKey = Chonk::MegaVerificationKey; - validate_vk_size(vk); + validate_vk_size(cmd.vk); - auto hiding_kernel_vk = std::make_shared(from_buffer(vk)); + auto hiding_kernel_vk = std::make_shared(from_buffer(cmd.vk)); + auto proof = fr_vec_from_wire(cmd.proof); - // Validate total field count: must match num_public_inputs + fixed overhead. const size_t expected_field_count = static_cast(hiding_kernel_vk->num_public_inputs) + ChonkProof::PROOF_LENGTH_WITHOUT_PUB_INPUTS; if (proof.size() != expected_field_count) { @@ -206,39 +183,36 @@ ChonkVerifyFromFields::Response ChonkVerifyFromFields::execute(const BBApiReques std::to_string(expected_field_count) + ", got " + std::to_string(proof.size())); } - // Split the flat field array into the structured ChonkProof. Layout knowledge stays here. auto structured = ChonkProof::from_field_elements(proof); auto vk_and_hash = std::make_shared(hiding_kernel_vk); ChonkNativeVerifier verifier(vk_and_hash); - const bool verified = verifier.verify(structured); - - return { .valid = verified }; + return { .valid = verifier.verify(structured) }; } -ChonkBatchVerify::Response ChonkBatchVerify::execute(const BBApiRequest& /*request*/) && +wire::ChonkBatchVerifyResponse handle_chonk_batch_verify(BBApiRequest& /*request*/, wire::ChonkBatchVerify&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); + BB_BENCH_NAME("ChonkBatchVerify"); - if (proofs.size() != vks.size()) { - throw_or_abort("ChonkBatchVerify: proofs.size() (" + std::to_string(proofs.size()) + ") != vks.size() (" + - std::to_string(vks.size()) + ")"); + if (cmd.proofs.size() != cmd.vks.size()) { + throw_or_abort("ChonkBatchVerify: proofs.size() (" + std::to_string(cmd.proofs.size()) + ") != vks.size() (" + + std::to_string(cmd.vks.size()) + ")"); } - if (proofs.empty()) { + if (cmd.proofs.empty()) { throw_or_abort("ChonkBatchVerify: no proofs provided"); } using VerificationKey = Chonk::MegaVerificationKey; - // Phase 1: Run all non-IPA verification for each proof, collecting IPA claims std::vector> ipa_claims; std::vector> ipa_transcripts; - ipa_claims.reserve(proofs.size()); - ipa_transcripts.reserve(proofs.size()); + ipa_claims.reserve(cmd.proofs.size()); + ipa_transcripts.reserve(cmd.proofs.size()); + auto proofs = chonk_proof_vec_from_wire(std::move(cmd.proofs)); for (size_t i = 0; i < proofs.size(); ++i) { - validate_vk_size(vks[i]); - auto hiding_kernel_vk = std::make_shared(from_buffer(vks[i])); + validate_vk_size(cmd.vks[i]); + auto hiding_kernel_vk = std::make_shared(from_buffer(cmd.vks[i])); const size_t expected_proof_size = static_cast(hiding_kernel_vk->num_public_inputs) + ChonkProof::PROOF_LENGTH_WITHOUT_PUB_INPUTS; @@ -257,15 +231,13 @@ ChonkBatchVerify::Response ChonkBatchVerify::execute(const BBApiRequest& /*reque ipa_transcripts.push_back(std::make_shared(std::move(result.ipa_proof))); } - // Phase 2: Batch IPA verification with single SRS MSM auto ipa_vk = VerifierCommitmentKey{ ECCVMFlavor::ECCVM_FIXED_SIZE }; - const bool verified = IPA::batch_reduce_verify(ipa_vk, ipa_claims, ipa_transcripts); - - return { .valid = verified }; + return { .valid = IPA::batch_reduce_verify(ipa_vk, ipa_claims, ipa_transcripts) }; } -static std::shared_ptr compute_chonk_vk_from_program(acir_format::AcirProgram& program, - bool use_zk_flavor) +namespace { +std::shared_ptr compute_chonk_vk_from_program(acir_format::AcirProgram& program, + bool use_zk_flavor) { Chonk::ClientCircuit builder = acir_format::create_circuit(program); if (use_zk_flavor) { @@ -274,44 +246,42 @@ static std::shared_ptr compute_chonk_vk_from_program } return std::make_shared(Chonk::ProverInstance(builder).get_precomputed()); } +} // namespace -ChonkComputeVk::Response ChonkComputeVk::execute([[maybe_unused]] const BBApiRequest& request) && +wire::ChonkComputeVkResponse handle_chonk_compute_vk(BBApiRequest& /*request*/, wire::ChonkComputeVk&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); + BB_BENCH_NAME("ChonkComputeVk"); info("ChonkComputeVk - deriving MegaVerificationKey for circuit '", - circuit.name, + cmd.circuit.name, "'", - use_zk_flavor ? " (MegaZK)" : ""); - - auto constraint_system = acir_format::circuit_buf_to_acir_format(std::move(circuit.bytecode)); + cmd.use_zk_flavor ? " (MegaZK)" : ""); + auto constraint_system = acir_format::circuit_buf_to_acir_format(std::move(cmd.circuit.bytecode)); acir_format::AcirProgram program{ constraint_system, /*witness=*/{} }; - auto verification_key = compute_chonk_vk_from_program(program, use_zk_flavor); + auto verification_key = compute_chonk_vk_from_program(program, cmd.use_zk_flavor); info("ChonkComputeVk - VK derived, size: ", to_buffer(*verification_key).size(), " bytes"); - return { .bytes = to_buffer(*verification_key), .fields = verification_key->to_field_elements() }; + return { .bytes = to_buffer(*verification_key), .fields = fr_vec_to_wire(verification_key->to_field_elements()) }; } -ChonkCheckPrecomputedVk::Response ChonkCheckPrecomputedVk::execute([[maybe_unused]] const BBApiRequest& request) && +wire::ChonkCheckPrecomputedVkResponse handle_chonk_check_precomputed_vk(BBApiRequest& /*request*/, + wire::ChonkCheckPrecomputedVk&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); - acir_format::AcirProgram program{ acir_format::circuit_buf_to_acir_format(std::move(circuit.bytecode)), + BB_BENCH_NAME("ChonkCheckPrecomputedVk"); + acir_format::AcirProgram program{ acir_format::circuit_buf_to_acir_format(std::move(cmd.circuit.bytecode)), /*witness=*/{} }; + auto computed_vk = compute_chonk_vk_from_program(program, cmd.use_zk_flavor); - auto computed_vk = compute_chonk_vk_from_program(program, use_zk_flavor); - - if (circuit.verification_key.empty()) { - info("FAIL: Expected precomputed vk for function ", circuit.name); + if (cmd.circuit.verification_key.empty()) { + info("FAIL: Expected precomputed vk for function ", cmd.circuit.name); throw_or_abort("Missing precomputed VK"); } - validate_vk_size(circuit.verification_key); + validate_vk_size(cmd.circuit.verification_key); + auto precomputed_vk = from_buffer>(cmd.circuit.verification_key); - // Deserialize directly from buffer - auto precomputed_vk = from_buffer>(circuit.verification_key); - - Response response; + wire::ChonkCheckPrecomputedVkResponse response; response.valid = true; if (*computed_vk != *precomputed_vk) { response.valid = false; @@ -320,62 +290,54 @@ ChonkCheckPrecomputedVk::Response ChonkCheckPrecomputedVk::execute([[maybe_unuse return response; } -ChonkStats::Response ChonkStats::execute([[maybe_unused]] BBApiRequest& request) && +wire::ChonkStatsResponse handle_chonk_stats(BBApiRequest& /*request*/, wire::ChonkStats&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); - Response response; + BB_BENCH_NAME("ChonkStats"); - const auto constraint_system = acir_format::circuit_buf_to_acir_format(std::move(circuit.bytecode)); + const auto constraint_system = acir_format::circuit_buf_to_acir_format(std::move(cmd.circuit.bytecode)); acir_format::AcirProgram program{ constraint_system, {} }; - - // Get IVC constraints if any const auto& ivc_constraints = constraint_system.hn_recursion_constraints; - // Create metadata with appropriate IVC context acir_format::ProgramMetadata metadata{ .ivc = ivc_constraints.empty() ? nullptr : acir_format::create_mock_chonk_from_constraints(ivc_constraints), - .collect_gates_per_opcode = include_gates_per_opcode + .collect_gates_per_opcode = cmd.include_gates_per_opcode }; - // Create and finalize circuit auto builder = acir_format::create_circuit(program, metadata); builder.finalize_circuit(); - // Set response values + wire::ChonkStatsResponse response; response.acir_opcodes = program.constraints.num_acir_opcodes; response.circuit_size = static_cast(builder.num_gates()); - - // Optionally include gates per opcode - if (include_gates_per_opcode) { + if (cmd.include_gates_per_opcode) { response.gates_per_opcode = std::vector(program.constraints.gates_per_opcode.begin(), program.constraints.gates_per_opcode.end()); } - // Log circuit details info("ChonkStats - circuit: ", - circuit.name, + cmd.circuit.name, ", acir_opcodes: ", response.acir_opcodes, ", circuit_size: ", response.circuit_size); - - // Print execution trace details builder.blocks.summarize(); - return response; } -ChonkCompressProof::Response ChonkCompressProof::execute(const BBApiRequest& /*request*/) && +wire::ChonkCompressProofResponse handle_chonk_compress_proof(BBApiRequest& /*request*/, wire::ChonkCompressProof&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); + BB_BENCH_NAME("ChonkCompressProof"); + auto proof = chonk_proof_from_wire(std::move(cmd.proof)); return { .compressed_proof = ProofCompressor::compress_chonk_proof(proof) }; } -ChonkDecompressProof::Response ChonkDecompressProof::execute(const BBApiRequest& /*request*/) && +wire::ChonkDecompressProofResponse handle_chonk_decompress_proof(BBApiRequest& /*request*/, + wire::ChonkDecompressProof&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); - size_t mega_num_pub = ProofCompressor::compressed_mega_num_public_inputs(compressed_proof.size()); - return { .proof = ProofCompressor::decompress_chonk_proof(compressed_proof, mega_num_pub) }; + BB_BENCH_NAME("ChonkDecompressProof"); + size_t mega_num_pub = ProofCompressor::compressed_mega_num_public_inputs(cmd.compressed_proof.size()); + auto proof = ProofCompressor::decompress_chonk_proof(cmd.compressed_proof, mega_num_pub); + return { .proof = chonk_proof_to_wire(proof) }; } // ── Batch Verifier Service ────────────────────────────────────────────────── @@ -402,10 +364,8 @@ void ChonkBatchVerifierService::start(std::vectoris_running()) { throw_or_abort("ChonkBatchVerifierStart: service already running. Call ChonkBatchVerifierStop first."); @@ -502,35 +459,36 @@ ChonkBatchVerifierStart::Response ChonkBatchVerifierStart::execute(BBApiRequest& using VerificationKey = Chonk::MegaVerificationKey; std::vector> parsed_vks; - parsed_vks.reserve(vks.size()); + parsed_vks.reserve(cmd.vks.size()); - for (size_t i = 0; i < vks.size(); ++i) { - validate_vk_size(vks[i]); - auto vk = std::make_shared(from_buffer(vks[i])); + for (size_t i = 0; i < cmd.vks.size(); ++i) { + validate_vk_size(cmd.vks[i]); + auto vk = std::make_shared(from_buffer(cmd.vks[i])); parsed_vks.push_back(std::make_shared(vk)); } request.batch_verifier_service = std::make_shared(); - request.batch_verifier_service->start(std::move(parsed_vks), num_cores, batch_size, fifo_path); + request.batch_verifier_service->start(std::move(parsed_vks), cmd.num_cores, cmd.batch_size, cmd.fifo_path); return {}; } -ChonkBatchVerifierQueue::Response ChonkBatchVerifierQueue::execute(BBApiRequest& request) && +wire::ChonkBatchVerifierQueueResponse handle_chonk_batch_verifier_queue(BBApiRequest& request, + wire::ChonkBatchVerifierQueue&& cmd) { if (!request.batch_verifier_service || !request.batch_verifier_service->is_running()) { throw_or_abort("ChonkBatchVerifierQueue: service not running. Call ChonkBatchVerifierStart first."); } request.batch_verifier_service->enqueue(VerifyRequest{ - .request_id = request_id, - .vk_index = vk_index, - .proof = ChonkProof::from_field_elements(proof_fields), + .request_id = cmd.request_id, + .vk_index = cmd.vk_index, + .proof = ChonkProof::from_field_elements(fr_vec_from_wire(cmd.proof_fields)), }); - return {}; } -ChonkBatchVerifierStop::Response ChonkBatchVerifierStop::execute(BBApiRequest& request) && +wire::ChonkBatchVerifierStopResponse handle_chonk_batch_verifier_stop(BBApiRequest& request, + wire::ChonkBatchVerifierStop&& /*cmd*/) { if (!request.batch_verifier_service || !request.batch_verifier_service->is_running()) { throw_or_abort("ChonkBatchVerifierStop: service not running."); @@ -543,17 +501,20 @@ ChonkBatchVerifierStop::Response ChonkBatchVerifierStop::execute(BBApiRequest& r #else // __wasm__ -ChonkBatchVerifierStart::Response ChonkBatchVerifierStart::execute(BBApiRequest& /*request*/) && +wire::ChonkBatchVerifierStartResponse handle_chonk_batch_verifier_start(BBApiRequest& /*request*/, + wire::ChonkBatchVerifierStart&& /*cmd*/) { throw_or_abort("ChonkBatchVerifierStart is not supported in WASM builds"); } -ChonkBatchVerifierQueue::Response ChonkBatchVerifierQueue::execute(BBApiRequest& /*request*/) && +wire::ChonkBatchVerifierQueueResponse handle_chonk_batch_verifier_queue(BBApiRequest& /*request*/, + wire::ChonkBatchVerifierQueue&& /*cmd*/) { throw_or_abort("ChonkBatchVerifierQueue is not supported in WASM builds"); } -ChonkBatchVerifierStop::Response ChonkBatchVerifierStop::execute(BBApiRequest& /*request*/) && +wire::ChonkBatchVerifierStopResponse handle_chonk_batch_verifier_stop(BBApiRequest& /*request*/, + wire::ChonkBatchVerifierStop&& /*cmd*/) { throw_or_abort("ChonkBatchVerifierStop is not supported in WASM builds"); } diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk.hpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk.hpp index 65c4763519f4..37d68176dc65 100644 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk.hpp +++ b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk.hpp @@ -1,16 +1,17 @@ #pragma once /** * @file bbapi_chonk.hpp - * @brief Chonk-specific command definitions for the Barretenberg RPC API. + * @brief Stateful Chonk batch-verifier service used by the IPC handlers. * - * This file contains command structures for Chonk (Client-side Incrementally Verifiable Computation) - * operations including circuit loading, accumulation, proving, verification key computation, - * and the batch verifier service (start/queue/stop lifecycle). + * The IPC command structs themselves are gone — the codegen-emitted wire + * types are the source of truth, and the bodies live in bbapi_chonk.cpp as + * `handle_chonk_*` functions matching the codegen dispatch signature. + * + * This header keeps the `ChonkBatchVerifierService` class definition because + * `BBApiRequest::batch_verifier_service` holds a `shared_ptr<...>` to it. */ -#include "barretenberg/bbapi/bbapi_shared.hpp" #include "barretenberg/chonk/chonk.hpp" #include "barretenberg/common/named_union.hpp" -#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/serialize/msgpack.hpp" #ifndef __wasm__ @@ -28,331 +29,6 @@ namespace bb::bbapi { -/** - * @struct ChonkStart - * @brief Initialize a new Chonk instance for incremental proof accumulation - * - * @note Only one IVC request can be made at a time for each batch_request. - */ -struct ChonkStart { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkStart"; - - /** - * @struct Response - * @brief Empty response indicating successful initialization - */ - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkStartResponse"; - // Empty response - success indicated by no exception - void msgpack(auto&& pack_fn) { pack_fn(); } - bool operator==(const Response&) const = default; - }; - // Number of circuits to be accumulated. - uint32_t num_circuits; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(num_circuits); - bool operator==(const ChonkStart&) const = default; -}; - -/** - * @struct ChonkLoad - * @brief Load a circuit into the Chonk instance for accumulation - */ -struct ChonkLoad { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkLoad"; - - /** - * @struct Response - * @brief Empty response indicating successful circuit loading - */ - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkLoadResponse"; - // Empty response - success indicated by no exception - void msgpack(auto&& pack_fn) { pack_fn(); } - bool operator==(const Response&) const = default; - }; - - /** @brief Circuit to be loaded with its bytecode and verification key */ - CircuitInput circuit; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(circuit); - bool operator==(const ChonkLoad&) const = default; -}; - -/** - * @struct ChonkAccumulate - * @brief Accumulate the previously loaded circuit into the IVC proof - */ -struct ChonkAccumulate { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkAccumulate"; - - /** - * @struct Response - * @brief Empty response indicating successful circuit accumulation - */ - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkAccumulateResponse"; - // Empty response - success indicated by no exception - void msgpack(auto&& pack_fn) { pack_fn(); } - bool operator==(const Response&) const = default; - }; - - /** @brief Serialized witness data for the last loaded circuit */ - std::vector witness; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(witness); - bool operator==(const ChonkAccumulate&) const = default; -}; - -/** - * @struct ChonkProve - * @brief Generate a proof for all accumulated circuits - */ -struct ChonkProve { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkProve"; - - /** - * @struct Response - * @brief Contains the generated IVC proof - */ - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkProveResponse"; - - /** @brief Complete IVC proof for all accumulated circuits */ - ChonkProof proof; - SERIALIZATION_FIELDS(proof); - bool operator==(const Response&) const = default; - }; - Response execute(BBApiRequest& request) &&; - void msgpack(auto&& pack_fn) { pack_fn(); } - bool operator==(const ChonkProve&) const = default; -}; - -/** - * @struct ChonkVerify - * @brief Verify a Chonk proof with its verification key - */ -struct ChonkVerify { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkVerify"; - - /** - * @struct Response - * @brief Contains the verification result - */ - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkVerifyResponse"; - - /** @brief True if the proof is valid */ - bool valid; - SERIALIZATION_FIELDS(valid); - bool operator==(const Response&) const = default; - }; - - /** @brief The Chonk proof to verify */ - ChonkProof proof; - /** @brief The verification key */ - std::vector vk; - Response execute(const BBApiRequest& request = {}) &&; - SERIALIZATION_FIELDS(proof, vk); - bool operator==(const ChonkVerify&) const = default; -}; - -/** - * @struct ChonkVerifyFromFields - * @brief Verify a Chonk proof passed as a flat field-element array (with public inputs prepended). - * - * The split into structured ChonkProof sub-proofs is done server-side via - * ChonkProof::from_field_elements, so callers do not need to know the per-component sub-proof - * sizes. This is the recommended entry point for TypeScript callers that hold the proof as a - * flat Fr[] (e.g. from tx.chonkProof.attachPublicInputs). - */ -struct ChonkVerifyFromFields { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkVerifyFromFields"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkVerifyFromFieldsResponse"; - - /** @brief True if the proof is valid */ - bool valid; - SERIALIZATION_FIELDS(valid); - bool operator==(const Response&) const = default; - }; - - /** @brief Flat proof field elements with public inputs prepended */ - std::vector proof; - /** @brief The verification key */ - std::vector vk; - Response execute(const BBApiRequest& request = {}) &&; - SERIALIZATION_FIELDS(proof, vk); - bool operator==(const ChonkVerifyFromFields&) const = default; -}; - -/** - * @struct ChonkComputeVk - * @brief Compute MegaHonk verification key for a circuit to be accumulated in Chonk - * - * @details This unified command replaces the former ChonkComputeStandaloneVk and ChonkComputeIvcVk. - * Both standalone circuits (to be accumulated) and the IVC hiding kernel use the same MegaVerificationKey, - * so a single implementation suffices for all Chonk VK computation needs. - */ -struct ChonkComputeVk { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkComputeVk"; - - /** - * @struct Response - * @brief Contains the computed verification key in multiple formats - */ - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkComputeVkResponse"; - - /** @brief Serialized MegaVerificationKey in binary format */ - std::vector bytes; - /** @brief Verification key as array of field elements */ - std::vector fields; - SERIALIZATION_FIELDS(bytes, fields); - bool operator==(const Response&) const = default; - }; - - CircuitInputNoVK circuit; - /** @brief When true, derive VK using MegaZKFlavor; otherwise MegaFlavor. - * The caller sets this to true for the hiding-kernel circuit. */ - bool use_zk_flavor = false; - Response execute([[maybe_unused]] const BBApiRequest& request = {}) &&; - SERIALIZATION_FIELDS(circuit, use_zk_flavor); - bool operator==(const ChonkComputeVk&) const = default; -}; - -/** - * @struct ChonkCheckPrecomputedVk - * @brief Verify that a precomputed verification key matches the circuit - */ -struct ChonkCheckPrecomputedVk { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkCheckPrecomputedVk"; - - /** - * @struct Response - * @brief Contains the validation result - */ - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkCheckPrecomputedVkResponse"; - - /** @brief True if the precomputed VK matches the circuit */ - bool valid; - /** @brief The actual VK it should be. */ - std::vector actual_vk; - SERIALIZATION_FIELDS(valid, actual_vk); - bool operator==(const Response&) const = default; - }; - - /** @brief Circuit with its precomputed verification key */ - CircuitInput circuit; - /** @brief When true, derive VK using MegaZKFlavor; otherwise MegaFlavor. - * The caller sets this to true for the hiding-kernel circuit. */ - bool use_zk_flavor = false; - - Response execute(const BBApiRequest& request = {}) &&; - SERIALIZATION_FIELDS(circuit, use_zk_flavor); - bool operator==(const ChonkCheckPrecomputedVk&) const = default; -}; - -/** - * @struct ChonkStats - * @brief Get gate counts for a circuit - */ -struct ChonkStats { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkStats"; - - /** - * @struct Response - * @brief Contains gate count information - */ - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkStatsResponse"; - - /** @brief Number of ACIR opcodes */ - uint32_t acir_opcodes; - /** @brief Circuit size (total number of gates) */ - uint32_t circuit_size; - /** @brief Optional: gate counts per opcode */ - std::vector gates_per_opcode; - SERIALIZATION_FIELDS(acir_opcodes, circuit_size, gates_per_opcode); - bool operator==(const Response&) const = default; - }; - - /** @brief The circuit to analyze */ - CircuitInputNoVK circuit; - /** @brief Whether to include detailed gate counts per opcode */ - bool include_gates_per_opcode; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(circuit, include_gates_per_opcode); - bool operator==(const ChonkStats&) const = default; -}; - -/** - * @struct ChonkBatchVerify - * @brief Batch-verify multiple Chonk proofs with a single IPA SRS MSM - */ -struct ChonkBatchVerify { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkBatchVerify"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkBatchVerifyResponse"; - bool valid; - SERIALIZATION_FIELDS(valid); - bool operator==(const Response&) const = default; - }; - - std::vector proofs; - std::vector> vks; - Response execute(const BBApiRequest& request = {}) &&; - SERIALIZATION_FIELDS(proofs, vks); - bool operator==(const ChonkBatchVerify&) const = default; -}; - -/** - * @struct ChonkCompressProof - * @brief Compress a Chonk proof to a compact byte representation - * - * @details Uses point compression and uniform 32-byte encoding to reduce proof size (~1.72x). - */ -struct ChonkCompressProof { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkCompressProof"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkCompressProofResponse"; - std::vector compressed_proof; - SERIALIZATION_FIELDS(compressed_proof); - bool operator==(const Response&) const = default; - }; - - ChonkProof proof; - Response execute(const BBApiRequest& request = {}) &&; - SERIALIZATION_FIELDS(proof); - bool operator==(const ChonkCompressProof&) const = default; -}; - -/** - * @struct ChonkDecompressProof - * @brief Decompress a compressed Chonk proof back to field elements - * - * @details Derives mega_num_public_inputs from the compressed size automatically. - */ -struct ChonkDecompressProof { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkDecompressProof"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkDecompressProofResponse"; - ChonkProof proof; - SERIALIZATION_FIELDS(proof); - bool operator==(const Response&) const = default; - }; - - std::vector compressed_proof; - Response execute(const BBApiRequest& request = {}) &&; - SERIALIZATION_FIELDS(compressed_proof); - bool operator==(const ChonkDecompressProof&) const = default; -}; - #ifndef __wasm__ /** * @brief FIFO-streaming batch verification service for Chonk proofs. @@ -393,67 +69,4 @@ class ChonkBatchVerifierService { }; #endif // __wasm__ -/** - * @struct ChonkBatchVerifierStart - * @brief Start the batch verifier service. - */ -struct ChonkBatchVerifierStart { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkBatchVerifierStart"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkBatchVerifierStartResponse"; - void msgpack(auto&& pack_fn) { pack_fn(); } - bool operator==(const Response&) const = default; - }; - - std::vector> vks; // Serialized verification keys - uint32_t num_cores = 0; // 0 = auto - uint32_t batch_size = 8; - std::string fifo_path; - - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(vks, num_cores, batch_size, fifo_path); - bool operator==(const ChonkBatchVerifierStart&) const = default; -}; - -/** - * @struct ChonkBatchVerifierQueue - * @brief Enqueue a proof for batch verification. - */ -struct ChonkBatchVerifierQueue { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkBatchVerifierQueue"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkBatchVerifierQueueResponse"; - void msgpack(auto&& pack_fn) { pack_fn(); } - bool operator==(const Response&) const = default; - }; - - uint64_t request_id = 0; - uint32_t vk_index = 0; - std::vector proof_fields; - - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(request_id, vk_index, proof_fields); - bool operator==(const ChonkBatchVerifierQueue&) const = default; -}; - -/** - * @struct ChonkBatchVerifierStop - * @brief Stop the batch verifier service. - */ -struct ChonkBatchVerifierStop { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkBatchVerifierStop"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ChonkBatchVerifierStopResponse"; - void msgpack(auto&& pack_fn) { pack_fn(); } - bool operator==(const Response&) const = default; - }; - - Response execute(BBApiRequest& request) &&; - void msgpack(auto&& pack_fn) { pack_fn(); } - bool operator==(const ChonkBatchVerifierStop&) const = default; -}; - } // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_crypto.cpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_crypto.cpp deleted file mode 100644 index b30cf0c890a2..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_crypto.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// === AUDIT STATUS === -// internal: { status: not started, auditors: [], commit: dd03c4a23ab067274b4964cacb36d1545f73fb14} -// external_1: { status: not started, auditors: [], commit: } -// external_2: { status: not started, auditors: [], commit: } -// ===================== - -/** - * @file bbapi_crypto.cpp - * @brief Implementation of cryptographic command execution for the Barretenberg RPC API - */ -#include "barretenberg/bbapi/bbapi_crypto.hpp" -#include "barretenberg/common/assert.hpp" -#include "barretenberg/common/throw_or_abort.hpp" -#include "barretenberg/crypto/aes128/aes128.hpp" -#include "barretenberg/crypto/blake2s/blake2s.hpp" -#include "barretenberg/crypto/pedersen_commitment/pedersen.hpp" -#include "barretenberg/crypto/pedersen_hash/pedersen.hpp" -#include "barretenberg/crypto/poseidon2/poseidon2.hpp" -#include "barretenberg/crypto/poseidon2/poseidon2_permutation.hpp" - -namespace bb::bbapi { - -Poseidon2Hash::Response Poseidon2Hash::execute(BB_UNUSED BBApiRequest& request) && -{ - return { crypto::Poseidon2::hash(inputs) }; -} - -Poseidon2Permutation::Response Poseidon2Permutation::execute(BB_UNUSED BBApiRequest& request) && -{ - using Permutation = crypto::Poseidon2Permutation; - - // inputs is already std::array, direct use - return { Permutation::permutation(inputs) }; -} - -PedersenCommit::Response PedersenCommit::execute(BB_UNUSED BBApiRequest& request) && -{ - crypto::GeneratorContext ctx; - ctx.offset = static_cast(hash_index); - return { crypto::pedersen_commitment::commit_native(inputs, ctx) }; -} - -PedersenHash::Response PedersenHash::execute(BB_UNUSED BBApiRequest& request) && -{ - crypto::GeneratorContext ctx; - ctx.offset = static_cast(hash_index); - return { crypto::pedersen_hash::hash(inputs, ctx) }; -} - -PedersenHashBuffer::Response PedersenHashBuffer::execute(BB_UNUSED BBApiRequest& request) && -{ - crypto::GeneratorContext ctx; - ctx.offset = static_cast(hash_index); - return { crypto::pedersen_hash::hash_buffer(input, ctx) }; -} - -Blake2s::Response Blake2s::execute(BB_UNUSED BBApiRequest& request) && -{ - return { crypto::blake2s(data) }; -} - -Blake2sToField::Response Blake2sToField::execute(BB_UNUSED BBApiRequest& request) && -{ - auto hash_result = crypto::blake2s(data); - return { fr::serialize_from_buffer(hash_result.data()) }; -} - -AesEncrypt::Response AesEncrypt::execute(BB_UNUSED BBApiRequest& request) && -{ - BB_ASSERT(length == plaintext.size(), "AesEncrypt: length must equal plaintext.size()"); - BB_ASSERT(length % 16 == 0, "AesEncrypt: length must be a multiple of 16"); - - // Copy plaintext as AES encrypts in-place - std::vector result = plaintext; - result.resize(length); - - crypto::aes128_encrypt_buffer_cbc(result.data(), iv.data(), key.data(), length); - - return { std::move(result) }; -} - -AesDecrypt::Response AesDecrypt::execute(BB_UNUSED BBApiRequest& request) && -{ - BB_ASSERT(length == ciphertext.size(), "AesDecrypt: length must equal ciphertext.size()"); - BB_ASSERT(length % 16 == 0, "AesDecrypt: length must be a multiple of 16"); - - // Copy ciphertext as AES decrypts in-place - std::vector result = ciphertext; - result.resize(length); - - crypto::aes128_decrypt_buffer_cbc(result.data(), iv.data(), key.data(), length); - - return { std::move(result) }; -} - -} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_crypto.hpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_crypto.hpp deleted file mode 100644 index 929da35e7d84..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_crypto.hpp +++ /dev/null @@ -1,215 +0,0 @@ -// === AUDIT STATUS === -// internal: { status: not started, auditors: [], commit: dd03c4a23ab067274b4964cacb36d1545f73fb14} -// external_1: { status: not started, auditors: [], commit: } -// external_2: { status: not started, auditors: [], commit: } -// ===================== - -#pragma once -/** - * @file bbapi_crypto.hpp - * @brief Cryptographic primitives command definitions for the Barretenberg RPC API. - * - * This file contains command structures for cryptographic operations including - * Poseidon2, Pedersen, Blake2s, and AES. - */ -#include "barretenberg/bbapi/bbapi_shared.hpp" -#include "barretenberg/common/named_union.hpp" -#include "barretenberg/ecc/curves/bn254/fr.hpp" -#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" -#include "barretenberg/serialize/msgpack.hpp" -#include -#include -#include - -namespace bb::bbapi { - -/** - * @struct Poseidon2Hash - * @brief Compute Poseidon2 hash of input field elements - */ -struct Poseidon2Hash { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Poseidon2Hash"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Poseidon2HashResponse"; - fr hash; - SERIALIZATION_FIELDS(hash); - bool operator==(const Response&) const = default; - }; - - std::vector inputs; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(inputs); - bool operator==(const Poseidon2Hash&) const = default; -}; - -/** - * @struct Poseidon2Permutation - * @brief Compute Poseidon2 permutation on state (4 field elements) - */ -struct Poseidon2Permutation { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Poseidon2Permutation"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Poseidon2PermutationResponse"; - std::array outputs; - SERIALIZATION_FIELDS(outputs); - bool operator==(const Response&) const = default; - }; - - std::array inputs; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(inputs); - bool operator==(const Poseidon2Permutation&) const = default; -}; - -/** - * @struct PedersenCommit - * @brief Compute Pedersen commitment to field elements - */ -struct PedersenCommit { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "PedersenCommit"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "PedersenCommitResponse"; - grumpkin::g1::affine_element point; - SERIALIZATION_FIELDS(point); - bool operator==(const Response&) const = default; - }; - - std::vector inputs; - uint32_t hash_index; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(inputs, hash_index); - bool operator==(const PedersenCommit&) const = default; -}; - -/** - * @struct PedersenHash - * @brief Compute Pedersen hash of field elements - */ -struct PedersenHash { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "PedersenHash"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "PedersenHashResponse"; - grumpkin::fq hash; - SERIALIZATION_FIELDS(hash); - bool operator==(const Response&) const = default; - }; - - std::vector inputs; - uint32_t hash_index; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(inputs, hash_index); - bool operator==(const PedersenHash&) const = default; -}; - -/** - * @struct PedersenHashBuffer - * @brief Compute Pedersen hash of raw buffer - */ -struct PedersenHashBuffer { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "PedersenHashBuffer"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "PedersenHashBufferResponse"; - grumpkin::fq hash; - SERIALIZATION_FIELDS(hash); - bool operator==(const Response&) const = default; - }; - - std::vector input; - uint32_t hash_index; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(input, hash_index); - bool operator==(const PedersenHashBuffer&) const = default; -}; - -/** - * @struct Blake2s - * @brief Compute Blake2s hash - */ -struct Blake2s { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Blake2s"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Blake2sResponse"; - std::array hash; - SERIALIZATION_FIELDS(hash); - bool operator==(const Response&) const = default; - }; - - std::vector data; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(data); - bool operator==(const Blake2s&) const = default; -}; - -/** - * @struct Blake2sToField - * @brief Compute Blake2s hash and convert to field element - */ -struct Blake2sToField { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Blake2sToField"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Blake2sToFieldResponse"; - fr field; - SERIALIZATION_FIELDS(field); - bool operator==(const Response&) const = default; - }; - - std::vector data; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(data); - bool operator==(const Blake2sToField&) const = default; -}; - -/** - * @struct AesEncrypt - * @brief AES-128 CBC encryption - */ -struct AesEncrypt { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "AesEncrypt"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "AesEncryptResponse"; - std::vector ciphertext; - SERIALIZATION_FIELDS(ciphertext); - bool operator==(const Response&) const = default; - }; - - std::vector plaintext; - std::array iv; - std::array key; - uint32_t length; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(plaintext, iv, key, length); - bool operator==(const AesEncrypt&) const = default; -}; - -/** - * @struct AesDecrypt - * @brief AES-128 CBC decryption - */ -struct AesDecrypt { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "AesDecrypt"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "AesDecryptResponse"; - std::vector plaintext; - SERIALIZATION_FIELDS(plaintext); - bool operator==(const Response&) const = default; - }; - - std::vector ciphertext; - std::array iv; - std::array key; - uint32_t length; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(ciphertext, iv, key, length); - bool operator==(const AesDecrypt&) const = default; -}; - -} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ecc.cpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ecc.cpp deleted file mode 100644 index 7bcc3fc2edfc..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ecc.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/** - * @file bbapi_ecc.cpp - * @brief Implementation of elliptic curve command execution for the Barretenberg RPC API - */ -#include "barretenberg/bbapi/bbapi_ecc.hpp" - -namespace bb::bbapi { - -GrumpkinMul::Response GrumpkinMul::execute(BBApiRequest& request) && -{ - if (!point.on_curve()) { - BBAPI_ERROR(request, "Input point must be on the curve"); - } - return { point * scalar }; -} - -GrumpkinAdd::Response GrumpkinAdd::execute(BBApiRequest& request) && -{ - if (!point_a.on_curve()) { - BBAPI_ERROR(request, "Input point_a must be on the curve"); - } - if (!point_b.on_curve()) { - BBAPI_ERROR(request, "Input point_b must be on the curve"); - } - return { point_a + point_b }; -} - -GrumpkinBatchMul::Response GrumpkinBatchMul::execute(BBApiRequest& request) && -{ - for (const auto& p : points) { - if (!p.on_curve()) { - BBAPI_ERROR(request, "Input point must be on the curve"); - } - } - auto output = grumpkin::g1::element::batch_mul_with_endomorphism(points, scalar); - return { std::move(output) }; -} - -GrumpkinGetRandomFr::Response GrumpkinGetRandomFr::execute(BB_UNUSED BBApiRequest& request) && -{ - return { bb::fr::random_element() }; -} - -GrumpkinReduce512::Response GrumpkinReduce512::execute(BB_UNUSED BBApiRequest& request) && -{ - auto bigint_input = from_buffer(input.data()); - uint512_t barretenberg_modulus(bb::fr::modulus); - uint512_t target_output = bigint_input % barretenberg_modulus; - return { bb::fr(target_output.lo) }; -} - -Secp256k1Mul::Response Secp256k1Mul::execute(BBApiRequest& request) && -{ - if (!point.on_curve()) { - BBAPI_ERROR(request, "Input point must be on the curve"); - } - return { point * scalar }; -} - -Secp256k1GetRandomFr::Response Secp256k1GetRandomFr::execute(BB_UNUSED BBApiRequest& request) && -{ - return { secp256k1::fr::random_element() }; -} - -Secp256k1Reduce512::Response Secp256k1Reduce512::execute(BB_UNUSED BBApiRequest& request) && -{ - auto bigint_input = from_buffer(input.data()); - uint512_t secp256k1_modulus(secp256k1::fr::modulus); - uint512_t target_output = bigint_input % secp256k1_modulus; - return { secp256k1::fr(target_output.lo) }; -} - -Bn254FrSqrt::Response Bn254FrSqrt::execute(BB_UNUSED BBApiRequest& request) && -{ - auto [is_sqr, root] = input.sqrt(); - return { is_sqr, root }; -} - -Bn254FqSqrt::Response Bn254FqSqrt::execute(BB_UNUSED BBApiRequest& request) && -{ - auto [is_sqr, root] = input.sqrt(); - return { is_sqr, root }; -} - -Bn254G1Mul::Response Bn254G1Mul::execute(BBApiRequest& request) && -{ - if (!point.on_curve()) { - BBAPI_ERROR(request, "Input point must be on the curve"); - } - auto result = point * scalar; - if (!result.on_curve()) { - BBAPI_ERROR(request, "Output point must be on the curve"); - } - return { result }; -} - -Bn254G2Mul::Response Bn254G2Mul::execute(BBApiRequest& request) && -{ - if (!point.on_curve()) { - BBAPI_ERROR(request, "Input point must be on the curve"); - } - // BN254 G2 has cofactor h2 ≈ 2^254. An on-curve point may lie in a cofactor subgroup of order - // dividing h2 rather than the prime-order subgroup; we do not want to allow such points - // as inputs to bbapi. - if (!point.is_in_prime_subgroup()) { - BBAPI_ERROR(request, "Input point must lie in the prime-order subgroup"); - } - auto result = point * scalar; - if (!result.on_curve()) { - BBAPI_ERROR(request, "Output point must be on the curve"); - } - return { result }; -} - -Bn254G1IsOnCurve::Response Bn254G1IsOnCurve::execute(BB_UNUSED BBApiRequest& request) && -{ - return { point.on_curve() }; -} - -Bn254G1FromCompressed::Response Bn254G1FromCompressed::execute(BBApiRequest& request) && -{ - // Convert 32-byte array to uint256_t - uint256_t compressed_value = from_buffer(compressed.data()); - // Decompress the point - auto point = bb::g1::affine_element::from_compressed(compressed_value); - // Verify the decompressed point is on the curve - if (!point.on_curve()) { - BBAPI_ERROR(request, "Decompressed point is not on the curve"); - } - return { point }; -} - -} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ecc.hpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ecc.hpp deleted file mode 100644 index 5d47a227447b..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ecc.hpp +++ /dev/null @@ -1,312 +0,0 @@ -#pragma once -/** - * @file bbapi_ecc.hpp - * @brief Elliptic curve operations command definitions for the Barretenberg RPC API. - * - * This file contains command structures for elliptic curve operations including - * Grumpkin, Secp256k1, and BN254 field operations. - */ -#include "barretenberg/bbapi/bbapi_shared.hpp" -#include "barretenberg/common/named_union.hpp" -#include "barretenberg/ecc/curves/bn254/bn254.hpp" -#include "barretenberg/ecc/curves/bn254/fr.hpp" -#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" -#include "barretenberg/ecc/curves/secp256k1/secp256k1.hpp" -#include "barretenberg/serialize/msgpack.hpp" -#include -#include -#include - -namespace bb::bbapi { - -/** - * @struct GrumpkinMul - * @brief Multiply a Grumpkin point by a scalar - */ -struct GrumpkinMul { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "GrumpkinMul"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "GrumpkinMulResponse"; - grumpkin::g1::affine_element point; - SERIALIZATION_FIELDS(point); - bool operator==(const Response&) const = default; - }; - - grumpkin::g1::affine_element point; - grumpkin::fr scalar; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(point, scalar); - bool operator==(const GrumpkinMul&) const = default; -}; - -/** - * @struct GrumpkinAdd - * @brief Add two Grumpkin points - */ -struct GrumpkinAdd { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "GrumpkinAdd"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "GrumpkinAddResponse"; - grumpkin::g1::affine_element point; - SERIALIZATION_FIELDS(point); - bool operator==(const Response&) const = default; - }; - - grumpkin::g1::affine_element point_a; - grumpkin::g1::affine_element point_b; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(point_a, point_b); - bool operator==(const GrumpkinAdd&) const = default; -}; - -/** - * @struct GrumpkinBatchMul - * @brief Multiply multiple Grumpkin points by a single scalar - */ -struct GrumpkinBatchMul { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "GrumpkinBatchMul"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "GrumpkinBatchMulResponse"; - std::vector points; - SERIALIZATION_FIELDS(points); - bool operator==(const Response&) const = default; - }; - - std::vector points; - grumpkin::fr scalar; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(points, scalar); - bool operator==(const GrumpkinBatchMul&) const = default; -}; - -/** - * @struct GrumpkinGetRandomFr - * @brief Get a random Grumpkin field element (BN254 Fr) - */ -struct GrumpkinGetRandomFr { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "GrumpkinGetRandomFr"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "GrumpkinGetRandomFrResponse"; - bb::fr value; - SERIALIZATION_FIELDS(value); - bool operator==(const Response&) const = default; - }; - - // Empty struct for commands with no input - use a dummy field for msgpack - uint8_t dummy = 0; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(dummy); - bool operator==(const GrumpkinGetRandomFr&) const = default; -}; - -/** - * @struct GrumpkinReduce512 - * @brief Reduce a 512-bit value modulo Grumpkin scalar field (BN254 Fr) - */ -struct GrumpkinReduce512 { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "GrumpkinReduce512"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "GrumpkinReduce512Response"; - bb::fr value; - SERIALIZATION_FIELDS(value); - bool operator==(const Response&) const = default; - }; - - std::array input; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(input); - bool operator==(const GrumpkinReduce512&) const = default; -}; - -/** - * @struct Secp256k1Mul - * @brief Multiply a Secp256k1 point by a scalar - */ -struct Secp256k1Mul { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Secp256k1Mul"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Secp256k1MulResponse"; - secp256k1::g1::affine_element point; - SERIALIZATION_FIELDS(point); - bool operator==(const Response&) const = default; - }; - - secp256k1::g1::affine_element point; - secp256k1::fr scalar; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(point, scalar); - bool operator==(const Secp256k1Mul&) const = default; -}; - -/** - * @struct Secp256k1GetRandomFr - * @brief Get a random Secp256k1 field element - */ -struct Secp256k1GetRandomFr { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Secp256k1GetRandomFr"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Secp256k1GetRandomFrResponse"; - secp256k1::fr value; - SERIALIZATION_FIELDS(value); - bool operator==(const Response&) const = default; - }; - - // Empty struct for commands with no input - use a dummy field for msgpack - uint8_t dummy = 0; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(dummy); - bool operator==(const Secp256k1GetRandomFr&) const = default; -}; - -/** - * @struct Secp256k1Reduce512 - * @brief Reduce a 512-bit value modulo Secp256k1 scalar field - */ -struct Secp256k1Reduce512 { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Secp256k1Reduce512"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Secp256k1Reduce512Response"; - secp256k1::fr value; - SERIALIZATION_FIELDS(value); - bool operator==(const Response&) const = default; - }; - - std::array input; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(input); - bool operator==(const Secp256k1Reduce512&) const = default; -}; - -/** - * @struct Bn254FrSqrt - * @brief Compute square root of a BN254 Fr (scalar field) element - */ -struct Bn254FrSqrt { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Bn254FrSqrt"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Bn254FrSqrtResponse"; - bool is_square_root; - bb::fr value; - SERIALIZATION_FIELDS(is_square_root, value); - bool operator==(const Response&) const = default; - }; - - bb::fr input; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(input); - bool operator==(const Bn254FrSqrt&) const = default; -}; - -/** - * @struct Bn254FqSqrt - * @brief Compute square root of a BN254 Fq (base field) element - */ -struct Bn254FqSqrt { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Bn254FqSqrt"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Bn254FqSqrtResponse"; - bool is_square_root; - bb::fq value; - SERIALIZATION_FIELDS(is_square_root, value); - bool operator==(const Response&) const = default; - }; - - bb::fq input; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(input); - bool operator==(const Bn254FqSqrt&) const = default; -}; - -/** - * @struct Bn254G1Mul - * @brief Multiply a BN254 G1 point by a scalar - */ -struct Bn254G1Mul { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Bn254G1Mul"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Bn254G1MulResponse"; - bb::g1::affine_element point; - SERIALIZATION_FIELDS(point); - bool operator==(const Response&) const = default; - }; - - bb::g1::affine_element point; - bb::fr scalar; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(point, scalar); - bool operator==(const Bn254G1Mul&) const = default; -}; - -/** - * @struct Bn254G2Mul - * @brief Multiply a BN254 G2 point by a scalar - */ -struct Bn254G2Mul { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Bn254G2Mul"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Bn254G2MulResponse"; - bb::g2::affine_element point; - SERIALIZATION_FIELDS(point); - bool operator==(const Response&) const = default; - }; - - bb::g2::affine_element point; - bb::fr scalar; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(point, scalar); - bool operator==(const Bn254G2Mul&) const = default; -}; - -/** - * @struct Bn254G1IsOnCurve - * @brief Check if a BN254 G1 point is on the curve - */ -struct Bn254G1IsOnCurve { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Bn254G1IsOnCurve"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Bn254G1IsOnCurveResponse"; - bool is_on_curve; - SERIALIZATION_FIELDS(is_on_curve); - bool operator==(const Response&) const = default; - }; - - bb::g1::affine_element point; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(point); - bool operator==(const Bn254G1IsOnCurve&) const = default; -}; - -/** - * @struct Bn254G1FromCompressed - * @brief Decompress a BN254 G1 point from compressed form - */ -struct Bn254G1FromCompressed { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Bn254G1FromCompressed"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Bn254G1FromCompressedResponse"; - bb::g1::affine_element point; - SERIALIZATION_FIELDS(point); - bool operator==(const Response&) const = default; - }; - - std::array compressed = {}; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(compressed); - bool operator==(const Bn254G1FromCompressed&) const = default; -}; - -} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ecdsa.cpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ecdsa.cpp deleted file mode 100644 index c664e74d65a5..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ecdsa.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @file bbapi_ecdsa.cpp - * @brief Implementation of ECDSA signature command execution for the Barretenberg RPC API - */ -#include "barretenberg/bbapi/bbapi_ecdsa.hpp" -#include "barretenberg/common/throw_or_abort.hpp" - -namespace bb::bbapi { - -// Secp256k1 implementations -EcdsaSecp256k1ComputePublicKey::Response EcdsaSecp256k1ComputePublicKey::execute(BB_UNUSED BBApiRequest& request) && -{ - return { secp256k1::g1::one * private_key }; -} - -EcdsaSecp256k1ConstructSignature::Response EcdsaSecp256k1ConstructSignature::execute(BB_UNUSED BBApiRequest& request) && -{ - auto pub_key = secp256k1::g1::one * private_key; - crypto::ecdsa_key_pair key_pair = { private_key, pub_key }; - - std::string message_str(reinterpret_cast(message.data()), message.size()); - auto sig = crypto::ecdsa_construct_signature( - message_str, key_pair); - - return { sig.r, sig.s, sig.v }; -} - -EcdsaSecp256k1RecoverPublicKey::Response EcdsaSecp256k1RecoverPublicKey::execute(BB_UNUSED BBApiRequest& request) && -{ - crypto::ecdsa_signature sig = { r, s, v }; - std::string message_str(reinterpret_cast(message.data()), message.size()); - return { crypto::ecdsa_recover_public_key( - message_str, sig) }; -} - -EcdsaSecp256k1VerifySignature::Response EcdsaSecp256k1VerifySignature::execute(BB_UNUSED BBApiRequest& request) && -{ - crypto::ecdsa_signature sig = { r, s, v }; - std::string message_str(reinterpret_cast(message.data()), message.size()); - return { crypto::ecdsa_verify_signature( - message_str, public_key, sig) }; -} - -// Secp256r1 implementations -EcdsaSecp256r1ComputePublicKey::Response EcdsaSecp256r1ComputePublicKey::execute(BB_UNUSED BBApiRequest& request) && -{ - return { secp256r1::g1::one * private_key }; -} - -EcdsaSecp256r1ConstructSignature::Response EcdsaSecp256r1ConstructSignature::execute(BB_UNUSED BBApiRequest& request) && -{ - auto pub_key = secp256r1::g1::one * private_key; - crypto::ecdsa_key_pair key_pair = { private_key, pub_key }; - - std::string message_str(reinterpret_cast(message.data()), message.size()); - auto sig = crypto::ecdsa_construct_signature( - message_str, key_pair); - - return { sig.r, sig.s, sig.v }; -} - -EcdsaSecp256r1RecoverPublicKey::Response EcdsaSecp256r1RecoverPublicKey::execute(BB_UNUSED BBApiRequest& request) && -{ - crypto::ecdsa_signature sig = { r, s, v }; - std::string message_str(reinterpret_cast(message.data()), message.size()); - return { crypto::ecdsa_recover_public_key( - message_str, sig) }; -} - -EcdsaSecp256r1VerifySignature::Response EcdsaSecp256r1VerifySignature::execute(BB_UNUSED BBApiRequest& request) && -{ - crypto::ecdsa_signature sig = { r, s, v }; - std::string message_str(reinterpret_cast(message.data()), message.size()); - return { crypto::ecdsa_verify_signature( - message_str, public_key, sig) }; -} - -} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ecdsa.hpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ecdsa.hpp deleted file mode 100644 index 61efa550bc4e..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ecdsa.hpp +++ /dev/null @@ -1,202 +0,0 @@ -#pragma once -/** - * @file bbapi_ecdsa.hpp - * @brief ECDSA signature command definitions for the Barretenberg RPC API. - * - * This file contains command structures for ECDSA signature operations - * on Secp256k1 and Secp256r1 curves. - */ -#include "barretenberg/bbapi/bbapi_shared.hpp" -#include "barretenberg/common/named_union.hpp" -#include "barretenberg/crypto/ecdsa/ecdsa.hpp" -#include "barretenberg/ecc/curves/secp256k1/secp256k1.hpp" -#include "barretenberg/ecc/curves/secp256r1/secp256r1.hpp" -#include "barretenberg/serialize/msgpack.hpp" -#include -#include -#include -#include - -namespace bb::bbapi { - -/** - * @struct EcdsaSecp256k1ComputePublicKey - * @brief Compute ECDSA public key from private key for secp256k1 - */ -struct EcdsaSecp256k1ComputePublicKey { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256k1ComputePublicKey"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256k1ComputePublicKeyResponse"; - secp256k1::g1::affine_element public_key; - SERIALIZATION_FIELDS(public_key); - bool operator==(const Response&) const = default; - }; - - secp256k1::fr private_key; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(private_key); - bool operator==(const EcdsaSecp256k1ComputePublicKey&) const = default; -}; - -/** - * @struct EcdsaSecp256r1ComputePublicKey - * @brief Compute ECDSA public key from private key for secp256r1 - */ -struct EcdsaSecp256r1ComputePublicKey { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256r1ComputePublicKey"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256r1ComputePublicKeyResponse"; - secp256r1::g1::affine_element public_key; - SERIALIZATION_FIELDS(public_key); - bool operator==(const Response&) const = default; - }; - - secp256r1::fr private_key; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(private_key); - bool operator==(const EcdsaSecp256r1ComputePublicKey&) const = default; -}; - -/** - * @struct EcdsaSecp256k1ConstructSignature - * @brief Construct an ECDSA signature for secp256k1 - */ -struct EcdsaSecp256k1ConstructSignature { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256k1ConstructSignature"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256k1ConstructSignatureResponse"; - std::array r; - std::array s; - uint8_t v; - SERIALIZATION_FIELDS(r, s, v); - bool operator==(const Response&) const = default; - }; - - std::vector message; - secp256k1::fr private_key; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(message, private_key); - bool operator==(const EcdsaSecp256k1ConstructSignature&) const = default; -}; - -/** - * @struct EcdsaSecp256r1ConstructSignature - * @brief Construct an ECDSA signature for secp256r1 - */ -struct EcdsaSecp256r1ConstructSignature { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256r1ConstructSignature"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256r1ConstructSignatureResponse"; - std::array r; - std::array s; - uint8_t v; - SERIALIZATION_FIELDS(r, s, v); - bool operator==(const Response&) const = default; - }; - - std::vector message; - secp256r1::fr private_key; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(message, private_key); - bool operator==(const EcdsaSecp256r1ConstructSignature&) const = default; -}; - -/** - * @struct EcdsaSecp256k1RecoverPublicKey - * @brief Recover public key from ECDSA signature for secp256k1 - */ -struct EcdsaSecp256k1RecoverPublicKey { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256k1RecoverPublicKey"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256k1RecoverPublicKeyResponse"; - secp256k1::g1::affine_element public_key; - SERIALIZATION_FIELDS(public_key); - bool operator==(const Response&) const = default; - }; - - std::vector message; - std::array r; - std::array s; - uint8_t v; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(message, r, s, v); - bool operator==(const EcdsaSecp256k1RecoverPublicKey&) const = default; -}; - -/** - * @struct EcdsaSecp256r1RecoverPublicKey - * @brief Recover public key from ECDSA signature for secp256r1 - */ -struct EcdsaSecp256r1RecoverPublicKey { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256r1RecoverPublicKey"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256r1RecoverPublicKeyResponse"; - secp256r1::g1::affine_element public_key; - SERIALIZATION_FIELDS(public_key); - bool operator==(const Response&) const = default; - }; - - std::vector message; - std::array r; - std::array s; - uint8_t v; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(message, r, s, v); - bool operator==(const EcdsaSecp256r1RecoverPublicKey&) const = default; -}; - -/** - * @struct EcdsaSecp256k1VerifySignature - * @brief Verify an ECDSA signature for secp256k1 - */ -struct EcdsaSecp256k1VerifySignature { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256k1VerifySignature"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256k1VerifySignatureResponse"; - bool verified; - SERIALIZATION_FIELDS(verified); - bool operator==(const Response&) const = default; - }; - - std::vector message; - secp256k1::g1::affine_element public_key; - std::array r; - std::array s; - uint8_t v; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(message, public_key, r, s, v); - bool operator==(const EcdsaSecp256k1VerifySignature&) const = default; -}; - -/** - * @struct EcdsaSecp256r1VerifySignature - * @brief Verify an ECDSA signature for secp256r1 - */ -struct EcdsaSecp256r1VerifySignature { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256r1VerifySignature"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "EcdsaSecp256r1VerifySignatureResponse"; - bool verified; - SERIALIZATION_FIELDS(verified); - bool operator==(const Response&) const = default; - }; - - std::vector message; - secp256r1::g1::affine_element public_key; - std::array r; - std::array s; - uint8_t v; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(message, public_key, r, s, v); - bool operator==(const EcdsaSecp256r1VerifySignature&) const = default; -}; - -} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_execute.cpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_execute.cpp deleted file mode 100644 index 4a59f76cd339..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_execute.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "bbapi_execute.hpp" - -namespace bb::bbapi { -namespace { // anonymous -struct Api { - Command commands; - bb::bbapi::CommandResponse responses; - SERIALIZATION_FIELDS(commands, responses); -}; -} // namespace -std::string get_msgpack_schema_as_json() -{ - return msgpack_schema_to_string(Api{}); -} -} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_execute.hpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_execute.hpp deleted file mode 100644 index b28cb2e849fe..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_execute.hpp +++ /dev/null @@ -1,173 +0,0 @@ -#pragma once - -#include "barretenberg/bbapi/bbapi_avm.hpp" -#include "barretenberg/bbapi/bbapi_chonk.hpp" -#include "barretenberg/bbapi/bbapi_crypto.hpp" -#include "barretenberg/bbapi/bbapi_ecc.hpp" -#include "barretenberg/bbapi/bbapi_ecdsa.hpp" -#include "barretenberg/bbapi/bbapi_schnorr.hpp" -#include "barretenberg/bbapi/bbapi_shared.hpp" -#include "barretenberg/bbapi/bbapi_srs.hpp" -#include "barretenberg/bbapi/bbapi_ultra_honk.hpp" -#include "barretenberg/common/throw_or_abort.hpp" -#include - -namespace bb::bbapi { - -using Command = NamedUnion; - -using CommandResponse = NamedUnion; - -/** - * @brief Executes a command by visiting a variant of all possible commands. - * - * @param command The command to execute, consumed by this function. - * @param request The circuit registry (acting as the request context). - * @return A variant of all possible command responses. - */ -inline CommandResponse execute(BBApiRequest& request, Command&& command) -{ - // Reset error state before execution - request.error_message.clear(); - - CommandResponse response = std::move(command).visit([&request](auto&& cmd) -> CommandResponse { - using CmdType = std::decay_t; - return std::forward(cmd).execute(request); - }); - - // Check if an error occurred during execution - if (!request.error_message.empty()) { - return ErrorResponse{ .message = std::move(request.error_message) }; - } - - return response; -} - -// The msgpack scheme is an ad-hoc format that allows for cbind/compiler.ts to -// generate TypeScript bindings for the API. -std::string get_msgpack_schema_as_json(); - -} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_handlers.cpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_handlers.cpp new file mode 100644 index 000000000000..4cd939f32be1 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_handlers.cpp @@ -0,0 +1,500 @@ +/** + * @file bbapi_handlers.cpp + * @brief Per-command handlers consumed by the codegen-emitted server dispatch. + * + * Each handler matches the signature declared by generated/bb_ipc_server.hpp + * but as a non-template overload for `BBApiRequest` so + * `make_bb_handler` resolves to these via overload resolution. + * + * Every handler converts wire fields to domain fields, calls + * `Cmd::execute()`, and converts the domain response back to wire fields — + * all explicit, all field-by-field. The shared converters live in + * `bbapi_wire_convert.hpp`. + */ +#include "barretenberg/bbapi/bbapi_handlers.hpp" +#include "barretenberg/api/api_avm.hpp" +#include "barretenberg/bbapi/bbapi_chonk.hpp" +#include "barretenberg/bbapi/bbapi_shared.hpp" +#include "barretenberg/bbapi/bbapi_wire_convert.hpp" +#include "barretenberg/bbapi/generated/bb_ipc_server.hpp" +#include "barretenberg/common/assert.hpp" +#include "barretenberg/common/serialize.hpp" +#include "barretenberg/common/thread.hpp" +#include "barretenberg/common/throw_or_abort.hpp" +#include "barretenberg/crypto/aes128/aes128.hpp" +#include "barretenberg/crypto/blake2s/blake2s.hpp" +#include "barretenberg/crypto/ecdsa/ecdsa.hpp" +#include "barretenberg/crypto/pedersen_commitment/pedersen.hpp" +#include "barretenberg/crypto/pedersen_hash/pedersen.hpp" +#include "barretenberg/crypto/poseidon2/poseidon2.hpp" +#include "barretenberg/crypto/poseidon2/poseidon2_permutation.hpp" +#include "barretenberg/crypto/schnorr/schnorr.hpp" +#include "barretenberg/crypto/sha256/sha256.hpp" +#include "barretenberg/srs/factories/bn254_crs_data.hpp" +#include "barretenberg/srs/factories/bn254_g1_chunk_hashes.hpp" +#include "barretenberg/srs/global_crs.hpp" +#include "barretenberg/vm2/tooling/stats.hpp" + +namespace bb::bbapi { + +namespace { + +// Reset the AVM per-stage timings registry so the snapshot we return reflects only this call. +void reset_avm_stats() +{ + ::bb::avm2::Stats::get().reset(); +} + +// Take a snapshot of the AVM per-stage timings registry as wire-typed stats entries. +std::vector snapshot_avm_stats_wire() +{ + auto snapshot = ::bb::avm2::Stats::get().snapshot(); + std::vector result; + result.reserve(snapshot.size()); + for (auto& [name, value] : snapshot) { + result.push_back(wire::AvmStat{ .name = std::move(name), .value_ms = value }); + } + return result; +} + +} // namespace + +// =========================================================================== +// AVM +// =========================================================================== + +wire::AvmProveResponse handle_avm_prove(BBApiRequest& /*ctx*/, wire::AvmProve&& cmd) +{ + reset_avm_stats(); + auto result = avm_prove_from_bytes(std::move(cmd.inputs)); + return { .proof = fr_vec_to_wire(result.proof), .stats = snapshot_avm_stats_wire() }; +} +wire::AvmVerifyResponse handle_avm_verify(BBApiRequest& /*ctx*/, wire::AvmVerify&& cmd) +{ + bool verified = avm_verify_from_bytes(fr_vec_from_wire(cmd.proof), std::move(cmd.public_inputs)); + return { .verified = verified }; +} +wire::AvmCheckCircuitResponse handle_avm_check_circuit(BBApiRequest& /*ctx*/, wire::AvmCheckCircuit&& cmd) +{ + reset_avm_stats(); + bool passed = avm_check_circuit_from_bytes(std::move(cmd.inputs)); + return { .passed = passed, .stats = snapshot_avm_stats_wire() }; +} + +// =========================================================================== +// Circuit + Chonk + UltraHonk +// =========================================================================== + +// UltraHonk handlers live in bbapi_ultra_honk.cpp. +// Chonk handlers live in bbapi_chonk.cpp. + +// =========================================================================== +// Hashing primitives +// =========================================================================== + +wire::Poseidon2HashResponse handle_poseidon2_hash(BBApiRequest& /*ctx*/, wire::Poseidon2Hash&& cmd) +{ + auto inputs = fr_vec_from_wire(cmd.inputs); + auto hash = crypto::Poseidon2::hash(inputs); + return { .hash = fr_to_wire(hash) }; +} +wire::Poseidon2PermutationResponse handle_poseidon2_permutation(BBApiRequest& /*ctx*/, wire::Poseidon2Permutation&& cmd) +{ + using Permutation = crypto::Poseidon2Permutation; + auto inputs = fr_array_from_wire<4>(cmd.inputs); + auto outputs = Permutation::permutation(inputs); + return { .outputs = fr_array_to_wire<4>(outputs) }; +} +wire::PedersenCommitResponse handle_pedersen_commit(BBApiRequest& /*ctx*/, wire::PedersenCommit&& cmd) +{ + crypto::GeneratorContext gctx; + gctx.offset = static_cast(cmd.hash_index); + auto inputs = fr_vec_from_wire(cmd.inputs); + auto point = crypto::pedersen_commitment::commit_native(inputs, gctx); + return { .point = grumpkin_point_to_wire(point) }; +} +wire::PedersenHashResponse handle_pedersen_hash(BBApiRequest& /*ctx*/, wire::PedersenHash&& cmd) +{ + crypto::GeneratorContext gctx; + gctx.offset = static_cast(cmd.hash_index); + auto inputs = fr_vec_from_wire(cmd.inputs); + auto hash = crypto::pedersen_hash::hash(inputs, gctx); + return { .hash = fr_to_wire(hash) }; +} +wire::PedersenHashBufferResponse handle_pedersen_hash_buffer(BBApiRequest& /*ctx*/, wire::PedersenHashBuffer&& cmd) +{ + crypto::GeneratorContext gctx; + gctx.offset = static_cast(cmd.hash_index); + auto hash = crypto::pedersen_hash::hash_buffer(cmd.input, gctx); + return { .hash = fr_to_wire(hash) }; +} +wire::Blake2sResponse handle_blake2s(BBApiRequest& /*ctx*/, wire::Blake2s&& cmd) +{ + return { .hash = crypto::blake2s(cmd.data) }; +} +wire::Blake2sToFieldResponse handle_blake2s_to_field(BBApiRequest& /*ctx*/, wire::Blake2sToField&& cmd) +{ + auto hash_result = crypto::blake2s(cmd.data); + return { .field = fr_to_wire(fr::serialize_from_buffer(hash_result.data())) }; +} +wire::AesEncryptResponse handle_aes_encrypt(BBApiRequest& /*ctx*/, wire::AesEncrypt&& cmd) +{ + BB_ASSERT(cmd.length == cmd.plaintext.size(), "AesEncrypt: length must equal plaintext.size()"); + BB_ASSERT(cmd.length % 16 == 0, "AesEncrypt: length must be a multiple of 16"); + + std::vector result = std::move(cmd.plaintext); + result.resize(cmd.length); + crypto::aes128_encrypt_buffer_cbc(result.data(), cmd.iv.data(), cmd.key.data(), cmd.length); + return { .ciphertext = std::move(result) }; +} +wire::AesDecryptResponse handle_aes_decrypt(BBApiRequest& /*ctx*/, wire::AesDecrypt&& cmd) +{ + BB_ASSERT(cmd.length == cmd.ciphertext.size(), "AesDecrypt: length must equal ciphertext.size()"); + BB_ASSERT(cmd.length % 16 == 0, "AesDecrypt: length must be a multiple of 16"); + + std::vector result = std::move(cmd.ciphertext); + result.resize(cmd.length); + crypto::aes128_decrypt_buffer_cbc(result.data(), cmd.iv.data(), cmd.key.data(), cmd.length); + return { .plaintext = std::move(result) }; +} + +// =========================================================================== +// Grumpkin curve +// =========================================================================== + +wire::GrumpkinMulResponse handle_grumpkin_mul(BBApiRequest& request, wire::GrumpkinMul&& cmd) +{ + auto point = grumpkin_point_from_wire(cmd.point); + auto scalar = field_from_wire(cmd.scalar); + if (!point.on_curve()) { + BBAPI_ERROR(request, "Input point must be on the curve"); + } + return { .point = grumpkin_point_to_wire(point * scalar) }; +} +wire::GrumpkinAddResponse handle_grumpkin_add(BBApiRequest& request, wire::GrumpkinAdd&& cmd) +{ + auto a = grumpkin_point_from_wire(cmd.point_a); + auto b = grumpkin_point_from_wire(cmd.point_b); + if (!a.on_curve()) { + BBAPI_ERROR(request, "Input point_a must be on the curve"); + } + if (!b.on_curve()) { + BBAPI_ERROR(request, "Input point_b must be on the curve"); + } + return { .point = grumpkin_point_to_wire(a + b) }; +} +wire::GrumpkinBatchMulResponse handle_grumpkin_batch_mul(BBApiRequest& request, wire::GrumpkinBatchMul&& cmd) +{ + auto points = grumpkin_point_vec_from_wire(cmd.points); + auto scalar = field_from_wire(cmd.scalar); + for (const auto& p : points) { + if (!p.on_curve()) { + BBAPI_ERROR(request, "Input point must be on the curve"); + } + } + auto output = grumpkin::g1::element::batch_mul_with_endomorphism(points, scalar); + return { .points = grumpkin_point_vec_to_wire(output) }; +} +wire::GrumpkinGetRandomFrResponse handle_grumpkin_get_random_fr(BBApiRequest& /*ctx*/, + wire::GrumpkinGetRandomFr&& /*cmd*/) +{ + return { .value = fr_to_wire(bb::fr::random_element()) }; +} +wire::GrumpkinReduce512Response handle_grumpkin_reduce512(BBApiRequest& /*ctx*/, wire::GrumpkinReduce512&& cmd) +{ + auto bigint_input = from_buffer(cmd.input.data()); + uint512_t barretenberg_modulus(bb::fr::modulus); + uint512_t target_output = bigint_input % barretenberg_modulus; + return { .value = fr_to_wire(bb::fr(target_output.lo)) }; +} + +// =========================================================================== +// Secp256k1 curve +// =========================================================================== + +wire::Secp256k1MulResponse handle_secp256k1_mul(BBApiRequest& request, wire::Secp256k1Mul&& cmd) +{ + auto point = secp256k1_point_from_wire(cmd.point); + auto scalar = field_from_wire(cmd.scalar); + if (!point.on_curve()) { + BBAPI_ERROR(request, "Input point must be on the curve"); + } + return { .point = secp256k1_point_to_wire(point * scalar) }; +} +wire::Secp256k1GetRandomFrResponse handle_secp256k1_get_random_fr(BBApiRequest& /*ctx*/, + wire::Secp256k1GetRandomFr&& /*cmd*/) +{ + return { .value = field_to_wire(secp256k1::fr::random_element()) }; +} +wire::Secp256k1Reduce512Response handle_secp256k1_reduce512(BBApiRequest& /*ctx*/, wire::Secp256k1Reduce512&& cmd) +{ + auto bigint_input = from_buffer(cmd.input.data()); + uint512_t secp256k1_modulus(secp256k1::fr::modulus); + uint512_t target_output = bigint_input % secp256k1_modulus; + return { .value = field_to_wire(secp256k1::fr(target_output.lo)) }; +} + +// =========================================================================== +// Bn254 curve +// =========================================================================== + +wire::Bn254FrSqrtResponse handle_bn254_fr_sqrt(BBApiRequest& /*ctx*/, wire::Bn254FrSqrt&& cmd) +{ + auto [is_sqr, root] = fr_from_wire(cmd.input).sqrt(); + return { .is_square_root = is_sqr, .value = fr_to_wire(root) }; +} +wire::Bn254FqSqrtResponse handle_bn254_fq_sqrt(BBApiRequest& /*ctx*/, wire::Bn254FqSqrt&& cmd) +{ + auto [is_sqr, root] = field_from_wire(cmd.input).sqrt(); + return { .is_square_root = is_sqr, .value = field_to_wire(root) }; +} +wire::Bn254G1MulResponse handle_bn254_g1_mul(BBApiRequest& request, wire::Bn254G1Mul&& cmd) +{ + auto point = bn254_g1_point_from_wire(cmd.point); + auto scalar = fr_from_wire(cmd.scalar); + if (!point.on_curve()) { + BBAPI_ERROR(request, "Input point must be on the curve"); + } + auto result = point * scalar; + if (!result.on_curve()) { + BBAPI_ERROR(request, "Output point must be on the curve"); + } + return { .point = bn254_g1_point_to_wire(result) }; +} +wire::Bn254G2MulResponse handle_bn254_g2_mul(BBApiRequest& request, wire::Bn254G2Mul&& cmd) +{ + auto point = bn254_g2_point_from_wire(cmd.point); + auto scalar = fr_from_wire(cmd.scalar); + if (!point.on_curve()) { + BBAPI_ERROR(request, "Input point must be on the curve"); + } + // BN254 G2 has cofactor h2 ≈ 2^254. An on-curve point may lie in a cofactor subgroup of order + // dividing h2 rather than the prime-order subgroup; we do not want to allow such points + // as inputs to bbapi. + if (!point.is_in_prime_subgroup()) { + BBAPI_ERROR(request, "Input point must lie in the prime-order subgroup"); + } + auto result = point * scalar; + if (!result.on_curve()) { + BBAPI_ERROR(request, "Output point must be on the curve"); + } + return { .point = bn254_g2_point_to_wire(result) }; +} +wire::Bn254G1IsOnCurveResponse handle_bn254_g1_is_on_curve(BBApiRequest& /*ctx*/, wire::Bn254G1IsOnCurve&& cmd) +{ + return { .is_on_curve = bn254_g1_point_from_wire(cmd.point).on_curve() }; +} +wire::Bn254G1FromCompressedResponse handle_bn254_g1_from_compressed(BBApiRequest& request, + wire::Bn254G1FromCompressed&& cmd) +{ + uint256_t compressed_value = from_buffer(cmd.compressed.data()); + auto point = bb::g1::affine_element::from_compressed(compressed_value); + if (!point.on_curve()) { + BBAPI_ERROR(request, "Decompressed point is not on the curve"); + } + return { .point = bn254_g1_point_to_wire(point) }; +} + +// =========================================================================== +// Schnorr +// =========================================================================== + +wire::SchnorrComputePublicKeyResponse handle_schnorr_compute_public_key(BBApiRequest& /*ctx*/, + wire::SchnorrComputePublicKey&& cmd) +{ + auto private_key = field_from_wire(cmd.private_key); + return { .public_key = grumpkin_point_to_wire(grumpkin::g1::one * private_key) }; +} +wire::SchnorrConstructSignatureResponse handle_schnorr_construct_signature(BBApiRequest& /*ctx*/, + wire::SchnorrConstructSignature&& cmd) +{ + auto private_key = field_from_wire(cmd.private_key); + grumpkin::g1::affine_element pub_key = grumpkin::g1::one * private_key; + crypto::schnorr_key_pair key_pair = { private_key, pub_key }; + + std::string message_str(reinterpret_cast(cmd.message.data()), cmd.message.size()); + auto sig = crypto::schnorr_construct_signature(message_str, key_pair); + crypto::secure_erase_bytes(&key_pair.private_key, sizeof(key_pair.private_key)); + + return { .s = sig.s, .e = sig.e }; +} +wire::SchnorrVerifySignatureResponse handle_schnorr_verify_signature(BBApiRequest& /*ctx*/, + wire::SchnorrVerifySignature&& cmd) +{ + std::string message_str(reinterpret_cast(cmd.message.data()), cmd.message.size()); + crypto::schnorr_signature sig = { cmd.s, cmd.e }; + auto public_key = grumpkin_point_from_wire(cmd.public_key); + + bool result = crypto::schnorr_verify_signature( + message_str, public_key, sig); + return { .verified = result }; +} + +// =========================================================================== +// ECDSA +// =========================================================================== + +wire::EcdsaSecp256k1ComputePublicKeyResponse handle_ecdsa_secp256k1_compute_public_key( + BBApiRequest& /*ctx*/, wire::EcdsaSecp256k1ComputePublicKey&& cmd) +{ + auto private_key = field_from_wire(cmd.private_key); + return { .public_key = secp256k1_point_to_wire(secp256k1::g1::one * private_key) }; +} +wire::EcdsaSecp256r1ComputePublicKeyResponse handle_ecdsa_secp256r1_compute_public_key( + BBApiRequest& /*ctx*/, wire::EcdsaSecp256r1ComputePublicKey&& cmd) +{ + auto private_key = field_from_wire(cmd.private_key); + return { .public_key = secp256r1_point_to_wire(secp256r1::g1::one * private_key) }; +} +wire::EcdsaSecp256k1ConstructSignatureResponse handle_ecdsa_secp256k1_construct_signature( + BBApiRequest& /*ctx*/, wire::EcdsaSecp256k1ConstructSignature&& cmd) +{ + auto private_key = field_from_wire(cmd.private_key); + auto pub_key = secp256k1::g1::one * private_key; + crypto::ecdsa_key_pair key_pair = { private_key, pub_key }; + std::string message_str(reinterpret_cast(cmd.message.data()), cmd.message.size()); + auto sig = crypto::ecdsa_construct_signature( + message_str, key_pair); + return { .r = sig.r, .s = sig.s, .v = sig.v }; +} +wire::EcdsaSecp256r1ConstructSignatureResponse handle_ecdsa_secp256r1_construct_signature( + BBApiRequest& /*ctx*/, wire::EcdsaSecp256r1ConstructSignature&& cmd) +{ + auto private_key = field_from_wire(cmd.private_key); + auto pub_key = secp256r1::g1::one * private_key; + crypto::ecdsa_key_pair key_pair = { private_key, pub_key }; + std::string message_str(reinterpret_cast(cmd.message.data()), cmd.message.size()); + auto sig = crypto::ecdsa_construct_signature( + message_str, key_pair); + return { .r = sig.r, .s = sig.s, .v = sig.v }; +} +wire::EcdsaSecp256k1RecoverPublicKeyResponse handle_ecdsa_secp256k1_recover_public_key( + BBApiRequest& /*ctx*/, wire::EcdsaSecp256k1RecoverPublicKey&& cmd) +{ + crypto::ecdsa_signature sig = { cmd.r, cmd.s, cmd.v }; + std::string message_str(reinterpret_cast(cmd.message.data()), cmd.message.size()); + auto pubkey = crypto::ecdsa_recover_public_key( + message_str, sig); + return { .public_key = secp256k1_point_to_wire(pubkey) }; +} +wire::EcdsaSecp256r1RecoverPublicKeyResponse handle_ecdsa_secp256r1_recover_public_key( + BBApiRequest& /*ctx*/, wire::EcdsaSecp256r1RecoverPublicKey&& cmd) +{ + crypto::ecdsa_signature sig = { cmd.r, cmd.s, cmd.v }; + std::string message_str(reinterpret_cast(cmd.message.data()), cmd.message.size()); + auto pubkey = crypto::ecdsa_recover_public_key( + message_str, sig); + return { .public_key = secp256r1_point_to_wire(pubkey) }; +} +wire::EcdsaSecp256k1VerifySignatureResponse handle_ecdsa_secp256k1_verify_signature( + BBApiRequest& /*ctx*/, wire::EcdsaSecp256k1VerifySignature&& cmd) +{ + crypto::ecdsa_signature sig = { cmd.r, cmd.s, cmd.v }; + std::string message_str(reinterpret_cast(cmd.message.data()), cmd.message.size()); + auto pubkey = secp256k1_point_from_wire(cmd.public_key); + bool verified = crypto::ecdsa_verify_signature( + message_str, pubkey, sig); + return { .verified = verified }; +} +wire::EcdsaSecp256r1VerifySignatureResponse handle_ecdsa_secp256r1_verify_signature( + BBApiRequest& /*ctx*/, wire::EcdsaSecp256r1VerifySignature&& cmd) +{ + crypto::ecdsa_signature sig = { cmd.r, cmd.s, cmd.v }; + std::string message_str(reinterpret_cast(cmd.message.data()), cmd.message.size()); + auto pubkey = secp256r1_point_from_wire(cmd.public_key); + bool verified = crypto::ecdsa_verify_signature( + message_str, pubkey, sig); + return { .verified = verified }; +} + +// =========================================================================== +// SRS init +// =========================================================================== + +wire::SrsInitSrsResponse handle_srs_init_srs(BBApiRequest& /*ctx*/, wire::SrsInitSrs&& cmd) +{ + constexpr size_t COMPRESSED_POINT_SIZE = 32; + constexpr size_t UNCOMPRESSED_POINT_SIZE = sizeof(g1::affine_element); // 64 + + auto& points_buf = cmd.points_buf; + auto num_points = cmd.num_points; + size_t bytes_per_point = num_points > 0 ? points_buf.size() / num_points : 0; + std::vector g1_points(num_points); + std::vector uncompressed_out; + + if (bytes_per_point == UNCOMPRESSED_POINT_SIZE) { + parallel_for([&](ThreadChunk chunk) { + for (auto i : chunk.range(static_cast(num_points))) { + g1_points[i] = from_buffer(points_buf.data(), i * UNCOMPRESSED_POINT_SIZE); + } + }); + } else if (bytes_per_point == COMPRESSED_POINT_SIZE) { + if (points_buf.size() == 0 || points_buf.size() % bb::srs::SRS_CHUNK_SIZE_BYTES != 0) { + throw_or_abort("SrsInitSrs: compressed points_buf size " + std::to_string(points_buf.size()) + + " must be a positive multiple of " + std::to_string(bb::srs::SRS_CHUNK_SIZE_BYTES)); + } + size_t num_full_chunks = points_buf.size() / bb::srs::SRS_CHUNK_SIZE_BYTES; + size_t chunks_to_verify = std::min(num_full_chunks, static_cast(bb::srs::SRS_NUM_FULL_CHUNKS)); + for (size_t i = 0; i < chunks_to_verify; ++i) { + auto chunk = std::span(points_buf.data() + i * bb::srs::SRS_CHUNK_SIZE_BYTES, + bb::srs::SRS_CHUNK_SIZE_BYTES); + auto hash = bb::crypto::sha256(chunk); + if (hash != bb::srs::BN254_G1_CHUNK_HASHES[i]) { + throw_or_abort("SrsInitSrs: g1 compressed chunk " + std::to_string(i) + " SHA-256 mismatch"); + } + } + parallel_for([&](ThreadChunk chunk) { + for (auto i : chunk.range(static_cast(num_points))) { + uint256_t c = from_buffer(points_buf.data(), i * COMPRESSED_POINT_SIZE); + g1_points[i] = g1::affine_element::from_compressed(c); + } + }); + uncompressed_out.resize(static_cast(num_points) * UNCOMPRESSED_POINT_SIZE); + parallel_for([&](ThreadChunk chunk) { + for (auto i : chunk.range(static_cast(num_points))) { + auto buf = to_buffer(g1_points[i]); + std::copy(buf.begin(), buf.end(), &uncompressed_out[i * UNCOMPRESSED_POINT_SIZE]); + } + }); + } else { + throw_or_abort("SrsInitSrs: invalid points_buf size. Expected 32 or 64 bytes per point, got " + + std::to_string(bytes_per_point)); + } + + if (num_points >= 1 && g1_points[0] != bb::srs::BN254_G1_FIRST_ELEMENT) { + throw_or_abort("SrsInitSrs: g1_points[0] is not the canonical BN254 generator"); + } + if (num_points >= 2 && g1_points[1] != bb::srs::get_bn254_g1_second_element()) { + throw_or_abort("SrsInitSrs: g1_points[1] does not match the canonical trusted-setup tau·G"); + } + + auto g2_hash = bb::crypto::sha256(std::span(cmd.g2_point.data(), cmd.g2_point.size())); + if (g2_hash != bb::srs::BN254_G2_ELEMENT_SHA256) { + throw_or_abort("SrsInitSrs: g2_point bytes do not match the canonical Aztec [x]_2 SHA-256"); + } + auto g2_point_elem = from_buffer(cmd.g2_point.data()); + if (!g2_point_elem.is_in_prime_subgroup()) { + throw_or_abort("SrsInitSrs: g2_point is not in the BN254 G2 prime-order subgroup"); + } + + bb::srs::init_bn254_mem_crs_factory(g1_points, g2_point_elem); + return { .points_buf = std::move(uncompressed_out) }; +} +wire::SrsInitGrumpkinSrsResponse handle_srs_init_grumpkin_srs(BBApiRequest& /*ctx*/, wire::SrsInitGrumpkinSrs&& cmd) +{ + const size_t required_size = static_cast(cmd.num_points) * sizeof(curve::Grumpkin::AffineElement); + if (cmd.points_buf.size() < required_size) { + throw_or_abort("SrsInitGrumpkinSrs: points_buf too small (" + std::to_string(cmd.points_buf.size()) + + " bytes) for num_points=" + std::to_string(cmd.num_points) + " (need " + + std::to_string(required_size) + ")"); + } + std::vector points(cmd.num_points); + for (uint32_t i = 0; i < cmd.num_points; ++i) { + points[i] = from_buffer(cmd.points_buf.data(), + i * sizeof(curve::Grumpkin::AffineElement)); + } + bb::srs::init_grumpkin_mem_crs_factory(points); + return {}; +} + +} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_handlers.hpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_handlers.hpp new file mode 100644 index 000000000000..17e2925d2e63 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_handlers.hpp @@ -0,0 +1,96 @@ +#pragma once +/** + * @file bbapi_handlers.hpp + * @brief Non-template handler declarations for the bb service. + * + * The codegen-emitted server (generated/bb_ipc_server.hpp) declares + * `template handle_(Ctx&, wire::Cmd&&)`. These free-function + * overloads provide concrete definitions for `Ctx = BBApiRequest`; overload + * resolution prefers them at the template instantiation point inside + * make_bb_handler(...). + */ +#include "barretenberg/bbapi/bbapi_shared.hpp" +#include "barretenberg/bbapi/generated/bb_types.hpp" + +namespace bb::bbapi { + +wire::AvmProveResponse handle_avm_prove(BBApiRequest& ctx, wire::AvmProve&& cmd); +wire::AvmVerifyResponse handle_avm_verify(BBApiRequest& ctx, wire::AvmVerify&& cmd); +wire::AvmCheckCircuitResponse handle_avm_check_circuit(BBApiRequest& ctx, wire::AvmCheckCircuit&& cmd); +wire::CircuitProveResponse handle_circuit_prove(BBApiRequest& ctx, wire::CircuitProve&& cmd); +wire::CircuitComputeVkResponse handle_circuit_compute_vk(BBApiRequest& ctx, wire::CircuitComputeVk&& cmd); +wire::CircuitInfoResponse handle_circuit_stats(BBApiRequest& ctx, wire::CircuitStats&& cmd); +wire::CircuitVerifyResponse handle_circuit_verify(BBApiRequest& ctx, wire::CircuitVerify&& cmd); +wire::ChonkComputeVkResponse handle_chonk_compute_vk(BBApiRequest& ctx, wire::ChonkComputeVk&& cmd); +wire::ChonkStartResponse handle_chonk_start(BBApiRequest& ctx, wire::ChonkStart&& cmd); +wire::ChonkLoadResponse handle_chonk_load(BBApiRequest& ctx, wire::ChonkLoad&& cmd); +wire::ChonkAccumulateResponse handle_chonk_accumulate(BBApiRequest& ctx, wire::ChonkAccumulate&& cmd); +wire::ChonkProveResponse handle_chonk_prove(BBApiRequest& ctx, wire::ChonkProve&& cmd); +wire::ChonkVerifyResponse handle_chonk_verify(BBApiRequest& ctx, wire::ChonkVerify&& cmd); +wire::ChonkVerifyFromFieldsResponse handle_chonk_verify_from_fields(BBApiRequest& ctx, + wire::ChonkVerifyFromFields&& cmd); +wire::ChonkBatchVerifyResponse handle_chonk_batch_verify(BBApiRequest& ctx, wire::ChonkBatchVerify&& cmd); +wire::VkAsFieldsResponse handle_vk_as_fields(BBApiRequest& ctx, wire::VkAsFields&& cmd); +wire::MegaVkAsFieldsResponse handle_mega_vk_as_fields(BBApiRequest& ctx, wire::MegaVkAsFields&& cmd); +wire::CircuitWriteSolidityVerifierResponse handle_circuit_write_solidity_verifier( + BBApiRequest& ctx, wire::CircuitWriteSolidityVerifier&& cmd); +wire::ChonkCheckPrecomputedVkResponse handle_chonk_check_precomputed_vk(BBApiRequest& ctx, + wire::ChonkCheckPrecomputedVk&& cmd); +wire::ChonkStatsResponse handle_chonk_stats(BBApiRequest& ctx, wire::ChonkStats&& cmd); +wire::ChonkCompressProofResponse handle_chonk_compress_proof(BBApiRequest& ctx, wire::ChonkCompressProof&& cmd); +wire::ChonkDecompressProofResponse handle_chonk_decompress_proof(BBApiRequest& ctx, wire::ChonkDecompressProof&& cmd); +wire::Poseidon2HashResponse handle_poseidon2_hash(BBApiRequest& ctx, wire::Poseidon2Hash&& cmd); +wire::Poseidon2PermutationResponse handle_poseidon2_permutation(BBApiRequest& ctx, wire::Poseidon2Permutation&& cmd); +wire::PedersenCommitResponse handle_pedersen_commit(BBApiRequest& ctx, wire::PedersenCommit&& cmd); +wire::PedersenHashResponse handle_pedersen_hash(BBApiRequest& ctx, wire::PedersenHash&& cmd); +wire::PedersenHashBufferResponse handle_pedersen_hash_buffer(BBApiRequest& ctx, wire::PedersenHashBuffer&& cmd); +wire::Blake2sResponse handle_blake2s(BBApiRequest& ctx, wire::Blake2s&& cmd); +wire::Blake2sToFieldResponse handle_blake2s_to_field(BBApiRequest& ctx, wire::Blake2sToField&& cmd); +wire::AesEncryptResponse handle_aes_encrypt(BBApiRequest& ctx, wire::AesEncrypt&& cmd); +wire::AesDecryptResponse handle_aes_decrypt(BBApiRequest& ctx, wire::AesDecrypt&& cmd); +wire::GrumpkinMulResponse handle_grumpkin_mul(BBApiRequest& ctx, wire::GrumpkinMul&& cmd); +wire::GrumpkinAddResponse handle_grumpkin_add(BBApiRequest& ctx, wire::GrumpkinAdd&& cmd); +wire::GrumpkinBatchMulResponse handle_grumpkin_batch_mul(BBApiRequest& ctx, wire::GrumpkinBatchMul&& cmd); +wire::GrumpkinGetRandomFrResponse handle_grumpkin_get_random_fr(BBApiRequest& ctx, wire::GrumpkinGetRandomFr&& cmd); +wire::GrumpkinReduce512Response handle_grumpkin_reduce512(BBApiRequest& ctx, wire::GrumpkinReduce512&& cmd); +wire::Secp256k1MulResponse handle_secp256k1_mul(BBApiRequest& ctx, wire::Secp256k1Mul&& cmd); +wire::Secp256k1GetRandomFrResponse handle_secp256k1_get_random_fr(BBApiRequest& ctx, wire::Secp256k1GetRandomFr&& cmd); +wire::Secp256k1Reduce512Response handle_secp256k1_reduce512(BBApiRequest& ctx, wire::Secp256k1Reduce512&& cmd); +wire::Bn254FrSqrtResponse handle_bn254_fr_sqrt(BBApiRequest& ctx, wire::Bn254FrSqrt&& cmd); +wire::Bn254FqSqrtResponse handle_bn254_fq_sqrt(BBApiRequest& ctx, wire::Bn254FqSqrt&& cmd); +wire::Bn254G1MulResponse handle_bn254_g1_mul(BBApiRequest& ctx, wire::Bn254G1Mul&& cmd); +wire::Bn254G2MulResponse handle_bn254_g2_mul(BBApiRequest& ctx, wire::Bn254G2Mul&& cmd); +wire::Bn254G1IsOnCurveResponse handle_bn254_g1_is_on_curve(BBApiRequest& ctx, wire::Bn254G1IsOnCurve&& cmd); +wire::Bn254G1FromCompressedResponse handle_bn254_g1_from_compressed(BBApiRequest& ctx, + wire::Bn254G1FromCompressed&& cmd); +wire::SchnorrComputePublicKeyResponse handle_schnorr_compute_public_key(BBApiRequest& ctx, + wire::SchnorrComputePublicKey&& cmd); +wire::SchnorrConstructSignatureResponse handle_schnorr_construct_signature(BBApiRequest& ctx, + wire::SchnorrConstructSignature&& cmd); +wire::SchnorrVerifySignatureResponse handle_schnorr_verify_signature(BBApiRequest& ctx, + wire::SchnorrVerifySignature&& cmd); +wire::EcdsaSecp256k1ComputePublicKeyResponse handle_ecdsa_secp256k1_compute_public_key( + BBApiRequest& ctx, wire::EcdsaSecp256k1ComputePublicKey&& cmd); +wire::EcdsaSecp256r1ComputePublicKeyResponse handle_ecdsa_secp256r1_compute_public_key( + BBApiRequest& ctx, wire::EcdsaSecp256r1ComputePublicKey&& cmd); +wire::EcdsaSecp256k1ConstructSignatureResponse handle_ecdsa_secp256k1_construct_signature( + BBApiRequest& ctx, wire::EcdsaSecp256k1ConstructSignature&& cmd); +wire::EcdsaSecp256r1ConstructSignatureResponse handle_ecdsa_secp256r1_construct_signature( + BBApiRequest& ctx, wire::EcdsaSecp256r1ConstructSignature&& cmd); +wire::EcdsaSecp256k1RecoverPublicKeyResponse handle_ecdsa_secp256k1_recover_public_key( + BBApiRequest& ctx, wire::EcdsaSecp256k1RecoverPublicKey&& cmd); +wire::EcdsaSecp256r1RecoverPublicKeyResponse handle_ecdsa_secp256r1_recover_public_key( + BBApiRequest& ctx, wire::EcdsaSecp256r1RecoverPublicKey&& cmd); +wire::EcdsaSecp256k1VerifySignatureResponse handle_ecdsa_secp256k1_verify_signature( + BBApiRequest& ctx, wire::EcdsaSecp256k1VerifySignature&& cmd); +wire::EcdsaSecp256r1VerifySignatureResponse handle_ecdsa_secp256r1_verify_signature( + BBApiRequest& ctx, wire::EcdsaSecp256r1VerifySignature&& cmd); +wire::SrsInitSrsResponse handle_srs_init_srs(BBApiRequest& ctx, wire::SrsInitSrs&& cmd); +wire::ChonkBatchVerifierStartResponse handle_chonk_batch_verifier_start(BBApiRequest& ctx, + wire::ChonkBatchVerifierStart&& cmd); +wire::ChonkBatchVerifierQueueResponse handle_chonk_batch_verifier_queue(BBApiRequest& ctx, + wire::ChonkBatchVerifierQueue&& cmd); +wire::ChonkBatchVerifierStopResponse handle_chonk_batch_verifier_stop(BBApiRequest& ctx, + wire::ChonkBatchVerifierStop&& cmd); +wire::SrsInitGrumpkinSrsResponse handle_srs_init_grumpkin_srs(BBApiRequest& ctx, wire::SrsInitGrumpkinSrs&& cmd); +} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_schnorr.cpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_schnorr.cpp deleted file mode 100644 index c845a1f37cf1..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_schnorr.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @file bbapi_schnorr.cpp - * @brief Implementation of Schnorr signature command execution for the Barretenberg RPC API - */ -#include "barretenberg/bbapi/bbapi_schnorr.hpp" - -namespace bb::bbapi { - -SchnorrComputePublicKey::Response SchnorrComputePublicKey::execute(BB_UNUSED BBApiRequest& request) && -{ - return { grumpkin::g1::one * private_key }; -} - -SchnorrConstructSignature::Response SchnorrConstructSignature::execute(BB_UNUSED BBApiRequest& request) && -{ - grumpkin::g1::affine_element pub_key = grumpkin::g1::one * private_key; - crypto::schnorr_key_pair key_pair = { private_key, pub_key }; - - std::string message_str(reinterpret_cast(message.data()), message.size()); - auto sig = crypto::schnorr_construct_signature(message_str, key_pair); - crypto::secure_erase_bytes(&key_pair.private_key, sizeof(key_pair.private_key)); - - return { sig.s, sig.e }; -} - -SchnorrVerifySignature::Response SchnorrVerifySignature::execute(BB_UNUSED BBApiRequest& request) && -{ - std::string message_str(reinterpret_cast(message.data()), message.size()); - crypto::schnorr_signature sig = { s, e }; - - bool result = crypto::schnorr_verify_signature( - message_str, public_key, sig); - - return { result }; -} - -} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_schnorr.hpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_schnorr.hpp deleted file mode 100644 index a8617dbd2c55..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_schnorr.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once -/** - * @file bbapi_schnorr.hpp - * @brief Schnorr signature command definitions for the Barretenberg RPC API. - * - * This file contains command structures for Schnorr signature operations - * on the Grumpkin curve. - */ -#include "barretenberg/bbapi/bbapi_shared.hpp" -#include "barretenberg/common/named_union.hpp" -#include "barretenberg/crypto/schnorr/schnorr.hpp" -#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" -#include "barretenberg/serialize/msgpack.hpp" -#include -#include -#include - -namespace bb::bbapi { - -/** - * @struct SchnorrComputePublicKey - * @brief Compute Schnorr public key from private key - */ -struct SchnorrComputePublicKey { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "SchnorrComputePublicKey"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "SchnorrComputePublicKeyResponse"; - grumpkin::g1::affine_element public_key; - SERIALIZATION_FIELDS(public_key); - bool operator==(const Response&) const = default; - }; - - grumpkin::fr private_key; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(private_key); - bool operator==(const SchnorrComputePublicKey&) const = default; -}; - -/** - * @struct SchnorrConstructSignature - * @brief Construct a Schnorr signature - */ -struct SchnorrConstructSignature { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "SchnorrConstructSignature"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "SchnorrConstructSignatureResponse"; - std::array s; - std::array e; - SERIALIZATION_FIELDS(s, e); - bool operator==(const Response&) const = default; - }; - - std::vector message; // Variable length - grumpkin::fr private_key; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(message, private_key); - bool operator==(const SchnorrConstructSignature&) const = default; -}; - -/** - * @struct SchnorrVerifySignature - * @brief Verify a Schnorr signature - */ -struct SchnorrVerifySignature { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "SchnorrVerifySignature"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "SchnorrVerifySignatureResponse"; - bool verified; - SERIALIZATION_FIELDS(verified); - bool operator==(const Response&) const = default; - }; - - std::vector message; - grumpkin::g1::affine_element public_key; - std::array s; - std::array e; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(message, public_key, s, e); - bool operator==(const SchnorrVerifySignature&) const = default; -}; - -} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_shared.hpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_shared.hpp index 9dc0f0913e42..a77b1a4db981 100644 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_shared.hpp +++ b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_shared.hpp @@ -283,7 +283,7 @@ template std::vector vk_to_uint256_fields(const VK& vk) * * @throws If oracle_hash_type is not poseidon2 */ -inline void validate_rollup_settings(const ProofSystemSettings& settings) +template inline void validate_rollup_settings(const Settings& settings) { if (!settings.ipa_accumulation) { return; // Not a rollup circuit, no validation needed @@ -314,7 +314,13 @@ inline void validate_rollup_settings(const ProofSystemSettings& settings) * @return The result of calling operation.template operator()() * */ -template auto dispatch_by_settings(const ProofSystemSettings& settings, Operation&& operation) +// Accepts any settings object exposing the same field names as the schema +// `ProofSystemSettings` (the domain one in this file and the codegen wire one +// in generated/bb_types.hpp are field-compatible). Templating on the settings +// type lets handlers pass `wire::ProofSystemSettings` directly without an +// extra wire→domain conversion at the boundary. +template +auto dispatch_by_settings(const Settings& settings, Operation&& operation) { // Rollup circuits: UltraFlavor with RollupIO (includes IPA accumulation for ECCVM) if (settings.ipa_accumulation) { diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_srs.cpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_srs.cpp deleted file mode 100644 index 9a6a4ddcac89..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_srs.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/** - * @file bbapi_srs.cpp - * @brief Implementation of SRS initialization command execution for the Barretenberg RPC API - */ -#include "barretenberg/bbapi/bbapi_srs.hpp" -#include "barretenberg/common/serialize.hpp" -#include "barretenberg/common/thread.hpp" -#include "barretenberg/crypto/sha256/sha256.hpp" -#include "barretenberg/ecc/curves/bn254/g1.hpp" -#include "barretenberg/ecc/curves/bn254/g2.hpp" -#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" -#include "barretenberg/numeric/uint256/uint256.hpp" -#include "barretenberg/srs/factories/bn254_crs_data.hpp" -#include "barretenberg/srs/factories/bn254_g1_chunk_hashes.hpp" -#include "barretenberg/srs/global_crs.hpp" -#include - -namespace bb::bbapi { - -SrsInitSrs::Response SrsInitSrs::execute(BB_UNUSED BBApiRequest& request) && -{ - constexpr size_t COMPRESSED_POINT_SIZE = 32; - constexpr size_t UNCOMPRESSED_POINT_SIZE = sizeof(g1::affine_element); // 64 - - size_t bytes_per_point = num_points > 0 ? points_buf.size() / num_points : 0; - std::vector g1_points(num_points); - std::vector uncompressed_out; - - if (bytes_per_point == UNCOMPRESSED_POINT_SIZE) { - // Already uncompressed: fast path with from_buffer - parallel_for([&](ThreadChunk chunk) { - for (auto i : chunk.range(static_cast(num_points))) { - g1_points[i] = from_buffer(points_buf.data(), i * UNCOMPRESSED_POINT_SIZE); - } - }); - } else if (bytes_per_point == COMPRESSED_POINT_SIZE) { - // Verify SHA-256 of every 4 MB chunk against the in-binary pin BN254_G1_CHUNK_HASHES. - // Require chunk-aligned input so every byte is covered (no partial trailing chunk). - if (points_buf.size() == 0 || points_buf.size() % bb::srs::SRS_CHUNK_SIZE_BYTES != 0) { - throw_or_abort("SrsInitSrs: compressed points_buf size " + std::to_string(points_buf.size()) + - " must be a positive multiple of " + std::to_string(bb::srs::SRS_CHUNK_SIZE_BYTES)); - } - size_t num_full_chunks = points_buf.size() / bb::srs::SRS_CHUNK_SIZE_BYTES; - size_t chunks_to_verify = std::min(num_full_chunks, static_cast(bb::srs::SRS_NUM_FULL_CHUNKS)); - for (size_t i = 0; i < chunks_to_verify; ++i) { - auto chunk = std::span(points_buf.data() + i * bb::srs::SRS_CHUNK_SIZE_BYTES, - bb::srs::SRS_CHUNK_SIZE_BYTES); - auto hash = bb::crypto::sha256(chunk); - if (hash != bb::srs::BN254_G1_CHUNK_HASHES[i]) { - throw_or_abort("SrsInitSrs: g1 compressed chunk " + std::to_string(i) + " SHA-256 mismatch"); - } - } - - // Compressed: decompress and return uncompressed bytes for caller to cache - parallel_for([&](ThreadChunk chunk) { - for (auto i : chunk.range(static_cast(num_points))) { - uint256_t c = from_buffer(points_buf.data(), i * COMPRESSED_POINT_SIZE); - g1_points[i] = g1::affine_element::from_compressed(c); - } - }); - // Serialize uncompressed points to return to caller for caching - uncompressed_out.resize(static_cast(num_points) * UNCOMPRESSED_POINT_SIZE); - parallel_for([&](ThreadChunk chunk) { - for (auto i : chunk.range(static_cast(num_points))) { - auto buf = to_buffer(g1_points[i]); - std::copy(buf.begin(), buf.end(), &uncompressed_out[i * UNCOMPRESSED_POINT_SIZE]); - } - }); - } else { - throw_or_abort("SrsInitSrs: invalid points_buf size. Expected 32 or 64 bytes per point, got " + - std::to_string(bytes_per_point)); - } - - // Pin the first two G1 points to their canonical trusted-setup values. Defense in depth on the - // compressed path; the only gate on the uncompressed (cached) path. - if (num_points >= 1 && g1_points[0] != bb::srs::BN254_G1_FIRST_ELEMENT) { - throw_or_abort("SrsInitSrs: g1_points[0] is not the canonical BN254 generator"); - } - if (num_points >= 2 && g1_points[1] != bb::srs::get_bn254_g1_second_element()) { - throw_or_abort("SrsInitSrs: g1_points[1] does not match the canonical trusted-setup tau·G"); - } - - // Defense in depth: hash-pin AND subgroup-check the G2 input. Hash equality alone is sufficient - // for the canonical case (it implies prime-order membership); the subgroup check is kept so - // that any future relaxation of the hash gate (e.g. a flag to allow a different trusted setup) - // does not silently reopen audit finding #7's small-subgroup attack. - auto g2_hash = bb::crypto::sha256(std::span(g2_point.data(), g2_point.size())); - if (g2_hash != bb::srs::BN254_G2_ELEMENT_SHA256) { - throw_or_abort("SrsInitSrs: g2_point bytes do not match the canonical Aztec [x]_2 SHA-256"); - } - auto g2_point_elem = from_buffer(g2_point.data()); - if (!g2_point_elem.is_in_prime_subgroup()) { - throw_or_abort("SrsInitSrs: g2_point is not in the BN254 G2 prime-order subgroup"); - } - - // Initialize BN254 SRS - bb::srs::init_bn254_mem_crs_factory(g1_points, g2_point_elem); - - return { .points_buf = std::move(uncompressed_out) }; -} - -SrsInitGrumpkinSrs::Response SrsInitGrumpkinSrs::execute(BB_UNUSED BBApiRequest& request) && -{ - // Validate buffer size before accessing raw pointer - const size_t required_size = static_cast(num_points) * sizeof(curve::Grumpkin::AffineElement); - if (points_buf.size() < required_size) { - throw_or_abort("SrsInitGrumpkinSrs: points_buf too small (" + std::to_string(points_buf.size()) + - " bytes) for num_points=" + std::to_string(num_points) + " (need " + - std::to_string(required_size) + ")"); - } - - // Parse Grumpkin affine elements from buffer - std::vector points(num_points); - for (uint32_t i = 0; i < num_points; ++i) { - points[i] = - from_buffer(points_buf.data(), i * sizeof(curve::Grumpkin::AffineElement)); - } - - // Initialize Grumpkin SRS - bb::srs::init_grumpkin_mem_crs_factory(points); - - return {}; -} - -} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_srs.hpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_srs.hpp deleted file mode 100644 index f59fc3ab4357..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_srs.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -/** - * @file bbapi_srs.hpp - * @brief SRS (Structured Reference String) initialization command definitions for the Barretenberg RPC API. - * - * This file contains command structures for initializing BN254 and Grumpkin SRS. - */ -#include "barretenberg/bbapi/bbapi_shared.hpp" -#include "barretenberg/common/named_union.hpp" -#include "barretenberg/serialize/msgpack.hpp" -#include -#include - -namespace bb::bbapi { - -/** - * @struct SrsInitSrs - * @brief Initialize BN254 SRS with G1 and G2 points - */ -struct SrsInitSrs { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "SrsInitSrs"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "SrsInitSrsResponse"; - std::vector - points_buf; // Uncompressed G1 points (64 bytes each), empty if input was already uncompressed - SERIALIZATION_FIELDS(points_buf); - bool operator==(const Response&) const = default; - }; - - std::vector points_buf; // G1 points: compressed (32 bytes each) or uncompressed (64 bytes each) - uint32_t num_points; - std::vector g2_point; // G2 point (128 bytes) - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(points_buf, num_points, g2_point); - bool operator==(const SrsInitSrs&) const = default; -}; - -/** - * @struct SrsInitGrumpkinSrs - * @brief Initialize Grumpkin SRS with Grumpkin points - */ -struct SrsInitGrumpkinSrs { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "SrsInitGrumpkinSrs"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "SrsInitGrumpkinSrsResponse"; - uint8_t dummy = 0; // Empty response needs a dummy field for msgpack - SERIALIZATION_FIELDS(dummy); - bool operator==(const Response&) const = default; - }; - - std::vector points_buf; // Grumpkin affine elements - uint32_t num_points; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(points_buf, num_points); - bool operator==(const SrsInitGrumpkinSrs&) const = default; -}; - -} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ultra_honk.cpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ultra_honk.cpp index e89c53f393f1..78150599298b 100644 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ultra_honk.cpp +++ b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ultra_honk.cpp @@ -1,5 +1,8 @@ -#include "barretenberg/bbapi/bbapi_ultra_honk.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/common/bb_bench.hpp" #include "barretenberg/common/serialize.hpp" #include "barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp" #include "barretenberg/dsl/acir_format/serde/witness_stack.hpp" @@ -13,6 +16,8 @@ namespace bb::bbapi { +namespace { + template acir_format::ProgramMetadata _create_program_metadata() { return acir_format::ProgramMetadata{ .has_ipa_claim = IO::HasIPA }; @@ -23,7 +28,6 @@ Circuit _compute_circuit(std::vector&& bytecode, std::vector&& { const acir_format::ProgramMetadata metadata = _create_program_metadata(); acir_format::AcirProgram program{ acir_format::circuit_buf_to_acir_format(std::move(bytecode)), {} }; - if (!witness.empty()) { program.witness = acir_format::witness_buf_to_witness_vector(std::move(witness)); } @@ -34,7 +38,6 @@ template std::shared_ptr> _compute_prover_instance(std::vector&& bytecode, std::vector&& witness) { - // Measure function time and debug print auto initial_time = std::chrono::high_resolution_clock::now(); typename Flavor::CircuitBuilder builder = _compute_circuit(std::move(bytecode), std::move(witness)); auto prover_instance = std::make_shared>(builder); @@ -42,9 +45,6 @@ std::shared_ptr> _compute_prover_instance(std::vector(final_time - initial_time); info("CircuitProve: Proving key computed in ", duration.count(), " ms"); - // Validate consistency between IO type and IPA proof presence - // IO::HasIPA indicates the circuit type requires IPA accumulation (rollup circuits) - // prover_instance->ipa_proof contains the actual IPA proof data from the circuit if constexpr (IO::HasIPA) { BB_ASSERT(!prover_instance->ipa_proof.empty(), "RollupIO circuit expected IPA proof but none was provided. " @@ -54,20 +54,19 @@ std::shared_ptr> _compute_prover_instance(std::vector -CircuitProve::Response _prove(std::vector&& bytecode, - std::vector&& witness, - std::vector&& vk_bytes) +wire::CircuitProveResponse _prove(std::vector&& bytecode, + std::vector&& witness, + std::vector&& vk_bytes) { using Proof = typename Flavor::Transcript::Proof; using VerificationKey = typename Flavor::VerificationKey; auto prover_instance = _compute_prover_instance(std::move(bytecode), std::move(witness)); - // Create or deserialize VK std::shared_ptr vk; if (vk_bytes.empty()) { info("WARNING: computing verification key while proving. Pass in a precomputed vk for better performance."); @@ -77,27 +76,26 @@ CircuitProve::Response _prove(std::vector&& bytecode, vk = std::make_shared(from_buffer(vk_bytes)); } - // Construct proof UltraProver_ prover{ prover_instance, vk }; Proof full_proof = prover.construct_proof(); - // Compute where to split (inner public inputs vs everything else) size_t num_public_inputs = prover.num_public_inputs(); BB_ASSERT_GTE(num_public_inputs, IO::PUBLIC_INPUTS_SIZE, "Public inputs should contain the expected IO structure."); size_t num_inner_public_inputs = num_public_inputs - IO::PUBLIC_INPUTS_SIZE; - // Optimization: if vk not provided, include it in response - CircuitComputeVk::Response vk_response; + wire::CircuitComputeVkResponse vk_response; if (vk_bytes.empty()) { - vk_response = { .bytes = to_buffer(*vk), .fields = vk_to_uint256_fields(*vk), .hash = to_buffer(vk->hash()) }; + vk_response = { .bytes = to_buffer(*vk), + .fields = uint256_vec_to_wire(vk_to_uint256_fields(*vk)), + .hash = to_buffer(vk->hash()) }; } - // Split proof: inner public inputs at front, rest is the "proof" - return { .public_inputs = - std::vector{ full_proof.begin(), - full_proof.begin() + static_cast(num_inner_public_inputs) }, - .proof = std::vector{ full_proof.begin() + static_cast(num_inner_public_inputs), - full_proof.end() }, + std::vector public_inputs{ full_proof.begin(), + full_proof.begin() + static_cast(num_inner_public_inputs) }; + std::vector proof{ full_proof.begin() + static_cast(num_inner_public_inputs), + full_proof.end() }; + return { .public_inputs = uint256_vec_to_wire(public_inputs), + .proof = uint256_vec_to_wire(proof), .vk = std::move(vk_response) }; } @@ -110,7 +108,6 @@ bool _verify(const std::vector& vk_bytes, using VKAndHash = typename Flavor::VKAndHash; using Verifier = UltraVerifier_; - // Validate VK size upfront before deserialization const size_t expected_vk_size = VerificationKey::calc_num_data_types() * sizeof(bb::fr); if (vk_bytes.size() != expected_vk_size) { info( @@ -122,7 +119,6 @@ bool _verify(const std::vector& vk_bytes, auto vk_and_hash = std::make_shared(vk); Verifier verifier{ vk_and_hash }; - // Validate proof size const size_t log_n = verifier.compute_log_n(); const size_t expected_size = ProofLength::Honk::template expected_proof_size(log_n); if (proof.size() != expected_size) { @@ -132,46 +128,24 @@ bool _verify(const std::vector& vk_bytes, auto complete_proof = concatenate_proof(public_inputs, proof); bool verified = verifier.verify_proof(complete_proof).result; - if (verified) { info("Proof verified successfully"); } else { info("Proof verification failed"); } - return verified; } -CircuitProve::Response CircuitProve::execute(BB_UNUSED const BBApiRequest& request) && -{ - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); - return dispatch_by_settings(settings, [&]() { - return _prove(std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key)); - }); -} - -CircuitComputeVk::Response CircuitComputeVk::execute(BB_UNUSED const BBApiRequest& request) && -{ - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); - return dispatch_by_settings(settings, [&]() { - auto prover_instance = _compute_prover_instance(std::move(circuit.bytecode), {}); - auto vk = std::make_shared(prover_instance->get_precomputed()); - return CircuitComputeVk::Response{ .bytes = to_buffer(*vk), - .fields = vk_to_uint256_fields(*vk), - .hash = to_buffer(vk->hash()) }; - }); -} - template -CircuitStats::Response _stats(std::vector&& bytecode, bool include_gates_per_opcode) +wire::CircuitInfoResponse _stats(std::vector&& bytecode, bool include_gates_per_opcode) { using Circuit = typename Flavor::CircuitBuilder; - // Parse the circuit to get gate count information auto constraint_system = acir_format::circuit_buf_to_acir_format(std::move(bytecode)); acir_format::ProgramMetadata metadata = _create_program_metadata(); metadata.collect_gates_per_opcode = include_gates_per_opcode; - CircuitStats::Response response; + + wire::CircuitInfoResponse response; response.num_acir_opcodes = static_cast(constraint_system.num_acir_opcodes); acir_format::AcirProgram program{ std::move(constraint_system), {} }; @@ -180,80 +154,87 @@ CircuitStats::Response _stats(std::vector&& bytecode, bool include_gate response.num_gates = static_cast(builder.get_finalized_total_circuit_size()); response.num_gates_dyadic = static_cast(builder.get_circuit_subgroup_size(response.num_gates)); - // note: will be empty if collect_gates_per_opcode is false response.gates_per_opcode = std::vector(program.constraints.gates_per_opcode.begin(), program.constraints.gates_per_opcode.end()); - return response; } -CircuitStats::Response CircuitStats::execute(BB_UNUSED const BBApiRequest& request) && +} // namespace + +wire::CircuitProveResponse handle_circuit_prove(BBApiRequest& /*ctx*/, wire::CircuitProve&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); - return dispatch_by_settings(settings, [&]() { - return _stats(std::move(circuit.bytecode), include_gates_per_opcode); + BB_BENCH_NAME("CircuitProve"); + return dispatch_by_settings(cmd.settings, [&]() { + return _prove( + std::move(cmd.circuit.bytecode), std::move(cmd.witness), std::move(cmd.circuit.verification_key)); }); } -CircuitVerify::Response CircuitVerify::execute(BB_UNUSED const BBApiRequest& request) && +wire::CircuitComputeVkResponse handle_circuit_compute_vk(BBApiRequest& /*ctx*/, wire::CircuitComputeVk&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); - bool verified = dispatch_by_settings(settings, [&]() { - return _verify(verification_key, public_inputs, proof); + BB_BENCH_NAME("CircuitComputeVk"); + return dispatch_by_settings(cmd.settings, [&]() { + auto prover_instance = _compute_prover_instance(std::move(cmd.circuit.bytecode), {}); + auto vk = std::make_shared(prover_instance->get_precomputed()); + return wire::CircuitComputeVkResponse{ .bytes = to_buffer(*vk), + .fields = uint256_vec_to_wire(vk_to_uint256_fields(*vk)), + .hash = to_buffer(vk->hash()) }; }); - return { verified }; } -VkAsFields::Response VkAsFields::execute(BB_UNUSED const BBApiRequest& request) && +wire::CircuitInfoResponse handle_circuit_stats(BBApiRequest& /*ctx*/, wire::CircuitStats&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); - - using VK = UltraFlavor::VerificationKey; - validate_vk_size(verification_key); - - // Standard UltraHonk flavors - auto vk = from_buffer(verification_key); - std::vector fields; - fields = vk.to_field_elements(); + BB_BENCH_NAME("CircuitStats"); + return dispatch_by_settings(cmd.settings, [&]() { + return _stats(std::move(cmd.circuit.bytecode), cmd.include_gates_per_opcode); + }); +} - return { std::move(fields) }; +wire::CircuitVerifyResponse handle_circuit_verify(BBApiRequest& /*ctx*/, wire::CircuitVerify&& cmd) +{ + BB_BENCH_NAME("CircuitVerify"); + auto pi_domain = uint256_vec_from_wire(cmd.public_inputs); + auto proof_domain = uint256_vec_from_wire(cmd.proof); + bool verified = dispatch_by_settings(cmd.settings, [&]() { + return _verify(cmd.verification_key, pi_domain, proof_domain); + }); + return { .verified = verified }; } -MegaVkAsFields::Response MegaVkAsFields::execute(BB_UNUSED const BBApiRequest& request) && +wire::VkAsFieldsResponse handle_vk_as_fields(BBApiRequest& /*ctx*/, wire::VkAsFields&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); + BB_BENCH_NAME("VkAsFields"); + using VK = UltraFlavor::VerificationKey; + validate_vk_size(cmd.verification_key); + auto vk = from_buffer(cmd.verification_key); + return { .fields = fr_vec_to_wire(vk.to_field_elements()) }; +} +wire::MegaVkAsFieldsResponse handle_mega_vk_as_fields(BBApiRequest& /*ctx*/, wire::MegaVkAsFields&& cmd) +{ + BB_BENCH_NAME("MegaVkAsFields"); using VK = MegaFlavor::VerificationKey; - validate_vk_size(verification_key); - - // MegaFlavor for private function verification keys - auto vk = from_buffer(verification_key); - std::vector fields; - fields = vk.to_field_elements(); - - return { std::move(fields) }; + validate_vk_size(cmd.verification_key); + auto vk = from_buffer(cmd.verification_key); + return { .fields = fr_vec_to_wire(vk.to_field_elements()) }; } -CircuitWriteSolidityVerifier::Response CircuitWriteSolidityVerifier::execute(BB_UNUSED const BBApiRequest& request) && +wire::CircuitWriteSolidityVerifierResponse handle_circuit_write_solidity_verifier( + BBApiRequest& /*ctx*/, wire::CircuitWriteSolidityVerifier&& cmd) { - BB_BENCH_NAME(MSGPACK_SCHEMA_NAME); + BB_BENCH_NAME("CircuitWriteSolidityVerifier"); using VK = UltraKeccakFlavor::VerificationKey; - validate_vk_size(verification_key); - - auto vk = std::make_shared(from_buffer(verification_key)); + validate_vk_size(cmd.verification_key); + auto vk = std::make_shared(from_buffer(cmd.verification_key)); - std::string contract = settings.disable_zk ? get_honk_solidity_verifier(vk) : get_honk_zk_solidity_verifier(vk); - -// If in wasm, we dont include the optimized solidity verifier - due to its large bundle size -// This will run generate twice, but this should only be run before deployment and not frequently + std::string contract = cmd.settings.disable_zk ? get_honk_solidity_verifier(vk) : get_honk_zk_solidity_verifier(vk); #ifndef __wasm__ - if (settings.optimized_solidity_verifier) { - contract = settings.disable_zk ? get_optimized_honk_solidity_verifier(vk) - : get_optimized_honk_zk_solidity_verifier(vk); + if (cmd.settings.optimized_solidity_verifier) { + contract = cmd.settings.disable_zk ? get_optimized_honk_solidity_verifier(vk) + : get_optimized_honk_zk_solidity_verifier(vk); } #endif - - return { std::move(contract) }; + return { .solidity_code = std::move(contract) }; } } // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ultra_honk.hpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ultra_honk.hpp deleted file mode 100644 index 9ab37bbfd6cc..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ultra_honk.hpp +++ /dev/null @@ -1,191 +0,0 @@ -#pragma once -/** - * @file bbapi_ultra_honk.hpp - * @brief UltraHonk-specific command definitions for the Barretenberg RPC API. - * - * This file contains command structures for UltraHonk proof system operations - * including circuit proving, verification, VK computation, and utility functions. - */ -#include "barretenberg/bbapi/bbapi_shared.hpp" -#include "barretenberg/common/named_union.hpp" -#include "barretenberg/honk/proof_system/types/proof.hpp" -#include "barretenberg/numeric/uint256/uint256.hpp" -#include "barretenberg/serialize/msgpack.hpp" -#include -#include -#include - -namespace bb::bbapi { - -struct CircuitComputeVk { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "CircuitComputeVk"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "CircuitComputeVkResponse"; - - std::vector bytes; // Serialized verification key - std::vector fields; // VK as field elements (unless keccak, then just uint256_t's) - std::vector hash; // The VK hash - SERIALIZATION_FIELDS(bytes, fields, hash); - bool operator==(const Response&) const = default; - }; - - CircuitInputNoVK circuit; - ProofSystemSettings settings; - SERIALIZATION_FIELDS(circuit, settings); - Response execute(const BBApiRequest& request = {}) &&; - bool operator==(const CircuitComputeVk&) const = default; -}; - -/** - * @struct CircuitProve - * @brief Represents a request to generate a proof. - * Currently, UltraHonk is the only proving system supported by BB (after plonk was deprecated and removed). - * This is used for one-shot proving, not our "IVC" scheme, Chonk. For that, use the Chonk* - * commands. - */ -struct CircuitProve { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "CircuitProve"; - - /** - * @brief Contains proof and public inputs. - * Both are given as vectors of fields. To be used for verification. - * Example uses of this Response would be verification in native BB, WASM BB, solidity or recursively through Noir. - */ - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "CircuitProveResponse"; - - std::vector public_inputs; - std::vector proof; - CircuitComputeVk::Response vk; - SERIALIZATION_FIELDS(public_inputs, proof, vk); - bool operator==(const Response&) const = default; - }; - - CircuitInput circuit; - std::vector witness; - ProofSystemSettings settings; - SERIALIZATION_FIELDS(circuit, witness, settings); - Response execute(const BBApiRequest& request = {}) &&; - bool operator==(const CircuitProve&) const = default; -}; - -/** - * @struct CircuitStats - * @brief Consolidated command for retrieving circuit information. - * Combines gate count, circuit size, and other metadata into a single command. - */ -struct CircuitStats { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "CircuitStats"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "CircuitInfoResponse"; - - uint32_t num_gates{}; - uint32_t num_gates_dyadic{}; - uint32_t num_acir_opcodes{}; - std::vector gates_per_opcode; - SERIALIZATION_FIELDS(num_gates, num_gates_dyadic, num_acir_opcodes, gates_per_opcode); - bool operator==(const Response&) const = default; - }; - - CircuitInput circuit; - bool include_gates_per_opcode = false; - ProofSystemSettings settings; - SERIALIZATION_FIELDS(circuit, include_gates_per_opcode, settings); - Response execute(const BBApiRequest& request = {}) &&; - bool operator==(const CircuitStats&) const = default; -}; - -/** - * @struct CircuitVerify - * @brief Verify a proof against a verification key and public inputs. - */ -struct CircuitVerify { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "CircuitVerify"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "CircuitVerifyResponse"; - - bool verified; - SERIALIZATION_FIELDS(verified); - bool operator==(const Response&) const = default; - }; - - std::vector verification_key; - std::vector public_inputs; - std::vector proof; - ProofSystemSettings settings; - SERIALIZATION_FIELDS(verification_key, public_inputs, proof, settings); - Response execute(const BBApiRequest& request = {}) &&; - bool operator==(const CircuitVerify&) const = default; -}; - -/** - * @struct VkAsFields - * @brief Convert a verification key to field elements representation. - * WORKTODO(bbapi): this should become mostly obsolete with having the verification keys always reported as field -elements as well, - * and having a simpler serialization method. - */ -struct VkAsFields { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "VkAsFields"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "VkAsFieldsResponse"; - - std::vector fields; - SERIALIZATION_FIELDS(fields); - bool operator==(const Response&) const = default; - }; - - std::vector verification_key; - SERIALIZATION_FIELDS(verification_key); - Response execute(const BBApiRequest& request = {}) &&; - bool operator==(const VkAsFields&) const = default; -}; - -/** - * @struct MegaVkAsFields - * @brief Convert a MegaFlavor verification key to field elements representation. - * Used for private function verification keys which use MegaFlavor (127 fields). - */ -struct MegaVkAsFields { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "MegaVkAsFields"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "MegaVkAsFieldsResponse"; - - std::vector fields; - SERIALIZATION_FIELDS(fields); - bool operator==(const Response&) const = default; - }; - - std::vector verification_key; - SERIALIZATION_FIELDS(verification_key); - Response execute(const BBApiRequest& request = {}) &&; - bool operator==(const MegaVkAsFields&) const = default; -}; - -/** - * @brief Command to generate Solidity verifier contract - */ -struct CircuitWriteSolidityVerifier { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "CircuitWriteSolidityVerifier"; - - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "CircuitWriteSolidityVerifierResponse"; - - std::string solidity_code; - SERIALIZATION_FIELDS(solidity_code); - bool operator==(const Response&) const = default; - }; - - std::vector verification_key; - ProofSystemSettings settings; - SERIALIZATION_FIELDS(verification_key, settings); - Response execute(const BBApiRequest& request = {}) &&; - bool operator==(const CircuitWriteSolidityVerifier&) const = default; -}; - -} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_wire_convert.hpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_wire_convert.hpp new file mode 100644 index 000000000000..f7d6d4fbef9e --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_wire_convert.hpp @@ -0,0 +1,344 @@ +#pragma once +/** + * @file bbapi_wire_convert.hpp + * @brief Wire <-> domain conversion helpers for the bbapi handlers. + * + * All conversions are field-by-field: each handler in bbapi_handlers.cpp + * builds the domain command struct from the wire fields, calls execute(), + * and builds the wire response from the domain response fields. + * + * Wire field types (Fr / Fq / Uint256 / … — all `std::array` + * aliases) and domain field types (`bb::fr`, `bb::fq`, `uint256_t`, …) + * share a 32-byte msgpack `bin32` encoding, so the byte-level conversion + * is a `serialize_to_buffer` / `serialize_from_buffer` call. + */ +#include "barretenberg/bbapi/bbapi_chonk.hpp" +#include "barretenberg/bbapi/bbapi_shared.hpp" +#include "barretenberg/bbapi/generated/bb_types.hpp" +#include "barretenberg/ecc/curves/bn254/bn254.hpp" +#include "barretenberg/ecc/curves/bn254/fq.hpp" +#include "barretenberg/ecc/curves/bn254/fq2.hpp" +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" +#include "barretenberg/ecc/curves/secp256k1/secp256k1.hpp" +#include "barretenberg/ecc/curves/secp256r1/secp256r1.hpp" +#include "barretenberg/numeric/uint256/uint256.hpp" +#include "barretenberg/serialize/msgpack.hpp" + +#include +#include +#include + +namespace bb::bbapi { + +// --------------------------------------------------------------------------- +// Field element conversions. All field types (bb::fr, bb::fq, grumpkin::fr, +// grumpkin::fq, secp256k1::*, secp256r1::*) pack as msgpack bin32. Wire `Fr` +// also packs as bin32 (codegen Fr struct with custom adaptor). So a wire +// `Fr` and any domain field element have identical wire bytes and can be +// converted via serialize_to_buffer / serialize_from_buffer. +// --------------------------------------------------------------------------- + +template inline ::Fr field_to_wire(const Field& d) +{ + ::Fr r{}; + Field::serialize_to_buffer(d, r.data()); + return r; +} + +template inline Field field_from_wire(const ::Fr& w) +{ + return Field::serialize_from_buffer(w.data()); +} + +inline ::Fr fr_to_wire(const bb::fr& d) +{ + return field_to_wire(d); +} +inline bb::fr fr_from_wire(const ::Fr& w) +{ + return field_from_wire(w); +} + +// Wire `Fr` / `Fq` / `Secp256k1Fr` / … are all `using` aliases for +// std::array, so what used to be `fr_wrap` / `fr_unwrap` is the +// identity. The handlers can pass the byte array straight through without +// helper calls; these names stay as compile-time-checked no-ops so existing +// call sites read clearly at the conversion boundary. +inline std::array fr_wrap(std::array bytes) +{ + return bytes; +} +inline std::array fr_unwrap(std::array bytes) +{ + return bytes; +} + +// Fixed-size array <-> std::vector for fields the schema declares as +// ["array","unsigned char",N] when N != 32 (which would collapse to fr). Used +// for AES iv/key (16 bytes) and GrumpkinReduce512/Secp256k1Reduce512 input +// (64 bytes). The wire side is a length-prefixed vector; the domain side is +// a fixed std::array. +template inline std::array array_from_vec(const std::vector& v) +{ + std::array r{}; + if (v.size() != N) + throw std::runtime_error("array_from_vec: size mismatch"); + std::memcpy(r.data(), v.data(), N); + return r; +} +template inline std::vector vec_from_array(const std::array& a) +{ + return std::vector(a.begin(), a.end()); +} + +inline std::vector<::Fr> fr_vec_to_wire(const std::vector& d) +{ + std::vector<::Fr> r; + r.reserve(d.size()); + for (const auto& x : d) { + r.push_back(fr_to_wire(x)); + } + return r; +} + +inline std::vector fr_vec_from_wire(const std::vector<::Fr>& w) +{ + std::vector r; + r.reserve(w.size()); + for (const auto& x : w) { + r.push_back(fr_from_wire(x)); + } + return r; +} + +template inline std::array<::Fr, N> fr_array_to_wire(const std::array& d) +{ + std::array<::Fr, N> r{}; + for (std::size_t i = 0; i < N; ++i) { + r[i] = fr_to_wire(d[i]); + } + return r; +} + +template inline std::array fr_array_from_wire(const std::array<::Fr, N>& w) +{ + std::array r{}; + for (std::size_t i = 0; i < N; ++i) { + r[i] = fr_from_wire(w[i]); + } + return r; +} + +// --------------------------------------------------------------------------- +// Curve point conversions. Wire types follow a uniform {Fr x, Fr y} shape. +// Domain types use the curve-specific affine_element. The default +// affine_element msgpack adapter packs as a 2-field map {x: bin32, y: bin32}, +// matching the wire encoding, so field-by-field conversion is safe. +// --------------------------------------------------------------------------- + +inline wire::GrumpkinPoint grumpkin_point_to_wire(const grumpkin::g1::affine_element& d) +{ + return { .x = field_to_wire(d.x), .y = field_to_wire(d.y) }; +} + +inline grumpkin::g1::affine_element grumpkin_point_from_wire(const wire::GrumpkinPoint& w) +{ + return { field_from_wire(w.x), field_from_wire(w.y) }; +} + +inline std::vector grumpkin_point_vec_to_wire(const std::vector& d) +{ + std::vector r; + r.reserve(d.size()); + for (const auto& p : d) { + r.push_back(grumpkin_point_to_wire(p)); + } + return r; +} + +inline std::vector grumpkin_point_vec_from_wire(const std::vector& w) +{ + std::vector r; + r.reserve(w.size()); + for (const auto& p : w) { + r.push_back(grumpkin_point_from_wire(p)); + } + return r; +} + +inline wire::Bn254G1Point bn254_g1_point_to_wire(const bb::g1::affine_element& d) +{ + return { .x = field_to_wire(d.x), .y = field_to_wire(d.y) }; +} + +inline bb::g1::affine_element bn254_g1_point_from_wire(const wire::Bn254G1Point& w) +{ + return { field_from_wire(w.x), field_from_wire(w.y) }; +} + +// Fq2 = { c0: bb::fq, c1: bb::fq }; wire field2 = std::array, 2>. +inline std::array, 2> fq2_to_wire(const bb::fq2& d) +{ + return { field_to_wire(d.c0), field_to_wire(d.c1) }; +} + +inline bb::fq2 fq2_from_wire(const std::array, 2>& w) +{ + return { field_from_wire(w[0]), field_from_wire(w[1]) }; +} + +inline wire::Bn254G2Point bn254_g2_point_to_wire(const bb::g2::affine_element& d) +{ + return { .x = fq2_to_wire(d.x), .y = fq2_to_wire(d.y) }; +} + +inline bb::g2::affine_element bn254_g2_point_from_wire(const wire::Bn254G2Point& w) +{ + return { fq2_from_wire(w.x), fq2_from_wire(w.y) }; +} + +inline wire::Secp256k1Point secp256k1_point_to_wire(const secp256k1::g1::affine_element& d) +{ + return { .x = field_to_wire(d.x), .y = field_to_wire(d.y) }; +} + +inline secp256k1::g1::affine_element secp256k1_point_from_wire(const wire::Secp256k1Point& w) +{ + return { field_from_wire(w.x), field_from_wire(w.y) }; +} + +inline wire::Secp256r1Point secp256r1_point_to_wire(const secp256r1::g1::affine_element& d) +{ + return { .x = field_to_wire(d.x), .y = field_to_wire(d.y) }; +} + +inline secp256r1::g1::affine_element secp256r1_point_from_wire(const wire::Secp256r1Point& w) +{ + return { field_from_wire(w.x), field_from_wire(w.y) }; +} + +// --------------------------------------------------------------------------- +// uint256_t ↔ Uint256 (= std::array). +// Wire format is 32 bytes big-endian (matches uint256_t::msgpack_pack). +// --------------------------------------------------------------------------- + +inline ::Uint256 uint256_to_wire(const bb::numeric::uint256_t& d) +{ + ::Uint256 r{}; + for (std::size_t i = 0; i < 4; ++i) { + const uint64_t v = d.data[3 - i]; + for (std::size_t j = 0; j < 8; ++j) { + r[i * 8 + j] = static_cast(v >> (56 - j * 8)); + } + } + return r; +} + +inline bb::numeric::uint256_t uint256_from_wire(const ::Uint256& w) +{ + uint64_t parts[4]{}; + for (std::size_t i = 0; i < 4; ++i) { + uint64_t v = 0; + for (std::size_t j = 0; j < 8; ++j) { + v = (v << 8) | w[i * 8 + j]; + } + parts[i] = v; + } + return bb::numeric::uint256_t(parts[3], parts[2], parts[1], parts[0]); +} + +inline std::vector<::Uint256> uint256_vec_to_wire(const std::vector& d) +{ + std::vector<::Uint256> r; + r.reserve(d.size()); + for (const auto& x : d) { + r.push_back(uint256_to_wire(x)); + } + return r; +} + +inline std::vector uint256_vec_from_wire(const std::vector<::Uint256>& w) +{ + std::vector r; + r.reserve(w.size()); + for (const auto& x : w) { + r.push_back(uint256_from_wire(x)); + } + return r; +} + +// --------------------------------------------------------------------------- +// Aggregate (struct) conversions: each handler builds these by moving fields +// across the wire ↔ domain boundary one at a time. +// --------------------------------------------------------------------------- + +inline CircuitInput circuit_input_from_wire(wire::CircuitInput&& w) +{ + return { .name = std::move(w.name), + .bytecode = std::move(w.bytecode), + .verification_key = std::move(w.verification_key) }; +} + +inline wire::CircuitInput circuit_input_to_wire(CircuitInput&& d) +{ + return { .name = std::move(d.name), + .bytecode = std::move(d.bytecode), + .verification_key = std::move(d.verification_key) }; +} + +inline CircuitInputNoVK circuit_input_no_vk_from_wire(wire::CircuitInputNoVK&& w) +{ + return { .name = std::move(w.name), .bytecode = std::move(w.bytecode) }; +} + +inline wire::CircuitInputNoVK circuit_input_no_vk_to_wire(CircuitInputNoVK&& d) +{ + return { .name = std::move(d.name), .bytecode = std::move(d.bytecode) }; +} + +inline ProofSystemSettings proof_system_settings_from_wire(wire::ProofSystemSettings&& w) +{ + return { .ipa_accumulation = w.ipa_accumulation, + .oracle_hash_type = std::move(w.oracle_hash_type), + .disable_zk = w.disable_zk, + .optimized_solidity_verifier = w.optimized_solidity_verifier }; +} + +inline wire::ProofSystemSettings proof_system_settings_to_wire(ProofSystemSettings&& d) +{ + return { .ipa_accumulation = d.ipa_accumulation, + .oracle_hash_type = std::move(d.oracle_hash_type), + .disable_zk = d.disable_zk, + .optimized_solidity_verifier = d.optimized_solidity_verifier }; +} + +inline ChonkProof chonk_proof_from_wire(wire::ChonkProof&& w) +{ + return ChonkProof(fr_vec_from_wire(w.hiding_oink_proof), + fr_vec_from_wire(w.merge_proof), + fr_vec_from_wire(w.eccvm_proof), + fr_vec_from_wire(w.ipa_proof), + fr_vec_from_wire(w.joint_proof)); +} + +inline wire::ChonkProof chonk_proof_to_wire(const ChonkProof& d) +{ + return { .hiding_oink_proof = fr_vec_to_wire(d.hiding_oink_proof), + .merge_proof = fr_vec_to_wire(d.merge_proof), + .eccvm_proof = fr_vec_to_wire(d.eccvm_proof), + .ipa_proof = fr_vec_to_wire(d.ipa_proof), + .joint_proof = fr_vec_to_wire(d.joint_proof) }; +} + +inline std::vector chonk_proof_vec_from_wire(std::vector&& w) +{ + std::vector r; + r.reserve(w.size()); + for (auto& p : w) { + r.push_back(chonk_proof_from_wire(std::move(p))); + } + return r; +} + +} // namespace bb::bbapi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/c_bind.cpp b/barretenberg/cpp/src/barretenberg/bbapi/c_bind.cpp index dd74f8dcf759..c9ecb5b74891 100644 --- a/barretenberg/cpp/src/barretenberg/bbapi/c_bind.cpp +++ b/barretenberg/cpp/src/barretenberg/bbapi/c_bind.cpp @@ -1,41 +1,34 @@ #include "c_bind.hpp" -#include "barretenberg/bbapi/bbapi_execute.hpp" +#include "barretenberg/bbapi/bbapi_handlers.hpp" #include "barretenberg/bbapi/bbapi_shared.hpp" +#include "barretenberg/bbapi/generated/bb_ipc_server.hpp" #include "barretenberg/common/throw_or_abort.hpp" #include "barretenberg/serialize/msgpack_impl.hpp" -#ifndef NO_MULTITHREADING -#include -#endif +#include +#include +#include namespace bb::bbapi { -// Global BBApiRequest object in anonymous namespace namespace { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) BBApiRequest global_request; } // namespace -/** - * @brief Main API function that processes commands and returns responses - * - * @param command The command to execute - * @return CommandResponse The response from executing the command - */ -CommandResponse bbapi(Command&& command) -{ -#ifndef BB_NO_EXCEPTIONS - try { -#endif - // Execute the command using the global request and return the response - return execute(global_request, std::move(command)); -#ifndef BB_NO_EXCEPTIONS - } catch (const std::exception& e) { - return ErrorResponse{ .message = e.what() }; - } -#endif -} - } // namespace bb::bbapi -// Use CBIND macro to export the bbapi function for WASM -CBIND_NOSCHEMA(bbapi, bb::bbapi::bbapi) +// WASM-exported bbapi entry point. Takes msgpack-encoded `[ [name, payload] ]` +// (tuple-wrapped command in NamedUnion shape), returns msgpack-encoded +// `[name, payload]` (response in NamedUnion shape). The codegen-emitted +// dispatcher owns the command-name → handle_ table and runs the +// per-call deserialize / serialize / exception → ErrorResponse plumbing. +WASM_EXPORT void bbapi(const uint8_t* input_in, size_t input_len_in, uint8_t** output_out, size_t* output_len_out) +{ + auto handler = bb::bbapi::make_bb_handler(bb::bbapi::global_request); + std::vector input(input_in, input_in + input_len_in); + std::vector response = handler(input); + + *output_out = static_cast(aligned_alloc(64, response.size() + 1)); + std::memcpy(*output_out, response.data(), response.size()); + *output_len_out = response.size(); +} diff --git a/barretenberg/cpp/src/barretenberg/bbapi/c_bind.hpp b/barretenberg/cpp/src/barretenberg/bbapi/c_bind.hpp index 7b7878d4412a..e21767f5cd89 100644 --- a/barretenberg/cpp/src/barretenberg/bbapi/c_bind.hpp +++ b/barretenberg/cpp/src/barretenberg/bbapi/c_bind.hpp @@ -1,12 +1,8 @@ #pragma once -#include "barretenberg/bbapi/bbapi_execute.hpp" #include "barretenberg/serialize/cbind_fwd.hpp" #include -namespace bb::bbapi { -// Function declaration for CLI usage -CommandResponse bbapi(Command&& command); -} // namespace bb::bbapi - -// Forward declaration for CBIND +// WASM-exported bbapi entry point. Takes msgpack `[ [name, payload] ]`, +// returns msgpack `[name, payload]`. See c_bind.cpp for the implementation +// (calls the codegen-emitted `make_bb_handler` dispatcher). CBIND_DECL(bbapi) diff --git a/barretenberg/cpp/src/barretenberg/bbapi/c_bind_exception.test.cpp b/barretenberg/cpp/src/barretenberg/bbapi/c_bind_exception.test.cpp index e38f71320b2c..de5e252653bc 100644 --- a/barretenberg/cpp/src/barretenberg/bbapi/c_bind_exception.test.cpp +++ b/barretenberg/cpp/src/barretenberg/bbapi/c_bind_exception.test.cpp @@ -1,62 +1,91 @@ -#include "barretenberg/bbapi/bbapi_execute.hpp" -#include "barretenberg/bbapi/bbapi_srs.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/bbapi/generated/bb_types.hpp" +#include "barretenberg/serialize/msgpack.hpp" +#include "ipc_runtime/ipc_server.hpp" #include #include #include +#include -using namespace bb::bbapi; +using namespace bb; #ifndef BB_NO_EXCEPTIONS -// Test that exceptions thrown during command execution are caught and converted to ErrorResponse -TEST(CBind, CatchesExceptionAndReturnsErrorResponse) +namespace { +// Pack a wire-typed command into the bb dispatcher's expected input: +// `[ [type_name, payload] ]`. +template std::vector pack_wire_command(const WireCmd& cmd) { - // Create an SrsInitSrs command with invalid data that will cause an exception - // The from_buffer calls in bbapi_srs.cpp will read past buffer boundaries - SrsInitSrs cmd; - cmd.num_points = 100; // Request 100 points (6400 bytes needed) - cmd.points_buf = std::vector(10, 0); // Only provide 10 bytes - will cause out of bounds access - cmd.g2_point = std::vector(10, 0); // Also too small (needs 128 bytes) + msgpack::sbuffer buf; + msgpack::packer pk(buf); + pk.pack_array(1); + pk.pack_array(2); + pk.pack(std::string(WireCmd::MSGPACK_SCHEMA_NAME)); + pk.pack(cmd); + return std::vector(buf.data(), buf.data() + buf.size()); +} - Command command = std::move(cmd); +// Extract the response type name from a packed `[name, payload]` response. +std::string response_type_name(const std::vector& bytes) +{ + auto unpacked = msgpack::unpack(reinterpret_cast(bytes.data()), bytes.size()); + auto obj = unpacked.get(); + if (obj.type != msgpack::type::ARRAY || obj.via.array.size != 2) { + return ""; + } + const auto& name_obj = obj.via.array.ptr[0]; + return std::string(name_obj.via.str.ptr, name_obj.via.str.size); +} - // Call bbapi - exception should be caught and converted to ErrorResponse - CommandResponse response = bbapi(std::move(command)); +// Extract the error message from an ErrorResponse-shaped `[name, {message: ...}]`. +std::string response_error_message(const std::vector& bytes) +{ + auto unpacked = msgpack::unpack(reinterpret_cast(bytes.data()), bytes.size()); + auto obj = unpacked.get(); + bbapi::wire::ErrorResponse err; + obj.via.array.ptr[1].convert(err); + return err.message; +} +} // namespace - // Check that we got an ErrorResponse using get_type_name() - std::string_view type_name = response.get_type_name(); - EXPECT_EQ(type_name, "ErrorResponse") << "Expected ErrorResponse but got: " << type_name; +// Test that exceptions thrown during command execution are caught by the +// codegen-emitted dispatcher and converted to ErrorResponse. +TEST(CBind, CatchesExceptionAndReturnsErrorResponse) +{ + // SrsInitSrs with num_points=100 requests 6400 bytes but points_buf has only 10. + bbapi::wire::SrsInitSrs cmd{ .points_buf = std::vector(10, 0), + .num_points = 100, + .g2_point = std::vector(10, 0) }; - // Also verify using std::holds_alternative on the underlying variant - bool is_error = std::holds_alternative(response.get()); - EXPECT_TRUE(is_error) << "Expected ErrorResponse variant"; + bbapi::BBApiRequest request; + auto handler = bbapi::make_bb_handler(request); + auto response = handler(pack_wire_command(cmd)); - if (is_error) { - const auto& error = std::get(response.get()); - EXPECT_FALSE(error.message.empty()) << "Error message should not be empty"; - std::cout << "Successfully caught exception with message: " << error.message << '\n'; - } + EXPECT_EQ(response_type_name(response), "ErrorResponse"); + auto msg = response_error_message(response); + EXPECT_FALSE(msg.empty()) << "Error message should not be empty"; + std::cout << "Successfully caught exception with message: " << msg << '\n'; } -// Test that valid operations still work correctly (no false positives) +// Shutdown is special: the dispatcher throws ShutdownRequested with the +// pre-packed ShutdownResponse, which we catch and unpack here. TEST(CBind, ValidOperationReturnsSuccess) { - // Create a Shutdown command which should succeed without throwing - Shutdown shutdown_cmd; - Command command = shutdown_cmd; - - // Call bbapi - should return success response - CommandResponse response = bbapi(std::move(command)); + bbapi::wire::Shutdown cmd{}; - // Check that we got a ShutdownResponse, not an ErrorResponse - std::string_view type_name = response.get_type_name(); - EXPECT_NE(type_name, "ErrorResponse") << "Valid command should not return ErrorResponse"; - EXPECT_EQ(type_name, "ShutdownResponse") << "Expected ShutdownResponse"; + bbapi::BBApiRequest request; + auto handler = bbapi::make_bb_handler(request); - // Also verify using std::holds_alternative on the underlying variant - bool is_shutdown = std::holds_alternative(response.get()); - EXPECT_TRUE(is_shutdown) << "Expected Shutdown::Response variant"; + std::vector response; + try { + response = handler(pack_wire_command(cmd)); + FAIL() << "Shutdown should have thrown ShutdownRequested"; + } catch (const ::ipc::ShutdownRequested& shutdown) { + response = shutdown.response(); + } + EXPECT_EQ(response_type_name(response), "ShutdownResponse"); } #else diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ipc_bench/ipc.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/ipc_bench/ipc.bench.cpp index 57005858b4b3..53eed26559d8 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/ipc_bench/ipc.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/ipc_bench/ipc.bench.cpp @@ -1,4 +1,5 @@ -#include "barretenberg/bbapi/bbapi.hpp" +#include "barretenberg/bbapi/bbapi_wire_convert.hpp" +#include "barretenberg/bbapi/generated/bb_types.hpp" #include "barretenberg/crypto/poseidon2/poseidon2.hpp" #include "barretenberg/ecc/curves/bn254/fr.hpp" #include "barretenberg/serialize/msgpack_impl.hpp" @@ -15,6 +16,21 @@ #include #include +namespace { +// Pack a wire-typed command into the bb dispatcher's expected input: +// `[ [type_name, payload] ]` (1-element tuple containing a [name, payload] pair). +template msgpack::sbuffer pack_wire_command(const WireCmd& cmd) +{ + msgpack::sbuffer buf; + msgpack::packer pk(buf); + pk.pack_array(1); + pk.pack_array(2); + pk.pack(std::string(WireCmd::MSGPACK_SCHEMA_NAME)); + pk.pack(cmd); + return buf; +} +} // namespace + using namespace benchmark; using namespace bb; @@ -160,14 +176,12 @@ template class Poseidon2BBMsgpack : fr by = fr::random_element(); while (!stop_background.load(std::memory_order_relaxed)) { - // Create Poseidon2Hash command - bb::bbapi::Poseidon2Hash hash_cmd; - hash_cmd.inputs = { uint256_t(bx), uint256_t(by) }; - bb::bbapi::Command command{ std::move(hash_cmd) }; + // Create wire Poseidon2Hash command + bb::bbapi::wire::Poseidon2Hash hash_cmd; + hash_cmd.inputs = { bb::bbapi::fr_to_wire(bx), bb::bbapi::fr_to_wire(by) }; - // Serialize command with tuple wrapping for CBIND compatibility - msgpack::sbuffer cmd_buffer; - msgpack::pack(cmd_buffer, std::make_tuple(command)); + // Pack as [ [name, payload] ] (matches the bb dispatcher's input shape). + msgpack::sbuffer cmd_buffer = pack_wire_command(hash_cmd); // Send with retry on backpressure (100ms timeout) constexpr uint64_t TIMEOUT_NS = 100000000; // 100ms @@ -213,13 +227,8 @@ template class Poseidon2BBMsgpack : // Send Shutdown command to bb so it exits gracefully (use client 0) if (clients[0]) { - // Create Shutdown command - bb::bbapi::Shutdown shutdown_cmd; - bb::bbapi::Command command{ std::move(shutdown_cmd) }; - - // Serialize command with tuple wrapping for CBIND compatibility - msgpack::sbuffer cmd_buffer; - msgpack::pack(cmd_buffer, std::make_tuple(command)); + bb::bbapi::wire::Shutdown shutdown_cmd; + msgpack::sbuffer cmd_buffer = pack_wire_command(shutdown_cmd); // Send shutdown command with retry (1s timeout) constexpr uint64_t TIMEOUT_NS = 1000000000; // 1 second @@ -260,14 +269,9 @@ template class Poseidon2BBMsgpack : constexpr uint64_t TIMEOUT_NS = 1000000000; // 1 second for (auto _ : state) { - // Create Poseidon2Hash command - bb::bbapi::Poseidon2Hash hash_cmd; - hash_cmd.inputs = { uint256_t(x), uint256_t(y) }; - bb::bbapi::Command command{ std::move(hash_cmd) }; - - // Serialize command with tuple wrapping for CBIND compatibility - msgpack::sbuffer cmd_buffer; - msgpack::pack(cmd_buffer, std::make_tuple(command)); + bb::bbapi::wire::Poseidon2Hash hash_cmd; + hash_cmd.inputs = { bb::bbapi::fr_to_wire(x), bb::bbapi::fr_to_wire(y) }; + msgpack::sbuffer cmd_buffer = pack_wire_command(hash_cmd); // Send command with retry on backpressure while (!clients[0]->send(cmd_buffer.data(), cmd_buffer.size(), TIMEOUT_NS)) { @@ -280,23 +284,28 @@ template class Poseidon2BBMsgpack : // Response not ready, retry } - // Deserialize response + // Deserialize response: expect `[name, payload]`. We only care that the + // call succeeded and that we can extract the hash bytes. auto unpacked = msgpack::unpack(reinterpret_cast(resp.data()), resp.size()); - bb::bbapi::CommandResponse response; - unpacked.get().convert(response); - - // Release the message - clients[0]->release(resp.size()); - - // Extract hash from response - const auto& response_variant = static_cast(response); - const auto* hash_response = std::get_if(&response_variant); - if (hash_response == nullptr) { + auto obj = unpacked.get(); + if (obj.type != msgpack::type::ARRAY || obj.via.array.size != 2) { + clients[0]->release(resp.size()); + state.SkipWithError("Invalid response shape"); + break; + } + const auto& name_obj = obj.via.array.ptr[0]; + const auto& payload_obj = obj.via.array.ptr[1]; + std::string name(name_obj.via.str.ptr, name_obj.via.str.size); + if (name != "Poseidon2HashResponse") { + clients[0]->release(resp.size()); state.SkipWithError("Invalid response type"); break; } + bb::bbapi::wire::Poseidon2HashResponse hash_response; + payload_obj.convert(hash_response); + clients[0]->release(resp.size()); - auto hash = hash_response->hash; + auto hash = hash_response.hash; DoNotOptimize(hash); } } diff --git a/barretenberg/cpp/src/barretenberg/wsdb/cli.cpp b/barretenberg/cpp/src/barretenberg/wsdb/cli.cpp index 384688ee300e..6b8484d29001 100644 --- a/barretenberg/cpp/src/barretenberg/wsdb/cli.cpp +++ b/barretenberg/cpp/src/barretenberg/wsdb/cli.cpp @@ -3,6 +3,7 @@ #include "barretenberg/common/throw_or_abort.hpp" #include "barretenberg/serialize/msgpack.hpp" #include "barretenberg/world_state/world_state.hpp" +#include "barretenberg/wsdb/generated/wsdb_ipc_server.hpp" #include "barretenberg/wsdb/wsdb_ipc_server.hpp" #include "barretenberg/bb/deps/cli11.hpp" @@ -17,20 +18,11 @@ namespace bb::wsdb { using namespace bb::world_state; using namespace bb::crypto::merkle_tree; -namespace { - -// Wire-format schema authority is barretenberg/cpp/src/barretenberg/wsdb/ -// wsdb_schema.json — that's what every language's codegen consumes. The -// `msgpack schema` subcommand kept the old NamedUnion-derived dump as a -// secondary introspection path; it's now redundant, so we just point at -// the canonical source. -std::string get_wsdb_schema_as_json() -{ - return "{\"note\":\"Wire format authority is " - "barretenberg/cpp/src/barretenberg/wsdb/wsdb_schema.json.\"}"; -} - -} // namespace +// The codegen-emitted `bb::wsdb::get_wsdb_schema_as_json()` (in +// generated/wsdb_server.hpp via wsdb_ipc_server.hpp) walks the per-service +// NamedUnion through ipc::msgpack_schema_to_string. wsdb_schema.json remains +// the canonical wire-format source on disk; this subcommand lets devs dump +// the current binary's understanding for diff. int parse_and_run_wsdb(int argc, char* argv[]) { diff --git a/barretenberg/cpp/src/barretenberg/wsdb/wsdb_handlers.cpp b/barretenberg/cpp/src/barretenberg/wsdb/wsdb_handlers.cpp index 505ffe5ee6ff..2baf02bb1b30 100644 --- a/barretenberg/cpp/src/barretenberg/wsdb/wsdb_handlers.cpp +++ b/barretenberg/cpp/src/barretenberg/wsdb/wsdb_handlers.cpp @@ -354,7 +354,7 @@ wire::WsdbCommitResponse handle_commit(WsdbRequest& ctx, wire::WsdbCommit&&) WorldStateStatusFull status; ctx.world_state.commit(status); return wire::WsdbCommitResponse{ - .status = msgpack_roundtrip_to_wire(status), + .status = world_state_status_full_to_wire(status), }; } @@ -394,7 +394,7 @@ wire::WsdbSyncBlockResponse handle_sync_block(WsdbRequest& ctx, wire::WsdbSyncBl padded_nullifiers, public_data_writes); return wire::WsdbSyncBlockResponse{ - .status = msgpack_roundtrip_to_wire(status), + .status = world_state_status_full_to_wire(status), }; } @@ -423,7 +423,7 @@ wire::WsdbFinalizeBlocksResponse handle_finalize_blocks(WsdbRequest& ctx, wire:: { WorldStateStatusSummary status = ctx.world_state.set_finalized_blocks(cmd.toBlockNumber); return wire::WsdbFinalizeBlocksResponse{ - .status = msgpack_roundtrip_to_wire(status), + .status = world_state_status_summary_to_wire(status), }; } @@ -431,7 +431,7 @@ wire::WsdbUnwindBlocksResponse handle_unwind_blocks(WsdbRequest& ctx, wire::Wsdb { WorldStateStatusFull status = ctx.world_state.unwind_blocks(cmd.toBlockNumber); return wire::WsdbUnwindBlocksResponse{ - .status = msgpack_roundtrip_to_wire(status), + .status = world_state_status_full_to_wire(status), }; } @@ -440,7 +440,7 @@ wire::WsdbRemoveHistoricalBlocksResponse handle_remove_historical_blocks(WsdbReq { WorldStateStatusFull status = ctx.world_state.remove_historical_blocks(cmd.toBlockNumber); return wire::WsdbRemoveHistoricalBlocksResponse{ - .status = msgpack_roundtrip_to_wire(status), + .status = world_state_status_full_to_wire(status), }; } @@ -453,7 +453,7 @@ wire::WsdbGetStatusResponse handle_get_status(WsdbRequest& ctx, wire::WsdbGetSta WorldStateStatusSummary status; ctx.world_state.get_status_summary(status); return wire::WsdbGetStatusResponse{ - .status = msgpack_roundtrip_to_wire(status), + .status = world_state_status_summary_to_wire(status), }; } diff --git a/barretenberg/cpp/src/barretenberg/wsdb/wsdb_wire_convert.hpp b/barretenberg/cpp/src/barretenberg/wsdb/wsdb_wire_convert.hpp index 59d2b5569e90..92adaefa7204 100644 --- a/barretenberg/cpp/src/barretenberg/wsdb/wsdb_wire_convert.hpp +++ b/barretenberg/cpp/src/barretenberg/wsdb/wsdb_wire_convert.hpp @@ -3,15 +3,18 @@ * @file wsdb_wire_convert.hpp * @brief Wire <-> domain conversion helpers for the aztec-wsdb service. * - * The codegen-emitted wire types in generated/wsdb_types.hpp are POD-shaped - * (uint32_t for tree IDs, std::array for field elements, etc). - * Domain types come from world_state/, crypto/merkle_tree/, ecc/. This file - * is the single place that translates between them — used by handlers (server + * All conversions are field-by-field. The codegen-emitted wire types in + * generated/wsdb_types.hpp are POD-shaped (uint32_t for tree IDs, + * std::array for field elements, etc); domain types come from + * world_state/, crypto/merkle_tree/, ecc/, lmdblib/. This file is the + * single place that translates between them — used by handlers (server * side) and by wsdb_ipc_merkle_db.cpp (AVM client side). */ +#include "barretenberg/crypto/merkle_tree/node_store/tree_meta.hpp" #include "barretenberg/crypto/merkle_tree/response.hpp" #include "barretenberg/crypto/merkle_tree/types.hpp" #include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/lmdblib/types.hpp" #include "barretenberg/world_state/types.hpp" #include "barretenberg/wsdb/generated/wsdb_types.hpp" @@ -19,21 +22,21 @@ namespace bb::wsdb { -inline ::Fr fr_to_wire(const bb::fr& d) +inline std::array fr_to_wire(const bb::fr& d) { - ::Fr r{}; + std::array r{}; bb::fr::serialize_to_buffer(d, r.data()); return r; } -inline bb::fr fr_from_wire(const ::Fr& w) +inline bb::fr fr_from_wire(const std::array& w) { return bb::fr::serialize_from_buffer(w.data()); } -inline std::vector<::Fr> fr_vec_to_wire(const std::vector& d) +inline std::vector> fr_vec_to_wire(const std::vector& d) { - std::vector<::Fr> r; + std::vector> r; r.reserve(d.size()); for (const auto& x : d) { r.push_back(fr_to_wire(x)); @@ -41,7 +44,7 @@ inline std::vector<::Fr> fr_vec_to_wire(const std::vector& d) return r; } -inline std::vector fr_vec_from_wire(const std::vector<::Fr>& w) +inline std::vector fr_vec_from_wire(const std::vector>& w) { std::vector r; r.reserve(w.size()); @@ -113,28 +116,136 @@ inline world_state::StateReference state_reference_from_wire( return r; } -// Generic msgpack roundtrip — for status/meta types whose wire and domain -// representations have isomorphic SERIALIZATION_FIELDS shapes (same field -// names + msgpack-compatible field types). Cheaper to roundtrip the bytes -// than to write field-by-field accessors for ~20 fields. -template inline Wire msgpack_roundtrip_to_wire(const Domain& d) +// World-state status conversions. Field-by-field walk of the +// WorldStateStatusFull / WorldStateStatusSummary aggregates and their +// nested DBStats / TreeDBStats / TreeMeta / WorldStateDBStats / +// WorldStateMeta components. The wire types live in `bb::wsdb::wire`, +// the domain types in `bb::lmdblib`, `bb::crypto::merkle_tree`, +// `bb::world_state`. + +inline wire::DBStats db_stats_to_wire(const bb::lmdblib::DBStats& d) +{ + return { .name = d.name, .numDataItems = d.numDataItems, .totalUsedSize = d.totalUsedSize }; +} + +inline bb::lmdblib::DBStats db_stats_from_wire(const wire::DBStats& w) +{ + return bb::lmdblib::DBStats(w.name, w.numDataItems, w.totalUsedSize); +} + +inline wire::TreeDBStats tree_db_stats_to_wire(const bb::crypto::merkle_tree::TreeDBStats& d) +{ + return { .mapSize = d.mapSize, + .physicalFileSize = d.physicalFileSize, + .blocksDBStats = db_stats_to_wire(d.blocksDBStats), + .nodesDBStats = db_stats_to_wire(d.nodesDBStats), + .leafPreimagesDBStats = db_stats_to_wire(d.leafPreimagesDBStats), + .leafIndicesDBStats = db_stats_to_wire(d.leafIndicesDBStats), + .blockIndicesDBStats = db_stats_to_wire(d.blockIndicesDBStats) }; +} + +inline bb::crypto::merkle_tree::TreeDBStats tree_db_stats_from_wire(const wire::TreeDBStats& w) +{ + return { w.mapSize, + w.physicalFileSize, + db_stats_from_wire(w.blocksDBStats), + db_stats_from_wire(w.nodesDBStats), + db_stats_from_wire(w.leafPreimagesDBStats), + db_stats_from_wire(w.leafIndicesDBStats), + db_stats_from_wire(w.blockIndicesDBStats) }; +} + +inline wire::TreeMeta tree_meta_to_wire(const bb::crypto::merkle_tree::TreeMeta& d) +{ + return { .name = d.name, + .depth = d.depth, + .size = d.size, + .committedSize = d.committedSize, + .root = fr_to_wire(d.root), + .initialSize = d.initialSize, + .initialRoot = fr_to_wire(d.initialRoot), + .oldestHistoricBlock = d.oldestHistoricBlock, + .unfinalizedBlockHeight = d.unfinalizedBlockHeight, + .finalizedBlockHeight = d.finalizedBlockHeight }; +} + +inline bb::crypto::merkle_tree::TreeMeta tree_meta_from_wire(const wire::TreeMeta& w) +{ + return { w.name, + w.depth, + w.size, + w.committedSize, + fr_from_wire(w.root), + w.initialSize, + fr_from_wire(w.initialRoot), + w.oldestHistoricBlock, + w.unfinalizedBlockHeight, + w.finalizedBlockHeight }; +} + +inline wire::WorldStateDBStats world_state_db_stats_to_wire(const bb::world_state::WorldStateDBStats& d) +{ + return { .noteHashTreeStats = tree_db_stats_to_wire(d.noteHashTreeStats), + .messageTreeStats = tree_db_stats_to_wire(d.messageTreeStats), + .archiveTreeStats = tree_db_stats_to_wire(d.archiveTreeStats), + .publicDataTreeStats = tree_db_stats_to_wire(d.publicDataTreeStats), + .nullifierTreeStats = tree_db_stats_to_wire(d.nullifierTreeStats) }; +} + +inline bb::world_state::WorldStateDBStats world_state_db_stats_from_wire(const wire::WorldStateDBStats& w) +{ + return { tree_db_stats_from_wire(w.noteHashTreeStats), + tree_db_stats_from_wire(w.messageTreeStats), + tree_db_stats_from_wire(w.archiveTreeStats), + tree_db_stats_from_wire(w.publicDataTreeStats), + tree_db_stats_from_wire(w.nullifierTreeStats) }; +} + +inline wire::WorldStateMeta world_state_meta_to_wire(const bb::world_state::WorldStateMeta& d) +{ + return { .noteHashTreeMeta = tree_meta_to_wire(d.noteHashTreeMeta), + .messageTreeMeta = tree_meta_to_wire(d.messageTreeMeta), + .archiveTreeMeta = tree_meta_to_wire(d.archiveTreeMeta), + .publicDataTreeMeta = tree_meta_to_wire(d.publicDataTreeMeta), + .nullifierTreeMeta = tree_meta_to_wire(d.nullifierTreeMeta) }; +} + +inline bb::world_state::WorldStateMeta world_state_meta_from_wire(const wire::WorldStateMeta& w) +{ + return { tree_meta_from_wire(w.noteHashTreeMeta), + tree_meta_from_wire(w.messageTreeMeta), + tree_meta_from_wire(w.archiveTreeMeta), + tree_meta_from_wire(w.publicDataTreeMeta), + tree_meta_from_wire(w.nullifierTreeMeta) }; +} + +inline wire::WorldStateStatusSummary world_state_status_summary_to_wire( + const bb::world_state::WorldStateStatusSummary& d) +{ + return { .unfinalizedBlockNumber = d.unfinalizedBlockNumber, + .finalizedBlockNumber = d.finalizedBlockNumber, + .oldestHistoricalBlock = d.oldestHistoricalBlock, + .treesAreSynched = d.treesAreSynched }; +} + +inline bb::world_state::WorldStateStatusSummary world_state_status_summary_from_wire( + const wire::WorldStateStatusSummary& w) +{ + return { w.unfinalizedBlockNumber, w.finalizedBlockNumber, w.oldestHistoricalBlock, w.treesAreSynched }; +} + +inline wire::WorldStateStatusFull world_state_status_full_to_wire(const bb::world_state::WorldStateStatusFull& d) { - msgpack::sbuffer buf; - msgpack::pack(buf, d); - auto unpacked = msgpack::unpack(buf.data(), buf.size()); - Wire w; - unpacked.get().convert(w); - return w; + return { .summary = world_state_status_summary_to_wire(d.summary), + .dbStats = world_state_db_stats_to_wire(d.dbStats), + .meta = world_state_meta_to_wire(d.meta) }; } -template inline Domain msgpack_roundtrip_from_wire(const Wire& w) +inline bb::world_state::WorldStateStatusFull world_state_status_full_from_wire(const wire::WorldStateStatusFull& w) { - msgpack::sbuffer buf; - msgpack::pack(buf, w); - auto unpacked = msgpack::unpack(buf.data(), buf.size()); - Domain d; - unpacked.get().convert(d); - return d; + return { world_state_status_summary_from_wire(w.summary), + world_state_db_stats_from_wire(w.dbStats), + world_state_meta_from_wire(w.meta) }; } } // namespace bb::wsdb diff --git a/barretenberg/rust/barretenberg-rs/src/fr_ext.rs b/barretenberg/rust/barretenberg-rs/src/fr_ext.rs index b1626a1668a5..5c8788ac4f9c 100644 --- a/barretenberg/rust/barretenberg-rs/src/fr_ext.rs +++ b/barretenberg/rust/barretenberg-rs/src/fr_ext.rs @@ -11,24 +11,24 @@ impl Fr { pub fn from_u64(value: u64) -> Self { let mut bytes = [0u8; 32]; bytes[24..32].copy_from_slice(&value.to_be_bytes()); - Fr(bytes) + Self(bytes) } /// Create a field element from 32 big-endian bytes. pub fn from_be_bytes(bytes: [u8; 32]) -> Self { - Fr(bytes) + Self(bytes) } /// Create a field element from 32 little-endian bytes. pub fn from_le_bytes(bytes: [u8; 32]) -> Self { - Fr(bytes) + Self(bytes) } /// Create a field element from a 32-byte buffer (no reduction). /// Panics if the buffer is not exactly 32 bytes long. pub fn from_buffer(buffer: &[u8]) -> Self { let bytes: [u8; 32] = buffer.try_into().expect("Buffer must be exactly 32 bytes"); - Fr(bytes) + Self(bytes) } /// Create a field element from a byte slice, truncating or zero-padding @@ -37,7 +37,7 @@ impl Fr { let mut bytes = [0u8; 32]; let len = buffer.len().min(32); bytes[..len].copy_from_slice(&buffer[..len]); - Fr(bytes) + Self(bytes) } /// Convert to a byte buffer (as used in msgpack). diff --git a/ipc-codegen/src/cpp_codegen.ts b/ipc-codegen/src/cpp_codegen.ts index 725a385015f5..12b61fa62fd9 100644 --- a/ipc-codegen/src/cpp_codegen.ts +++ b/ipc-codegen/src/cpp_codegen.ts @@ -19,7 +19,15 @@ import type { Field, Command, } from "./schema_visitor.ts"; -import { toSnakeCase } from "./naming.ts"; +import { toPascalCase, toSnakeCase } from "./naming.ts"; + +// Convert a schema alias name into its C++ type name. Strips a trailing +// `_t` (uint256_t → Uint256) and PascalCases the rest, so `fr` → `Fr`, +// `secp256k1_fr` → `Secp256k1Fr`, `uint256_t` → `Uint256`. +function toAliasName(name: string): string { + const trimmed = name.endsWith("_t") ? name.slice(0, -2) : name; + return toPascalCase(trimmed); +} export interface CppCodegenOptions { /** C++ namespace for generated code, e.g. 'bb::cdb' */ @@ -359,6 +367,39 @@ ${methods} generateStandaloneTypes(schema: CompiledSchema): string { const { namespace: ns, prefix } = this.opts; + // Collect every distinct bin32 alias name in the schema. Each becomes + // a `using` declaration so wire fields with semantic aliases (Fr / Fq / + // Secp256k1Fr / …) surface with their names. All share the same + // underlying type (std::array) and msgpack bin encoding + // (via ipc_runtime/std_array_bin.hpp's global adapter). + const aliasNames = new Set(); + const collect = (type: import("./schema_visitor.ts").Type): void => { + if ( + type.kind === "primitive" && + type.primitive === "bin32_alias" && + type.originalName + ) { + // Capitalise first letter for the C++ alias (fr → Fr, secp256k1_fr → Secp256k1Fr). + aliasNames.add(toAliasName(type.originalName)); + } else if ( + type.kind === "vector" || + type.kind === "array" || + type.kind === "optional" + ) { + if (type.element) collect(type.element); + } + }; + for (const s of schema.structs.values()) { + for (const f of s.fields) collect(f.type); + } + for (const s of schema.responses.values()) { + for (const f of s.fields) collect(f.type); + } + const aliasDecls = [...aliasNames] + .sort() + .map((n) => `using ${n} = std::array;`) + .join("\n"); + // Map schema types to C++ types const mapType = (type: import("./schema_visitor.ts").Type): string => { switch (type.kind) { @@ -381,9 +422,14 @@ ${methods} case "bytes": return "std::vector"; case "fr": - return "Fr"; // std::array + // Legacy path (kept in case anything still produces this). + return "std::array"; + case "bin32_alias": + return type.originalName + ? toAliasName(type.originalName) + : "std::array"; case "field2": - return "std::array"; + return "std::array, 2>"; case "enum_u32": return "uint32_t"; case "map_u32_pair": @@ -480,68 +526,14 @@ ${methods} #endif // --------------------------------------------------------------------------- -// 32-byte field element (Fr/Fq). -// -// Wire format: msgpack \`bin\` (2-byte header + raw 32 bytes), matching the -// schema's ["fr", "bin32"] alias and barretenberg's own bb::fr msgpack adapter. -// -// The default std::array msgpack adapter packs as \`array\` which -// is the wrong wire encoding (and incompatible with bb::fr / Rust / -// msgpackr-bin clients), so we wrap the array in a struct and provide -// explicit msgpack::adaptor specializations below. +// Wire aliases for the schema's bin32 alias family. msgpack-c's +// std::array adapter already packs as \`bin\` (which is +// what we want — byte-identical with bb::fr / Rust serde_bytes / msgpackr +// Uint8Array), so the aliases below carry no extra machinery beyond the +// std::array typedef. // --------------------------------------------------------------------------- -struct Fr { - std::array bytes{}; - - uint8_t* data() { return bytes.data(); } - const uint8_t* data() const { return bytes.data(); } - constexpr std::size_t size() const { return bytes.size(); } - uint8_t& operator[](std::size_t i) { return bytes[i]; } - const uint8_t& operator[](std::size_t i) const { return bytes[i]; } - - bool operator==(const Fr&) const = default; -}; - -namespace msgpack { -MSGPACK_API_VERSION_NAMESPACE(v1) { -namespace adaptor { - -template <> struct pack<::Fr> { - template packer& operator()(packer& o, ::Fr const& v) const - { - o.pack_bin(static_cast(v.bytes.size())); - o.pack_bin_body(reinterpret_cast(v.bytes.data()), static_cast(v.bytes.size())); - return o; - } -}; - -template <> struct convert<::Fr> { - msgpack::object const& operator()(msgpack::object const& o, ::Fr& v) const - { - // Preferred: bin (matches schema + bb::fr). - if (o.type == msgpack::type::BIN) { - if (o.via.bin.size != v.bytes.size()) - throw msgpack::type_error(); - std::memcpy(v.bytes.data(), o.via.bin.ptr, v.bytes.size()); - return o; - } - // Fallback: array — kept so this type can decode payloads - // emitted by msgpack libraries that don't distinguish. - if (o.type == msgpack::type::ARRAY) { - if (o.via.array.size != v.bytes.size()) - throw msgpack::type_error(); - for (std::size_t i = 0; i < v.bytes.size(); ++i) { - o.via.array.ptr[i].convert(v.bytes[i]); - } - return o; - } - throw msgpack::type_error(); - } -}; -} // namespace adaptor -} // MSGPACK_API_VERSION_NAMESPACE(v1) -} // namespace msgpack +${aliasDecls} namespace ${ns}${this.opts.wireNamespace ? "::" + this.opts.wireNamespace : ""} { @@ -893,6 +885,22 @@ ${commandStructs} const { namespace: ns, prefix } = this.opts; const errorTypeName = schema.errorTypeName || `${prefix}ErrorResponse`; const typesHeader = `${toSnakeCase(prefix)}_types.hpp`; + const prefixLower = toSnakeCase(prefix); + + // Per-service NamedUnions + schema reflection. The codegen-emitted + // Command / CommandResponse aggregate every wire type + // so the binary can pack its own schema back out via + // ipc::msgpack_schema_to_string. This is the C++-canonical dev workflow: + // edit a wire type, rebuild, dump the schema, commit the JSON. + const cmdUnionMembers = schema.commands + .map((c) => `wire::${c.name}`) + .join(",\n "); + const respUnionMembers = [ + errorTypeName, + ...schema.commands.map((c) => c.responseType), + ] + .map((r) => `wire::${r}`) + .join(",\n "); // Handler declarations — template const handlerDecls = schema.commands @@ -946,6 +954,8 @@ ${commandStructs} #include "${typesHeader}" #include "ipc_runtime/ipc_server.hpp" +#include "ipc_runtime/named_union.hpp" +#include "ipc_runtime/schema.hpp" #include "ipc_runtime/serve_helper.hpp" #include "ipc_runtime/signal_handlers.hpp" #include "msgpack_struct_map_impl.hpp" @@ -1067,6 +1077,27 @@ void serve(const std::string& input_path, Ctx& ctx) }); } +// --------------------------------------------------------------------------- +// Schema reflection — the binary serialises its own understanding of the wire +// format. Edit a wire type, rebuild, dump the schema, commit the JSON. +// --------------------------------------------------------------------------- + +using ${prefix}Command = ::ipc::NamedUnion<${cmdUnionMembers}>; +using ${prefix}CommandResponse = ::ipc::NamedUnion<${respUnionMembers}>; + +namespace detail { +struct ${prefix}Api { + ${prefix}Command commands; + ${prefix}CommandResponse responses; + SERIALIZATION_FIELDS(commands, responses); +}; +} // namespace detail + +inline std::string get_${prefixLower}_schema_as_json() +{ + return ::ipc::msgpack_schema_to_string(detail::${prefix}Api{}); +} + } // namespace ${ns} `; } diff --git a/ipc-codegen/src/rust_codegen.ts b/ipc-codegen/src/rust_codegen.ts index e3ce5acf9d3a..2482aad0a835 100644 --- a/ipc-codegen/src/rust_codegen.ts +++ b/ipc-codegen/src/rust_codegen.ts @@ -8,8 +8,16 @@ * - No complex abstraction */ -import type { CompiledSchema, Type, Struct, Field } from './schema_visitor.ts'; -import { toSnakeCase, toPascalCase } from './naming.ts'; +import type { CompiledSchema, Type, Struct, Field } from "./schema_visitor.ts"; +import { toSnakeCase, toPascalCase } from "./naming.ts"; + +// Convert a schema alias name into its Rust type name. Strips a trailing +// `_t` (uint256_t → Uint256) and PascalCases the rest, so `fr` → `Fr`, +// `secp256k1_fr` → `Secp256k1Fr`, `uint256_t` → `Uint256`. +function toAliasName(name: string): string { + const trimmed = name.endsWith("_t") ? name.slice(0, -2) : name; + return toPascalCase(trimmed); +} export interface RustCodegenOptions { /** Prefix for stripping from method names, e.g. 'Wsdb' makes WsdbGetTreeInfo -> get_tree_info */ @@ -29,19 +37,22 @@ export interface RustCodegenOptions { } export class RustCodegen { - private errorTypeName: string = 'ErrorResponse'; + private errorTypeName: string = "ErrorResponse"; private opts: Required; constructor(options?: RustCodegenOptions) { - const prefix = options?.prefix ?? ''; - const name = prefix || 'Barretenberg'; + const prefix = options?.prefix ?? ""; + const name = prefix || "Barretenberg"; this.opts = { prefix, apiStructName: options?.apiStructName ?? `${name}Api`, - backendImport: options?.backendImport ?? 'super::backend::Backend', + backendImport: options?.backendImport ?? "super::backend::Backend", errorImport: options?.errorImport ?? `super::error::{IpcError, Result}`, - typesImport: options?.typesImport ?? `super::${toSnakeCase(prefix || 'bb')}_types::*`, - typesDocComment: options?.typesDocComment ?? `Generated types for ${name} IPC protocol`, + typesImport: + options?.typesImport ?? + `super::${toSnakeCase(prefix || "bb")}_types::*`, + typesDocComment: + options?.typesDocComment ?? `Generated types for ${name} IPC protocol`, apiDocComment: options?.apiDocComment ?? `${name} IPC client API`, }; } @@ -49,60 +60,78 @@ export class RustCodegen { // Type mapping: Schema type -> Rust type private mapType(type: Type): string { switch (type.kind) { - case 'primitive': + case "primitive": switch (type.primitive) { - case 'bool': return 'bool'; - case 'u8': return 'u8'; - case 'u16': return 'u16'; - case 'u32': return 'u32'; - case 'u64': return 'u64'; - case 'f64': return 'f64'; - case 'string': return 'String'; - case 'bytes': return 'Vec'; - case 'fr': return 'Fr'; // 32-byte field element - case 'field2': return '[Fr; 2]'; // Extension field (Fq2) + case "bool": + return "bool"; + case "u8": + return "u8"; + case "u16": + return "u16"; + case "u32": + return "u32"; + case "u64": + return "u64"; + case "f64": + return "f64"; + case "string": + return "String"; + case "bytes": + return "Vec"; + case "fr": + return "Fr"; // legacy path (current schemas emit bin32_alias instead) + case "bin32_alias": + return type.originalName ? toAliasName(type.originalName) : "Fr"; + case "field2": + return "[Fr; 2]"; // Extension field (Fq2) } break; - case 'vector': + case "vector": return `Vec<${this.mapType(type.element!)}>`; - case 'array': + case "array": const elemType = this.mapType(type.element!); // Large arrays become Vec for ergonomics - return type.size! > 32 ? `Vec<${elemType}>` : `[${elemType}; ${type.size}]`; + return type.size! > 32 + ? `Vec<${elemType}>` + : `[${elemType}; ${type.size}]`; - case 'optional': + case "optional": return `Option<${this.mapType(type.element!)}>`; - case 'struct': + case "struct": // Convert struct names to PascalCase for Rust conventions return toPascalCase(type.struct!.name); } - return 'Unknown'; + return "Unknown"; } // Check if field needs serde(with = "serde_bytes") private needsSerdeBytes(type: Type): boolean { - return type.kind === 'primitive' && type.primitive === 'bytes'; + return type.kind === "primitive" && type.primitive === "bytes"; } // Check if field needs serde(with = "serde_vec_bytes") private needsSerdeVecBytes(type: Type): boolean { - return type.kind === 'vector' && this.needsSerdeBytes(type.element!); + return type.kind === "vector" && this.needsSerdeBytes(type.element!); } // Check if field needs serde(with = "serde_array4_bytes") - for [Vec; 4] (Poseidon2 state) private needsSerdeArray4Bytes(type: Type): boolean { - return type.kind === 'array' && type.size === 4 && this.needsSerdeBytes(type.element!); + return ( + type.kind === "array" && + type.size === 4 && + this.needsSerdeBytes(type.element!) + ); } // Generate struct field private generateField(field: Field): string { const rustName = toSnakeCase(field.name); const rustType = this.mapType(field.type); - let attrs = ''; + let attrs = ""; // Add serde rename if needed if (field.name !== rustName) { @@ -124,21 +153,22 @@ export class RustCodegen { // Generate a struct definition private generateStruct(struct: Struct, isCommand: boolean): string { const rustName = toPascalCase(struct.name); - const fields = struct.fields.map(f => this.generateField(f)).join('\n'); + const fields = struct.fields.map((f) => this.generateField(f)).join("\n"); // Add serde rename if struct name changed - const serdeRename = struct.name !== rustName - ? `\n#[serde(rename = "${struct.name}")]` - : ''; + const serdeRename = + struct.name !== rustName ? `\n#[serde(rename = "${struct.name}")]` : ""; // Commands have a __typename used for NamedUnion identification, but it's handled // by the Command enum's custom serde, not by the struct itself. const typenameField = isCommand ? ` #[serde(rename = "__typename", skip, default)]\n pub type_name: String,\n` - : ''; + : ""; // Generate constructor for commands - const constructor = isCommand ? this.generateConstructor(struct, rustName) : ''; + const constructor = isCommand + ? this.generateConstructor(struct, rustName) + : ""; return `/// ${struct.name} #[derive(Debug, Clone, Serialize, Deserialize)]${serdeRename} @@ -149,14 +179,14 @@ ${typenameField}${fields} // Generate constructor for command structs private generateConstructor(struct: Struct, rustName: string): string { - const params = struct.fields.map(f => - `${toSnakeCase(f.name)}: ${this.mapType(f.type)}` - ).join(', '); + const params = struct.fields + .map((f) => `${toSnakeCase(f.name)}: ${this.mapType(f.type)}`) + .join(", "); const fieldInits = [ ` type_name: "${struct.name}".to_string(),`, - ...struct.fields.map(f => ` ${toSnakeCase(f.name)},`), - ].join('\n'); + ...struct.fields.map((f) => ` ${toSnakeCase(f.name)},`), + ].join("\n"); return ` @@ -171,26 +201,26 @@ ${fieldInits} // Generate Command enum private generateCommandEnum(schema: CompiledSchema): string { - const names = schema.commands.map(c => c.name); + const names = schema.commands.map((c) => c.name); const variants = names - .map(name => { + .map((name) => { const rustName = toPascalCase(name); return ` ${rustName}(${rustName}),`; }) - .join('\n'); + .join("\n"); const serializeCases = names - .map(name => { + .map((name) => { const rustName = toPascalCase(name); return ` Command::${rustName}(data) => { tuple.serialize_element("${name}")?; tuple.serialize_element(data)?; }`; }) - .join('\n'); + .join("\n"); const deserializeCases = names - .map(name => { + .map((name) => { const rustName = toPascalCase(name); return ` "${name}" => { let data = seq.next_element()? @@ -198,11 +228,9 @@ ${fieldInits} Ok(Command::${rustName}(data)) }`; }) - .join('\n'); + .join("\n"); - const variantNames = names - .map(name => `"${name}"`) - .join(', '); + const variantNames = names.map((name) => `"${name}"`).join(", "); return `/// Command enum - wraps all possible commands #[derive(Debug, Clone)] @@ -251,30 +279,32 @@ ${deserializeCases} // Generate Response enum private generateResponseEnum(schema: CompiledSchema): string { // Include all response types from commands plus ErrorResponse if it exists - const commandResponseTypes = Array.from(new Set(schema.commands.map(c => c.responseType))); - const errorName = schema.errorTypeName || 'ErrorResponse'; + const commandResponseTypes = Array.from( + new Set(schema.commands.map((c) => c.responseType)), + ); + const errorName = schema.errorTypeName || "ErrorResponse"; const responseTypes = schema.responses.has(errorName) ? [...commandResponseTypes, errorName] : commandResponseTypes; const variants = responseTypes - .map(name => { + .map((name) => { const rustName = toPascalCase(name); return ` ${rustName}(${rustName}),`; }) - .join('\n'); + .join("\n"); const serializeCases = responseTypes - .map(name => { + .map((name) => { const rustName = toPascalCase(name); return ` Response::${rustName}(data) => { tuple.serialize_element("${name}")?; tuple.serialize_element(data)?; }`; }) - .join('\n'); + .join("\n"); const deserializeCases = responseTypes - .map(name => { + .map((name) => { const rustName = toPascalCase(name); return ` "${name}" => { let data = seq.next_element()? @@ -282,9 +312,9 @@ ${deserializeCases} Ok(Response::${rustName}(data)) }`; }) - .join('\n'); + .join("\n"); - const variantNames = responseTypes.map(name => `"${name}"`).join(', '); + const variantNames = responseTypes.map((name) => `"${name}"`).join(", "); return `/// Response enum - wraps all possible responses #[derive(Debug, Clone)] @@ -418,54 +448,92 @@ mod serde_array4_bytes { // Generate types file generateTypes(schema: CompiledSchema, schemaHash?: string): string { - this.errorTypeName = schema.errorTypeName || 'ErrorResponse'; + this.errorTypeName = schema.errorTypeName || "ErrorResponse"; // Create set of top-level command struct names (only these need __typename) - const commandNames = new Set(schema.commands.map(c => c.name)); + const commandNames = new Set(schema.commands.map((c) => c.name)); + + // Collect every distinct bin32 alias name in the schema. Each becomes a + // `pub type` alias to the shared `Bin32` newtype so wire fields with + // semantic aliases (Fr / Fq / Secp256k1Fr / …) keep their names. + const aliasNames = new Set(); + const collect = (type: Type): void => { + if ( + type.kind === "primitive" && + type.primitive === "bin32_alias" && + type.originalName + ) { + aliasNames.add(toAliasName(type.originalName)); + } else if ( + type.kind === "vector" || + type.kind === "array" || + type.kind === "optional" + ) { + if (type.element) collect(type.element); + } + }; + for (const s of schema.structs.values()) { + for (const f of s.fields) collect(f.type); + } + for (const s of schema.responses.values()) { + for (const f of s.fields) collect(f.type); + } + // Make sure `Fr` always exists (legacy path / field2 expansion uses it). + aliasNames.add("Fr"); + const aliasDecls = [...aliasNames] + .sort() + .map((n) => `pub type ${n} = Bin32;`) + .join("\n"); // Generate all structs (commands first, then responses) const commandStructs = Array.from(schema.structs.values()) - .map(s => this.generateStruct(s, commandNames.has(s.name))) - .join('\n\n'); + .map((s) => this.generateStruct(s, commandNames.has(s.name))) + .join("\n\n"); const responseStructs = Array.from(schema.responses.values()) - .map(s => this.generateStruct(s, false)) - .join('\n\n'); + .map((s) => this.generateStruct(s, false)) + .join("\n\n"); - const hashLine = schemaHash ? `\n/// Schema version hash for compatibility checking\npub const SCHEMA_HASH: &str = "${schemaHash}";\n` : ''; + const hashLine = schemaHash + ? `\n/// Schema version hash for compatibility checking\npub const SCHEMA_HASH: &str = "${schemaHash}";\n` + : ""; return `//! AUTOGENERATED - DO NOT EDIT //! ${this.opts.typesDocComment} use serde::{Deserialize, Serialize}; ${hashLine} -/// 32-byte field element (Fr/Fq). Fixed-size, stack-allocated, no heap. -/// Serializes as msgpack bin32 on the wire. +/// 32 raw bytes encoded as msgpack bin32. Shared underlying type for every +/// bin32 alias the schema declares (Fr / Fq / Secp256k1Fr / …) — the aliases +/// below are zero-cost pub type declarations onto this newtype, so callers +/// can write Fr(bytes) / Fq(bytes) and the wire encoding stays uniform. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Fr(pub [u8; 32]); +pub struct Bin32(pub [u8; 32]); -impl Fr { +impl Bin32 { pub fn from_bytes(bytes: [u8; 32]) -> Self { Self(bytes) } pub fn to_bytes(&self) -> &[u8; 32] { &self.0 } pub fn as_slice(&self) -> &[u8] { &self.0 } } -impl Serialize for Fr { +impl Serialize for Bin32 { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { serializer.serialize_bytes(&self.0) } } -impl<'de> Deserialize<'de> for Fr { +impl<'de> Deserialize<'de> for Bin32 { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de> { let bytes: Vec = >::deserialize(deserializer)?; let arr: [u8; 32] = bytes.try_into() .map_err(|v: Vec| serde::de::Error::invalid_length(v.len(), &"32 bytes"))?; - Ok(Fr(arr)) + Ok(Bin32(arr)) } } +${aliasDecls} + ${this.generateSerdeHelpers()} ${commandStructs} @@ -480,37 +548,47 @@ ${this.generateResponseEnum(schema)} /** Strip the service prefix from a command name for the method name */ private methodName(commandName: string): string { - const withoutPrefix = this.opts.prefix && commandName.startsWith(this.opts.prefix) - ? commandName.slice(this.opts.prefix.length) - : commandName; + const withoutPrefix = + this.opts.prefix && commandName.startsWith(this.opts.prefix) + ? commandName.slice(this.opts.prefix.length) + : commandName; return toSnakeCase(withoutPrefix); } // Generate API method - private generateApiMethod(command: {name: string, fields: Field[], responseType: string}): string { + private generateApiMethod(command: { + name: string; + fields: Field[]; + responseType: string; + }): string { const methodName = this.methodName(command.name); const cmdRustName = toPascalCase(command.name); const respRustName = toPascalCase(command.responseType); - const params = command.fields.map(f => { - const rustType = this.mapType(f.type); - // Only convert simple Vec to &[u8], not nested types - const apiType = rustType === 'Vec' ? '&[u8]' : rustType; - return `${toSnakeCase(f.name)}: ${apiType}`; - }).join(', '); - - const paramConversions = command.fields.map(f => { - const name = toSnakeCase(f.name); - const rustType = this.mapType(f.type); - // Only convert slices back to Vec - if (rustType === 'Vec') { - return `${name}.to_vec()`; - } - return name; - }).join(', '); + const params = command.fields + .map((f) => { + const rustType = this.mapType(f.type); + // Only convert simple Vec to &[u8], not nested types + const apiType = rustType === "Vec" ? "&[u8]" : rustType; + return `${toSnakeCase(f.name)}: ${apiType}`; + }) + .join(", "); + + const paramConversions = command.fields + .map((f) => { + const name = toSnakeCase(f.name); + const rustType = this.mapType(f.type); + // Only convert slices back to Vec + if (rustType === "Vec") { + return `${name}.to_vec()`; + } + return name; + }) + .join(", "); // Extract error type name from the error import (e.g., 'IpcError' from 'crate::error::{IpcError, Result}') - const errorType = this.opts.errorImport.match(/\{(\w+),/)?.[1] ?? 'IpcError'; + const errorType = + this.opts.errorImport.match(/\{(\w+),/)?.[1] ?? "IpcError"; return ` /// Execute ${command.name} pub fn ${methodName}(&mut self, ${params}) -> Result<${respRustName}> { @@ -529,28 +607,38 @@ ${this.generateResponseEnum(schema)} // Generate API file generateApi(schema: CompiledSchema): string { - this.errorTypeName = schema.errorTypeName || 'ErrorResponse'; - const { apiStructName, backendImport, errorImport, typesImport, apiDocComment } = this.opts; + this.errorTypeName = schema.errorTypeName || "ErrorResponse"; + const { + apiStructName, + backendImport, + errorImport, + typesImport, + apiDocComment, + } = this.opts; // Find shutdown command name (may be prefixed, e.g. WsdbShutdown) - const shutdownCmd = schema.commands.find(c => c.name.endsWith('Shutdown')); + const shutdownCmd = schema.commands.find((c) => + c.name.endsWith("Shutdown"), + ); const shutdownName = shutdownCmd ? toPascalCase(shutdownCmd.name) : null; const apiMethods = schema.commands - .filter(c => !c.name.endsWith('Shutdown')) - .map(c => this.generateApiMethod(c)) - .join('\n\n'); + .filter((c) => !c.name.endsWith("Shutdown")) + .map((c) => this.generateApiMethod(c)) + .join("\n\n"); - const shutdownMethod = shutdownName ? ` + const shutdownMethod = shutdownName + ? ` /// Shutdown backend gracefully pub fn shutdown(&mut self) -> Result<()> { let cmd = Command::${shutdownName}(${shutdownName}::new()); let _ = self.execute(cmd)?; self.backend.destroy() } -` : ''; +` + : ""; - const errorType = errorImport.match(/\{(\w+),/)?.[1] ?? 'IpcError'; + const errorType = errorImport.match(/\{(\w+),/)?.[1] ?? "IpcError"; return `//! AUTOGENERATED - DO NOT EDIT //! ${apiDocComment} @@ -598,24 +686,24 @@ ${shutdownMethod} /** Generate a Handler trait and serve() function */ generateServer(schema: CompiledSchema): string { - this.errorTypeName = schema.errorTypeName || 'ErrorResponse'; + this.errorTypeName = schema.errorTypeName || "ErrorResponse"; const { prefix, errorImport, typesImport } = this.opts; - const errorType = errorImport.match(/\{(\w+),/)?.[1] ?? 'IpcError'; + const errorType = errorImport.match(/\{(\w+),/)?.[1] ?? "IpcError"; const errorRespType = toPascalCase(this.errorTypeName); const traitMethods = schema.commands - .filter(c => !c.name.endsWith('Shutdown')) - .map(c => { + .filter((c) => !c.name.endsWith("Shutdown")) + .map((c) => { const methodName = this.methodName(c.name); const cmdRustName = toPascalCase(c.name); const respRustName = toPascalCase(c.responseType); return ` fn ${methodName}(&mut self, cmd: ${cmdRustName}) -> Result<${respRustName}>;`; }) - .join('\n'); + .join("\n"); const dispatchArms = schema.commands - .filter(c => !c.name.endsWith('Shutdown')) - .map(c => { + .filter((c) => !c.name.endsWith("Shutdown")) + .map((c) => { const methodName = this.methodName(c.name); const cmdRustName = toPascalCase(c.name); const respRustName = toPascalCase(c.responseType); @@ -626,23 +714,25 @@ ${shutdownMethod} } }`; }) - .join('\n'); + .join("\n"); // Handle shutdown arm - const shutdownCmd = schema.commands.find(c => c.name.endsWith('Shutdown')); + const shutdownCmd = schema.commands.find((c) => + c.name.endsWith("Shutdown"), + ); const shutdownArm = shutdownCmd ? ` Command::${toPascalCase(shutdownCmd.name)}(_) => { return Err(${errorType}::Backend("shutdown requested".to_string())); }` - : ''; + : ""; return `//! AUTOGENERATED - DO NOT EDIT -//! Server-side dispatch for ${prefix || 'service'} IPC protocol +//! Server-side dispatch for ${prefix || "service"} IPC protocol use ${errorImport}; use ${typesImport}; -/// Handler trait — implement this to serve ${prefix || 'service'} commands. +/// Handler trait — implement this to serve ${prefix || "service"} commands. pub trait Handler { ${traitMethods} } @@ -670,15 +760,16 @@ ${shutdownArm} const ctxName = `${prefix}Context`; const stubs = schema.commands - .filter(c => !c.name.endsWith('Shutdown')) - .map(c => { + .filter((c) => !c.name.endsWith("Shutdown")) + .map((c) => { const methodName = this.methodName(c.name); const cmdRustName = toPascalCase(c.name); const respRustName = toPascalCase(c.responseType); return ` fn ${methodName}(&mut self, _cmd: ${typesModule}::${cmdRustName}) -> Result<${typesModule}::${respRustName}> { unimplemented!("${c.name}") }`; - }).join('\n\n'); + }) + .join("\n\n"); return `// Handler stubs — implement your service logic here. // This file is generated ONCE. Edit freely — it will not be overwritten. @@ -736,7 +827,7 @@ fn main() { /** Generate Cargo.toml for a standalone service */ generateBuildFile(schema: CompiledSchema): string { const { prefix } = this.opts; - const pkgName = toSnakeCase(prefix).replace(/_/g, '-'); + const pkgName = toSnakeCase(prefix).replace(/_/g, "-"); return `[package] name = "${pkgName}-service" diff --git a/ipc-codegen/src/schema_visitor.ts b/ipc-codegen/src/schema_visitor.ts index 572c0d6330a4..44974a4d69f9 100644 --- a/ipc-codegen/src/schema_visitor.ts +++ b/ipc-codegen/src/schema_visitor.ts @@ -8,13 +8,26 @@ * - Output is "compiled schema" with resolved types */ -export type PrimitiveType = 'bool' | 'u8' | 'u16' | 'u32' | 'u64' | 'f64' | 'string' | 'bytes' | 'fr' | 'field2' | 'enum_u32' | 'map_u32_pair'; +export type PrimitiveType = + | "bool" + | "u8" + | "u16" + | "u32" + | "u64" + | "f64" + | "string" + | "bytes" + | "fr" + | "bin32_alias" // ["alias", [, "bin32"]] — 32 bytes named per alias + | "field2" + | "enum_u32" + | "map_u32_pair"; export interface Type { - kind: 'primitive' | 'vector' | 'array' | 'optional' | 'struct'; + kind: "primitive" | "vector" | "array" | "optional" | "struct"; primitive?: PrimitiveType; - element?: Type; // For vector, array, optional - size?: number; // For array + element?: Type; // For vector, array, optional + size?: number; // For array struct?: Struct; // For struct types originalName?: string; // Original type name from schema (e.g. 'MerkleTreeId', 'unordered_map') } @@ -69,18 +82,23 @@ export class SchemaVisitor { // First, visit all response types (including ErrorResponse) for (const [respName, respSchema] of responsePairs) { - if (typeof respSchema !== 'string') { + if (typeof respSchema !== "string") { const respStruct = this.visitStruct(respName, respSchema); this.responses.set(respName, respStruct); } } // Find the error response type name (e.g. 'WsdbErrorResponse') - const errorResponses = responsePairs.filter(([name]: [string, any]) => name.endsWith('ErrorResponse')); - const errorTypeName = errorResponses.length > 0 ? errorResponses[0][0] : undefined; + const errorResponses = responsePairs.filter(([name]: [string, any]) => + name.endsWith("ErrorResponse"), + ); + const errorTypeName = + errorResponses.length > 0 ? errorResponses[0][0] : undefined; // Visit all commands and pair with responses - const normalResponses = responsePairs.filter(([name]: [string, any]) => !name.endsWith('ErrorResponse')); + const normalResponses = responsePairs.filter( + ([name]: [string, any]) => !name.endsWith("ErrorResponse"), + ); for (let i = 0; i < commandPairs.length; i++) { const [cmdName, cmdSchema] = commandPairs[i]; const [respName] = normalResponses[i]; @@ -110,7 +128,7 @@ export class SchemaVisitor { // Schema is an object with __typename and fields for (const [key, value] of Object.entries(schema)) { - if (key === '__typename') continue; + if (key === "__typename") continue; fields.push({ name: key, @@ -123,7 +141,7 @@ export class SchemaVisitor { private visitType(schema: any): Type { // Primitive string type - if (typeof schema === 'string') { + if (typeof schema === "string") { return this.resolvePrimitive(schema); } @@ -132,52 +150,66 @@ export class SchemaVisitor { const [kind, args] = schema; switch (kind) { - case 'vector': { + case "vector": { const [elemType] = args as [any]; // Special case: vector = bytes - if (elemType === 'unsigned char') { - return { kind: 'primitive', primitive: 'bytes' }; + if (elemType === "unsigned char") { + return { kind: "primitive", primitive: "bytes" }; } return { - kind: 'vector', + kind: "vector", element: this.visitType(elemType), }; } - case 'array': { + case "array": { const [elemType, size] = args as [any, number]; - // Special case: array = field element (Fr/Fq) - if (elemType === 'unsigned char' && size === 32) { - return { kind: 'primitive', primitive: 'fr' }; - } - // Special case: array (other sizes) = bytes - if (elemType === 'unsigned char') { - return { kind: 'primitive', primitive: 'bytes' }; - } + // ["array", ["unsigned char", N]] → std::array in + // C++. The default msgpack adapter for std::array + // packs it as `array`, which is the wrong wire shape; + // ipc_runtime/std_array_bin.hpp ships a global override that + // makes it pack as `bin` instead (matching bb::fr / Rust / + // msgpackr). So no special-casing here — the schema's array + // declaration produces a fixed-size byte buffer with the right + // wire encoding via the adapter. return { - kind: 'array', + kind: "array", element: this.visitType(elemType), size, }; } - case 'optional': { + case "optional": { const [elemType] = args as [any]; return { - kind: 'optional', + kind: "optional", element: this.visitType(elemType), }; } - case 'shared_ptr': { + case "shared_ptr": { // Dereference shared_ptr - just use inner type const [innerType] = args as [any]; return this.visitType(innerType); } - case 'alias': { - // Alias types (like uint256_t) are treated as bytes - return { kind: 'primitive', primitive: 'bytes' }; + case "alias": { + // Aliases carry [aliasName, underlyingKind]. The underlying kind is + // bin32 (32 raw bytes) for the field-element / uint256 family; + // anything else falls back to a plain byte buffer. + // We preserve the alias *name* so the codegen can emit + // using = std::array; + // and downstream callers see semantically-named types + // (Fr, Fq, Secp256k1Fr, …) even though they share one byte shape. + const [aliasName, underlying] = args as [string, string]; + if (underlying === "bin32") { + return { + kind: "primitive", + primitive: "bin32_alias", + originalName: aliasName, + }; + } + return { kind: "primitive", primitive: "bytes" }; } default: @@ -186,7 +218,7 @@ export class SchemaVisitor { } // Inline struct definition - if (typeof schema === 'object' && schema.__typename) { + if (typeof schema === "object" && schema.__typename) { const structName = schema.__typename as string; // Check if already visited if (!this.structs.has(structName)) { @@ -194,7 +226,7 @@ export class SchemaVisitor { this.structs.set(structName, struct); } return { - kind: 'struct', + kind: "struct", struct: this.structs.get(structName)!, }; } @@ -204,30 +236,30 @@ export class SchemaVisitor { private resolvePrimitive(name: string): Type { const primitiveMap: Record = { - 'bool': 'bool', - 'int': 'u32', - 'unsigned int': 'u32', - 'unsigned short': 'u16', - 'unsigned long': 'u64', - 'unsigned long long': 'u64', - 'unsigned char': 'u8', - 'double': 'f64', - 'string': 'string', - 'bin32': 'bytes', - 'field2': 'field2', // Extension field (Fq2) - pair of field elements - 'MerkleTreeId': 'enum_u32', // C++ enum serialized as uint32 - 'unordered_map': 'map_u32_pair', // StateReference: map> + bool: "bool", + int: "u32", + "unsigned int": "u32", + "unsigned short": "u16", + "unsigned long": "u64", + "unsigned long long": "u64", + "unsigned char": "u8", + double: "f64", + string: "string", + bin32: "bytes", + field2: "field2", // Extension field (Fq2) - pair of field elements + MerkleTreeId: "enum_u32", // C++ enum serialized as uint32 + unordered_map: "map_u32_pair", // StateReference: map> }; const primitive = primitiveMap[name]; if (primitive) { - return { kind: 'primitive', primitive, originalName: name }; + return { kind: "primitive", primitive, originalName: name }; } // Unknown primitive - treat as struct reference // This will be resolved later if it's a real struct return { - kind: 'struct', + kind: "struct", struct: { name, fields: [] }, // Placeholder }; } diff --git a/ipc-codegen/src/zig_codegen.ts b/ipc-codegen/src/zig_codegen.ts index c49b90e26f0e..4c5eb7a903b7 100644 --- a/ipc-codegen/src/zig_codegen.ts +++ b/ipc-codegen/src/zig_codegen.ts @@ -17,6 +17,14 @@ import type { } from "./schema_visitor.ts"; import { toSnakeCase, toPascalCase } from "./naming.ts"; +// Convert a schema alias name into its Zig type name. Strips a trailing `_t` +// (uint256_t → Uint256) and PascalCases the rest, so `fr` → `Fr`, +// `secp256k1_fr` → `Secp256k1Fr`, `uint256_t` → `Uint256`. +function toAliasName(name: string): string { + const trimmed = name.endsWith("_t") ? name.slice(0, -2) : name; + return toPascalCase(trimmed); +} + export interface ZigCodegenOptions { /** Service prefix to strip from method names (e.g., 'Wsdb') */ prefix?: string; @@ -57,7 +65,9 @@ export class ZigCodegen { case "bytes": return "[]const u8"; case "fr": - return "Fr"; // [32]u8 + return "Fr"; // legacy path (current schemas emit bin32_alias) + case "bin32_alias": + return type.originalName ? toAliasName(type.originalName) : "Fr"; case "field2": return "[2]Fr"; case "enum_u32": @@ -100,6 +110,7 @@ export class ZigCodegen { case "bytes": return `try Payload.binToPayload(${fieldExpr}, allocator)`; case "fr": + case "bin32_alias": return `try Payload.binToPayload(&${fieldExpr}, allocator)`; case "enum_u32": return `Payload{ .uint = @intCast(${fieldExpr}) }`; @@ -150,6 +161,7 @@ export class ZigCodegen { case "bytes": return `${payloadExpr}.bin.value()`; case "fr": + case "bin32_alias": return `${payloadExpr}.bin.value()[0..32].*`; case "enum_u32": return `@intCast(try ${payloadExpr}.asUint())`; @@ -341,6 +353,38 @@ ${variants} ...schema.responses.values(), ]; + // Collect every distinct bin32 alias name in the schema. Each becomes a + // `pub const` alias so wire fields with semantic aliases (Fr / Fq / + // Secp256k1Fr / …) keep their names; all share the underlying [32]u8. + const aliasNames = new Set(); + const collect = (type: Type): void => { + if ( + type.kind === "primitive" && + type.primitive === "bin32_alias" && + type.originalName + ) { + aliasNames.add(toAliasName(type.originalName)); + } else if ( + type.kind === "vector" || + type.kind === "array" || + type.kind === "optional" + ) { + if (type.element) collect(type.element); + } + }; + for (const s of schema.structs.values()) { + for (const f of s.fields) collect(f.type); + } + for (const s of schema.responses.values()) { + for (const f of s.fields) collect(f.type); + } + // Make sure `Fr` always exists (legacy path / field2 expansion uses it). + aliasNames.add("Fr"); + const aliasDecls = [...aliasNames] + .sort() + .map((n) => `pub const ${n} = [32]u8;`) + .join("\n"); + const structDefs = allStructs .map((s) => this.generateStruct(s)) .join("\n\n"); @@ -360,8 +404,13 @@ const msgpack = @import("msgpack"); const Payload = msgpack.Payload; const PackerIO = msgpack.PackerIO; ${hashLine} -/// 32-byte field element (Fr/Fq). Fixed-size, stack-allocated. -pub const Fr = [32]u8; +// --------------------------------------------------------------------------- +// Bin32 aliases (Fr / Fq / Secp256k1Fr / …). Each is a zero-cost pub const +// alias to [32]u8; the wire encoding (msgpack bin32) is applied by the +// fieldToPayload / fieldFromPayload helpers. +// --------------------------------------------------------------------------- + +${aliasDecls} // --------------------------------------------------------------------------- // Type definitions diff --git a/ipc-runtime/cpp/ipc_runtime/named_union.hpp b/ipc-runtime/cpp/ipc_runtime/named_union.hpp new file mode 100644 index 000000000000..6e559a49efdf --- /dev/null +++ b/ipc-runtime/cpp/ipc_runtime/named_union.hpp @@ -0,0 +1,135 @@ +#pragma once +/** + * @file named_union.hpp + * @brief Tagged-union with msgpack [name, payload] wire format. + * + * Each type in the union must declare: + * static constexpr const char MSGPACK_SCHEMA_NAME[] = "..."; + * + * This is the standalone, namespace-`ipc::` version intended for use by + * codegen-emitted reflection in services that don't link barretenberg. + * Barretenberg's own bb::NamedUnion (common/named_union.hpp) is a sibling + * implementation kept for in-tree consumers; the two are wire-compatible + * (both serialise as [type_name, payload]) but live in distinct namespaces. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ipc { + +template +concept HasMsgpackSchemaName = requires { + { T::MSGPACK_SCHEMA_NAME } -> std::convertible_to; +}; + +template class NamedUnion { +public: + using VariantType = std::variant; + +private: + VariantType value_; + + template + static std::optional get_index_from_name(std::string_view name) { + if constexpr (I < sizeof...(Types)) { + using CurrentType = std::variant_alternative_t; + if (name == CurrentType::MSGPACK_SCHEMA_NAME) { + return I; + } + return get_index_from_name(name); + } + return std::nullopt; + } + + template + static VariantType construct_by_index(size_t index, auto &o) { + if constexpr (I < sizeof...(Types)) { + if (I == index) { + using CurrentType = std::variant_alternative_t; + CurrentType obj; + o.convert(obj); + return obj; + } + return construct_by_index(index, o); + } + throw std::runtime_error("ipc::NamedUnion: invalid variant index"); + } + +public: + NamedUnion() = default; + + template + requires(std::is_constructible_v) + // NOLINTNEXTLINE(bugprone-forwarding-reference-overload) + NamedUnion(T &&t) : value_(std::forward(t)) {} + + operator VariantType &() { return value_; } + operator const VariantType &() const { return value_; } + + VariantType &get() { return value_; } + const VariantType &get() const { return value_; } + + template decltype(auto) visit(Visitor &&vis) && { + return std::visit(std::forward(vis), std::move(value_)); + } + template decltype(auto) visit(Visitor &&vis) const & { + return std::visit(std::forward(vis), value_); + } + + std::string_view get_type_name() const { + return std::visit( + [](const auto &obj) -> std::string_view { + return std::decay_t::MSGPACK_SCHEMA_NAME; + }, + value_); + } + + void msgpack_pack(auto &packer) const { + packer.pack_array(2); + std::string_view type_name = get_type_name(); + packer.pack(type_name); + std::visit([&packer](const auto &obj) { packer.pack(obj); }, value_); + } + + void msgpack_unpack(msgpack::object const &o) { + if (o.type != msgpack::type::ARRAY || o.via.array.size != 2) { + throw std::runtime_error("ipc::NamedUnion: expected array of size 2"); + } + const auto &arr = o.via.array; + if (arr.ptr[0].type != msgpack::type::STR) { + throw std::runtime_error( + "ipc::NamedUnion: expected first element to be a string (type name)"); + } + std::string_view type_name = + std::string_view(arr.ptr[0].via.str.ptr, arr.ptr[0].via.str.size); + auto index_opt = get_index_from_name(type_name); + if (!index_opt.has_value()) { + throw std::runtime_error("ipc::NamedUnion: unknown type name " + + std::string(type_name)); + } + value_ = construct_by_index(*index_opt, arr.ptr[1]); + } + + // Schema reflection — emits ["named_union", [[name, schema], ...]] via + // the schema packer (see reflect.hpp). + void msgpack_schema(auto &packer) const { + packer.pack_array(2); + packer.pack("named_union"); + packer.pack_array(sizeof...(Types)); + ( + [&packer]() { + packer.pack_array(2); + packer.pack(Types::MSGPACK_SCHEMA_NAME); + packer.pack_schema(*std::make_unique()); + }(), + ...); + } +}; + +} // namespace ipc diff --git a/ipc-runtime/cpp/ipc_runtime/schema.hpp b/ipc-runtime/cpp/ipc_runtime/schema.hpp new file mode 100644 index 000000000000..195f94c041b5 --- /dev/null +++ b/ipc-runtime/cpp/ipc_runtime/schema.hpp @@ -0,0 +1,220 @@ +#pragma once +/** + * @file schema.hpp + * @brief Compile-time msgpack schema reflection for codegen-emitted types. + * + * Walks a type's `msgpack(pack_fn)` method (which SERIALIZATION_FIELDS or the + * codegen-emitted bundled adaptor provides) and produces a JSON description + * of its msgpack layout. The output format is consumed by ipc-codegen as the + * canonical schema source — the binary serialises its own understanding of + * the wire format and that becomes the input for cross-language codegen. + * + * This is the `ipc::`-namespaced sibling of barretenberg's + * `serialize/msgpack_impl/schema_impl.hpp`. The two are independent — they + * don't share template specialisations and can coexist in the same TU. + * + * The schema reflection itself is in this file (no barretenberg headers, + * stdlib + msgpack-c only) so labs-owned services consuming ipc-codegen + * output don't need to link barretenberg. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ipc { + +// ---------------------------------------------------------------------------- +// Type names +// ---------------------------------------------------------------------------- + +template std::string schema_name(T const &) { + if constexpr (requires { T::MSGPACK_SCHEMA_NAME; }) { + return T::MSGPACK_SCHEMA_NAME; + } else { + char *demangled = + abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, nullptr); + std::string result = demangled ? demangled : typeid(T).name(); + if (demangled) + std::free(demangled); // NOLINT + // basic_string<...> → "string" + if (result.find("basic_string") != std::string::npos) + return "string"; + if (result == "i") + return "int"; + // Strip template args (Foo<...> → Foo) + if (auto pos = result.find('<'); pos != std::string::npos) + result = result.substr(0, pos); + // Strip namespace prefix (a::b::c → c) + if (auto pos = result.rfind(':'); pos != std::string::npos) + result = result.substr(pos + 1); + return result; + } +} + +// ---------------------------------------------------------------------------- +// Concepts +// ---------------------------------------------------------------------------- + +namespace schema_detail { +struct DoNothing { + void operator()(auto...) {} +}; +template +concept HasMsgPack = requires(T t, DoNothing nop) { t.msgpack(nop); }; +template +concept HasMsgPackSchema = + requires(const T t, DoNothing nop) { t.msgpack_schema(nop); }; +} // namespace schema_detail + +// ---------------------------------------------------------------------------- +// Schema packer +// ---------------------------------------------------------------------------- + +struct SchemaPacker; + +template +inline void schema_pack(SchemaPacker &packer, T const &obj); + +struct SchemaPacker : msgpack::packer { + SchemaPacker(msgpack::sbuffer &stream) : packer(stream) {} + + std::set emitted_types; + bool set_emitted(const std::string &type) { + if (emitted_types.find(type) == emitted_types.end()) { + emitted_types.insert(type); + return false; + } + return true; + } + + template void pack_schema(T const &obj) { + schema_pack(*this, obj); + } + + template void pack_template_type(const std::string &name) { + pack_array(2); + pack(name); + pack_array(sizeof...(Args)); + (schema_pack(*this, *std::make_unique()), ...); + } + + // ["alias", [, ]] — preserves the alias name in + // the emitted schema while pinning the underlying msgpack type. + void pack_alias(const std::string &schema_name, + const std::string &msgpack_name) { + pack_array(2); + pack("alias"); + pack_array(2); + pack(schema_name); + pack(msgpack_name); + } + + template + void pack_with_name(const std::string &type, T const &object) { + if (set_emitted(type)) { + pack(type); + return; + } + const_cast(object).msgpack([&](auto &...args) { + size_t kv_size = sizeof...(args); + pack_map(uint32_t(1 + kv_size / 2)); + pack("__typename"); + pack(type); + _schema_pack_map_content(*this, args...); + }); + } +}; + +inline void _schema_pack_map_content(SchemaPacker &) {} + +template +inline void _schema_pack_map_content(SchemaPacker &packer, std::string key, + const Value &value, const Rest &...rest) { + packer.pack(key); + schema_pack(packer, value); + _schema_pack_map_content(packer, rest...); +} + +// Fallback for types with no msgpack method (primitives, etc.) +template + requires(!schema_detail::HasMsgPackSchema && !schema_detail::HasMsgPack) +inline void schema_pack(SchemaPacker &packer, T const &obj) { + packer.pack(schema_name(obj)); +} + +// Type with custom msgpack_schema method (e.g. NamedUnion) +template +inline void schema_pack(SchemaPacker &packer, T const &obj) { + obj.msgpack_schema(packer); +} + +// Type with SERIALIZATION_FIELDS — pack as a map +template + requires(!schema_detail::HasMsgPackSchema) +inline void schema_pack(SchemaPacker &packer, T const &object) { + packer.pack_with_name(schema_name(object), object); +} + +// Container overloads +template +inline void schema_pack(SchemaPacker &packer, std::vector const &) { + packer.pack_template_type("vector"); +} +template +inline void schema_pack(SchemaPacker &packer, std::optional const &) { + packer.pack_template_type("optional"); +} +template +inline void schema_pack(SchemaPacker &packer, std::tuple const &) { + packer.pack_template_type("tuple"); +} +template +inline void schema_pack(SchemaPacker &packer, std::map const &) { + packer.pack_template_type("map"); +} +template +inline void schema_pack(SchemaPacker &packer, std::variant const &) { + packer.pack_template_type("variant"); +} +template +inline void schema_pack(SchemaPacker &packer, std::array const &) { + // Special case: array is the bin32 / fixed-byte form + // (matches how the AztecProtocol msgpack-c fork encodes it). + if constexpr (std::is_same_v || + std::is_same_v) { + packer.pack_alias("bin32", "bin32"); + } else { + packer.pack_array(2); + packer.pack("array"); + packer.pack_array(2); + schema_pack(packer, *std::make_unique()); + packer.pack(N); + } +} + +// ---------------------------------------------------------------------------- +// Convenience: serialise an object's schema to a JSON-ish string +// ---------------------------------------------------------------------------- + +inline std::string msgpack_schema_to_string(auto const &obj) { + msgpack::sbuffer output; + SchemaPacker printer{output}; + schema_pack(printer, obj); + msgpack::object_handle oh = msgpack::unpack(output.data(), output.size()); + std::stringstream pretty; + pretty << oh.get() << std::endl; + return pretty.str(); +} + +} // namespace ipc