Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
avm_ipc_server.cpp
Go to the documentation of this file.
8
9#include <chrono>
10#include <csignal>
11#include <iostream>
12#include <memory>
13#include <string>
14#include <thread>
15#include <unistd.h>
16#include <vector>
17
18#ifdef __linux__
19#include <sys/prctl.h>
20#elif defined(__APPLE__)
21#include <sys/event.h>
22#endif
23
24namespace bb::avm {
25
26// ---------------------------------------------------------------------------
27// Platform-specific parent death monitoring
28// ---------------------------------------------------------------------------
29
30static void setup_parent_death_monitoring()
31{
32#ifdef __linux__
33 if (prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) {
34 std::cerr << "Warning: Could not set parent death signal" << '\n';
35 }
36#elif defined(__APPLE__)
37 pid_t parent_pid = getppid();
38 std::thread([parent_pid]() {
39 int kq = kqueue();
40 if (kq == -1) {
41 std::cerr << "Warning: Could not create kqueue for parent monitoring" << '\n';
42 return;
43 }
44 struct kevent change;
45 EV_SET(&change, parent_pid, EVFILT_PROC, EV_ADD | EV_ENABLE, NOTE_EXIT, 0, nullptr);
46 if (kevent(kq, &change, 1, nullptr, 0, nullptr) == -1) {
47 std::cerr << "Warning: Could not monitor parent process" << '\n';
48 close(kq);
49 return;
50 }
51 struct kevent event;
52 kevent(kq, nullptr, 0, &event, 1, nullptr);
53 std::cerr << "Parent process exited, shutting down..." << '\n';
54 close(kq);
55 std::exit(0);
56 }).detach();
57#endif
58}
59
60// ---------------------------------------------------------------------------
61// IPC server execution
62// ---------------------------------------------------------------------------
63
64int execute_avm_server(const std::string& input_path, const std::string& wsdb_path, const std::string& cdb_path)
65{
66 // Connect to WSDB server with retry
67 std::cerr << "Connecting to aztec-wsdb at " << wsdb_path << '\n';
68 constexpr int max_retries = 50;
69 constexpr int retry_delay_ms = 100;
71 for (int attempt = 0; attempt < max_retries; ++attempt) {
72 try {
73 wsdb_client = std::make_unique<wsdb::WsdbIpcClient>(wsdb_path);
74 break;
75 } catch (const std::exception& e) {
76 if (attempt == max_retries - 1) {
77 std::cerr << "Failed to connect to aztec-wsdb after " << max_retries << " attempts: " << e.what()
78 << '\n';
79 return 1;
80 }
81 std::this_thread::sleep_for(std::chrono::milliseconds(retry_delay_ms));
82 }
83 }
84
85 // Connect to CDB server with retry (TS server may still be binding its socket)
86 std::cerr << "Connecting to aztec-cdb at " << cdb_path << '\n';
88 for (int attempt = 0; attempt < max_retries; ++attempt) {
89 try {
90 cdb_client = std::make_unique<cdb::CdbIpcContractDB>(cdb_path);
91 break;
92 } catch (const std::exception& e) {
93 if (attempt == max_retries - 1) {
94 std::cerr << "Failed to connect to aztec-cdb after " << max_retries << " attempts: " << e.what()
95 << '\n';
96 return 1;
97 }
98 std::this_thread::sleep_for(std::chrono::milliseconds(retry_delay_ms));
99 }
100 }
101
102 AvmRequest request{ .cdb_client = *cdb_client, .wsdb_client = *wsdb_client };
103
104 // Create IPC server
106
107 if (input_path.size() >= 5 && input_path.substr(input_path.size() - 5) == ".sock") {
108 server = ipc::IpcServer::create_socket(input_path, 1);
109 std::cerr << "Socket server at " << input_path << '\n';
110 } else {
111 std::cerr << "Error: --input path must end with .sock" << '\n';
112 return 1;
113 }
114
115 // Set up signal handlers
116 static ipc::IpcServer* global_server = server.get();
117
118 auto graceful_shutdown_handler = [](int signal) {
119 std::cerr << "\nReceived signal " << signal << ", shutting down gracefully..." << '\n';
120 if (global_server) {
121 global_server->request_shutdown();
122 }
123 };
124
125 auto fatal_error_handler = [](int signal) {
126 const char* signal_name = (signal == SIGBUS) ? "SIGBUS" : (signal == SIGSEGV) ? "SIGSEGV" : "UNKNOWN";
127 std::cerr << "\nFatal error: received " << signal_name << '\n';
128 if (global_server) {
129 global_server->close();
130 }
131 std::exit(1);
132 };
133
134 // SIGUSR1 cancels the active simulation without killing the process.
135 // TypeScript sends this signal when a tx exceeds its deadline.
136 auto cancel_simulation_handler = [](int /*signal*/) {
138 if (token) {
139 token->cancel();
140 }
141 };
142
143 (void)std::signal(SIGTERM, graceful_shutdown_handler);
144 (void)std::signal(SIGINT, graceful_shutdown_handler);
145 (void)std::signal(SIGUSR1, cancel_simulation_handler);
146 (void)std::signal(SIGBUS, fatal_error_handler);
147 (void)std::signal(SIGSEGV, fatal_error_handler);
148
149 setup_parent_death_monitoring();
150
151 if (!server->listen()) {
152 std::cerr << "Error: Could not start IPC server" << '\n';
153 return 1;
154 }
155
156 std::cerr << "aztec-avm IPC server ready" << '\n';
157
158 // Run server with AVM command handler
159 server->run([&request](int client_id, std::span<const uint8_t> raw_request) -> std::vector<uint8_t> {
160 try {
161 // Deserialize msgpack command
162 auto unpacked = msgpack::unpack(reinterpret_cast<const char*>(raw_request.data()), raw_request.size());
163 auto obj = unpacked.get();
164
165 // Expect array of size 1 (tuple wrapping)
166 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
167 if (obj.type != msgpack::type::ARRAY || obj.via.array.size != 1) {
168 std::cerr << "Error: Expected array of size 1 from client " << client_id << '\n';
169 return {};
170 }
171
172 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
173 auto& command_obj = obj.via.array.ptr[0];
174
175 // Check for shutdown before converting
176 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
177 if (command_obj.type == msgpack::type::ARRAY && command_obj.via.array.size == 2 &&
178 command_obj.via.array.ptr[0].type == msgpack::type::STR) {
179 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
180 std::string_view command_name(command_obj.via.array.ptr[0].via.str.ptr,
181 command_obj.via.array.ptr[0].via.str.size);
182 bool is_shutdown = (command_name == "AvmShutdown");
183
184 // Convert and execute
185 AvmCommand command;
186 command_obj.convert(command);
187 auto response = avm_dispatch(request, std::move(command));
188
189 // Serialize response
190 msgpack::sbuffer response_buffer;
191 msgpack::pack(response_buffer, response);
192 std::vector<uint8_t> result(response_buffer.data(), response_buffer.data() + response_buffer.size());
193
194 if (is_shutdown) {
195 throw ipc::ShutdownRequested(std::move(result));
196 }
197
198 return result;
199 }
200
201 // Fallback: try converting directly
202 AvmCommand command;
203 command_obj.convert(command);
204 auto response = avm_dispatch(request, std::move(command));
205
206 msgpack::sbuffer response_buffer;
207 msgpack::pack(response_buffer, response);
208 return std::vector<uint8_t>(response_buffer.data(), response_buffer.data() + response_buffer.size());
209
210 } catch (const ipc::ShutdownRequested&) {
211 throw;
212 } catch (const std::exception& e) {
213 std::cerr << "Error processing request from client " << client_id << ": " << e.what() << '\n';
214 std::cerr.flush();
215
216 AvmErrorResponse error_response{ .message = std::string(e.what()) };
217 AvmCommandResponse response = error_response;
218
219 msgpack::sbuffer response_buffer;
220 msgpack::pack(response_buffer, response);
221 return std::vector<uint8_t>(response_buffer.data(), response_buffer.data() + response_buffer.size());
222 }
223 });
224
225 server->close();
226 return 0;
227}
228
229} // namespace bb::avm
AvmCommand NamedUnion, AvmRequest context, and dispatch function.
A wrapper around std::variant that provides msgpack serialization based on type names.
Abstract interface for IPC server.
virtual void close()=0
Close the server and all client connections.
static std::unique_ptr< IpcServer > create_socket(const std::string &socket_path, int max_clients)
virtual void request_shutdown()
Request graceful shutdown.
Exception thrown by handler to signal graceful shutdown.
std::atomic< avm2::simulation::CancellationToken * > g_active_cancellation_token
int execute_avm_server(const std::string &input_path, const std::string &wsdb_path, const std::string &cdb_path)
Start the aztec-avm IPC server.
AvmCommandResponse avm_dispatch(AvmRequest &request, AvmCommand &&command)
Top-level AVM API entry point. Takes an AvmRequest and dispatches the command.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
simulation::PublicDataTreeReadWriteEvent event
Error response returned when a command fails.
Context passed to each command's execute() method. Provides access to WSDB and CDB IPC clients.
cdb::CdbIpcContractDB & cdb_client