Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
avm_simulate_napi.cpp
Go to the documentation of this file.
2
3#include <memory>
4#include <vector>
5
13
14namespace bb::nodejs {
15
16namespace {
17// Log levels from TS foundation/src/log/log-levels.ts: ['silent', 'fatal', 'error', 'warn', 'info', 'verbose', 'debug',
18// 'trace'] Map: 0=silent, 1=fatal, 2=error, 3=warn, 4=info, 5=verbose, 6=debug, 7=trace
19constexpr int LOG_LEVEL_VERBOSE = 5;
20constexpr int LOG_LEVEL_TRACE = 7;
21
22// Helper to set logging flags based on TS log level
23inline void set_logging_from_level(int log_level)
24{
25 // Turn verbose_logging on if log level is verbose (5) or above
26 verbose_logging = (log_level >= LOG_LEVEL_VERBOSE);
27 // Turn debug_logging on if log level is trace (7) or above
28 debug_logging = (log_level >= LOG_LEVEL_TRACE);
29}
30
31// Callback method names
32constexpr const char* CALLBACK_GET_CONTRACT_INSTANCE = "getContractInstance";
33constexpr const char* CALLBACK_GET_CONTRACT_CLASS = "getContractClass";
34constexpr const char* CALLBACK_ADD_CONTRACTS = "addContracts";
35constexpr const char* CALLBACK_GET_BYTECODE = "getBytecodeCommitment";
36constexpr const char* CALLBACK_GET_DEBUG_NAME = "getDebugFunctionName";
37constexpr const char* CALLBACK_CREATE_CHECKPOINT = "createCheckpoint";
38constexpr const char* CALLBACK_COMMIT_CHECKPOINT = "commitCheckpoint";
39constexpr const char* CALLBACK_REVERT_CHECKPOINT = "revertCheckpoint";
40
41// RAII helper to automatically release thread-safe functions
42// Used inside the async lambda to ensure cleanup in all code paths
43class TsfnReleaser {
45
46 public:
47 explicit TsfnReleaser(std::vector<std::shared_ptr<Napi::ThreadSafeFunction>> tsfns)
48 : tsfns_(std::move(tsfns))
49 {}
50
51 ~TsfnReleaser()
52 {
53 for (auto& tsfn : tsfns_) {
54 if (tsfn) {
55 tsfn->Release();
56 }
57 }
58 }
59
60 // Prevent copying and moving
61 TsfnReleaser(const TsfnReleaser&) = delete;
62 TsfnReleaser& operator=(const TsfnReleaser&) = delete;
63 TsfnReleaser(TsfnReleaser&&) = delete;
64 TsfnReleaser& operator=(TsfnReleaser&&) = delete;
65};
66
67// Helper to create thread-safe function wrapper
68inline std::shared_ptr<Napi::ThreadSafeFunction> make_tsfn(Napi::Env env, Napi::Function fn, const char* name)
69{
70 return std::make_shared<Napi::ThreadSafeFunction>(Napi::ThreadSafeFunction::New(env, fn, name, 0, 1));
71}
72
73// Bundle all contract-related thread-safe functions with named access
74struct ContractTsfns {
83
85 {
88 }
89};
90
91// Helper to validate and extract contract provider callbacks
92struct ContractCallbacks {
93 static constexpr const char* ALL_METHODS[] = { CALLBACK_GET_CONTRACT_INSTANCE, CALLBACK_GET_CONTRACT_CLASS,
94 CALLBACK_ADD_CONTRACTS, CALLBACK_GET_BYTECODE,
95 CALLBACK_GET_DEBUG_NAME, CALLBACK_CREATE_CHECKPOINT,
96 CALLBACK_COMMIT_CHECKPOINT, CALLBACK_REVERT_CHECKPOINT };
97
98 static void validate(Napi::Env env, Napi::Object provider)
99 {
100 for (const char* method : ALL_METHODS) {
101 if (!provider.Has(method)) {
102 throw Napi::TypeError::New(
103 env, std::string("contractProvider must have ") + method + " method. Missing methods: " + method);
104 }
105 }
106 }
107
108 static Napi::Function get(Napi::Object provider, const char* name)
109 {
110 return provider.Get(name).As<Napi::Function>();
111 }
112};
113} // namespace
114
115Napi::Value AvmSimulateNapi::simulate(const Napi::CallbackInfo& cb_info)
116{
117 Napi::Env env = cb_info.Env();
118
119 // Validate arguments - expects 4 arguments
120 // arg[0]: inputs Buffer (required)
121 // arg[1]: contractProvider object (required)
122 // arg[2]: worldStateHandle external (required)
123 // arg[3]: logLevel number (required) - index into TS LogLevels array
124 if (cb_info.Length() < 4) {
125 throw Napi::TypeError::New(env,
126 "Wrong number of arguments. Expected 4 arguments: inputs Buffer, contractProvider "
127 "object, worldStateHandle, and logLevel.");
128 }
129
130 if (!cb_info[0].IsBuffer()) {
131 throw Napi::TypeError::New(env,
132 "First argument must be a Buffer containing serialized AvmFastSimulationInputs");
133 }
134
135 if (!cb_info[1].IsObject()) {
136 throw Napi::TypeError::New(env, "Second argument must be a contractProvider object");
137 }
138
139 if (!cb_info[2].IsExternal()) {
140 throw Napi::TypeError::New(env, "Third argument must be a WorldState handle (External)");
141 }
142
143 if (!cb_info[3].IsNumber()) {
144 throw Napi::TypeError::New(env, "Fourth argument must be a log level number (0-7)");
145 }
146
147 // Extract log level and set logging flags
148 int log_level = cb_info[3].As<Napi::Number>().Int32Value();
149 set_logging_from_level(log_level);
150
151 // Extract the inputs buffer
152 auto inputs_buffer = cb_info[0].As<Napi::Buffer<uint8_t>>();
153 size_t length = inputs_buffer.Length();
154
155 // Copy the buffer data into C++ memory (we can't access Napi objects from worker thread)
156 auto data = std::make_shared<std::vector<uint8_t>>(inputs_buffer.Data(), inputs_buffer.Data() + length);
157
158 // Extract and validate contract provider callbacks
159 auto contract_provider = cb_info[1].As<Napi::Object>();
160 ContractCallbacks::validate(env, contract_provider);
161
162 // Create thread-safe function wrappers for callbacks
163 // These allow us to call TypeScript from the C++ worker thread
164 ContractTsfns tsfns{
165 .instance = make_tsfn(env,
166 ContractCallbacks::get(contract_provider, CALLBACK_GET_CONTRACT_INSTANCE),
167 CALLBACK_GET_CONTRACT_INSTANCE),
168 .class_ = make_tsfn(
169 env, ContractCallbacks::get(contract_provider, CALLBACK_GET_CONTRACT_CLASS), CALLBACK_GET_CONTRACT_CLASS),
170 .add_contracts =
171 make_tsfn(env, ContractCallbacks::get(contract_provider, CALLBACK_ADD_CONTRACTS), CALLBACK_ADD_CONTRACTS),
172 .bytecode =
173 make_tsfn(env, ContractCallbacks::get(contract_provider, CALLBACK_GET_BYTECODE), CALLBACK_GET_BYTECODE),
174 .debug_name =
175 make_tsfn(env, ContractCallbacks::get(contract_provider, CALLBACK_GET_DEBUG_NAME), CALLBACK_GET_DEBUG_NAME),
176 .create_checkpoint = make_tsfn(
177 env, ContractCallbacks::get(contract_provider, CALLBACK_CREATE_CHECKPOINT), CALLBACK_CREATE_CHECKPOINT),
178 .commit_checkpoint = make_tsfn(
179 env, ContractCallbacks::get(contract_provider, CALLBACK_COMMIT_CHECKPOINT), CALLBACK_COMMIT_CHECKPOINT),
180 .revert_checkpoint = make_tsfn(
181 env, ContractCallbacks::get(contract_provider, CALLBACK_REVERT_CHECKPOINT), CALLBACK_REVERT_CHECKPOINT),
182 };
183
184 // Extract WorldState handle (3rd argument)
185 auto external = cb_info[2].As<Napi::External<world_state::WorldState>>();
186 world_state::WorldState* ws_ptr = external.Data();
187
188 // Create a deferred promise
190
191 // Create async operation that will run on a worker thread
192 auto* op = new AsyncOperation(env, deferred, [data, tsfns, ws_ptr](msgpack::sbuffer& result_buffer) {
193 // Ensure all thread-safe functions are released in all code paths
194 TsfnReleaser releaser = TsfnReleaser(tsfns.to_vector());
195
196 try {
197 // Deserialize inputs from msgpack
199 msgpack::object_handle obj_handle =
200 msgpack::unpack(reinterpret_cast<const char*>(data->data()), data->size());
201 msgpack::object obj = obj_handle.get();
202 obj.convert(inputs);
203
204 // Create TsCallbackContractDB with TypeScript callbacks
205 TsCallbackContractDB contract_db(*tsfns.instance,
206 *tsfns.class_,
207 *tsfns.add_contracts,
208 *tsfns.bytecode,
209 *tsfns.debug_name,
210 *tsfns.create_checkpoint,
211 *tsfns.commit_checkpoint,
212 *tsfns.revert_checkpoint);
213
214 // Create AVM API and run simulation with the callback-based contracts DB and
215 // WorldState reference
216 avm2::AvmSimAPI avm;
217 avm2::TxSimulationResult result = avm.simulate(inputs, contract_db, *ws_ptr);
218
219 // Serialize the simulation result with msgpack into the return buffer to TS.
220 msgpack::pack(result_buffer, result);
221 } catch (const std::exception& e) {
222 // Rethrow with context (RAII wrappers will clean up automatically)
223 throw std::runtime_error(std::string("AVM simulation failed: ") + e.what());
224 } catch (...) {
225 throw std::runtime_error("AVM simulation failed with unknown exception");
226 }
227 });
228
229 // Napi is now responsible for destroying this object
230 op->Queue();
231
232 return deferred->Promise();
233}
234
235Napi::Value AvmSimulateNapi::simulateWithHintedDbs(const Napi::CallbackInfo& cb_info)
236{
237 Napi::Env env = cb_info.Env();
238
239 // Validate arguments - expects 2 arguments
240 // arg[0]: inputs Buffer (required) - AvmProvingInputs
241 // arg[1]: logLevel number (required) - index into TS LogLevels array
242 if (cb_info.Length() < 2) {
243 throw Napi::TypeError::New(env,
244 "Wrong number of arguments. Expected 2 arguments: AvmProvingInputs/AvmCircuitInputs "
245 "msgpack Buffer and logLevel.");
246 }
247
248 if (!cb_info[0].IsBuffer()) {
249 throw Napi::TypeError::New(
250 env, "First argument must be a Buffer containing serialized AvmProvingInputs/AvmCircuitInputs");
251 }
252
253 if (!cb_info[1].IsNumber()) {
254 throw Napi::TypeError::New(env, "Second argument must be a log level number (0-7)");
255 }
256
257 // Extract log level and set logging flags
258 int log_level = cb_info[1].As<Napi::Number>().Int32Value();
259 set_logging_from_level(log_level);
260
261 // Extract the inputs buffer
262 auto inputs_buffer = cb_info[0].As<Napi::Buffer<uint8_t>>();
263 size_t length = inputs_buffer.Length();
264
265 // Copy the buffer data into C++ memory (we can't access Napi objects from worker thread)
266 auto data = std::make_shared<std::vector<uint8_t>>(inputs_buffer.Data(), inputs_buffer.Data() + length);
267
268 // Create a deferred promise
270
271 // Create async operation that will run on a worker thread
272 auto* op = new AsyncOperation(env, deferred, [data](msgpack::sbuffer& result_buffer) {
273 try {
274 // Deserialize inputs from msgpack
276 msgpack::object_handle obj_handle =
277 msgpack::unpack(reinterpret_cast<const char*>(data->data()), data->size());
278 msgpack::object obj = obj_handle.get();
279 obj.convert(inputs);
280
281 // Create AVM Sim API and run simulation with the hinted DBs
282 // All hints are already in the inputs, so no runtime contract DB callbacks needed
283 avm2::AvmSimAPI avm;
285
286 // Serialize the simulation result with msgpack into the return buffer to TS.
287 msgpack::pack(result_buffer, result);
288 } catch (const std::exception& e) {
289 // Rethrow with context
290 throw std::runtime_error(std::string("AVM simulation with hinted DBs failed: ") + e.what());
291 } catch (...) {
292 throw std::runtime_error("AVM simulation with hinted DBs failed with unknown exception");
293 }
294 });
295
296 // Napi is now responsible for destroying this object
297 op->Queue();
298
299 return deferred->Promise();
300}
301
302} // namespace bb::nodejs
std::shared_ptr< Napi::ThreadSafeFunction > class_
std::shared_ptr< Napi::ThreadSafeFunction > debug_name
std::shared_ptr< Napi::ThreadSafeFunction > instance
std::vector< std::shared_ptr< Napi::ThreadSafeFunction > > tsfns_
std::shared_ptr< Napi::ThreadSafeFunction > revert_checkpoint
std::shared_ptr< Napi::ThreadSafeFunction > commit_checkpoint
std::shared_ptr< Napi::ThreadSafeFunction > create_checkpoint
std::shared_ptr< Napi::ThreadSafeFunction > bytecode
std::shared_ptr< Napi::ThreadSafeFunction > add_contracts
StrictMock< MockContractDB > contract_db
TxSimulationResult simulate_with_hinted_dbs(const AvmProvingInputs &inputs)
TxSimulationResult simulate(const FastSimulationInputs &inputs, simulation::ContractDBInterface &contract_db, world_state::WorldState &ws)
Encapsulatest some work that can be done off the JavaScript main thread.
Definition async_op.hpp:27
static Napi::Value simulate(const Napi::CallbackInfo &info)
NAPI function to simulate AVM execution.
static Napi::Value simulateWithHintedDbs(const Napi::CallbackInfo &info)
NAPI function to simulate AVM execution with pre-collected hints.
Implementation of ContractDBInterface that uses NAPI callbacks to TypeScript.
Holds the Merkle trees responsible for storing the state of the Aztec protocol.
const std::vector< MemoryValue > data
uint8_t const size_t length
Definition data_store.hpp:9
AvmProvingInputs inputs
bool debug_logging
Definition log.cpp:12
bool verbose_logging
Definition log.cpp:6
STL namespace.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13