diff --git a/Makefile b/Makefile index f616809a0c05..a4cfb857e509 100644 --- a/Makefile +++ b/Makefile @@ -55,13 +55,13 @@ endef # Fast bootstrap. fast: release-image barretenberg boxes playground docs aztec-up \ - bb-tests l1-contracts-tests yarn-project-tests boxes-tests playground-tests aztec-up-tests docs-tests noir-protocol-circuits-tests release-image-tests spartan claude-tests + bb-tests l1-contracts-tests yarn-project-tests boxes-tests playground-tests aztec-up-tests docs-tests noir-protocol-circuits-tests release-image-tests spartan claude-tests ipc-codegen-tests # Full bootstrap. full: fast bb-full-tests bb-cpp-full yarn-project-benches # Release. Everything plus copy bb cross compiles to ts projects. -release: fast bb-cpp-release-dir bb-ts-cross-copy +release: fast bb-cpp-release-dir bb-ts-cross-copy ipc-runtime-cross #============================================================================== # Noir @@ -211,7 +211,7 @@ bb-cpp-release-dir: bb-cpp-native bb-cpp-cross bb-cpp-full: bb-cpp bb-cpp-gcc bb-cpp-fuzzing bb-cpp-asan bb-cpp-smt bb-cpp-cross-arm64-macos bb-cpp-cross-arm64-ios bb-cpp-cross-arm64-android # BB TypeScript - TypeScript bindings -bb-ts: bb-cpp-wasm bb-cpp-wasm-threads bb-cpp-native +bb-ts: bb-cpp-wasm bb-cpp-wasm-threads bb-cpp-native ipc-runtime $(call build,$@,barretenberg/ts) # Copies the cross-compiles into bb.js. @@ -275,6 +275,37 @@ bb-tests: bb-cpp-native-tests bb-acir-tests bb-ts-tests bb-sol-tests bb-bbup-tes bb-full-tests: bb-cpp-wasm-threads-tests bb-cpp-asan-tests bb-cpp-smt-tests +#============================================================================== +# IPC Codegen +#============================================================================== + +.PHONY: ipc-codegen ipc-codegen-tests +ipc-codegen: + $(call build,$@,ipc-codegen) + +ipc-codegen-tests: ipc-codegen + $(call test,$@,ipc-codegen) + +.PHONY: ipc-runtime ipc-runtime-tests ipc-runtime-cross +ipc-runtime: + $(call build,$@,ipc-runtime) + +ipc-runtime-tests: ipc-runtime + $(call test,$@,ipc-runtime) + +# Cross-compile the NAPI addon for the 3 non-host release targets. +# Host (amd64-linux) addon is produced by the standalone `ipc-runtime` target. +ipc-runtime-cross-arm64-linux: + $(call build,$@,ipc-runtime,build_cross arm64-linux) + +ipc-runtime-cross-amd64-macos: + $(call build,$@,ipc-runtime,build_cross amd64-macos) + +ipc-runtime-cross-arm64-macos: + $(call build,$@,ipc-runtime,build_cross arm64-macos) + +ipc-runtime-cross: ipc-runtime ipc-runtime-cross-arm64-linux ipc-runtime-cross-amd64-macos ipc-runtime-cross-arm64-macos + #============================================================================== # .claude tooling #============================================================================== diff --git a/aztec-up/bootstrap.sh b/aztec-up/bootstrap.sh index 4618253bca97..3097f5418ad8 100755 --- a/aztec-up/bootstrap.sh +++ b/aztec-up/bootstrap.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash source $(git rev-parse --show-toplevel)/ci3/source_bootstrap -hash=$(hash_str $(cache_content_hash ^aztec-up/) $(../yarn-project/bootstrap.sh hash)) +hash=$(hash_str $(cache_content_hash ^aztec-up/) $(../ipc-runtime/bootstrap.sh hash) $(../yarn-project/bootstrap.sh hash)) # Bare aliases ("nightly", "latest") resolve to this major version. DEFAULT_MAJOR_VERSION=${AZTEC_TOOLCHAIN_DEFAULT_MAJOR_VERSION:-4} @@ -103,6 +103,7 @@ EOF # TODO(AD): we have kludged a retry here. a local NPM install ought to be robust enough not to. echo "Deploying packages to local npm registry (version: $version)..." { + echo $root/ipc-runtime/ts echo $root/barretenberg/ts $root/noir/bootstrap.sh get_projects $root/yarn-project/bootstrap.sh get_projects diff --git a/barretenberg/.gitignore b/barretenberg/.gitignore index ba04309b3eac..891a8e0ec957 100644 --- a/barretenberg/.gitignore +++ b/barretenberg/.gitignore @@ -13,3 +13,6 @@ bench-out rust/barretenberg-rs/src/generated_types.rs rust/barretenberg-rs/src/api.rs ts/src/cbind/generated/ + +# Codegen output dirs (ipc-codegen emits into a `generated/` subdir under each consumer) +**/generated/ diff --git a/barretenberg/acir_tests/yarn.lock b/barretenberg/acir_tests/yarn.lock index 8608993b859f..4070613de6d4 100644 --- a/barretenberg/acir_tests/yarn.lock +++ b/barretenberg/acir_tests/yarn.lock @@ -24,6 +24,7 @@ __metadata: version: 0.0.0-use.local resolution: "@aztec/bb.js@portal:../../ts::locator=bbjs-test%40workspace%3Abbjs-test" dependencies: + "@aztec/ipc-runtime": "portal:../../ipc-runtime/ts" comlink: "npm:^4.4.1" commander: "npm:^12.1.0" idb-keyval: "npm:^6.2.1" @@ -39,6 +40,7 @@ __metadata: version: 0.0.0-use.local resolution: "@aztec/bb.js@portal:../../ts::locator=browser-test-app%40workspace%3Abrowser-test-app" dependencies: + "@aztec/ipc-runtime": "portal:../../ipc-runtime/ts" comlink: "npm:^4.4.1" commander: "npm:^12.1.0" idb-keyval: "npm:^6.2.1" @@ -50,6 +52,18 @@ __metadata: languageName: node linkType: soft +"@aztec/ipc-runtime@portal:../../ipc-runtime/ts::locator=%40aztec%2Fbb.js%40portal%3A..%2F..%2Fts%3A%3Alocator%3Dbbjs-test%2540workspace%253Abbjs-test": + version: 0.0.0-use.local + resolution: "@aztec/ipc-runtime@portal:../../ipc-runtime/ts::locator=%40aztec%2Fbb.js%40portal%3A..%2F..%2Fts%3A%3Alocator%3Dbbjs-test%2540workspace%253Abbjs-test" + languageName: node + linkType: soft + +"@aztec/ipc-runtime@portal:../../ipc-runtime/ts::locator=%40aztec%2Fbb.js%40portal%3A..%2F..%2Fts%3A%3Alocator%3Dbrowser-test-app%2540workspace%253Abrowser-test-app": + version: 0.0.0-use.local + resolution: "@aztec/ipc-runtime@portal:../../ipc-runtime/ts::locator=%40aztec%2Fbb.js%40portal%3A..%2F..%2Fts%3A%3Alocator%3Dbrowser-test-app%2540workspace%253Abrowser-test-app" + languageName: node + linkType: soft + "@babel/code-frame@npm:^7.0.0": version: 7.26.2 resolution: "@babel/code-frame@npm:7.26.2" diff --git a/barretenberg/cpp/.rebuild_patterns b/barretenberg/cpp/.rebuild_patterns index 4c7ba2459ea5..2c14a6c49984 100644 --- a/barretenberg/cpp/.rebuild_patterns +++ b/barretenberg/cpp/.rebuild_patterns @@ -4,3 +4,9 @@ ^barretenberg/cpp/scripts/ ^barretenberg/cpp/bootstrap.sh ^barretenberg/cpp/CMakePresets.json +# bbapi and ipc_runtime generate C++ headers via ipc-codegen at CMake-time. +# Treat the codegen sources + templates as part of bb-cpp's input so the CI +# cache key invalidates when the codegen changes. +^ipc-codegen/src/.*\.ts$ +^ipc-codegen/templates/cpp/.*$ +^ipc-runtime/cpp/.*$ diff --git a/barretenberg/cpp/format.sh b/barretenberg/cpp/format.sh index 8ac9d9749ecd..a33c4c59a4c8 100755 --- a/barretenberg/cpp/format.sh +++ b/barretenberg/cpp/format.sh @@ -19,7 +19,7 @@ elif [ "$1" == "changed" ]; then format_files "$files" fi elif [ "$1" == "check" ]; then - files=$(find ./src -iname *.hpp -o -iname *.cpp -o -iname *.tcc | grep -v bb/deps) + files=$(find ./src -iname *.hpp -o -iname *.cpp -o -iname *.tcc | grep -v bb/deps | grep -v '/generated/') echo "$files" | parallel -N10 clang-format-20 --dry-run --Werror elif [ -n "$1" ]; then files=$(git diff-index --relative --name-only $1 | grep -e '\.\(cpp\|hpp\|tcc\)$') diff --git a/barretenberg/cpp/src/CMakeLists.txt b/barretenberg/cpp/src/CMakeLists.txt index 20cfb695c920..875ebb4b6855 100644 --- a/barretenberg/cpp/src/CMakeLists.txt +++ b/barretenberg/cpp/src/CMakeLists.txt @@ -52,7 +52,7 @@ if(WASM) add_link_options(-Wl,--export-memory,--import-memory,--stack-first,-z,stack-size=1048576,--max-memory=4294967296) endif() -include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${MSGPACK_INCLUDE} ${TRACY_INCLUDE} ${LMDB_INCLUDE} ${LIBDEFLATE_INCLUDE} ${HTTPLIB_INCLUDE} ${BACKWARD_INCLUDE} ${NLOHMANN_JSON_INCLUDE}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${MSGPACK_INCLUDE} ${TRACY_INCLUDE} ${LMDB_INCLUDE} ${LIBDEFLATE_INCLUDE} ${HTTPLIB_INCLUDE} ${BACKWARD_INCLUDE} ${NLOHMANN_JSON_INCLUDE} ${CMAKE_CURRENT_SOURCE_DIR}/../../../ipc-runtime/cpp) # Add avm-transpiler include path when library is provided if(AVM_TRANSPILER_LIB) @@ -126,12 +126,22 @@ if(NOT FUZZING AND NOT WASM AND NOT BB_LITE) add_subdirectory(barretenberg/world_state) # NOTE: Do not conditionally base this on the AVM flag as it defines a necessary vm2_sim library. add_subdirectory(barretenberg/vm2) - add_subdirectory(barretenberg/ipc) add_subdirectory(barretenberg/wsdb) - add_subdirectory(barretenberg/wsdb_client) + add_subdirectory(barretenberg/vm2_wsdb) + add_subdirectory(barretenberg/cdb) + add_subdirectory(barretenberg/kvdb) + add_subdirectory(barretenberg/avm) add_subdirectory(barretenberg/nodejs_module) endif() +# Pull in ipc-runtime as a C++ dependency. Provides the `ipc_runtime` +# library target (static, or INTERFACE under WASM with transport sources +# stubbed) that bbapi/wsdb/etc link against for the codegen-emitted +# bb_ipc_server.hpp dispatcher. +if(NOT FUZZING AND NOT BB_LITE) + add_subdirectory(${CMAKE_SOURCE_DIR}/../../ipc-runtime/cpp ${CMAKE_BINARY_DIR}/ipc-runtime) +endif() + if(FUZZING_AVM) if(FUZZING) # Only add these if they weren't added above (when NOT FUZZING AND NOT WASM) diff --git a/barretenberg/cpp/src/barretenberg/api/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/api/CMakeLists.txt index 9fcf0636252a..beb18a9f041a 100644 --- a/barretenberg/cpp/src/barretenberg/api/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/api/CMakeLists.txt @@ -5,6 +5,11 @@ if(AVM_TRANSPILER_LIB) target_link_libraries(api_objects PRIVATE avm_transpiler) endif() -if(NOT WASM AND NOT BB_LITE) - target_link_libraries(api_objects PRIVATE ipc) +# api_objects compiles api_msgpack.cpp / api_chonk.cpp / api_ultra_honk.cpp +# which transitively include bbapi/generated/bb_types.hpp → ipc_runtime/throw.hpp. +# ipc_runtime is a STATIC lib natively and an INTERFACE (header-only) lib under +# WASM, so linking under both modes just propagates the include path. +if(NOT BB_LITE) + target_include_directories(api_objects PRIVATE ${CMAKE_SOURCE_DIR}/src/barretenberg/bbapi/generated) + target_link_libraries(api_objects PRIVATE ipc_runtime) endif() 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 bc50a2749129..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 @@ -9,15 +11,8 @@ #include #if !defined(__wasm__) && !defined(_WIN32) -#include "barretenberg/ipc/ipc_server.hpp" -#include -#include -#include -#ifdef __linux__ -#include -#elif defined(__APPLE__) -#include -#endif +#include "ipc_runtime/ipc_server.hpp" +#include "ipc_runtime/signal_handlers.hpp" #endif namespace bb { @@ -27,168 +22,68 @@ 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; } #if !defined(__wasm__) && !defined(_WIN32) -// Set up platform-specific parent death monitoring -// This ensures the bb process exits when the parent (Node.js) dies -static void setup_parent_death_monitoring() -{ -#ifdef __linux__ - // Linux: Use prctl to request SIGTERM when parent dies - // This is kernel-level and very reliable - if (prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) { - std::cerr << "Warning: Could not set parent death signal" << '\n'; - } -#elif defined(__APPLE__) - // macOS: Use kqueue to monitor parent process - // Spawn a dedicated thread that blocks waiting for parent to exit - pid_t parent_pid = getppid(); - std::thread([parent_pid]() { - int kq = kqueue(); - if (kq == -1) { - std::cerr << "Warning: Could not create kqueue for parent monitoring" << '\n'; - return; - } - - struct kevent change; - EV_SET(&change, parent_pid, EVFILT_PROC, EV_ADD | EV_ENABLE, NOTE_EXIT, 0, nullptr); - if (kevent(kq, &change, 1, nullptr, 0, nullptr) == -1) { - std::cerr << "Warning: Could not monitor parent process" << '\n'; - close(kq); - return; - } - - // Block until parent exits - struct kevent event; - kevent(kq, nullptr, 0, &event, 1, nullptr); - - std::cerr << "Parent process exited, shutting down..." << '\n'; - close(kq); - std::exit(0); - }).detach(); -#endif -} - int execute_msgpack_ipc_server(std::unique_ptr server) { - // Store server pointer for signal handler cleanup (works for both socket and shared memory) - // MUST be set before listen() since SIGBUS can occur during listen() - static ipc::IpcServer* global_server = server.get(); - - // Register signal handlers for graceful cleanup - // MUST be registered before listen() since SIGBUS can occur during initialization - // SIGTERM: Sent by processes/test frameworks on shutdown - // SIGINT: Sent by Ctrl+C - auto graceful_shutdown_handler = [](int signal) { - std::cerr << "\nReceived signal " << signal << ", shutting down gracefully..." << '\n'; - if (global_server) { - global_server->request_shutdown(); - } - }; - - // Register handlers for fatal memory errors (SIGBUS, SIGSEGV) - // These occur when shared memory exhaustion happens during initialization - auto fatal_error_handler = [](int signal) { - const char* signal_name = "UNKNOWN"; - if (signal == SIGBUS) { - signal_name = "SIGBUS"; - } else if (signal == SIGSEGV) { - signal_name = "SIGSEGV"; - } - std::cerr << "\nFatal error: received " << signal_name << " during initialization" << '\n'; - std::cerr << "This likely means shared memory exhaustion (try reducing --max-clients)" << '\n'; - - // Clean up IPC resources before exiting - if (global_server) { - global_server->close(); - } - - std::exit(1); - }; - - (void)std::signal(SIGTERM, graceful_shutdown_handler); - (void)std::signal(SIGINT, graceful_shutdown_handler); - (void)std::signal(SIGBUS, fatal_error_handler); - (void)std::signal(SIGSEGV, fatal_error_handler); - - // Set up parent death monitoring (kills this process when parent dies) - setup_parent_death_monitoring(); + // Install runtime lifecycle handlers (SIGTERM/SIGINT → request_shutdown, + // SIGBUS/SIGSEGV → close+exit, parent-death watch via prctl/kqueue). + // MUST be installed before listen() since SIGBUS can occur during init + // when shared memory is exhausted. + ipc::install_default_signal_handlers(*server); if (!server->listen()) { std::cerr << "Error: Could not start IPC server" << '\n'; @@ -197,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_msgpack.hpp b/barretenberg/cpp/src/barretenberg/api/api_msgpack.hpp index 8f4a22cffa7a..027bc487e33d 100644 --- a/barretenberg/cpp/src/barretenberg/api/api_msgpack.hpp +++ b/barretenberg/cpp/src/barretenberg/api/api_msgpack.hpp @@ -6,7 +6,7 @@ #include #ifndef __wasm__ -#include "barretenberg/ipc/ipc_server.hpp" +#include "ipc_runtime/ipc_server.hpp" #endif namespace bb { 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/avm/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/avm/CMakeLists.txt new file mode 100644 index 000000000000..3f0107e8470c --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/CMakeLists.txt @@ -0,0 +1,73 @@ +set(AVM_SCHEMA ${CMAKE_CURRENT_SOURCE_DIR}/avm_schema.json) +set(AVM_GEN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/generated) +set(AVM_GEN_OUTPUTS + ${AVM_GEN_DIR}/avm_ipc_client.cpp + ${AVM_GEN_DIR}/avm_ipc_client.hpp + ${AVM_GEN_DIR}/avm_ipc_server.hpp + ${AVM_GEN_DIR}/avm_types.hpp + ${AVM_GEN_DIR}/ipc_codegen/msgpack_adaptor.hpp + ${AVM_GEN_DIR}/ipc_codegen/named_union.hpp + ${AVM_GEN_DIR}/ipc_codegen/schema.hpp + ${AVM_GEN_DIR}/ipc_codegen/throw.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 ${AVM_GEN_OUTPUTS} + COMMAND node --experimental-strip-types --experimental-transform-types --no-warnings + ${IPC_CODEGEN_DIR}/src/generate.ts + --schema ${AVM_SCHEMA} + --lang cpp + --out ${AVM_GEN_DIR} + --client --server + --cpp-namespace bb::avm + --cpp-include-dir barretenberg/avm/generated + --prefix Avm + DEPENDS ${AVM_SCHEMA} ${IPC_CODEGEN_SRC} + COMMENT "Generating AVM IPC client + server from avm_schema.json" + VERBATIM +) +add_custom_target(avm_ipc_generated DEPENDS ${AVM_GEN_OUTPUTS}) + +# aztec-avm binary (standalone AVM simulator server). Connects to a running aztec-wsdb over +# UDS for tree access (via wsdb::WsdbIpcClient) and to a TS-hosted CdbIpcServer for +# contract data. +add_executable( + aztec-avm + main.cpp + cli.cpp + avm_execute.cpp + avm_ipc_server.cpp +) +add_dependencies(aztec-avm avm_ipc_generated) +target_include_directories( + aztec-avm + PRIVATE + ${AVM_GEN_DIR} +) +target_link_libraries( + aztec-avm + PRIVATE + barretenberg + vm2_sim + wsdb_ipc_merkle_db + wsdb_ipc_client + cdb_ipc_client + ipc_runtime + env +) +if(ENABLE_STACKTRACES) + target_link_libraries( + aztec-avm + PUBLIC + Backward::Interface + ) + target_link_options( + aztec-avm + PRIVATE + -ldw -lelf + ) +endif() diff --git a/barretenberg/cpp/src/barretenberg/avm/avm_execute.cpp b/barretenberg/cpp/src/barretenberg/avm/avm_execute.cpp new file mode 100644 index 000000000000..67567102bd69 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/avm_execute.cpp @@ -0,0 +1,130 @@ +#include "barretenberg/avm/avm_execute.hpp" +#include "barretenberg/common/log.hpp" +#include "barretenberg/serialize/msgpack.hpp" +#include "barretenberg/serialize/msgpack_impl.hpp" +#include "barretenberg/vm2/avm_sim_api.hpp" +#include "barretenberg/vm2/common/avm_io.hpp" +#include "barretenberg/vm2/simulation/lib/cancellation_token.hpp" +#include "barretenberg/vm2/simulation_helper.hpp" +#include "barretenberg/vm2_wsdb/wsdb_ipc_merkle_db.hpp" + +namespace bb::avm { + +using namespace bb::avm2; +using namespace bb::world_state; + +// Global cancellation token for the currently active simulation. +// Set before simulation starts, cleared after. SIGUSR1 handler reads this to cancel. +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +std::atomic g_active_cancellation_token{ nullptr }; + +// --------------------------------------------------------------------------- +// Helper: serialize a value to msgpack bytes +// --------------------------------------------------------------------------- + +template static std::vector serialize_to_msgpack(const T& value) +{ + msgpack::sbuffer buf; + msgpack::pack(buf, value); + return std::vector(buf.data(), buf.data() + buf.size()); +} + +template static T deserialize_from_msgpack(const std::vector& bytes) +{ + auto unpacked = msgpack::unpack(reinterpret_cast(bytes.data()), bytes.size()); + T value; + unpacked.get().convert(value); + return value; +} + +// --------------------------------------------------------------------------- +// AvmSimulate +// --------------------------------------------------------------------------- + +template <> wire::AvmSimulateResponse handle_simulate(AvmRequest& request, wire::AvmSimulate&& command) +{ + // Deserialize AvmFastSimulationInputs from opaque bytes + auto sim_inputs = deserialize_from_msgpack(command.inputs); + + // Always use the externally-provided forkId. The caller (TXE / PublicProcessor) is + // responsible for creating the WSDB fork AND registering its contractsDB on the CDB + // server before invoking AvmSimulate. Previously we treated forkId == 0 as + // "no fork provided, create one here" — but 0 is a valid forkId (the genesis fork), + // and creating a fresh fork here meant CDB had no contractsDB registered for it, + // producing "no contracts DB registered for forkId N" errors at lookup time. + uint64_t fork_id = sim_inputs.ws_revision.forkId; + vinfo("Using external WSDB fork ", fork_id, " for AVM simulation"); + + // Route CDB requests to the correct PublicContractsDB via fork ID + request.cdb_client.set_fork_id(fork_id); + + // Create a cancellation token for this simulation and expose it globally + // so the SIGUSR1 handler can signal cancellation from TypeScript. + auto cancellation_token = std::make_shared(); + g_active_cancellation_token.store(cancellation_token.get(), std::memory_order_release); + + try { + // Create revision pointing to the fork. blockNumber = LATEST sentinel routes the WSDB + // through its non-historical (current-state) path so the fork's uncommitted leaves are + // visible. Using 0 here makes the WSDB treat the query as historical against the empty + // genesis tree, missing any in-fork uncommitted state (e.g. contracts deployed by an + // earlier tx in the same block). + WorldStateRevision revision = { + .forkId = fork_id, + .blockNumber = WorldStateRevision::LATEST, + .includeUncommitted = true, + }; + + // Create IPC-backed MerkleDB and ContractDB + bb::avm2::simulation::WsdbIpcMerkleDB merkle_db(request.wsdb_client, revision); + + // Run simulation using the helper that takes raw DB interfaces. + // Route to hint collection or fast path based on config. + AvmSimulationHelper simulation_helper; + auto result = sim_inputs.config.collect_hints + ? simulation_helper.simulate_for_hint_collection_internal(request.cdb_client, + merkle_db, + sim_inputs.config, + sim_inputs.tx, + sim_inputs.global_variables, + sim_inputs.protocol_contracts, + cancellation_token) + : simulation_helper.simulate_fast_internal(request.cdb_client, + merkle_db, + sim_inputs.config, + sim_inputs.tx, + sim_inputs.global_variables, + sim_inputs.protocol_contracts, + cancellation_token); + + g_active_cancellation_token.store(nullptr, std::memory_order_release); + + // Fork lifecycle is owned by the caller; nothing to clean up here. + + return wire::AvmSimulateResponse{ .result = serialize_to_msgpack(result) }; + } catch (...) { + g_active_cancellation_token.store(nullptr, std::memory_order_release); + throw; + } +} + +// --------------------------------------------------------------------------- +// AvmSimulateWithHints +// --------------------------------------------------------------------------- + +template <> +wire::AvmSimulateWithHintsResponse handle_simulate_with_hints(AvmRequest& request, wire::AvmSimulateWithHints&& command) +{ + (void)request; + + // Deserialize AvmProvingInputs from opaque bytes + auto proving_inputs = deserialize_from_msgpack(command.inputs); + + // Run simulation with hinted DBs (self-contained, no external DB needed) + AvmSimAPI api; + auto result = api.simulate_with_hinted_dbs(proving_inputs); + + return wire::AvmSimulateWithHintsResponse{ .result = serialize_to_msgpack(result) }; +} + +} // namespace bb::avm diff --git a/barretenberg/cpp/src/barretenberg/avm/avm_execute.hpp b/barretenberg/cpp/src/barretenberg/avm/avm_execute.hpp new file mode 100644 index 000000000000..4f9a18f0b33f --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/avm_execute.hpp @@ -0,0 +1,29 @@ +#pragma once +/** + * @file avm_execute.hpp + * @brief AVM IPC handler context. + */ + +#include "barretenberg/avm/generated/avm_ipc_server.hpp" +#include "barretenberg/cdb/cdb_ipc_client.hpp" +#include "barretenberg/vm2/simulation/lib/cancellation_token.hpp" +#include "barretenberg/wsdb/generated/wsdb_ipc_client.hpp" + +#include + +namespace bb::avm { + +/** Global cancellation token for the active simulation. SIGUSR1 handler uses this. */ +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +extern std::atomic g_active_cancellation_token; + +/** + * @brief Context passed to each command's execute() method. + * Provides access to WSDB and CDB IPC clients. + */ +struct AvmRequest { + cdb::CdbIpcContractDB& cdb_client; + wsdb::WsdbIpcClient& wsdb_client; +}; + +} // namespace bb::avm diff --git a/barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.cpp b/barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.cpp new file mode 100644 index 000000000000..4cf98283eccf --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.cpp @@ -0,0 +1,95 @@ +#include "barretenberg/avm/avm_ipc_server.hpp" +#include "barretenberg/avm/avm_execute.hpp" +#include "barretenberg/cdb/cdb_ipc_client.hpp" +#include "barretenberg/common/log.hpp" +#include "barretenberg/wsdb/generated/wsdb_ipc_client.hpp" +#include "ipc_runtime/ipc_server.hpp" +#include "ipc_runtime/serve_helper.hpp" +#include "ipc_runtime/signal_handlers.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace bb::avm { + +int execute_avm_server(const std::string& input_path, const std::string& wsdb_path, const std::string& cdb_path) +{ + // Connect to WSDB server with retry + std::cerr << "Connecting to aztec-wsdb at " << wsdb_path << '\n'; + constexpr int max_retries = 50; + constexpr int retry_delay_ms = 100; + std::unique_ptr wsdb_client; + for (int attempt = 0; attempt < max_retries; ++attempt) { + try { + wsdb_client = std::make_unique(wsdb_path); + break; + } catch (const std::exception& e) { + if (attempt == max_retries - 1) { + std::cerr << "Failed to connect to aztec-wsdb after " << max_retries << " attempts: " << e.what() + << '\n'; + return 1; + } + std::this_thread::sleep_for(std::chrono::milliseconds(retry_delay_ms)); + } + } + + // Connect to CDB server with retry (TS server may still be binding its socket) + std::cerr << "Connecting to aztec-cdb at " << cdb_path << '\n'; + std::unique_ptr cdb_client; + for (int attempt = 0; attempt < max_retries; ++attempt) { + try { + cdb_client = std::make_unique(cdb_path); + break; + } catch (const std::exception& e) { + if (attempt == max_retries - 1) { + std::cerr << "Failed to connect to aztec-cdb after " << max_retries << " attempts: " << e.what() + << '\n'; + return 1; + } + std::this_thread::sleep_for(std::chrono::milliseconds(retry_delay_ms)); + } + } + + AvmRequest request{ .cdb_client = *cdb_client, .wsdb_client = *wsdb_client }; + + ipc::ServerOptions opts; + opts.max_shm_clients = 1; + auto server = ipc::make_server(input_path, opts); + if (!server) { + std::cerr << "Error: --input path must end with .sock or .shm: " << input_path << '\n'; + return 1; + } + + std::cerr << "aztec-avm listening on " << input_path << '\n'; + ipc::install_default_signal_handlers(*server); + auto cancel_simulation_handler = [](int /*signal*/) { + auto* token = g_active_cancellation_token.load(std::memory_order_acquire); + if (token) { + token->cancel(); + } + }; + + (void)std::signal(SIGUSR1, cancel_simulation_handler); + + if (!server->listen()) { + std::cerr << "Error: Could not start IPC server" << '\n'; + return 1; + } + + std::cerr << "aztec-avm IPC server ready" << '\n'; + + auto handler = make_avm_handler(request); + server->run([&handler](int /*client_id*/, std::span raw_request) -> std::vector { + return handler(std::vector(raw_request.begin(), raw_request.end())); + }); + + server->close(); + return 0; +} + +} // namespace bb::avm diff --git a/barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.hpp b/barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.hpp new file mode 100644 index 000000000000..a3dc6bd13895 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace bb::avm { + +/** + * @brief Start the aztec-avm IPC server. + * + * Connects to WSDB and CDB as IPC clients, then runs the server loop + * dispatching incoming simulation commands. + * + * @param input_path IPC socket path for TS client connections. + * @param wsdb_path Socket path to the running aztec-wsdb server. + * @param cdb_path Socket path to the running aztec-cdb server. + * @return 0 on success, non-zero on error. + */ +int execute_avm_server(const std::string& input_path, const std::string& wsdb_path, const std::string& cdb_path); + +} // namespace bb::avm diff --git a/barretenberg/cpp/src/barretenberg/avm/avm_schema.json b/barretenberg/cpp/src/barretenberg/avm/avm_schema.json new file mode 100644 index 000000000000..58e153873fbb --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/avm_schema.json @@ -0,0 +1,59 @@ +{ + "commands": [ + "named_union", + [ + [ + "AvmSimulate", + { + "__typename": "AvmSimulate", + "inputs": ["vector", ["unsigned char"]] + } + ], + [ + "AvmSimulateWithHints", + { + "__typename": "AvmSimulateWithHints", + "inputs": ["vector", ["unsigned char"]] + } + ], + [ + "AvmShutdown", + { + "__typename": "AvmShutdown" + } + ] + ] + ], + "responses": [ + "named_union", + [ + [ + "AvmErrorResponse", + { + "__typename": "AvmErrorResponse", + "message": "string" + } + ], + [ + "AvmSimulateResponse", + { + "__typename": "AvmSimulateResponse", + "result": ["vector", ["unsigned char"]] + } + ], + [ + "AvmSimulateWithHintsResponse", + { + "__typename": "AvmSimulateWithHintsResponse", + "result": ["vector", ["unsigned char"]] + } + ], + [ + "AvmShutdownResponse", + { + "__typename": "AvmShutdownResponse" + } + ] + ] + ] +} diff --git a/barretenberg/cpp/src/barretenberg/avm/cli.cpp b/barretenberg/cpp/src/barretenberg/avm/cli.cpp new file mode 100644 index 000000000000..823ea55daf1d --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/cli.cpp @@ -0,0 +1,62 @@ +#include "barretenberg/avm/cli.hpp" +#include "barretenberg/avm/avm_execute.hpp" +#include "barretenberg/avm/avm_ipc_server.hpp" +#include "barretenberg/common/log.hpp" + +#include "barretenberg/bb/deps/cli11.hpp" +#include +#include + +namespace bb::avm { + +int parse_and_run_avm(int argc, char* argv[]) +{ + CLI::App app{ "aztec-avm: Standalone AVM simulator server" }; + app.require_subcommand(1); + + // ----------------------------------------------------------------------- + // Subcommand: msgpack + // ----------------------------------------------------------------------- + CLI::App* msgpack_command = app.add_subcommand("msgpack", "Msgpack API interface."); + + // msgpack schema + CLI::App* msgpack_schema_command = + msgpack_command->add_subcommand("schema", "Output a msgpack schema encoded as JSON to stdout."); + + // msgpack run + CLI::App* msgpack_run_command = msgpack_command->add_subcommand("run", "Start the AVM simulator IPC server."); + + std::string input_path; + msgpack_run_command->add_option("-i,--input", input_path, "IPC socket/shm path (.sock or .shm)")->required(); + + std::string wsdb_path; + msgpack_run_command->add_option("--wsdb", wsdb_path, "WSDB server socket path")->required(); + + std::string cdb_path; + msgpack_run_command->add_option("--cdb", cdb_path, "CDB server socket path")->required(); + + // Parse CLI + try { + app.parse(argc, argv); + } catch (const CLI::ParseError& e) { + return app.exit(e); + } + + try { + if (msgpack_schema_command->parsed()) { + std::cout << get_avm_schema_as_json() << std::endl; + return 0; + } + + if (msgpack_run_command->parsed()) { + return execute_avm_server(input_path, wsdb_path, cdb_path); + } + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << '\n'; + return 1; + } + + return 0; +} + +} // namespace bb::avm diff --git a/barretenberg/cpp/src/barretenberg/avm/cli.hpp b/barretenberg/cpp/src/barretenberg/avm/cli.hpp new file mode 100644 index 000000000000..c8bf203ee119 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/cli.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace bb::avm { + +int parse_and_run_avm(int argc, char* argv[]); + +} // namespace bb::avm diff --git a/barretenberg/cpp/src/barretenberg/avm/main.cpp b/barretenberg/cpp/src/barretenberg/avm/main.cpp new file mode 100644 index 000000000000..cba683be6c1a --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/main.cpp @@ -0,0 +1,6 @@ +#include "barretenberg/avm/cli.hpp" + +int main(int argc, char* argv[]) +{ + return bb::avm::parse_and_run_avm(argc, argv); +} diff --git a/barretenberg/cpp/src/barretenberg/bb/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/bb/CMakeLists.txt index fa6b7858a1eb..fb209afb1ab8 100644 --- a/barretenberg/cpp/src/barretenberg/bb/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/bb/CMakeLists.txt @@ -22,8 +22,12 @@ if (NOT(FUZZING)) if(AVM_TRANSPILER_LIB) target_link_libraries(bb PRIVATE avm_transpiler) endif() - if(NOT WASM AND NOT BB_LITE) - target_link_libraries(bb PRIVATE ipc) + # ipc_runtime is INTERFACE-only under WASM (headers, no transport), STATIC + # natively — linking in both modes just propagates include paths so + # bb/cli.cpp can find ipc_runtime/throw.hpp via the codegen-emitted + # bb_types.hpp it transitively pulls in. + if(NOT BB_LITE) + target_link_libraries(bb PRIVATE ipc_runtime) endif() if(ENABLE_STACKTRACES) target_link_libraries( @@ -62,8 +66,9 @@ if (NOT(FUZZING)) if(AVM_TRANSPILER_LIB) target_link_libraries(bb-avm PRIVATE avm_transpiler) endif() - if(NOT WASM AND NOT BB_LITE) - target_link_libraries(bb-avm PRIVATE ipc) + # ipc_runtime as above — INTERFACE under WASM, STATIC native. + if(NOT BB_LITE) + target_link_libraries(bb-avm PRIVATE ipc_runtime) endif() if(ENABLE_STACKTRACES) target_link_libraries( 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..a9b4de796ea1 100644 --- a/barretenberg/cpp/src/barretenberg/bbapi/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/bbapi/CMakeLists.txt @@ -1,6 +1,66 @@ +# Generate BB IPC wire types and dispatchers from bb_schema.json. WASM builds +# also consume the generated server header, so codegen must run outside +# native-only blocks. +set(BB_SCHEMA ${CMAKE_CURRENT_SOURCE_DIR}/bb_schema.json) +set(BB_GEN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/generated) + +if(NOT FUZZING) + 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}/ipc_codegen/msgpack_adaptor.hpp + ${BB_GEN_DIR}/ipc_codegen/named_union.hpp + ${BB_GEN_DIR}/ipc_codegen/schema.hpp + ${BB_GEN_DIR}/ipc_codegen/throw.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 + ) + 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 +target_include_directories(bbapi_objects PRIVATE ${BB_GEN_DIR}) +target_include_directories(bbapi PUBLIC ${BB_GEN_DIR}) + +if(NOT FUZZING) + add_dependencies(bbapi_objects bb_codegen) + # Codegen-emitted bb_ipc_{client,server}.hpp + bb_types.hpp include + # ipc_runtime headers and vendored ipc_codegen support headers. + # + # Link to both `bbapi_objects` (the OBJECT lib) AND `bbapi` (the static + # lib that wraps it). barretenberg_module makes dependents (api, bb, + # benchmark/ipc_bench, …) link to `bbapi`, not `bbapi_objects`, so + # without the second linkage the ipc_runtime INTERFACE_INCLUDE_DIRECTORIES + # never propagate to downstream users. + target_link_libraries(bbapi_objects PUBLIC ipc_runtime) + target_link_libraries(bbapi 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. Tests are native-only. if(NOT WASM AND NOT FUZZING) - target_link_libraries(bbapi_tests PRIVATE vm2_stub) + target_include_directories(bbapi_test_objects PRIVATE ${BB_GEN_DIR}) + 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/bb_curve_constants.json b/barretenberg/cpp/src/barretenberg/bbapi/bb_curve_constants.json new file mode 100644 index 000000000000..20dab049c505 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/bbapi/bb_curve_constants.json @@ -0,0 +1,36 @@ +{ + "bn254_fr_modulus": "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001", + "bn254_fq_modulus": "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47", + "bn254_g1_generator": { + "x": "0000000000000000000000000000000000000000000000000000000000000001", + "y": "0000000000000000000000000000000000000000000000000000000000000002" + }, + "bn254_g2_generator": { + "x": [ + "1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed", + "198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2" + ], + "y": [ + "12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b" + ] + }, + "grumpkin_fr_modulus": "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47", + "grumpkin_fq_modulus": "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001", + "grumpkin_g1_generator": { + "x": "0000000000000000000000000000000000000000000000000000000000000001", + "y": "0000000000000002cf135e7506a45d632d270d45f1181294833fc48d823f272c" + }, + "secp256k1_fr_modulus": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "secp256k1_fq_modulus": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "secp256k1_g1_generator": { + "x": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "y": "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8" + }, + "secp256r1_fr_modulus": "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", + "secp256r1_fq_modulus": "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", + "secp256r1_g1_generator": { + "x": "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", + "y": "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5" + } +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bb_schema.json b/barretenberg/cpp/src/barretenberg/bbapi/bb_schema.json new file mode 100644 index 000000000000..9c7cf2ba2d89 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/bbapi/bb_schema.json @@ -0,0 +1,2 @@ +{"__typename":"BbApi","commands":["named_union",[["AvmProve",{"__typename":"AvmProve","inputs":["vector",["unsigned char"]]}],["AvmVerify",{"__typename":"AvmVerify","proof":["vector",[["alias",["fr","bin32"]]]],"public_inputs":["vector",["unsigned char"]]}],["AvmCheckCircuit",{"__typename":"AvmCheckCircuit","inputs":["vector",["unsigned char"]]}],["CircuitProve",{"__typename":"CircuitProve","circuit":{"__typename":"CircuitInput","name":"string","bytecode":["vector",["unsigned char"]],"verification_key":["vector",["unsigned char"]]},"witness":["vector",["unsigned char"]],"settings":{"__typename":"ProofSystemSettings","ipa_accumulation":"bool","oracle_hash_type":"string","disable_zk":"bool","optimized_solidity_verifier":"bool"}}],["CircuitComputeVk",{"__typename":"CircuitComputeVk","circuit":{"__typename":"CircuitInputNoVK","name":"string","bytecode":["vector",["unsigned char"]]},"settings":"ProofSystemSettings"}],["CircuitStats",{"__typename":"CircuitStats","circuit":"CircuitInput","include_gates_per_opcode":"bool","settings":"ProofSystemSettings"}],["CircuitVerify",{"__typename":"CircuitVerify","verification_key":["vector",["unsigned char"]],"public_inputs":["vector",[["alias",["uint256_t","bin32"]]]],"proof":["vector",[["alias",["uint256_t","bin32"]]]],"settings":"ProofSystemSettings"}],["ChonkComputeVk",{"__typename":"ChonkComputeVk","circuit":"CircuitInputNoVK","use_zk_flavor":"bool"}],["ChonkStart",{"__typename":"ChonkStart","num_circuits":"unsigned int"}],["ChonkLoad",{"__typename":"ChonkLoad","circuit":"CircuitInput"}],["ChonkAccumulate",{"__typename":"ChonkAccumulate","witness":["vector",["unsigned char"]]}],["ChonkProve",{"__typename":"ChonkProve"}],["ChonkVerify",{"__typename":"ChonkVerify","proof":{"__typename":"ChonkProof","hiding_oink_proof":["vector",[["alias",["fr","bin32"]]]],"merge_proof":["vector",[["alias",["fr","bin32"]]]],"eccvm_proof":["vector",[["alias",["fr","bin32"]]]],"ipa_proof":["vector",[["alias",["fr","bin32"]]]],"joint_proof":["vector",[["alias",["fr","bin32"]]]]},"vk":["vector",["unsigned char"]]}],["ChonkVerifyFromFields",{"__typename":"ChonkVerifyFromFields","proof":["vector",[["alias",["fr","bin32"]]]],"vk":["vector",["unsigned char"]]}],["ChonkBatchVerify",{"__typename":"ChonkBatchVerify","proofs":["vector",["ChonkProof"]],"vks":["vector",[["vector",["unsigned char"]]]]}],["VkAsFields",{"__typename":"VkAsFields","verification_key":["vector",["unsigned char"]]}],["MegaVkAsFields",{"__typename":"MegaVkAsFields","verification_key":["vector",["unsigned char"]]}],["CircuitWriteSolidityVerifier",{"__typename":"CircuitWriteSolidityVerifier","verification_key":["vector",["unsigned char"]],"settings":"ProofSystemSettings"}],["ChonkCheckPrecomputedVk",{"__typename":"ChonkCheckPrecomputedVk","circuit":"CircuitInput","use_zk_flavor":"bool"}],["ChonkStats",{"__typename":"ChonkStats","circuit":"CircuitInputNoVK","include_gates_per_opcode":"bool"}],["ChonkCompressProof",{"__typename":"ChonkCompressProof","proof":"ChonkProof"}],["ChonkDecompressProof",{"__typename":"ChonkDecompressProof","compressed_proof":["vector",["unsigned char"]]}],["Poseidon2Hash",{"__typename":"Poseidon2Hash","inputs":["vector",[["alias",["fr","bin32"]]]]}],["Poseidon2Permutation",{"__typename":"Poseidon2Permutation","inputs":["array",[["alias",["fr","bin32"]],4]]}],["PedersenCommit",{"__typename":"PedersenCommit","inputs":["vector",[["alias",["fr","bin32"]]]],"hash_index":"unsigned int"}],["PedersenHash",{"__typename":"PedersenHash","inputs":["vector",[["alias",["fr","bin32"]]]],"hash_index":"unsigned int"}],["PedersenHashBuffer",{"__typename":"PedersenHashBuffer","input":["vector",["unsigned char"]],"hash_index":"unsigned int"}],["Blake2s",{"__typename":"Blake2s","data":["vector",["unsigned char"]]}],["Blake2sToField",{"__typename":"Blake2sToField","data":["vector",["unsigned char"]]}],["AesEncrypt",{"__typename":"AesEncrypt","plaintext":["vector",["unsigned char"]],"iv":["array",["unsigned char",16]],"key":["array",["unsigned char",16]],"length":"unsigned int"}],["AesDecrypt",{"__typename":"AesDecrypt","ciphertext":["vector",["unsigned char"]],"iv":["array",["unsigned char",16]],"key":["array",["unsigned char",16]],"length":"unsigned int"}],["GrumpkinMul",{"__typename":"GrumpkinMul","point":{"__typename":"GrumpkinPoint","x":["alias",["fr","bin32"]],"y":["alias",["fr","bin32"]]},"scalar":["alias",["fq","bin32"]]}],["GrumpkinAdd",{"__typename":"GrumpkinAdd","point_a":"GrumpkinPoint","point_b":"GrumpkinPoint"}],["GrumpkinBatchMul",{"__typename":"GrumpkinBatchMul","points":["vector",["GrumpkinPoint"]],"scalar":["alias",["fq","bin32"]]}],["GrumpkinGetRandomFr",{"__typename":"GrumpkinGetRandomFr","dummy":"unsigned char"}],["GrumpkinReduce512",{"__typename":"GrumpkinReduce512","input":["array",["unsigned char",64]]}],["Secp256k1Mul",{"__typename":"Secp256k1Mul","point":{"__typename":"Secp256k1Point","x":["alias",["secp256k1_fq","bin32"]],"y":["alias",["secp256k1_fq","bin32"]]},"scalar":["alias",["secp256k1_fr","bin32"]]}],["Secp256k1GetRandomFr",{"__typename":"Secp256k1GetRandomFr","dummy":"unsigned char"}],["Secp256k1Reduce512",{"__typename":"Secp256k1Reduce512","input":["array",["unsigned char",64]]}],["Bn254FrSqrt",{"__typename":"Bn254FrSqrt","input":["alias",["fr","bin32"]]}],["Bn254FqSqrt",{"__typename":"Bn254FqSqrt","input":["alias",["fq","bin32"]]}],["Bn254G1Mul",{"__typename":"Bn254G1Mul","point":{"__typename":"Bn254G1Point","x":["alias",["fq","bin32"]],"y":["alias",["fq","bin32"]]},"scalar":["alias",["fr","bin32"]]}],["Bn254G2Mul",{"__typename":"Bn254G2Mul","point":{"__typename":"Bn254G2Point","x":["array",[["alias",["fq","bin32"]],2]],"y":["array",[["alias",["fq","bin32"]],2]]},"scalar":["alias",["fr","bin32"]]}],["Bn254G1IsOnCurve",{"__typename":"Bn254G1IsOnCurve","point":"Bn254G1Point"}],["Bn254G1FromCompressed",{"__typename":"Bn254G1FromCompressed","compressed":"bin32"}],["SchnorrComputePublicKey",{"__typename":"SchnorrComputePublicKey","private_key":["alias",["fq","bin32"]]}],["SchnorrConstructSignature",{"__typename":"SchnorrConstructSignature","message":["vector",["unsigned char"]],"private_key":["alias",["fq","bin32"]]}],["SchnorrVerifySignature",{"__typename":"SchnorrVerifySignature","message":["vector",["unsigned char"]],"public_key":"GrumpkinPoint","s":"bin32","e":"bin32"}],["EcdsaSecp256k1ComputePublicKey",{"__typename":"EcdsaSecp256k1ComputePublicKey","private_key":["alias",["secp256k1_fr","bin32"]]}],["EcdsaSecp256r1ComputePublicKey",{"__typename":"EcdsaSecp256r1ComputePublicKey","private_key":["alias",["secp256r1_fr","bin32"]]}],["EcdsaSecp256k1ConstructSignature",{"__typename":"EcdsaSecp256k1ConstructSignature","message":["vector",["unsigned char"]],"private_key":["alias",["secp256k1_fr","bin32"]]}],["EcdsaSecp256r1ConstructSignature",{"__typename":"EcdsaSecp256r1ConstructSignature","message":["vector",["unsigned char"]],"private_key":["alias",["secp256r1_fr","bin32"]]}],["EcdsaSecp256k1RecoverPublicKey",{"__typename":"EcdsaSecp256k1RecoverPublicKey","message":["vector",["unsigned char"]],"r":"bin32","s":"bin32","v":"unsigned char"}],["EcdsaSecp256r1RecoverPublicKey",{"__typename":"EcdsaSecp256r1RecoverPublicKey","message":["vector",["unsigned char"]],"r":"bin32","s":"bin32","v":"unsigned char"}],["EcdsaSecp256k1VerifySignature",{"__typename":"EcdsaSecp256k1VerifySignature","message":["vector",["unsigned char"]],"public_key":"Secp256k1Point","r":"bin32","s":"bin32","v":"unsigned char"}],["EcdsaSecp256r1VerifySignature",{"__typename":"EcdsaSecp256r1VerifySignature","message":["vector",["unsigned char"]],"public_key":{"__typename":"Secp256r1Point","x":["alias",["secp256r1_fq","bin32"]],"y":["alias",["secp256r1_fq","bin32"]]},"r":"bin32","s":"bin32","v":"unsigned char"}],["SrsInitSrs",{"__typename":"SrsInitSrs","points_buf":["vector",["unsigned char"]],"num_points":"unsigned int","g2_point":["vector",["unsigned char"]]}],["ChonkBatchVerifierStart",{"__typename":"ChonkBatchVerifierStart","vks":["vector",[["vector",["unsigned char"]]]],"num_cores":"unsigned int","batch_size":"unsigned int","fifo_path":"string"}],["ChonkBatchVerifierQueue",{"__typename":"ChonkBatchVerifierQueue","request_id":"unsigned long","vk_index":"unsigned int","proof_fields":["vector",[["alias",["fr","bin32"]]]]}],["ChonkBatchVerifierStop",{"__typename":"ChonkBatchVerifierStop"}],["SrsInitGrumpkinSrs",{"__typename":"SrsInitGrumpkinSrs","points_buf":["vector",["unsigned char"]],"num_points":"unsigned int"}],["Shutdown",{"__typename":"Shutdown"}]]],"responses":["named_union",[["ErrorResponse",{"__typename":"ErrorResponse","message":"string"}],["AvmProveResponse",{"__typename":"AvmProveResponse","proof":["vector",[["alias",["fr","bin32"]]]],"stats":["vector",[{"__typename":"AvmStat","name":"string","value_ms":"unsigned long"}]]}],["AvmVerifyResponse",{"__typename":"AvmVerifyResponse","verified":"bool"}],["AvmCheckCircuitResponse",{"__typename":"AvmCheckCircuitResponse","passed":"bool","stats":["vector",["AvmStat"]]}],["CircuitProveResponse",{"__typename":"CircuitProveResponse","public_inputs":["vector",[["alias",["uint256_t","bin32"]]]],"proof":["vector",[["alias",["uint256_t","bin32"]]]],"vk":{"__typename":"CircuitComputeVkResponse","bytes":["vector",["unsigned char"]],"fields":["vector",[["alias",["uint256_t","bin32"]]]],"hash":["vector",["unsigned char"]]}}],["CircuitComputeVkResponse","CircuitComputeVkResponse"],["CircuitInfoResponse",{"__typename":"CircuitInfoResponse","num_gates":"unsigned int","num_gates_dyadic":"unsigned int","num_acir_opcodes":"unsigned int","gates_per_opcode":["vector",["unsigned int"]]}],["CircuitVerifyResponse",{"__typename":"CircuitVerifyResponse","verified":"bool"}],["ChonkComputeVkResponse",{"__typename":"ChonkComputeVkResponse","bytes":["vector",["unsigned char"]],"fields":["vector",[["alias",["fr","bin32"]]]]}],["ChonkStartResponse",{"__typename":"ChonkStartResponse"}],["ChonkLoadResponse",{"__typename":"ChonkLoadResponse"}],["ChonkAccumulateResponse",{"__typename":"ChonkAccumulateResponse"}],["ChonkProveResponse",{"__typename":"ChonkProveResponse","proof":"ChonkProof"}],["ChonkVerifyResponse",{"__typename":"ChonkVerifyResponse","valid":"bool"}],["ChonkVerifyFromFieldsResponse",{"__typename":"ChonkVerifyFromFieldsResponse","valid":"bool"}],["ChonkBatchVerifyResponse",{"__typename":"ChonkBatchVerifyResponse","valid":"bool"}],["VkAsFieldsResponse",{"__typename":"VkAsFieldsResponse","fields":["vector",[["alias",["fr","bin32"]]]]}],["MegaVkAsFieldsResponse",{"__typename":"MegaVkAsFieldsResponse","fields":["vector",[["alias",["fr","bin32"]]]]}],["CircuitWriteSolidityVerifierResponse",{"__typename":"CircuitWriteSolidityVerifierResponse","solidity_code":"string"}],["ChonkCheckPrecomputedVkResponse",{"__typename":"ChonkCheckPrecomputedVkResponse","valid":"bool","actual_vk":["vector",["unsigned char"]]}],["ChonkStatsResponse",{"__typename":"ChonkStatsResponse","acir_opcodes":"unsigned int","circuit_size":"unsigned int","gates_per_opcode":["vector",["unsigned int"]]}],["ChonkCompressProofResponse",{"__typename":"ChonkCompressProofResponse","compressed_proof":["vector",["unsigned char"]]}],["ChonkDecompressProofResponse",{"__typename":"ChonkDecompressProofResponse","proof":"ChonkProof"}],["Poseidon2HashResponse",{"__typename":"Poseidon2HashResponse","hash":["alias",["fr","bin32"]]}],["Poseidon2PermutationResponse",{"__typename":"Poseidon2PermutationResponse","outputs":["array",[["alias",["fr","bin32"]],4]]}],["PedersenCommitResponse",{"__typename":"PedersenCommitResponse","point":"GrumpkinPoint"}],["PedersenHashResponse",{"__typename":"PedersenHashResponse","hash":["alias",["fr","bin32"]]}],["PedersenHashBufferResponse",{"__typename":"PedersenHashBufferResponse","hash":["alias",["fr","bin32"]]}],["Blake2sResponse",{"__typename":"Blake2sResponse","hash":"bin32"}],["Blake2sToFieldResponse",{"__typename":"Blake2sToFieldResponse","field":["alias",["fr","bin32"]]}],["AesEncryptResponse",{"__typename":"AesEncryptResponse","ciphertext":["vector",["unsigned char"]]}],["AesDecryptResponse",{"__typename":"AesDecryptResponse","plaintext":["vector",["unsigned char"]]}],["GrumpkinMulResponse",{"__typename":"GrumpkinMulResponse","point":"GrumpkinPoint"}],["GrumpkinAddResponse",{"__typename":"GrumpkinAddResponse","point":"GrumpkinPoint"}],["GrumpkinBatchMulResponse",{"__typename":"GrumpkinBatchMulResponse","points":["vector",["GrumpkinPoint"]]}],["GrumpkinGetRandomFrResponse",{"__typename":"GrumpkinGetRandomFrResponse","value":["alias",["fr","bin32"]]}],["GrumpkinReduce512Response",{"__typename":"GrumpkinReduce512Response","value":["alias",["fr","bin32"]]}],["Secp256k1MulResponse",{"__typename":"Secp256k1MulResponse","point":"Secp256k1Point"}],["Secp256k1GetRandomFrResponse",{"__typename":"Secp256k1GetRandomFrResponse","value":["alias",["secp256k1_fr","bin32"]]}],["Secp256k1Reduce512Response",{"__typename":"Secp256k1Reduce512Response","value":["alias",["secp256k1_fr","bin32"]]}],["Bn254FrSqrtResponse",{"__typename":"Bn254FrSqrtResponse","is_square_root":"bool","value":["alias",["fr","bin32"]]}],["Bn254FqSqrtResponse",{"__typename":"Bn254FqSqrtResponse","is_square_root":"bool","value":["alias",["fq","bin32"]]}],["Bn254G1MulResponse",{"__typename":"Bn254G1MulResponse","point":"Bn254G1Point"}],["Bn254G2MulResponse",{"__typename":"Bn254G2MulResponse","point":"Bn254G2Point"}],["Bn254G1IsOnCurveResponse",{"__typename":"Bn254G1IsOnCurveResponse","is_on_curve":"bool"}],["Bn254G1FromCompressedResponse",{"__typename":"Bn254G1FromCompressedResponse","point":"Bn254G1Point"}],["SchnorrComputePublicKeyResponse",{"__typename":"SchnorrComputePublicKeyResponse","public_key":"GrumpkinPoint"}],["SchnorrConstructSignatureResponse",{"__typename":"SchnorrConstructSignatureResponse","s":"bin32","e":"bin32"}],["SchnorrVerifySignatureResponse",{"__typename":"SchnorrVerifySignatureResponse","verified":"bool"}],["EcdsaSecp256k1ComputePublicKeyResponse",{"__typename":"EcdsaSecp256k1ComputePublicKeyResponse","public_key":"Secp256k1Point"}],["EcdsaSecp256r1ComputePublicKeyResponse",{"__typename":"EcdsaSecp256r1ComputePublicKeyResponse","public_key":"Secp256r1Point"}],["EcdsaSecp256k1ConstructSignatureResponse",{"__typename":"EcdsaSecp256k1ConstructSignatureResponse","r":"bin32","s":"bin32","v":"unsigned char"}],["EcdsaSecp256r1ConstructSignatureResponse",{"__typename":"EcdsaSecp256r1ConstructSignatureResponse","r":"bin32","s":"bin32","v":"unsigned char"}],["EcdsaSecp256k1RecoverPublicKeyResponse",{"__typename":"EcdsaSecp256k1RecoverPublicKeyResponse","public_key":"Secp256k1Point"}],["EcdsaSecp256r1RecoverPublicKeyResponse",{"__typename":"EcdsaSecp256r1RecoverPublicKeyResponse","public_key":"Secp256r1Point"}],["EcdsaSecp256k1VerifySignatureResponse",{"__typename":"EcdsaSecp256k1VerifySignatureResponse","verified":"bool"}],["EcdsaSecp256r1VerifySignatureResponse",{"__typename":"EcdsaSecp256r1VerifySignatureResponse","verified":"bool"}],["SrsInitSrsResponse",{"__typename":"SrsInitSrsResponse","points_buf":["vector",["unsigned char"]]}],["ChonkBatchVerifierStartResponse",{"__typename":"ChonkBatchVerifierStartResponse"}],["ChonkBatchVerifierQueueResponse",{"__typename":"ChonkBatchVerifierQueueResponse"}],["ChonkBatchVerifierStopResponse",{"__typename":"ChonkBatchVerifierStopResponse"}],["SrsInitGrumpkinSrsResponse",{"__typename":"SrsInitGrumpkinSrsResponse","dummy":"unsigned char"}],["ShutdownResponse",{"__typename":"ShutdownResponse"}]]]} + 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_chonk_pinned_inputs.test.cpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk_pinned_inputs.test.cpp deleted file mode 100644 index 305eb0d651a9..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk_pinned_inputs.test.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "barretenberg/bbapi/bbapi_chonk.hpp" -#include "barretenberg/bbapi/bbapi_shared.hpp" -#include "barretenberg/chonk/chonk_proof.hpp" -#include "barretenberg/chonk/private_execution_steps.hpp" -#include "barretenberg/common/log.hpp" -#include "barretenberg/srs/global_crs.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace { - -class ChonkPinnedIvcInputsTest : public ::testing::Test { - protected: - static void SetUpTestSuite() { bb::srs::init_file_crs_factory(bb::srs::bb_crs_path()); } - - static std::filesystem::path find_repo_root() - { - if (const char* env = std::getenv("AZTEC_REPO_ROOT"); env != nullptr && *env != '\0') { - return std::filesystem::path{ env }; - } - return std::filesystem::weakly_canonical(std::filesystem::current_path() / "../../.."); - } - - static std::filesystem::path pinned_inputs_root() - { - if (const char* env = std::getenv("CHONK_PINNED_IVC_INPUTS_DIR"); env != nullptr && *env != '\0') { - return std::filesystem::path{ env }; - } - return find_repo_root() / "yarn-project/end-to-end/example-app-ivc-inputs-out"; - } - - static std::vector find_flow_dirs(const std::filesystem::path& inputs_root) - { - std::vector flows; - if (!std::filesystem::is_directory(inputs_root)) { - return flows; - } - for (const auto& entry : std::filesystem::directory_iterator(inputs_root)) { - if (entry.is_directory() && std::filesystem::exists(entry.path() / "ivc-inputs.msgpack")) { - flows.push_back(entry.path()); - } - } - std::sort(flows.begin(), flows.end()); - return flows; - } - - static void apply_flow_selection(std::vector& flows) - { - if (const char* filter = std::getenv("CHONK_PINNED_IVC_FLOW"); filter != nullptr && *filter != '\0') { - flows.erase(std::remove_if(flows.begin(), - flows.end(), - [filter](const auto& flow) { - return flow.filename().string().find(filter) == std::string::npos; - }), - flows.end()); - } - - const char* limit_env = std::getenv("CHONK_PINNED_IVC_FLOW_LIMIT"); - if (limit_env == nullptr || *limit_env == '\0') { - return; - } - char* end = nullptr; - const long limit = std::strtol(limit_env, &end, 10); - if (end != limit_env && *end == '\0' && limit > 0 && static_cast(limit) < flows.size()) { - flows.resize(static_cast(limit)); - } - } - - static void run_flow(const std::filesystem::path& flow_dir) - { - const std::filesystem::path inputs_path = flow_dir / "ivc-inputs.msgpack"; - info("ChonkPinnedIvcInputs: loading ", inputs_path.string()); - - auto raw_steps = bb::PrivateExecutionStepRaw::load_and_decompress(inputs_path); - ASSERT_FALSE(raw_steps.empty()) << "no execution steps in " << inputs_path; - - const auto hiding_bytecode = raw_steps.back().bytecode; - - bb::bbapi::BBApiRequest request; - request.vk_policy = bb::bbapi::VkPolicy::DEFAULT; - - bb::bbapi::ChonkStart{ .num_circuits = static_cast(raw_steps.size()) }.execute(request); - - for (auto& step : raw_steps) { - bb::bbapi::ChonkLoad{ - .circuit = { .name = std::move(step.function_name), - .bytecode = std::move(step.bytecode), - .verification_key = std::move(step.vk) } - }.execute(request); - bb::bbapi::ChonkAccumulate{ .witness = std::move(step.witness) }.execute(request); - } - - auto prove_response = bb::bbapi::ChonkProve{}.execute(request); - auto vk_response = - bb::bbapi::ChonkComputeVk{ .circuit = { .bytecode = hiding_bytecode }, .use_zk_flavor = true }.execute(); - - auto verify_response = - bb::bbapi::ChonkVerify{ .proof = std::move(prove_response.proof), .vk = std::move(vk_response.bytes) } - .execute(); - EXPECT_TRUE(verify_response.valid) << "ChonkVerify rejected " << flow_dir.filename(); - } -}; - -TEST_F(ChonkPinnedIvcInputsTest, AllPinnedFlows) -{ - const auto inputs_root = pinned_inputs_root(); - auto flows = find_flow_dirs(inputs_root); - ASSERT_FALSE(flows.empty()) << "no pinned Chonk flows under " << inputs_root - << ". Run `barretenberg/cpp/scripts/chonk_inputs.sh download` first."; - - apply_flow_selection(flows); - const char* flow_filter = std::getenv("CHONK_PINNED_IVC_FLOW"); - ASSERT_FALSE(flows.empty() && flow_filter != nullptr && *flow_filter != '\0') - << "CHONK_PINNED_IVC_FLOW='" << flow_filter << "' matched no pinned flows under " << inputs_root; - ASSERT_FALSE(flows.empty()) << "no pinned Chonk flows found under " << inputs_root; - - for (const auto& flow : flows) { - SCOPED_TRACE("flow: " + flow.filename().string()); - run_flow(flow); - } -} - -} // namespace 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 571ca7804e27..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_ecc.cpp +++ /dev/null @@ -1,137 +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 { grumpkin::g1::element(point).mul_const_time(scalar).to_affine_const_time() }; -} - -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"); - } - } - std::vector output; - output.reserve(points.size()); - for (const auto& p : points) { - output.emplace_back(grumpkin::g1::element(p).mul_const_time(scalar).to_affine_const_time()); - } - 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 { secp256k1::g1::element(point).mul_const_time(scalar).to_affine_const_time() }; -} - -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 = bb::g1::element(point).mul_const_time(scalar).to_affine_const_time(); - 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 e2f351a302f1..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::element(secp256k1::g1::one).mul_const_time(private_key).to_affine_const_time() }; -} - -EcdsaSecp256k1ConstructSignature::Response EcdsaSecp256k1ConstructSignature::execute(BB_UNUSED BBApiRequest& request) && -{ - auto pub_key = secp256k1::g1::element(secp256k1::g1::one).mul_const_time(private_key).to_affine_const_time(); - 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::element(secp256r1::g1::one).mul_const_time(private_key).to_affine_const_time() }; -} - -EcdsaSecp256r1ConstructSignature::Response EcdsaSecp256r1ConstructSignature::execute(BB_UNUSED BBApiRequest& request) && -{ - auto pub_key = secp256r1::g1::element(secp256r1::g1::one).mul_const_time(private_key).to_affine_const_time(); - 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..73eb34a9562f --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_handlers.cpp @@ -0,0 +1,505 @@ +/** + * @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_as(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_as(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_as(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) }; +} +// Schnorr signing takes a pre-derived field element. The wire keeps +// `message: vector` for layout consistency with other byte-buffer +// endpoints; callers must pass the 32-byte big-endian field encoding. +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 }; + + BB_ASSERT_EQ( + cmd.message.size(), size_t{ 32 }, "SchnorrConstructSignature: message must be 32 bytes (field element)"); + auto message_field = grumpkin::fq::serialize_from_buffer(cmd.message.data()); + auto sig = crypto::schnorr_construct_signature(message_field, key_pair); + crypto::secure_erase_bytes(&key_pair.private_key, sizeof(key_pair.private_key)); + + return { .s = field_to_wire(sig.s), .e = field_to_wire(sig.e) }; +} +wire::SchnorrVerifySignatureResponse handle_schnorr_verify_signature(BBApiRequest& /*ctx*/, + wire::SchnorrVerifySignature&& cmd) +{ + BB_ASSERT_EQ(cmd.message.size(), size_t{ 32 }, "SchnorrVerifySignature: message must be 32 bytes (field element)"); + auto message_field = grumpkin::fq::serialize_from_buffer(cmd.message.data()); + crypto::schnorr_signature sig = { field_from_wire(cmd.s), field_from_wire(cmd.e) }; + auto public_key = grumpkin_point_from_wire(cmd.public_key); + + bool result = crypto::schnorr_verify_signature(message_field, 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 f5c365a23479..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_schnorr.cpp +++ /dev/null @@ -1,35 +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::element(grumpkin::g1::one).mul_const_time(private_key).to_affine_const_time() }; -} - -SchnorrConstructSignature::Response SchnorrConstructSignature::execute(BB_UNUSED BBApiRequest& request) && -{ - grumpkin::g1::affine_element pub_key = - grumpkin::g1::element(grumpkin::g1::one).mul_const_time(private_key).to_affine_const_time(); - crypto::schnorr_key_pair key_pair = { private_key, pub_key }; - - auto sig = crypto::schnorr_construct_signature(message_field, 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) && -{ - crypto::schnorr_signature sig = { s, e }; - - bool result = crypto::schnorr_verify_signature(message_field, 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 e538bba399e7..000000000000 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_schnorr.hpp +++ /dev/null @@ -1,82 +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" - -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"; - grumpkin::fr s; - grumpkin::fr e; - SERIALIZATION_FIELDS(s, e); - bool operator==(const Response&) const = default; - }; - - grumpkin::fq message_field; - grumpkin::fr private_key; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(message_field, 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; - }; - - grumpkin::fq message_field; - grumpkin::g1::affine_element public_key; - grumpkin::fr s; - grumpkin::fr e; - Response execute(BBApiRequest& request) &&; - SERIALIZATION_FIELDS(message_field, 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..357fb9724c1e 100644 --- a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_shared.hpp +++ b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_shared.hpp @@ -3,8 +3,7 @@ * @file bbapi_shared.hpp * @brief Shared type definitions for the Barretenberg RPC API. * - * This file contains common data structures used across multiple bbapi modules, - * including circuit input types and proof system settings. + * This file contains shared state and helpers used across bbapi modules. */ #include "barretenberg/chonk/chonk.hpp" @@ -22,6 +21,8 @@ #include "barretenberg/flavor/ultra_starknet_zk_flavor.hpp" #endif #include +#include +#include #include #include @@ -53,106 +54,6 @@ enum class VkPolicy { REWRITE // Check the VK and rewrite the input file with correct VK if mismatch (for check command) }; -/** - * @struct CircuitInputNoVK - * @brief A circuit to be used in either ultrahonk or chonk verification key derivation. - */ -struct CircuitInputNoVK { - /** - * @brief Human-readable name for the circuit - * - * This name is not used for processing but serves as a debugging aid and - * provides context for circuit identification in logs and diagnostics. - */ - std::string name; - - /** - * @brief Serialized bytecode representation of the circuit - * - * Contains the ACIR program in serialized form. The format (bincode or msgpack) - * is determined by examining the first byte of the bytecode. - */ - std::vector bytecode; - - SERIALIZATION_FIELDS(name, bytecode); - bool operator==(const CircuitInputNoVK& other) const = default; -}; - -/** - * @struct CircuitInput - * @brief A circuit to be used in either ultrahonk or Chonk proving. - */ -struct CircuitInput { - /** - * @brief Human-readable name for the circuit - * - * This name is not used for processing but serves as a debugging aid and - * provides context for circuit identification in logs and diagnostics. - */ - std::string name; - - /** - * @brief Serialized bytecode representation of the circuit - * - * Contains the ACIR program in serialized form. The format (bincode or msgpack) - * is determined by examining the first byte of the bytecode. - */ - std::vector bytecode; - - /** - * @brief Verification key of the circuit. This could be derived, but it is more efficient to have it fixed ahead of - * time. As well, this guards against unexpected changes in the verification key. - */ - std::vector verification_key; - - SERIALIZATION_FIELDS(name, bytecode, verification_key); - bool operator==(const CircuitInput& other) const = default; -}; - -struct ProofSystemSettings { - /** - * @brief Optional flag to indicate if the proof should be generated with IPA accumulation (i.e. for rollup - * circuits). - */ - bool ipa_accumulation = false; - - /** - * @brief The oracle hash type to be used for the proof. - * - * This is used to determine the hash function used in the proof generation. - * Valid values are "poseidon2", "keccak", and "starknet". - */ - std::string oracle_hash_type = "poseidon2"; - - /** - * @brief Flag to disable blinding of the proof. - * Useful for cases that don't require privacy, such as when all inputs are public or zk-SNARK proofs themselves. - */ - bool disable_zk = false; - - // TODO(md): remove this once considered stable - bool optimized_solidity_verifier = false; - - SERIALIZATION_FIELDS(ipa_accumulation, oracle_hash_type, disable_zk, optimized_solidity_verifier); - bool operator==(const ProofSystemSettings& other) const = default; -}; - -/** - * @brief Convert oracle hash type string to enum for internal use - */ -enum class OracleHashType { POSEIDON2, KECCAK, STARKNET }; - -inline OracleHashType parse_oracle_hash_type(const std::string& type) -{ - if (type == "keccak") { - return OracleHashType::KECCAK; - } - if (type == "starknet") { - return OracleHashType::STARKNET; - } - return OracleHashType::POSEIDON2; // default -} - /** * @brief Convert VK policy string to enum for internal use */ @@ -195,16 +96,6 @@ struct BBApiRequest { #endif }; -/** - * @brief Error response returned when a command fails - */ -struct ErrorResponse { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ErrorResponse"; - std::string message; - SERIALIZATION_FIELDS(message); - bool operator==(const ErrorResponse&) const = default; -}; - /** * @brief Macro to set error in BBApiRequest and return default response */ @@ -214,19 +105,6 @@ struct ErrorResponse { return {}; \ } while (0) -struct Shutdown { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "Shutdown"; - struct Response { - static constexpr const char MSGPACK_SCHEMA_NAME[] = "ShutdownResponse"; - // Empty response - success indicated by no exception - void msgpack(auto&& pack_fn) { pack_fn(); } - bool operator==(const Response&) const = default; - }; - void msgpack(auto&& pack_fn) { pack_fn(); } - Response execute(const BBApiRequest&) && { return {}; } - bool operator==(const Shutdown&) const = default; -}; - /** * @brief Concatenate public inputs and proof into a complete proof for verification * @details Joins the separated public_inputs and proof portions back together. @@ -283,7 +161,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 +192,8 @@ 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) +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..166ccdaeb470 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_wire_convert.hpp @@ -0,0 +1,286 @@ +#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 / … — nominal bin32 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. The wire +// aliases are nominal C++ wrappers over the same 32 bytes, so conversions are +// just serialize_to_buffer / serialize_from_buffer at the boundary. +// --------------------------------------------------------------------------- + +inline const std::array& wire_bytes(const std::array& w) +{ + return w; +} + +template inline const std::array& wire_bytes(const Wire& w) +{ + return static_cast&>(w); +} + +template inline std::array field_to_bytes(const Field& d) +{ + std::array r{}; + Field::serialize_to_buffer(d, r.data()); + return r; +} + +template inline std::array field_to_wire(const Field& d) +{ + return field_to_bytes(d); +} + +template inline Wire field_to_wire_as(const Field& d) +{ + return Wire{ field_to_bytes(d) }; +} + +template inline Field field_from_wire(const Wire& w) +{ + return Field::serialize_from_buffer(wire_bytes(w).data()); +} + +inline Fr fr_to_wire(const bb::fr& d) +{ + return field_to_wire_as(d); +} +inline bb::fr fr_from_wire(const Fr& w) +{ + return field_from_wire(w); +} + +inline std::vector fr_vec_to_wire(const std::vector& d) +{ + std::vector 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& 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_array_to_wire(const std::array& d) +{ + std::array 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& 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_as(d.x), .y = field_to_wire_as(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_as(d.x), .y = field_to_wire_as(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 Fq2 is two fq bin32 aliases. +inline std::array fq2_to_wire(const bb::fq2& d) +{ + return { field_to_wire_as(d.c0), field_to_wire_as(d.c1) }; +} + +inline bb::fq2 fq2_from_wire(const std::array& 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_as(d.x), .y = field_to_wire_as(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_as(d.x), .y = field_to_wire_as(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_vec_to_wire(const std::vector& d) +{ + std::vector 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& w) +{ + std::vector r; + r.reserve(w.size()); + for (const auto& x : w) { + r.push_back(uint256_from_wire(x)); + } + return r; +} + +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..ccc824e76e20 100644 --- a/barretenberg/cpp/src/barretenberg/bbapi/c_bind.cpp +++ b/barretenberg/cpp/src/barretenberg/bbapi/c_bind.cpp @@ -1,41 +1,37 @@ #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 FFI 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 ipc_ffi_entry(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..79b0014bd5b4 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 -CBIND_DECL(bbapi) +// WASM-exported FFI 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(ipc_ffi_entry) 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/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/ipc_bench/CMakeLists.txt index 47ece59c46a8..dee03501e51e 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/ipc_bench/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/benchmark/ipc_bench/CMakeLists.txt @@ -1 +1,10 @@ -barretenberg_module(ipc_bench crypto_poseidon2 ipc) +barretenberg_module(ipc_bench crypto_poseidon2 ipc_runtime) + +# barretenberg_module's bench codepath only links MODULE_DEPENDENCIES to the +# exe, not to the *_bench_objects lib. Existing bench deps live under +# barretenberg/cpp/src/ which is already on -I, so they didn't notice; +# ipc_runtime lives at /ipc-runtime/cpp/ so its include directory has to be +# threaded into the bench_objects target explicitly. +if(TARGET ipc_bench_objects) + target_link_libraries(ipc_bench_objects PRIVATE ipc_runtime) +endif() 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 6a9b8afcf011..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,8 +1,9 @@ -#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/ipc/ipc_client.hpp" #include "barretenberg/serialize/msgpack_impl.hpp" +#include "ipc_runtime/ipc_client.hpp" #include #include #include @@ -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/cdb/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/cdb/CMakeLists.txt new file mode 100644 index 000000000000..e89f864fe00d --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/cdb/CMakeLists.txt @@ -0,0 +1,85 @@ +set(CDB_SCHEMA ${CMAKE_CURRENT_SOURCE_DIR}/cdb_schema.json) +set(CDB_GEN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/generated) +set(CDB_GEN_OUTPUTS + ${CDB_GEN_DIR}/cdb_ipc_client.cpp + ${CDB_GEN_DIR}/cdb_ipc_client.hpp + ${CDB_GEN_DIR}/cdb_ipc_server.hpp + ${CDB_GEN_DIR}/cdb_types.hpp + ${CDB_GEN_DIR}/ipc_codegen/msgpack_adaptor.hpp + ${CDB_GEN_DIR}/ipc_codegen/named_union.hpp + ${CDB_GEN_DIR}/ipc_codegen/schema.hpp + ${CDB_GEN_DIR}/ipc_codegen/throw.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 ${CDB_GEN_OUTPUTS} + COMMAND node --experimental-strip-types --experimental-transform-types --no-warnings + ${IPC_CODEGEN_DIR}/src/generate.ts + --schema ${CDB_SCHEMA} + --lang cpp + --out ${CDB_GEN_DIR} + --client --server + --cpp-namespace bb::cdb + --cpp-include-dir barretenberg/cdb/generated + --prefix Cdb + DEPENDS ${CDB_SCHEMA} ${IPC_CODEGEN_SRC} + COMMENT "Generating CDB IPC client + server from cdb_schema.json" + VERBATIM +) +add_custom_target(cdb_ipc_generated DEPENDS ${CDB_GEN_OUTPUTS}) + +# IPC client adapter (used by AVM simulator to talk to the TS-hosted CDB server) +add_library( + cdb_ipc_client + STATIC + ${CDB_GEN_DIR}/cdb_ipc_client.cpp + cdb_ipc_client.cpp +) +add_dependencies(cdb_ipc_client cdb_ipc_generated) +target_include_directories( + cdb_ipc_client + PUBLIC + ${CDB_GEN_DIR} +) +target_link_libraries( + cdb_ipc_client + PUBLIC + barretenberg + ipc_runtime +) + +# aztec-cdb binary (schema provider for codegen) +add_executable( + aztec-cdb + main.cpp + cli.cpp +) +add_dependencies(aztec-cdb cdb_ipc_generated) +target_include_directories( + aztec-cdb + PRIVATE + ${CDB_GEN_DIR} +) +target_link_libraries( + aztec-cdb + PRIVATE + barretenberg + ipc_runtime + env +) +if(ENABLE_STACKTRACES) + target_link_libraries( + aztec-cdb + PUBLIC + Backward::Interface + ) + target_link_options( + aztec-cdb + PRIVATE + -ldw -lelf + ) +endif() diff --git a/barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client.cpp b/barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client.cpp new file mode 100644 index 000000000000..7340dc9b7961 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client.cpp @@ -0,0 +1,105 @@ +#include "barretenberg/cdb/cdb_ipc_client.hpp" + +#include "barretenberg/serialize/msgpack.hpp" +#include "barretenberg/serialize/msgpack_impl.hpp" + +namespace bb::cdb { + +namespace { + +template std::vector serialize_to_msgpack(const T& value) +{ + msgpack::sbuffer buf; + msgpack::pack(buf, value); + return std::vector(buf.data(), buf.data() + buf.size()); +} + +template T deserialize_from_msgpack(const std::vector& bytes) +{ + auto unpacked = msgpack::unpack(reinterpret_cast(bytes.data()), bytes.size()); + T value; + unpacked.get().convert(value); + return value; +} + +template Alias field_to_wire(const avm2::FF& field) +{ + Alias wire{}; + avm2::FF::serialize_to_buffer(field, wire.data()); + return wire; +} + +template avm2::FF field_from_wire(const Alias& wire) +{ + return avm2::FF::serialize_from_buffer(wire.data()); +} + +template std::optional decode_optional_msgpack(const std::optional>& bytes) +{ + if (!bytes.has_value()) { + return std::nullopt; + } + return deserialize_from_msgpack(*bytes); +} + +} // namespace + +CdbIpcContractDB::CdbIpcContractDB(const std::string& socket_path) + : client_(std::make_unique(socket_path)) +{} + +CdbIpcContractDB::~CdbIpcContractDB() = default; + +std::optional CdbIpcContractDB::get_contract_instance(const avm2::AztecAddress& address) const +{ + auto resp = client_->get_contract_instance( + CdbGetContractInstance{ .address = field_to_wire(address), .forkId = fork_id_ }); + return decode_optional_msgpack(resp.instance); +} + +std::optional CdbIpcContractDB::get_contract_class(const avm2::ContractClassId& class_id) const +{ + auto resp = client_->get_contract_class( + CdbGetContractClass{ .classId = field_to_wire(class_id), .forkId = fork_id_ }); + return decode_optional_msgpack(resp.contractClass); +} + +std::optional CdbIpcContractDB::get_bytecode_commitment(const avm2::ContractClassId& class_id) const +{ + auto resp = client_->get_bytecode_commitment( + CdbGetBytecodeCommitment{ .classId = field_to_wire(class_id), .forkId = fork_id_ }); + return resp.commitment.has_value() ? std::optional(field_from_wire(*resp.commitment)) : std::nullopt; +} + +std::optional CdbIpcContractDB::get_debug_function_name(const avm2::AztecAddress& address, + const avm2::FunctionSelector& selector) const +{ + auto resp = + client_->get_debug_function_name(CdbGetDebugFunctionName{ .address = field_to_wire(address), + .selector = field_to_wire(selector), + .forkId = fork_id_ }); + return resp.name; +} + +void CdbIpcContractDB::add_contracts(const avm2::ContractDeploymentData& contract_deployment_data) +{ + client_->add_contracts(CdbAddContracts{ .contractDeploymentData = serialize_to_msgpack(contract_deployment_data), + .forkId = fork_id_ }); +} + +void CdbIpcContractDB::create_checkpoint() +{ + client_->create_checkpoint(CdbCreateCheckpoint{ .forkId = fork_id_ }); +} + +void CdbIpcContractDB::commit_checkpoint() +{ + client_->commit_checkpoint(CdbCommitCheckpoint{ .forkId = fork_id_ }); +} + +void CdbIpcContractDB::revert_checkpoint() +{ + client_->revert_checkpoint(CdbRevertCheckpoint{ .forkId = fork_id_ }); +} + +} // namespace bb::cdb diff --git a/barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client.hpp b/barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client.hpp new file mode 100644 index 000000000000..1b024a7e0c7c --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client.hpp @@ -0,0 +1,45 @@ +#pragma once +/** + * @brief ContractDBInterface adapter over the generated CDB IPC client. + * + * Translates ContractDBInterface calls (AVM domain types) into + * CdbIpcClient command structs, delegating IPC transport to the + * auto-generated client. + */ + +#include "barretenberg/cdb/generated/cdb_ipc_client.hpp" +#include "barretenberg/vm2/simulation/interfaces/db.hpp" + +#include +#include + +namespace bb::cdb { + +class CdbIpcContractDB final : public avm2::simulation::ContractDBInterface { + public: + explicit CdbIpcContractDB(const std::string& socket_path); + ~CdbIpcContractDB() override; + + /** Set the fork ID used to route CDB requests to the correct PublicContractsDB. */ + void set_fork_id(uint64_t fork_id) { fork_id_ = fork_id; } + uint64_t get_fork_id() const { return fork_id_; } + + // ContractDBInterface implementation + std::optional get_contract_instance(const avm2::AztecAddress& address) const override; + std::optional get_contract_class(const avm2::ContractClassId& class_id) const override; + std::optional get_bytecode_commitment(const avm2::ContractClassId& class_id) const override; + std::optional get_debug_function_name(const avm2::AztecAddress& address, + const avm2::FunctionSelector& selector) const override; + + void add_contracts(const avm2::ContractDeploymentData& contract_deployment_data) override; + + void create_checkpoint() override; + void commit_checkpoint() override; + void revert_checkpoint() override; + + private: + std::unique_ptr client_; + uint64_t fork_id_ = 0; +}; + +} // namespace bb::cdb diff --git a/barretenberg/cpp/src/barretenberg/cdb/cdb_schema.json b/barretenberg/cpp/src/barretenberg/cdb/cdb_schema.json new file mode 100644 index 000000000000..ad9d124114b2 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/cdb/cdb_schema.json @@ -0,0 +1,145 @@ +{ + "commands": [ + "named_union", + [ + [ + "CdbGetContractInstance", + { + "__typename": "CdbGetContractInstance", + "address": ["alias", ["AztecAddress", "bin32"]], + "forkId": "unsigned long" + } + ], + [ + "CdbGetContractClass", + { + "__typename": "CdbGetContractClass", + "classId": ["alias", ["ContractClassId", "bin32"]], + "forkId": "unsigned long" + } + ], + [ + "CdbGetBytecodeCommitment", + { + "__typename": "CdbGetBytecodeCommitment", + "classId": ["alias", ["ContractClassId", "bin32"]], + "forkId": "unsigned long" + } + ], + [ + "CdbGetDebugFunctionName", + { + "__typename": "CdbGetDebugFunctionName", + "address": ["alias", ["AztecAddress", "bin32"]], + "selector": ["alias", ["FunctionSelector", "bin32"]], + "forkId": "unsigned long" + } + ], + [ + "CdbAddContracts", + { + "__typename": "CdbAddContracts", + "contractDeploymentData": ["vector", ["unsigned char"]], + "forkId": "unsigned long" + } + ], + [ + "CdbCreateCheckpoint", + { + "__typename": "CdbCreateCheckpoint", + "forkId": "unsigned long" + } + ], + [ + "CdbCommitCheckpoint", + { + "__typename": "CdbCommitCheckpoint", + "forkId": "unsigned long" + } + ], + [ + "CdbRevertCheckpoint", + { + "__typename": "CdbRevertCheckpoint", + "forkId": "unsigned long" + } + ], + [ + "CdbShutdown", + { + "__typename": "CdbShutdown" + } + ] + ] + ], + "responses": [ + "named_union", + [ + [ + "CdbErrorResponse", + { + "__typename": "CdbErrorResponse", + "message": "string" + } + ], + [ + "CdbGetContractInstanceResponse", + { + "__typename": "CdbGetContractInstanceResponse", + "instance": ["optional", [["vector", ["unsigned char"]]]] + } + ], + [ + "CdbGetContractClassResponse", + { + "__typename": "CdbGetContractClassResponse", + "contractClass": ["optional", [["vector", ["unsigned char"]]]] + } + ], + [ + "CdbGetBytecodeCommitmentResponse", + { + "__typename": "CdbGetBytecodeCommitmentResponse", + "commitment": ["optional", [["alias", ["Fr", "bin32"]]]] + } + ], + [ + "CdbGetDebugFunctionNameResponse", + { + "__typename": "CdbGetDebugFunctionNameResponse", + "name": ["optional", ["string"]] + } + ], + [ + "CdbAddContractsResponse", + { + "__typename": "CdbAddContractsResponse" + } + ], + [ + "CdbCreateCheckpointResponse", + { + "__typename": "CdbCreateCheckpointResponse" + } + ], + [ + "CdbCommitCheckpointResponse", + { + "__typename": "CdbCommitCheckpointResponse" + } + ], + [ + "CdbRevertCheckpointResponse", + { + "__typename": "CdbRevertCheckpointResponse" + } + ], + [ + "CdbShutdownResponse", + { + "__typename": "CdbShutdownResponse" + } + ] + ] + ] +} diff --git a/barretenberg/cpp/src/barretenberg/cdb/cli.cpp b/barretenberg/cpp/src/barretenberg/cdb/cli.cpp new file mode 100644 index 000000000000..f5a90ad70636 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/cdb/cli.cpp @@ -0,0 +1,44 @@ +#include "barretenberg/cdb/cli.hpp" +#include "barretenberg/cdb/generated/cdb_ipc_server.hpp" + +#include "barretenberg/bb/deps/cli11.hpp" +#include +#include + +namespace bb::cdb { + +int parse_and_run_cdb(int argc, char* argv[]) +{ + CLI::App app{ "aztec-cdb: Contract database schema provider" }; + app.require_subcommand(1); + + // ----------------------------------------------------------------------- + // Subcommand: msgpack + // ----------------------------------------------------------------------- + CLI::App* msgpack_command = app.add_subcommand("msgpack", "Msgpack API interface."); + + // msgpack schema + CLI::App* msgpack_schema_command = + msgpack_command->add_subcommand("schema", "Output a msgpack schema encoded as JSON to stdout."); + + // Parse CLI + try { + app.parse(argc, argv); + } catch (const CLI::ParseError& e) { + return app.exit(e); + } + + try { + if (msgpack_schema_command->parsed()) { + std::cout << get_cdb_schema_as_json() << std::endl; + return 0; + } + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << '\n'; + return 1; + } + + return 0; +} + +} // namespace bb::cdb diff --git a/barretenberg/cpp/src/barretenberg/cdb/cli.hpp b/barretenberg/cpp/src/barretenberg/cdb/cli.hpp new file mode 100644 index 000000000000..731b46e38ba4 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/cdb/cli.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace bb::cdb { + +int parse_and_run_cdb(int argc, char* argv[]); + +} // namespace bb::cdb diff --git a/barretenberg/cpp/src/barretenberg/cdb/main.cpp b/barretenberg/cpp/src/barretenberg/cdb/main.cpp new file mode 100644 index 000000000000..412c538eb1db --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/cdb/main.cpp @@ -0,0 +1,6 @@ +#include "barretenberg/cdb/cli.hpp" + +int main(int argc, char* argv[]) +{ + return bb::cdb::parse_and_run_cdb(argc, argv); +} diff --git a/barretenberg/cpp/src/barretenberg/common/try_catch_shim.hpp b/barretenberg/cpp/src/barretenberg/common/try_catch_shim.hpp index 2cb08e6f2225..b1273b072ab7 100644 --- a/barretenberg/cpp/src/barretenberg/common/try_catch_shim.hpp +++ b/barretenberg/cpp/src/barretenberg/common/try_catch_shim.hpp @@ -3,7 +3,14 @@ #include // Tool to make header only libraries (i.e. CLI11 and msgpack, though it has a bundled copy) -// not use exceptions with minimally invaslive changes +// not use exceptions with minimally invaslive changes. +// +// Macros are guarded so any parent project (e.g. ipc_codegen/throw.hpp under +// codegen-emitted code) that predefines them wins. Same convention as +// ipc_codegen/throw.hpp, so the two headers can be #included in any order +// without redefinition warnings. + +#ifndef THROW #ifdef BB_NO_EXCEPTIONS struct __AbortStream { @@ -21,3 +28,5 @@ struct __AbortStream { #define THROW throw #define RETHROW THROW #endif + +#endif // THROW diff --git a/barretenberg/cpp/src/barretenberg/ipc/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/ipc/CMakeLists.txt deleted file mode 100644 index 99e29c91e898..000000000000 --- a/barretenberg/cpp/src/barretenberg/ipc/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -barretenberg_module(ipc common) -set_target_properties(ipc_objects PROPERTIES POSITION_INDEPENDENT_CODE ON) diff --git a/barretenberg/cpp/src/barretenberg/ipc/README.md b/barretenberg/cpp/src/barretenberg/ipc/README.md deleted file mode 100644 index 387f0efa27c0..000000000000 --- a/barretenberg/cpp/src/barretenberg/ipc/README.md +++ /dev/null @@ -1,389 +0,0 @@ -# Barretenberg IPC Module - -Modern C++ inter-process communication (IPC) library providing unified abstractions over multiple transport mechanisms. Designed for high-performance request/response patterns between processes. - -## Overview - -The IPC module provides: -- **Abstract interfaces** (`IpcClient`, `IpcServer`) for transport-independent code -- **Unix domain sockets** transport for simplicity and broad compatibility -- **Shared memory** transport for ultra-low latency (sub-microsecond) -- **Factory pattern** for easy instantiation -- **Multi-client support** with dynamic capacity (sockets) or fixed capacity (shared memory) -- **Built-in server loop** with graceful shutdown support - -## Architecture - -``` -┌─────────────────────────────────────────────────┐ -│ Abstract Interfaces │ -│ IpcClient IpcServer │ -│ - connect() - listen() │ -│ - send() - wait_for_data() │ -│ - recv() - receive() / release() │ -│ - close() - send() / close() │ -│ - run(handler) │ -└──────────────┬─────────────────┬────────────────┘ - │ │ - ┌───────┴────────┐ ┌──────┴──────────┐ - │ Socket │ │ Shared Memory │ - │ Implementation │ │ Implementation │ - └────────────────┘ └─────────────────┘ -``` - -### Transport Implementations - -#### 1. Unix Domain Sockets (`SocketClient` / `SocketServer`) - -**Architecture:** -- Standard POSIX socket API with epoll for multi-client handling -- Direct implementation (no wrapper layers) -- Dynamic client capacity with O(1) client lookup -- Requires system calls for each message - -**Use when:** -- Simplicity and compatibility are priorities -- Latency requirements are moderate (5-15 µs) -- Need unlimited dynamic client capacity -- Running in environments without shared memory support - -**Example:** -```cpp -#include "barretenberg/ipc/ipc_server.hpp" -#include "barretenberg/ipc/ipc_client.hpp" - -// Server process -auto server = IpcServer::create_socket("/tmp/my.sock", 10); // max 10 initial clients -server->listen(); - -server->run([](int client_id, std::span request) { - // Process request, return response - std::vector response = process(request); - return response; -}); - -// Client process -auto client = IpcClient::create_socket("/tmp/my.sock"); -client->connect(); - -std::vector request = {...}; -client->send(request.data(), request.size()); - -std::vector response(1024); -ssize_t n = client->recv(response.data(), response.size()); -``` - -#### 2. Shared Memory (`ShmClient` / `ShmServer`) - -**Architecture:** -- Lock-free SPSC/MPSC ring buffers (see `shm/README.md` for details) -- Requests: MPSC (multi-producer single-consumer) ring -- Responses: Dedicated SPSC ring per client -- Adaptive spin + futex for efficient blocking -- Fixed client capacity (set at server creation) - -**Use when:** -- Ultra-low latency is critical (0.3-1 µs hot, 3-6 µs cold) -- Number of clients is known and fixed -- Linux/POSIX shared memory available -- Zero-copy message passing desired - -**Example:** -```cpp -#include "barretenberg/ipc/ipc_server.hpp" -#include "barretenberg/ipc/ipc_client.hpp" - -// Server process -auto server = IpcServer::create_shm("my_shm", 4); // exactly 4 clients -server->listen(); - -server->run([](int client_id, std::span request) { - std::vector response = process(request); - return response; -}); - -// Client process (run 4 instances) -auto client = IpcClient::create_shm("my_shm", 4); -client->connect(); // Atomically claims client_id 0, 1, 2, or 3 - -std::vector request = {...}; -client->send(request.data(), request.size()); - -std::vector response(1024); -ssize_t n = client->recv(response.data(), response.size()); -``` - -## API Reference - -### IpcClient Interface - -```cpp -class IpcClient { -public: - virtual ~IpcClient() = default; - - // Connect to server - virtual bool connect() = 0; - - // Send request to server - virtual bool send(const void* data, size_t len, uint64_t timeout_ns = 0) = 0; - - // Receive response from server - virtual ssize_t recv(void* buffer, size_t max_len, uint64_t timeout_ns = 0) = 0; - - // Close connection - virtual void close() = 0; - - // Factory methods - static std::unique_ptr create_socket(const std::string& socket_path); - static std::unique_ptr create_shm(const std::string& base_name, size_t max_clients); -}; -``` - -### IpcServer Interface - -```cpp -class IpcServer { -public: - using Handler = std::function(int client_id, std::span request)>; - - virtual ~IpcServer() = default; - - // Start listening for connections - virtual bool listen() = 0; - - // Wait for data from any client (spins then blocks, returns client_id) - virtual int wait_for_data(uint64_t spin_ns) = 0; - - // Receive next message (blocks until complete, zero-copy for SHM) - virtual std::span receive(int client_id) = 0; - - // Release/consume the received message - virtual void release(int client_id, size_t message_size) = 0; - - // Send to specific client - virtual bool send(int client_id, const void* data, size_t len) = 0; - - // Close server - virtual void close() = 0; - - // High-level event loop with handler (uses peek/release internally) - virtual void run(Handler handler); - - // Factory methods - static std::unique_ptr create_socket(const std::string& socket_path, int max_clients); - static std::unique_ptr create_shm(const std::string& base_name, size_t max_clients, - size_t request_ring_size = 1MB, size_t response_ring_size = 1MB); -}; -``` - -The receive/release pattern ensures: -- **Zero-copy for SHM**: `receive()` returns a span pointing directly into the ring buffer -- **No message loss**: Both implementations guarantee complete messages; incomplete = corruption -- **Explicit lifecycle**: Messages are only consumed when `release()` is explicitly called with size -- **Semantic equivalence**: Both socket and SHM implementations block until complete message available - -### Graceful Shutdown - -The server's `run()` method supports graceful shutdown via exception: - -```cpp -#include "barretenberg/ipc/ipc_server.hpp" - -server->run([](int client_id, std::span request) { - if (is_shutdown_request(request)) { - std::vector goodbye = encode_goodbye(); - throw ShutdownRequested(goodbye); // Sends response, then exits cleanly - } - return process_normal_request(request); -}); -// Destructors run here, cleaning up all resources -``` - -## Performance Comparison - -### Latency (Round-trip time) - -| Transport | Hot Path | Cold Path | Notes | -|----------------|----------------|--------------|--------------------------------| -| Sockets | 6-15 µs | 10-20 µs | Requires syscalls per message | -| Shared Memory | 0.3-1 µs | 3-6 µs | Zero-copy, adaptive spin+futex | - -### Throughput - -| Transport | Single Client | Multi-Client (3) | Notes | -|----------------|----------------|------------------|--------------------------| -| Sockets | ~150K msgs/s | ~120K msgs/s | Epoll scales well | -| Shared Memory | ~1M msgs/s | ~700K msgs/s | Lock-free, per-client queues | - -*Benchmarks measured on AMD Ryzen 9 5950X, small messages (<1KB)* - -## Implementation Details - -### Socket Transport - -**SocketClient:** -- Direct socket file descriptor management -- RAII cleanup (close on destruction) -- Blocking I/O with optional timeout -- Length-prefixed messages (4-byte header) - -**SocketServer:** -- Epoll for efficient multi-client event handling -- Dynamic client table (grows on demand) -- O(1) fd→client_id lookup via `std::unordered_map` -- Automatic cleanup on client disconnect - -### Shared Memory Transport - -**ShmClient:** -- Atomically claims client ID from shared counter -- Connects to MPSC ring (producer role) for requests -- Connects to dedicated SPSC ring (consumer role) for responses -- Length-prefixed messages (4-byte header) matching socket behavior - -**ShmServer:** -- Creates MPSC consumer for receiving requests from all clients -- Pre-creates SPSC rings for each client's responses -- Round-robin polling across client rings -- Shared doorbell futex for efficient wakeup - -For deep dive into shared memory ring buffer architecture, see [`shm/README.md`](shm/README.md). - -## Build Integration - -The IPC module is included in Barretenberg's main CMake build: - -```cmake -# CMakeLists.txt -add_library(ipc - ipc_client.cpp - ipc_server.cpp - socket_client.cpp - socket_server.cpp - # shm implementations are header-only -) - -target_link_libraries(ipc - PRIVATE pthread # For futex operations - PRIVATE rt # For shm_open/shm_unlink -) -``` - -## Testing - -Run IPC tests with: - -```bash -# From barretenberg/cpp -cd build-no-avm -ninja ipc_bench # Performance benchmarks -./bin/ipc_bench -``` - -The benchmark compares socket vs shared memory performance with single and multiple clients. - -## Best Practices - -### Choosing a Transport - -**Use sockets when:** -- You need dynamic client capacity -- Compatibility and simplicity are priorities -- Moderate latency (10-15 µs) is acceptable -- You're crossing network boundaries (with TCP sockets) - -**Use shared memory when:** -- Ultra-low latency (<1 µs) is critical -- Number of clients is known and fixed -- Running on same machine with POSIX shared memory -- You want zero-copy message passing - -### Error Handling - -All operations return bool (success) or -1 (error) for easy checking: - -```cpp -if (!client->connect()) { - // Handle connection failure -} - -if (!client->send(data, len)) { - // Handle send failure -} - -ssize_t n = client->recv(buffer, size); -if (n < 0) { - // Handle receive failure/timeout -} -``` - -### Resource Management - -Both transports use RAII for automatic cleanup: - -```cpp -{ - auto server = IpcServer::create_socket("/tmp/my.sock", 10); - server->listen(); - // Use server... -} // Destructor closes connections and cleans up resources -``` - -For shared memory, the server should explicitly unlink on clean shutdown: - -```cpp -auto server = IpcServer::create_shm("my_shm", 4); -server->listen(); -server->run(handler); -// Destructor automatically calls unlink to remove shared memory objects -``` - -### Message Framing - -Both transports use 4-byte length prefixes for messages: - -``` -┌────────────┬──────────────────┐ -│ Length │ Payload │ -│ (4 bytes) │ (Length bytes) │ -└────────────┴──────────────────┘ -``` - -This is handled automatically by the implementations. - -### Threading Model - -- **Socket server:** Single-threaded event loop with epoll -- **Socket client:** Thread-safe (each client is independent) -- **Shared memory server:** Single-threaded consumer (by design) -- **Shared memory client:** Lock-free producer (multiple clients can send concurrently) - -## Limitations - -### Sockets -- System call overhead (cannot eliminate) -- Buffer copying on send (recv is internal buffer, minimal copy) -- File descriptor limits (ulimit) - -### Shared Memory -- Fixed client capacity (must specify at creation) -- Linux-specific (uses futex, though portable to other POSIX systems) -- Requires cleanup of /dev/shm objects (automatic on destruction) -- No security boundaries (all clients can access all memory) - -## Future Enhancements - -Potential improvements for future versions: - -- [ ] Zero-copy socket option (SCM_RIGHTS / vmsplice) -- [ ] Configurable ring buffer sizes for shared memory -- [ ] Metrics/telemetry API (latency histograms, throughput) -- [ ] Windows named pipes support -- [ ] Cross-platform shared memory abstraction (Windows shared memory objects) - -## See Also - -- [`shm/README.md`](shm/README.md) - Deep dive into lock-free ring buffer implementation -- [`shm.test.cpp`](shm.test.cpp) - Comprehensive test suite -- Benchmarks in `barretenberg/cpp/build-no-avm/bin/ipc_bench` diff --git a/barretenberg/cpp/src/barretenberg/ipc/ipc_server.hpp b/barretenberg/cpp/src/barretenberg/ipc/ipc_server.hpp deleted file mode 100644 index 0684ebeea898..000000000000 --- a/barretenberg/cpp/src/barretenberg/ipc/ipc_server.hpp +++ /dev/null @@ -1,214 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace bb::ipc { - -/** - * @brief Exception thrown by handler to signal graceful shutdown - * - * Carries the response data to be sent before shutting down. - */ -class ShutdownRequested : public std::exception { - std::vector response_; - - public: - explicit ShutdownRequested(std::vector response) - : response_(std::move(response)) - {} - const std::vector& response() const { return response_; } - const char* what() const noexcept override { return "Server shutdown requested"; } -}; - -/** - * @brief Abstract interface for IPC server - * - * Provides a unified interface for accepting client connections and exchanging messages. - * Implementations handle transport-specific details (Unix domain sockets, shared memory, etc). - */ -class IpcServer { - public: - IpcServer() = default; - virtual ~IpcServer() = default; - - // Abstract interface - no copy or move - IpcServer(const IpcServer&) = delete; - IpcServer& operator=(const IpcServer&) = delete; - IpcServer(IpcServer&&) = delete; - IpcServer& operator=(IpcServer&&) = delete; - - /** - * @brief Start listening for client connections - * @return true if successful, false otherwise - */ - virtual bool listen() = 0; - - /** - * @brief Wait for data from any connected client - * - * @param timeout_ns Maximum time to wait in nanoseconds (0 = non-blocking poll) - * @return Client ID that has data available, or -1 on timeout/error - */ - virtual int wait_for_data(uint64_t timeout_ns) = 0; - - /** - * @brief Receive next message from a specific client - * - * Blocks until a complete message is available. Returns a span pointing to the message data. - * For shared memory, this is a zero-copy view directly into the ring buffer. - * For sockets, this is a view into an internal buffer. - * - * The message remains valid until release() is called with the message size. - * - * @param client_id Client to receive from - * @return Span of message data (empty only on error/disconnect) - */ - virtual std::span receive(int client_id) = 0; - - /** - * @brief Release/consume the previously received message - * - * Must be called after receive() to advance to the next message. - * For shared memory, this releases space in the ring buffer. - * For sockets, this is a no-op (message already consumed during receive). - * - * @param client_id Client whose message to release - * @param message_size Size of the message being released (from span.size()) - */ - virtual void release(int client_id, size_t message_size) = 0; - - /** - * @brief Send a message to a specific client - * @param client_id Client to send to - * @param data Pointer to message data - * @param len Length of message in bytes - * @return true if sent successfully, false on error - */ - virtual bool send(int client_id, const void* data, size_t len) = 0; - - /** - * @brief Close the server and all client connections - */ - virtual void close() = 0; - - /** - * @brief Request graceful shutdown. - * - * Sets shutdown flag and wakes all blocked threads. Safe to call from signal handlers. - * After this returns, the run() loop will exit on its next iteration. - * Call close() afterward to clean up resources. - */ - virtual void request_shutdown() - { - shutdown_requested_.store(true, std::memory_order_release); - wakeup_all(); - } - - /** - * @brief High-level request handler function type - * - * Takes client_id and request data, returns response data. - * Return empty vector to skip sending a response. - */ - using Handler = std::function(int client_id, std::span request)>; - - /** - * @brief Accept a new client connection (optional for some transports) - * @param timeout_ns Timeout in nanoseconds (0 = non-blocking, <0 = infinite) - * @return Client ID if successful, -1 if no pending connection or error - * - * Note: Some transports (like shared memory) may not need explicit accept calls. - */ - virtual int accept() { return -1; } - - /** - * @brief Run server event loop with handler - * - * Continuously waits for client requests and invokes handler. - * Handler is responsible for deserializing request, processing, and serializing response. - * This is a convenience method that encapsulates the typical server loop. - * - * Uses peek/release pattern: - * - peek() returns a span (zero-copy for SHM, internal buffer for sockets) - * - handler processes the request - * - release() explicitly consumes the message - * - * This design ensures no messages are lost and enables zero-copy for shared memory. - * - * Server exits gracefully when handler throws ShutdownRequested exception. - * - * @param handler Function to process requests and generate responses - */ - virtual void run(const Handler& handler) - { - while (!shutdown_requested_.load(std::memory_order_acquire)) { - // Try to accept new clients (non-blocking for socket servers) - accept(); - - int client_id = wait_for_data(100000000); // 100ms timeout - if (client_id < 0) { - // Timeout or error - check shutdown flag on next iteration - continue; - } - - // Receive message (blocks until complete message available, zero-copy for SHM) - auto request = receive(client_id); - if (request.empty()) { - continue; - } - - try { - auto response = handler(client_id, request); - if (!response.empty()) { - send(client_id, response.data(), response.size()); - } - - // Explicitly release/consume the message - release(client_id, request.size()); - } catch (const ShutdownRequested& shutdown) { - // Release message before shutting down - release(client_id, request.size()); - - // Send final response before shutting down - if (!shutdown.response().empty()) { - send(client_id, shutdown.response().data(), shutdown.response().size()); - } - // Graceful shutdown - exit loop and let destructors run - return; - } - } - } - - // Factory methods - static std::unique_ptr create_socket(const std::string& socket_path, int max_clients); - static std::unique_ptr create_shm(const std::string& base_name, - size_t request_ring_size = static_cast(1024 * 1024), - size_t response_ring_size = static_cast(1024 * 1024)); - static std::unique_ptr create_mpsc_shm(const std::string& base_name, - size_t max_clients, - size_t request_ring_size = static_cast(1024 * 1024), - size_t response_ring_size = static_cast(1024 * 1024)); - - protected: - std::atomic shutdown_requested_{ false }; - - /** - * @brief Wake all blocked threads (for graceful shutdown) - * - * Wakes any threads blocked in wait_for_data() or other blocking operations. - * Used by signal handlers to trigger graceful shutdown without waiting for timeouts. - */ - virtual void wakeup_all() {}; -}; - -} // namespace bb::ipc diff --git a/barretenberg/cpp/src/barretenberg/kvdb/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/kvdb/CMakeLists.txt new file mode 100644 index 000000000000..6a18fad3e561 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/kvdb/CMakeLists.txt @@ -0,0 +1,68 @@ +set(KVDB_SCHEMA ${CMAKE_CURRENT_SOURCE_DIR}/kvdb_schema.json) +set(KVDB_GEN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/generated) +set(KVDB_GEN_OUTPUTS + ${KVDB_GEN_DIR}/kvdb_ipc_client.cpp + ${KVDB_GEN_DIR}/kvdb_ipc_client.hpp + ${KVDB_GEN_DIR}/kvdb_ipc_server.hpp + ${KVDB_GEN_DIR}/kvdb_types.hpp + ${KVDB_GEN_DIR}/ipc_codegen/msgpack_adaptor.hpp + ${KVDB_GEN_DIR}/ipc_codegen/named_union.hpp + ${KVDB_GEN_DIR}/ipc_codegen/schema.hpp + ${KVDB_GEN_DIR}/ipc_codegen/throw.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 ${KVDB_GEN_OUTPUTS} + COMMAND node --experimental-strip-types --experimental-transform-types --no-warnings + ${IPC_CODEGEN_DIR}/src/generate.ts + --schema ${KVDB_SCHEMA} + --lang cpp + --out ${KVDB_GEN_DIR} + --client --server + --cpp-namespace bb::kvdb + --cpp-include-dir barretenberg/kvdb/generated + --prefix Kvdb + DEPENDS ${KVDB_SCHEMA} ${IPC_CODEGEN_SRC} + COMMENT "Generating KVDB IPC client + server from kvdb_schema.json" + VERBATIM +) +add_custom_target(kvdb_ipc_generated DEPENDS ${KVDB_GEN_OUTPUTS}) + +# aztec-kvdb binary (standalone LMDB-backed key-value store server). Used by +# archiver, p2p, pxe, slasher, and validator-ha-signer via the @aztec/kv-store +# `lmdb-v2` backend. Communicates with the TS host over UDS or MPSC SHM. +add_executable( + aztec-kvdb + main.cpp + cli.cpp + kvdb_ipc_server.cpp +) +add_dependencies(aztec-kvdb kvdb_ipc_generated) +target_include_directories( + aztec-kvdb + PRIVATE + ${KVDB_GEN_DIR} +) +target_link_libraries( + aztec-kvdb + PRIVATE + lmdblib + ipc_runtime + env +) +if(ENABLE_STACKTRACES) + target_link_libraries( + aztec-kvdb + PUBLIC + Backward::Interface + ) + target_link_options( + aztec-kvdb + PRIVATE + -ldw -lelf + ) +endif() diff --git a/barretenberg/cpp/src/barretenberg/kvdb/cli.cpp b/barretenberg/cpp/src/barretenberg/kvdb/cli.cpp new file mode 100644 index 000000000000..ed33f577b641 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/kvdb/cli.cpp @@ -0,0 +1,75 @@ +#include "barretenberg/kvdb/cli.hpp" + +#include "barretenberg/bb/deps/cli11.hpp" +#include "barretenberg/kvdb/generated/kvdb_ipc_server.hpp" +#include "barretenberg/kvdb/kvdb_ipc_server.hpp" + +#include +#include +#include + +namespace bb::kvdb { + +int parse_and_run_kvdb(int argc, char* argv[]) +{ + CLI::App app{ "aztec-kvdb: Standalone LMDB-backed key-value store server" }; + app.require_subcommand(1); + + CLI::App* msgpack_command = app.add_subcommand("msgpack", "Msgpack API interface."); + CLI::App* msgpack_schema_command = + msgpack_command->add_subcommand("schema", "Output a msgpack schema encoded as JSON to stdout."); + CLI::App* msgpack_run_command = msgpack_command->add_subcommand("run", "Start the kvdb IPC server."); + + std::string input_path; + msgpack_run_command->add_option( + "-i,--input", input_path, "IPC socket/shm path (.sock for UDS, .shm for shared memory)"); + + std::string data_dir; + msgpack_run_command->add_option("-d,--data-dir", data_dir, "Data directory for LMDB store")->required(); + + uint64_t map_size_bytes = 1024UL * 1024; + msgpack_run_command->add_option("--map-size", map_size_bytes, "LMDB map size in bytes (default: 1 MiB)") + ->check(CLI::PositiveNumber); + + uint32_t max_readers = 16; + msgpack_run_command->add_option("--max-readers", max_readers, "LMDB max readers (default: 16)") + ->check(CLI::PositiveNumber); + + size_t request_ring_size = 1024UL * 1024; + msgpack_run_command + ->add_option( + "--request-ring-size", request_ring_size, "Request ring buffer size for shared memory IPC (default: 1MB)") + ->check(CLI::PositiveNumber); + + size_t response_ring_size = 1024UL * 1024; + msgpack_run_command + ->add_option("--response-ring-size", + response_ring_size, + "Response ring buffer size for shared memory IPC (default: 1MB)") + ->check(CLI::PositiveNumber); + + try { + app.parse(argc, argv); + } catch (const CLI::ParseError& e) { + return app.exit(e); + } + + try { + if (msgpack_schema_command->parsed()) { + std::cout << get_kvdb_schema_as_json() << std::endl; + return 0; + } + + if (msgpack_run_command->parsed()) { + return execute_kvdb_server( + input_path, data_dir, map_size_bytes, max_readers, request_ring_size, response_ring_size); + } + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << '\n'; + return 1; + } + + return 0; +} + +} // namespace bb::kvdb diff --git a/barretenberg/cpp/src/barretenberg/kvdb/cli.hpp b/barretenberg/cpp/src/barretenberg/kvdb/cli.hpp new file mode 100644 index 000000000000..f2d7c1da5992 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/kvdb/cli.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace bb::kvdb { + +int parse_and_run_kvdb(int argc, char* argv[]); + +} // namespace bb::kvdb diff --git a/barretenberg/cpp/src/barretenberg/kvdb/kvdb_ipc_server.cpp b/barretenberg/cpp/src/barretenberg/kvdb/kvdb_ipc_server.cpp new file mode 100644 index 000000000000..3447b4731509 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/kvdb/kvdb_ipc_server.cpp @@ -0,0 +1,337 @@ +#include "barretenberg/kvdb/kvdb_ipc_server.hpp" + +#include "barretenberg/common/try_catch_shim.hpp" +// clang-format off +#include "barretenberg/lmdblib/lmdb_store.hpp" +#include "barretenberg/kvdb/generated/kvdb_types.hpp" +// clang-format on +#include "ipc_runtime/ipc_server.hpp" +#include "ipc_runtime/serve_helper.hpp" +#include "ipc_runtime/signal_handlers.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bb::kvdb { + +namespace { + +constexpr uint32_t DEFAULT_CURSOR_PAGE_SIZE = 10; + +struct CursorState { + lmdblib::LMDBCursor::SharedPtr cursor; + bool reverse; +}; + +class KvdbServer { + public: + explicit KvdbServer(lmdblib::LMDBStore& store) + : store_(store) + {} + + wire::KvdbOpenDatabaseResponse open_database(const wire::KvdbOpenDatabase& req) + { + store_.open_database(req.db, !req.uniqueKeys.value_or(true)); + return { true }; + } + + wire::KvdbGetResponse get(const wire::KvdbGet& req) + { + lmdblib::OptionalValuesVector vals; + lmdblib::KeysVector keys = req.keys; + store_.get(keys, vals, req.db); + return { std::move(vals) }; + } + + wire::KvdbHasResponse has(const wire::KvdbHas& req) + { + std::vector exists; + store_.has(key_optional_values_from_wire(req.entries), exists, req.db); + return { std::move(exists) }; + } + + wire::KvdbStartCursorResponse start_cursor(const wire::KvdbStartCursor& req) + { + bool reverse = req.reverse.value_or(false); + uint32_t page_size = req.count.value_or(DEFAULT_CURSOR_PAGE_SIZE); + bool one_page = req.onePage.value_or(false); + lmdblib::Key key = req.key; + + auto tx = store_.create_shared_read_transaction(); + lmdblib::LMDBCursor::SharedPtr cursor = store_.create_cursor(tx, req.db); + bool start_ok = cursor->set_at_key(key); + if (!start_ok) { + start_ok = cursor->set_at_key_gte(key); + if (start_ok && reverse) { + lmdblib::KeyDupValuesVector throwaway; + start_ok = !cursor->read_prev(1, throwaway); + } else if (!start_ok && reverse) { + start_ok = cursor->set_at_end(); + } + } + if (!start_ok) { + return { std::nullopt, {} }; + } + + auto [done, first_page] = advance_page(*cursor, reverse, page_size); + if (done || one_page) { + return { std::nullopt, key_values_to_wire(first_page) }; + } + + auto cursor_id = cursor->id(); + { + std::lock_guard lock(cursor_mutex_); + cursors_[cursor_id] = { cursor, reverse }; + } + return { cursor_id, key_values_to_wire(first_page) }; + } + + wire::KvdbAdvanceCursorResponse advance_cursor(const wire::KvdbAdvanceCursor& req) + { + CursorState state; + { + std::lock_guard lock(cursor_mutex_); + state = cursors_.at(req.cursor); + } + uint32_t page_size = req.count.value_or(DEFAULT_CURSOR_PAGE_SIZE); + auto [done, entries] = advance_page(*state.cursor, state.reverse, page_size); + return { key_values_to_wire(entries), done }; + } + + wire::KvdbAdvanceCursorCountResponse advance_cursor_count(const wire::KvdbAdvanceCursorCount& req) + { + CursorState state; + { + std::lock_guard lock(cursor_mutex_); + state = cursors_.at(req.cursor); + } + auto [done, count] = advance_count(*state.cursor, state.reverse, req.endKey); + return { count, done }; + } + + wire::KvdbCloseCursorResponse close_cursor(const wire::KvdbCloseCursor& req) + { + std::lock_guard lock(cursor_mutex_); + cursors_.erase(req.cursor); + return { true }; + } + + wire::KvdbBatchResponse batch(const wire::KvdbBatch& req) + { + std::vector put_batches; + put_batches.reserve(req.batches.size()); + for (const auto& entry : req.batches) { + put_batches.push_back(lmdblib::LMDBStore::PutData{ + key_values_from_wire(entry.addEntries), key_optional_values_from_wire(entry.removeEntries), entry.db }); + } + auto start = std::chrono::high_resolution_clock::now(); + store_.put(put_batches); + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration duration_ns = end - start; + return { duration_ns.count() }; + } + + wire::KvdbStatsResponse get_stats() + { + std::vector stats; + auto [map_size, physical_file_size] = store_.get_stats(stats); + std::vector wire_stats; + wire_stats.reserve(stats.size()); + for (const auto& stat : stats) { + wire_stats.push_back( + { .name = stat.name, .numDataItems = stat.numDataItems, .totalUsedSize = stat.totalUsedSize }); + } + return { std::move(wire_stats), map_size, physical_file_size }; + } + + wire::KvdbCloseResponse close() + { + // Drop all cursors so any in-flight reads release their transactions. + std::lock_guard lock(cursor_mutex_); + cursors_.clear(); + return { true }; + } + + wire::KvdbCopyStoreResponse copy_store(const wire::KvdbCopyStore& req) + { + store_.copy_store(req.dstPath, req.compact.value_or(false)); + return { true }; + } + + static lmdblib::KeyDupValuesVector key_values_from_wire(const std::vector& entries) + { + lmdblib::KeyDupValuesVector result; + result.reserve(entries.size()); + for (const auto& entry : entries) { + result.emplace_back(entry.key, entry.values); + } + return result; + } + + static std::vector key_values_to_wire(const lmdblib::KeyDupValuesVector& entries) + { + std::vector result; + result.reserve(entries.size()); + for (const auto& [key, values] : entries) { + result.push_back({ .key = key, .values = values }); + } + return result; + } + + static lmdblib::KeyOptionalValuesVector key_optional_values_from_wire( + const std::vector& entries) + { + lmdblib::KeyOptionalValuesVector result; + result.reserve(entries.size()); + for (const auto& entry : entries) { + result.emplace_back(entry.key, entry.values); + } + return result; + } + + static std::pair advance_page(const lmdblib::LMDBCursor& cursor, + bool reverse, + uint64_t page_size) + { + lmdblib::KeyDupValuesVector entries; + bool done = reverse ? cursor.read_prev(page_size, entries) : cursor.read_next(page_size, entries); + return std::make_pair(done, std::move(entries)); + } + + static std::pair advance_count(const lmdblib::LMDBCursor& cursor, + bool reverse, + const lmdblib::Key& end_key) + { + uint64_t count = 0; + bool done = reverse ? cursor.count_until_prev(end_key, count) : cursor.count_until_next(end_key, count); + return std::make_pair(done, count); + } + + private: + lmdblib::LMDBStore& store_; + std::mutex cursor_mutex_; + std::unordered_map cursors_; +}; + +} // namespace + +wire::KvdbOpenDatabaseResponse handle_open_database(KvdbServer& ctx, wire::KvdbOpenDatabase&& cmd); +wire::KvdbGetResponse handle_get(KvdbServer& ctx, wire::KvdbGet&& cmd); +wire::KvdbHasResponse handle_has(KvdbServer& ctx, wire::KvdbHas&& cmd); +wire::KvdbStartCursorResponse handle_start_cursor(KvdbServer& ctx, wire::KvdbStartCursor&& cmd); +wire::KvdbAdvanceCursorResponse handle_advance_cursor(KvdbServer& ctx, wire::KvdbAdvanceCursor&& cmd); +wire::KvdbAdvanceCursorCountResponse handle_advance_cursor_count(KvdbServer& ctx, wire::KvdbAdvanceCursorCount&& cmd); +wire::KvdbCloseCursorResponse handle_close_cursor(KvdbServer& ctx, wire::KvdbCloseCursor&& cmd); +wire::KvdbBatchResponse handle_batch(KvdbServer& ctx, wire::KvdbBatch&& cmd); +wire::KvdbStatsResponse handle_stats(KvdbServer& ctx, wire::KvdbStats&&); +wire::KvdbCloseResponse handle_close(KvdbServer& ctx, wire::KvdbClose&&); +wire::KvdbCopyStoreResponse handle_copy_store(KvdbServer& ctx, wire::KvdbCopyStore&& cmd); + +} // namespace bb::kvdb + +#include "barretenberg/kvdb/generated/kvdb_ipc_server.hpp" + +namespace bb::kvdb { + +wire::KvdbOpenDatabaseResponse handle_open_database(KvdbServer& ctx, wire::KvdbOpenDatabase&& cmd) +{ + return ctx.open_database(cmd); +} + +wire::KvdbGetResponse handle_get(KvdbServer& ctx, wire::KvdbGet&& cmd) +{ + return ctx.get(cmd); +} + +wire::KvdbHasResponse handle_has(KvdbServer& ctx, wire::KvdbHas&& cmd) +{ + return ctx.has(cmd); +} + +wire::KvdbStartCursorResponse handle_start_cursor(KvdbServer& ctx, wire::KvdbStartCursor&& cmd) +{ + return ctx.start_cursor(cmd); +} + +wire::KvdbAdvanceCursorResponse handle_advance_cursor(KvdbServer& ctx, wire::KvdbAdvanceCursor&& cmd) +{ + return ctx.advance_cursor(cmd); +} + +wire::KvdbAdvanceCursorCountResponse handle_advance_cursor_count(KvdbServer& ctx, wire::KvdbAdvanceCursorCount&& cmd) +{ + return ctx.advance_cursor_count(cmd); +} + +wire::KvdbCloseCursorResponse handle_close_cursor(KvdbServer& ctx, wire::KvdbCloseCursor&& cmd) +{ + return ctx.close_cursor(cmd); +} + +wire::KvdbBatchResponse handle_batch(KvdbServer& ctx, wire::KvdbBatch&& cmd) +{ + return ctx.batch(cmd); +} + +wire::KvdbStatsResponse handle_stats(KvdbServer& ctx, wire::KvdbStats&&) +{ + return ctx.get_stats(); +} + +wire::KvdbCloseResponse handle_close(KvdbServer& ctx, wire::KvdbClose&&) +{ + return ctx.close(); +} + +wire::KvdbCopyStoreResponse handle_copy_store(KvdbServer& ctx, wire::KvdbCopyStore&& cmd) +{ + return ctx.copy_store(cmd); +} + +int execute_kvdb_server(const std::string& input_path, + const std::string& data_dir, + uint64_t map_size_bytes, + uint32_t max_readers, + size_t request_ring_size, + size_t response_ring_size) +{ + // Match the legacy NAPI wrapper's defaults (max_readers=16, kMaxDbs=2). + auto store = std::make_unique(data_dir, map_size_bytes, max_readers, 2); + KvdbServer kvdb_server(*store); + + ipc::ServerOptions opts; + opts.max_shm_clients = 1; + opts.shm_request_ring_size = request_ring_size; + opts.shm_response_ring_size = response_ring_size; + auto server = ipc::make_server(input_path, opts); + if (!server) { + std::cerr << "Error: --input path must end with .sock or .shm: " << input_path << '\n'; + return 1; + } + + std::cerr << "aztec-kvdb listening on " << input_path << '\n'; + ipc::install_default_signal_handlers(*server); + + if (!server->listen()) { + std::cerr << "Error: Could not start IPC server" << '\n'; + return 1; + } + std::cerr << "aztec-kvdb IPC server ready" << '\n'; + + auto handler = make_kvdb_handler(kvdb_server); + server->run([&handler](int /*client_id*/, std::span raw_request) -> std::vector { + return handler(std::vector(raw_request.begin(), raw_request.end())); + }); + + server->close(); + return 0; +} + +} // namespace bb::kvdb diff --git a/barretenberg/cpp/src/barretenberg/kvdb/kvdb_ipc_server.hpp b/barretenberg/cpp/src/barretenberg/kvdb/kvdb_ipc_server.hpp new file mode 100644 index 000000000000..6f2ce2c87445 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/kvdb/kvdb_ipc_server.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace bb::kvdb { + +/** + * @brief Run the aztec-kvdb IPC server until shutdown. + * + * Opens the LMDB store at `data_dir` (creating it if missing), then listens + * on `input_path` for msgpack-encoded KvdbCommand requests. The path's suffix + * picks the transport: `.sock` for Unix Domain Socket, `.shm` for MPSC shared + * memory. + */ +int execute_kvdb_server(const std::string& input_path, + const std::string& data_dir, + uint64_t map_size_bytes, + uint32_t max_readers, + size_t request_ring_size, + size_t response_ring_size); + +} // namespace bb::kvdb diff --git a/barretenberg/cpp/src/barretenberg/kvdb/kvdb_schema.json b/barretenberg/cpp/src/barretenberg/kvdb/kvdb_schema.json new file mode 100644 index 000000000000..0030445ffe37 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/kvdb/kvdb_schema.json @@ -0,0 +1,236 @@ +{ + "commands": [ + "named_union", + [ + [ + "KvdbOpenDatabase", + { + "__typename": "KvdbOpenDatabase", + "db": "string", + "uniqueKeys": ["optional", ["bool"]] + } + ], + [ + "KvdbGet", + { + "__typename": "KvdbGet", + "keys": ["vector", [["vector", ["unsigned char"]]]], + "db": "string" + } + ], + [ + "KvdbHas", + { + "__typename": "KvdbHas", + "entries": [ + "vector", + [ + { + "__typename": "KvdbKeyOptionalValues", + "key": ["vector", ["unsigned char"]], + "values": ["optional", [["vector", [["vector", ["unsigned char"]]]]]] + } + ] + ], + "db": "string" + } + ], + [ + "KvdbStartCursor", + { + "__typename": "KvdbStartCursor", + "key": ["vector", ["unsigned char"]], + "reverse": ["optional", ["bool"]], + "count": ["optional", ["unsigned int"]], + "onePage": ["optional", ["bool"]], + "db": "string" + } + ], + [ + "KvdbAdvanceCursor", + { + "__typename": "KvdbAdvanceCursor", + "cursor": "unsigned long", + "count": ["optional", ["unsigned int"]] + } + ], + [ + "KvdbAdvanceCursorCount", + { + "__typename": "KvdbAdvanceCursorCount", + "cursor": "unsigned long", + "endKey": ["vector", ["unsigned char"]] + } + ], + [ + "KvdbCloseCursor", + { + "__typename": "KvdbCloseCursor", + "cursor": "unsigned long" + } + ], + [ + "KvdbBatch", + { + "__typename": "KvdbBatch", + "batches": [ + "vector", + [ + { + "__typename": "KvdbNamedBatch", + "db": "string", + "addEntries": [ + "vector", + [ + { + "__typename": "KvdbKeyValues", + "key": ["vector", ["unsigned char"]], + "values": ["vector", [["vector", ["unsigned char"]]]] + } + ] + ], + "removeEntries": ["vector", ["KvdbKeyOptionalValues"]] + } + ] + ] + } + ], + [ + "KvdbStats", + { + "__typename": "KvdbStats" + } + ], + [ + "KvdbClose", + { + "__typename": "KvdbClose" + } + ], + [ + "KvdbCopyStore", + { + "__typename": "KvdbCopyStore", + "dstPath": "string", + "compact": ["optional", ["bool"]] + } + ], + [ + "KvdbShutdown", + { + "__typename": "KvdbShutdown" + } + ] + ] + ], + "responses": [ + "named_union", + [ + [ + "KvdbErrorResponse", + { + "__typename": "KvdbErrorResponse", + "message": "string" + } + ], + [ + "KvdbOpenDatabaseResponse", + { + "__typename": "KvdbOpenDatabaseResponse", + "ok": "bool" + } + ], + [ + "KvdbGetResponse", + { + "__typename": "KvdbGetResponse", + "values": ["vector", [["optional", [["vector", [["vector", ["unsigned char"]]]]]]]] + } + ], + [ + "KvdbHasResponse", + { + "__typename": "KvdbHasResponse", + "exists": ["vector", ["bool"]] + } + ], + [ + "KvdbStartCursorResponse", + { + "__typename": "KvdbStartCursorResponse", + "cursor": ["optional", ["unsigned long"]], + "entries": ["vector", ["KvdbKeyValues"]] + } + ], + [ + "KvdbAdvanceCursorResponse", + { + "__typename": "KvdbAdvanceCursorResponse", + "entries": ["vector", ["KvdbKeyValues"]], + "done": "bool" + } + ], + [ + "KvdbAdvanceCursorCountResponse", + { + "__typename": "KvdbAdvanceCursorCountResponse", + "count": "unsigned long", + "done": "bool" + } + ], + [ + "KvdbCloseCursorResponse", + { + "__typename": "KvdbCloseCursorResponse", + "ok": "bool" + } + ], + [ + "KvdbBatchResponse", + { + "__typename": "KvdbBatchResponse", + "durationNs": "unsigned long" + } + ], + [ + "KvdbStatsResponse", + { + "__typename": "KvdbStatsResponse", + "stats": [ + "vector", + [ + { + "__typename": "KvdbDbStats", + "name": "string", + "numDataItems": "unsigned long", + "totalUsedSize": "unsigned long" + } + ] + ], + "dbMapSizeBytes": "unsigned long", + "dbPhysicalFileSizeBytes": "unsigned long" + } + ], + [ + "KvdbCloseResponse", + { + "__typename": "KvdbCloseResponse", + "ok": "bool" + } + ], + [ + "KvdbCopyStoreResponse", + { + "__typename": "KvdbCopyStoreResponse", + "ok": "bool" + } + ], + [ + "KvdbShutdownResponse", + { + "__typename": "KvdbShutdownResponse" + } + ] + ] + ] +} diff --git a/barretenberg/cpp/src/barretenberg/kvdb/main.cpp b/barretenberg/cpp/src/barretenberg/kvdb/main.cpp new file mode 100644 index 000000000000..f8829f295fa4 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/kvdb/main.cpp @@ -0,0 +1,6 @@ +#include "barretenberg/kvdb/cli.hpp" + +int main(int argc, char* argv[]) +{ + return bb::kvdb::parse_and_run_kvdb(argc, argv); +} diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/nodejs_module/CMakeLists.txt index 1eb6cee25bd1..cf4bd5466d96 100644 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/nodejs_module/CMakeLists.txt @@ -27,7 +27,7 @@ string(REGEX REPLACE "[\r\n\"]" "" NODE_API_HEADERS_DIR ${NODE_API_HEADERS_DIR}) add_library(nodejs_module SHARED ${SOURCE_FILES}) set_target_properties(nodejs_module PROPERTIES PREFIX "" SUFFIX ".node") target_include_directories(nodejs_module PRIVATE ${NODE_API_HEADERS_DIR} ${NODE_ADDON_API_DIR}) -target_link_libraries(nodejs_module PRIVATE world_state ipc vm2_sim) +target_link_libraries(nodejs_module PRIVATE ipc_runtime) # On macOS, Node.js N-API symbols are provided by the runtime, not at link time if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/avm_simulate_napi.cpp b/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/avm_simulate_napi.cpp deleted file mode 100644 index 5819bcf3a743..000000000000 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/avm_simulate_napi.cpp +++ /dev/null @@ -1,427 +0,0 @@ -#include "barretenberg/nodejs_module/avm_simulate/avm_simulate_napi.hpp" - -#include -#include -#include - -#include "barretenberg/common/log.hpp" -#include "barretenberg/nodejs_module/avm_simulate/ts_callback_contract_db.hpp" -#include "barretenberg/nodejs_module/util/async_op.hpp" -#include "barretenberg/serialize/msgpack.hpp" -#include "barretenberg/serialize/msgpack_impl/msgpack_impl.hpp" -#include "barretenberg/vm2/avm_sim_api.hpp" -#include "barretenberg/vm2/common/avm_io.hpp" -#include "barretenberg/vm2/simulation/lib/cancellation_token.hpp" - -namespace bb::nodejs { -namespace { - -// Log levels from TS foundation/src/log/log-levels.ts: ['silent', 'fatal', 'error', 'warn', 'info', 'verbose', 'debug', -// 'trace'] Map: 0=silent, 1=fatal, 2=error, 3=warn, 4=info, 5=verbose, 6=debug, 7=trace - -// Helper to set logging level based on TS log level -inline void set_logging_from_level(int ts_log_level) -{ - // Map TS log level (0-7) to C++ LogLevel enum - // TS: 0=silent, 1=fatal, 2=error, 3=warn, 4=info, 5=verbose, 6=debug, 7=trace - // C++: SILENT=0, FATAL=1, ERROR=2, WARN=3, INFO=4, VERBOSE=5, DEBUG=6, TRACE=7 - // They map 1:1 - if (ts_log_level >= 0 && ts_log_level <= 7) { - bb_log_level = static_cast(ts_log_level); - } else { - log_warn("Invalid log level from TypeScript: ", ts_log_level, ". Using default."); - } -} - -// Map C++ LogLevel enum to TypeScript log level string -// C++ LogLevel: SILENT=0, FATAL=1, ERROR=2, WARN=3, INFO=4, VERBOSE=5, DEBUG=6, TRACE=7 -// TS LogLevels: ['silent', 'fatal', 'error', 'warn', 'info', 'verbose', 'debug', 'trace'] -inline const char* cpp_log_level_to_ts(LogLevel level) -{ - switch (level) { - case LogLevel::SILENT: - return "silent"; - case LogLevel::FATAL: - return "fatal"; - case LogLevel::ERROR: - return "error"; - case LogLevel::WARN: - return "warn"; - case LogLevel::INFO: - return "info"; - case LogLevel::VERBOSE: - return "verbose"; - case LogLevel::DEBUG: - return "debug"; - case LogLevel::TRACE: - return "trace"; - default: - return "info"; - } -} - -// Helper to create a LogFunction wrapper from a ThreadSafeFunction -// This allows C++ logging to call back to TypeScript logger from worker threads -LogFunction create_log_function_from_tsfn(const std::shared_ptr& logger_tsfn) -{ - return [logger_tsfn](LogLevel level, const std::string& msg) { - // Convert C++ LogLevel to TS log level string - const char* ts_level = cpp_log_level_to_ts(level); - - // Call TypeScript logger function on the JS main thread - // Using BlockingCall to ensure synchronous execution - // Ignore errors - logging failures shouldn't crash the simulation - // NOTE: We copy the string because it might be destroyed before the callback is called. - logger_tsfn->BlockingCall([ts_level, msg](Napi::Env env, Napi::Function js_logger) { - // Create arguments: (level: string, msg: string) - auto level_js = Napi::String::New(env, ts_level); - auto msg_js = Napi::String::New(env, msg); - js_logger.Call({ level_js, msg_js }); - }); - }; -} - -// Callback method names -constexpr const char* CALLBACK_GET_CONTRACT_INSTANCE = "getContractInstance"; -constexpr const char* CALLBACK_GET_CONTRACT_CLASS = "getContractClass"; -constexpr const char* CALLBACK_ADD_CONTRACTS = "addContracts"; -constexpr const char* CALLBACK_GET_BYTECODE = "getBytecodeCommitment"; -constexpr const char* CALLBACK_GET_DEBUG_NAME = "getDebugFunctionName"; -constexpr const char* CALLBACK_CREATE_CHECKPOINT = "createCheckpoint"; -constexpr const char* CALLBACK_COMMIT_CHECKPOINT = "commitCheckpoint"; -constexpr const char* CALLBACK_REVERT_CHECKPOINT = "revertCheckpoint"; - -// RAII helper to automatically release thread-safe functions -// Used inside the async lambda to ensure cleanup in all code paths -class TsfnReleaser { - std::vector> tsfns_; - - public: - explicit TsfnReleaser(std::vector> tsfns) - : tsfns_(std::move(tsfns)) - {} - - ~TsfnReleaser() - { - for (auto& tsfn : tsfns_) { - if (tsfn) { - tsfn->Release(); - } - } - } - - // Prevent copying and moving - TsfnReleaser(const TsfnReleaser&) = delete; - TsfnReleaser& operator=(const TsfnReleaser&) = delete; - TsfnReleaser(TsfnReleaser&&) = delete; - TsfnReleaser& operator=(TsfnReleaser&&) = delete; -}; - -// Helper to create thread-safe function wrapper -inline std::shared_ptr make_tsfn(Napi::Env env, Napi::Function fn, const char* name) -{ - return std::make_shared(Napi::ThreadSafeFunction::New(env, fn, name, 0, 1)); -} - -// Bundle all contract-related thread-safe functions with named access -struct ContractTsfns { - std::shared_ptr instance; - std::shared_ptr class_; - std::shared_ptr add_contracts; - std::shared_ptr bytecode; - std::shared_ptr debug_name; - std::shared_ptr create_checkpoint; - std::shared_ptr commit_checkpoint; - std::shared_ptr revert_checkpoint; - - std::vector> to_vector() const - { - return { instance, class_, add_contracts, bytecode, debug_name, create_checkpoint, - commit_checkpoint, revert_checkpoint }; - } -}; - -// Helper to validate and extract contract provider callbacks -struct ContractCallbacks { - static constexpr const char* ALL_METHODS[] = { CALLBACK_GET_CONTRACT_INSTANCE, CALLBACK_GET_CONTRACT_CLASS, - CALLBACK_ADD_CONTRACTS, CALLBACK_GET_BYTECODE, - CALLBACK_GET_DEBUG_NAME, CALLBACK_CREATE_CHECKPOINT, - CALLBACK_COMMIT_CHECKPOINT, CALLBACK_REVERT_CHECKPOINT }; - - static void validate(Napi::Env env, Napi::Object provider) - { - for (const char* method : ALL_METHODS) { - if (!provider.Has(method)) { - throw Napi::TypeError::New( - env, std::string("contractProvider must have ") + method + " method. Missing methods: " + method); - } - } - } - - static Napi::Function get(Napi::Object provider, const char* name) - { - return provider.Get(name).As(); - } -}; -} // namespace - -Napi::Value AvmSimulateNapi::simulate(const Napi::CallbackInfo& cb_info) -{ - Napi::Env env = cb_info.Env(); - - // Validate arguments - expects 3-6 arguments - // arg[0]: inputs Buffer (required) - // arg[1]: contractProvider object (required) - // arg[2]: worldStateHandle external (required) - // arg[3]: logLevel number (optional) - index into TS LogLevels array, -1 if omitted - // arg[4]: loggerFunction (optional) - can be null/undefined - // arg[5]: cancellationToken external (optional) - if (cb_info.Length() < 3) { - throw Napi::TypeError::New( - env, - "Wrong number of arguments. Expected 3-6 arguments: inputs Buffer, contractProvider " - "object, worldStateHandle, optional logLevel, optional loggerFunction, and optional cancellationToken."); - } - - /******************************* - *** AvmFastSimulationInputs *** - *******************************/ - if (!cb_info[0].IsBuffer()) { - throw Napi::TypeError::New(env, - "First argument must be a Buffer containing serialized AvmFastSimulationInputs"); - } - // Extract the inputs buffer - auto inputs_buffer = cb_info[0].As>(); - size_t length = inputs_buffer.Length(); - // Copy the buffer data into C++ memory (we can't access Napi objects from worker thread) - auto data = std::make_shared>(inputs_buffer.Data(), inputs_buffer.Data() + length); - - /*********************************** - *** ContractProvider (required) *** - ***********************************/ - if (!cb_info[1].IsObject()) { - throw Napi::TypeError::New(env, "Second argument must be a contractProvider object"); - } - // Extract and validate contract provider callbacks - auto contract_provider = cb_info[1].As(); - ContractCallbacks::validate(env, contract_provider); - // Create thread-safe function wrappers for callbacks - // These allow us to call TypeScript from the C++ worker thread - ContractTsfns tsfns{ - .instance = make_tsfn(env, - ContractCallbacks::get(contract_provider, CALLBACK_GET_CONTRACT_INSTANCE), - CALLBACK_GET_CONTRACT_INSTANCE), - .class_ = make_tsfn( - env, ContractCallbacks::get(contract_provider, CALLBACK_GET_CONTRACT_CLASS), CALLBACK_GET_CONTRACT_CLASS), - .add_contracts = - make_tsfn(env, ContractCallbacks::get(contract_provider, CALLBACK_ADD_CONTRACTS), CALLBACK_ADD_CONTRACTS), - .bytecode = - make_tsfn(env, ContractCallbacks::get(contract_provider, CALLBACK_GET_BYTECODE), CALLBACK_GET_BYTECODE), - .debug_name = - make_tsfn(env, ContractCallbacks::get(contract_provider, CALLBACK_GET_DEBUG_NAME), CALLBACK_GET_DEBUG_NAME), - .create_checkpoint = make_tsfn( - env, ContractCallbacks::get(contract_provider, CALLBACK_CREATE_CHECKPOINT), CALLBACK_CREATE_CHECKPOINT), - .commit_checkpoint = make_tsfn( - env, ContractCallbacks::get(contract_provider, CALLBACK_COMMIT_CHECKPOINT), CALLBACK_COMMIT_CHECKPOINT), - .revert_checkpoint = make_tsfn( - env, ContractCallbacks::get(contract_provider, CALLBACK_REVERT_CHECKPOINT), CALLBACK_REVERT_CHECKPOINT), - }; - - /***************************** - *** WorldState (required) *** - *****************************/ - if (!cb_info[2].IsExternal()) { - throw Napi::TypeError::New(env, "Third argument must be a WorldState handle (External)"); - } - // Extract WorldState handle (3rd argument) - auto external = cb_info[2].As>(); - world_state::WorldState* ws_ptr = external.Data(); - - /*************************** - *** LogLevel (optional) *** - ***************************/ - int log_level = -1; - if (cb_info.Length() > 3 && cb_info[3].IsNumber()) { - log_level = cb_info[3].As().Int32Value(); - set_logging_from_level(log_level); - } - - /********************************* - *** LoggerFunction (optional) *** - *********************************/ - std::shared_ptr logger_tsfn = nullptr; - if (cb_info.Length() > 4 && !cb_info[4].IsNull() && !cb_info[4].IsUndefined()) { - if (cb_info[4].IsFunction()) { - // Logger function provided - create thread-safe wrapper - auto logger_function = cb_info[4].As(); - logger_tsfn = make_tsfn(env, logger_function, "LoggerCallback"); - // Create LogFunction wrapper and set it as the global log function - // This will be used by C++ logging macros (info, debug, vinfo, important) - set_log_function(create_log_function_from_tsfn(logger_tsfn)); - } else { - throw Napi::TypeError::New(env, "Fifth argument must be a logger function, null, or undefined"); - } - } - - /************************************* - *** Cancellation Token (optional) *** - *************************************/ - avm2::simulation::CancellationTokenPtr cancellation_token = nullptr; - if (cb_info.Length() > 5 && cb_info[5].IsExternal()) { - auto token_external = cb_info[5].As>(); - // Wrap the raw pointer in a shared_ptr that does NOT delete (since the External owns it) - cancellation_token = std::shared_ptr( - token_external.Data(), [](avm2::simulation::CancellationToken*) { - // No-op deleter: the External (via shared_ptr destructor callback) owns the token - }); - } - - /********************************************************** - *** Create Deferred Promise and launch async operation *** - **********************************************************/ - - auto deferred = std::make_shared(env); - // Run on a dedicated std::thread (not libuv pool) to prevent libuv thread pool - // exhaustion when callbacks need libuv threads for I/O. - ThreadedAsyncOperation::Run( - env, deferred, [data, tsfns, logger_tsfn, ws_ptr, cancellation_token](msgpack::sbuffer& result_buffer) { - // Collect all thread-safe functions including logger for cleanup - auto all_tsfns = tsfns.to_vector(); - all_tsfns.push_back(logger_tsfn); - // Ensure all thread-safe functions are released in all code paths - TsfnReleaser releaser = TsfnReleaser(std::move(all_tsfns)); - - try { - // Deserialize inputs from msgpack - avm2::AvmFastSimulationInputs inputs; - msgpack::object_handle obj_handle = - msgpack::unpack(reinterpret_cast(data->data()), data->size()); - msgpack::object obj = obj_handle.get(); - obj.convert(inputs); - - // Create TsCallbackContractDB with TypeScript callbacks - TsCallbackContractDB contract_db(*tsfns.instance, - *tsfns.class_, - *tsfns.add_contracts, - *tsfns.bytecode, - *tsfns.debug_name, - *tsfns.create_checkpoint, - *tsfns.commit_checkpoint, - *tsfns.revert_checkpoint); - - // Create AVM API and run simulation with the callback-based contracts DB, - // WorldState reference, and optional cancellation token - avm2::AvmSimAPI avm; - avm2::TxSimulationResult result = avm.simulate(inputs, contract_db, *ws_ptr, cancellation_token); - - // Serialize the simulation result with msgpack into the return buffer to TS. - msgpack::pack(result_buffer, result); - } catch (const avm2::simulation::CancelledException& e) { - // Cancellation is an expected condition, rethrow with context - throw std::runtime_error("Simulation cancelled"); - } catch (const std::exception& e) { - // Rethrow with context (RAII wrappers will clean up automatically) - throw std::runtime_error(std::string("AVM simulation failed: ") + e.what()); - } catch (...) { - throw std::runtime_error("AVM simulation failed with unknown exception"); - } - }); - - return deferred->Promise(); -} - -Napi::Value AvmSimulateNapi::simulateWithHintedDbs(const Napi::CallbackInfo& cb_info) -{ - Napi::Env env = cb_info.Env(); - - // Validate arguments - expects 2 arguments - // arg[0]: inputs Buffer (required) - AvmProvingInputs - // arg[1]: logLevel number (required) - index into TS LogLevels array - if (cb_info.Length() < 2) { - throw Napi::TypeError::New(env, - "Wrong number of arguments. Expected 2 arguments: AvmProvingInputs/AvmCircuitInputs " - "msgpack Buffer and logLevel."); - } - - if (!cb_info[0].IsBuffer()) { - throw Napi::TypeError::New( - env, "First argument must be a Buffer containing serialized AvmProvingInputs/AvmCircuitInputs"); - } - - if (!cb_info[1].IsNumber()) { - throw Napi::TypeError::New(env, "Second argument must be a log level number (0-7)"); - } - - // Extract log level and set logging flags - int log_level = cb_info[1].As().Int32Value(); - set_logging_from_level(log_level); - - // Extract the inputs buffer - auto inputs_buffer = cb_info[0].As>(); - size_t length = inputs_buffer.Length(); - - // Copy the buffer data into C++ memory (we can't access Napi objects from worker thread) - auto data = std::make_shared>(inputs_buffer.Data(), inputs_buffer.Data() + length); - - // Create a deferred promise - auto deferred = std::make_shared(env); - - // Run on a dedicated std::thread (not libuv pool) - ThreadedAsyncOperation::Run(env, deferred, [data](msgpack::sbuffer& result_buffer) { - try { - // Deserialize inputs from msgpack - avm2::AvmProvingInputs inputs; - msgpack::object_handle obj_handle = - msgpack::unpack(reinterpret_cast(data->data()), data->size()); - msgpack::object obj = obj_handle.get(); - obj.convert(inputs); - - // Create AVM Sim API and run simulation with the hinted DBs - // All hints are already in the inputs, so no runtime contract DB callbacks needed - avm2::AvmSimAPI avm; - avm2::TxSimulationResult result = avm.simulate_with_hinted_dbs(inputs); - - // Serialize the simulation result with msgpack into the return buffer to TS. - msgpack::pack(result_buffer, result); - } catch (const std::exception& e) { - // Rethrow with context - throw std::runtime_error(std::string("AVM simulation with hinted DBs failed: ") + e.what()); - } catch (...) { - throw std::runtime_error("AVM simulation with hinted DBs failed with unknown exception"); - } - }); - - return deferred->Promise(); -} - -Napi::Value AvmSimulateNapi::createCancellationToken(const Napi::CallbackInfo& cb_info) -{ - Napi::Env env = cb_info.Env(); - - // Create a new CancellationToken. We use a shared_ptr to manage the lifetime, - // and the destructor callback in the External will clean it up when GC runs. - auto* token = new avm2::simulation::CancellationToken(); - - // Create an External with a destructor callback that deletes the token - return Napi::External::New( - env, token, [](Napi::Env /*env*/, avm2::simulation::CancellationToken* t) { delete t; }); -} - -Napi::Value AvmSimulateNapi::cancelSimulation(const Napi::CallbackInfo& cb_info) -{ - Napi::Env env = cb_info.Env(); - - if (cb_info.Length() < 1 || !cb_info[0].IsExternal()) { - throw Napi::TypeError::New(env, "Expected a CancellationToken External as argument"); - } - - auto token_external = cb_info[0].As>(); - avm2::simulation::CancellationToken* token = token_external.Data(); - - // Signal cancellation - this is thread-safe (atomic store) - token->cancel(); - - return env.Undefined(); -} - -} // namespace bb::nodejs diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/avm_simulate_napi.hpp b/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/avm_simulate_napi.hpp deleted file mode 100644 index f24c598cba5f..000000000000 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/avm_simulate_napi.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include - -namespace bb::nodejs { - -/** - * @brief NAPI wrapper for the C++ AVM simulation. - * - * This class provides the bridge between TypeScript and the C++ avm_simulate*() functions. - * It handles deserialization of inputs, execution on a worker thread, and serialization of results. - * - * The simulate variation uses real world state and uses callbacks to TS for contract DB. - * - * The simulateWithHintedDbs variation uses pre-collected hints for world state and contracts DB. - * There are no callbacks to TS or direct calls to world state. - */ -class AvmSimulateNapi { - public: - /** - * @brief NAPI function to simulate AVM execution - * - * Expected arguments: - * - info[0]: Buffer containing serialized AvmFastSimulationInputs (msgpack) - * - info[1]: Object with contract provider callbacks: - * - getContractInstance(address: string): Promise - * - getContractClass(classId: string): Promise - * - info[2]: External WorldState handle (pointer to world_state::WorldState) - * - info[3]: Log level number (0-7) - * - info[4]: External CancellationToken handle (optional) - * - * Returns: Promise containing serialized simulation results - * - * @param info NAPI callback info containing arguments - * @return Napi::Value Promise that resolves with simulation results - */ - static Napi::Value simulate(const Napi::CallbackInfo& info); - - /** - * @brief NAPI function to simulate AVM execution with pre-collected hints - * - * Expected arguments: - * - info[0]: Buffer containing serialized AvmProvingInputs (msgpack) - * - * @param info NAPI callback info containing arguments - * @return Napi::Value Promise that resolves with simulation results - */ - static Napi::Value simulateWithHintedDbs(const Napi::CallbackInfo& info); - - /** - * @brief Create a cancellation token that can be used to cancel a simulation. - * - * Returns: External - a handle to a new cancellation token - * - * @param info NAPI callback info (no arguments expected) - * @return Napi::Value External handle to the cancellation token - */ - static Napi::Value createCancellationToken(const Napi::CallbackInfo& info); - - /** - * @brief Cancel a simulation by signaling the provided cancellation token. - * - * Expected arguments: - * - info[0]: External CancellationToken handle - * - * @param info NAPI callback info containing the token - * @return Napi::Value undefined - */ - static Napi::Value cancelSimulation(const Napi::CallbackInfo& info); -}; - -} // namespace bb::nodejs diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_contract_db.cpp b/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_contract_db.cpp deleted file mode 100644 index 7a3db13e83bf..000000000000 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_contract_db.cpp +++ /dev/null @@ -1,260 +0,0 @@ -#include "ts_callback_contract_db.hpp" -#include "ts_callback_utils.hpp" - -#include -#include - -#include "barretenberg/common/log.hpp" - -namespace bb::nodejs { - -TsCallbackContractDB::TsCallbackContractDB(Napi::ThreadSafeFunction instanceCallback, - Napi::ThreadSafeFunction classCallback, - Napi::ThreadSafeFunction addContractsCallback, - Napi::ThreadSafeFunction bytecodeCommitmentCallback, - Napi::ThreadSafeFunction debugNameCallback, - Napi::ThreadSafeFunction createCheckpointCallback, - Napi::ThreadSafeFunction commitCheckpointCallback, - Napi::ThreadSafeFunction revertCheckpointCallback) - : contract_instance_callback_(std::move(instanceCallback)) - , contract_class_callback_(std::move(classCallback)) - , add_contracts_callback_(std::move(addContractsCallback)) - , bytecode_commitment_callback_(std::move(bytecodeCommitmentCallback)) - , debug_name_callback_(std::move(debugNameCallback)) - , create_checkpoint_callback_(std::move(createCheckpointCallback)) - , commit_checkpoint_callback_(std::move(commitCheckpointCallback)) - , revert_checkpoint_callback_(std::move(revertCheckpointCallback)) -{} - -std::optional TsCallbackContractDB::get_contract_instance( - const bb::avm2::AztecAddress& address) const -{ - if (released_) { - throw std::runtime_error("Cannot call get_contract_instance after releasing callbacks"); - } - - debug("TsCallbackContractDB: Fetching contract instance for address ", address); - - try { - auto result_data = - invoke_single_string_callback(contract_instance_callback_, format(address), "contract instance"); - - if (!result_data.has_value()) { - vinfo("Contract instance not found: ", address); - return std::nullopt; - } - - auto instance = deserialize_from_msgpack(*result_data, "contract instance"); - return std::make_optional(std::move(instance)); - } catch (const std::exception& e) { - throw std::runtime_error(std::string("Failed to get contract instance for address ") + format(address) + ": " + - e.what()); - } -} - -std::optional TsCallbackContractDB::get_contract_class( - const bb::avm2::ContractClassId& class_id) const -{ - if (released_) { - throw std::runtime_error("Cannot call get_contract_class after releasing callbacks"); - } - - debug("TsCallbackContractDB: Fetching contract class for class_id ", class_id); - - try { - auto result_data = invoke_single_string_callback(contract_class_callback_, format(class_id), "contract class"); - - if (!result_data.has_value()) { - vinfo("Contract class not found: ", class_id); - return std::nullopt; - } - - auto contract_class = deserialize_from_msgpack(*result_data, "contract class"); - return std::make_optional(std::move(contract_class)); - } catch (const std::exception& e) { - throw std::runtime_error(std::string("Failed to get contract class for class_id ") + format(class_id) + ": " + - e.what()); - } -} - -void TsCallbackContractDB::add_contracts(const bb::avm2::ContractDeploymentData& contract_deployment_data) -{ - if (released_) { - throw std::runtime_error("Cannot call add_contracts after releasing callbacks"); - } - - debug("TsCallbackContractDB: Adding contracts"); - - try { - auto serialized_data = serialize_to_msgpack(contract_deployment_data); - invoke_buffer_void_callback(add_contracts_callback_, std::move(serialized_data), "add_contracts"); - } catch (const std::exception& e) { - throw std::runtime_error(std::string("Failed to add contracts: ") + e.what()); - } -} - -std::optional TsCallbackContractDB::get_bytecode_commitment( - const bb::avm2::ContractClassId& class_id) const -{ - if (released_) { - throw std::runtime_error("Cannot call get_bytecode_commitment after releasing callbacks"); - } - - debug("TsCallbackContractDB: Fetching bytecode commitment for class_id ", class_id); - - try { - auto result_data = - invoke_single_string_callback(bytecode_commitment_callback_, format(class_id), "bytecode commitment"); - - if (!result_data.has_value()) { - vinfo("Bytecode commitment not found: ", class_id); - return std::nullopt; - } - - auto commitment = deserialize_from_msgpack(*result_data, "bytecode commitment"); - return commitment; - } catch (const std::exception& e) { - throw std::runtime_error(std::string("Failed to get bytecode commitment for class_id ") + format(class_id) + - ": " + e.what()); - } -} - -std::optional TsCallbackContractDB::get_debug_function_name(const bb::avm2::AztecAddress& address, - const bb::avm2::FF& selector) const -{ - if (released_) { - throw std::runtime_error("Cannot call get_debug_function_name after releasing callbacks"); - } - - debug("TsCallbackContractDB: Fetching debug function name for address ", address, " selector ", selector); - - try { - auto result_data = invoke_double_string_callback( - debug_name_callback_, format(address), format(selector), "debug function name"); - - if (!result_data.has_value()) { - debug("Debug function name not found for address ", address, " selector ", selector); - return std::nullopt; - } - - // Convert the vector of bytes back to a string - std::string name(result_data->begin(), result_data->end()); - return name; - } catch (const std::exception& e) { - throw std::runtime_error(std::string("Failed to get debug function name for address ") + format(address) + - " selector " + format(selector) + ": " + e.what()); - } -} - -void TsCallbackContractDB::create_checkpoint() -{ - if (released_) { - throw std::runtime_error("Cannot call create_checkpoint after releasing callbacks"); - } - - debug("TsCallbackContractDB: Creating checkpoint"); - - try { - // Call the TypeScript callback with no arguments - auto result = invoke_ts_callback_with_promise( - create_checkpoint_callback_, - "create_checkpoint", - [](Napi::Env env, Napi::Function js_callback, std::shared_ptr data) { - auto js_result = js_callback.Call({}); - - if (!js_result.IsPromise()) { - data->error_message = "TypeScript callback did not return a Promise"; - data->result_promise.set_value(std::nullopt); - return; - } - - auto promise = js_result.As(); - auto resolve_handler = create_void_resolve_handler(env, data); - auto reject_handler = create_reject_handler(env, data); - attach_promise_handlers(promise, resolve_handler, reject_handler); - }); - } catch (const std::exception& e) { - throw std::runtime_error(std::string("Failed to create checkpoint: ") + e.what()); - } -} - -void TsCallbackContractDB::commit_checkpoint() -{ - if (released_) { - throw std::runtime_error("Cannot call commit_checkpoint after releasing callbacks"); - } - - debug("TsCallbackContractDB: Committing checkpoint"); - - try { - // Call the TypeScript callback with no arguments - auto result = invoke_ts_callback_with_promise( - commit_checkpoint_callback_, - "commit_checkpoint", - [](Napi::Env env, Napi::Function js_callback, std::shared_ptr data) { - auto js_result = js_callback.Call({}); - - if (!js_result.IsPromise()) { - data->error_message = "TypeScript callback did not return a Promise"; - data->result_promise.set_value(std::nullopt); - return; - } - - auto promise = js_result.As(); - auto resolve_handler = create_void_resolve_handler(env, data); - auto reject_handler = create_reject_handler(env, data); - attach_promise_handlers(promise, resolve_handler, reject_handler); - }); - } catch (const std::exception& e) { - throw std::runtime_error(std::string("Failed to commit checkpoint: ") + e.what()); - } -} - -void TsCallbackContractDB::revert_checkpoint() -{ - if (released_) { - throw std::runtime_error("Cannot call revert_checkpoint after releasing callbacks"); - } - - debug("TsCallbackContractDB: Reverting checkpoint"); - - try { - // Call the TypeScript callback with no arguments - auto result = invoke_ts_callback_with_promise( - revert_checkpoint_callback_, - "revert_checkpoint", - [](Napi::Env env, Napi::Function js_callback, std::shared_ptr data) { - auto js_result = js_callback.Call({}); - - if (!js_result.IsPromise()) { - data->error_message = "TypeScript callback did not return a Promise"; - data->result_promise.set_value(std::nullopt); - return; - } - - auto promise = js_result.As(); - auto resolve_handler = create_void_resolve_handler(env, data); - auto reject_handler = create_reject_handler(env, data); - attach_promise_handlers(promise, resolve_handler, reject_handler); - }); - } catch (const std::exception& e) { - throw std::runtime_error(std::string("Failed to revert checkpoint: ") + e.what()); - } -} - -void TsCallbackContractDB::release() -{ - if (!released_) { - contract_instance_callback_.Release(); - contract_class_callback_.Release(); - add_contracts_callback_.Release(); - bytecode_commitment_callback_.Release(); - debug_name_callback_.Release(); - create_checkpoint_callback_.Release(); - commit_checkpoint_callback_.Release(); - revert_checkpoint_callback_.Release(); - released_ = true; - } -} - -} // namespace bb::nodejs diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_contract_db.hpp b/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_contract_db.hpp deleted file mode 100644 index 25e5a2a3576f..000000000000 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_contract_db.hpp +++ /dev/null @@ -1,149 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "barretenberg/vm2/common/aztec_types.hpp" -#include "barretenberg/vm2/simulation/interfaces/db.hpp" - -namespace bb::nodejs { - -/** - * @brief Implementation of ContractDBInterface that uses NAPI callbacks to TypeScript - * - * This class bridges C++ contract data queries to TypeScript's PublicContractsDB. - * During simulation, when C++ needs contract instances or classes, it calls back - * to TypeScript through thread-safe NAPI functions. - * - * Thread Safety: - * - Uses Napi::ThreadSafeFunction to safely call TypeScript from C++ worker threads - * - BlockingCall ensures synchronous execution with the JavaScript event loop - * - * Lifecycle: - * - Thread-safe functions must be released after use to avoid memory leaks - * - Caller is responsible for releasing TSFNs by calling release() - */ -class TsCallbackContractDB : public avm2::simulation::ContractDBInterface { - public: - /** - * @brief Constructs a callback-based contracts database - * - * @param instanceCallback Thread-safe function to fetch contract instances from TypeScript - * Expected signature: (address: string) => Promise - * @param classCallback Thread-safe function to fetch contract classes from TypeScript - * Expected signature: (classId: string) => Promise - * @param addContractsCallback Thread-safe function to add contracts - * Expected signature: (contractDeploymentData: Buffer) => Promise - * @param bytecodeCommitmentCallback Thread-safe function to fetch bytecode commitments - * Expected signature: (classId: string) => Promise - * @param debugNameCallback Thread-safe function to fetch debug function names - * Expected signature: (address: string, selector: string) => Promise - * @param createCheckpointCallback Thread-safe function to create a checkpoint - * Expected signature: () => Promise - * @param commitCheckpointCallback Thread-safe function to commit a checkpoint - * Expected signature: () => Promise - * @param revertCheckpointCallback Thread-safe function to revert a checkpoint - * Expected signature: () => Promise - */ - TsCallbackContractDB(Napi::ThreadSafeFunction instanceCallback, - Napi::ThreadSafeFunction classCallback, - Napi::ThreadSafeFunction addContractsCallback, - Napi::ThreadSafeFunction bytecodeCommitmentCallback, - Napi::ThreadSafeFunction debugNameCallback, - Napi::ThreadSafeFunction createCheckpointCallback, - Napi::ThreadSafeFunction commitCheckpointCallback, - Napi::ThreadSafeFunction revertCheckpointCallback); - - /** - * @brief Fetches a contract instance by address - * - * Calls back to TypeScript to retrieve the contract instance. The TypeScript callback - * should return a msgpack-serialized ContractInstanceHint buffer, or undefined if not found. - * - * @param address The contract address to lookup - * @return std::optional The contract instance if found, nullopt otherwise - */ - std::optional get_contract_instance( - const bb::avm2::AztecAddress& address) const override; - - /** - * @brief Fetches a contract class by class ID - * - * Calls back to TypeScript to retrieve the contract class. The TypeScript callback - * should return a msgpack-serialized ContractClassHint buffer, or undefined if not found. - * - * @param class_id The contract class ID to lookup - * @return std::optional The contract class if found, nullopt otherwise - */ - std::optional get_contract_class(const bb::avm2::ContractClassId& class_id) const override; - - /** - * @brief Adds contracts from deployment data - * - * @param contract_deployment_data The contract deployment data - */ - void add_contracts(const bb::avm2::ContractDeploymentData& contract_deployment_data) override; - - /** - * @brief Fetches bytecode commitment for a contract class - * - * @param class_id The contract class ID - * @return std::optional The bytecode commitment if found, nullopt otherwise - */ - std::optional get_bytecode_commitment(const bb::avm2::ContractClassId& class_id) const override; - - /** - * @brief Fetches debug function name for a contract function - * - * @param address The contract address - * @param selector The function selector - * @return std::optional The function name if found, nullopt otherwise - */ - std::optional get_debug_function_name(const bb::avm2::AztecAddress& address, - const bb::avm2::FF& selector) const override; - - /** - * @brief Creates a new checkpoint - * - * Creates a checkpoint in the TypeScript contracts DB, enabling rollbacks to current state. - */ - void create_checkpoint() override; - - /** - * @brief Commits the current checkpoint - * - * Accepts the current checkpoint's state as latest. - */ - void commit_checkpoint() override; - - /** - * @brief Reverts the current checkpoint - * - * Discards the current checkpoint's state and rolls back to the previous checkpoint. - */ - void revert_checkpoint() override; - - /** - * @brief Releases the thread-safe function handles - * - * Must be called before destruction to properly clean up NAPI resources. - * This tells Node.js that the C++ side is done with the callbacks. - */ - void release(); - - private: - Napi::ThreadSafeFunction contract_instance_callback_; - Napi::ThreadSafeFunction contract_class_callback_; - Napi::ThreadSafeFunction add_contracts_callback_; - Napi::ThreadSafeFunction bytecode_commitment_callback_; - Napi::ThreadSafeFunction debug_name_callback_; - Napi::ThreadSafeFunction create_checkpoint_callback_; - Napi::ThreadSafeFunction commit_checkpoint_callback_; - Napi::ThreadSafeFunction revert_checkpoint_callback_; - - // Track whether TSFNs have been released to avoid double-release - mutable bool released_ = false; -}; - -} // namespace bb::nodejs diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_utils.cpp b/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_utils.cpp deleted file mode 100644 index 166bc187dc76..000000000000 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_utils.cpp +++ /dev/null @@ -1,289 +0,0 @@ -#include "ts_callback_utils.hpp" - -#include -#include - -#include "barretenberg/serialize/msgpack.hpp" -#include "barretenberg/serialize/msgpack_impl/msgpack_impl.hpp" - -namespace bb::nodejs { - -std::string extract_error_from_napi_value(const Napi::CallbackInfo& cb_info) -{ - if (cb_info.Length() > 0) { - if (cb_info[0].IsString()) { - return cb_info[0].As().Utf8Value(); - } - if (cb_info[0].IsObject()) { - auto err_obj = cb_info[0].As(); - auto msg = err_obj.Get("message"); - if (msg.IsString()) { - return msg.As().Utf8Value(); - } - } - } - return "Unknown error from TypeScript"; -} - -Napi::Function create_buffer_resolve_handler(Napi::Env env, std::shared_ptr cb_results) -{ - // Capture shared_ptr by value to ensure CallbackResults outlives the Promise handler. - // This prevents use-after-free when timeouts occur before the Promise resolves. - return Napi::Function::New( - env, - [cb_results](const Napi::CallbackInfo& cb_info) -> Napi::Value { - Napi::Env env = cb_info.Env(); - try { - // Check if first arg is undefined or null - if (cb_info.Length() > 0 && !cb_info[0].IsUndefined() && !cb_info[0].IsNull()) { - // Check if the first argument is a buffer - if (cb_info[0].IsBuffer()) { - auto buffer = cb_info[0].As>(); - std::vector vec(buffer.Data(), buffer.Data() + buffer.Length()); - cb_results->result_promise.set_value(std::move(vec)); - } else { - cb_results->error_message = "Callback returned non-Buffer value"; - cb_results->result_promise.set_value(std::nullopt); - } - } else { - // Got undefined/null - not found - cb_results->result_promise.set_value(std::nullopt); - } - } catch (const std::exception& e) { - cb_results->error_message = std::string("Exception in resolve handler: ") + e.what(); - cb_results->result_promise.set_value(std::nullopt); - } - return env.Undefined(); - }, - "resolveHandler"); -} - -Napi::Function create_string_resolve_handler(Napi::Env env, std::shared_ptr cb_results) -{ - // Capture shared_ptr by value to ensure CallbackResults outlives the Promise handler. - return Napi::Function::New( - env, - [cb_results](const Napi::CallbackInfo& cb_info) -> Napi::Value { - Napi::Env env = cb_info.Env(); - try { - // Check if first arg is undefined or null - if (cb_info.Length() > 0 && !cb_info[0].IsUndefined() && !cb_info[0].IsNull()) { - // Check if the first argument is a string - if (cb_info[0].IsString()) { - std::string name = cb_info[0].As().Utf8Value(); - std::vector vec(name.begin(), name.end()); - cb_results->result_promise.set_value(std::move(vec)); - } else { - cb_results->error_message = "Callback returned non-string value"; - cb_results->result_promise.set_value(std::nullopt); - } - } else { - // Got undefined/null - not found - cb_results->result_promise.set_value(std::nullopt); - } - } catch (const std::exception& e) { - cb_results->error_message = std::string("Exception in resolve handler: ") + e.what(); - cb_results->result_promise.set_value(std::nullopt); - } - return env.Undefined(); - }, - "resolveHandler"); -} - -Napi::Function create_void_resolve_handler(Napi::Env env, std::shared_ptr cb_results) -{ - // Capture shared_ptr by value to ensure CallbackResults outlives the Promise handler. - return Napi::Function::New( - env, - [cb_results](const Napi::CallbackInfo& cb_info) -> Napi::Value { - cb_results->result_promise.set_value(std::nullopt); - return cb_info.Env().Undefined(); - }, - "resolveHandler"); -} - -Napi::Function create_reject_handler(Napi::Env env, std::shared_ptr cb_results) -{ - // Capture shared_ptr by value to ensure CallbackResults outlives the Promise handler. - return Napi::Function::New( - env, - [cb_results](const Napi::CallbackInfo& cb_info) -> Napi::Value { - cb_results->error_message = extract_error_from_napi_value(cb_info); - cb_results->result_promise.set_value(std::nullopt); - return cb_info.Env().Undefined(); - }, - "rejectHandler"); -} - -void attach_promise_handlers(Napi::Promise promise, Napi::Function resolve_handler, Napi::Function reject_handler) -{ - auto then_prop = promise.Get("then"); - if (!then_prop.IsFunction()) { - throw std::runtime_error("Promise does not have .then() method"); - } - - auto then_fn = then_prop.As(); - then_fn.Call(promise, { resolve_handler, reject_handler }); -} - -template std::vector serialize_to_msgpack(const T& data) -{ - msgpack::sbuffer buffer; - msgpack::pack(buffer, data); - return std::vector(buffer.data(), buffer.data() + buffer.size()); -} - -template T deserialize_from_msgpack(const std::vector& data, const std::string& type_name) -{ - try { - T result; - msgpack::object_handle obj_handle = msgpack::unpack(reinterpret_cast(data.data()), data.size()); - msgpack::object obj = obj_handle.get(); - obj.convert(result); - return result; - } catch (const std::exception& e) { - throw std::runtime_error(std::string("Failed to deserialize ") + type_name + ": " + e.what()); - } -} - -std::optional> invoke_ts_callback_with_promise( - const Napi::ThreadSafeFunction& callback, - const std::string& operation_name, - std::function)> call_js_function, - std::chrono::seconds timeout) -{ - // Create promise/future pair for synchronization. - // The shared_ptr is passed to call_js_function which MUST capture it in Promise handlers. - // This ensures CallbackResults outlives the Promise, even if we timeout and return early. - auto callback_data = std::make_shared(); - auto future = callback_data->result_promise.get_future(); - - // Call TypeScript callback on the JS main thread. - // We pass the shared_ptr to the call_js_function so it can be captured by Promise handlers. - auto status = callback.BlockingCall( - callback_data.get(), - [call_js_function, callback_data](Napi::Env env, Napi::Function js_callback, CallbackResults* /*cb_results*/) { - try { - // Call the TypeScript function with the shared_ptr (not raw pointer). - // The call_js_function MUST capture this shared_ptr in Promise handlers. - call_js_function(env, js_callback, callback_data); - - } catch (const std::exception& e) { - callback_data->error_message = std::string("Exception calling TypeScript: ") + e.what(); - callback_data->result_promise.set_value(std::nullopt); - } - }); - - if (status != napi_ok) { - throw std::runtime_error("Failed to invoke TypeScript callback for " + operation_name); - } - - // Wait for the promise to be fulfilled (with timeout). - // If timeout occurs, we throw but callback_data stays alive via shared_ptr in Promise handlers. - auto wait_status = future.wait_for(timeout); - if (wait_status == std::future_status::timeout) { - throw std::runtime_error("Timeout waiting for TypeScript callback for " + operation_name); - } - - // Get the result - auto result_data = future.get(); - - // Check for errors - if (!callback_data->error_message.empty()) { - throw std::runtime_error("Error from TypeScript callback: " + callback_data->error_message); - } - - return result_data; -} - -std::optional> invoke_single_string_callback(const Napi::ThreadSafeFunction& callback, - const std::string& input_str, - const std::string& operation_name) -{ - return invoke_ts_callback_with_promise( - callback, - operation_name, - [input_str](Napi::Env env, Napi::Function js_callback, std::shared_ptr cb_results) { - auto js_input = Napi::String::New(env, input_str); - auto js_result = js_callback.Call({ js_input }); - - if (!js_result.IsPromise()) { - cb_results->error_message = "TypeScript callback did not return a Promise"; - cb_results->result_promise.set_value(std::nullopt); - return; - } - - auto promise = js_result.As(); - // Pass shared_ptr to handlers so CallbackResults outlives the Promise - auto resolve_handler = create_buffer_resolve_handler(env, cb_results); - auto reject_handler = create_reject_handler(env, cb_results); - attach_promise_handlers(promise, resolve_handler, reject_handler); - }); -} - -std::optional> invoke_double_string_callback(const Napi::ThreadSafeFunction& callback, - const std::string& input_str1, - const std::string& input_str2, - const std::string& operation_name) -{ - return invoke_ts_callback_with_promise( - callback, - operation_name, - [input_str1, - input_str2](Napi::Env env, Napi::Function js_callback, std::shared_ptr cb_results) { - auto js_input1 = Napi::String::New(env, input_str1); - auto js_input2 = Napi::String::New(env, input_str2); - auto js_result = js_callback.Call({ js_input1, js_input2 }); - - if (!js_result.IsPromise()) { - cb_results->error_message = "TypeScript callback did not return a Promise"; - cb_results->result_promise.set_value(std::nullopt); - return; - } - - auto promise = js_result.As(); - // Pass shared_ptr to handlers so CallbackResults outlives the Promise - auto resolve_handler = create_string_resolve_handler(env, cb_results); - auto reject_handler = create_reject_handler(env, cb_results); - attach_promise_handlers(promise, resolve_handler, reject_handler); - }); -} - -void invoke_buffer_void_callback(const Napi::ThreadSafeFunction& callback, - std::vector buffer_data, - const std::string& operation_name) -{ - auto result = invoke_ts_callback_with_promise( - callback, - operation_name, - [buffer_data = std::move(buffer_data)]( - Napi::Env env, Napi::Function js_callback, std::shared_ptr cb_results) { - auto js_buffer = Napi::Buffer::Copy(env, buffer_data.data(), buffer_data.size()); - auto js_result = js_callback.Call({ js_buffer }); - - if (!js_result.IsPromise()) { - cb_results->error_message = "TypeScript callback did not return a Promise"; - cb_results->result_promise.set_value(std::nullopt); - return; - } - - auto promise = js_result.As(); - // Pass shared_ptr to handlers so CallbackResults outlives the Promise - auto resolve_handler = create_void_resolve_handler(env, cb_results); - auto reject_handler = create_reject_handler(env, cb_results); - attach_promise_handlers(promise, resolve_handler, reject_handler); - }); - - // For void callbacks, we just need to ensure no errors occurred - // The result itself is ignored (will be nullopt for void) -} - -// Explicit template instantiations for types used in this codebase -template std::vector serialize_to_msgpack(const bb::avm2::ContractDeploymentData& data); -template bb::avm2::ContractInstance deserialize_from_msgpack(const std::vector& data, - const std::string& type_name); -template bb::avm2::ContractClass deserialize_from_msgpack(const std::vector& data, - const std::string& type_name); -template bb::avm2::FF deserialize_from_msgpack(const std::vector& data, const std::string& type_name); - -} // namespace bb::nodejs diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_utils.hpp b/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_utils.hpp deleted file mode 100644 index acadc2251130..000000000000 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_utils.hpp +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "barretenberg/vm2/common/aztec_types.hpp" - -namespace bb::nodejs { - -/** - * @brief Helper struct to pass data between C++ worker thread and JS main thread - */ -struct CallbackResults { - std::promise>> result_promise; - std::string error_message; -}; - -/** - * @brief Extracts error message from a Napi value (string or Error object) - */ -std::string extract_error_from_napi_value(const Napi::CallbackInfo& cb_info); - -/** - * @brief Creates a resolve handler for promises that return Buffer | undefined - * @note Takes shared_ptr to ensure CallbackResults outlives the Promise handler - */ -Napi::Function create_buffer_resolve_handler(Napi::Env env, std::shared_ptr cb_results); - -/** - * @brief Creates a resolve handler for promises that return string | undefined - * @note Takes shared_ptr to ensure CallbackResults outlives the Promise handler - */ -Napi::Function create_string_resolve_handler(Napi::Env env, std::shared_ptr cb_results); - -/** - * @brief Creates a resolve handler for promises that return void - * @note Takes shared_ptr to ensure CallbackResults outlives the Promise handler - */ -Napi::Function create_void_resolve_handler(Napi::Env env, std::shared_ptr cb_results); - -/** - * @brief Creates a reject handler for promises - * @note Takes shared_ptr to ensure CallbackResults outlives the Promise handler - */ -Napi::Function create_reject_handler(Napi::Env env, std::shared_ptr cb_results); - -/** - * @brief Attaches resolve and reject handlers to a promise - */ -void attach_promise_handlers(Napi::Promise promise, Napi::Function resolve_handler, Napi::Function reject_handler); - -/** - * @brief Serializes data to msgpack format - */ -template std::vector serialize_to_msgpack(const T& data); - -/** - * @brief Deserializes msgpack data to a specific type - */ -template T deserialize_from_msgpack(const std::vector& data, const std::string& type_name); - -/** - * @brief Generic callback invoker that handles the full BlockingCall pattern - * - * This template function encapsulates the entire promise-based async callback flow: - * 1. Creates promise/future synchronization - * 2. Invokes JS callback via BlockingCall - * 3. Handles promise resolution/rejection - * 4. Waits with timeout - * 5. Returns optional result - * - * @note The call_js_function receives a shared_ptr to CallbackResults. The shared_ptr MUST be - * captured by the Promise handlers to ensure the CallbackResults outlives the Promise. - * This prevents use-after-free when timeouts occur before the Promise resolves. - */ -std::optional> invoke_ts_callback_with_promise( - const Napi::ThreadSafeFunction& callback, - const std::string& operation_name, - std::function)> call_js_function, - std::chrono::seconds timeout = std::chrono::seconds(60)); - -/** - * @brief Helper for callbacks that take a single string argument and return Buffer | undefined - */ -std::optional> invoke_single_string_callback(const Napi::ThreadSafeFunction& callback, - const std::string& input_str, - const std::string& operation_name); - -/** - * @brief Helper for callbacks that take two string arguments and return string | undefined - */ -std::optional> invoke_double_string_callback(const Napi::ThreadSafeFunction& callback, - const std::string& input_str1, - const std::string& input_str2, - const std::string& operation_name); - -/** - * @brief Helper for callbacks that take a buffer and return void - */ -void invoke_buffer_void_callback(const Napi::ThreadSafeFunction& callback, - std::vector buffer_data, - const std::string& operation_name); - -/** - * @brief Converts an FF (field element) to a hex string - */ -std::string ff_to_string(const bb::avm2::FF& value); - -} // namespace bb::nodejs diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/init_module.cpp b/barretenberg/cpp/src/barretenberg/nodejs_module/init_module.cpp index f3c7a3255ae5..552f2ab7bdb6 100644 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/init_module.cpp +++ b/barretenberg/cpp/src/barretenberg/nodejs_module/init_module.cpp @@ -1,25 +1,16 @@ -#include "barretenberg/nodejs_module/avm_simulate/avm_simulate_napi.hpp" -#include "barretenberg/nodejs_module/lmdb_store/lmdb_store_wrapper.hpp" #include "barretenberg/nodejs_module/msgpack_client/msgpack_client_async.hpp" #include "barretenberg/nodejs_module/msgpack_client/msgpack_client_wrapper.hpp" -#include "barretenberg/nodejs_module/world_state/world_state.hpp" #include "napi.h" +// nodejs_module.node now exposes only the SHM transport. All other former NAPI +// classes (LMDBStore, NativeAvm, NativeWorldState) have been moved out-of-process +// into aztec-{kvdb,avm,wsdb}. This addon is a thin transport library. Napi::Object Init(Napi::Env env, Napi::Object exports) { - exports.Set(Napi::String::New(env, "WorldState"), bb::nodejs::WorldStateWrapper::get_class(env)); - exports.Set(Napi::String::New(env, "LMDBStore"), bb::nodejs::lmdb_store::LMDBStoreWrapper::get_class(env)); exports.Set(Napi::String::New(env, "MsgpackClient"), bb::nodejs::msgpack_client::MsgpackClientWrapper::get_class(env)); exports.Set(Napi::String::New(env, "MsgpackClientAsync"), bb::nodejs::msgpack_client::MsgpackClientAsync::get_class(env)); - exports.Set(Napi::String::New(env, "avmSimulate"), Napi::Function::New(env, bb::nodejs::AvmSimulateNapi::simulate)); - exports.Set(Napi::String::New(env, "avmSimulateWithHintedDbs"), - Napi::Function::New(env, bb::nodejs::AvmSimulateNapi::simulateWithHintedDbs)); - exports.Set(Napi::String::New(env, "createCancellationToken"), - Napi::Function::New(env, bb::nodejs::AvmSimulateNapi::createCancellationToken)); - exports.Set(Napi::String::New(env, "cancelSimulation"), - Napi::Function::New(env, bb::nodejs::AvmSimulateNapi::cancelSimulation)); return exports; } diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/lmdb_store/lmdb_store_message.hpp b/barretenberg/cpp/src/barretenberg/nodejs_module/lmdb_store/lmdb_store_message.hpp deleted file mode 100644 index b63bd1849f7d..000000000000 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/lmdb_store/lmdb_store_message.hpp +++ /dev/null @@ -1,144 +0,0 @@ -#pragma once -#include "barretenberg/lmdblib/types.hpp" -#include "barretenberg/messaging/header.hpp" -#include "barretenberg/serialize/msgpack.hpp" -#include "msgpack/adaptor/define_decl.hpp" -#include -#include -#include - -namespace bb::nodejs::lmdb_store { - -using namespace bb::messaging; - -enum LMDBStoreMessageType { - OPEN_DATABASE = FIRST_APP_MSG_TYPE, - - GET, - HAS, - - START_CURSOR, - ADVANCE_CURSOR, - ADVANCE_CURSOR_COUNT, - CLOSE_CURSOR, - - BATCH, - - STATS, - - CLOSE, - COPY_STORE, -}; - -struct OpenDatabaseRequest { - std::string db; - std::optional uniqueKeys; - SERIALIZATION_FIELDS(db, uniqueKeys); -}; - -struct GetRequest { - lmdblib::KeysVector keys; - std::string db; - SERIALIZATION_FIELDS(keys, db); -}; - -struct GetResponse { - lmdblib::OptionalValuesVector values; - SERIALIZATION_FIELDS(values); -}; - -struct HasRequest { - // std::map> entries; - lmdblib::KeyOptionalValuesVector entries; - std::string db; - SERIALIZATION_FIELDS(entries, db); -}; - -struct HasResponse { - // std::map exists; - std::vector exists; - SERIALIZATION_FIELDS(exists); -}; - -struct Batch { - lmdblib::KeyDupValuesVector addEntries; - lmdblib::KeyOptionalValuesVector removeEntries; - - SERIALIZATION_FIELDS(addEntries, removeEntries); -}; - -struct BatchRequest { - std::map batches; - SERIALIZATION_FIELDS(batches); -}; - -struct StartCursorRequest { - lmdblib::Key key; - std::optional reverse; - std::optional count; - std::optional onePage; - std::string db; - SERIALIZATION_FIELDS(key, reverse, count, onePage, db); -}; - -struct StartCursorResponse { - std::optional cursor; - lmdblib::KeyDupValuesVector entries; - SERIALIZATION_FIELDS(cursor, entries); -}; - -struct AdvanceCursorRequest { - uint64_t cursor; - std::optional count; - SERIALIZATION_FIELDS(cursor, count); -}; - -struct AdvanceCursorCountRequest { - uint64_t cursor; - lmdblib::Key endKey; - SERIALIZATION_FIELDS(cursor, endKey); -}; - -struct CloseCursorRequest { - uint64_t cursor; - SERIALIZATION_FIELDS(cursor); -}; - -struct AdvanceCursorResponse { - lmdblib::KeyDupValuesVector entries; - bool done; - SERIALIZATION_FIELDS(entries, done); -}; - -struct AdvanceCursorCountResponse { - uint64_t count; - bool done; - SERIALIZATION_FIELDS(count, done); -}; - -struct BoolResponse { - bool ok; - SERIALIZATION_FIELDS(ok); -}; - -struct BatchResponse { - uint64_t durationNs; - SERIALIZATION_FIELDS(durationNs); -}; - -struct StatsResponse { - std::vector stats; - uint64_t dbMapSizeBytes; - uint64_t dbPhysicalFileSizeBytes; - SERIALIZATION_FIELDS(stats, dbMapSizeBytes, dbPhysicalFileSizeBytes); -}; - -struct CopyStoreRequest { - std::string dstPath; - std::optional compact; - SERIALIZATION_FIELDS(dstPath, compact); -}; - -} // namespace bb::nodejs::lmdb_store - -MSGPACK_ADD_ENUM(bb::nodejs::lmdb_store::LMDBStoreMessageType) diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/lmdb_store/lmdb_store_wrapper.cpp b/barretenberg/cpp/src/barretenberg/nodejs_module/lmdb_store/lmdb_store_wrapper.cpp deleted file mode 100644 index 4aea32c58f58..000000000000 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/lmdb_store/lmdb_store_wrapper.cpp +++ /dev/null @@ -1,286 +0,0 @@ -#include "barretenberg/nodejs_module/lmdb_store/lmdb_store_wrapper.hpp" -#include "barretenberg/lmdblib/lmdb_store.hpp" -#include "barretenberg/lmdblib/types.hpp" -#include "barretenberg/nodejs_module/lmdb_store/lmdb_store_message.hpp" -#include "napi.h" -#include -#include -#include -#include -#include -#include -#include - -using namespace bb::nodejs; -using namespace bb::nodejs::lmdb_store; - -const uint64_t DEFAULT_MAP_SIZE = 1024UL * 1024; -const uint64_t DEFAULT_MAX_READERS = 16; -const uint64_t DEFAULT_CURSOR_PAGE_SIZE = 10; - -LMDBStoreWrapper::LMDBStoreWrapper(const Napi::CallbackInfo& info) - : ObjectWrap(info) -{ - Napi::Env env = info.Env(); - - size_t data_dir_index = 0; - std::string data_dir; - if (info.Length() > data_dir_index && info[data_dir_index].IsString()) { - data_dir = info[data_dir_index].As(); - } else { - throw Napi::TypeError::New(env, "Directory needs to be a string"); - } - - size_t map_size_index = 1; - uint64_t map_size = DEFAULT_MAP_SIZE; - if (info.Length() > map_size_index) { - if (info[map_size_index].IsNumber()) { - // Int64Value is the widest integer accessor in N-API (no Uint64Value exists) - int64_t val = info[map_size_index].As().Int64Value(); - if (val <= 0) { - throw Napi::TypeError::New(env, "Map size must be a positive number"); - } - map_size = static_cast(val); - } else { - throw Napi::TypeError::New(env, "Map size must be a number or an object"); - } - } - - size_t max_readers_index = 2; - uint max_readers = DEFAULT_MAX_READERS; - if (info.Length() > max_readers_index) { - if (info[max_readers_index].IsNumber()) { - max_readers = info[max_readers_index].As().Uint32Value(); - } else if (!info[max_readers_index].IsUndefined()) { - throw Napi::TypeError::New(env, "The number of readers must be a number"); - } - } - - _store = std::make_unique(data_dir, map_size, max_readers, 2); - - _msg_processor.register_handler(LMDBStoreMessageType::OPEN_DATABASE, this, &LMDBStoreWrapper::open_database); - - _msg_processor.register_handler(LMDBStoreMessageType::GET, this, &LMDBStoreWrapper::get); - _msg_processor.register_handler(LMDBStoreMessageType::HAS, this, &LMDBStoreWrapper::has); - - _msg_processor.register_handler(LMDBStoreMessageType::START_CURSOR, this, &LMDBStoreWrapper::start_cursor); - _msg_processor.register_handler(LMDBStoreMessageType::ADVANCE_CURSOR, this, &LMDBStoreWrapper::advance_cursor); - _msg_processor.register_handler( - LMDBStoreMessageType::ADVANCE_CURSOR_COUNT, this, &LMDBStoreWrapper::advance_cursor_count); - _msg_processor.register_handler(LMDBStoreMessageType::CLOSE_CURSOR, this, &LMDBStoreWrapper::close_cursor); - - _msg_processor.register_handler(LMDBStoreMessageType::BATCH, this, &LMDBStoreWrapper::batch); - - _msg_processor.register_handler(LMDBStoreMessageType::STATS, this, &LMDBStoreWrapper::get_stats); - - // The close operation requires exclusive execution, no other operations can be run concurrently with it - _msg_processor.register_handler(LMDBStoreMessageType::CLOSE, this, &LMDBStoreWrapper::close, true); - - _msg_processor.register_handler(LMDBStoreMessageType::COPY_STORE, this, &LMDBStoreWrapper::copy_store, true); -} - -Napi::Value LMDBStoreWrapper::call(const Napi::CallbackInfo& info) -{ - return _msg_processor.process_message(info); -} - -Napi::Function LMDBStoreWrapper::get_class(Napi::Env env) -{ - return DefineClass(env, - "Store", - { - LMDBStoreWrapper::InstanceMethod("call", &LMDBStoreWrapper::call), - }); -} - -// Simply verify that the store is still valid and that close has not been called -void LMDBStoreWrapper::verify_store() const -{ - if (_store) { - return; - } - throw std::runtime_error(format("LMDB store unavailable, was close already called?")); -} - -BoolResponse LMDBStoreWrapper::open_database(const OpenDatabaseRequest& req) -{ - verify_store(); - _store->open_database(req.db, !req.uniqueKeys.value_or(true)); - return { true }; -} - -GetResponse LMDBStoreWrapper::get(const GetRequest& req) -{ - verify_store(); - lmdblib::OptionalValuesVector vals; - lmdblib::KeysVector keys = req.keys; - _store->get(keys, vals, req.db); - return { vals }; -} - -HasResponse LMDBStoreWrapper::has(const HasRequest& req) -{ - verify_store(); - std::vector exists; - _store->has(req.entries, exists, req.db); - return { exists }; -} - -StartCursorResponse LMDBStoreWrapper::start_cursor(const StartCursorRequest& req) -{ - verify_store(); - bool reverse = req.reverse.value_or(false); - uint32_t page_size = req.count.value_or(DEFAULT_CURSOR_PAGE_SIZE); - bool one_page = req.onePage.value_or(false); - lmdblib::Key key = req.key; - - auto tx = _store->create_shared_read_transaction(); - lmdblib::LMDBCursor::SharedPtr cursor = _store->create_cursor(tx, req.db); - bool start_ok = cursor->set_at_key(key); - - if (!start_ok) { - // we couldn't find exactly the requested key. Find the next biggest one. - start_ok = cursor->set_at_key_gte(key); - // if we found a key that's greater _and_ we want to go in reverse order - // then we're actually outside the requested bounds, we need to go back one position - if (start_ok && reverse) { - lmdblib::KeyDupValuesVector entries; - // read_prev returns `true` if there's nothing more to read - // turn this into a "not ok" because there's nothing in the db for this cursor to read - start_ok = !cursor->read_prev(1, entries); - } else if (!start_ok && reverse) { - // we couldn't find a key greater than our starting point _and_ we want to go in reverse.. - // then we start at the end of the database (the client requested to start at a key greater than anything in - // the DB) - start_ok = cursor->set_at_end(); - } - - // in case we're iterating in ascending order and we can't find the exact key or one that's greater than it - // then that means theren's nothing in the DB for the cursor to read - } - - // we couldn't find a starting position - if (!start_ok) { - return { std::nullopt, {} }; - } - - auto [done, first_page] = _advance_cursor(*cursor, reverse, page_size); - // cursor finished after reading a single page or client only wanted the first page - if (done || one_page) { - return { std::nullopt, first_page }; - } - - auto cursor_id = cursor->id(); - { - std::lock_guard lock(_cursor_mutex); - _cursors[cursor_id] = { cursor, reverse }; - } - - return { cursor_id, first_page }; -} - -BoolResponse LMDBStoreWrapper::close_cursor(const CloseCursorRequest& req) -{ - { - std::lock_guard lock(_cursor_mutex); - _cursors.erase(req.cursor); - } - return { true }; -} - -AdvanceCursorResponse LMDBStoreWrapper::advance_cursor(const AdvanceCursorRequest& req) -{ - CursorData data; - - { - std::lock_guard lock(_cursor_mutex); - data = _cursors.at(req.cursor); - } - - uint32_t page_size = req.count.value_or(DEFAULT_CURSOR_PAGE_SIZE); - auto [done, entries] = _advance_cursor(*data.cursor, data.reverse, page_size); - return { entries, done }; -} - -AdvanceCursorCountResponse LMDBStoreWrapper::advance_cursor_count(const AdvanceCursorCountRequest& req) -{ - CursorData data; - - { - std::lock_guard lock(_cursor_mutex); - data = _cursors.at(req.cursor); - } - - auto [done, count] = _advance_cursor_count(*data.cursor, data.reverse, req.endKey); - return { count, done }; -} - -BatchResponse LMDBStoreWrapper::batch(const BatchRequest& req) -{ - verify_store(); - std::vector batches; - batches.reserve(req.batches.size()); - - for (const auto& data : req.batches) { - lmdblib::LMDBStore::PutData batch{ data.second.addEntries, data.second.removeEntries, data.first }; - batches.push_back(batch); - } - - auto start = std::chrono::high_resolution_clock::now(); - _store->put(batches); - auto end = std::chrono::high_resolution_clock::now(); - std::chrono::duration duration_ns = end - start; - - return { duration_ns.count() }; -} - -StatsResponse LMDBStoreWrapper::get_stats() -{ - verify_store(); - std::vector stats; - auto [map_size, physical_file_size] = _store->get_stats(stats); - return { stats, map_size, physical_file_size }; -} - -BoolResponse LMDBStoreWrapper::close() -{ - // prevent this store from receiving further messages - _msg_processor.close(); - - { - // close all of the open read cursors - std::lock_guard cursors(_cursor_mutex); - _cursors.clear(); - } - - // and finally close the database handle - _store.reset(nullptr); - - return { true }; -} - -BoolResponse LMDBStoreWrapper::copy_store(const CopyStoreRequest& req) -{ - verify_store(); - _store->copy_store(req.dstPath, req.compact.value_or(false)); - - return { true }; -} - -std::pair LMDBStoreWrapper::_advance_cursor(const lmdblib::LMDBCursor& cursor, - bool reverse, - uint64_t page_size) -{ - lmdblib::KeyDupValuesVector entries; - bool done = reverse ? cursor.read_prev(page_size, entries) : cursor.read_next(page_size, entries); - return std::make_pair(done, entries); -} - -std::pair LMDBStoreWrapper::_advance_cursor_count(const lmdblib::LMDBCursor& cursor, - bool reverse, - const lmdblib::Key& end_key) -{ - uint64_t count = 0; - bool done = reverse ? cursor.count_until_prev(end_key, count) : cursor.count_until_next(end_key, count); - return std::make_pair(done, count); -} diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/lmdb_store/lmdb_store_wrapper.hpp b/barretenberg/cpp/src/barretenberg/nodejs_module/lmdb_store/lmdb_store_wrapper.hpp deleted file mode 100644 index 4fea656edc5b..000000000000 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/lmdb_store/lmdb_store_wrapper.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include "barretenberg/lmdblib/lmdb_cursor.hpp" -#include "barretenberg/lmdblib/lmdb_store.hpp" -#include "barretenberg/lmdblib/types.hpp" -#include "barretenberg/messaging/dispatcher.hpp" -#include "barretenberg/messaging/header.hpp" -#include "barretenberg/nodejs_module/lmdb_store/lmdb_store_message.hpp" -#include "barretenberg/nodejs_module/util/message_processor.hpp" -#include -#include -#include -#include - -namespace bb::nodejs::lmdb_store { - -struct CursorData { - lmdblib::LMDBCursor::SharedPtr cursor; - bool reverse; -}; -/** - * @brief Manages the interaction between the JavaScript runtime and the LMDB instance. - */ -class LMDBStoreWrapper : public Napi::ObjectWrap { - public: - LMDBStoreWrapper(const Napi::CallbackInfo&); - - /** - * @brief The only instance method exposed to JavaScript. Takes a msgpack Message and returns a Promise - */ - Napi::Value call(const Napi::CallbackInfo&); - - static Napi::Function get_class(Napi::Env env); - - private: - std::unique_ptr _store; - - std::mutex _cursor_mutex; - std::unordered_map _cursors; - - bb::nodejs::AsyncMessageProcessor _msg_processor; - - void verify_store() const; - - BoolResponse open_database(const OpenDatabaseRequest& req); - - GetResponse get(const GetRequest& req); - HasResponse has(const HasRequest& req); - - StartCursorResponse start_cursor(const StartCursorRequest& req); - AdvanceCursorResponse advance_cursor(const AdvanceCursorRequest& req); - AdvanceCursorCountResponse advance_cursor_count(const AdvanceCursorCountRequest& req); - BoolResponse close_cursor(const CloseCursorRequest& req); - - BatchResponse batch(const BatchRequest& req); - - StatsResponse get_stats(); - - BoolResponse close(); - - BoolResponse copy_store(const CopyStoreRequest& req); - - static std::pair _advance_cursor(const lmdblib::LMDBCursor& cursor, - bool reverse, - uint64_t page_size); - - static std::pair _advance_cursor_count(const lmdblib::LMDBCursor& cursor, - bool reverse, - const lmdblib::Key& end_key); -}; - -} // namespace bb::nodejs::lmdb_store diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_async.cpp b/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_async.cpp index 5b46c505f5ed..5f67568f8a46 100644 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_async.cpp +++ b/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_async.cpp @@ -1,5 +1,5 @@ #include "barretenberg/nodejs_module/msgpack_client/msgpack_client_async.hpp" -#include "barretenberg/ipc/ipc_client.hpp" +#include "ipc_runtime/ipc_client.hpp" #include "napi.h" #include #include @@ -18,7 +18,7 @@ MsgpackClientAsync::MsgpackClientAsync(const Napi::CallbackInfo& info) std::string shm_name = info[0].As(); // Create shared memory client (SPSC-only, no max_clients needed) - client_ = bb::ipc::IpcClient::create_shm(shm_name); + client_ = ipc::IpcClient::create_shm(shm_name); // Connect to bb server if (!client_->connect()) { diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_async.hpp b/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_async.hpp index 580bde934132..d93be3fc33b8 100644 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_async.hpp +++ b/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_async.hpp @@ -1,6 +1,6 @@ #pragma once -#include "barretenberg/ipc/ipc_client.hpp" +#include "ipc_runtime/ipc_client.hpp" #include "napi.h" #include #include @@ -71,7 +71,7 @@ class MsgpackClientAsync : public Napi::ObjectWrap { void poll_responses(); // IPC client for shared memory communication - std::unique_ptr client_; + std::unique_ptr client_; // Background polling thread (detached - will be cleaned up by OS on process exit) std::thread poll_thread_; diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_wrapper.cpp b/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_wrapper.cpp index b72114a00abf..1799f05d1687 100644 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_wrapper.cpp +++ b/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_wrapper.cpp @@ -1,5 +1,5 @@ #include "barretenberg/nodejs_module/msgpack_client/msgpack_client_wrapper.hpp" -#include "barretenberg/ipc/ipc_client.hpp" +#include "ipc_runtime/ipc_client.hpp" #include "napi.h" #include #include @@ -18,7 +18,7 @@ MsgpackClientWrapper::MsgpackClientWrapper(const Napi::CallbackInfo& info) std::string shm_name = info[0].As(); // Create shared memory client (SPSC-only, no max_clients needed) - client_ = bb::ipc::IpcClient::create_shm(shm_name); + client_ = ipc::IpcClient::create_shm(shm_name); // Connect to bb server if (!client_->connect()) { diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_wrapper.hpp b/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_wrapper.hpp index e426376d9636..7063a7d42635 100644 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_wrapper.hpp +++ b/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_wrapper.hpp @@ -1,6 +1,6 @@ #pragma once -#include "barretenberg/ipc/ipc_client.hpp" +#include "ipc_runtime/ipc_client.hpp" #include "napi.h" #include @@ -32,7 +32,7 @@ class MsgpackClientWrapper : public Napi::ObjectWrap { static Napi::Function get_class(Napi::Env env); private: - std::unique_ptr client_; + std::unique_ptr client_; bool connected_ = false; }; diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/util/async_op.hpp b/barretenberg/cpp/src/barretenberg/nodejs_module/util/async_op.hpp index e91306c7a62c..13a933cd5a81 100644 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/util/async_op.hpp +++ b/barretenberg/cpp/src/barretenberg/nodejs_module/util/async_op.hpp @@ -1,16 +1,11 @@ #pragma once #include "barretenberg/serialize/msgpack_impl.hpp" -#include #include #include #include #include -#ifndef _WIN32 -#include -#endif - namespace bb::nodejs { using async_fn = std::function; @@ -83,19 +78,17 @@ class AsyncOperation : public Napi::AsyncWorker { * a Napi::ThreadSafeFunction, so the event loop returns immediately after launch * and is woken up only when the work is done. * - * Prevent use-after-free: the TSFN callback runs asynchronously on the JS thread - * (napi_tsfn_blocking only blocks on queue insertion, NOT on callback completion). - * Both the worker thread lambda and the callback capture a shared_ptr to keep the - * object alive until both are done. - * - * Usage: `ThreadedAsyncOperation::Run(env, deferred, fn);` + * Usage: `auto* op = new ThreadedAsyncOperation(env, deferred, fn); op->Queue();` + * The object self-destructs after resolving/rejecting the promise. */ -class ThreadedAsyncOperation : public std::enable_shared_from_this { +class ThreadedAsyncOperation { public: ThreadedAsyncOperation(Napi::Env env, std::shared_ptr deferred, async_fn fn) : _fn(std::move(fn)) , _deferred(std::move(deferred)) { + // Create a no-op JS function as the TSFN target — we use the native callback form of BlockingCall + // to resolve/reject the promise, so the JS function is never actually called directly. auto dummy = Napi::Function::New(env, [](const Napi::CallbackInfo&) {}); _completion_tsfn = Napi::ThreadSafeFunction::New(env, dummy, "ThreadedAsyncOpComplete", 0, 1); } @@ -107,88 +100,38 @@ class ThreadedAsyncOperation : public std::enable_shared_from_this deferred, async_fn fn) - { - auto op = std::make_shared(env, std::move(deferred), std::move(fn)); - op->Queue(); - } - - private: - // AVM simulation call chains are deep. Non-main threads get a 512 KB default stack on - // macOS versus 8 MB on Linux, so a default std::thread overflows its stack-guard page and - // aborts with SIGBUS on macOS arm64. The libuv pool that AsyncOperation runs on sizes its - // threads from RLIMIT_STACK, which is why that path never hit this. Pin a generous stack so - // the worker has the same headroom on every platform. - static constexpr size_t WORKER_STACK_SIZE = 32UL * 1024 * 1024; - void Queue() { - auto self = shared_from_this(); - launch_detached_with_large_stack([self]() { + std::thread([this]() { try { - self->_fn(self->_result); - self->_success = true; + _fn(_result); + _success = true; } catch (const std::exception& e) { - self->_error = e.what(); - self->_success = false; + _error = e.what(); + _success = false; } catch (...) { - self->_error = "Unknown exception occurred during threaded async operation"; - self->_success = false; + _error = "Unknown exception occurred during threaded async operation"; + _success = false; } - // Post completion to the JS main thread. The callback captures `self` - // (shared_ptr) so the object stays alive until the callback runs. - // napi_tsfn_blocking only blocks on queue insertion, not on callback - // completion, so we cannot use raw pointers here. - self->_completion_tsfn.BlockingCall([self](Napi::Env env, Napi::Function /*js_callback*/) { - if (self->_success) { - auto buf = Napi::Buffer::Copy(env, self->_result.data(), self->_result.size()); - self->_deferred->Resolve(buf); - } else { - auto error = Napi::Error::New(env, self->_error); - self->_deferred->Reject(error.Value()); - } - self->_completion_tsfn.Release(); - }); - }); - } - - // Launch `work` on a detached OS thread with an explicitly large stack (see WORKER_STACK_SIZE). - // std::thread cannot set a stack size, so use pthreads where available and fall back to a - // default-stack std::thread only if pthread creation is unavailable or fails. - static void launch_detached_with_large_stack(std::function work) - { -#ifndef _WIN32 - pthread_attr_t attr; - if (pthread_attr_init(&attr) == 0) { - pthread_attr_setstacksize(&attr, WORKER_STACK_SIZE); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - auto* heap_work = new std::function(std::move(work)); - pthread_t tid; - int rc = pthread_create( - &tid, - &attr, - [](void* arg) -> void* { - std::unique_ptr> fn(static_cast*>(arg)); - (*fn)(); - return nullptr; - }, - heap_work); - pthread_attr_destroy(&attr); - - if (rc == 0) { - return; - } - - // pthread_create failed; reclaim the work and fall back to a default std::thread. - std::unique_ptr> reclaimed(heap_work); - work = std::move(*reclaimed); - } -#endif - std::thread(std::move(work)).detach(); + // Post completion back to the JS main thread + _completion_tsfn.BlockingCall( + this, [](Napi::Env env, Napi::Function /*js_callback*/, ThreadedAsyncOperation* op) { + if (op->_success) { + auto buf = Napi::Buffer::Copy(env, op->_result.data(), op->_result.size()); + op->_deferred->Resolve(buf); + } else { + auto error = Napi::Error::New(env, op->_error); + op->_deferred->Reject(error.Value()); + } + // Release the TSFN and self-destruct + op->_completion_tsfn.Release(); + delete op; + }); + }).detach(); } + private: async_fn _fn; std::shared_ptr _deferred; Napi::ThreadSafeFunction _completion_tsfn; diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/world_state/world_state.cpp b/barretenberg/cpp/src/barretenberg/nodejs_module/world_state/world_state.cpp deleted file mode 100644 index 848e3b234096..000000000000 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/world_state/world_state.cpp +++ /dev/null @@ -1,977 +0,0 @@ -#include "barretenberg/world_state/world_state.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "barretenberg/crypto/merkle_tree/hash_path.hpp" -#include "barretenberg/crypto/merkle_tree/indexed_tree/indexed_leaf.hpp" -#include "barretenberg/crypto/merkle_tree/response.hpp" -#include "barretenberg/crypto/merkle_tree/types.hpp" -#include "barretenberg/ecc/curves/bn254/fr.hpp" -#include "barretenberg/messaging/header.hpp" -#include "barretenberg/nodejs_module/util/async_op.hpp" -#include "barretenberg/nodejs_module/world_state/world_state.hpp" -#include "barretenberg/nodejs_module/world_state/world_state_message.hpp" -#include "barretenberg/serialize/msgpack.hpp" -#include "barretenberg/world_state/fork.hpp" -#include "barretenberg/world_state/types.hpp" -#include "napi.h" - -using namespace bb::nodejs; -using namespace bb::world_state; -using namespace bb::crypto::merkle_tree; -using namespace bb::messaging; - -const uint64_t DEFAULT_MAP_SIZE = 1024UL * 1024; - -WorldStateWrapper::WorldStateWrapper(const Napi::CallbackInfo& info) - : ObjectWrap(info) -{ - uint64_t thread_pool_size = 16; - std::string data_dir; - std::unordered_map map_size{ - { MerkleTreeId::ARCHIVE, DEFAULT_MAP_SIZE }, - { MerkleTreeId::NULLIFIER_TREE, DEFAULT_MAP_SIZE }, - { MerkleTreeId::NOTE_HASH_TREE, DEFAULT_MAP_SIZE }, - { MerkleTreeId::PUBLIC_DATA_TREE, DEFAULT_MAP_SIZE }, - { MerkleTreeId::L1_TO_L2_MESSAGE_TREE, DEFAULT_MAP_SIZE }, - }; - std::unordered_map tree_height; - std::unordered_map tree_prefill; - std::vector prefilled_public_data; - std::vector tree_ids{ - MerkleTreeId::NULLIFIER_TREE, MerkleTreeId::NOTE_HASH_TREE, MerkleTreeId::PUBLIC_DATA_TREE, - MerkleTreeId::L1_TO_L2_MESSAGE_TREE, MerkleTreeId::ARCHIVE, - }; - uint32_t initial_header_generator_point = 0; - - Napi::Env env = info.Env(); - - size_t data_dir_index = 0; - if (info.Length() > data_dir_index && info[data_dir_index].IsString()) { - data_dir = info[data_dir_index].As(); - } else { - throw Napi::TypeError::New(env, "Directory needs to be a string"); - } - - size_t tree_height_index = 1; - if (info.Length() > tree_height_index && info[tree_height_index].IsObject()) { - Napi::Object obj = info[tree_height_index].As(); - - for (auto tree_id : tree_ids) { - if (obj.Has(tree_id)) { - tree_height[tree_id] = obj.Get(tree_id).As().Uint32Value(); - } - } - } else { - throw Napi::TypeError::New(env, "Tree heights must be a map"); - } - - size_t tree_prefill_index = 2; - if (info.Length() > tree_prefill_index && info[tree_prefill_index].IsObject()) { - Napi::Object obj = info[tree_prefill_index].As(); - - for (auto tree_id : tree_ids) { - if (obj.Has(tree_id)) { - tree_prefill[tree_id] = obj.Get(tree_id).As().Uint32Value(); - } - } - } else { - throw Napi::TypeError::New(env, "Tree prefill must be a map"); - } - - size_t prefilled_public_data_index = 3; - if (info.Length() > prefilled_public_data_index && info[prefilled_public_data_index].IsArray()) { - Napi::Array arr = info[prefilled_public_data_index].As(); - for (uint32_t i = 0; i < arr.Length(); ++i) { - Napi::Array deserialized = arr.Get(i).As(); - if (deserialized.Length() != 2 || !deserialized.Get(uint32_t(0)).IsBuffer() || - !deserialized.Get(uint32_t(1)).IsBuffer()) { - throw Napi::TypeError::New(env, "Prefilled public data value must be a buffer array of size 2"); - } - Napi::Buffer slot_buf = deserialized.Get(uint32_t(0)).As>(); - Napi::Buffer value_buf = deserialized.Get(uint32_t(1)).As>(); - uint256_t slot = 0; - uint256_t value = 0; - for (size_t j = 0; j < 32; ++j) { - slot = (slot << 8) | slot_buf[j]; - value = (value << 8) | value_buf[j]; - } - prefilled_public_data.push_back(PublicDataLeafValue(slot, value)); - } - } else { - throw Napi::TypeError::New(env, "Prefilled public data must be an array"); - } - - size_t initial_header_generator_point_index = 4; - if (info.Length() > initial_header_generator_point_index && info[initial_header_generator_point_index].IsNumber()) { - initial_header_generator_point = info[initial_header_generator_point_index].As().Uint32Value(); - } else { - throw Napi::TypeError::New(env, "Header generator point needs to be a number"); - } - - uint64_t genesis_timestamp = 0; - size_t genesis_timestamp_index = 5; - if (info.Length() > genesis_timestamp_index) { - if (info[genesis_timestamp_index].IsNumber()) { - genesis_timestamp = static_cast(info[genesis_timestamp_index].As().Int64Value()); - } else { - throw Napi::TypeError::New(env, "Genesis timestamp needs to be a number"); - } - } - - // optional parameters - size_t map_size_index = 6; - if (info.Length() > map_size_index) { - if (info[map_size_index].IsObject()) { - Napi::Object obj = info[map_size_index].As(); - - for (auto tree_id : tree_ids) { - if (obj.Has(tree_id)) { - // Int64Value is the widest integer accessor in N-API (no Uint64Value exists) - int64_t val = obj.Get(tree_id).As().Int64Value(); - if (val <= 0) { - throw Napi::TypeError::New(env, "Map size must be a positive number"); - } - map_size[tree_id] = static_cast(val); - } - } - } else if (info[map_size_index].IsNumber()) { - // Int64Value is the widest integer accessor in N-API (no Uint64Value exists) - int64_t val = info[map_size_index].As().Int64Value(); - if (val <= 0) { - throw Napi::TypeError::New(env, "Map size must be a positive number"); - } - uint64_t size = static_cast(val); - for (auto tree_id : tree_ids) { - map_size[tree_id] = size; - } - } else { - throw Napi::TypeError::New(env, "Map size must be a number or an object"); - } - } - - size_t thread_pool_size_index = 7; - if (info.Length() > thread_pool_size_index) { - if (!info[thread_pool_size_index].IsNumber()) { - throw Napi::TypeError::New(env, "Thread pool size must be a number"); - } - - thread_pool_size = info[thread_pool_size_index].As().Uint32Value(); - } - - _ws = std::make_unique(thread_pool_size, - data_dir, - map_size, - tree_height, - tree_prefill, - prefilled_public_data, - initial_header_generator_point, - genesis_timestamp); - - _dispatcher.register_target( - WorldStateMessageType::GET_TREE_INFO, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return get_tree_info(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::GET_STATE_REFERENCE, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return get_state_reference(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::GET_INITIAL_STATE_REFERENCE, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return get_initial_state_reference(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::GET_LEAF_VALUE, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return get_leaf_value(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::GET_LEAF_PREIMAGE, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return get_leaf_preimage(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::GET_SIBLING_PATH, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return get_sibling_path(obj, buffer); }); - - _dispatcher.register_target(WorldStateMessageType::GET_BLOCK_NUMBERS_FOR_LEAF_INDICES, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { - return get_block_numbers_for_leaf_indices(obj, buffer); - }); - - _dispatcher.register_target( - WorldStateMessageType::FIND_LEAF_INDICES, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return find_leaf_indices(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::FIND_SIBLING_PATHS, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return find_sibling_paths(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::FIND_LOW_LEAF, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return find_low_leaf(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::APPEND_LEAVES, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return append_leaves(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::BATCH_INSERT, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return batch_insert(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::SEQUENTIAL_INSERT, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return sequential_insert(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::UPDATE_ARCHIVE, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return update_archive(obj, buffer); }); - - _dispatcher.register_target(WorldStateMessageType::COMMIT, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return commit(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::ROLLBACK, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return rollback(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::SYNC_BLOCK, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return sync_block(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::CREATE_FORK, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return create_fork(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::DELETE_FORK, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return delete_fork(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::FINALIZE_BLOCKS, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return set_finalized(obj, buffer); }); - - _dispatcher.register_target(WorldStateMessageType::UNWIND_BLOCKS, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return unwind(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::REMOVE_HISTORICAL_BLOCKS, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return remove_historical(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::GET_STATUS, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return get_status(obj, buffer); }); - - _dispatcher.register_target(WorldStateMessageType::CLOSE, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return close(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::CREATE_CHECKPOINT, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return checkpoint(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::COMMIT_CHECKPOINT, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return commit_checkpoint(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::REVERT_CHECKPOINT, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return revert_checkpoint(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::COMMIT_ALL_CHECKPOINTS, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return commit_all_checkpoints_to(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::REVERT_ALL_CHECKPOINTS, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return revert_all_checkpoints_to(obj, buffer); }); - - _dispatcher.register_target( - WorldStateMessageType::COPY_STORES, - [this](msgpack::object& obj, msgpack::sbuffer& buffer) { return copy_stores(obj, buffer); }); -} - -Napi::Value WorldStateWrapper::call(const Napi::CallbackInfo& info) -{ - Napi::Env env = info.Env(); - // keep this in a shared pointer so that AsyncOperation can resolve/reject the promise once the execution is - // complete on an separate thread - auto deferred = std::make_shared(env); - - if (info.Length() < 1) { - deferred->Reject(Napi::TypeError::New(env, "Wrong number of arguments").Value()); - } else if (!info[0].IsBuffer()) { - deferred->Reject(Napi::TypeError::New(env, "Argument must be a buffer").Value()); - } else if (!_ws) { - deferred->Reject(Napi::TypeError::New(env, "World state has been closed").Value()); - } else { - auto buffer = info[0].As>(); - size_t length = buffer.Length(); - // we mustn't access the Napi::Env outside of this top-level function - // so copy the data to a variable we own - // and make it a shared pointer so that it doesn't get destroyed as soon as we exit this code block - auto data = std::make_shared>(length); - std::copy_n(buffer.Data(), length, data->data()); - - auto* op = new AsyncOperation(env, deferred, [=, this](msgpack::sbuffer& buf) { - msgpack::object_handle obj_handle = msgpack::unpack(data->data(), length); - msgpack::object obj = obj_handle.get(); - _dispatcher.on_new_data(obj, buf); - }); - - // Napi is now responsible for destroying this object - op->Queue(); - } - - return deferred->Promise(); -} - -Napi::Value WorldStateWrapper::getHandle(const Napi::CallbackInfo& info) -{ - Napi::Env env = info.Env(); - - if (!_ws) { - throw Napi::Error::New(env, "World state has been closed"); - } - - // Return a NAPI External that wraps the raw WorldState pointer - // This allows other NAPI functions to access the WorldState instance - return Napi::External::New(env, _ws.get()); -} - -bool WorldStateWrapper::get_tree_info(msgpack::object& obj, msgpack::sbuffer& buffer) const -{ - TypedMessage request; - obj.convert(request); - auto info = _ws->get_tree_info(request.value.revision, request.value.treeId); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg( - WorldStateMessageType::GET_TREE_INFO, - header, - { request.value.treeId, info.meta.root, info.meta.size, info.meta.depth }); - - msgpack::pack(buffer, resp_msg); - - return true; -} - -bool WorldStateWrapper::get_state_reference(msgpack::object& obj, msgpack::sbuffer& buffer) const -{ - TypedMessage request; - obj.convert(request); - auto state = _ws->get_state_reference(request.value.revision); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg( - WorldStateMessageType::GET_STATE_REFERENCE, header, { state }); - - msgpack::pack(buffer, resp_msg); - - return true; -} - -bool WorldStateWrapper::get_initial_state_reference(msgpack::object& obj, msgpack::sbuffer& buffer) const -{ - HeaderOnlyMessage request; - obj.convert(request); - auto state = _ws->get_initial_state_reference(); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg( - WorldStateMessageType::GET_INITIAL_STATE_REFERENCE, header, { state }); - - msgpack::pack(buffer, resp_msg); - - return true; -} - -bool WorldStateWrapper::get_leaf_value(msgpack::object& obj, msgpack::sbuffer& buffer) const -{ - TypedMessage request; - obj.convert(request); - - switch (request.value.treeId) { - case MerkleTreeId::NOTE_HASH_TREE: - case MerkleTreeId::L1_TO_L2_MESSAGE_TREE: - case MerkleTreeId::ARCHIVE: { - auto leaf = _ws->get_leaf(request.value.revision, request.value.treeId, request.value.leafIndex); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage> resp_msg(WorldStateMessageType::GET_LEAF_VALUE, header, leaf); - msgpack::pack(buffer, resp_msg); - break; - } - - case MerkleTreeId::PUBLIC_DATA_TREE: { - auto leaf = _ws->get_leaf( - request.value.revision, request.value.treeId, request.value.leafIndex); - MsgHeader header(request.header.messageId); - messaging::TypedMessage> resp_msg( - WorldStateMessageType::GET_LEAF_VALUE, header, leaf); - msgpack::pack(buffer, resp_msg); - break; - } - - case MerkleTreeId::NULLIFIER_TREE: { - auto leaf = _ws->get_leaf( - request.value.revision, request.value.treeId, request.value.leafIndex); - MsgHeader header(request.header.messageId); - messaging::TypedMessage> resp_msg( - WorldStateMessageType::GET_LEAF_VALUE, header, leaf); - msgpack::pack(buffer, resp_msg); - break; - } - - default: - throw std::runtime_error("Unsupported tree type"); - } - - return true; -} - -bool WorldStateWrapper::get_leaf_preimage(msgpack::object& obj, msgpack::sbuffer& buffer) const -{ - TypedMessage request; - obj.convert(request); - - MsgHeader header(request.header.messageId); - - switch (request.value.treeId) { - case MerkleTreeId::NULLIFIER_TREE: { - auto leaf = _ws->get_indexed_leaf( - request.value.revision, request.value.treeId, request.value.leafIndex); - messaging::TypedMessage>> resp_msg( - WorldStateMessageType::GET_LEAF_PREIMAGE, header, leaf); - msgpack::pack(buffer, resp_msg); - break; - } - - case MerkleTreeId::PUBLIC_DATA_TREE: { - auto leaf = _ws->get_indexed_leaf( - request.value.revision, request.value.treeId, request.value.leafIndex); - - messaging::TypedMessage>> resp_msg( - WorldStateMessageType::GET_LEAF_PREIMAGE, header, leaf); - msgpack::pack(buffer, resp_msg); - break; - } - - default: - throw std::runtime_error("Unsupported tree type"); - } - - return true; -} - -bool WorldStateWrapper::get_sibling_path(msgpack::object& obj, msgpack::sbuffer& buffer) const -{ - TypedMessage request; - obj.convert(request); - - fr_sibling_path path = _ws->get_sibling_path(request.value.revision, request.value.treeId, request.value.leafIndex); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::GET_SIBLING_PATH, header, path); - - msgpack::pack(buffer, resp_msg); - - return true; -} - -bool WorldStateWrapper::get_block_numbers_for_leaf_indices(msgpack::object& obj, msgpack::sbuffer& buffer) const -{ - TypedMessage request; - obj.convert(request); - - GetBlockNumbersForLeafIndicesResponse response; - _ws->get_block_numbers_for_leaf_indices( - request.value.revision, request.value.treeId, request.value.leafIndices, response.blockNumbers); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg( - WorldStateMessageType::GET_BLOCK_NUMBERS_FOR_LEAF_INDICES, header, response); - - msgpack::pack(buffer, resp_msg); - - return true; -} - -bool WorldStateWrapper::find_leaf_indices(msgpack::object& obj, msgpack::sbuffer& buffer) const -{ - TypedMessage request; - obj.convert(request); - - FindLeafIndicesResponse response; - - switch (request.value.treeId) { - case MerkleTreeId::NOTE_HASH_TREE: - case MerkleTreeId::L1_TO_L2_MESSAGE_TREE: - case MerkleTreeId::ARCHIVE: { - TypedMessage> r1; - obj.convert(r1); - _ws->find_leaf_indices( - request.value.revision, request.value.treeId, r1.value.leaves, response.indices, r1.value.startIndex); - break; - } - - case MerkleTreeId::PUBLIC_DATA_TREE: { - TypedMessage> r2; - obj.convert(r2); - _ws->find_leaf_indices( - request.value.revision, request.value.treeId, r2.value.leaves, response.indices, r2.value.startIndex); - break; - } - case MerkleTreeId::NULLIFIER_TREE: { - TypedMessage> r3; - obj.convert(r3); - _ws->find_leaf_indices( - request.value.revision, request.value.treeId, r3.value.leaves, response.indices, r3.value.startIndex); - break; - } - default: - throw std::runtime_error("Unsupported tree type"); - } - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg( - WorldStateMessageType::FIND_LEAF_INDICES, header, response); - msgpack::pack(buffer, resp_msg); - - return true; -} - -bool WorldStateWrapper::find_sibling_paths(msgpack::object& obj, msgpack::sbuffer& buffer) const -{ - TypedMessage request; - obj.convert(request); - - FindLeafPathsResponse response; - - switch (request.value.treeId) { - case MerkleTreeId::NOTE_HASH_TREE: - case MerkleTreeId::L1_TO_L2_MESSAGE_TREE: - case MerkleTreeId::ARCHIVE: { - TypedMessage> r1; - obj.convert(r1); - _ws->find_sibling_paths(request.value.revision, request.value.treeId, r1.value.leaves, response.paths); - break; - } - - case MerkleTreeId::PUBLIC_DATA_TREE: { - TypedMessage> r2; - obj.convert(r2); - _ws->find_sibling_paths( - request.value.revision, request.value.treeId, r2.value.leaves, response.paths); - break; - } - case MerkleTreeId::NULLIFIER_TREE: { - TypedMessage> r3; - obj.convert(r3); - _ws->find_sibling_paths( - request.value.revision, request.value.treeId, r3.value.leaves, response.paths); - break; - } - default: - throw std::runtime_error("Unsupported tree type"); - } - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg( - WorldStateMessageType::FIND_SIBLING_PATHS, header, response); - msgpack::pack(buffer, resp_msg); - - return true; -} - -bool WorldStateWrapper::find_low_leaf(msgpack::object& obj, msgpack::sbuffer& buffer) const -{ - TypedMessage request; - obj.convert(request); - - GetLowIndexedLeafResponse low_leaf_info = - _ws->find_low_leaf_index(request.value.revision, request.value.treeId, request.value.key); - - MsgHeader header(request.header.messageId); - TypedMessage response( - WorldStateMessageType::FIND_LOW_LEAF, header, { low_leaf_info.is_already_present, low_leaf_info.index }); - msgpack::pack(buffer, response); - - return true; -} - -bool WorldStateWrapper::append_leaves(msgpack::object& obj, msgpack::sbuffer& buf) -{ - TypedMessage request; - obj.convert(request); - - switch (request.value.treeId) { - case MerkleTreeId::NOTE_HASH_TREE: - case MerkleTreeId::L1_TO_L2_MESSAGE_TREE: - case MerkleTreeId::ARCHIVE: { - TypedMessage> r1; - obj.convert(r1); - _ws->append_leaves(r1.value.treeId, r1.value.leaves, r1.value.forkId); - break; - } - case MerkleTreeId::PUBLIC_DATA_TREE: { - TypedMessage> r2; - obj.convert(r2); - _ws->append_leaves(r2.value.treeId, r2.value.leaves, r2.value.forkId); - break; - } - case MerkleTreeId::NULLIFIER_TREE: { - TypedMessage> r3; - obj.convert(r3); - _ws->append_leaves(r3.value.treeId, r3.value.leaves, r3.value.forkId); - break; - } - default: - throw std::runtime_error("Unsupported tree type"); - } - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::APPEND_LEAVES, header, {}); - msgpack::pack(buf, resp_msg); - - return true; -} - -bool WorldStateWrapper::batch_insert(msgpack::object& obj, msgpack::sbuffer& buffer) -{ - TypedMessage request; - obj.convert(request); - - switch (request.value.treeId) { - case MerkleTreeId::PUBLIC_DATA_TREE: { - TypedMessage> r1; - obj.convert(r1); - auto result = _ws->batch_insert_indexed_leaves( - request.value.treeId, r1.value.leaves, r1.value.subtreeDepth, r1.value.forkId); - MsgHeader header(request.header.messageId); - messaging::TypedMessage> resp_msg( - WorldStateMessageType::BATCH_INSERT, header, result); - msgpack::pack(buffer, resp_msg); - - break; - } - case MerkleTreeId::NULLIFIER_TREE: { - TypedMessage> r2; - obj.convert(r2); - auto result = _ws->batch_insert_indexed_leaves( - request.value.treeId, r2.value.leaves, r2.value.subtreeDepth, r2.value.forkId); - MsgHeader header(request.header.messageId); - messaging::TypedMessage> resp_msg( - WorldStateMessageType::BATCH_INSERT, header, result); - msgpack::pack(buffer, resp_msg); - break; - } - default: - throw std::runtime_error("Unsupported tree type"); - } - - return true; -} - -bool WorldStateWrapper::sequential_insert(msgpack::object& obj, msgpack::sbuffer& buffer) -{ - TypedMessage request; - obj.convert(request); - - switch (request.value.treeId) { - case MerkleTreeId::PUBLIC_DATA_TREE: { - TypedMessage> r1; - obj.convert(r1); - auto result = _ws->insert_indexed_leaves( - request.value.treeId, r1.value.leaves, r1.value.forkId); - MsgHeader header(request.header.messageId); - messaging::TypedMessage> resp_msg( - WorldStateMessageType::SEQUENTIAL_INSERT, header, result); - msgpack::pack(buffer, resp_msg); - - break; - } - case MerkleTreeId::NULLIFIER_TREE: { - TypedMessage> r2; - obj.convert(r2); - auto result = _ws->insert_indexed_leaves( - request.value.treeId, r2.value.leaves, r2.value.forkId); - MsgHeader header(request.header.messageId); - messaging::TypedMessage> resp_msg( - WorldStateMessageType::SEQUENTIAL_INSERT, header, result); - msgpack::pack(buffer, resp_msg); - break; - } - default: - throw std::runtime_error("Unsupported tree type"); - } - - return true; -} - -bool WorldStateWrapper::update_archive(msgpack::object& obj, msgpack::sbuffer& buf) -{ - TypedMessage request; - obj.convert(request); - - _ws->update_archive(request.value.blockStateRef, request.value.blockHeaderHash, request.value.forkId); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::UPDATE_ARCHIVE, header, {}); - msgpack::pack(buf, resp_msg); - - return true; -} - -bool WorldStateWrapper::commit(msgpack::object& obj, msgpack::sbuffer& buf) -{ - HeaderOnlyMessage request; - obj.convert(request); - - WorldStateStatusFull status; - _ws->commit(status); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::COMMIT, header, { status }); - msgpack::pack(buf, resp_msg); - - return true; -} - -bool WorldStateWrapper::rollback(msgpack::object& obj, msgpack::sbuffer& buf) -{ - HeaderOnlyMessage request; - obj.convert(request); - - _ws->rollback(); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::ROLLBACK, header, {}); - msgpack::pack(buf, resp_msg); - - return true; -} - -bool WorldStateWrapper::sync_block(msgpack::object& obj, msgpack::sbuffer& buf) -{ - TypedMessage request; - obj.convert(request); - - WorldStateStatusFull status = _ws->sync_block(request.value.blockStateRef, - request.value.blockHeaderHash, - request.value.paddedNoteHashes, - request.value.paddedL1ToL2Messages, - request.value.paddedNullifiers, - request.value.publicDataWrites); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::SYNC_BLOCK, header, { status }); - msgpack::pack(buf, resp_msg); - - return true; -} - -bool WorldStateWrapper::create_fork(msgpack::object& obj, msgpack::sbuffer& buf) -{ - TypedMessage request; - obj.convert(request); - - std::optional blockNumber = - request.value.latest ? std::nullopt : std::optional(request.value.blockNumber); - - uint64_t forkId = _ws->create_fork(blockNumber); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::CREATE_FORK, header, { forkId }); - msgpack::pack(buf, resp_msg); - - return true; -} - -bool WorldStateWrapper::delete_fork(msgpack::object& obj, msgpack::sbuffer& buf) -{ - TypedMessage request; - obj.convert(request); - - _ws->delete_fork(request.value.forkId); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::DELETE_FORK, header, {}); - msgpack::pack(buf, resp_msg); - - return true; -} - -bool WorldStateWrapper::close(msgpack::object& obj, msgpack::sbuffer& buf) -{ - HeaderOnlyMessage request; - obj.convert(request); - - // The only reason this API exists is for testing purposes in TS (e.g. close db, open new db instance to test - // persistence) - _ws.reset(nullptr); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::CLOSE, header, {}); - msgpack::pack(buf, resp_msg); - - return true; -} - -bool WorldStateWrapper::set_finalized(msgpack::object& obj, msgpack::sbuffer& buf) const -{ - TypedMessage request; - obj.convert(request); - WorldStateStatusSummary status = _ws->set_finalized_blocks(request.value.toBlockNumber); - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg( - WorldStateMessageType::FINALIZE_BLOCKS, header, { status }); - msgpack::pack(buf, resp_msg); - - return true; -} - -bool WorldStateWrapper::unwind(msgpack::object& obj, msgpack::sbuffer& buf) const -{ - TypedMessage request; - obj.convert(request); - - WorldStateStatusFull status = _ws->unwind_blocks(request.value.toBlockNumber); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::UNWIND_BLOCKS, header, { status }); - msgpack::pack(buf, resp_msg); - - return true; -} - -bool WorldStateWrapper::remove_historical(msgpack::object& obj, msgpack::sbuffer& buf) const -{ - TypedMessage request; - obj.convert(request); - WorldStateStatusFull status = _ws->remove_historical_blocks(request.value.toBlockNumber); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg( - WorldStateMessageType::REMOVE_HISTORICAL_BLOCKS, header, { status }); - msgpack::pack(buf, resp_msg); - - return true; -} - -bool WorldStateWrapper::checkpoint(msgpack::object& obj, msgpack::sbuffer& buffer) -{ - TypedMessage request; - obj.convert(request); - - uint32_t depth = _ws->checkpoint(request.value.forkId); - - MsgHeader header(request.header.messageId); - CheckpointDepthResponse resp_value{ depth }; - messaging::TypedMessage resp_msg( - WorldStateMessageType::CREATE_CHECKPOINT, header, resp_value); - msgpack::pack(buffer, resp_msg); - - return true; -} - -bool WorldStateWrapper::commit_checkpoint(msgpack::object& obj, msgpack::sbuffer& buffer) -{ - TypedMessage request; - obj.convert(request); - - _ws->commit_checkpoint(request.value.forkId); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::COMMIT_CHECKPOINT, header, {}); - msgpack::pack(buffer, resp_msg); - - return true; -} - -bool WorldStateWrapper::revert_checkpoint(msgpack::object& obj, msgpack::sbuffer& buffer) -{ - TypedMessage request; - obj.convert(request); - - _ws->revert_checkpoint(request.value.forkId); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::REVERT_CHECKPOINT, header, {}); - msgpack::pack(buffer, resp_msg); - - return true; -} - -bool WorldStateWrapper::commit_all_checkpoints_to(msgpack::object& obj, msgpack::sbuffer& buffer) -{ - TypedMessage request; - obj.convert(request); - - _ws->commit_all_checkpoints_to(request.value.forkId, request.value.depth); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::COMMIT_ALL_CHECKPOINTS, header, {}); - msgpack::pack(buffer, resp_msg); - - return true; -} - -bool WorldStateWrapper::revert_all_checkpoints_to(msgpack::object& obj, msgpack::sbuffer& buffer) -{ - TypedMessage request; - obj.convert(request); - - _ws->revert_all_checkpoints_to(request.value.forkId, request.value.depth); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::REVERT_ALL_CHECKPOINTS, header, {}); - msgpack::pack(buffer, resp_msg); - - return true; -} - -bool WorldStateWrapper::get_status(msgpack::object& obj, msgpack::sbuffer& buf) const -{ - HeaderOnlyMessage request; - obj.convert(request); - - WorldStateStatusSummary status; - _ws->get_status_summary(status); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::GET_STATUS, header, { status }); - msgpack::pack(buf, resp_msg); - - return true; -} - -bool WorldStateWrapper::copy_stores(msgpack::object& obj, msgpack::sbuffer& buffer) -{ - TypedMessage request; - obj.convert(request); - - _ws->copy_stores(request.value.dstPath, request.value.compact.value_or(false)); - - MsgHeader header(request.header.messageId); - messaging::TypedMessage resp_msg(WorldStateMessageType::COPY_STORES, header, {}); - msgpack::pack(buffer, resp_msg); - - return true; -} - -Napi::Function WorldStateWrapper::get_class(Napi::Env env) -{ - return DefineClass(env, - "WorldState", - { - WorldStateWrapper::InstanceMethod("call", &WorldStateWrapper::call), - WorldStateWrapper::InstanceMethod("getHandle", &WorldStateWrapper::getHandle), - }); -} diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/world_state/world_state.hpp b/barretenberg/cpp/src/barretenberg/nodejs_module/world_state/world_state.hpp deleted file mode 100644 index cd4f0d02e8e1..000000000000 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/world_state/world_state.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include "barretenberg/messaging/dispatcher.hpp" -#include "barretenberg/nodejs_module/world_state/world_state_message.hpp" -#include "barretenberg/world_state/types.hpp" -#include "barretenberg/world_state/world_state.hpp" -#include -#include -#include - -namespace bb::nodejs { - -/** - * @brief Manages the interaction between the JavaScript runtime and the WorldState class. - */ -class WorldStateWrapper : public Napi::ObjectWrap { - public: - WorldStateWrapper(const Napi::CallbackInfo&); - - /** - * @brief The only instance method exposed to JavaScript. Takes a msgpack Message and returns a Promise - */ - Napi::Value call(const Napi::CallbackInfo&); - - /** - * @brief Get a NAPI External handle to the underlying WorldState pointer. - * This allows other NAPI functions to access the WorldState instance directly. - */ - Napi::Value getHandle(const Napi::CallbackInfo&); - - /** - * @brief Register the WorldStateAddon class with the JavaScript runtime. - */ - static Napi::Function get_class(Napi::Env); - - private: - std::unique_ptr _ws; - bb::messaging::MessageDispatcher _dispatcher; - - bool get_tree_info(msgpack::object& obj, msgpack::sbuffer& buffer) const; - bool get_state_reference(msgpack::object& obj, msgpack::sbuffer& buffer) const; - bool get_initial_state_reference(msgpack::object& obj, msgpack::sbuffer& buffer) const; - - bool get_leaf_value(msgpack::object& obj, msgpack::sbuffer& buffer) const; - bool get_leaf_preimage(msgpack::object& obj, msgpack::sbuffer& buffer) const; - bool get_sibling_path(msgpack::object& obj, msgpack::sbuffer& buffer) const; - bool get_block_numbers_for_leaf_indices(msgpack::object& obj, msgpack::sbuffer& buffer) const; - - bool find_leaf_indices(msgpack::object& obj, msgpack::sbuffer& buffer) const; - bool find_low_leaf(msgpack::object& obj, msgpack::sbuffer& buffer) const; - bool find_sibling_paths(msgpack::object& obj, msgpack::sbuffer& buffer) const; - - bool append_leaves(msgpack::object& obj, msgpack::sbuffer& buffer); - bool batch_insert(msgpack::object& obj, msgpack::sbuffer& buffer); - bool sequential_insert(msgpack::object& obj, msgpack::sbuffer& buffer); - - bool update_archive(msgpack::object& obj, msgpack::sbuffer& buffer); - - bool commit(msgpack::object& obj, msgpack::sbuffer& buffer); - bool rollback(msgpack::object& obj, msgpack::sbuffer& buffer); - - bool sync_block(msgpack::object& obj, msgpack::sbuffer& buffer); - - bool create_fork(msgpack::object& obj, msgpack::sbuffer& buffer); - bool delete_fork(msgpack::object& obj, msgpack::sbuffer& buffer); - - bool close(msgpack::object& obj, msgpack::sbuffer& buffer); - - bool set_finalized(msgpack::object& obj, msgpack::sbuffer& buffer) const; - bool unwind(msgpack::object& obj, msgpack::sbuffer& buffer) const; - bool remove_historical(msgpack::object& obj, msgpack::sbuffer& buffer) const; - - bool get_status(msgpack::object& obj, msgpack::sbuffer& buffer) const; - - bool checkpoint(msgpack::object& obj, msgpack::sbuffer& buffer); - bool commit_checkpoint(msgpack::object& obj, msgpack::sbuffer& buffer); - bool revert_checkpoint(msgpack::object& obj, msgpack::sbuffer& buffer); - bool commit_all_checkpoints_to(msgpack::object& obj, msgpack::sbuffer& buffer); - bool revert_all_checkpoints_to(msgpack::object& obj, msgpack::sbuffer& buffer); - - bool copy_stores(msgpack::object& obj, msgpack::sbuffer& buffer); -}; - -} // namespace bb::nodejs diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/world_state/world_state_message.hpp b/barretenberg/cpp/src/barretenberg/nodejs_module/world_state/world_state_message.hpp deleted file mode 100644 index 8f6b481ad41a..000000000000 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/world_state/world_state_message.hpp +++ /dev/null @@ -1,272 +0,0 @@ -#pragma once -#include "barretenberg/crypto/merkle_tree/hash_path.hpp" -#include "barretenberg/crypto/merkle_tree/indexed_tree/indexed_leaf.hpp" -#include "barretenberg/crypto/merkle_tree/response.hpp" -#include "barretenberg/crypto/merkle_tree/types.hpp" -#include "barretenberg/ecc/curves/bn254/fr.hpp" -#include "barretenberg/messaging/header.hpp" -#include "barretenberg/serialize/msgpack.hpp" -#include "barretenberg/world_state/fork.hpp" -#include "barretenberg/world_state/types.hpp" -#include -#include -#include - -namespace bb::nodejs { - -using namespace bb::messaging; -using namespace bb::world_state; - -enum WorldStateMessageType { - GET_TREE_INFO = FIRST_APP_MSG_TYPE, - GET_STATE_REFERENCE, - GET_INITIAL_STATE_REFERENCE, - - GET_LEAF_VALUE, - GET_LEAF_PREIMAGE, - GET_SIBLING_PATH, - GET_BLOCK_NUMBERS_FOR_LEAF_INDICES, - - FIND_LEAF_INDICES, - FIND_LOW_LEAF, - FIND_SIBLING_PATHS, - - APPEND_LEAVES, - BATCH_INSERT, - SEQUENTIAL_INSERT, - - UPDATE_ARCHIVE, - - COMMIT, - ROLLBACK, - - SYNC_BLOCK, - - CREATE_FORK, - DELETE_FORK, - - FINALIZE_BLOCKS, - UNWIND_BLOCKS, - REMOVE_HISTORICAL_BLOCKS, - - GET_STATUS, - - CREATE_CHECKPOINT, - COMMIT_CHECKPOINT, - REVERT_CHECKPOINT, - COMMIT_ALL_CHECKPOINTS, - REVERT_ALL_CHECKPOINTS, - - COPY_STORES, - - CLOSE = 999, -}; - -struct TreeIdOnlyRequest { - MerkleTreeId treeId; - SERIALIZATION_FIELDS(treeId); -}; - -struct CreateForkRequest { - bool latest; - block_number_t blockNumber; - SERIALIZATION_FIELDS(latest, blockNumber); -}; - -struct CreateForkResponse { - uint64_t forkId; - SERIALIZATION_FIELDS(forkId); -}; - -struct DeleteForkRequest { - uint64_t forkId; - SERIALIZATION_FIELDS(forkId); -}; - -struct ForkIdOnlyRequest { - uint64_t forkId; - SERIALIZATION_FIELDS(forkId); -}; - -struct ForkIdWithDepthRequest { - uint64_t forkId; - uint32_t depth; - SERIALIZATION_FIELDS(forkId, depth); -}; - -struct CheckpointDepthResponse { - uint32_t depth; - SERIALIZATION_FIELDS(depth); -}; - -struct TreeIdAndRevisionRequest { - MerkleTreeId treeId; - WorldStateRevision revision; - SERIALIZATION_FIELDS(treeId, revision); -}; - -struct EmptyResponse { - bool ok{ true }; - SERIALIZATION_FIELDS(ok); -}; - -struct GetTreeInfoRequest { - MerkleTreeId treeId; - WorldStateRevision revision; - SERIALIZATION_FIELDS(treeId, revision); -}; - -struct GetTreeInfoResponse { - MerkleTreeId treeId; - fr root; - index_t size; - uint32_t depth; - SERIALIZATION_FIELDS(treeId, root, size, depth); -}; - -struct GetStateReferenceRequest { - WorldStateRevision revision; - SERIALIZATION_FIELDS(revision); -}; - -struct GetStateReferenceResponse { - StateReference state; - SERIALIZATION_FIELDS(state); -}; - -struct GetInitialStateReferenceResponse { - StateReference state; - SERIALIZATION_FIELDS(state); -}; - -struct GetLeafValueRequest { - MerkleTreeId treeId; - WorldStateRevision revision; - index_t leafIndex; - SERIALIZATION_FIELDS(treeId, revision, leafIndex); -}; - -struct GetLeafPreimageRequest { - MerkleTreeId treeId; - WorldStateRevision revision; - index_t leafIndex; - SERIALIZATION_FIELDS(treeId, revision, leafIndex); -}; - -struct GetSiblingPathRequest { - MerkleTreeId treeId; - WorldStateRevision revision; - index_t leafIndex; - SERIALIZATION_FIELDS(treeId, revision, leafIndex); -}; - -struct GetBlockNumbersForLeafIndicesRequest { - MerkleTreeId treeId; - WorldStateRevision revision; - std::vector leafIndices; - SERIALIZATION_FIELDS(treeId, revision, leafIndices); -}; - -struct GetBlockNumbersForLeafIndicesResponse { - std::vector> blockNumbers; - SERIALIZATION_FIELDS(blockNumbers); -}; - -template struct FindLeafIndicesRequest { - MerkleTreeId treeId; - WorldStateRevision revision; - std::vector leaves; - index_t startIndex; - SERIALIZATION_FIELDS(treeId, revision, leaves, startIndex); -}; - -struct FindLeafIndicesResponse { - std::vector> indices; - SERIALIZATION_FIELDS(indices); -}; - -template struct FindLeafPathsRequest { - MerkleTreeId treeId; - WorldStateRevision revision; - std::vector leaves; - SERIALIZATION_FIELDS(treeId, revision, leaves); -}; - -struct FindLeafPathsResponse { - std::vector> paths; - SERIALIZATION_FIELDS(paths); -}; - -struct FindLowLeafRequest { - MerkleTreeId treeId; - WorldStateRevision revision; - fr key; - SERIALIZATION_FIELDS(treeId, revision, key); -}; - -struct FindLowLeafResponse { - bool alreadyPresent; - index_t index; - SERIALIZATION_FIELDS(alreadyPresent, index); -}; - -struct BlockShiftRequest { - block_number_t toBlockNumber; - SERIALIZATION_FIELDS(toBlockNumber); -}; - -template struct AppendLeavesRequest { - MerkleTreeId treeId; - std::vector leaves; - Fork::Id forkId{ CANONICAL_FORK_ID }; - SERIALIZATION_FIELDS(treeId, leaves, forkId); -}; - -template struct BatchInsertRequest { - MerkleTreeId treeId; - std::vector leaves; - uint32_t subtreeDepth; - Fork::Id forkId{ CANONICAL_FORK_ID }; - SERIALIZATION_FIELDS(treeId, leaves, subtreeDepth, forkId); -}; - -template struct InsertRequest { - MerkleTreeId treeId; - std::vector leaves; - Fork::Id forkId{ CANONICAL_FORK_ID }; - SERIALIZATION_FIELDS(treeId, leaves, forkId); -}; - -struct UpdateArchiveRequest { - StateReference blockStateRef; - bb::fr blockHeaderHash; - Fork::Id forkId{ CANONICAL_FORK_ID }; - SERIALIZATION_FIELDS(blockStateRef, blockHeaderHash, forkId); -}; - -struct SyncBlockRequest { - block_number_t blockNumber; - StateReference blockStateRef; - bb::fr blockHeaderHash; - std::vector paddedNoteHashes, paddedL1ToL2Messages; - std::vector paddedNullifiers; - std::vector publicDataWrites; - - SERIALIZATION_FIELDS(blockNumber, - blockStateRef, - blockHeaderHash, - paddedNoteHashes, - paddedL1ToL2Messages, - paddedNullifiers, - publicDataWrites); -}; - -struct CopyStoresRequest { - std::string dstPath; - std::optional compact; - SERIALIZATION_FIELDS(dstPath, compact); -}; - -} // namespace bb::nodejs - -MSGPACK_ADD_ENUM(bb::nodejs::WorldStateMessageType) diff --git a/barretenberg/cpp/src/barretenberg/serialize/msgpack.test.cpp b/barretenberg/cpp/src/barretenberg/serialize/msgpack.test.cpp index 0d269d15ba37..58d54d0f00e4 100644 --- a/barretenberg/cpp/src/barretenberg/serialize/msgpack.test.cpp +++ b/barretenberg/cpp/src/barretenberg/serialize/msgpack.test.cpp @@ -1,4 +1,11 @@ #include "barretenberg/serialize/msgpack.hpp" +#include + // Mostly to be sure the function is constexpr. static_assert(::msgpack_detail::camel_case("gas_used") == "gasUsed"); + +TEST(MsgpackSerialize, CamelCase) +{ + EXPECT_EQ(::msgpack_detail::camel_case("gas_used"), "gasUsed"); +} diff --git a/barretenberg/cpp/src/barretenberg/serialize/msgpack_check_eq.hpp b/barretenberg/cpp/src/barretenberg/serialize/msgpack_check_eq.hpp index 35c7c107cb25..18f59680f08b 100644 --- a/barretenberg/cpp/src/barretenberg/serialize/msgpack_check_eq.hpp +++ b/barretenberg/cpp/src/barretenberg/serialize/msgpack_check_eq.hpp @@ -2,7 +2,6 @@ #include "barretenberg/common/log.hpp" #include "msgpack.hpp" -#include "msgpack_impl/drop_keys.hpp" #include #include diff --git a/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl.hpp b/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl.hpp index 7c86a5d588bc..f9f7aef543de 100644 --- a/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl.hpp @@ -1,22 +1,16 @@ #pragma once // Meant to be the main header included by *.cpp files* that use msgpack. // Note: heavy header due to serialization logic, don't include if msgpack.hpp will do -// CBinding helpers that take a function or a lambda and -// - bind the input as a coded msgpack array of all the arguments (using template metamagic) -// - bind the return value to an out buffer, where the caller must free the memory #include -#include #include "barretenberg/common/mem.hpp" #include "barretenberg/common/try_catch_shim.hpp" #include "msgpack_impl/check_memory_span.hpp" #include "msgpack_impl/concepts.hpp" -#include "msgpack_impl/func_traits.hpp" #include "msgpack_impl/msgpack_impl.hpp" #include "msgpack_impl/name_value_pair_macro.hpp" -#include "msgpack_impl/schema_impl.hpp" #include "msgpack_impl/schema_name.hpp" #include "msgpack_impl/struct_map_impl.hpp" @@ -46,70 +40,3 @@ inline std::pair msgpack_encode_buffer(auto&& obj, memcpy(output, buffer.data(), buffer.size()); return { output, buffer.size() }; } - -// This function is intended to bind a function to a MessagePack-formatted input data, -// perform the function with the unpacked data, then pack the result back into MessagePack format. -// Note: output_out and output_len_out are IN-OUT parameters: -// IN: Caller provides scratch buffer pointer and size -// OUT: Returns actual result buffer (may be scratch or newly allocated) and size -inline void msgpack_cbind_impl(const auto& func, // The function to be applied - const uint8_t* input_in, // The input data in MessagePack format - size_t input_len_in, // The length of the input data - uint8_t** output_out, // IN-OUT: scratch buffer ptr / result buffer ptr - size_t* output_len_out) // IN-OUT: scratch buffer size / result size -{ - using FuncTraits = decltype(get_func_traits()); - // Args: the parameter types of the function as a tuple. - typename FuncTraits::Args params; - - // Unpack the input data into the parameter tuple. - msgpack::unpack(reinterpret_cast(input_in), input_len_in).get().convert(params); - - // Read IN values: caller-provided scratch buffer - uint8_t* scratch_buf = *output_out; - size_t scratch_size = *output_len_out; - - // Apply the function to the parameters, then encode the result into a MessagePack buffer. - // Try to use scratch buffer; allocate if result doesn't fit. - auto [output, output_len] = msgpack_encode_buffer(FuncTraits::apply(func, params), scratch_buf, scratch_size); - - // Write OUT values: actual result buffer and size - // If result fit in scratch, output == scratch_buf (pointer unchanged) - // If result didn't fit, output is newly allocated buffer (pointer changed) - *output_out = output; - *output_len_out = output_len; -} - -// returns a C-style string json of the schema -inline void msgpack_cbind_schema_impl(auto func, uint8_t** output_out, size_t* output_len_out) -{ - (void)func; // unused except for type - // Object representation of the cbind - auto cbind_obj = get_func_traits(); - std::string schema = msgpack_schema_to_string(cbind_obj); - *output_out = static_cast(aligned_alloc(64, schema.size() + 1)); - memcpy(*output_out, schema.c_str(), schema.size() + 1); - *output_len_out = schema.size(); -} - -// The CBIND_NOSCHEMA macro generates a function named 'cname' that decodes the input arguments from msgpack format, -// calls the target function, and then encodes the return value back into msgpack format. It should be used over CBIND -// in cases where we do not want schema generation, such as meta-functions that themselves give information to control -// how the schema is interpreted. -#define CBIND_NOSCHEMA(cname, func) \ - WASM_EXPORT void cname(const uint8_t* input_in, size_t input_len_in, uint8_t** output_out, size_t* output_len_out) \ - { \ - msgpack_cbind_impl(func, input_in, input_len_in, output_out, output_len_out); \ - } - -// The CBIND macro is a convenient utility that abstracts away several steps in binding C functions with msgpack -// serialization. It creates two separate functions: -// 1. cname function: This decodes the input arguments from msgpack format, calls the target function, -// and then encodes the return value back into msgpack format. -// 2. cname##__schema function: This creates a JSON schema of the function's input arguments and return type. -#define CBIND(cname, func) \ - CBIND_NOSCHEMA(cname, func) \ - WASM_EXPORT void cname##__schema(uint8_t** output_out, size_t* output_len_out) \ - { \ - msgpack_cbind_schema_impl(func, output_out, output_len_out); \ - } diff --git a/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/concepts.hpp b/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/concepts.hpp index 78ca01eeaa1d..c67a98c256c4 100644 --- a/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/concepts.hpp +++ b/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/concepts.hpp @@ -1,18 +1,30 @@ #pragma once +#ifndef IPC_CODEGEN_MSGPACK_CONCEPTS_DEFINED +#define IPC_CODEGEN_MSGPACK_CONCEPTS_DEFINED + struct DoNothing { void operator()(auto...) {} }; + namespace msgpack_concepts { + template concept HasMsgPack = requires(T t, DoNothing nop) { t.msgpack(nop); }; +template +concept MsgpackConstructible = requires(T object, Args... args) { T{ args... }; }; + +} // namespace msgpack_concepts + +#endif + +namespace msgpack_concepts { + template concept HasMsgPackSchema = requires(const T t, DoNothing nop) { t.msgpack_schema(nop); }; template concept HasMsgPackPack = requires(T t, DoNothing nop) { t.msgpack_pack(nop); }; -template -concept MsgpackConstructible = requires(T object, Args... args) { T{ args... }; }; } // namespace msgpack_concepts diff --git a/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/drop_keys.hpp b/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/drop_keys.hpp index 7f60ef7e74c4..c03b4e50bb4e 100644 --- a/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/drop_keys.hpp +++ b/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/drop_keys.hpp @@ -1,6 +1,9 @@ #pragma once #include +#ifndef IPC_CODEGEN_MSGPACK_DROP_KEYS_DEFINED +#define IPC_CODEGEN_MSGPACK_DROP_KEYS_DEFINED + namespace msgpack { template auto drop_keys_impl(Tuple&& tuple, std::index_sequence) { @@ -20,3 +23,5 @@ template auto drop_keys(std::tuple&& tuple) return drop_keys_impl(tuple, compile_time_0_to_n_div_2); } } // namespace msgpack + +#endif diff --git a/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/func_traits.hpp b/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/func_traits.hpp deleted file mode 100644 index 256d87f64d31..000000000000 --- a/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/func_traits.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include "../msgpack.hpp" -#include -#include - -// Base template for function traits -template struct func_traits; - -// Common implementation for all function types -template struct func_traits_base { - using Args = std::tuple::type...>; - Args args; - R ret; - SERIALIZATION_FIELDS(args, ret); - - template static R apply(Func&& f, Tuple&& t) - { - return std::apply([&f](auto&&... args) { return f(std::forward(std::forward(args))...); }, - std::forward(t)); - } -}; - -// Specializations inherit from common base -template struct func_traits : func_traits_base {}; - -template struct func_traits : func_traits_base {}; - -template -struct func_traits : func_traits_base {}; - -// Simplified trait getter -template constexpr auto get_func_traits() -{ - if constexpr (requires { &T::operator(); }) { - return func_traits {}; - } else { - return func_traits{}; - } -} diff --git a/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/schema_impl.hpp b/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/schema_impl.hpp deleted file mode 100644 index 1b16b2e1c36e..000000000000 --- a/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/schema_impl.hpp +++ /dev/null @@ -1,227 +0,0 @@ -#pragma once - -#include "schema_name.hpp" -#include -#include -#include -#include - -struct MsgpackSchemaPacker; - -// Forward declare for MsgpackSchemaPacker -template inline void _msgpack_schema_pack(MsgpackSchemaPacker& packer, const T& obj); - -/** - * Define a serialization schema based on compile-time information about a type being serialized. - * This is then consumed by typescript to make bindings. - */ -struct MsgpackSchemaPacker : msgpack::packer { - MsgpackSchemaPacker(msgpack::sbuffer& stream) - : packer(stream) - {} - // For tracking emitted types - std::set emitted_types; - // Returns if already was emitted - bool set_emitted(const std::string& type) - { - if (emitted_types.find(type) == emitted_types.end()) { - emitted_types.insert(type); - return false; - } - return true; - } - - /** - * Pack a type indicating it is an alias of a certain msgpack type - * Packs in the form ["alias", [schema_name, msgpack_name]] - * @param schema_name The CPP type. - * @param msgpack_name The msgpack type. - */ - void pack_alias(const std::string& schema_name, const std::string& msgpack_name) - { - // We will pack a size 2 tuple - pack_array(2); - pack("alias"); - // That has a size 2 tuple as its 2nd arg - pack_array(2); - pack(schema_name); - pack(msgpack_name); - } - - /** - * Pack the schema of a given object. - * @tparam T the object's type. - * @param obj the object. - */ - template void pack_schema(const T& obj) { _msgpack_schema_pack(*this, obj); } - - // Recurse over any templated containers - // Outputs e.g. ['vector', ['sub-type']] - template void pack_template_type(const std::string& schema_name) - { - // We will pack a size 2 tuple - pack_array(2); - pack(schema_name); - pack_array(sizeof...(Args)); - - // Note: if this fails to compile, check first in list of template Arg's - // it may need a msgpack_schema_pack specialization (particularly if it doesn't define SERIALIZATION_FIELDS). - (_msgpack_schema_pack(*this, *std::make_unique()), ...); /* pack schemas of all template Args */ - } - /** - * @brief Encode a type that defines msgpack based on its key value pairs. - * - * @tparam T the msgpack()'able type - * @param packer Our special packer. - * @param object The object in question. - */ - template void pack_with_name(const std::string& type, T const& object) - { - if (set_emitted(type)) { - pack(type); - return; // already emitted - } - msgpack::check_msgpack_usage(object); - // Encode as map - const_cast(object).msgpack([&](auto&... args) { - size_t kv_size = sizeof...(args); - // Calculate the number of entries in our map (half the size of keys + values, plus the typename) - pack_map(uint32_t(1 + kv_size / 2)); - pack("__typename"); - pack(type); - // Pack the map content based on the args to msgpack - _schema_pack_map_content(*this, args...); - }); - } -}; - -// Helper for packing (key, value, key, value, ...) arguments -inline void _schema_pack_map_content(MsgpackSchemaPacker&) -{ - // base case -} - -namespace msgpack_concepts { -template -concept SchemaPackable = requires(T value, MsgpackSchemaPacker packer) { msgpack_schema_pack(packer, value); }; -} // namespace msgpack_concepts - -// Helper for packing (key, value, key, value, ...) arguments -template -inline void _schema_pack_map_content(MsgpackSchemaPacker& packer, - std::string key, - const Value& value, - const Rest&... rest) -{ - static_assert( - msgpack_concepts::SchemaPackable, - "see the first type argument in the error trace, it might require a specialization of msgpack_schema_pack"); - packer.pack(key); - msgpack_schema_pack(packer, value); - _schema_pack_map_content(packer, rest...); -} - -template - requires(!msgpack_concepts::HasMsgPackSchema && !msgpack_concepts::HasMsgPack) -inline void msgpack_schema_pack(MsgpackSchemaPacker& packer, T const& obj) -{ - packer.pack(msgpack_schema_name(obj)); -} - -/** - * Schema pack base case for types with no special msgpack method. - * @tparam T the type. - * @param packer the schema packer. - */ -template -inline void msgpack_schema_pack(MsgpackSchemaPacker& packer, T const& obj) -{ - obj.msgpack_schema(packer); -} - -/** - * @brief Encode a type that defines msgpack based on its key value pairs. - * - * @tparam T the msgpack()'able type - * @param packer Our special packer. - * @param object The object in question. - */ -template - requires(!msgpack_concepts::HasMsgPackSchema) -inline void msgpack_schema_pack(MsgpackSchemaPacker& packer, T const& object) -{ - std::string type = msgpack_schema_name(object); - packer.pack_with_name(type, object); -} - -/** - * @brief Helper method for better error reporting. Clang does not give the best errors for argument lists. - */ -template inline void _msgpack_schema_pack(MsgpackSchemaPacker& packer, const T& obj) -{ - static_assert(msgpack_concepts::SchemaPackable, - "see the first type argument in the error trace, it might need a msgpack_schema method!"); - msgpack_schema_pack(packer, obj); -} - -template inline void msgpack_schema_pack(MsgpackSchemaPacker& packer, std::tuple const&) -{ - packer.pack_template_type("tuple"); -} - -template inline void msgpack_schema_pack(MsgpackSchemaPacker& packer, std::map const&) -{ - packer.pack_template_type("map"); -} - -template inline void msgpack_schema_pack(MsgpackSchemaPacker& packer, std::optional const&) -{ - packer.pack_template_type("optional"); -} - -template inline void msgpack_schema_pack(MsgpackSchemaPacker& packer, std::vector const&) -{ - packer.pack_template_type("vector"); -} - -template inline void msgpack_schema_pack(MsgpackSchemaPacker& packer, std::variant const&) -{ - packer.pack_template_type("variant"); -} - -template inline void msgpack_schema_pack(MsgpackSchemaPacker& packer, std::shared_ptr const&) -{ - packer.pack_template_type("shared_ptr"); -} - -// Outputs e.g. ['array', ['array-type', 'N']] -template -inline void msgpack_schema_pack(MsgpackSchemaPacker& packer, std::array const&) -{ - // We will pack a size 2 tuple - packer.pack_array(2); - packer.pack("array"); - // That has a size 2 tuple as its 2nd arg - packer.pack_array(2); /* param list format for consistency*/ - // To avoid WASM problems with large stack objects, we use a heap allocation. - // Small note: This works because make_unique goes of scope only when the whole line is done. - _msgpack_schema_pack(packer, *std::make_unique()); - packer.pack(N); -} - -/** - * @brief Print's an object's derived msgpack schema as a string. - * - * @param obj The object to print schema of. - * @return std::string The schema as a string. - */ -inline std::string msgpack_schema_to_string(const auto& obj) -{ - msgpack::sbuffer output; - MsgpackSchemaPacker printer{ output }; - _msgpack_schema_pack(printer, obj); - msgpack::object_handle oh = msgpack::unpack(output.data(), output.size()); - std::stringstream pretty_output; - pretty_output << oh.get() << std::endl; - return pretty_output.str(); -} diff --git a/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/struct_map_impl.hpp b/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/struct_map_impl.hpp index a17347214cb4..78b832ca60c4 100644 --- a/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/struct_map_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/serialize/msgpack_impl/struct_map_impl.hpp @@ -10,6 +10,9 @@ #include "drop_keys.hpp" #include +#ifndef IPC_CODEGEN_MSGPACK_STRUCT_MAP_ADAPTOR_DEFINED +#define IPC_CODEGEN_MSGPACK_STRUCT_MAP_ADAPTOR_DEFINED + namespace msgpack::adaptor { // reads structs with msgpack() method from a JSON-like dictionary template struct convert { @@ -61,3 +64,5 @@ template struct pack { }; } // namespace msgpack::adaptor + +#endif diff --git a/barretenberg/cpp/src/barretenberg/serialize/msgpack_schema.test.cpp b/barretenberg/cpp/src/barretenberg/serialize/msgpack_schema.test.cpp deleted file mode 100644 index 709713beeede..000000000000 --- a/barretenberg/cpp/src/barretenberg/serialize/msgpack_schema.test.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "barretenberg/serialize/msgpack.hpp" -#include "barretenberg/serialize/msgpack_impl.hpp" - -#include - -using namespace bb; - -// Sanity checking for msgpack - -struct GoodExample { - fr a; - fr b; - SERIALIZATION_FIELDS(a, b); -} good_example; - -struct BadExampleOverlap { - fr a; - fr b; - SERIALIZATION_FIELDS(a, a); -} bad_example_overlap; - -struct BadExampleIncomplete { - fr a; - fr b; - SERIALIZATION_FIELDS(a); -} bad_example_incomplete; - -struct BadExampleCompileTimeError { - std::vector a; - fr b; - - SERIALIZATION_FIELDS(b); // Type mismatch, expect 'a', will catch at compile-time -} bad_example_compile_time_error; - -struct BadExampleOutOfObject { - fr a; - fr b; - void msgpack(auto ar) - { - BadExampleOutOfObject other_object; - ar("a", other_object.a, "b", other_object.b); - } -} bad_example_out_of_object; - -// TODO eventually move to barretenberg -TEST(msgpack_tests, msgpack_sanity_sanity) -{ - EXPECT_EQ(msgpack::check_msgpack_method(good_example), ""); - EXPECT_EQ(msgpack::check_msgpack_method(bad_example_overlap), - "Overlap in BadExampleOverlap SERIALIZATION_FIELDS() params detected!"); - EXPECT_EQ(msgpack::check_msgpack_method(bad_example_incomplete), - "Incomplete BadExampleIncomplete SERIALIZATION_FIELDS() params! Not all of object specified."); - - // If we actually try to msgpack BadExampleCompileTimeError we will statically error - // This is great, but we need to check the underlying facility *somehow* - auto checker = [&](auto&... values) { - std::string incomplete_msgpack_status = "error"; - if constexpr (msgpack_concepts::MsgpackConstructible) { - incomplete_msgpack_status = ""; - } - EXPECT_EQ(incomplete_msgpack_status, "error"); - }; - bad_example_compile_time_error.msgpack(checker); - - EXPECT_EQ(msgpack::check_msgpack_method(bad_example_out_of_object), - "Some BadExampleOutOfObject SERIALIZATION_FIELDS() params don't exist in object!"); -} - -struct ComplicatedSchema { - std::vector> array; - std::optional good_or_not; - fr bare; - std::variant huh; - SERIALIZATION_FIELDS(array, good_or_not, bare, huh); -} complicated_schema; - -TEST(msgpack_tests, msgpack_schema_sanity) -{ - EXPECT_EQ( - msgpack_schema_to_string(good_example), - "{\"__typename\":\"GoodExample\",\"a\":[\"alias\",[\"fr\",\"bin32\"]],\"b\":[\"alias\",[\"fr\",\"bin32\"]]}\n"); - EXPECT_EQ(msgpack_schema_to_string(complicated_schema), - "{\"__typename\":\"ComplicatedSchema\",\"array\":[\"vector\",[[\"array\",[[\"alias\",[\"fr\",\"bin32\"]]," - "20]]]],\"good_or_not\":[\"optional\",[{\"__typename\":\"GoodExample\",\"a\":[\"alias\",[\"fr\"," - "\"bin32\"]],\"b\":[\"alias\",[\"fr\",\"bin32\"]]}]],\"bare\":[\"alias\",[\"fr\",\"bin32\"]],\"huh\":[" - "\"variant\",[[\"alias\",[\"fr\",\"bin32\"]],\"GoodExample\"]]}\n"); -} diff --git a/barretenberg/cpp/src/barretenberg/vm2/avm_sim_api.cpp b/barretenberg/cpp/src/barretenberg/vm2/avm_sim_api.cpp index 2d706a15e64a..d16dc16bc698 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/avm_sim_api.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/avm_sim_api.cpp @@ -10,7 +10,7 @@ using namespace bb::avm2::simulation; TxSimulationResult AvmSimAPI::simulate(const FastSimulationInputs& inputs, simulation::ContractDBInterface& contract_db, - world_state::WorldState& ws, + simulation::LowLevelMerkleDBInterface& merkle_db, simulation::CancellationTokenPtr cancellation_token) { vinfo("Simulating..."); @@ -18,25 +18,23 @@ TxSimulationResult AvmSimAPI::simulate(const FastSimulationInputs& inputs, if (inputs.config.collect_hints) { return AVM_TRACK_TIME_V("simulation/all", - simulation_helper.simulate_for_hint_collection(contract_db, - inputs.ws_revision, - ws, - inputs.config, - inputs.tx, - inputs.global_variables, - inputs.protocol_contracts, - cancellation_token)); - } else { - return AVM_TRACK_TIME_V("simulation/all", - simulation_helper.simulate_fast_with_existing_ws(contract_db, - inputs.ws_revision, - ws, - inputs.config, - inputs.tx, - inputs.global_variables, - inputs.protocol_contracts, - cancellation_token)); + simulation_helper.simulate_for_hint_collection_internal(contract_db, + merkle_db, + inputs.config, + inputs.tx, + inputs.global_variables, + inputs.protocol_contracts, + cancellation_token)); } + + return AVM_TRACK_TIME_V("simulation/all", + simulation_helper.simulate_fast_internal(contract_db, + merkle_db, + inputs.config, + inputs.tx, + inputs.global_variables, + inputs.protocol_contracts, + cancellation_token)); } TxSimulationResult AvmSimAPI::simulate_with_hinted_dbs(const ProvingInputs& inputs) diff --git a/barretenberg/cpp/src/barretenberg/vm2/avm_sim_api.hpp b/barretenberg/cpp/src/barretenberg/vm2/avm_sim_api.hpp index 77fb9c977a74..c1b496327d1c 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/avm_sim_api.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/avm_sim_api.hpp @@ -15,7 +15,7 @@ class AvmSimAPI { TxSimulationResult simulate(const FastSimulationInputs& inputs, simulation::ContractDBInterface& contract_db, - world_state::WorldState& ws, + simulation::LowLevelMerkleDBInterface& merkle_db, simulation::CancellationTokenPtr cancellation_token = nullptr); TxSimulationResult simulate_with_hinted_dbs(const AvmProvingInputs& inputs); }; diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp index 0bc66fc810a0..ced8480f4a01 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp @@ -576,21 +576,17 @@ TxSimulationResult AvmSimulationHelper::simulate_fast_with_existing_ws( raw_contract_db, raw_merkle_db, config, tx, global_variables, protocol_contracts, cancellation_token); } -TxSimulationResult AvmSimulationHelper::simulate_for_hint_collection( +TxSimulationResult AvmSimulationHelper::simulate_for_hint_collection_internal( simulation::ContractDBInterface& raw_contract_db, - const world_state::WorldStateRevision& world_state_revision, - world_state::WorldState& ws, + simulation::LowLevelMerkleDBInterface& raw_merkle_db, const PublicSimulatorConfig& config, const Tx& tx, const GlobalVariables& global_variables, const ProtocolContracts& protocol_contracts, CancellationTokenPtr cancellation_token) { - // If you are not collecting hints, don't use this method. - BB_ASSERT(config.collect_hints && "Use simulate_fast_with_existing_ws instead"); - - // Create PureRawMerkleDB with the provided WorldState instance and cancellation token - PureRawMerkleDB raw_merkle_db(world_state_revision, ws, /*cache_tree_roots=*/true, cancellation_token); + (void)cancellation_token; // Not yet used in this path. + BB_ASSERT(config.collect_hints && "Use simulate_fast_internal instead"); auto starting_tree_roots = raw_merkle_db.get_tree_roots(); HintingContractsDB hinting_contract_db(raw_contract_db); @@ -609,11 +605,29 @@ TxSimulationResult AvmSimulationHelper::simulate_for_hint_collection( tx_result.hints = std::move(collected_hints); - // Need to std::move to avoid copying (due to structured bindings). - // This was fixed in C++23 via http://wg21.link/P2266R3. return std::move(tx_result); } +TxSimulationResult AvmSimulationHelper::simulate_for_hint_collection( + simulation::ContractDBInterface& raw_contract_db, + const world_state::WorldStateRevision& world_state_revision, + world_state::WorldState& ws, + const PublicSimulatorConfig& config, + const Tx& tx, + const GlobalVariables& global_variables, + const ProtocolContracts& protocol_contracts, + CancellationTokenPtr cancellation_token) +{ + // If you are not collecting hints, don't use this method. + BB_ASSERT(config.collect_hints && "Use simulate_fast_with_existing_ws instead"); + + // Create PureRawMerkleDB with the provided WorldState instance and cancellation token + PureRawMerkleDB raw_merkle_db(world_state_revision, ws, /*cache_tree_roots=*/true, cancellation_token); + + return simulate_for_hint_collection_internal( + raw_contract_db, raw_merkle_db, config, tx, global_variables, protocol_contracts, cancellation_token); +} + EventsContainer AvmSimulationHelper::simulate_for_witgen(const ExecutionHints& hints) { // TODO(fcarreiro): decide if we want to pass a config here. diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.hpp index 6bae3274dec9..dcb665e814dd 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.hpp @@ -37,7 +37,9 @@ class AvmSimulationHelper { // An extra entry point that is not used in production. TxSimulationResult simulate_fast_with_hinted_dbs(const ExecutionHints& hints, const PublicSimulatorConfig& config); - protected: + // Fast simulation against any LowLevelMerkleDBInterface implementation (in-process, IPC, or hinted). + // Used by the standalone aztec-avm and the NAPI AVM after the WSDB cutover, both of which + // construct a WSDB-IPC-backed merkle DB rather than using an in-process WorldState reference. TxSimulationResult simulate_fast_internal(simulation::ContractDBInterface& raw_contract_db, simulation::LowLevelMerkleDBInterface& raw_merkle_db, const PublicSimulatorConfig& config, @@ -46,6 +48,20 @@ class AvmSimulationHelper { const ProtocolContracts& protocol_contracts, simulation::CancellationTokenPtr cancellation_token = nullptr); + // Hint-collecting simulation against any LowLevelMerkleDBInterface implementation. Mirrors + // simulate_fast_internal but wraps the DBs in the hinting proxies used by witgen and dumps + // the recorded hints into the result. Used by the prover-node path on both the NAPI AVM + // (after the WSDB cutover) and the standalone aztec-avm. + TxSimulationResult simulate_for_hint_collection_internal( + simulation::ContractDBInterface& raw_contract_db, + simulation::LowLevelMerkleDBInterface& raw_merkle_db, + const PublicSimulatorConfig& config, + const Tx& tx, + const GlobalVariables& global_variables, + const ProtocolContracts& protocol_contracts, + simulation::CancellationTokenPtr cancellation_token = nullptr); + + protected: template