From 6ceb26c6ce9c51fd865b3eab5a2a9136ca371aad Mon Sep 17 00:00:00 2001 From: Charlie <5764343+charlielye@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:52:21 +0000 Subject: [PATCH 01/69] feat: replace NAPI with IPC for world state and AVM simulator Co-Authored-By: Claude Opus 4.6 --- barretenberg/cpp/src/CMakeLists.txt | 3 + .../cpp/src/barretenberg/avm/CMakeLists.txt | 46 + .../cpp/src/barretenberg/avm/avm_commands.hpp | 68 ++ .../cpp/src/barretenberg/avm/avm_execute.cpp | 141 +++ .../cpp/src/barretenberg/avm/avm_execute.hpp | 60 ++ .../src/barretenberg/avm/avm_ipc_server.cpp | 219 ++++ .../src/barretenberg/avm/avm_ipc_server.hpp | 20 + barretenberg/cpp/src/barretenberg/avm/cli.cpp | 79 ++ barretenberg/cpp/src/barretenberg/avm/cli.hpp | 7 + .../cpp/src/barretenberg/avm/main.cpp | 6 + .../barretenberg/avm/wsdb_ipc_merkle_db.cpp | 214 ++++ .../barretenberg/avm/wsdb_ipc_merkle_db.hpp | 66 ++ .../cpp/src/barretenberg/cdb/CMakeLists.txt | 40 + .../cpp/src/barretenberg/cdb/cdb_commands.hpp | 218 ++++ .../cpp/src/barretenberg/cdb/cdb_execute.hpp | 57 ++ .../src/barretenberg/cdb/cdb_ipc_client.cpp | 56 ++ .../src/barretenberg/cdb/cdb_ipc_client.hpp | 40 + .../cdb/cdb_ipc_client_generated.cpp | 139 +++ .../cdb/cdb_ipc_client_generated.hpp | 47 + barretenberg/cpp/src/barretenberg/cdb/cli.cpp | 61 ++ barretenberg/cpp/src/barretenberg/cdb/cli.hpp | 7 + .../cpp/src/barretenberg/cdb/main.cpp | 6 + .../cpp/src/barretenberg/ipc/ipc_client.cpp | 6 + .../cpp/src/barretenberg/ipc/ipc_client.hpp | 1 + .../cpp/src/barretenberg/ipc/ipc_server.cpp | 9 + .../cpp/src/barretenberg/ipc/ipc_server.hpp | 4 + .../src/barretenberg/ipc/mpsc_shm_client.hpp | 111 ++ .../src/barretenberg/ipc/mpsc_shm_server.hpp | 154 +++ .../barretenberg/nodejs_module/CMakeLists.txt | 2 +- .../avm_simulate/avm_simulate_napi.cpp | 431 -------- .../avm_simulate/avm_simulate_napi.hpp | 72 -- .../avm_simulate/ts_callback_contract_db.cpp | 260 ----- .../avm_simulate/ts_callback_contract_db.hpp | 149 --- .../avm_simulate/ts_callback_utils.cpp | 289 ------ .../avm_simulate/ts_callback_utils.hpp | 114 --- .../nodejs_module/init_module.cpp | 10 - .../msgpack_client/msgpack_client_async.cpp | 9 +- .../nodejs_module/world_state/world_state.cpp | 950 ------------------ .../nodejs_module/world_state/world_state.hpp | 84 -- .../world_state/world_state_message.hpp | 272 ----- .../gadgets/contract_instance_manager.cpp | 3 +- .../barretenberg/vm2/simulation_helper.cpp | 32 + .../barretenberg/vm2/simulation_helper.hpp | 13 +- .../cpp/src/barretenberg/wsdb/CMakeLists.txt | 44 + .../cpp/src/barretenberg/wsdb/cli.cpp | 132 +++ .../cpp/src/barretenberg/wsdb/cli.hpp | 7 + .../cpp/src/barretenberg/wsdb/main.cpp | 6 + .../src/barretenberg/wsdb/wsdb_commands.hpp | 536 ++++++++++ .../src/barretenberg/wsdb/wsdb_execute.cpp | 408 ++++++++ .../src/barretenberg/wsdb/wsdb_execute.hpp | 115 +++ .../wsdb/wsdb_ipc_client_generated.cpp | 225 +++++ .../wsdb/wsdb_ipc_client_generated.hpp | 65 ++ .../src/barretenberg/wsdb/wsdb_ipc_server.cpp | 341 +++++++ .../src/barretenberg/wsdb/wsdb_ipc_server.hpp | 26 + barretenberg/ts/.gitignore | 3 + barretenberg/ts/package.json | 13 +- barretenberg/ts/scripts/copy_native.sh | 4 +- barretenberg/ts/src/aztec-avm/generate.ts | 64 ++ barretenberg/ts/src/aztec-avm/index.ts | 289 ++++++ barretenberg/ts/src/aztec-cdb/generate.ts | 89 ++ barretenberg/ts/src/aztec-wsdb/generate.ts | 89 ++ barretenberg/ts/src/aztec-wsdb/index.ts | 442 ++++++++ .../ts/src/bb_backends/browser/platform.ts | 5 + .../ts/src/bb_backends/node/platform.ts | 72 ++ barretenberg/ts/src/cbind/cpp_codegen.ts | 241 +++++ barretenberg/ts/src/cbind/rust_codegen.ts | 11 +- barretenberg/ts/src/cbind/schema_visitor.ts | 15 +- .../ts/src/cbind/typescript_codegen.ts | 39 +- barretenberg/ts/src/index.ts | 2 +- barretenberg/ts/tsconfig.browser.json | 2 +- .../boxes/vanilla/contracts/src/test/first.nr | 2 +- .../private_voting_contract/src/test/first.nr | 2 +- release-image/Dockerfile | 2 + yarn-project/aztec-node/package.json | 1 + .../aztec-node/src/aztec-node/server.ts | 94 +- .../avm_check_circuit3.test.ts | 7 - .../avm_proving_tests/avm_proving_tester.ts | 50 +- .../src/avm_integration.test.ts | 2 +- .../src/rollup_ivc_integration.test.ts | 2 +- yarn-project/native/src/native_module.ts | 180 +--- .../prover-client/src/mocks/test_context.ts | 1 + .../prover-node/src/job/epoch-proving-job.ts | 4 +- yarn-project/pxe/tsconfig.json | 4 +- yarn-project/simulator/package.json | 2 + .../simulator/src/public/cdb_ipc_server.ts | 295 ++++++ .../fixtures/public_tx_simulation_tester.ts | 80 +- yarn-project/simulator/src/public/index.ts | 6 +- .../apps_tests/deployments.test.ts | 74 +- .../apps_tests/timeout_race.test.ts | 14 +- .../public_processor/apps_tests/token.test.ts | 73 +- .../public_processor/guarded_merkle_tree.ts | 4 +- .../public_processor/public_processor.ts | 11 +- .../apps_tests/amm.test.ts | 13 +- .../apps_tests/avm_gadgets.test.ts | 13 +- .../apps_tests/avm_minimal.test.ts | 10 +- .../apps_tests/avm_test.test.ts | 7 +- .../apps_tests/bench.test.ts | 102 +- .../apps_tests/cpp_exception_handling.test.ts | 12 +- .../apps_tests/custom_bc.test.ts | 26 +- .../apps_tests/opcode_spam.test.ts | 62 +- .../apps_tests/token.test.ts | 13 +- .../contract_provider_for_cpp.ts | 125 --- .../cpp_public_tx_simulator.ts | 186 ++-- ...cpp_public_tx_simulator_with_hinted_dbs.ts | 134 --- .../cpp_vs_ts_public_tx_simulator.ts | 239 ----- .../dumping_cpp_public_tx_simulator.ts | 83 -- .../public/public_tx_simulator/factories.ts | 30 +- .../src/public/public_tx_simulator/index.ts | 9 +- .../ipc_vs_ts_public_tx_simulator.ts | 170 ++++ .../public_tx_simulator.ts | 4 + .../src/interfaces/merkle_tree_operations.ts | 4 +- .../src/world-state/world_state_revision.ts | 33 - .../oracle/txe_oracle_top_level_context.ts | 6 +- yarn-project/validator-client/package.json | 1 + .../src/checkpoint_builder.ts | 27 +- .../src/validator.integration.test.ts | 33 +- yarn-project/world-state/package.json | 3 +- yarn-project/world-state/src/native/index.ts | 1 + .../src/native/ipc_world_state_instance.ts | 694 +++++++++++++ .../src/native/merkle_trees_facade.ts | 14 +- .../src/native/native_world_state.test.ts | 18 +- .../src/native/native_world_state.ts | 100 +- .../src/native/native_world_state_instance.ts | 281 +----- .../world-state/src/synchronizer/factory.ts | 10 +- yarn-project/world-state/tsconfig.json | 3 - yarn-project/yarn.lock | 7 +- 126 files changed, 7119 insertions(+), 4124 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/avm/CMakeLists.txt create mode 100644 barretenberg/cpp/src/barretenberg/avm/avm_commands.hpp create mode 100644 barretenberg/cpp/src/barretenberg/avm/avm_execute.cpp create mode 100644 barretenberg/cpp/src/barretenberg/avm/avm_execute.hpp create mode 100644 barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.cpp create mode 100644 barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.hpp create mode 100644 barretenberg/cpp/src/barretenberg/avm/cli.cpp create mode 100644 barretenberg/cpp/src/barretenberg/avm/cli.hpp create mode 100644 barretenberg/cpp/src/barretenberg/avm/main.cpp create mode 100644 barretenberg/cpp/src/barretenberg/avm/wsdb_ipc_merkle_db.cpp create mode 100644 barretenberg/cpp/src/barretenberg/avm/wsdb_ipc_merkle_db.hpp create mode 100644 barretenberg/cpp/src/barretenberg/cdb/CMakeLists.txt create mode 100644 barretenberg/cpp/src/barretenberg/cdb/cdb_commands.hpp create mode 100644 barretenberg/cpp/src/barretenberg/cdb/cdb_execute.hpp create mode 100644 barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client.cpp create mode 100644 barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client.hpp create mode 100644 barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client_generated.cpp create mode 100644 barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client_generated.hpp create mode 100644 barretenberg/cpp/src/barretenberg/cdb/cli.cpp create mode 100644 barretenberg/cpp/src/barretenberg/cdb/cli.hpp create mode 100644 barretenberg/cpp/src/barretenberg/cdb/main.cpp create mode 100644 barretenberg/cpp/src/barretenberg/ipc/mpsc_shm_client.hpp create mode 100644 barretenberg/cpp/src/barretenberg/ipc/mpsc_shm_server.hpp delete mode 100644 barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/avm_simulate_napi.cpp delete mode 100644 barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/avm_simulate_napi.hpp delete mode 100644 barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_contract_db.cpp delete mode 100644 barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_contract_db.hpp delete mode 100644 barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_utils.cpp delete mode 100644 barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/ts_callback_utils.hpp delete mode 100644 barretenberg/cpp/src/barretenberg/nodejs_module/world_state/world_state.cpp delete mode 100644 barretenberg/cpp/src/barretenberg/nodejs_module/world_state/world_state.hpp delete mode 100644 barretenberg/cpp/src/barretenberg/nodejs_module/world_state/world_state_message.hpp create mode 100644 barretenberg/cpp/src/barretenberg/wsdb/CMakeLists.txt create mode 100644 barretenberg/cpp/src/barretenberg/wsdb/cli.cpp create mode 100644 barretenberg/cpp/src/barretenberg/wsdb/cli.hpp create mode 100644 barretenberg/cpp/src/barretenberg/wsdb/main.cpp create mode 100644 barretenberg/cpp/src/barretenberg/wsdb/wsdb_commands.hpp create mode 100644 barretenberg/cpp/src/barretenberg/wsdb/wsdb_execute.cpp create mode 100644 barretenberg/cpp/src/barretenberg/wsdb/wsdb_execute.hpp create mode 100644 barretenberg/cpp/src/barretenberg/wsdb/wsdb_ipc_client_generated.cpp create mode 100644 barretenberg/cpp/src/barretenberg/wsdb/wsdb_ipc_client_generated.hpp create mode 100644 barretenberg/cpp/src/barretenberg/wsdb/wsdb_ipc_server.cpp create mode 100644 barretenberg/cpp/src/barretenberg/wsdb/wsdb_ipc_server.hpp create mode 100644 barretenberg/ts/src/aztec-avm/generate.ts create mode 100644 barretenberg/ts/src/aztec-avm/index.ts create mode 100644 barretenberg/ts/src/aztec-cdb/generate.ts create mode 100644 barretenberg/ts/src/aztec-wsdb/generate.ts create mode 100644 barretenberg/ts/src/aztec-wsdb/index.ts create mode 100644 barretenberg/ts/src/cbind/cpp_codegen.ts create mode 100644 yarn-project/simulator/src/public/cdb_ipc_server.ts delete mode 100644 yarn-project/simulator/src/public/public_tx_simulator/contract_provider_for_cpp.ts delete mode 100644 yarn-project/simulator/src/public/public_tx_simulator/cpp_public_tx_simulator_with_hinted_dbs.ts delete mode 100644 yarn-project/simulator/src/public/public_tx_simulator/cpp_vs_ts_public_tx_simulator.ts delete mode 100644 yarn-project/simulator/src/public/public_tx_simulator/dumping_cpp_public_tx_simulator.ts create mode 100644 yarn-project/simulator/src/public/public_tx_simulator/ipc_vs_ts_public_tx_simulator.ts create mode 100644 yarn-project/world-state/src/native/ipc_world_state_instance.ts diff --git a/barretenberg/cpp/src/CMakeLists.txt b/barretenberg/cpp/src/CMakeLists.txt index e140ca0ad528..2a60b543019e 100644 --- a/barretenberg/cpp/src/CMakeLists.txt +++ b/barretenberg/cpp/src/CMakeLists.txt @@ -77,6 +77,9 @@ endif() add_subdirectory(barretenberg/api) add_subdirectory(barretenberg/bb) add_subdirectory(barretenberg/bbapi) +add_subdirectory(barretenberg/wsdb) +add_subdirectory(barretenberg/cdb) +add_subdirectory(barretenberg/avm) add_subdirectory(barretenberg/benchmark) add_subdirectory(barretenberg/boomerang_value_detection) add_subdirectory(barretenberg/circuit_checker) diff --git a/barretenberg/cpp/src/barretenberg/avm/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/avm/CMakeLists.txt new file mode 100644 index 000000000000..f88bc12121cf --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/CMakeLists.txt @@ -0,0 +1,46 @@ +if(NOT(FUZZING) AND NOT(WASM)) + # IPC adapter library (WsdbIpcMerkleDB + related adapters) + add_library( + avm_ipc_adapters + STATIC + wsdb_ipc_merkle_db.cpp + ) + target_link_libraries( + avm_ipc_adapters + PUBLIC + barretenberg + wsdb_ipc_client + cdb_ipc_client + ipc + ) + + # aztec-avm binary (standalone AVM simulator server) + add_executable( + aztec-avm + main.cpp + cli.cpp + avm_execute.cpp + avm_ipc_server.cpp + ) + target_link_libraries( + aztec-avm + PRIVATE + barretenberg + vm2_sim + avm_ipc_adapters + ipc + env + ) + if(ENABLE_STACKTRACES) + target_link_libraries( + aztec-avm + PUBLIC + Backward::Interface + ) + target_link_options( + aztec-avm + PRIVATE + -ldw -lelf + ) + endif() +endif() diff --git a/barretenberg/cpp/src/barretenberg/avm/avm_commands.hpp b/barretenberg/cpp/src/barretenberg/avm/avm_commands.hpp new file mode 100644 index 000000000000..9be0968ad4cb --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/avm_commands.hpp @@ -0,0 +1,68 @@ +#pragma once +/** + * @file avm_commands.hpp + * @brief NamedUnion command structs for the aztec-avm simulation API. + * + * Commands use opaque std::vector for inputs/outputs since + * AvmFastSimulationInputs and TxSimulationResult are large, complex types + * with existing msgpack serialization. + */ + +#include "barretenberg/serialize/msgpack.hpp" + +#include +#include +#include + +namespace bb::avm { + +// Forward declaration +struct AvmRequest; + +// --------------------------------------------------------------------------- +// Simulation commands +// --------------------------------------------------------------------------- + +struct AvmSimulate { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "AvmSimulate"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "AvmSimulateResponse"; + std::vector result; + SERIALIZATION_FIELDS(result); + bool operator==(const Response&) const = default; + }; + // Msgpack-serialized AvmFastSimulationInputs + std::vector inputs; + SERIALIZATION_FIELDS(inputs); + Response execute(AvmRequest& request) &&; + bool operator==(const AvmSimulate&) const = default; +}; + +struct AvmSimulateWithHints { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "AvmSimulateWithHints"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "AvmSimulateWithHintsResponse"; + std::vector result; + SERIALIZATION_FIELDS(result); + bool operator==(const Response&) const = default; + }; + // Msgpack-serialized AvmProvingInputs + std::vector inputs; + SERIALIZATION_FIELDS(inputs); + Response execute(AvmRequest& request) &&; + bool operator==(const AvmSimulateWithHints&) const = default; +}; + +struct AvmShutdown { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "AvmShutdown"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "AvmShutdownResponse"; + void msgpack(auto&& pack_fn) { pack_fn(); } + bool operator==(const Response&) const = default; + }; + void msgpack(auto&& pack_fn) { pack_fn(); } + Response execute(AvmRequest& request) &&; + bool operator==(const AvmShutdown&) const = default; +}; + +} // namespace bb::avm 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..ac7436fad629 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/avm_execute.cpp @@ -0,0 +1,141 @@ +#include "barretenberg/avm/avm_execute.hpp" +#include "barretenberg/avm/wsdb_ipc_merkle_db.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_helper.hpp" +#include "barretenberg/wsdb/wsdb_commands.hpp" + +namespace bb::avm { + +using namespace bb::avm2; +using namespace bb::world_state; + +// --------------------------------------------------------------------------- +// 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; +} + +// --------------------------------------------------------------------------- +// Top-level dispatch +// --------------------------------------------------------------------------- + +AvmCommandResponse avm_dispatch(AvmRequest& request, AvmCommand&& command) +{ + return execute(request, std::move(command)); +} + +// --------------------------------------------------------------------------- +// AvmSimulate +// --------------------------------------------------------------------------- + +AvmSimulate::Response AvmSimulate::execute(AvmRequest& request) && +{ + // Deserialize AvmFastSimulationInputs from opaque bytes + auto sim_inputs = deserialize_from_msgpack(inputs); + + // If a fork ID was provided (block builder's fork), use it directly. + // Otherwise create a temporary fork for this simulation. + const bool use_external_fork = sim_inputs.ws_revision.forkId != 0; + uint64_t fork_id = sim_inputs.ws_revision.forkId; + + if (!use_external_fork) { + auto fork_resp = request.wsdb_client.create_fork(wsdb::WsdbCreateFork{ .latest = true, .blockNumber = 0 }); + fork_id = fork_resp.forkId; + vinfo("Created WSDB fork ", fork_id, " for AVM simulation"); + } else { + vinfo("Using external WSDB fork ", fork_id, " for AVM simulation"); + } + + try { + // Create revision pointing to the fork + WorldStateRevision revision = { + .forkId = fork_id, + .blockNumber = 0, + .includeUncommitted = true, + }; + + // Create IPC-backed MerkleDB and ContractDB + 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) + : simulation_helper.simulate_fast_internal(request.cdb_client, + merkle_db, + sim_inputs.config, + sim_inputs.tx, + sim_inputs.global_variables, + sim_inputs.protocol_contracts); + + // Only clean up fork if we created it + if (!use_external_fork) { + request.wsdb_client.delete_fork(wsdb::WsdbDeleteFork{ .forkId = fork_id }); + } + + return Response{ .result = serialize_to_msgpack(result) }; + } catch (...) { + // Only clean up fork on error if we created it + if (!use_external_fork) { + try { + request.wsdb_client.delete_fork(wsdb::WsdbDeleteFork{ .forkId = fork_id }); + } catch (...) { + // Ignore cleanup errors + } + } + throw; + } +} + +// --------------------------------------------------------------------------- +// AvmSimulateWithHints +// --------------------------------------------------------------------------- + +AvmSimulateWithHints::Response AvmSimulateWithHints::execute(AvmRequest& request) && +{ + (void)request; + + // Deserialize AvmProvingInputs from opaque bytes + auto proving_inputs = deserialize_from_msgpack(inputs); + + // Run simulation with hinted DBs (self-contained, no external DB needed) + AvmSimAPI api; + auto result = api.simulate_with_hinted_dbs(proving_inputs); + + return Response{ .result = serialize_to_msgpack(result) }; +} + +// --------------------------------------------------------------------------- +// AvmShutdown +// --------------------------------------------------------------------------- + +AvmShutdown::Response AvmShutdown::execute(AvmRequest& request) && +{ + (void)request; + return Response{}; +} + +} // 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..c61eff7d8920 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/avm_execute.hpp @@ -0,0 +1,60 @@ +#pragma once +/** + * @file avm_execute.hpp + * @brief AvmCommand NamedUnion, AvmRequest context, and dispatch function. + */ + +#include "barretenberg/avm/avm_commands.hpp" +#include "barretenberg/cdb/cdb_ipc_client.hpp" +#include "barretenberg/common/named_union.hpp" +#include "barretenberg/wsdb/wsdb_ipc_client_generated.hpp" + +namespace bb::avm { + +/** + * @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; +}; + +/** + * @brief Error response returned when a command fails. + */ +struct AvmErrorResponse { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "AvmErrorResponse"; + std::string message; + SERIALIZATION_FIELDS(message); + bool operator==(const AvmErrorResponse&) const = default; +}; + +/** + * @brief Union of all AVM commands (request types). + */ +using AvmCommand = NamedUnion; + +/** + * @brief Union of all AVM response types. + */ +using AvmCommandResponse = + NamedUnion; + +/** + * @brief Execute an AVM command using the visitor pattern. + */ +inline AvmCommandResponse execute(AvmRequest& request, AvmCommand&& command) +{ + return std::move(command).visit([&request](auto&& cmd) -> AvmCommandResponse { + using CmdType = std::decay_t; + return std::forward(cmd).execute(request); + }); +} + +/** + * @brief Top-level AVM API entry point. Takes an AvmRequest and dispatches the command. + */ +AvmCommandResponse avm_dispatch(AvmRequest& request, AvmCommand&& command); + +} // 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..24956fa8d9c8 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/avm_ipc_server.cpp @@ -0,0 +1,219 @@ +#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/ipc/ipc_server.hpp" +#include "barretenberg/serialize/msgpack.hpp" +#include "barretenberg/wsdb/wsdb_ipc_client_generated.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include +#elif defined(__APPLE__) +#include +#endif + +namespace bb::avm { + +// --------------------------------------------------------------------------- +// Platform-specific parent death monitoring +// --------------------------------------------------------------------------- + +static void setup_parent_death_monitoring() +{ +#ifdef __linux__ + if (prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) { + std::cerr << "Warning: Could not set parent death signal" << '\n'; + } +#elif defined(__APPLE__) + 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; + } + 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 +} + +// --------------------------------------------------------------------------- +// IPC server execution +// --------------------------------------------------------------------------- + +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 }; + + // Create IPC server + std::unique_ptr server; + + if (input_path.size() >= 5 && input_path.substr(input_path.size() - 5) == ".sock") { + server = ipc::IpcServer::create_socket(input_path, 1); + std::cerr << "Socket server at " << input_path << '\n'; + } else { + std::cerr << "Error: --input path must end with .sock" << '\n'; + return 1; + } + + // Set up signal handlers + static ipc::IpcServer* global_server = server.get(); + + auto graceful_shutdown_handler = [](int signal) { + std::cerr << "\nReceived signal " << signal << ", shutting down gracefully..." << '\n'; + if (global_server) { + global_server->request_shutdown(); + } + }; + + auto fatal_error_handler = [](int signal) { + const char* signal_name = (signal == SIGBUS) ? "SIGBUS" : (signal == SIGSEGV) ? "SIGSEGV" : "UNKNOWN"; + std::cerr << "\nFatal error: received " << signal_name << '\n'; + 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); + + setup_parent_death_monitoring(); + + if (!server->listen()) { + std::cerr << "Error: Could not start IPC server" << '\n'; + return 1; + } + + std::cerr << "aztec-avm IPC server ready" << '\n'; + + // Run server with AVM command handler + server->run([&request](int client_id, std::span raw_request) -> std::vector { + try { + // Deserialize msgpack command + auto unpacked = msgpack::unpack(reinterpret_cast(raw_request.data()), raw_request.size()); + auto obj = unpacked.get(); + + // Expect array of size 1 (tuple wrapping) + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) + if (obj.type != msgpack::type::ARRAY || obj.via.array.size != 1) { + std::cerr << "Error: Expected array of size 1 from client " << client_id << '\n'; + return {}; + } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) + auto& command_obj = obj.via.array.ptr[0]; + + // Check for shutdown before converting + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) + if (command_obj.type == msgpack::type::ARRAY && command_obj.via.array.size == 2 && + command_obj.via.array.ptr[0].type == msgpack::type::STR) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) + std::string_view command_name(command_obj.via.array.ptr[0].via.str.ptr, + command_obj.via.array.ptr[0].via.str.size); + bool is_shutdown = (command_name == "AvmShutdown"); + + // Convert and execute + AvmCommand command; + command_obj.convert(command); + auto response = avm_dispatch(request, 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 (is_shutdown) { + throw ipc::ShutdownRequested(std::move(result)); + } + + return result; + } + + // Fallback: try converting directly + AvmCommand command; + command_obj.convert(command); + auto response = avm_dispatch(request, std::move(command)); + + msgpack::sbuffer response_buffer; + msgpack::pack(response_buffer, response); + return std::vector(response_buffer.data(), response_buffer.data() + response_buffer.size()); + + } catch (const ipc::ShutdownRequested&) { + throw; + } catch (const std::exception& e) { + std::cerr << "Error processing request from client " << client_id << ": " << e.what() << '\n'; + std::cerr.flush(); + + AvmErrorResponse error_response{ .message = std::string(e.what()) }; + AvmCommandResponse response = error_response; + + msgpack::sbuffer response_buffer; + msgpack::pack(response_buffer, response); + return std::vector(response_buffer.data(), response_buffer.data() + response_buffer.size()); + } + }); + + 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/cli.cpp b/barretenberg/cpp/src/barretenberg/avm/cli.cpp new file mode 100644 index 000000000000..d9ac0aa1519e --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/cli.cpp @@ -0,0 +1,79 @@ +#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/serialize/msgpack.hpp" +#include "barretenberg/serialize/msgpack_impl.hpp" + +#include "barretenberg/bb/deps/cli11.hpp" +#include +#include + +namespace bb::avm { + +namespace { + +struct AvmApi { + AvmCommand commands; + AvmCommandResponse responses; + SERIALIZATION_FIELDS(commands, responses); +}; + +std::string get_avm_schema_as_json() +{ + return msgpack_schema_to_string(AvmApi{}); +} + +} // namespace + +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 path (.sock)")->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/avm/wsdb_ipc_merkle_db.cpp b/barretenberg/cpp/src/barretenberg/avm/wsdb_ipc_merkle_db.cpp new file mode 100644 index 000000000000..05d0e43159ed --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/wsdb_ipc_merkle_db.cpp @@ -0,0 +1,214 @@ +#include "barretenberg/avm/wsdb_ipc_merkle_db.hpp" +#include "barretenberg/common/log.hpp" +#include "barretenberg/serialize/msgpack.hpp" +#include "barretenberg/serialize/msgpack_impl.hpp" +#include "barretenberg/vm2/common/aztec_constants.hpp" +#include "barretenberg/wsdb/wsdb_commands.hpp" + +namespace bb::avm { + +// Use avm2::simulation for interface types, but NOT world_state (it transitively +// imports crypto::merkle_tree which conflicts with avm2::simulation aliases). +using namespace avm2::simulation; + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +template std::vector WsdbIpcMerkleDB::serialize_to_msgpack(const T& value) +{ + msgpack::sbuffer buf; + msgpack::pack(buf, value); + return std::vector(buf.data(), buf.data() + buf.size()); +} + +template T WsdbIpcMerkleDB::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; +} + +// --------------------------------------------------------------------------- +// Constructor +// --------------------------------------------------------------------------- + +WsdbIpcMerkleDB::WsdbIpcMerkleDB(wsdb::WsdbIpcClient& client, world_state::WorldStateRevision revision) + : client_(client) + , revision_(revision) +{} + +// --------------------------------------------------------------------------- +// Tree roots +// --------------------------------------------------------------------------- + +avm2::TreeSnapshots WsdbIpcMerkleDB::get_tree_roots() const +{ + auto l1_info = client_.get_tree_info( + wsdb::WsdbGetTreeInfo{ .treeId = MerkleTreeId::L1_TO_L2_MESSAGE_TREE, .revision = revision_ }); + auto nh_info = + client_.get_tree_info(wsdb::WsdbGetTreeInfo{ .treeId = MerkleTreeId::NOTE_HASH_TREE, .revision = revision_ }); + auto null_info = + client_.get_tree_info(wsdb::WsdbGetTreeInfo{ .treeId = MerkleTreeId::NULLIFIER_TREE, .revision = revision_ }); + auto pd_info = + client_.get_tree_info(wsdb::WsdbGetTreeInfo{ .treeId = MerkleTreeId::PUBLIC_DATA_TREE, .revision = revision_ }); + + return avm2::TreeSnapshots{ + .l1_to_l2_message_tree = + avm2::AppendOnlyTreeSnapshot{ .root = l1_info.root, .next_available_leaf_index = l1_info.size }, + .note_hash_tree = + avm2::AppendOnlyTreeSnapshot{ .root = nh_info.root, .next_available_leaf_index = nh_info.size }, + .nullifier_tree = + avm2::AppendOnlyTreeSnapshot{ .root = null_info.root, .next_available_leaf_index = null_info.size }, + .public_data_tree = + avm2::AppendOnlyTreeSnapshot{ .root = pd_info.root, .next_available_leaf_index = pd_info.size }, + }; +} + +// --------------------------------------------------------------------------- +// Query methods +// --------------------------------------------------------------------------- + +SiblingPath WsdbIpcMerkleDB::get_sibling_path(MerkleTreeId tree_id, index_t leaf_index) const +{ + auto resp = client_.get_sibling_path( + wsdb::WsdbGetSiblingPath{ .treeId = tree_id, .revision = revision_, .leafIndex = leaf_index }); + return resp.path; +} + +crypto::merkle_tree::GetLowIndexedLeafResponse WsdbIpcMerkleDB::get_low_indexed_leaf(MerkleTreeId tree_id, + const avm2::FF& value) const +{ + auto resp = client_.find_low_leaf(wsdb::WsdbFindLowLeaf{ .treeId = tree_id, .revision = revision_, .key = value }); + return GetLowIndexedLeafResponse(resp.alreadyPresent, resp.index); +} + +avm2::FF WsdbIpcMerkleDB::get_leaf_value(MerkleTreeId tree_id, index_t leaf_index) const +{ + auto resp = client_.get_leaf_value( + wsdb::WsdbGetLeafValue{ .treeId = tree_id, .revision = revision_, .leafIndex = leaf_index }); + if (!resp.value.has_value()) { + throw std::runtime_error("Invalid get_leaf_value request for tree " + + std::to_string(static_cast(tree_id)) + " index " + + std::to_string(leaf_index)); + } + return deserialize_from_msgpack(resp.value.value()); +} + +IndexedLeaf WsdbIpcMerkleDB::get_leaf_preimage_public_data_tree(index_t leaf_index) const +{ + auto resp = client_.get_leaf_preimage(wsdb::WsdbGetLeafPreimage{ + .treeId = MerkleTreeId::PUBLIC_DATA_TREE, .revision = revision_, .leafIndex = leaf_index }); + if (!resp.preimage.has_value()) { + throw std::runtime_error("Invalid get_leaf_preimage_public_data_tree request for index " + + std::to_string(leaf_index)); + } + return deserialize_from_msgpack>(resp.preimage.value()); +} + +IndexedLeaf WsdbIpcMerkleDB::get_leaf_preimage_nullifier_tree(index_t leaf_index) const +{ + auto resp = client_.get_leaf_preimage(wsdb::WsdbGetLeafPreimage{ + .treeId = MerkleTreeId::NULLIFIER_TREE, .revision = revision_, .leafIndex = leaf_index }); + if (!resp.preimage.has_value()) { + throw std::runtime_error("Invalid get_leaf_preimage_nullifier_tree request for index " + + std::to_string(leaf_index)); + } + return deserialize_from_msgpack>(resp.preimage.value()); +} + +// --------------------------------------------------------------------------- +// State modification methods +// --------------------------------------------------------------------------- + +SequentialInsertionResult WsdbIpcMerkleDB::insert_indexed_leaves_public_data_tree( + const PublicDataLeafValue& leaf_value) +{ + std::vector> serialized_leaves = { serialize_to_msgpack(leaf_value) }; + auto resp = client_.sequential_insert(wsdb::WsdbSequentialInsert{ + .treeId = MerkleTreeId::PUBLIC_DATA_TREE, .leaves = std::move(serialized_leaves), .forkId = revision_.forkId }); + return deserialize_from_msgpack>(resp.result); +} + +SequentialInsertionResult WsdbIpcMerkleDB::insert_indexed_leaves_nullifier_tree( + const NullifierLeafValue& leaf_value) +{ + std::vector> serialized_leaves = { serialize_to_msgpack(leaf_value) }; + auto resp = client_.sequential_insert(wsdb::WsdbSequentialInsert{ + .treeId = MerkleTreeId::NULLIFIER_TREE, .leaves = std::move(serialized_leaves), .forkId = revision_.forkId }); + return deserialize_from_msgpack>(resp.result); +} + +void WsdbIpcMerkleDB::append_leaves(MerkleTreeId tree_id, std::span leaves) +{ + std::vector> serialized_leaves; + serialized_leaves.reserve(leaves.size()); + for (const auto& leaf : leaves) { + serialized_leaves.push_back(serialize_to_msgpack(leaf)); + } + client_.append_leaves(wsdb::WsdbAppendLeaves{ + .treeId = tree_id, .leaves = std::move(serialized_leaves), .forkId = revision_.forkId }); +} + +void WsdbIpcMerkleDB::pad_tree(MerkleTreeId tree_id, size_t num_leaves) +{ + switch (tree_id) { + case MerkleTreeId::NULLIFIER_TREE: { + std::vector> padding_leaves; + padding_leaves.reserve(num_leaves); + auto empty_leaf = NullifierLeafValue::empty(); + for (size_t i = 0; i < num_leaves; i++) { + padding_leaves.push_back(serialize_to_msgpack(empty_leaf)); + } + client_.batch_insert(wsdb::WsdbBatchInsert{ .treeId = MerkleTreeId::NULLIFIER_TREE, + .leaves = std::move(padding_leaves), + .subtreeDepth = NULLIFIER_SUBTREE_HEIGHT, + .forkId = revision_.forkId }); + break; + } + case MerkleTreeId::NOTE_HASH_TREE: { + std::vector> padding_leaves; + padding_leaves.reserve(num_leaves); + auto zero = avm2::FF(0); + for (size_t i = 0; i < num_leaves; i++) { + padding_leaves.push_back(serialize_to_msgpack(zero)); + } + client_.append_leaves(wsdb::WsdbAppendLeaves{ + .treeId = MerkleTreeId::NOTE_HASH_TREE, .leaves = std::move(padding_leaves), .forkId = revision_.forkId }); + break; + } + default: + throw std::runtime_error("Padding not supported for tree " + std::to_string(static_cast(tree_id))); + } +} + +// --------------------------------------------------------------------------- +// Checkpoint methods +// --------------------------------------------------------------------------- + +void WsdbIpcMerkleDB::create_checkpoint() +{ + client_.create_checkpoint(wsdb::WsdbCreateCheckpoint{ .forkId = revision_.forkId }); + uint32_t current_id = checkpoint_stack_.top(); + checkpoint_stack_.push(current_id + 1); +} + +void WsdbIpcMerkleDB::commit_checkpoint() +{ + client_.commit_checkpoint(wsdb::WsdbCommitCheckpoint{ .forkId = revision_.forkId }); + checkpoint_stack_.pop(); +} + +void WsdbIpcMerkleDB::revert_checkpoint() +{ + client_.revert_checkpoint(wsdb::WsdbRevertCheckpoint{ .forkId = revision_.forkId }); + checkpoint_stack_.pop(); +} + +uint32_t WsdbIpcMerkleDB::get_checkpoint_id() const +{ + return checkpoint_stack_.top(); +} + +} // namespace bb::avm diff --git a/barretenberg/cpp/src/barretenberg/avm/wsdb_ipc_merkle_db.hpp b/barretenberg/cpp/src/barretenberg/avm/wsdb_ipc_merkle_db.hpp new file mode 100644 index 000000000000..b12f495bd038 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/avm/wsdb_ipc_merkle_db.hpp @@ -0,0 +1,66 @@ +#pragma once +/** + * @file wsdb_ipc_merkle_db.hpp + * @brief LowLevelMerkleDBInterface implementation backed by WSDB IPC. + * + * Connects to an aztec-wsdb process over Unix Domain Socket and translates + * each LowLevelMerkleDBInterface call into the corresponding WSDB IPC command. + */ + +#include "barretenberg/vm2/simulation/interfaces/db.hpp" +#include "barretenberg/world_state/types.hpp" +#include "barretenberg/wsdb/wsdb_commands.hpp" +#include "barretenberg/wsdb/wsdb_execute.hpp" +#include "barretenberg/wsdb/wsdb_ipc_client_generated.hpp" + +#include + +namespace bb::avm { + +class WsdbIpcMerkleDB final : public avm2::simulation::LowLevelMerkleDBInterface { + public: + /** + * @brief Construct from a connected WSDB IPC client and world state revision. + * @param client Reference to a connected WsdbIpcClient. + * @param revision The world state revision (includes forkId) to use for queries. + */ + WsdbIpcMerkleDB(wsdb::WsdbIpcClient& client, world_state::WorldStateRevision revision); + + avm2::TreeSnapshots get_tree_roots() const override; + + // Query methods + avm2::simulation::SiblingPath get_sibling_path(avm2::simulation::MerkleTreeId tree_id, + avm2::simulation::index_t leaf_index) const override; + crypto::merkle_tree::GetLowIndexedLeafResponse get_low_indexed_leaf(avm2::simulation::MerkleTreeId tree_id, + const avm2::FF& value) const override; + avm2::FF get_leaf_value(avm2::simulation::MerkleTreeId tree_id, + avm2::simulation::index_t leaf_index) const override; + avm2::simulation::IndexedLeaf get_leaf_preimage_public_data_tree( + avm2::simulation::index_t leaf_index) const override; + avm2::simulation::IndexedLeaf get_leaf_preimage_nullifier_tree( + avm2::simulation::index_t leaf_index) const override; + + // State modification methods + avm2::simulation::SequentialInsertionResult + insert_indexed_leaves_public_data_tree(const avm2::simulation::PublicDataLeafValue& leaf_value) override; + avm2::simulation::SequentialInsertionResult + insert_indexed_leaves_nullifier_tree(const avm2::simulation::NullifierLeafValue& leaf_value) override; + void append_leaves(avm2::simulation::MerkleTreeId tree_id, std::span leaves) override; + void pad_tree(avm2::simulation::MerkleTreeId tree_id, size_t num_leaves) override; + + // Checkpoint methods + void create_checkpoint() override; + void commit_checkpoint() override; + void revert_checkpoint() override; + uint32_t get_checkpoint_id() const override; + + private: + template static std::vector serialize_to_msgpack(const T& value); + template static T deserialize_from_msgpack(const std::vector& bytes); + + wsdb::WsdbIpcClient& client_; + world_state::WorldStateRevision revision_; + std::stack checkpoint_stack_{ { 0 } }; +}; + +} // namespace bb::avm diff --git a/barretenberg/cpp/src/barretenberg/cdb/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/cdb/CMakeLists.txt new file mode 100644 index 000000000000..190ce5d3a7c4 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/cdb/CMakeLists.txt @@ -0,0 +1,40 @@ +if(NOT(FUZZING) AND NOT(WASM)) + # IPC client library (used by AVM simulator to talk to aztec-cdb) + add_library( + cdb_ipc_client + STATIC + cdb_ipc_client_generated.cpp + cdb_ipc_client.cpp + ) + target_link_libraries( + cdb_ipc_client + PUBLIC + barretenberg + ipc + ) + + # aztec-cdb binary (schema provider for codegen) + add_executable( + aztec-cdb + main.cpp + cli.cpp + ) + target_link_libraries( + aztec-cdb + PRIVATE + barretenberg + ipc + ) + if(ENABLE_STACKTRACES) + target_link_libraries( + aztec-cdb + PUBLIC + Backward::Interface + ) + target_link_options( + aztec-cdb + PRIVATE + -ldw -lelf + ) + endif() +endif() diff --git a/barretenberg/cpp/src/barretenberg/cdb/cdb_commands.hpp b/barretenberg/cpp/src/barretenberg/cdb/cdb_commands.hpp new file mode 100644 index 000000000000..af1d5ff71215 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/cdb/cdb_commands.hpp @@ -0,0 +1,218 @@ +#pragma once +/** + * @file cdb_commands.hpp + * @brief NamedUnion command structs for the aztec-cdb contracts database API. + * + * Each command follows the bbapi pattern: + * - static constexpr MSGPACK_SCHEMA_NAME for NamedUnion dispatch + * - Nested Response struct with its own MSGPACK_SCHEMA_NAME + * - Request fields with SERIALIZATION_FIELDS + * - execute(CdbRequest&) && method (implemented in cdb_execute.cpp) + */ + +#include "barretenberg/serialize/msgpack.hpp" +#include "barretenberg/vm2/common/aztec_types.hpp" +#include "barretenberg/vm2/common/field.hpp" + +#include +#include +#include + +namespace bb::cdb { + +// Forward declaration +struct CdbRequest; + +// --------------------------------------------------------------------------- +// Contract queries (matching ContractDBInterface) +// --------------------------------------------------------------------------- + +struct CdbGetContractInstance { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbGetContractInstance"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbGetContractInstanceResponse"; + std::optional instance; + SERIALIZATION_FIELDS(instance); + bool operator==(const Response&) const = default; + }; + avm2::AztecAddress address; + Response execute(CdbRequest& request) &&; + SERIALIZATION_FIELDS(address); + bool operator==(const CdbGetContractInstance&) const = default; +}; + +struct CdbGetContractClass { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbGetContractClass"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbGetContractClassResponse"; + std::optional contractClass; + SERIALIZATION_FIELDS(contractClass); + bool operator==(const Response&) const = default; + }; + avm2::ContractClassId classId; + Response execute(CdbRequest& request) &&; + SERIALIZATION_FIELDS(classId); + bool operator==(const CdbGetContractClass&) const = default; +}; + +struct CdbGetBytecodeCommitment { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbGetBytecodeCommitment"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbGetBytecodeCommitmentResponse"; + std::optional commitment; + SERIALIZATION_FIELDS(commitment); + bool operator==(const Response&) const = default; + }; + avm2::ContractClassId classId; + Response execute(CdbRequest& request) &&; + SERIALIZATION_FIELDS(classId); + bool operator==(const CdbGetBytecodeCommitment&) const = default; +}; + +struct CdbGetDebugFunctionName { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbGetDebugFunctionName"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbGetDebugFunctionNameResponse"; + std::optional name; + SERIALIZATION_FIELDS(name); + bool operator==(const Response&) const = default; + }; + avm2::AztecAddress address; + avm2::FunctionSelector selector; + Response execute(CdbRequest& request) &&; + SERIALIZATION_FIELDS(address, selector); + bool operator==(const CdbGetDebugFunctionName&) const = default; +}; + +// --------------------------------------------------------------------------- +// Contract mutation (used by AVM during simulation) +// --------------------------------------------------------------------------- + +struct CdbAddContracts { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbAddContracts"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbAddContractsResponse"; + void msgpack(auto&& pack_fn) { pack_fn(); } + bool operator==(const Response&) const = default; + }; + avm2::ContractDeploymentData contractDeploymentData; + Response execute(CdbRequest& request) &&; + SERIALIZATION_FIELDS(contractDeploymentData); + bool operator==(const CdbAddContracts&) const = default; +}; + +// --------------------------------------------------------------------------- +// Checkpoint operations (tx-scoped rollback support) +// --------------------------------------------------------------------------- + +struct CdbCreateCheckpoint { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbCreateCheckpoint"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbCreateCheckpointResponse"; + void msgpack(auto&& pack_fn) { pack_fn(); } + bool operator==(const Response&) const = default; + }; + Response execute(CdbRequest& request) &&; + void msgpack(auto&& pack_fn) { pack_fn(); } + bool operator==(const CdbCreateCheckpoint&) const = default; +}; + +struct CdbCommitCheckpoint { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbCommitCheckpoint"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbCommitCheckpointResponse"; + void msgpack(auto&& pack_fn) { pack_fn(); } + bool operator==(const Response&) const = default; + }; + Response execute(CdbRequest& request) &&; + void msgpack(auto&& pack_fn) { pack_fn(); } + bool operator==(const CdbCommitCheckpoint&) const = default; +}; + +struct CdbRevertCheckpoint { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbRevertCheckpoint"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbRevertCheckpointResponse"; + void msgpack(auto&& pack_fn) { pack_fn(); } + bool operator==(const Response&) const = default; + }; + Response execute(CdbRequest& request) &&; + void msgpack(auto&& pack_fn) { pack_fn(); } + bool operator==(const CdbRevertCheckpoint&) const = default; +}; + +// --------------------------------------------------------------------------- +// Management operations (used by TS node to populate store) +// --------------------------------------------------------------------------- + +struct CdbAddContractClass { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbAddContractClass"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbAddContractClassResponse"; + void msgpack(auto&& pack_fn) { pack_fn(); } + bool operator==(const Response&) const = default; + }; + avm2::ContractClass contractClass; + avm2::FF bytecodeCommitment; + Response execute(CdbRequest& request) &&; + SERIALIZATION_FIELDS(contractClass, bytecodeCommitment); + bool operator==(const CdbAddContractClass&) const = default; +}; + +struct CdbAddContractInstance { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbAddContractInstance"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbAddContractInstanceResponse"; + void msgpack(auto&& pack_fn) { pack_fn(); } + bool operator==(const Response&) const = default; + }; + avm2::AztecAddress address; + avm2::ContractInstance instance; + Response execute(CdbRequest& request) &&; + SERIALIZATION_FIELDS(address, instance); + bool operator==(const CdbAddContractInstance&) const = default; +}; + +struct CdbRegisterFunctionSignatures { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbRegisterFunctionSignatures"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbRegisterFunctionSignaturesResponse"; + void msgpack(auto&& pack_fn) { pack_fn(); } + bool operator==(const Response&) const = default; + }; + std::vector signatures; + Response execute(CdbRequest& request) &&; + SERIALIZATION_FIELDS(signatures); + bool operator==(const CdbRegisterFunctionSignatures&) const = default; +}; + +struct CdbGetContractClassIds { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbGetContractClassIds"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbGetContractClassIdsResponse"; + std::vector classIds; + SERIALIZATION_FIELDS(classIds); + bool operator==(const Response&) const = default; + }; + Response execute(CdbRequest& request) &&; + void msgpack(auto&& pack_fn) { pack_fn(); } + bool operator==(const CdbGetContractClassIds&) const = default; +}; + +// --------------------------------------------------------------------------- +// Lifecycle +// --------------------------------------------------------------------------- + +struct CdbShutdown { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbShutdown"; + struct Response { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbShutdownResponse"; + void msgpack(auto&& pack_fn) { pack_fn(); } + bool operator==(const Response&) const = default; + }; + void msgpack(auto&& pack_fn) { pack_fn(); } + Response execute(CdbRequest& request) &&; + bool operator==(const CdbShutdown&) const = default; +}; + +} // namespace bb::cdb diff --git a/barretenberg/cpp/src/barretenberg/cdb/cdb_execute.hpp b/barretenberg/cpp/src/barretenberg/cdb/cdb_execute.hpp new file mode 100644 index 000000000000..7a7f8adbde5b --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/cdb/cdb_execute.hpp @@ -0,0 +1,57 @@ +#pragma once +/** + * @file cdb_execute.hpp + * @brief CdbCommand NamedUnion, CdbRequest context, and dispatch function. + */ + +#include "barretenberg/cdb/cdb_commands.hpp" +#include "barretenberg/common/named_union.hpp" + +namespace bb::cdb { + +/** + * @brief Error response returned when a command fails. + */ +struct CdbErrorResponse { + static constexpr const char MSGPACK_SCHEMA_NAME[] = "CdbErrorResponse"; + std::string message; + SERIALIZATION_FIELDS(message); + bool operator==(const CdbErrorResponse&) const = default; +}; + +/** + * @brief Union of all cdb commands (request types). + */ +using CdbCommand = NamedUnion; + +/** + * @brief Union of all cdb response types. + */ +using CdbCommandResponse = NamedUnion; + +} // namespace bb::cdb 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..c78b7c7ece96 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client.cpp @@ -0,0 +1,56 @@ +#include "barretenberg/cdb/cdb_ipc_client.hpp" + +namespace bb::cdb { + +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 = address }); + return resp.instance; +} + +std::optional CdbIpcContractDB::get_contract_class(const avm2::ContractClassId& class_id) const +{ + auto resp = client_->get_contract_class(CdbGetContractClass{ .classId = class_id }); + return resp.contractClass; +} + +std::optional CdbIpcContractDB::get_bytecode_commitment(const avm2::ContractClassId& class_id) const +{ + auto resp = client_->get_bytecode_commitment(CdbGetBytecodeCommitment{ .classId = class_id }); + return resp.commitment; +} + +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 = address, .selector = selector }); + return resp.name; +} + +void CdbIpcContractDB::add_contracts(const avm2::ContractDeploymentData& contract_deployment_data) +{ + client_->add_contracts(CdbAddContracts{ .contractDeploymentData = contract_deployment_data }); +} + +void CdbIpcContractDB::create_checkpoint() +{ + client_->create_checkpoint(); +} + +void CdbIpcContractDB::commit_checkpoint() +{ + client_->commit_checkpoint(); +} + +void CdbIpcContractDB::revert_checkpoint() +{ + client_->revert_checkpoint(); +} + +} // 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..653a42e98374 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client.hpp @@ -0,0 +1,40 @@ +#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/cdb_ipc_client_generated.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; + + // 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_; +}; + +} // namespace bb::cdb diff --git a/barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client_generated.cpp b/barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client_generated.cpp new file mode 100644 index 000000000000..46c0f3d28eb5 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client_generated.cpp @@ -0,0 +1,139 @@ +// AUTOGENERATED FILE - DO NOT EDIT + +#include "barretenberg/cdb/cdb_ipc_client_generated.hpp" +#include "barretenberg/cdb/cdb_execute.hpp" +#include "barretenberg/serialize/msgpack.hpp" +#include "barretenberg/serialize/msgpack_impl.hpp" + +#include +#include + +namespace bb::cdb { + +CdbIpcClient::CdbIpcClient(const std::string& socket_path) + : client_(ipc::IpcClient::create_socket(socket_path)) +{ + if (!client_->connect()) { + throw std::runtime_error("Failed to connect to server at " + socket_path); + } +} + +CdbIpcClient::~CdbIpcClient() +{ + if (client_) { + client_->close(); + } +} + +template typename Cmd::Response CdbIpcClient::send(Cmd&& cmd) const +{ + // Wrap command in CdbCommand NamedUnion, then in a 1-element tuple (matches server expectations) + CdbCommand command = std::forward(cmd); + auto wrapped = std::make_tuple(std::move(command)); + + // Serialize to msgpack + msgpack::sbuffer send_buffer; + msgpack::pack(send_buffer, wrapped); + + // Send to server + constexpr uint64_t timeout_ns = 30'000'000'000ULL; // 30 seconds + if (!client_->send(send_buffer.data(), send_buffer.size(), timeout_ns)) { + throw std::runtime_error("Failed to send command to server"); + } + + // Receive response + auto response_span = client_->receive(timeout_ns); + if (response_span.empty()) { + throw std::runtime_error("Empty response from server"); + } + + // Deserialize response + auto unpacked = msgpack::unpack(reinterpret_cast(response_span.data()), response_span.size()); + auto response_obj = unpacked.get(); + + CdbCommandResponse response; + response_obj.convert(response); + + // Release the receive buffer + client_->release(response_span.size()); + + // Check for error response + return std::move(response).visit([](auto&& resp) -> typename Cmd::Response { + using RespType = std::decay_t; + + if constexpr (std::is_same_v) { + throw std::runtime_error("Server error: " + resp.message); + } else if constexpr (std::is_same_v) { + return std::forward(resp); + } else { + throw std::runtime_error("Unexpected response type from server"); + } + }); +} + +CdbGetContractInstance::Response CdbIpcClient::get_contract_instance(CdbGetContractInstance cmd) const +{ + return send(std::move(cmd)); +} + +CdbGetContractClass::Response CdbIpcClient::get_contract_class(CdbGetContractClass cmd) const +{ + return send(std::move(cmd)); +} + +CdbGetBytecodeCommitment::Response CdbIpcClient::get_bytecode_commitment(CdbGetBytecodeCommitment cmd) +{ + return send(std::move(cmd)); +} + +CdbGetDebugFunctionName::Response CdbIpcClient::get_debug_function_name(CdbGetDebugFunctionName cmd) const +{ + return send(std::move(cmd)); +} + +void CdbIpcClient::add_contracts(CdbAddContracts cmd) +{ + send(std::move(cmd)); +} + +void CdbIpcClient::create_checkpoint() +{ + send(CdbCreateCheckpoint{}); +} + +void CdbIpcClient::commit_checkpoint() +{ + send(CdbCommitCheckpoint{}); +} + +void CdbIpcClient::revert_checkpoint() +{ + send(CdbRevertCheckpoint{}); +} + +void CdbIpcClient::add_contract_class(CdbAddContractClass cmd) +{ + send(std::move(cmd)); +} + +void CdbIpcClient::add_contract_instance(CdbAddContractInstance cmd) +{ + send(std::move(cmd)); +} + +void CdbIpcClient::register_function_signatures(CdbRegisterFunctionSignatures cmd) +{ + send(std::move(cmd)); +} + +CdbGetContractClassIds::Response CdbIpcClient::get_contract_class_ids() const +{ + return send(CdbGetContractClassIds{}); +} + +void CdbIpcClient::shutdown() +{ + send(CdbShutdown{}); +} + +} // namespace bb::cdb diff --git a/barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client_generated.hpp b/barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client_generated.hpp new file mode 100644 index 000000000000..d8869857c2e6 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/cdb/cdb_ipc_client_generated.hpp @@ -0,0 +1,47 @@ +// AUTOGENERATED FILE - DO NOT EDIT +#pragma once + +#include "barretenberg/cdb/cdb_execute.hpp" +#include "barretenberg/common/try_catch_shim.hpp" +#include "barretenberg/ipc/ipc_client.hpp" + +#include +#include + +namespace bb::cdb { + +/** + * @brief Auto-generated IPC client. + * + * Each method sends a msgpack-serialized command to the server over UDS + * and returns the typed response. All methods block until the response arrives. + */ +class CdbIpcClient { + public: + explicit CdbIpcClient(const std::string& socket_path); + ~CdbIpcClient(); + + CdbIpcClient(const CdbIpcClient&) = delete; + CdbIpcClient& operator=(const CdbIpcClient&) = delete; + + CdbGetContractInstance::Response get_contract_instance(CdbGetContractInstance cmd) const; + CdbGetContractClass::Response get_contract_class(CdbGetContractClass cmd) const; + CdbGetBytecodeCommitment::Response get_bytecode_commitment(CdbGetBytecodeCommitment cmd); + CdbGetDebugFunctionName::Response get_debug_function_name(CdbGetDebugFunctionName cmd) const; + void add_contracts(CdbAddContracts cmd); + void create_checkpoint(); + void commit_checkpoint(); + void revert_checkpoint(); + void add_contract_class(CdbAddContractClass cmd); + void add_contract_instance(CdbAddContractInstance cmd); + void register_function_signatures(CdbRegisterFunctionSignatures cmd); + CdbGetContractClassIds::Response get_contract_class_ids() const; + void shutdown(); + + private: + template typename Cmd::Response send(Cmd&& cmd) const; + + mutable std::unique_ptr client_; +}; + +} // namespace bb::cdb diff --git a/barretenberg/cpp/src/barretenberg/cdb/cli.cpp b/barretenberg/cpp/src/barretenberg/cdb/cli.cpp new file mode 100644 index 000000000000..06df5abb281a --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/cdb/cli.cpp @@ -0,0 +1,61 @@ +#include "barretenberg/cdb/cli.hpp" +#include "barretenberg/cdb/cdb_execute.hpp" +#include "barretenberg/serialize/msgpack.hpp" +#include "barretenberg/serialize/msgpack_impl.hpp" + +#include "barretenberg/bb/deps/cli11.hpp" +#include +#include + +namespace bb::cdb { + +namespace { + +struct CdbApi { + CdbCommand commands; + CdbCommandResponse responses; + SERIALIZATION_FIELDS(commands, responses); +}; + +std::string get_cdb_schema_as_json() +{ + return msgpack_schema_to_string(CdbApi{}); +} + +} // namespace + +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/ipc/ipc_client.cpp b/barretenberg/cpp/src/barretenberg/ipc/ipc_client.cpp index afeb8998c2e7..49d6c5da4df9 100644 --- a/barretenberg/cpp/src/barretenberg/ipc/ipc_client.cpp +++ b/barretenberg/cpp/src/barretenberg/ipc/ipc_client.cpp @@ -1,4 +1,5 @@ #include "barretenberg/ipc/ipc_client.hpp" +#include "barretenberg/ipc/mpsc_shm_client.hpp" #include "barretenberg/ipc/shm_client.hpp" #include "barretenberg/ipc/socket_client.hpp" #include @@ -17,4 +18,9 @@ std::unique_ptr IpcClient::create_shm(const std::string& base_name) return std::make_unique(base_name); } +std::unique_ptr IpcClient::create_mpsc_shm(const std::string& base_name, size_t client_id) +{ + return std::make_unique(base_name, client_id); +} + } // namespace bb::ipc diff --git a/barretenberg/cpp/src/barretenberg/ipc/ipc_client.hpp b/barretenberg/cpp/src/barretenberg/ipc/ipc_client.hpp index 3f76a91ee82e..a14879efe4b2 100644 --- a/barretenberg/cpp/src/barretenberg/ipc/ipc_client.hpp +++ b/barretenberg/cpp/src/barretenberg/ipc/ipc_client.hpp @@ -72,6 +72,7 @@ class IpcClient { // Factory methods static std::unique_ptr create_socket(const std::string& socket_path); static std::unique_ptr create_shm(const std::string& base_name); + static std::unique_ptr create_mpsc_shm(const std::string& base_name, size_t client_id); }; } // namespace bb::ipc diff --git a/barretenberg/cpp/src/barretenberg/ipc/ipc_server.cpp b/barretenberg/cpp/src/barretenberg/ipc/ipc_server.cpp index 4f23bafe146d..4833d7029d4c 100644 --- a/barretenberg/cpp/src/barretenberg/ipc/ipc_server.cpp +++ b/barretenberg/cpp/src/barretenberg/ipc/ipc_server.cpp @@ -1,4 +1,5 @@ #include "barretenberg/ipc/ipc_server.hpp" +#include "barretenberg/ipc/mpsc_shm_server.hpp" #include "barretenberg/ipc/shm_server.hpp" #include "barretenberg/ipc/socket_server.hpp" #include @@ -19,4 +20,12 @@ std::unique_ptr IpcServer::create_shm(const std::string& base_name, return std::make_unique(base_name, request_ring_size, response_ring_size); } +std::unique_ptr IpcServer::create_mpsc_shm(const std::string& base_name, + size_t max_clients, + size_t request_ring_size, + size_t response_ring_size) +{ + return std::make_unique(base_name, max_clients, request_ring_size, response_ring_size); +} + } // namespace bb::ipc diff --git a/barretenberg/cpp/src/barretenberg/ipc/ipc_server.hpp b/barretenberg/cpp/src/barretenberg/ipc/ipc_server.hpp index a0b43bcb9592..0684ebeea898 100644 --- a/barretenberg/cpp/src/barretenberg/ipc/ipc_server.hpp +++ b/barretenberg/cpp/src/barretenberg/ipc/ipc_server.hpp @@ -194,6 +194,10 @@ class IpcServer { 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 }; diff --git a/barretenberg/cpp/src/barretenberg/ipc/mpsc_shm_client.hpp b/barretenberg/cpp/src/barretenberg/ipc/mpsc_shm_client.hpp new file mode 100644 index 000000000000..51922b14f9a1 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/ipc/mpsc_shm_client.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include "ipc_client.hpp" +#include "shm/mpsc_shm.hpp" +#include "shm/spsc_shm.hpp" +#include "shm_common.hpp" +#include +#include +#include +#include +#include +#include + +namespace bb::ipc { + +/** + * @brief IPC client for multi-client shared memory server + * + * Uses MpscProducer for sending requests and a dedicated SPSC ring for + * receiving responses. Each client is assigned a unique client_id. + */ +class MpscShmClient : public IpcClient { + public: + MpscShmClient(std::string base_name, size_t client_id) + : base_name_(std::move(base_name)) + , client_id_(client_id) + {} + + ~MpscShmClient() override = default; + + // Non-copyable, non-movable + MpscShmClient(const MpscShmClient&) = delete; + MpscShmClient& operator=(const MpscShmClient&) = delete; + MpscShmClient(MpscShmClient&&) = delete; + MpscShmClient& operator=(MpscShmClient&&) = delete; + + bool connect() override + { + if (producer_.has_value()) { + return true; // Already connected + } + + try { + // Connect as producer to the MPSC request system + producer_ = MpscProducer::connect(base_name_ + "_req", client_id_); + + // Connect to our dedicated SPSC response ring + std::string resp_name = base_name_ + "_resp_" + std::to_string(client_id_); + response_ring_ = SpscShm::connect(resp_name); + + return true; + } catch (...) { + producer_.reset(); + response_ring_.reset(); + return false; + } + } + + bool send(const void* data, size_t len, uint64_t timeout_ns) override + { + if (!producer_.has_value()) { + return false; + } + + // Claim space for length prefix + data + size_t total_size = sizeof(uint32_t) + len; + void* buf = producer_->claim(total_size, static_cast(timeout_ns)); + if (buf == nullptr) { + return false; + } + + // Write length prefix + data + auto len_u32 = static_cast(len); + std::memcpy(buf, &len_u32, sizeof(uint32_t)); + std::memcpy(static_cast(buf) + sizeof(uint32_t), data, len); + + // Publish (rings doorbell to wake server) + producer_->publish(total_size); + return true; + } + + std::span receive(uint64_t timeout_ns) override + { + if (!response_ring_.has_value()) { + return {}; + } + return ring_receive_msg(response_ring_.value(), timeout_ns); + } + + void release(size_t message_size) override + { + if (!response_ring_.has_value()) { + return; + } + response_ring_->release(sizeof(uint32_t) + message_size); + } + + void close() override + { + producer_.reset(); + response_ring_.reset(); + } + + private: + std::string base_name_; + size_t client_id_; + std::optional producer_; + std::optional response_ring_; +}; + +} // namespace bb::ipc diff --git a/barretenberg/cpp/src/barretenberg/ipc/mpsc_shm_server.hpp b/barretenberg/cpp/src/barretenberg/ipc/mpsc_shm_server.hpp new file mode 100644 index 000000000000..f6b52b9c5524 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/ipc/mpsc_shm_server.hpp @@ -0,0 +1,154 @@ +#pragma once + +#include "ipc_server.hpp" +#include "shm/mpsc_shm.hpp" +#include "shm/spsc_shm.hpp" +#include "shm_common.hpp" +#include +#include +#include +#include +#include +#include + +namespace bb::ipc { + +/** + * @brief IPC server implementation using shared memory with multi-client support + * + * Uses MPSC (multi-producer single-consumer) for requests and per-client SPSC + * rings for responses. Supports up to max_clients concurrent clients. + * + * Shared memory layout: + * - Request: MPSC consumer with one SPSC ring per client (client writes, server reads) + * - Response: Separate SPSC ring per client (server writes, client reads) + */ +class MpscShmServer : public IpcServer { + public: + static constexpr size_t DEFAULT_RING_SIZE = 1 << 20; // 1MB + + MpscShmServer(std::string base_name, + size_t max_clients, + size_t request_ring_size = DEFAULT_RING_SIZE, + size_t response_ring_size = DEFAULT_RING_SIZE) + : base_name_(std::move(base_name)) + , max_clients_(max_clients) + , request_ring_size_(request_ring_size) + , response_ring_size_(response_ring_size) + {} + + ~MpscShmServer() override { close(); } + + // Non-copyable, non-movable + MpscShmServer(const MpscShmServer&) = delete; + MpscShmServer& operator=(const MpscShmServer&) = delete; + MpscShmServer(MpscShmServer&&) = delete; + MpscShmServer& operator=(MpscShmServer&&) = delete; + + bool listen() override + { + if (request_consumer_.has_value()) { + return true; // Already listening + } + + // Clean up any leftover shared memory + MpscConsumer::unlink(base_name_ + "_req", max_clients_); + for (size_t i = 0; i < max_clients_; i++) { + SpscShm::unlink(base_name_ + "_resp_" + std::to_string(i)); + } + + try { + // Create MPSC consumer for requests (one ring per client) + request_consumer_ = MpscConsumer::create(base_name_ + "_req", max_clients_, request_ring_size_); + + // Create per-client SPSC response rings + response_rings_.reserve(max_clients_); + for (size_t i = 0; i < max_clients_; i++) { + std::string resp_name = base_name_ + "_resp_" + std::to_string(i); + response_rings_.push_back(SpscShm::create(resp_name, response_ring_size_)); + } + + return true; + } catch (...) { + close(); + return false; + } + } + + int wait_for_data(uint64_t timeout_ns) override + { + if (!request_consumer_.has_value()) { + return -1; + } + // MpscConsumer::wait_for_data returns ring index = client_id + return request_consumer_->wait_for_data(static_cast(timeout_ns)); + } + + std::span receive(int client_id) override + { + if (!request_consumer_.has_value() || client_id < 0 || static_cast(client_id) >= max_clients_) { + return {}; + } + // Peek on the specific client's request ring via MpscConsumer + void* len_ptr = request_consumer_->peek(static_cast(client_id), sizeof(uint32_t), 100000000); + if (len_ptr == nullptr) { + return {}; + } + uint32_t msg_len = 0; + std::memcpy(&msg_len, len_ptr, sizeof(uint32_t)); + + void* msg_ptr = request_consumer_->peek(static_cast(client_id), sizeof(uint32_t) + msg_len, 100000000); + if (msg_ptr == nullptr) { + return {}; + } + return std::span(static_cast(msg_ptr) + sizeof(uint32_t), msg_len); + } + + void release(int client_id, size_t message_size) override + { + if (!request_consumer_.has_value() || client_id < 0 || static_cast(client_id) >= max_clients_) { + return; + } + request_consumer_->release(static_cast(client_id), sizeof(uint32_t) + message_size); + } + + bool send(int client_id, const void* data, size_t len) override + { + if (client_id < 0 || static_cast(client_id) >= response_rings_.size()) { + return false; + } + return ring_send_msg(response_rings_[static_cast(client_id)], data, len, 100000000); + } + + void close() override + { + request_consumer_.reset(); + response_rings_.clear(); + + // Clean up shared memory + MpscConsumer::unlink(base_name_ + "_req", max_clients_); + for (size_t i = 0; i < max_clients_; i++) { + SpscShm::unlink(base_name_ + "_resp_" + std::to_string(i)); + } + } + + void wakeup_all() override + { + if (request_consumer_.has_value()) { + request_consumer_->wakeup_all(); + } + for (auto& ring : response_rings_) { + ring.wakeup_all(); + } + } + + private: + std::string base_name_; + size_t max_clients_; + size_t request_ring_size_; + size_t response_ring_size_; + std::optional request_consumer_; + std::vector response_rings_; +}; + +} // namespace bb::ipc diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/nodejs_module/CMakeLists.txt index 1eb6cee25bd1..a7259db60b44 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 world_state ipc) # 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 01c19f45e6b5..000000000000 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/avm_simulate/avm_simulate_napi.cpp +++ /dev/null @@ -1,431 +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); - // Create threaded operation that runs on a dedicated std::thread (not libuv pool). - // This prevents libuv thread pool exhaustion when callbacks need libuv threads for I/O. - auto* op = new ThreadedAsyncOperation( - 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"); - } - }); - - op->Queue(); - - 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); - - // Create threaded operation that runs on a dedicated std::thread (not libuv pool) - auto* op = new ThreadedAsyncOperation(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"); - } - }); - - op->Queue(); - - 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..1a5a0d0dd396 100644 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/init_module.cpp +++ b/barretenberg/cpp/src/barretenberg/nodejs_module/init_module.cpp @@ -1,25 +1,15 @@ -#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" 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/msgpack_client/msgpack_client_async.cpp b/barretenberg/cpp/src/barretenberg/nodejs_module/msgpack_client/msgpack_client_async.cpp index 5b46c505f5ed..1f5b1c50dc1e 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 @@ -17,8 +17,13 @@ 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); + // Arg 1 (optional): client ID for MPSC mode (number) + if (info.Length() >= 2 && info[1].IsNumber()) { + size_t client_id = info[1].As().Uint32Value(); + client_ = bb::ipc::IpcClient::create_mpsc_shm(shm_name, client_id); + } else { + client_ = bb::ipc::IpcClient::create_shm(shm_name); + } // Connect to bb server if (!client_->connect()) { 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 2386799b19a0..000000000000 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/world_state/world_state.cpp +++ /dev/null @@ -1,950 +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"); - } - - // optional parameters - size_t map_size_index = 5; - 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)) { - map_size[tree_id] = obj.Get(tree_id).As().Uint32Value(); - } - } - } else if (info[map_size_index].IsNumber()) { - uint64_t size = info[map_size_index].As().Uint32Value(); - 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 = 6; - 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); - - _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; - } - } - - 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; - } - } - - 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; - } - } - - 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/vm2/simulation/gadgets/contract_instance_manager.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/contract_instance_manager.cpp index 5a5d77aba744..9a16e5dfe0cc 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/contract_instance_manager.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/contract_instance_manager.cpp @@ -94,7 +94,8 @@ std::optional ContractInstanceManager::get_contract_instance(c return std::nullopt; } - BB_ASSERT(maybe_instance.has_value(), "Contract instance should be found if nullifier exists"); + BB_ASSERT(maybe_instance.has_value(), + "Contract instance should be found if nullifier exists: address=" << contract_address); const ContractInstance& instance = maybe_instance.value(); // Validate that the contract instance is the latest if there have been any updates. diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp index b2468f74de92..2489bd6acea7 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp @@ -604,6 +604,38 @@ TxSimulationResult AvmSimulationHelper::simulate_for_hint_collection( return std::move(tx_result); } +TxSimulationResult AvmSimulationHelper::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, + CancellationTokenPtr 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); + HintingRawDB hinting_merkle_db(raw_merkle_db); + + // We use NoopEventEmitters here because we don't want to collect events. + auto [/* unused */ events_, tx_result] = simulate_for_witgen_internal( + hinting_contract_db, hinting_merkle_db, config, tx, global_variables, protocol_contracts); + + ExecutionHints collected_hints = ExecutionHints{ .global_variables = global_variables, + .tx = tx, + .protocol_contracts = protocol_contracts, + .starting_tree_roots = starting_tree_roots }; + hinting_contract_db.dump_hints(collected_hints); + hinting_merkle_db.dump_hints(collected_hints); + + tx_result.hints = std::move(collected_hints); + + return std::move(tx_result); +} + 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..7cca20f4911a 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.hpp @@ -37,7 +37,7 @@ 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: + // Direct entry point that takes pre-constructed DB interfaces (used by aztec-avm binary). TxSimulationResult simulate_fast_internal(simulation::ContractDBInterface& raw_contract_db, simulation::LowLevelMerkleDBInterface& raw_merkle_db, const PublicSimulatorConfig& config, @@ -46,6 +46,17 @@ class AvmSimulationHelper { const ProtocolContracts& protocol_contracts, simulation::CancellationTokenPtr cancellation_token = nullptr); + // Hint collection entry point that takes pre-constructed DB interfaces (used by aztec-avm binary). + 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